Add Support for Priority

This commit is contained in:
Mo Tarbin 2024-11-03 22:37:21 -05:00
parent 240633177c
commit 6b0d2fe9cd
6 changed files with 233 additions and 100 deletions

View file

@ -88,7 +88,7 @@ const UpdateChoreAssignee = (id, assignee) => {
return Fetch(`${API_URL}/chores/${id}/assignee`, { return Fetch(`${API_URL}/chores/${id}/assignee`, {
method: 'PUT', method: 'PUT',
headers: HEADERS(), headers: HEADERS(),
body: JSON.stringify({ assignee:Number(assignee) }), body: JSON.stringify({ assignee: Number(assignee) }),
}) })
} }
@ -108,13 +108,20 @@ const DeleteChore = id => {
} }
const SaveChore = chore => { const SaveChore = chore => {
console.log('chore', chore)
return Fetch(`${API_URL}/chores/`, { return Fetch(`${API_URL}/chores/`, {
method: 'PUT', method: 'PUT',
headers: HEADERS(), headers: HEADERS(),
body: JSON.stringify(chore), body: JSON.stringify(chore),
}) })
} }
const UpdateChorePriority = (id, priority) => {
return Fetch(`${API_URL}/chores/${id}/priority `, {
method: 'PUT',
headers: HEADERS(),
body: JSON.stringify({ priority: priority }),
})
}
const GetChoreHistory = choreId => { const GetChoreHistory = choreId => {
return Fetch(`${API_URL}/chores/${choreId}/history`, { return Fetch(`${API_URL}/chores/${choreId}/history`, {
method: 'GET', method: 'GET',
@ -312,8 +319,9 @@ export {
SaveThing, SaveThing,
signUp, signUp,
SkipChore, SkipChore,
UpdateChoreAssignee,
UpdateChoreHistory, UpdateChoreHistory,
UpdateChorePriority,
UpdateThingState, UpdateThingState,
UpdateUserDetails, UpdateUserDetails,
UpdateChoreAssignee,
} }

31
src/utils/Priorities.jsx Normal file
View file

@ -0,0 +1,31 @@
import {
HorizontalRule,
KeyboardControlKey,
KeyboardDoubleArrowUp,
PriorityHigh,
} from '@mui/icons-material'
const Priorities = [
{
name: 'P4',
value: 4,
icon: <HorizontalRule />,
},
{
name: 'P3 ',
value: 3,
icon: <KeyboardControlKey />,
},
{
name: 'P2',
value: 2,
icon: <KeyboardDoubleArrowUp />,
},
{
name: 'P1',
value: 1,
icon: <PriorityHigh />,
},
]
export default Priorities

View file

@ -630,10 +630,11 @@ const ChoreEdit = () => {
description: 'before a task is due in few hours', description: 'before a task is due in few hours',
id: 'predue', id: 'predue',
}, },
{ // {
title: 'Overdue', // title: 'Overdue',
description: 'A notification when a task is overdue', // description: 'A notification when a task is overdue',
}, // id: 'overdue',
// },
{ {
title: 'Nagging', title: 'Nagging',
description: 'Daily reminders until the task is completed', description: 'Daily reminders until the task is completed',

View file

@ -5,6 +5,7 @@ import {
Checklist, Checklist,
Edit, Edit,
History, History,
LowPriority,
PeopleAlt, PeopleAlt,
Person, Person,
SwitchAccessShortcut, SwitchAccessShortcut,
@ -17,11 +18,15 @@ import {
Checkbox, Checkbox,
Chip, Chip,
Container, Container,
Dropdown,
FormControl, FormControl,
Grid, Grid,
Input, Input,
ListItem, ListItem,
ListItemContent, ListItemContent,
Menu,
MenuButton,
MenuItem,
Sheet, Sheet,
Snackbar, Snackbar,
styled, styled,
@ -36,7 +41,9 @@ import {
GetChoreDetailById, GetChoreDetailById,
MarkChoreComplete, MarkChoreComplete,
SkipChore, SkipChore,
UpdateChorePriority,
} from '../../utils/Fetcher' } from '../../utils/Fetcher'
import Priorities from '../../utils/Priorities'
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal' import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
const IconCard = styled('div')({ const IconCard = styled('div')({
display: 'flex', display: 'flex',
@ -48,6 +55,7 @@ const IconCard = styled('div')({
height: '40px', height: '40px',
marginRight: '16px', marginRight: '16px',
}) })
const ChoreView = () => { const ChoreView = () => {
const [chore, setChore] = useState({}) const [chore, setChore] = useState({})
const navigate = useNavigate() const navigate = useNavigate()
@ -63,13 +71,17 @@ const ChoreView = () => {
const [secondsLeftToCancel, setSecondsLeftToCancel] = 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({})
const [chorePriority, setChorePriority] = useState(null)
useEffect(() => { useEffect(() => {
Promise.all([ Promise.all([
GetChoreDetailById(choreId).then(resp => { GetChoreDetailById(choreId).then(resp => {
if (resp.ok) { if (resp.ok) {
return resp.json().then(data => { return resp.json().then(data => {
setChore(data.res) setChore(data.res)
setChorePriority(
Priorities.find(p => p.value === data.res.priority),
)
document.title = 'Donetick: ' + data.res.name
}) })
} }
}), }),
@ -89,7 +101,14 @@ const ChoreView = () => {
generateInfoCards(chore) generateInfoCards(chore)
} }
}, [chore, performers]) }, [chore, performers])
const handleUpdatePriority = priority => {
if (priority.value === 0) {
setChorePriority(null)
} else {
setChorePriority(priority)
}
UpdateChorePriority(choreId, priority.value)
}
const generateInfoCards = chore => { const generateInfoCards = chore => {
const cards = [ const cards = [
{ {
@ -217,6 +236,8 @@ const ChoreView = () => {
flexDirection: 'column', flexDirection: 'column',
// space between : // space between :
justifyContent: 'space-between', justifyContent: 'space-between',
// max height of the container:
maxHeight: 'calc(100vh - 500px)',
}} }}
> >
<Box <Box
@ -238,17 +259,13 @@ const ChoreView = () => {
> >
{chore.name} {chore.name}
</Typography> </Typography>
<Chip startDecorator={<CalendarMonth />} size='lg' sx={{ mb: 4 }}> <Chip startDecorator={<CalendarMonth />} size='md' sx={{ mb: 1 }}>
{chore.nextDueDate {chore.nextDueDate
? `Due at ${moment(chore.nextDueDate).format('MM/DD/YYYY hh:mm A')}` ? `Due at ${moment(chore.nextDueDate).format('MM/DD/YYYY hh:mm A')}`
: 'N/A'} : 'N/A'}
</Chip> </Chip>
</Box> </Box>
<Box> <Box>
<Typography level='title-md' sx={{ mb: 0.5 }}>
Details
</Typography>
<Sheet <Sheet
sx={{ sx={{
mb: 1, mb: 1,
@ -280,6 +297,101 @@ const ChoreView = () => {
))} ))}
</Grid> </Grid>
</Sheet> </Sheet>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
gap: 1,
alignContent: 'center',
justifyContent: 'center',
mb: 1,
}}
>
<Dropdown>
<MenuButton
color={
chorePriority?.name === 'P1'
? 'danger'
: chorePriority?.name === 'P2'
? 'warning'
: 'neutral'
}
sx={{
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
p: 1,
}}
fullWidth
>
{chorePriority ? chorePriority.icon : <LowPriority />}
{chorePriority ? chorePriority.name : 'No Priority'}
</MenuButton>
<Menu>
{Priorities.map((priority, index) => (
<MenuItem
key={index}
onClick={() => {
handleUpdatePriority(priority)
}}
>
{priority.name}
</MenuItem>
))}
<Divider />
<MenuItem
onClick={() => {
handleUpdatePriority({
name: 'No Priority',
value: 0,
})
setChorePriority(null)
}}
>
No Priority
</MenuItem>
</Menu>
</Dropdown>
<Button
size='sm'
color='neutral'
variant='outlined'
fullWidth
onClick={() => {
navigate(`/chores/${choreId}/history`)
}}
sx={{
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
p: 1,
}}
>
<History />
History
</Button>
<Button
size='sm'
color='neutral'
variant='outlined'
fullWidth
sx={{
// top right of the card:
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
p: 1,
}}
onClick={() => {
navigate(`/chores/${choreId}/edit`)
}}
>
<Edit />
Edit
</Button>
</Box>
{chore.notes && ( {chore.notes && (
<> <>
<Typography level='title-md' sx={{ mb: 1 }}> <Typography level='title-md' sx={{ mb: 1 }}>
@ -298,9 +410,7 @@ const ChoreView = () => {
my: 2, my: 2,
}} }}
/> */} /> */}
<Typography level='title-md' sx={{ mt: 1 }}>
Actions
</Typography>
<Card <Card
sx={{ sx={{
p: 2, p: 2,
@ -412,7 +522,8 @@ const ChoreView = () => {
}} }}
/> />
)} )}
<Box
<Box
sx={{ sx={{
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'row',
@ -421,22 +532,20 @@ const ChoreView = () => {
justifyContent: 'center', justifyContent: 'center',
}} }}
> >
<Button <Button
fullWidth fullWidth
size='lg' size='lg'
onClick={handleTaskCompletion} onClick={handleTaskCompletion}
disabled={isPendingCompletion} disabled={isPendingCompletion}
color={isPendingCompletion ? 'danger' : 'success'} color={isPendingCompletion ? 'danger' : 'success'}
startDecorator={<Check />} startDecorator={<Check />}
sx={ sx={{
{
flex: 4, flex: 4,
} }}
} >
> <Box>Mark as done</Box>
<Box>Mark as done</Box> </Button>
</Button>
<Button <Button
fullWidth fullWidth
size='lg' size='lg'
@ -458,53 +567,14 @@ const ChoreView = () => {
}) })
}} }}
startDecorator={<SwitchAccessShortcut />} startDecorator={<SwitchAccessShortcut />}
sx={ sx={{
{ flex: 1,
flex: 1, }}
}
}
> >
<Box>Skip</Box> <Box>Skip</Box>
</Button> </Button>
</Box> </Box>
<Divider sx={{ my: 0.5 }}>More</Divider>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
gap: 1,
alignContent: 'center',
justifyContent: 'center',
}}
>
<Button
startDecorator={<History />}
size='lg'
color='primary'
variant='outlined'
fullWidth
onClick={() => {
navigate(`/chores/${choreId}/history`)
}}
>
History
</Button>
<Button
startDecorator={<Edit />}
size='lg'
color='primary'
variant='outlined'
fullWidth
onClick={() => {
navigate(`/chores/${choreId}/edit`)
}}
>
Edit
</Button>
</Box>
<Snackbar <Snackbar
open={isPendingCompletion} open={isPendingCompletion}
endDecorator={ endDecorator={

View file

@ -3,6 +3,8 @@ import {
Check, Check,
Delete, Delete,
Edit, Edit,
HorizontalRule,
KeyboardControlKey,
KeyboardDoubleArrowUp, KeyboardDoubleArrowUp,
LocalOffer, LocalOffer,
ManageSearch, ManageSearch,
@ -10,6 +12,7 @@ import {
MoreVert, MoreVert,
Nfc, Nfc,
NoteAdd, NoteAdd,
PriorityHigh,
RecordVoiceOver, RecordVoiceOver,
Repeat, Repeat,
Report, Report,
@ -38,6 +41,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 { UserContext } from '../../contexts/UserContext'
import { import {
MarkChoreComplete, MarkChoreComplete,
SkipChore, SkipChore,
@ -76,14 +80,7 @@ const ChoreCard = ({
const [isPendingCompletion, setIsPendingCompletion] = React.useState(false) const [isPendingCompletion, setIsPendingCompletion] = React.useState(false)
const [secondsLeftToCancel, setSecondsLeftToCancel] = React.useState(null) const [secondsLeftToCancel, setSecondsLeftToCancel] = React.useState(null)
const [timeoutId, setTimeoutId] = React.useState(null) const [timeoutId, setTimeoutId] = React.useState(null)
// useEffect(() => { const { userProfile } = React.useContext(UserContext)
// GetAllUsers()
// .then(response => response.json())
// .then(data => {
// setPerformers(data.res)
// })
// }, [])
useEffect(() => { useEffect(() => {
document.addEventListener('mousedown', handleMenuOutsideClick) document.addEventListener('mousedown', handleMenuOutsideClick)
return () => { return () => {
@ -282,7 +279,18 @@ const ChoreCard = ({
return <LocalOffer /> return <LocalOffer />
} }
} }
const getPriorityIcon = priority => {
switch (Number(priority)) {
case 1:
return <PriorityHigh />
case 2:
return <KeyboardDoubleArrowUp />
case 3:
return <KeyboardControlKey />
default:
return <HorizontalRule />
}
}
const getRecurrentChipText = chore => { const getRecurrentChipText = chore => {
const dayOfMonthSuffix = n => { const dayOfMonthSuffix = n => {
if (n >= 11 && n <= 13) { if (n >= 11 && n <= 13) {
@ -438,16 +446,37 @@ const ChoreCard = ({
</Avatar> </Avatar>
<Box display='flex' flexDirection='column'> <Box display='flex' flexDirection='column'>
<Typography level='title-md'>{getName(chore.name)}</Typography> <Typography level='title-md'>{getName(chore.name)}</Typography>
<Typography level='body-md' color='text.disabled'> {chore.assignedTo !== userProfile.id && (
Assigned to{' '} <Typography level='body-md' color='text.disabled'>
<Chip variant='outlined'> Assigned to{' '}
{ <Chip variant='outlined'>
performers.find(p => p.id === chore.assignedTo) {
?.displayName performers.find(p => p.id === chore.assignedTo)
} ?.displayName
</Chip> }
</Typography> </Chip>
</Typography>
)}
<Box> <Box>
{chore.priority > 0 && (
<Chip
sx={{
position: 'relative',
mr: 0.5,
top: 2,
zIndex: 1,
}}
color={
chore.priority === 1
? 'danger'
: chore.priority === 2
? 'warning'
: 'neutral'
}
>
P{chore.priority}
</Chip>
)}
{chore.labels?.split(',').map((label, index) => ( {chore.labels?.split(',').map((label, index) => (
<Chip <Chip
variant='solid' variant='solid'

View file

@ -66,12 +66,6 @@ const MyChores = () => {
return aDueDate - bDueDate // Sort ascending by due date return aDueDate - bDueDate // Sort ascending by due date
} }
const handleSelectedFilter = selected => {
setFilteredChores(FILTERS[selected](chores))
setSelectedFilter(selected)
}
useEffect(() => { useEffect(() => {
if (userProfile === null) { if (userProfile === null) {
GetUserProfile() GetUserProfile()