From 90efb5be001393134aaef07fe11e9ad605692043 Mon Sep 17 00:00:00 2001 From: Mo Tarbin Date: Sat, 20 Jul 2024 03:40:41 -0400 Subject: Support for history Modification and deletion --- src/utils/Fetcher.jsx | 16 +++++ src/views/History/ChoreHistory.jsx | 57 ++++++++++++++- src/views/History/HistoryCard.jsx | 18 +++-- src/views/Modals/EditHistoryModal.jsx | 121 ++++++++++++++++++++++++++++++++ src/views/Settings/APITokenSettings.jsx | 2 +- 5 files changed, 206 insertions(+), 8 deletions(-) create mode 100644 src/views/Modals/EditHistoryModal.jsx (limited to 'src') 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 = () => { {choreHistory.map((historyEntry, index) => ( { + setIsEditModalOpen(true) + setEditHistory(historyEntry) + }} historyEntry={historyEntry} performers={performers} allHistory={choreHistory} @@ -224,6 +235,46 @@ const ChoreHistory = () => { ))} + { + 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} + /> ) } 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 = } else { text = 'No Due Date' - color = 'info' + color = 'neutral' icon = } @@ -77,7 +83,7 @@ const HistoryCard = ({ allHistory, performers, historyEntry, index }) => { return ( <> - + {' '} {/* Adjusted spacing and alignment */} @@ -98,7 +104,11 @@ const HistoryCard = ({ allHistory, performers, historyEntry, index }) => { }} > - {moment(historyEntry.completedAt).format('ddd MM/DD/yyyy HH:mm')} + {historyEntry.completedAt + ? moment(historyEntry.completedAt).format( + 'ddd MM/DD/yyyy HH:mm', + ) + : 'Skipped'} {getCompletedChip(historyEntry)} 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 ( + + + + Edit History + + Due Date + { + setDueDate(e.target.value) + }} + /> + Completed Date + { + setCompletedDate(e.target.value) + }} + /> + Note + { + if (e.target.value.trim() === '') { + setNotes(null) + return + } + setNotes(e.target.value) + }} + size='md' + sx={{ + mb: 1, + }} + /> + + {/* 3 button save , cancel and delete */} + + + + + + { + 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', + }} + /> + + + ) +} +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 (
- Long Live Token + Access Token Create token to use with the API to update things that trigger task or -- cgit