\")",
+ }}
+ />
+
+ {/* Header */}
+
+
+ {/* Weekday header */}
+
+ {WEEKDAY_LABELS.map((d, i) => (
+
+ {d}
+
+ ))}
+
+
+ {/* Grid */}
+
+ {cells.map((cell, idx) => {
+ const k = ymd(cell.date);
+ const dayStops = stopsByDate.get(k) ?? [];
+ const visibleStops = visibleStopsByDate.get(k) ?? [];
+ const isToday = isSameDay(cell.date, today);
+ const isSelected = k === selectedDate;
+ const isWeekend = cell.date.getDay() === 0 || cell.date.getDay() === 6;
+ const overflow = dayStops.length - visibleStops.length;
+
+ return (
+
+ );
+ })}
+
+
+ {/* Legend */}
+
+
+
+ {/* Day detail panel */}
+
+
+ );
+}
diff --git a/src/components/admin/stops/StopsDashboardClient.tsx b/src/components/admin/stops/StopsDashboardClient.tsx
new file mode 100644
index 0000000..ce29829
--- /dev/null
+++ b/src/components/admin/stops/StopsDashboardClient.tsx
@@ -0,0 +1,184 @@
+"use client";
+
+import { useMemo, useState } from "react";
+import { type Stop, type StopView, getStopStatus } from "./types";
+import StopsCalendar from "./StopsCalendar";
+import StopsLocations from "./StopsLocations";
+import StopsList from "./StopsList";
+
+type Props = {
+ stops: Stop[];
+};
+
+const TABS: { value: StopView; label: string; hint: string }[] = [
+ { value: "calendar", label: "Calendar", hint: "Month at a glance" },
+ { value: "locations", label: "Locations", hint: "Stops grouped by city" },
+ { value: "list", label: "List", hint: "All stops in order" },
+];
+
+export default function StopsDashboardClient({ stops }: Props) {
+ const [view, setView] = useState