import type { DropResult } from "react-beautiful-dnd"; import { t } from "@lingui/core/macro"; import { DragDropContext, Draggable } from "react-beautiful-dnd"; import { HiPlus, HiXMark } from "react-icons/hi2"; import CircularProgress from "~/components/CircularProgress"; import { StrictModeDroppable as Droppable } from "~/components/StrictModeDroppable"; import { useModal } from "~/providers/modal"; import { usePopup } from "~/providers/popup"; import { api } from "~/utils/api"; import ChecklistItemRow from "./ChecklistItemRow"; import ChecklistNameInput from "./ChecklistNameInput"; import NewChecklistItemForm from "./NewChecklistItemForm"; interface ChecklistItem { publicId: string; title: string; completed: boolean; } interface Checklist { publicId: string; name: string; items: ChecklistItem[]; } interface ChecklistsProps { checklists: Checklist[]; cardPublicId: string; activeChecklistForm?: string | null; setActiveChecklistForm?: (id: string | null) => void; viewOnly?: boolean; } export default function Checklists({ checklists, cardPublicId, activeChecklistForm, setActiveChecklistForm, viewOnly = false, }: ChecklistsProps) { const { openModal } = useModal(); const { showPopup } = usePopup(); const utils = api.useUtils(); const reorderItemMutation = api.checklist.updateItem.useMutation({ onMutate: async (vars) => { await utils.card.byId.cancel({ cardPublicId }); const previous = utils.card.byId.getData({ cardPublicId }); utils.card.byId.setData({ cardPublicId }, (old) => { if (!old) return old; const updatedChecklists = old.checklists.map((cl) => { const itemIndex = cl.items.findIndex( (item) => item.publicId === vars.checklistItemPublicId, ); if (itemIndex === -1 || vars.index === undefined) return cl; const newIndex = vars.index; const items = Array.from(cl.items); const [movedItem] = items.splice(itemIndex, 1); if (!movedItem) return cl; items.splice(newIndex, 0, movedItem); return { ...cl, items }; }); return { ...old, checklists: updatedChecklists } as typeof old; }); return { previous }; }, onError: (_err, _vars, ctx) => { if (ctx?.previous) utils.card.byId.setData({ cardPublicId }, ctx.previous); showPopup({ header: t`Unable to reorder checklist item`, message: t`Please try again later, or contact customer support.`, icon: "error", }); }, onSettled: async () => { await utils.card.byId.invalidate({ cardPublicId }); }, }); const onDragEnd = (result: DropResult) => { if (!result.destination) return; const { source, destination, draggableId } = result; if (source.droppableId !== destination.droppableId) return; if (source.index === destination.index) return; reorderItemMutation.mutate({ checklistItemPublicId: draggableId, index: destination.index, }); }; if (checklists.length === 0) return null; return (
{checklists.map((checklist) => { const completedItems = checklist.items.filter( (item) => item.completed, ); const progress = checklist.items.length > 0 && completedItems.length > 0 ? (completedItems.length / checklist.items.length) * 100 : 2; return (
{!viewOnly && (
{completedItems.length}/{checklist.items.length}
)} {viewOnly && (
{completedItems.length}/{checklist.items.length}
)}
{(provided) => (
{checklist.items.map((item, index) => ( {(provided, snapshot) => (
setActiveChecklistForm?.(checklist.publicId) } viewOnly={viewOnly} dragHandleProps={provided.dragHandleProps} isDragging={snapshot.isDragging} />
)}
))} {provided.placeholder}
)}
{activeChecklistForm === checklist.publicId && !viewOnly && (
setActiveChecklistForm?.(null)} readOnly={viewOnly} />
)}
); })}
); }