
An app analytics dashboard with a metric switcher, an interactive area chart, and a regional breakdown.
@esuiss
bases/radix/components/blocks/dash-analytics.tsx
"use client"
import * as React from "react"
import {
Area,
AreaChart,
CartesianGrid,
useActiveTooltipDataPoints,
XAxis,
} from "recharts"
import { IconPlaceholder } from "@/app/(create)/components/icon-placeholder"
import {
type ChartConfig,
ChartContainer,
ChartTooltip,
} from "@/components/ui/chart"
import {
Item,
ItemActions,
ItemContent,
ItemGroup,
ItemSeparator,
ItemTitle,
} from "@/components/ui/item"
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarInset,
SidebarMenu,
SidebarMenuBadge,
SidebarMenuButton,
SidebarMenuItem,
SidebarProvider,
SidebarRail,
SidebarTrigger,
useSidebar,
} from "@/components/ui/sidebar"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
const apps = [
{
value: "route",
label: "Route",
hue: 150,
icon: (
<IconPlaceholder
lucide="ChartColumnIcon"
tabler="IconChartBar"
hugeicons="ChartColumnIcon"
phosphor="ChartBarIcon"
remixicon="RiBarChartLine"
className="size-3.5"
/>
),
},
{
value: "notebook",
label: "Notebook",
hue: 50,
icon: (
<IconPlaceholder
lucide="SquarePenIcon"
tabler="IconEdit"
hugeicons="PencilEdit02Icon"
phosphor="NotePencilIcon"
remixicon="RiEditBoxLine"
className="size-3.5"
/>
),
},
{
value: "fitness",
label: "Fitness+",
hue: 20,
icon: (
<IconPlaceholder
lucide="FlameIcon"
tabler="IconFlame"
hugeicons="FireIcon"
phosphor="FlameIcon"
remixicon="RiFireLine"
className="size-3.5"
/>
),
},
]
type NavItem = {
value: string
label: string
icon: React.ReactNode
badge?: React.ReactNode
group?: string
}
function NavSidebar({
items,
value,
onValueChange,
header,
footer,
}: {
items: NavItem[]
value: string
onValueChange: (value: string) => void
header?: React.ReactNode
footer?: React.ReactNode
}) {
const { isMobile, setOpenMobile } = useSidebar()
const groups = [...new Set(items.map((item) => item.group ?? ""))]
return (
<Sidebar collapsible="icon">
{header && (
<SidebarHeader className="group-data-[collapsible=icon]:hidden">
{header}
</SidebarHeader>
)}
<SidebarContent>
{groups.map((group) => (
<SidebarGroup key={group}>
{group && <SidebarGroupLabel>{group}</SidebarGroupLabel>}
<SidebarGroupContent>
<SidebarMenu>
{items
.filter((item) => (item.group ?? "") === group)
.map((item) => (
<SidebarMenuItem key={item.value}>
<SidebarMenuButton
isActive={item.value === value}
tooltip={item.label}
aria-current={item.value === value ? "page" : undefined}
onClick={() => {
onValueChange(item.value)
if (isMobile) setOpenMobile(false)
}}
>
{item.icon}
<span>{item.label}</span>
</SidebarMenuButton>
{item.badge !== undefined && (
<SidebarMenuBadge>{item.badge}</SidebarMenuBadge>
)}
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
))}
</SidebarContent>
{footer && (
<SidebarFooter className="group-data-[collapsible=icon]:hidden">
{footer}
</SidebarFooter>
)}
<SidebarRail />
</Sidebar>
)
}
const items: NavItem[] = [
...apps.map((app) => ({
value: app.value,
label: app.label,
group: "Apps",
icon: (
<span
style={{
background: `linear-gradient(160deg, oklch(0.8 0.14 ${app.hue}), oklch(0.58 0.14 ${app.hue + 25}))`,
}}
className="flex size-6 shrink-0 items-center justify-center rounded-md text-white"
>
{app.icon}
</span>
),
})),
{
value: "analytics",
label: "Analytics",
icon: (
<IconPlaceholder
lucide="ChartColumnIcon"
tabler="IconChartBar"
hugeicons="ChartColumnIcon"
phosphor="ChartBarIcon"
remixicon="RiBarChartLine"
/>
),
group: "Reports",
},
{
value: "sales",
label: "Sales and Trends",
icon: (
<IconPlaceholder
lucide="TagIcon"
tabler="IconTag"
hugeicons="Tag01Icon"
phosphor="TagIcon"
remixicon="RiPriceTag3Line"
/>
),
group: "Reports",
},
{
value: "ratings",
label: "Ratings",
icon: (
<IconPlaceholder
lucide="StarIcon"
tabler="IconStar"
hugeicons="StarIcon"
phosphor="StarIcon"
remixicon="RiStarLine"
/>
),
group: "Reports",
},
]
const metrics = [
{
value: "impressions",
label: "Impressions",
unit: "K",
data: [12, 14, 13, 18, 17, 22, 25, 24, 28, 31, 29, 34],
},
{
value: "downloads",
label: "Downloads",
unit: "K",
data: [2.1, 2.4, 2.2, 3.1, 2.9, 3.8, 4.2, 4, 4.6, 5.1, 4.8, 5.6],
},
{
value: "revenue",
label: "Revenue",
unit: "K $",
data: [8, 9, 9, 11, 12, 14, 15, 15, 17, 19, 18, 21],
},
{
value: "sessions",
label: "Sessions",
unit: "K",
data: [40, 42, 41, 47, 48, 55, 58, 57, 62, 66, 64, 71],
},
]
const regions = [
{ name: "United States", share: 0.46 },
{ name: "Germany", share: 0.21 },
{ name: "United Kingdom", share: 0.14 },
{ name: "Netherlands", share: 0.09 },
]
const chartConfig = {
value: { label: "Value", color: "var(--primary)" },
} satisfies ChartConfig
function ActiveWeek({
onChange,
}: {
onChange: (index: number | null) => void
}) {
const points = useActiveTooltipDataPoints<{ index?: number }>()
const index = points?.[0]?.index ?? null
React.useEffect(() => onChange(index), [index, onChange])
return null
}
export function DashAnalytics() {
const id = React.useId()
const [section, setSection] = React.useState("route")
const [metricValue, setMetricValue] = React.useState("downloads")
const [active, setActive] = React.useState<number | null>(null)
const appIndex = Math.max(
0,
apps.findIndex((app) => app.value === section)
)
const metric =
metrics.find((item) => item.value === metricValue) ?? metrics[1]
if (!metric) return null
const data = metric.data.map((value, index) => ({
week: `Week ${index + 1}`,
index,
value: Number((value * (1 - appIndex * 0.28)).toFixed(2)),
}))
const current = active ?? data.length - 1
const point = data[current]
if (!point) return null
const latest = data.at(-1)?.value ?? 0
const format = (value: number) => `${value.toFixed(1)} ${metric.unit}`
const title = items.find((item) => item.value === section)?.label
return (
<SidebarProvider>
<NavSidebar items={items} value={section} onValueChange={setSection} />
<SidebarInset>
<div className="flex flex-col gap-5 px-5 py-5.5 md:px-7">
<div className="flex flex-wrap items-start gap-4">
<SidebarTrigger className="-ms-2 mt-1" />
<div className="flex flex-1 flex-col">
<h1 className="text-3xl font-semibold tracking-tight">{title}</h1>
<p className="text-sm text-muted-foreground">
Last 12 weeks · All regions
</p>
</div>
<ToggleGroup
spacing={0}
type="single"
value={metricValue}
onValueChange={(value: string) => {
if (value) setMetricValue(value)
}}
aria-label="Metric"
className="no-scrollbar max-w-full overflow-x-auto"
>
{metrics.map((item) => (
<ToggleGroupItem key={item.value} value={item.value} size="sm">
{item.label}
</ToggleGroupItem>
))}
</ToggleGroup>
</div>
<div className="rounded-xl bg-muted px-4 pt-3.5 pb-2.5">
<div className="flex items-end gap-2.5">
<div className="flex flex-1 flex-col" aria-live="polite">
<span className="text-sm text-muted-foreground">
{current === data.length - 1 ? "This week" : point.week} ·{" "}
{metric.label}
</span>
<span className="text-3xl font-bold tracking-tight tabular-nums">
{format(point.value)}
</span>
</div>
<span className="text-sm font-semibold text-[color-mix(in_oklab,var(--color-green-500),var(--foreground)_45%)] dark:text-green-500">
+{12 + metrics.indexOf(metric) * 3}%
</span>
</div>
<ChartContainer
config={chartConfig}
className="mt-3 aspect-auto h-44 w-full"
>
<AreaChart
accessibilityLayer
data={data}
margin={{ top: 8, right: 6, left: 6, bottom: 0 }}
>
<defs>
<linearGradient id={`${id}-fill`} x1="0" y1="0" x2="0" y2="1">
<stop
offset="0%"
stopColor="var(--color-value)"
stopOpacity={0.22}
/>
<stop
offset="100%"
stopColor="var(--color-value)"
stopOpacity={0}
/>
</linearGradient>
</defs>
<CartesianGrid vertical={false} strokeDasharray="2 4" />
<XAxis dataKey="week" hide />
<ChartTooltip
content={() => null}
cursor={{ stroke: "var(--color-input)" }}
defaultIndex={data.length - 1}
/>
<Area
dataKey="value"
type="linear"
stroke="var(--color-value)"
strokeWidth={2.5}
fill={`url(#${id}-fill)`}
activeDot={{
r: 5,
fill: "var(--color-value)",
stroke: "var(--color-muted)",
strokeWidth: 2,
}}
animationDuration={500}
/>
<ActiveWeek onChange={setActive} />
</AreaChart>
</ChartContainer>
</div>
<h2 className="-mb-2.5 px-1 text-sm font-semibold">By region</h2>
<ItemGroup variant="inset">
{regions.map((region, index) => (
<React.Fragment key={region.name}>
{index > 0 && <ItemSeparator />}
<Item size="sm" role="listitem">
<ItemContent>
<ItemTitle className="font-normal">{region.name}</ItemTitle>
</ItemContent>
<ItemActions className="gap-4">
<span className="hidden h-1.5 w-35 overflow-hidden rounded-full bg-secondary sm:block">
<span
style={{ width: `${(region.share / 0.46) * 100}%` }}
className="block h-full rounded-full bg-primary transition-[width] duration-500 ease-[cubic-bezier(0.32,0.72,0,1)]"
/>
</span>
<span className="w-18 text-end tabular-nums">
{format(latest * region.share)}
</span>
</ItemActions>
</Item>
</React.Fragment>
))}
</ItemGroup>
</div>
</SidebarInset>
</SidebarProvider>
)
}

























