Tasks: Portal del paciente de CitaClara (002)
Input: Design documents from /specs/002-portal-paciente/ (spec.md, plan.md, research.md, data-model.md, contracts/portal.md, quickstart.md)
Prerequisites: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/
Tests: La spec exige partición exhaustiva sin solapes, cero fugas entre fichas, rechazos sin mutación, tramo libre reutilizable y responsive 390/1440 (SC-001…SC-008) y la constitución VI exige test trazable por regla. Se incluyen tareas de test obligatorias.
Organization: Tasks are grouped by user story to enable independent implementation and testing of each story.
Format: [ID] [P?] [Story] Description
- [P]: Can run in parallel (different files, no dependencies)
- [Story]: Which user story this task belongs to (e.g., US1, US2, US3)
- Include exact file paths in descriptions
Path Conventions
- Monolito Next.js:
app/,lib/,components/,prisma/,tests/,e2e/en la raíz (tema5/citaclara/)
Phase 1: Setup (Shared Infrastructure)
Purpose: Verificación del punto de partida (sin migración en esta feature)
✓- [X] T001 Verificar esquema actual en prisma/schema.prisma (existen Clinica.telefono, Cita.cancelacionOrigen/canceladaEn; 002 no añade columnas) y semilla Eleva con teléfono en prisma/seed.ts
✓- [X] T002 [P] Verificar primitivas reutilizables en lib/ventanas.ts (capturarAhora, cancelacionEnPlazo), lib/tiempo.ts (formatearTramo), lib/identidad.ts (normalización/avisos), lib/sesion-paciente.ts (sesión 30 min) y lib/cancelacion.ts (ejecutarCancelarCita origen portal)
Phase 2: Foundational (Blocking Prerequisites)
Purpose: Proyección pura de lectura que bloquea todas las historias
⚠️ CRITICAL: No user story work can begin until this phase is complete
✓- [X] T003 Implementar clasificarCitasDePortal, tipos FilaPortal/VistaPortal/EstadoVisiblePortal en lib/portal.ts y ESQUEMA_CONFIRMAR_CANCELACION_PORTAL en lib/validacion.ts (FR-001/FR-002; partición exhaustiva sin solapes, orden proximas asc / anteriores desc, cancelable vía 006 FR-005, tramo con formatearTramo, cero importes FR-013)
✓- [X] T004 [P] Test unitario de partición/orden/bordes/etiquetas en tests/unit/test_portal.test.ts (enCurso inicio<=ahora<fin, inicio==ahora→enCurso, fin==ahora→anteriores, 24h00 dentro / 23h59 fuera, exhaustiva sin solapes, sin importes) — debe FALLAR antes de T003
Checkpoint: Foundation ready - user story implementation can now begin in parallel
Phase 3: User Story 3 - Entrar sin cuentas ni contraseñas (Priority: P1)
Goal: Acceso con correo+teléfono hacia pacienteId, sesión de 30 min con cierre instantáneo, denegación genérica sin fugas (incluido duplicado).
Independent Test: Entrar con credenciales de la semilla, con combinación incorrecta y con combinación duplicada; solo se ven las citas propias; caducidad y cierre exigen reidentificación.
Tests for User Story 3 ⚠️
NOTE: Write these tests FIRST, ensure they FAIL before implementation
✓- [X] T005 [US3] Test de integración de acceso/sesión en tests/integration/test_portal_paciente.test.ts (ok → GET /api/portal/citas 200 solo propias; incorrecto 401 sin citas; duplicado 409 sin citas; caducada 30 min → 401; cierre → token anterior 401 + cookie limpiada)
Implementation for User Story 3
✓- [X] T006 [US3] Implementar app/portal/acceso/page.tsx (formulario correo+teléfono → POST /api/portal/sesion existente 007, avisos únicos con teléfono, redirige a /portal/mis-citas) y app/portal/page.tsx (redirige a mis-citas/acceso según verificarSesionPortal con cookies())
✓- [X] T007 [US3] Implementar GET /api/portal/citas/route.ts en modo solo-sesión (verifica con verificarSesionPortal, renueva con renovarSesionPortal y reemite cookie, WHERE pacienteId, 401 SIN_SESION + limpieza si falla; nunca acepta pacienteId del cliente)
Checkpoint: At this point, User Story 3 should be fully functional and testable independently (puerta sin la cual US1/US2 no son verificables)
Phase 4: User Story 1 - Ver mis citas futuras y pasadas (Priority: P1) 🎯 MVP
Goal: Tres secciones En curso / Próximas / Anteriores con tramo, servicio, profesional y estado en lenguaje de paciente, responsive 390/1440, sin importes.
Independent Test: Ficha con historia y futuras → futuras como reservada venidera y pasadas con estado final, sin mezclas ni citas ajenas; ficha sin futuras → avisos «No tienes…» + historial.
Tests for User Story 1 ⚠️
✓- [X] T008 [US1] Test de integración de lectura clasificada en tests/integration/test_portal_paciente.test.ts (partición exacta de la ficha, orden asc/desc, fila canónica con tramo/servicio/profesional/estado, ficha vacía de futuras, cero citas ajenas; mismo fichero que T005: sin marcador [P])
✓- [X] T009 [P] [US1] Test e2e de visualización en e2e/portal-paciente.spec.ts (acceso → secciones visibles en 390 px y 1440 px, textos es-ES, cero importes, sin jerga)
Implementation for User Story 1
✓- [X] T010 [US1] Implementar app/portal/mis-citas/page.tsx (Server Component con cookies() + verificarSesionPortal, redirige a /portal/acceso sin sesión, consulta Prisma por pacienteId con profesional/servicio, clasifica con clasificarCitasDePortal(citas, capturarAhora()), muestra teléfono de la clínica y «Cerrar sesión»)
✓- [X] T011 [P] [US1] Implementar components/portal-cita.tsx (tarjeta accesible con tramo/servicio/profesional/estado, sección En curso resaltada sin botón, resto según cancelable; lenguaje de clínica, min-h-11, sin importes)
Checkpoint: At this point, User Story 3 AND User Story 1 should be fully functional and testable independently
Phase 5: User Story 2 - Cancelar una cita futura (Priority: P1)
Goal: Cancelar reservada en plazo con confirmación previa vía caso de uso único 005; fuera de plazo/final/ajena/doble se rechaza sin mutar y el tramo liberado es reutilizable por recepción.
Independent Test: Cita en plazo → cancelada, sale de Próximas, entra en Anteriores, tramo libre; resto de la matriz → rechazo sin mutación.
Tests for User Story 2 ⚠️
✓- [X] T012 [US2] Test de integración de cancelación del portal en tests/integration/test_portal_paciente.test.ts (en plazo aplica + tramo reutilizable con POST /api/citas; fuera de plazo sigue reservada; final/ajena sin mutar; doble PATCH idempotente; GET nunca escribe; mismo fichero que T005/T008: sin [P])
✓- [X] T013 [P] [US2] Test e2e de cancelación en e2e/portal-paciente.spec.ts (localizar → diálogo ¿Seguro…? Se liberará tu hueco → confirmar → sale de Próximas y entra en Anteriores; mismo fichero que T009: redactar en secuencia)
Implementation for User Story 2
✓- [X] T014 [US2] Implementar PATCH /api/portal/citas/[id]/cancelar/route.ts (sesión→pacienteIdVerificado, exige { confirmar: true }, llama a ejecutarCancelarCita origen portal, propaga catálogo 005 con sus estados; sin transición propia, sin pacienteId del cliente)
✓- [X] T015 [US2] Conectar confirmación y cancelación en components/portal-cita.tsx + app/portal/mis-citas/page.tsx (diálogo previo, PATCH portal, refresco a Anteriores, aviso Ya no se puede cancelar por internet; llama a la clínica al <tel> cuando no cancelable)
Checkpoint: All user stories should now be independently functional
Phase 6: Polish & Cross-Cutting Concerns
Purpose: Puertas de merge de la constitución y la spec
✓- [X] T016 [P] Validar quickstart.md de la 002 (tabla US3/US1/US2 + greps de unicidad)
✓- [X] T017 [P] Verificar unicidad por grep: transición solo en lib/cancelacion.ts, normalización solo en lib/identidad.ts, cero now()/CURRENT_TIMESTAMP fuera de capturarAhora(), cero importes en app/portal y lib/portal.ts
✓- [X] T018 [P] Pasar npm run typecheck, npm run lint, npm test (incluida la suite antisolape 001), npm run test:e2e -- portal-paciente y npm run comprobar:es en verde
Dependencies & Execution Order
Phase Dependencies
- Setup (Phase 1): No dependencies - can start immediately
- Foundational (Phase 2): Depends on Setup completion - BLOCKS all user stories
- User Stories (Phase 3+): All depend on Foundational phase completion
- US3 primero (es la puerta: sin ella US1/US2 no son verificables), luego US1 (MVP visible), luego US2
- Or sequentially in priority order (todas P1: US3 → US1 → US2)
- Polish (Final Phase): Depends on all desired user stories being complete
User Story Dependencies
- User Story 3 (P1): Can start after Foundational (Phase 2) - No dependencies on other stories (puerta obligada)
- User Story 1 (P1): Depends on Foundational + US3 (necesita sesión para leer solo lo propio)
- User Story 2 (P1): Depends on Foundational + US3 + US1 (necesita ver la cita y estar identificado para cancelarla)
Within Each User Story
- Tests (if included) MUST be written and FAIL before implementation
lib/portal.tsantes que rutas; rutas antes que páginas; páginas antes que cableado de confirmación- Story complete before moving to next priority
Parallel Opportunities
- T002 y T004 pueden ir en paralelo (distintos ficheros)
- T008 comparte
tests/integration/test_portal_paciente.test.tscon T005/T012: redactar en secuencia (sin [P] entre ellos) - T009/T013 comparten
e2e/portal-paciente.spec.ts: redactar en secuencia - T011 puede ir en paralelo con T010 (componente vs página, se integran en T015)
- T016, T017, T018 (polish) pueden ir en paralelo
Implementation Strategy
MVP First (US3 + US1)
- Complete Phase 1: Setup
- Complete Phase 2: Foundational (CRITICAL - blocks all stories)
- Complete US3 (acceso/sesión) → puerta verificable
- Complete US1 (ver citas) → STOP and VALIDATE: portal legible de punta a punta
- Deploy/demo if ready
Incremental Delivery
- Complete Setup + Foundational → Foundation ready
- Add US3 → Test independently → puerta lista
- Add US1 → Test independently → Deploy/Demo (MVP visible!)
- Add US2 → Test independently → Deploy/Demo (ahorro para Sonia)
- Each story adds value without breaking previous stories
Parallel Team Strategy
With multiple developers:
- Team completes Setup + Foundational together
- Once Foundational is done:
- Developer A: US3 (acceso/sesión + GET portal)
- Developer B: US1 (páginas + tarjeta, sobre el GET de A)
- Developer C: US2 (PATCH portal + confirmación, tras A/B)
- Stories complete and integrate independently
Notes
- [P] tasks = different files, no dependencies
- [Story] label maps task to specific user story for traceability
- Each user story should be independently completable and testable
- Verify tests fail before implementing
- Commit after each task or logical group
- Stop at any checkpoint to validate story independently
- Trazabilidad:
lib/portal.tsno muta estados (lareservadacon fin pasado siguereservadaen BD); la única escritura esejecutarCancelarCitaorigenportal; FR-013: ningún importe en el portal