"use client"; import { useEffect, useState } from "react"; import { useRouter } from "next/navigation"; import { LogOut, RefreshCcw, RotateCcw, Save } from "lucide-react"; import { NoPermissionState } from "@/components/auth/no-permission-state"; import { useSuperAdminSession } from "@/components/auth/session-context"; import { PageHeader } from "@/components/dashboard/page-header"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; import { Textarea } from "@/components/ui/textarea"; import { useToast } from "@/components/ui/toast"; import { listSuperAdminSessions } from "@/lib/api/auth"; import { getSuperAdminSettings, getSuperAdminSettingsHistory, restoreSuperAdminSettingsHistory, updateSuperAdminSettings, } from "@/lib/api/superadmin"; import { logoutSuperAdmin, refreshSuperAdmin } from "@/lib/auth/client"; import { formatDateTime } from "@/lib/format"; import { SUPERADMIN_PERMISSIONS, hasPermission } from "@/lib/permissions"; import type { SessionItem, SessionsResponse, SuperAdminSettingsHistoryEntry, SuperAdminSettingsResponse, } from "@/types/api"; const EMPTY_SESSIONS: SessionsResponse = { items: [] }; export default function SettingsPage() { const { permissions } = useSuperAdminSession(); const [historyItems, setHistoryItems] = useState([]); const [sessions, setSessions] = useState([]); const [settingsResponse, setSettingsResponse] = useState(null); const [saving, setSaving] = useState(false); const [loading, setLoading] = useState(false); const { toast } = useToast(); const router = useRouter(); const canReadSettings = hasPermission(permissions, SUPERADMIN_PERMISSIONS.SETTINGS_READ); const canWriteSettings = hasPermission(permissions, SUPERADMIN_PERMISSIONS.SETTINGS_WRITE); const canManageSessions = hasPermission( permissions, SUPERADMIN_PERMISSIONS.SESSIONS_MANAGE, ); useEffect(() => { let active = true; const loadData = async () => { if (!canReadSettings) { setLoading(false); return; } try { const [sessionsResponse, nextSettings, nextHistory] = await Promise.all([ canManageSessions ? listSuperAdminSessions() : Promise.resolve(EMPTY_SESSIONS), getSuperAdminSettings(), getSuperAdminSettingsHistory({ page: 1, limit: 8, sortOrder: "desc" }), ]); if (!active) return; setSessions(sessionsResponse.items ?? []); setSettingsResponse(nextSettings); setHistoryItems(nextHistory.items ?? []); } catch (error) { if (!active) return; toast({ title: "Failed to load settings", description: String(error), variant: "danger" }); } }; void loadData(); return () => { active = false; }; }, [canManageSessions, canReadSettings, toast]); const settings = settingsResponse?.settings; const runtime = settingsResponse?.runtime; const updateField = >(key: K, value: NonNullable[K]) => { setSettingsResponse((prev) => prev ? { ...prev, settings: { ...prev.settings, [key]: value, }, } : prev, ); }; if (!canReadSettings) { return (
); } return (
{ if (!settings || !canWriteSettings) return; setSaving(true); try { const next = await updateSuperAdminSettings(settings); setSettingsResponse(next); const history = await getSuperAdminSettingsHistory({ page: 1, limit: 8, sortOrder: "desc" }); setHistoryItems(history.items ?? []); toast({ title: "Settings saved", description: "Central settings updated successfully.", variant: "success" }); } catch (error) { toast({ title: "Save failed", description: String(error), variant: "danger" }); } finally { setSaving(false); } }} disabled={!settings || !canWriteSettings || saving} > Save settings } />
Session controls {canManageSessions ? (
Visible sessions
{sessions.length}
) : null}
Session strategy
{runtime?.sessionStrategy ?? "httpOnly_cookies"}
Stored settings {canWriteSettings ? "Editable" : "Read only"}
updateField("siteName", event.target.value)} /> updateField("publicBaseUrl", event.target.value)} />
updateField("dashboardApiBaseUrl", event.target.value)} /> updateField( "corsOrigins", event.target.value .split(",") .map((value) => value.trim()) .filter(Boolean), ) } />