2f0597c81b
Includes c2-core (FastAPI/MQTT/Firestore), discord-bot (slash commands), frontend (Next.js admin UI), and mosquitto config.
94 lines
3.6 KiB
TypeScript
94 lines
3.6 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { useCalls } from "@/lib/useCalls";
|
|
import { useSystems } from "@/lib/useSystems";
|
|
import { CallRow } from "@/components/CallRow";
|
|
|
|
export default function CallsPage() {
|
|
const [limitCount, setLimitCount] = useState(100);
|
|
const { calls, loading } = useCalls(limitCount);
|
|
const { systems } = useSystems();
|
|
const systemMap = Object.fromEntries(systems.map((s) => [s.system_id, s]));
|
|
|
|
const active = calls.filter((c) => c.status === "active");
|
|
const ended = calls.filter((c) => c.status === "ended");
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<h1 className="text-xl font-bold text-white font-mono">Calls</h1>
|
|
<span className="text-xs text-gray-500 font-mono">{calls.length} loaded</span>
|
|
</div>
|
|
|
|
{active.length > 0 && (
|
|
<section>
|
|
<h2 className="text-sm font-semibold text-orange-400 uppercase tracking-wider mb-3">
|
|
Live ({active.length})
|
|
</h2>
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full text-sm">
|
|
<thead>
|
|
<tr className="text-xs text-gray-500 uppercase tracking-wider border-b border-gray-800">
|
|
<th className="px-4 py-2 text-left">Time</th>
|
|
<th className="px-4 py-2 text-left">Talkgroup</th>
|
|
<th className="px-4 py-2 text-left">System</th>
|
|
<th className="px-4 py-2 text-left">Node</th>
|
|
<th className="px-4 py-2 text-left">Duration</th>
|
|
<th className="px-4 py-2 text-left">Audio</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{active.map((c) => (
|
|
<CallRow key={c.call_id} call={c} systemName={systemMap[c.system_id ?? ""]?.name} />
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
<section>
|
|
<h2 className="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-3">
|
|
History
|
|
</h2>
|
|
{loading ? (
|
|
<p className="text-gray-600 text-sm font-mono">Loading…</p>
|
|
) : ended.length === 0 ? (
|
|
<p className="text-gray-600 text-sm font-mono">No calls recorded yet.</p>
|
|
) : (
|
|
<>
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full text-sm">
|
|
<thead>
|
|
<tr className="text-xs text-gray-500 uppercase tracking-wider border-b border-gray-800">
|
|
<th className="px-4 py-2 text-left">Time</th>
|
|
<th className="px-4 py-2 text-left">Talkgroup</th>
|
|
<th className="px-4 py-2 text-left">System</th>
|
|
<th className="px-4 py-2 text-left">Node</th>
|
|
<th className="px-4 py-2 text-left">Duration</th>
|
|
<th className="px-4 py-2 text-left">Audio</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{ended.map((c) => (
|
|
<CallRow key={c.call_id} call={c} systemName={systemMap[c.system_id ?? ""]?.name} />
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{ended.length >= limitCount && (
|
|
<button
|
|
onClick={() => setLimitCount((n) => n + 100)}
|
|
className="mt-4 text-sm text-indigo-400 hover:text-indigo-300 font-mono transition-colors"
|
|
>
|
|
Load more
|
|
</button>
|
|
)}
|
|
</>
|
|
)}
|
|
</section>
|
|
</div>
|
|
);
|
|
}
|