"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; onAssign?: (taskId: string) => void; } function statusColor(status: string): string { switch (status) { case "todo": case "pending": return "#F59E0B"; // yellow case "in_progress": return "#3B82F6"; // blue case "done": return "#22C55E"; // green case "cancelled": return "#6B7280"; // gray default: return "#F59E0B"; } } 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]; } function isDueSoon(dateStr: string): boolean { const due = new Date(dateStr).getTime(); const now = Date.now(); return due - now <= 7 * 24 * 60 * 60 * 1000; } function isPast(dateStr: string): boolean { return new Date(dateStr).getTime() < Date.now(); } function formatDate(dateStr: string): string { return new Date(dateStr).toLocaleDateString(undefined, { month: "short", day: "numeric" }); } export default function TaskCard({ task, onComplete, onAssign }: TaskCardProps) { const { t } = useTranslation(); const taskDone = task.status === "done"; const [swipeOffset, setSwipeOffset] = useState(0); const [swiped, setSwiped] = useState(false); const cardRef = useRef(null); const SWIPE_THRESHOLD = 120; const assignees = task.assigned_to || []; const visibleAssignees = assignees.slice(0, 3); const groupColor = task.group_color; const sColor = statusColor(task.status); const swipeHandlers = useSwipeable({ onSwiping: (e) => { if (e.dir === "Right" && !taskDone && onComplete) { setSwipeOffset(Math.min(e.deltaX, 160)); } }, 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 */} {onComplete && !taskDone && (
SWIPE_THRESHOLD ? "bg-green-500" : "bg-green-400/80" }`} >
{t("tasks.status.done")}
)}
{/* LEFT: title + optional due date */}
{task.title}
{task.due_at && isDueSoon(task.due_at) && (
{formatDate(task.due_at)}
)}
{/* RIGHT: avatars + big status dot */}
{/* Avatars */}
{visibleAssignees.map((userId, i) => (
0 ? -8 : 0, border: "2px solid #13131A", background: userColor(userId), display: "flex", alignItems: "center", justifyContent: "center", fontSize: 10, fontWeight: 700, color: "white", zIndex: 3 - i, position: "relative", flexShrink: 0, }} > {userId.slice(0, 2).toUpperCase()}
))} {/* + add user button */}
{ e.preventDefault(); e.stopPropagation(); if (onAssign) onAssign(task.id); }} title="Přidat uživatele" style={{ width: 26, height: 26, borderRadius: "50%", marginLeft: visibleAssignees.length > 0 ? -8 : 0, border: "2px dashed #3A3A5A", background: "transparent", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 14, color: "#4A4A6A", position: "relative", zIndex: 0, flexShrink: 0, }} > +
{/* Big colored status dot */}
); }