Files
mapcn/public/r/analytics-map.json
2026-03-16 17:20:51 +05:30

49 lines
12 KiB
JSON

{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "analytics-map",
"title": "Analytics Map",
"description": "Real-time analytics overview with a world map, breakdown cards, and device stats.",
"dependencies": [
"recharts",
"lucide-react"
],
"registryDependencies": [
"map",
"card",
"chart"
],
"files": [
{
"path": "src/registry/blocks/analytics-map/page.tsx",
"content": "\"use client\";\n\nimport {\n Map,\n MapControls,\n MapMarker,\n MarkerContent,\n MarkerTooltip,\n} from \"@/registry/map\";\nimport { OverviewCard } from \"./components/overview-card\";\nimport { BreakdownCard } from \"./components/breakdown-card\";\nimport {\n locations,\n visitedPagesRows,\n countriesRows,\n referrersRows,\n browsersRows,\n} from \"./data\";\n\nconst MAP_HEIGHT = \"38rem\";\n\nexport function AnalyticsMapBlock() {\n return (\n <div\n className=\"bg-background relative min-h-screen\"\n style={{ \"--map-height\": MAP_HEIGHT } as React.CSSProperties}\n >\n <div className=\"relative h-(--map-height)\">\n <Map\n center={[-2, 16]}\n zoom={1.5}\n scrollZoom={false}\n renderWorldCopies={true}\n >\n <MapControls showFullscreen />\n {locations.map((location) => (\n <MapMarker\n key={location.city}\n longitude={location.lng}\n latitude={location.lat}\n >\n <MarkerContent>\n <div\n className=\"rounded-full bg-blue-500/70\"\n style={{\n width: location.size * 3,\n height: location.size * 3,\n }}\n />\n </MarkerContent>\n <MarkerTooltip\n offset={20}\n className=\"bg-background text-foreground border\"\n >\n <p className=\"text-muted-foreground font-medium\">\n {location.city}\n </p>\n <p className=\"mt-1\">\n <span className=\"font-medium tabular-nums\">\n {location.size}\n </span>{\" \"}\n active users\n </p>\n </MarkerTooltip>\n </MapMarker>\n ))}\n </Map>\n <div\n className=\"via-background/30 to-background pointer-events-none absolute inset-x-0 bottom-0 h-40 bg-linear-to-b from-transparent\"\n aria-hidden\n />\n <OverviewCard />\n </div>\n\n <div className=\"grid gap-4 p-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n <BreakdownCard title=\"Visited pages\" rows={visitedPagesRows} />\n <BreakdownCard title=\"Referrers\" rows={referrersRows} />\n <BreakdownCard title=\"Countries\" rows={countriesRows} />\n <BreakdownCard title=\"Browsers\" rows={browsersRows} />\n </div>\n </div>\n );\n}\n",
"type": "registry:page",
"target": "app/analytics/page.tsx"
},
{
"path": "src/registry/blocks/analytics-map/data.ts",
"content": "import { type ChartConfig } from \"@/components/ui/chart\";\n\nexport interface LocationPoint {\n city: string;\n lng: number;\n lat: number;\n size: number;\n}\n\nexport interface BreakdownRow {\n label: string;\n value: number;\n}\n\nexport const locations: LocationPoint[] = [\n { city: \"San Francisco\", lng: -122.4194, lat: 37.7749, size: 16 },\n { city: \"New York\", lng: -74.006, lat: 40.7128, size: 15 },\n { city: \"Toronto\", lng: -79.3832, lat: 43.6532, size: 11 },\n { city: \"Mexico City\", lng: -99.1332, lat: 19.4326, size: 10 },\n { city: \"Sao Paulo\", lng: -46.6333, lat: -23.5505, size: 12 },\n { city: \"Buenos Aires\", lng: -58.3816, lat: -34.6037, size: 9 },\n { city: \"London\", lng: -0.1276, lat: 51.5074, size: 14 },\n { city: \"Berlin\", lng: 13.405, lat: 52.52, size: 11 },\n { city: \"Paris\", lng: 2.3522, lat: 48.8566, size: 13 },\n { city: \"Madrid\", lng: -3.7038, lat: 40.4168, size: 10 },\n { city: \"Cairo\", lng: 31.2357, lat: 30.0444, size: 9 },\n { city: \"Lagos\", lng: 3.3792, lat: 6.5244, size: 10 },\n { city: \"Mumbai\", lng: 72.8777, lat: 19.076, size: 13 },\n { city: \"Dubai\", lng: 55.2708, lat: 25.2048, size: 11 },\n { city: \"Seoul\", lng: 126.978, lat: 37.5665, size: 12 },\n { city: \"Singapore\", lng: 103.8198, lat: 1.3521, size: 10 },\n { city: \"Tokyo\", lng: 139.6917, lat: 35.6895, size: 12 },\n { city: \"Sydney\", lng: 151.2093, lat: -33.8688, size: 9 },\n { city: \"Auckland\", lng: 174.7633, lat: -36.8485, size: 8 },\n];\n\nexport const usersPerDay = [\n { day: \"Mon\", users: 320 },\n { day: \"Tue\", users: 410 },\n { day: \"Wed\", users: 560 },\n { day: \"Thu\", users: 640 },\n { day: \"Fri\", users: 780 },\n { day: \"Sat\", users: 690 },\n { day: \"Sun\", users: 720 },\n];\n\nexport const usersPerDayChartConfig = {\n users: {\n label: \"Users\",\n color: \"var(--color-blue-500)\",\n },\n} satisfies ChartConfig;\n\nexport const deviceCategoryData = [\n { name: \"Desktop\", value: 73.3, fill: \"var(--color-blue-500)\" },\n { name: \"Mobile\", value: 25.0, fill: \"var(--color-blue-400)\" },\n { name: \"Tablet\", value: 1.7, fill: \"var(--color-blue-300)\" },\n];\n\nexport const deviceCategoryChartConfig = {\n desktop: { label: \"Desktop\", color: \"var(--color-blue-500)\" },\n mobile: { label: \"Mobile\", color: \"var(--color-blue-400)\" },\n tablet: { label: \"Tablet\", color: \"var(--color-blue-300)\" },\n} satisfies ChartConfig;\n\nexport const visitedPagesRows: BreakdownRow[] = [\n { label: \"Home\", value: 31 },\n { label: \"Pricing\", value: 23 },\n { label: \"Docs / Basic Map\", value: 18 },\n { label: \"Installation\", value: 12 },\n { label: \"Components\", value: 9 },\n { label: \"Blog\", value: 6 },\n];\n\nexport const countriesRows: BreakdownRow[] = [\n { label: \"United States\", value: 27 },\n { label: \"India\", value: 14 },\n { label: \"United Kingdom\", value: 8 },\n { label: \"Germany\", value: 6 },\n { label: \"Japan\", value: 4 },\n { label: \"Australia\", value: 2 },\n];\n\nexport const referrersRows: BreakdownRow[] = [\n { label: \"google\", value: 38 },\n { label: \"direct\", value: 26 },\n { label: \"github.com\", value: 19 },\n { label: \"x.com\", value: 11 },\n { label: \"ui.shadcn.com\", value: 8 },\n { label: \"other\", value: 5 },\n];\n\nexport const browsersRows: BreakdownRow[] = [\n { label: \"Chrome\", value: 52 },\n { label: \"Safari\", value: 21 },\n { label: \"Firefox\", value: 14 },\n { label: \"Edge\", value: 8 },\n { label: \"Other\", value: 5 },\n];\n",
"type": "registry:component",
"target": "app/analytics/data.ts"
},
{
"path": "src/registry/blocks/analytics-map/components/overview-card.tsx",
"content": "\"use client\";\n\nimport { Card, CardContent, CardHeader } from \"@/components/ui/card\";\nimport { ChartContainer } from \"@/components/ui/chart\";\nimport { TrendingUp } from \"lucide-react\";\nimport { Area, AreaChart, Cell, Pie, PieChart } from \"recharts\";\nimport {\n deviceCategoryChartConfig,\n deviceCategoryData,\n usersPerDay,\n usersPerDayChartConfig,\n} from \"../data\";\n\nfunction MetricChart() {\n return (\n <ChartContainer\n config={usersPerDayChartConfig}\n className=\"aspect-auto h-8 w-full\"\n >\n <AreaChart data={usersPerDay} margin={{ left: 4, right: 4, top: 4 }}>\n <defs>\n <linearGradient id=\"usersGradient\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop\n offset=\"0%\"\n stopColor=\"var(--color-users)\"\n stopOpacity={0.4}\n />\n <stop\n offset=\"100%\"\n stopColor=\"var(--color-users)\"\n stopOpacity={0}\n />\n </linearGradient>\n </defs>\n\n <Area\n type=\"natural\"\n dataKey=\"users\"\n stroke=\"var(--color-users)\"\n strokeWidth={1.5}\n fill=\"url(#usersGradient)\"\n />\n </AreaChart>\n </ChartContainer>\n );\n}\n\nexport function OverviewCard() {\n return (\n <Card className=\"bg-card/70 absolute top-4 left-4 z-10 w-60 backdrop-blur-sm\">\n <CardHeader>\n <div>\n <p className=\"text-muted-foreground pb-2 text-[10px] tracking-wider uppercase\">\n Users in last 7 days\n </p>\n <p className=\"text-3xl leading-none font-semibold\">3,803</p>\n </div>\n </CardHeader>\n\n <CardContent>\n <MetricChart />\n <div className=\"mt-4 flex items-center gap-1.5 text-xs\">\n <TrendingUp className=\"size-3 text-emerald-500\" />\n <span className=\"font-medium text-emerald-500\">+12.5%</span>\n <span className=\"text-muted-foreground\">vs previous 7 days</span>\n </div>\n\n <div className=\"border-border/60 mt-4 border-t pt-4\">\n <p className=\"text-muted-foreground text-[10px] tracking-wider uppercase\">\n Device category in last 7 days\n </p>\n\n <ChartContainer\n config={deviceCategoryChartConfig}\n className=\"mx-auto mt-3 aspect-square h-32 w-32\"\n >\n <PieChart>\n <Pie\n data={deviceCategoryData}\n dataKey=\"value\"\n nameKey=\"name\"\n innerRadius={32}\n outerRadius={52}\n strokeWidth={2}\n >\n {deviceCategoryData.map((entry) => (\n <Cell key={entry.name} fill={entry.fill} />\n ))}\n </Pie>\n </PieChart>\n </ChartContainer>\n\n <div className=\"mt-3 grid grid-cols-3 gap-2\">\n {deviceCategoryData.map((device) => (\n <div key={device.name} className=\"text-center\">\n <p className=\"text-muted-foreground flex items-center justify-center gap-1.5 text-[10px] tracking-wide uppercase\">\n <span\n className=\"size-2 rounded-full\"\n style={{ backgroundColor: device.fill }}\n />\n {device.name}\n </p>\n <p className=\"text-foreground mt-1 leading-none font-medium tabular-nums\">\n {device.value}%\n </p>\n </div>\n ))}\n </div>\n </div>\n </CardContent>\n </Card>\n );\n}\n",
"type": "registry:component",
"target": "app/analytics/components/overview-card.tsx"
},
{
"path": "src/registry/blocks/analytics-map/components/breakdown-card.tsx",
"content": "\"use client\";\n\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport { type BreakdownRow } from \"../data\";\n\ninterface BreakdownCardProps {\n title: string;\n rows: BreakdownRow[];\n}\n\nexport function BreakdownCard({ title, rows }: BreakdownCardProps) {\n const maxRowValue =\n rows.length > 0 ? Math.max(...rows.map((row) => row.value)) : 0;\n\n return (\n <Card>\n <CardHeader>\n <CardTitle className=\"text-sm font-medium\">{title}</CardTitle>\n </CardHeader>\n\n <CardContent>\n <div className=\"text-muted-foreground mb-2 flex items-center justify-between text-[11px] tracking-wider uppercase\">\n <span>{title}</span>\n <span>Visitors</span>\n </div>\n <div className=\"space-y-3\">\n {rows.map((row) => (\n <div key={row.label} className=\"space-y-1.5\">\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"text-foreground/90 truncate\">{row.label}</span>\n <span className=\"text-foreground font-medium\">{row.value}</span>\n </div>\n <div className=\"bg-muted h-1 rounded-full\">\n <div\n className=\"h-full rounded-full bg-blue-500/85\"\n style={{ width: `${(row.value / maxRowValue) * 100}%` }}\n />\n </div>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n );\n}\n",
"type": "registry:component",
"target": "app/analytics/components/breakdown-card.tsx"
}
],
"meta": {
"iframeHeight": "970px"
},
"categories": [
"analytics",
"dashboard"
],
"type": "registry:block"
}