import { t } from "@lingui/core/macro"; import { format } from "date-fns"; import { useEffect, useState } from "react"; import { HiMiniPlus } from "react-icons/hi2"; import DateSelector from "~/components/DateSelector"; import { usePopup } from "~/providers/popup"; import { api } from "~/utils/api"; import { invalidateCard } from "~/utils/cardInvalidation"; interface DueDateSelectorProps { cardPublicId: string; dueDate: Date | null | undefined; isLoading?: boolean; } export function DueDateSelector({ cardPublicId, dueDate, isLoading = false, }: DueDateSelectorProps) { const { showPopup } = usePopup(); const utils = api.useUtils(); const [isOpen, setIsOpen] = useState(false); const [pendingDate, setPendingDate] = useState( dueDate, ); // Sync pendingDate with dueDate when it changes externally useEffect(() => { if (!isOpen) { setPendingDate(dueDate); } }, [dueDate, isOpen]); const updateDueDate = api.card.update.useMutation({ onMutate: async (update) => { await utils.card.byId.cancel(); const previousCard = utils.card.byId.getData({ cardPublicId }); utils.card.byId.setData({ cardPublicId }, (oldCard) => { if (!oldCard) return oldCard; return { ...oldCard, dueDate: update.dueDate !== undefined ? (update.dueDate as Date | null) : oldCard.dueDate, }; }); return { previousCard }; }, onError: (_error, _update, context) => { utils.card.byId.setData({ cardPublicId }, context?.previousCard); showPopup({ header: t`Unable to update due date`, message: t`Please try again later, or contact customer support.`, icon: "error", }); }, onSettled: async () => { await invalidateCard(utils, cardPublicId); await utils.board.byId.invalidate(); }, }); const handleDateSelect = (date: Date | undefined) => { // Only update local state, don't fire mutation setPendingDate(date ?? null); }; const handleBackdropClick = () => { // Only fire mutation if date actually changed const pendingIsNull = pendingDate === null || pendingDate === undefined; const dueIsNull = dueDate === null || dueDate === undefined; let dateChanged = false; if (pendingIsNull && !dueIsNull) { dateChanged = true; } else if (!pendingIsNull && dueIsNull) { dateChanged = true; } else if (!pendingIsNull && !dueIsNull) { // Both are non-null at this point if (pendingDate instanceof Date && dueDate instanceof Date) { dateChanged = pendingDate.getTime() !== dueDate.getTime(); } } // Close popover immediately setIsOpen(false); // Fire mutation if date changed (optimistic update will handle UI) if (dateChanged) { updateDueDate.mutate({ cardPublicId, dueDate: pendingDate ?? null, }); } }; return (
{isOpen && ( <>
{ e.stopPropagation(); }} onMouseDown={(e) => { e.stopPropagation(); }} >
)}
); }