2f0597c81b
Includes c2-core (FastAPI/MQTT/Firestore), discord-bot (slash commands), frontend (Next.js admin UI), and mosquitto config.
52 lines
1.6 KiB
TypeScript
52 lines
1.6 KiB
TypeScript
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`;
|
|
}
|
|
|
|
export function CallRow({ call, systemName }: Props) {
|
|
const isActive = call.status === "active";
|
|
|
|
return (
|
|
<tr className="border-b border-gray-800 hover:bg-gray-900/50 font-mono text-sm">
|
|
<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">
|
|
{call.talkgroup_name || call.talkgroup_id || "—"}
|
|
</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"
|
|
className="text-blue-400 hover:text-blue-300 text-xs"
|
|
>
|
|
audio
|
|
</a>
|
|
) : (
|
|
<span className="text-gray-700 text-xs">—</span>
|
|
)}
|
|
</td>
|
|
</tr>
|
|
);
|
|
}
|