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