Update Chore view

This commit is contained in:
Mo Tarbin 2024-07-15 21:58:23 -04:00
parent 7a490116b7
commit 93512eb666
3 changed files with 170 additions and 149 deletions

View file

@ -73,6 +73,16 @@ const MarkChoreComplete = (id, note, completedDate) => {
body: JSON.stringify(body), body: JSON.stringify(body),
}) })
} }
const SkipChore = id => {
return Fetch(`${API_URL}/chores/${id}/skip`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
})
}
const CreateChore = chore => { const CreateChore = chore => {
return Fetch(`${API_URL}/chores/`, { return Fetch(`${API_URL}/chores/`, {
method: 'POST', method: 'POST',
@ -277,6 +287,7 @@ export {
SaveChore, SaveChore,
SaveThing, SaveThing,
signUp, signUp,
SkipChore,
UpdateThingState, UpdateThingState,
UpdateUserDetails, UpdateUserDetails,
} }

View file

@ -3,11 +3,10 @@ import {
CancelScheduleSend, CancelScheduleSend,
Check, Check,
Checklist, Checklist,
Edit,
History, History,
Note,
PeopleAlt, PeopleAlt,
Person, Person,
SwitchAccessShortcut,
Timelapse, Timelapse,
} from '@mui/icons-material' } from '@mui/icons-material'
import { import {
@ -22,7 +21,6 @@ import {
Input, Input,
ListItem, ListItem,
ListItemContent, ListItemContent,
ListItemDecorator,
Sheet, Sheet,
Snackbar, Snackbar,
styled, styled,
@ -35,15 +33,17 @@ import {
GetAllUsers, GetAllUsers,
GetChoreDetailById, GetChoreDetailById,
MarkChoreComplete, MarkChoreComplete,
SkipChore,
} from '../../utils/Fetcher' } from '../../utils/Fetcher'
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
const IconCard = styled('div')({ const IconCard = styled('div')({
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
backgroundColor: '#f0f0f0', // Adjust the background color as needed backgroundColor: '#f0f0f0', // Adjust the background color as needed
borderRadius: '50%', borderRadius: '50%',
minWidth: '50px', minWidth: '40px',
height: '50px', height: '40px',
marginRight: '16px', marginRight: '16px',
}) })
const ChoreView = () => { const ChoreView = () => {
@ -60,6 +60,8 @@ const ChoreView = () => {
const [timeoutId, setTimeoutId] = useState(null) const [timeoutId, setTimeoutId] = useState(null)
const [secondsLeftToCancel, setSecondsLeftToCancel] = useState(null) const [secondsLeftToCancel, setSecondsLeftToCancel] = useState(null)
const [completedDate, setCompletedDate] = useState(null) const [completedDate, setCompletedDate] = useState(null)
const [confirmModelConfig, setConfirmModelConfig] = useState({})
useEffect(() => { useEffect(() => {
Promise.all([ Promise.all([
GetChoreDetailById(choreId).then(resp => { GetChoreDetailById(choreId).then(resp => {
@ -88,24 +90,21 @@ const ChoreView = () => {
const generateInfoCards = chore => { const generateInfoCards = chore => {
const cards = [ const cards = [
// {
// size: 6,
// icon: <CalendarMonth />,
// text: 'Due Date',
// subtext: moment(chore.nextDueDate).format('MM/DD/YYYY hh:mm A'),
// },
{ {
size: 6, size: 6,
icon: <PeopleAlt />, icon: <PeopleAlt />,
text: 'Assigned To', text: 'Assigned To',
subtext: performers.find(p => p.id === chore.assignedTo)?.displayName, subtext: performers.find(p => p.id === chore.assignedTo)?.displayName,
}, },
// { {
// size: 6, size: 6,
// icon: <Person />, icon: <CalendarMonth />,
// text: 'Created By', text: 'Due Date',
// subtext: performers.find(p => p.id === chore.createdBy)?.displayName, subtext: chore.nextDueDate
// }, ? moment(chore.nextDueDate).fromNow()
: 'N/A',
},
// { // {
// icon: <TextFields />, // icon: <TextFields />,
// text: 'Frequency', // text: 'Frequency',
@ -116,37 +115,40 @@ const ChoreView = () => {
{ {
size: 6, size: 6,
icon: <Checklist />, icon: <Checklist />,
text: 'Completed', text: 'Total Completed',
subtext: `${chore.totalCompletedCount} times`, subtext: `${chore.totalCompletedCount} times`,
}, },
{ {
size: 6, size: 6,
icon: <Timelapse />, icon: <Timelapse />,
text: 'Last time', text: 'Last Completed',
subtext: subtext:
chore.lastCompletedDate && // chore.lastCompletedDate &&
moment(chore.lastCompletedDate).format('MM/DD/YYYY hh:mm A'), // moment(chore.lastCompletedDate).format('MM/DD/YYYY hh:mm A'),
chore.lastCompletedDate && moment(chore.lastCompletedDate).fromNow(),
}, },
{ {
size: 6, size: 6,
icon: <Person />, icon: <Person />,
text: 'Last', text: 'Last Performer',
subtext: chore.lastCompletedDate subtext: chore.lastCompletedDate
? `${ ? `${
chore.lastCompletedDate &&
moment(chore.lastCompletedDate).fromNow()
// moment(chore.lastCompletedDate).format('MM/DD/YYYY hh:mm A'))
}(${
performers.find(p => p.id === chore.lastCompletedBy)?.displayName performers.find(p => p.id === chore.lastCompletedBy)?.displayName
})` }`
: 'Never', : 'Never',
}, },
{ {
size: 12, size: 6,
icon: <Note />, icon: <Person />,
text: 'Recent Note', text: 'Created By',
subtext: chore.notes || '--', subtext: performers.find(p => p.id === chore.createdBy)?.displayName,
}, },
// {
// size: 12,
// icon: <Note />,
// text: 'Recent Note',
// subtext: chore.notes || '--',
// },
] ]
setInfoCards(cards) setInfoCards(cards)
} }
@ -195,7 +197,16 @@ const ChoreView = () => {
setTimeoutId(id) setTimeoutId(id)
} }
const handleSkippingTask = () => {
SkipChore(choreId).then(response => {
if (response.ok) {
response.json().then(data => {
const newChore = data.res
setChore(newChore)
})
}
})
}
return ( return (
<Container <Container
maxWidth='sm' maxWidth='sm'
@ -232,69 +243,80 @@ const ChoreView = () => {
</Chip> </Chip>
</Box> </Box>
<Box> <Box>
<Grid container spacing={1}> <Typography level='title-md' sx={{ mb: 1 }}>
{infoCards.map((info, index) => ( Details
<Grid key={index} item xs={info.size} sm={info.size}> </Typography>
<Sheet
sx={{ <Sheet
mb: 1, sx={{
borderRadius: 'lg', mb: 1,
p: 1, borderRadius: 'lg',
boxShadow: 'sm', p: 2,
}} }}
> variant='outlined'
<ListItem> >
<ListItemDecorator> <Grid container spacing={1}>
<IconCard>{info.icon}</IconCard> {infoCards.map((detail, index) => (
</ListItemDecorator> <Grid item xs={4} key={index}>
{/* divider between the list items: */}
<ListItem key={index}>
<ListItemContent> <ListItemContent>
<Typography level='body1' sx={{ fontWeight: 'md' }}> <Typography level='body-xs' sx={{ fontWeight: 'md' }}>
{info.text} {detail.text}
</Typography> </Typography>
<Typography level='body1' color='text.tertiary'> <Typography level='body-sm' color='text.tertiary'>
{info.subtext ? info.subtext : '--'} {detail.subtext ? detail.subtext : '--'}
</Typography> </Typography>
</ListItemContent> </ListItemContent>
</ListItem> </ListItem>
</Sheet> </Grid>
</Grid> ))}
))}
<Grid item xs={6}>
<Button
startDecorator={<History />}
size='lg'
color='success'
variant='outlined'
fullWidth
onClick={() => {
navigate(`/chores/${choreId}/history`)
}}
>
History
</Button>
</Grid> </Grid>
<Grid item xs={6}> </Sheet>
<Button {chore.notes && (
startDecorator={<Edit />} <>
size='lg' <Typography level='title-md' sx={{ mb: 1 }}>
color='success' Previous note:
variant='outlined' </Typography>
fullWidth <Sheet variant='outlined' sx={{ p: 2, borderRadius: 'lg' }}>
onClick={() => { <Typography level='body-md' sx={{ mb: 1 }}>
navigate(`/chores/${choreId}/edit`) {chore.notes || '--'}
}} </Typography>
> </Sheet>
Edit </>
</Button> )}
</Grid>
</Grid> <Box
sx={{
display: 'flex',
flexDirection: 'row',
gap: 1,
alignContent: 'center',
justifyContent: 'center',
}}
>
<Button
startDecorator={<History />}
size='lg'
color='success'
variant='plain'
onClick={() => {
navigate(`/chores/${choreId}/history`)
}}
>
View History
</Button>
</Box>
</Box> </Box>
{/* <Divider {/* <Divider
sx={{ sx={{
my: 2, my: 2,
}} }}
/> */} /> */}
<Typography level='title-md' sx={{ mt: 1 }}>
Actions
</Typography>
<Card <Card
sx={{ sx={{
p: 2, p: 2,
@ -319,11 +341,11 @@ const ChoreView = () => {
}} }}
size='md' size='md'
sx={{ sx={{
my: 1, mb: 1,
}} }}
/> />
<FormControl size='sm' sx={{ width: 400 }}> <FormControl size='sm'>
<Checkbox <Checkbox
defaultChecked={completedDate !== null} defaultChecked={completedDate !== null}
checked={completedDate !== null} checked={completedDate !== null}
@ -358,23 +380,9 @@ const ChoreView = () => {
/> />
)} )}
{/* {completedDate === null && (
// placeholder for the completion date with margin:
<Box
sx={{
height: 56,
}}
/>
)} */}
<Button <Button
fullWidth fullWidth
size='lg' size='lg'
sx={{
height: 50,
// mb: 2,
}}
onClick={handleTaskCompletion} onClick={handleTaskCompletion}
disabled={isPendingCompletion} disabled={isPendingCompletion}
color={isPendingCompletion ? 'danger' : 'success'} color={isPendingCompletion ? 'danger' : 'success'}
@ -382,49 +390,57 @@ const ChoreView = () => {
> >
<Box>Mark as done</Box> <Box>Mark as done</Box>
</Button> </Button>
{/* <Button <Button
sx={{ fullWidth
borderRadius: '32px', size='lg'
mt: 1, onClick={() => {
height: 50, setConfirmModelConfig({
zIndex: 1, isOpen: true,
}} title: 'Skip Task',
onClick={() => {
Navigate('/my/chores')
}}
color={isPendingCompletion ? 'danger' : 'success'}
startDecorator={isPendingCompletion ? <Close /> : <Check />}
fullWidth
>
<Box>Mark as {isPendingCompletion ? 'completed' : 'done'}</Box>
</Button> */}
</Card>
<Snackbar message: 'Are you sure you want to skip this task?',
open={isPendingCompletion}
endDecorator={ confirmText: 'Skip',
<Button cancelText: 'Cancel',
onClick={() => { onClose: confirmed => {
if (timeoutId) { if (confirmed) {
clearTimeout(timeoutId) handleSkippingTask()
setIsPendingCompletion(false) }
setTimeoutId(null) setConfirmModelConfig({})
setSecondsLeftToCancel(null) // Reset or adjust as needed },
} })
}} }}
size='lg' startDecorator={<SwitchAccessShortcut />}
variant='outlined' >
color='danger' <Box>Skip</Box>
startDecorator={<CancelScheduleSend />} </Button>
> <Snackbar
Cancel open={isPendingCompletion}
</Button> endDecorator={
} <Button
> onClick={() => {
<Typography level='body-md' textAlign={'center'}> if (timeoutId) {
Task will be marked as completed in {secondsLeftToCancel} seconds clearTimeout(timeoutId)
</Typography> setIsPendingCompletion(false)
</Snackbar> 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} />
</Card>
</Container> </Container>
) )
} }

View file

@ -38,7 +38,7 @@ import moment from 'moment'
import React, { useEffect } from 'react' import React, { useEffect } from 'react'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { API_URL } from '../../Config' import { API_URL } from '../../Config'
import { MarkChoreComplete } from '../../utils/Fetcher' import { MarkChoreComplete, SkipChore } from '../../utils/Fetcher'
import { Fetch } from '../../utils/TokenManager' import { Fetch } from '../../utils/TokenManager'
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal' import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
import DateModal from '../Modals/Inputs/DateModal' import DateModal from '../Modals/Inputs/DateModal'
@ -521,13 +521,7 @@ const ChoreCard = ({
</MenuItem> </MenuItem>
<MenuItem <MenuItem
onClick={() => { onClick={() => {
Fetch(`${API_URL}/chores/${chore.id}/skip`, { SkipChore(chore.id).then(response => {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
}).then(response => {
if (response.ok) { if (response.ok) {
response.json().then(data => { response.json().then(data => {
const newChore = data.res const newChore = data.res