feat: show recognized plate in live feed + active sessions

Surface the advisory ANPR plate (device_events kind="read", keyed by
session identity — unsigned, prunable, never an access decision) next to
entry/exit events in the live feed and on active-session rows.

Resolved at serialize time (new plate-lookup.ts; prefers an entry read;
one device_events scan per page) like subscriber-name enrichment — the
signed ledger is untouched. Adds plate? to the shared LedgerEvent and to
ActiveSession/SessionLookup; a small amber badge in the UI.

Caveat: a vehicle_entry is signed + pushed over WS before the async ANPR
read lands, so a fresh feed row may show no plate until reload; always
present on active sessions.

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
2026-06-20 15:45:57 +02:00
parent b0c9ba0f8c
commit cdb55a8652
10 changed files with 171 additions and 11 deletions
+11 -1
View File
@@ -165,7 +165,17 @@ function EventRow({ e, onOpen }: { e: LedgerEvent; onOpen: (e: LedgerEvent) => v
>
<span className="text-term-muted">{hhmmss(e.occurredAt)}</span>
<span className={`shrink-0 font-semibold ${style?.color ?? "text-term-text"}`}>{label}</span>
<span className="truncate text-term-text">{displayIdentity(e)}</span>
<span className="flex min-w-0 items-center gap-2">
<span className="truncate text-term-text">{displayIdentity(e)}</span>
{e.plate && (
<span
className="shrink-0 rounded border border-term-border px-1 text-[11px] font-semibold tracking-wide text-term-amber"
title={t("booth.plateTitle")}
>
{e.plate}
</span>
)}
</span>
<span className="text-term-muted">#{e.index}</span>
{showDetail && (
<div className="col-start-3 col-end-5 flex flex-wrap items-center gap-x-2 gap-y-1">