From b6ecdbec90de2c3edad4e8c88b0724592f0df83e Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 19 Dec 2025 17:37:04 +0200 Subject: [PATCH] Fix recur instance done (#727) * Fix recur instance done * Fix completed not showing --- .../routes/tasks/queries/metrics-queries.js | 54 ++++++++++++++++++- frontend/components/Task/GroupedTaskList.tsx | 13 +++-- frontend/components/Task/TaskHeader.tsx | 4 +- frontend/components/Task/TaskList.tsx | 2 +- .../components/Task/TaskStatusControl.tsx | 8 +-- frontend/components/Task/TasksToday.tsx | 20 +++++++ 6 files changed, 89 insertions(+), 12 deletions(-) diff --git a/backend/routes/tasks/queries/metrics-queries.js b/backend/routes/tasks/queries/metrics-queries.js index f204342..bce0ff9 100644 --- a/backend/routes/tasks/queries/metrics-queries.js +++ b/backend/routes/tasks/queries/metrics-queries.js @@ -333,7 +333,8 @@ async function fetchTasksCompletedToday(userId, userTimezone) { const safeTimezone = getSafeTimezone(userTimezone); const todayBounds = getTodayBoundsInUTC(safeTimezone); - return await Task.findAll({ + // Fetch regular completed tasks + const regularCompletedTasks = await Task.findAll({ where: { user_id: userId, status: Task.STATUS.DONE, @@ -345,8 +346,57 @@ async function fetchTasksCompletedToday(userId, userTimezone) { }, }, include: getTaskIncludeConfig(), - order: [['completed_at', 'DESC']], }); + + // Fetch recurring tasks completed today via recurring_completions table + const { RecurringCompletion } = require('../../../models'); + const recurringCompletions = await RecurringCompletion.findAll({ + where: { + completed_at: { + [Op.gte]: todayBounds.start, + [Op.lte]: todayBounds.end, + }, + skipped: false, + }, + include: [ + { + model: Task, + as: 'Task', + where: { + user_id: userId, + parent_task_id: null, + }, + include: getTaskIncludeConfig(), + }, + ], + }); + + // Extract the tasks from recurring completions and add completed_at and status + const recurringCompletedTasks = recurringCompletions.map((rc) => { + const task = rc.Task; + // Add virtual completed_at and status for display purposes + task.dataValues.completed_at = rc.completed_at; + task.dataValues.status = Task.STATUS.DONE; + // Also set the direct property to ensure it's accessible + task.status = Task.STATUS.DONE; + task.completed_at = rc.completed_at; + return task; + }); + + // Combine both lists + const allCompletedTasks = [ + ...regularCompletedTasks, + ...recurringCompletedTasks, + ]; + + // Sort by completed_at DESC + allCompletedTasks.sort((a, b) => { + const aTime = a.completed_at || a.dataValues.completed_at; + const bTime = b.completed_at || b.dataValues.completed_at; + return new Date(bTime) - new Date(aTime); + }); + + return allCompletedTasks; } module.exports = { diff --git a/frontend/components/Task/GroupedTaskList.tsx b/frontend/components/Task/GroupedTaskList.tsx index fc64986..b6fa56a 100644 --- a/frontend/components/Task/GroupedTaskList.tsx +++ b/frontend/components/Task/GroupedTaskList.tsx @@ -338,7 +338,10 @@ const GroupedTaskList: React.FC = ({ {/* Day column tasks */}
{dayTasks.map((task) => ( -
+
= ({ {projectTasks.map((task) => (
= ({ : standaloneTask.map((task) => (
= ({ > {/* Show template only if it's not virtual */} {!isVirtualTemplate && ( -
+
= ({ .map((instance) => (
= ({ > {/* Full view (md and larger) */}
-
+
= ({ filteredTasks.map((task) => (
= ({ const completionButtonLabel = statusDisplay.label; return ( -
+
= ({
{completionMenuOpen === 'desktop' && (
{renderStatusMenuOptions('desktop')}
@@ -497,7 +499,7 @@ const TaskStatusControl: React.FC = ({
{completionMenuOpen === 'mobile' && (
{renderStatusMenuOptions('mobile')}
diff --git a/frontend/components/Task/TasksToday.tsx b/frontend/components/Task/TasksToday.tsx index 86fb313..696de94 100644 --- a/frontend/components/Task/TasksToday.tsx +++ b/frontend/components/Task/TasksToday.tsx @@ -1024,6 +1024,26 @@ const TasksToday: React.FC = () => { // The updatedTask is already the result of the API call from TaskItem // Use the centralized task update handler to update UI optimistically await handleTaskUpdate(updatedTask); + + // Check if this was a recurring task completion that needs refresh + // Recurring tasks get advanced after completion, so they won't appear in completed list + // without a refetch + const isRecurringParent = + updatedTask.recurrence_type && + updatedTask.recurrence_type !== 'none' && + !updatedTask.recurring_parent_id; + + if (isRecurringParent) { + // Refetch tasks to get the updated completed list for recurring tasks + const result = await fetchTasks('?type=today'); + if (isMounted.current) { + setMetrics((prevMetrics) => ({ + ...prevMetrics, + tasks_completed_today: + result.tasks_completed_today || [], + })); + } + } } catch (error) { console.error('Error toggling task completion:', error); }