Scaffold an admin activity log feature into a Next.js + Supabase project. Adds an audit_log database table (append-only, admin-only via RLS), a /admin/activity page that shows a feed of who-did-what plus a free-form "log an update" form, an API route at /api/admin/audit-log, and a logAuditEvent() helper that other admin endpoints can call to record events. Use when the user wants to add audit logging, an activity feed, a "what's new" / changelog page, or who-did-what tracking to an existing N...
Scanned 5/27/2026
Install via CLI
openskills install SamDaMan636/claude-skill-activity-log---
name: setup-activity-log
description: Scaffold an admin activity log feature into a Next.js + Supabase project. Adds an audit_log database table (append-only, admin-only via RLS), a /admin/activity page that shows a feed of who-did-what plus a free-form "log an update" form, an API route at /api/admin/audit-log, and a logAuditEvent() helper that other admin endpoints can call to record events. Use when the user wants to add audit logging, an activity feed, a "what's new" / changelog page, or who-did-what tracking to an existing Next.js + Supabase admin dashboard.
---
# Setup Activity Log
This skill installs a complete admin activity-log feature into the current project. Covers both auto-captured admin actions (settings changes, invites, etc.) and manual "we shipped X" notes typed by an admin.
## Pre-flight checks
Before writing any files, verify the target project:
1. **Stack check** — read `package.json`. The project must have:
- `next` (app router — confirm by checking for `src/app/` or `app/`)
- `@supabase/ssr` and `@supabase/supabase-js`
- shadcn-style UI primitives at `src/components/ui/` (`card.tsx`, `button.tsx`, `input.tsx`, `textarea.tsx`)
- `lucide-react`
- `sonner` (for toasts) — if missing, swap toasts for inline error text in `page.tsx`
- `zod` (for input validation in the API route)
If any are missing, tell the user and stop. Don't half-install.
2. **Auth pattern detection** — find how admin endpoints check authorization in this project:
- Search for `requireAdmin`, `isAdmin`, `requireAdminStrict`, or similar in `src/lib/auth.ts` or `src/lib/`
- Note the exact import path and function name — you'll substitute this into `route.ts`
- If the project uses a different pattern entirely (e.g. middleware-only, no helper), ask the user how admin endpoints are gated and adapt
3. **Profile/role schema** — the SQL migration assumes a `profiles` table with a `role` column where admins have `role = 'admin'`. Before running it:
- Search the existing supabase/ folder for the schema
- If the project uses different names (e.g. `users.is_admin = true`), edit the RLS policies in `templates/add_audit_log.sql` accordingly
- Note this for the user
4. **Supabase admin client** — confirm `src/lib/supabase/admin.ts` exists and exports `createAdminClient`. If it's at a different path, update the import in `templates/audit-log.ts` and `templates/route.ts`.
5. **Supabase server client** — confirm `src/lib/supabase/server.ts` exists with a `createClient` async function. Update import in `templates/audit-log.ts` if needed.
## Installation
Once pre-flight passes, copy the four template files into place. Read each from `templates/` (in this skill's folder) and write to the target paths below. **Do not modify the templates** other than the small substitutions noted in pre-flight.
| Template | Target path |
|---|---|
| `templates/add_audit_log.sql` | `supabase/add_audit_log.sql` (or wherever this project keeps migrations) |
| `templates/audit-log.ts` | `src/lib/audit-log.ts` |
| `templates/route.ts` | `src/app/api/admin/audit-log/route.ts` |
| `templates/page.tsx` | `src/app/admin/activity/page.tsx` |
Then update the admin sidebar:
- Find the admin sidebar component (commonly `src/components/admin/admin-sidebar.tsx` or `src/components/layout/admin-nav.tsx`)
- Import the `History` icon from `lucide-react`
- Add a nav entry: `{ href: "/admin/activity", label: "Activity", icon: History }` — place it just before the "Settings" entry
Don't instrument any endpoints automatically. The user will add `logAuditEvent()` calls to specific admin endpoints later as needs arise.
## After installation
Tell the user to do exactly two things:
1. **Run the SQL migration**
- Open https://supabase.com/dashboard → the relevant project
- SQL Editor → New query → paste the contents of the new `add_audit_log.sql` file → Run
- Expect "Success. No rows returned."
2. **Refresh and verify**
- Visit `/admin/activity` while signed in as admin
- Should see an empty feed and a "Log an update" form
- Type a test entry, hit "Log entry", confirm it appears
Then offer to walk them through wiring auto-logging into a specific endpoint. The pattern is one line at the end of any admin handler:
```ts
import { logAuditEvent } from "@/lib/audit-log"
await logAuditEvent({
action: "settings.update", // domain.verb
target: "brand_name", // optional human-readable identifier
summary: "Updated brand name to X",
before: oldValue, // optional snapshot
after: newValue, // optional snapshot
})
```
For client-side admin pages that write to Supabase directly (e.g. a settings page that does `.from('app_settings').upsert(...)`), they can insert into `audit_log` directly — RLS allows admins to insert their own rows. Pattern:
```ts
const { data: { user } } = await supabase.auth.getUser()
if (user?.email) {
await supabase.from("audit_log").insert({
actor_id: user.id,
actor_email: user.email,
source: "auto",
action: "settings.update",
target: changedKeys.join(", "),
summary: `Updated settings: ${changedKeys.join(", ")}`,
before: oldSnapshot,
after: newSnapshot,
})
}
```
## Action naming convention
Use `domain.verb` format so the feed groups related events:
- `settings.update` — admin saved settings
- `user.invite` — new user invited
- `user.temp_password` — admin issued a temp password
- `content.upload` / `content.delete` — content management
- `feature.deploy` — code change shipped (insert with `source: 'auto'` so no "manual" badge)
- `manual.note` — free-form admin note via the form (set automatically by the API)
Add new actions to `ACTION_ICONS` and `actionLabel()` in `templates/page.tsx` (or have the user do it once in their copy) so they render with proper icons and human labels.
No comments yet. Be the first to comment!