Files
server-26/drb-frontend/components/CallRow.tsx
T
2026-04-11 13:44:08 -04:00

117 lines
4.1 KiB
TypeScript

"use client";
import { useState } from "react";
import type { CallRecord } from "@/lib/types";
interface Props {
call: CallRecord;
systemName?: string;
}
function duration(started: string, ended: string | null): string {
if (!ended) return "active";
const ms = new Date(ended).getTime() - new Date(started).getTime();
const s = Math.floor(ms / 1000);
return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m ${s % 60}s`;
}
const TAG_COLORS: Record<string, string> = {
fire: "bg-red-900 text-red-300",
police: "bg-blue-900 text-blue-300",
ems: "bg-yellow-900 text-yellow-300",
accident: "bg-orange-900 text-orange-300",
};
export function CallRow({ call, systemName }: Props) {
const [expanded, setExpanded] = useState(false);
const isActive = call.status === "active";
const hasDetails = call.transcript || (call.tags && call.tags.length > 0) || call.incident_id;
return (
<>
<tr
className={`border-b border-gray-800 font-mono text-sm ${hasDetails ? "cursor-pointer hover:bg-gray-900/50" : "hover:bg-gray-900/30"}`}
onClick={() => hasDetails && setExpanded((v) => !v)}
>
<td className="px-4 py-2 text-gray-400 text-xs">
{new Date(call.started_at).toLocaleTimeString()}
</td>
<td className="px-4 py-2 text-gray-300">
<span>{call.talkgroup_name || call.talkgroup_id || "—"}</span>
{call.tags && call.tags.length > 0 && (
<span className={`ml-2 text-xs px-1.5 py-0.5 rounded-full capitalize ${TAG_COLORS[call.tags[0]] ?? "bg-gray-800 text-gray-400"}`}>
{call.tags[0]}
</span>
)}
</td>
<td className="px-4 py-2 text-gray-400">{systemName ?? call.system_id ?? "—"}</td>
<td className="px-4 py-2 text-gray-400">{call.node_id}</td>
<td className="px-4 py-2">
{isActive ? (
<span className="text-orange-400 animate-pulse"> live</span>
) : (
<span className="text-gray-500">{duration(call.started_at, call.ended_at)}</span>
)}
</td>
<td className="px-4 py-2">
{call.audio_url ? (
<a
href={call.audio_url}
target="_blank"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()}
className="text-blue-400 hover:text-blue-300 text-xs"
>
audio
</a>
) : (
<span className="text-gray-700 text-xs"></span>
)}
</td>
<td className="px-4 py-2 text-gray-600 text-xs">
{hasDetails && (expanded ? "▲" : "▼")}
</td>
</tr>
{expanded && hasDetails && (
<tr className="bg-gray-900/60 border-b border-gray-800">
<td colSpan={7} className="px-6 py-3 space-y-2">
{/* Tags */}
{call.tags && call.tags.length > 0 && (
<div className="flex flex-wrap gap-1">
{call.tags.map((tag) => (
<span
key={tag}
className={`text-xs px-2 py-0.5 rounded-full capitalize ${TAG_COLORS[tag] ?? "bg-gray-800 text-gray-400"}`}
>
{tag}
</span>
))}
</div>
)}
{/* Incident link */}
{call.incident_id && (
<p className="text-xs font-mono text-indigo-400">
Incident:{" "}
<a href="/incidents" className="underline hover:text-indigo-300">
{call.incident_id.slice(0, 8)}
</a>
</p>
)}
{/* Transcript */}
{call.transcript ? (
<pre className="text-xs text-gray-300 bg-gray-800 rounded-lg px-4 py-3 whitespace-pre-wrap font-mono leading-relaxed max-h-40 overflow-y-auto">
{call.transcript}
</pre>
) : (
<p className="text-xs text-gray-600 font-mono italic">No transcript available.</p>
)}
</td>
</tr>
)}
</>
);
}