---
id: w4/m14/t004
title: "`AuditEventRow` + status badge (allowed/denied)"
worker: worker4
status: done
estimate: 30m
depends_on: [w4/m14/t003]
---

## Objective

A single-row presentational component rendering one `AuditEvent` — timestamp, actor, actor method, action (verb), a status badge, and resource — the row-level counterpart of `features/team/components/member-row.tsx`.

## Context

- Precedent: `features/team/components/member-row.tsx` (a `<TableRow>` per item) and `features/api-keys/components/api-key-row.tsx`.
- `status` from the backend is `"success"` | `"denied"` (`internal/audit/graphql.go`'s `renderStatus`) — map to `audit.statusAllowed`/`audit.statusDenied` copy, not the raw wire string. Use the existing `Badge` component (`common/components/ui/badge.tsx`) — success/neutral variant for allowed, destructive variant for denied, matching how other panels signal state (check `Badge` usage in `team-panel.tsx`/`api-key-row.tsx` for the variant convention already established).
- `resource` is an OpenFGA object string (e.g. `workspace:tea-abc`, `service:srv-xyz`) — render as-is (monospace/muted), no parsing needed.
- `actor` may be empty (unauthenticated caller, per `core.AuditEvent`'s doc comment) — render a placeholder, don't leave a blank cell.

## Steps

1. Create `dashboard/src/features/audit/components/audit-event-row.tsx`: `<TableRow>` with cells for timestamp (locale-formatted), actor (+ actorMethod as a small secondary label), action, status badge, resource.
2. Format `timestamp` (RFC3339 string) via the same date-formatting approach already used elsewhere in the dashboard (grep for an existing formatter before adding a new date library dependency).

## Files

- `dashboard/src/features/audit/components/audit-event-row.tsx` (new)

## Acceptance criteria

- [ ] Renders all six fields; denied events are visually distinct from allowed ones.
- [ ] Empty `actor` renders a placeholder, not a blank cell.

## Out of scope

- The table shell / list container (t005).
