@noclaf/cli 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +22 -1
  2. package/dist/adapters/supabase/calendar-gateway.js +48 -0
  3. package/dist/adapters/supabase/calendar-gateway.js.map +1 -0
  4. package/dist/adapters/supabase/calendar-write-gateway.js +35 -0
  5. package/dist/adapters/supabase/calendar-write-gateway.js.map +1 -0
  6. package/dist/adapters/supabase/checklist-gateway.js +63 -0
  7. package/dist/adapters/supabase/checklist-gateway.js.map +1 -0
  8. package/dist/adapters/supabase/gateway.js +10 -0
  9. package/dist/adapters/supabase/gateway.js.map +1 -1
  10. package/dist/adapters/supabase/release-gateway.js +61 -0
  11. package/dist/adapters/supabase/release-gateway.js.map +1 -0
  12. package/dist/adapters/supabase/sprint-gateway.js +59 -0
  13. package/dist/adapters/supabase/sprint-gateway.js.map +1 -0
  14. package/dist/adapters/supabase/task-gateway.js +36 -33
  15. package/dist/adapters/supabase/task-gateway.js.map +1 -1
  16. package/dist/core/types.js +4 -6
  17. package/dist/core/types.js.map +1 -1
  18. package/dist/lib/calendar-logic.js +59 -0
  19. package/dist/lib/calendar-logic.js.map +1 -0
  20. package/dist/lib/calendar-write-logic.js +6 -0
  21. package/dist/lib/calendar-write-logic.js.map +1 -0
  22. package/dist/lib/checklist-logic.js +5 -0
  23. package/dist/lib/checklist-logic.js.map +1 -0
  24. package/dist/lib/dates.js +21 -0
  25. package/dist/lib/dates.js.map +1 -0
  26. package/dist/lib/pg-error.js +4 -0
  27. package/dist/lib/pg-error.js.map +1 -1
  28. package/dist/lib/release-logic.js +10 -0
  29. package/dist/lib/release-logic.js.map +1 -0
  30. package/dist/lib/sprint-logic.js +31 -0
  31. package/dist/lib/sprint-logic.js.map +1 -0
  32. package/dist/tools/activate-sprint.js +44 -0
  33. package/dist/tools/activate-sprint.js.map +1 -0
  34. package/dist/tools/add-checklist-item.js +44 -0
  35. package/dist/tools/add-checklist-item.js.map +1 -0
  36. package/dist/tools/add-holiday.js +41 -0
  37. package/dist/tools/add-holiday.js.map +1 -0
  38. package/dist/tools/add-home-office.js +31 -0
  39. package/dist/tools/add-home-office.js.map +1 -0
  40. package/dist/tools/add-vacation.js +39 -0
  41. package/dist/tools/add-vacation.js.map +1 -0
  42. package/dist/tools/create-release.js +46 -0
  43. package/dist/tools/create-release.js.map +1 -0
  44. package/dist/tools/create-sprint.js +66 -0
  45. package/dist/tools/create-sprint.js.map +1 -0
  46. package/dist/tools/create-task.js +26 -8
  47. package/dist/tools/create-task.js.map +1 -1
  48. package/dist/tools/end-sprint.js +37 -0
  49. package/dist/tools/end-sprint.js.map +1 -0
  50. package/dist/tools/finalize-release.js +45 -0
  51. package/dist/tools/finalize-release.js.map +1 -0
  52. package/dist/tools/get-calendar.js +40 -0
  53. package/dist/tools/get-calendar.js.map +1 -0
  54. package/dist/tools/get-sprint.js +38 -0
  55. package/dist/tools/get-sprint.js.map +1 -0
  56. package/dist/tools/get-task.js +1 -0
  57. package/dist/tools/get-task.js.map +1 -1
  58. package/dist/tools/index.js +42 -0
  59. package/dist/tools/index.js.map +1 -1
  60. package/dist/tools/list-releases.js +32 -0
  61. package/dist/tools/list-releases.js.map +1 -0
  62. package/dist/tools/list-sprints.js +30 -0
  63. package/dist/tools/list-sprints.js.map +1 -0
  64. package/dist/tools/list-tasks.js +18 -16
  65. package/dist/tools/list-tasks.js.map +1 -1
  66. package/dist/tools/remove-checklist-item.js +42 -0
  67. package/dist/tools/remove-checklist-item.js.map +1 -0
  68. package/dist/tools/remove-checklist.js +42 -0
  69. package/dist/tools/remove-checklist.js.map +1 -0
  70. package/dist/tools/remove-holiday.js +31 -0
  71. package/dist/tools/remove-holiday.js.map +1 -0
  72. package/dist/tools/remove-home-office.js +32 -0
  73. package/dist/tools/remove-home-office.js.map +1 -0
  74. package/dist/tools/remove-vacation.js +31 -0
  75. package/dist/tools/remove-vacation.js.map +1 -0
  76. package/dist/tools/sprint-common.js +13 -0
  77. package/dist/tools/sprint-common.js.map +1 -0
  78. package/dist/tools/update-checklist-item.js +51 -0
  79. package/dist/tools/update-checklist-item.js.map +1 -0
  80. package/dist/tools/update-release.js +58 -0
  81. package/dist/tools/update-release.js.map +1 -0
  82. package/dist/tools/update-sprint.js +56 -0
  83. package/dist/tools/update-sprint.js.map +1 -0
  84. package/dist/tools/update-task.js +46 -31
  85. package/dist/tools/update-task.js.map +1 -1
  86. package/package.json +1 -1
