import { Check, Delete, Edit, HowToReg, KeyboardDoubleArrowUp, LocalOffer, ManageSearch, MoreTime, MoreVert, Nfc, NoteAdd, RecordVoiceOver, Repeat, Report, SwitchAccessShortcut, TimesOneMobiledata, Update, Webhook, } from '@mui/icons-material' import { Avatar, Box, Card, Chip, CircularProgress, Divider, Grid, IconButton, Menu, MenuItem, Typography, } from '@mui/joy' import moment from 'moment' import React, { useEffect } from 'react' import { useNavigate } from 'react-router-dom' import { API_URL } from '../../Config' import writeToNFC from '../../service/NFCWriter' import { Fetch } from '../../utils/TokenManager' import ConfirmationModal from '../Modals/Inputs/ConfirmationModal' import DateModal from '../Modals/Inputs/DateModal' import SelectModal from '../Modals/Inputs/SelectModal' import TextModal from '../Modals/Inputs/TextModal' const ChoreCard = ({ chore, performers, onChoreUpdate, onChoreRemove, sx }) => { const [activeUserId, setActiveUserId] = React.useState(0) const [isChangeDueDateModalOpen, setIsChangeDueDateModalOpen] = React.useState(false) const [isCompleteWithPastDateModalOpen, setIsCompleteWithPastDateModalOpen] = React.useState(false) const [isChangeAssigneeModalOpen, setIsChangeAssigneeModalOpen] = React.useState(false) const [isCompleteWithNoteModalOpen, setIsCompleteWithNoteModalOpen] = React.useState(false) const [confirmModelConfig, setConfirmModelConfig] = React.useState({}) const [anchorEl, setAnchorEl] = React.useState(null) const menuRef = React.useRef(null) const navigate = useNavigate() const [isDisabled, setIsDisabled] = React.useState(false) // useEffect(() => { // GetAllUsers() // .then(response => response.json()) // .then(data => { // setPerformers(data.res) // }) // }, []) useEffect(() => { document.addEventListener('mousedown', handleMenuOutsideClick) return () => { document.removeEventListener('mousedown', handleMenuOutsideClick) } }, [anchorEl]) const handleMenuOpen = event => { setAnchorEl(event.currentTarget) } const handleMenuClose = () => { setAnchorEl(null) } const handleMenuOutsideClick = event => { if ( anchorEl && !anchorEl.contains(event.target) && !menuRef.current.contains(event.target) ) { handleMenuClose() } } const handleEdit = () => { navigate(`/chores/${chore.id}/edit`) } const handleDelete = () => { setConfirmModelConfig({ isOpen: true, title: 'Delete Chore', confirmText: 'Delete', cancelText: 'Cancel', message: 'Are you sure you want to delete this chore?', onClose: isConfirmed => { console.log('isConfirmed', isConfirmed) if (isConfirmed === true) { Fetch(`${API_URL}/chores/${chore.id}`, { method: 'DELETE', }).then(response => { if (response.ok) { onChoreRemove(chore) } }) } setConfirmModelConfig({}) }, }) } const handleCompleteChore = () => { Fetch(`${API_URL}/chores/${chore.id}/do`, { method: 'POST', }).then(response => { if (response.ok) { response.json().then(data => { const newChore = data.res onChoreUpdate(newChore, 'completed') }) } }) setIsDisabled(true) setTimeout(() => setIsDisabled(false), 3000) // Re-enable the button after 5 seconds } const handleChangeDueDate = newDate => { if (activeUserId === null) { alert('Please select a performer') return } Fetch(`${API_URL}/chores/${chore.id}/dueDate`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ dueDate: newDate ? new Date(newDate).toISOString() : null, UpdatedBy: activeUserId, }), }).then(response => { if (response.ok) { response.json().then(data => { const newChore = data.res onChoreUpdate(newChore, 'rescheduled') }) } }) } const handleCompleteWithPastDate = newDate => { if (activeUserId === null) { alert('Please select a performer') return } Fetch( `${API_URL}/chores/${chore.id}/do?completedDate=${new Date( newDate, ).toISOString()}`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({}), }, ).then(response => { if (response.ok) { response.json().then(data => { const newChore = data.res onChoreUpdate(newChore, 'completed') }) } }) } const handleAssigneChange = assigneeId => { // TODO: Implement assignee change } const handleCompleteWithNote = note => { Fetch(`${API_URL}/chores/${chore.id}/do`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ note: note, }), }).then(response => { if (response.ok) { response.json().then(data => { const newChore = data.res onChoreUpdate(newChore, 'completed') }) } }) } const getDueDateChipText = nextDueDate => { if (chore.nextDueDate === null) return 'No Due Date' // if due in next 48 hours, we should it in this format : Tomorrow 11:00 AM const diff = moment(nextDueDate).diff(moment(), 'hours') if (diff < 48 && diff > 0) { return moment(nextDueDate).calendar().replace(' at', '') } return 'Due ' + moment(nextDueDate).fromNow() } const getDueDateChipColor = nextDueDate => { if (chore.nextDueDate === null) return 'neutral' const diff = moment(nextDueDate).diff(moment(), 'hours') if (diff < 48 && diff > 0) { return 'warning' } if (diff < 0) { return 'danger' } return 'neutral' } const getIconForLabel = label => { if (!label || label.trim() === '') return <> switch (String(label).toLowerCase()) { case 'high': return case 'important': return default: return } } const getRecurrentChipText = chore => { const dayOfMonthSuffix = n => { if (n >= 11 && n <= 13) { return 'th' } switch (n % 10) { case 1: return 'st' case 2: return 'nd' case 3: return 'rd' default: return 'th' } } if (chore.frequencyType === 'once') { return 'Once' } else if (chore.frequencyType === 'trigger') { return 'Trigger' } else if (chore.frequencyType === 'daily') { return 'Daily' } else if (chore.frequencyType === 'weekly') { return 'Weekly' } else if (chore.frequencyType === 'monthly') { return 'Monthly' } else if (chore.frequencyType === 'yearly') { return 'Yearly' } else if (chore.frequencyType === 'days_of_the_week') { let days = JSON.parse(chore.frequencyMetadata).days days = days.map(d => moment().day(d).format('ddd')) return days.join(', ') } else if (chore.frequencyType === 'day_of_the_month') { let freqData = JSON.parse(chore.frequencyMetadata) const months = freqData.months.map(m => moment().month(m).format('MMM')) return `${chore.frequency}${dayOfMonthSuffix( chore.frequency, )} of ${months.join(', ')}` } else if (chore.frequencyType === 'interval') { return `Every ${chore.frequency} ${ JSON.parse(chore.frequencyMetadata).unit }` } else { return chore.frequencyType } } const getFrequencyIcon = chore => { if (['once', 'no_repeat'].includes(chore.frequencyType)) { return } else if (chore.frequencyType === 'trigger') { return } else { return } } return ( <> {getDueDateChipText(chore.nextDueDate)}
{getFrequencyIcon(chore)} {getRecurrentChipText(chore)}
{/* Box in top right with Chip showing next due date */} {chore.name.charAt(0).toUpperCase()} {chore.name} Assigned to{' '} { performers.find(p => p.id === chore.assignedTo) ?.displayName } {chore.labels?.split(',').map(label => ( {label} ))} {/* {chore.nextDueDate === null ? '--' : 'Due ' + moment(chore.nextDueDate).fromNow()} */} {/* */}
{isDisabled && ( )}
{/*
*/} { setIsCompleteWithNoteModalOpen(true) }} > Complete with note { setIsCompleteWithPastDateModalOpen(true) }} > Complete in past { Fetch(`${API_URL}/chores/${chore.id}/skip`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({}), }).then(response => { if (response.ok) { response.json().then(data => { const newChore = data.res onChoreUpdate(newChore, 'skipped') handleMenuClose() }) } }) }} > Skip to next due date { setIsChangeAssigneeModalOpen(true) }} > Delegate to someone else Complete as someone else { navigate(`/chores/${chore.id}/history`) }} > History { setIsChangeDueDateModalOpen(true) }} > Change due date { // write current chore URL to NFC writeToNFC(`${window.location.origin}/chores/${chore.id}`) }} > Write to NFC Edit Delete
{ setIsChangeDueDateModalOpen(false) }} onSave={handleChangeDueDate} /> { setIsCompleteWithPastDateModalOpen(false) }} onSave={handleCompleteWithPastDate} /> { setIsChangeAssigneeModalOpen(false) }} onSave={handleAssigneChange} /> { setIsCompleteWithNoteModalOpen(false) }} okText={'Complete'} onSave={handleCompleteWithNote} />
) } export default ChoreCard