Updates to intel and correlation

This commit is contained in:
Logan
2026-04-23 01:26:41 -04:00
parent bcd3406ae8
commit 317f9d2a9d
12 changed files with 468 additions and 150 deletions
+24 -3
View File
@@ -15,6 +15,22 @@ const TYPE_COLORS: Record<string, string> = {
other: "bg-gray-800 text-gray-300",
};
const SEVERITY_COLORS: Record<string, string> = {
major: "bg-red-950 text-red-400",
moderate: "bg-orange-950 text-orange-400",
minor: "bg-gray-800 text-gray-400",
};
function severityBadge(severity: string | null | undefined) {
if (!severity || severity === "unknown") return null;
const cls = SEVERITY_COLORS[severity] ?? "bg-gray-800 text-gray-400";
return (
<span className={`text-xs font-mono px-2 py-0.5 rounded-full capitalize ${cls}`}>
{severity}
</span>
);
}
function typeBadge(type: string | null) {
const cls = TYPE_COLORS[type ?? "other"] ?? TYPE_COLORS.other;
return (
@@ -51,6 +67,7 @@ function IncidentRow({ incident, isAdmin, onResolve }: {
{incident.status}
</span>
</td>
<td className="px-4 py-3">{severityBadge(incident.severity)}</td>
<td className="px-4 py-3 text-gray-400 text-xs font-mono">{incident.call_ids.length}</td>
<td className="px-4 py-3 text-gray-400 text-xs font-mono">{fmtTime(incident.started_at)}</td>
<td className="px-4 py-3 text-gray-400 text-xs font-mono">{fmtTime(incident.updated_at)}</td>
@@ -167,9 +184,12 @@ function IncidentCards({ incidents, isAdmin, onResolve }: {
)}
</div>
<p className="text-white text-sm font-semibold leading-snug">{inc.title ?? "—"}</p>
<p className="text-gray-500 text-xs mt-1 font-mono">
{fmtTime(inc.started_at)} · {inc.call_ids.length} call{inc.call_ids.length !== 1 ? "s" : ""}
</p>
<div className="flex items-center gap-2 mt-1">
{severityBadge(inc.severity)}
<p className="text-gray-500 text-xs font-mono">
{fmtTime(inc.started_at)} · {inc.call_ids.length} call{inc.call_ids.length !== 1 ? "s" : ""}
</p>
</div>
</div>
))}
</div>
@@ -196,6 +216,7 @@ function IncidentTable({ incidents, isAdmin, onResolve }: {
<th className="px-4 py-3">Type</th>
<th className="px-4 py-3">Title</th>
<th className="px-4 py-3">Status</th>
<th className="px-4 py-3">Severity</th>
<th className="px-4 py-3">Calls</th>
<th className="px-4 py-3">Started</th>
<th className="px-4 py-3">Updated</th>
+16 -9
View File
@@ -31,8 +31,13 @@ export function CallRow({ call, systemName, isAdmin }: Props) {
const [editText, setEditText] = useState("");
const [saving, setSaving] = useState(false);
const [saveError, setSaveError] = useState<string | null>(null);
// Resolve incident links: prefer new list, fall back to legacy single field.
const incidentIds: string[] = (call.incident_ids?.length ?? 0) > 0
? call.incident_ids
: call.incident_id ? [call.incident_id] : [];
const isActive = call.status === "active";
const hasDetails = call.transcript || call.transcript_corrected || (call.tags && call.tags.length > 0) || call.incident_id || call.audio_url;
const hasDetails = call.transcript || call.transcript_corrected || (call.tags && call.tags.length > 0) || incidentIds.length > 0 || call.audio_url;
const displayTranscript = (!showOriginal && call.transcript_corrected) ? call.transcript_corrected : call.transcript;
const hasBoth = !!(call.transcript && call.transcript_corrected);
const hasSegments = call.segments && call.segments.length > 1;
@@ -121,14 +126,16 @@ export function CallRow({ call, systemName, isAdmin }: Props) {
</div>
)}
{/* Incident link */}
{call.incident_id && (
<p className="text-xs font-mono text-indigo-400">
Incident:{" "}
<a href={`/incidents/${call.incident_id}`} className="underline hover:text-indigo-300">
{call.incident_id.slice(0, 8)}
</a>
</p>
{/* Incident links — one per scene detected in the recording */}
{incidentIds.length > 0 && (
<div className="flex flex-wrap gap-x-3 gap-y-0.5 text-xs font-mono text-indigo-400">
<span className="text-gray-600">{incidentIds.length === 1 ? "Incident:" : "Incidents:"}</span>
{incidentIds.map((id) => (
<a key={id} href={`/incidents/${id}`} className="underline hover:text-indigo-300">
{id.slice(0, 8)}
</a>
))}
</div>
)}
{/* Transcript */}
+6
View File
@@ -94,6 +94,12 @@ export const c2api = {
reissueNodeKey: (nodeId: string) =>
request(`/nodes/${nodeId}/reissue-key`, { method: "POST" }),
// Ten-codes
getTenCodes: (systemId: string) =>
request<{ ten_codes: Record<string, string> }>(`/systems/${systemId}/ten-codes`),
updateTenCodes: (systemId: string, ten_codes: Record<string, string>) =>
request(`/systems/${systemId}/ten-codes`, { method: "PUT", body: JSON.stringify({ ten_codes }) }),
// Vocabulary
getVocabulary: (systemId: string) =>
request<{ vocabulary: string[]; vocabulary_pending: { term: string; source: "induction" | "correction"; added_at: string }[]; vocabulary_bootstrapped: boolean }>(
+5 -1
View File
@@ -27,6 +27,7 @@ export interface SystemRecord {
vocabulary?: string[];
vocabulary_pending?: VocabularyPendingTerm[];
vocabulary_bootstrapped?: boolean;
ten_codes?: Record<string, string>; // {"10-10": "Commercial Alarm", ...}
}
export interface TranscriptSegment {
@@ -48,7 +49,10 @@ export interface CallRecord {
transcript: string | null;
transcript_corrected: string | null;
segments: TranscriptSegment[] | null;
incident_id: string | null;
/** New: one entry per scene detected in the recording. */
incident_ids: string[];
/** Legacy field — present on calls recorded before the multi-scene migration. */
incident_id?: string | null;
location: string | null;
tags: string[];
status: "active" | "ended";