{
  "$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": [
    "@mapcn/map",
    "card",
    "chart"
  ],
  "files": [
    {
      "path": "src/registry/blocks/analytics-map/page.tsx",
      "content": "\"use client\";\n\nimport {\n  Map,\n  MapControls,\n  MapGeoJSON,\n  MapMarker,\n  MarkerContent,\n  MarkerTooltip,\n} from \"@/components/ui/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\n// Country borders from a public CDN, or swap in your own GeoJSON.\nconst WORLD_GEOJSON =\n  \"https://cdn.jsdelivr.net/gh/nvkelso/natural-earth-vector@v5.1.2/geojson/ne_110m_admin_0_countries.geojson\";\n\nexport default function Page() {\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=\"bg-card relative h-(--map-height)\">\n        <Map\n          center={[-2, 16]}\n          zoom={1.4}\n          maxZoom={4}\n          minZoom={1.4}\n          scrollZoom={false}\n          dragRotate={false}\n          pitchWithRotate={false}\n          renderWorldCopies\n          blank\n        >\n          <MapGeoJSON data={WORLD_GEOJSON} linePaint={false} />\n          <MapControls className=\"bottom-2\" />\n          {locations.map((location) => (\n            <MapMarker\n              key={location.city}\n              longitude={location.lng}\n              latitude={location.lat}\n            >\n              <MarkerContent className=\"group\">\n                <div\n                  className=\"bg-chart-2/80 group-hover:bg-chart-2/90 rounded-full transition-[transform,background-color] group-hover:scale-110\"\n                  style={{\n                    width: location.size * 3,\n                    height: location.size * 3,\n                  }}\n                />\n              </MarkerContent>\n              <MarkerTooltip\n                offset={20}\n                className=\"bg-popover text-popover-foreground border\"\n              >\n                <p className=\"font-medium\">{location.city}</p>\n                <p className=\"text-muted-foreground mt-0.5\">\n                  {location.size} 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: \"1\", users: 420 },\n  { day: \"2\", users: 468 },\n  { day: \"3\", users: 512 },\n  { day: \"4\", users: 390 },\n  { day: \"5\", users: 540 },\n  { day: \"6\", users: 305 },\n  { day: \"7\", users: 268 },\n  { day: \"8\", users: 498 },\n  { day: \"9\", users: 610 },\n  { day: \"10\", users: 472 },\n  { day: \"11\", users: 845 },\n  { day: \"12\", users: 690 },\n  { day: \"13\", users: 360 },\n  { day: \"14\", users: 322 },\n  { day: \"15\", users: 588 },\n  { day: \"16\", users: 654 },\n  { day: \"17\", users: 720 },\n  { day: \"18\", users: 505 },\n  { day: \"19\", users: 932 },\n  { day: \"20\", users: 410 },\n  { day: \"21\", users: 372 },\n  { day: \"22\", users: 640 },\n  { day: \"23\", users: 712 },\n  { day: \"24\", users: 598 },\n  { day: \"25\", users: 1040 },\n  { day: \"26\", users: 805 },\n  { day: \"27\", users: 445 },\n  { day: \"28\", users: 398 },\n  { day: \"29\", users: 702 },\n  { day: \"30\", users: 868 },\n];\n\nexport const usersPerDayChartConfig = {\n  users: {\n    label: \"Users\",\n    color: \"var(--chart-2)\",\n  },\n} satisfies ChartConfig;\n\nexport const deviceCategoryData = [\n  { name: \"Desktop\", value: 73.3, fill: \"var(--chart-1)\" },\n  { name: \"Mobile\", value: 25.0, fill: \"var(--chart-2)\" },\n  { name: \"Tablet\", value: 1.7, fill: \"var(--chart-3)\" },\n];\n\nexport const deviceCategoryChartConfig = {\n  desktop: { label: \"Desktop\", color: \"var(--chart-1)\" },\n  mobile: { label: \"Mobile\", color: \"var(--chart-2)\" },\n  tablet: { label: \"Tablet\", color: \"var(--chart-3)\" },\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, 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-14 w-full\"\n    >\n      <AreaChart data={usersPerDay}>\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/80 absolute top-4 left-4 z-10 w-60 gap-1 backdrop-blur-sm\">\n      <CardHeader>\n        <div>\n          <p className=\"text-muted-foreground pb-2 text-[10px] tracking-wider uppercase\">\n            Users in last 30 days\n          </p>\n          <p className=\"text-3xl leading-none font-semibold\">17,234</p>\n        </div>\n      </CardHeader>\n\n      <CardContent>\n        <MetricChart />\n        <div className=\"mt-2 flex items-center gap-1.5 text-xs\">\n          <TrendingUp className=\"text-foreground size-3\" />\n          <span className=\"text-foreground font-medium\">+12.5%</span>\n          <span className=\"text-muted-foreground\">vs previous 30 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 30 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            </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 className=\"gap-2\">\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-1.5\">\n          {rows.map((row) => {\n            const pct = maxRowValue ? (row.value / maxRowValue) * 100 : 0;\n            return (\n              <div\n                key={row.label}\n                className=\"relative flex items-center justify-between overflow-hidden rounded-md px-2 py-1.5 text-xs\"\n              >\n                <div\n                  className=\"bg-chart-2/20 absolute inset-y-0 left-0 rounded-md\"\n                  style={{ width: `${pct}%` }}\n                  aria-hidden\n                />\n                <span className=\"text-foreground/90 relative truncate pr-2\">\n                  {row.label}\n                </span>\n                <span className=\"text-foreground relative font-medium tabular-nums\">\n                  {row.value.toLocaleString()}\n                </span>\n              </div>\n            );\n          })}\n        </div>\n      </CardContent>\n    </Card>\n  );\n}\n",
      "type": "registry:component",
      "target": "app/analytics/components/breakdown-card.tsx"
    }
  ],
  "meta": {
    "iframeHeight": "940px"
  },
  "categories": [
    "analytics",
    "dashboard"
  ],
  "type": "registry:block"
}