package/README.md CHANGED
@@ -92,30 +92,51 @@ Operações do MCP, com o nome **sem o prefixo** (declarado por grupo — o pref
92
92
  `src/brand.ts`). Tabela gerada da fonte por `bun run gen:docs`.
93
93
 
94
94
  <!-- TOOLS:START -->
95
- <!-- gerado por `bun run gen:docs` — não edite à mão. 23 tools. -->
95
+ <!-- gerado por `bun run gen:docs` — não edite à mão. 44 tools. -->
96
96
 
97
97
  ### Tools de produto — prefixo `nos_`
98
98
 
99
99
  | Operação | O que faz |
100
100
  | --- | --- |
101
+ | `activate_sprint` | Reativa uma sprint: encerra qualquer outra ativa do projeto (tasks não concluídas voltam pro backlog) e reabre a escolhida com status "ativa", start_date = hoje e end_date = null (como a UI). |
101
102
  | `add_checklist` | Cria uma checklist nomeada (com itens) numa tarefa — a checklist nativa que aparece clicável na UI, melhor que enfiar um DoD no texto da descrição. |
103
+ | `add_checklist_item` | Adiciona um item desmarcado ao fim de uma checklist existente. |
104
+ | `add_holiday` | Cria feriado no calendário do time (só admin/gerente). |
105
+ | `add_home_office` | Marca home office do PRÓPRIO usuário num dia (o banco pode recusar por limite mensal). |
106
+ | `add_vacation` | Registra férias de um usuário no calendário do time (só admin/gerente). |
102
107
  | `attach_pr` | Anexa a URL de um pull request a uma tarefa e a coloca em review: grava `pr_url`, seta `review_state=pending` e move o status pra `code_review`. |
103
108
  | `comment_task` | Adiciona um comentário em uma tarefa. |
109
+ | `create_release` | Cria uma release (status planning) no projeto. |
110
+ | `create_sprint` | Cria uma sprint já ATIVA. |
104
111
  | `create_task` | Cria uma nova tarefa no projeto atual. |
105
112
  | `end_allocation` | [ENCERRA O DIA] Bate ponto de SAÍDA do usuário e fecha qualquer task_allocation aberta. |
113
+ | `end_sprint` | Encerra a sprint: tasks fora de done/production saem da sprint e voltam pro backlog; done/production ficam. |
106
114
  | `end_task_allocation` | [PARA TIMER DE TASK] Para a task_allocation ativa. |
115
+ | `finalize_release` | Finaliza a release: status delivered + delivered_at. |
107
116
  | `get_active_allocation` | Retorna o estado atual de cronometragem do usuário: alocação ativa, task atual sendo cronometrada, e segmentos por task na alocação atual. |
117
+ | `get_calendar` | Calendário pessoal: só os dias com eventos (feriados, meus prazos, férias, home office, releases, aniversários e tempo de casa). |
108
118
  | `get_context` | Retorna o contexto completo do projeto NOS ativo: dados do projeto, repositórios, sprint ativa, membros e usuário autenticado. |
119
+ | `get_sprint` | Detalha uma sprint: contagem de tasks por status e tasks vencidas (end_date < hoje e fora de done/production), com key/title/end_date. |
109
120
  | `get_task` | Retorna detalhes completos de uma tarefa: descrição, assignees, checklist, comentários, histórico recente, repositórios vinculados e labels. |
110
121
  | `list_my_allocations` | Lista alocações passadas do usuário (encerradas), ordenadas por data decrescente. |
111
122
  | `list_projects` | Lista os projetos do NOS aos quais você tem acesso. |
123
+ | `list_releases` | Lista as releases do projeto (id, name, status, target_date, delivered_at). |
124
+ | `list_sprints` | Lista as sprints do projeto (ativa e encerradas) com status e datas, mais recentes primeiro. |
112
125
  | `list_tasks` | Lista tarefas do projeto atual, filtradas pelos parâmetros opcionais. |
113
126
  | `move_task` | Atalho pra mudar o status de uma tarefa (move entre colunas do Kanban). |
114
127
  | `record_delivery` | Registra uma ENTREGA do loop de build (um ticket concluído / PR aberto) na analítica de entrega do NOS. |
128
+ | `remove_checklist` | Remove uma checklist inteira (os itens vão junto). |
129
+ | `remove_checklist_item` | Remove um item de checklist. |
130
+ | `remove_holiday` | Exclui feriado pelo id (só admin/gerente). |
131
+ | `remove_home_office` | Remove home office do PRÓPRIO usuário num dia. |
132
+ | `remove_vacation` | Exclui férias pelo id (só admin/gerente). |
115
133
  | `set_project` | Define o projeto ativo da sessão (em memória, dura enquanto o servidor estiver rodando). |
