Feature Specification: Portal del paciente de CitaClara
Feature Branch: 002-portal-paciente
Created: 2026-09-29
Status: Draft
Input: User description: "Portal del paciente. Los pacientes de la clínica quieren ver sus citas y cancelarlas sin llamar por teléfono (Sonia, recepción de Eleva, dedica "la mitad de la mañana" a esto). Alcance: un paciente accede a una página personal moderna donde ve sus citas futuras y pasadas y puede cancelar una cita futura. Interfaz limpia y responsive, pensada para móvil. Preguntas que la spec debe dejar decididas (formúlalas cerradas, para Sara): cómo accede el paciente sin crear cuentas ni contraseñas (las clínicas pequeñas no van a gestionar altas), hasta cuándo puede cancelar, y qué pasa con el hueco liberado. Datos: Cita casos reales de la semilla en los ejemplos. Fuera de alcance v1: reservar o mover citas online, pagos."
Clarifications
Sesión 2026-09-29 (decidido con Sara — Q1=A, Q2=A, Q3=A)
- Q1: ¿Cómo entra el paciente en su página personal, sin crear cuentas ni contraseñas? → A: con el correo electrónico + teléfono que ya figuran en su ficha, sin alta, sin contraseña y sin nada que la clínica tenga que gestionar.
- Q2: ¿Hasta cuándo puede el paciente cancelar su cita desde el portal? → A: hasta 24 horas antes del inicio; dentro de las últimas 24 horas el portal lo rechaza y remite al teléfono de la clínica.
- Q3: Cuando el paciente cancela online, ¿qué pasa con su hueco? → A: el tramo vuelve automáticamente a estar libre y la recepción puede reutilizarlo para una nueva reserva, igual que cuando cancela recepción en la 001.
Sesión 2026-09-30 (decidido — Q4=A, Q5=A, Q6=A, Q7=A, Q8=A)
- Q4: Una vez identificado el paciente, ¿cómo se mantiene su sesión? → A: cookie de sesión firmada con caducidad de 30 minutos de inactividad, renovada con cada uso, con acción visible «Cerrar sesión» que la invalida al instante. Sin «recordar este dispositivo» ni persistencia en el navegador más allá de esa cookie. Superada la caducidad, se vuelve a pedir correo y teléfono.
- Q5: La 001 admite duplicados de correo y de teléfono en las fichas. Si la combinación correo + teléfono coincide con más de una ficha, ¿cómo se desambigua? → A: el acceso se deniega con aviso genérico («Tienes varias fichas con esos datos; llama a la clínica») y se deriva al teléfono de la clínica. Nunca se entra y nunca se enumeran ni distinguen las fichas coincidentes.
- Q6: ¿Dónde va una cita
reservadaen curso (inicio ya pasado, fin todavía futuro)? → A: tercera sección «En curso», arriba de la página, marcada «en curso» y con su tramo resaltado. No es cancelable —el plazo de 24 h ha vencido— y muestra el aviso de llamar a la clínica. - Q7: El aviso de las 24 h remite «al teléfono de la clínica», pero la entidad Clínica del modelo de datos de la 001 no tiene ese campo. ¿De dónde sale? → A: se incorpora
telefonocomo campo obligatorio de la entidad Clínica (modelo de datos y semilla de la 001) y el aviso lo muestra. Es una enmienda de la 001, registrada abajo. - Q8: ¿Se muestra el precio del servicio en la página del paciente en v1? → A: no. El portal v1 no muestra ningún importe; FR-013 queda como regla de guarda para el futuro, no como funcionalidad.
User Scenarios & Testing
User Story 1 — Ver mis citas futuras y pasadas (Priority: P1)
Una paciente (p. ej. Ana García López, ficha de la semilla de Eleva) entra en su página personal desde el móvil y ve, separadas y ordenadas, sus citas futuras (las que aún tiene reservadas) y sus citas pasadas (completadas, canceladas y no asistidas), cada una con día, hora, servicio, profesional y estado, para no tener que llamar a Sonia para preguntar «¿cuándo tengo la próxima?».
Why this priority: Es el dolor directo («la mitad de la mañana» de Sonia al teléfono) y la base de todo lo demás: sin ver sus citas, el paciente no puede decidir qué cancelar. Aporta valor por sí sola aunque aún no exista la cancelación.
Independent Test: Se puede probar por completo entrando como un paciente de la semilla que tenga historia y reservas futuras (p. ej. cualquier paciente con citas en las 8 semanas de historia y en las 2 semanas futuras) y comprobando que las futuras aparecen como reservada con fecha venidera y las pasadas con su estado final, sin mezclas ni citas de otros pacientes.
Acceptance Scenarios:
- Given la paciente Ana García López tiene una «sesión fisio» (45 min, 40,00 €) con María reservada el 08/10/2026 de 10:00 a 10:45, When abre su página personal, Then ve esa cita en «Próximas citas» con «08/10/2026, 10:00–10:45 · sesión fisio · María · reservada».
- Given el mismo paciente tiene una «consulta nutrición» (30 min, 35,00 €) con Lucía ya completada el 10/09/2026 de 12:00 a 12:30, When baja a «Citas anteriores», Then ve esa cita con «10/09/2026, 12:00–12:30 · consulta nutrición · Lucía · completada», separada de las futuras.
- Given un paciente sin ninguna cita futura (solo historia), When abre su página, Then ve «No tienes citas en curso» y «No tienes próximas citas» y sí su historial, sin errores y sin ver citas de nadie más.
- Given la página personal abierta en un móvil (390 px) y en un portátil (1440 px), When se comparan ambas, Then en las dos la información es legible sin manual, sin jerga técnica, con contraste y tamaño accesibles.
User Story 2 — Cancelar una cita futura (Priority: P1)
El paciente, viendo sus próximas citas, cancela la que no va a poder usar (p. ej. la «sesión fisio» del 08/10/2026 con María), confirma la acción y la cita pasa a cancelada; la página lo refleja de inmediato para que no llame por teléfono.
Why this priority: Es el ahorro directo para Sonia y para el paciente; convierte una llamada de media mañana en dos toques. Depende de US1 (necesita ver la cita para cancelarla) pero se prueba y entrega como incremento propio.
Independent Test: Se puede probar por completo tomando una cita futura en estado reservada de un paciente de la semilla, cancelándola desde el portal (con confirmación previa) y comprobando que cambia a cancelada, desaparece de «Próximas» y aparece en «Anteriores», y que el tramo queda libre y reutilizable por recepción.
Acceptance Scenarios:
- Given una cita futura en estado
reservadacon más de 24 horas de antelación (p. ej. hoy + 5 días, «primera visita fisio» 60 min con Jorge), When el paciente pulsa «Cancelar», confirma en el diálogo y el sistema registra la cancelación, Then la cita pasa acancelada, deja «Próximas citas», aparece en «Anteriores» como cancelada y el tramo vuelve a estar libre para recepción. - Given una cita futura en estado
reservadadentro de las últimas 24 horas, When el paciente intenta cancelarla desde el portal, Then el portal la rechaza con un aviso claro en español («Ya no se puede cancelar por internet; llama a la clínica, por favor») y la cita siguereservada. - Given una cita ya pasada o ya en estado final (
completada,cancelada,no_asistida), When el paciente la visualiza, Then no se le ofrece ningún botón de cancelar y cualquier intento directo se rechaza sin cambiar el estado. - Given una cita futura
reservadaque el paciente cancela dos veces seguidas (doble clic o reintento), When llega la segunda petición, Then solo la primera surte efecto; la segunda se rechaza como «ya cancelada» sin duplicar efectos ni errores.
User Story 3 — Entrar sin cuentas ni contraseñas (Priority: P1)
El paciente entra en su página personal identificándose con lo que la clínica ya sabe de él (correo electrónico + teléfono de su ficha), sin registrarse, sin contraseña y sin que la clínica gestione altas; si se equivoca, ve un aviso genérico que no revela datos de otros pacientes.
Why this priority: Es la puerta de entrada obligada: sin ella no hay portal. Era P2 «porque no aporta valor asistencial por sí sola», pero US1 y US2 son P1 y sus pruebas empiezan literalmente «entrando como un paciente», así que ninguna era verificable sin ella. Se eleva a P1: es prerrequisito, no valor por sí mismo.
Independent Test: Se puede probar por completo entrando con el correo + teléfono correctos de un paciente de la semilla, entrando con una combinación incorrecta y comprobando que solo se ven las citas del paciente identificado y nunca las de otro.
Acceptance Scenarios:
- Given la pantalla de acceso del portal, When la paciente introduce su correo y teléfono correctos (p. ej.
ana.garcia.lopez@correo.es+ su teléfono de la semilla), Then entra en su página personal y solo ve sus propias citas. - Given la pantalla de acceso del portal, When introduce una combinación incorrecta o inexistente, Then no entra y ve un aviso genérico en español («No hemos encontrado esos datos; revísalos o llama a la clínica»), sin revelar si el correo o el teléfono existen.
- Given la pantalla de acceso del portal, When introduce un correo y un teléfono que coinciden a la vez con más de una ficha de la semilla, Then no entra y ve un aviso genérico («Tienes varias fichas con esos datos; llama a la clínica») que no enumera ni distingue las fichas.
- Given un paciente dentro de su sesión, When pasan 30 minutos sin actividad, Then la sesión caduca y la siguiente petición exige de nuevo correo y teléfono; con «Cerrar sesión» el acceso se cierra al instante.
Edge Cases
- ¿Qué pasa si una cita está «en curso» (p. ej. de 10:00 a 10:45, abierta a las 10:20)? Va a su propia sección «En curso», arriba y con el tramo resaltado; no ofrece «Cancelar» porque el plazo de 24 h ha vencido, y muestra el aviso con el teléfono de la clínica. Si termina mientras el paciente mira, en la siguiente carga pasa a «Citas anteriores» como
reservadacon fin pasado. - ¿Qué pasa si el paciente tiene dos fichas con el mismo nombre (duplicados admitidos en la 001)? Cada ficha ve solo sus propias citas; nunca se mezclan citas de fichas distintas aunque el nombre coincida.
- ¿Qué pasa si dos fichas comparten correo y teléfono exactos? El acceso se deniega con aviso genérico y reenvío al teléfono de la clínica: es la única forma de no filtrar citas entre fichas sin pedirle nada a la clínica (Q5).
- ¿Qué pasa si el paciente teclea el correo o el teléfono con mayúsculas, espacios o el prefijo
+34? La comparación se normaliza de forma documentada en el plan y una variante equivalente debe dar el mismo resultado que la ficha; un dato que no se puede normalizar a esa variante se trata como incorrecto. - ¿Qué pasa si la cita termina exactamente cuando empieza otra del mismo profesional? No afecta al portal: la cancelación libera exactamente su tramo
[inicio, fin)y no toca los tramos contiguos. - ¿Qué pasa si el paciente intenta cancelar una cita que recepción acaba de marcar como
completadaono_asistida? Se rechaza: desde un estado final no hay transición posible, ni desde recepción ni desde el portal. - ¿Qué pasa si dos cancelaciones de la misma cita llegan a la vez (doble clic)? Solo una surte efecto; la otra se rechaza como «ya cancelada» (idempotencia de la cancelación).
- ¿Qué pasa si el reloj del paciente y el del sistema difieren al validar las 24 horas? La comparación usa un único instante de referencia del sistema («ahora»); una cita cuyo inicio menos «ahora» sea estrictamente menor de 24 horas se considera fuera de plazo.
- ¿Qué pasa si el nombre del paciente o del servicio tiene tildes o ñ (p. ej. «Lucía», «consulta nutrición»)? Se muestran tal cual, sin alteraciones.
- ¿Qué pasa si el paciente abre el enlace o la página en un móvil antiguo con pantalla estrecha? La página sigue siendo legible y el botón de cancelar sigue siendo pulsable sin zoom (responsive 390 px).
- ¿Qué pasa si el paciente intenta «reservar» o «mover» una cita desde el portal? No existe esa acción en v1: la página no la ofrece y cualquier intento directo se rechaza (fuera de alcance).
Requirements
Functional Requirements
- FR-001: El sistema MUST ofrecer una página personal por paciente donde, tras identificarse, el paciente ve sus citas separadas en tres secciones: «En curso» (estado
reservadacon inicio ya pasado y fin todavía futuro), «Próximas citas» (estadoreservadacon inicio futuro) y «Citas anteriores» (estadoscompletada,canceladayno_asistida, más cualquierreservadacuyo fin ya sea pasado). Esta clasificación es una proyección de lectura: no crea estados nuevos, solo proyecta el estadoreservadade la 001 (ver 001 FR-011). «En curso» se muestra arriba de la página; «Próximas citas» se ordena de más cercana a más lejana y «Citas anteriores» de más reciente a más antigua. La clasificación MUST ser exhaustiva y sin solapes: cada cita de la ficha aparece en exactamente una sección. - FR-002: Cada cita visible MUST mostrar al menos día, mes, año, hora de inicio y fin (p. ej. «08/10/2026, 10:00–10:45»), nombre del servicio, nombre del profesional y estado con palabra de paciente («reservada», «en curso», «completada», «cancelada», «no asistida»), sin jerga técnica.
- FR-003: El acceso del paciente MUST NOT exigir crear cuentas, altas ni contraseñas, ni gestión alguna por parte de la clínica. El sistema MUST identificar al paciente mediante el correo electrónico y el teléfono que ya figuran en su ficha existente; no se crea ningún dato nuevo para poder entrar. Identificado, el sistema MUST mantener la sesión mediante cookie firmada con caducidad de 30 minutos de inactividad, renovada con cada uso, con acción visible «Cerrar sesión» que la invalida de inmediato; el sistema MUST NOT persistir la identificación en el navegador más allá de esa cookie ni ofrecer «recordar este dispositivo». Superada la caducidad o tras «Cerrar sesión», el sistema MUST exigir de nuevo correo y teléfono.
- FR-004: Con datos de acceso incorrectos el sistema MUST denegar la entrada con un aviso genérico en español de España («No hemos encontrado esos datos; revísalos o llama a la clínica»), MUST NOT revelar si un correo o teléfono existe y MUST NOT mostrar ninguna cita. Si la combinación correo + teléfono coincide con más de una ficha, el sistema MUST denegar igualmente el acceso con un aviso genérico («Tienes varias fichas con esos datos; llama a la clínica») que MUST NOT enumerar ni distinguir las fichas coincidentes.
- FR-005: Un paciente identificado MUST ver exclusivamente las citas de su propia ficha durante toda la sesión; el sistema MUST NOT mostrar jamás citas de otra ficha, aunque haya nombres, correos o teléfonos duplicados, y MUST NOT ofrecer ninguna forma de enumerar, buscar ni cambiar de ficha.
- FR-006: El sistema MUST ofrecer la acción «Cancelar» exclusivamente sobre citas futuras en estado
reservadade ese paciente, con confirmación explícita previa («¿Seguro que quieres cancelar esta cita?») y texto del coste de la acción («Se liberará tu hueco»). Las citas de «En curso» MUST NOT ofrecer esa acción (el plazo de 24 h ha vencido) y MUST mostrar el aviso de llamar a la clínica de FR-008. - FR-007: Al confirmar, la cita MUST pasar de
reservadaacanceladaejecutando el caso de uso únicocancelarCitade la 001 (001 FR-011; detalle compartido enspecs/005-cancelacion-paciente/spec.md): mismas reglas de estados, antelación de 24 h, idempotencia y liberación del tramo. Esta feature MUST NOT implementar su propia transición. MUST rechazarse cualquier cancelación sobre citas en estado final o sobre citas de otro paciente. La doble cancelación MUST ser idempotente (la segunda se rechaza como «ya cancelada»). - FR-008 (antelación): El sistema MUST rechazar con aviso claro en español la cancelación desde el portal de toda cita cuyo inicio esté a menos de 24 horas del instante de referencia «ahora», remitiendo al teléfono de la clínica (campo
Clínica.telefono, firmeza en el aviso); la cita MUST seguirreservada. El límite es de 24 horas exactas: una cita que empieza dentro de 24 h sigue bloqueada, y a las 24 h justas o más ya se puede cancelar. - FR-009 (hueco liberado): Toda cita cancelada desde el portal MUST liberar su tramo
[inicio, fin), que vuelve a estar disponible para nuevas reservas de recepción sin violar RN1; el sistema MUST NOT dejar el tramo bloqueado ni pendiente de revisión manual. Una citacanceladano bloquea nuevos solapes, igual que en la 001. - FR-010: La página personal MUST ser moderna, limpia y responsive, pensada para móvil primero (legible y operable a 390 px sin zoom ni desplazamiento horizontal) y correcta a 1440 px, con contraste y tamaños de letra accesibles y lenguaje de clínica pequeña.
- FR-011: Todos los textos visibles, avisos y errores MUST estar en español de España.
- FR-012 (exactitud — tiempo): Toda fecha y hora visible MUST mostrarse sin ambigüedad (día, mes, año, hora y minutos, p. ej. «08/10/2026, 10:00–10:45»); la antelación de 24 horas se computa con precisión de minuto sobre el instante «ahora» del sistema. Ejemplo límite: una «sesión fisio» de 45 min que empieza a las 10:00 termina a las 10:45 del mismo día.
- FR-013 (exactitud — dinero): En v1 el portal MUST NOT mostrar ningún importe —ni precio de servicio, ni total, ni pendiente— y MUST NOT cobrar. Si en el futuro se mostrara algún importe de referencia, MUST ser el precio congelado de la cita (001 FR-006) y MUST cuadrar al céntimo en euros con dos decimales y símbolo € (p. ej. «40,00 €»). Queda como regla de guarda, no como funcionalidad de v1.
- FR-014 (semilla determinista): Los ejemplos y las pruebas MUST usar exclusivamente datos reproducibles de la semilla v1 de Eleva (3 profesionales: María y Jorge de fisioterapia, Lucía de nutrición; 4 servicios: «sesión fisio» 45 min 40,00 €, «primera visita fisio» 60 min 50,00 €, «consulta nutrición» 30 min 35,00 €, «primera nutrición» 45 min 45,00 €; ~40 pacientes; 8 semanas de historia y 2 futuras). Ejemplo canónico, suponiendo la semilla cargada el 29/09/2026: Ana García López, «sesión fisio» con María, jueves 08/10/2026 10:00–10:45, dentro de la ventana de 2 semanas futuras. Toda fecha absoluta de los ejemplos MUST caer dentro de esa ventana; si la fecha de carga de la semilla cambia, los ejemplos MUST re-datarse en consecuencia.
- FR-015 (alcance v1): Quedan fuera de v1 y MUST NOT implementarse en esta feature: reservar citas online, mover o cambiar citas online, pagos online y recordatorios (van en specs propias). El portal es de solo lectura más cancelación.
- FR-016 (dependencia de la 001): El aviso de las 24 h y la sección «En curso» MUST mostrar el teléfono de la clínica, tomado del campo obligatorio
telefonode la entidad Clínica (enmienda S-01 aceptada en la 001; ver 001 FR-001). La normalización de correo y teléfono para identificar al paciente es la compartida despecs/007-identidad-paciente/spec.md; esta feature MUST NOT definir la suya propia.
Key Entities
- Página personal del paciente: Vista privada de una ficha de paciente existente (nombre, teléfono, correo de la 001); solo muestra las citas cuya
pacienteIdcoincide con la ficha identificada mediante correo + teléfono. Sin cuentas, contraseñas ni enlaces que la clínica deba entregar. - Sesión del paciente: Estado de acceso del portal, sostenido por cookie firmada, con 30 minutos de caducidad por inactividad renovada con cada uso. Cierra sola por inactividad o de inmediato con «Cerrar sesión». No persiste la identificación más allá de la cookie y no permite cambiar de ficha sin volver a identificarse.
- Vista «En curso» / «Próximas» / «Anteriores»: Proyección de lectura sobre Cita (profesional, servicio, inicio, fin, estado). «En curso» =
reservadacon inicio pasado y fin futuro, arriba y no cancelable; «Próximas» =reservadacon inicio futuro; «Anteriores» = estados finales yreservadacon fin pasado. Cada fila muestra tramo, servicio, profesional y estado en lenguaje de paciente; ningún importe. - Cancelación del paciente: Transición
reservada→canceladainiciada por el propio paciente desde su página, con confirmación previa, con un límite de antelación de 24 horas y con liberación inmediata del tramo. Reutiliza la regla de estados de la 001;no_asistidasigue siendo exclusiva de recepción.
Success Criteria
Measurable Outcomes
- SC-001: Un paciente con datos de la semilla encuentra su próxima cita (servicio, profesional, día y hora) en menos de 30 segundos desde que entra, sin ayuda y sin llamar. Medición: protocolo manual con cronómetro sobre móvil 390 px; no es aserción automática de tiempo.
- SC-002: Un paciente cancela una cita futura con antelación suficiente en menos de 1 minuto (entrar, localizar, confirmar) y la cita queda
canceladay visible en «Anteriores». Medición: protocolo manual con cronómetro; no es aserción automática de tiempo. - SC-003: El 100 % de los intentos de cancelar citas ajenas, pasadas o ya finales se rechaza sin cambiar ningún estado; ningún paciente ve jamás citas de otra ficha. Cero fugas entre fichas en cualquier verificación.
- SC-004: El 100 % de los intentos de cancelar dentro de las últimas 24 horas se rechaza con aviso claro y la cita sigue
reservada. - SC-005: El 100 % de las citas canceladas desde el portal libera su tramo: recepción puede reservar ese mismo tramo para el mismo profesional sin error de solape, y la suite antisolape de la 001 sigue en verde.
- SC-006: La página es legible y operable en móvil (390 px) y portátil (1440 px), y todos los textos y fechas (p. ej. «08/10/2026, 10:00–10:45») aparecen en español de España. Al no mostrarse importes en v1, laExactitud del dinero se verifica en la 001 y FR-013 queda como regla de guarda.
- SC-007: El 100 % de los intentos de acceso con una combinación correo + teléfono que coincida con más de una ficha se deniega con aviso genérico; ninguna cita de ninguna de esas fichas llega a mostrarse.
- SC-008: Toda petición al portal con sesión caducada (30 min sin actividad) o cerrada («Cerrar sesión») se rechaza y exige reidentificación; tras reidentificarse el paciente recupera exactamente sus mismas citas, sin datos residuales de la sesión anterior en el navegador.
Assumptions
- Decisiones cerradas de Sara (2026-09-29) incorporadas a la spec: acceso con correo + teléfono de la ficha (sin cuentas ni contraseñas), cancelación permitida hasta 24 horas antes del inicio, y hueco liberado y reutilizable por recepción de inmediato.
- Decisiones cerradas (2026-09-30) incorporadas a la spec: sesión por cookie firmada de 30 min de inactividad con «Cerrar sesión» y sin persistencia en el navegador; denegación de acceso cuando la combinación correo + teléfono coincide con más de una ficha; tercera sección «En curso» para citas
reservadaen curso, no cancelable;Clínica.telefonocomo campo obligatorio; y ningún importe visible en v1. - Enmienda aceptada en la 001 (Principio I, S-01 jul2026):
telefonocomo campo obligatorio de la entidad Clínica —modelo de datos (data-model.md) y semilla («Eleva»)— porque FR-008 y FR-016 de esta spec lo necesitan. Ver 001 FR-001. - El portal trabaja sobre el mismo modelo de la 001 (Clínica Eleva, fichas con nombre + teléfono + correo, citas con inicio/fin y cuatro estados); no crea clínicas, profesionales, servicios ni pacientes nuevos.
- «Ahora» es el instante único del sistema en el momento de validar; la antelación se computa como
inicio − ahoracon precisión de minuto. - Zona horaria: hora local de la clínica en España (Europe/Madrid, referencia compartida en
specs/006-tiempo-referencia/spec.md); formato día/mes/año y 24 h. - Pagos, reservas y cambios online y recordatorios van en specs propias; este portal no los anticipa ni los bloquea.
- La autenticación de recepción con clave de clínica (001) no se ve afectada; el acceso del paciente es un camino separado sin clave de panel.