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() {
-
- {/* 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,
+ )}
+ %
+
+
+
+ />
-
- )}
-
- ))}
+ )}
+
+ ))}
) : (
@@ -128,6 +136,16 @@ const FolderManagementCard: React.FC = () => {
)}
+ {folders.length > visibleFoldersCount && (
+
+ )}
+