UI Updates
This commit is contained in:
@@ -2,10 +2,12 @@
|
||||
|
||||
import { useState } from "react";
|
||||
import type { CallRecord } from "@/lib/types";
|
||||
import { c2api } from "@/lib/c2api";
|
||||
|
||||
interface Props {
|
||||
call: CallRecord;
|
||||
systemName?: string;
|
||||
isAdmin?: boolean;
|
||||
}
|
||||
|
||||
function duration(started: string, ended: string | null): string {
|
||||
@@ -22,13 +24,35 @@ const TAG_COLORS: Record<string, string> = {
|
||||
accident: "bg-orange-900 text-orange-300",
|
||||
};
|
||||
|
||||
export function CallRow({ call, systemName }: Props) {
|
||||
export function CallRow({ call, systemName, isAdmin }: Props) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const [showOriginal, setShowOriginal] = useState(false);
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [editText, setEditText] = useState("");
|
||||
const [saving, setSaving] = useState(false);
|
||||
const isActive = call.status === "active";
|
||||
const hasDetails = call.transcript || call.transcript_corrected || (call.tags && call.tags.length > 0) || call.incident_id;
|
||||
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;
|
||||
|
||||
function startEdit() {
|
||||
setEditText(call.transcript_corrected ?? call.transcript ?? "");
|
||||
setEditing(true);
|
||||
}
|
||||
|
||||
async function saveEdit(e: React.MouseEvent) {
|
||||
e.stopPropagation();
|
||||
setSaving(true);
|
||||
try {
|
||||
await c2api.patchTranscript(call.call_id, editText);
|
||||
setEditing(false);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -104,16 +128,64 @@ export function CallRow({ call, systemName }: Props) {
|
||||
)}
|
||||
|
||||
{/* Transcript */}
|
||||
{displayTranscript ? (
|
||||
{editing ? (
|
||||
<div className="space-y-2" onClick={(e) => e.stopPropagation()}>
|
||||
<textarea
|
||||
value={editText}
|
||||
onChange={(e) => setEditText(e.target.value)}
|
||||
rows={4}
|
||||
className="w-full bg-gray-800 border border-gray-600 rounded-lg px-3 py-2 text-xs text-gray-200 font-mono focus:outline-none focus:border-indigo-500 resize-none"
|
||||
/>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={saveEdit}
|
||||
disabled={saving}
|
||||
className="text-xs bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 text-white px-3 py-1 rounded transition-colors"
|
||||
>
|
||||
{saving ? "Saving…" : "Save & reprocess"}
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); setEditing(false); }}
|
||||
className="text-xs text-gray-500 hover:text-gray-300 px-3 py-1 transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : hasSegments ? (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
{hasBoth && (
|
||||
<span className="text-xs text-gray-600 font-mono">
|
||||
{showOriginal ? "original" : "corrected"}
|
||||
</span>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-gray-600 font-mono">{call.segments!.length} transmissions</span>
|
||||
{isAdmin && (
|
||||
<button onClick={(e) => { e.stopPropagation(); startEdit(); }} className="text-xs text-gray-600 hover:text-gray-400 font-mono transition-colors">edit</button>
|
||||
)}
|
||||
{!hasBoth && call.transcript_corrected && (
|
||||
<span className="text-xs text-gray-600 font-mono">corrected</span>
|
||||
</div>
|
||||
<div className="bg-gray-800 rounded-lg px-4 py-3 space-y-2 max-h-48 overflow-y-auto">
|
||||
{call.segments!.map((seg, i) => (
|
||||
<div key={i} className="flex gap-3 text-xs font-mono">
|
||||
<span className="text-gray-600 shrink-0">{i + 1}. [{seg.start}s]</span>
|
||||
<span className="text-gray-300">{seg.text}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{hasBoth && (
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); setShowOriginal((v) => !v); }}
|
||||
className="text-xs text-gray-600 hover:text-gray-400 font-mono transition-colors"
|
||||
>
|
||||
{showOriginal ? "show corrected ↑" : "show original ↓"}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
) : displayTranscript ? (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
{hasBoth && <span className="text-xs text-gray-600 font-mono">{showOriginal ? "original" : "corrected"}</span>}
|
||||
{!hasBoth && call.transcript_corrected && <span className="text-xs text-gray-600 font-mono">corrected</span>}
|
||||
</div>
|
||||
{isAdmin && (
|
||||
<button onClick={(e) => { e.stopPropagation(); startEdit(); }} className="text-xs text-gray-600 hover:text-gray-400 font-mono transition-colors">edit</button>
|
||||
)}
|
||||
</div>
|
||||
<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">
|
||||
|
||||
Reference in New Issue
Block a user