Merge branch 'dev'

This commit is contained in:
Mo Tarbin 2024-07-20 03:41:23 -04:00
commit cb4fcc82f2
6 changed files with 208 additions and 10 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "fe-template", "name": "donetick",
"private": true, "private": true,
"version": "0.1.63", "version": "0.1.65",
"type": "module", "type": "module",
"lint-staged": { "lint-staged": {
"*.{js,jsx,ts,tsx}": [ "*.{js,jsx,ts,tsx}": [

View file

@ -112,6 +112,20 @@ const GetChoreHistory = choreId => {
headers: HEADERS(), 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 = () => { const GetAllCircleMembers = () => {
return Fetch(`${API_URL}/circles/members`, { return Fetch(`${API_URL}/circles/members`, {
@ -264,6 +278,7 @@ export {
CreateLongLiveToken, CreateLongLiveToken,
CreateThing, CreateThing,
DeleteChore, DeleteChore,
DeleteChoreHistory,
DeleteCircleMember, DeleteCircleMember,
DeleteLongLiveToken, DeleteLongLiveToken,
DeleteThing, DeleteThing,
@ -288,6 +303,7 @@ export {
SaveThing, SaveThing,
signUp, signUp,
SkipChore, SkipChore,
UpdateChoreHistory,
UpdateThingState, UpdateThingState,
UpdateUserDetails, UpdateUserDetails,
} }

View file

@ -15,9 +15,14 @@ import moment from 'moment'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { Link, useParams } from 'react-router-dom' import { Link, useParams } from 'react-router-dom'
import { API_URL } from '../../Config' import { API_URL } from '../../Config'
import { GetAllCircleMembers } from '../../utils/Fetcher' import {
DeleteChoreHistory,
GetAllCircleMembers,
UpdateChoreHistory,
} from '../../utils/Fetcher'
import { Fetch } from '../../utils/TokenManager' import { Fetch } from '../../utils/TokenManager'
import LoadingComponent from '../components/Loading' import LoadingComponent from '../components/Loading'
import EditHistoryModal from '../Modals/EditHistoryModal'
import HistoryCard from './HistoryCard' import HistoryCard from './HistoryCard'
const ChoreHistory = () => { const ChoreHistory = () => {
@ -28,6 +33,8 @@ const ChoreHistory = () => {
const [isLoading, setIsLoading] = useState(true) // Add loading state const [isLoading, setIsLoading] = useState(true) // Add loading state
const { choreId } = useParams() const { choreId } = useParams()
const [isEditModalOpen, setIsEditModalOpen] = useState(false)
const [editHistory, setEditHistory] = useState({})
useEffect(() => { useEffect(() => {
setIsLoading(true) // Start loading setIsLoading(true) // Start loading
@ -63,12 +70,12 @@ const ChoreHistory = () => {
const averageDelay = const averageDelay =
histories.reduce((acc, chore) => { histories.reduce((acc, chore) => {
if (chore.dueDate) { if (chore.dueDate && chore.completedAt) {
// Only consider chores with a due date // Only consider chores with a due date
return acc + moment(chore.completedAt).diff(chore.dueDate, 'hours') return acc + moment(chore.completedAt).diff(chore.dueDate, 'hours')
} }
return acc return acc
}, 0) / histories.length }, 0) / histories.filter(chore => chore.dueDate).length
const averageDelayMoment = moment.duration(averageDelay, 'hours') const averageDelayMoment = moment.duration(averageDelay, 'hours')
const maximumDelay = histories.reduce((acc, chore) => { const maximumDelay = histories.reduce((acc, chore) => {
if (chore.dueDate) { if (chore.dueDate) {
@ -215,6 +222,10 @@ const ChoreHistory = () => {
<List sx={{ p: 0 }}> <List sx={{ p: 0 }}>
{choreHistory.map((historyEntry, index) => ( {choreHistory.map((historyEntry, index) => (
<HistoryCard <HistoryCard
onClick={() => {
setIsEditModalOpen(true)
setEditHistory(historyEntry)
}}
historyEntry={historyEntry} historyEntry={historyEntry}
performers={performers} performers={performers}
allHistory={choreHistory} allHistory={choreHistory}
@ -224,6 +235,46 @@ const ChoreHistory = () => {
))} ))}
</List> </List>
</Sheet> </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> </Container>
) )
} }

View file

@ -11,7 +11,13 @@ import {
} from '@mui/joy' } from '@mui/joy'
import moment from 'moment' import moment from 'moment'
const HistoryCard = ({ allHistory, performers, historyEntry, index }) => { const HistoryCard = ({
allHistory,
performers,
historyEntry,
index,
onClick,
}) => {
function formatTimeDifference(startDate, endDate) { function formatTimeDifference(startDate, endDate) {
const diffInMinutes = moment(startDate).diff(endDate, 'minutes') const diffInMinutes = moment(startDate).diff(endDate, 'minutes')
let timeValue = diffInMinutes let timeValue = diffInMinutes
@ -64,7 +70,7 @@ const HistoryCard = ({ allHistory, performers, historyEntry, index }) => {
icon = <Timelapse /> icon = <Timelapse />
} else { } else {
text = 'No Due Date' text = 'No Due Date'
color = 'info' color = 'neutral'
icon = <CalendarViewDay /> icon = <CalendarViewDay />
} }
@ -77,7 +83,7 @@ const HistoryCard = ({ allHistory, performers, historyEntry, index }) => {
return ( return (
<> <>
<ListItem sx={{ gap: 1.5, alignItems: 'flex-start' }}> <ListItem sx={{ gap: 1.5, alignItems: 'flex-start' }} onClick={onClick}>
{' '} {' '}
{/* Adjusted spacing and alignment */} {/* Adjusted spacing and alignment */}
<ListItemDecorator> <ListItemDecorator>
@ -98,7 +104,11 @@ const HistoryCard = ({ allHistory, performers, historyEntry, index }) => {
}} }}
> >
<Typography level='body1' sx={{ fontWeight: 'md' }}> <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> </Typography>
{getCompletedChip(historyEntry)} {getCompletedChip(historyEntry)}
</Box> </Box>

View file

@ -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

View file

@ -38,7 +38,7 @@ const APITokenSettings = () => {
return ( return (
<div className='grid gap-4 py-4' id='apitokens'> <div className='grid gap-4 py-4' id='apitokens'>
<Typography level='h3'>Long Live Token</Typography> <Typography level='h3'>Access Token</Typography>
<Divider /> <Divider />
<Typography level='body-sm'> <Typography level='body-sm'>
Create token to use with the API to update things that trigger task or Create token to use with the API to update things that trigger task or