Tasks: Portal del paciente de CitaClara (002)

Task list template for feature implementation

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.ts antes 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.ts con 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)

  1. Complete Phase 1: Setup
  2. Complete Phase 2: Foundational (CRITICAL - blocks all stories)
  3. Complete US3 (acceso/sesión) → puerta verificable
  4. Complete US1 (ver citas) → STOP and VALIDATE: portal legible de punta a punta
  5. Deploy/demo if ready

Incremental Delivery

  1. Complete Setup + Foundational → Foundation ready
  2. Add US3 → Test independently → puerta lista
  3. Add US1 → Test independently → Deploy/Demo (MVP visible!)
  4. Add US2 → Test independently → Deploy/Demo (ahorro para Sonia)
  5. Each story adds value without breaking previous stories

Parallel Team Strategy

With multiple developers:

  1. Team completes Setup + Foundational together
  2. 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)
  3. 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.ts no muta estados (la reservada con fin pasado sigue reservada en BD); la única escritura es ejecutarCancelarCita origen portal; FR-013: ningún importe en el portal