Make Archive a real page instead of a redirect
/calls was a ten-line stub that redirected to /incidents, so there was nowhere in the app to look at a call. The nav's "Archive" link led to the incident list, and a call that never correlated was invisible entirely -- which is backwards when correlation quality is the thing under development, because the orphans are the evidence. Its stated blocker (Gitea #17/#18) closed weeks ago. The page browses the org's calls newest-first over the new /calls/search route, filtered by link state (all / orphans / linked), transcript presence, and system, with a transcript substring search and cursor paging. A row expands to the full transcript, a playback link minted on demand, and the correlation path that decided it. The counts line -- how many of the loaded calls are orphaned, how many have no transcript at all -- is the number worth watching during an AI window. Attribution is the point of it: attach an orphan to the incident it belongs to, or detach one the correlator got wrong. Both go through the routes fixed in the previous commit, so a manual attachment now actually shows up on the incident. Admin-only. It exposes every call in the org regardless of node ownership and carries controls that rewrite incident membership. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
140dfbfc74
commit
457e6d7e0f
@@ -67,6 +67,33 @@ export const c2api = {
|
||||
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
||||
return request<unknown[]>(`/calls${qs}`);
|
||||
},
|
||||
/**
|
||||
* Paged, filterable call archive — backs the /calls page. Distinct from
|
||||
* getCalls(), which returns every call unordered and cannot page.
|
||||
* `next_cursor` is null when the scan reached the end of the collection.
|
||||
*/
|
||||
searchCalls: (params: {
|
||||
limit?: number;
|
||||
cursor?: string | null;
|
||||
system_id?: string;
|
||||
node_id?: string;
|
||||
talkgroup_id?: number;
|
||||
link?: "any" | "orphan" | "linked";
|
||||
transcript?: "any" | "yes" | "no";
|
||||
q?: string;
|
||||
}) => {
|
||||
const qs = new URLSearchParams();
|
||||
for (const [k, v] of Object.entries(params)) {
|
||||
if (v !== undefined && v !== null && v !== "") qs.set(k, String(v));
|
||||
}
|
||||
return request<{
|
||||
calls: import("@/lib/types").CallRecord[];
|
||||
next_cursor: string | null;
|
||||
scanned: number;
|
||||
matched: number;
|
||||
window_exhausted: boolean;
|
||||
}>(`/calls/search?${qs.toString()}`);
|
||||
},
|
||||
patchTranscript: (callId: string, transcript: string) =>
|
||||
request(`/calls/${callId}/transcript`, { method: "PATCH", body: JSON.stringify({ transcript }) }),
|
||||
closeStallCalls: (olderThanMinutes: number, dryRun: boolean) =>
|
||||
@@ -85,7 +112,11 @@ export const c2api = {
|
||||
deleteIncident: (id: string) =>
|
||||
request(`/incidents/${id}`, { method: "DELETE" }),
|
||||
linkCallToIncident: (incidentId: string, callId: string) =>
|
||||
request(`/incidents/${incidentId}/calls/${callId}`, { method: "POST" }),
|
||||
request<{ ok: boolean; incident_ids: string[] }>(
|
||||
`/incidents/${incidentId}/calls/${callId}`, { method: "POST" }),
|
||||
unlinkCallFromIncident: (incidentId: string, callId: string) =>
|
||||
request<{ ok: boolean; incident_emptied: boolean }>(
|
||||
`/incidents/${incidentId}/calls/${callId}`, { method: "DELETE" }),
|
||||
summarizeIncident: (id: string) =>
|
||||
request(`/incidents/${id}/summarize`, { method: "POST" }),
|
||||
|
||||
|
||||
Reference in New Issue
Block a user