Add useWindowWidth hook and HistoryCard component
This commit is contained in:
parent
c4bf06b11c
commit
a24134f852
4 changed files with 157 additions and 105 deletions
19
src/hooks/useWindowWidth.js
Normal file
19
src/hooks/useWindowWidth.js
Normal 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
|
|
@ -45,7 +45,14 @@ import DateModal from '../Modals/Inputs/DateModal'
|
|||
import SelectModal from '../Modals/Inputs/SelectModal'
|
||||
import TextModal from '../Modals/Inputs/TextModal'
|
||||
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 [isChangeDueDateModalOpen, setIsChangeDueDateModalOpen] =
|
||||
React.useState(false)
|
||||
|
@ -367,6 +374,7 @@ const ChoreCard = ({ chore, performers, onChoreUpdate, onChoreRemove, sx }) => {
|
|||
</Chip>
|
||||
|
||||
<Card
|
||||
style={viewOnly ? { pointerEvents: 'none' } : {}}
|
||||
variant='plain'
|
||||
sx={{
|
||||
...sx,
|
||||
|
|
|
@ -3,12 +3,10 @@ import {
|
|||
Avatar,
|
||||
Box,
|
||||
Button,
|
||||
Chip,
|
||||
CircularProgress,
|
||||
Container,
|
||||
Grid,
|
||||
List,
|
||||
ListDivider,
|
||||
ListItem,
|
||||
ListItemContent,
|
||||
ListItemDecorator,
|
||||
|
@ -21,6 +19,7 @@ import { Link, useParams } from 'react-router-dom'
|
|||
import { API_URL } from '../../Config'
|
||||
import { GetAllCircleMembers } from '../../utils/Fetcher'
|
||||
import { Fetch } from '../../utils/TokenManager'
|
||||
import HistoryCard from './HistoryCard'
|
||||
|
||||
const ChoreHistory = () => {
|
||||
const [choreHistory, setChoresHistory] = useState([])
|
||||
|
@ -144,25 +143,6 @@ const ChoreHistory = () => {
|
|||
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) {
|
||||
return <CircularProgress /> // Show loading indicator
|
||||
}
|
||||
|
@ -251,89 +231,14 @@ const ChoreHistory = () => {
|
|||
{/* Chore History List (Updated Style) */}
|
||||
|
||||
<List sx={{ p: 0 }}>
|
||||
{choreHistory.map((chore, index) => (
|
||||
<>
|
||||
<ListItem sx={{ gap: 1.5, alignItems: 'flex-start' }}>
|
||||
{' '}
|
||||
{/* Adjusted spacing and alignment */}
|
||||
<ListItemDecorator>
|
||||
<Avatar sx={{ mr: 1 }}>
|
||||
{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>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
{choreHistory.map((historyEntry, index) => (
|
||||
<HistoryCard
|
||||
historyEntry={historyEntry}
|
||||
performers={performers}
|
||||
allHistory={choreHistory}
|
||||
key={index}
|
||||
index={index}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
|
|
120
src/views/History/HistoryCard.jsx
Normal file
120
src/views/History/HistoryCard.jsx
Normal 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
|
Loading…
Add table
Reference in a new issue