diff --git a/src/views/components/DueDatePickerField.jsx b/src/views/components/DueDatePickerField.jsx
index 6410f8e..bb0685a 100644
--- a/src/views/components/DueDatePickerField.jsx
+++ b/src/views/components/DueDatePickerField.jsx
@@ -1,16 +1,31 @@
import {
+ Bedtime,
CalendarMonth,
Close,
+ EventNote,
+ LightMode,
NextWeek,
+ NightsStay,
Today,
WbSunny,
+ WbTwilight,
Weekend,
} from '@mui/icons-material'
-import { Box, Button, IconButton, Input, Sheet, Typography } from '@mui/joy'
-import { ClickAwayListener, Popper } from '@mui/material'
+import {
+ Box,
+ Button,
+ Checkbox,
+ IconButton,
+ Input,
+ List,
+ ListItem,
+ Typography,
+} from '@mui/joy'
import moment from 'moment'
-import { useEffect, useMemo, useRef, useState } from 'react'
-import { Z_INDEX } from '../../constants/zIndex'
+import { useEffect, useMemo, useState } from 'react'
+import Calendar from 'react-calendar'
+import { useLocalization } from '../../contexts/LocalizationContext'
+import { useResponsiveModal } from '../../hooks/useResponsiveModal'
const DueDatePickerField = ({
dueDateOnly,
@@ -24,7 +39,34 @@ const DueDatePickerField = ({
size = 'sm',
}) => {
const [isOpen, setIsOpen] = useState(false)
- const buttonRef = useRef(null)
+ 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()
@@ -50,6 +92,11 @@ const DueDatePickerField = ({
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
}
@@ -57,25 +104,43 @@ const DueDatePickerField = ({
const handleQuickSchedule = option => {
const date = getQuickScheduleDate(option)
- const dateStr = date.toISOString().split('T')[0]
- onDueDateChange?.({ target: { value: dateStr } })
- setIsOpen(false)
+ setLocalDueDateOnly(date.toISOString().split('T')[0])
}
- useEffect(() => {
- if (!isOpen) return
-
- const handleEscape = event => {
- if (event.key === 'Escape') {
- setIsOpen(false)
- }
+ const handleQuickTime = timeStr => {
+ // Tap the active chip again to deselect it
+ if (localUseCustomTime && localDueTime === timeStr) {
+ setLocalUseCustomTime(false)
+ setLocalDueTime(null)
+ return
}
-
- document.addEventListener('keydown', handleEscape)
- return () => {
- document.removeEventListener('keydown', handleEscape)
+ if (!localDueDateOnly) {
+ setLocalDueDateOnly(new Date().toISOString().split('T')[0])
}
- }, [isOpen])
+ 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'
@@ -94,210 +159,469 @@ const DueDatePickerField = ({
}, [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',
- },
- }}
- >
-
-
- )}
+
+
+ {dueDateLabel}
+
+
+ {hasDueDate && onClear && (
+ {
+ e.stopPropagation()
+ onClear?.()
+ }}
+ sx={{
+ position: 'absolute',
+ top: -12,
+ right: -16,
+ zIndex: 10,
+ maxHeight: 18,
+ maxWidth: 18,
+ borderRadius: '50%',
+ '&:hover': {
+ bgcolor: 'danger.softBg',
+ },
+ }}
+ >
+
+
+ )}
+
- {isOpen && (
-
- setIsOpen(false)}>
-
-
- Due Date
-
-
- }
- onClick={() => handleQuickSchedule('today')}
- >
- Today
-
- }
- onClick={() => handleQuickSchedule('tomorrow')}
- >
- Tomorrow
-
- }
- onClick={() => handleQuickSchedule('weekend')}
- >
- Weekend
-
- }
- onClick={() => handleQuickSchedule('next-week')}
- >
- Next week
-
-
-
-
- Due time (optional)
-
- {
- if (!useCustomTime) {
- onUseCustomTimeChange?.(true)
- }
- onDueTimeChange?.(e)
+ setIsOpen(false)}
+ title='Due Date'
+ footer={
+
+ {hasDueDate && (
+
-
-
- )}
-
+ sx={{ mr: 'auto' }}
+ >
+ Remove
+
+ )}
+ setIsOpen(false)}
+ >
+ Cancel
+
+
+ Apply
+
+
+ }
+ >
+
+ {/* 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
+
+
+
+ setLocalUseCustomTime(false)}
+ >
+ Anytime
+
+ setLocalUseCustomTime(true)}
+ >
+ Specific time
+
+
+
+
+ >
)
}