{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "leaflet-last-mile-courier-dispatch",
  "title": "Leaflet Last Mile Courier Dispatch",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/leaflet-last-mile-courier-dispatch/LeafletLastMileCourierDispatch.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { ref, computed } from 'vue'\nimport { BatteryCharging, Navigation, Phone, Radio, Truck } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Button } from '@/components/ui/button'\nimport { Badge } from '@/components/ui/badge'\nimport { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'\nimport { LeafletMap, LeafletMarker, LeafletPolyline, type LeafletMapRef } from '@/components/ui/leaflet-map'\n\nexport interface DeliveryStop {\n  id: string\n  stopNumber: number\n  trackingCode: string\n  recipient: string\n  address: string\n  parcels: number\n  timeWindow: string\n  eta: string\n  status: 'delivered' | 'en_route' | 'scheduled' | 'failed'\n  podType?: 'signature' | 'photo_scanned' | 'front_door'\n  podTimestamp?: string\n  coords?: [number, number]\n}\n\nexport interface LeafletLastMileCourierDispatchProps {\n  routeId?: string\n  driverName?: string\n  vehicleCode?: string\n  class?: HTMLAttributes['class']\n}\n\nconst props = withDefaults(defineProps<LeafletLastMileCourierDispatchProps>(), {\n  routeId: 'RTE-METRO-409',\n  driverName: 'Elena Rostova',\n  vehicleCode: 'VAN-EV-09 (Ford E-Transit)',\n})\n\nconst stops = ref<DeliveryStop[]>([\n  {\n    id: 'stp-1',\n    stopNumber: 1,\n    trackingCode: 'TRK-9941-8812',\n    recipient: 'Apex Logistics HQ',\n    address: '742 Evergreen Terrace, Sector 4',\n    parcels: 3,\n    timeWindow: '08:30 - 09:30',\n    eta: '08:52 AM',\n    status: 'delivered',\n    podType: 'signature',\n    podTimestamp: '08:54 AM (Signed by J. Doe)',\n    coords: [-73.991, 40.743],\n  },\n  {\n    id: 'stp-2',\n    stopNumber: 2,\n    trackingCode: 'TRK-9941-8813',\n    recipient: 'David K. Miller',\n    address: '1204 Pinehurst Ave, Apt 3B',\n    parcels: 1,\n    timeWindow: '09:30 - 10:30',\n    eta: '09:45 AM',\n    status: 'delivered',\n    podType: 'photo_scanned',\n    podTimestamp: '09:48 AM (Doorstep Verified)',\n    coords: [-73.987, 40.747],\n  },\n  {\n    id: 'stp-3',\n    stopNumber: 3,\n    trackingCode: 'TRK-9941-8814',\n    recipient: 'Global Tech Labs',\n    address: '450 Innovation Parkway, Suite 100',\n    parcels: 4,\n    timeWindow: '10:00 - 11:30',\n    eta: '10:25 AM',\n    status: 'en_route',\n    coords: [-73.982, 40.751],\n  },\n  {\n    id: 'stp-4',\n    stopNumber: 4,\n    trackingCode: 'TRK-9941-8815',\n    recipient: 'Sarah Jenkins',\n    address: '88 Riverfront Blvd',\n    parcels: 2,\n    timeWindow: '11:30 - 12:30',\n    eta: '11:45 AM',\n    status: 'scheduled',\n    coords: [-73.978, 40.755],\n  },\n  {\n    id: 'stp-5',\n    stopNumber: 5,\n    trackingCode: 'TRK-9941-8816',\n    recipient: 'Metro Health Clinic',\n    address: '310 Health Sciences Way',\n    parcels: 1,\n    timeWindow: '12:30 - 14:00',\n    eta: '01:10 PM',\n    status: 'scheduled',\n    coords: [-73.971, 40.758],\n  },\n])\n\nconst completedCount = computed(() => stops.value.filter((s) => s.status === 'delivered').length)\nconst totalStops = computed(() => stops.value.length)\nconst onTimeRate = computed(() => '98.4%')\n\nconst routePath = computed<[number, number][]>(\n  () => stops.value.map((s) => s.coords).filter(Boolean) as [number, number][],\n)\n\nconst mapRef = ref<LeafletMapRef | null>(null)\n\nfunction focusStop(stop: DeliveryStop) {\n  if (!stop.coords) return\n  mapRef.value?.flyTo?.({ center: stop.coords, zoom: 15, duration: 800 })\n}\n\nfunction fitRoute() {\n  const coords = routePath.value\n  if (coords.length < 2) return\n  const lngs = coords.map((c) => c[0])\n  const lats = coords.map((c) => c[1])\n  mapRef.value?.fitBounds?.(\n    [\n      [Math.min(...lngs), Math.min(...lats)],\n      [Math.max(...lngs), Math.max(...lats)],\n    ],\n    { padding: [48, 48] },\n  )\n}\n\nfunction markDelivered(id: string) {\n  const stop = stops.value.find((s) => s.id === id)\n  if (stop) {\n    stop.status = 'delivered'\n    stop.podType = 'signature'\n    stop.podTimestamp = 'Just now (Signed)'\n\n    // Move next scheduled to en_route\n    const next = stops.value.find((s) => s.status === 'scheduled')\n    if (next) next.status = 'en_route'\n  }\n}\n</script>\n\n<template>\n  <div data-slot=\"leaflet-last-mile-courier-dispatch\" :class=\"cn('w-full space-y-6', props.class)\">\n    <!-- Header / Dispatch Telemetry Overview -->\n    <Card>\n      <CardHeader class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n        <div class=\"space-y-1\">\n          <div class=\"flex items-center gap-2\">\n            <span class=\"text-foreground font-mono text-sm font-semibold\">{{ routeId }}</span>\n            <Badge variant=\"outline\" class=\"font-mono text-xs\">\n              {{ vehicleCode }}\n            </Badge>\n            <Badge variant=\"secondary\" class=\"gap-1 text-xs\">\n              <Radio class=\"text-foreground size-3\" />\n              Live Telemetry Active\n            </Badge>\n          </div>\n          <CardTitle class=\"text-xl\">Last-Mile Courier Dispatch & POD Telemetry</CardTitle>\n          <CardDescription>\n            Courier: <span class=\"text-foreground font-medium\">{{ driverName }}</span> · Urban Route Dispatch Matrix\n          </CardDescription>\n        </div>\n\n        <div class=\"flex items-center gap-2\">\n          <Button variant=\"outline\" size=\"sm\" class=\"gap-1.5\">\n            <Phone class=\"size-3.5\" />\n            Contact Driver\n          </Button>\n          <Button variant=\"default\" size=\"sm\" class=\"gap-1.5\" @click=\"fitRoute\">\n            <Navigation class=\"size-3.5\" />\n            Live GPS Map\n          </Button>\n        </div>\n      </CardHeader>\n\n      <CardContent class=\"border-border grid grid-cols-1 gap-4 border-t pt-4 sm:grid-cols-4\">\n        <!-- Progress Metric -->\n        <div class=\"border-border space-y-1 rounded-lg border p-3\">\n          <div class=\"text-muted-foreground text-xs font-medium\">ROUTE COMPLETION</div>\n          <div class=\"flex items-baseline justify-between pt-1\">\n            <div class=\"font-mono text-xl font-semibold\">{{ completedCount }} / {{ totalStops }}</div>\n            <span class=\"text-muted-foreground font-mono text-xs\">\n              {{ Math.round((completedCount / totalStops) * 100) }}%\n            </span>\n          </div>\n        </div>\n\n        <!-- On-time SLA -->\n        <div class=\"border-border space-y-1 rounded-lg border p-3\">\n          <div class=\"text-muted-foreground text-xs font-medium\">ON-TIME WINDOW SLA</div>\n          <div class=\"pt-1 font-mono text-xl font-semibold\">{{ onTimeRate }}</div>\n        </div>\n\n        <!-- Vehicle Telemetry -->\n        <div class=\"border-border space-y-1 rounded-lg border p-3\">\n          <div class=\"text-muted-foreground flex items-center gap-1 text-xs font-medium\">\n            <BatteryCharging class=\"size-3.5\" />\n            <span>EV BATTERY / RANGE</span>\n          </div>\n          <div class=\"pt-1 font-mono text-xl font-semibold\">74% · 142 km</div>\n        </div>\n\n        <!-- Next Stop ETA -->\n        <div class=\"border-border space-y-1 rounded-lg border p-3\">\n          <div class=\"text-muted-foreground text-xs font-medium\">CURRENT TARGET ETA</div>\n          <div class=\"pt-1 font-mono text-xl font-semibold\">10:25 AM (Stop #3)</div>\n        </div>\n      </CardContent>\n    </Card>\n\n    <!-- Live GPS Route Map -->\n    <Card class=\"overflow-hidden\">\n      <CardHeader class=\"border-border/70 border-b pb-3\">\n        <div class=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n          <div>\n            <CardTitle class=\"flex items-center gap-2 text-base font-semibold\">\n              <Navigation class=\"text-info size-4\" />\n              <span>Live GPS Telemetry & Corridor Map</span>\n            </CardTitle>\n            <CardDescription>\n              Real-time courier positioning with sequenced drop waypoints and completion status.\n            </CardDescription>\n          </div>\n          <div class=\"flex items-center gap-2\">\n            <span class=\"bg-success inline-flex size-2 animate-ping rounded-full\" />\n            <span class=\"text-muted-foreground font-mono text-xs\">GPS Synced · 3s ago</span>\n          </div>\n        </div>\n      </CardHeader>\n      <div class=\"relative h-72 w-full sm:h-80\">\n        <LeafletMap ref=\"mapRef\" variant=\"dark\" :center=\"[-73.982, 40.75]\" :zoom=\"13.8\" class=\"size-full\">\n          <!-- Route Polyline -->\n          <LeafletPolyline :lng-lat-path=\"routePath\" color=\"#0284c7\" :weight=\"3\" :dash-array=\"[2, 2]\" />\n\n          <!-- Delivery Stop Markers -->\n          <LeafletMarker v-for=\"stop in stops\" :key=\"stop.id\" :lng-lat=\"stop.coords!\" anchor=\"center\">\n            <div class=\"flex cursor-pointer flex-col items-center\" @click=\"focusStop(stop)\">\n              <div\n                class=\"border-background flex size-6 items-center justify-center rounded-full border-2 font-mono text-xs font-bold shadow-md\"\n                :class=\"\n                  stop.status === 'delivered'\n                    ? 'bg-success text-white'\n                    : stop.status === 'en_route'\n                      ? 'bg-warning text-white ring-2 ring-amber-400/50'\n                      : 'bg-muted text-muted-foreground'\n                \"\n              >\n                {{ stop.stopNumber }}\n              </div>\n              <span\n                class=\"border-border/80 bg-background/90 py-0.2 mt-0.5 rounded border px-1 font-mono text-xs font-medium shadow-xs backdrop-blur-xs\"\n              >\n                {{ stop.recipient.split(' ')[0] }}\n              </span>\n            </div>\n          </LeafletMarker>\n\n          <!-- Live Courier Van Marker -->\n          <LeafletMarker :lng-lat=\"[-73.984, 40.749]\" anchor=\"center\" :z-index-offset=\"1000\">\n            <div class=\"relative flex size-9 items-center justify-center\">\n              <span class=\"bg-info absolute inline-flex size-full animate-ping rounded-full opacity-60\" />\n              <div\n                class=\"bg-info relative flex size-8 items-center justify-center rounded-full border-2 border-white text-white shadow-lg\"\n              >\n                <Truck class=\"size-4\" />\n              </div>\n            </div>\n          </LeafletMarker>\n        </LeafletMap>\n      </div>\n    </Card>\n\n    <!-- Dispatch Sequence Table -->\n    <Card>\n      <CardHeader class=\"pb-3\">\n        <CardTitle class=\"text-base font-semibold\">Delivery Manifest & Proof of Delivery (POD)</CardTitle>\n        <CardDescription>\n          Ordered drop sequence with recipient signature capture and delivery window timestamps.\n        </CardDescription>\n      </CardHeader>\n\n      <CardContent class=\"p-0\">\n        <Table>\n          <TableHeader>\n            <TableRow>\n              <TableHead class=\"w-[60px]\">Stop</TableHead>\n              <TableHead class=\"w-[180px]\">Tracking ID</TableHead>\n              <TableHead class=\"w-[260px]\">Recipient & Address</TableHead>\n              <TableHead>Window / ETA</TableHead>\n              <TableHead class=\"text-right\">Parcels</TableHead>\n              <TableHead>Status & Proof of Delivery</TableHead>\n              <TableHead class=\"w-[100px] text-right\">Action</TableHead>\n            </TableRow>\n          </TableHeader>\n          <TableBody>\n            <TableRow\n              v-for=\"stop in stops\"\n              :key=\"stop.id\"\n              :class=\"stop.status === 'en_route' ? 'bg-muted/40 font-medium' : ''\"\n            >\n              <!-- Stop Number -->\n              <TableCell class=\"text-muted-foreground font-mono text-xs\"> #0{{ stop.stopNumber }} </TableCell>\n\n              <!-- Tracking Code -->\n              <TableCell>\n                <div class=\"text-foreground font-mono text-xs font-semibold\">{{ stop.trackingCode }}</div>\n              </TableCell>\n\n              <!-- Recipient & Address -->\n              <TableCell>\n                <div class=\"text-foreground font-medium\">{{ stop.recipient }}</div>\n                <div class=\"text-muted-foreground truncate text-xs\">{{ stop.address }}</div>\n              </TableCell>\n\n              <!-- Time Window & ETA -->\n              <TableCell>\n                <div class=\"text-foreground text-xs\">{{ stop.timeWindow }}</div>\n                <div class=\"text-muted-foreground font-mono text-xs\">ETA: {{ stop.eta }}</div>\n              </TableCell>\n\n              <!-- Parcels Count -->\n              <TableCell class=\"text-right font-mono text-sm\"> {{ stop.parcels }} pkg </TableCell>\n\n              <!-- Status & Proof of Delivery -->\n              <TableCell>\n                <div class=\"flex flex-col gap-1\">\n                  <div class=\"flex items-center gap-2\">\n                    <Badge\n                      :variant=\"\n                        stop.status === 'delivered' ? 'default' : stop.status === 'en_route' ? 'secondary' : 'outline'\n                      \"\n                      class=\"capitalize\"\n                    >\n                      {{ stop.status.replace('_', ' ') }}\n                    </Badge>\n                  </div>\n                  <span v-if=\"stop.podTimestamp\" class=\"text-muted-foreground font-mono text-xs\">\n                    {{ stop.podTimestamp }}\n                  </span>\n                </div>\n              </TableCell>\n\n              <!-- Action -->\n              <TableCell class=\"text-right\">\n                <Button\n                  v-if=\"stop.status === 'en_route'\"\n                  size=\"sm\"\n                  variant=\"default\"\n                  class=\"h-7 text-xs\"\n                  @click=\"markDelivered(stop.id)\"\n                >\n                  Confirm POD\n                </Button>\n                <span v-else-if=\"stop.status === 'delivered'\" class=\"text-muted-foreground text-xs\"> Logged </span>\n                <span v-else class=\"text-muted-foreground text-xs\"> Queued </span>\n              </TableCell>\n            </TableRow>\n          </TableBody>\n        </Table>\n      </CardContent>\n\n      <CardFooter class=\"border-border text-muted-foreground flex items-center justify-between border-t py-3 text-xs\">\n        <div>Geotagged proof of delivery records are synced automatically to customer tracking portals.</div>\n        <div class=\"text-foreground font-medium\">\n          Route summary: <span class=\"font-mono\">{{ totalStops }}</span> total consignments\n        </div>\n      </CardFooter>\n    </Card>\n  </div>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/LeafletLastMileCourierDispatch.vue"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/card.json",
    "https://uipkge.dev/r/vue/button.json",
    "https://uipkge.dev/r/vue/badge.json",
    "https://uipkge.dev/r/vue/table.json",
    "https://uipkge.dev/r/vue/leaflet-map.json"
  ],
  "description": "Last-mile courier dispatch matrix on free OpenStreetMap tiles — no API key. Route progress, ETA telemetry, and Proof of Delivery (POD) logging.",
  "categories": [
    "dashboard",
    "data-display"
  ]
}