116
134
  | `set_review` | Registra o veredito de um review no ticket: seta `tasks.review_state`. |
117
135
  | `start_allocation` | Inicia uma alocação (bate o ponto) para o usuário atual. |
118
136
  | `start_task_allocation` | Começa a cronometrar tempo numa task específica. |
137
+ | `update_checklist_item` | Marca/desmarca (checked) e/ou edita o texto (text) de um item de checklist. |
138
+ | `update_release` | Edita uma release: name, target_date, description e/ou status (planning\|in_progress\|cancelled). |
139
+ | `update_sprint` | Edita nome e/ou datas de uma sprint. |
119
140
  | `update_task` | Atualiza campos de uma tarefa existente. |
120
141
 
121
142
  ### Tools que espelham o CLI — prefixo `noclaf_`
@@ -0,0 +1,48 @@
1
+ import { FINISHED_FILTER } from '../../core/types.js';
2
+ /** Adaptador Supabase do `CalendarGateway` (leitura do calendário pessoal). */
3
+ export function calendarGateway(db) {
4
+ const ok = (r, what) => {
5
+ if (r.error)
6
+ throw new Error(`Erro ao ler ${what}: ${r.error.message}`);
7
+ return r.data ?? [];
8
+ };
9
+ const names = async (ids) => {
10
+ if (ids.length === 0)
11
+ return new Map();
12
+ const rows = ok(await db.from('profiles').select('id, full_name').in('id', ids), 'perfis');
13
+ return new Map(rows.map((p) => [p.id, p.full_name ?? 'Sem nome']));
14
+ };
15
+ return {
16
+ async getCalendarSources(userId, from, to) {
17
+ const [holidays, vacations, homeOffice, releases, people, tasks] = await Promise.all([
18
+ db.from('holidays').select('id, date, name, type, scope').gte('date', from).lte('date', to),
19
+ db.from('vacations').select('id, user_id, start_date, end_date, notes').lte('start_date', to).gte('end_date', from),
20
+ db.from('home_office_days').select('user_id, date').gte('date', from).lte('date', to),
21
+ db.from('releases').select('project_id, name, status, target_date').gte('target_date', from).lte('target_date', to),
22
+ db.from('profiles').select('full_name, birth_date, company_anniversary_date').eq('is_active', true),
23
+ db.from('tasks')
24
+ .select('key, title, status, end_date, task_assignees!inner(user_id), projects(name)')
25
+ .eq('task_assignees.user_id', userId)
26
+ .gte('end_date', from).lte('end_date', to)
27
+ .not('status', 'in', FINISHED_FILTER),
28
+ ]);
29
+ const vac = ok(vacations, 'férias');
30
+ const ho = ok(homeOffice, 'home office');
31
+ const name = await names([...new Set([...vac.map((v) => v.user_id), ...ho.map((h) => h.user_id)])]);
32
+ return {
33
+ holidays: ok(holidays, 'feriados'),
34
+ deadlines: ok(tasks, 'prazos').map((t) => ({
35
+ key: t.key, title: t.title, status: t.status, end_date: t.end_date,
36
+ project: t.projects?.name ?? '',
37
+ })),
38
+ vacations: vac.map((v) => ({ ...v, person: name.get(v.user_id) ?? 'Sem nome' })),
39
+ homeOffice: ho.map((h) => ({ date: h.date, person: name.get(h.user_id) ?? 'Sem nome' })),
40
+ releases: ok(releases, 'releases'),
41
+ people: ok(people, 'perfis').map((p) => ({
42
+ name: p.full_name ?? 'Sem nome', birth_date: p.birth_date, company_anniversary_date: p.company_anniversary_date,
43
+ })),
44
+ };
45
+ },
46
+ };
47
+ }
48
+ //# sourceMappingURL=calendar-gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendar-gateway.js","sourceRoot":"","sources":["../../../src/adapters/supabase/calendar-gateway.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /** Adaptador Supabase da escrita no calendário; RLS decide quem pode. */
2
+ export function calendarWriteGateway(db) {
3
+ const one = (r, what) => {
4
+ if (r.error || !r.data)
5
+ throw new Error(`Erro ao ${what}: ${r.error?.message ?? 'sem retorno'}`);
6
+ return r.data;
7
+ };
8
+ const many = (r, what) => {
9
+ if (r.error)
10
+ throw new Error(`Erro ao ${what}: ${r.error.message}`);
11
+ return r.data ?? [];
12
+ };
13
+ return {
14
+ async addHoliday(h) {
15
+ return one(await db.from('holidays').insert(h).select('id').single(), 'criar feriado');
16
+ },
17
+ async removeHoliday(id) {
18
+ return many(await db.from('holidays').delete().eq('id', id).select('id'), 'excluir feriado');
19
+ },
20
+ async addVacation(v) {
21
+ return one(await db.from('vacations').insert(v).select('id').single(), 'criar férias');
22
+ },
23
+ async removeVacation(id) {
24
+ return many(await db.from('vacations').delete().eq('id', id).select('id'), 'excluir férias');
25
+ },
26
+ async addHomeOffice(userId, date) {
27
+ return one(await db.from('home_office_days').insert({ user_id: userId, date }).select('id').single(), 'criar home office');
28
+ },
29
+ async removeHomeOffice(userId, date) {
30
+ const r = await db.from('home_office_days').delete().eq('user_id', userId).eq('date', date).select('id');
31
+ return many(r, 'excluir home office');
32
+ },
33
+ };
34
+ }
35
+ //# sourceMappingURL=calendar-write-gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendar-write-gateway.js","sourceRoot":"","sources":["../../../src/adapters/supabase/calendar-write-gateway.ts"],"names":[],"mappings":"AAGA,yEAAyE;AACzE,MAAM,UAAU,oBAAoB,CAAC,EAAY;IAC/C,MAAM,GAAG,GAAG,CAAI,CAAwD,EAAE,IAAY,EAAK,EAAE;QAC3F,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,CAAC,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,CAAC,CAAC;QACjG,OAAO,CAAC,CAAC,IAAI,CAAC;IAChB,CAAC,CAAC;IACF,MAAM,IAAI,GAAG,CAAI,CAA0D,EAAE,IAAY,EAAO,EAAE;QAChG,IAAI,CAAC,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACpE,OAAO,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IACtB,CAAC,CAAC;IACF,OAAO;QACL,KAAK,CAAC,UAAU,CAAC,CAAC;YAChB,OAAO,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC;QACzF,CAAC;QACD,KAAK,CAAC,aAAa,CAAC,EAAE;YACpB,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,iBAAiB,CAAC,CAAC;QAC/F,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,CAAC;YACjB,OAAO,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,cAAc,CAAC,CAAC;QACzF,CAAC;QACD,KAAK,CAAC,cAAc,CAAC,EAAE;YACrB,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC,CAAC;QAC/F,CAAC;QACD,KAAK,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI;YAC9B,OAAO,GAAG,CACR,MAAM,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EACzF,mBAAmB,CACpB,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,IAAI;YACjC,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACzG,OAAO,IAAI,CAAC,CAAC,EAAE,qBAAqB,CAAC,CAAC;QACxC,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,63 @@
1
+ import { nextItemOrder } from '../../lib/checklist-logic.js';
2
+ /** Adaptador Supabase da edição de checklists; a posse é validada via task -> projeto. */
3
+ export function checklistGateway(db) {
4
+ const fail = (what, e) => {
5
+ if (e)
6
+ throw new Error(`Erro ao ${what}: ${e.message}`);
7
+ };
8
+ const inProject = async (taskId, projectId) => {
9
+ const r = await db.from('tasks').select('id').eq('id', taskId).eq('project_id', projectId).maybeSingle();
10
+ fail('validar tarefa', r.error);
11
+ return !!r.data;
12
+ };
13
+ const itemTask = async (itemId, projectId) => {
14
+ const r = await db.from('task_checklist_items').select('task_id').eq('id', itemId).maybeSingle();
15
+ fail('ler item', r.error);
16
+ return r.data && (await inProject(r.data.task_id, projectId)) ? r.data.task_id : null;
17
+ };
18
+ const listTask = async (listId, projectId) => {
19
+ const r = await db.from('task_checklists').select('task_id').eq('id', listId).maybeSingle();
20
+ fail('ler checklist', r.error);
21
+ return r.data && (await inProject(r.data.task_id, projectId)) ? r.data.task_id : null;
22
+ };
23
+ return {
24
+ async updateChecklistItem(projectId, itemId, patch) {
25
+ if (!(await itemTask(itemId, projectId)))
26
+ return null;
27
+ const r = await db.from('task_checklist_items').update(patch).eq('id', itemId).select('id, text, checked');
28
+ fail('atualizar item', r.error);
29
+ return r.data?.[0] ?? null;
30
+ },
31
+ async addChecklistItem(projectId, checklistId, text) {
32
+ const taskId = await listTask(checklistId, projectId);
33
+ if (!taskId)
34
+ return null;
35
+ const cur = await db.from('task_checklist_items').select('order').eq('checklist_id', checklistId);
36
+ fail('ler itens', cur.error);
37
+ const order = nextItemOrder((cur.data ?? []).map((i) => i.order));
38
+ const r = await db
39
+ .from('task_checklist_items')
40
+ .insert({ task_id: taskId, checklist_id: checklistId, text, checked: false, order })
41
+ .select('id')
42
+ .single();
43
+ fail('criar item', r.error);
44
+ return { id: r.data.id, order };
45
+ },
46
+ async removeChecklistItem(projectId, itemId) {
47
+ if (!(await itemTask(itemId, projectId)))
48
+ return null;
49
+ const r = await db.from('task_checklist_items').delete().eq('id', itemId).select('id');
50
+ fail('remover item', r.error);
51
+ return r.data ?? [];
52
+ },
53
+ async removeChecklist(projectId, checklistId) {
54
+ if (!(await listTask(checklistId, projectId)))
55
+ return null;
56
+ // itens saem junto: FK checklist_id ON DELETE CASCADE.
57
+ const r = await db.from('task_checklists').delete().eq('id', checklistId).select('id');
58
+ fail('remover checklist', r.error);
59
+ return r.data ?? [];
60
+ },
61
+ };
62
+ }
63
+ //# sourceMappingURL=checklist-gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checklist-gateway.js","sourceRoot":"","sources":["../../../src/adapters/supabase/checklist-gateway.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,11 @@ import { contextGateway } from './context-gateway.js';
2
2
  import { taskGateway } from './task-gateway.js';
