Skip to content

T12.3 basic admin audit log for changes (match reassignment, session edits) #80

@Taleef7

Description

@Taleef7

Parent epic

Epic E12: policies, safety, and reliability (P0) — #77

Objective

Build an admin-only audit log view at /admin/audit showing recent platform actions — payments marked paid, tutors approved, sessions rescheduled, tutors reassigned — so the admin has a clear record of all significant changes.


Background

From docs/ARCHITECTURE.md section 5.6 (audit_logs table):

create table public.audit_logs (
  id bigint generated by default as identity primary key,
  actor_user_id uuid references public.user_profiles(user_id),
  action text not null,
  entity_type text not null,
  entity_id uuid,
  details jsonb not null default '{}'::jsonb,
  created_at timestamptz not null default now()
);
create index on public.audit_logs (created_at desc);

The audit log is written by Server Actions throughout the platform (payment verified, match created, session rescheduled, tutor approved, etc.).


Migration: <ts>_create_audit_logs.sql

Include the audit_logs table if not already created as part of an earlier migration. RLS:

alter table public.audit_logs enable row level security;

-- Admin only reads
create policy "audit_logs_admin_select"
  on public.audit_logs for select to authenticated
  using (public.is_admin(auth.uid()));

-- Written by security definer functions or admin server actions
create policy "audit_logs_admin_insert"
  on public.audit_logs for insert to authenticated
  with check (public.is_admin(auth.uid()));

Admin audit log page: app/admin/audit/page.tsx

const { data: logs } = await adminClient
  .from('audit_logs')
  .select('id, action, entity_type, entity_id, details, created_at, user_profiles!actor_user_id(display_name)')
  .order('created_at', { ascending: false })
  .limit(200)

Log table

Timestamp Actor Action Entity Details
Feb 23, 7:15 PM Admin payment_marked_paid payment package_id: xxx...
Feb 23, 6:50 PM Admin tutor_approved tutor_profile
Feb 22, 8:00 PM Ali Hassan session_status_updated session status: done
Feb 22, 7:59 PM Ahmed Ali

Action labels

export const AUDIT_ACTION_LABELS: Record<string, string> = {
  payment_marked_paid: '💳 Payment marked paid',
  payment_rejected: '❌ Payment rejected',
  tutor_approved: '✅ Tutor approved',
  tutor_approval_revoked: '🚫 Tutor approval revoked',
  tutor_assigned: '🎓 Tutor assigned to request',
  tutor_reassigned: '🔄 Tutor reassigned',
  sessions_generated: '📅 Sessions generated',
  session_rescheduled: '📅 Session rescheduled',
  session_status_updated: '📝 Session status updated',
  meet_link_updated: '🔗 Meet link updated',
}

Acceptance criteria

  • audit_logs table migration exists (created as part of E3 or this task)
  • /admin/audit page exists and is admin-only
  • Shows recent 200 audit log entries ordered newest first
  • Table shows: timestamp (admin TZ), actor name, action label, entity type, entity ID, details
  • Pagination or "load more" for logs beyond 200 (optional for MVP)
  • Action labels are human-readable

Definition of done

  • audit_logs table exists in migrations
  • /admin/audit/page.tsx renders the log table
  • Human-readable action labels applied
  • Admin-only route (protected by app/admin/layout.tsx)

References

  • docs/ARCHITECTURE.md — section 5.6 (audit_logs table), section 6.4 (RLS for audit_logs)
  • docs/OPS.md — section 12 (data hygiene rules — platform is source of truth)
  • docs/MVP.md — section 11.2 (admin audit log for changes)

Metadata

Metadata

Assignees

Labels

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions