Fix test issues

This commit is contained in:
Chris Veleris 2025-08-18 16:33:27 +03:00 committed by Chris
parent dbe724f1a7
commit a4e2a97cb1
2 changed files with 107 additions and 43 deletions

View file

@ -1683,7 +1683,77 @@ router.patch('/task/:id', async (req, res) => {
taskAttributes.parent_task_id = null;
}
// Check if any recurrence settings are changing and cleanup future instances if needed
const recurrenceFields = [
'recurrence_type',
'recurrence_interval',
'recurrence_end_date',
'recurrence_weekday',
'recurrence_month_day',
'recurrence_week_of_month',
'completion_based',
];
const recurrenceChanged = recurrenceFields.some((field) => {
const newValue = req.body[field];
return newValue !== undefined && newValue !== task[field];
});
// Only cleanup if recurrence changed AND the old task was recurring (not 'none')
// This prevents cleanup when changing TO 'none' from 'none'
if (recurrenceChanged && task.recurrence_type !== 'none') {
// Find child instances of this recurring task
const childTasks = await Task.findAll({
where: { recurring_parent_id: task.id },
});
if (childTasks.length > 0) {
const now = new Date();
// Separate future and past instances
const futureInstances = childTasks.filter((child) => {
if (!child.due_date) return true; // Tasks without due_date are considered future (not yet scheduled)
return new Date(child.due_date) > now;
});
// Only cleanup future instances if not changing to 'none'
const newRecurrenceType =
recurrence_type !== undefined
? recurrence_type
: task.recurrence_type;
if (newRecurrenceType !== 'none') {
// Delete future instances since recurrence changed
for (const futureInstance of futureInstances) {
await futureInstance.destroy();
}
}
// Past instances remain as orphaned instances (no changes needed)
// This allows users to keep their completed/in-progress work
}
}
await task.update(taskAttributes);
// Generate new recurring tasks after updating recurrence settings (if still recurring)
if (recurrenceChanged && task.recurrence_type !== 'none') {
const newRecurrenceType =
recurrence_type !== undefined
? recurrence_type
: task.recurrence_type;
if (newRecurrenceType !== 'none') {
try {
// Generate new recurring tasks for the updated pattern
await generateRecurringTasks(req.currentUser.id, 7);
} catch (error) {
console.error(
'Error generating new recurring tasks after update:',
error
);
// Don't fail the update if regeneration fails
}
}
}
await updateTaskTags(task, tagsData, req.currentUser.id);
// Handle subtasks updates
@ -2147,16 +2217,45 @@ router.delete('/task/:id', async (req, res) => {
return res.status(404).json({ error: 'Task not found.' });
}
// Check for child tasks - prevent deletion of parent tasks with children
// Check for child tasks and handle smart deletion for recurring tasks
const childTasks = await Task.findAll({
where: { recurring_parent_id: req.params.id },
});
// If this is a recurring parent task with children, prevent deletion
// If this is a recurring parent task with children, implement smart deletion
if (childTasks.length > 0) {
return res
.status(400)
.json({ error: 'There was a problem deleting the task.' });
const now = new Date();
// Separate past and future instances
const futureInstances = childTasks.filter((child) => {
if (!child.due_date) return true; // Tasks without due_date are considered future (not yet scheduled)
return new Date(child.due_date) > now;
});
const pastInstances = childTasks.filter((child) => {
if (!child.due_date) return false; // Tasks without due_date are considered future, not past
return new Date(child.due_date) <= now;
});
// Delete future instances
for (const futureInstance of futureInstances) {
await futureInstance.destroy();
}
// Orphan past instances (remove parent relationship)
for (const pastInstance of pastInstances) {
await pastInstance.update({
recurring_parent_id: null,
recurrence_type: 'none',
recurrence_interval: null,
recurrence_end_date: null,
last_generated_date: null,
recurrence_weekday: null,
recurrence_month_day: null,
recurrence_week_of_month: null,
completion_based: false,
});
}
}
const taskEvents = await TaskEvent.findAll({

View file

@ -1,4 +1,4 @@
import React, { useEffect, useState, useCallback } from 'react';
import React, { useEffect, useState } from 'react';
import { useParams, Link, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import {
@ -393,31 +393,6 @@ const TaskDetails: React.FC = () => {
}
};
const handleSubtaskClick = useCallback(
(e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
// Store the intent to open modal in sessionStorage (survives re-mounts)
const modalState = {
isOpen: true,
focusSubtasks: true,
taskId: uid,
timestamp: Date.now(),
};
sessionStorage.setItem(
'pendingModalState',
JSON.stringify(modalState)
);
// Set state immediately
setFocusSubtasks(true);
setIsTaskModalOpen(true);
},
[uid]
);
if (loading) {
return <LoadingScreen />;
}
@ -700,8 +675,7 @@ const TaskDetails: React.FC = () => {
className="group"
>
<div
onClick={handleSubtaskClick}
className={`rounded-lg shadow-sm bg-white dark:bg-gray-900 border-2 cursor-pointer transition-all duration-200 ${
className={`rounded-lg shadow-sm bg-white dark:bg-gray-900 border-2 transition-all duration-200 ${
subtask.status ===
'in_progress' ||
subtask.status === 1
@ -813,10 +787,7 @@ const TaskDetails: React.FC = () => {
))}
</div>
) : (
<div
onClick={handleSubtaskClick}
className="rounded-lg shadow-sm bg-white dark:bg-gray-900 border-2 border-gray-50 dark:border-gray-800 p-6 cursor-pointer hover:border-blue-200 dark:hover:border-blue-700 transition-colors duration-200"
>
<div className="rounded-lg shadow-sm bg-white dark:bg-gray-900 border-2 border-gray-50 dark:border-gray-800 p-6">
<div className="flex flex-col items-center justify-center py-8 text-gray-500 dark:text-gray-400">
<ListBulletIcon className="h-12 w-12 mb-3 opacity-50" />
<span className="text-sm text-center">
@ -825,12 +796,6 @@ const TaskDetails: React.FC = () => {
'No subtasks yet'
)}
</span>
<span className="text-xs text-center mt-2 text-blue-500 dark:text-blue-400">
{t(
'task.clickToAddSubtasks',
'Click to add subtasks'
)}
</span>
</div>
</div>
)}