diff --git a/backend/tests/integration/views.test.js b/backend/tests/integration/views.test.js index be666b1..ae37f42 100644 --- a/backend/tests/integration/views.test.js +++ b/backend/tests/integration/views.test.js @@ -313,18 +313,18 @@ describe('Views Routes', () => { name: 'personal', }); - // Create tasks with tags + // Create tasks with tags (mix of active and completed) const task1 = await Task.create({ user_id: user.id, name: 'Work task 1', - status: 0, + status: 0, // active }); await task1.addTag(workTag); const task2 = await Task.create({ user_id: user.id, name: 'Urgent work task', - status: 0, + status: 0, // active }); await task2.addTag(workTag); await task2.addTag(urgentTag); @@ -332,9 +332,17 @@ describe('Views Routes', () => { const task3 = await Task.create({ user_id: user.id, name: 'Personal task', - status: 0, + status: 0, // active }); await task3.addTag(personalTag); + + // Add completed task with work tag + const task4 = await Task.create({ + user_id: user.id, + name: 'Completed work task', + status: 2, // completed (done) + }); + await task4.addTag(workTag); }); it('should create view with tags and retrieve matching results', async () => { @@ -365,8 +373,9 @@ describe('Views Routes', () => { const tasks = searchResponse.body.results.filter( (r) => r.type === 'Task' ); - expect(tasks.length).toBe(2); - // Both tasks should have 'work' in their name (case-insensitive) + // Should now return 3 tasks (2 active + 1 completed) + expect(tasks.length).toBe(3); + // All tasks should have 'work' in their name (case-insensitive) expect( tasks.every((t) => t.name.toLowerCase().includes('work')) ).toBe(true); @@ -422,6 +431,83 @@ describe('Views Routes', () => { expect(getResponse.status).toBe(200); expect(getResponse.body.tags).toEqual(['personal']); }); + + it('should return both active and completed tasks in search results', async () => { + // Create a view with work tag + const createResponse = await agent.post('/api/views').send({ + name: 'All Work Tasks View', + filters: ['Task'], + tags: ['work'], + }); + + expect(createResponse.status).toBe(201); + + // Search for work tasks + const searchResponse = await agent.get('/api/search').query({ + tags: 'work', + filters: 'Task', + }); + + expect(searchResponse.status).toBe(200); + const tasks = searchResponse.body.results.filter( + (r) => r.type === 'Task' + ); + + // Should return 3 tasks: 2 active + 1 completed + expect(tasks.length).toBe(3); + + // Verify we have both active and completed tasks + const activeTasks = tasks.filter( + (t) => t.status === 0 || t.status === 'active' + ); + const completedTasks = tasks.filter( + (t) => t.status === 2 || t.status === 'done' + ); + + expect(activeTasks.length).toBe(2); + expect(completedTasks.length).toBe(1); + + // Verify the completed task is included + const completedTask = tasks.find( + (t) => t.name === 'Completed work task' + ); + expect(completedTask).toBeDefined(); + expect(completedTask.status).toBe(2); // done status + }); + + it('should include completed tasks with correct status values', async () => { + // Create tasks with different completion statuses + const archivedTask = await Task.create({ + user_id: user.id, + name: 'Archived work task', + status: 3, // archived + }); + await archivedTask.addTag(workTag); + + // Search for all work tasks + const searchResponse = await agent.get('/api/search').query({ + tags: 'work', + filters: 'Task', + }); + + expect(searchResponse.status).toBe(200); + const tasks = searchResponse.body.results.filter( + (r) => r.type === 'Task' + ); + + // Should now have 4 tasks (2 active, 1 done, 1 archived) + expect(tasks.length).toBe(4); + + // Verify different status types are present + const statusTypes = tasks.map((t) => t.status); + expect(statusTypes).toContain(0); // active + expect(statusTypes).toContain(2); // done + expect(statusTypes).toContain(3); // archived + + // Frontend will filter these out, but backend should provide them all + const nonActiveTasks = tasks.filter((t) => t.status >= 2); + expect(nonActiveTasks.length).toBe(2); // done + archived + }); }); describe('User Isolation', () => { diff --git a/frontend/components/ViewDetail.tsx b/frontend/components/ViewDetail.tsx index d6d72a2..9384dc7 100644 --- a/frontend/components/ViewDetail.tsx +++ b/frontend/components/ViewDetail.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useState, useEffect, useRef, useMemo } from 'react'; import { useParams, useNavigate, Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { @@ -95,6 +95,18 @@ const ViewDetail: React.FC = () => { } }, [isEditingName, editedName]); + // Filter out completed tasks for display count + const activeTasks = useMemo(() => { + return tasks.filter((task) => { + const isCompleted = + task.status === 'done' || + task.status === 'archived' || + task.status === 2 || + task.status === 3; + return !isCompleted; + }); + }, [tasks]); + const fetchViewAndResults = async () => { if (!uid) return; @@ -505,7 +517,7 @@ const ViewDetail: React.FC = () => { {tasks.length > 0 && (

- {t('tasks.title')} ({tasks.length}) + {t('tasks.title')} ({activeTasks.length})