Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffd3aabc55 | ||
|
|
8cac57ed67 | ||
|
|
d4fae890bb |
No files matched your search
Binary file not shown.
@@ -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 },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user