| 1 | import "./globals.css"; |
| 2 | import type { Metadata } from "next"; |
| 3 | import Link from "next/link"; |
| 4 | import OmniSearch from "@/components/OmniSearch"; |
| 5 | |
| 6 | export const metadata: Metadata = { |
| 7 | title: "SIGHTLINE - Geospatial Intelligence", |
| 8 | description: "Real public camera & sensor aggregation platform", |
| 9 | }; |
| 10 | |
| 11 | const NAV: { group: string; items: [string, string, string][] }[] = [ |
| 12 | { group: "Operations", items: [ |
| 13 | ["/", "Command", "◳"], |
| 14 | ["/wall", "Live Wall", "▣"], |
| 15 | ["/map", "Live Map", "◉"], |
| 16 | ["/cameras", "Camera Grid", "▦"], |
| 17 | ["/satellites", "Satellite Cameras", "✦"], |
| 18 | ["/recognize", "ALPR Recognition", "◎"], |
| 19 | ]}, |
| 20 | { group: "Intelligence", items: [ |
| 21 | ["/states", "Nationwide", "⬡"], |
| 22 | ["/search", "Search", "⌕"], |
| 23 | ["/profiles", "Profile Engine", "◐"], |
| 24 | ["/investigations", "Investigations", "❒"], |
| 25 | ["/watchlists", "Watchlists", "◈"], |
| 26 | ["/alerts", "Alerts", "▲"], |
| 27 | ]}, |
| 28 | { group: "System", items: [ |
| 29 | ["/import", "Ingest", "↥"], |
| 30 | ["/audit", "Audit Log", "≣"], |
| 31 | ["/admin", "Admin", "⚙"], |
| 32 | ]}, |
| 33 | ]; |
| 34 | |
| 35 | export default function RootLayout({ children }: { children: React.ReactNode }) { |
| 36 | return ( |
| 37 | <html lang="en"> |
| 38 | <body> |
| 39 | <div className="shell"> |
| 40 | <aside className="sidebar"> |
| 41 | <div className="brand"> |
| 42 | <div className="logo"><span className="dot" /> SIGHT<b>LINE</b></div> |
| 43 | <small>Geospatial Intelligence</small> |
| 44 | </div> |
| 45 | <nav className="nav"> |
| 46 | {NAV.map((g) => ( |
| 47 | <div key={g.group}> |
| 48 | <div className="group">{g.group}</div> |
| 49 | {g.items.map(([href, label, ico]) => ( |
| 50 | <Link key={href} href={href}><span className="ico">{ico}</span>{label}</Link> |
| 51 | ))} |
| 52 | </div> |
| 53 | ))} |
| 54 | </nav> |
| 55 | </aside> |
| 56 | <div className="main"> |
| 57 | <div className="topbar"> |
| 58 | <OmniSearch /> |
| 59 | <div style={{ flex: 1 }} /> |
| 60 | <span className="badge ok"><span className="live-dot" /> LIVE</span> |
| 61 | </div> |
| 62 | <div className="content">{children}</div> |
| 63 | </div> |
| 64 | </div> |
| 65 | </body> |
| 66 | </html> |
| 67 | ); |
| 68 | } |