Add useWindowWidth hook and HistoryCard component

This commit is contained in:
Mo Tarbin 2024-07-09 17:39:16 -04:00
parent c4bf06b11c
commit a24134f852
4 changed files with 157 additions and 105 deletions

View file

@ -0,0 +1,19 @@
import { useEffect, useState } from 'react'
const useWindowWidth = () => {
const [windowWidth, setWindowWidth] = useState()
useEffect(() => {
const handleResize = () => {
setWindowWidth(window.innerWidth)
}
window.addEventListener('resize', handleResize)
// Cleanup function to remove the event listener
return () => window.removeEventListener('resize', handleResize)
}, [])
return windowWidth
}
export default useWindowWidth

View file

@ -45,7 +45,14 @@ import DateModal from '../Modals/Inputs/DateModal'
import SelectModal from '../Modals/Inputs/SelectModal' import SelectModal from '../Modals/Inputs/SelectModal'
import TextModal from '../Modals/Inputs/TextModal' import TextModal from '../Modals/Inputs/TextModal'
import WriteNFCModal from '../Modals/Inputs/WriteNFCModal' import WriteNFCModal from '../Modals/Inputs/WriteNFCModal'
const ChoreCard = ({ chore, performers, onChoreUpdate, onChoreRemove, sx }) => { const ChoreCard = ({
chore,
performers,
onChoreUpdate,
onChoreRemove,
sx,
viewOnly,
}) => {
const [activeUserId, setActiveUserId] = React.useState(0) const [activeUserId, setActiveUserId] = React.useState(0)
const [isChangeDueDateModalOpen, setIsChangeDueDateModalOpen] = const [isChangeDueDateModalOpen, setIsChangeDueDateModalOpen] =
React.useState(false) React.useState(false)
@ -367,6 +374,7 @@ const ChoreCard = ({ chore, performers, onChoreUpdate, onChoreRemove, sx }) => {
</Chip> </Chip>
<Card <Card
style={viewOnly ? { pointerEvents: 'none' } : {}}
variant='plain' variant='plain'
sx={{ sx={{
...sx, ...sx,

View file

@ -3,12 +3,10 @@ import {
Avatar, Avatar,
Box, Box,
Button, Button,
Chip,
CircularProgress, CircularProgress,
Container, Container,
Grid, Grid,
List, List,
ListDivider,
ListItem, ListItem,
ListItemContent, ListItemContent,
ListItemDecorator, ListItemDecorator,
@ -21,6 +19,7 @@ import { Link, useParams } from 'react-router-dom'
import { API_URL } from '../../Config' import { API_URL } from '../../Config'
import { GetAllCircleMembers } from '../../utils/Fetcher' import { GetAllCircleMembers } from '../../utils/Fetcher'
import { Fetch } from '../../utils/TokenManager' import { Fetch } from '../../utils/TokenManager'
import HistoryCard from './HistoryCard'
const ChoreHistory = () => { const ChoreHistory = () => {
const [choreHistory, setChoresHistory] = useState([]) const [choreHistory, setChoresHistory] = useState([])
@ -144,25 +143,6 @@ const ChoreHistory = () => {
setHistoryInfo(historyInfo) setHistoryInfo(historyInfo)
} }
function formatTimeDifference(startDate, endDate) {
const diffInMinutes = moment(startDate).diff(endDate, 'minutes')
let timeValue = diffInMinutes
let unit = 'minute'
if (diffInMinutes >= 60) {
const diffInHours = moment(startDate).diff(endDate, 'hours')
timeValue = diffInHours
unit = 'hour'
if (diffInHours >= 24) {
const diffInDays = moment(startDate).diff(endDate, 'days')
timeValue = diffInDays
unit = 'day'
}
}
return `${timeValue} ${unit}${timeValue !== 1 ? 's' : ''}`
}
if (isLoading) { if (isLoading) {
return <CircularProgress /> // Show loading indicator return <CircularProgress /> // Show loading indicator
} }
@ -251,89 +231,14 @@ const ChoreHistory = () => {
{/* Chore History List (Updated Style) */} {/* Chore History List (Updated Style) */}
<List sx={{ p: 0 }}> <List sx={{ p: 0 }}>
{choreHistory.map((chore, index) => ( {choreHistory.map((historyEntry, index) => (
<> <HistoryCard
<ListItem sx={{ gap: 1.5, alignItems: 'flex-start' }}> historyEntry={historyEntry}
{' '} performers={performers}
{/* Adjusted spacing and alignment */} allHistory={choreHistory}
<ListItemDecorator> key={index}
<Avatar sx={{ mr: 1 }}> index={index}
{performers />
.find(p => p.userId === chore.completedBy)
?.displayName?.charAt(0) || '?'}
</Avatar>
</ListItemDecorator>
<ListItemContent sx={{ my: 0 }}>
{' '}
{/* Removed vertical margin */}
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<Typography level='body1' sx={{ fontWeight: 'md' }}>
{moment(chore.completedAt).format('ddd MM/DD/yyyy HH:mm')}
</Typography>
<Chip>
{chore.dueDate && chore.completedAt > chore.dueDate
? 'Late'
: 'On Time'}
</Chip>
</Box>
<Typography level='body2' color='text.tertiary'>
<Chip>
{
performers.find(p => p.userId === chore.completedBy)
?.displayName
}
</Chip>{' '}
completed
{chore.completedBy !== chore.assignedTo && (
<>
{', '}
assigned to{' '}
<Chip>
{
performers.find(p => p.userId === chore.assignedTo)
?.displayName
}
</Chip>
</>
)}
</Typography>
{chore.dueDate && (
<Typography level='body2' color='text.tertiary'>
Due: {moment(chore.dueDate).format('ddd MM/DD/yyyy')}
</Typography>
)}
{chore.notes && (
<Typography level='body2' color='text.tertiary'>
Note: {chore.notes}
</Typography>
)}
</ListItemContent>
</ListItem>
{index < choreHistory.length - 1 && (
<>
<ListDivider component='li'>
{/* time between two completion: */}
{index < choreHistory.length - 1 &&
choreHistory[index + 1].completedAt && (
<Typography level='body3' color='text.tertiary'>
{formatTimeDifference(
chore.completedAt,
choreHistory[index + 1].completedAt,
)}{' '}
before
</Typography>
)}
</ListDivider>
</>
)}
</>
))} ))}
</List> </List>
</Box> </Box>

View file

@ -0,0 +1,120 @@
import {
Avatar,
Box,
Chip,
ListDivider,
ListItem,
ListItemContent,
ListItemDecorator,
Typography,
} from '@mui/joy'
import moment from 'moment'
const HistoryCard = ({ allHistory, performers, historyEntry, index }) => {
function formatTimeDifference(startDate, endDate) {
const diffInMinutes = moment(startDate).diff(endDate, 'minutes')
let timeValue = diffInMinutes
let unit = 'minute'
if (diffInMinutes >= 60) {
const diffInHours = moment(startDate).diff(endDate, 'hours')
timeValue = diffInHours
unit = 'hour'
if (diffInHours >= 24) {
const diffInDays = moment(startDate).diff(endDate, 'days')
timeValue = diffInDays
unit = 'day'
}
}
return `${timeValue} ${unit}${timeValue !== 1 ? 's' : ''}`
}
return (
<>
<ListItem sx={{ gap: 1.5, alignItems: 'flex-start' }}>
{' '}
{/* Adjusted spacing and alignment */}
<ListItemDecorator>
<Avatar sx={{ mr: 1 }}>
{performers
.find(p => p.userId === historyEntry.completedBy)
?.displayName?.charAt(0) || '?'}
</Avatar>
</ListItemDecorator>
<ListItemContent sx={{ my: 0 }}>
{' '}
{/* Removed vertical margin */}
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<Typography level='body1' sx={{ fontWeight: 'md' }}>
{moment(historyEntry.completedAt).format('ddd MM/DD/yyyy HH:mm')}
</Typography>
<Chip>
{historyEntry.dueDate &&
historyEntry.completedAt > historyEntry.dueDate
? 'Late'
: 'On Time'}
</Chip>
</Box>
<Typography level='body2' color='text.tertiary'>
<Chip>
{
performers.find(p => p.userId === historyEntry.completedBy)
?.displayName
}
</Chip>{' '}
completed
{historyEntry.completedBy !== historyEntry.assignedTo && (
<>
{', '}
assigned to{' '}
<Chip>
{
performers.find(p => p.userId === historyEntry.assignedTo)
?.displayName
}
</Chip>
</>
)}
</Typography>
{historyEntry.dueDate && (
<Typography level='body2' color='text.tertiary'>
Due: {moment(historyEntry.dueDate).format('ddd MM/DD/yyyy')}
</Typography>
)}
{historyEntry.notes && (
<Typography level='body2' color='text.tertiary'>
Note: {historyEntry.notes}
</Typography>
)}
</ListItemContent>
</ListItem>
{index < allHistory.length - 1 && (
<>
<ListDivider component='li'>
{/* time between two completion: */}
{index < allHistory.length - 1 &&
allHistory[index + 1].completedAt && (
<Typography level='body3' color='text.tertiary'>
{formatTimeDifference(
historyEntry.completedAt,
allHistory[index + 1].completedAt,
)}{' '}
before
</Typography>
)}
</ListDivider>
</>
)}
</>
)
}
export default HistoryCard