[Phase 3] Implement user management page for admins

Resolved 💬 3 comments Opened Nov 7, 2025 by gloos Closed Jan 9, 2026

Description

Create an admin-only user management page that allows administrators to view all users, manage roles, and monitor user activity.

Dependencies

  • Issue #52 (Role-based access control must be implemented first)
  • Issue #53 (Admin RPC functions required)

Requirements

Page Location: apps/web/src/app/(authenticated)/admin/users/page.tsx

Functionality:

  1. Display paginated user list with:
  • Email
  • Role (user/admin)
  • Registration date
  • Last login
  • Total files uploaded
  • Total LLM usage cost
  1. Role management:
  • Toggle user/admin role
  • Confirmation dialog for role changes
  1. User activity summary:
  • Link to view user's files
  • Link to view user's LLM usage details

Server Component Pattern:

import { createRouteHandlerClient } from '@/lib/supabase';
import { requireAdmin } from '@/lib/auth-utils';

export default async function UsersPage() {
  await requireAdmin();
  
  const supabase = createRouteHandlerClient();
  const { data: users } = await supabase.rpc('get_all_users_admin', {
    p_limit: 50,
    p_offset: 0
  });
  
  return <UserManagementTable users={users} />;
}

Required RPC (add to migration 20251108000004):

CREATE OR REPLACE FUNCTION get_all_users_admin(
  p_limit INTEGER DEFAULT 50,
  p_offset INTEGER DEFAULT 0
)
RETURNS TABLE (
  id UUID,
  email TEXT,
  role TEXT,
  created_at TIMESTAMPTZ,
  last_sign_in_at TIMESTAMPTZ,
  file_count BIGINT,
  total_llm_cost NUMERIC
) SECURITY DEFINER AS $$
BEGIN
  -- Verify admin access
  IF NOT EXISTS (
    SELECT 1 FROM profiles 
    WHERE id = auth.uid() AND role = 'admin'
  ) THEN
    RAISE EXCEPTION 'Unauthorized: Admin access required';
  END IF;
  
  RETURN QUERY
  SELECT 
    p.id,
    au.email::TEXT,
    p.role,
    p.created_at,
    au.last_sign_in_at,
    COUNT(DISTINCT f.id) as file_count,
    COALESCE(SUM(l.cost_usd), 0) as total_llm_cost
  FROM profiles p
  JOIN auth.users au ON au.id = p.id
  LEFT JOIN files f ON f.created_by = p.id
  LEFT JOIN llm_usage l ON l.user_id = p.id
  GROUP BY p.id, au.email, p.role, p.created_at, au.last_sign_in_at
  ORDER BY p.created_at DESC
  LIMIT p_limit OFFSET p_offset;
END;
$$ LANGUAGE plpgsql;

Acceptance Criteria

  • [ ] Admin middleware protection works (non-admins redirected)
  • [ ] User list displays all required fields
  • [ ] Pagination works correctly
  • [ ] Role toggle updates database and shows confirmation
  • [ ] Links to user files and usage details work
  • [ ] Server Component pattern (no client-side data fetching)

Testing

  • [ ] Verify admin can access page
  • [ ] Verify regular users get 403/redirect
  • [ ] Test role change functionality
  • [ ] Verify pagination with 50+ users

Phase

Phase 3: Admin Features

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