Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

77 changes: 77 additions & 0 deletions src/features/dashboard/components/analytics-chart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Area, AreaChart, ResponsiveContainer, XAxis, YAxis } from 'recharts'

const data = [
{
name: 'Mon',
clicks: Math.floor(Math.random() * 900) + 100,
uniques: Math.floor(Math.random() * 700) + 80,
},
{
name: 'Tue',
clicks: Math.floor(Math.random() * 900) + 100,
uniques: Math.floor(Math.random() * 700) + 80,
},
{
name: 'Wed',
clicks: Math.floor(Math.random() * 900) + 100,
uniques: Math.floor(Math.random() * 700) + 80,
},
{
name: 'Thu',
clicks: Math.floor(Math.random() * 900) + 100,
uniques: Math.floor(Math.random() * 700) + 80,
},
{
name: 'Fri',
clicks: Math.floor(Math.random() * 900) + 100,
uniques: Math.floor(Math.random() * 700) + 80,
},
{
name: 'Sat',
clicks: Math.floor(Math.random() * 900) + 100,
uniques: Math.floor(Math.random() * 700) + 80,
},
{
name: 'Sun',
clicks: Math.floor(Math.random() * 900) + 100,
uniques: Math.floor(Math.random() * 700) + 80,
},
]

export function AnalyticsChart() {
return (
<ResponsiveContainer width='100%' height={300}>
<AreaChart data={data}>
<XAxis
dataKey='name'
stroke='#888888'
fontSize={12}
tickLine={false}
axisLine={false}
/>
<YAxis
stroke='#888888'
fontSize={12}
tickLine={false}
axisLine={false}
/>
<Area
type='monotone'
dataKey='clicks'
stroke='currentColor'
className='text-primary'
fill='currentColor'
fillOpacity={0.15}
/>
<Area
type='monotone'
dataKey='uniques'
stroke='currentColor'
className='text-muted-foreground'
fill='currentColor'
fillOpacity={0.1}
/>
</AreaChart>
</ResponsiveContainer>
)
}
193 changes: 193 additions & 0 deletions src/features/dashboard/components/analytics.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card'
import { AnalyticsChart } from './analytics-chart'

