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();
if (!userId) {
return new NextResponse("Unauthorized", { status: 401 });
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// Ensure user is synced
@@ -60,9 +60,10 @@ export async function DELETE(
// Only superAdmin can delete gyms
if (!currentUser || currentUser.role !== "superAdmin") {
return new NextResponse("Forbidden - Only superAdmin can delete gyms", {
status: 403,
});
return NextResponse.json(
{ error: "Forbidden - Only superAdmin can delete gyms" },
{ status: 403 },
);
}
await ensureGymsTable();
@@ -70,7 +71,7 @@ export async function DELETE(
// Check if gym exists
const gymRows = await db.all(sql`SELECT * FROM gyms WHERE id = ${gymId}`);
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
@@ -84,6 +85,9 @@ export async function DELETE(
});
} catch (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
if (role === "superAdmin") {
// If gymId param provided, filter by it
if (targetGymId && metadata?.gymId !== targetGymId) {
if (targetGymId && (metadata?.gymId ?? null) !== targetGymId) {
return false;
}
return true;
} else {
// Non-superAdmins: must match their gym
return metadata?.gymId === userGymId;
// Non-superAdmins: must match their gym (normalize null/undefined)
return (metadata?.gymId ?? null) === (userGymId ?? null);
}
})
.map((inv) => ({
@@ -214,9 +214,9 @@ export async function POST(req: Request) {
const invitation = await client.invitations.createInvitation({
emailAddress: inviteeEmail,
publicMetadata: {
roleAssigned,
role: roleAssigned,
gymId: gymIdForInvite,
inviterUserId: inviter.id,
createdBy: inviter.id,
},
});
+2 -4
View File
@@ -85,14 +85,12 @@ export async function POST(request: NextRequest) {
try {
const client = await clerkClient();
// Build publicMetadata - only include gymId if it exists
// Build publicMetadata with consistent field names
const publicMetadata: Record<string, any> = {
role: data.role,
createdBy: userId,
gymId: assignedGymId ?? null,
};
if (assignedGymId) {
publicMetadata.gymId = assignedGymId;
}
// Determine redirect URL based on role
// Staff (admin, trainer, superAdmin) → Admin web app
+10 -4
View File
@@ -199,14 +199,20 @@ export default function SettingsPage() {
setDeletingGym(true);
setGymMessage(null);
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" });
setSelectedGym(null);
setGymStats(null);
fetchGyms();
} catch (error) {
await fetchGyms();
} catch (error: any) {
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 {
setDeletingGym(false);
}
@@ -174,6 +174,10 @@ export function CreateUserModal({
// Reset form
resetModal();
// Add delay to allow Clerk API to propagate invitation
await new Promise((resolve) => setTimeout(resolve, 1000));
onSuccess();
onOpenChange(false);
} catch (error) {
+15
View File
@@ -365,6 +365,21 @@ export function useInvitations(gymId?: string) {
(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;
},
});
}