OSAGI

Audit Log Table

Dashboards

About

Audit log section: search, action filter, Dangerous only, a paged table whose columns give way on phones, and a CSV export of every matching record with formulas neutralised. From the Ops Dashboards Pack → ai.osagi.finance/kits/ops-dashboards

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Audit Log Table to your project and fits it to your theme.

Code

How Audit Log Table is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

"use client";

import { useCallback } from "react";
import {
  AuditLogTable,
  type AuditRecord,
} from "@/components/ui/audit-log-table";

const settings = {
  dangerousOnly: false,
  failExport: false,
  pageSize: 8,
};

/** Fixed times (no clock): the same rows on the server and in the browser. */
const BASE = Date.parse("2026-10-17T09:30:00Z");
const MINUTE = 60_000;

const at = (minutesAgo: number) =>
  new Date(BASE - minutesAgo * MINUTE).toISOString();

const RECORDS: AuditRecord[] = [
  {
    action: "user.role_changed",
    actor: "Amelia Brooks",
    dangerous: true,
    detail: "Role changed from Viewer to Admin.",
    ip: "203.0.113.24",
    target: "lucas.meyer@acme.dev",
  },
  {
    action: "session.signed_in",
    actor: "Lucas Meyer",
    detail: "Signed in with a passkey.",
    ip: "198.51.100.7",
    target: "lucas.meyer@acme.dev",
  },
  {
    action: "api_key.rotated",
    actor: "Noah Bennett",
    dangerous: true,
    detail: "Production key rotated; the old one stops working in 24 hours.",
    ip: "203.0.113.51",
    target: "key_live_payments",
  },
  {
    action: "alert_rule.updated",
    actor: "Mia Turner",
    detail: "Threshold raised from 80% to 90% disk.",
    ip: "198.51.100.33",
    target: "rule/disk-space",
  },
  {
    action: "export.downloaded",
    actor: "Grace Kim",
    detail: "Monthly invoices, 1,284 rows.",
    ip: "192.0.2.14",
    target: "invoices-2026-09.csv",
  },
  {
    action: "user.invited",
    actor: "Amelia Brooks",
    detail: "Invited as Support agent.",
    ip: "203.0.113.24",
    target: "sophia.lee@acme.dev",
  },
  {
    action: "project.deleted",
    actor: "Ethan Cole",
    dangerous: true,
    detail: "Project and its 3 environments deleted.",
    ip: "198.51.100.90",
    target: "project/legacy-billing",
  },
  {
    action: "session.signed_in",
    actor: "Grace Kim",
    detail: "Signed in with email code.",
    ip: "192.0.2.14",
    target: "grace.kim@acme.dev",
  },
  {
    action: "webhook.created",
    actor: "deploy-bot",
    detail: "Sends deploy events to the release channel.",
    target: "https://hooks.acme.dev/releases",
  },
  {
    action: "settings.updated",
    actor: "Mia Turner",
    detail: "Session timeout set to 12 hours.",
    ip: "198.51.100.33",
    target: "workspace/security",
  },
  {
    action: "two_factor.disabled",
    actor: "Lucas Meyer",
    dangerous: true,
    detail: "Turned off by an admin after a lost phone.",
    ip: "198.51.100.7",
    target: "lucas.meyer@acme.dev",
  },
  {
    action: "alert_rule.paused",
    actor: "Noah Bennett",
    detail: "Paused for the database migration window.",
    ip: "203.0.113.51",
    target: "rule/replica-lag",
  },
  {
    action: "user.removed",
    actor: "Amelia Brooks",
    dangerous: true,
    detail: "Access removed at the end of the contract.",
    ip: "203.0.113.24",
    target: "contractor.ops@acme.dev",
  },
  {
    action: "export.downloaded",
    actor: "Sophia Lee",
    detail: "Support tickets, last 30 days.",
    ip: "192.0.2.77",
    target: "tickets-30d.csv",
  },
  {
    action: "session.signed_in",
    actor: "Ethan Cole",
    detail: "Signed in with a passkey.",
    ip: "198.51.100.90",
    target: "ethan.cole@acme.dev",
  },
  {
    action: "settings.updated",
    actor: "Grace Kim",
    detail:
      '=HYPERLINK("https://example.com") pasted into a note: exported as text.',
    ip: "192.0.2.14",
    target: "workspace/notes",
  },
  {
    action: "api_key.created",
    actor: "deploy-bot",
    detail: "Read-only key for the status page.",
    target: "key_live_status",
  },
  {
    action: "user.role_changed",
    actor: "Mia Turner",
    detail: "Role changed from Admin to Editor.",
    ip: "198.51.100.33",
    target: "noah.bennett@acme.dev",
  },
  {
    action: "session.signed_in",
    actor: "Amelia Brooks",
    detail: "Signed in with a passkey.",
    ip: "203.0.113.24",
    target: "amelia.brooks@acme.dev",
  },
  {
    action: "backup.restored",
    actor: "Noah Bennett",
    dangerous: true,
    detail: "Staging database restored from the 02:00 snapshot.",
    ip: "203.0.113.51",
    target: "db/staging",
  },
  {
    action: "webhook.deleted",
    actor: "Ethan Cole",
    detail: "Old CI hook removed.",
    ip: "198.51.100.90",
    target: "https://ci.acme.dev/hook",
  },
  {
    action: "export.downloaded",
    actor: "Mia Turner",
    detail: "Audit log, last 7 days.",
    ip: "198.51.100.33",
    target: "audit-log-7d.csv",
  },
  {
    action: "session.signed_in",
    actor: "Sophia Lee",
    detail: "Signed in with email code.",
    ip: "192.0.2.77",
    target: "sophia.lee@acme.dev",
  },
].map((record, i) => ({ ...record, at: at(i * 37 + 3), id: `evt-${i + 1}` }));

/** An audit log of a small team: search, filter, page through and export it. */
export default function Demo(props: Partial<typeof settings>) {
  const s = { ...settings, ...props };
  const failingExport = useCallback(async () => {
    await new Promise((resolve) => setTimeout(resolve, 600));
    throw new Error(
      "The export service did not answer. Try again in a minute."
    );
  }, []);
  return (
    <div className="flex min-h-[640px] w-full items-start justify-center bg-background p-4 sm:p-8">
      <AuditLogTable
        className="max-w-5xl"
        defaultDangerousOnly={s.dangerousOnly}
        fileName="audit-log-demo.csv"
        key={`${s.dangerousOnly}`}
        onExport={s.failExport ? failingExport : undefined}
        pageSize={s.pageSize}
        records={RECORDS}
      />
    </div>
  );
}

Dependencies

More by OSAGI

View all
  • AI Orb

    AI Orb

    83
  • KPI Tile

    KPI Tile

    28
  • OTP Input

    OTP Input

    12
  • Reasoning Thread

    Reasoning Thread

    8
  • Assistant Composer

    Assistant Composer

    8
  • Release Notes

    Release Notes

  • Docs TOC

    Docs TOC

  • Docs Search

    Docs Search

More in Dashboards

View all
  • Dashboard with Collapsible Sidebar

    Dashboard with Collapsible Sidebar

    2.4K
  • Sidebar

    Sidebar

    235
  • SaaS Template

    SaaS Template

    2.4K
  • Stats Bento

    Stats Bento

    1.1K
  • Financial Bento Grid

    Financial Bento Grid

    71
  • Stats cards with links

    Stats cards with links

    33
  • Course Design Cards

    Course Design Cards

    1.1K
  • Chat template

    Chat template

    598