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/AccountSettings.jsx b/frontend/src/components/settings/account/AccountSettings.jsx similarity index 61% rename from frontend/src/components/AccountSettings.jsx rename to frontend/src/components/settings/account/AccountSettings.jsx index c4ad495..7210f6f 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, @@ -8,13 +8,15 @@ import { 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 DeleteAccountModal from '../../modals/account/DeleteAccountModal'; +import SecuritySettings from './SecuritySettings'; +import ProfileSettings from './ProfileSettings'; // Reducer for managing settings state const initialState = { @@ -53,178 +55,12 @@ 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 }) => (