Migrate workspace settings to ts

This commit is contained in:
2025-05-18 12:21:18 +02:00
parent 7044e42e94
commit 9125cbdad3
8 changed files with 118 additions and 48 deletions

View File

@@ -1,7 +1,11 @@
import React from 'react'; import React from 'react';
import { Accordion, Title } from '@mantine/core'; import { Accordion, Title } from '@mantine/core';
const AccordionControl = ({ children }) => ( interface AccordionControlProps {
children: React.ReactNode;
}
const AccordionControl: React.FC<AccordionControlProps> = ({ children }) => (
<Accordion.Control> <Accordion.Control>
<Title order={4}>{children}</Title> <Title order={4}>{children}</Title>
</Accordion.Control> </Accordion.Control>

View File

@@ -1,12 +1,21 @@
import React from 'react'; import React from 'react';
import { Text, Switch, Group, Box, Title } from '@mantine/core'; import { Text, Switch, Group, Box } from '@mantine/core';
import { useWorkspace } from '../../../contexts/WorkspaceContext'; import { useWorkspace } from '../../../contexts/WorkspaceContext';
import { Theme } from '@/types/theme';
const AppearanceSettings = ({ themeSettings, onThemeChange }) => { interface AppearanceSettingsProps {
themeSettings?: Theme;
onThemeChange: (newTheme: Theme) => void;
}
const AppearanceSettings: React.FC<AppearanceSettingsProps> = ({
themeSettings,
onThemeChange,
}) => {
const { colorScheme, updateColorScheme } = useWorkspace(); const { colorScheme, updateColorScheme } = useWorkspace();
const handleThemeChange = () => { const handleThemeChange = (): void => {
const newTheme = colorScheme === 'dark' ? 'light' : 'dark'; const newTheme = colorScheme === 'dark' ? Theme.Light : Theme.Dark;
updateColorScheme(newTheme); updateColorScheme(newTheme);
onThemeChange(newTheme); onThemeChange(newTheme);
}; };

View File

@@ -4,13 +4,13 @@ import DeleteWorkspaceModal from '../../modals/workspace/DeleteWorkspaceModal';
import { useWorkspace } from '../../../contexts/WorkspaceContext'; import { useWorkspace } from '../../../contexts/WorkspaceContext';
import { useModalContext } from '../../../contexts/ModalContext'; import { useModalContext } from '../../../contexts/ModalContext';
const DangerZoneSettings = () => { const DangerZoneSettings: React.FC = () => {
const { currentWorkspace, workspaces, deleteCurrentWorkspace } = const { currentWorkspace, workspaces, deleteCurrentWorkspace } =
useWorkspace(); useWorkspace();
const { setSettingsModalVisible } = useModalContext(); const { setSettingsModalVisible } = useModalContext();
const [deleteModalOpened, setDeleteModalOpened] = useState(false); const [deleteModalOpened, setDeleteModalOpened] = useState<boolean>(false);
const handleDelete = async () => { const handleDelete = async (): Promise<void> => {
await deleteCurrentWorkspace(); await deleteCurrentWorkspace();
setDeleteModalOpened(false); setDeleteModalOpened(false);
setSettingsModalVisible(false); setSettingsModalVisible(false);

View File

@@ -1,7 +1,14 @@
import React from 'react'; import React from 'react';
import { Text, Switch, Tooltip, Group, Box } from '@mantine/core'; import { Text, Switch, Tooltip, Group, Box } from '@mantine/core';
const EditorSettings = ({ interface EditorSettingsProps {
autoSave: boolean;
showHiddenFiles: boolean;
onAutoSaveChange: (value: boolean) => void;
onShowHiddenFilesChange: (value: boolean) => void;
}
const EditorSettings: React.FC<EditorSettingsProps> = ({
autoSave, autoSave,
showHiddenFiles, showHiddenFiles,
onAutoSaveChange, onAutoSaveChange,

View File

@@ -1,7 +1,16 @@
import React from 'react'; import React from 'react';
import { Title, Box, TextInput, Text, Grid } from '@mantine/core'; import { Box, TextInput, Text, Grid } from '@mantine/core';
import { Workspace } from '@/types/workspace';
const GeneralSettings = ({ name, onInputChange }) => { interface GeneralSettingsProps {
name: string;
onInputChange: (key: keyof Workspace, value: string) => void;
}
const GeneralSettings: React.FC<GeneralSettingsProps> = ({
name,
onInputChange,
}) => {
return ( return (
<Box mb="md"> <Box mb="md">
<Grid gutter="md" align="center"> <Grid gutter="md" align="center">
@@ -10,7 +19,7 @@ const GeneralSettings = ({ name, onInputChange }) => {
</Grid.Col> </Grid.Col>
<Grid.Col span={6}> <Grid.Col span={6}>
<TextInput <TextInput
value={name || ''} value={name}
onChange={(event) => onChange={(event) =>
onInputChange('name', event.currentTarget.value) onInputChange('name', event.currentTarget.value)
} }

View File

@@ -8,8 +8,21 @@ import {
Group, Group,
Grid, Grid,
} from '@mantine/core'; } from '@mantine/core';
import { Workspace } from '@/types/workspace';
const GitSettings = ({ interface GitSettingsProps {
gitEnabled: boolean;
gitUrl: string;
gitUser: string;
gitToken: string;
gitAutoCommit: boolean;
gitCommitMsgTemplate: string;
gitCommitName: string;
gitCommitEmail: string;
onInputChange: (key: keyof Workspace, value: any) => void;
}
const GitSettings: React.FC<GitSettingsProps> = ({
gitEnabled, gitEnabled,
gitUrl, gitUrl,
gitUser, gitUser,
@@ -21,7 +34,7 @@ const GitSettings = ({
onInputChange, onInputChange,
}) => { }) => {
return ( return (
<Stack spacing="md"> <Stack gap="md">
<Grid gutter="md" align="center"> <Grid gutter="md" align="center">
<Grid.Col span={6}> <Grid.Col span={6}>
<Text size="sm">Enable Git Repository</Text> <Text size="sm">Enable Git Repository</Text>

View File

@@ -17,23 +17,35 @@ import GeneralSettings from './GeneralSettings';
import { useModalContext } from '../../../contexts/ModalContext'; import { useModalContext } from '../../../contexts/ModalContext';
import DangerZoneSettings from './DangerZoneSettings'; import DangerZoneSettings from './DangerZoneSettings';
import AccordionControl from '../AccordionControl'; import AccordionControl from '../AccordionControl';
import { SettingsActionType, SettingsAction } from '../../../types/settings';
import { Workspace } from '../../../types/workspace';
const initialState = { // State and reducer for workspace settings
interface WorkspaceSettingsState {
localSettings: Partial<Workspace>;
initialSettings: Partial<Workspace>;
hasUnsavedChanges: boolean;
}
const initialState: WorkspaceSettingsState = {
localSettings: {}, localSettings: {},
initialSettings: {}, initialSettings: {},
hasUnsavedChanges: false, hasUnsavedChanges: false,
}; };
function settingsReducer(state, action) { function settingsReducer(
state: WorkspaceSettingsState,
action: SettingsAction<Partial<Workspace>>
): WorkspaceSettingsState {
switch (action.type) { switch (action.type) {
case 'INIT_SETTINGS': case SettingsActionType.INIT_SETTINGS:
return { return {
...state, ...state,
localSettings: action.payload, localSettings: action.payload || {},
initialSettings: action.payload, initialSettings: action.payload || {},
hasUnsavedChanges: false, hasUnsavedChanges: false,
}; };
case 'UPDATE_LOCAL_SETTINGS': case SettingsActionType.UPDATE_LOCAL_SETTINGS:
const newLocalSettings = { ...state.localSettings, ...action.payload }; const newLocalSettings = { ...state.localSettings, ...action.payload };
const hasChanges = const hasChanges =
JSON.stringify(newLocalSettings) !== JSON.stringify(newLocalSettings) !==
@@ -43,7 +55,7 @@ function settingsReducer(state, action) {
localSettings: newLocalSettings, localSettings: newLocalSettings,
hasUnsavedChanges: hasChanges, hasUnsavedChanges: hasChanges,
}; };
case 'MARK_SAVED': case SettingsActionType.MARK_SAVED:
return { return {
...state, ...state,
initialSettings: state.localSettings, initialSettings: state.localSettings,
@@ -54,16 +66,16 @@ function settingsReducer(state, action) {
} }
} }
const WorkspaceSettings = () => { const WorkspaceSettings: React.FC = () => {
const { currentWorkspace, updateSettings } = useWorkspace(); const { currentWorkspace, updateSettings } = useWorkspace();
const { settingsModalVisible, setSettingsModalVisible } = useModalContext(); const { settingsModalVisible, setSettingsModalVisible } = useModalContext();
const [state, dispatch] = useReducer(settingsReducer, initialState); const [state, dispatch] = useReducer(settingsReducer, initialState);
const isInitialMount = useRef(true); const isInitialMount = useRef<boolean>(true);
useEffect(() => { useEffect(() => {
if (isInitialMount.current) { if (isInitialMount.current && currentWorkspace) {
isInitialMount.current = false; isInitialMount.current = false;
const settings = { const settings: Partial<Workspace> = {
name: currentWorkspace.name, name: currentWorkspace.name,
theme: currentWorkspace.theme, theme: currentWorkspace.theme,
autoSave: currentWorkspace.autoSave, autoSave: currentWorkspace.autoSave,
@@ -77,15 +89,21 @@ const WorkspaceSettings = () => {
gitCommitName: currentWorkspace.gitCommitName, gitCommitName: currentWorkspace.gitCommitName,
gitCommitEmail: currentWorkspace.gitCommitEmail, gitCommitEmail: currentWorkspace.gitCommitEmail,
}; };
dispatch({ type: 'INIT_SETTINGS', payload: settings }); dispatch({ type: SettingsActionType.INIT_SETTINGS, payload: settings });
} }
}, [currentWorkspace]); }, [currentWorkspace]);
const handleInputChange = useCallback((key, value) => { const handleInputChange = useCallback(
dispatch({ type: 'UPDATE_LOCAL_SETTINGS', payload: { [key]: value } }); (key: keyof Workspace, value: any): void => {
}, []); dispatch({
type: SettingsActionType.UPDATE_LOCAL_SETTINGS,
payload: { [key]: value } as Partial<Workspace>,
});
},
[]
);
const handleSubmit = async () => { const handleSubmit = async (): Promise<void> => {
try { try {
if (!state.localSettings.name?.trim()) { if (!state.localSettings.name?.trim()) {
notifications.show({ notifications.show({
@@ -96,7 +114,7 @@ const WorkspaceSettings = () => {
} }
await updateSettings(state.localSettings); await updateSettings(state.localSettings);
dispatch({ type: 'MARK_SAVED' }); dispatch({ type: SettingsActionType.MARK_SAVED });
notifications.show({ notifications.show({
message: 'Settings saved successfully', message: 'Settings saved successfully',
color: 'green', color: 'green',
@@ -105,7 +123,9 @@ const WorkspaceSettings = () => {
} catch (error) { } catch (error) {
console.error('Failed to save settings:', error); console.error('Failed to save settings:', error);
notifications.show({ notifications.show({
message: 'Failed to save settings: ' + error.message, message:
'Failed to save settings: ' +
(error instanceof Error ? error.message : String(error)),
color: 'red', color: 'red',
}); });
} }
@@ -123,7 +143,7 @@ const WorkspaceSettings = () => {
centered centered
size="lg" size="lg"
> >
<Stack spacing="xl"> <Stack gap="xl">
{state.hasUnsavedChanges && ( {state.hasUnsavedChanges && (
<Badge color="yellow" variant="light"> <Badge color="yellow" variant="light">
Unsaved Changes Unsaved Changes
@@ -133,7 +153,7 @@ const WorkspaceSettings = () => {
<Accordion <Accordion
defaultValue={['general', 'appearance', 'editor', 'git', 'danger']} defaultValue={['general', 'appearance', 'editor', 'git', 'danger']}
multiple multiple
styles={(theme) => ({ styles={(theme: any) => ({
control: { control: {
paddingTop: theme.spacing.md, paddingTop: theme.spacing.md,
paddingBottom: theme.spacing.md, paddingBottom: theme.spacing.md,
@@ -162,7 +182,7 @@ const WorkspaceSettings = () => {
<AccordionControl>General</AccordionControl> <AccordionControl>General</AccordionControl>
<Accordion.Panel> <Accordion.Panel>
<GeneralSettings <GeneralSettings
name={state.localSettings.name} name={state.localSettings.name || ''}
onInputChange={handleInputChange} onInputChange={handleInputChange}
/> />
</Accordion.Panel> </Accordion.Panel>
@@ -173,7 +193,7 @@ const WorkspaceSettings = () => {
<Accordion.Panel> <Accordion.Panel>
<AppearanceSettings <AppearanceSettings
themeSettings={state.localSettings.theme} themeSettings={state.localSettings.theme}
onThemeChange={(newTheme) => onThemeChange={(newTheme: string) =>
handleInputChange('theme', newTheme) handleInputChange('theme', newTheme)
} }
/> />
@@ -184,12 +204,12 @@ const WorkspaceSettings = () => {
<AccordionControl>Editor</AccordionControl> <AccordionControl>Editor</AccordionControl>
<Accordion.Panel> <Accordion.Panel>
<EditorSettings <EditorSettings
autoSave={state.localSettings.autoSave} autoSave={state.localSettings.autoSave || false}
onAutoSaveChange={(value) => onAutoSaveChange={(value: boolean) =>
handleInputChange('autoSave', value) handleInputChange('autoSave', value)
} }
showHiddenFiles={state.localSettings.showHiddenFiles} showHiddenFiles={state.localSettings.showHiddenFiles || false}
onShowHiddenFilesChange={(value) => onShowHiddenFilesChange={(value: boolean) =>
handleInputChange('showHiddenFiles', value) handleInputChange('showHiddenFiles', value)
} }
/> />
@@ -200,14 +220,16 @@ const WorkspaceSettings = () => {
<AccordionControl>Git Integration</AccordionControl> <AccordionControl>Git Integration</AccordionControl>
<Accordion.Panel> <Accordion.Panel>
<GitSettings <GitSettings
gitEnabled={state.localSettings.gitEnabled} gitEnabled={state.localSettings.gitEnabled || false}
gitUrl={state.localSettings.gitUrl} gitUrl={state.localSettings.gitUrl || ''}
gitUser={state.localSettings.gitUser} gitUser={state.localSettings.gitUser || ''}
gitToken={state.localSettings.gitToken} gitToken={state.localSettings.gitToken || ''}
gitAutoCommit={state.localSettings.gitAutoCommit} gitAutoCommit={state.localSettings.gitAutoCommit || false}
gitCommitMsgTemplate={state.localSettings.gitCommitMsgTemplate} gitCommitMsgTemplate={
gitCommitName={state.localSettings.gitCommitName} state.localSettings.gitCommitMsgTemplate || ''
gitCommitEmail={state.localSettings.gitCommitEmail} }
gitCommitName={state.localSettings.gitCommitName || ''}
gitCommitEmail={state.localSettings.gitCommitEmail || ''}
onInputChange={handleInputChange} onInputChange={handleInputChange}
/> />
</Accordion.Panel> </Accordion.Panel>

View File

@@ -3,23 +3,29 @@ import { Theme } from './theme';
export interface WorkspaceSettings { export interface WorkspaceSettings {
theme: Theme; theme: Theme;
autoSave: boolean; autoSave: boolean;
showHiddenFiles: boolean;
gitEnabled: boolean; gitEnabled: boolean;
gitUrl: string; gitUrl: string;
gitUser: string; gitUser: string;
gitToken: string; gitToken: string;
gitAutoCommit: boolean; gitAutoCommit: boolean;
gitCommitMsgTemplate: string; gitCommitMsgTemplate: string;
gitCommitName: string;
gitCommitEmail: string;
} }
export const DEFAULT_WORKSPACE_SETTINGS: WorkspaceSettings = { export const DEFAULT_WORKSPACE_SETTINGS: WorkspaceSettings = {
theme: Theme.Light, theme: Theme.Light,
autoSave: false, autoSave: false,
showHiddenFiles: false,
gitEnabled: false, gitEnabled: false,
gitUrl: '', gitUrl: '',
gitUser: '', gitUser: '',
gitToken: '', gitToken: '',
gitAutoCommit: false, gitAutoCommit: false,
gitCommitMsgTemplate: '${action} ${filename}', gitCommitMsgTemplate: '${action} ${filename}',
gitCommitName: '',
gitCommitEmail: '',
}; };
export interface Workspace extends WorkspaceSettings { export interface Workspace extends WorkspaceSettings {