Show ability to undo task on choreView
https://github.com/donetick/donetick/issues/505
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
Archive,
|
Archive,
|
||||||
CalendarMonth,
|
CalendarMonth,
|
||||||
CancelScheduleSend,
|
|
||||||
Check,
|
Check,
|
||||||
Checklist,
|
Checklist,
|
||||||
CloseFullscreen,
|
CloseFullscreen,
|
||||||
@@ -35,7 +34,6 @@ import {
|
|||||||
MenuButton,
|
MenuButton,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
Sheet,
|
Sheet,
|
||||||
Snackbar,
|
|
||||||
Typography,
|
Typography,
|
||||||
} from '@mui/joy'
|
} from '@mui/joy'
|
||||||
import { Divider } from '@mui/material'
|
import { Divider } from '@mui/material'
|
||||||
@@ -54,6 +52,7 @@ import {
|
|||||||
useStartChore,
|
useStartChore,
|
||||||
} from '../../queries/TimeQueries'
|
} from '../../queries/TimeQueries'
|
||||||
import { useCircleMembers, useUserProfile } from '../../queries/UserQueries.jsx'
|
import { useCircleMembers, useUserProfile } from '../../queries/UserQueries.jsx'
|
||||||
|
import { useNotification } from '../../service/NotificationProvider'
|
||||||
import { ChoreStatus, notInCompletionWindow } from '../../utils/Chores.jsx'
|
import { ChoreStatus, notInCompletionWindow } from '../../utils/Chores.jsx'
|
||||||
import { getTextColorFromBackgroundColor } from '../../utils/Colors.jsx'
|
import { getTextColorFromBackgroundColor } from '../../utils/Colors.jsx'
|
||||||
import {
|
import {
|
||||||
@@ -63,6 +62,7 @@ import {
|
|||||||
RejectChore,
|
RejectChore,
|
||||||
SkipChore,
|
SkipChore,
|
||||||
UnArchiveChore,
|
UnArchiveChore,
|
||||||
|
UndoChoreAction,
|
||||||
UpdateChorePriority,
|
UpdateChorePriority,
|
||||||
} from '../../utils/Fetcher'
|
} from '../../utils/Fetcher'
|
||||||
import Priorities from '../../utils/Priorities'
|
import Priorities from '../../utils/Priorities'
|
||||||
@@ -82,12 +82,11 @@ const ChoreView = () => {
|
|||||||
const { choreId } = useParams()
|
const { choreId } = useParams()
|
||||||
const [note, setNote] = useState(null)
|
const [note, setNote] = useState(null)
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
|
const { showSuccess, showError, showUndo } = useNotification()
|
||||||
|
|
||||||
const [searchParams] = useSearchParams()
|
const [searchParams] = useSearchParams()
|
||||||
|
|
||||||
const [isPendingCompletion, setIsPendingCompletion] = useState(false)
|
|
||||||
const [timeoutId, setTimeoutId] = useState(null)
|
const [timeoutId, setTimeoutId] = useState(null)
|
||||||
const [secondsLeftToCancel, setSecondsLeftToCancel] = useState(null)
|
|
||||||
const [completedDate, setCompletedDate] = useState(null)
|
const [completedDate, setCompletedDate] = useState(null)
|
||||||
const [confirmModelConfig, setConfirmModelConfig] = useState({
|
const [confirmModelConfig, setConfirmModelConfig] = useState({
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
@@ -189,20 +188,7 @@ const ChoreView = () => {
|
|||||||
setInfoCards(cards)
|
setInfoCards(cards)
|
||||||
}
|
}
|
||||||
const handleTaskCompletion = () => {
|
const handleTaskCompletion = () => {
|
||||||
setIsPendingCompletion(true)
|
let id = setTimeout(() => {
|
||||||
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(() => {
|
|
||||||
MarkChoreComplete(
|
MarkChoreComplete(
|
||||||
choreId,
|
choreId,
|
||||||
impersonatedUser
|
impersonatedUser
|
||||||
@@ -220,11 +206,8 @@ const ChoreView = () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setIsPendingCompletion(false)
|
|
||||||
clearTimeout(id)
|
clearTimeout(id)
|
||||||
clearInterval(countdownInterval) // Ensure to clear this interval as well
|
|
||||||
setTimeoutId(null)
|
setTimeoutId(null)
|
||||||
setSecondsLeftToCancel(null)
|
|
||||||
// Invalidate chores cache to refetch data
|
// Invalidate chores cache to refetch data
|
||||||
queryClient.invalidateQueries(['chores'])
|
queryClient.invalidateQueries(['chores'])
|
||||||
})
|
})
|
||||||
@@ -241,6 +224,16 @@ const ChoreView = () => {
|
|||||||
}, 3000)
|
}, 3000)
|
||||||
|
|
||||||
setTimeoutId(id)
|
setTimeoutId(id)
|
||||||
|
|
||||||
|
// Show undo notification
|
||||||
|
showSuccess({
|
||||||
|
title: 'Task Completed',
|
||||||
|
message: 'Your task has been marked as complete',
|
||||||
|
undoAction: () => {
|
||||||
|
clearTimeout(id)
|
||||||
|
setTimeoutId(null)
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const handleSkippingTask = () => {
|
const handleSkippingTask = () => {
|
||||||
SkipChore(choreId).then(response => {
|
SkipChore(choreId).then(response => {
|
||||||
@@ -250,6 +243,36 @@ const ChoreView = () => {
|
|||||||
setChore(newChore)
|
setChore(newChore)
|
||||||
// Invalidate chores cache to refetch data
|
// Invalidate chores cache to refetch data
|
||||||
queryClient.invalidateQueries(['chores'])
|
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'
|
size='lg'
|
||||||
onClick={handleTaskCompletion}
|
onClick={handleTaskCompletion}
|
||||||
disabled={
|
disabled={
|
||||||
isPendingCompletion ||
|
|
||||||
notInCompletionWindow(chore) ||
|
notInCompletionWindow(chore) ||
|
||||||
(chore.lastCompletedDate !== null &&
|
(chore.lastCompletedDate !== null &&
|
||||||
chore.frequencyType === 'once')
|
chore.frequencyType === 'once')
|
||||||
}
|
}
|
||||||
color={isPendingCompletion ? 'danger' : 'success'}
|
color='success'
|
||||||
startDecorator={<Check />}
|
startDecorator={<Check />}
|
||||||
sx={{
|
sx={{
|
||||||
flex: 4,
|
flex: 4,
|
||||||
@@ -1017,31 +1039,6 @@ const ChoreView = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Snackbar
|
|
||||||
open={isPendingCompletion}
|
|
||||||
endDecorator={
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
if (timeoutId) {
|
|
||||||
clearTimeout(timeoutId)
|
|
||||||
setIsPendingCompletion(false)
|
|
||||||
setTimeoutId(null)
|
|
||||||
setSecondsLeftToCancel(null) // Reset or adjust as needed
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
size='lg'
|
|
||||||
variant='outlined'
|
|
||||||
color='danger'
|
|
||||||
startDecorator={<CancelScheduleSend />}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Typography level='body-md' textAlign={'center'}>
|
|
||||||
Task will be marked as completed in {secondsLeftToCancel} seconds
|
|
||||||
</Typography>
|
|
||||||
</Snackbar>
|
|
||||||
<ConfirmationModal config={confirmModelConfig} />
|
<ConfirmationModal config={confirmModelConfig} />
|
||||||
<ConfirmationModal config={timerActionConfig} />
|
<ConfirmationModal config={timerActionConfig} />
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Reference in New Issue
Block a user