Implement Workstream M: Admin Pages (Frontend)

Add superuser admin interface for managing organizations and users:
- Admin layout with access control (redirects non-superusers)
- Admin dashboard with org/user counts and quick actions
- Org management: list, create, view/edit details, manage sites
- User management: list, view details, toggle superuser, confirm email
- SuperuserBadge component for consistent superuser indication
- Sidebar shows admin link (shield icon) for superusers only
- Centralized date formatting utility at $lib/utils/format-date.ts
- Test plan documentation at docs/test-plans/admin.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
RevIQ
2026-01-09 18:18:36 +08:00
parent 41b52750d8
commit c8152ce86e
25 changed files with 2068 additions and 86 deletions

317
docs/test-plans/admin.md Normal file
View File

@@ -0,0 +1,317 @@
# Test Plan: Admin Dashboard (Workstream M)
## Overview
Manual UI test plan for superuser-only admin management pages:
- `/admin` - Admin dashboard
- `/admin/orgs` - Organization list
- `/admin/orgs/new` - Create organization
- `/admin/orgs/[slug]` - Organization details
- `/admin/users` - User list
- `/admin/users/[email]` - User details
## Prerequisites
- Dev server running: `bun run --cwd apps/publisher-dashboard dev`
- Test accounts:
- Superuser account (has `is_superuser = true`)
- Regular user account (not a superuser)
- At least one organization with sites
- At least one user who is not a superuser
---
## 1. Access Control
### 1.1 Superuser Access
- [ ] Superuser visiting `/admin` sees admin dashboard
- [ ] Superuser can access all admin sub-pages
### 1.2 Non-Superuser Access
- [ ] Regular user visiting `/admin` gets redirected to `/dashboard`
- [ ] Toast error message: "Access denied. Superuser privileges required."
- [ ] Regular user visiting `/admin/orgs` gets redirected
- [ ] Regular user visiting `/admin/users` gets redirected
### 1.3 Unauthenticated Access
- [ ] Unauthenticated user visiting `/admin` redirects to `/auth/login`
- [ ] After login as superuser, returns to `/admin`
---
## 2. Admin Dashboard (`/admin`)
### 2.1 Display
- [ ] Page title is "Admin Dashboard"
- [ ] Red "Admin" badge visible at top
- [ ] Summary cards display:
- Organizations card with correct count
- Users card with correct count
- [ ] Cards are clickable and navigate to respective list pages
### 2.2 Quick Actions
- [ ] "New Organization" button visible
- [ ] Button navigates to `/admin/orgs/new`
### 2.3 Loading States
- [ ] Loading spinner shows while fetching data
- [ ] Error state displays if API fails
---
## 3. Organization List (`/admin/orgs`)
### 3.1 Display
- [ ] Page title is "Organizations"
- [ ] Header shows "Organizations (count)" with correct count
- [ ] "New Organization" button visible in header
- [ ] Table displays all organizations (not just user's orgs)
### 3.2 Table Content
- [ ] Slug column displays org slug
- [ ] Display Name column shows org name
- [ ] Created At column shows formatted date
- [ ] Actions column has View and Delete buttons
### 3.3 View Action
- [ ] View button navigates to `/admin/orgs/[slug]`
### 3.4 Delete Action
- [ ] Delete button opens confirmation dialog
- [ ] Dialog shows org name and warning message
- [ ] Cancel button closes dialog without action
- [ ] Confirm button deletes organization
- [ ] Success toast: "Organization deleted"
- [ ] Org disappears from list after deletion
- [ ] Error toast on failure
### 3.5 Empty State
- [ ] Shows appropriate message when no organizations exist
---
## 4. Create Organization (`/admin/orgs/new`)
### 4.1 Display
- [ ] Page title is "New Organization"
- [ ] Back link "Back to organizations" works
### 4.2 Form Fields
- [ ] Slug input: accepts lowercase alphanumeric and hyphens
- [ ] Slug input: auto-converts uppercase to lowercase
- [ ] Slug input: strips invalid characters
- [ ] Display Name input: accepts any text
- [ ] Owner Email input: validates email format
### 4.3 Form Validation
- [ ] Submit button disabled when fields are empty
- [ ] Submit button enabled when all fields filled
- [ ] Form submits on button click
### 4.4 Submit Flow
- [ ] Loading state on submit button
- [ ] Success toast: "Organization created"
- [ ] Redirects to `/admin/orgs` on success
- [ ] Error toast on failure (e.g., slug already exists)
---
## 5. Organization Details (`/admin/orgs/[slug]`)
### 5.1 Header Section
- [ ] Org logo displays if set, placeholder icon otherwise
- [ ] Display name shown prominently
- [ ] Slug displayed
- [ ] Created date shown
### 5.2 Settings Card
- [ ] Display name input pre-filled with current value
- [ ] Logo URL input pre-filled if set
- [ ] Save button disabled when no changes
- [ ] Save button enabled when form is dirty
- [ ] Success toast on save: "Organization updated"
- [ ] Changes reflected after save
### 5.3 Sites Card
- [ ] Title shows "Sites (count)"
- [ ] Table shows all sites for the org
- [ ] Each site has domain and Remove button
### 5.4 Add Site
- [ ] Domain input visible
- [ ] Add button visible
- [ ] Adding valid domain shows success toast
- [ ] New site appears in list
- [ ] Error toast on invalid/duplicate domain
### 5.5 Remove Site
- [ ] Remove button opens confirmation dialog
- [ ] Dialog shows domain being removed
- [ ] Confirm removes site from list
- [ ] Success toast on removal
### 5.6 Danger Zone
- [ ] Card has red border styling
- [ ] Warning text about permanent deletion
- [ ] Delete button opens confirmation dialog
- [ ] Confirm deletes org and redirects to `/admin/orgs`
- [ ] Success toast on deletion
### 5.7 Navigation
- [ ] Back link works
- [ ] 404 error for non-existent org slug
---
## 6. User List (`/admin/users`)
### 6.1 Display
- [ ] Page title is "Users"
- [ ] Header shows "Users (count)" with correct count
- [ ] Table displays all users in system
### 6.2 Table Content
- [ ] Email column displays user email
- [ ] Display Name column shows name (or "-" if not set)
- [ ] Email Verified column shows checkmark or X icon
- [ ] Superuser column shows SuperuserBadge for superusers
- [ ] Actions column has View button
### 6.3 View Action
- [ ] View button navigates to `/admin/users/[email]`
- [ ] Email is URL-encoded in the link
### 6.4 Empty State
- [ ] Shows appropriate message when no users exist
---
## 7. User Details (`/admin/users/[email]`)
### 7.1 Header Section
- [ ] Avatar with initials displays
- [ ] Display name shown (or "Unknown" if not set)
- [ ] Email shown below name
- [ ] SuperuserBadge shown if user is superuser
### 7.2 Profile Info Card
- [ ] Email displayed (read-only)
- [ ] Display Name displayed
- [ ] Full Name displayed
- [ ] Phone Number displayed
- [ ] Email Verified status (Yes/No)
### 7.3 Permissions Card
- [ ] Superuser checkbox visible
- [ ] Checkbox reflects current status
- [ ] Save button disabled when no changes
- [ ] Save button enabled when checkbox changed
### 7.4 Toggle Superuser
- [ ] Can grant superuser to regular user
- [ ] Can revoke superuser from superuser (if not self)
- [ ] Success toast on save
- [ ] Cannot demote self (checkbox disabled when viewing own profile)
- [ ] Warning shown when viewing own profile
### 7.5 Actions Card
- [ ] "Confirm Email" button visible only if email not verified
- [ ] Hidden if email already verified
- [ ] Button confirms email on click
- [ ] Success toast: "Email confirmed"
- [ ] Button disappears after confirmation
### 7.6 Navigation
- [ ] Back link works
- [ ] 404 error for non-existent user email
---
## 8. Sidebar Navigation
### 8.1 Admin Link
- [ ] Shield icon visible for superusers
- [ ] Hidden for regular users
- [ ] Tooltip shows "Admin" on hover
- [ ] Clicking navigates to `/admin`
- [ ] Active state (red tint) when on `/admin` routes
---
## 9. Cross-Cutting Concerns
### 9.1 Loading States
- [ ] All pages show loading spinner during data fetch
- [ ] Buttons show loading state during operations
### 9.2 Error Handling
- [ ] API errors display user-friendly messages
- [ ] Toast notifications for action results
- [ ] Error states don't crash the app
### 9.3 Responsive Design
- [ ] Pages render correctly on mobile viewport
- [ ] Tables scroll horizontally on small screens
- [ ] Forms stack vertically on mobile
### 9.4 Query Invalidation
- [ ] After org create: org list refreshes
- [ ] After org delete: org list refreshes
- [ ] After org update: org details refresh
- [ ] After add site: sites list refreshes
- [ ] After remove site: sites list refreshes
- [ ] After user update: user details refresh
- [ ] After confirm email: user details refresh
---
## 10. Edge Cases
### 10.1 Self-Demotion Prevention
- [ ] Cannot remove own superuser status
- [ ] Warning message explains why
### 10.2 Special Characters in Email
- [ ] User with `+` in email can be viewed
- [ ] User with `.` in email can be viewed
- [ ] Email properly URL-encoded/decoded
### 10.3 Long Content
- [ ] Long org names truncate or wrap properly
- [ ] Long email addresses don't break layout
- [ ] Long URLs in logo field don't break layout
### 10.4 Empty States
- [ ] Org with no sites shows "No sites" message
- [ ] Empty org list shows appropriate message
- [ ] Empty user list shows appropriate message
---
## Test Matrix: Admin vs Non-Admin
| Feature | Superuser | Regular User |
|---------|-----------|--------------|
| View admin dashboard | Yes | Redirected |
| View org list | Yes | Redirected |
| Create organization | Yes | Redirected |
| View org details | Yes | Redirected |
| Edit org settings | Yes | Redirected |
| Manage org sites | Yes | Redirected |
| Delete organization | Yes | Redirected |
| View user list | Yes | Redirected |
| View user details | Yes | Redirected |
| Toggle superuser | Yes (not self) | Redirected |
| Confirm user email | Yes | Redirected |
| See admin link in sidebar | Yes | No |
---
## Regression Checklist
After any changes to admin pages, verify:
- [ ] Access control still redirects non-superusers
- [ ] All CRUD operations function
- [ ] Error states still display
- [ ] Navigation works end-to-end
- [ ] Sidebar admin link visibility correct