153 lines
5.0 KiB
TypeScript
153 lines
5.0 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { useParams, useNavigate, useLocation } from 'react-router-dom';
|
|
import { LayoutList, LayoutGrid, Plus } from 'lucide-react';
|
|
import { cn } from '@/lib/utils';
|
|
import { useTasksStore } from '@/stores/tasks';
|
|
import { TaskItem } from '@/components/TaskItem';
|
|
import { AddTask } from '@/components/AddTask';
|
|
import { CompletedSection } from '@/components/CompletedSection';
|
|
import { BoardView } from '@/pages/Board';
|
|
import { api } from '@/lib/api';
|
|
import type { Project as ProjectType, Section } from '@/types';
|
|
|
|
export function ProjectPage() {
|
|
const { id } = useParams<{ id: string }>();
|
|
const location = useLocation();
|
|
const navigate = useNavigate();
|
|
const { tasks, completedTasks, isLoading, fetchTasks, fetchCompletedTasks, setSelectedTask } = useTasksStore();
|
|
const [project, setProject] = useState<ProjectType | null>(null);
|
|
const [sections, setSections] = useState<Section[]>([]);
|
|
|
|
const isBoardView = location.pathname.endsWith('/board');
|
|
|
|
useEffect(() => {
|
|
if (!id) return;
|
|
fetchTasks({ projectId: id, completed: false });
|
|
fetchCompletedTasks({ projectId: id });
|
|
api.getProject(id).then((p) => {
|
|
setProject(p);
|
|
setSections(p.sections || []);
|
|
}).catch(console.error);
|
|
}, [id]);
|
|
|
|
if (!project) {
|
|
return (
|
|
<div className="flex items-center justify-center py-12 text-gray-500">
|
|
Loading project...
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const toggleView = () => {
|
|
if (isBoardView) {
|
|
navigate(`/project/${id}`);
|
|
} else {
|
|
navigate(`/project/${id}/board`);
|
|
}
|
|
};
|
|
|
|
// Group tasks by section
|
|
const unsectionedTasks = tasks.filter((t) => !t.sectionId);
|
|
const tasksBySection = sections.map((section) => ({
|
|
section,
|
|
tasks: tasks.filter((t) => t.sectionId === section.id),
|
|
}));
|
|
|
|
return (
|
|
<div className="max-w-5xl mx-auto">
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between mb-6">
|
|
<div className="flex items-center gap-3">
|
|
<span
|
|
className="w-4 h-4 rounded"
|
|
style={{ backgroundColor: project.color }}
|
|
/>
|
|
<h1 className="text-2xl font-bold text-gray-900">{project.name}</h1>
|
|
</div>
|
|
<div className="flex items-center gap-1 bg-gray-100 rounded-lg p-0.5">
|
|
<button
|
|
onClick={() => !isBoardView || toggleView()}
|
|
className={cn(
|
|
'flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-md transition-colors',
|
|
!isBoardView
|
|
? 'bg-white text-gray-900 shadow-sm'
|
|
: 'text-gray-500 hover:text-gray-700'
|
|
)}
|
|
>
|
|
<LayoutList className="w-4 h-4" />
|
|
List
|
|
</button>
|
|
<button
|
|
onClick={() => isBoardView || toggleView()}
|
|
className={cn(
|
|
'flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-md transition-colors',
|
|
isBoardView
|
|
? 'bg-white text-gray-900 shadow-sm'
|
|
: 'text-gray-500 hover:text-gray-700'
|
|
)}
|
|
>
|
|
<LayoutGrid className="w-4 h-4" />
|
|
Board
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{isBoardView ? (
|
|
<BoardView
|
|
project={project}
|
|
sections={sections}
|
|
tasks={tasks}
|
|
isLoading={isLoading}
|
|
/>
|
|
) : (
|
|
/* List view */
|
|
<div className="space-y-6">
|
|
{isLoading ? (
|
|
<div className="text-center py-12 text-gray-500">Loading tasks...</div>
|
|
) : (
|
|
<>
|
|
{/* Unsectioned tasks */}
|
|
{unsectionedTasks.length > 0 && (
|
|
<div className="space-y-1">
|
|
{unsectionedTasks.map((task) => (
|
|
<TaskItem
|
|
key={task.id}
|
|
task={task}
|
|
onClick={() => setSelectedTask(task)}
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* Add task for unsectioned */}
|
|
<AddTask projectId={id} />
|
|
|
|
{/* Sections */}
|
|
{tasksBySection.map(({ section, tasks: sectionTasks }) => (
|
|
<div key={section.id}>
|
|
<h3 className="text-sm font-semibold text-gray-700 mb-2 px-3 py-1 border-b border-gray-200">
|
|
{section.name}
|
|
</h3>
|
|
<div className="space-y-1">
|
|
{sectionTasks.map((task) => (
|
|
<TaskItem
|
|
key={task.id}
|
|
task={task}
|
|
onClick={() => setSelectedTask(task)}
|
|
/>
|
|
))}
|
|
</div>
|
|
<AddTask projectId={id} sectionId={section.id} />
|
|
</div>
|
|
))}
|
|
|
|
{/* Completed tasks */}
|
|
<CompletedSection tasks={completedTasks} />
|
|
</>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|