diff --git a/docs/superpowers/plans/2026-03-13-my-tasks-page.md b/docs/superpowers/plans/2026-03-13-my-tasks-page.md new file mode 100644 index 0000000..d1be129 --- /dev/null +++ b/docs/superpowers/plans/2026-03-13-my-tasks-page.md @@ -0,0 +1,584 @@ +# My Tasks Page Implementation Plan + +> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add a "/my-tasks" page that displays all non-archived tasks across projects with Kanban and List views, filtered by current user by default. + +**Architecture:** Backend: add SearchFilter annotations on Task entity for server-side filtering. Frontend: new page with filter bar + two view modes (Kanban/List), reusing existing TaskCard and TaskModal components. + +**Tech Stack:** PHP 8.4 / API Platform 4 (SearchFilter), Nuxt 4 / Vue 3, Tailwind CSS, MalioSelect, Pinia + +**Spec:** `docs/superpowers/specs/2026-03-13-my-tasks-page-design.md` + +--- + +## Chunk 1: Backend — API Filters + +### Task 1: Add SearchFilter annotations on Task entity + +**Files:** +- Modify: `src/Entity/Task.php:35` (ApiFilter line) + +- [ ] **Step 1: Add new SearchFilter properties** + +In `src/Entity/Task.php`, replace the existing `#[ApiFilter(SearchFilter::class, ...)]` line (line 35) with an expanded version that includes `assignee`, `priority`, `effort`, `tags`, and `status`: + +```php +#[ApiFilter(SearchFilter::class, properties: ['project' => 'exact', 'group' => 'exact', 'assignee' => 'exact', 'priority' => 'exact', 'effort' => 'exact', 'tags' => 'exact', 'status' => 'exact'])] +``` + +- [ ] **Step 2: Disable pagination on GetCollection** + +In `src/Entity/Task.php`, modify the `GetCollection` operation (line 25) to disable pagination: + +```php +new GetCollection(paginationEnabled: false), +``` + +- [ ] **Step 3: Verify filters work** + +Run in the container: +```bash +docker exec -t php-lesstime-fpm php bin/console debug:router | grep tasks +``` +Then test the API call: +```bash +curl -s 'http://localhost:8082/api/tasks?archived=false&assignee=/api/users/1' -H 'Cookie: BEARER=...' | head -c 500 +``` +Expected: JSON response with filtered tasks. + +- [ ] **Step 4: Commit** + +```bash +git add src/Entity/Task.php +git commit -m "feat(backend) : add SearchFilter for assignee, priority, effort, tags, status on Task" +``` + +--- + +## Chunk 2: Frontend — Service, i18n, Sidebar + +### Task 2: Add `getFiltered` method to task service + +**Files:** +- Modify: `frontend/services/tasks.ts` + +- [ ] **Step 1: Add the `getFiltered` method** + +Add after the `getByProjectArchived` method (after line 27) in `frontend/services/tasks.ts`: + +```typescript +async function getFiltered(params: Record): Promise { + const data = await api.get>('/tasks', params as Record) + return extractHydraMembers(data) +} +``` + +- [ ] **Step 2: Export the new method** + +Update the return statement (line 47) to include `getFiltered`: + +```typescript +return { getAll, getByProject, getByProjectArchived, getFiltered, create, update, remove } +``` + +- [ ] **Step 3: Commit** + +```bash +git add frontend/services/tasks.ts +git commit -m "feat(frontend) : add getFiltered method to task service" +``` + +### Task 3: Add i18n translations + +**Files:** +- Modify: `frontend/i18n/locales/fr.json` + +- [ ] **Step 1: Add myTasks and sidebar keys** + +Add these entries to `frontend/i18n/locales/fr.json` (before the closing `}`): + +```json +"myTasks": { + "title": "Mes tâches", + "viewKanban": "Vue Kanban", + "viewList": "Vue Liste", + "allProjects": "Tous les projets", + "allGroups": "Tous les groupes", + "allTypes": "Tous les types", + "allPriorities": "Toutes les priorités", + "allEfforts": "Tous les efforts", + "allAssignees": "Tous", + "noTasks": "Aucune tâche", + "backlog": "Backlog" +}, +"sidebar": { + "myTasks": "Mes tâches" +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add frontend/i18n/locales/fr.json +git commit -m "feat(frontend) : add i18n translations for my-tasks page" +``` + +### Task 4: Add sidebar navigation link + +**Files:** +- Modify: `frontend/layouts/default.vue:23-35` (nav section) + +- [ ] **Step 1: Add SidebarLink for "Mes tâches"** + +In `frontend/layouts/default.vue`, add a new `SidebarLink` between the "Tableau de bord" link (line 29) and the "Projets" link (line 30): + +```vue + +``` + +- [ ] **Step 2: Commit** + +```bash +git add frontend/layouts/default.vue +git commit -m "feat(frontend) : add Mes tâches link to sidebar navigation" +``` + +--- + +## Chunk 3: Frontend — My Tasks Page (Kanban + List views) + +### Task 5: Create the my-tasks page + +**Files:** +- Create: `frontend/pages/my-tasks.vue` + +- [ ] **Step 1: Create the page file with imports and data loading** + +Create `frontend/pages/my-tasks.vue` with the full page implementation. The page structure: + +**Script section** — data loading pattern (same as `projects/[id]/index.vue`): + +```typescript + +``` + +**Template section:** + +```vue + +``` + +Note: add the `timerStore` and `isTimerOnTask` helper in the script section: + +```typescript +const timerStore = useTimerStore() + +function isTimerOnTask(task: Task): boolean { + const entry = timerStore.activeEntry + if (!entry?.task) return false + const entryTaskId = typeof entry.task === 'string' + ? entry.task + : (entry.task['@id'] ?? entry.task.id) + const taskId = task['@id'] ?? task.id + return entryTaskId === taskId || entryTaskId === `/api/tasks/${task.id}` +} +``` + +- [ ] **Step 2: Verify the page loads** + +Run: `make dev-nuxt` + +Navigate to `http://localhost:3002/my-tasks`. +Expected: page loads with filters and shows tasks assigned to current user in Kanban view. + +- [ ] **Step 3: Test view toggle** + +Click the list icon. Expected: tasks display in list format with title, badges, project code. +Click the kanban icon. Expected: tasks display in columns by status. + +- [ ] **Step 4: Test filters** + +Change the assignee filter to "Tous". Expected: all tasks from all users appear. +Select a specific project. Expected: only tasks from that project appear. +Reset all filters. Expected: all non-archived tasks appear. + +- [ ] **Step 5: Test TaskModal integration** + +Click on a task card/row. Expected: TaskModal opens with task details pre-filled. +Edit and save. Expected: modal closes, tasks reload with updated data. + +- [ ] **Step 6: Commit** + +```bash +git add frontend/pages/my-tasks.vue +git commit -m "feat(frontend) : add my-tasks page with Kanban and List views" +```