nyuchimzizi
Mzizi — an open-architecture project of the Bundu Foundation, operated and developed by Nyuchi. Built on the Five African Minerals palette.
Built by Nyuchi Africav4.0.39
Branded route planning display composing route-card, stop-card, and itinerary-timeline primitives with gold mineral accent. Shows origin, destination, transit mode, duration, fare estimate, and step-by-step route. Integrates with Harare Metro data backbone and Nyuchi Logistics fleet.
View the full component source code below.
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
import { useNyuchiHarness } from "@/lib/harness"
/* ═══════════════════════════════════════════════════════════════
NYUCHI ROUTE PLANNER — Brand Component (Pre-Wired)
Mineral: Gold (commerce, transport, movement).
Composes route-card + stop-card + itinerary-timeline + fare-calculator.
✅ HARNESS ✅ TOKENS ✅ STRICT MINERAL RULES ✅ TOUCH 48px+
═══════════════════════════════════════════════════════════════ */
interface RouteStop { name: string; time?: string; type?: "origin" | "stop" | "transfer" | "destination" }
interface NyuchiRoutePlannerProps {
origin: string; destination: string
stops?: RouteStop[]; mode?: "bus" | "kombi" | "taxi" | "walk" | "mixed"
duration?: string; distance?: string; fare?: string; currency?: string
departureTime?: string; arrivalTime?: string
onBook?: () => void; onSave?: () => void; onClick?: () => void; className?: string
}
const modeIcons = { bus: "🚌", kombi: "🚐", taxi: "🚕", walk: "🚶", mixed: "🔀" } as const
export function NyuchiRoutePlanner({ loading = false, origin, destination, stops = [], mode = "bus", duration, distance, fare, currency = "ZWL", departureTime, arrivalTime, onBook, onSave, onClick, className }: NyuchiRoutePlannerProps) {
const { log, motion, LiveRegion } = useNyuchiHarness("route-planner")
const animStyle = React.useMemo(() => motion.prefersReduced ? {} : { animation: `nyuchi-fade-slide-up ${motion.enterDuration}ms ${motion.enterEasing} both` }, [motion])
if (loading) return (<div data-slot="nyuchi-route-planner" data-portal="https://design.nyuchi.com/components/nyuchi-route-planner" data-loading role="article" aria-label="Route plan" className="animate-pulse rounded-[var(--radius-lg,14px)] bg-card p-4 ring-1 ring-foreground/10 space-y-3"><div className="flex items-center gap-3"><div className="size-10 shrink-0 rounded-full bg-muted" /><div className="flex-1 space-y-1.5"><div className="h-3.5 w-1/2 rounded bg-muted" /><div className="h-2.5 w-2/3 rounded bg-muted" /></div></div>{Array.from({length:3}).map((_,i)=>(<div key={i} className="ml-5 flex items-center gap-3 border-l-2 border-muted pl-4 py-2"><div className="size-2 rounded-full bg-muted" /><div className="flex-1"><div className="h-3 w-1/3 rounded bg-muted" /></div></div>))}</div>)
if (loading) return (<div data-slot="nyuchi-route-planner" data-loading role="article" aria-label="Route plan" className="animate-pulse rounded-[var(--radius-lg,14px)] bg-card p-4 ring-1 ring-foreground/10 space-y-3"><div className="flex items-center gap-3"><div className="size-8 rounded-full bg-muted" /><div className="h-3.5 w-1/2 rounded bg-muted" /></div><div className="ml-4 border-l-2 border-muted pl-4 space-y-3">{Array.from({length:3}).map((_,i)=>(<div key={i} className="flex items-center gap-3"><div className="size-6 rounded-full bg-muted" /><div className="flex-1 h-3 rounded bg-muted" /></div>))}</div><div className="flex items-center gap-3"><div className="size-8 rounded-full bg-muted" /><div className="h-3.5 w-1/3 rounded bg-muted" /></div></div>)
return (
<div data-slot="nyuchi-route-planner" role="article" aria-label="Route plan" onClick={onClick} className={cn("bg-card rounded-[var(--radius-lg,14px)] border border-border overflow-hidden", onClick && "cursor-pointer", className)}>
<div className="border-b border-border bg-[var(--color-gold,#FFD740)]/5 px-4 py-3 flex items-center gap-3">
<span className="text-lg">{modeIcons[mode]}</span>
<div className="flex-1 min-w-0"><p className="text-sm font-semibold text-foreground" style={{ fontFamily: "var(--font-serif)" }}>{origin} → {destination}</p>
{(duration || distance) && <p className="text-[11px] text-muted-foreground">{[duration, distance].filter(Boolean).join(" · ")}</p>}
</div>
{fare && <div className="text-right"><p className="text-sm font-bold text-[var(--color-gold,#FFD740)]">{new Intl.NumberFormat(undefined, { style: "currency", currency: currency || "USD" }).format(Number(fare) || 0)}</p><p className="text-[10px] text-muted-foreground">est. fare</p></div>}
</div>
{stops.length > 0 && (
<div className="px-4 py-3 space-y-0">{stops.map((stop, i) => (
<div key={i} className="flex items-start gap-3 py-1.5">
<div className="flex flex-col items-center"><div className={cn("size-2.5 rounded-full mt-1", stop.type === "origin" || stop.type === "destination" ? "bg-[var(--color-gold,#FFD740)]" : stop.type === "transfer" ? "bg-[var(--color-cobalt,#00B0FF)]" : "bg-muted-foreground/30")} />{i < stops.length - 1 && <div className="w-px flex-1 bg-border min-h-[16px]" />}</div>
<div className="flex-1 min-w-0"><p className={cn("text-xs", stop.type === "origin" || stop.type === "destination" ? "font-semibold text-foreground" : "text-muted-foreground")}>{stop.name}</p>{stop.time && <p className="text-[10px] text-muted-foreground">{stop.time}</p>}</div>
</div>
))}</div>
)}
{(departureTime || arrivalTime) && <div className="border-t border-border px-4 py-2 flex justify-between text-[11px] text-muted-foreground"><span>Depart: {departureTime || "—"}</span><span>Arrive: {arrivalTime || "—"}</span></div>}
{(onBook || onSave) && (
<div className="border-t border-border p-3 flex gap-2">
{onBook && <button onClick={e => { e.stopPropagation(); onBook() }} className="flex-1 h-12 rounded-full bg-[var(--color-gold,#FFD740)] text-[#0A0A0A] text-[13px] font-medium transition-opacity focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--color-primary,#00B0FF)] hover:opacity-80">Book Ride</button>}
{onSave && <button onClick={e => { e.stopPropagation(); onSave() }} className="h-12 rounded-full bg-muted text-foreground border border-border px-5 text-[13px] font-medium transition-opacity focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--color-primary,#00B0FF)] hover:opacity-80">Save</button>}
</div>
)}
</div>
)
}
npx shadcn@latest add https://mzizi.dev/api/v1/ui/nyuchi-route-plannerFetch this component's metadata and source code from the registry API.
/api/v1/ui/nyuchi-route-planner