diff --git a/src/app/(protected)/projects/[slug]/page.tsx b/src/app/(protected)/projects/[slug]/page.tsx index bc2bafc..99fe1f6 100644 --- a/src/app/(protected)/projects/[slug]/page.tsx +++ b/src/app/(protected)/projects/[slug]/page.tsx @@ -32,7 +32,7 @@ export default function ProjectPage() {
-
+

{project.name}

{project.description && ( diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 50acd41..005fa1e 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -35,7 +35,7 @@ export default function LoginPage() { return (
-
+

Team Board

AI Agent Collaboration Platform

diff --git a/src/components/KanbanBoard.tsx b/src/components/KanbanBoard.tsx index b273014..da4f77c 100644 --- a/src/components/KanbanBoard.tsx +++ b/src/components/KanbanBoard.tsx @@ -28,6 +28,7 @@ export default function KanbanBoard({ projectId }: Props) { const [newTaskTitle, setNewTaskTitle] = useState(""); const [addingTo, setAddingTo] = useState(null); const [draggedTask, setDraggedTask] = useState(null); + const [activeColumn, setActiveColumn] = useState(null); const loadTasks = async () => { try { @@ -47,14 +48,22 @@ export default function KanbanBoard({ projectId }: Props) { const task = tasks.find((t) => t.id === draggedTask); if (!task || task.status === status) return; - // Optimistic update setTasks((prev) => prev.map((t) => (t.id === draggedTask ? { ...t, status } : t))); setDraggedTask(null); try { await updateTask(draggedTask, { status }); } catch { - loadTasks(); // revert + loadTasks(); + } + }; + + const handleMoveTask = async (taskId: string, newStatus: string) => { + setTasks((prev) => prev.map((t) => (t.id === taskId ? { ...t, status: newStatus } : t))); + try { + await updateTask(taskId, { status: newStatus }); + } catch { + loadTasks(); } }; @@ -78,51 +87,147 @@ export default function KanbanBoard({ projectId }: Props) { return
Загрузка...
; } + // Mobile: column selector + vertical list + // Desktop: horizontal kanban return ( -
- {COLUMNS.map((col) => { - const colTasks = tasks.filter((t) => t.status === col.key); - return ( -
e.preventDefault()} - onDrop={() => handleDrop(col.key)} - > - {/* Column header */} -
-
- {col.label} - {colTasks.length} -
+
+ {/* Mobile column tabs */} +
+ {COLUMNS.map((col) => { + const count = tasks.filter((t) => t.status === col.key).length; + return ( + + ); + })} +
- {/* Cards */} -
+ {/* Mobile: single column view */} +
+ {(() => { + const col = COLUMNS.find((c) => c.key === (activeColumn || "draft"))!; + const colTasks = tasks.filter((t) => t.status === col.key); + const colIndex = COLUMNS.findIndex((c) => c.key === col.key); + return ( +
{colTasks.map((task) => (
setDraggedTask(task.id)} - className="bg-[var(--card)] border border-[var(--border)] rounded-lg p-3 cursor-grab - hover:border-[var(--accent)] transition-colors active:cursor-grabbing" + className="bg-[var(--card)] border border-[var(--border)] rounded-lg p-3" >
- {task.title} + {task.title}
{task.description && ( -

{task.description}

+

{task.description}

)} + {/* Move buttons */} +
+ {colIndex > 0 && ( + + )} + {colIndex < COLUMNS.length - 1 && ( + + )} +
))} - {/* Add task */} {addingTo === col.key ? ( -
+ setNewTaskTitle(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") handleAddTask(col.key); + if (e.key === "Escape") setAddingTo(null); + }} + onBlur={() => !newTaskTitle.trim() && setAddingTo(null)} + /> + ) : ( + + )} +
+ ); + })()} +
+ + {/* Desktop: horizontal kanban */} +
+ {COLUMNS.map((col) => { + const colTasks = tasks.filter((t) => t.status === col.key); + return ( +
e.preventDefault()} + onDrop={() => handleDrop(col.key)} + > +
+
+ {col.label} + {colTasks.length} +
+ +
+ {colTasks.map((task) => ( +
setDraggedTask(task.id)} + className="bg-[var(--card)] border border-[var(--border)] rounded-lg p-3 cursor-grab + hover:border-[var(--accent)] transition-colors active:cursor-grabbing" + > +
+
+ {task.title} +
+ {task.description && ( +

{task.description}

+ )} +
+ ))} + + {addingTo === col.key ? ( { - if (!newTaskTitle.trim()) setAddingTo(null); - }} + onBlur={() => !newTaskTitle.trim() && setAddingTo(null)} /> -
- ) : ( - - )} + ) : ( + + )} +
-
- ); - })} + ); + })} +
); } diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 2b345aa..13f2e84 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,7 +1,9 @@ "use client"; +import { useState } from "react"; import Link from "next/link"; import { Project } from "@/lib/api"; +import { logout } from "@/lib/auth-client"; interface Props { projects: Project[]; @@ -9,22 +11,30 @@ interface Props { } export default function Sidebar({ projects, activeSlug }: Props) { - return ( - + + ); + + return ( + <> + {/* Mobile hamburger */} + + + {/* Mobile overlay */} + {open && ( +
setOpen(false)} /> + )} + + {/* Sidebar: always visible on desktop, slide-in on mobile */} + + ); }