3
3
  import { allocationGateway } from './allocation-gateway.js';
4
4
  import { analyticsGateway } from './analytics-gateway.js';
5
+ import { sprintGateway } from './sprint-gateway.js';
6
+ import { calendarGateway } from './calendar-gateway.js';
7
+ import { calendarWriteGateway } from './calendar-write-gateway.js';
8
+ import { releaseGateway } from './release-gateway.js';
9
+ import { checklistGateway } from './checklist-gateway.js';
5
10
  /**
6
11
  * Adaptador Supabase da `Gateway`, composto pelos gateways de domínio. Toda query
7
12
  * PostgREST vive nos módulos `*-gateway.ts` — este arquivo só junta as peças.
@@ -13,6 +18,11 @@ export function createSupabaseGateway(db) {
13
18
  ...taskGateway(db),
14
19
  ...allocationGateway(db),
15
20
  ...analyticsGateway(db),
21
+ ...sprintGateway(db),
22
+ ...calendarGateway(db),
23
+ ...calendarWriteGateway(db),
24
+ ...releaseGateway(db),
25
+ ...checklistGateway(db),
16
26
  };
17
27
  }
18
28
  //# sourceMappingURL=gateway.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gateway.js","sourceRoot":"","sources":["../../../src/adapters/supabase/gateway.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,EAAY;IAChD,OAAO;QACL,GAAG,cAAc,CAAC,EAAE,CAAC;QACrB,GAAG,WAAW,CAAC,EAAE,CAAC;QAClB,GAAG,iBAAiB,CAAC,EAAE,CAAC;QACxB,GAAG,gBAAgB,CAAC,EAAE,CAAC;KACxB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"gateway.js","sourceRoot":"","sources":["../../../src/adapters/supabase/gateway.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,EAAY;IAChD,OAAO;QACL,GAAG,cAAc,CAAC,EAAE,CAAC;QACrB,GAAG,WAAW,CAAC,EAAE,CAAC;QAClB,GAAG,iBAAiB,CAAC,EAAE,CAAC;QACxB,GAAG,gBAAgB,CAAC,EAAE,CAAC;QACvB,GAAG,aAAa,CAAC,EAAE,CAAC;QACpB,GAAG,eAAe,CAAC,EAAE,CAAC;QACtB,GAAG,oBAAoB,CAAC,EAAE,CAAC;QAC3B,GAAG,cAAc,CAAC,EAAE,CAAC;QACrB,GAAG,gBAAgB,CAAC,EAAE,CAAC;KACxB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,61 @@
1
+ import { FINISHED_FILTER } from '../../core/types.js';
2
+ const COLS = 'id, project_id, name, status, target_date, delivered_at';
3
+ /** Adaptador Supabase do `ReleaseGateway`. */
4
+ export function releaseGateway(db) {
5
+ return {
6
+ async listReleases(projectId, status) {
7
+ let q = db.from('releases').select(COLS).eq('project_id', projectId);
8
+ if (status)
9
+ q = q.eq('status', status);
10
+ const { data, error } = await q.order('target_date', { ascending: true });
11
+ if (error)
12
+ throw new Error(`Erro ao listar releases: ${error.message}`);
13
+ return data ?? [];
14
+ },
15
+ async findRelease(projectId, releaseId) {
16
+ const { data, error } = await db
17
+ .from('releases')
18
+ .select(COLS)
19
+ .eq('project_id', projectId)
20
+ .eq('id', releaseId)
21
+ .maybeSingle();
22
+ if (error)
23
+ throw new Error(`Erro ao buscar release: ${error.message}`);
24
+ return data ?? null;
25
+ },
26
+ async createRelease(r) {
27
+ const { data, error } = await db.from('releases').insert(r).select(COLS).single();
28
+ if (error)
29
+ throw new Error(`Erro ao criar release: ${error.message}`);
30
+ return data;
31
+ },
32
+ async updateRelease(releaseId, patch) {
33
+ const { data, error } = await db.from('releases').update(patch).eq('id', releaseId).select(COLS).single();
34
+ if (error)
35
+ throw new Error(`Erro ao atualizar release: ${error.message}`);
36
+ return data;
37
+ },
38
+ async finalizeRelease(releaseId, unlinkPending) {
39
+ // Desvincular é idempotente: roda antes, então retry após falha não deixa release delivered com pendentes.
40
+ if (unlinkPending) {
41
+ const { error } = await db
42
+ .from('tasks')
43
+ .update({ release_id: null, updated_at: new Date().toISOString() })
44
+ .eq('release_id', releaseId)
45
+ .not('status', 'in', FINISHED_FILTER);
46
+ if (error)
47
+ throw new Error(`Erro ao desvincular tasks pendentes: ${error.message}`);
48
+ }
49
+ const { data, error } = await db
50
+ .from('releases')
51
+ .update({ status: 'delivered', delivered_at: new Date().toISOString() })
52
+ .eq('id', releaseId)
53
+ .select(COLS)
54
+ .single();
55
+ if (error)
56
+ throw new Error(`Erro ao finalizar release: ${error.message}`);
57
+ return data;
58
+ },
59
+ };
60
+ }
61
+ //# sourceMappingURL=release-gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release-gateway.js","sourceRoot":"","sources":["../../../src/adapters/supabase/release-gateway.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,IAAI,GAAG,yDAAyD,CAAC;AAEvE,8CAA8C;AAC9C,MAAM,UAAU,cAAc,CAAC,EAAY;IACzC,OAAO;QACL,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM;YAClC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;YACrE,IAAI,MAAM;gBAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YACvC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1E,IAAI,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACxE,OAAO,IAAI,IAAI,EAAE,CAAC;QACpB,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,SAAS,EAAE,SAAS;YACpC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE;iBAC7B,IAAI,CAAC,UAAU,CAAC;iBAChB,MAAM,CAAC,IAAI,CAAC;iBACZ,EAAE,CAAC,YAAY,EAAE,SAAS,CAAC;iBAC3B,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC;iBACnB,WAAW,EAAE,CAAC;YACjB,IAAI,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACvE,OAAO,IAAI,IAAI,IAAI,CAAC;QACtB,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,CAAC;YACnB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;YAClF,IAAI,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACtE,OAAO,IAAI,CAAC;QACd,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,SAAS,EAAE,KAAK;YAClC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;YAC1G,IAAI,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC1E,OAAO,IAAI,CAAC;QACd,CAAC;QAED,KAAK,CAAC,eAAe,CAAC,SAAS,EAAE,aAAa;YAC5C,2GAA2G;YAC3G,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE;qBACvB,IAAI,CAAC,OAAO,CAAC;qBACb,MAAM,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;qBAClE,EAAE,CAAC,YAAY,EAAE,SAAS,CAAC;qBAC3B,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC;gBACxC,IAAI,KAAK;oBAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACtF,CAAC;YACD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE;iBAC7B,IAAI,CAAC,UAAU,CAAC;iBAChB,MAAM,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;iBACvE,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC;iBACnB,MAAM,CAAC,IAAI,CAAC;iBACZ,MAAM,EAAE,CAAC;YACZ,IAAI,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC1E,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,59 @@
1
+ const COLS = 'id, project_id, name, status, start_date, end_date';
2
+ /** Adaptador Supabase do `SprintGateway` (ciclo de vida de sprints). */
3
+ export function sprintGateway(db) {
4
+ return {
5
+ async listSprints(projectId) {
6
+ const { data, error } = await db
7
+ .from('sprints')
8
+ .select(COLS)
9
+ .eq('project_id', projectId)
10
+ .order('created_at', { ascending: false });
11
+ if (error)
12
+ throw new Error(`Erro ao listar sprints: ${error.message}`);
13
+ return data ?? [];
14
+ },
15
+ async findSprint(projectId, idOrActive) {
16
+ let q = db.from('sprints').select(COLS).eq('project_id', projectId);
17
+ q = idOrActive === 'active'
18
+ ? q.eq('status', 'ativa').order('created_at', { ascending: false }).limit(1)
19
+ : q.eq('id', idOrActive);
20
+ const { data, error } = await q.maybeSingle();
21
+ if (error)
22
+ throw new Error(`Erro ao buscar sprint: ${error.message}`);
23
+ return data ?? null;
24
+ },
25
+ async listSprintTasks(sprintId) {
26
+ const { data, error } = await db
27
+ .from('tasks')
28
+ .select('id, key, title, status, end_date')
29
+ .eq('sprint_id', sprintId);
30
+ if (error)
31
+ throw new Error(`Erro ao listar tasks da sprint: ${error.message}`);
32
+ return data ?? [];
33
+ },
34
+ async createSprint(projectId, name, startDate, endDate) {
35
+ const { data, error } = await db
36
+ .from('sprints')
37
+ .insert({ project_id: projectId, name, status: 'ativa', start_date: startDate, end_date: endDate })
38
+ .select(COLS)
39
+ .single();
40
+ if (error)
41
+ throw new Error(`Erro ao criar sprint: ${error.message}`);
42
+ return data;
43
+ },
44
+ async updateSprint(sprintId, patch) {
45
+ const { data, error } = await db.from('sprints').update(patch).eq('id', sprintId).select(COLS).single();
46
+ if (error)
47
+ throw new Error(`Erro ao atualizar sprint: ${error.message}`);
48
+ return data;
49
+ },
50
+ async returnTasksToBacklog(taskIds) {
51
+ if (taskIds.length === 0)
52
+ return;
53
+ const { error } = await db.from('tasks').update({ sprint_id: null, status: 'backlog' }).in('id', taskIds);
54
+ if (error)
55
+ throw new Error(`Erro ao devolver tasks ao backlog: ${error.message}`);
56
+ },
57
+ };
58
+ }
59
+ //# sourceMappingURL=sprint-gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sprint-gateway.js","sourceRoot":"","sources":["../../../src/adapters/supabase/sprint-gateway.ts"],"names":[],"mappings":"AAIA,MAAM,IAAI,GAAG,oDAAoD,CAAC;AAElE,wEAAwE;AACxE,MAAM,UAAU,aAAa,CAAC,EAAY;IACxC,OAAO;QACL,KAAK,CAAC,WAAW,CAAC,SAAiB;YACjC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE;iBAC7B,IAAI,CAAC,SAAS,CAAC;iBACf,MAAM,CAAC,IAAI,CAAC;iBACZ,EAAE,CAAC,YAAY,EAAE,SAAS,CAAC;iBAC3B,KAAK,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;YAC7C,IAAI,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACvE,OAAO,IAAI,IAAI,EAAE,CAAC;QACpB,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,SAAiB,EAAE,UAAkB;YACpD,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;YACpE,CAAC,GAAG,UAAU,KAAK,QAAQ;gBACzB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC5E,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAC3B,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YAC9C,IAAI,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACtE,OAAO,IAAI,IAAI,IAAI,CAAC;QACtB,CAAC;QAED,KAAK,CAAC,eAAe,CAAC,QAAgB;YACpC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE;iBAC7B,IAAI,CAAC,OAAO,CAAC;iBACb,MAAM,CAAC,kCAAkC,CAAC;iBAC1C,EAAE,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;YAC7B,IAAI,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC/E,OAAO,IAAI,IAAI,EAAE,CAAC;QACpB,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO;YACpD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE;iBAC7B,IAAI,CAAC,SAAS,CAAC;iBACf,MAAM,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;iBAClG,MAAM,CAAC,IAAI,CAAC;iBACZ,MAAM,EAAE,CAAC;YACZ,IAAI,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACrE,OAAO,IAAI,CAAC;QACd,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,KAAK;YAChC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;YACxG,IAAI,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACzE,OAAO,IAAI,CAAC;QACd,CAAC;QAED,KAAK,CAAC,oBAAoB,CAAC,OAAiB;YAC1C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YACjC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC1G,IAAI,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACpF,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -36,7 +36,9 @@ export function formatTaskList(tasks, assigneesByTask) {
36
36
  priority: t.priority,
