The audit-log table carries `trace_id` + `actor_id_hash` columns
already populated by the BFF (ADR-0013) but they were plain text —
an investigator could see the values but had to copy them out of the
table to do anything with them. This PR turns both into interactive
affordances:
* **`trace_id` becomes a Jaeger deep link.** Each non-null
`traceId` renders as `<a target="_blank" rel="noopener
noreferrer">` pointing at `${jaegerBaseUrl}/trace/<id>`. The
base URL is per-env in `apps/portal-admin/src/environments/
environment.ts` (dev: `localhost:16686` matching the compose
observability profile; prod: the future infra ADR will swap to
whatever trace backend is settled on — Tempo, Grafana Cloud,
on-prem Jaeger…). Anonymous events (no traceId) keep the dash
placeholder. Closes the "join audit + traces by trace_id"
promise of ADR-0012 / ADR-0013 with the lowest-coupling option
available today (no new BFF endpoint, no log aggregator
required).
* **`actor_id_hash` becomes a click-to-filter button.** Each
non-null hash renders as a `<button>` styled to read inline
like the hash text but with a hover affordance + focus ring +
underline. Clicking it sets `actorIdHash.set(hash)`, resets
the offset to 0, and re-runs the query — pivoting the table on
that single actor's events. Anonymous rows keep the
`(anonymous)` plain-text rendering since there's no filter
value to apply. Each pivot still emits its own
`admin.audit.query` audit row server-side (ADR-0020) so the
drill is auditable.
Why not inline-expand Pino logs under the row? Considered, deferred
to a future chantier — the BFF's Pino output goes to stdout today
with no queryable backend; standing up a log aggregator (Loki /
OpenSearch / …) is a separate infra ADR. Jaeger jump-off carries
~99 % of the investigator's needs because the trace already
contains span attributes (db.statement, http.status_code, exception
events) for the same scope.
Implementation:
* `audit.ts` gains `jaegerUrl(traceId)` (builds the URL with
encodeURIComponent) and `filterByActor(hash)` (mutates the
actor filter + offset + re-fetches). Imports
`environment.jaegerBaseUrl`.
* `audit.html` swaps the trace cell to an anchor (or dash) and
the actor cell to a clickable button (or anonymous text),
each with a `title` attribute for hover hint.
* `audit.scss` adds `.actor-hash--clickable` (button reset +
dotted-underline hover) and `.trace-link` (brand-coloured
underlined link), both with focus rings + dark-mode swaps.
Verification:
* 54 portal-admin specs pass (was 50; +4 for the four new
behaviours: trace anchor + dash + actor pivot + anonymous
plain text).
* `pnpm nx build portal-admin` clean; lazy `audit` chunk grows
marginally (4.36 → 4.44 KB gzip) — well under the per-chunk
budget.