Contrato UI — Agenda del día (US1 + VII/VIII)

Ruta: app/(panel)/agenda. Componente raíz <AgendaDia profesionalId fecha /> (Server Component que pide GET /api/agenda).

Props y estados

  • Props: { profesionalId: string; fecha: string /* YYYY-MM-DD */ }.
  • Estados: cargando (esqueleto) · lista (citas + huecos) · vacia (La jornada está libre) · error (aviso es-ES + reintentar).
  • Cada tarjeta de cita muestra: tramoTexto (10:00–10:45), servicio, paciente y Estado (Reservada/Completada/Cancelada/No asistida) — sin jerga, en es-ES.
  • Acciones por tarjeta reservada: Completar · Cancelar · No se presentó (confirman y llaman PATCH /api/citas/[id]/estado en <15 s — SC-004).
  • Alta: botón Nueva cita → formulario con profesional/servicio/paciente/día/hora; muestra fin calculado y errores HUECO_OCUPADO/PASADO tal cual devuelve la API.

Accesibilidad y responsive (VII)

  • Contraste AA, foco visible, letra base ≥16px, toques ≥44px; lang="es-ES".
  • Portátil 1440px: columna agenda + lateral de detalle. Móvil 390px: lista apilada, acciones a pulgar, sin scroll horizontal.
  • Importes siempre 40,00 €; fechas siempre 05/10/2026, 10:00–10:45.

Glosario código ↔ etiqueta (es-ES)

Código Etiqueta UI
reservada Reservada
completada Completada
cancelada Cancelada
no_asistida No asistida (botón de acción: No se presentó)

Trazabilidad e2e (Playwright)

  • agenda-dia.spec.ts: María con citas ⇒ tramos ocupados + resto libre; Jorge sin citas ⇒ La jornada está libre; 1440px y 390px legibles.
  • alta-cita.spec.ts: válida <60 s; solapada ⇒ Ese tramo ya está ocupado; pasada ⇒ No se pueden crear citas en el pasado.
  • desenlace.spec.ts: cada final <15 s; ilegal ⇒ aviso; cancelada libera hueco.
  • acceso-semilla.spec.ts: clave ok/ko, semilla Eleva idéntica tras reset.