import React, { useReducer, useEffect, useCallback } from 'react'; import { Modal, Badge, Button, Group, Title, Stack, Accordion, } from '@mantine/core'; import { notifications } from '@mantine/notifications'; import { useWorkspace } from '../../../hooks/useWorkspace'; import AppearanceSettings from './AppearanceSettings'; import EditorSettings from './EditorSettings'; import GitSettings from './GitSettings'; import GeneralSettings from './GeneralSettings'; import { useModalContext } from '../../../contexts/ModalContext'; import DangerZoneSettings from './DangerZoneSettings'; import AccordionControl from '../AccordionControl'; import { type Theme, type Workspace, type SettingsAction, SettingsActionType, } from '@/types/models'; import { getAccordionStyles } from '@/utils/themeStyles'; // State and reducer for workspace settings interface WorkspaceSettingsState { localSettings: Partial; initialSettings: Partial; hasUnsavedChanges: boolean; } const initialState: WorkspaceSettingsState = { localSettings: {}, initialSettings: {}, hasUnsavedChanges: false, }; function settingsReducer( state: WorkspaceSettingsState, action: SettingsAction> ): WorkspaceSettingsState { switch (action.type) { case SettingsActionType.INIT_SETTINGS: return { ...state, localSettings: action.payload || {}, initialSettings: action.payload || {}, hasUnsavedChanges: false, }; case SettingsActionType.UPDATE_LOCAL_SETTINGS: { const newLocalSettings = { ...state.localSettings, ...action.payload }; const hasChanges = JSON.stringify(newLocalSettings) !== JSON.stringify(state.initialSettings); return { ...state, localSettings: newLocalSettings, hasUnsavedChanges: hasChanges, }; } case SettingsActionType.MARK_SAVED: return { ...state, initialSettings: state.localSettings, hasUnsavedChanges: false, }; default: return state; } } const WorkspaceSettings: React.FC = () => { const { currentWorkspace, updateSettings, updateColorScheme, colorScheme } = useWorkspace(); const { settingsModalVisible, setSettingsModalVisible } = useModalContext(); const [state, dispatch] = useReducer(settingsReducer, initialState); useEffect(() => { if (currentWorkspace && settingsModalVisible) { const settings: Partial = { name: currentWorkspace.name, theme: currentWorkspace.theme, autoSave: currentWorkspace.autoSave, showHiddenFiles: currentWorkspace.showHiddenFiles, gitEnabled: currentWorkspace.gitEnabled, gitUrl: currentWorkspace.gitUrl, gitUser: currentWorkspace.gitUser, gitToken: currentWorkspace.gitToken, gitAutoCommit: currentWorkspace.gitAutoCommit, gitCommitMsgTemplate: currentWorkspace.gitCommitMsgTemplate, gitCommitName: currentWorkspace.gitCommitName, gitCommitEmail: currentWorkspace.gitCommitEmail, }; dispatch({ type: SettingsActionType.INIT_SETTINGS, payload: settings }); } }, [currentWorkspace, settingsModalVisible]); const handleInputChange = useCallback( (key: K, value: Workspace[K]): void => { dispatch({ type: SettingsActionType.UPDATE_LOCAL_SETTINGS, payload: { [key]: value } as Partial, }); }, [] ); const handleSubmit = async (): Promise => { try { if (!state.localSettings.name?.trim()) { notifications.show({ message: 'Workspace name cannot be empty', color: 'red', }); return; } // Save with current Mantine theme const settingsToSave = { ...state.localSettings, theme: colorScheme as Theme, }; await updateSettings(settingsToSave); dispatch({ type: SettingsActionType.MARK_SAVED }); notifications.show({ message: 'Settings saved successfully', color: 'green', }); setSettingsModalVisible(false); } catch (error) { console.error('Failed to save settings:', error); notifications.show({ message: 'Failed to save settings: ' + (error instanceof Error ? error.message : String(error)), color: 'red', }); } }; const handleClose = useCallback(() => { // Revert theme to saved state if (state.initialSettings.theme) { updateColorScheme(state.initialSettings.theme); } setSettingsModalVisible(false); }, [setSettingsModalVisible, state.initialSettings.theme, updateColorScheme]); return ( Workspace Settings} centered size="lg" > {state.hasUnsavedChanges && ( Unsaved Changes )} ({ ...getAccordionStyles(theme), chevron: { '&[data-rotate]': { transform: 'rotate(180deg)', }, }, })} > General Appearance Editor handleInputChange('autoSave', value) } showHiddenFiles={state.localSettings.showHiddenFiles || false} onShowHiddenFilesChange={(value: boolean) => handleInputChange('showHiddenFiles', value) } /> Git Integration Danger Zone ); }; export default WorkspaceSettings;