export function Analytics() {
type Item = { name: string; value: number }

const SimpleBarList = ({
items,
valueFormatter,
barClass,
}: {
items: Item[]
valueFormatter: (n: number) => string
barClass: string
}) => {
const max = Math.max(...items.map((i) => i.value), 1)
return (
<ul className='space-y-3'>
{items.map((i) => {
const width = `${Math.round((i.value / max) * 100)}%`
return (
<li
key={i.name}
className='flex items-center justify-between gap-3'
>
<div className='min-w-0 flex-1'>
<div className='text-muted-foreground mb-1 truncate text-xs'>
{i.name}
</div>
<div className='bg-muted h-2.5 w-full rounded-full'>
<div
className={`h-2.5 rounded-full ${barClass}`}
style={{ width }}
/>
</div>
</div>
<div className='ps-2 text-xs font-medium tabular-nums'>
{valueFormatter(i.value)}
</div>
</li>
)
})}
</ul>
)
}
return (
<div className='space-y-4'>
<Card>
<CardHeader>
<CardTitle>Traffic Overview</CardTitle>
<CardDescription>Weekly clicks and unique visitors</CardDescription>
</CardHeader>
<CardContent className='px-6'>
<AnalyticsChart />
</CardContent>
</Card>
<div className='grid gap-4 sm:grid-cols-2 lg:grid-cols-4'>
<Card>
<CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
<CardTitle className='text-sm font-medium'>Total Clicks</CardTitle>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 24 24'
fill='none'
stroke='currentColor'
strokeLinecap='round'
strokeLinejoin='round'
strokeWidth='2'
className='text-muted-foreground h-4 w-4'
>
<path d='M3 3v18h18' />
<path d='M7 15l4-4 4 4 4-6' />
</svg>
</CardHeader>
<CardContent>
<div className='text-2xl font-bold'>1,248</div>
<p className='text-muted-foreground text-xs'>+12.4% vs last week</p>
</CardContent>
</Card>
<Card>
<CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
<CardTitle className='text-sm font-medium'>
Unique Visitors
</CardTitle>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 24 24'
fill='none'
stroke='currentColor'
strokeLinecap='round'
strokeLinejoin='round'
strokeWidth='2'
className='text-muted-foreground h-4 w-4'
>
<circle cx='12' cy='7' r='4' />
<path d='M6 21v-2a6 6 0 0 1 12 0v2' />
</svg>
</CardHeader>
<CardContent>
<div className='text-2xl font-bold'>832</div>
<p className='text-muted-foreground text-xs'>+5.8% vs last week</p>
</CardContent>
</Card>
<Card>
<CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
<CardTitle className='text-sm font-medium'>Bounce Rate</CardTitle>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 24 24'
fill='none'
stroke='currentColor'
strokeLinecap='round'
strokeLinejoin='round'
strokeWidth='2'
className='text-muted-foreground h-4 w-4'
>
<path d='M3 12h6l3 6 3-6h6' />
</svg>
</CardHeader>
<CardContent>
<div className='text-2xl font-bold'>42%</div>
<p className='text-muted-foreground text-xs'>-3.2% vs last week</p>
</CardContent>
</Card>
<Card>
<CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
<CardTitle className='text-sm font-medium'>Avg. Session</CardTitle>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 24 24'
fill='none'
stroke='currentColor'
strokeLinecap='round'
strokeLinejoin='round'
strokeWidth='2'
className='text-muted-foreground h-4 w-4'
>
<circle cx='12' cy='12' r='10' />
<path d='M12 6v6l4 2' />
</svg>
</CardHeader>
<CardContent>
<div className='text-2xl font-bold'>3m 24s</div>
<p className='text-muted-foreground text-xs'>+18s vs last week</p>
</CardContent>
</Card>
</div>
<div className='grid grid-cols-1 gap-4 lg:grid-cols-7'>
<Card className='col-span-1 lg:col-span-4'>
<CardHeader>
<CardTitle>Referrers</CardTitle>
<CardDescription>Top sources driving traffic</CardDescription>
</CardHeader>
<CardContent>
<SimpleBarList
items={[
{ name: 'Direct', value: 512 },
{ name: 'Product Hunt', value: 238 },
{ name: 'Twitter', value: 174 },
{ name: 'Blog', value: 104 },
]}
barClass='bg-primary'
valueFormatter={(n) => `${n}`}
/>
</CardContent>
</Card>
<Card className='col-span-1 lg:col-span-3'>
<CardHeader>
<CardTitle>Devices</CardTitle>
<CardDescription>How users access your app</CardDescription>
</CardHeader>
<CardContent>
<SimpleBarList
items={[
{ name: 'Desktop', value: 74 },
{ name: 'Mobile', value: 22 },
{ name: 'Tablet', value: 4 },
]}
barClass='bg-muted-foreground'
valueFormatter={(n) => `${n}%`}
/>
</CardContent>
</Card>
</div>
</div>
)
}
8 changes: 5 additions & 3 deletions src/features/dashboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { TopNav } from '@/components/layout/top-nav'
import { ProfileDropdown } from '@/components/profile-dropdown'
import { Search } from '@/components/search'
import { ThemeSwitch } from '@/components/theme-switch'
import { Analytics } from './components/analytics'
import { Overview } from './components/overview'
import { RecentSales } from './components/recent-sales'

Expand Down Expand Up @@ -47,9 +48,7 @@ export function Dashboard() {
<div className='w-full overflow-x-auto pb-2'>
<TabsList>
<TabsTrigger value='overview'>Overview</TabsTrigger>
<TabsTrigger value='analytics' disabled>
Analytics
</TabsTrigger>
<TabsTrigger value='analytics'>Analytics</TabsTrigger>
<TabsTrigger value='reports' disabled>
Reports
</TabsTrigger>
Expand Down Expand Up @@ -184,6 +183,9 @@ export function Dashboard() {
</Card>
</div>
</TabsContent>
<TabsContent value='analytics' className='space-y-4'>
<Analytics />
</TabsContent>
</Tabs>
</Main>
</>
Expand Down