From 4019a7c2257075f9253538ad6cc2dd41170fc27c Mon Sep 17 00:00:00 2001 From: Mo Tarbin Date: Fri, 3 Jul 2026 01:58:08 -0400 Subject: [PATCH] feat: update AddTaskModal and SmartTaskTitleInput components for improved functionality and styling --- src/views/components/AddTaskModal.jsx | 217 +++---------------- src/views/components/SmartTaskTitleInput.css | 36 ++- src/views/components/SmartTaskTitleInput.jsx | 5 +- 3 files changed, 60 insertions(+), 198 deletions(-) diff --git a/src/views/components/AddTaskModal.jsx b/src/views/components/AddTaskModal.jsx index 1bb2e07..cffb741 100644 --- a/src/views/components/AddTaskModal.jsx +++ b/src/views/components/AddTaskModal.jsx @@ -44,20 +44,20 @@ const getDefaultNotification = () => { ] localStorage.setItem( - 'defaultNotification', + 'defaultNotificationTemplate', JSON.stringify(defaultNotification), ) return defaultNotification } -const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { +const TaskInput = ({ onChoreUpdate, isModalOpen, onClose }) => { const { ResponsiveModal } = useResponsiveModal() const isMobile = useMediaQuery(theme => theme.breakpoints.down('sm')) const pickerEmptyDisplay = isMobile ? 'icon' : 'icon-text' const { data: userLabels, isLoading: userLabelsLoading } = useLabels() const { data: circleMembers, isLoading: isCircleMembersLoading } = useCircleMembers() - const { data: projects = [], isLoading: isProjectsLoading } = useProjects() + const { isLoading: isProjectsLoading } = useProjects() const createChoreMutation = useCreateChore() const { data: userProfile } = useUserProfile() @@ -80,9 +80,8 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { const [taskTitle, setTaskTitle] = useState('') const [renderedParts, setRenderedParts] = useState([]) - const textareaRef = useRef(null) - const mainInputRef = useRef(null) const richTextEditorRef = useRef(null) + const latestRef = useRef({}) const [priority, setPriority] = useState(0) const [dueDate, setDueDate] = useState(null) const [description, setDescription] = useState(null) @@ -92,13 +91,11 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { const [notificationMetadata, setNotificationMetadata] = useState({ templates: getDefaultNotification(), }) - const [frequencyHumanReadable, setFrequencyHumanReadable] = useState(null) const [subTasks, setSubTasks] = useState(null) const [points, setPoints] = useState(-1) const [isAnyoneTask, setIsAnyoneTask] = useState(false) const [hasDescription, setHasDescription] = useState(false) const [hasSubTasks, setHasSubTasks] = useState(false) - const [hasDeadline, setHasDeadline] = useState(false) const [deadlineOffset, setDeadlineOffset] = useState(-1) const [dueDateOnly, setDueDateOnly] = useState(null) const [dueTime, setDueTime] = useState(null) @@ -134,12 +131,17 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { } }, [hasDescription]) - // set showKeyboardShortcuts true as soon as the user hold ctrl or cmd key: useEffect(() => { const handleKeyDown = event => { + const { + isModalOpen, + hasDescription, + dueDate, + createChore, + handleCloseModal, + } = latestRef.current const isHoldingCmd = event.ctrlKey || event.metaKey if (isHoldingCmd) { - // event.preventDefault() setShowKeyboardShortcuts(true) } if ( @@ -152,10 +154,8 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { setShowKeyboardShortcuts(false) } if (isHoldingCmd && event.key.toLowerCase() === 'j' && isModalOpen) { - // add subtask: setHasSubTasks(true) setShowKeyboardShortcuts(false) - // set focus on the first subtask input: } if ( isHoldingCmd && @@ -163,7 +163,6 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { isModalOpen && !dueDate ) { - // add due date: const tomorrow = moment().add(1, 'day') setDueDateOnly(tomorrow.format('YYYY-MM-DD')) setDueDate(tomorrow.endOf('day').format('YYYY-MM-DDTHH:mm:59')) @@ -171,7 +170,6 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { setDueTime(null) setShowKeyboardShortcuts(false) } - // Enter key to create task if ( event.key === 'Enter' && (event.ctrlKey || event.metaKey) && @@ -181,7 +179,6 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { createChore() return } - // Escape key to cancel/close modal if (event.key === 'Escape' && isModalOpen) { event.preventDefault() handleCloseModal() @@ -202,22 +199,6 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { } }, []) - useEffect(() => { - if (isModalOpen && textareaRef.current) { - textareaRef.current.focus() - textareaRef.current.selectionStart = textareaRef.current.value?.length - textareaRef.current.selectionEnd = textareaRef.current.value?.length - } - }, [isModalOpen]) - - useEffect(() => { - if (autoFocus > 0 && mainInputRef.current) { - mainInputRef.current.focus() - mainInputRef.current.selectionStart = mainInputRef.current.value?.length - mainInputRef.current.selectionEnd = mainInputRef.current.value?.length - } - }, [autoFocus]) - const renderHighlightedSentence = useCallback( ( sentence, @@ -405,7 +386,6 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { if (repeat.result) { setFrequency(repeat.result) - setFrequencyHumanReadable(repeat.name) } const syncDueDateStates = parsedDate => { @@ -569,7 +549,6 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { setTaskTitle('') setDueDate(null) setFrequency(null) - setFrequencyHumanReadable(null) setPriority(0) setPoints(-1) setIsAnyoneTask(false) @@ -580,7 +559,6 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { setLabelsV2([]) setAssignees([]) setProjectId(getInitialProject()) - setHasDeadline(false) setDeadlineOffset(-1) setDueDateOnly(null) setDueTime(null) @@ -644,8 +622,8 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { } } if (!frequency && dueDate) { - // use dueDate converted to UTC: - chore.nextDueDate = new Date(dueDate).toUTCString() + // Use RFC3339/ISO-8601 format expected by backend. + chore.nextDueDate = new Date(dueDate).toISOString() chore.notificationMetadata = notificationMetadata } @@ -672,6 +650,15 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { }) handleCloseModal(false) } + + latestRef.current = { + isModalOpen, + hasDescription, + dueDate, + createChore, + handleCloseModal, + } + if (isCircleMembersLoading || isProjectsLoading) { return <> } @@ -712,6 +699,7 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { size='lg' variant='solid' color='primary' + disabled={!taskTitle.trim()} onClick={createChore} > Create @@ -775,7 +763,7 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { { setTaskText(text) }} @@ -823,15 +811,18 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { { onChange={setLabelsV2} onClear={() => setLabelsV2([])} labels={userLabels || []} - // emptyDisplay='icon-text' /> - {/* setProjectId(getInitialProject())} there should be no option to unselect a project, so we don't need an onClear handler - projects={projects || []} - emptyDisplay={pickerEmptyDisplay} - /> */} { emptyDisplay={pickerEmptyDisplay} /> - {/* - Title: - setTaskTitle(e.target.value)} - sx={{ width: '100%', fontSize: '16px' }} - /> - */} {!hasDescription && ( @@ -951,20 +926,6 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { Subtasks )} - - {/* {!hasDeadline && dueDate && ( - - )} */} {hasDescription && ( @@ -990,128 +951,6 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => { /> )} - - {/* {projects.length >= 1 && ( - - Project - - - )} */} - - {/* - Assignees - - {assignees.length > 0 ? ( - assignees.map((assignee, index) => ( - - {assignee.displayName || assignee.username} - - )) - ) : ( - - {userProfile.displayName} - - )} - - */} - {/* {hasDeadline && dueDate && ( - - Deadline - - - after due date - - - )} */} - ) } diff --git a/src/views/components/SmartTaskTitleInput.css b/src/views/components/SmartTaskTitleInput.css index 34ebe4a..c2bb002 100644 --- a/src/views/components/SmartTaskTitleInput.css +++ b/src/views/components/SmartTaskTitleInput.css @@ -1,3 +1,18 @@ +:root, +[data-joy-color-scheme='light'] { + --highlight-date-color: #b45309; + --highlight-repeat-color: #15803d; + --highlight-label-color: #1d4ed8; + --highlight-priority-color: #be123c; +} + +[data-joy-color-scheme='dark'] { + --highlight-date-color: #fca5a5; + --highlight-repeat-color: #86efac; + --highlight-label-color: #93c5fd; + --highlight-priority-color: #f9a8d4; +} + .smart-task-display { position: absolute; width: 100%; @@ -11,27 +26,36 @@ white-space: pre-wrap; box-sizing: border-box; } + .smart-task-common { font-size: 1.2em; line-height: 1.2em; font-family: inherit; - caret-color: #f08080; + caret-color: var(--highlight-date-color); } .highlight-date { - color: #f08080; + color: var(--highlight-date-color); } .highlight-repeat { - color: #90ee90; + color: var(--highlight-repeat-color); } .highlight-label { - color: #add8e6; + color: var(--highlight-label-color); } .highlight-priority { - color: #ffb6c1; + color: var(--highlight-priority-color); +} + +.highlight-assignee { + color: var(--highlight-repeat-color); +} + +.highlight-points { + color: var(--highlight-label-color); } .task-input { @@ -39,4 +63,6 @@ width: 100%; border-radius: 8px; box-sizing: border-box; + border: 1px solid var(--joy-palette-neutral-outlinedBorder, #d0d5dd); + overflow: auto; } diff --git a/src/views/components/SmartTaskTitleInput.jsx b/src/views/components/SmartTaskTitleInput.jsx index 8432c44..afc873e 100644 --- a/src/views/components/SmartTaskTitleInput.jsx +++ b/src/views/components/SmartTaskTitleInput.jsx @@ -183,10 +183,7 @@ const SmartTaskTitleInput = ({ return (
-
+