-
-
-
+
+ = {
@@ -27,7 +28,7 @@ const countBgColors: Record = {
emerald: "bg-emerald-500/15 text-emerald-400",
}
-export function KanbanColumn({ status, tasks, onTaskClick, onAddTask, onDrop }: KanbanColumnProps) {
+export function KanbanColumn({ status, tasks, onTaskClick, onAddTask, onDrop, mobile }: KanbanColumnProps) {
const config = STATUS_CONFIG[status]
function handleDragOver(e: React.DragEvent) {
@@ -43,6 +44,48 @@ export function KanbanColumn({ status, tasks, onTaskClick, onAddTask, onDrop }:
}
}
+ if (mobile) {
+ // Mobile: full-width, no column header (tabs handle that), no fixed width
+ return (
+
+
-
-
- OGraph Task Board
-+
+
OGraph Tasks
+{connected ? `${taskCount} task${taskCount !== 1 ? "s" : ""}` - : "Not connected — configure in Settings"} + : "Not connected"}
+
diff --git a/packages/board/src/components/KanbanBoard.tsx b/packages/board/src/components/KanbanBoard.tsx
index dc75548..8559b09 100644
--- a/packages/board/src/components/KanbanBoard.tsx
+++ b/packages/board/src/components/KanbanBoard.tsx
@@ -1,4 +1,5 @@
-import { ALL_STATUSES, type Task, type TaskStatus } from "@/types"
+import { useState } from "react"
+import { ALL_STATUSES, STATUS_CONFIG, type Task, type TaskStatus } from "@/types"
import { KanbanColumn } from "@/components/KanbanColumn"
interface KanbanBoardProps {
@@ -8,30 +9,85 @@ interface KanbanBoardProps {
onMoveTask: (taskId: number, status: TaskStatus) => void
}
-export function KanbanBoard({ tasks, onTaskClick, onAddTask, onMoveTask }: KanbanBoardProps) {
- return (
-
-
- {ALL_STATUSES.map((status) => {
- const columnTasks = tasks
- .filter((t) => t.status === status)
- .sort((a, b) => {
- // Sort by priority first (p0 first), then by updatedAt descending
- const prioOrder = a.priority.localeCompare(b.priority)
- if (prioOrder !== 0) return prioOrder
- return b.updatedAt - a.updatedAt
- })
+const tabColors: Record = {
+ zinc: "border-zinc-500 text-zinc-300",
+ blue: "border-blue-500 text-blue-400",
+ amber: "border-amber-500 text-amber-400",
+ purple: "border-purple-500 text-purple-400",
+ emerald: "border-emerald-500 text-emerald-400",
+}
- return (
+export function KanbanBoard({ tasks, onTaskClick, onAddTask, onMoveTask }: KanbanBoardProps) {
+ const [mobileTab, setMobileTab] = useState("todo")
+
+ const getColumnTasks = (status: TaskStatus) =>
+ tasks
+ .filter((t) => t.status === status)
+ .sort((a, b) => {
+ const prioOrder = a.priority.localeCompare(b.priority)
+ if (prioOrder !== 0) return prioOrder
+ return b.updatedAt - a.updatedAt
+ })
+
+ return (
+ <>
+ {/* Mobile: tab switcher */}
+
+ >
)
}
diff --git a/packages/board/src/components/KanbanColumn.tsx b/packages/board/src/components/KanbanColumn.tsx
index dd8abb7..0104fd8 100644
--- a/packages/board/src/components/KanbanColumn.tsx
+++ b/packages/board/src/components/KanbanColumn.tsx
@@ -9,6 +9,7 @@ interface KanbanColumnProps {
onTaskClick: (task: Task) => void
onAddTask: (status: TaskStatus) => void
onDrop: (taskId: number, status: TaskStatus) => void
+ mobile?: boolean
}
const borderColors: Record
+
+
+ {/* Mobile: single column view */}
+
+ {ALL_STATUSES.map((status) => {
+ const config = STATUS_CONFIG[status]
+ const count = tasks.filter((t) => t.status === status).length
+ const isActive = mobileTab === status
+ return (
+ setMobileTab(status)}
+ className={`
+ shrink-0 px-3 py-2.5 text-xs font-medium border-b-2 transition-all
+ ${isActive
+ ? tabColors[config.color]
+ : "border-transparent text-zinc-500 hover:text-zinc-300"
+ }
+ `}
+ >
+ {config.label}
+ {count > 0 && (
+
+ {count}
+
+ )}
+
+ )
+ })}
+
+
+
+
+
+ {/* Desktop: horizontal kanban */}
+
+ {ALL_STATUSES.map((status) => (
- )
- })}
-
+ ))}
+
+ onAddTask(status)}
+ >
+
+ Add Task
+
+
+ )
+ }
+
+ // Desktop: fixed-width column
return (
+ {tasks.map((task) => (
+
+
+ {
+ e.dataTransfer.setData("text/plain", String(task.id))
+ e.dataTransfer.effectAllowed = "move"
+ }}
+ >
+
+
+ ))}
+
+ {tasks.length === 0 && (
+
+ No tasks
+
+ )}
+