diff options
Diffstat (limited to '')
-rw-r--r-- | package.json | 4 | ||||
-rw-r--r-- | src/utils/Fetcher.jsx | 16 | ||||
-rw-r--r-- | src/views/History/ChoreHistory.jsx | 57 | ||||
-rw-r--r-- | src/views/History/HistoryCard.jsx | 18 | ||||
-rw-r--r-- | src/views/Modals/EditHistoryModal.jsx | 121 | ||||
-rw-r--r-- | src/views/Settings/APITokenSettings.jsx | 2 |
6 files changed, 208 insertions, 10 deletions
diff --git a/package.json b/package.json index 47f121d..815d24d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "fe-template", + "name": "donetick", "private": true, - "version": "0.1.63", + "version": "0.1.65", "type": "module", "lint-staged": { "*.{js,jsx,ts,tsx}": [ diff --git a/src/utils/Fetcher.jsx b/src/utils/Fetcher.jsx index 1161543..17b6cd9 100644 --- a/src/utils/Fetcher.jsx +++ b/src/utils/Fetcher.jsx @@ -112,6 +112,20 @@ const GetChoreHistory = choreId => { headers: HEADERS(), }) } +const DeleteChoreHistory = (choreId, id) => { + return Fetch(`${API_URL}/chores/${choreId}/history/${id}`, { + method: 'DELETE', + headers: HEADERS(), + }) +} + +const UpdateChoreHistory = (choreId, id, choreHistory) => { + return Fetch(`${API_URL}/chores/${choreId}/history/${id}`, { + method: 'PUT', + headers: HEADERS(), + body: JSON.stringify(choreHistory), + }) +} const GetAllCircleMembers = () => { return Fetch(`${API_URL}/circles/members`, { @@ -264,6 +278,7 @@ export { CreateLongLiveToken, CreateThing, DeleteChore, + DeleteChoreHistory, DeleteCircleMember, DeleteLongLiveToken, DeleteThing, @@ -288,6 +303,7 @@ export { SaveThing, signUp, SkipChore, + UpdateChoreHistory, UpdateThingState, UpdateUserDetails, } diff --git a/src/views/History/ChoreHistory.jsx b/src/views/History/ChoreHistory.jsx index 1db1f06..9419f59 100644 --- a/src/views/History/ChoreHistory.jsx +++ b/src/views/History/ChoreHistory.jsx @@ -15,9 +15,14 @@ import moment from 'moment' import React, { useEffect, useState } from 'react' import { Link, useParams } from 'react-router-dom' import { API_URL } from '../../Config' -import { GetAllCircleMembers } from '../../utils/Fetcher' +import { + DeleteChoreHistory, + GetAllCircleMembers, + UpdateChoreHistory, +} from '../../utils/Fetcher' import { Fetch } from '../../utils/TokenManager' import LoadingComponent from '../components/Loading' +import EditHistoryModal from '../Modals/EditHistoryModal' import HistoryCard from './HistoryCard' const ChoreHistory = () => { @@ -28,6 +33,8 @@ const ChoreHistory = () => { const [isLoading, setIsLoading] = useState(true) // Add loading state const { choreId } = useParams() + const [isEditModalOpen, setIsEditModalOpen] = useState(false) + const [editHistory, setEditHistory] = useState({}) useEffect(() => { setIsLoading(true) // Start loading @@ -63,12 +70,12 @@ const ChoreHistory = () => { const averageDelay = histories.reduce((acc, chore) => { - if (chore.dueDate) { + if (chore.dueDate && chore.completedAt) { // Only consider chores with a due date return acc + moment(chore.completedAt).diff(chore.dueDate, 'hours') } return acc - }, 0) / histories.length + }, 0) / histories.filter(chore => chore.dueDate).length const averageDelayMoment = moment.duration(averageDelay, 'hours') const maximumDelay = histories.reduce((acc, chore) => { if (chore.dueDate) { @@ -215,6 +222,10 @@ const ChoreHistory = () => { <List sx={{ p: 0 }}> {choreHistory.map((historyEntry, index) => ( <HistoryCard + onClick={() => { + setIsEditModalOpen(true) + setEditHistory(historyEntry) + }} historyEntry={historyEntry} performers={performers} allHistory={choreHistory} @@ -224,6 +235,46 @@ const ChoreHistory = () => { ))} </List> </Sheet> + <EditHistoryModal + config={{ + isOpen: isEditModalOpen, + onClose: () => { + setIsEditModalOpen(false) + }, + onSave: updated => { + UpdateChoreHistory(choreId, editHistory.id, { + completedAt: updated.completedAt, + dueDate: updated.dueDate, + notes: updated.notes, + }).then(res => { + if (!res.ok) { + console.error('Failed to update chore history:', res) + return + } + + const newRecord = res.json().then(data => { + const newRecord = data.res + const newHistory = choreHistory.map(record => + record.id === newRecord.id ? newRecord : record, + ) + setChoresHistory(newHistory) + setEditHistory(newRecord) + setIsEditModalOpen(false) + }) + }) + }, + onDelete: () => { + DeleteChoreHistory(choreId, editHistory.id).then(() => { + const newHistory = choreHistory.filter( + record => record.id !== editHistory.id, + ) + setChoresHistory(newHistory) + setIsEditModalOpen(false) + }) + }, + }} + historyRecord={editHistory} + /> </Container> ) } diff --git a/src/views/History/HistoryCard.jsx b/src/views/History/HistoryCard.jsx index 5e71f52..34a0b33 100644 --- a/src/views/History/HistoryCard.jsx +++ b/src/views/History/HistoryCard.jsx @@ -11,7 +11,13 @@ import { } from '@mui/joy' import moment from 'moment' -const HistoryCard = ({ allHistory, performers, historyEntry, index }) => { +const HistoryCard = ({ + allHistory, + performers, + historyEntry, + index, + onClick, +}) => { function formatTimeDifference(startDate, endDate) { const diffInMinutes = moment(startDate).diff(endDate, 'minutes') let timeValue = diffInMinutes @@ -64,7 +70,7 @@ const HistoryCard = ({ allHistory, performers, historyEntry, index }) => { icon = <Timelapse /> } else { text = 'No Due Date' - color = 'info' + color = 'neutral' icon = <CalendarViewDay /> } @@ -77,7 +83,7 @@ const HistoryCard = ({ allHistory, performers, historyEntry, index }) => { return ( <> - <ListItem sx={{ gap: 1.5, alignItems: 'flex-start' }}> + <ListItem sx={{ gap: 1.5, alignItems: 'flex-start' }} onClick={onClick}> {' '} {/* Adjusted spacing and alignment */} <ListItemDecorator> @@ -98,7 +104,11 @@ const HistoryCard = ({ allHistory, performers, historyEntry, index }) => { }} > <Typography level='body1' sx={{ fontWeight: 'md' }}> - {moment(historyEntry.completedAt).format('ddd MM/DD/yyyy HH:mm')} + {historyEntry.completedAt + ? moment(historyEntry.completedAt).format( + 'ddd MM/DD/yyyy HH:mm', + ) + : 'Skipped'} </Typography> {getCompletedChip(historyEntry)} </Box> diff --git a/src/views/Modals/EditHistoryModal.jsx b/src/views/Modals/EditHistoryModal.jsx new file mode 100644 index 0000000..1baab49 --- /dev/null +++ b/src/views/Modals/EditHistoryModal.jsx @@ -0,0 +1,121 @@ +import { + Box, + Button, + FormLabel, + Input, + Modal, + ModalDialog, + Typography, +} from '@mui/joy' +import moment from 'moment' +import { useEffect, useState } from 'react' +import ConfirmationModal from './Inputs/ConfirmationModal' + +function EditHistoryModal({ config, historyRecord }) { + useEffect(() => { + setCompletedDate( + moment(historyRecord.completedAt).format('YYYY-MM-DDTHH:mm'), + ) + setDueDate(moment(historyRecord.dueDate).format('YYYY-MM-DDTHH:mm')) + setNotes(historyRecord.notes) + }, [historyRecord]) + + const [completedDate, setCompletedDate] = useState( + moment(historyRecord.completedDate).format('YYYY-MM-DDTHH:mm'), + ) + const [dueDate, setDueDate] = useState( + moment(historyRecord.dueDate).format('YYYY-MM-DDTHH:mm'), + ) + const [notes, setNotes] = useState(historyRecord.notes) + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false) + return ( + <Modal open={config?.isOpen} onClose={config?.onClose}> + <ModalDialog> + <Typography level='h4' mb={1}> + Edit History + </Typography> + <FormLabel>Due Date</FormLabel> + <Input + type='datetime-local' + value={dueDate} + onChange={e => { + setDueDate(e.target.value) + }} + /> + <FormLabel>Completed Date</FormLabel> + <Input + type='datetime-local' + value={completedDate} + onChange={e => { + setCompletedDate(e.target.value) + }} + /> + <FormLabel>Note</FormLabel> + <Input + fullWidth + multiline + label='Additional Notes' + placeholder='Additional Notes' + value={notes} + onChange={e => { + if (e.target.value.trim() === '') { + setNotes(null) + return + } + setNotes(e.target.value) + }} + size='md' + sx={{ + mb: 1, + }} + /> + + {/* 3 button save , cancel and delete */} + <Box display={'flex'} justifyContent={'space-around'} mt={1}> + <Button + onClick={() => + config.onSave({ + id: historyRecord.id, + completedAt: moment(completedDate).toISOString(), + dueDate: moment(dueDate).toISOString(), + notes, + }) + } + fullWidth + sx={{ mr: 1 }} + > + Save + </Button> + <Button onClick={config.onClose} variant='outlined'> + Cancel + </Button> + <Button + onClick={() => { + setIsDeleteModalOpen(true) + }} + variant='outlined' + color='danger' + > + Delete + </Button> + </Box> + <ConfirmationModal + config={{ + isOpen: isDeleteModalOpen, + onClose: isConfirm => { + if (isConfirm) { + config.onDelete(historyRecord.id) + } + setIsDeleteModalOpen(false) + }, + title: 'Delete History', + message: 'Are you sure you want to delete this history?', + confirmText: 'Delete', + cancelText: 'Cancel', + }} + /> + </ModalDialog> + </Modal> + ) +} +export default EditHistoryModal diff --git a/src/views/Settings/APITokenSettings.jsx b/src/views/Settings/APITokenSettings.jsx index 5bd9887..6262906 100644 --- a/src/views/Settings/APITokenSettings.jsx +++ b/src/views/Settings/APITokenSettings.jsx @@ -38,7 +38,7 @@ const APITokenSettings = () => { return ( <div className='grid gap-4 py-4' id='apitokens'> - <Typography level='h3'>Long Live Token</Typography> + <Typography level='h3'>Access Token</Typography> <Divider /> <Typography level='body-sm'> Create token to use with the API to update things that trigger task or |