diff --git a/frontend/src/components/Navigation/Navbar/Navbar.tsx b/frontend/src/components/Navigation/Navbar/Navbar.tsx index c565b7f6d..d32d0777d 100644 --- a/frontend/src/components/Navigation/Navbar/Navbar.tsx +++ b/frontend/src/components/Navigation/Navbar/Navbar.tsx @@ -6,6 +6,7 @@ import { selectAvatar, selectName } from '@/features/onboardingSelectors'; import { clearSearch } from '@/features/searchSlice'; import { convertFileSrc } from '@tauri-apps/api/core'; import { FaceSearchDialog } from '@/components/Dialog/FaceSearchDialog'; +import { Link } from 'react-router'; export function Navbar() { const userName = useSelector(selectName); @@ -20,10 +21,10 @@ export function Navbar() {
{/* Logo */}
- + PictoPy Logo PictoPy - +
{/* Search Bar */} @@ -82,13 +83,13 @@ export function Navbar() { Welcome {userName} - + User avatar - +
diff --git a/frontend/src/pages/SettingsPage/Settings.tsx b/frontend/src/pages/SettingsPage/Settings.tsx index fdc84cd8f..472f788fb 100644 --- a/frontend/src/pages/SettingsPage/Settings.tsx +++ b/frontend/src/pages/SettingsPage/Settings.tsx @@ -1,28 +1,87 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; +import { useLocation, useNavigate } from 'react-router'; // Import modular components import FolderManagementCard from './components/FolderManagementCard'; import UserPreferencesCard from './components/UserPreferencesCard'; import ApplicationControlsCard from './components/ApplicationControlsCard'; +import AccountSettingsCard from './components/AccountSettingsCard'; /** * Settings page component * Acts as an orchestrator for the settings sections */ const Settings: React.FC = () => { - return ( -
-
- {/* Folder Management */} - + const location = useLocation(); + const navigate = useNavigate(); + const [activeTab, setActiveTab] = useState('general'); + + const handleTabChange = (tab: string) => { + setActiveTab(tab); + navigate(`#${tab}`, { replace: true }); + }; - {/* User Preferences */} - + useEffect(() => { + const hash = location.hash; + if (hash === '#account') { + setActiveTab('account'); + } else if (hash === '#general') { + setActiveTab('general'); + } else if (hash === '') { + setActiveTab('general'); + navigate(`#general`, { replace: true }); + } + }, [location.hash]); + const baseTabStyle = 'px-4 py-2 rounded-md font-medium transition-colors'; + const activeTabStyle = 'bg-background text-foreground'; + const inactiveTabStyle = + 'text-muted-foreground hover:bg-gray-100 dark:hover:bg-gray-800'; + return ( + <> +

Settings

+
+
+
+ + +
+
+ {activeTab === 'general' && ( + <> + + + + + )} - {/* Application Controls */} - + {activeTab === 'account' && ( + <> + + + )} +
+
-
+ ); }; diff --git a/frontend/src/pages/SettingsPage/components/AccountSettingsCard.tsx b/frontend/src/pages/SettingsPage/components/AccountSettingsCard.tsx new file mode 100644 index 000000000..65070f8a4 --- /dev/null +++ b/frontend/src/pages/SettingsPage/components/AccountSettingsCard.tsx @@ -0,0 +1,127 @@ +import React, { useState } from 'react'; // No need for useEffect +import { useDispatch } from 'react-redux'; +import { Label } from '@/components/ui/label'; +import { Input } from '@/components/ui/input'; +import { setAvatar, setName } from '@/features/onboardingSlice'; +import { User } from 'lucide-react'; +import SettingsCard from './SettingsCard'; +import { avatars } from '@/constants/avatars'; +import { CardContent } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; + +const AccountSettingsCard: React.FC = () => { + const dispatch = useDispatch(); + const [name, setLocalName] = useState( + () => localStorage.getItem('name') || '', + ); + const [selectedAvatar, setLocalAvatar] = useState(() => { + const stored = localStorage.getItem('avatar') || ''; + return avatars.includes(stored) ? stored : ''; + }); + const [nameError, setNameError] = useState(false); + + // The redundant useEffect has been removed. + + const handleAvatarSelect = (avatar: string) => { + setLocalAvatar(avatar); + }; + + const handleNameChange = (value: string) => { + setLocalName(value); + if (nameError) { + setNameError(false); + } + }; + + const handleSave = () => { + if (!name.trim()) { + setNameError(true); + return; + } + + setNameError(false); + if (!selectedAvatar) return; + + try { + dispatch(setName(name)); + dispatch(setAvatar(selectedAvatar)); + localStorage.setItem('name', name); + localStorage.setItem('avatar', selectedAvatar); + } catch (error) { + console.error('Failed to save settings:'); + } + }; + + return ( + + +
+ {/* Name Change */} +
+ + handleNameChange(e.target.value)} + className={`h-10 w-full text-sm placeholder:text-sm ${ + nameError + ? 'border-red-500 placeholder:text-red-500/80 focus-visible:ring-red-500' + : '' + }`} + /> +
+ + {/* Avatar Section */} +
+ +
+ {avatars.map((avatar) => { + const isSelected = selectedAvatar === avatar; + return ( + + ); + })} +
+
+
+ + {/* Save Changes Button */} + +
+
+ ); +}; + +export default AccountSettingsCard; diff --git a/frontend/src/pages/SettingsPage/components/FolderManagementCard.tsx b/frontend/src/pages/SettingsPage/components/FolderManagementCard.tsx index db4b029fa..c1283a49c 100644 --- a/frontend/src/pages/SettingsPage/components/FolderManagementCard.tsx +++ b/frontend/src/pages/SettingsPage/components/FolderManagementCard.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Folder, Trash2, Check } from 'lucide-react'; import { Switch } from '@/components/ui/switch'; @@ -29,6 +29,12 @@ const FolderManagementCard: React.FC = () => { (state: RootState) => state.folders.taggingStatus, ); + const [visibleFoldersCount, setVisibleFoldersCount] = useState(6); + + const handleViewMore = () => { + setVisibleFoldersCount((prevCount) => prevCount + 5); + }; + return ( { > {folders.length > 0 ? (
- {folders.map((folder: FolderDetails, index: number) => ( -
-
-
-
- - - {folder.folder_path} - + {folders + .slice(0, visibleFoldersCount) + .map((folder: FolderDetails) => ( +
+
+
+
+ + + {folder.folder_path} + +
-
-
-
- - AI Tagging - - toggleAITagging(folder)} - disabled={ - enableAITaggingPending || disableAITaggingPending - } - /> -
+
+
+ + AI Tagging + + toggleAITagging(folder)} + disabled={ + enableAITaggingPending || disableAITaggingPending + } + /> +
- + +
-
- {folder.AI_Tagging && ( -
-
- AI Tagging Progress - +
+ AI Tagging Progress + = 100 + ? 'flex items-center gap-1 text-green-500' + : 'text-muted-foreground' + } + > + {(taggingStatus[folder.folder_id]?.tagging_percentage ?? + 0) >= 100 && } + {Math.round( + taggingStatus[folder.folder_id]?.tagging_percentage ?? + 0, + )} + % + +
+ = 100 - ? 'flex items-center gap-1 text-green-500' - : 'text-muted-foreground' + ? 'bg-green-500' + : 'bg-blue-500' } - > - {(taggingStatus[folder.folder_id]?.tagging_percentage ?? - 0) >= 100 && } - {Math.round( - taggingStatus[folder.folder_id]?.tagging_percentage ?? - 0, - )} - % -
+ />
- = 100 - ? 'bg-green-500' - : 'bg-blue-500' - } - /> -
- )} -
- ))} + )} +
+ ))}
) : (
@@ -128,6 +136,16 @@ const FolderManagementCard: React.FC = () => {
)} + {folders.length > visibleFoldersCount && ( + + )} +