feat(frontend): add profile page for user management

- Add Profile page for viewing/editing user info
- Add display name editing functionality
- Add change password functionality with validation
- Add Profile button to Sidebar navigation
- Handle authentication errors gracefully
This commit is contained in:
Matteo
2026-01-24 17:12:26 +01:00
parent b834f777b5
commit 112d58a92a
2 changed files with 355 additions and 2 deletions
+34 -2
View File
@@ -1,10 +1,11 @@
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { LayoutGrid, Folder, Plus, Trash2, Edit2, Archive, FolderOpen, Settings as SettingsIcon } from 'lucide-react';
import { LayoutGrid, Folder, Plus, Trash2, Edit2, Archive, FolderOpen, Settings as SettingsIcon, User, LogOut } from 'lucide-react';
import type { Collection } from '../types';
import clsx from 'clsx';
import { ConfirmModal } from './ConfirmModal';
import { Logo } from './Logo';
import { useAuth } from '../context/AuthContext';
interface SidebarProps {
collections: Collection[];
@@ -120,6 +121,8 @@ export const Sidebar: React.FC<SidebarProps> = ({
onDeleteCollection,
onDrop
}) => {
const navigate = useNavigate();
const { logout, user } = useAuth();
const [isCreating, setIsCreating] = useState(false);
const [newCollectionName, setNewCollectionName] = useState('');
const [editingId, setEditingId] = useState<string | null>(null);
@@ -127,7 +130,6 @@ export const Sidebar: React.FC<SidebarProps> = ({
const [contextMenu, setContextMenu] = useState<{ x: number; y: number; type: 'item' | 'background'; id?: string } | null>(null);
const [collectionToDelete, setCollectionToDelete] = useState<string | null>(null);
const [isTrashDragOver, setIsTrashDragOver] = useState(false);
const navigate = useNavigate();
useEffect(() => {
const handleClickOutside = () => setContextMenu(null);
@@ -284,6 +286,19 @@ export const Sidebar: React.FC<SidebarProps> = ({
<span className="min-w-0 flex-1 text-left">Trash</span>
</button>
<button
onClick={() => navigate('/profile')}
className={clsx(
"w-full flex items-center gap-3 px-3 py-2 text-sm font-bold rounded-xl transition-all duration-200 border-2 border-black dark:border-neutral-700 shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] dark:shadow-[2px_2px_0px_0px_rgba(255,255,255,0.2)]",
selectedCollectionId === 'PROFILE'
? "bg-indigo-50 dark:bg-neutral-800 text-indigo-900 dark:text-neutral-200 -translate-y-0.5"
: "bg-white dark:bg-neutral-900 text-slate-900 dark:text-neutral-200 hover:bg-slate-50 dark:hover:bg-neutral-800 hover:shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] dark:hover:shadow-[4px_4px_0px_0px_rgba(255,255,255,0.2)] hover:-translate-y-0.5"
)}
>
<User size={18} />
<span className="min-w-0 flex-1 text-left">Profile</span>
</button>
<button
onClick={() => navigate('/settings')}
className={clsx(
@@ -296,6 +311,23 @@ export const Sidebar: React.FC<SidebarProps> = ({
<SettingsIcon size={18} />
<span className="min-w-0 flex-1 text-left">Settings</span>
</button>
{/* User info and logout */}
<div className="mt-auto pt-4 border-t-2 border-slate-200 dark:border-neutral-700">
{user && (
<div className="px-3 py-2 text-xs text-slate-500 dark:text-neutral-500 mb-2">
<div className="font-semibold text-slate-700 dark:text-neutral-300">{user.name}</div>
<div className="truncate">{user.email}</div>
</div>
)}
<button
onClick={logout}
className="w-full flex items-center gap-3 px-3 py-2 text-sm font-bold rounded-xl transition-all duration-200 border-2 border-rose-300 dark:border-rose-700 bg-white dark:bg-neutral-900 text-rose-600 dark:text-rose-400 hover:bg-rose-50 dark:hover:bg-rose-900/30 hover:shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] dark:hover:shadow-[2px_2px_0px_0px_rgba(255,255,255,0.2)] hover:-translate-y-0.5 cursor-pointer"
>
<LogOut size={18} />
<span className="min-w-0 flex-1 text-left">Logout</span>
</button>
</div>
</div>
</div>