A filterable event table with type badges, actors, IP addresses and detail affordances.
| Event | Actor | IP address | Timestamp | Details |
|---|---|---|---|---|
Membersmember.role_updated Felix Weber · Viewer → Admin | Ingrid Solberg | 172.64.18.24 | Jul 13, 14:32 | |
api_key.rotated prod-billing-service | Marcus Chen | 198.51.100.77 | Jul 13, 11:05 | |
Authauth.login_blocked 5 failed attempts · jonah.kaplan@meridianhq.com | Meridian Security | 203.0.113.42 | Jul 13, 09:47 | |
Datadata.export_created Contacts · 12,408 rows · CSV | Amara Diallo | 92.118.30.6 | Jul 12, 17:21 | |
auth.new_device Chrome on macOS · São Paulo, Brazil | Rosa Delgado | 198.51.100.9 | Jul 12, 10:12 | |
Securityworkspace.2fa_enforced All members · grace period ends July 19 | Ingrid Solberg | 172.64.18.24 | Jul 12, 08:30 | |
API keyswebhook.deleted hooks.legacy.meridianhq.com | Marcus Chen | 198.51.100.77 | Jul 11, 16:44 |
Install with the CLI, or add the packages and paste the source below.
Add Audit log to your app with its source-owned dependencies.
npx cronus-ui add audit-log
Install the Cronus UI packages, then paste the block below.
Copy the source for Filterable table. Every class is a semantic token, so it re-themes with your app.
import {Avatar,AvatarFallback,Badge,Button,Card,CardAction,CardContent,CardDescription,CardFooter,CardHeader,CardTitle,Input,Select,SelectContent,SelectItem,SelectTrigger,SelectValue,Separator,Table,TableBody,TableCell,TableHead,TableHeader,TableRow,} from "@cronus-ui/ui";import { ChevronRight, Download, Search } from "lucide-react";interface AuditRow {id: string;event: string;detail: string;type: string;typeVariant: "info" | "success" | "warning" | "destructive" | "secondary";actor: string;initials: string;ip: string;time: string;}const auditRows: AuditRow[] = [{ id: "row-901", event: "member.role_updated", detail: "Felix Weber · Viewer → Admin", type: "Members", typeVariant: "info", actor: "Ingrid Solberg", initials: "IS", ip: "172.64.18.24", time: "Jul 13, 14:32" },{ id: "row-897", event: "api_key.rotated", detail: "prod-billing-service", type: "API keys", typeVariant: "secondary", actor: "Marcus Chen", initials: "MC", ip: "198.51.100.77", time: "Jul 13, 11:05" },{ id: "row-894", event: "auth.login_blocked", detail: "5 failed attempts · jonah.kaplan@meridianhq.com", type: "Auth", typeVariant: "destructive", actor: "Meridian Security", initials: "MS", ip: "203.0.113.42", time: "Jul 13, 09:47" },{ id: "row-882", event: "data.export_created", detail: "Contacts · 12,408 rows · CSV", type: "Data", typeVariant: "warning", actor: "Amara Diallo", initials: "AD", ip: "92.118.30.6", time: "Jul 12, 17:21" },{ id: "row-871", event: "auth.new_device", detail: "Chrome on macOS · São Paulo, Brazil", type: "Auth", typeVariant: "secondary", actor: "Rosa Delgado", initials: "RD", ip: "198.51.100.9", time: "Jul 12, 10:12" },{ id: "row-868", event: "workspace.2fa_enforced", detail: "All members · grace period ends July 19", type: "Security", typeVariant: "success", actor: "Ingrid Solberg", initials: "IS", ip: "172.64.18.24", time: "Jul 12, 08:30" },{ id: "row-859", event: "webhook.deleted", detail: "hooks.legacy.meridianhq.com", type: "API keys", typeVariant: "warning", actor: "Marcus Chen", initials: "MC", ip: "198.51.100.77", time: "Jul 11, 16:44" },];export function AuditLogTableBlock() {return (<Card aria-label="Audit log" className="mx-auto w-full max-w-5xl gap-0 pb-0 shadow-md"><CardHeader><CardTitle className="font-display text-lg">Audit events</CardTitle><CardDescription>Every privileged action in the workspace, retained for 365 days.</CardDescription><CardAction><Button variant="outline" size="sm"><Download className="size-4" aria-hidden="true" />Export CSV</Button></CardAction></CardHeader><CardContent className="flex flex-col gap-3 pt-5 lg:flex-row lg:items-center"><div className="relative flex-1"><SearchclassName="pointer-events-none absolute start-3 top-1/2 size-4 -translate-y-1/2 text-fg-tertiary"aria-hidden="true"/><Inputtype="search"placeholder="Search events, actors, or IP addresses…"aria-label="Search audit events"className="ps-9"/></div><div className="flex flex-col gap-3 sm:flex-row"><Select defaultValue="all"><SelectTrigger className="w-full sm:w-44" aria-label="Filter by event type"><SelectValue placeholder="All event types" /></SelectTrigger><SelectContent><SelectItem value="all">All event types</SelectItem><SelectItem value="auth">Authentication</SelectItem><SelectItem value="members">Members</SelectItem><SelectItem value="api">API keys</SelectItem><SelectItem value="data">Data exports</SelectItem></SelectContent></Select><Select defaultValue="30d"><SelectTrigger className="w-full sm:w-40" aria-label="Date range"><SelectValue /></SelectTrigger><SelectContent><SelectItem value="24h">Last 24 hours</SelectItem><SelectItem value="7d">Last 7 days</SelectItem><SelectItem value="30d">Last 30 days</SelectItem></SelectContent></Select></div></CardContent><CardContent className="px-0 pt-4"><Table><TableHeader><TableRow><TableHead className="ps-4 sm:ps-6">Event</TableHead><TableHead>Actor</TableHead><TableHead>IP address</TableHead><TableHead>Timestamp</TableHead><TableHead className="pe-4 text-end sm:pe-6"><span className="sr-only">Details</span></TableHead></TableRow></TableHeader><TableBody>{auditRows.map((row) => (<TableRow key={row.id}><TableCell className="ps-4 sm:ps-6"><div className="flex flex-col gap-1"><div className="flex items-center gap-2"><Badge variant={row.typeVariant}>{row.type}</Badge><span className="font-mono text-xs text-fg">{row.event}</span></div><span className="text-xs text-fg-tertiary">{row.detail}</span></div></TableCell><TableCell><div className="flex items-center gap-2"><Avatar className="size-6"><AvatarFallback className="text-[10px]">{row.initials}</AvatarFallback></Avatar><span className="text-sm text-fg">{row.actor}</span></div></TableCell><TableCell className="font-mono text-xs text-fg-secondary">{row.ip}</TableCell><TableCell className="text-fg-secondary">{row.time}</TableCell><TableCell className="pe-4 text-end sm:pe-6"><Button variant="ghost" size="icon-sm"><ChevronRight className="size-4" aria-hidden="true" /><span className="sr-only">View details for {row.event}</span></Button></TableCell></TableRow>))}</TableBody></Table></CardContent><Separator /><CardFooter className="flex-wrap justify-between gap-3 py-4"><span className="text-sm text-fg-tertiary">1,284 events in the last 30 days</span><div className="flex items-center gap-2"><Button variant="outline" size="sm">Previous</Button><Button variant="outline" size="sm">Next</Button></div></CardFooter></Card>);}
| Event | Actor | IP address | Timestamp | Details |
|---|---|---|---|---|
Membersmember.role_updated Felix Weber · Viewer → Admin | Ingrid Solberg | 172.64.18.24 | Jul 13, 14:32 | |
api_key.rotated prod-billing-service | Marcus Chen | 198.51.100.77 | Jul 13, 11:05 | |
Authauth.login_blocked 5 failed attempts · jonah.kaplan@meridianhq.com | Meridian Security | 203.0.113.42 | Jul 13, 09:47 | |
Datadata.export_created Contacts · 12,408 rows · CSV | Amara Diallo | 92.118.30.6 | Jul 12, 17:21 | |
auth.new_device Chrome on macOS · São Paulo, Brazil | Rosa Delgado | 198.51.100.9 | Jul 12, 10:12 | |
Securityworkspace.2fa_enforced All members · grace period ends July 19 | Ingrid Solberg | 172.64.18.24 | Jul 12, 08:30 | |
API keyswebhook.deleted hooks.legacy.meridianhq.com | Marcus Chen | 198.51.100.77 | Jul 11, 16:44 |