{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "delivery-tracker",
  "title": "Delivery Tracker",
  "description": "Live order tracking with route progress, courier position, and order details.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "@mapcn/map",
    "card",
    "badge",
    "button"
  ],
  "files": [
    {
      "path": "src/registry/blocks/delivery-tracker/page.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport { useTheme } from \"next-themes\";\nimport { Clock3, House, Store, Utensils, Truck, UserRound } from \"lucide-react\";\n\nimport { Map, MapMarker, MapRoute, MarkerContent } from \"@/components/ui/map\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport {\n  buildRouteUrl,\n  deliveryMeals,\n  dropoff,\n  mapView,\n  pickup,\n  progressFraction,\n  routeStyle,\n  type OsrmRouteData,\n} from \"./data\";\n\nfunction formatDistance(meters?: number) {\n  if (!meters) return \"--\";\n  if (meters < 1000) return `${Math.round(meters)} m`;\n  return `${(meters / 1000).toFixed(1)} km`;\n}\n\nfunction formatDuration(seconds?: number) {\n  if (!seconds) return \"--\";\n  const minutes = Math.round(seconds / 60);\n  if (minutes < 60) return `${minutes} min`;\n  const hours = Math.floor(minutes / 60);\n  const remainingMinutes = minutes % 60;\n  return `${hours}h ${remainingMinutes}m`;\n}\n\nexport default function Page() {\n  const [routeData, setRouteData] = useState<OsrmRouteData | null>(null);\n  const [loading, setLoading] = useState(true);\n  const { resolvedTheme } = useTheme();\n  const remainingRouteColor =\n    resolvedTheme === \"dark\"\n      ? routeStyle.remaining.color.dark\n      : routeStyle.remaining.color.light;\n\n  useEffect(() => {\n    async function fetchRoute() {\n      setLoading(true);\n      try {\n        const response = await fetch(buildRouteUrl(pickup, dropoff));\n        const data = await response.json();\n        const route = data?.routes?.[0];\n        if (!route?.geometry?.coordinates) return;\n\n        setRouteData({\n          coordinates: route.geometry.coordinates as [number, number][],\n          duration: route.duration as number,\n          distance: route.distance as number,\n        });\n      } catch (error) {\n        console.error(\"Failed to fetch route:\", error);\n      } finally {\n        setLoading(false);\n      }\n    }\n\n    fetchRoute();\n  }, []);\n\n  const progressCoordinates = useMemo(() => {\n    const total = routeData?.coordinates?.length ?? 0;\n    const progressCount = Math.max(2, Math.floor(total * progressFraction));\n    return routeData?.coordinates?.slice(0, progressCount) ?? [];\n  }, [routeData]);\n\n  const courierPosition = progressCoordinates[progressCoordinates.length - 1];\n\n  return (\n    <div className=\"flex min-h-screen items-center justify-center p-8\">\n      <div className=\"bg-sidebar mx-auto grid w-[1200px] rounded-xl border md:grid-cols-[1.05fr_1fr]\">\n        <div className=\"flex flex-col p-5 md:p-6\">\n          <div className=\"space-y-1\">\n            <h3 className=\"text-2xl font-semibold tracking-tight\">\n              Track Delivery\n            </h3>\n            <p className=\"text-muted-foreground text-sm\">Mon Feb 10 - 2-3 PM</p>\n          </div>\n\n          <Card className=\"mt-5\">\n            <CardHeader>\n              <CardTitle className=\"font-medium\">\n                Order items ({deliveryMeals.length})\n              </CardTitle>\n            </CardHeader>\n            <CardContent className=\"space-y-5\">\n              {deliveryMeals.map((meal) => (\n                <div key={meal.name} className=\"flex items-center gap-3\">\n                  <div className=\"bg-muted grid size-8 place-items-center rounded-full text-xs\">\n                    <Utensils className=\"text-muted-foreground size-4\" />\n                  </div>\n                  <div className=\"min-w-4 flex-1\">\n                    <p className=\"truncate pb-1 text-sm font-medium\">\n                      {meal.name}\n                    </p>\n                    <p className=\"text-muted-foreground text-xs\">\n                      {meal.price}\n                    </p>\n                  </div>\n                  <Badge\n                    variant=\"secondary\"\n                    className=\"h-6 rounded-full px-2.5\"\n                  >\n                    x{meal.quantity}\n                  </Badge>\n                </div>\n              ))}\n              <div className=\"border-border/60 flex items-center justify-between border-t pt-3 text-sm\">\n                <span className=\"text-muted-foreground\">Bundle total</span>\n                <span className=\"font-medium\">$189.00</span>\n              </div>\n            </CardContent>\n          </Card>\n\n          <div className=\"mt-4 grid gap-3 sm:grid-cols-2\">\n            <Card>\n              <CardContent className=\"space-y-2\">\n                <p className=\"text-muted-foreground text-sm\">\n                  Pickup confirmed\n                </p>\n                <p className=\"text-sm font-medium\">Mon, Feb 10 at 1:48 PM</p>\n              </CardContent>\n            </Card>\n            <Card>\n              <CardContent className=\"space-y-2\">\n                <p className=\"text-muted-foreground text-sm\">\n                  Remaining travel\n                </p>\n                <p className=\"text-sm font-medium\">\n                  {formatDuration(routeData?.duration)}\n                  <span className=\"text-muted-foreground font-normal\">\n                    {\" · \"}\n                    {formatDistance(routeData?.distance)}\n                  </span>\n                </p>\n              </CardContent>\n            </Card>\n          </div>\n\n          <div className=\"mt-6 flex flex-wrap items-center gap-2\">\n            <Button size=\"sm\">\n              <Clock3 />\n              View timeline\n            </Button>\n            <Button variant=\"outline\" size=\"sm\">\n              <UserRound />\n              Contact courier\n            </Button>\n          </div>\n        </div>\n\n        <div className=\"relative h-[450px] overflow-hidden rounded-xl shadow-sm md:h-full\">\n          <Map\n            loading={loading}\n            center={mapView.center}\n            zoom={mapView.zoom}\n            minZoom={mapView.minZoom}\n            maxZoom={mapView.maxZoom}\n          >\n            <MapRoute\n              id=\"delivery-full-route\"\n              coordinates={routeData?.coordinates ?? []}\n              color={remainingRouteColor}\n              width={routeStyle.remaining.width}\n              opacity={routeStyle.remaining.opacity}\n              interactive={false}\n            />\n            <MapRoute\n              id=\"delivery-progress-route\"\n              coordinates={progressCoordinates}\n              color={routeStyle.progress.color}\n              width={routeStyle.progress.width}\n              opacity={routeStyle.progress.opacity}\n              interactive={false}\n            />\n\n            {courierPosition && (\n              <MapMarker\n                longitude={courierPosition[0]}\n                latitude={courierPosition[1]}\n                offset={[0, 10]}\n              >\n                <MarkerContent>\n                  <div\n                    className=\"relative grid size-9 place-items-center rounded-full shadow-md\"\n                    style={{ backgroundColor: routeStyle.progress.color }}\n                  >\n                    <Truck className=\"size-4 text-white\" />\n                    <div className=\"bg-popover text-popover-foreground absolute bottom-full left-1/2 mb-2.5 -translate-x-1/2 rounded-md border px-2 py-1 text-xs font-medium whitespace-nowrap shadow-md\">\n                      {formatDuration(routeData?.duration)} away\n                      <span className=\"bg-popover absolute top-full left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rotate-45 border-r border-b\" />\n                    </div>\n                  </div>\n                </MarkerContent>\n              </MapMarker>\n            )}\n\n            <MapMarker longitude={pickup.lng} latitude={pickup.lat}>\n              <MarkerContent>\n                <div className=\"grid size-7 place-items-center rounded-full bg-emerald-500 shadow-md\">\n                  <Store className=\"size-3.5 text-white\" />\n                </div>\n              </MarkerContent>\n            </MapMarker>\n\n            <MapMarker longitude={dropoff.lng} latitude={dropoff.lat}>\n              <MarkerContent>\n                <div className=\"grid size-7 place-items-center rounded-full bg-rose-500 shadow-md\">\n                  <House className=\"size-3.5 text-white\" />\n                </div>\n              </MarkerContent>\n            </MapMarker>\n          </Map>\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:page",
      "target": "app/delivery/page.tsx"
    },
    {
      "path": "src/registry/blocks/delivery-tracker/data.ts",
      "content": "export interface DeliveryMeal {\n  name: string;\n  price: string;\n  quantity: number;\n}\n\nexport interface OsrmRouteData {\n  coordinates: [number, number][];\n  duration: number;\n  distance: number;\n}\n\nexport const deliveryMeals: DeliveryMeal[] = [\n  { name: \"Spicy Tofu Grain Bowl\", price: \"$44.00\", quantity: 1 },\n  { name: \"Herb Chicken Rice Box\", price: \"$58.00\", quantity: 2 },\n  { name: \"Roasted Veggie Wrap\", price: \"$29.00\", quantity: 1 },\n];\n\n/** Pickup (origin) and dropoff (destination) coordinates. */\nexport const pickup = { lng: -122.4185, lat: 37.7645 };\nexport const dropoff = { lng: -122.434, lat: 37.7475 };\n\n/** Initial map viewport. */\nexport const mapView = {\n  center: [-122.4263, 37.756] as [number, number],\n  zoom: 13.6,\n  minZoom: 12,\n  maxZoom: 15,\n};\n\n/** Fraction of the route the courier has already covered (0–1). */\nexport const progressFraction = 0.62;\n\n/**\n * OSRM demo routing endpoint. Swap in your own routing service by returning a\n * URL that responds with GeoJSON route geometry.\n */\nexport function buildRouteUrl(\n  from: { lng: number; lat: number },\n  to: { lng: number; lat: number },\n) {\n  return `https://router.project-osrm.org/route/v1/driving/${from.lng},${from.lat};${to.lng},${to.lat}?overview=full&geometries=geojson`;\n}\n\n/**\n * Route line styling. WebGL paint can't read CSS variables, so colors are\n * concrete hex. `progress` highlights the covered path; `remaining` is themed\n * for the road still ahead.\n */\nexport const routeStyle = {\n  progress: { color: \"#3b82f6\", width: 6, opacity: 0.95 },\n  remaining: {\n    width: 5.2,\n    opacity: 0.5,\n    color: { light: \"#6b7280\", dark: \"#9ca3af\" },\n  },\n} as const;\n",
      "type": "registry:component",
      "target": "app/delivery/data.ts"
    }
  ],
  "meta": {
    "iframeHeight": "720px"
  },
  "categories": [
    "tracking",
    "delivery"
  ],
  "type": "registry:block"
}
