00-day-brief.md
00 — Day brief · 2026-08-15 · RONDA
Decisiones ALS-2 BRIEF
| Campo | Valor |
|---|---|
| Fecha | 2026-08-15 |
| Nombre | RONDA |
| Complejidad | Nivel 2 (no L1 consecutivo tras DERIVA) |
| Sector | Gaming / comunidad de mesa local |
| Tipo | Feed de mesas + RSVP + inbox HOST JWT |
| Plataforma | Web responsive (feed + dock · desktop anfitrión) |
| Registro | R-HY — flyer de caja de juego (clásico) + dock flotante (innovador usable) |
| Shell | S-DOCK — dock flotante Hoy · Mesas · Publicar · Inbox · Yo |
| Home | H-FEED — cronología de mesas de esta noche / esta semana |
| Flujo | F-BOOK — asientos de una mesa; solicitud ≠ plaza |
| Por qué no DERIVA | No S-SPLIT / H-MAP / F-MAP; no turismo; no plano |
| Por qué no PIZARRA | No S-CMD / H-SEARCH / F-SEARCH; no catálogo municipal |
Terna unicidad
R-HY · S-DOCK · H-FEED · F-BOOK
Cumple memory §6.5: 0 códigos iguales a N−1 (DERIVA) y ≥2 distintos vs N−2 (PIZARRA). Ventana de 4 días ya tenía R-IN y R-CL; hoy R-HY. Experimento §8: feed, no mapa ni search.
Directivas aplicadas
- D-P0-01…09, D-P1-01 (≥12 UX), D-P0-08 (≥10 UI), D-P1-05 JWT HOST
- Tipografía Zilla Slab + Mulish (evitar Lora, Karla, Newsreader, Atkinson, Outfit, Archivo, Fraunces)
- Paleta signage — blanco / tinta / amarillo cromo / felt / liner. No dusk, no chalky, no slate/teal, no limestone, no wine
- Home no es hero 2-col ni mapa (AP-12). Superficie = feed + dock
Alcance L2 must
- Home feed cronológico de 5 mesas (foto, juego, hora, local, asientos)
- Ficha de mesa (reglas cortas, anfitrión, aforo, punto de encuentro)
- Formulario público RSVP (solicitud ≠ plaza confirmada)
- Success con código
RD-MMDD-NNN - Login + inbox HOST JWT (NEW → CONTACTED → SEATED / WAITLIST / CANCELLED)
- Publicar mesa (HOST)
- Empty / loading / error / mobile feed + dock
- ≥12 UX + ≥10 UI Paper
Mood visual (Paper)
- Candidatos: taberna candlelit (primer instinto), felt casino, phosphor arcade, signage, alpine
- Elegido: signage — no el primer instinto taberna (cerca de DERIVA dusk / PLIEGO wine)
- Paleta:
#FFFFFFsuelo ·#111111tinta ·#F5C400cromo ·#1F4D3Afelt ·#FFF6C2liner ·#E24B2Aficha - Tipo: Zilla Slab 56/36/24 display · Mulish 16/14 UI
Supuestos
- S1: Un barrio tipo Malasaña–Lavapiés sostiene 5–12 mesas públicas por semana (rango operativo).
- S2: El jugador llega con “qué se juega esta noche”, no a comprar un juego.
- S3: El anfitrión necesita cola con estado, no un Discord ni un marketplace.
- S4: La plaza se confirma en el local; RONDA no cobra.
Hipótesis
| ID | Hipótesis | Señal |
|---|---|---|
| H1 | El feed reduce abandono vs listado de eventos o grupo de WhatsApp | % sesiones con tap en mesa |
| H2 | Copy “solicitud, no plaza” baja sillas fantasma | ↓ “pensé que tenía silla” |
| H3 | Inbox SEATED nunca supera seats | invariante de aforo |
00-paper-reference.md
Paper reference · RONDA
| URL | https://app.paper.design/file/01M023RM2TZ49CYM2FN1FRRGNY |
| File ID | 01M023RM2TZ49CYM2FN1FRRGNY |
UX-count: 12 UI-count: 11
UX-00 Cover · UX-01 Stakeholders · UX-02 Personas · UX-03 JTBD · UX-04 Stories · UX-05 Journey · UX-06 Blueprint · UX-07 Site map · UX-08 Flujos · UX-09 Datos+permisos · UX-10 Métricas · UX-11 Research UI-00 Tokens · UI-01 Feed Hoy · UI-02 Ficha mesa · UI-03 RSVP · UI-04 Success · UI-05 Login · UI-06 Inbox HOST · UI-07 Empty · UI-08 Error · UI-09 Loading · UI-10 Mobile feed
UX process (prefijo UX- · mínimo 12)
| ID | Nombre | Contenido |
|---|---|---|
| UX-00 | Cover | Portada RONDA · eslogan «Esta noche hay mesa.» · terna R-HY · S-DOCK · H-FEED · F-BOOK · L2 gaming de mesa local · mood signage · fecha 2026-08-15 |
| UX-01 | Stakeholders | PLAYER anónimo, HOST, local (café/bar/librería), comunidad de mesa, no-Discord |
| UX-02 | Personas | Leo Navas (29, jugador, móvil) · Nerea Solís (36, anfitriona, inbox en el bar) |
| UX-03 | JTBD | Job «qué hay esta noche» + job «llenar la mesa sin sillas fantasma» |
| UX-04 | Stories | Must: feed Hoy, ficha, RSVP público, código RD-, login HOST, inbox 5 estados, publicar, empty/error/loading |
| UX-05 | Journey | Scroll feed → ficha → solicitud honesta → acuse → contacto HOST → SEATED / WAITLIST |
| UX-06 | Blueprint | Frontstage feed/dock/form · backstage mesa física · sistemas Nest/Neon/JWT |
| UX-07 | Site map | Público feed-first + auth HOST; dock Hoy · Mesas · Publicar · Inbox · Yo |
| UX-08 | Flujos | F-BOOK: card → ficha → POST RSVP → success; F-HOST inbox + publicar |
| UX-09 | Datos+permisos | GameTable · SeatRequest · User HOST · Session; JWT en list/patch/publish |
| UX-10 | Métricas | North star contacto antes de hora de mesa; tap mesa; invariante SEATED ≤ seats |
| UX-11 | Research | Comprobado / supuesto / hipótesis / decisión — sin entrevistas de campo inventadas |
UI producto (prefijo UI- · mínimo 10)
| ID | Nombre | Flujo |
|---|---|---|
| UI-00 | Tokens | DS signage: suelo blanco, tinta, cromo, felt, liner, ficha; Zilla Slab + Mulish |
| UI-01 | Feed Hoy | H-FEED: cronología de mesas de esta noche + dock flotante |
| UI-02 | Ficha mesa | Foto real, juego, hora, local, aforo, anfitrión, reglas cortas, CTA |
| UI-03 | RSVP | Form público; disclaimer solicitud ≠ plaza confirmada |
| UI-04 | Success | Acuse + código RD-MMDD-NNN + próximos pasos |
| UI-05 | Login | Acceso JWT HOST al inbox |
| UI-06 | Inbox HOST | Lista + estados NEW → CONTACTED → SEATED / WAITLIST / CANCELLED |
| UI-07 | Empty | Cero mesas esta noche / inbox vacío |
| UI-08 | Error | Fallo de red / API; reintentar |
| UI-09 | Loading | Skeleton feed / inbox |
| UI-10 | Mobile feed | Home feed ~390px + dock inferior flotante |
Regla: wizard de N pasos → N boards UI solo para esos pasos, además de success/empty/error/loading/staff. RONDA no es wizard: el núcleo es feed → ficha → form de 1 pantalla.
1. Mapa de canvas (bandas)
| § | Banda | Propósito | Artboards clave |
|---|---|---|---|
| 1 | UX PROCESS | Modelo de servicio de mesa local | UX-00…UX-11 |
| 2 | DESIGN SYSTEM | Tokens signage + dock + badges | UI-00 |
| 3 | PUBLIC FEED | Cara jugador feed-first | UI-01, UI-02, UI-03, UI-04, UI-07, UI-10 |
| 4 | HOST | Flujos autenticados | UI-05, UI-06 |
| 5 | STATES | Resiliencia | UI-08, UI-09 |
Layout canvas (referencia de diseño): origen (0,0) · gaps ~100–120px · UX en 3×4 · UI pública en fila feed → ficha → RSVP → success · mobile debajo del feed. [DECISIÓN] Bandas jerárquicas D-P0-04 / D-P1-04.
2. Mapeo Paper → Angular
| Artboard | Ruta app | Componente |
|---|---|---|
| UI-00 Tokens | — | tokens Tailwind + fuentes |
| UI-01 Feed Hoy | / | FeedPage |
| UI-02 Ficha mesa | /mesas/:slug | TablePage |
| UI-03 RSVP | /mesas/:slug/asiento | RsvpPage |
| UI-04 Success | /ok | SuccessPage |
| UI-05 Login | /login | LoginPage |
| UI-06 Inbox HOST | /inbox | InboxPage |
| UI-07 Empty | / 0 mesas · /inbox 0 items | empty en feed / inbox |
| UI-08 Error | feed / inbox / form | banner error + retry |
| UI-09 Loading | feed / inbox | skeletons |
| UI-10 Mobile feed | / viewport 390 | mismo FeedPage + dock |
Rutas HOST adicionales (no artboard UI dedicado v1, sí en IA): /inbox/:id detalle solicitud · /publicar crear mesa · /yo sesión.
3. Tokens de diseño en Paper
| Token | Valor | Uso |
|---|---|---|
| Suelo / bg | #FFFFFF | Fondo de página, cartelería |
| Tinta | #111111 | Texto fuerte, wordmark, CTA ink |
| Cromo | #F5C400 | Acento dock activo, focus, “esta noche” |
| Felt | #1F4D3A | Superficie de mesa, SEATED, acciones positivas |
| Liner | #FFF6C2 | Cards, rails, fondo dock |
| Ficha | #E24B2A | WAITLIST, urgencia, asientos restantes bajos |
| Display | Zilla Slab 56/36/24 | Titulares, wordmark, hora de mesa |
| UI | Mulish 16/14 | Body, labels, form, dock labels |
4. Checklist de densidad (anti thin-frames)
| Criterio | UX | DS | Public feed | HOST | States |
|---|---|---|---|---|---|
| Jerarquía tipográfica visible | Sí | Sí | Sí | Sí | Sí |
| Microcopy real (no lorem) | Sí | Sí | Sí | Sí | Sí |
| Tokens signage aplicados | — | Sí | Sí | Sí | Sí |
| Datos de seed creíbles | Personas | — | 5 mesas + fotos | Códigos RD- | Empty realista |
| Media / iconografía | Cover | — | Fotos mesa | Badges estado | Skeletons |
| Dock flotante presente | — | Spec | UI-01, UI-10 | UI-06 | — |
5. Media
assets/hero.jpg— atmósfera de mesa / signage de localassets/tables/azulejos.jpg— Azulejos de Lavapiésassets/tables/cartas.jpg— Cubilete y cartasassets/tables/minis.jpg— Mazmorra de Chamberíassets/tables/cooperativo.jpg— Farol cooperativoassets/tables/coleccion.jpg— Colección de patio
[COMPROBADO] Los seis archivos existen en el case 2026-08-15-ronda/assets/.
6. Enlaces
- Paper: https://app.paper.design/file/01M023RM2TZ49CYM2FN1FRRGNY
- Case:
/Users/cristian/orca/ux-projects/2026-08-15-ronda/ - App:
/Users/cristian/orca/ronda-app/ - GitHub: https://github.com/Criscode2022/ronda-app (se creará)
- Neon project:
curly-lab-77015594 - API:
http://localhost:3015 - Web:
http://localhost:4200
01-project-definition.md
01 — Definición de proyecto — RONDA
1. Identidad
| Campo | Valor |
|---|---|
| Nombre | RONDA |
| Significado | Ronda de juego / ronda de copas en la mesa: el turno que se abre esta noche |
| Eslogan | ”Esta noche hay mesa.” |
| Una frase | Feed cronológico de mesas de juego locales + RSVP público + inbox HOST JWT. |
| Sector | Gaming / comunidad de mesa local |
| Tipo | Web L2 — feed + RSVP + inbox HOST — Nivel 2 |
| Plataforma | Web responsive (móvil-first jugador + desktop anfitrión) |
| Mercado demo | España · Madrid · Malasaña, Lavapiés, Chamberí (y Embajadores como local de seed) |
| Idioma | es-ES |
| Fecha caso | 2026-08-15 |
| Terna | R-HY · S-DOCK · H-FEED · F-BOOK |
[COMPROBADO] Nombre, eslogan, terna, nivel y sector salen de docs/00-day-brief.md.
2. Problema
Principal (hipótesis de diseño)
[HIPÓTESIS] Quien quiere jugar esta noche no abre un marketplace ni un Discord: pregunta “qué hay” en un grupo de WhatsApp, un cartel del bar o un hilo que se pierde. [SUPUESTO] El anfitrión confirma sillas por mensaje suelto, sin cola, sin código y sin tope de aforo visible.
Secundarios
| Problema | Quién lo sufre | Efecto |
|---|---|---|
| Oferta enterrada en chats y carteles | PLAYER (Leo) | Llega tarde o no llega; no sabe qué se juega |
| “¿Quedan asientos?” por DM | Ambos | Cola informal, sillas fantasma |
| Mensaje enviado sin acuse | PLAYER | No sabe si llegó; escribe otra vez |
| Excel / WhatsApp sin estado | HOST (Nerea) | No sabe a quién ya contestó |
| Expectativa de “ya tengo silla” | Ambos | No-shows y mesa descompensada |
Supuestos (no investigación primaria propia)
- S1: Un barrio tipo Malasaña–Lavapiés sostiene 5–12 mesas públicas por semana (rango operativo). [SUPUESTO]
- S2: El jugador llega con “qué se juega esta noche”, no a comprar un juego. [SUPUESTO]
- S3: El anfitrión necesita cola con estado, no un Discord ni un marketplace. [SUPUESTO]
- S4: La plaza se confirma en el local; RONDA no cobra. [SUPUESTO]
Hipótesis de producto
| ID | Hipótesis | Señal de validación (futura) |
|---|---|---|
| H1 | El feed reduce abandono vs listado de eventos o grupo de WhatsApp | % sesiones con tap en mesa |
| H2 | Copy “solicitud, no plaza” baja sillas fantasma | ↓ “pensé que tenía silla” |
| H3 | Inbox SEATED nunca supera seats | invariante de aforo |
No se afirman estadísticas de demanda de ocio de mesa inventadas. Todo lo anterior es razonamiento de diseño etiquetado.
3. Propuesta de valor
| Para | Valor |
|---|---|
| PLAYER (Leo Navas) | Ve qué mesas hay esta noche; pide asiento en un minuto; se lleva un código. |
| HOST (Nerea Solís) | Ve solicitudes nuevas, marca contacto antes de abrir, sienta o pone en espera sin WhatsApp. |
| Local (café / bar / librería) | Canal mínimo: mesa visible + pipeline de gente, sin TPV ni evento de ticket. |
No es RONDA
| Excluido | Por qué |
|---|---|
| Mapa / plano de ciudad | Terna H-FEED, no H-MAP (eso fue DERIVA) |
| Search-first / catálogo facetado | Terna no es H-SEARCH (eso fue PIZARRA) |
| Kanban de solicitudes | F-BOOK es cola de asiento, no tablero |
| E-commerce / TPV / entrada de pago | S4: la plaza se confirma en el local; no cobra |
| Discord / chat in-app / matching | S3: cola con estado, no comunidad persistente |
| Hero 2-col + 3 cards de “juegos estrella” | Anti-patrón AP-12; contradice H-FEED |
4. Objetivos
Negocio / caso de estudio
- Demostrar vertical slice L2 gaming feed-first con GameTable + SeatRequest + JWT HOST.
- Portfolio coherente: Paper (12 UX + 11 UI) + docs + app runnable.
- Terna R-HY · S-DOCK · H-FEED · F-BOOK frente a DERIVA (mapa) y PIZARRA (search).
Usuario
| Rol | Objetivo medible en demo |
|---|---|
| PLAYER | Ver “esta noche” en el primer viewport y enviar RSVP en < 2 min |
| HOST | Marcar CONTACTED / SEATED / WAITLIST en < 3 taps desde el inbox |
No objetivos v1 (explícitos)
- Pagos, fianza, TPV
- Cuenta de jugador / “mis solicitudes”
- Chat, matching o reputación
- Mapa de locales
- Notificación email / WhatsApp transaccional
- Inventario de juegos en venta
- Multi-anfitrión con roles editoriales
5. Roles y permisos (resumen)
| Acción | Público (PLAYER) | HOST |
|---|---|---|
| Ver feed Hoy / Mesas | Sí | Sí |
| Ver ficha de mesa | Sí | Sí |
| POST solicitud RSVP | Sí | Sí (mismo form) |
| Login JWT | No (no cuenta jugador) | Sí |
| Listar solicitudes | No (401) | Sí |
| Cambiar status | No | Sí |
Publicar mesa POST /api/tables | No (401) | Sí |
| Ver / editar sesión (Yo) | No | Sí |
[DECISIÓN] Un solo rol autenticado: HOST. El jugador es anónimo en captura.
6. Métricas (modelo, no instrumentadas en v1 salvo base)
| Tipo | Métrica | Definición |
|---|---|---|
| North Star | % solicitudes válidas CONTACTED antes de startAt de la mesa | Calidad operativa del contacto, no “entradas vendidas” |
| Activación | 1ª solicitud pública | POST create 2xx |
| Feed | % sesiones con tap en card de mesa | H1 |
| Expectativa | Reclamaciones “ya tenía silla” | H2 (cualitativa) |
| Integridad | SEATED ≤ seats por mesa | H3 |
| Salud | Error rate API tables/create/list | 4xx/5xx |
7. Alcance funcional v1 (L2)
| Módulo | Incluido |
|---|---|
| Home feed | Cronología de 5 mesas (foto, juego, hora, local, asientos) + dock |
| Ficha | Reglas cortas, anfitrión, aforo, punto de encuentro, CTA |
| RSVP | Form público + POST /api/requests |
| Success | Código RD-MMDD-NNN + próximos pasos |
| Auth | POST /api/auth/login → JWT HOST |
| Inbox | GET list, PATCH status (NEW → CONTACTED → SEATED / WAITLIST / CANCELLED) |
| Publicar | POST /api/tables (HOST) |
| Estados UI | Empty, loading skeleton, error de red |
| Mobile feed | Misma home a ~390px + dock flotante |
| Seed | 1 HOST, 5 mesas, solicitudes de ejemplo |
8. Criterios de aceptación de producto
- Un visitante puede ver el feed cronológico sin autenticación y abrir una ficha.
- Un visitante puede enviar una solicitud sin cuenta y recibir un código
RD-…. - La UI dice explícitamente que la solicitud no confirma plaza.
- Sin token, GET/PATCH
/api/requests*y POST/api/tablesresponden 401. - GET
/api/tablesdevuelve las 5 mesas seed ordenadas porstartAtascendente. - Un HOST puede iniciar sesión y ver el inbox ordenado (
createdAtdesc). - El detalle permite transicionar
NEW | CONTACTED | SEATED | WAITLIST | CANCELLED. - La home es el feed: no hay hero 2-col + 3 cards como superficie principal.
- Barrios cubiertos en seed: Lavapiés, Embajadores, Chamberí, Malasaña.
- Fotos de mesa reales en fichas (assets del case).
- Si los asientos visibles están cubiertos, el CTA ofrece WAITLIST, no “reserva tu silla”.
- Un HOST autenticado puede publicar una mesa (
POST /api/tables).
9. Stack y artefactos
| Capa | Detalle |
|---|---|
| Frontend | Angular + Tailwind · puerto 4200 |
| Backend | NestJS · puerto 3015 |
| DB | Neon PostgreSQL · ensureSchema · project curly-lab-77015594 |
| Auth | JWT (HOST) + tabla sessions |
| Diseño | Paper 01M023RM2TZ49CYM2FN1FRRGNY |
| Repo app | /Users/cristian/orca/ronda-app/ · GitHub Criscode2022/ronda-app |
[COMPROBADO] Puerto, Neon, Paper file ID y path de app constan en el brief de este encargo.
10. Riesgos y mitigaciones
| Riesgo | Impacto | Mitigación v1 |
|---|---|---|
| Spam en form público | Inbox ruidoso | Validación server; honeypot / rate-limit en L2+ |
| Expectativa de silla instantánea | No-show y mesa rota | Copy “solicitud ≠ plaza”; SEATED solo HOST |
| Overbooking | Conflicto en la mesa física | WAITLIST + regla H3 documentada; HOST no debe SEATED > seats |
| Confundir con tienda / evento de ticket | Expectativa de pago | Copy “se confirma en el local”; sin precio |
| PII de jugadores en solicitudes | Privacidad | Solo HOST lista; doc 11 |
| Feed vacío un martes | Abandono | Empty state “esta noche no hay mesa” + CTA ver semana |
| Home percibida como landing de marca | Pérdida de craft | Signage + feed como superficie; dock, no hero 2-col |
| Confusión Discord / “únete al server” | Expectativa de chat | No hay canal; hay cola |
11. Glosario
| Término | Definición en RONDA |
|---|---|
| Mesa | game_tables: juego, hora, local, aforo, anfitrión |
| Solicitud / RSVP | seat_requests; no es plaza confirmada |
| Plaza / asiento | Solo cuando HOST marca SEATED |
| Lista de espera | WAITLIST cuando seats están cubiertos |
| HOST | Usuario autenticado que publica y sienta |
| PLAYER | Visitante anónimo que pide asiento |
| Código RD- | Identificador corto oral (RD-0815-001) |
| Dock | Shell S-DOCK: Hoy · Mesas · Publicar · Inbox · Yo |
| Hoy | Recorte del feed: mesas con startAt en el día civil Europe/Madrid |
| Mesas | Recorte semanal / todas las publicadas próximas |
12. Decisiones de diseño (cierre de brief)
| ID | Decisión | Alternativa descartada |
|---|---|---|
| D1 | Home = feed cronológico (H-FEED) | Hero 2-col + 3 cards (AP-12) · mapa (DERIVA) · search (PIZARRA) |
| D2 | Shell dock flotante (S-DOCK) | Top-nav sticky · sidebar CRM · command search |
| D3 | Registro híbrido flyer + dock (R-HY) | Taberna candlelit (primer instinto) · dusk · wine |
| D4 | Flujo F-BOOK (asiento) | Wizard, kanban, search, mapa |
| D5 | Mood signage | Felt casino, phosphor arcade, alpine, taberna |
| D6 | Un rol autenticado HOST | Multi-rol jugador / local / admin |
| D7 | Tabla game_tables (no tables) | Nombre SQL reservado |
| D8 | Estado SEATED (no ENROLLED) | Vocabulario municipal de PIZARRA |
| D9 | RONDA no cobra | TPV / fianza / Eventbrite |
13. Relación con el día anterior
| Case | Terna | Por qué RONDA no lo copia |
|---|---|---|
| DERIVA (N−1) | S-SPLIT / H-MAP / F-MAP | No turismo, no plano, no split mapa |
| PIZARRA (N−2) | S-CMD / H-SEARCH / F-SEARCH | No catálogo municipal, no search-first |
[COMPROBADO] El brief exige 0 códigos iguales a N−1 y ≥2 distintos vs N−2. Terna de hoy: R-HY · S-DOCK · H-FEED · F-BOOK.
02-ux-research-strategy.md
02 — Estrategia de investigación UX — RONDA
Importante: este documento contiene artefactos de diseño y razonamiento secundario.
No hay entrevistas de campo primarias ni estadísticas inventadas presentadas como dato medido.
Etiquetas: [COMPROBADO] en producto, [SUPUESTO], [HIPÓTESIS], [DECISIÓN].
1. Objetivos de investigación (del caso)
| Objetivo | Método en este caso | Salida |
|---|---|---|
| Entender actores de la mesa local | Modelado de stakeholders + personas | §3–4 |
| Definir job de “esta noche” y de aforo | JTBD + stories Must | §5 |
| Mapear fricción chat → silla | Journey + service blueprint | §6–7 |
| Traducir a requisitos L2 feed-first | Matriz hallazgo → requisito → feature | §8 |
2. Fuentes y límites
Fuentes admisibles (secundarias / operativas)
- Conocimiento general de bares de juego, cafés con mesa reservable y grupos de WhatsApp de barrio.
- Analogía operativa con feeds + RSVP + inbox JWT de la serie daily.
- Restricciones ALS-2: no mapa, no search-first, no kanban, no e-commerce, no Discord.
Límites éticos de verdad
| Prohibido | Permitido |
|---|---|
| “El 68% de jugadores abandona el grupo de WhatsApp” sin fuente | “[SUPUESTO] el jugador llega con ‘qué hay esta noche’” |
| Citas de entrevistas ficticias como campo real | Quotes de persona etiquetadas como constructo de diseño |
| SLA medido “contacto en 2 h en Lavapiés” como KPI real | North star modelo “CONTACTED antes de startAt” |
| Aforo real de un local concreto | Aforos de seed de producto |
| “Nerea existe y nos dijo…” | Nerea Solís es persona de diseño |
3. Stakeholders
| Stakeholder | Influencia | Interés | Necesidad principal |
|---|---|---|---|
| PLAYER / jugador de mesa | Baja formal | Muy alta | Ver qué hay esta noche y dejar solicitud con acuse |
| HOST / anfitrión (Nerea) | Alta | Muy alta | Cola con estado y código oral |
| Acompañante / pareja de juego | Media | Alta | Hora, local, aforo claros |
| Local (café, bar, librería, terraza) | Alta adopción | Media | Mesa llena a su hora, sin overbooking de sillas |
| Comunidad de mesa del barrio | Media | Media | Ritmo semanal visible, no un foro eterno |
| Tienda / editorial de juegos | Baja v1 | Baja | Fuera: no es canal de venta |
| Discord / Meetup / WhatsApp | Competencia / canal paralelo | — | No clonar el chat; sí sustituir la cola informal |
Mapa de poder (resumen)
- Decisor de adopción: HOST habitual del local (quien abre mesa).
- Usuario frecuente de captura: PLAYER (feed + form) y HOST (inbox diario).
- Riesgo de rechazo: si la home no enseña “esta noche” en el primer viewport, o si el form implica “ya tienes silla”.
4. Personas
P1 — Leo Navas · PLAYER
| Campo | Detalle |
|---|---|
| Edad / contexto | 29 años; vive cerca de Tribunal; juega de semana cuando sale del curro |
| Digital | Alta; móvil primero; no quiere otra app de chat |
| Goals | Ver qué hay esta noche, si queda hueco, dejar datos y un código |
| Pains | Grupos muertos, “¿alguien para Azul?”, no saber si el mensaje llegó |
| Quote de diseño | “Dime qué hay esta noche y si de verdad me van a guardar silla.” |
| Seed demo | leo.navas@example.com · solicitud azulejos RD-0815-001 |
Escenario: Abre RONDA en el metro, ve Azulejos de Lavapiés a las 20:30 en La Palma 12, pide asiento, guarda RD-0815-001.
[DECISIÓN] Leo no tiene cuenta. Crear usuario jugador sería fricción y no aporta al job de captura.
P2 — Nerea Solís · HOST
| Campo | Detalle |
|---|---|
| Edad / contexto | 36 años; anfitriona habitual; abre mesa en café/bar antes de que llegue gente |
| Digital | Media–alta; móvil en la barra; desktop si publica la semana |
| Goals | Ver NEW del día, contactar antes de la hora, sentar o pasar a WAITLIST sin WhatsApp |
| Pains | Mensajes sueltos, no sabe quién ya fue avisado, sillas fantasma |
| Quote de diseño | “Si está en NEW, es mía. Si está SEATED, hay silla. Si no, WAITLIST.” |
| Email demo | nerea@ronda.club / password123 |
Escenario: Login en el bar → inbox → Leo NEW → escribe / llama → CONTACTED → si hay silla SEATED; si la mesa está llena WAITLIST.
Anti-personas
| Quién | Por qué no es target v1 |
|---|---|
| Comprador de marketplace de juegos | Eso es e-commerce, no ronda de mesa |
| Organizador de torneo federado con ranking | Otro producto (liga, no feed de esta noche) |
| Turista que busca “escape room” | Experiencia de ticket, no comunidad de barrio |
| Moderador de Discord que quiere un clone | RONDA no es chat |
5. JTBD y user stories
Job principal (PLAYER)
Cuando quiero saber qué se juega esta noche cerca,
quiero ver las mesas en un feed y dejar mis datos en un solo paso,
para que el anfitrión me contacte sin perder el mensaje y sin creer que ya tengo silla.
Job principal (HOST)
Cuando abro mesa y empiezan a llegar peticiones,
quiero verlas con juego, hora y estado,
para contactar antes de la hora y sentar o poner en espera sin overbooking.
Jobs secundarios
| Job | Rol |
|---|---|
| Distinguir “hoy” de “esta semana” | PLAYER |
| Referir una solicitud por código corto en la barra | Ambos |
| Ver cuántas NEW / WAITLIST hay antes de abrir | HOST |
| Publicar una mesa nueva desde el dock | HOST |
| Cancelar duplicado o desistimiento | HOST |
Stories Must (v1)
| ID | Story | AC |
|---|---|---|
| US1 | Como jugador, quiero ver las mesas de esta noche en un feed | GET /api/tables ordenado por startAt; cards con foto, hora, local, asientos |
| US2 | Como jugador, quiero abrir la ficha con reglas, aforo y punto de encuentro | GET :slug; foto real; CTA |
| US3 | Como jugador, quiero enviar RSVP sin cuenta | POST 201 + redirect success |
| US4 | Como jugador, quiero un código y saber que no tengo silla todavía | code en /ok + copy disclaimer |
| US5 | Como HOST, quiero entrar con email/password | JWT + redirect /inbox |
| US6 | Como HOST, quiero listar solicitudes | GET list JWT |
| US7 | Como HOST, quiero cambiar estado incl. WAITLIST y SEATED | PATCH status |
| US8 | Como HOST, quiero publicar una mesa | POST /api/tables JWT |
| US9 | Como cualquiera, quiero ver empty / error / loading | UI-07, UI-08, UI-09 |
| US10 | Como jugador en móvil, quiero el feed + dock usable a ~390px | UI-10; targets ≥44px |
MoSCoW (v1 L2)
| Prioridad | Ítems |
|---|---|
| Must | Feed Hoy, ficha, RSVP, success, login, inbox, 5 estados, publicar, empty/loading/error, mobile feed + dock |
| Should | Recorte Mesas (semana); asientos restantes visibles; labels ES de enums; detalle /inbox/:id |
| Could | Copiar código al portapapeles; click-to-call; persistir scroll del feed |
| Won’t | Pagos, cuenta jugador, mapa, Discord, email transaccional, search facetado |
6. Journey (PLAYER → HOST)
| Fase | Actor | Acción | Touchpoint | Emoción [HIPÓTESIS] |
|---|---|---|---|---|
| 1 Mirar | Leo | Abre RONDA; ve “esta noche” | Feed Hoy + dock | Curiosidad baja fricción |
| 2 Elegir | Leo | Toca Azulejos de Lavapiés 20:30 | Card → ficha | Control |
| 3 Entender | Leo | Lee local, aforo 4, tile-laying, Nerea | /mesas/azulejos | Confianza o duda de cupo |
| 4 Pedir | Leo | Form + disclaimer | /mesas/azulejos/asiento | Prudencia |
| 5 Acuse | Leo | Ve RD-0815-001 | /ok | Alivio (no euforia de “ya estoy”) |
| 6 Contacto | Nerea | Ve NEW, escribe | Inbox + detalle | Control operativo |
| 7 Cierre | Nerea | SEATED o WAITLIST | PATCH | Cierre honesto |
Momentos de verdad
- Feed en el primer viewport — si hay que “descubrir la marca”, se rompe S2. [HIPÓTESIS]
- Disclaimer en form y success — si falta, H2 falla.
- Código RD- — prueba de “ha llegado” para la barra y el mensaje.
- WAITLIST visible — evita la mentira social del “ya te apunto”.
- Dock no esconde Publicar / Inbox — el HOST en el bar no busca un menú hamburger. [HIPÓTESIS]
7. Service blueprint (resumen)
| Capa | Elementos |
|---|---|
| Frontstage PLAYER | Feed, dock, ficha, form, success |
| Frontstage HOST | Login, inbox, detalle, publicar, Yo |
| Backstage | Mensaje / llamada / silla física en el local (fuera de app) |
| Sistemas | Nest API :3015, Neon curly-lab-77015594, JWT + sessions, Angular feed |
| Soportes | Seed 5 mesas + fotos, Paper, docs |
| Fallos | 0 mesas hoy; 401 sin token; 404 slug; red caída → UI error |
Fallos de servicio y respuesta de diseño
| Fallo | Evidencia de UI | Recuperación |
|---|---|---|
| Noche sin mesas | UI-07 Empty feed | “Esta noche no hay mesa” + ver semana |
| Inbox vacío | UI-07 Empty inbox | “Cuando alguien pida asiento, aparece aquí” |
| API caída | UI-08 Error | Reintentar |
| Latencia feed | UI-09 Loading | Skeleton de 5 cards |
| Form inválido | Inline field | No navegar a success |
| Cupo lleno (ops) | HOST ve seats vs count | Forzar WAITLIST, no SEATED [H3] |
8. Matriz hallazgo → requisito → feature
| Hallazgo | Tipo | Requisito | Feature v1 |
|---|---|---|---|
| Llega con “qué hay esta noche” | [SUPUESTO] S2 | Feed es la home | UI-01 / H-FEED |
| Chat no ordena por hora | [HIPÓTESIS] H1 | Cronología startAt | GET tables ordenado |
| Sin acuse | [HIPÓTESIS] | Código oral | Success + RD- |
| Confunden solicitud con silla | [HIPÓTESIS] H2 | Copy irrenunciable | Disclaimer form/success |
| Overbooking informal | [HIPÓTESIS] H3 | Estado WAITLIST + seats | Enum + campo seats |
| HOST opera en el bar | [SUPUESTO] | Dock + inbox móvil | S-DOCK / UI-06 / UI-10 |
| PII de jugadores | [DECISIÓN] | Auth JWT HOST | Login + guards API |
| L2 compacto, no Discord | [DECISIÓN] | Un rol HOST; mesas seed | Sin chat, sin cuenta PLAYER |
9. Preguntas abiertas (no bloquean v1)
| ID | Pregunta | Cómo se resolvería después |
|---|---|---|
| Q1 | ¿Un HOST por local o cola compartida de barrio? | venueId + scope de User L2+ |
| Q2 | ¿Email automático “hemos recibido tu solicitud”? | Hook post-create; copy ya promete contacto |
| Q3 | ¿Checkbox RGPD + política? | Legal L2+; minimización ya aplicada |
| Q4 | ¿Sincronizar aforo con sillas físicas del local? | Fuente de verdad única; hoy es HOST |
| Q5 | ¿Permitir party_size > 1 y descontar asientos? | Campo en request + regla ops; v1 default 1 |
| Q6 | ¿“Hoy” corta a medianoche o a las 04:00? | [DECISIÓN] día civil Europe/Madrid 00:00–24:00; L2+ madrugada |
10. Plan de research futuro (si hubiera campo real)
| Método | Muestra orientativa | Pregunta |
|---|---|---|
| Test de usabilidad feed | 5–6 jugadores 22–40 | ¿Encuentran mesa de esta noche en < 30 s? |
| Shadowing HOST 1 noche | 1–2 locales | ¿El inbox sustituye el WhatsApp de la barra? |
| Revisión de no-shows | Mesas con SEATED | Validar H2 (sillas fantasma) |
| Card sort de mecánicas | 6–8 participantes | ¿Azulejos / minis / coop se entienden? |
Estos métodos no se han ejecutado. No se reportan hallazgos como si lo hubieran sido.
11. Síntesis
RONDA se diseña como feed de mesas con cola de asiento, no como tienda, no como mapa y no como Discord.
La investigación del caso es constructiva y etiquetada: personas, JTBD y journey son artefactos de diseño; las métricas de validación quedan para uso real futuro.
03-information-architecture.md
03 — Arquitectura de información — RONDA
1. Principios de IA
| Principio | Aplicación |
|---|---|
| Feed first | La home es la cronología de esta noche. No hay landing de marca por delante. |
| Card → ficha → asiento | Profundidad 2 desde el feed hasta el form |
| Público vs HOST | Feed y form abiertos; inbox y publicar solo autenticados |
| Lenguaje de dominio | Mesa, asiento, solicitud, ronda, local — no “evento SKU”, “lead CRM”, “ticket” |
| Código visible | RD-… en success e inbox para referencia oral / barra |
| Honestidad de estado | Solicitud ≠ sentado; WAITLIST es ciudadano de primera |
| Dock siempre a mano | S-DOCK: cinco destinos, no mega-menú |
2. Sitemap
/ Feed Hoy (público) — mesas del día civil, startAt ASC
/mesas Feed semanal / próximas (público)
/mesas/:slug Ficha de mesa (público)
/mesas/:slug/asiento Formulario RSVP (público)
/ok Success post-solicitud (público)
/login Login JWT HOST
/inbox Lista de solicitudes (auth HOST)
/inbox/:id Detalle + estado (auth HOST)
/publicar Crear mesa (auth HOST)
/yo Sesión HOST: nombre, salir (auth HOST)
/** → redirect /
Árbol por audiencia
| Audiencia | Nodos relevantes |
|---|---|
| PLAYER | Feed Hoy → (Mesas) → Ficha → Asiento → Ok |
| HOST | Login → Inbox → Detail · Publicar · Yo (y feed público si consulta oferta) |
| Ambos | Wordmark → / (HOST autenticado: wordmark puede ir a /inbox) |
[DECISIÓN] /mesas no es un buscador: es el mismo patrón de feed con recorte temporal más amplio.
3. Navegación
Dock flotante (S-DOCK)
| Slot | Destino | Visibilidad | Notas |
|---|---|---|---|
| Hoy | / | Todos | Recorte día civil; estado activo en home |
| Mesas | /mesas | Todos | Próximas / semana |
| Publicar | /publicar | HOST; si guest → /login?next=/publicar | Centro del dock, acento cromo |
| Inbox | /inbox | HOST; si guest → /login?next=/inbox | Badge de NEW opcional v1 Should |
| Yo | /yo si auth; /login si guest | Todos | Sesión o entrada HOST |
[DECISIÓN] El dock es la única navegación primaria. No hay top-nav de 7 ítems ni sidebar CRM.
Pública (además del dock)
| Elemento | Destino | Notas |
|---|---|---|
| Wordmark RONDA | / | Zilla Slab; no “club marketplace” |
| Card de mesa | /mesas/:slug | Resultado del feed |
| Pedir asiento | /mesas/:slug/asiento | Primary en ficha |
| Entrada HOST discreta | /login | También vía Yo |
HOST (autenticado)
| Elemento | Destino | Visibilidad |
|---|---|---|
| Inbox | /inbox | HOST |
| Badge rol | — | HOST |
| Nombre | Nerea Solís | HOST |
| Publicar | /publicar | HOST |
| Salir | limpia token + session → /login | HOST |
| Card solicitud | /inbox/:id | HOST |
| Volver (detalle) | /inbox | HOST |
4. Inventario de contenido
| Pantalla | Contenidos |
|---|---|
| Feed Hoy | Wordmark, fecha del día, lista cronológica (foto, título, hora, local, barrio, asientos), empty, loading, dock |
| Feed Mesas | Mismo patrón; agrupación por día; recorte semana |
| Ficha | Hero foto, título, mecánica, hora, local, dirección, aforo, anfitrión, reglas cortas, disclaimer, CTA |
| RSVP | Título mesa, hora, disclaimer, campos form, submit |
| Success | Mensaje, código RD-, “no es plaza”, siguiente paso |
| Login | Título anfitrión, email, password, submit, error |
| Inbox | Lista (code, nombre, mesa, status, createdAt), empty, error |
| Detail | Código, jugador, mesa, aforo vs demanda, notas, acciones estado |
| Publicar | Título, mecánica, barrio, local, dirección, fecha/hora, asientos, resumen, submit |
| Yo | Nombre, email, rol HOST, salir |
5. Taxonomía
RequestStatus
| Status API | Label UI | Semántica |
|---|---|---|
NEW | Nueva | Acaba de llegar; sin contacto |
CONTACTED | Contactada | HOST inició mensaje / llamada |
SEATED | Sentada | Plaza confirmada en el local (o compromiso firme del HOST) |
WAITLIST | Lista de espera | Cupo cubierto o reserva de hueco |
CANCELLED | Cancelada | Desiste / duplicado / no localizable |
Orden de inbox: createdAt descendente (más reciente primero).
[DECISIÓN] No se ordena por “prioridad de socio del local” en v1.
Mechanic
| API | Label UI | Mesas seed |
|---|---|---|
TILE | Azulejos / tile-laying | Azulejos de Lavapiés |
CARDS | Cartas y dados | Cubilete y cartas |
MINIS | Miniaturas | Mazmorra de Chamberí |
COOP | Cooperativo | Farol cooperativo |
COLLECTION | Colección | Colección de patio |
Neighborhood
| API | Label UI | Seed |
|---|---|---|
LAVAPIES | Lavapiés | azulejos |
EMBAJADORES | Embajadores | cartas |
CHAMBERI | Chamberí | minis |
MALASANA | Malasaña | cooperativo, coleccion |
Barrios y locales del seed son contenido de producto demo, no una agenda real publicada. [SUPUESTO de catálogo]
Recorte temporal (no es faceta de search)
| Recorte | Criterio |
|---|---|
| Hoy | startAt ∈ día civil Europe/Madrid |
| Mesas | startAt ≥ now − 30 min (aún alcanzable) y ≤ now + 7 días |
[DECISIÓN] No hay query q ni chips de mecánica en v1. Eso sería H-SEARCH (PIZARRA). El feed se recorre.
6. Modelo mental vs UI
| Modelo mental | Representación |
|---|---|
| “¿Qué hay esta noche?” | Feed Hoy ordenado por hora |
| “¿Dónde quedamos?” | Ficha: local + dirección |
| “¿Quedan sillas?” | Asientos restantes / CTA WAITLIST |
| “Me apunto” | Form /asiento + disclaimer |
| “Me dieron un número” | code RD- |
| “Lista de gente nueva” | Inbox + badge NEW |
| “Ya le escribí” | CONTACTED |
| “Tiene silla” | SEATED |
| “La mesa está llena” | WAITLIST |
| “Abro mesa el jueves” | /publicar |
7. Query string (contrato mínimo)
| Param | Ejemplo | Efecto |
|---|---|---|
code | RD-0815-001 | Success: muestra el código |
next | /inbox | Login: redirect post-auth |
from | hoy | mesas | Ficha: volver al recorte |
[DECISIÓN] El feed no serializa filtros de mecánica/barrio en URL en v1. Evita disfrazar un search.
8. Rutas API alineadas a IA
| UI | API |
|---|---|
| Feed Hoy / Mesas | GET /api/tables |
| Ficha | GET /api/tables/:slug |
| Submit RSVP | POST /api/requests |
| Login | POST /api/auth/login |
| Inbox list | GET /api/requests |
| Cambiar estado | PATCH /api/requests/:id/status |
| Publicar mesa | POST /api/tables |
Detalle inbox: GET /api/requests/:id (JWT) — Should de implementación, AC de F7.
9. Decisiones de IA descartadas
| Idea | Por qué no en L2 v1 |
|---|---|
/explorar editorial + feed secundario | Rompe H-FEED |
| Área “mis solicitudes” por email mágico | Cuenta de facto; authz delicada |
| Mapa de locales con pins | H-MAP de DERIVA |
| Search + chips mecánica/barrio | H-SEARCH de PIZARRA |
| Nested nav “Barrios → Locales → Mesas” | Directorio, no ronda |
| Kanban de solicitudes | F-KAN; aquí la cola es inbox |
| Wizard de 4 pasos de RSVP | F-ONB; el form es 1 pantalla |
| Foro / Discord embebido | S3: no es chat |
04-user-flows.md
04 — Flujos de usuario — RONDA
Convenciones
- Actor: Guest (PLAYER) | HOST
- Éxito: resultado observable
- Errores: UI + código HTTP cuando aplica
- Flujo canónico: F-BOOK (pedir asiento de una mesa)
F1 — Descubrimiento por feed (PLAYER)
/ → feed Hoy (H-FEED)
→ GET /api/tables
→ lista cronológica | empty | error | skeleton
→ click card → /mesas/:slug
→ dock Mesas → /mesas (recorte semana)
→ dock Yo → /login
| Paso | Acción | Sistema |
|---|---|---|
| 1 | Aterriza; la primera card de esta noche es el primer foco | Render home |
| 2 | Recorre hora, local, asientos | GET tables |
| 3 | Abre ficha o cambia a Mesas | Router |
| 4 | Dock permanece visible | Shell S-DOCK |
Éxito: al menos una ficha alcanzable, o empty accionable.
AC: no hay que hacer scroll de “marca” para llegar a la primera mesa.
Errores / estados
| Caso | Comportamiento |
|---|---|
| 0 mesas hoy | UI-07: “Esta noche no hay mesa.” + CTA “Ver la semana” → /mesas |
| 0 mesas en semana | UI-07: “Aún no hay mesas publicadas.” |
| Red / 5xx | UI-08 + Reintentar |
| Primera carga | UI-09 skeleton de 5 cards |
F2 — Entender mesa (PLAYER)
/mesas/:slug → GET /api/tables/:slug
→ foto, juego, hora, local, dirección, aforo, anfitrión, reglas, disclaimer
→ CTA "Pedir asiento" → /mesas/:slug/asiento
→ si asientos cubiertos → CTA "Apuntarme a la lista de espera"
→ 404 slug → mensaje + volver al feed
| Paso | Acción | Sistema |
|---|---|---|
| 1 | Lee oferta presencial | GameTable |
| 2 | Contrasta aforo y hora | Campos seats, startAt |
| 3 | Decide pedir o volver al feed | Router |
Éxito: CTA visible; disclaimer “pedir asiento no reserva silla” visible antes del form.
Error: 404 si slug inexistente.
AC ficha
| # | Criterio |
|---|---|
| 1 | Foto real del imageKey (no color sólido) |
| 2 | Hora en Europe/Madrid, formato HH:mm |
| 3 | Local + barrio + dirección corta |
| 4 | Asientos totales visibles |
| 5 | Nombre del HOST |
| 6 | Mecánica en label humano (tile-laying → Azulejos) |
F3 — Pedir asiento / RSVP (PLAYER, público)
/mesas/:slug/asiento
→ validación cliente
→ POST /api/requests {
tableId | slug,
fullName, email, phone?,
partySize?,
notes?
}
→ 201 SeatRequest { code, status: NEW, ... }
→ /ok?code=RD-…
| Campo | Validación cliente (mín.) | API |
|---|---|---|
| tableId / slug | required (de la ruta) | existe en DB |
| fullName | required, min 2 | min 2 |
| required, email | email válido | |
| phone | optional | optional |
| partySize | optional, 1–2, default 1 | 1–2 |
| notes | optional | default "" |
Éxito: registro status=NEW, código RD-MMDD-NNN, pantalla success.
No hay cuenta de jugador en v1.
Errores
| Caso | Comportamiento |
|---|---|
| Validación DTO | 400 + mensaje de campo |
| slug / tableId inexistente | 400 / 404 |
| Red caída | Error de red en form; no navegar a /ok |
| Email mal formado | 400 |
| Doble submit | Botón disabled mientras pending |
[DECISIÓN] No se bloquea el POST aunque seats estén cubiertos: el jugador puede entrar en WAITLIST después, por decisión HOST. Bloquear en cliente mentiría sobre la barra. El CTA cambia de copy, no de permiso.
F4 — Success (PLAYER)
/ok?code=RD-… → copy de acuse + code
→ “Esto no confirma la silla”
→ CTA volver al feed / ir a la mesa
| Paso | Acción | Sistema |
|---|---|---|
| 1 | Lee acuse y código | UI (query/state) |
| 2 | Conserva el código (captura, nota) | Fuera de app |
| 3 | Espera mensaje / llamada del HOST | Fuera de app |
AC: el código es seleccionable; el disclaimer es visible sin scroll en desktop.
F5 — Login JWT (HOST)
/login → POST /api/auth/login { email, password }
→ 200 { accessToken, user } → localStorage → crea session
→ redirect `next` o /inbox
→ 401 → mensaje error en form
| Campo | Validación cliente | API |
|---|---|---|
| required, email | email válido | |
| password | required, min 6 | min 6 |
Éxito: token guardado; user role=HOST, nombre Nerea Solís.
Credencial demo: nerea@ronda.club / password123. [COMPROBADO]
| Caso | Comportamiento |
|---|---|
| Credenciales inválidas | 401 + mensaje |
| Red caída | Error de red en UI |
| Token caducado en inbox | 401 en GET → re-login |
next abierto | Solo paths internos (/inbox, /publicar, /yo) |
F6 — Inbox (HOST)
/inbox (token en cliente)
→ GET /api/requests
→ Render cards
| Caso | Comportamiento |
|---|---|
| Lista con items | Cards: code, nombre, mesa, status, createdAt |
| Lista vacía | Empty: “Nadie ha pedido asiento todavía.” |
| Fallo red / 401 | Error + reintentar / re-login |
[DECISIÓN] Inbox sin filtros de status en v1 (filtro = L2+). Orden createdAt desc.
F7 — Detalle y cambio de estado (HOST)
/inbox/:id
→ GET /api/requests/:id (si el endpoint existe; si no, item del list)
→ UI: contacto + mesa + seats + nº solicitudes de la mesa + notas
→ PATCH /api/requests/:id/status { status }
→ 200 SeatRequest actualizado
Transiciones típicas (máquina simple L2)
| Desde | Hacia | Intención |
|---|---|---|
| NEW | CONTACTED | Nerea inició contacto |
| CONTACTED | SEATED | Hay silla; compromiso firme |
| CONTACTED | WAITLIST | Cupo cubierto / pendiente de baja |
| NEW | WAITLIST | Mesa ya llena al abrir la solicitud |
| NEW | SEATED | Confirmación inmediata en barra (atajo) |
| * | CANCELLED | Desiste, duplicado, no localizable |
| WAITLIST | SEATED | Se libera silla |
| SEATED | CANCELLED | Baja posterior / no-show |
| * | * | Corrección operativa (API acepta enum) |
Éxito: badge actualizado; lista refleja al volver.
Errores: 404 id; 401 sin token; 400 status inválido.
Regla de integridad H3 (ops, no trigger DB v1): HOST no debe marcar SEATED si seatedCount >= seats. La UI avisa; el API L2 v1 no rechaza (corrección humana). Rechazo duro = L2+.
F8 — Publicar mesa (HOST)
/publicar
→ validación cliente
→ POST /api/tables {
title, mechanic, neighborhood,
venue, address, startAt, seats,
summary, description?
}
→ 201 GameTable { slug, ... }
→ /mesas/:slug
| Campo | Validación cliente | API |
|---|---|---|
| title | required, min 3 | min 3 |
| mechanic | required enum | enum Mechanic |
| neighborhood | required enum | enum Neighborhood |
| venue | required | required |
| address | required | required |
| startAt | required, datetime futuro o hoy | ISO-8601 |
| seats | required, 2–12 | int 2–12 |
| summary | required, max 180 | max 180 |
| description | optional | optional |
| imageKey | optional | default placeholder |
Éxito: mesa aparece en GET /api/tables y en el feed.
Error: 401 sin token; 400 DTO.
[DECISIÓN] El slug se genera en servidor (kebab del título + sufijo si colisión). El HOST no elige slug.
F9 — Logout (HOST)
Click "Salir" en /yo o inbox
→ borra token cliente
→ (opcional) invalida fila en sessions
→ /login
Matriz de errores global
| Código | Cuándo | UI |
|---|---|---|
| 400 | DTO inválido | Mensaje campo / genérico |
| 401 | Sin/mal token | Re-login |
| 404 | id o slug no existe | Mensaje + volver |
| 5xx / network | API caída | Error + retry |
Eventos de dominio (para analytics / blueprint)
| Evento | Dispara |
|---|---|
table_opened | Click card o deep-link ficha |
rsvp_submitted | POST 201 |
host_login | POST login 200 |
status_changed | PATCH 200 |
table_published | POST tables 201 |
Flujos fuera de alcance v1
- Registro / recuperación de password de HOST
- “Mis solicitudes” por código público
- Notificación email / WhatsApp automática
- Filtros multi-criterio en inbox
- Asignación multi-HOST por local
- Pago / fianza
- Edición / baja de mesa publicada por UI (solo alta)
- Check-in QR en la puerta
05-data-model.md
05 — Modelo de datos — RONDA
1. Visión general
Dominio L2 de feed de mesas + cola de asiento:
| Entidad | Tabla SQL | Propósito |
|---|---|---|
| User | users | Identidad de anfitrión (rol HOST) |
| Session | sessions | Sesión JWT / token persistido |
| GameTable | game_tables | Mesa publicada (oferta de esta noche) |
| SeatRequest | seat_requests | Solicitud de asiento ligada a una mesa |
Base: PostgreSQL (Neon project curly-lab-77015594) · Bootstrap: ensureSchema al arrancar la API · IDs: cuid() o uuid.
[DECISIÓN] Se llama game_tables y no tables (palabra reservada SQL).
[DECISIÓN] ensureSchema (CREATE TABLE IF NOT EXISTS + seed idempotente) en v1; no se exige Prisma para el slice.
2. Enums (aplicación; persistidos como TEXT + check)
Role
| Valor | Descripción |
|---|---|
HOST | Operador del inbox y alta de mesas; único rol autenticado v1 |
RequestStatus
| Valor | Descripción |
|---|---|
NEW | Recién creada por el form público |
CONTACTED | HOST ha iniciado contacto |
SEATED | Plaza confirmada (silla en la mesa) |
WAITLIST | En espera de silla |
CANCELLED | Anulada |
Mechanic
TILE | CARDS | MINIS | COOP | COLLECTION
Neighborhood
LAVAPIES | EMBAJADORES | CHAMBERI | MALASANA
3. Diagrama ER (texto)
users
id, email, password_hash, name, role(HOST)
created_at, updated_at
1 ──< sessions
1 ──< game_tables -- host_id
sessions
id, user_id → users
token_hash, expires_at
created_at
game_tables
id, slug (unique), title, summary, description
mechanic, neighborhood, venue, address
start_at, seats, image_key
host_id → users
created_at, updated_at
1 ──< seat_requests
seat_requests
id, code (unique)
table_id → game_tables
full_name, email, phone?
party_size (default 1)
notes
status (default NEW)
created_at, updated_at
No hay FK entre
seat_requestsyusersen v1: el form es anónimo. HOST opera sobre el conjunto global.
4. Tablas (ensureSchema)
users
| Campo | Tipo | Constraints |
|---|---|---|
| id | TEXT | PK |
| TEXT | UNIQUE NOT NULL | |
| password_hash | TEXT | NOT NULL (bcrypt) |
| name | TEXT | NOT NULL |
| role | TEXT | NOT NULL DEFAULT 'HOST' |
| created_at | TIMESTAMPTZ | DEFAULT now() |
| updated_at | TIMESTAMPTZ | DEFAULT now() |
sessions
| Campo | Tipo | Constraints |
|---|---|---|
| id | TEXT | PK |
| user_id | TEXT | NOT NULL FK → users(id) ON DELETE CASCADE |
| token_hash | TEXT | NOT NULL |
| expires_at | TIMESTAMPTZ | NOT NULL |
| created_at | TIMESTAMPTZ | DEFAULT now() |
[DECISIÓN] El JWT viaja en Authorization: Bearer. sessions permite invalidar (logout) sin esperar expiración del JWT.
game_tables
| Campo | Tipo | Constraints |
|---|---|---|
| id | TEXT | PK |
| slug | TEXT | UNIQUE NOT NULL, kebab-case |
| title | TEXT | NOT NULL |
| summary | TEXT | card (~140) |
| description | TEXT | ficha; default '' |
| mechanic | TEXT | NOT NULL |
| neighborhood | TEXT | NOT NULL |
| venue | TEXT | nombre del local |
| address | TEXT | dirección corta |
| start_at | TIMESTAMPTZ | hora de la mesa |
| seats | INTEGER | > 0 |
| image_key | TEXT | filename en assets (azulejos.jpg …) |
| host_id | TEXT | NOT NULL FK → users(id) |
| created_at / updated_at | TIMESTAMPTZ |
Índice recomendado: (start_at ASC) para el feed.
seat_requests
| Campo | Tipo | Constraints |
|---|---|---|
| id | TEXT | PK |
| code | TEXT | UNIQUE NOT NULL, formato RD-MMDD-NNN |
| table_id | TEXT | NOT NULL FK → game_tables(id) |
| full_name | TEXT | NOT NULL |
| TEXT | NOT NULL | |
| phone | TEXT | NULL |
| party_size | INTEGER | DEFAULT 1, 1–2 |
| notes | TEXT | DEFAULT '' |
| status | TEXT | DEFAULT 'NEW' |
| created_at / updated_at | TIMESTAMPTZ |
Índice recomendado: (created_at DESC) para inbox; (table_id, status) para conteo SEATED.
5. Reglas de integridad y negocio
| Regla | Implementación |
|---|---|
| Create público | POST /api/requests sin guard |
| List/status | JwtAuthGuard → 401 sin token |
| Publicar mesa | POST /api/tables con guard HOST |
| Catálogo público | GET /api/tables y :slug sin guard |
| Código único | code unique; RD-MMDD- + secuencia 001–999 del día [DECISIÓN] (retry NNN+1 si colisión, máx. 5) |
| Status default | NEW al crear |
| Password | Nunca en claro; solo password_hash |
| Notas | Coalesce a "" si omitidas |
| Orden inbox | created_at desc |
| Orden feed | start_at asc (esta noche primero; no “destacados”) |
| Timezone códigos y “Hoy” | Europe/Madrid [DECISIÓN] |
| SEATED vs seats | Aviso UI; no check DB v1 (H3 operativa) |
| Logout | DELETE o expire de sessions + borrar token cliente |
6. Contratos API
GET /api/tables (público)
Response 200: GameTable[] ordenado por start_at asc. Incluye campos de card + ficha; sin PII de solicitudes.
Query opcional L2+ (no Must): from, to ISO. v1 puede filtrar “hoy” en cliente.
GET /api/tables/:slug (público)
GameTable + hostName + seatedCount (agregado) o 404.
POST /api/tables (JWT HOST)
Body
| Campo | Tipo | Req |
|---|---|---|
| title | string | sí |
| mechanic | Mechanic | sí |
| neighborhood | Neighborhood | sí |
| venue | string | sí |
| address | string | sí |
| startAt | string ISO | sí |
| seats | number | sí |
| summary | string | sí |
| description | string | no |
| imageKey | string | no |
Response 201: GameTable con slug generado.
POST /api/requests (público)
Body
| Campo | Tipo | Req |
|---|---|---|
| tableId o slug | string | sí (uno de los dos) |
| fullName | string | sí |
| string | sí | |
| phone | string | no |
| partySize | number | no (default 1) |
| notes | string | no |
Response 201: SeatRequest + table embebido mínimo (title, slug, startAt, venue).
POST /api/auth/login
Body: { email, password }
Response 200: { accessToken, user: { id, email, name, role } }
Efecto: inserta fila en sessions.
GET /api/requests (JWT HOST)
Array SeatRequest + table, created_at desc.
PATCH /api/requests/:id/status (JWT HOST)
Body: { status: RequestStatus }
Response 200: SeatRequest actualizado.
GET /api/requests/:id (JWT HOST) — Should
SeatRequest + table (seats) + seatedCount o 404.
7. Seed de referencia (2026-08-15)
HOST
| Nombre | Password | |
|---|---|---|
| Nerea Solís | nerea@ronda.club | password123 |
[COMPROBADO] Credencial demo del brief.
game_tables
| slug | Título | Mecánica | Barrio | Local | Hora | Seats | Asset |
|---|---|---|---|---|---|---|---|
| azulejos | Azulejos de Lavapiés | TILE | LAVAPIES | Café La Palma 12 | 20:30 | 4 | azulejos.jpg |
| cartas | Cubilete y cartas | CARDS | EMBAJADORES | Bar El Sur | 19:00 | 5 | cartas.jpg |
| minis | Mazmorra de Chamberí | MINIS | CHAMBERI | Local asociativo | 18:00 | 5 | minis.jpg |
| cooperativo | Farol cooperativo | COOP | MALASANA | Librería Tipos Infames | 21:00 | 4 | cooperativo.jpg |
| coleccion | Colección de patio | COLLECTION | MALASANA | Terraza Malasaña | 17:30 | 6 | coleccion.jpg |
[COMPROBADO] Títulos, locales, horas y aforos salen del brief.
Direcciones extendidas, textos de reglas y
start_atconcreto (fecha del seed) son contenido demo. [SUPUESTO de catálogo]
Textos seed recomendados (no lorem):
| slug | Summary | Reglas cortas (description) |
|---|---|---|
| azulejos | Tile-laying a las 20:30 en La Palma 12. Cuatro sillas, café y azulejos. | Enseñamos la primera ronda. Trae ganas, no caja. Mesa de 4; si llegas tarde avisa. |
| cartas | Dados y baza en El Sur, Embajadores, 19:00. Cinco sillas. | Cubilete compartido. Reglas en servilleta. No es cash game. |
| minis | Mazmorra corta en Chamberí, 18:00. Cinco sillas, minis de la casa. | One-shot 2 h. Fichas y dados en la mesa. Nivel 1, sin ficha de personaje previa. |
| cooperativo | Farol cooperativo en Tipos Infames, 21:00. Cuatro sillas. | Perdemos juntos o no. Tráiler de reglas 5 min. Silencio de librería hasta las 21:10. |
| coleccion | Colección al sol de terraza en Malasaña, 17:30. Seis sillas. | Sets y cartas al aire. Si llueve, se cancela en inbox. |
seat_requests
| Código | Nombre | Mesa | Estado | Notas demo |
|---|---|---|---|---|
| RD-0815-001 | Leo Navas | azulejos | NEW | “Llego del metro hacia Lavapiés; primera vez con azulejos.” |
| RD-0815-002 | Marta Gil | cartas | CONTACTED | — |
| RD-0815-003 | Iago Freire | coleccion | SEATED | — |
| RD-0815-004 | Youssef Amrani | cooperativo | WAITLIST | Mesa de 4 ya cubierta en barra |
| RD-0815-005 | Pilar Soto | minis | CANCELLED | Duplicada |
El sufijo de fecha del código de seed es el día del caso; la generación runtime usa Europe/Madrid.
Idempotencia seed: upsert HOST por email; upsert mesas por slug; upsert requests por code. No duplicar al re-arrancar.
8. Generación de código
RD- + MMDD (Europe/Madrid) + - + NNN
NNN = siguiente entero del día, padded 3. Si unique falla, reintentar NNN+1 (máx. 5).
Ejemplo el 15 de agosto: RD-0815-001.
9. Evolución posible (no implementada)
| Cambio | Nivel |
|---|---|
| Check DB SEATED + party_size ≤ seats | L2+ |
Tabla venues normalizada | L3 |
| Soft delete + audit log de status | L2+ |
| User PLAYER con historial | L3 |
party_size > 2 y reservas de grupo | L3 |
| Edición / cancelación de mesa | L2+ |
06-tech-stack.md
06 — Stack tecnológico — RONDA
1. Visión
| Capa | Tecnología | Notas |
|---|---|---|
| Frontend | Angular standalone + signals | Puerto 4200 · script pnpm start |
| Estilos | Tailwind CSS | Tokens signage (suelo/tinta/cromo/felt/liner/ficha) |
| Backend | NestJS TypeScript | Puerto 3015, prefijo /api · script pnpm api |
| Persistencia | SQL + ensureSchema | Tablas users, sessions, game_tables, seat_requests |
| DB | Neon PostgreSQL | project curly-lab-77015594 |
| Auth | JWT + tabla sessions | Rol HOST |
| Diseño | Paper | 01M023RM2TZ49CYM2FN1FRRGNY |
| Repo | /Users/cristian/orca/ronda-app/ | GitHub Criscode2022/ronda-app |
| Package manager | pnpm | apps independientes vía pnpm --filter |
Stack fijo del cron (D-P0-03). No React/Next/Firebase.
[COMPROBADO] Puerto 3015, Neon y pnpm constan en el brief de implementación.
2. Estructura
ronda-app/
├── apps/api/ # Nest + ensureSchema (paquete independiente)
├── apps/web/ # Angular + Tailwind (paquete independiente)
├── package.json # scripts api / start + pnpm filter
└── pnpm-workspace.yaml
D-P1-03 (adaptado): apps independientes. [DECISIÓN] pnpm workspaces + --filter (no npm workspaces que rompen Angular; no npm --prefix si el brief pide pnpm).
Scripts raíz esperados:
| Script | Acción |
|---|---|
pnpm api | Arranca Nest en :3015 |
pnpm start | Arranca Angular en :4200 |
pnpm --filter api … | Comandos solo API |
pnpm --filter web … | Comandos solo web |
3. API pública vs JWT
| Método | Ruta | Auth |
|---|---|---|
| GET | /api/tables | Público |
| GET | /api/tables/:slug | Público |
| POST | /api/requests | Público |
| POST | /api/auth/login | Público |
| GET | /api/requests | JWT HOST |
| PATCH | /api/requests/:id/status | JWT HOST |
| POST | /api/tables | JWT HOST |
| GET | /api/requests/:id | JWT HOST (Should) |
CORS: origen http://localhost:4200 en local.
4. Justificación
| Elección | Razón |
|---|---|
| Angular + Nest | Alineación con la serie daily y el handoff del estudio |
| Neon serverless | Volumen bajo (5–12 mesas/semana, S1); no hace falta cola Redis |
| ensureSchema | Bootstrap diario rápido; 4 tablas; seed idempotente al boot |
| JWT + sessions | D-P1-05: panel interno cerrado; logout invalidable |
| Feed en servidor, recorte Hoy en cliente o query | Source of truth = start_at; no Elasticsearch |
| Puerto 3015 | Evitar colisión con DERIVA :3014 y PIZARRA :3013 |
5. Variables
.env local (nunca git):
| Variable | Servicio | Descripción |
|---|---|---|
DATABASE_URL | API | Neon curly-lab-77015594 |
JWT_SECRET | API | Firma tokens |
PORT | API | opcional, 3015 |
JWT_EXPIRES | API | opcional (p. ej. 12h) |
.env.example solo placeholders.
Web: URL de API en ApiService (default http://localhost:3015/api).
6. Dependencias de producto (mínimas)
API
@nestjs/common/core/platform-express@nestjs/jwt+ passport-jwt (o verificación manual)bcrypt(obcryptjs)- cliente Postgres (
pg) class-validator+class-transformer
Web
- Angular standalone
- Tailwind 3
- fuentes: Zilla Slab + Mulish (Google Fonts o self-host)
7. Arranque (contrato)
cd /Users/cristian/orca/ronda-app
pnpm install
# apps/api/.env → DATABASE_URL + JWT_SECRET
pnpm api # http://localhost:3015
pnpm start # http://localhost:4200
Al boot, la API ejecuta ensureSchema y seed si las tablas están vacías o por upsert.
8. Lo que este stack no es
| No | Por qué |
|---|---|
| Prisma obligatorio | Brief pide ensureSchema |
| npm workspaces | AP-06 |
| Firebase Auth | Stack fijo JWT + Neon |
| Next.js / React | D-P0-03 |
| Mapa / tiles | Fuera de producto |
07-creative-direction.md
07 — Dirección creativa — RONDA
1. Mood
Signage — cartelería de local de barrio: pizarra de tiza sustituida por placa esmaltada, número de mesa, hora en negro y un amarillo de semáforo que dice “abierto esta noche”.
No es taberna candlelit (primer instinto). No es dusk cartográfico (DERIVA). No es wine de editorial (PLIEGO). No es chalky de aula (PIZARRA).
Candidatos del brief y veredicto:
| Candidato | Veredicto |
|---|---|
| Taberna candlelit | Descartado — cerca de DERIVA dusk / PLIEGO wine |
| Felt casino | Descartado — implica apuesta |
| Phosphor arcade | Descartado — 8-bit, no mesa de café |
| Signage | Elegido |
| Alpine | Descartado — outdoor, no barrio |
[COMPROBADO] Mood signage y paleta salen de 00-day-brief.md.
[DECISIÓN] R-HY = flyer clásico de caja de juego (titular Zilla, foto de mesa, hora grande) + dock flotante innovador usable.
2. Paleta
| Rol | Token | Hex | Referente |
|---|---|---|---|
| Suelo | bg | #FFFFFF | Placa / papel de carta del local |
| Tinta | ink | #111111 | Serigrafía de cartel |
| Cromo | chrome | #F5C400 | Amarillo de “abierto” / dock activo |
| Felt | felt | #1F4D3A | Paño de mesa, SEATED |
| Liner | liner | #FFF6C2 | Reverso de caja, cards |
| Ficha | chip | #E24B2A | Ficha naranja, WAITLIST, urgencia |
| Tinta suave | ink-muted | #3A3A3A | Meta (barrio, mecánica) |
| Peligro | danger | #E24B2A | Misma ficha; no introducir rojo extra |
[COMPROBADO] Hex literales del brief: #FFFFFF #111111 #F5C400 #1F4D3A #FFF6C2 #E24B2A.
Combinaciones legales
| Superficie | Texto | Uso |
|---|---|---|
#FFFFFF | #111111 | Body, feed |
#FFF6C2 | #111111 | Cards |
#F5C400 | #111111 | CTA primario, dock activo |
#1F4D3A | #FFFFFF | Badge SEATED, superficie de mesa |
#111111 | #FFFFFF | CTA secundario, wordmark invertido |
#E24B2A | #FFFFFF | Badge WAITLIST (texto ≥14px bold) |
No usar cromo 13px sobre blanco como único indicador (contraste de acento, no de texto).
3. Tipo
| Rol | Familia | Cortes | Uso |
|---|---|---|---|
| Display | Zilla Slab | 56 / 36 / 24 · 600–700 | Wordmark, hora, título de mesa |
| UI | Mulish | 16 / 14 · 400/600/700 | Body, labels, form, dock |
[COMPROBADO] Familias y escalas del brief.
Evitar (cooldown / contaminación de serie): Lora, Karla, Newsreader, Atkinson, Outfit, Archivo, Fraunces.
4. Principios
- La cronología es el monumento. Hora grande, no hero 2 columnas.
- Radio 0–6px. Cartel, no píldora atlética.
- Foto real de la mesa, nunca color sólido ni
image_gencomo captura de producto (D-P0-09). - Copy honesto: solicitud ≠ plaza.
- Dock flotante siempre. Cinco slots, cromo en el activo.
- Contraste AA tinta sobre blanco/liner; labels ≥14px.
- Flyer + dock: el card parece recorte de caja; la navegación no parece caja.
5. Referentes visuales (no clonar)
| Referente | Qué se toma | Qué se deja |
|---|---|---|
| Cartel de bar de copas | Hora, dirección, tinta/amarillo | Foto de copas, script caligráfico |
| Inserto de caja de juego | Foto de componentes, título slab | Rating BGG, precio, código de barras |
| Señal de metro Madrid | Claridad, pocos colores | Pictogramas de línea |
| Casino felt | Verde profundo puntual | Fichas por todas partes |
6. Art direction de media
| Asset | Debe verse | No debe verse |
|---|---|---|
hero.jpg | Atmósfera de mesa / local, luz real | Mockup de UI, stock de dados genérico recortado mal |
azulejos.jpg | Azulejos / tiles en mesa de café | Packaging de Amazon |
cartas.jpg | Cartas y cubilete, barra | Poker cash agresivo |
minis.jpg | Miniaturas, mazmorra de salón | Wargame militar realista bélico |
cooperativo.jpg | Mesa compartida, librería | Escape room de ticket |
coleccion.jpg | Terraza, tarde, colección | Club de playa |
7. Motion
| Gesto | Spec |
|---|---|
| Entrada feed | Fade 160ms, stagger ≤40ms por card |
| Dock | TranslateY 0; no hide-on-scroll en v1 |
| Badge estado | Crossfade 120ms |
| Prohibido | Parallax, scroll-jacking, confetti de “reserva” |
8. Anti-dirección
| No | Por qué |
|---|---|
| Dusk teal / mapa | DERIVA |
| Chalky pizarra escolar | PIZARRA |
| Slate/teal SaaS | Cooldown de serie |
| Hero 2-col + 3 steps | AP-12 |
| Rounded-full lime | App de fitness |
| Emoji como icono de mecánica | Signage usa palabra, no sticker |
08-design-system.md
08 — Design system — RONDA
1. Tokens
Ver Paper UI-00 y apps/web/tailwind.config.js.
| Token Tailwind | Valor | Uso |
|---|---|---|
ronda-bg | #FFFFFF | Fondo de página |
ronda-ink | #111111 | Texto, bordes fuertes |
ronda-chrome | #F5C400 | Acento, CTA, dock activo |
ronda-felt | #1F4D3A | SEATED, superficies de mesa |
ronda-liner | #FFF6C2 | Cards, rails, fondo dock |
ronda-chip | #E24B2A | WAITLIST, asientos bajos |
ronda-muted | #3A3A3A | Meta |
font-display | Zilla Slab | Titulares |
font-sans | Mulish | UI |
radius-sm | 2px | Chips de mecánica |
radius-md | 6px | Cards, inputs |
radius-lg | 12px | Dock container |
Tipo — escala
| Estilo | Familia | Size / line | Peso | Uso |
|---|---|---|---|---|
| Display XL | Zilla Slab | 56 / 60 | 700 | Portada, hora hero en ficha |
| Display L | Zilla Slab | 36 / 40 | 700 | Título mesa, “Esta noche” |
| Display M | Zilla Slab | 24 / 28 | 600 | Sección, código RD- |
| Body | Mulish | 16 / 24 | 400 | Párrafos, form |
| Meta | Mulish | 14 / 20 | 600 | Barrio, mecánica, dock label |
| Micro | Mulish | 12 / 16 | 600 | Badge; no usar en cromo sobre blanco |
Espaciado
Escala 4: 8 / 12 / 16 / 24 / 32 / 48.
Feed: gap 16 entre cards. Dock: padding 8, gap 4 entre slots. Ficha: bloque 24.
2. Componentes
| Pieza | Spec |
|---|---|
| Dock | Fijo bottom; fondo liner 92% + blur; 5 slots 56×48; icono 20 + label 11–12; activo = cromo + tinta |
| Wordmark | “RONDA” Zilla 24/700 tracking amplio; subtítulo “Esta noche hay mesa.” Mulish 14 |
| Card de mesa | Foto 16/10 object-top; hora Zilla 24; título; local · barrio; asientos n/seats; mecánica label |
| Button primary | Fondo cromo, texto tinta, h-44, radius 6 |
| Button secondary | Fondo tinta, texto blanco |
| Button ghost | Borde tinta 1px, fondo transparente |
| Button waitlist | Fondo ficha, texto blanco; label “Lista de espera” |
| Badge NEW | cromo / tinta |
| Badge CONTACTED | liner / tinta |
| Badge SEATED | felt / blanco |
| Badge WAITLIST | ficha / blanco |
| Badge CANCELLED | liner / muted; line-through opcional |
| Form field | Label Mulish 14/700 visible; borde tinta 1px; h-44; focus outline cromo 2px offset 2 |
| Disclaimer | Fondo liner, borde izquierda cromo 4px, texto 14 |
| Skeleton | Bloques liner, 5 filas feed; sin shimmer agresivo |
| Empty | Titular Zilla 24 + 1 acción |
| Error | Banner tinta + CTA Reintentar cromo |
3. Layouts
Feed (UI-01 / UI-10)
[ wordmark + fecha ]
[ card ]
[ card ]
[ card ]
[ … ]
[ dock flotante ]
Desktop ≥960px: cards a 640–720px centradas (columna de cartel), no grid 3×N de marketplace.
[DECISIÓN] Una columna refuerza H-FEED y evita “catálogo de SKU”.
Ficha (UI-02)
Foto full-bleed de la columna → hora + título → meta (local, barrio, asientos, HOST) → reglas → disclaimer → CTA sticky sobre el dock.
Inbox (UI-06)
Lista densa: código · nombre · mesa · badge. Desktop puede añadir columna de acciones.
4. Estados
| Estado | Superficie | Contenido |
|---|---|---|
| Empty feed hoy | UI-07 | “Esta noche no hay mesa.” + Ver la semana |
| Empty inbox | UI-07 | “Nadie ha pedido asiento todavía.” |
| Loading | UI-09 | 5 skeletons |
| Error red | UI-08 | “No hemos podido cargar las mesas.” + Reintentar |
| Validación email | Form | Texto bajo campo, no solo borde |
| 401 inbox | Login | Redirect /login?next=/inbox |
| Aforo 0 | Ficha / form | CTA WAITLIST; disclaimer igual |
5. Iconografía del dock
| Slot | Significado visual | No |
|---|---|---|
| Hoy | Reloj / sol bajo | Emoji 🔥 |
| Mesas | Stack de cartas / mesa vista cenital | Pin de mapa |
| Publicar | + en placa | FAB Material genérico desligado del dock |
| Inbox | Bandeja | Burbuja de chat Discord |
| Yo | Figura simple | Avatar foto real v1 |
Iconos 20×20 stroke 1.75, tinta. Activo: trazo tinta sobre disco cromo.
6. Breakpoints
| Nombre | Ancho | Comportamiento |
|---|---|---|
| Mobile | 390 | Feed + dock; CTA 100% |
| Tablet | 768 | Columna 600, dock igual |
| Desktop | 960+ | Columna 680; inbox puede 2 col |
Targets táctiles ≥44px (dock slots 56×48 cumple).
7. No usar
| Prohibido | Motivo |
|---|---|
| Rounded-full lime / teal | Fitness / SaaS |
| Hero 2-col + 3 cards | AP-12 |
| Badges “SaaS pill” pastel | Rompe signage |
| Emoji como icono de mecánica | Ver 07 |
| Top-nav sticky de 7 links | Rompe S-DOCK |
| Grid marketplace 3×N | Rompe H-FEED |
| Confetti al success | Mentiría “ya tienes silla” |
09-content-guide.md
09 — Guía de contenido — RONDA
1. Voz
Cercana de barrio, concreta, de barra. Tuteo. Segunda persona.
Sin jerga de marketplace (“experiencia premium”), sin Discord (“únete al server”), sin sede electrónica.
El producto informa y recoge, no vende una noche mágica.
2. Palabras permitidas / prohibidas
| Usar | No usar |
|---|---|
| Solicitud / pedir asiento | Reserva confirmada, booking, “ya estás dentro” |
| El anfitrión te escribe | “Plaza garantizada”, “ticket emitido” |
| Lista de espera | Overbooking, sold out (tono concierto) |
| Mesa | Evento, sesión, experience, lobby |
| Local / café / bar / terraza | Venue, campus, store |
| Esta noche hay mesa | “Descubre nuestro ecosistema” |
| Código RD- | Referencia UUID, localizador de vuelo |
| Sentada / SEATED | Inscrita, matriculada, enrolled |
| Anfitrión / HOST | Community manager, dungeon master (salvo mesa minis) |
3. Microcopy clave (literales de producto)
| Superficie | Copy |
|---|---|
| Eslogan | Esta noche hay mesa. |
| Home | Hoy en Malasaña, Lavapiés y Chamberí. |
| Card CTA implícito | Toda la card es el enlace; no “Ver más” |
| Disclaimer ficha | Pedir asiento no confirma la silla. El anfitrión te escribe. |
| Form título | Pedir asiento en {título} |
| Form submit (hay hueco) | Enviar solicitud |
| Form submit (lleno) | Pedir lista de espera |
| Form disclaimer | Esto es una solicitud, no una plaza confirmada. |
| Success titular | Solicitud enviada. |
| Success cuerpo | Guarda este código. No tienes silla todavía. Nerea (o el anfitrión) te contacta antes de la hora. |
| Success código | RD-0815-001 (seleccionable) |
| Empty hoy | Esta noche no hay mesa. |
| Empty hoy CTA | Ver la semana |
| Empty inbox | Nadie ha pedido asiento todavía. |
| Error feed | No hemos podido cargar las mesas. |
| Error retry | Reintentar |
| Login titular | Anfitrión |
| Login submit | Entrar |
| Login error | Email o contraseña no valen. |
| Inbox titular | Solicitudes |
| PATCH SEATED | Sentar |
| PATCH WAITLIST | Lista de espera |
| PATCH CONTACTED | Marcar contactada |
| PATCH CANCELLED | Cancelar |
| Publicar titular | Abrir mesa |
| Publicar submit | Publicar esta noche |
| Yo / salir | Salir |
[DECISIÓN] El disclaimer aparece tres veces: ficha, form, success. No es opcional.
4. Códigos
Formato RD-MMDD-NNN. Se lee en voz alta en la barra: “erre de, ocho quince, cero cero uno”.
No UUID. No QR obligatorio en v1.
5. Nombres propios del seed
| Nombre | Uso |
|---|---|
| Leo Navas | PLAYER; solicitud azulejos |
| Nerea Solís | HOST; copy de “te escribe Nerea” en success de seed |
| Café La Palma 12 | Local azulejos |
| Bar El Sur | Local cartas |
| Local asociativo (Chamberí) | Local minis |
| Librería Tipos Infames | Local cooperativo |
| Terraza Malasaña | Local colección |
No inventar reseñas de Google ni horarios oficiales de esos locales. [SUPUESTO de catálogo demo]
6. Tono por estado
| Status | Frase inbox (HOST) | Frase que Nerea podría mandar (fuera de app) |
|---|---|---|
| NEW | Sin contacto | — |
| CONTACTED | Escrita / llamada | “He visto tu RD-0815-001. ¿Llegas a las 20:30?” |
| SEATED | Silla | “Tienes silla. Trae el código.” |
| WAITLIST | Espera | “La mesa de 4 está llena. Te aviso si hay baja.” |
| CANCELLED | Fuera | “Cancelamos esta solicitud.” |
La app no envía esos mensajes en v1. El copy solo prepara el habla.
7. Capitalización y números
- Wordmark: RONDA en versales.
- Horas:
20:30(24 h, Europe/Madrid), no8:30 PM. - Asientos:
2/4 sillas(ocupadas estimadas / total) cuando hay dato; si no,4 sillas. - Barrios: Malasaña, Lavapiés, Chamberí, Embajadores (tilde correcta).
8. Idioma
es-ES. Fácil lectura. EN no en v1.
No mezclar “table” en UI. En API sí: game_tables, tableId.
9. Accesibilidad de copy
- No transmitir estado solo con color (“el amarillo significa nueva”).
- Badge lleva texto: Nueva, Contactada, Sentada, Lista de espera, Cancelada.
- Alt de fotos: “Azulejos sobre mesa de café en Lavapiés”, no “imagen1”.
10. Piezas que no se escriben
| No | Motivo |
|---|---|
| Precios, “consumición mínima” | RONDA no cobra; el local es otro contrato |
| “Plazas confirmadas al instante” | Rompe H2 |
| Ranking de anfitriones | Reputación, otro producto |
| Lorem / “lorem ipsum mesa” | Prohibido en el case |
10-accessibility.md
10 — Accesibilidad — RONDA
Objetivo: WCAG 2.2 AA. No se declara conformidad legal certificada.
Este documento cubre la UI (feed, form, dock, inbox). No audita la accesibilidad física de los locales seed. [SUPUESTO]
1. Decisiones
| Tema | Decisión |
|---|---|
| Tipo UI | Mulish ≥16px body |
| Display | Zilla Slab no se usa por debajo de 24px |
| Contraste | Tinta #111111 sobre #FFFFFF / #FFF6C2 |
| CTA cromo | Texto tinta sobre #F5C400 (no cromo como texto pequeño) |
| Felt / ficha | Texto blanco ≥14px bold |
| Foco | Outline 2px cromo, offset 2px, visible (no outline-none global) |
| Dock | 5 botones reales (<a> / <button>), no divs clicables |
| Imágenes | alt descriptivo de la mesa |
| Form | Labels visibles, no placeholder-only |
| Errores | Texto, no solo color |
| Mobile | Targets ≥44px (dock 56×48, CTA h-44) |
| Movimiento | Fade 160ms; prefers-reduced-motion: reduce → 0 |
| Live regions | aria-live="polite" en recuento empty/error del feed |
2. Contraste (comprobación de diseño)
| Par | Uso | Nota |
|---|---|---|
#111111 / #FFFFFF | Body | Pasa AA y AAA cuerpo |
#111111 / #FFF6C2 | Cards | Pasa AA |
#111111 / #F5C400 | CTA / dock activo | Pasa AA para texto ≥14px bold |
#FFFFFF / #1F4D3A | Badge SEATED | Pasa AA |
#FFFFFF / #E24B2A | Badge WAITLIST | Verificar ≥14px bold; no usar en 12px |
#F5C400 / #FFFFFF como texto | — | Prohibido para body |
3. Teclado
| Flujo | Orden |
|---|---|
| Feed | Wordmark → primera card → … → dock (Hoy, Mesas, Publicar, Inbox, Yo) |
| Ficha | Back → CTA → dock |
| Form | Nombre → email → teléfono → notas → submit → dock |
| Inbox | Lista (cada card) → detalle acciones → dock |
| Login | Email → password → submit |
- Enter en card = navegar a ficha.
- Escape no cierra el dock (no es modal).
- Focus trap solo si hubiera modal; v1 no tiene modal salvo confirmación CANCELLED (Should).
4. Semántica
| Pieza | Markup |
|---|---|
| Feed | <main> + lista <ul> de cards; cada card un <article> o <li> con un único <a> |
| Hora | <time datetime="…"> |
| Dock | <nav aria-label="Principal"> |
| Slot activo | aria-current="page" |
| Form | <form> + <label for> |
| Disclaimer | No es alert; es texto normal + borde |
| Success código | <p><code> seleccionable |
| Badges | texto visible; no solo icono |
| Skeletons | aria-busy="true" en el contenedor; aria-hidden en placeholders |
5. Lector de pantalla — copy
| Situación | Anuncio |
|---|---|
| Card | “Azulejos de Lavapiés, veinte treinta, Café La Palma 12, Lavapiés, cuatro sillas, tile-laying” |
| CTA lleno | “Apuntarme a la lista de espera, Azulejos de Lavapiés” |
| Success | “Solicitud enviada. Código erre de ocho quince cero cero uno. No tienes silla todavía.” |
| Error | Anuncio en live region; foco al banner |
6. Producto vs local físico
| Lo que RONDA puede hacer | Lo que no afirma |
|---|---|
| Hora, dirección, aforo de sillas de juego | Rampa, baño accesible, bucle magnético del café |
| Form usable con teclado y zoom 200% | Que La Palma 12 sea un local accesible |
[DECISIÓN] v1 no incluye faceta a11y de sede (eso era PIZARRA). Un campo libre notes permite “voy en silla” sin fingir auditoría.
7. Riesgos
| Riesgo | Mitigación |
|---|---|
| Dock tapa el CTA en móvil | Padding-bottom del main ≥ 80px; CTA de ficha por encima |
| Zilla Slab en meta 12px | Prohibido; meta = Mulish 14 |
| Cromo como único estado activo | + aria-current + label “Hoy” |
| Foto sin alt | Checklist seed: 5 alts |
| Status bar iOS en Paper | No es producto |
| Motion del dock bounce | No bounce; reduced-motion = estático |
8. QA a11y (mínimo)
| # | Prueba | Pasa si |
|---|---|---|
| 1 | Teclado feed → ficha → form → success | Sin trampa; foco visible |
| 2 | Teclado login → inbox → PATCH | Acciones alcanzables |
| 3 | Zoom 200% móvil 390 | Dock usable; no solapa inputs |
| 4 | Lighthouse a11y ≥ 90 en / | Sin contrast fails de tokens |
| 5 | VoiceOver/NVDA: legend/hora anunciados | Card comprensible |
| 6 | prefers-reduced-motion | Sin stagger |
| 7 | Contraste badges WAITLIST / SEATED | Texto blanco bold ≥14 |
9. Criterios de aceptación
- Ningún control del dock o CTA mide menos de 44×44 CSS px.
- El disclaimer no depende del color cromo para ser entendido.
- Las 5 fotos seed tienen
altno vacío y no genérico. - El foco no se pierde al reintentar un error de red.
11-privacy-security.md
11 — Privacidad y seguridad — RONDA
No es un dictamen legal ni un DPIA. Es el contrato de producto para el vertical slice.
1. Datos
| Dato | Tabla | Clasificación | Uso |
|---|---|---|---|
| Nombre, email, teléfono | seat_requests | PII | Contactar solicitud |
| Notas | seat_requests | PII opcional | Contexto HOST |
| party_size | seat_requests | Operativo | Aforo |
| Mesa, estado, código | seat_requests + game_tables | Operativo | Cola y sillas |
| Email HOST, password hash | users | Credencial | Solo HOST |
| token_hash, expires_at | sessions | Secreto de sesión | Logout / caducidad |
Sin datos de salud, menores con tutor, geolocalización continua, ni pagos.
[DECISIÓN] El jugador no crea cuenta. Menos superficie de credenciales.
2. Base (supuesto de diseño)
[SUPUESTO] Interés del HOST en organizar una mesa privada/pública de ocio + consentimiento de contacto al enviar el form.
El texto del form debe mencionarlo:
Usaremos tu nombre y email (y teléfono si lo dejas) solo para escribirte por esta mesa.
No se cede a editoriales, no se usa para marketing de terceros.
3. Minimización
| Recogemos | No recogemos en v1 |
|---|---|
| fullName, email | DNI, fecha de nacimiento |
| phone opcional | Dirección de casa |
| notes libres | Foto de perfil |
| partySize 1–2 | Lista de juegos que posee |
[DECISIÓN] Teléfono opcional: el email basta para el acuse; el HOST prefiere WhatsApp en la práctica [HIPÓTESIS], pero no exigimos el número.
4. Retención
| Dato | Hipótesis operativa |
|---|---|
| Solicitudes | 12 meses desde start_at de la mesa; luego borrado o anonimización (full_name → “—”, email hash) |
| Sessions | Hasta expires_at o logout |
| Mesas | Mientras el HOST no las archive (archivo = L2+) |
| Demo Neon | No es producción; se puede resetear |
[HIPÓTESIS] 12 meses cubre una temporada de mesa y reclamaciones de “yo escribí”. No es plazo legal afirmado.
5. Autorización
| Recurso | Público | HOST JWT |
|---|---|---|
| GET tables / :slug | Sí | Sí |
| POST requests | Sí | Sí |
| GET / PATCH requests | 401 | Sí |
| POST tables | 401 | Sí |
| Password hashes | Nunca en response | Nunca |
Guards en API son la autoridad. La UI solo esconde.
[COMPROBADO] D-P1-05: panel interno cerrado desde el día 1.
JWT + sessions
- Login válido → bcrypt compare → insert
sessions→ emite JWT (sub, email, role,sid). - Request protegida → verifica firma +
sidno expirado. - Logout → expire/delete session; el JWT deja de valer aunque no haya caducado.
[DECISIÓN] Bearer header, no cookie de primer partido en v1 (demo localhost). CSRF residual bajo; XSS sigue siendo el riesgo (no innerHTML de notas sin escape).
6. Seguridad v1
| Control | Detalle |
|---|---|
| TLS hacia Neon | Connection string sslmode |
| Validación DTOs | class-validator |
| Password | bcrypt cost ≥ 10 |
| Secretos | Fuera de git; .env local |
| Público no lista PII | GET tables sin emails de requests |
| Rate limit POST | L2+; documentado como hueco |
| Honeypot | L2+ |
| CORS | Origen web conocido |
7. Amenazas y respuesta
| Amenaza | Impacto | Respuesta v1 |
|---|---|---|
| Enumeración de inbox | PII de jugadores | 401 sin token |
| Spam de RSVP | Cola inútil | Validación; rate-limit L2+ |
| Token robado en localStorage | Suplantación HOST | Expiración corta; sessions invalidables |
| Overposting status | Caos de aforo | Enum cerrado; H3 operativa |
IDOR :id | Ver PII ajena | Un solo HOST en v1; igual exigir JWT |
| Seed en producción | Credencial conocida | Rotar JWT_SECRET y password si se publica |
8. Credencial demo
| Campo | Valor |
|---|---|
nerea@ronda.club | |
| Password | password123 |
[COMPROBADO] Brief. No reutilizar en un deploy público sin rotar.
9. Riesgos residuales (aceptados L2)
- No hay checkbox RGPD formal (Q3).
- No hay cifrado de columna PII (TLS en tránsito + Neon en reposo).
- localStorage XSS.
- Un HOST ve todas las solicitudes (cola única de demo).
10. Criterios de aceptación
- GET
/api/requestssinAuthorization→ 401. - PATCH status sin token → 401.
- POST
/api/tablessin token → 401. - GET
/api/tablesno incluye emails de jugadores. - Response de login no incluye
password_hash. .envno se commitea.
12-analytics.md
12 — Analítica — RONDA
Instrumentación modelo. v1 puede no emitir eventos reales; este doc es el contrato para cuando se encienda.
No se presentan tasas de conversión inventadas.
1. North star
% de solicitudes válidas (nombre + email + mesa existente) que pasan a CONTACTED antes de startAt de esa mesa.
Por qué no “mesas llenas” ni “pageviews”:
| Métrica vanidosa | Por qué no es éxito |
|---|---|
| Visitas al feed | Un cartel de WhatsApp también se mira |
| Mesas con 0 huecos SEATED | Puede ser overbooking (falla H3) |
| Cuentas creadas | El jugador no tiene cuenta |
La north star mide contacto honesto a tiempo, alineada a S3/H2.
2. Hipótesis ↔ señales
| ID | Hipótesis | Evento / invariante |
|---|---|---|
| H1 | El feed reduce abandono vs listado o WhatsApp | % sesiones con table_opened |
| H2 | Copy “solicitud ≠ plaza” baja sillas fantasma | ↓ tickets “pensé que tenía silla”; ratio SEATED que no aparecen (fuera de app) |
| H3 | SEATED nunca supera seats | seated_count ≤ seats por mesa |
3. Eventos
| Evento | Dónde | Props (sin PII en claro) |
|---|---|---|
feed_viewed | / o /mesas | recorte (hoy/semana), result_count |
table_opened | Card / ficha | slug |
rsvp_started | Form | slug |
rsvp_submitted | POST 201 | code, slug, waitlist_cta (bool) |
rsvp_failed | POST error | reason (validation/network/4xx) |
host_login | Login 200 | — |
host_login_failed | 401 | — |
inbox_viewed | /inbox | count |
status_changed | PATCH 200 | from, to, slug |
table_published | POST tables 201 | slug, seats |
[DECISIÓN] No enviar email ni teléfono a analytics. El code es el identificador.
4. Funnel
Land feed → table_opened → rsvp_started → rsvp_submitted
→ host_login → status_changed(CONTACTED) ≤ startAt
→ status_changed(SEATED) | WAITLIST
| Paso | Definición |
|---|---|
| Land | feed_viewed |
| Interés | table_opened / feed_viewed → H1 |
| Intención | rsvp_started / table_opened |
| Captura | rsvp_submitted / rsvp_started |
| Ops | CONTACTED antes de startAt → North star |
| Cierre | SEATED o WAITLIST; SEATED ≤ seats → H3 |
5. Invariantes (salud, no vanity)
| Check | Query conceptual | Alerta |
|---|---|---|
| H3 | COUNT(*) FILTER (status='SEATED') > seats | Cualquier mesa |
| Códigos únicos | duplicate code | > 0 |
| 401 inesperados | GET tables 401 | No debe ocurrir |
| 5xx | cualquier ruta | > 1% |
6. Dimensiones
| Dimensión | Valores |
|---|---|
| recorte | hoy, semana |
| mechanic | TILE, CARDS, MINIS, COOP, COLLECTION |
| neighborhood | LAVAPIES, EMBAJADORES, CHAMBERI, MALASANA |
| viewport | mobile ≤480, desktop |
Sin user-id de jugador.
7. Privacidad de analítica
- Sin fingerprinting.
- Sin email en claro.
- IP no se guarda en el plano de producto.
- Eventos de demo pueden quedarse en consola.
8. Qué no medimos en v1
| No | Motivo |
|---|---|
| Heatmaps | Craft, no research de campo fingido |
| NPS | Muestra nula |
| “Minutos en mesa” | No hay check-in físico |
| Followers de HOST | No es red social |
9. Criterios de aceptación (si se implementa tracker)
- Cada evento de la tabla §3 tiene nombre estable snake_case.
rsvp_submittedincluyecodecon prefijoRD-.- Ningún payload de analytics incluye
emailophone. - El dashboard interno (si existe) no es Must del L2.
13-qa-test-plan.md
13 — Plan de pruebas — RONDA
1. Smoke obligatorio (D-P1-06)
El brief de implementación fija el mínimo:
| # | Acción | Esperado |
|---|---|---|
| 1 | GET /api/tables | 200 · array length ≥ 5 · slugs seed presentes |
| 2 | POST /api/requests (slug real, nombre + email) | 201 · code con prefijo RD- · status=NEW |
| 3 | POST /api/auth/login nerea@ronda.club / password123 | 200 · accessToken · user.role=HOST |
| 4 | GET /api/requests con Bearer | 200 · incluye el POST reciente |
| 5 | pnpm start / ng build | Web arranca o build OK |
[COMPROBADO] Smoke = GET tables + POST request + login JWT.
2. Casos funcionales
| ID | Caso | Esperado |
|---|---|---|
| S1 | Feed orden | start_at ascendente; coleccion 17:30 antes que minis 18:00 |
| S2 | Slug azulejos | GET /api/tables/azulejos 200 · venue La Palma 12 · seats 4 |
| S3 | Slug inexistente | 404 |
| S4 | RSVP email inválido | 400; form no navega a /ok |
| S5 | RSVP mesa llena (copy) | CTA “lista de espera”; POST sigue 201 NEW |
| S6 | Success | Código visible; copy “no tienes silla todavía” |
| S7 | Inbox sin JWT | 401 / redirect login |
| S8 | PATCH CONTACTED | 200 · badge Contactada |
| S9 | PATCH SEATED | 200 |
| S10 | PATCH WAITLIST | 200 |
| S11 | PATCH CANCELLED | 200 |
| S12 | POST /api/tables sin token | 401 |
| S13 | POST /api/tables con JWT | 201 · aparece en GET tables |
| S14 | Login malo | 401 · mensaje en form |
| S15 | Empty feed (DB sin mesas de hoy) | UI-07 + CTA semana |
| S16 | API caída | UI-08 + Reintentar conserva la vista |
| S17 | Loading | UI-09 skeletons antes del primer paint de datos |
| S18 | Mobile 390 | Dock visible; CTA ≥44px; no hero 2-col |
| S19 | Fotos seed | 5 image_key resuelven a assets |
| S20 | Código formato | regex ^RD-\d{4}-\d{3}$ |
| S21 | GET requests no filtra PII al público | sin token 401 (no 200 []) |
| S22 | Password no viaja de vuelta | login body response sin hash |
3. Datos de prueba
| Uso | Valor |
|---|---|
| HOST | nerea@ronda.club / password123 |
| PLAYER | Leo Navas · leo.navas@example.com |
| Mesa feliz | azulejos |
| Mesa waitlist demo | cooperativo (seed YA WAITLIST) |
| Código seed | RD-0815-001 |
4. Regresión de diversidad / craft
| Check | Falla si |
|---|---|
| Home es feed | Hay hero 2-col + 3 cards de marca (AP-12) |
| Shell es dock | Hay sidebar CRM o command search como nav primaria |
| Copy honesto | Success dice “reserva confirmada” |
| Paleta | Se cuelan dusk/teal/chalky de días previos |
| Tipo | Newsreader / Atkinson / Outfit |
5. Hipótesis (no se “prueban” en la demo)
H1, H2, H3 requieren uso real. El QA verifica que existen las superficies que permitirían medirlas (tap en card, disclaimer, badge SEATED + seats).
6. Criterios de salida QA v1
- Smoke §1 en verde.
- S2, S6, S7, S12, S18, S20 en verde.
- Cero blockers de copy deshonesto.
- Sin
.enven el repo.
7. Fuera de este plan
Playwright e2e completo, carga, fuzzing, auditoría de locales físicos.
14-dev-handoff.md
14 — Handoff desarrollo — RONDA
1. Arranque
cd /Users/cristian/orca/ronda-app
# apps independientes · pnpm filter
pnpm install
# apps/api/.env
# DATABASE_URL= # Neon curly-lab-77015594
# JWT_SECRET=
# PORT=3015
pnpm api # Nest → http://localhost:3015
pnpm start # Angular → http://localhost:4200
Al boot: ensureSchema crea users, sessions, game_tables, seat_requests y semilla idempotente.
Demo: nerea@ronda.club / password123.
2. Paridad Paper
| Debe verse en Angular | Artboard |
|---|---|
| Tokens signage + Zilla/Mulish | UI-00 |
| Feed cronológico + dock | UI-01, UI-10 |
| Ficha con foto real | UI-02 |
| RSVP + disclaimer | UI-03 |
Success RD- | UI-04 |
| Login HOST | UI-05 |
| Inbox estados | UI-06 |
| Empty / error / loading | UI-07…09 |
No clonar DERIVA (mapa split) ni PIZARRA (command search).
3. Contratos
| Tema | Contrato |
|---|---|
| Login | { accessToken, user } camelCase (no access_token) |
| Feed | GET /api/tables → array ordenado por startAt |
| Ficha | GET /api/tables/:slug |
| RSVP | POST /api/requests body slug o tableId + fullName + email |
| Inbox | GET /api/requests Bearer |
| Status | PATCH /api/requests/:id/status { status } |
| Publicar | POST /api/tables Bearer |
| Código | RD-MMDD-NNN Europe/Madrid |
| Status enum | NEW | CONTACTED | SEATED | WAITLIST | CANCELLED |
Campos JSON en camelCase hacia el web aunque SQL sea snake_case. [DECISIÓN]
4. Mapa de rutas web
| Path | Guard cliente | Página |
|---|---|---|
/ | no | Feed Hoy |
/mesas | no | Feed semana |
/mesas/:slug | no | Ficha |
/mesas/:slug/asiento | no | RSVP |
/ok | no | Success |
/login | no | Login |
/inbox | soft (redirect) | Inbox |
/inbox/:id | soft | Detalle |
/publicar | soft | Alta mesa |
/yo | soft | Sesión |
Soft guard: si no hay token, /login?next=. La API es la autoridad.
5. Assets
Copiar desde el case:
2026-08-15-ronda/assets/hero.jpg
2026-08-15-ronda/assets/tables/*.jpg
→ apps/web/src/assets/tables/
image_key del seed = filename (azulejos.jpg, …).
6. DoD implementación L2
| Check | OK |
|---|---|
pnpm api sirve :3015 | |
ensureSchema 4 tablas | |
| Seed 1 HOST + 5 mesas | |
| Smoke GET + POST + login | |
| JWT protege requests y POST tables | |
| Web feed + dock + ficha + form + success + inbox | |
| Disclaimer ×3 | |
| Tokens Tailwind = paleta brief | |
ng build o equivalente OK | |
| README del repo app con credencial |
7. No hacer
| Prohibido | Motivo |
|---|---|
| npm workspaces | AP-06 |
Commit de .env | Secreto |
| Prometer plaza en copy | H2 / AP-09 |
Tabla SQL tables | Reservada → game_tables |
Estado ENROLLED | Vocabulario PIZARRA |
| Search bar como home | Rompe H-FEED |
| Mapa Leaflet | Rompe terna |
image_gen como captura de producto | D-P0-09 |
8. Contactos de diseño (artefactos)
| Artefacto | Path |
|---|---|
| Brief | ux-projects/2026-08-15-ronda/docs/00-day-brief.md |
| Paper | https://app.paper.design/file/01M023RM2TZ49CYM2FN1FRRGNY |
| Datos | docs/05-data-model.md |
| Flujos | docs/04-user-flows.md |
| IX | docs/16-interaction-specs.md |
| Build | docs/20-implementation.md |
15-roadmap.md
15 — Roadmap — RONDA
1. Hecho en L2 (esta ejecución documental)
| Entrega | Evidencia |
|---|---|
| Definición producto + terna | docs 00-brief, 01 |
| Personas Leo / Nerea, JTBD, journey | docs 02 |
| IA feed + dock | docs 03 |
| Flujos F-BOOK + HOST | docs 04 |
| Modelo 4 tablas + seed 5 mesas | docs 05 |
| Stack Angular/Nest/Neon/JWT/pnpm :3015 | docs 06, 20 |
| Dirección signage + DS | docs 07, 08 |
| Copy honesto solicitud ≠ plaza | docs 09 |
| Paper ref 12 UX + 11 UI | docs 00-paper-reference |
| Suite 00–20 + README + executive | case folder |
2. Implementación L2 (misma complejidad, código)
Contrato en docs 14 y 20. No es deuda silenciosa del case documental: es el build del repo ronda-app.
| Ítem | Prioridad |
|---|---|
| ensureSchema + seed | P0 |
| Endpoints Must + smoke | P0 |
| Web feed/ficha/RSVP/success/login/inbox | P0 |
POST /api/tables + /publicar | P0 (brief) |
| Dock + tokens | P0 |
Guards Angular canActivate | P1 |
3. L2+ (misma complejidad, polish)
| Ítem | Notas |
|---|---|
| Rate limit + honeypot POST requests | Anti-spam |
| Checkbox privacidad | Q3 |
| Aviso / bloqueo suave SEATED ≥ seats | H3 |
| Filtros de status en inbox | Query API |
| Playwright S1–S22 | CI |
| Copy code clipboard | Could |
| mailto / tel en detalle | Ops barra |
| Editar / cancelar mesa publicada | HOST |
| API URL por environment | Deploy |
prefers-reduced-motion verificado | a11y |
| Badge NEW en dock | Should |
Estas son subidas de pulido, no parches del L2 documental.
[DECISIÓN] No dejar backlog del vertical slice como “mañana”. Lo de arriba es polish o subida de nivel.
4. L3 (requiere brief nuevo de diversidad)
- Multi-HOST / un local varios anfitriones
- Cuenta PLAYER y “mis solicitudes”
- Email / WhatsApp transaccional
- Check-in QR en puerta
- Venues normalizados + calendario iCal
- Check DB transaccional de aforo
- Audit log
- Facetas search (rompería H-FEED si se convierten en home)
5. Explícitamente fuera (no backlog disfrazado)
| Ítem | Por qué |
|---|---|
| Pagos / TPV / Eventbrite | S4 |
| Discord / chat | S3 |
| Mapa de ciudad | Terna ≠ DERIVA |
| Search-first home | Terna ≠ PIZARRA |
| Kanban | ≠ PRIMA |
| E-commerce de juegos | Otro producto |
| App nativa | Fuera de stack daily |
| Matching ELO | Otro producto (liga) |
6. Orden de ataque si hay continuidad de código
- Contratos 05–06 + seed 05 + smoke 13
- Web paridad UI-01…10
- Guards + H3 UI + contacto tel
- Rate limit si hay tráfico
- Evaluar brief L3 (no parche silencioso)
16-interaction-specs.md
16 — Especificación de interacción — RONDA
1. Convenciones
| Token | Valor |
|---|---|
| Duración corta | 120–160ms |
| Easing | ease-out |
| Reduced motion | 0ms |
| Target | ≥44×44 |
| Pending | control disabled + aria-busy |
2. Dock (S-DOCK)
| Gesto | Resultado |
|---|---|
| Tap slot | Navega; aria-current se mueve |
| Tap Publicar / Inbox sin sesión | /login?next=… |
| Scroll del feed | Dock no se esconde en v1 |
| Teclado | Tab cíclico de slots al final del documento |
| Rotación landscape 390 | Dock permanece; main padding-bottom 80 |
Estado activo: disco cromo + label tinta.
Estado default: tinta sobre liner.
No: badge numérico obligatorio; si se implementa, aria-label="Inbox, 3 nuevas".
Slots: Hoy / · Mesas /mesas · Publicar /publicar · Inbox /inbox · Yo /yo o /login.
3. Feed Hoy / Mesas
| Gesto | Resultado |
|---|---|
| Tap card | /mesas/:slug (fila completa clicable, un solo enlace) |
| Pull-to-refresh | No en v1; Reintentar solo en error |
| Primera carga | 5 skeletons; no spinner centrado de marca |
| 0 resultados Hoy | Empty + CTA “Ver la semana” |
| Error | Banner fijo bajo wordmark + Reintentar (refetch) |
| Hover desktop | Translación 0; borde tinta 1px extra opcional |
AC: la primera card de esta noche está en el viewport inicial a 390 y a 1280 (sin hero de 60vh).
Orden visual = startAt ASC. Si dos mesas comparten hora, desempate title ASC.
4. Ficha
| Gesto | Resultado |
|---|---|
| CTA primario si hay hueco estimado | “Pedir asiento” → /mesas/:slug/asiento |
CTA si seatedCount >= seats | “Apuntarme a la lista de espera” → mismo form |
| Back / wordmark | Feed de origen (from=hoy|mesas) |
| Foto | No lightbox v1 |
| Disclaimer | Visible antes del CTA (no debajo del fold en desktop) |
Hueco estimado = seats - seatedCount (agregado). Si el API no manda seatedCount, mostrar seats y CTA estándar. [DECISIÓN]
5. Form RSVP
| Campo | Interacción |
|---|---|
| fullName | Autocomplete name |
type=email, autocomplete email | |
| phone | type=tel, opcional |
| notes | textarea 3 filas |
| partySize | default 1; rango 1–2 |
| Gesto | Resultado |
|---|---|
| Submit inválido | Foco al primer campo error; no POST |
| Submit válido | Botón disabled; POST; 201 → /ok?code= |
| Error red | Botón “Reintentar”; no limpia campos |
| Doble tap | Ignorado mientras pending |
Submit deshabilitado si nombre < 2 o email inválido.
6. Success
| Gesto | Resultado |
|---|---|
| Long-press / select | Código seleccionable |
| Copiar (Could) | navigator.clipboard; toast 1.5s “Código copiado” |
| CTA primario | Volver al feed / |
| CTA secundario | Volver a la ficha |
Sin animación de confetti. Sin “¡reserva confirmada!”.
Si se entra a /ok sin code: mensaje “Solicitud enviada” genérico + CTA feed. No inventar un código.
7. Login
| Gesto | Resultado |
|---|---|
| Submit | POST login; pending en botón |
| 200 | Guarda token + user; navega next sanitizado o /inbox |
| 401 | Texto bajo el form; password no se limpia (sí se puede seleccionar) |
| Enter | Submit |
next permitido: paths que empiezan por / y no // ni http.
8. Inbox
| Gesto | Resultado |
|---|---|
| Sin token | Redirect login |
| Tap card | /inbox/:id |
| Empty | Ilustración no obligatoria; titular + 1 línea |
| Error 401 mid-session | Redirect login |
| Error red | Banner + Reintentar |
No swipe-to-archive en v1.
9. Detalle + estados
| Acción | Confirmación | Resultado |
|---|---|---|
| Contactada | No | PATCH CONTACTED |
| Sentar | No, salvo seatedCount >= seats → aviso | PATCH SEATED |
| Lista de espera | No | PATCH WAITLIST |
| Cancelar | Sí (“¿Cancelar RD-…?”) | PATCH CANCELLED |
Aviso H3 (no bloqueo API v1):
“Hay tantas o más solicitudes sentadas que sillas. ¿Seguro?”
Optimistic UI: badge cambia al tap; si PATCH falla, rollback + toast error.
10. Publicar
| Gesto | Resultado |
|---|---|
startAt | datetime-local; min = hoy 00:00 Europe/Madrid |
| seats | stepper 2–12 o input number |
| Submit | POST tables; 201 → /mesas/:slug |
| 401 | login |
| Validación | mismos criterios doc 04 F8 |
No hay preview Paper-like. El HOST ve la ficha real.
11. Yo / logout
Tap “Salir” → limpia storage → (opcional DELETE session) → /login.
No confirmación.
12. Motion
| Superficie | Spec |
|---|---|
| Cards feed | Fade 160ms, stagger ≤40ms (off si reduced-motion) |
| Cambio de badge | Crossfade 120ms |
| Dock | Sin hide; sin bounce |
| Skeletons | Estáticos o pulso 1.2s opacity 0.6–1; off si reduced-motion |
Prohibido: scroll-jacking, page transitions de 400ms, parallax en foto.
13. Criterios de aceptación de interacción
- Un PLAYER en 390px llega de feed a success en ≤ 4 taps (card, CTA, submit → success).
- Un HOST en 390px marca CONTACTED en ≤ 3 taps desde inbox (card, acción).
- Ningún submit permite doble POST.
- El dock no cubre el CTA de ficha (padding / sticky por encima).
- Reduced-motion elimina stagger.
- La home no es hero 2-col (AP-12).
17-prototype-map.md
17 — Mapa de prototipo — RONDA
Paper no es clicable vía MCP. El prototipo vivo es la app Angular en /Users/cristian/orca/ronda-app/ (pnpm start :4200).
1. Paper → rutas
| Ruta | Paper | Rol | Must |
|---|---|---|---|
/ | UI-01 Feed Hoy, UI-07 Empty, UI-08 Error, UI-09 Loading, UI-10 Mobile | PLAYER | Sí |
/mesas | mismo patrón UI-01 (recorte semana) | PLAYER | Should |
/mesas/:slug | UI-02 Ficha mesa | PLAYER | Sí |
/mesas/:slug/pedir | UI-03 RSVP | PLAYER | Sí |
/ok | UI-04 Success | PLAYER | Sí |
/login | UI-05 Login | HOST | Sí |
/inbox | UI-06 Inbox HOST | HOST | Sí |
/inbox/:id | (extensión de UI-06) | HOST | Should |
/publicar | (no artboard UI dedicado; spec 04 F8 / 16 §10) | HOST | Sí (API + pantalla) |
/yo | (sesión) | HOST | Should |
| — | UI-00 Tokens | Sistema | Sí |
| — | UX-00…UX-11 | Proceso | Sí |
2. Guion de demo (click-through)
| Paso | Actor | Ruta | Qué decir |
|---|---|---|---|
| 1 | Leo | / | “Esta noche hay mesa” — ve 17:30…21:00 |
| 2 | Leo | /mesas/azulejos | La Palma 12, 4 sillas, tile-laying |
| 3 | Leo | /mesas/azulejos/asiento | Disclaimer visible |
| 4 | Leo | /ok?code=RD-0815-00N | Código; no es plaza |
| 5 | Nerea | /login | nerea@ronda.club |
| 6 | Nerea | /inbox | NEW de Leo |
| 7 | Nerea | detalle | CONTACTED → SEATED o WAITLIST |
| 8 | Nerea | /publicar | Abre una mesa extra |
3. Estados en el mismo prototipo
| Estado | Cómo provocarlo |
|---|---|
| Empty Hoy | Semilla con start_at fuera del día, o flag demo |
| Error | Parar pnpm api y recargar / |
| Loading | Throttle red DevTools |
| Mobile | 390×844 |
4. Deep links seed
| URL local | Destino |
|---|---|
http://localhost:4200/ | Feed |
http://localhost:4200/mesas/azulejos | Azulejos de Lavapiés |
http://localhost:4200/mesas/cartas | Cubilete y cartas |
http://localhost:4200/mesas/minis | Mazmorra de Chamberí |
http://localhost:4200/mesas/cooperativo | Farol cooperativo |
http://localhost:4200/mesas/coleccion | Colección de patio |
http://localhost:4200/login | HOST |
API :3015 · Web :4200 · proxy /api opcional.
5. Qué no es prototipo
- El file Paper no sustituye flujos clicables.
- Un HTML estático en
presentation/no es el vertical slice. - Capturas
image_genno son evidencia de producto (D-P0-09).
18-completeness-audit.md
18 — Auditoría de completitud — RONDA (2026-08-15)
1. Alcance auditado
Vertical slice L2: feed cronológico de mesas de juego locales + RSVP público + panel HOST JWT, con docs, referencia Paper, contratos Angular + Nest + ensureSchema + Neon.
Este encargo cubre la suite documental. Paper hi-fi y el repo ronda-app pueden existir en paralelo; la auditoría marca lo que esta suite cierra.
2. Checklist CRON / ALS-2
| Requisito | Estado | Evidencia |
|---|---|---|
| Diversidad sector/tipo/nivel | OK | Gaming mesa L2; no mapa; no search; no kanban; no Discord |
| Terna ≥2 códigos vs N−1/N−2 | OK | R-HY · S-DOCK · H-FEED · F-BOOK (brief) |
| Day brief + anti-patrones | OK | docs/00-day-brief.md (no sobrescrito) |
| Paper ≥12 UX + ≥10 UI | OK | UX-count 12 · UI-count 11 · file 01M023RM2TZ49CYM2FN1FRRGNY |
| Docs 00–20 | OK | suite en docs/ + executive + README |
| JWT HOST | OK | Role HOST · D-P1-05 · tabla sessions |
| API + seed + Neon | OK | contratos curly-lab-77015594, port 3015 |
| Web tokens | OK | Zilla Slab + Mulish · paleta signage |
| Feed-first (no AP-12) | OK | UI-01 / docs 03, 07, 08 |
| Hipótesis no fake field | OK | etiquetas en doc 02 |
| Copy solicitud ≠ plaza | OK | docs 01, 09, 16 |
3. Cobertura funcional
| Feature brief | Spec | UI Paper | API | Docs |
|---|---|---|---|---|
| Home feed cronológico 5 mesas | Sí | UI-01, UI-10 | GET tables | 03, 04, 16 |
| Ficha + foto | Sí | UI-02 | GET :slug | 05, 09 |
| Form RSVP | Sí | UI-03 | POST requests | 04, 05 |
| Success + RD- | Sí | UI-04 | code | 04, 09 |
| Login HOST | Sí | UI-05 | POST login | 04, 06 |
| Inbox 5 estados | Sí | UI-06 | GET + PATCH | 04, 05 |
| Publicar mesa | Sí | (pantalla spec) | POST tables | 04, 20 |
| Empty | Sí | UI-07 | 200 [] | 16 |
| Error | Sí | UI-08 | 5xx/red | 16 |
| Loading | Sí | UI-09 | pending | 16 |
| Design system | Sí | UI-00 | — | 08 |
| Seed 5 mesas | Sí | — | ensureSchema | 05 |
| Dock | Sí | UI-01, UI-10 | — | 03, 08 |
4. Cobertura Paper (literales del brief)
| ID | Nombre | En 00-paper-reference |
|---|---|---|
| UX-00 | Cover | Sí |
| UX-01 | Stakeholders | Sí |
| UX-02 | Personas | Sí |
| UX-03 | JTBD | Sí |
| UX-04 | Stories | Sí |
| UX-05 | Journey | Sí |
| UX-06 | Blueprint | Sí |
| UX-07 | Site map | Sí |
| UX-08 | Flujos | Sí |
| UX-09 | Datos+permisos | Sí |
| UX-10 | Métricas | Sí |
| UX-11 | Research | Sí |
| UI-00 | Tokens | Sí |
| UI-01 | Feed Hoy | Sí |
| UI-02 | Ficha mesa | Sí |
| UI-03 | RSVP | Sí |
| UI-04 | Success | Sí |
| UI-05 | Login | Sí |
| UI-06 | Inbox HOST | Sí |
| UI-07 | Empty | Sí |
| UI-08 | Error | Sí |
| UI-09 | Loading | Sí |
| UI-10 | Mobile feed | Sí |
[COMPROBADO] Nombres coinciden con el encargo. UX-count: 12 y UI-count: 11 literales para el gate check-paper-reference.mjs.
5. Rúbrica de calidad (auto SCORE orientativo · docs)
| Eje | Score 1–5 | Comentario |
|---|---|---|
| Diversidad | 5 | Feed+dock vs mapa DERIVA y search PIZARRA |
| Craft visual (spec) | 4–5 | Signage + Zilla/Mulish + fotos mesa documentadas |
| Densidad UX docs | 5 | Suite L2 con tablas, AC, etiquetas |
| Completitud código L2 | — | App fuera o en paralelo a este encargo de docs |
| Authz | 5 | Contrato: guard en list/status/publish |
| Verdad investigación | 5 | Sin entrevistas ni stats de campo falsas |
6. Huecos aceptados (no regresiones de cierre documental)
| Hueco | Clasificación |
|---|---|
| Pagos / Discord / mapa / search-home | Fuera L2 |
| e2e automatizado | L2+ |
| Route guards formales Angular | L2+ |
| Check DB aforo | L2+ / L3 |
| Checkbox RGPD | L2+ documentado |
Artboard UI dedicado de /publicar | Spec en 04/16; no bloquea count UI ≥10 |
| Paper/app no necesariamente tocados aquí | Encargo = documentación |
7. Veredicto
COMPLETO para entrega documental del caso 2026-08-15.
La suite define producto L2 usable (tablas, AC, riesgos, contratos) sin presentar investigación de campo como hecho.
Cualquier ampliación mapa / search-first / pagos / Discord requiere nuevo brief de diversidad, no parche silencioso.
19-backlog-completo.md
19 — Backlog completo — RONDA
Inventario de ítems. Los del alcance L2 documental del día están Done.
El resto es opcional / siguiente nivel, no deuda oculta del cierre.
1. Done — L2 case documental (2026-08-15)
| ID | Ítem | Capa |
|---|---|---|
| D01 | Definición producto RONDA + eslogan | Docs |
| D02 | Day brief diversidad gaming L2 | Docs (preexistente) |
| D03 | Terna R-HY · S-DOCK · H-FEED · F-BOOK | Docs |
| D04 | Personas Leo Navas / Nerea Solís | Docs + Paper ref |
| D05 | JTBD + stories Must | Docs |
| D06 | IA feed-first + dock | Docs |
| D07 | Flujos F1–F9 + AC | Docs |
| D08 | Modelo users / sessions / game_tables / seat_requests | Docs |
| D09 | Contratos API :3015 | Docs |
| D10 | Auth JWT HOST + sessions | Docs |
| D11 | 5 estados incl. WAITLIST y SEATED | Docs |
| D12 | Código RD-MMDD-NNN | Docs |
| D13 | Disclaimer solicitud ≠ plaza | Docs |
| D14 | North star CONTACTED antes de startAt | Docs |
| D15 | Tokens signage + Zilla Slab / Mulish | Docs |
| D16 | Paper UX-00…11 + UI-00…10 | Docs |
| D17 | Suite docs 00–20 + README + executive | Docs |
| D18 | Seed 5 mesas + solicitudes especificado | Docs |
| D19 | Assets listados | README |
| D20 | QA smoke + S1–S22 | Docs |
| D21 | Publicar mesa (HOST) especificado | Docs |
| D22 | Neon curly-lab-77015594 + pnpm arranque | Docs |
2. Backlog L2 implementación (repo ronda-app)
| ID | Ítem | Prioridad | Notas |
|---|---|---|---|
| I01 | Scaffold apps/api + apps/web pnpm | P0 | filter independientes |
| I02 | ensureSchema 4 tablas | P0 | |
| I03 | Seed Nerea + 5 mesas + requests | P0 | |
| I04 | GET tables / :slug | P0 | |
| I05 | POST requests + código RD- | P0 | |
| I06 | POST login JWT + sessions | P0 | |
| I07 | GET requests + PATCH status | P0 | |
| I08 | POST tables HOST | P0 | Must del brief, no L3 |
| I09 | Feed + dock Angular | P0 | |
| I10 | Ficha / RSVP / success / login / inbox | P0 | |
| I11 | Tokens Tailwind + fotos | P0 | |
| I12 | Smoke curl + build | P0 | D-P1-06 |
3. Backlog L2+ (polish)
| ID | Ítem | Prioridad | Notas |
|---|---|---|---|
| B01 | canActivate guards Angular | P1 | UX auth |
| B02 | Filtros por status en inbox | P2 | Query API |
| B03 | Aviso / bloqueo suave SEATED ≥ seats | P1 | H3 |
| B04 | Captcha / rate limit POST | P1 | anti-spam |
| B05 | Checkbox privacidad | P1 | RGPD |
| B06 | Playwright smoke | P1 | CI |
| B07 | Skeleton UI-09 en código | P2 | |
| B08 | mailto/tel en detail | P2 | ops barra |
| B09 | Copy code clipboard | P3 | |
| B10 | API URL por environment | P1 | deploy |
| B11 | Badge NEW en dock | P2 | |
| B12 | Página 404 amigable | P3 | |
| B13 | Editar / cancelar mesa | P2 | |
| B14 | Recorte Hoy server-side | P2 | query from/to |
4. Backlog L3 (requiere brief nuevo)
| ID | Ítem | Dependencia |
|---|---|---|
| C01 | Multi-HOST / scope por local | venues + membership |
| C02 | Cuenta PLAYER | authz |
| C03 | Email / WhatsApp transaccional | provider |
| C04 | Lookup público por código | authz |
| C05 | Check DB aforo | transacción |
| C06 | Audit log | Event |
| C07 | Check-in QR | startAt window |
| C08 | Calendario iCal | schedule |
5. Backlog explícitamente fuera
| ID | Ítem |
|---|---|
| E01 | Pagos / TPV / fianza |
| E02 | Discord / chat / matching |
| E03 | Mapa de locales |
| E04 | Search-first como home |
| E05 | Kanban de solicitudes |
| E06 | E-commerce de juegos |
| E07 | App nativa |
| E08 | Matching ELO |
6. Explicitamente no-backlog
| Idea | Razón |
|---|---|
| Hero 2-col + 3 cards | AP-12 / rompe H-FEED |
| Sidebar CRM | S-SIDE |
| Wizard RSVP 4 pasos | F-ONB |
| Command search municipal | PIZARRA |
| Split mapa | DERIVA |
7. Orden de ataque recomendado (continuidad de código)
- I01–I12 (vertical slice runnable)
- B01 + B06 + B10
- B03 + B08
- B04 + B05 si hay tráfico
- Evaluar brief L3 — no parche silencioso
8. Trazabilidad
| Origen | Ítems |
|---|---|
| Day brief must-have | D01–D22, I01–I12 |
| Doc 15 L2+ | B01–B14 |
| Doc 15 L3 / fuera | C01–C08, E01–E08 |
20-implementation.md
20 — Implementación — RONDA
1. Resumen ejecutivo técnico
| Campo | Valor |
|---|---|
| App path | /Users/cristian/orca/ronda-app |
| API | NestJS · puerto 3015 · prefijo /api |
| Web | Angular standalone · puerto 4200 |
| Package | pnpm · apps/api + apps/web independientes (pnpm --filter) |
| DB | Neon PostgreSQL · ensureSchema · project curly-lab-77015594 |
| Tablas | users, sessions, game_tables, seat_requests |
| Auth | JWT Bearer · role HOST · persistencia sessions |
| Dominio | User, Session, GameTable, SeatRequest |
| Fecha | 2026-08-15 |
| GitHub | https://github.com/Criscode2022/ronda-app (se creará) |
Este documento es la especificación de build alineada al case. No sustituye al código: si el repo diverge, gana el contrato de docs 05 + este archivo tras actualizar ambos.
[COMPROBADO] Path, puertos, tablas, Neon y scripts pnpm api / pnpm start salen del brief de implementación.
2. Cómo arrancar
cd /Users/cristian/orca/ronda-app
pnpm install
# apps/api/.env
# DATABASE_URL= # Neon curly-lab-77015594
# JWT_SECRET=
# PORT=3015
pnpm api # http://localhost:3015
pnpm start # http://localhost:4200
Apps independientes: no npm workspaces. Usar:
pnpm --filter api add <pkg>
pnpm --filter web add <pkg>
Credenciales
| Rol | Password | |
|---|---|---|
| HOST | nerea@ronda.club | password123 |
3. ensureSchema
Al boot de Nest (onModuleInit o main.ts antes de listen):
CREATE TABLE IF NOT EXISTSde las 4 tablas (doc 05).- Índices:
game_tables(start_at),seat_requests(created_at DESC),seat_requests(table_id, status),users(email),sessions(token_hash). - Seed idempotente:
- upsert HOST
nerea@ronda.club(bcrypt depassword123) - upsert 5
game_tablesporslug - upsert
seat_requestsporcode
- upsert HOST
No borrar datos de usuario en cada boot si ya existen filas de requests distintas al seed. El seed solo garantiza las filas canónicas.
Tablas (recordatorio)
| Tabla | Contenido |
|---|---|
users | HOST Nerea |
sessions | sid + token_hash + expires_at |
game_tables | 5 mesas seed + altas HOST |
seat_requests | RSVP públicos |
4. Módulos API a implementar
Auth
POST /api/auth/login- Valida email/password; compara bcrypt; inserta
sessions; emite JWT consub, email, role,sid. - Guard JWT protege lectura/escritura de solicitudes y alta de mesas.
Tables (público lectura · HOST escritura)
| Método | Ruta | Auth | Notas |
|---|---|---|---|
| GET | /api/tables | No | orden start_at ASC |
| GET | /api/tables/:slug | No | 404 si no existe; incluir hostName, seatedCount |
| POST | /api/tables | JWT HOST | slug generado; host_id = sub |
Requests
| Método | Ruta | Auth | Notas |
|---|---|---|---|
| POST | /api/requests | No | create; status NEW; code RD- |
| GET | /api/requests | JWT | list + table; created_at desc |
| PATCH | /api/requests/:id/status | JWT | body { status } |
| GET | /api/requests/:id | JWT | Should; + table + seatedCount |
Generación de código
RD- + MMDD (Europe/Madrid) + - + NNN
NNN = siguiente entero del día, padded 3. Si unique falla, reintentar NNN+1 (máx. 5).
[DECISIÓN] Timezone Europe/Madrid, no UTC, para que “Hoy” y el código coincidan con la barra.
5. Frontend a implementar
| Página | Ruta | Responsabilidad |
|---|---|---|
| FeedPage | / | GET tables, recorte Hoy, empty/loading/error, dock |
| TablesWeekPage | /mesas | mismo GET, recorte 7 días |
| TablePage | /mesas/:slug | GET slug, foto, disclaimer, CTA |
| RsvpPage | /mesas/:slug/pedir | form create público + disclaimer |
| SuccessPage | /ok | confirmación + código + “no es plaza” |
| LoginPage | /login | form → ApiService.login → inbox |
| InboxPage | /inbox | list + empty/error + logout |
| RequestDetailPage | /inbox/:id | get + patch status + aviso aforo |
| PublishPage | /publicar | form POST tables |
| MePage | /yo | nombre, salir |
Shell: DockComponent persistente.
ApiService centraliza base URL http://localhost:3015/api, token storage (ronda_token, ronda_user), métodos tipados (GameTable, SeatRequest, User).
6. Decisiones de implementación
| Decisión | Razón |
|---|---|
| Puerto API 3015 | Evitar colisión con DERIVA 3014 / PIZARRA 3013 |
game_tables | tables es reservada |
| ensureSchema, no Prisma obligatorio | Brief de build |
| pnpm filter | Brief; apps independientes |
| Soft auth en páginas | Simple; API es autoridad |
start_at TIMESTAMPTZ | Fuente de verdad del feed |
| Sin FK User–SeatRequest | Form anónimo |
| Código RD-MMDD-NNN | Referencia oral corta |
| Templates standalone | Velocidad daily |
| No bloquear POST por aforo | H2/H3: HOST decide WAITLIST |
| Home no usa hero como layout | AP-12 / H-FEED |
| SEATED no ENROLLED | Dominio de mesa, no municipal |
| JSON camelCase | Convenio web serie daily |
7. Variables de entorno
| Variable | Servicio | Descripción |
|---|---|---|
DATABASE_URL | API | Neon curly-lab-77015594 |
JWT_SECRET | API | Firma tokens |
PORT | API | opcional, 3015 |
JWT_EXPIRES | API | opcional, default 12h |
Web: URL de API en ApiService (default localhost:3015).
8. Smoke de implementación (mínimo)
ensureSchemaOK (tablas existen).GET /api/tableslength ≥ 5, incluyeazulejos,cartas,minis,cooperativo,coleccion.POST /api/requests201 +codeRD-.POST /api/auth/login200 +accessToken.GET /api/requestsBearer incluye el POST.GET /api/requestssin token 401.POST /api/tablessin token 401.- Web: feed dominante, dock, disclaimer, fotos.
9. Estructura de ficheros clave
ronda-app/
├── pnpm-workspace.yaml
├── package.json # scripts: api, start
├── apps/api/
│ ├── package.json
│ ├── src/
│ │ ├── main.ts
│ │ ├── app.module.ts
│ │ ├── db/
│ │ │ ├── ensure-schema.ts
│ │ │ └── seed.ts
│ │ ├── auth/
│ │ │ ├── auth.controller.ts
│ │ │ ├── auth.service.ts
│ │ │ ├── jwt.strategy.ts
│ │ │ └── jwt-auth.guard.ts
│ │ ├── tables/
│ │ │ ├── tables.controller.ts
│ │ │ └── tables.service.ts
│ │ └── requests/
│ │ ├── requests.controller.ts
│ │ └── requests.service.ts
│ └── .env.example
├── apps/web/
│ ├── package.json
│ ├── tailwind.config.js
│ └── src/app/
│ ├── app.routes.ts
│ ├── core/api.service.ts
│ ├── shell/dock.component.ts
│ └── pages/
│ ├── feed/
│ ├── table-detail/
│ ├── rsvp/
│ ├── success/
│ ├── login/
│ ├── inbox/
│ ├── request-detail/
│ ├── publish/
│ └── me/
└── README.md
Nombres de fichero orientativos; el contrato es de rutas y tablas, no de filenames exactos.
10. Seed — checklist de implementación
| # | Check |
|---|---|
| 1 | User Nerea Solís / nerea@ronda.club / password bcrypt de password123 |
| 2 | 5 game_tables con slugs de doc 05 y image_key alineado a assets |
| 3 | Barrios: Lavapiés, Embajadores, Chamberí, Malasaña |
| 4 | Horas 17:30 / 18:00 / 19:00 / 20:30 / 21:00 Europe/Madrid |
| 5 | Asientos 6 / 5 / 5 / 4 / 4 según seed (coleccion 6, minis 5, cartas 5, azulejos 4, cooperativo 4) |
| 6 | Requests RD-0815-001…005 cubriendo NEW, CONTACTED, SEATED, WAITLIST, CANCELLED |
| 7 | Leo → azulejos NEW |
| 8 | Youssef → cooperativo WAITLIST |
| 9 | Idempotencia: upsert por email/slug/code |
11. Alineación case ↔ app
| Doc | Evidencia esperada en código |
|---|---|
| 05 data model | ensureSchema columnas y enums TEXT |
| 03–04 IA/flows | routes + controllers |
| 08 DS | tailwind colors + fontFamily |
| 09 content | strings en templates (disclaimer ×3) |
| 00 paper | URLs en README case |
| 11 security | Guard en GET/PATCH requests y POST tables |
12. Notas de cierre técnico
- El case documental L2 está especificado (docs + Paper ref + contratos).
- Must-have de producto del brief están escritos con AC.
- Mejoras (guards Angular, e2e, rate limit, check aforo) viven en backlog L2+, no como deuda silenciosa.
- Repo GitHub
Criscode2022/ronda-appse creará; este doc no lo asume ya pusheado.