---
id: w4/m14/t005
title: "`AuditLogPanel`: Card+Table shell, loading/empty/error/unavailable states, load-more, admin-only"
worker: worker4
status: done
estimate: 45m
depends_on: [w4/m14/t002, w4/m14/t004]
---

## Objective

The panel component that assembles t002's hook + t004's row into a `Card` matching `TeamPanel`/`ApiKeysPanel`'s shape, handling every state the hook can report.

## Context

- Precedent: `features/team/components/team-panel.tsx` — `Card` > `CardHeader` (title/description) > `CardContent` with a `Table`, using `PanelCenteredState`/`PanelTableSkeleton` (`common/components/panel-states`) for loading/empty/error, and hiding admin-only chrome when `canManage` is false.
- This panel is **entirely** admin-gated (unlike Team, which shows a read-only list to non-admins) — per t002, a `forbidden` result means render nothing (`return null`), not an empty/error card. Confirm this against Render's actual behavior in t007; a non-admin dashboard user should not even see an empty "Audit Log" card.
- `unavailable` (503, store not wired) is a distinct state from `error` — use `PanelCenteredState` with copy explaining the feature isn't configured, not a generic error message.
- Pagination: no existing "load more" pattern exists elsewhere in this dashboard to reuse (checked — nothing currently implements cursor pagination); keep it simple: a `Button` below the table, `disabled` while `loadingMore`, hidden when `!hasMore`.

## Steps

1. Create `dashboard/src/features/audit/components/audit-log-panel.tsx`.
2. Wire `useCurrentWorkspace()` → `workspaceId` → `useAuditLog(workspaceId)`.
3. Render: `null` if `forbidden`; `PanelTableSkeleton` while initial `loading`; `PanelCenteredState` for `unavailable`/`error`; empty state when `events.length === 0`; otherwise a `Table` of `AuditEventRow`s + the load-more `Button`.

## Files

- `dashboard/src/features/audit/components/audit-log-panel.tsx` (new)

## Acceptance criteria

- [ ] Non-admin (`forbidden`) sees nothing.
- [ ] Store-less (`unavailable`) sees an explanatory state, not a crash or generic error.
- [ ] Admin with events sees a newest-first table; "Load more" appends further pages and disappears when exhausted.

## Out of scope

- Mounting the panel on a route (t006).
