"use client"; import { useState, useRef } from "react"; import { Task } from "@/lib/api"; import { useTranslation } from "@/lib/i18n"; import Link from "next/link"; import { useSwipeable } from "react-swipeable"; interface TaskCardProps { task: Task; onComplete?: (taskId: string) => void; } const PRIORITY_COLORS: Record = { urgent: "#ef4444", high: "#f97316", medium: "#eab308", low: "#22c55e", }; const PRIORITY_ICONS: Record = { urgent: "\u25c6", high: "\u25b2", medium: "\u25cf", low: "\u25bd", }; const STATUS_DOT_COLORS: Record = { pending: "#9CA3AF", in_progress: "#F59E0B", done: "#22C55E", completed: "#22C55E", cancelled: "#EF4444", }; function isDone(status: string): boolean { return status === "done" || status === "completed"; } function isInProgress(status: string): boolean { return status === "in_progress"; } /** Generate a consistent color from a string (user ID) */ function userColor(userId: string): string { const colors = [ "#3B82F6", "#8B5CF6", "#EC4899", "#F59E0B", "#10B981", "#06B6D4", "#F97316", "#6366F1", ]; let hash = 0; for (let i = 0; i < userId.length; i++) { hash = ((hash << 5) - hash) + userId.charCodeAt(i); hash |= 0; } return colors[Math.abs(hash) % colors.length]; } /** Get initials from user ID (first 2 chars uppercase) */ function userInitials(userId: string): string { return userId.slice(0, 2).toUpperCase(); } export default function TaskCard({ task, onComplete }: TaskCardProps) { const { t } = useTranslation(); const priorityColor = PRIORITY_COLORS[task.priority] || PRIORITY_COLORS.medium; const priorityIcon = PRIORITY_ICONS[task.priority] || PRIORITY_ICONS.medium; const statusDotColor = STATUS_DOT_COLORS[task.status] || STATUS_DOT_COLORS.pending; const taskDone = isDone(task.status); const taskActive = isInProgress(task.status); const [swipeOffset, setSwipeOffset] = useState(0); const [swiped, setSwiped] = useState(false); const cardRef = useRef(null); const SWIPE_THRESHOLD = 120; const MAX_AVATARS = 3; const assignees = task.assigned_to || []; const visibleAssignees = assignees.slice(0, MAX_AVATARS); const extraCount = assignees.length - MAX_AVATARS; const swipeHandlers = useSwipeable({ onSwiping: (e) => { if (e.dir === "Right" && !taskDone && onComplete) { const offset = Math.min(e.deltaX, 160); setSwipeOffset(offset); } }, onSwipedRight: (e) => { if (e.absX > SWIPE_THRESHOLD && !taskDone && onComplete) { setSwiped(true); setTimeout(() => { onComplete(task.id); }, 300); } else { setSwipeOffset(0); } }, onSwiped: () => { if (!swiped) setSwipeOffset(0); }, trackMouse: false, trackTouch: true, preventScrollOnSwipe: false, delta: 10, }); const showCompleteHint = swipeOffset > 40; return (
{/* Swipe background - green complete indicator */} {onComplete && !taskDone && (
SWIPE_THRESHOLD ? "bg-green-500" : "bg-green-400/80" }`} >
{t("tasks.status.done")}
)}
{/* LEFT: User avatars (overlapping circles) */}
0 ? 28 : 0 }}> {visibleAssignees.length > 0 && (
{visibleAssignees.map((userId, idx) => (
0 ? -8 : 0, position: "relative", zIndex: MAX_AVATARS - idx, }} title={userId} > {userInitials(userId)}
))} {extraCount > 0 && (
+{extraCount}
)}
)}
{/* Status dot — big colored circle */}
{/* MIDDLE: Task title — single line, truncated */}

{task.title}

{/* RIGHT: Group icon */} {task.group_icon && ( {task.group_icon} )} {/* RIGHT: Priority indicator */} {priorityIcon}
); }