From 1b57be1d64d2e62016542b509f618bd0515e1e85 Mon Sep 17 00:00:00 2001 From: Mo Tarbin Date: Mon, 9 Feb 2026 23:18:45 -0500 Subject: [PATCH] Show ability to undo task on choreView https://github.com/donetick/donetick/issues/505 --- src/views/ChoreEdit/ChoreView.jsx | 93 +++++++++++++++---------------- 1 file changed, 45 insertions(+), 48 deletions(-) diff --git a/src/views/ChoreEdit/ChoreView.jsx b/src/views/ChoreEdit/ChoreView.jsx index 0a003c5..5cd7ee6 100644 --- a/src/views/ChoreEdit/ChoreView.jsx +++ b/src/views/ChoreEdit/ChoreView.jsx @@ -1,7 +1,6 @@ import { Archive, CalendarMonth, - CancelScheduleSend, Check, Checklist, CloseFullscreen, @@ -35,7 +34,6 @@ import { MenuButton, MenuItem, Sheet, - Snackbar, Typography, } from '@mui/joy' import { Divider } from '@mui/material' @@ -54,6 +52,7 @@ import { useStartChore, } from '../../queries/TimeQueries' import { useCircleMembers, useUserProfile } from '../../queries/UserQueries.jsx' +import { useNotification } from '../../service/NotificationProvider' import { ChoreStatus, notInCompletionWindow } from '../../utils/Chores.jsx' import { getTextColorFromBackgroundColor } from '../../utils/Colors.jsx' import { @@ -63,6 +62,7 @@ import { RejectChore, SkipChore, UnArchiveChore, + UndoChoreAction, UpdateChorePriority, } from '../../utils/Fetcher' import Priorities from '../../utils/Priorities' @@ -82,12 +82,11 @@ const ChoreView = () => { const { choreId } = useParams() const [note, setNote] = useState(null) const queryClient = useQueryClient() + const { showSuccess, showError, showUndo } = useNotification() const [searchParams] = useSearchParams() - const [isPendingCompletion, setIsPendingCompletion] = useState(false) const [timeoutId, setTimeoutId] = useState(null) - const [secondsLeftToCancel, setSecondsLeftToCancel] = useState(null) const [completedDate, setCompletedDate] = useState(null) const [confirmModelConfig, setConfirmModelConfig] = useState({ isOpen: false, @@ -189,20 +188,7 @@ const ChoreView = () => { setInfoCards(cards) } const handleTaskCompletion = () => { - setIsPendingCompletion(true) - let seconds = 3 // Starting countdown from 3 seconds - setSecondsLeftToCancel(seconds) - - const countdownInterval = setInterval(() => { - seconds -= 1 - setSecondsLeftToCancel(seconds) - - if (seconds <= 0) { - clearInterval(countdownInterval) // Stop the countdown when it reaches 0 - } - }, 1000) - - const id = setTimeout(() => { + let id = setTimeout(() => { MarkChoreComplete( choreId, impersonatedUser @@ -220,11 +206,8 @@ const ChoreView = () => { } }) .then(() => { - setIsPendingCompletion(false) clearTimeout(id) - clearInterval(countdownInterval) // Ensure to clear this interval as well setTimeoutId(null) - setSecondsLeftToCancel(null) // Invalidate chores cache to refetch data queryClient.invalidateQueries(['chores']) }) @@ -241,6 +224,16 @@ const ChoreView = () => { }, 3000) setTimeoutId(id) + + // Show undo notification + showSuccess({ + title: 'Task Completed', + message: 'Your task has been marked as complete', + undoAction: () => { + clearTimeout(id) + setTimeoutId(null) + }, + }) } const handleSkippingTask = () => { SkipChore(choreId).then(response => { @@ -250,6 +243,36 @@ const ChoreView = () => { setChore(newChore) // Invalidate chores cache to refetch data queryClient.invalidateQueries(['chores']) + + // Show undo notification + showSuccess({ + message: 'Task skipped', + undoAction: async () => { + try { + const undoResponse = await UndoChoreAction(choreId) + if (undoResponse.ok) { + // Refetch chore details after undo + const detailResponse = await GetChoreDetailById(choreId) + if (detailResponse.ok) { + const detailData = await detailResponse.json() + setChore(detailData.res) + queryClient.invalidateQueries(['chores']) + } + showUndo({ + title: 'Undo Successful', + message: 'Task skip has been undone.', + }) + } else { + throw new Error('Failed to undo') + } + } catch (error) { + showError({ + title: 'Undo Failed', + message: 'Unable to undo the action. Please try again.', + }) + } + }, + }) }) } }) @@ -924,12 +947,11 @@ const ChoreView = () => { size='lg' onClick={handleTaskCompletion} disabled={ - isPendingCompletion || notInCompletionWindow(chore) || (chore.lastCompletedDate !== null && chore.frequencyType === 'once') } - color={isPendingCompletion ? 'danger' : 'success'} + color='success' startDecorator={} sx={{ flex: 4, @@ -1017,31 +1039,6 @@ const ChoreView = () => { )} - { - if (timeoutId) { - clearTimeout(timeoutId) - setIsPendingCompletion(false) - setTimeoutId(null) - setSecondsLeftToCancel(null) // Reset or adjust as needed - } - }} - size='lg' - variant='outlined' - color='danger' - startDecorator={} - > - Cancel - - } - > - - Task will be marked as completed in {secondsLeftToCancel} seconds - -