Fix today recurring missing (#548)
* Expose today task from a recurring series * fixup! Expose today task from a recurring series * fixup! fixup! Expose today task from a recurring series
This commit is contained in:
parent
b6748aa0d7
commit
b0041bafe1
12 changed files with 341 additions and 44 deletions
|
|
@ -6,6 +6,7 @@ const {
|
|||
getTaskTodayMoveCount,
|
||||
getTaskTodayMoveCounts,
|
||||
} = require('../../../services/taskEventService');
|
||||
const taskRepository = require('../../../repositories/TaskRepository');
|
||||
|
||||
async function serializeTask(
|
||||
task,
|
||||
|
|
@ -54,11 +55,23 @@ async function serializeTask(
|
|||
}
|
||||
}
|
||||
|
||||
let recurringParentUid = null;
|
||||
if (taskJson.recurring_parent_id) {
|
||||
const parentTask = await taskRepository.findById(
|
||||
taskJson.recurring_parent_id,
|
||||
{
|
||||
attributes: ['uid'],
|
||||
}
|
||||
);
|
||||
recurringParentUid = parentTask?.uid || null;
|
||||
}
|
||||
|
||||
return {
|
||||
...taskWithoutSubtasks,
|
||||
name: displayName,
|
||||
original_name: taskJson.name,
|
||||
uid: task.uid,
|
||||
recurring_parent_uid: recurringParentUid,
|
||||
due_date: processDueDateForResponse(taskJson.due_date, safeTimezone),
|
||||
tags: taskJson.Tags || [],
|
||||
Project: taskJson.Project
|
||||
|
|
|
|||
|
|
@ -19,7 +19,10 @@ const { logEvent } = require('../../services/taskEventService');
|
|||
const { serializeTask, serializeTasks } = require('./core/serializers');
|
||||
const { updateTaskTags } = require('./operations/tags');
|
||||
const { filterTasksByParams } = require('./queries/query-builders');
|
||||
const { getSafeTimezone } = require('../../utils/timezone-utils');
|
||||
const {
|
||||
getSafeTimezone,
|
||||
getTodayBoundsInUTC,
|
||||
} = require('../../utils/timezone-utils');
|
||||
|
||||
const {
|
||||
validateProjectAccess,
|
||||
|
|
@ -76,7 +79,36 @@ router.get('/tasks', async (req, res) => {
|
|||
|
||||
await handleRecurringTasks(userId, type);
|
||||
|
||||
const tasks = await filterTasksByParams(req.query, userId, timezone);
|
||||
let tasks = await filterTasksByParams(req.query, userId, timezone);
|
||||
|
||||
// For type=today, exclude templates that have instances with due_date in today's range
|
||||
if (type === 'today') {
|
||||
const safeTimezone = getSafeTimezone(timezone);
|
||||
const todayBounds = getTodayBoundsInUTC(safeTimezone);
|
||||
|
||||
// Find all instances with due_date in today's range
|
||||
const instancesForToday = tasks.filter(
|
||||
(t) =>
|
||||
t.recurring_parent_id &&
|
||||
t.due_date &&
|
||||
new Date(t.due_date) >= todayBounds.start &&
|
||||
new Date(t.due_date) <= todayBounds.end
|
||||
);
|
||||
|
||||
// Get parent IDs of those instances
|
||||
const parentIdsWithTodayInstances = new Set(
|
||||
instancesForToday.map((t) => t.recurring_parent_id)
|
||||
);
|
||||
|
||||
// Filter out templates that have instances for today
|
||||
tasks = tasks.filter(
|
||||
(t) =>
|
||||
!t.recurrence_type ||
|
||||
t.recurrence_type === 'none' ||
|
||||
t.recurring_parent_id !== null ||
|
||||
!parentIdsWithTodayInstances.has(t.id)
|
||||
);
|
||||
}
|
||||
|
||||
const groupedTasks = await buildGroupedTasks(
|
||||
tasks,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ const { computeTaskMetrics } = require('../queries/metrics-computation');
|
|||
async function handleRecurringTasks(userId, queryType) {
|
||||
if (queryType === 'upcoming') {
|
||||
await generateRecurringTasksWithLock(userId, 7);
|
||||
} else if (queryType === 'today') {
|
||||
await generateRecurringTasksWithLock(userId, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ const permissionsService = require('../../../services/permissionsService');
|
|||
const {
|
||||
getSafeTimezone,
|
||||
getUpcomingRangeInUTC,
|
||||
getTodayBoundsInUTC,
|
||||
} = require('../../../utils/timezone-utils');
|
||||
|
||||
async function filterTasksByParams(
|
||||
|
|
@ -101,8 +102,10 @@ async function filterTasksByParams(
|
|||
];
|
||||
|
||||
switch (params.type) {
|
||||
case 'today':
|
||||
whereClause.recurring_parent_id = null;
|
||||
case 'today': {
|
||||
const safeTimezone = getSafeTimezone(userTimezone);
|
||||
const todayBounds = getTodayBoundsInUTC(safeTimezone);
|
||||
|
||||
whereClause.status = {
|
||||
[Op.notIn]: [
|
||||
Task.STATUS.DONE,
|
||||
|
|
@ -111,7 +114,42 @@ async function filterTasksByParams(
|
|||
'archived',
|
||||
],
|
||||
};
|
||||
whereClause[Op.or] = [
|
||||
{
|
||||
[Op.and]: [
|
||||
{
|
||||
[Op.or]: [
|
||||
{ recurrence_type: 'none' },
|
||||
{ recurrence_type: null },
|
||||
],
|
||||
},
|
||||
{ recurring_parent_id: null },
|
||||
],
|
||||
},
|
||||
{
|
||||
[Op.and]: [
|
||||
{ recurrence_type: { [Op.ne]: 'none' } },
|
||||
{ recurrence_type: { [Op.ne]: null } },
|
||||
{ recurring_parent_id: null },
|
||||
{ today: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
[Op.and]: [
|
||||
{ recurring_parent_id: { [Op.ne]: null } },
|
||||
{
|
||||
due_date: {
|
||||
[Op.between]: [
|
||||
todayBounds.start,
|
||||
todayBounds.end,
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
break;
|
||||
}
|
||||
case 'upcoming': {
|
||||
const safeTimezone = getSafeTimezone(userTimezone);
|
||||
const upcomingRange = getUpcomingRangeInUTC(safeTimezone, 7);
|
||||
|
|
|
|||
38
backend/scripts/generate-today-instances.js
Normal file
38
backend/scripts/generate-today-instances.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
const {
|
||||
generateRecurringTasksWithLock,
|
||||
} = require('../services/recurringTaskService');
|
||||
const { User } = require('../models');
|
||||
|
||||
async function generateTasks() {
|
||||
// Get first user
|
||||
const user = await User.findOne();
|
||||
|
||||
if (!user) {
|
||||
console.log('No users found');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Generating recurring tasks for user ${user.id} (${user.email})`
|
||||
);
|
||||
|
||||
const tasks = await generateRecurringTasksWithLock(user.id, 1);
|
||||
|
||||
console.log(`Generated ${tasks.length} task instances`);
|
||||
|
||||
if (tasks.length > 0) {
|
||||
console.log('\nGenerated tasks:');
|
||||
tasks.forEach((t) => {
|
||||
console.log(
|
||||
`- ${t.name} (due: ${t.due_date ? t.due_date.toISOString().split('T')[0] : 'none'})`
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
generateTasks().catch((err) => {
|
||||
console.error('Error:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
56
backend/scripts/test-query.js
Normal file
56
backend/scripts/test-query.js
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
const { Task, sequelize } = require('../models');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
async function testQuery() {
|
||||
const whereClause = {
|
||||
parent_task_id: null,
|
||||
status: {
|
||||
[Op.notIn]: [
|
||||
Task.STATUS.DONE,
|
||||
Task.STATUS.ARCHIVED,
|
||||
'done',
|
||||
'archived',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
whereClause[Op.or] = [
|
||||
{
|
||||
[Op.and]: [
|
||||
{
|
||||
[Op.or]: [
|
||||
{ recurrence_type: 'none' },
|
||||
{ recurrence_type: null },
|
||||
],
|
||||
},
|
||||
{ recurring_parent_id: null },
|
||||
],
|
||||
},
|
||||
{
|
||||
[Op.and]: [{ recurring_parent_id: { [Op.ne]: null } }],
|
||||
},
|
||||
];
|
||||
|
||||
// Log the SQL that will be generated
|
||||
const query = Task.findAll({
|
||||
where: whereClause,
|
||||
attributes: ['id', 'name', 'recurrence_type', 'recurring_parent_id'],
|
||||
logging: console.log,
|
||||
});
|
||||
|
||||
console.log('\nThis query should:');
|
||||
console.log(
|
||||
'✓ Include: Regular tasks (recurrence_type = null/none, recurring_parent_id = null)'
|
||||
);
|
||||
console.log('✓ Include: Recurring instances (recurring_parent_id != null)');
|
||||
console.log(
|
||||
'✗ Exclude: Recurring parent templates (recurrence_type = daily/weekly/etc, recurring_parent_id = null)'
|
||||
);
|
||||
|
||||
await sequelize.close();
|
||||
}
|
||||
|
||||
testQuery().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -73,8 +73,10 @@ const processRecurringTask = async (task, now, lookAheadDate = null) => {
|
|||
return newTasks;
|
||||
}
|
||||
|
||||
if (!task.last_generated_date && task.due_date) {
|
||||
const originalDueDate = new Date(task.due_date.getTime());
|
||||
if (!task.last_generated_date) {
|
||||
const originalDueDate = task.due_date
|
||||
? new Date(task.due_date.getTime())
|
||||
: new Date(now.getTime());
|
||||
|
||||
if (originalDueDate <= generateUpTo) {
|
||||
const startOfDay = new Date(originalDueDate);
|
||||
|
|
|
|||
|
|
@ -643,37 +643,38 @@ describe('Recurring Tasks API', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should exclude recurring task instances from type=today API response', async () => {
|
||||
it('should include recurring task instances in type=today API response', async () => {
|
||||
const response = await agent.get('/api/tasks?type=today');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.tasks).toBeDefined();
|
||||
|
||||
// Should only return regular task + recurring template, not the instance
|
||||
expect(response.body.tasks.length).toBe(2);
|
||||
// Should return at least the regular task + recurring instance
|
||||
// Parent tasks should NOT appear in today view
|
||||
expect(response.body.tasks.length).toBeGreaterThanOrEqual(2);
|
||||
|
||||
const taskIds = response.body.tasks.map((t) => t.id);
|
||||
expect(taskIds).toContain(regularTask.id);
|
||||
expect(taskIds).toContain(parentTask.id);
|
||||
expect(taskIds).not.toContain(childTask.id); // Instance should be filtered out
|
||||
expect(taskIds).not.toContain(parentTask.id); // Parent should NOT be included
|
||||
expect(taskIds).toContain(childTask.id); // Instance should be included
|
||||
});
|
||||
|
||||
it('should preserve original names for recurring tasks in type=today API response', async () => {
|
||||
it('should preserve original names for recurring task instances in type=today API response', async () => {
|
||||
const response = await agent.get('/api/tasks?type=today');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.tasks).toBeDefined();
|
||||
|
||||
// Find the recurring task in the response
|
||||
const recurringTask = response.body.tasks.find(
|
||||
(t) => t.id === parentTask.id
|
||||
// Find the recurring task instance in the response
|
||||
const recurringInstance = response.body.tasks.find(
|
||||
(t) => t.id === childTask.id
|
||||
);
|
||||
expect(recurringTask).toBeDefined();
|
||||
expect(recurringInstance).toBeDefined();
|
||||
|
||||
// Should show original name, not "Daily"
|
||||
expect(recurringTask.name).toBe('Take vitamins');
|
||||
expect(recurringTask.original_name).toBe('Take vitamins');
|
||||
expect(recurringTask.name).not.toBe('Daily');
|
||||
// Instances should show original name, not "Daily"
|
||||
expect(recurringInstance.name).toBe('Take vitamins');
|
||||
expect(recurringInstance.original_name).toBe('Take vitamins');
|
||||
expect(recurringInstance.name).not.toBe('Daily');
|
||||
});
|
||||
|
||||
it('should show generic names for non-today API calls (backward compatibility)', async () => {
|
||||
|
|
@ -763,4 +764,99 @@ describe('Recurring Tasks API', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Recurring tasks in Today view', () => {
|
||||
it('should show recurring task instances in type=today API response', async () => {
|
||||
const recurringTaskService = require('../../services/recurringTaskService');
|
||||
|
||||
// Create a recurring daily task with due date today
|
||||
const today = new Date();
|
||||
today.setHours(12, 0, 0, 0);
|
||||
|
||||
const taskData = {
|
||||
name: 'Daily standup meeting',
|
||||
recurrence_type: 'daily',
|
||||
recurrence_interval: 1,
|
||||
due_date: today.toISOString(),
|
||||
priority: 1,
|
||||
completion_based: false,
|
||||
};
|
||||
|
||||
const createResponse = await agent.post('/api/task').send(taskData);
|
||||
expect(createResponse.status).toBe(201);
|
||||
|
||||
const recurringTaskId = createResponse.body.id;
|
||||
|
||||
// Generate recurring task instances
|
||||
await recurringTaskService.generateRecurringTasks(user.id, 2);
|
||||
|
||||
// Verify instances were created
|
||||
const instances = await Task.findAll({
|
||||
where: {
|
||||
user_id: user.id,
|
||||
recurring_parent_id: recurringTaskId,
|
||||
},
|
||||
});
|
||||
|
||||
expect(instances.length).toBeGreaterThan(0);
|
||||
|
||||
// Fetch tasks with type=today
|
||||
const todayResponse = await agent.get('/api/tasks?type=today');
|
||||
expect(todayResponse.status).toBe(200);
|
||||
|
||||
// Find the recurring task instance in the today response
|
||||
const todayTasks = todayResponse.body.tasks;
|
||||
|
||||
// Check if we have the recurring instance (but not the parent)
|
||||
const recurringTasksInToday = todayTasks.filter(
|
||||
(task) => task.recurring_parent_id === recurringTaskId
|
||||
);
|
||||
|
||||
// Should find at least one instance (the one due today)
|
||||
expect(recurringTasksInToday.length).toBeGreaterThan(0);
|
||||
|
||||
// Verify at least one task with this name appears
|
||||
const taskWithName = todayTasks.find(
|
||||
(task) => task.name === 'Daily standup meeting'
|
||||
);
|
||||
expect(taskWithName).toBeDefined();
|
||||
expect(taskWithName.recurring_parent_id).toBe(recurringTaskId);
|
||||
});
|
||||
|
||||
it('should include recurring_parent_uid in serialized task instances', async () => {
|
||||
const today = new Date();
|
||||
const taskResponse = await agent.post('/api/task').send({
|
||||
name: 'Recurring parent test',
|
||||
recurrence_type: 'daily',
|
||||
recurrence_interval: 1,
|
||||
due_date: today.toISOString().split('T')[0],
|
||||
});
|
||||
|
||||
expect(taskResponse.status).toBe(201);
|
||||
const recurringTask = taskResponse.body;
|
||||
|
||||
await agent.post('/api/tasks/generate-recurring');
|
||||
|
||||
// Find the generated instance
|
||||
const generatedInstance = await Task.findOne({
|
||||
where: {
|
||||
user_id: user.id,
|
||||
recurring_parent_id: recurringTask.id,
|
||||
},
|
||||
});
|
||||
|
||||
expect(generatedInstance).toBeDefined();
|
||||
|
||||
const response = await agent.get('/api/tasks?type=today');
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const instance = response.body.tasks.find(
|
||||
(task) => task.recurring_parent_id === recurringTask.id
|
||||
);
|
||||
|
||||
expect(instance).toBeDefined();
|
||||
expect(instance.recurring_parent_uid).toBeDefined();
|
||||
expect(instance.recurring_parent_uid).toBe(recurringTask.uid);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -70,10 +70,15 @@ test('user can set task priority to high', async ({ page, baseURL }) => {
|
|||
// Wait for dropdown to close
|
||||
await expect(page.locator('[data-testid="priority-dropdown"][data-state="closed"]')).toBeVisible();
|
||||
|
||||
// Verify modal is still open after priority change
|
||||
await expect(page.locator('[data-testid="task-modal"][data-state="idle"]')).toBeVisible();
|
||||
|
||||
// Wait for the save button to be stable after priority change
|
||||
await page.waitForTimeout(200);
|
||||
await expect(page.locator('[data-testid="task-save-button"]')).toBeVisible();
|
||||
await page.locator('[data-testid="task-save-button"]').click();
|
||||
await page.waitForTimeout(500);
|
||||
const saveButton0 = page.locator('[data-testid="task-save-button"]');
|
||||
await expect(saveButton0).toBeAttached({ timeout: 5000 });
|
||||
await expect(saveButton0).toBeVisible({ timeout: 5000 });
|
||||
await saveButton0.click();
|
||||
|
||||
// Wait for saving state then idle state
|
||||
await expect(page.locator('[data-testid="task-modal"][data-state="saving"]')).toBeVisible();
|
||||
|
|
@ -115,10 +120,15 @@ test('user can set task priority to medium and low', async ({ page, baseURL }) =
|
|||
await mediumPriorityOption.click();
|
||||
await expect(page.locator('[data-testid="priority-dropdown"][data-state="closed"]')).toBeVisible();
|
||||
|
||||
// Verify modal is still open after priority change
|
||||
await expect(page.locator('[data-testid="task-modal"][data-state="idle"]')).toBeVisible();
|
||||
|
||||
// Wait for the save button to be stable after priority change
|
||||
await page.waitForTimeout(200);
|
||||
await expect(page.locator('[data-testid="task-save-button"]')).toBeVisible();
|
||||
await page.locator('[data-testid="task-save-button"]').click();
|
||||
await page.waitForTimeout(500);
|
||||
const saveButton1 = page.locator('[data-testid="task-save-button"]');
|
||||
await expect(saveButton1).toBeAttached({ timeout: 5000 });
|
||||
await expect(saveButton1).toBeVisible({ timeout: 5000 });
|
||||
await saveButton1.click();
|
||||
await expect(page.locator('[data-testid="task-modal"][data-state="saving"]')).toBeVisible();
|
||||
await expect(page.locator('[data-testid="task-modal"]')).not.toBeVisible({ timeout: 10000 });
|
||||
|
||||
|
|
@ -149,10 +159,15 @@ test('user can set task priority to medium and low', async ({ page, baseURL }) =
|
|||
await lowPriorityOption.click();
|
||||
await expect(page.locator('[data-testid="priority-dropdown"][data-state="closed"]')).toBeVisible();
|
||||
|
||||
// Verify modal is still open after priority change
|
||||
await expect(page.locator('[data-testid="task-modal"][data-state="idle"]')).toBeVisible();
|
||||
|
||||
// Wait for the save button to be stable after priority change
|
||||
await page.waitForTimeout(200);
|
||||
await expect(page.locator('[data-testid="task-save-button"]')).toBeVisible();
|
||||
await page.locator('[data-testid="task-save-button"]').click();
|
||||
await page.waitForTimeout(500);
|
||||
const saveButton2 = page.locator('[data-testid="task-save-button"]');
|
||||
await expect(saveButton2).toBeAttached({ timeout: 5000 });
|
||||
await expect(saveButton2).toBeVisible({ timeout: 5000 });
|
||||
await saveButton2.click();
|
||||
await expect(page.locator('[data-testid="task-modal"][data-state="saving"]')).toBeVisible();
|
||||
await expect(page.locator('[data-testid="task-modal"]')).not.toBeVisible({ timeout: 10000 });
|
||||
|
||||
|
|
@ -195,10 +210,15 @@ test('user can set a due date for a task', async ({ page, baseURL }) => {
|
|||
await dayButton.click();
|
||||
await expect(page.locator('[data-testid="datepicker"][data-state="closed"]')).toBeVisible();
|
||||
|
||||
// Verify modal is still open after date change
|
||||
await expect(page.locator('[data-testid="task-modal"][data-state="idle"]')).toBeVisible();
|
||||
|
||||
// Wait for the save button to be stable after date change
|
||||
await page.waitForTimeout(200);
|
||||
await expect(page.locator('[data-testid="task-save-button"]')).toBeVisible();
|
||||
await page.locator('[data-testid="task-save-button"]').click();
|
||||
await page.waitForTimeout(500);
|
||||
const saveButton3 = page.locator('[data-testid="task-save-button"]');
|
||||
await expect(saveButton3).toBeAttached({ timeout: 5000 });
|
||||
await expect(saveButton3).toBeVisible({ timeout: 5000 });
|
||||
await saveButton3.click();
|
||||
await expect(page.locator('[data-testid="task-modal"][data-state="saving"]')).toBeVisible();
|
||||
await expect(page.locator('[data-testid="task-modal"]')).not.toBeVisible({ timeout: 10000 });
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import {
|
|||
deleteTask,
|
||||
toggleTaskCompletion,
|
||||
fetchTaskByUid,
|
||||
fetchTaskById,
|
||||
fetchTaskNextIterations,
|
||||
TaskIteration,
|
||||
} from '../../utils/tasksService';
|
||||
|
|
@ -252,11 +251,11 @@ const TaskDetails: React.FC = () => {
|
|||
// Load parent task for child tasks (recurring instances)
|
||||
useEffect(() => {
|
||||
const loadParentTask = async () => {
|
||||
if (task?.recurring_parent_id) {
|
||||
if (task?.recurring_parent_uid) {
|
||||
try {
|
||||
setLoadingParent(true);
|
||||
const parent = await fetchTaskById(
|
||||
task.recurring_parent_id
|
||||
const parent = await fetchTaskByUid(
|
||||
task.recurring_parent_uid
|
||||
);
|
||||
setParentTask(parent);
|
||||
} catch (error) {
|
||||
|
|
@ -269,7 +268,7 @@ const TaskDetails: React.FC = () => {
|
|||
};
|
||||
|
||||
loadParentTask();
|
||||
}, [task?.recurring_parent_id]);
|
||||
}, [task?.recurring_parent_uid]);
|
||||
|
||||
const handleEdit = (e?: React.MouseEvent) => {
|
||||
if (e) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import DiscardChangesDialog from '../Shared/DiscardChangesDialog';
|
|||
import { useToast } from '../Shared/ToastContext';
|
||||
import { Project } from '../../entities/Project';
|
||||
import { useStore } from '../../store/useStore';
|
||||
import { fetchTaskById } from '../../utils/tasksService';
|
||||
import { fetchTaskByUid } from '../../utils/tasksService';
|
||||
import {
|
||||
analyzeTaskName,
|
||||
TaskAnalysis,
|
||||
|
|
@ -100,7 +100,7 @@ const TaskModal: React.FC<TaskModalProps> = ({
|
|||
const expandedSections = {
|
||||
...baseSections,
|
||||
subtasks: baseSections.subtasks || autoFocusSubtasks,
|
||||
recurrence: baseSections.recurrence || !!task.recurring_parent_id, // Auto-expand for child tasks
|
||||
recurrence: baseSections.recurrence || !!task.recurring_parent_uid, // Auto-expand for child tasks
|
||||
};
|
||||
|
||||
const { showSuccessToast, showErrorToast } = useToast();
|
||||
|
|
@ -179,11 +179,11 @@ const TaskModal: React.FC<TaskModalProps> = ({
|
|||
// Handle parent task fetching separately
|
||||
useEffect(() => {
|
||||
const fetchParentTask = async () => {
|
||||
if (task.recurring_parent_id && isOpen) {
|
||||
if (task.recurring_parent_uid && isOpen) {
|
||||
setParentTaskLoading(true);
|
||||
try {
|
||||
const parent = await fetchTaskById(
|
||||
task.recurring_parent_id
|
||||
const parent = await fetchTaskByUid(
|
||||
task.recurring_parent_uid
|
||||
);
|
||||
setParentTask(parent);
|
||||
} catch (error) {
|
||||
|
|
@ -198,7 +198,7 @@ const TaskModal: React.FC<TaskModalProps> = ({
|
|||
};
|
||||
|
||||
fetchParentTask();
|
||||
}, [task.recurring_parent_id, isOpen]);
|
||||
}, [task.recurring_parent_uid, isOpen]);
|
||||
|
||||
// Fetch task intelligence setting from user profile
|
||||
useEffect(() => {
|
||||
|
|
@ -934,7 +934,7 @@ const TaskModal: React.FC<TaskModalProps> = ({
|
|||
>
|
||||
<ArrowPathIcon className="h-5 w-5" />
|
||||
{(formData.recurrence_type ||
|
||||
formData.recurring_parent_id) && (
|
||||
formData.recurring_parent_uid) && (
|
||||
<span className="absolute -top-1 -right-1 w-3 h-3 bg-green-500 rounded-full"></span>
|
||||
)}
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ export interface Task {
|
|||
recurrence_week_of_month?: number;
|
||||
completion_based?: boolean;
|
||||
recurring_parent_id?: number;
|
||||
recurring_parent_uid?: string;
|
||||
last_generated_date?: string;
|
||||
completed_at: string | null;
|
||||
parent_task_id?: number;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue