"use client" import { useState, useEffect } from "react" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog" import { Textarea } from "@/components/ui/textarea" import { Label } from "@/components/ui/label" import { useTaskStore, Task, TaskType, TaskStatus, Priority, Project } from "@/stores/useTaskStore" import { BacklogView } from "@/components/BacklogView" import { Plus, MessageSquare, Calendar, Tag, Trash2, Edit2, X, Check, MoreHorizontal, LayoutGrid, ListTodo } from "lucide-react" const typeColors: Record = { idea: "bg-purple-500", task: "bg-blue-500", bug: "bg-red-500", research: "bg-green-500", plan: "bg-amber-500", } const typeLabels: Record = { idea: "๐Ÿ’ก Idea", task: "๐Ÿ“‹ Task", bug: "๐Ÿ› Bug", research: "๐Ÿ”ฌ Research", plan: "๐Ÿ“ Plan", } const priorityColors: Record = { low: "text-slate-400", medium: "text-blue-400", high: "text-orange-400", urgent: "text-red-400", } const allStatuses: TaskStatus[] = ["open", "backlog", "blocked", "in-progress", "review", "validate", "archived", "canceled", "done"] // Sprint board columns mapped to workflow statuses const sprintColumns = [ { key: "todo", label: "To Do", statuses: ["open", "backlog"] // OPEN, TO DO }, { key: "inprogress", label: "In Progress", statuses: ["blocked", "in-progress", "review", "validate"] // BLOCKED, IN PROGRESS, REVIEW, VALIDATE }, { key: "done", label: "Done", statuses: ["archived", "canceled", "done"] // ARCHIVED, CANCELED, DONE }, ] as const export default function Home() { const { projects, tasks, sprints, selectedProjectId, selectedTaskId, selectedSprintId, selectProject, addProject, deleteProject, addTask, updateTask, deleteTask, selectTask, addComment, deleteComment, getTasksByProject, syncFromServer, isLoading, } = useTaskStore() const [newProjectName, setNewProjectName] = useState("") const [showNewProject, setShowNewProject] = useState(false) const [newTaskOpen, setNewTaskOpen] = useState(false) const [newTask, setNewTask] = useState>({ title: "", description: "", type: "task", priority: "medium", status: "backlog", tags: [], }) const [newComment, setNewComment] = useState("") const [editingTask, setEditingTask] = useState(null) const [viewMode, setViewMode] = useState<'kanban' | 'backlog'>('kanban') // Sync from server on mount useEffect(() => { syncFromServer() }, [syncFromServer]) const selectedProject = projects.find((p) => p.id === selectedProjectId) const selectedTask = tasks.find((t) => t.id === selectedTaskId) const projectTasks = selectedProjectId ? getTasksByProject(selectedProjectId) : [] // Get current active sprint (across all projects) const now = new Date() const currentSprint = sprints.find((s) => s.status === 'active' && new Date(s.startDate) <= now && new Date(s.endDate) >= now ) // Filter tasks to only show current sprint tasks in Kanban (from ALL projects) const sprintTasks = currentSprint ? tasks.filter((t) => t.sprintId === currentSprint.id) : [] const handleAddProject = () => { if (newProjectName.trim()) { addProject(newProjectName.trim()) setNewProjectName("") setShowNewProject(false) } } const handleAddTask = () => { if (newTask.title?.trim() && selectedProjectId) { addTask({ ...newTask, projectId: selectedProjectId, status: newTask.status || "backlog", sprintId: currentSprint?.id, // Auto-assign to current sprint } as any) setNewTask({ title: "", description: "", type: "task", priority: "medium", status: "backlog", tags: [] }) setNewTaskOpen(false) } } const handleAddComment = () => { if (newComment.trim() && selectedTaskId) { addComment(selectedTaskId, newComment.trim(), "user") setNewComment("") } } const handleUpdateTaskStatus = (taskId: string, newStatus: TaskStatus) => { updateTask(taskId, { status: newStatus }) } return (
{/* Header */}

OpenClaw Project Hub

Track ideas, tasks, bugs, and plans โ€” with threaded notes

{isLoading && ( Syncing... )} {tasks.length} tasks ยท {projects.length} projects
{/* Sidebar - Sprint Info */} {/* Main Content */}
{selectedProject ? ( <>

{selectedProject.name}

{sprintTasks.length} tasks ยท {sprintTasks.filter((t) => t.status === "done").length} done

{/* View Toggle */}
{/* View Content */} {viewMode === 'backlog' ? ( ) : ( <> {/* Current Sprint Header */}

Sprint 1

Feb 16 - Feb 22, 2026

Active
{/* Kanban Columns */}
{sprintColumns.map((column) => { // Filter tasks by column statuses const columnTasks = sprintTasks.filter((t) => column.statuses.includes(t.status) ) return (

{column.label}

{columnTasks.length}
{columnTasks.map((task) => { const taskProject = projects.find((p) => p.id === task.projectId) return ( selectTask(task.id)} >
{typeLabels[task.type]} {taskProject && ( {taskProject.name} )}

{task.title}

{task.description && (

{task.description}

)}
{task.priority} {task.comments.length > 0 && ( {task.comments.length} )}
{task.dueDate && ( {new Date(task.dueDate).toLocaleDateString()} )}
{task.tags.length > 0 && (
{task.tags.map((tag) => ( {tag} ))}
)}
)})}
) })}
)} ) : (

Select a project to view tasks

)}
{/* New Task Dialog */} New Task
setNewTask({ ...newTask, title: e.target.value })} className="w-full mt-1.5 px-3 py-2 bg-slate-800 border border-slate-700 rounded-lg text-white placeholder-slate-500 focus:outline-none focus:border-blue-500" placeholder="What needs to be done?" />