import {
Bedtime,
CalendarMonth,
Close,
EventNote,
LightMode,
NextWeek,
NightsStay,
Today,
WbSunny,
WbTwilight,
Weekend,
} from '@mui/icons-material'
import {
Box,
Button,
Checkbox,
IconButton,
Input,
List,
ListItem,
Typography,
} from '@mui/joy'
import moment from 'moment'
import { useEffect, useMemo, useState } from 'react'
import Calendar from 'react-calendar'
import { useLocalization } from '../../contexts/LocalizationContext'
import { useResponsiveModal } from '../../hooks/useResponsiveModal'
const DueDatePickerField = ({
dueDateOnly,
dueTime,
useCustomTime,
onDueDateChange,
onDueTimeChange,
onUseCustomTimeChange,
onClear,
emptyDisplay = 'icon-text',
size = 'sm',
}) => {
const [isOpen, setIsOpen] = useState(false)
const { ResponsiveModal } = useResponsiveModal()
const { firstDayOfWeek } = useLocalization()
// Local buffered state — only committed on Apply
const [localDueDateOnly, setLocalDueDateOnly] = useState(dueDateOnly)
const [localDueTime, setLocalDueTime] = useState(dueTime)
const [localUseCustomTime, setLocalUseCustomTime] = useState(useCustomTime)
// Sync local state from props whenever the modal opens
useEffect(() => {
if (isOpen) {
setLocalDueDateOnly(dueDateOnly)
setLocalDueTime(dueTime)
setLocalUseCustomTime(useCustomTime)
}
}, [isOpen, dueDateOnly, dueTime, useCustomTime])
const calendarType =
firstDayOfWeek === 1
? 'iso8601'
: firstDayOfWeek === 6
? 'islamic'
: 'gregory'
const pillListSx = {
'--List-gap': '8px',
'--ListItem-radius': '20px',
}
const getQuickScheduleDate = option => {
const now = new Date()
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
switch (option) {
case 'today':
return today
case 'tomorrow': {
const tomorrow = new Date(today)
tomorrow.setDate(today.getDate() + 1)
return tomorrow
}
case 'weekend': {
const weekend = new Date(today)
const daysUntilSaturday = (6 - today.getDay() + 7) % 7 || 7
weekend.setDate(today.getDate() + daysUntilSaturday)
return weekend
}
case 'next-week': {
const nextWeek = new Date(today)
const daysUntilMonday = (1 - today.getDay() + 7) % 7 || 7
nextWeek.setDate(today.getDate() + daysUntilMonday)
return nextWeek
}
case 'next-month': {
const nextMonth = new Date(today)
nextMonth.setMonth(today.getMonth() + 1)
return nextMonth
}
default:
return today
}
}
const handleQuickSchedule = option => {
const date = getQuickScheduleDate(option)
setLocalDueDateOnly(date.toISOString().split('T')[0])
}
const handleQuickTime = timeStr => {
// Tap the active chip again to deselect it
if (localUseCustomTime && localDueTime === timeStr) {
setLocalUseCustomTime(false)
setLocalDueTime(null)
return
}
if (!localDueDateOnly) {
setLocalDueDateOnly(new Date().toISOString().split('T')[0])
}
setLocalUseCustomTime(true)
setLocalDueTime(timeStr)
}
const handleCalendarChange = selected => {
if (!selected || Array.isArray(selected)) return
setLocalDueDateOnly(moment(selected).format('YYYY-MM-DD'))
}
const handleLocalTimeInputChange = e => {
setLocalUseCustomTime(true)
setLocalDueTime(e.target.value)
}
const handleSave = () => {
onDueDateChange?.({ target: { value: localDueDateOnly || '' } })
onUseCustomTimeChange?.(localUseCustomTime)
if (localUseCustomTime && localDueTime) {
onDueTimeChange?.({ target: { value: localDueTime } })
} else {
onDueTimeChange?.({ target: { value: '' } })
}
setIsOpen(false)
}
const hasDueDate = Boolean(dueDateOnly)
const shouldShowLabel = hasDueDate || emptyDisplay === 'icon-text'
const dueDateLabel = useMemo(() => {
if (!dueDateOnly) {
return 'Due'
}
const formattedDate = moment(dueDateOnly).format('MMM D')
if (useCustomTime && dueTime) {
return `${formattedDate}, ${dueTime}`
}
return formattedDate
}, [dueDateOnly, dueTime, useCustomTime])
return (
<>
{hasDueDate && onClear && (
{
e.stopPropagation()
onClear?.()
}}
sx={{
position: 'absolute',
top: -12,
right: -16,
zIndex: 10,
maxHeight: 18,
maxWidth: 18,
borderRadius: '50%',
'&:hover': {
bgcolor: 'danger.softBg',
},
}}
>
)}
setIsOpen(false)}
title='Due Date'
footer={
{hasDueDate && (
)}
}
>
{/* Date shortcuts */}
Quick date
{[
{
key: 'today',
label: 'Today',
icon: ,
},
{
key: 'tomorrow',
label: 'Tomorrow',
icon: ,
},
{
key: 'weekend',
label: 'Weekend',
icon: ,
},
{
key: 'next-week',
label: 'Next week',
icon: ,
},
{
key: 'next-month',
label: 'Next month',
icon: ,
},
].map(opt => {
const dateStr = getQuickScheduleDate(opt.key)
.toISOString()
.split('T')[0]
return (
handleQuickSchedule(opt.key)}
overlay
disableIcon
variant='soft'
label={
{opt.icon}
{opt.label}
}
/>
)
})}
{/* Time shortcuts */}
Quick time
{[
{
time: '09:00',
label: 'Morning',
icon: ,
},
{
time: '12:00',
label: 'Noon',
icon: ,
},
{
time: '15:00',
label: 'Afternoon',
icon: ,
},
{
time: '18:00',
label: 'Evening',
icon: ,
},
{
time: '22:00',
label: 'Night',
icon: ,
},
].map(opt => (
handleQuickTime(opt.time)}
overlay
disableIcon
variant='soft'
label={
{opt.icon}
{opt.label}
}
/>
))}
['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'][date.getDay()]
}
formatMonth={(locale, date) =>
[
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
][date.getMonth()]
}
/>
Custom time
>
)
}
export default DueDatePickerField