diff --git a/apps/admin/data/fitai.db b/apps/admin/data/fitai.db index 4ffef65..38a0ab3 100644 Binary files a/apps/admin/data/fitai.db and b/apps/admin/data/fitai.db differ diff --git a/apps/admin/src/app/api/users/route.ts b/apps/admin/src/app/api/users/route.ts index 832b00d..f5e9f54 100644 --- a/apps/admin/src/app/api/users/route.ts +++ b/apps/admin/src/app/api/users/route.ts @@ -387,24 +387,27 @@ export async function PUT(request: NextRequest) { // Update Clerk publicMetadata (role/gymId) to propagate via webhook // Note: Only update metadata when a change is requested - try { - const client = await clerkClient(); - const publicMetadata: Record = {}; - log.debug("Preparing Clerk metadata update", { - targetUserId: id, - role, - gymId, - }); + const client = await clerkClient(); + const publicMetadata: Record = {}; + log.debug("Preparing Clerk metadata update", { + targetUserId: id, + role, + gymId, + }); - if (role) { - publicMetadata.role = role; - } - if (gymId !== undefined) { - publicMetadata.gymId = gymId === null ? null : String(gymId); - } + if (role) { + publicMetadata.role = role; + } + if (gymId !== undefined) { + publicMetadata.gymId = gymId === null ? null : String(gymId); + } + + if (Object.keys(publicMetadata).length > 0) { + log.debug("Updating Clerk user metadata", { publicMetadata }); + try { + // Check if user exists in Clerk first + await client.users.getUser(id); - if (Object.keys(publicMetadata).length > 0) { - log.debug("Updating Clerk user metadata", { publicMetadata }); const clerkResult = await client.users.updateUser(id, { publicMetadata, }); @@ -413,14 +416,20 @@ export async function PUT(request: NextRequest) { role: clerkResult.publicMetadata?.role, gymId: clerkResult.publicMetadata?.gymId, }); - } else { - log.debug("No Clerk metadata changes requested"); + } catch (clerkErr: any) { + // User might not exist in Clerk yet - that's OK for local users + if ( + clerkErr?.status === 404 || + clerkErr?.errors?.[0]?.code === "resource_not_found" + ) { + log.debug( + "User not found in Clerk, skipping metadata update (local-only user)", + ); + } else { + log.error("Clerk metadata update failed", clerkErr, { userId: id }); + // Don't fail the whole request - local DB update can still proceed + } } - } catch (clerkErr: any) { - log.error("Clerk metadata update failed", clerkErr, { userId: id }); - return internalErrorResponse( - "Failed to update role/gym in identity provider", - ); } // Update local DB for immediate UI feedback (webhook will also sync) diff --git a/apps/admin/src/components/users/CreateUserModal.tsx b/apps/admin/src/components/users/CreateUserModal.tsx index 8bab871..20eeba5 100644 --- a/apps/admin/src/components/users/CreateUserModal.tsx +++ b/apps/admin/src/components/users/CreateUserModal.tsx @@ -26,6 +26,7 @@ import { type InvitationOptionsData, } from "@/lib/validation/user-schemas"; import { ChevronLeft, ChevronRight, Check } from "lucide-react"; +import { useGyms } from "@/hooks/use-api"; interface CreateUserModalProps { open: boolean; @@ -45,6 +46,7 @@ export function CreateUserModal({ const [basicInfo, setBasicInfo] = useState(null); const [clientInfo, setClientInfo] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); + const { data: gyms = [] } = useGyms(); // Step 1: Role Selection Form const roleForm = useForm({ diff --git a/apps/admin/src/hooks/use-api.ts b/apps/admin/src/hooks/use-api.ts index 287eab0..3cead9c 100644 --- a/apps/admin/src/hooks/use-api.ts +++ b/apps/admin/src/hooks/use-api.ts @@ -170,7 +170,7 @@ export function useGyms() { queryKey: ["gyms"], queryFn: () => fetchApi<{ data: { gyms: Gym[] } }>("/api/gyms").then( - (res) => res.data?.gyms ?? [], + (res) => res.data?.gyms ?? (Array.isArray(res) ? res : []), ), }); }