import React, { useMemo, useState, useEffect } from 'react'; import { Modal, Text, ActionIcon, Tooltip } from '@mantine/core'; import { useNavigate, useLocation } from 'react-router-dom'; import LocalIcon from '@app/components/shared/LocalIcon'; import { createConfigNavSections } from '@app/components/shared/config/configNavSections'; import { NavKey, VALID_NAV_KEYS } from '@app/components/shared/config/types'; import { useAppConfig } from '@app/contexts/AppConfigContext'; import '@app/components/shared/AppConfigModal.css'; import { useIsMobile } from '@app/hooks/useIsMobile'; import { Z_INDEX_OVER_FULLSCREEN_SURFACE, Z_INDEX_OVER_CONFIG_MODAL } from '@app/styles/zIndex'; interface AppConfigModalProps { opened: boolean; onClose: () => void; } const AppConfigModal: React.FC = ({ opened, onClose }) => { const [active, setActive] = useState('general'); const isMobile = useIsMobile(); const navigate = useNavigate(); const location = useLocation(); const { config } = useAppConfig(); // Extract section from URL path (e.g., /settings/people -> people) const getSectionFromPath = (pathname: string): NavKey | null => { const match = pathname.match(/\/settings\/([^/]+)/); if (match && match[1]) { const section = match[1] as NavKey; return VALID_NAV_KEYS.includes(section as NavKey) ? section : null; } return null; }; // Sync active state with URL path useEffect(() => { const section = getSectionFromPath(location.pathname); if (opened && section) { setActive(section); } else if (opened && location.pathname.startsWith('/settings') && !section) { // If at /settings without a section, redirect to general navigate('/settings/general', { replace: true }); } }, [location.pathname, opened, navigate]); // Handle custom events for backwards compatibility useEffect(() => { const handler = (ev: Event) => { const detail = (ev as CustomEvent).detail as { key?: NavKey } | undefined; if (detail?.key) { navigate(`/settings/${detail.key}`); } }; window.addEventListener('appConfig:navigate', handler as EventListener); return () => window.removeEventListener('appConfig:navigate', handler as EventListener); }, [navigate]); const colors = useMemo(() => ({ navBg: 'var(--modal-nav-bg)', sectionTitle: 'var(--modal-nav-section-title)', navItem: 'var(--modal-nav-item)', navItemActive: 'var(--modal-nav-item-active)', navItemActiveBg: 'var(--modal-nav-item-active-bg)', contentBg: 'var(--modal-content-bg)', headerBorder: 'var(--modal-header-border)', }), []); // Get isAdmin and runningEE from app config const isAdmin = config?.isAdmin ?? false; const runningEE = config?.runningEE ?? false; console.log('[AppConfigModal] Config:', { isAdmin, runningEE, fullConfig: config }); // Left navigation structure and icons const configNavSections = useMemo(() => createConfigNavSections( isAdmin, runningEE ), [isAdmin, runningEE] ); const activeLabel = useMemo(() => { for (const section of configNavSections) { const found = section.items.find(i => i.key === active); if (found) return found.label; } return ''; }, [configNavSections, active]); const activeComponent = useMemo(() => { for (const section of configNavSections) { const found = section.items.find(i => i.key === active); if (found) return found.component; } return null; }, [configNavSections, active]); const handleClose = () => { // Navigate back to home when closing modal navigate('/', { replace: true }); onClose(); }; return (
{/* Left navigation */}
{configNavSections.map(section => (
{!isMobile && ( {section.title} )}
{section.items.map(item => { const isActive = active === item.key; const isDisabled = item.disabled ?? false; const color = isActive ? colors.navItemActive : colors.navItem; const iconSize = isMobile ? 28 : 18; const navItemContent = (
{ if (!isDisabled) { setActive(item.key); navigate(`/settings/${item.key}`); } }} className={`modal-nav-item ${isMobile ? 'mobile' : ''}`} style={{ background: isActive ? colors.navItemActiveBg : 'transparent', opacity: isDisabled ? 0.5 : 1, cursor: isDisabled ? 'not-allowed' : 'pointer', }} data-tour={`admin-${item.key}-nav`} > {!isMobile && ( {item.label} )}
); return isDisabled && item.disabledTooltip ? ( {navItemContent} ) : ( {navItemContent} ); })}
))}
{/* Right content */}
{/* Sticky header with section title and small close button */}
{activeLabel}
{activeComponent}
); }; export default AppConfigModal;