Updates to intel and correlation
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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 */}
|
||||
|
||||
@@ -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 }>(
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user