Fix recurring tasks duplication in productivity assistant (#313)
* Fix an issue with recurring tasks in prod assistant * fixup! Fix an issue with recurring tasks in prod assistant
This commit is contained in:
parent
86fe74fdf8
commit
6dcb794a26
3 changed files with 122 additions and 7 deletions
|
|
@ -192,9 +192,11 @@ async function serializeTask(task, userTimezone = 'UTC', options = {}) {
|
|||
|
||||
// For recurring task templates, show recurrence type instead of original name
|
||||
// unless skipDisplayNameTransform option is true
|
||||
// Skip this transformation for 'today' type queries to show actual task names
|
||||
let displayName = taskJson.name;
|
||||
if (
|
||||
!options.skipDisplayNameTransform &&
|
||||
!options.preserveOriginalName &&
|
||||
taskJson.recurrence_type &&
|
||||
taskJson.recurrence_type !== 'none' &&
|
||||
!taskJson.recurring_parent_id
|
||||
|
|
@ -516,6 +518,8 @@ async function filterTasksByParams(params, userId, userTimezone) {
|
|||
// Filter by type
|
||||
switch (params.type) {
|
||||
case 'today':
|
||||
// Ensure we exclude recurring task instances for today view
|
||||
whereClause.recurring_parent_id = null;
|
||||
whereClause.status = {
|
||||
[Op.notIn]: [
|
||||
Task.STATUS.DONE,
|
||||
|
|
@ -1162,10 +1166,18 @@ router.get('/tasks', async (req, res) => {
|
|||
req.currentUser.timezone
|
||||
);
|
||||
|
||||
// Preserve original names for recurring tasks in 'today' view for productivity assistant
|
||||
const serializationOptions =
|
||||
req.query.type === 'today' ? { preserveOriginalName: true } : {};
|
||||
|
||||
const response = {
|
||||
tasks: await Promise.all(
|
||||
tasks.map((task) =>
|
||||
serializeTask(task, req.currentUser.timezone)
|
||||
serializeTask(
|
||||
task,
|
||||
req.currentUser.timezone,
|
||||
serializationOptions
|
||||
)
|
||||
)
|
||||
),
|
||||
metrics: {
|
||||
|
|
@ -1174,29 +1186,46 @@ router.get('/tasks', async (req, res) => {
|
|||
tasks_in_progress_count: metrics.tasks_in_progress_count,
|
||||
tasks_in_progress: await Promise.all(
|
||||
metrics.tasks_in_progress.map((task) =>
|
||||
serializeTask(task, req.currentUser.timezone)
|
||||
serializeTask(
|
||||
task,
|
||||
req.currentUser.timezone,
|
||||
serializationOptions
|
||||
)
|
||||
)
|
||||
),
|
||||
tasks_due_today: await Promise.all(
|
||||
metrics.tasks_due_today.map((task) =>
|
||||
serializeTask(task, req.currentUser.timezone)
|
||||
serializeTask(
|
||||
task,
|
||||
req.currentUser.timezone,
|
||||
serializationOptions
|
||||
)
|
||||
)
|
||||
),
|
||||
today_plan_tasks: await Promise.all(
|
||||
metrics.today_plan_tasks.map((task) =>
|
||||
serializeTask(task, req.currentUser.timezone)
|
||||
serializeTask(
|
||||
task,
|
||||
req.currentUser.timezone,
|
||||
serializationOptions
|
||||
)
|
||||
)
|
||||
),
|
||||
suggested_tasks: await Promise.all(
|
||||
metrics.suggested_tasks.map((task) =>
|
||||
serializeTask(task, req.currentUser.timezone)
|
||||
serializeTask(
|
||||
task,
|
||||
req.currentUser.timezone,
|
||||
serializationOptions
|
||||
)
|
||||
)
|
||||
),
|
||||
tasks_completed_today: await Promise.all(
|
||||
metrics.tasks_completed_today.map(async (task) => {
|
||||
const serialized = await serializeTask(
|
||||
task,
|
||||
req.currentUser.timezone
|
||||
req.currentUser.timezone,
|
||||
serializationOptions
|
||||
);
|
||||
return {
|
||||
...serialized,
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ describe('Global Recurring Task Instance Filtering', () => {
|
|||
expect(response.body.tasks).toBeDefined();
|
||||
|
||||
const taskNames = response.body.tasks.map((t) => t.name);
|
||||
expect(taskNames).toContain('Daily');
|
||||
expect(taskNames).toContain('Daily Workout Template'); // Now preserves original name for type=today
|
||||
expect(taskNames).toContain('Regular Task');
|
||||
expect(taskNames).not.toContain('Daily Workout - Aug 23');
|
||||
expect(taskNames).not.toContain('Daily Workout - Aug 24');
|
||||
|
|
|
|||
|
|
@ -605,4 +605,90 @@ describe('Recurring Tasks API', () => {
|
|||
expect(parentStillExists).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /api/tasks?type=today - Today view filtering and naming', () => {
|
||||
let parentTask, childTask, regularTask;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Create a regular non-recurring task
|
||||
regularTask = await Task.create({
|
||||
name: 'Regular Task',
|
||||
recurrence_type: 'none',
|
||||
user_id: user.id,
|
||||
status: 0,
|
||||
today: true,
|
||||
});
|
||||
|
||||
// Create a recurring parent task (template)
|
||||
parentTask = await Task.create({
|
||||
name: 'Take vitamins',
|
||||
recurrence_type: 'daily',
|
||||
recurrence_interval: 1,
|
||||
user_id: user.id,
|
||||
status: 0,
|
||||
today: true,
|
||||
});
|
||||
|
||||
// Create a recurring child task (instance)
|
||||
childTask = await Task.create({
|
||||
name: 'Take vitamins',
|
||||
recurrence_type: 'none',
|
||||
recurring_parent_id: parentTask.id,
|
||||
user_id: user.id,
|
||||
status: 0,
|
||||
today: true,
|
||||
due_date: new Date(),
|
||||
});
|
||||
});
|
||||
|
||||
it('should exclude recurring task instances from 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);
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
it('should preserve original names for recurring tasks 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
|
||||
);
|
||||
expect(recurringTask).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');
|
||||
});
|
||||
|
||||
it('should show generic names for non-today API calls (backward compatibility)', async () => {
|
||||
const response = await agent.get('/api/tasks'); // No 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
|
||||
);
|
||||
expect(recurringTask).toBeDefined();
|
||||
|
||||
// Should show generic recurrence name for backward compatibility
|
||||
expect(recurringTask.name).toBe('Daily');
|
||||
expect(recurringTask.original_name).toBe('Take vitamins');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue