import { useRef } from 'react'; import { useSortable } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; import type { DraggableAttributes } from '@dnd-kit/core'; import type { SyntheticListenerMap } from '@dnd-kit/core/dist/hooks/utilities'; import type { Task } from '../../types'; import { DonutGauge } from './DonutGauge'; function fmtDate(iso: string | null | undefined): string { if (!iso) return ''; const d = new Date(iso); return `${d.getFullYear()}.${String(d.getMonth() + 1).padStart(2, '0')}.${String(d.getDate()).padStart(2, '0')}`; } function fmtDateRange(task: Task): string { if (!task.showDate || (!task.startDate && !task.dueDate)) return ''; const start = task.startDate ? fmtDate(task.startDate) : '?'; const end = task.dueDate ? fmtDate(task.dueDate) : '?'; return `${start} ~ ${end}`; } function firstDescriptionLine(text: string | null | undefined): string { if (!text) return ''; const line = text.split('\n').map((l) => l.replace(/^[•·\-]\s*/, '').trim()).find(Boolean); return line ?? ''; } type SectionOption = { value: string; label: string }; export function SortableTaskCard({ task, variant = 'project', onSelect, }: { task: Task; variant?: 'project' | 'routine'; sectionOptions?: SectionOption[]; accent?: string; onSelect?: (task: Task) => void; }) { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: task.id }); const pointerStart = useRef<{ x: number; y: number } | null>(null); const handlePointerDown = (e: React.PointerEvent) => { if (e.button !== 0) return; pointerStart.current = { x: e.clientX, y: e.clientY }; listeners?.onPointerDown?.(e); }; const handlePointerUp = (e: React.PointerEvent) => { if (e.button !== 0 || !pointerStart.current) return; const dx = e.clientX - pointerStart.current.x; const dy = e.clientY - pointerStart.current.y; pointerStart.current = null; if (!isDragging && Math.hypot(dx, dy) < 8) { onSelect?.(task); } }; return ( ); } export function TaskCard({ task, variant = 'project', dragRef, dragStyle, dragAttributes, dragListeners, onPointerDown, onPointerUp, }: { task: Task; variant?: 'project' | 'routine'; dragRef?: (node: HTMLElement | null) => void; dragStyle?: React.CSSProperties; dragAttributes?: DraggableAttributes; dragListeners?: SyntheticListenerMap; onPointerDown?: (e: React.PointerEvent) => void; onPointerUp?: (e: React.PointerEvent) => void; }) { const dragHandlers = { onPointerDown: (e: React.PointerEvent) => { onPointerDown?.(e); }, onPointerUp: (e: React.PointerEvent) => { onPointerUp?.(e); }, onKeyDown: dragListeners?.onKeyDown as React.KeyboardEventHandler | undefined, }; if (variant === 'routine') { const descLine = firstDescriptionLine(task.description); return (
{task.title} {descLine &&

• {descLine}

}
); } const dateRange = fmtDateRange(task); const descLine = task.showDescription ? firstDescriptionLine(task.description) : ''; const showProgress = task.showProgress !== false; return (
{task.title}
{dateRange && (
작업 기간 {dateRange}
)} {descLine && (
주요 내용 {descLine}
)} {task.showIssue && task.issueNote && (
이슈 {task.issueNote}
)}
{showProgress && (
)}
); }