From eb8c1786032ad4385fdfba74dddde8f6156b1e5b Mon Sep 17 00:00:00 2001 From: Mo Tarbin Date: Sat, 4 Jul 2026 12:00:12 -0400 Subject: [PATCH] Add ability to move project from choreactionMenu fix header not syncing on mychoreHeader --- src/views/Chores/components/MyChoreHeader.jsx | 55 ++- src/views/Chores/hooks/useChoreActions.js | 23 + src/views/components/ChoreActionMenu.jsx | 467 +++++++++++------- 3 files changed, 336 insertions(+), 209 deletions(-) diff --git a/src/views/Chores/components/MyChoreHeader.jsx b/src/views/Chores/components/MyChoreHeader.jsx index ab9ae4a..feb40f6 100644 --- a/src/views/Chores/components/MyChoreHeader.jsx +++ b/src/views/Chores/components/MyChoreHeader.jsx @@ -9,12 +9,10 @@ const MyChoreHeader = ({ tempFilter, tempFilterMeta, }) => { - if ( - !activeFilterId && - !tempFilter && - (!selectedProject || selectedProject.id === 'default') - ) - return null + const isVisible = + !!activeFilterId || + !!tempFilter || + (!!selectedProject && selectedProject.id !== 'default') const renderIcon = () => { if (tempFilter) { @@ -53,18 +51,41 @@ const MyChoreHeader = ({ : activeFilter?.description || selectedProject?.description return ( - - {renderIcon()} - - - {name} - - {description && ( - - {description} + + + {renderIcon()} + + + {name} - )} - + + + {description} + + + + ) } diff --git a/src/views/Chores/hooks/useChoreActions.js b/src/views/Chores/hooks/useChoreActions.js index 6bf279e..b66cb96 100644 --- a/src/views/Chores/hooks/useChoreActions.js +++ b/src/views/Chores/hooks/useChoreActions.js @@ -12,6 +12,7 @@ import { MarkChoreComplete, NudgeChore, RejectChore, + SaveChore, SkipChore, UndoChoreAction, UpdateChoreAssignee, @@ -664,6 +665,28 @@ export const useChoreActions = ({ } break + case 'moveToProject': { + const project = extraData?.project + const projectId = project?.id === null ? null : project?.id + const updatedChore = { ...chore, projectId } + try { + const response = await SaveChore(updatedChore) + if (response.ok) { + updateChoreInState(updatedChore, 'moved-to-project') + showSuccess({ + title: 'Task Moved', + message: `Task moved to ${project?.name || 'Default Project'}.`, + }) + } + } catch (error) { + showError({ + title: 'Failed to move task', + message: error?.message || 'Unable to move task to project', + }) + } + break + } + case 'completeWithNote': case 'completeWithPastDate': case 'changeAssignee': diff --git a/src/views/components/ChoreActionMenu.jsx b/src/views/components/ChoreActionMenu.jsx index 57c58e8..88d8c42 100644 --- a/src/views/components/ChoreActionMenu.jsx +++ b/src/views/components/ChoreActionMenu.jsx @@ -1,8 +1,10 @@ import { Archive, + ArrowBack, Cancel, CopyAll, Delete, + DriveFileMove, Edit, ManageSearch, MoreTime, @@ -20,10 +22,25 @@ import { WbSunny, Weekend, } from '@mui/icons-material' -import { Divider, IconButton, Menu, MenuItem, Tooltip } from '@mui/joy' +import { + Avatar, + Divider, + IconButton, + ListItemContent, + ListItemDecorator, + Menu, + MenuItem, + Tooltip, + Typography, +} from '@mui/joy' import React, { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' +import LABEL_COLORS, { + getTextColorFromBackgroundColor, +} from '../../utils/Colors' import { isOfficialDonetickInstanceSync } from '../../utils/FeatureToggle' +import { getIconComponent } from '../../utils/ProjectIcons' +import { useProjects } from '../Projects/ProjectQueries' const ChoreActionMenu = ({ chore, @@ -43,10 +60,11 @@ const ChoreActionMenu = ({ }) => { const [anchorEl, setAnchorEl] = React.useState(null) const [isOfficialInstance, setIsOfficialInstance] = useState(false) + const [showProjectPicker, setShowProjectPicker] = useState(false) const menuRef = React.useRef(null) const navigate = useNavigate() + const { data: projects = [] } = useProjects() - // Check if this is the official donetick.com instance useEffect(() => { try { setIsOfficialInstance(isOfficialDonetickInstanceSync()) @@ -83,6 +101,12 @@ const ChoreActionMenu = ({ const handleMenuClose = () => { setAnchorEl(null) + setShowProjectPicker(false) + } + + const handleMoveToProject = project => { + onAction?.('moveToProject', chore, { project }) + handleMenuClose() } const handleEdit = () => { @@ -134,7 +158,6 @@ const ChoreActionMenu = ({ switch (option) { case 'today': { - // Schedule for today at the next available slot: 9am, 12pm, 5pm, or now if after 5pm const nowHour = now.getHours() const scheduled = new Date(today) if (nowHour < 9) { @@ -144,7 +167,6 @@ const ChoreActionMenu = ({ } else if (nowHour < 17) { scheduled.setHours(17, 0, 0, 0) } else { - // After 5pm, use current time scheduled.setHours( now.getHours(), now.getMinutes(), @@ -163,7 +185,7 @@ const ChoreActionMenu = ({ case 'tomorrow': { const tomorrow = new Date(today) tomorrow.setDate(today.getDate() + 1) - tomorrow.setHours(12, 0, 0, 0) // Set to noon + tomorrow.setHours(12, 0, 0, 0) return tomorrow } case 'tomorrow-afternoon': { @@ -195,6 +217,18 @@ const ChoreActionMenu = ({ handleMenuClose() } + const renderProjectAvatar = (color, icon) => { + const bg = color || LABEL_COLORS[0].value + const IconComponent = getIconComponent(icon || 'FolderOpen') + return ( + + + + ) + } + return ( <> - { - e.stopPropagation() - onCompleteWithNote?.() - handleMenuClose() - }} - > - - Complete with note - - { - e.stopPropagation() - onCompleteWithPastDate?.() - handleMenuClose() - }} - > - - Complete in past - - { - e.stopPropagation() - handleSkip() - }} - > - - Skip to next due date - - { - e.stopPropagation() - onChangeAssignee?.() - handleMenuClose() - }} - > - - Delegate to someone else - - {isOfficialInstance && ( - { - e.stopPropagation() - onNudge?.() - handleMenuClose() - }} - > - - Send nudge - - )} - - { - e.stopPropagation() - handleHistory() - }} - > - - History - - - e.stopPropagation()} - > - - + { e.stopPropagation() - handleQuickSchedule('today') + setShowProjectPicker(false) }} + sx={{ gap: 1 }} > - - - - - + + Move to project + + + + { e.stopPropagation() - handleQuickSchedule('tomorrow') + handleMoveToProject({ id: null, name: 'Default Project' }) }} > - - - - {/* - + {renderProjectAvatar(LABEL_COLORS[0].value, 'FolderOpen')} + + + Default Project + + + {projects.map(project => ( + { + e.stopPropagation() + handleMoveToProject(project) + }} + > + + {renderProjectAvatar(project.color, project.icon)} + + + {project.name} + + + ))} + + ) : ( + <> + { e.stopPropagation() - handleQuickSchedule('tomorrow-afternoon') + onCompleteWithNote?.() + handleMenuClose() }} > - - - */} - - + Complete with note + + { e.stopPropagation() - handleQuickSchedule('weekend') + onCompleteWithPastDate?.() + handleMenuClose() }} > - - - - - + Complete in past + + { e.stopPropagation() - handleQuickSchedule('next-week') + handleSkip() }} > - - - - - + Skip to next due date + + { + e.stopPropagation() + onChangeAssignee?.() + handleMenuClose() + }} + > + + Delegate to someone else + + {isOfficialInstance && ( + { + e.stopPropagation() + onNudge?.() + handleMenuClose() + }} + > + + Send nudge + + )} + + { + e.stopPropagation() + handleHistory() + }} + > + + History + + + e.stopPropagation()} + > + + { + e.stopPropagation() + handleQuickSchedule('today') + }} + > + + + + + { + e.stopPropagation() + handleQuickSchedule('tomorrow') + }} + > + + + + + { + e.stopPropagation() + handleQuickSchedule('weekend') + }} + > + + + + + { + e.stopPropagation() + handleQuickSchedule('next-week') + }} + > + + + + + { + e.stopPropagation() + handleQuickSchedule('remove') + }} + > + + + + + + { + e.stopPropagation() + onChangeDueDate?.() + handleMenuClose() + }} + > + + Change due date + + { + e.stopPropagation() + onWriteNFC?.() + handleMenuClose() + }} + > + + Write to NFC + + { + e.stopPropagation() + handleEdit() + }} + > + + Edit + + { + e.stopPropagation() + handleClone() + }} + > + + Clone + + { + e.stopPropagation() + handleView() + }} + > + + View + + { + e.stopPropagation() + handleArchive() + }} color='neutral' + > + {chore.isActive ? : } + {chore.isActive ? 'Archive' : 'Unarchive'} + + {projects.length > 0 && ( + { + e.stopPropagation() + setShowProjectPicker(true) + }} + > + + Move to project + + )} + + { e.stopPropagation() - handleQuickSchedule('remove') + handleDelete() }} + color='danger' > - - - - - - { - e.stopPropagation() - onChangeDueDate?.() - handleMenuClose() - }} - > - - Change due date - - { - e.stopPropagation() - onWriteNFC?.() - handleMenuClose() - }} - > - - Write to NFC - - { - e.stopPropagation() - handleEdit() - }} - > - - Edit - - { - e.stopPropagation() - handleClone() - }} - > - - Clone - - { - e.stopPropagation() - handleView() - }} - > - - View - - { - e.stopPropagation() - handleArchive() - }} - color='neutral' - > - {chore.isActive ? : } - {chore.isActive ? 'Archive' : 'Unarchive'} - - - { - e.stopPropagation() - handleDelete() - }} - color='danger' - > - - Delete - + + Delete + + + )} )