{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "leaflet-sales-territory-map",
  "title": "Leaflet Sales Territory Map",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/leaflet-sales-territory-map/LeafletSalesTerritoryMap.vue",
      "content": "<script lang=\"ts\">\nexport type TerritoryStatus = 'attained' | 'ontrack' | 'attention'\n\nexport interface SalesTerritory {\n  id: string\n  name: string\n  code: string\n  rep: string\n  arr: string\n  attainment: number\n  status: TerritoryStatus\n  /** Territory centroid as [lng, lat]. */\n  centerLngLat: [number, number]\n  /** Territory boundary ring as [lng, lat][]. */\n  polygon: [number, number][]\n}\n\n// Hand-drawn plausible US region boundaries — four rep territories tiling CONUS.\nexport const DEFAULT_TERRITORIES: SalesTerritory[] = [\n  {\n    id: 'pacific-west',\n    name: 'Pacific West',\n    code: 'PAC',\n    rep: 'Sierra Delgado',\n    arr: '$2.8M',\n    attainment: 112,\n    status: 'attained',\n    centerLngLat: [-119.8, 39.5],\n    polygon: [\n      [-124.4, 32.5],\n      [-114.0, 32.5],\n      [-114.0, 35.0],\n      [-114.6, 39.0],\n      [-120.0, 42.0],\n      [-117.0, 45.9],\n      [-117.0, 49.0],\n      [-124.7, 48.4],\n      [-124.2, 40.0],\n      [-124.4, 32.5],\n    ],\n  },\n  {\n    id: 'mountain-central',\n    name: 'Mountain Central',\n    code: 'MTN',\n    rep: 'Colt Brannigan',\n    arr: '$2.2M',\n    attainment: 97,\n    status: 'ontrack',\n    centerLngLat: [-104.8, 39.9],\n    polygon: [\n      [-117.0, 49.0],\n      [-95.0, 49.0],\n      [-95.0, 43.5],\n      [-97.5, 40.0],\n      [-95.5, 36.5],\n      [-100.0, 34.5],\n      [-103.0, 32.0],\n      [-106.5, 31.8],\n      [-108.2, 31.3],\n      [-111.0, 31.3],\n      [-114.0, 32.5],\n      [-114.0, 35.0],\n      [-114.6, 39.0],\n      [-120.0, 42.0],\n      [-117.0, 45.9],\n      [-117.0, 49.0],\n    ],\n  },\n  {\n    id: 'southeast',\n    name: 'Southeast',\n    code: 'SE',\n    rep: 'Harper Quinn',\n    arr: '$1.9M',\n    attainment: 84,\n    status: 'attention',\n    centerLngLat: [-83.0, 33.5],\n    polygon: [\n      [-95.5, 36.5],\n      [-94.0, 33.0],\n      [-93.0, 30.0],\n      [-89.0, 29.0],\n      [-84.0, 29.9],\n      [-82.6, 28.0],\n      [-81.0, 25.8],\n      [-80.1, 25.1],\n      [-80.5, 26.8],\n      [-81.4, 30.4],\n      [-80.8, 32.2],\n      [-77.5, 34.0],\n      [-75.5, 35.5],\n      [-76.0, 36.9],\n      [-77.5, 39.0],\n      [-80.5, 40.8],\n      [-83.0, 39.5],\n      [-84.8, 38.0],\n      [-88.0, 37.0],\n      [-90.5, 36.0],\n      [-95.5, 36.5],\n    ],\n  },\n  {\n    id: 'northeast',\n    name: 'Northeast',\n    code: 'NE',\n    rep: 'Devon Whitaker',\n    arr: '$3.4M',\n    attainment: 121,\n    status: 'attained',\n    centerLngLat: [-74.5, 41.8],\n    polygon: [\n      [-95.0, 49.0],\n      [-95.0, 45.9],\n      [-92.5, 44.5],\n      [-89.5, 43.0],\n      [-87.0, 41.5],\n      [-83.5, 41.7],\n      [-79.8, 42.3],\n      [-76.8, 43.5],\n      [-74.0, 45.0],\n      [-71.0, 45.0],\n      [-70.0, 43.6],\n      [-70.9, 42.9],\n      [-74.0, 40.5],\n      [-75.0, 38.5],\n      [-77.5, 39.0],\n      [-80.5, 40.8],\n      [-83.0, 39.5],\n      [-84.8, 38.0],\n      [-88.0, 37.0],\n      [-90.5, 36.0],\n      [-95.5, 36.5],\n      [-97.5, 40.0],\n      [-95.0, 43.5],\n      [-95.0, 49.0],\n    ],\n  },\n]\n\n/** Plain-hex fills — Leaflet renders paths to SVG/canvas, so CSS vars do not resolve there. */\nexport const TERRITORY_STATUS_STYLES: Record<TerritoryStatus, { fill: string; stroke: string }> = {\n  attained: { fill: '#22c55e', stroke: '#4ade80' },\n  ontrack: { fill: '#38bdf8', stroke: '#7dd3fc' },\n  attention: { fill: '#f59e0b', stroke: '#fbbf24' },\n}\n</script>\n\n<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { ref } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { Badge } from '@/components/ui/badge'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport { LeafletMap, LeafletMarker, LeafletPolygon, type LeafletMapRef } from '@/components/ui/leaflet-map'\n\nexport interface LeafletSalesTerritoryMapProps {\n  class?: HTMLAttributes['class']\n  territories?: SalesTerritory[]\n}\n\n// Geographic center of CONUS — stable reference so the map's center watcher never re-fires.\nconst US_CENTER: [number, number] = [-98.5795, 39.8283]\n\nconst props = withDefaults(defineProps<LeafletSalesTerritoryMapProps>(), {\n  territories: () => DEFAULT_TERRITORIES,\n})\n\nconst selectedId = ref(props.territories[0]?.id ?? '')\nconst mapRef = ref<LeafletMapRef | null>(null)\n\nfunction selectTerritory(territory: SalesTerritory) {\n  selectedId.value = territory.id\n  mapRef.value?.flyTo?.({\n    center: territory.centerLngLat,\n    zoom: 5,\n    duration: 800,\n  })\n}\n\nfunction statusVariant(status: TerritoryStatus) {\n  if (status === 'attained') return 'info'\n  if (status === 'ontrack') return 'secondary'\n  return 'warning'\n}\n</script>\n\n<template>\n  <div data-slot=\"leaflet-sales-territory-map\" :class=\"cn('flex h-full min-h-0 flex-col gap-4', props.class)\">\n    <div class=\"flex flex-wrap items-center justify-between gap-3\">\n      <h2 class=\"text-foreground text-lg font-semibold tracking-tight\">Territories</h2>\n      <p class=\"text-muted-foreground text-xs\">{{ territories.length }} regions</p>\n    </div>\n\n    <div class=\"grid min-h-0 flex-1 grid-cols-1 gap-4 lg:grid-cols-12\">\n      <Card class=\"border-border flex min-h-0 flex-col overflow-hidden shadow-xs lg:col-span-8\">\n        <div class=\"relative min-h-[360px] flex-1 overflow-hidden\">\n          <LeafletMap ref=\"mapRef\" variant=\"dark\" :center=\"US_CENTER\" :zoom=\"4\" class=\"absolute inset-0 size-full\">\n            <LeafletPolygon\n              v-for=\"t in territories\"\n              :key=\"`boundary-${t.id}`\"\n              :lng-lat-path=\"t.polygon\"\n              :color=\"TERRITORY_STATUS_STYLES[t.status].stroke\"\n              :weight=\"selectedId === t.id ? 2.5 : 1.5\"\n              :opacity=\"0.9\"\n              :fill=\"true\"\n              :fill-color=\"TERRITORY_STATUS_STYLES[t.status].fill\"\n              :fill-opacity=\"selectedId === t.id ? 0.3 : 0.14\"\n              @click=\"selectTerritory(t)\"\n            />\n            <LeafletMarker v-for=\"t in territories\" :key=\"t.id\" :lng-lat=\"t.centerLngLat\" anchor=\"center\">\n              <button\n                type=\"button\"\n                class=\"rounded-full border px-2 py-0.5 font-mono text-xs font-semibold shadow-xs\"\n                :class=\"\n                  selectedId === t.id\n                    ? 'border-primary bg-primary text-primary-foreground'\n                    : 'border-border bg-card text-foreground'\n                \"\n                @click.stop=\"selectTerritory(t)\"\n              >\n                {{ t.code }} {{ t.attainment }}%\n              </button>\n            </LeafletMarker>\n          </LeafletMap>\n        </div>\n      </Card>\n\n      <Card class=\"border-border flex min-h-0 flex-col overflow-hidden shadow-xs lg:col-span-4\">\n        <CardHeader class=\"border-border border-b p-4\">\n          <CardTitle class=\"text-sm font-semibold\">Quota</CardTitle>\n        </CardHeader>\n        <CardContent class=\"divide-border min-h-0 flex-1 divide-y overflow-auto p-0\">\n          <button\n            v-for=\"t in territories\"\n            :key=\"t.id\"\n            type=\"button\"\n            :class=\"\n              cn(\n                'hover:bg-accent/50 flex w-full items-center justify-between gap-3 px-4 py-3 text-left transition-colors',\n                selectedId === t.id && 'bg-accent/60',\n              )\n            \"\n            @click=\"selectTerritory(t)\"\n          >\n            <div class=\"min-w-0\">\n              <p class=\"text-foreground truncate text-sm font-medium\">{{ t.code }} · {{ t.name }}</p>\n              <p class=\"text-muted-foreground truncate font-mono text-xs tabular-nums\">{{ t.rep }} · {{ t.arr }}</p>\n            </div>\n            <Badge :variant=\"statusVariant(t.status)\" class=\"shrink-0 text-xs tabular-nums\">{{ t.attainment }}%</Badge>\n          </button>\n        </CardContent>\n      </Card>\n    </div>\n  </div>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/LeafletSalesTerritoryMap.vue"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/badge.json",
    "https://uipkge.dev/r/vue/card.json",
    "https://uipkge.dev/r/vue/leaflet-map.json"
  ],
  "description": "Sales territory map on free OpenStreetMap/Esri tiles — no API key. Region boundary polygons and rep pins with quota attainment; select a territory to fly the camera.",
  "categories": [
    "analytics",
    "dashboard"
  ]
}