Compare commits

...
Author SHA1 Message Date
dimitar ffd3aabc55 delete gym working 2026-03-19 00:44:39 +01:00
dimitar 8cac57ed67 db updated 2026-03-19 00:39:23 +01:00
dimitar d4fae890bb invitation flow finished 2026-03-18 23:58:14 +01:00
7 changed files with 46 additions and 19 deletions

No files matched your search

Binary file not shown.
+10 -6
View File
@@ -30,7 +30,7 @@ export async function DELETE(
const { userId } = await auth(); const { userId } = await auth();
if (!userId) { if (!userId) {
return new NextResponse("Unauthorized", { status: 401 }); return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
} }
// Ensure user is synced // Ensure user is synced
@@ -60,9 +60,10 @@ export async function DELETE(
// Only superAdmin can delete gyms // Only superAdmin can delete gyms
if (!currentUser || currentUser.role !== "superAdmin") { if (!currentUser || currentUser.role !== "superAdmin") {
return new NextResponse("Forbidden - Only superAdmin can delete gyms", { return NextResponse.json(
status: 403, { error: "Forbidden - Only superAdmin can delete gyms" },
}); { status: 403 },
);
} }
await ensureGymsTable(); await ensureGymsTable();
@@ -70,7 +71,7 @@ export async function DELETE(
// Check if gym exists // Check if gym exists
const gymRows = await db.all(sql`SELECT * FROM gyms WHERE id = ${gymId}`); const gymRows = await db.all(sql`SELECT * FROM gyms WHERE id = ${gymId}`);
if (gymRows.length === 0) { if (gymRows.length === 0) {
return new NextResponse("Gym not found", { status: 404 }); return NextResponse.json({ error: "Gym not found" }, { status: 404 });
} }
// Soft delete - mark as inactive // Soft delete - mark as inactive
@@ -84,6 +85,9 @@ export async function DELETE(
}); });
} catch (error) { } catch (error) {
log.error("Failed to delete gym", error); log.error("Failed to delete gym", error);
return new NextResponse("Internal Server Error", { status: 500 }); return NextResponse.json(
{ error: "Internal Server Error" },
{ status: 500 },
);
} }
} }
+5 -5
View File
@@ -54,13 +54,13 @@ export async function GET(request: NextRequest) {
// Gym filter: SuperAdmin can see all, others only their gym // Gym filter: SuperAdmin can see all, others only their gym
if (role === "superAdmin") { if (role === "superAdmin") {
// If gymId param provided, filter by it // If gymId param provided, filter by it
if (targetGymId && metadata?.gymId !== targetGymId) { if (targetGymId && (metadata?.gymId ?? null) !== targetGymId) {
return false; return false;
} }
return true; return true;
} else { } else {
// Non-superAdmins: must match their gym // Non-superAdmins: must match their gym (normalize null/undefined)
return metadata?.gymId === userGymId; return (metadata?.gymId ?? null) === (userGymId ?? null);
} }
}) })
.map((inv) => ({ .map((inv) => ({
@@ -214,9 +214,9 @@ export async function POST(req: Request) {
const invitation = await client.invitations.createInvitation({ const invitation = await client.invitations.createInvitation({
emailAddress: inviteeEmail, emailAddress: inviteeEmail,
publicMetadata: { publicMetadata: {
roleAssigned, role: roleAssigned,
gymId: gymIdForInvite, gymId: gymIdForInvite,
inviterUserId: inviter.id, createdBy: inviter.id,
}, },
}); });
+2 -4
View File
@@ -85,14 +85,12 @@ export async function POST(request: NextRequest) {
try { try {
const client = await clerkClient(); const client = await clerkClient();
// Build publicMetadata - only include gymId if it exists // Build publicMetadata with consistent field names
const publicMetadata: Record<string, any> = { const publicMetadata: Record<string, any> = {
role: data.role, role: data.role,
createdBy: userId, createdBy: userId,
gymId: assignedGymId ?? null,
}; };
if (assignedGymId) {
publicMetadata.gymId = assignedGymId;
}
// Determine redirect URL based on role // Determine redirect URL based on role
// Staff (admin, trainer, superAdmin) → Admin web app // Staff (admin, trainer, superAdmin) → Admin web app
+10 -4
View File
@@ -199,14 +199,20 @@ export default function SettingsPage() {
setDeletingGym(true); setDeletingGym(true);
setGymMessage(null); setGymMessage(null);
try { try {
await axios.delete(`/api/gyms/${gymId}`); const response = await axios.delete(`/api/gyms/${gymId}`);
log.info("Delete gym response:", response.data);
setGymMessage({ type: "success", text: "Gym deleted successfully" }); setGymMessage({ type: "success", text: "Gym deleted successfully" });
setSelectedGym(null); setSelectedGym(null);
setGymStats(null); setGymStats(null);
fetchGyms(); await fetchGyms();
} catch (error) { } catch (error: any) {
log.error("Failed to delete gym", error); log.error("Failed to delete gym", error);
setGymMessage({ type: "error", text: "Failed to delete gym" }); const errorMessage =
error.response?.data?.error ||
error.response?.data ||
error.message ||
"Failed to delete gym";
setGymMessage({ type: "error", text: errorMessage });
} finally { } finally {
setDeletingGym(false); setDeletingGym(false);
} }
@@ -174,6 +174,10 @@ export function CreateUserModal({
// Reset form // Reset form
resetModal(); resetModal();
// Add delay to allow Clerk API to propagate invitation
await new Promise((resolve) => setTimeout(resolve, 1000));
onSuccess(); onSuccess();
onOpenChange(false); onOpenChange(false);
} catch (error) { } catch (error) {
+15
View File
@@ -365,6 +365,21 @@ export function useInvitations(gymId?: string) {
(res) => res.data?.invitations ?? [], (res) => res.data?.invitations ?? [],
); );
}, },
refetchInterval: (query) => {
const data = query.state.data;
const hasData = data && data.length > 0;
const fetchCount = query.state.dataUpdateCount;
// Poll every 2 seconds if:
// 1. No invitations returned yet
// 2. Haven't exceeded 5 attempts (10 seconds total)
if (!hasData && fetchCount < 5) {
return 2000;
}
// Stop polling after 10 seconds or when data is present
return false;
},
}); });
} }