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:
@@ -293,6 +293,7 @@ function FanIncidentLayer({
|
||||
{inc.location && <p className="text-xs text-gray-600">{inc.location}</p>}
|
||||
<a
|
||||
href={`/incidents/${inc.incident_id}`}
|
||||
onClick={(e) => { e.stopPropagation(); window.location.href = `/incidents/${inc.incident_id}`; e.preventDefault(); }}
|
||||
className="text-xs text-blue-600 hover:underline block mt-0.5"
|
||||
>
|
||||
View incident →
|
||||
@@ -451,6 +452,11 @@ export default function MapView({ nodes, activeCalls, incidents = [], lastUpdate
|
||||
/>
|
||||
</LayersControl.Overlay>
|
||||
|
||||
{/* Overlay: News / RSS alerts — placeholder for future integration */}
|
||||
<LayersControl.Overlay name="News Alerts">
|
||||
<FeatureGroup />
|
||||
</LayersControl.Overlay>
|
||||
|
||||
{/* Overlay: ADS-B — placeholder for future integration */}
|
||||
<LayersControl.Overlay name="ADS-B">
|
||||
<FeatureGroup />
|
||||
@@ -513,40 +519,50 @@ export default function MapView({ nodes, activeCalls, incidents = [], lastUpdate
|
||||
const age = inc.started_at ? timeAgo(new Date(inc.started_at)) : null;
|
||||
const unitCount = inc.units?.length ?? 0;
|
||||
return (
|
||||
<button
|
||||
<div
|
||||
key={inc.incident_id}
|
||||
onClick={() => handleIncidentSelect(inc)}
|
||||
className="w-full text-left bg-gray-950/85 backdrop-blur-sm border rounded-lg px-3 py-2 text-xs font-mono hover:brightness-110 transition-all"
|
||||
style={{ borderColor: color + "55" }}
|
||||
>
|
||||
<div className="flex items-center gap-1.5 mb-0.5">
|
||||
<span
|
||||
className="inline-block w-2 h-2 rounded-sm flex-shrink-0"
|
||||
style={{ background: color }}
|
||||
/>
|
||||
<span
|
||||
className="uppercase tracking-wide font-semibold text-[10px]"
|
||||
style={{ color }}
|
||||
>
|
||||
{inc.type ?? "other"}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-white font-semibold leading-snug truncate">
|
||||
{inc.title ?? "Incident"}
|
||||
</p>
|
||||
{inc.location && (
|
||||
<p className="text-gray-500 truncate mt-0.5">{inc.location}</p>
|
||||
)}
|
||||
<div className="flex items-center justify-between mt-0.5">
|
||||
{age && <span className="text-gray-600">{age}</span>}
|
||||
{unitCount > 0 && (
|
||||
<span className="text-gray-600">{unitCount} unit{unitCount !== 1 ? "s" : ""}</span>
|
||||
<button
|
||||
onClick={() => handleIncidentSelect(inc)}
|
||||
className="w-full text-left"
|
||||
>
|
||||
<div className="flex items-center gap-1.5 mb-0.5">
|
||||
<span
|
||||
className="inline-block w-2 h-2 rounded-sm flex-shrink-0"
|
||||
style={{ background: color }}
|
||||
/>
|
||||
<span
|
||||
className="uppercase tracking-wide font-semibold text-[10px]"
|
||||
style={{ color }}
|
||||
>
|
||||
{inc.type ?? "other"}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-white font-semibold leading-snug truncate">
|
||||
{inc.title ?? "Incident"}
|
||||
</p>
|
||||
{inc.location && (
|
||||
<p className="text-gray-500 truncate mt-0.5">{inc.location}</p>
|
||||
)}
|
||||
</div>
|
||||
{!inc.location_coords && (
|
||||
<p className="text-gray-700 italic mt-0.5">no coords</p>
|
||||
)}
|
||||
</button>
|
||||
<div className="flex items-center justify-between mt-0.5">
|
||||
{age && <span className="text-gray-600">{age}</span>}
|
||||
{unitCount > 0 && (
|
||||
<span className="text-gray-600">{unitCount} unit{unitCount !== 1 ? "s" : ""}</span>
|
||||
)}
|
||||
</div>
|
||||
{!inc.location_coords && (
|
||||
<p className="text-gray-700 italic mt-0.5">no coords</p>
|
||||
)}
|
||||
</button>
|
||||
<a
|
||||
href={`/incidents/${inc.incident_id}`}
|
||||
className="block text-[10px] text-blue-700 hover:text-blue-500 mt-1 transition-colors"
|
||||
>
|
||||
View details →
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user