From babc1d95bab1d1b338e205c9c45eea075d888fcc Mon Sep 17 00:00:00 2001 From: Citali Date: Sat, 5 Jul 2025 10:49:28 +0200 Subject: [PATCH] Kanban shenanigans --- src/components/kanban/KanbanBoard.tsx | 25 +++++-- src/components/kanban/KanbanCardModal.tsx | 9 ++- src/components/kanban/KanbanCardPreview.tsx | 82 +++++++++++++-------- src/components/kanban/KanbanColumn.tsx | 2 +- 4 files changed, 79 insertions(+), 39 deletions(-) diff --git a/src/components/kanban/KanbanBoard.tsx b/src/components/kanban/KanbanBoard.tsx index e1a066f..a1de5f0 100644 --- a/src/components/kanban/KanbanBoard.tsx +++ b/src/components/kanban/KanbanBoard.tsx @@ -3,7 +3,7 @@ import { mockCards } from "@/data/mockCards" import type { KanbanCardData } from "@/types/kanban" import { DndContext, DragEndEvent, closestCenter } from "@dnd-kit/core" -import { useState } from "react" +import { useEffect, useState } from "react" import { KanbanColumn } from "./KanbanColumn" export type Status = "PENDING" | "ACCEPTED" | "IN_PROGRESS" | "DONE" | "REJECTED" @@ -13,6 +13,11 @@ const columns: Status[] = ["PENDING", "ACCEPTED", "IN_PROGRESS", "DONE", "REJECT export function KanbanBoard() { const [cards, setCards] = useState>(mockCards) + const [mounted, setMounted] = useState(false) + useEffect(() => { + setMounted(true) + }, []) + function findColumnByCardId(cardId: string): Status | null { for (const status of columns) { if (cards[status].some((c) => c.id === cardId)) return status @@ -41,12 +46,18 @@ export function KanbanBoard() { } return ( - -
- {columns.map((status) => ( +
+ {mounted ? ( + + {columns.map((status) => ( + + ))} + + ) : ( + columns.map((status) => ( - ))} -
- + )) + )} +
) } diff --git a/src/components/kanban/KanbanCardModal.tsx b/src/components/kanban/KanbanCardModal.tsx index ca77acf..4d84f3b 100644 --- a/src/components/kanban/KanbanCardModal.tsx +++ b/src/components/kanban/KanbanCardModal.tsx @@ -1,8 +1,8 @@ "use client" -import { Dialog, DialogContent } from "@/components/ui/dialog" +import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog" import type { KanbanCardData } from "@/types/kanban" - +import { VisuallyHidden } from "@radix-ui/react-visually-hidden" export interface KanbanCardModalProps { card: KanbanCardData | null onClose: () => void @@ -12,6 +12,11 @@ export function KanbanCardModal({ card, onClose }: KanbanCardModalProps) { return ( + + + {card?.title ?? "Card Details"} + + {card && (

{card.title}

diff --git a/src/components/kanban/KanbanCardPreview.tsx b/src/components/kanban/KanbanCardPreview.tsx index 93cb41c..9d18004 100644 --- a/src/components/kanban/KanbanCardPreview.tsx +++ b/src/components/kanban/KanbanCardPreview.tsx @@ -1,43 +1,71 @@ +// src/components/kanban/KanbanCardPreview.tsx "use client" -import type { KanbanCardData } from "@/types/kanban" +import { cn } from "@/lib/utils" +import { KanbanCardData } from "@/types/kanban" import { useDraggable } from "@dnd-kit/core" +import { PointerEvent, useRef } from "react" -interface KanbanCardPreviewProps extends KanbanCardData { +interface KanbanCardPreviewProps { + card: KanbanCardData onClick: () => void } -export function KanbanCardPreview({ - id, - title, - labels = [], - todos = [], - onClick, -}: KanbanCardPreviewProps) { - const { setNodeRef, attributes, listeners, transform, isDragging } = useDraggable({ - id, +export function KanbanCardPreview({ card, onClick }: KanbanCardPreviewProps) { + const { + attributes, + listeners = {}, + setNodeRef, + transform, + isDragging, + } = useDraggable({ + id: card.id, + data: { card }, }) - const done = todos.filter((t) => t.done).length - const total = todos.length + const dragThreshold = 5 + const pointerStart = useRef<{ x: number; y: number } | null>(null) + + const handlePointerDown = (e: PointerEvent) => { + pointerStart.current = { x: e.clientX, y: e.clientY } + listeners.onPointerDown?.(e) + } + + const handlePointerUp = (e: PointerEvent) => { + if (!pointerStart.current) return + const dx = Math.abs(e.clientX - pointerStart.current.x) + const dy = Math.abs(e.clientY - pointerStart.current.y) + const isDrag = dx > dragThreshold || dy > dragThreshold + if (!isDrag) { + onClick() + } + pointerStart.current = null + } + + const todoCount = card.todos?.length ?? 0 + const doneCount = card.todos?.filter((t) => t.done).length ?? 0 return (
-
- {labels.map((label) => ( - + {card.labels?.map((label) => ( +
))}
- -
{title}
- - {total > 0 && ( -
- {done} / {total} tasks done -
- )} +

{card.title}

+
+ {doneCount}/{todoCount} ToDos +
) } diff --git a/src/components/kanban/KanbanColumn.tsx b/src/components/kanban/KanbanColumn.tsx index 958daa2..6812ebf 100644 --- a/src/components/kanban/KanbanColumn.tsx +++ b/src/components/kanban/KanbanColumn.tsx @@ -27,7 +27,7 @@ export function KanbanColumn({ status, items }: KanbanColumnProps) { {items.map((item) => ( setActiveCard(item)} /> ))}