Fix project loading, task detail panel, collapsible sidebar, due dates
This commit is contained in:
@@ -2,7 +2,8 @@ import { useState, useRef, useEffect } from 'react';
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
Inbox, Calendar, CalendarDays, Plus, ChevronDown, ChevronRight,
|
||||
Hash, Settings, LogOut, User, FolderPlus, Tag, X, Check
|
||||
Hash, Settings, LogOut, User, FolderPlus, Tag, X, Check,
|
||||
PanelLeftClose, PanelLeftOpen
|
||||
} from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
@@ -13,6 +14,8 @@ const PROJECT_COLORS = [
|
||||
'#ec4899', '#06b6d4', '#f97316', '#6366f1', '#14b8a6',
|
||||
];
|
||||
|
||||
const SIDEBAR_COLLAPSED_KEY = 'sidebar-collapsed';
|
||||
|
||||
export function Sidebar() {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
@@ -24,6 +27,13 @@ export function Sidebar() {
|
||||
const [newProjectName, setNewProjectName] = useState('');
|
||||
const [newProjectColor, setNewProjectColor] = useState(PROJECT_COLORS[0]);
|
||||
const [isCreatingProject, setIsCreatingProject] = useState(false);
|
||||
const [isCollapsed, setIsCollapsed] = useState(() => {
|
||||
try {
|
||||
return localStorage.getItem(SIDEBAR_COLLAPSED_KEY) === 'true';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
const newProjectInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -32,6 +42,14 @@ export function Sidebar() {
|
||||
}
|
||||
}, [showNewProject]);
|
||||
|
||||
const toggleCollapsed = () => {
|
||||
const next = !isCollapsed;
|
||||
setIsCollapsed(next);
|
||||
try {
|
||||
localStorage.setItem(SIDEBAR_COLLAPSED_KEY, String(next));
|
||||
} catch {}
|
||||
};
|
||||
|
||||
const handleCreateProject = async () => {
|
||||
if (!newProjectName.trim() || isCreatingProject) return;
|
||||
setIsCreatingProject(true);
|
||||
@@ -67,6 +85,92 @@ export function Sidebar() {
|
||||
{ path: '/today', icon: Calendar, label: 'Today', color: '#22c55e' },
|
||||
{ path: '/upcoming', icon: CalendarDays, label: 'Upcoming', color: '#8b5cf6' },
|
||||
];
|
||||
|
||||
// Collapsed sidebar
|
||||
if (isCollapsed) {
|
||||
return (
|
||||
<aside className="w-14 h-screen bg-gray-50 border-r border-gray-200 flex flex-col items-center">
|
||||
{/* Expand button */}
|
||||
<div className="p-2 pt-3">
|
||||
<button
|
||||
onClick={toggleCollapsed}
|
||||
className="p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
title="Expand sidebar"
|
||||
>
|
||||
<PanelLeftOpen className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* User avatar */}
|
||||
<div className="p-2">
|
||||
<div className="w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white text-sm font-medium" title={user?.name}>
|
||||
{user?.name?.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Nav icons */}
|
||||
<nav className="flex-1 py-2 space-y-1">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.path}
|
||||
to={item.path}
|
||||
className={cn(
|
||||
'flex items-center justify-center w-10 h-10 rounded-lg transition-colors',
|
||||
location.pathname === item.path
|
||||
? 'bg-blue-50 text-blue-600'
|
||||
: 'text-gray-700 hover:bg-gray-100'
|
||||
)}
|
||||
title={item.label}
|
||||
>
|
||||
<item.icon className="w-4 h-4" style={{ color: item.color }} />
|
||||
</Link>
|
||||
))}
|
||||
|
||||
{/* Project icons */}
|
||||
<div className="pt-3 border-t border-gray-200 mt-3 space-y-1">
|
||||
{regularProjects.slice(0, 5).map((project) => (
|
||||
<Link
|
||||
key={project.id}
|
||||
to={`/project/${project.id}`}
|
||||
className={cn(
|
||||
'flex items-center justify-center w-10 h-10 rounded-lg transition-colors',
|
||||
location.pathname.startsWith(`/project/${project.id}`)
|
||||
? 'bg-blue-50'
|
||||
: 'hover:bg-gray-100'
|
||||
)}
|
||||
title={project.name}
|
||||
>
|
||||
<span
|
||||
className="w-3 h-3 rounded"
|
||||
style={{ backgroundColor: project.color }}
|
||||
/>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Bottom icons */}
|
||||
<div className="p-2 border-t border-gray-200 space-y-1">
|
||||
{user?.role === 'admin' && (
|
||||
<Link
|
||||
to="/admin"
|
||||
className="flex items-center justify-center w-10 h-10 rounded-lg text-gray-700 hover:bg-gray-100"
|
||||
title="Admin"
|
||||
>
|
||||
<Settings className="w-4 h-4" />
|
||||
</Link>
|
||||
)}
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="flex items-center justify-center w-10 h-10 rounded-lg text-gray-700 hover:bg-gray-100"
|
||||
title="Sign out"
|
||||
>
|
||||
<LogOut className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className="w-64 h-screen bg-gray-50 border-r border-gray-200 flex flex-col">
|
||||
@@ -80,6 +184,13 @@ export function Sidebar() {
|
||||
<p className="text-sm font-medium text-gray-900 truncate">{user?.name}</p>
|
||||
<p className="text-xs text-gray-500 truncate">{user?.email}</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={toggleCollapsed}
|
||||
className="p-1.5 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
title="Collapse sidebar"
|
||||
>
|
||||
<PanelLeftClose className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user