Build an Audit Log Viewer widget providing a searchable, filterable DataGrid with actor, action, resource, and timestamp columns. The widget supports payload preview in a detail panel, export capability, and standardized loading/empty/error states. It complements the existing activity-feed-widget.tsx by providing a structured, tabular audit format for compliance and debugging use cases.
Introduction
Overview
SaaS applications need audit logs for compliance, debugging, and security investigation. The design system provides ActivityFeedWidget for informal, chronological activity feeds, but lacks a structured tabular audit log with search, filtering, column sorting, and payload inspection. Teams building admin panels, compliance dashboards, and incident review tools need this pattern.
Goals
Provide an AuditLogViewer widget with actor, action, resource, timestamp columns as a structured DataGrid.
Support full-text search across actor, action, and resource fields.
Support column-level filtering (action type, actor, date range).
Provide a detail panel for payload/diff preview when a row is selected.
Support export (CSV/JSON) of visible or selected log entries.
Handle loading, empty, and error states.
Non-Goals
Log ingestion or storage (backend concern).
Real-time streaming of new log entries (future enhancement).
Log retention policy management.
Scope
In Scope
Item
Description
AuditLogViewer widget
DataGrid-based table with actor, action, resource, timestamp, and optional metadata columns.
Search
Full-text search across actor name, action type, and resource identifier.
Filters
Action type filter (dropdown), actor filter (search/select), date range filter (DateRangePicker).
Detail panel
SlideOutPanel or expandable row showing the full event payload as formatted JSON or key-value pairs.
Export
”Export” button exporting visible rows as CSV or JSON; consumer provides the export handler.
State handling
Loading skeleton, empty state, error state with retry.
Stories and tests
Storybook stories for all states; unit tests for search, filter, and detail panel.
Out of Scope
Item
Reason
Log ingestion
Backend concern; widget consumes an array of log entries.
Real-time streaming
Future enhancement; initial version works with a static or periodically refreshed dataset.
Log retention/archival
Administrative concern outside the widget.
Users and Pain Points
User
Pain Point
Compliance officers
Need structured audit logs for SOC 2, GDPR, and HIPAA compliance reviews; current ActivityFeedWidget is informal.
DevOps engineers
Debugging production incidents requires searching through structured logs by actor, action, and time range.
Security teams
Investigating access patterns requires filtering by actor and action type with payload inspection.
Admin panel developers
Build custom audit log tables from scratch for every SaaS product.
Definitions
Term
Definition
Audit log entry
A structured record of a system event: who (actor), what (action), on which resource, when (timestamp), and optionally what changed (payload).
Actor
The user or system that performed the action (name, email, avatar).
Action
The type of event (e.g., “user.created”, “key.revoked”, “role.updated”).
Resource
The entity affected by the action (type + identifier, e.g., “API Key: production-key-1”).
Payload
The structured data associated with the event (e.g., before/after diff, request parameters).
Current State
ActivityFeedWidget (src/components/widgets/activity-feed-widget.tsx): Informal chronological feed with avatar, event description, and timestamp. Supports max items, refresh, and loading state. Not structured for search/filter/export.
DataGrid (src/components/ui/data-grid.tsx): Full-featured table with sort, filter, paginate, row selection.
ListToolbar: Toolbar with search and filter controls.
DateRangePicker: Available for date range filtering.
SlideOutPanel: Available for detail panel display.
A toolbar search input filters across actor name, action type, resource identifier, and resource type. Debounced at 200ms.
Filters
Action type: Multi-select dropdown with consumer-provided action types.
Actor: Searchable select with consumer-provided actor list.
Date range: DateRangePicker with preset ranges (Last hour, Last 24h, Last 7 days, Custom).
Status: Success/failure toggle.
Detail Panel
Clicking a row opens a SlideOutPanel showing:
Full event metadata (actor, action, resource, timestamp, IP address, user agent).
Payload as formatted JSON with syntax highlighting.
Optional before/after diff view for update events.
Export
An “Export” button in the toolbar triggers onExport(format, entries) callback. The widget provides the visible/filtered entries; the consumer handles file generation.
Requirements
Requirement Priorities
Must Have: Table with core columns, search, sort, loading/empty/error states.
Should Have: Action type and date range filters, detail panel, export.
Could Have: Actor filter, before/after diff view, column customization.
Functional Requirements
ID
Requirement
Priority
FR-01
Widget renders a DataGrid with timestamp, actor, action, resource, and status columns.
Must
FR-02
Timestamp column sorts descending by default; all columns support sorting.
Must
FR-03
Search input filters across actor, action, and resource fields with 200ms debounce.
Must
FR-04
Action type multi-select filter narrows visible entries.
Should
FR-05
Date range filter using DateRangePicker with preset ranges.
Should
FR-06
Clicking a row opens a detail panel showing full event metadata and payload.
Should
FR-07
Payload renders as formatted JSON with monospace font.
Should
FR-08
Export button triggers onExport callback with format and visible entries.
Should
FR-09
Loading state renders skeleton rows.
Must
FR-10
Empty state renders “No audit log entries” with configurable message.
Must
FR-11
Error state renders error message with retry button.
Must
FR-12
Action type badges are color-coded by category (consumer provides category-color mapping).
Should
Non-Functional Requirements
ID
Requirement
Target
NFR-01
Renders 500 log entries in < 100ms.
Measured via React Profiler.
NFR-02
Bundle size
< 8 KB gzipped (excluding shared DataGrid and SlideOutPanel deps).
NFR-03
Search responsiveness
< 50ms perceived filter latency after debounce.
NFR-04
Dark mode
Full token-based dark mode support.
NFR-05
Payload display
Formatted JSON with proper indentation; no horizontal overflow.
API/Interface Requirements
interface AuditLogEntry {
id:string;
timestamp:string; // ISO 8601 datetime
actor: {
id:string;
name:string;
email?:string;
avatarSrc?:string;
};
action:string; // e.g. "user.created", "key.revoked"
actionCategory?:string; // e.g. "auth", "billing", "admin"
resource: {
type:string; // e.g. "API Key", "User", "Project"
id:string;
label?:string; // human-readable name
};
status?:"success"|"failure";
payload?:Record<string, unknown>;
metadata?:Record<string, string>; // IP, user agent, etc.
}
interface AuditLogViewerProps {
entries:AuditLogEntry[];
loading?:boolean;
error?:string;
onRetry?:()=>void;
actionTypes?:string[]; // available action types for filter
actionCategoryColors?:Record<string, string>; // category → badge color
DataGrid follows proper grid semantics with sortable column headers.
A11Y-02
Detail panel is announced as a dialog/region and traps focus.
A11Y-03
Search input has aria-label="Search audit logs".
A11Y-04
Filter controls are keyboard accessible and announce their current state.
A11Y-05
Status badges include aria-label for screen readers (not just color-coded).
A11Y-06
All components pass axe-core automated checks with zero violations.
Content and Documentation Requirements
ID
Requirement
DOC-01
Storybook docs page with usage guidelines, prop table, and interactive examples.
DOC-02
Recipe showing AuditLogViewer with TanStack Query for paginated server-side log fetching.
DOC-03
Guide on structuring audit log entries for consistency across services.
Dependencies
Dependency
Type
Risk
src/components/ui/data-grid.tsx
Internal
Low — core table primitive.
src/components/ui/list-toolbar.tsx
Internal
Low — search and filter toolbar.
src/components/ui/slide-out-panel.tsx
Internal
Low — detail panel.
src/components/ui/date-range-picker.tsx
Internal
Low — date range filter.
src/components/ui/badge.tsx
Internal
Low — action type badges.
src/components/ui/avatar.tsx
Internal
Low — actor display.
src/components/ui/status-card.tsx
Internal
Low — empty/error states.
Risks and Tradeoffs
Risk
Likelihood
Impact
Mitigation
Large log datasets (10K+ entries) cause client-side performance issues
Medium
High
Document that server-side pagination is recommended for 1K+ entries; provide onPageChange callback pattern.
Payload JSON rendering is slow for deeply nested objects
Low
Medium
Limit initial render depth; expand on demand.
Action type taxonomy varies widely across consumer applications
Medium
Low
Consumer provides their own action types; widget is taxonomy-agnostic.
Open Questions
#
Question
Owner
Status
OQ-01
Should the detail panel support a before/after diff view for update events?
David Holmes
Open
OQ-02
Should the widget support server-side pagination via onPageChange callback?
David Holmes
Open
OQ-03
Should the export handle file generation or just pass data to the consumer?
David Holmes
Open
Acceptance Criteria
#
Criterion
AC-01
Widget renders a table with timestamp, actor, action, resource, and status columns.
AC-02
Search filters entries across actor, action, and resource fields.
AC-03
Action type and date range filters narrow visible entries.
AC-04
Clicking a row opens a detail panel with full event metadata and formatted payload.
AC-05
Export button triggers the onExport callback with the current visible entries.
AC-06
Loading, empty, and error states render appropriate visual treatments.
AC-07
All components pass axe-core checks with zero violations.
AC-08
Storybook stories exist for all states and interaction flows.
AC-09
pnpm typecheck and pnpm vitest run --project unit pass with zero errors.
LLM Handoff Instructions
When implementing this FRD:
Createsrc/components/widgets/audit-log-viewer.tsx. Follow the widget composition pattern from activity-feed-widget.tsx and people-table.tsx.
DataGrid columns: Timestamp (relative time cell), Actor (Avatar + name + email cell), Action (Badge cell), Resource (type + label cell), Status (Badge cell).
Toolbar: Use ListToolbar with search input. Add action type multi-select and date range filter as additional toolbar items.
Detail panel: Use SlideOutPanel. Render metadata as a key-value list and payload as a <pre> with monospace font and proper indentation (JSON.stringify(payload, null, 2)).
Export: Toolbar button triggers onExport(format, visibleEntries). The consumer generates the file.
State handling: Same pattern as ResourceTable — loading skeleton, empty StatusCard, error StatusCard with retry.