fix(frontend): finish the token migration — raw grey classes -> theme tokens
UI_REDESIGN chunk 4 was never finished: 39 app/component files still used raw Tailwind greys, kept readable in light mode only by the !important override sheet. In dark mode text-gray-600 is ~2.3:1 on surface, and the light override maps it to #94a3b8 (~2.6:1 on white) — unreadable in both. Mechanical mapping (886 classes): gray-100/200 -> ink, 300/400 -> ink-2, 500/600/700 -> ink-muted, bg 950/900/800 -> page/surface/raised, border 800 -> line, 700/600 -> line-strong, plus hover/divide/placeholder. Alpha suffixes kept (tokens are color-mix, so /60 works). bg-gray-700, text-white and marketing pages left alone. text-[9px] -> text-[10px]. Frontend unverified: no Node on the authoring box (#207). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
3968b49b80
commit
bf206334e3
@@ -85,17 +85,17 @@ export function CallRow({ call, systemName, isAdmin }: Props) {
|
||||
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"}`}
|
||||
className={`border-b border-line font-mono text-sm ${hasDetails ? "cursor-pointer hover:bg-raised/50" : "hover:bg-raised/30"}`}
|
||||
onClick={() => hasDetails && setExpanded((v) => !v)}
|
||||
>
|
||||
<td className="px-4 py-2 text-gray-400 text-xs whitespace-nowrap">
|
||||
<span className="text-gray-600">{new Date(call.started_at).toLocaleDateString([], { month: "short", day: "numeric" })} </span>
|
||||
<td className="px-4 py-2 text-ink-2 text-xs whitespace-nowrap">
|
||||
<span className="text-ink-muted">{new Date(call.started_at).toLocaleDateString([], { month: "short", day: "numeric" })} </span>
|
||||
{new Date(call.started_at).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}
|
||||
</td>
|
||||
<td className="px-4 py-2 text-gray-300">
|
||||
<td className="px-4 py-2 text-ink-2">
|
||||
<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"}`}>
|
||||
<span className={`ml-2 text-xs px-1.5 py-0.5 rounded-full capitalize ${TAG_COLORS[call.tags[0]] ?? "bg-raised text-ink-2"}`}>
|
||||
{call.tags[0]}
|
||||
</span>
|
||||
)}
|
||||
@@ -104,29 +104,29 @@ export function CallRow({ call, systemName, isAdmin }: Props) {
|
||||
<span className="ml-2">{severityBadge(call.severity)}</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-4 py-2 text-gray-400 hidden sm:table-cell">{systemName ?? call.system_id ?? "—"}</td>
|
||||
<td className="px-4 py-2 text-gray-400 hidden sm:table-cell">{call.node_id}</td>
|
||||
<td className="px-4 py-2 text-ink-2 hidden sm:table-cell">{systemName ?? call.system_id ?? "—"}</td>
|
||||
<td className="px-4 py-2 text-ink-2 hidden sm:table-cell">{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>
|
||||
<span className="text-ink-muted">{duration(call.started_at, call.ended_at)}</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-4 py-2 text-xs">
|
||||
{hasAudio ? (
|
||||
<span className="text-blue-400">▶</span>
|
||||
) : (
|
||||
<span className="text-gray-700">—</span>
|
||||
<span className="text-ink-muted">—</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-4 py-2 text-gray-600 text-xs">
|
||||
<td className="px-4 py-2 text-ink-muted text-xs">
|
||||
{hasDetails && (expanded ? "▲" : "▼")}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{expanded && hasDetails && (
|
||||
<tr className="bg-gray-900/60 border-b border-gray-800">
|
||||
<tr className="bg-surface/60 border-b border-line">
|
||||
<td colSpan={7} className="px-6 py-3 space-y-2">
|
||||
{/* Audio player */}
|
||||
{hasAudio && (
|
||||
@@ -140,7 +140,7 @@ export function CallRow({ call, systemName, isAdmin }: Props) {
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
) : (
|
||||
<p className="text-xs text-gray-600 font-mono">Loading audio…</p>
|
||||
<p className="text-xs text-ink-muted font-mono">Loading audio…</p>
|
||||
)
|
||||
)}
|
||||
|
||||
@@ -150,7 +150,7 @@ export function CallRow({ call, systemName, isAdmin }: Props) {
|
||||
{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"}`}
|
||||
className={`text-xs px-2 py-0.5 rounded-full capitalize ${TAG_COLORS[tag] ?? "bg-raised text-ink-2"}`}
|
||||
>
|
||||
{tag}
|
||||
</span>
|
||||
@@ -161,7 +161,7 @@ export function CallRow({ call, systemName, isAdmin }: Props) {
|
||||
{/* Incident links — one per scene detected in the recording */}
|
||||
{incidentIds.length > 0 && (
|
||||
<div className="flex flex-wrap gap-x-3 gap-y-0.5 text-xs font-mono text-indigo-400">
|
||||
<span className="text-gray-600">{incidentIds.length === 1 ? "Incident:" : "Incidents:"}</span>
|
||||
<span className="text-ink-muted">{incidentIds.length === 1 ? "Incident:" : "Incidents:"}</span>
|
||||
{incidentIds.map((id) => (
|
||||
<a key={id} href={`/incidents/${id}`} className="underline hover:text-indigo-300">
|
||||
{id.slice(0, 8)}…
|
||||
@@ -172,9 +172,9 @@ export function CallRow({ call, systemName, isAdmin }: Props) {
|
||||
|
||||
{/* Correlation debug — admin only */}
|
||||
{isAdmin && call.corr_path && (
|
||||
<div className="flex flex-wrap gap-x-3 gap-y-0.5 text-xs font-mono text-gray-600">
|
||||
<div className="flex flex-wrap gap-x-3 gap-y-0.5 text-xs font-mono text-ink-muted">
|
||||
<span>corr:</span>
|
||||
<span className="text-gray-400">{call.corr_path}</span>
|
||||
<span className="text-ink-2">{call.corr_path}</span>
|
||||
{call.corr_incident_idle_min != null && (
|
||||
<span>idle {call.corr_incident_idle_min}min</span>
|
||||
)}
|
||||
@@ -200,7 +200,7 @@ export function CallRow({ call, systemName, isAdmin }: Props) {
|
||||
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"
|
||||
className="w-full bg-raised border border-line-strong rounded-lg px-3 py-2 text-xs text-ink font-mono focus:outline-none focus:border-indigo-500 resize-none"
|
||||
/>
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<button
|
||||
@@ -212,7 +212,7 @@ export function CallRow({ call, systemName, isAdmin }: Props) {
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); setEditing(false); }}
|
||||
className="text-xs text-gray-500 hover:text-gray-300 px-3 py-1 transition-colors"
|
||||
className="text-xs text-ink-muted hover:text-ink px-3 py-1 transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
@@ -224,23 +224,23 @@ export function CallRow({ call, systemName, isAdmin }: Props) {
|
||||
) : hasSegments ? (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-gray-600 font-mono">
|
||||
<span className="text-xs text-ink-muted font-mono">
|
||||
{hasBoth && !showOriginal ? "corrected" : `${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>
|
||||
<button onClick={(e) => { e.stopPropagation(); startEdit(); }} className="text-xs text-ink-muted hover:text-ink-2 font-mono transition-colors">edit</button>
|
||||
)}
|
||||
</div>
|
||||
{hasBoth && !showOriginal ? (
|
||||
<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-48 overflow-y-auto">
|
||||
<pre className="text-xs text-ink-2 bg-raised rounded-lg px-4 py-3 whitespace-pre-wrap font-mono leading-relaxed max-h-48 overflow-y-auto">
|
||||
{call.transcript_corrected}
|
||||
</pre>
|
||||
) : (
|
||||
<div className="bg-gray-800 rounded-lg px-4 py-3 space-y-2 max-h-48 overflow-y-auto">
|
||||
<div className="bg-raised 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>
|
||||
<span className="text-ink-muted shrink-0">{i + 1}. [{seg.start}s]</span>
|
||||
<span className="text-ink-2">{seg.text}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -248,7 +248,7 @@ export function CallRow({ call, systemName, isAdmin }: Props) {
|
||||
{hasBoth && (
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); setShowOriginal((v) => !v); }}
|
||||
className="text-xs text-gray-600 hover:text-gray-400 font-mono transition-colors"
|
||||
className="text-xs text-ink-muted hover:text-ink-2 font-mono transition-colors"
|
||||
>
|
||||
{showOriginal ? "show corrected ↑" : "show original ↓"}
|
||||
</button>
|
||||
@@ -258,27 +258,27 @@ export function CallRow({ call, systemName, isAdmin }: Props) {
|
||||
<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>}
|
||||
{hasBoth && <span className="text-xs text-ink-muted font-mono">{showOriginal ? "original" : "corrected"}</span>}
|
||||
{!hasBoth && call.transcript_corrected && <span className="text-xs text-ink-muted 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>
|
||||
<button onClick={(e) => { e.stopPropagation(); startEdit(); }} className="text-xs text-ink-muted hover:text-ink-2 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">
|
||||
<pre className="text-xs text-ink-2 bg-raised rounded-lg px-4 py-3 whitespace-pre-wrap font-mono leading-relaxed max-h-40 overflow-y-auto">
|
||||
{displayTranscript}
|
||||
</pre>
|
||||
{hasBoth && (
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); setShowOriginal((v) => !v); }}
|
||||
className="text-xs text-gray-600 hover:text-gray-400 font-mono transition-colors"
|
||||
className="text-xs text-ink-muted hover:text-ink-2 font-mono transition-colors"
|
||||
>
|
||||
{showOriginal ? "show corrected ↑" : "show original ↓"}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-xs text-gray-600 font-mono italic">No transcript available.</p>
|
||||
<p className="text-xs text-ink-muted font-mono italic">No transcript available.</p>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user