'use client' import { useState, useCallback, useEffect, useRef } from 'react' import { AnimatePresence, motion } from 'framer-motion' import { X, GearSix, Buildings, User } from '@phosphor-icons/react' import { Button } from '@ciphera-net/ui' import { useUnifiedSettings } from '@/lib/unified-settings-context' import { useAuth } from '@/lib/auth/context' import { useSite } from '@/lib/swr/dashboard' import { listSites, type Site } from '@/lib/api/sites' // Tab content components — Site import SiteGeneralTab from './tabs/SiteGeneralTab' import SiteGoalsTab from './tabs/SiteGoalsTab' import SiteVisibilityTab from './tabs/SiteVisibilityTab' import SitePrivacyTab from './tabs/SitePrivacyTab' import SiteBotSpamTab from './tabs/SiteBotSpamTab' import SiteReportsTab from './tabs/SiteReportsTab' import SiteIntegrationsTab from './tabs/SiteIntegrationsTab' // Tab content components — Workspace import WorkspaceGeneralTab from './tabs/WorkspaceGeneralTab' import WorkspaceBillingTab from './tabs/WorkspaceBillingTab' import WorkspaceMembersTab from './tabs/WorkspaceMembersTab' import WorkspaceNotificationsTab from './tabs/WorkspaceNotificationsTab' import WorkspaceAuditTab from './tabs/WorkspaceAuditTab' // Tab content components — Account import AccountProfileTab from './tabs/AccountProfileTab' import AccountSecurityTab from './tabs/AccountSecurityTab' import AccountDevicesTab from './tabs/AccountDevicesTab' // ─── Types ────────────────────────────────────────────────────── type SettingsContext = 'site' | 'workspace' | 'account' interface TabDef { id: string label: string } const SITE_TABS: TabDef[] = [ { id: 'general', label: 'General' }, { id: 'goals', label: 'Goals' }, { id: 'visibility', label: 'Visibility' }, { id: 'privacy', label: 'Privacy' }, { id: 'bot-spam', label: 'Bot & Spam' }, { id: 'reports', label: 'Reports' }, { id: 'integrations', label: 'Integrations' }, ] const WORKSPACE_TABS: TabDef[] = [ { id: 'general', label: 'General' }, { id: 'members', label: 'Members' }, { id: 'billing', label: 'Billing' }, { id: 'notifications', label: 'Notifications' }, { id: 'audit', label: 'Audit Log' }, ] const ACCOUNT_TABS: TabDef[] = [ { id: 'profile', label: 'Profile' }, { id: 'security', label: 'Security' }, { id: 'devices', label: 'Devices' }, ] // ─── Context Switcher ─────────────────────────────────────────── function ContextSwitcher({ active, onChange, activeSiteDomain, }: { active: SettingsContext onChange: (ctx: SettingsContext) => void activeSiteDomain: string | null }) { return (
{/* Site button — locked to current site, no dropdown */} {activeSiteDomain && ( )}
) } // ─── Tab Bar ──────────────────────────────────────────────────── function TabBar({ tabs, activeTab, onChange, }: { tabs: TabDef[] activeTab: string onChange: (id: string) => void }) { return (
{tabs.map(tab => ( ))}
) } // ─── Tab Content ──────────────────────────────────────────────── function ComingSoon({ label }: { label: string }) { return (

{label}

