"use client"; import { useEffect, useState } from "react"; import { toast } from "sonner"; import { useSortable } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import Image from "next/image"; // Import Image import { useAuth } from "@/hooks/use-auth"; import { deleteAttachment } from "@/services/api-attachments"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog"; // Import AlertDialog import { TodoForm } from "@/components/todo-form"; import { Icons } from "@/components/icons"; import { cn } from "@/lib/utils"; import type { Todo, Tag } from "@/services/api-types"; interface TodoRowProps { todo: Todo; tags: Tag[]; onUpdate: (todo: Partial) => Promise; // Make async onDelete: () => void; onAttachmentsChanged?: (attachments: AttachmentInfo[]) => void; // Add callback isDraggable?: boolean; } export function TodoRow({ todo, tags, onUpdate, onDelete, onAttachmentsChanged, isDraggable = false, }: TodoRowProps) { const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); const [isViewDialogOpen, setIsViewDialogOpen] = useState(false); const { token } = useAuth(); const { attributes, listeners, setNodeRef, transform, transition, isDragging, } = useSortable({ id: todo.id, disabled: !isDraggable, }); const style = { transform: CSS.Transform.toString(transform), transition, opacity: isDragging ? 0.5 : 1, }; const todoTags = tags.filter((tag) => todo.tagIds?.includes(tag.id)); const hasAttachments = todo.attachments && todo.attachments.length > 0; const handleStatusToggle = () => { const newStatus = todo.status === "completed" ? "pending" : "completed"; onUpdate({ status: newStatus }); }; const formatDate = (dateString?: string | null) => { if (!dateString) return null; const date = new Date(dateString); return date.toLocaleDateString(); }; const handleDeleteAttachment = async (attachmentId: string) => { if (!token) { toast.error("Authentication required."); return; } try { await deleteAttachment(todo.id, attachmentId, token); toast.success("Attachment deleted."); const updatedAttachments = todo.attachments.filter( (att) => att.fileId !== attachmentId ); onAttachmentsChanged?.(updatedAttachments); // Also update the local state for the dialog if it's open setFormData((prev) => ({ ...prev, attachments: updatedAttachments })); } catch (error) { console.error("Failed to delete attachment:", error); toast.error("Failed to delete attachment."); } }; // State to manage form data within the row component context if needed, e.g., for the view dialog const [formData, setFormData] = useState(todo); useEffect(() => { setFormData(todo); }, [todo]); return ( <>
handleStatusToggle()} className="h-5 w-5 rounded-full flex-shrink-0" // Added flex-shrink-0 />
setIsViewDialogOpen(true)} > {" "} {/* Make main area clickable */}
{todo.title} {/* Indicators */}
{hasAttachments && ( )} {/* Add other indicators like subtasks if needed */}
{todo.description && (

{todo.description}

)}
{/* Right-aligned section */}
{" "} {/* Added ml-auto and flex-shrink-0 */} {/* Tags */} {todoTags.length > 0 && (
{" "} {/* Hide tags on very small screens */} {todoTags.slice(0, 2).map((tag) => (
))} {todoTags.length > 2 && ( +{todoTags.length - 2} )}
)} {/* Deadline */} {todo.deadline && ( {formatDate(todo.deadline)} )} {/* Action Buttons (Appear on Hover) */}
{/* Delete Confirmation */} Delete Todo? Are you sure you want to delete "{todo.title}"? This action cannot be undone. Cancel Delete
{/* Edit Dialog */} Edit Todo Update your todo details and attachments. { await onUpdate(updatedTodoData); setIsEditDialogOpen(false); }} onAttachmentsChanged={(newAttachments) => { // If the parent needs immediate notification of attachment changes onAttachmentsChanged?.(newAttachments); // Optionally update local state if needed within this component context setFormData((prev) => ({ ...prev, attachments: newAttachments })); }} /> {/* View Dialog */} {/* Re-use the View Dialog structure from TodoCard, adapting as needed */} {/* ... Header content (status badge, title, etc.) ... */}
{/* Status Badge */}
{todo.status.replace("-", " ")} {/* Deadline Badge */} {todo.deadline && ( {" "} {" "} {formatDate(todo.deadline)}{" "} )}
{formData.title} Created {formatDate(formData.createdAt)} {/* Cover Image (find first image attachment) */} {formData.attachments?.find((att) => att.contentType.startsWith("image/") ) && (
att.contentType.startsWith("image/") )?.fileUrl || "/placeholder.svg" } alt={formData.title} fill style={{ objectFit: "cover" }} sizes="450px" />
)}
{/* Description */} {formData.description && (
{formData.description}
)} {/* Tags */} {todoTags.length > 0 && (
{" "}

TAGS

{" "}
{todoTags.map((tag) => ( {tag.name} ))}
{" "}
)} {/* Subtasks */} {formData.subtasks && formData.subtasks.length > 0 && (

SUBTASKS ( {formData.subtasks.filter((s) => s.completed).length}/ {formData.subtasks.length})

{/* ... Subtask list rendering ... */}
)} {/* Attachments */} {(formData.attachments?.length ?? 0) > 0 && (

ATTACHMENTS

{formData.attachments.map((att) => (
{/* Icon or Thumbnail */} {att.contentType.startsWith("image/") ? (
{att.fileName}
) : (
)} {/* File Info */}
{" "} {att.fileName}{" "} {" "} {(att.size / 1024).toFixed(1)} KB -{" "} {att.contentType}{" "}
{/* Delete Button */} {" "} Delete Attachment? {" "} {" "} Are you sure you want to delete " {att.fileName}"? This action cannot be undone.{" "} {" "} {" "} Cancel{" "} handleDeleteAttachment(att.fileId)} className="bg-destructive text-destructive-foreground hover:bg-destructive/90" > {" "} Delete{" "} {" "}
))}
)}
{/* Footer Actions */}
); }