Frontend redesign chunk 4: navigation and routing
Build & Deploy / Build & push images (push) Successful in 4m21s
Build & Deploy / Deploy to VM (push) Failing after 11m50s

Rewrite Nav.tsx to the five-destination IA from UI_REDESIGN.md §3 (Live,
Incidents, Archive, Watch, Network) on tokens/sans type, with Settings,
Admin, Trips and Profile moved into the avatar dropdown instead of sitting
as nav peers. Network stays gated to admin/operator, matching the write
boundary its constituent pages (nodes/systems/tokens) already had.

Delete app/dashboard/page.tsx — its incident cards become the Live rail,
its node cards become Network, its call table becomes Archive; nothing on
it is unique. Add app/map/page.tsx -> redirect('/') and rewrite
app/calls/page.tsx -> redirect('/incidents') (Archive/search is blocked on
backend work, chunk 12).

ChromeSwitcher now gives a signed-in user at "/" the app shell instead of
marketing chrome; app/page.tsx branches the same way, sending a signed-in
provisioned user to /incidents as an honest interim until the Live screen
itself lands (chunk 6) — marketing content and behavior for signed-out
visitors is unchanged.

Left the light-mode !important overrides in globals.css in place past this
chunk (deviating from the chunk 4 acceptance criteria) — they still back
every page outside this redesign's 11-chunk scope (settings, admin,
profile, marketing). Deleting them now would break light mode on all of
those. Logged in DEFERRED.md.

Per UI_REDESIGN.md chunk 4.
This commit is contained in:
Logan Cusano
2026-08-19 23:01:13 -04:00
parent 8fdedee25b
commit eaae452d4e
6 changed files with 151 additions and 571 deletions
+5 -78
View File
@@ -1,80 +1,7 @@
"use client";
import { redirect } from "next/navigation";
import { useEffect, useState } from "react";
import dynamic from "next/dynamic";
import { useNodes } from "@/lib/useNodes";
import { useActiveCalls } from "@/lib/useCalls";
import { useActiveIncidents } from "@/lib/useIncidents";
const MapView = dynamic(() => import("@/components/MapView"), { ssr: false });
export default function MapPage() {
const { nodes, loading } = useNodes();
const activeCalls = useActiveCalls();
const incidents = useActiveIncidents();
const [kiosk, setKiosk] = useState(false);
const [lastUpdated, setLastUpdated] = useState<Date | null>(null);
// Track when data last refreshed
useEffect(() => {
if (!loading) setLastUpdated(new Date());
}, [nodes, activeCalls, incidents, loading]);
// Kiosk mode: full-viewport fixed overlay sits above the sticky nav (z-40 → z-50)
if (kiosk) {
return (
<div className="fixed inset-0 z-50 bg-gray-950">
<MapView
nodes={nodes}
activeCalls={activeCalls}
incidents={incidents}
lastUpdated={lastUpdated}
/>
<button
onClick={() => setKiosk(false)}
title="Exit fullscreen"
className="absolute bottom-[5.5rem] left-3 z-[1002] bg-gray-950/90 border border-gray-700 rounded px-3 py-1.5 text-xs font-mono text-gray-300 hover:text-white hover:border-gray-500 transition-colors flex items-center gap-1.5"
>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
<path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/>
</svg>
Exit fullscreen
</button>
</div>
);
}
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<h1 className="text-xl font-bold text-white font-mono">Map</h1>
<button
onClick={() => setKiosk(true)}
title="Fullscreen / kiosk mode"
className="text-xs font-mono text-gray-500 hover:text-gray-300 transition-colors flex items-center gap-1.5"
>
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
</svg>
Fullscreen
</button>
</div>
{loading ? (
<div className="flex items-center justify-center h-[calc(100vh-10rem)] border border-gray-800 rounded-lg text-gray-600 font-mono text-sm">
Loading map…
</div>
) : (
<div className="w-full h-[calc(100vh-10rem)] border border-gray-800 rounded-lg overflow-hidden">
<MapView
nodes={nodes}
activeCalls={activeCalls}
incidents={incidents}
lastUpdated={lastUpdated}
/>
</div>
)}
</div>
);
// The map is no longer a destination you navigate to — it's the product,
// and the product is the landing page. See UI_REDESIGN.md §3.
export default function MapPageRedirect() {
redirect("/");
}