mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-05 23:44:22 +00:00
251 lines
7.9 KiB
TypeScript
251 lines
7.9 KiB
TypeScript
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<Workspace>;
|
|
initialSettings: Partial<Workspace>;
|
|
hasUnsavedChanges: boolean;
|
|
}
|
|
|
|
const initialState: WorkspaceSettingsState = {
|
|
localSettings: {},
|
|
initialSettings: {},
|
|
hasUnsavedChanges: false,
|
|
};
|
|
|
|
function settingsReducer(
|
|
state: WorkspaceSettingsState,
|
|
action: SettingsAction<Partial<Workspace>>
|
|
): 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<Workspace> = {
|
|
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(
|
|
<K extends keyof Workspace>(key: K, value: Workspace[K]): void => {
|
|
dispatch({
|
|
type: SettingsActionType.UPDATE_LOCAL_SETTINGS,
|
|
payload: { [key]: value } as Partial<Workspace>,
|
|
});
|
|
},
|
|
[]
|
|
);
|
|
|
|
const handleSubmit = async (): Promise<void> => {
|
|
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 (
|
|
<Modal
|
|
opened={settingsModalVisible}
|
|
onClose={handleClose}
|
|
title={<Title order={2}>Workspace Settings</Title>}
|
|
centered
|
|
size="lg"
|
|
>
|
|
<Stack gap="xl">
|
|
{state.hasUnsavedChanges && (
|
|
<Badge color="yellow" variant="light">
|
|
Unsaved Changes
|
|
</Badge>
|
|
)}
|
|
|
|
<Accordion
|
|
defaultValue={['general', 'appearance', 'editor', 'git', 'danger']}
|
|
multiple
|
|
styles={(theme) => ({
|
|
...getAccordionStyles(theme),
|
|
chevron: {
|
|
'&[data-rotate]': {
|
|
transform: 'rotate(180deg)',
|
|
},
|
|
},
|
|
})}
|
|
>
|
|
<Accordion.Item value="general">
|
|
<AccordionControl>General</AccordionControl>
|
|
<Accordion.Panel>
|
|
<GeneralSettings
|
|
name={state.localSettings.name || ''}
|
|
onInputChange={handleInputChange}
|
|
/>
|
|
</Accordion.Panel>
|
|
</Accordion.Item>
|
|
|
|
<Accordion.Item value="appearance">
|
|
<AccordionControl>Appearance</AccordionControl>
|
|
<Accordion.Panel>
|
|
<AppearanceSettings />
|
|
</Accordion.Panel>
|
|
</Accordion.Item>
|
|
|
|
<Accordion.Item value="editor">
|
|
<AccordionControl>Editor</AccordionControl>
|
|
<Accordion.Panel>
|
|
<EditorSettings
|
|
autoSave={state.localSettings.autoSave || false}
|
|
onAutoSaveChange={(value: boolean) =>
|
|
handleInputChange('autoSave', value)
|
|
}
|
|
showHiddenFiles={state.localSettings.showHiddenFiles || false}
|
|
onShowHiddenFilesChange={(value: boolean) =>
|
|
handleInputChange('showHiddenFiles', value)
|
|
}
|
|
/>
|
|
</Accordion.Panel>
|
|
</Accordion.Item>
|
|
|
|
<Accordion.Item value="git">
|
|
<AccordionControl>Git Integration</AccordionControl>
|
|
<Accordion.Panel>
|
|
<GitSettings
|
|
gitEnabled={state.localSettings.gitEnabled || false}
|
|
gitUrl={state.localSettings.gitUrl || ''}
|
|
gitUser={state.localSettings.gitUser || ''}
|
|
gitToken={state.localSettings.gitToken || ''}
|
|
gitAutoCommit={state.localSettings.gitAutoCommit || false}
|
|
gitCommitMsgTemplate={
|
|
state.localSettings.gitCommitMsgTemplate || ''
|
|
}
|
|
gitCommitName={state.localSettings.gitCommitName || ''}
|
|
gitCommitEmail={state.localSettings.gitCommitEmail || ''}
|
|
onInputChange={handleInputChange}
|
|
/>
|
|
</Accordion.Panel>
|
|
</Accordion.Item>
|
|
|
|
<Accordion.Item value="danger">
|
|
<AccordionControl>Danger Zone</AccordionControl>
|
|
<Accordion.Panel>
|
|
<DangerZoneSettings />
|
|
</Accordion.Panel>
|
|
</Accordion.Item>
|
|
</Accordion>
|
|
|
|
<Group justify="flex-end">
|
|
<Button variant="default" onClick={handleClose}>
|
|
Cancel
|
|
</Button>
|
|
<Button onClick={() => void handleSubmit()}>Save Changes</Button>
|
|
</Group>
|
|
</Stack>
|
|
</Modal>
|
|
);
|
|
};
|
|
|
|
export default WorkspaceSettings;
|