diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 75040d1..5e6baad 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -2,8 +2,8 @@ import React from 'react'; import { MantineProvider, ColorSchemeScript } from '@mantine/core'; import { Notifications } from '@mantine/notifications'; import { ModalsProvider } from '@mantine/modals'; -import Layout from './components/Layout'; -import LoginPage from './components/LoginPage'; +import Layout from './components/layout/Layout'; +import LoginPage from './components/auth/LoginPage'; import { WorkspaceProvider } from './contexts/WorkspaceContext'; import { ModalProvider } from './contexts/ModalContext'; import { AuthProvider, useAuth } from './contexts/AuthContext'; diff --git a/frontend/src/components/LoginPage.jsx b/frontend/src/components/auth/LoginPage.jsx similarity index 96% rename from frontend/src/components/LoginPage.jsx rename to frontend/src/components/auth/LoginPage.jsx index c5c55f1..590ba33 100644 --- a/frontend/src/components/LoginPage.jsx +++ b/frontend/src/components/auth/LoginPage.jsx @@ -9,7 +9,7 @@ import { Text, Stack, } from '@mantine/core'; -import { useAuth } from '../contexts/AuthContext'; +import { useAuth } from '../../contexts/AuthContext'; const LoginPage = () => { const [email, setEmail] = useState(''); diff --git a/frontend/src/components/ContentView.jsx b/frontend/src/components/editor/ContentView.jsx similarity index 91% rename from frontend/src/components/ContentView.jsx rename to frontend/src/components/editor/ContentView.jsx index 24e686f..11eda30 100644 --- a/frontend/src/components/ContentView.jsx +++ b/frontend/src/components/editor/ContentView.jsx @@ -2,8 +2,8 @@ import React from 'react'; import { Text, Center } from '@mantine/core'; import Editor from './Editor'; import MarkdownPreview from './MarkdownPreview'; -import { getFileUrl } from '../services/api'; -import { isImageFile } from '../utils/fileHelpers'; +import { getFileUrl } from '../../services/api'; +import { isImageFile } from '../../utils/fileHelpers'; const ContentView = ({ activeTab, diff --git a/frontend/src/components/Editor.jsx b/frontend/src/components/editor/Editor.jsx similarity index 97% rename from frontend/src/components/Editor.jsx rename to frontend/src/components/editor/Editor.jsx index 902bb9b..47ec20d 100644 --- a/frontend/src/components/Editor.jsx +++ b/frontend/src/components/editor/Editor.jsx @@ -5,7 +5,7 @@ import { EditorView, keymap } from '@codemirror/view'; import { markdown } from '@codemirror/lang-markdown'; import { defaultKeymap } from '@codemirror/commands'; import { oneDark } from '@codemirror/theme-one-dark'; -import { useWorkspace } from '../contexts/WorkspaceContext'; +import { useWorkspace } from '../../contexts/WorkspaceContext'; const Editor = ({ content, handleContentChange, handleSave, selectedFile }) => { const { colorScheme } = useWorkspace(); diff --git a/frontend/src/components/MarkdownPreview.jsx b/frontend/src/components/editor/MarkdownPreview.jsx similarity index 96% rename from frontend/src/components/MarkdownPreview.jsx rename to frontend/src/components/editor/MarkdownPreview.jsx index 90a8402..51b80ee 100644 --- a/frontend/src/components/MarkdownPreview.jsx +++ b/frontend/src/components/editor/MarkdownPreview.jsx @@ -8,8 +8,8 @@ import rehypeReact from 'rehype-react'; import rehypePrism from 'rehype-prism'; import * as prod from 'react/jsx-runtime'; import { notifications } from '@mantine/notifications'; -import { remarkWikiLinks } from '../utils/remarkWikiLinks'; -import { useWorkspace } from '../contexts/WorkspaceContext'; +import { remarkWikiLinks } from '../../utils/remarkWikiLinks'; +import { useWorkspace } from '../../contexts/WorkspaceContext'; const MarkdownPreview = ({ content, handleFileSelect }) => { const [processedContent, setProcessedContent] = useState(null); diff --git a/frontend/src/components/FileActions.jsx b/frontend/src/components/files/FileActions.jsx similarity index 94% rename from frontend/src/components/FileActions.jsx rename to frontend/src/components/files/FileActions.jsx index b387022..d8b4f18 100644 --- a/frontend/src/components/FileActions.jsx +++ b/frontend/src/components/files/FileActions.jsx @@ -6,8 +6,8 @@ import { IconGitPullRequest, IconGitCommit, } from '@tabler/icons-react'; -import { useModalContext } from '../contexts/ModalContext'; -import { useWorkspace } from '../contexts/WorkspaceContext'; +import { useModalContext } from '../../contexts/ModalContext'; +import { useWorkspace } from '../../contexts/WorkspaceContext'; const FileActions = ({ handlePullChanges, selectedFile }) => { const { settings } = useWorkspace(); diff --git a/frontend/src/components/FileTree.jsx b/frontend/src/components/files/FileTree.jsx similarity index 100% rename from frontend/src/components/FileTree.jsx rename to frontend/src/components/files/FileTree.jsx diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/layout/Header.jsx similarity index 61% rename from frontend/src/components/Header.jsx rename to frontend/src/components/layout/Header.jsx index be18fd8..7b06bae 100644 --- a/frontend/src/components/Header.jsx +++ b/frontend/src/components/layout/Header.jsx @@ -1,8 +1,8 @@ import React from 'react'; import { Group, Text } from '@mantine/core'; -import UserMenu from './UserMenu'; -import WorkspaceSwitcher from './WorkspaceSwitcher'; -import Settings from './Settings'; +import UserMenu from '../navigation/UserMenu'; +import WorkspaceSwitcher from '../navigation/WorkspaceSwitcher'; +import WorkspaceSettings from '../settings/workspace/WorkspaceSettings'; const Header = () => { return ( @@ -14,7 +14,7 @@ const Header = () => { - + ); }; diff --git a/frontend/src/components/Layout.jsx b/frontend/src/components/layout/Layout.jsx similarity index 89% rename from frontend/src/components/Layout.jsx rename to frontend/src/components/layout/Layout.jsx index 74b64aa..1b5094f 100644 --- a/frontend/src/components/Layout.jsx +++ b/frontend/src/components/layout/Layout.jsx @@ -3,9 +3,9 @@ import { AppShell, Container, Loader, Center } from '@mantine/core'; import Header from './Header'; import Sidebar from './Sidebar'; import MainContent from './MainContent'; -import { useFileNavigation } from '../hooks/useFileNavigation'; -import { useFileList } from '../hooks/useFileList'; -import { useWorkspace } from '../contexts/WorkspaceContext'; +import { useFileNavigation } from '../../hooks/useFileNavigation'; +import { useFileList } from '../../hooks/useFileList'; +import { useWorkspace } from '../../contexts/WorkspaceContext'; const Layout = () => { const { currentWorkspace, loading: workspaceLoading } = useWorkspace(); diff --git a/frontend/src/components/MainContent.jsx b/frontend/src/components/layout/MainContent.jsx similarity index 86% rename from frontend/src/components/MainContent.jsx rename to frontend/src/components/layout/MainContent.jsx index 731b250..fe6d760 100644 --- a/frontend/src/components/MainContent.jsx +++ b/frontend/src/components/layout/MainContent.jsx @@ -2,15 +2,15 @@ import React, { useState, useCallback, useMemo } from 'react'; import { Tabs, Breadcrumbs, Group, Box, Text, Flex } from '@mantine/core'; import { IconCode, IconEye, IconPointFilled } from '@tabler/icons-react'; -import ContentView from './ContentView'; -import CreateFileModal from './modals/CreateFileModal'; -import DeleteFileModal from './modals/DeleteFileModal'; -import CommitMessageModal from './modals/CommitMessageModal'; +import ContentView from '../editor/ContentView'; +import CreateFileModal from '../modals/file/CreateFileModal'; +import DeleteFileModal from '../modals/file/DeleteFileModal'; +import CommitMessageModal from '../modals/git/CommitMessageModal'; -import { useFileContent } from '../hooks/useFileContent'; -import { useFileOperations } from '../hooks/useFileOperations'; -import { useGitOperations } from '../hooks/useGitOperations'; -import { useWorkspace } from '../contexts/WorkspaceContext'; +import { useFileContent } from '../../hooks/useFileContent'; +import { useFileOperations } from '../../hooks/useFileOperations'; +import { useGitOperations } from '../../hooks/useGitOperations'; +import { useWorkspace } from '../../contexts/WorkspaceContext'; const MainContent = ({ selectedFile, handleFileSelect, loadFileList }) => { const [activeTab, setActiveTab] = useState('source'); diff --git a/frontend/src/components/Sidebar.jsx b/frontend/src/components/layout/Sidebar.jsx similarity index 78% rename from frontend/src/components/Sidebar.jsx rename to frontend/src/components/layout/Sidebar.jsx index 04619c7..c6e96fc 100644 --- a/frontend/src/components/Sidebar.jsx +++ b/frontend/src/components/layout/Sidebar.jsx @@ -1,9 +1,9 @@ import React, { useEffect } from 'react'; import { Box } from '@mantine/core'; -import FileActions from './FileActions'; -import FileTree from './FileTree'; -import { useGitOperations } from '../hooks/useGitOperations'; -import { useWorkspace } from '../contexts/WorkspaceContext'; +import FileActions from '../files/FileActions'; +import FileTree from '../files/FileTree'; +import { useGitOperations } from '../../hooks/useGitOperations'; +import { useWorkspace } from '../../contexts/WorkspaceContext'; const Sidebar = ({ selectedFile, handleFileSelect, files, loadFileList }) => { const { settings } = useWorkspace(); diff --git a/frontend/src/components/modals/account/DeleteAccountModal.jsx b/frontend/src/components/modals/account/DeleteAccountModal.jsx new file mode 100644 index 0000000..cc77459 --- /dev/null +++ b/frontend/src/components/modals/account/DeleteAccountModal.jsx @@ -0,0 +1,55 @@ +import React, { useState } from 'react'; +import { + Modal, + Stack, + Text, + PasswordInput, + Group, + Button, +} from '@mantine/core'; + +const DeleteAccountModal = ({ opened, onClose, onConfirm }) => { + const [password, setPassword] = useState(''); + + return ( + + + + Warning: This action cannot be undone + + + Please enter your password to confirm account deletion. + + setPassword(e.currentTarget.value)} + required + /> + + + + + + + ); +}; + +export default DeleteAccountModal; diff --git a/frontend/src/components/modals/account/EmailPasswordModal.jsx b/frontend/src/components/modals/account/EmailPasswordModal.jsx new file mode 100644 index 0000000..cb585d2 --- /dev/null +++ b/frontend/src/components/modals/account/EmailPasswordModal.jsx @@ -0,0 +1,51 @@ +import React, { useState } from 'react'; +import { + Modal, + Text, + Button, + Group, + Stack, + PasswordInput, +} from '@mantine/core'; + +const EmailPasswordModal = ({ opened, onClose, onConfirm, email }) => { + const [password, setPassword] = useState(''); + + return ( + + + + Please enter your password to confirm changing your email to: {email} + + setPassword(e.currentTarget.value)} + required + /> + + + + + + + ); +}; + +export default EmailPasswordModal; diff --git a/frontend/src/components/modals/CreateFileModal.jsx b/frontend/src/components/modals/file/CreateFileModal.jsx similarity index 94% rename from frontend/src/components/modals/CreateFileModal.jsx rename to frontend/src/components/modals/file/CreateFileModal.jsx index c06952b..b44a843 100644 --- a/frontend/src/components/modals/CreateFileModal.jsx +++ b/frontend/src/components/modals/file/CreateFileModal.jsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import { Modal, TextInput, Button, Group, Box } from '@mantine/core'; -import { useModalContext } from '../../contexts/ModalContext'; +import { useModalContext } from '../../../contexts/ModalContext'; const CreateFileModal = ({ onCreateFile }) => { const [fileName, setFileName] = useState(''); diff --git a/frontend/src/components/modals/DeleteFileModal.jsx b/frontend/src/components/modals/file/DeleteFileModal.jsx similarity index 93% rename from frontend/src/components/modals/DeleteFileModal.jsx rename to frontend/src/components/modals/file/DeleteFileModal.jsx index edd4fbf..056bb21 100644 --- a/frontend/src/components/modals/DeleteFileModal.jsx +++ b/frontend/src/components/modals/file/DeleteFileModal.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { Modal, Text, Button, Group } from '@mantine/core'; -import { useModalContext } from '../../contexts/ModalContext'; +import { useModalContext } from '../../../contexts/ModalContext'; const DeleteFileModal = ({ onDeleteFile, selectedFile }) => { const { deleteFileModalVisible, setDeleteFileModalVisible } = diff --git a/frontend/src/components/modals/CommitMessageModal.jsx b/frontend/src/components/modals/git/CommitMessageModal.jsx similarity index 95% rename from frontend/src/components/modals/CommitMessageModal.jsx rename to frontend/src/components/modals/git/CommitMessageModal.jsx index 8976498..d0707b9 100644 --- a/frontend/src/components/modals/CommitMessageModal.jsx +++ b/frontend/src/components/modals/git/CommitMessageModal.jsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import { Modal, TextInput, Button, Group, Box } from '@mantine/core'; -import { useModalContext } from '../../contexts/ModalContext'; +import { useModalContext } from '../../../contexts/ModalContext'; const CommitMessageModal = ({ onCommitAndPush }) => { const [message, setMessage] = useState(''); diff --git a/frontend/src/components/modals/CreateWorkspaceModal.jsx b/frontend/src/components/modals/workspace/CreateWorkspaceModal.jsx similarity index 94% rename from frontend/src/components/modals/CreateWorkspaceModal.jsx rename to frontend/src/components/modals/workspace/CreateWorkspaceModal.jsx index 265eccf..d847af1 100644 --- a/frontend/src/components/modals/CreateWorkspaceModal.jsx +++ b/frontend/src/components/modals/workspace/CreateWorkspaceModal.jsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { Modal, TextInput, Button, Group, Box } from '@mantine/core'; -import { useModalContext } from '../../contexts/ModalContext'; -import { createWorkspace } from '../../services/api'; +import { useModalContext } from '../../../contexts/ModalContext'; +import { createWorkspace } from '../../../services/api'; import { notifications } from '@mantine/notifications'; const CreateWorkspaceModal = ({ onWorkspaceCreated }) => { diff --git a/frontend/src/components/modals/DeleteWorkspaceModal.jsx b/frontend/src/components/modals/workspace/DeleteWorkspaceModal.jsx similarity index 100% rename from frontend/src/components/modals/DeleteWorkspaceModal.jsx rename to frontend/src/components/modals/workspace/DeleteWorkspaceModal.jsx diff --git a/frontend/src/components/UserMenu.jsx b/frontend/src/components/navigation/UserMenu.jsx similarity index 96% rename from frontend/src/components/UserMenu.jsx rename to frontend/src/components/navigation/UserMenu.jsx index 6d50b44..c5d0e68 100644 --- a/frontend/src/components/UserMenu.jsx +++ b/frontend/src/components/navigation/UserMenu.jsx @@ -9,8 +9,8 @@ import { Divider, } from '@mantine/core'; import { IconUser, IconLogout, IconSettings } from '@tabler/icons-react'; -import { useAuth } from '../contexts/AuthContext'; -import AccountSettings from './AccountSettings'; +import { useAuth } from '../../contexts/AuthContext'; +import AccountSettings from '../settings/account/AccountSettings'; const UserMenu = () => { const [accountSettingsOpened, setAccountSettingsOpened] = useState(false); diff --git a/frontend/src/components/WorkspaceSwitcher.jsx b/frontend/src/components/navigation/WorkspaceSwitcher.jsx similarity index 96% rename from frontend/src/components/WorkspaceSwitcher.jsx rename to frontend/src/components/navigation/WorkspaceSwitcher.jsx index e29f24d..6ddc0af 100644 --- a/frontend/src/components/WorkspaceSwitcher.jsx +++ b/frontend/src/components/navigation/WorkspaceSwitcher.jsx @@ -15,10 +15,10 @@ import { useMantineTheme, } from '@mantine/core'; import { IconFolders, IconSettings, IconFolderPlus } from '@tabler/icons-react'; -import { useWorkspace } from '../contexts/WorkspaceContext'; -import { useModalContext } from '../contexts/ModalContext'; -import { listWorkspaces } from '../services/api'; -import CreateWorkspaceModal from './modals/CreateWorkspaceModal'; +import { useWorkspace } from '../../contexts/WorkspaceContext'; +import { useModalContext } from '../../contexts/ModalContext'; +import { listWorkspaces } from '../../services/api'; +import CreateWorkspaceModal from '../modals/workspace/CreateWorkspaceModal'; const WorkspaceSwitcher = () => { const { currentWorkspace, switchWorkspace } = useWorkspace(); diff --git a/frontend/src/components/settings/AccordionControl.jsx b/frontend/src/components/settings/AccordionControl.jsx new file mode 100644 index 0000000..03cd02e --- /dev/null +++ b/frontend/src/components/settings/AccordionControl.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { Accordion, Title } from '@mantine/core'; + +const AccordionControl = ({ children }) => ( + + {children} + +); + +export default AccordionControl; diff --git a/frontend/src/components/AccountSettings.jsx b/frontend/src/components/settings/account/AccountSettings.jsx similarity index 54% rename from frontend/src/components/AccountSettings.jsx rename to frontend/src/components/settings/account/AccountSettings.jsx index c4ad495..098240b 100644 --- a/frontend/src/components/AccountSettings.jsx +++ b/frontend/src/components/settings/account/AccountSettings.jsx @@ -1,4 +1,4 @@ -import React, { useState, useReducer, useRef } from 'react'; +import React, { useState, useReducer, useRef, useEffect } from 'react'; import { Modal, Badge, @@ -7,14 +7,15 @@ import { Title, Stack, Accordion, - TextInput, - PasswordInput, - Box, - Text, } from '@mantine/core'; import { notifications } from '@mantine/notifications'; -import { useAuth } from '../contexts/AuthContext'; -import { useProfileSettings } from '../hooks/useProfileSettings'; +import { useAuth } from '../../../contexts/AuthContext'; +import { useProfileSettings } from '../../../hooks/useProfileSettings'; +import EmailPasswordModal from '../../modals/account/EmailPasswordModal'; +import SecuritySettings from './SecuritySettings'; +import ProfileSettings from './ProfileSettings'; +import DangerZoneSettings from './DangerZoneSettings'; +import AccordionControl from '../AccordionControl'; // Reducer for managing settings state const initialState = { @@ -53,196 +54,15 @@ function settingsReducer(state, action) { } } -// Password confirmation modal for email changes -const EmailPasswordModal = ({ opened, onClose, onConfirm, email }) => { - const [password, setPassword] = useState(''); - - return ( - - - - Please enter your password to confirm changing your email to: {email} - - setPassword(e.currentTarget.value)} - required - /> - - - - - - - ); -}; - -// Delete account confirmation modal -const DeleteAccountModal = ({ opened, onClose, onConfirm }) => { - const [password, setPassword] = useState(''); - - return ( - - - - Warning: This action cannot be undone - - - Please enter your password to confirm account deletion. - - setPassword(e.currentTarget.value)} - required - /> - - - - - - - ); -}; - -const AccordionControl = ({ children }) => ( - - {children} - -); - -const ProfileSettings = ({ settings, onInputChange }) => ( - - - onInputChange('displayName', e.currentTarget.value)} - placeholder="Enter display name" - /> - onInputChange('email', e.currentTarget.value)} - placeholder="Enter email" - /> - - -); - -const SecuritySettings = ({ settings, onInputChange }) => { - const [confirmPassword, setConfirmPassword] = useState(''); - const [error, setError] = useState(''); - - const handlePasswordChange = (field, value) => { - if (field === 'confirmNewPassword') { - setConfirmPassword(value); - // Check if passwords match when either password field changes - if (value !== settings.newPassword) { - setError('Passwords do not match'); - } else { - setError(''); - } - } else { - onInputChange(field, value); - // Check if passwords match when either password field changes - if (field === 'newPassword' && value !== confirmPassword) { - setError('Passwords do not match'); - } else if (value === confirmPassword) { - setError(''); - } - } - }; - - return ( - - - - handlePasswordChange('currentPassword', e.currentTarget.value) - } - placeholder="Enter current password" - /> - - handlePasswordChange('newPassword', e.currentTarget.value) - } - placeholder="Enter new password" - /> - - handlePasswordChange('confirmNewPassword', e.currentTarget.value) - } - placeholder="Confirm new password" - error={error} - /> - - Password must be at least 8 characters long. Leave password fields - empty if you don't want to change it. - - - - ); -}; - -const DangerZone = ({ onDeleteClick }) => ( - - - -); - const AccountSettings = ({ opened, onClose }) => { - const { user, logout, refreshUser } = useAuth(); - const { loading, updateProfile, deleteAccount } = useProfileSettings(); + const { user, refreshUser } = useAuth(); + const { loading, updateProfile } = useProfileSettings(); const [state, dispatch] = useReducer(settingsReducer, initialState); const isInitialMount = useRef(true); - const [deleteModalOpened, setDeleteModalOpened] = useState(false); const [emailModalOpened, setEmailModalOpened] = useState(false); // Initialize settings on mount - React.useEffect(() => { + useEffect(() => { if (isInitialMount.current && user) { isInitialMount.current = false; const settings = { @@ -333,15 +153,6 @@ const AccountSettings = ({ opened, onClose }) => { } }; - const handleDelete = async (password) => { - const result = await deleteAccount(password); - if (result.success) { - setDeleteModalOpened(false); - onClose(); - logout(); - } - }; - return ( <> { Danger Zone - setDeleteModalOpened(true)} /> + @@ -430,12 +241,6 @@ const AccountSettings = ({ opened, onClose }) => { onConfirm={handleEmailConfirm} email={state.localSettings.email} /> - - setDeleteModalOpened(false)} - onConfirm={handleDelete} - /> ); }; diff --git a/frontend/src/components/settings/account/DangerZoneSettings.jsx b/frontend/src/components/settings/account/DangerZoneSettings.jsx new file mode 100644 index 0000000..9fed1bc --- /dev/null +++ b/frontend/src/components/settings/account/DangerZoneSettings.jsx @@ -0,0 +1,43 @@ +import React, { useState } from 'react'; +import { Box, Button, Text } from '@mantine/core'; +import DeleteAccountModal from '../../modals/account/DeleteAccountModal'; +import { useAuth } from '../../../contexts/AuthContext'; +import { useProfileSettings } from '../../../hooks/useProfileSettings'; + +const DangerZoneSettings = () => { + const { logout } = useAuth(); + const { deleteAccount } = useProfileSettings(); + const [deleteModalOpened, setDeleteModalOpened] = useState(false); + + const handleDelete = async (password) => { + const result = await deleteAccount(password); + if (result.success) { + setDeleteModalOpened(false); + logout(); + } + }; + + return ( + + + Once you delete your account, there is no going back. Please be certain. + + + + setDeleteModalOpened(false)} + onConfirm={handleDelete} + /> + + ); +}; + +export default DangerZoneSettings; diff --git a/frontend/src/components/settings/account/ProfileSettings.jsx b/frontend/src/components/settings/account/ProfileSettings.jsx new file mode 100644 index 0000000..687df65 --- /dev/null +++ b/frontend/src/components/settings/account/ProfileSettings.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { Box, Stack, TextInput } from '@mantine/core'; + +const ProfileSettings = ({ settings, onInputChange }) => ( + + + onInputChange('displayName', e.currentTarget.value)} + placeholder="Enter display name" + /> + onInputChange('email', e.currentTarget.value)} + placeholder="Enter email" + /> + + +); + +export default ProfileSettings; diff --git a/frontend/src/components/settings/account/SecuritySettings.jsx b/frontend/src/components/settings/account/SecuritySettings.jsx new file mode 100644 index 0000000..3568ebc --- /dev/null +++ b/frontend/src/components/settings/account/SecuritySettings.jsx @@ -0,0 +1,65 @@ +import React, { useState } from 'react'; +import { Box, PasswordInput, Stack, Text } from '@mantine/core'; + +const SecuritySettings = ({ settings, onInputChange }) => { + const [confirmPassword, setConfirmPassword] = useState(''); + const [error, setError] = useState(''); + + const handlePasswordChange = (field, value) => { + if (field === 'confirmNewPassword') { + setConfirmPassword(value); + // Check if passwords match when either password field changes + if (value !== settings.newPassword) { + setError('Passwords do not match'); + } else { + setError(''); + } + } else { + onInputChange(field, value); + // Check if passwords match when either password field changes + if (field === 'newPassword' && value !== confirmPassword) { + setError('Passwords do not match'); + } else if (value === confirmPassword) { + setError(''); + } + } + }; + + return ( + + + + handlePasswordChange('currentPassword', e.currentTarget.value) + } + placeholder="Enter current password" + /> + + handlePasswordChange('newPassword', e.currentTarget.value) + } + placeholder="Enter new password" + /> + + handlePasswordChange('confirmNewPassword', e.currentTarget.value) + } + placeholder="Confirm new password" + error={error} + /> + + Password must be at least 8 characters long. Leave password fields + empty if you don't want to change it. + + + + ); +}; + +export default SecuritySettings; diff --git a/frontend/src/components/settings/AppearanceSettings.jsx b/frontend/src/components/settings/workspace/AppearanceSettings.jsx similarity index 90% rename from frontend/src/components/settings/AppearanceSettings.jsx rename to frontend/src/components/settings/workspace/AppearanceSettings.jsx index 0c811ba..79e2055 100644 --- a/frontend/src/components/settings/AppearanceSettings.jsx +++ b/frontend/src/components/settings/workspace/AppearanceSettings.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { Text, Switch, Group, Box, Title } from '@mantine/core'; -import { useWorkspace } from '../../contexts/WorkspaceContext'; +import { useWorkspace } from '../../../contexts/WorkspaceContext'; const AppearanceSettings = ({ themeSettings, onThemeChange }) => { const { colorScheme, updateColorScheme } = useWorkspace(); diff --git a/frontend/src/components/settings/DangerZoneSettings.jsx b/frontend/src/components/settings/workspace/DangerZoneSettings.jsx similarity index 84% rename from frontend/src/components/settings/DangerZoneSettings.jsx rename to frontend/src/components/settings/workspace/DangerZoneSettings.jsx index 4052a92..dbc0e30 100644 --- a/frontend/src/components/settings/DangerZoneSettings.jsx +++ b/frontend/src/components/settings/workspace/DangerZoneSettings.jsx @@ -1,8 +1,8 @@ import React, { useState } from 'react'; import { Box, Button, Title } from '@mantine/core'; -import DeleteWorkspaceModal from '../modals/DeleteWorkspaceModal'; -import { useWorkspace } from '../../contexts/WorkspaceContext'; -import { useModalContext } from '../../contexts/ModalContext'; +import DeleteWorkspaceModal from '../../modals/workspace/DeleteWorkspaceModal'; +import { useWorkspace } from '../../../contexts/WorkspaceContext'; +import { useModalContext } from '../../../contexts/ModalContext'; const DangerZoneSettings = () => { const { currentWorkspace, workspaces, deleteCurrentWorkspace } = diff --git a/frontend/src/components/settings/EditorSettings.jsx b/frontend/src/components/settings/workspace/EditorSettings.jsx similarity index 100% rename from frontend/src/components/settings/EditorSettings.jsx rename to frontend/src/components/settings/workspace/EditorSettings.jsx diff --git a/frontend/src/components/settings/GeneralSettings.jsx b/frontend/src/components/settings/workspace/GeneralSettings.jsx similarity index 100% rename from frontend/src/components/settings/GeneralSettings.jsx rename to frontend/src/components/settings/workspace/GeneralSettings.jsx diff --git a/frontend/src/components/settings/GitSettings.jsx b/frontend/src/components/settings/workspace/GitSettings.jsx similarity index 99% rename from frontend/src/components/settings/GitSettings.jsx rename to frontend/src/components/settings/workspace/GitSettings.jsx index 4884ca5..9d83a42 100644 --- a/frontend/src/components/settings/GitSettings.jsx +++ b/frontend/src/components/settings/workspace/GitSettings.jsx @@ -6,7 +6,6 @@ import { Stack, PasswordInput, Group, - Title, Grid, } from '@mantine/core'; diff --git a/frontend/src/components/Settings.jsx b/frontend/src/components/settings/workspace/WorkspaceSettings.jsx similarity index 90% rename from frontend/src/components/Settings.jsx rename to frontend/src/components/settings/workspace/WorkspaceSettings.jsx index a4ee4aa..9a9a78d 100644 --- a/frontend/src/components/Settings.jsx +++ b/frontend/src/components/settings/workspace/WorkspaceSettings.jsx @@ -9,13 +9,14 @@ import { Accordion, } from '@mantine/core'; import { notifications } from '@mantine/notifications'; -import { useWorkspace } from '../contexts/WorkspaceContext'; -import AppearanceSettings from './settings/AppearanceSettings'; -import EditorSettings from './settings/EditorSettings'; -import GitSettings from './settings/GitSettings'; -import GeneralSettings from './settings/GeneralSettings'; -import { useModalContext } from '../contexts/ModalContext'; -import DangerZoneSettings from './settings/DangerZoneSettings'; +import { useWorkspace } from '../../../contexts/WorkspaceContext'; +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'; const initialState = { localSettings: {}, @@ -53,13 +54,7 @@ function settingsReducer(state, action) { } } -const AccordionControl = ({ children }) => ( - - {children} - -); - -const Settings = () => { +const WorkspaceSettings = () => { const { currentWorkspace, updateSettings } = useWorkspace(); const { settingsModalVisible, setSettingsModalVisible } = useModalContext(); const [state, dispatch] = useReducer(settingsReducer, initialState); @@ -121,7 +116,7 @@ const Settings = () => { Settings} + title={Workspace Settings} centered size="lg" > @@ -228,4 +223,4 @@ const Settings = () => { ); }; -export default Settings; +export default WorkspaceSettings;