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 (
<>
-
-
-
-
- {isOfficialInstance && (
-
- )}
-
-
-
-
+
+ {
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
+
+ >
+ )}
>
)