This section is being migrated. For now, use the existing settings page.

) } function TabContent({ context, activeTab, siteId, onDirtyChange, onRegisterSave, }: { context: SettingsContext activeTab: string siteId: string | null onDirtyChange: (dirty: boolean) => void onRegisterSave: (fn: () => Promise) => void }) { const dirtyProps = { onDirtyChange, onRegisterSave } // Site tabs if (context === 'site' && siteId) { switch (activeTab) { case 'general': return case 'goals': return case 'visibility': return case 'privacy': return case 'bot-spam': return case 'reports': return case 'integrations': return } } // Workspace tabs if (context === 'workspace') { switch (activeTab) { case 'general': return case 'billing': return case 'members': return case 'notifications': return case 'audit': return } } // Account tabs if (context === 'account') { switch (activeTab) { case 'profile': return case 'security': return case 'devices': return } } return null } // ─── Main Modal ───────────────────────────────────────────────── export default function UnifiedSettingsModal() { const { isOpen, openUnifiedSettings, closeUnifiedSettings: closeSettings, initialTab: initTab } = useUnifiedSettings() const { user } = useAuth() const [context, setContext] = useState('site') const [siteTabs, setSiteTabs] = useState('general') const [workspaceTabs, setWorkspaceTabs] = useState('general') const [accountTabs, setAccountTabs] = useState('profile') const [sites, setSites] = useState([]) const [activeSiteId, setActiveSiteId] = useState(null) // ─── Dirty state & pending navigation ──────────────────────── const isDirtyRef = useRef(false) const [isDirtyVisible, setIsDirtyVisible] = useState(false) const pendingActionRef = useRef<(() => void) | null>(null) const [hasPendingAction, setHasPendingAction] = useState(false) const saveHandlerRef = useRef<(() => Promise) | null>(null) const [saving, setSaving] = useState(false) const handleDirtyChange = useCallback((dirty: boolean) => { isDirtyRef.current = dirty setIsDirtyVisible(dirty) // If user saved and there was a pending action, execute it if (!dirty && pendingActionRef.current) { const action = pendingActionRef.current pendingActionRef.current = null setHasPendingAction(false) action() } }, []) const handleRegisterSave = useCallback((fn: () => Promise) => { saveHandlerRef.current = fn }, []) const handleSaveFromBar = useCallback(async () => { if (!saveHandlerRef.current) return setSaving(true) try { await saveHandlerRef.current() } finally { setSaving(false) } }, []) /** Run action if clean, or store as pending if dirty */ const guardedAction = useCallback((action: () => void) => { if (isDirtyRef.current) { pendingActionRef.current = action setHasPendingAction(true) } else { action() } }, []) const handleDiscard = useCallback(() => { isDirtyRef.current = false setIsDirtyVisible(false) setHasPendingAction(false) saveHandlerRef.current = null const action = pendingActionRef.current pendingActionRef.current = null action?.() }, []) // Apply initial tab when modal opens useEffect(() => { if (isOpen && initTab) { if (initTab.context) setContext(initTab.context) if (initTab.tab) { if (initTab.context === 'site') setSiteTabs(initTab.tab) else if (initTab.context === 'workspace') setWorkspaceTabs(initTab.tab) else if (initTab.context === 'account') setAccountTabs(initTab.tab) } } }, [isOpen, initTab]) // Reset dirty state when modal opens useEffect(() => { if (isOpen) { isDirtyRef.current = false pendingActionRef.current = null setHasPendingAction(false) } }, [isOpen]) // Detect site from URL and load sites list when modal opens useEffect(() => { if (!isOpen || !user?.org_id) return if (typeof window !== 'undefined') { const match = window.location.pathname.match(/\/sites\/([a-f0-9-]+)/) if (match) { setActiveSiteId(match[1]) setContext('site') } else { setActiveSiteId(null) if (!initTab?.context) setContext('workspace') } } listSites().then(data => { setSites(Array.isArray(data) ? data : []) }).catch(() => {}) }, [isOpen, user?.org_id]) // Global keyboard shortcuts: `,` toggles settings, Escape closes useEffect(() => { const handler = (e: KeyboardEvent) => { const tag = (e.target as HTMLElement)?.tagName if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return if (e.key === ',' && !e.metaKey && !e.ctrlKey && !e.altKey) { e.preventDefault() if (isOpen) guardedAction(closeSettings) else openUnifiedSettings() } if (e.key === 'Escape' && isOpen) { guardedAction(closeSettings) } } window.addEventListener('keydown', handler) return () => window.removeEventListener('keydown', handler) }, [isOpen, openUnifiedSettings, closeSettings, guardedAction]) const tabs = context === 'site' ? SITE_TABS : context === 'workspace' ? WORKSPACE_TABS : ACCOUNT_TABS const activeTab = context === 'site' ? siteTabs : context === 'workspace' ? workspaceTabs : accountTabs const setActiveTab = context === 'site' ? setSiteTabs : context === 'workspace' ? setWorkspaceTabs : setAccountTabs const handleContextChange = useCallback((ctx: SettingsContext) => { guardedAction(() => { setContext(ctx) if (ctx === 'site') setSiteTabs('general') else if (ctx === 'workspace') setWorkspaceTabs('general') else if (ctx === 'account') setAccountTabs('profile') }) }, [guardedAction]) const handleTabChange = useCallback((tabId: string) => { guardedAction(() => setActiveTab(tabId)) }, [guardedAction, setActiveTab]) const handleClose = useCallback(() => { guardedAction(closeSettings) }, [guardedAction, closeSettings]) const handleBackdropClick = useCallback(() => { guardedAction(closeSettings) }, [guardedAction, closeSettings]) return ( <> {/* Backdrop — fades in/out */}
{/* Glass panel — always mounted, instant show/hide, blur always composited */}
e.stopPropagation()} > {/* Content fades in/out independently — glass stays solid */} {isOpen && (
{/* Header */}

Settings

{/* Context Switcher */} s.id === activeSiteId)?.domain ?? null} /> {/* Tabs */}
{/* Content */}
{/* Save bar */} {isDirtyVisible && (
{hasPendingAction ? 'Save or discard to continue' : 'Unsaved changes'}
{hasPendingAction && ( )}
)}
)}
) }