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:
@@ -155,6 +155,14 @@ export function ActiveSessions({ onPick }: { onPick: (identity: string) => void
|
||||
<span className="text-term-text">
|
||||
{s.subscription ? `★ ${s.subscriptionHolder ?? t("subs.unnamed")}` : s.identity}
|
||||
</span>
|
||||
{s.plate && (
|
||||
<span
|
||||
className="rounded border border-term-border px-1 font-semibold tracking-wide text-term-amber"
|
||||
title={t("booth.plateTitle")}
|
||||
>
|
||||
{s.plate}
|
||||
</span>
|
||||
)}
|
||||
<span className="text-term-muted">{formatRelativeDateTime(s.enteredAt, t)}</span>
|
||||
<span className="text-term-muted">{formatDuration(s.enteredAt, new Date().toISOString())}</span>
|
||||
<span
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -762,6 +762,8 @@ export interface SessionLookup {
|
||||
subscription: boolean;
|
||||
subscriptionId: string | null;
|
||||
subscriptionHolder: string | null;
|
||||
/** Advisory licence plate recognized for this session (ANPR). Null when none. */
|
||||
plate: string | null;
|
||||
}
|
||||
|
||||
/** Look up a ticket/session for the booth modal (entry/exit, paid, amount owed). */
|
||||
@@ -789,6 +791,8 @@ export interface ActiveSession {
|
||||
subscription: boolean;
|
||||
subscriptionId: string | null;
|
||||
subscriptionHolder: string | null;
|
||||
/** Advisory licence plate recognized for this session (ANPR). Null when none. */
|
||||
plate: string | null;
|
||||
}
|
||||
|
||||
/** Active sessions: still-inside OR exited-but-within-grace (barrier unconfirmed). */
|
||||
|
||||
@@ -106,6 +106,7 @@ export const en: Catalog = {
|
||||
badgeOverstay: "overstay",
|
||||
badgeOverstayTitle:
|
||||
"Paid session. The customer failed to exit during the grace period. A new period began.",
|
||||
plateTitle: "Licence plate recognized by the camera (advisory — not an access decision).",
|
||||
// filters
|
||||
filterSearchSessions: "Search ticket / subscriber / plate…",
|
||||
filterSearchFeed: "Search event / identity / plate…",
|
||||
|
||||
@@ -108,6 +108,7 @@ export const sq = {
|
||||
badgeOverstay: "tej afatit",
|
||||
badgeOverstayTitle:
|
||||
"Sesion i paguar. Klienti nuk doli brënda afatit kohor. Ka filluar një periudhë e re tarifimi.",
|
||||
plateTitle: "Targa e njohur nga kamera (orientuese — nuk është vendim aksesi).",
|
||||
// filtra
|
||||
filterSearchSessions: "Kërko biletë / abonent / targë…",
|
||||
filterSearchFeed: "Kërko event / identitet / targë…",
|
||||
|
||||
Reference in New Issue
Block a user