From 1cabb9b383864e235dc6bddb3b07dc3e16a24475 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 14:46:22 +0000 Subject: [PATCH] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/admin.users.tsx | 101 +++++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 4 deletions(-) diff --git a/src/routes/admin.users.tsx b/src/routes/admin.users.tsx index 121aa71..22381e9 100644 --- a/src/routes/admin.users.tsx +++ b/src/routes/admin.users.tsx @@ -68,6 +68,7 @@ interface UserRow { full_name: string; created_at: string; hourly_rate: number | null; + title: string | null; role: AppRole | null; } @@ -79,16 +80,27 @@ const ROLE_LABEL: Record = { staff: "Staff", }; +const TITLE_OPTIONS = [ + "Attorney", + "Paralegal", + "Legal Assistant", + "Admin", + "Other", +] as const; +type UserTitle = (typeof TITLE_OPTIONS)[number]; + function UsersPage() { const { user: currentUser } = useAuth(); const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [inviteOpen, setInviteOpen] = useState(false); + const [search, setSearch] = useState(""); + const [titleFilter, setTitleFilter] = useState("all"); const load = async () => { setLoading(true); const [{ data: profiles }, { data: rolesData }] = await Promise.all([ - supabase.from("profiles").select("id, email, full_name, created_at, hourly_rate"), + supabase.from("profiles").select("id, email, full_name, created_at, hourly_rate, title"), supabase.from("user_roles").select("user_id, role"), ]); const roleMap = new Map(); @@ -99,6 +111,7 @@ function UsersPage() { full_name: p.full_name, created_at: p.created_at, hourly_rate: p.hourly_rate, + title: p.title ?? null, role: roleMap.get(p.id) ?? null, })); rows.sort((a, b) => a.email.localeCompare(b.email)); @@ -147,6 +160,38 @@ function UsersPage() { load(); }; + const handleTitleChange = async (userId: string, title: string | null) => { + const { error } = await supabase + .from("profiles") + .update({ title }) + .eq("id", userId); + if (error) { + toast.error("Could not update title", { description: error.message }); + return; + } + toast.success("Title updated"); + load(); + }; + + const filteredUsers = users.filter((u) => { + if (titleFilter !== "all") { + if (titleFilter === "__none__" ? u.title : u.title !== titleFilter) return false; + } + const q = search.trim().toLowerCase(); + if (!q) return true; + return ( + u.email.toLowerCase().includes(q) || + (u.full_name ?? "").toLowerCase().includes(q) || + (u.title ?? "").toLowerCase().includes(q) + ); + }); + + const titleCounts = TITLE_OPTIONS.reduce>((acc, t) => { + acc[t] = users.filter((u) => u.title === t).length; + return acc; + }, {}); + const noTitleCount = users.filter((u) => !u.title).length; + return ( Team members - {users.length} total + + {filteredUsers.length} of {users.length} shown + +
+
+ + setSearch(e.target.value)} + placeholder="Search by name, email, or title" + className="pl-8" + /> +
+ +
{loading ? (
- ) : users.length === 0 ? ( + ) : filteredUsers.length === 0 ? (
No users yet.
) : ( @@ -188,13 +260,14 @@ function UsersPage() { Name Email + Title Role Hourly rate Actions - {users.map((u) => { + {filteredUsers.map((u) => { const isSelf = u.id === currentUser?.id; return ( @@ -207,6 +280,26 @@ function UsersPage() { )} {u.email} + + +