aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Mo Tarbin <mhed.t91@gmail.com>2024-07-20 03:40:41 -0400
committerLibravatar Mo Tarbin <mhed.t91@gmail.com>2024-07-20 03:40:41 -0400
commit90efb5be001393134aaef07fe11e9ad605692043 (patch)
tree9cbce35ea79248aaef510f93e8f731e9a0ef6468 /src
parentaed179d6b3aa9c294893b352a7e61f93076a2ad9 (diff)
downloaddonetick-frontend-90efb5be001393134aaef07fe11e9ad605692043.tar.gz
donetick-frontend-90efb5be001393134aaef07fe11e9ad605692043.tar.bz2
donetick-frontend-90efb5be001393134aaef07fe11e9ad605692043.zip
Support for history Modification and deletion
Diffstat (limited to 'src')
-rw-r--r--src/utils/Fetcher.jsx16
-rw-r--r--src/views/History/ChoreHistory.jsx57
-rw-r--r--src/views/History/HistoryCard.jsx18
-rw-r--r--src/views/Modals/EditHistoryModal.jsx121
-rw-r--r--src/views/Settings/APITokenSettings.jsx2
5 files changed, 206 insertions, 8 deletions
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