UI Updates
app/map/page.tsx
Removed IncidentCard component and the incidents grid below the map — the on-map sidebar inside MapView is the single display
Moved kiosk exit button from top-3 left-3 (overlapping zoom controls) to bottom-[5.5rem] left-3
components/MapView.tsx
Fixed popup "View incident →" link — adds stopPropagation() + window.location.href to prevent Leaflet intercepting the click
Added "View details →" link on each sidebar incident card so you can navigate from the map panel without opening a popup
Added "News Alerts" overlay layer (placeholder, ready for RSS/feed integration)
lib/types.ts
Added preferred_token_id?: string | null to SystemRecord
lib/c2api.ts
Added setPreferredToken(tokenId, systemId) calling PUT /tokens/{tokenId}/prefer/{systemId} (backend already existed)
app/systems/page.tsx
Added PreferredTokenPanel component — loads the token pool lazily on expand, shows radio buttons to set/clear the preferred token, displayed on each system card above the AI flags panel
This commit is contained in:
@@ -2,48 +2,12 @@
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import Link from "next/link";
|
||||
import { useNodes } from "@/lib/useNodes";
|
||||
import { useActiveCalls } from "@/lib/useCalls";
|
||||
import { useActiveIncidents } from "@/lib/useIncidents";
|
||||
import type { IncidentRecord } from "@/lib/types";
|
||||
|
||||
const MapView = dynamic(() => import("@/components/MapView"), { ssr: false });
|
||||
|
||||
const TYPE_COLORS: Record<string, string> = {
|
||||
fire: "border-red-800 bg-red-950 text-red-300",
|
||||
police: "border-blue-800 bg-blue-950 text-blue-300",
|
||||
ems: "border-yellow-800 bg-yellow-950 text-yellow-300",
|
||||
accident: "border-orange-800 bg-orange-950 text-orange-300",
|
||||
other: "border-gray-700 bg-gray-900 text-gray-300",
|
||||
};
|
||||
|
||||
function IncidentCard({ incident }: { incident: IncidentRecord }) {
|
||||
const cls = TYPE_COLORS[incident.type ?? "other"] ?? TYPE_COLORS.other;
|
||||
return (
|
||||
<Link
|
||||
href={`/incidents/${incident.incident_id}`}
|
||||
className={`block border rounded-lg p-3 hover:brightness-110 transition-all ${cls}`}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-2 mb-1">
|
||||
<span className="text-xs font-mono font-semibold uppercase tracking-wide">
|
||||
{incident.type ?? "other"}
|
||||
</span>
|
||||
<span className="text-xs opacity-60 font-mono">
|
||||
{incident.call_ids.length} call{incident.call_ids.length !== 1 ? "s" : ""}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm font-bold leading-tight">{incident.title ?? "Incident"}</p>
|
||||
{incident.location && (
|
||||
<p className="text-xs opacity-70 mt-1 font-mono truncate">{incident.location}</p>
|
||||
)}
|
||||
{!incident.location_coords && (
|
||||
<p className="text-xs opacity-40 mt-1 font-mono italic">location not geocoded yet</p>
|
||||
)}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export default function MapPage() {
|
||||
const { nodes, loading } = useNodes();
|
||||
const activeCalls = useActiveCalls();
|
||||
@@ -69,7 +33,7 @@ export default function MapPage() {
|
||||
<button
|
||||
onClick={() => setKiosk(false)}
|
||||
title="Exit fullscreen"
|
||||
className="absolute top-3 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"
|
||||
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"/>
|
||||
@@ -111,18 +75,6 @@ export default function MapPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{incidents.length > 0 && (
|
||||
<section>
|
||||
<h2 className="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-3">
|
||||
Active Incidents ({incidents.length})
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3">
|
||||
{incidents.map((inc) => (
|
||||
<IncidentCard key={inc.incident_id} incident={inc} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user