"use client"; import { useEffect, useState } from "react"; import axios from "axios"; import { Database, Download, RefreshCw, AlertTriangle, Check, Loader2, Trash2, Users, CalendarCheck, TrendingUp, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { useUser } from "@clerk/nextjs"; import log from "@/lib/logger"; import { MEMBERSHIP_FEATURES } from "@/lib/membership/features"; interface Backup { name: string; size: number; createdAt: string; } interface Gym { id: string; name: string; location?: string | null; latitude?: number | null; longitude?: number | null; geofenceRadiusMeters?: number | null; geofenceEnabled?: boolean; status: "active" | "inactive"; adminUserId: string; createdAt?: number; } interface GymStats { totalUsers: number; admins: number; trainers: number; clients: number; membershipStats: { basic: number; premium: number; vip: number; }; activeClients: number; attendanceLast30Days: number; } export default function SettingsPage() { const { user } = useUser(); const [backups, setBackups] = useState([]); const [loading, setLoading] = useState(true); const [creatingBackup, setCreatingBackup] = useState(false); const [restoring, setRestoring] = useState(null); const [message, setMessage] = useState<{ type: "success" | "error"; text: string; } | null>(null); // Gym picker state const [gyms, setGyms] = useState([]); const [gymsLoading, setGymsLoading] = useState(true); const [gymMessage, setGymMessage] = useState<{ type: "success" | "error"; text: string; } | null>(null); // Selected gym for details const [selectedGym, setSelectedGym] = useState(null); const [gymStats, setGymStats] = useState(null); const [statsLoading, setStatsLoading] = useState(false); const [deletingGym, setDeletingGym] = useState(false); const [savingGeofence, setSavingGeofence] = useState(false); const [geofenceLatitude, setGeofenceLatitude] = useState(""); const [geofenceLongitude, setGeofenceLongitude] = useState(""); const [geofenceRadiusMeters, setGeofenceRadiusMeters] = useState("30"); const [geofenceEnabled, setGeofenceEnabled] = useState(true); // Create Gym modal state const [showCreateGym, setShowCreateGym] = useState(false); const [gymName, setGymName] = useState(""); const [gymLocation, setGymLocation] = useState(""); const [creatingGym, setCreatingGym] = useState(false); const fetchBackups = async () => { try { const response = await axios.get("/api/admin/backups"); setBackups(response.data); } catch (error) { log.error("Failed to fetch backups", error); } finally { setLoading(false); } }; const fetchGyms = async () => { setGymsLoading(true); setGymMessage(null); try { const res = await axios.get("/api/gyms"); setGyms(Array.isArray(res.data) ? res.data : []); } catch (error) { log.error("Failed to fetch gyms", error); setGymMessage({ type: "error", text: "Failed to load gyms" }); } finally { setGymsLoading(false); } }; const fetchGymStats = async (gymId: string) => { setStatsLoading(true); try { const res = await axios.get(`/api/gyms/${gymId}/stats`); if (res.data?.stats) { setGymStats(res.data.stats); } } catch (error) { log.error("Failed to fetch gym stats", error); } finally { setStatsLoading(false); } }; useEffect(() => { fetchBackups(); fetchGyms(); }, []); useEffect(() => { if (selectedGym) { fetchGymStats(selectedGym.id); } else { setGymStats(null); } }, [selectedGym]); const handleCreateBackup = async () => { setCreatingBackup(true); setMessage(null); try { await axios.post("/api/admin/backups"); await fetchBackups(); setMessage({ type: "success", text: "Backup created successfully" }); } catch (error) { log.error("Failed to create backup", error); setMessage({ type: "error", text: "Failed to create backup" }); } finally { setCreatingBackup(false); } }; const handleRestore = async (filename: string) => { if ( !window.confirm( `Are you sure you want to restore from ${filename}? This will overwrite the current database.`, ) ) { return; } setRestoring(filename); setMessage(null); try { await axios.post("/api/admin/backups/restore", { filename }); setMessage({ type: "success", text: "Database restored successfully" }); } catch (error) { log.error("Failed to restore backup", error); setMessage({ type: "error", text: "Failed to restore backup" }); } finally { setRestoring(null); } }; const formatSize = (bytes: number) => { const units = ["B", "KB", "MB", "GB"]; let size = bytes; let unitIndex = 0; while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex++; } return `${size.toFixed(2)} ${units[unitIndex]}`; }; const formatDate = (dateString: string) => { return new Date(dateString).toLocaleString(); }; const handleSelectGym = async (gym: Gym | null) => { setSelectedGym(gym); setGymStats(null); if (gym) { setGeofenceLatitude( gym.latitude !== null && gym.latitude !== undefined ? String(gym.latitude) : "", ); setGeofenceLongitude( gym.longitude !== null && gym.longitude !== undefined ? String(gym.longitude) : "", ); setGeofenceRadiusMeters(String(gym.geofenceRadiusMeters ?? 30)); setGeofenceEnabled(gym.geofenceEnabled ?? true); } }; const handleSaveGeofence = async () => { if (!selectedGym) return; const latitude = geofenceLatitude.trim() === "" ? null : Number(geofenceLatitude); const longitude = geofenceLongitude.trim() === "" ? null : Number(geofenceLongitude); const radius = Number(geofenceRadiusMeters); if ( latitude !== null && (!Number.isFinite(latitude) || latitude < -90 || latitude > 90) ) { setGymMessage({ type: "error", text: "Latitude must be between -90 and 90", }); return; } if ( longitude !== null && (!Number.isFinite(longitude) || longitude < -180 || longitude > 180) ) { setGymMessage({ type: "error", text: "Longitude must be between -180 and 180", }); return; } if (!Number.isFinite(radius) || radius <= 0) { setGymMessage({ type: "error", text: "Radius must be a positive number", }); return; } setSavingGeofence(true); setGymMessage(null); try { const response = await axios.patch(`/api/gyms/${selectedGym.id}`, { latitude, longitude, geofenceRadiusMeters: radius, geofenceEnabled, }); setGymMessage({ type: "success", text: "Geofence settings updated" }); const updatedGym = response.data as Gym; setSelectedGym(updatedGym); setGyms((prev) => prev.map((gym) => (gym.id === updatedGym.id ? updatedGym : gym)), ); } catch (error) { log.error("Failed to update geofence settings", error); setGymMessage({ type: "error", text: "Failed to update geofence settings", }); } finally { setSavingGeofence(false); } }; const handleDeleteGym = async (gymId: string) => { if ( !window.confirm( "Are you sure you want to delete this gym? This action cannot be undone.", ) ) { return; } setDeletingGym(true); setGymMessage(null); try { 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); await fetchGyms(); } catch (error: any) { log.error("Failed to delete gym", error); const errorMessage = error.response?.data?.error || error.response?.data || error.message || "Failed to delete gym"; setGymMessage({ type: "error", text: errorMessage }); } finally { setDeletingGym(false); } }; const userRole = (user?.publicMetadata?.role as string) ?? "client"; const isSuperAdmin = userRole === "superAdmin"; return (

Settings

Manage your application settings and database.

{/* Gym Management */}

Gym Management

{isSuperAdmin ? "Select a gym to view details, create or delete gyms" : "Select your gym or proceed without a gym"}

{user ? ( <> Current role:{" "} {String(user.publicMetadata?.role ?? "unknown")} ) : ( "Loading user metadata..." )}

{isSuperAdmin && ( )}
{gymMessage && (
{gymMessage.type === "success" ? ( ) : ( )} {gymMessage.text}
)} {showCreateGym && (

Create Gym

{ e.preventDefault(); try { setCreatingGym(true); await axios.post("/api/gyms", { name: gymName.trim(), location: gymLocation.trim() || undefined, }); setGymMessage({ type: "success", text: "Gym created successfully", }); setShowCreateGym(false); setGymName(""); setGymLocation(""); fetchGyms(); } catch (error) { log.error("Failed to create gym", error); setGymMessage({ type: "error", text: "Failed to create gym", }); } finally { setCreatingGym(false); } }} >
setGymName(e.target.value)} className="w-full border border-gray-300 rounded px-3 py-2" required />
setGymLocation(e.target.value)} className="w-full border border-gray-300 rounded px-3 py-2" placeholder="Enter location" />
)}
{/* Gym List */}