37
37
  type: t.type,
38
38
  sprint_id: t.sprint_id,
39
+ start_date: t.start_date,
39
40
  end_date: t.end_date,
41
+ release_id: t.release_id,
40
42
  created_at: t.created_at,
41
43
  assignees: assigneesByTask.get(t.id) ?? [],
42
44
  }));
@@ -64,14 +66,10 @@ export function buildChecklistInsert(taskId, title, icon, order) {
64
66
  export function taskGateway(db) {
65
67
  return {
66
68
  async listTasks(filter) {
67
- const restrict = await resolveAssigneeFilter(db, filter.userId, filter.assignee);
68
69
  const sprint = await resolveSprintFilter(db, filter.projectId, filter.sprint);
69
70
  if (sprint.noActive)
70
71
  return { tasks: [], note: 'Não há sprint ativa neste projeto.' };
71
- // Short-circuit: filtro IN com array vazio → nada bate.
72
- if (restrict?.mode === 'in' && restrict.ids.length === 0)
73
- return { tasks: [] };
74
- const rows = await queryFilteredTasks(db, filter, restrict, sprint.filter);
72
+ const rows = await queryFilteredTasks(db, filter, sprint.filter);
75
73
  if (rows.length === 0)
76
74
  return { tasks: [] };
77
75
  const { assigneeRows, profilesById } = await loadAssigneeProfiles(db, rows.map((t) => t.id));
@@ -192,7 +190,7 @@ export function taskGateway(db) {
192
190
  const insertRes = await db
193
191
  .from('tasks')
194
192
  .insert(payload)
195
- .select('id, key, title, status, priority, type, sprint_id, end_date, created_at')
193
+ .select('id, key, title, status, priority, type, sprint_id, start_date, end_date, created_at')
196
194
  .single();
197
195
  if (insertRes.error)
198
196
  throw new Error(`Erro ao criar tarefa: ${insertRes.error.message}`);
@@ -210,7 +208,7 @@ export function taskGateway(db) {
210
208
  async getCurrentTask(taskId, projectId) {
211
209
  const res = await db
212
210
  .from('tasks')
213
- .select('id, title, description, status, priority, type, sprint_id, end_date, project_id')
211
+ .select('id, title, description, status, priority, type, sprint_id, release_id, start_date, end_date, project_id')
214
212
  .eq('id', taskId)
215
213
  .eq('project_id', projectId)
216
214
  .maybeSingle();
@@ -297,7 +295,7 @@ export function taskGateway(db) {
297
295
  async reloadTask(taskId) {
298
296
  const finalRes = await db
299
297
  .from('tasks')
300
- .select('id, key, title, status, priority, type, sprint_id, end_date, updated_at')
298
+ .select('id, key, title, status, priority, type, sprint_id, start_date, end_date, updated_at')
301
299
  .eq('id', taskId)
302
300
  .single();
303
301
  if (finalRes.error)
@@ -414,21 +412,6 @@ export function taskGateway(db) {
414
412
  },
415
413
  };
416
414
  }
417
- async function resolveAssigneeFilter(db, userId, assignee) {
418
- if (!assignee)
419
- return null;
420
- if (assignee === 'unassigned') {
421
- const sub = await db.from('task_assignees').select('task_id');
422
- if (sub.error)
423
- throw new Error(`Erro filtrando por assignee: ${sub.error.message}`);
424
- return { mode: 'not_in', ids: (sub.data ?? []).map((r) => r.task_id) };
425
- }
426
- const userFilter = assignee === 'me' ? userId : assignee;
427
- const sub = await db.from('task_assignees').select('task_id').eq('user_id', userFilter);
428
- if (sub.error)
429
- throw new Error(`Erro filtrando por assignee: ${sub.error.message}`);
430
- return { mode: 'in', ids: (sub.data ?? []).map((r) => r.task_id) };
431
- }
432
415
  async function resolveSprintFilter(db, projectId, sprint) {
433
416
  if (!sprint)
434
417
  return { noActive: false, filter: null };
@@ -449,15 +432,33 @@ async function resolveSprintFilter(db, projectId, sprint) {
449
432
  return { noActive: false, filter: { mode: 'is_null' } };
450
433
  return { noActive: false, filter: { mode: 'eq', value: sprint } };
451
434
  }
452
- async function queryFilteredTasks(db, filter, restrictIds, sprintFilter) {
453
- let query = db
454
- .from('tasks')
455
- .select('id, key, title, status, priority, type, sprint_id, end_date, created_at')
456
- .eq('project_id', filter.projectId);
457
- if (restrictIds?.mode === 'in')
458
- query = query.in('id', restrictIds.ids);
459
- else if (restrictIds?.mode === 'not_in' && restrictIds.ids.length > 0)
460
- query = query.not('id', 'in', `(${restrictIds.ids.join(',')})`);
435
+ function applyAssigneeFilter(query, filter) {
436
+ const { assignee } = filter;
437
+ if (assignee === 'unassigned')
438
+ return query.is('task_assignees', null);
439
+ if (assignee)
440
+ return query.eq('task_assignees.user_id', assignee === 'me' ? filter.userId : assignee);
441
+ return query;
442
+ }
443
+ /** Sobreposição: end_date >= from E coalesce(start_date, end_date) <= to (sem end_date não entra). */
444
+ function applyOverlapFilter(query, filter) {
445
+ let q = query;
446
+ if (filter.from)
447
+ q = q.gte('end_date', filter.from);
448
+ if (filter.to) {
449
+ q = q.not('end_date', 'is', null);
450
+ q = q.or(`start_date.lte.${filter.to},and(start_date.is.null,end_date.lte.${filter.to})`);
451
+ }
452
+ return q;
453
+ }
454
+ async function queryFilteredTasks(db, filter, sprintFilter) {
455
+ const cols = 'id, key, title, status, priority, type, sprint_id, start_date, end_date, release_id, created_at';
456
+ const { assignee } = filter;
457
+ // Join filtrado no PostgREST: evita mandar centenas de ids na URL (.in/.not.in).
458
+ const embed = !assignee ? '' : assignee === 'unassigned' ? ', task_assignees!left(user_id)' : ', task_assignees!inner(user_id)';
459
+ let query = db.from('tasks').select(`${cols}${embed}`).eq('project_id', filter.projectId);
460
+ query = applyAssigneeFilter(query, filter);
461
+ query = applyOverlapFilter(query, filter);
461
462
  if (sprintFilter?.mode === 'eq')
462
463
  query = query.eq('sprint_id', sprintFilter.value);
463
464
  else if (sprintFilter?.mode === 'is_null')
@@ -480,7 +481,9 @@ async function queryFilteredTasks(db, filter, restrictIds, sprintFilter) {
480
481
  const { data, error } = await query;
481
482
  if (error)
482
483
  throw new Error(`Erro ao listar tarefas: ${error.message}`);
483
- return data ?? [];
484
+ // O embed serve só de filtro: não vaza pro resultado.
485
+ const rows = (data ?? []);
486
+ return rows.map(({ task_assignees: _ta, ...row }) => row);
484
487
  }
485
488
  async function loadAssigneeProfiles(db, taskIds) {
486
489
  const assigneesRes = await db.from('task_assignees').select('task_id, user_id').in('task_id', taskIds);