Frontend redesign chunk 4: navigation and routing
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:
@@ -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("/");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user