Gyms

{gymsLoading ? (
Loading...
) : gyms.length === 0 ? (
No active gyms found.
) : (
{gyms.map((gym) => (
handleSelectGym(gym)} >
{gym.name}

{gym.location || "No location"}

{gym.status}
))}
)}
{/* Gym Details / Stats */}
{selectedGym ? (

{selectedGym.name} - Details

{isSuperAdmin && ( )}
{/* Gym Info */}

Location

{selectedGym.location || "Not specified"}

Status

{selectedGym.status}

Geofence

{selectedGym.geofenceEnabled === false ? "Disabled" : `${selectedGym.geofenceRadiusMeters ?? 30}m`}

{/* Geofence Settings */}
Attendance Geofence
setGeofenceLatitude(e.target.value)} className="w-full border border-gray-300 rounded px-3 py-2" placeholder="e.g. 37.7749" />
setGeofenceLongitude(e.target.value)} className="w-full border border-gray-300 rounded px-3 py-2" placeholder="e.g. -122.4194" />
setGeofenceRadiusMeters(e.target.value) } className="w-full border border-gray-300 rounded px-3 py-2" />

Default radius is 30m and geofence is enabled by default.

{/* Stats */} {statsLoading ? (
) : gymStats ? (
Total Users

{gymStats.totalUsers}

Active Clients

{gymStats.activeClients}

Check-ins (30d)

{gymStats.attendanceLast30Days}

Trainers

{gymStats.trainers}

) : null} {/* Membership Distribution */} {gymStats && (
Membership Distribution

{gymStats.membershipStats.basic}

Basic

{gymStats.membershipStats.premium}

Premium

{gymStats.membershipStats.vip}

VIP

)} {/* Membership Feature Access */}
Membership Feature Access
Feature Basic Premium VIP
Recommendations per month {MEMBERSHIP_FEATURES.basic.recommendationsPerMonth} Unlimited Unlimited
Nutrition tracking {MEMBERSHIP_FEATURES.basic.nutritionTracking ? "Yes" : "No"} {MEMBERSHIP_FEATURES.premium.nutritionTracking ? "Yes" : "No"} {MEMBERSHIP_FEATURES.vip.nutritionTracking ? "Yes" : "No"}
Hydration tracking {MEMBERSHIP_FEATURES.basic.hydrationTracking ? "Yes" : "No"} {MEMBERSHIP_FEATURES.premium.hydrationTracking ? "Yes" : "No"} {MEMBERSHIP_FEATURES.vip.hydrationTracking ? "Yes" : "No"}
Advanced statistics {MEMBERSHIP_FEATURES.basic.advancedStatistics ? "Yes" : "No"} {MEMBERSHIP_FEATURES.premium.advancedStatistics ? "Yes" : "No"} {MEMBERSHIP_FEATURES.vip.advancedStatistics ? "Yes" : "No"}
) : (

Select a gym to view details

)}
{/* Database Management */}

Database Management

Create backups and restore your database

{message && (
{message.type === "success" ? ( ) : ( )} {message.text}
)}
{loading ? ( ) : backups.length === 0 ? ( ) : ( backups.map((backup) => ( )) )}
Filename Size Created At Actions
Loading backups...
No backups found
{backup.name} {formatSize(backup.size)} {formatDate(backup.createdAt)}
); }