Backend (incident_correlator.py):
- Create path (line ~1274): title only uses "at {location}" when location_coords is also set
- Update path (line ~1226): same guard — best_coords must be truthy alongside best_location
Frontend (MapView.tsx):
- Desktop sidebar: cards with location_coords → <button> fly-to; cards without → <a href> that navigates to the incident page with "View details →" text
- Mobile drawer: same split — with coords fly-to+close, without coords navigate via <a>
- Removed the "no coords" italic placeholder text; the card behavior itself makes it clear
This commit is contained in:
@@ -1223,7 +1223,7 @@ async def _update_incident(
|
||||
talkgroup_name
|
||||
or (f"TGID {talkgroup_id}" if talkgroup_id else inc.get("title", "").split(" — ")[-1])
|
||||
)
|
||||
if primary_tag and best_location and primary_tag.lower() != best_location.lower():
|
||||
if primary_tag and best_location and best_coords and primary_tag.lower() != best_location.lower():
|
||||
updates["title"] = f"{primary_tag} at {best_location}"
|
||||
elif primary_tag and tg_label:
|
||||
updates["title"] = f"{primary_tag} — {tg_label}"
|
||||
@@ -1271,7 +1271,7 @@ async def _create_incident(
|
||||
# Build a descriptive title from tags + location when available
|
||||
content_tags = [t for t in tags if t != "auto-generated"]
|
||||
primary_tag = _tag_to_title(content_tags[0]) if content_tags else None
|
||||
if primary_tag and location and primary_tag.lower() != location.lower():
|
||||
if primary_tag and location and location_coords and primary_tag.lower() != location.lower():
|
||||
title = f"{primary_tag} at {location}"
|
||||
elif primary_tag:
|
||||
title = f"{primary_tag} — {tg_label}"
|
||||
|
||||
@@ -518,51 +518,59 @@ export default function MapView({ nodes, activeCalls, incidents = [], lastUpdate
|
||||
const color = INCIDENT_COLORS[inc.type ?? "other"] ?? INCIDENT_COLORS.other;
|
||||
const age = inc.started_at ? timeAgo(new Date(inc.started_at)) : null;
|
||||
const unitCount = inc.units?.length ?? 0;
|
||||
const baseClass = "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";
|
||||
const cardBody = (
|
||||
<>
|
||||
<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>
|
||||
)}
|
||||
</div>
|
||||
{!inc.location_coords && (
|
||||
<p className="text-[10px] text-blue-700 mt-1">View details →</p>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
if (inc.location_coords) {
|
||||
return (
|
||||
<button
|
||||
key={inc.incident_id}
|
||||
onClick={() => handleIncidentSelect(inc)}
|
||||
className={baseClass}
|
||||
style={{ borderColor: color + "55" }}
|
||||
>
|
||||
{cardBody}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div
|
||||
<a
|
||||
key={inc.incident_id}
|
||||
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"
|
||||
href={`/incidents/${inc.incident_id}`}
|
||||
className={`block ${baseClass}`}
|
||||
style={{ borderColor: color + "55" }}
|
||||
>
|
||||
<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 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>
|
||||
{cardBody}
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
@@ -580,22 +588,39 @@ export default function MapView({ nodes, activeCalls, incidents = [], lastUpdate
|
||||
<div className="bg-gray-950/95 border-t border-gray-800 max-h-52 overflow-y-auto px-3 py-2 space-y-1.5">
|
||||
{incidents.map((inc) => {
|
||||
const color = INCIDENT_COLORS[inc.type ?? "other"] ?? INCIDENT_COLORS.other;
|
||||
return (
|
||||
<button
|
||||
key={inc.incident_id}
|
||||
onClick={() => {
|
||||
setDrawerOpen(false);
|
||||
handleIncidentSelect(inc);
|
||||
}}
|
||||
className="w-full text-left border rounded px-2 py-1.5 text-xs font-mono"
|
||||
style={{ borderColor: color + "55" }}
|
||||
>
|
||||
const label = (
|
||||
<>
|
||||
<span className="font-semibold" style={{ color }}>
|
||||
{inc.type ?? "other"}
|
||||
</span>
|
||||
{" — "}
|
||||
<span className="text-white">{inc.title ?? "Incident"}</span>
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
if (inc.location_coords) {
|
||||
return (
|
||||
<button
|
||||
key={inc.incident_id}
|
||||
onClick={() => {
|
||||
setDrawerOpen(false);
|
||||
handleIncidentSelect(inc);
|
||||
}}
|
||||
className="w-full text-left border rounded px-2 py-1.5 text-xs font-mono"
|
||||
style={{ borderColor: color + "55" }}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<a
|
||||
key={inc.incident_id}
|
||||
href={`/incidents/${inc.incident_id}`}
|
||||
className="block w-full text-left border rounded px-2 py-1.5 text-xs font-mono"
|
||||
style={{ borderColor: color + "55" }}
|
||||
>
|
||||
{label}
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user