{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "leaflet-asset-tracking-map",
  "title": "Leaflet Asset Tracking Map",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-react/blocks/leaflet-asset-tracking-map/LeafletAssetTrackingMap.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport { cn } from '@/lib/utils'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'\nimport { LeafletMap, LeafletMarker, LeafletPolyline, type LeafletMapRef } from '@/components/ui/leaflet-map'\n\ntype LngLat = [number, number]\n\nexport interface ContainerAsset {\n  id: string\n  containerNo: string\n  type: 'Reefer' | 'Dry Cargo' | 'Hazardous'\n  location: string\n  coords: LngLat\n  temp?: string\n  humidity?: string\n  battery: string\n  geofence: 'Inside Zone' | 'In Transit' | 'Exited Perimeter'\n  status: 'Nominal' | 'Alert' | 'Secured'\n}\n\nexport interface LeafletAssetTrackingMapProps extends React.HTMLAttributes<HTMLDivElement> {\n  assets?: ContainerAsset[]\n}\n\nconst DEFAULT_ASSETS: ContainerAsset[] = [\n  {\n    id: 'c-1',\n    containerNo: 'MSKU-948102',\n    type: 'Reefer',\n    location: 'Pasir Panjang Terminal, Berth P5',\n    coords: [103.792, 1.276],\n    temp: '-18.4°C',\n    humidity: '84%',\n    battery: '92%',\n    geofence: 'Inside Zone',\n    status: 'Nominal',\n  },\n  {\n    id: 'c-2',\n    containerNo: 'CMAU-310892',\n    type: 'Dry Cargo',\n    location: 'Singapore Strait, Eastbound Lane',\n    coords: [103.945, 1.205],\n    battery: '78%',\n    geofence: 'In Transit',\n    status: 'Nominal',\n  },\n  {\n    id: 'c-3',\n    containerNo: 'HLXU-772901',\n    type: 'Reefer',\n    location: 'Jurong Island Anchorage',\n    coords: [103.71, 1.245],\n    temp: '+4.2°C',\n    humidity: '62%',\n    battery: '64%',\n    geofence: 'Inside Zone',\n    status: 'Alert',\n  },\n  {\n    id: 'c-4',\n    containerNo: 'OOLU-550183',\n    type: 'Hazardous',\n    location: 'Tuas Port, Hazardous Bay T2',\n    coords: [103.635, 1.285],\n    battery: '95%',\n    geofence: 'Inside Zone',\n    status: 'Secured',\n  },\n]\n\n/** Eastbound / westbound shipping lanes through the Singapore Strait. */\nconst SHIPPING_LANES: LngLat[][] = [\n  [\n    [103.5, 1.16],\n    [103.7, 1.18],\n    [103.9, 1.2],\n    [104.1, 1.22],\n  ],\n  [\n    [103.5, 1.21],\n    [103.7, 1.24],\n    [103.9, 1.27],\n    [104.1, 1.29],\n  ],\n]\n\nexport function LeafletAssetTrackingMap({\n  className,\n  assets = DEFAULT_ASSETS,\n  ...props\n}: LeafletAssetTrackingMapProps) {\n  const [activeId, setActiveId] = React.useState(assets[0]?.id ?? '')\n  const mapRef = React.useRef<LeafletMapRef>(null)\n  const activeAsset = assets.find((a) => a.id === activeId) ?? assets[0]\n\n  const selectAsset = (a: ContainerAsset) => {\n    setActiveId(a.id)\n    mapRef.current?.flyTo({ center: a.coords, zoom: 11.5 })\n  }\n\n  return (\n    <div\n      data-slot=\"leaflet-asset-tracking-map\"\n      className={cn('grid gap-4 lg:grid-cols-[1fr_320px]', className)}\n      {...props}\n    >\n      <div className=\"border-border bg-card relative h-[480px] overflow-hidden rounded-xl border\">\n        <LeafletMap\n          ref={mapRef}\n          variant=\"satellite-streets\"\n          center={[103.8519, 1.29027]}\n          zoom={11}\n          className=\"h-full w-full\"\n        >\n          {/* Strait shipping lanes */}\n          {SHIPPING_LANES.map((lane, i) => (\n            <LeafletPolyline key={i} lngLatPath={lane} color=\"#22d3ee\" weight={2} opacity={0.55} dashArray=\"6 8\" />\n          ))}\n\n          {assets.map((a) => (\n            <LeafletMarker key={a.id} lngLat={a.coords} anchor=\"bottom\">\n              <button\n                type=\"button\"\n                className=\"group relative flex cursor-pointer flex-col items-center\"\n                onClick={() => selectAsset(a)}\n              >\n                <span\n                  className={cn(\n                    'flex size-7 items-center justify-center rounded-md shadow-md ring-2',\n                    a.status === 'Alert'\n                      ? 'bg-warning text-white ring-amber-400'\n                      : 'bg-primary text-primary-foreground ring-primary/25',\n                  )}\n                >\n                  <svg\n                    className=\"size-4\"\n                    viewBox=\"0 0 24 24\"\n                    fill=\"none\"\n                    stroke=\"currentColor\"\n                    strokeWidth=\"2\"\n                    strokeLinecap=\"round\"\n                    strokeLinejoin=\"round\"\n                    aria-hidden=\"true\"\n                  >\n                    <path d=\"M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z\" />\n                    <path d=\"m3.3 7 8.7 5 8.7-5\" />\n                    <path d=\"M12 22V12\" />\n                  </svg>\n                </span>\n                <span className=\"border-border bg-background/90 mt-1 rounded border px-1 py-0.5 font-mono text-xs font-bold shadow-xs\">\n                  {a.containerNo.split('-')[1]}\n                </span>\n              </button>\n            </LeafletMarker>\n          ))}\n        </LeafletMap>\n      </div>\n\n      {/* Asset Telemetry & Sensor Card */}\n      <Card className=\"flex flex-col justify-between\">\n        <CardHeader>\n          <div className=\"flex items-center justify-between\">\n            <Badge variant=\"outline\" className=\"font-mono text-xs\">\n              {activeAsset.type}\n            </Badge>\n            <Badge\n              className={\n                activeAsset.status === 'Alert'\n                  ? 'border-warning/20 bg-warning/10 text-warning'\n                  : 'border-success/20 bg-success/10 text-success'\n              }\n            >\n              {activeAsset.status}\n            </Badge>\n          </div>\n          <CardTitle className=\"mt-2 font-mono text-lg\">{activeAsset.containerNo}</CardTitle>\n          <CardDescription className=\"text-xs\">{activeAsset.location}</CardDescription>\n        </CardHeader>\n        <CardContent className=\"space-y-4\">\n          <div className=\"border-border bg-muted/40 grid grid-cols-2 gap-2 rounded-lg border p-2.5 font-mono text-xs\">\n            {activeAsset.temp && (\n              <div>\n                <div className=\"text-muted-foreground text-xs uppercase\">Cold Chain Temp</div>\n                <div\n                  className={cn(\n                    'text-sm font-bold',\n                    activeAsset.status === 'Alert' ? 'text-warning' : 'text-foreground',\n                  )}\n                >\n                  {activeAsset.temp}\n                </div>\n              </div>\n            )}\n            {activeAsset.humidity && (\n              <div>\n                <div className=\"text-muted-foreground text-xs uppercase\">Humidity</div>\n                <div className=\"text-foreground text-sm font-bold\">{activeAsset.humidity}</div>\n              </div>\n            )}\n            <div>\n              <div className=\"text-muted-foreground text-xs uppercase\">IoT Battery</div>\n              <div className=\"text-foreground text-sm font-bold\">{activeAsset.battery}</div>\n            </div>\n            <div>\n              <div className=\"text-muted-foreground text-xs uppercase\">Geofence</div>\n              <div className=\"text-foreground text-sm font-bold\">{activeAsset.geofence}</div>\n            </div>\n          </div>\n\n          <div className=\"border-border space-y-1 border-t pt-3\">\n            <div className=\"text-muted-foreground mb-1 font-mono text-xs tracking-wider uppercase\">\n              Tracked Cargo Units\n            </div>\n            {assets.map((a) => (\n              <button\n                key={a.id}\n                type=\"button\"\n                className={cn(\n                  'flex w-full items-center justify-between rounded-md px-2 py-1 text-xs transition-colors',\n                  activeId === a.id\n                    ? 'bg-accent text-accent-foreground font-semibold'\n                    : 'hover:bg-muted text-muted-foreground',\n                )}\n                onClick={() => selectAsset(a)}\n              >\n                <span className=\"font-mono\">{a.containerNo}</span>\n                <span\n                  className={cn('font-mono text-xs', a.status === 'Alert' ? 'text-warning font-bold' : 'opacity-70')}\n                >\n                  {a.type}\n                </span>\n              </button>\n            ))}\n          </div>\n\n          <Button\n            variant=\"outline\"\n            size=\"sm\"\n            className=\"w-full font-mono text-xs\"\n            onClick={() => selectAsset(activeAsset)}\n          >\n            Center on Container\n          </Button>\n        </CardContent>\n      </Card>\n    </div>\n  )\n}\n\nexport default LeafletAssetTrackingMap\n",
      "type": "registry:block",
      "target": "~/components/blocks/LeafletAssetTrackingMap.tsx"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/react/badge.json",
    "https://uipkge.dev/r/react/button.json",
    "https://uipkge.dev/r/react/card.json",
    "https://uipkge.dev/r/react/leaflet-map.json"
  ],
  "description": "Intermodal cargo container tracking on free Esri satellite tiles — no API key — with shipping lanes, cold-chain sensor status, and geofence state.",
  "categories": [
    "logistics",
    "supply-chain",
    "map"
  ]
}