9c5e76e03f
Anade tres capas sobre el reporte diario del issue 0093:
1) Bocadillo del agente: cuadro azul encima de "Tareas hechas" con un
resumen en lenguaje natural (max 4 frases) generado por claude -p
sobre el JSON del reporte. Botones Regenerar e icono Settings.
2) Settings del prompt: modal con textarea editable para el template
del agente (key=daily_report_prompt). Compartido por todos los
usuarios. Boton Restablecer por defecto.
3) PDF descargable: boton que abre ventana nueva con HTML imprimible
(estilo A4, KPIs filtrados, tabla con enlaces absolutos por card).
Permite compartir el listado de tareas hechas con los solicitantes.
Backend:
- Migration 013 anade tablas daily_summaries y settings; seed del
prompt por defecto en castellano.
- daily_summary.go con GetSetting/SetSetting, GetDailySummary/Upsert,
runClaudePrompt (envuelve claude -p) y GenerateDailySummary que
orquesta DailyReportFor + plantilla + claude + persist.
- Nuevos endpoints:
* GET /api/reports/daily/summary
* POST /api/reports/daily/summary
* GET /api/settings/{key}
* PUT /api/settings/{key}
Frontend:
- api.ts: getDailySummary, generateDailySummary, getSetting, setSetting.
- DailyReport.tsx: estado de summary, settingsOpen, promptDraft,
filterRequester, filterAssignee, filteredDoneCards, exportPDF.
- Bocadillo con IconSparkles + IconRefresh + IconSettings.
- Modal de prompt con Guardar/Cancelar/Reset.
- Filtros Select por solicitante y asignado encima de la tabla.
- exportPDF abre window.open con HTML self-contained que incluye
enlaces ${origin}/?card=${id} y window.print() automatico.
E2E nuevo (daily-summary-pdf.spec.ts): CRUD del setting, GET summary
shape, presencia del boton PDF/Settings/Regenerar en el modal. No
invoca claude real (binario externo, no disponible en CI).
Suite completa 11/11 pasa.
57 lines
2.6 KiB
TypeScript
57 lines
2.6 KiB
TypeScript
import { test, expect } from "@playwright/test";
|
|
import { pw_kanban_login } from "../../../../frontend/functions/browser/pw_kanban_login";
|
|
|
|
const USER = process.env.KANBAN_USER || "e2e_user";
|
|
const PWD = process.env.KANBAN_PWD || "e2e_test_pw_2026";
|
|
|
|
/**
|
|
* Issue 0094: bocadillo del agente + settings de prompt + PDF.
|
|
* No invocamos claude binario; testeamos endpoints settings y la UI estatica.
|
|
*/
|
|
test.describe("daily summary + pdf (issue 0094)", () => {
|
|
test("settings prompt CRUD roundtrip", async ({ page }) => {
|
|
await page.goto("/");
|
|
await pw_kanban_login(page, { username: USER, password: PWD });
|
|
|
|
// Lectura inicial: existe seed.
|
|
const initial = await page.request.get("/api/settings/daily_report_prompt").then((r) => r.json());
|
|
expect(initial.value).toContain("MAXIMO");
|
|
|
|
// Cambio.
|
|
const newVal = "test prompt " + Date.now();
|
|
const put = await page.request.put("/api/settings/daily_report_prompt", { data: { value: newVal } });
|
|
expect([200, 204]).toContain(put.status());
|
|
|
|
// Verifica.
|
|
const after = await page.request.get("/api/settings/daily_report_prompt").then((r) => r.json());
|
|
expect(after.value).toBe(newVal);
|
|
|
|
// Restaurar.
|
|
await page.request.put("/api/settings/daily_report_prompt", { data: { value: initial.value } });
|
|
});
|
|
|
|
test("daily summary GET vacio inicialmente, persiste si guardas manualmente", async ({ page }) => {
|
|
await page.goto("/");
|
|
await pw_kanban_login(page, { username: USER, password: PWD });
|
|
const today = new Date().toISOString().slice(0, 10);
|
|
const before = await page.request.get(`/api/reports/daily/summary?date=${today}`).then((r) => r.json());
|
|
// Either exists=false OR exists=true with a string summary. Both valid.
|
|
expect(typeof before.exists).toBe("boolean");
|
|
expect(typeof before.summary === "string").toBe(true);
|
|
});
|
|
|
|
test("UI: bocadillo + boton PDF + boton settings visibles en modal", async ({ page }) => {
|
|
await page.goto("/");
|
|
await pw_kanban_login(page, { username: USER, password: PWD });
|
|
await page.getByRole("tab", { name: /Calendario/i }).click();
|
|
await page.waitForSelector('[data-test^="calendar-day-"]', { timeout: 5000 });
|
|
await page.locator('[data-test^="calendar-day-"]').first().dispatchEvent("click");
|
|
|
|
const modal = page.locator('[role="dialog"]').filter({ hasText: /Reporte diario/i });
|
|
await expect(modal).toBeVisible();
|
|
await expect(modal.locator('[data-test="daily-report-pdf"]')).toBeVisible();
|
|
await expect(modal.getByRole("button", { name: /Configurar prompt/i })).toBeVisible();
|
|
await expect(modal.getByRole("button", { name: /Regenerar|Generar/i }).first()).toBeVisible();
|
|
});
|
|
});
|