00-day-brief.md
00 — Day brief · 2026-08-22 · RAMAL
Decisiones ALS-2 BRIEF
| Campo | Valor |
|---|---|
| Fecha | 2026-08-22 |
| Nombre | RAMAL |
| Complejidad | Nivel 3 (no L2 consecutivo tras TEMPLE) |
| Sector | Telecomunicaciones / comunidad vecinal (Vallecas) |
| Tipo | Portada de red de bloque + checklist de alta + mesa VOCAL JWT (split) |
| Plataforma | Web responsive (portada pública · mesa split desktop) |
| Registro | R-CL — institucional de asociación, no NOC cian (ventana 4 pedía clásico) |
| Shell | S-SPLIT — lista de altas | ficha; la portada pública no lleva top-nav de marketing |
| Home | H-FULL — portada a sangre; lead + 3 nodos en el recorte 16/10 |
| Flujo | F-ONB — checklist de alta (vivienda, planta, azotea); no wizard chrome |
| Por qué no TEMPLE | No S-MIN / H-KIOSK / F-BOOK; no ensayo |
| Por qué no PREGÓN | No chat ni kanban; alta ≠ parte municipal |
| Por qué no operador | RAMAL no es Movistar ni un speedtest |
Terna unicidad
R-CL · S-SPLIT · H-FULL · F-ONB
0 códigos iguales a N−1 (TEMPLE: R-HY · S-MIN · H-KIOSK · F-BOOK).
≥2 distintos vs N−2 (PREGÓN) y N−3 (MATIZ).
Ventana 4: R-HY + R-IN + R-HY + R-IN → hoy R-CL.
S-SPLIT, H-FULL y F-ONB no están en los últimos 5.
Ground (CRON §5.6)
- Familia: G-ICE
- Hex:
#E7F0F5 - Por qué: escarcha en teja a las 8, no negro de flight case (TEMPLE), no fotocopia (PREGÓN), no yeso (MATIZ), no hormigón (ZAGUÁN), no blanco de kiosco (MARGEN).
Mood visual (Paper)
- Candidatos: cyan SaaS / NOC (primer instinto telecom), ice, mineral, tidal, alpine
- Elegido: ice — no el primer instinto tablero de operador. Una red de bloque se ve desde la azotea un martes con escarcha, no desde un NOC.
- Paleta:
#E7F0F5hielo (ground)#1A2A32mástil mojado#FFFFFFficha#3D7A96cobre oxidado del mástil#5C6B73galvanizado#B42318precinto (poco)
- Tipo: Newsreader display · Figtree UI
Alcance L3 must
- Home H-FULL: portada + 3 nodos (lead + 3 datos)
- Checklist de alta (4 ítems, una pantalla, no wizard)
- Acuse con código (alta ≠ cobertura)
- Login JWT VOCAL
- Mesa split: lista de altas | ficha + aceptar/rechazar
- Empty / error / loading / mobile portada
- ≥12 UX + ≥10 UI
Microcopy duro
- Alta ≠ cobertura del operador. RAMAL no sustituye a Movistar.
- Nodo ≠ router de casa. El nodo está en la azotea del bloque.
- RAMAL no instala hoy. Aceptar no sube a la azotea.
- Checklist ≠ técnico. Completar el alta no programa una visita.
Supuestos
- S1: Una asociación atiende 4–8 altas/semana; la demo tiene 3 nodos y 5 altas (3 PENDIENTE, 2 ACEPTADA).
- S2: La vecina pide alta desde el salón; no crea cuenta.
- S3: Un VOCAL (Pablo) acepta o rechaza; el técnico no entra en el slice L3 de código (sí en docs).
- S4: Sin pago, sin radio planning, sin mapa GIS. [DECISIÓN]
Hipótesis
| ID | Señal |
|---|---|
| H1 | Una portada de 3 nodos reduce “esto es un operador” |
| H2 | El checklist deja claro que no hay técnico mañana |
| H3 | Split lista|ficha basta; no hace falta kanban |
00-paper-reference.md
Paper reference · RAMAL
| URL | https://app.paper.design/file/01M0M4FJGXPG315PDNT7PCAY3J |
| File ID | 01M0M4FJGXPG315PDNT7PCAY3J |
UX-count: 12 UI-count: 12
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 Portada · UI-02 Portada mobile · UI-03 Alta · UI-04 Alta mobile · UI-05 Success · UI-06 Login · UI-07 Mesa split · UI-08 Ficha · UI-09 Empty · UI-10 Error · UI-11 Loading
UX process (prefijo UX- · mínimo 12)
| ID | Nombre | Contenido |
|---|---|---|
| UX-00 | Cover | RAMAL · «La red del bloque, no el operador.» · R-CL · S-SPLIT · H-FULL · F-ONB · L3 telecom comunidad · ice · 2026-08-22 |
| UX-01 | Stakeholders | VECINA (Inés), VOCAL (Pablo), técnico fuera del slice, operador comercial fuera |
| UX-02 | Personas | Inés Soto 52 · Pablo Ruiz 38 · Nerea Gil 29 (técnica, docs) |
| UX-03 | JTBD | Job «pedir nodo en mi bloque sin llamar a un comercial» |
| UX-04 | Stories | Must: portada, checklist, acuse, login, split, aceptar |
| UX-05 | Journey | Lee 3 nodos → checklist → código → (Pablo) acepta |
| UX-06 | Blueprint | Frontstage portada · backstage junta · Nest/Neon/JWT |
| UX-07 | Site map | / portada · /alta · /ok/:code · /login · /mesa |
| UX-08 | Flujos | F-ONB público; split VOCAL |
| UX-09 | Datos | User VOCAL · Node · Alta; JWT en mesa |
| UX-10 | Métricas | North star % altas que Pablo mueve en 7 días |
| UX-11 | Research | Comprobado / supuesto / hipótesis / decisión |
UI producto (prefijo UI- · mínimo 10)
| ID | Nombre | Flujo |
|---|---|---|
| UI-00 | Tokens | DS ice: hielo, mástil, ficha, cobre, galvanizado |
| UI-01 | Portada | H-FULL 1440: foto a sangre + 3 nodos. Lead + 3 datos |
| UI-02 | Portada mobile | 390 |
| UI-03 | Alta | Checklist 4 ítems |
| UI-04 | Alta mobile | 390 |
| UI-05 | Success | Código. Alta ≠ cobertura |
| UI-06 | Login | JWT VOCAL |
| UI-07 | Mesa split | Lista | ficha |
| UI-08 | Ficha | Aceptar / rechazar |
| UI-09 | Empty | Sin altas |
| UI-10 | Error | Red |
| UI-11 | Loading | Skeletons |
Mapeo Paper → Angular
| Artboard | Ruta | Componente |
|---|---|---|
| UI-01 Portada | / | HomePage |
| UI-03 Alta | /alta | AltaPage |
| UI-05 Success | /ok/:code | OkPage |
| UI-06 Login | /login | LoginPage |
| UI-07 Mesa | /mesa | MesaPage |
| UI-08 Ficha | /mesa/:code | AltaFichaPage |
01-project-definition.md
01 — Definición de proyecto — RAMAL
1. Identidad
| Campo | Valor |
|---|---|
| Nombre | RAMAL |
| Significado | Ramal es el tramo de red que baja del mástil al bloque, no el operador comercial. Ni la cobertura ni el router de casa. Se entra a la portada; se sale con un alta. |
| Eslogan | «La red del bloque, no el operador.» |
| Una frase | Portada de nodos + checklist de alta + mesa VOCAL JWT (split lista | ficha). |
| Sector | Telecomunicaciones / comunidad vecinal (Vallecas) |
| Tipo | Web L3 — portada H-FULL + checklist F-ONB + mesa split — Nivel 3, herramienta de altas usable |
| Plataforma | Web responsive (portada pública · mesa split desktop) |
| Mercado demo | España · Madrid · Vallecas · Palomeras (una asociación, no una operadora) |
| Idioma | es-ES |
| Fecha caso | 2026-08-22 |
| Terna | R-CL · S-SPLIT · H-FULL · F-ONB |
[COMPROBADO] Nombre, terna, nivel, sector, ground y paleta salen de docs/00-day-brief.md.
[SUPUESTO de catálogo] Vallecas / Palomeras / Sierra Gádor es atrezzo de mercado, no el inventario de una red real.
2. Problema
Principal (hipótesis de diseño)
[HIPÓTESIS] La vecina de un bloque de Palomeras no necesita un comercial de fibra: necesita pedir nodo en su bloque sin llamar a un operador. Hoy eso vive en un grupo, una junta y una expectativa de cobertura. El alta se lee como contrato de Movistar; el nodo se lee como router de casa; el producto se lee como speedtest. [SUPUESTO] Una asociación atiende 4–8 altas/semana; no opera un NOC ni un chat municipal.
Secundarios
| Problema | Quién lo sufre | Efecto |
|---|---|---|
| «¿Hay fibra en el 12?» por WhatsApp | VECINA (Inés) | Espera, llama a un comercial, pide cobertura |
| Alta se lee como cobertura | VECINA + junta | Esperan instalación, velocidad, operador |
| Nodo se lee como router de casa | VECINA | «¿Me cambian el Wi-Fi?» |
| La mesa se lee como kanban | VOCAL (Pablo) | Esperan Recibido / En curso / Cerrado |
| El checklist se lee como visita técnica | VECINA + Nerea | Esperan que alguien suba a la azotea mañana |
| La dueña pide speedtest y TPV el día 1 | Asociación | El L3 se vuelve operador |
Supuestos (no investigación primaria propia)
- S1: Una asociación atiende 4–8 altas/semana; la demo tiene 3 nodos y 5 altas (3 PENDIENTE, 2 ACEPTADA). [SUPUESTO]
- S2: La vecina pide alta desde el salón; no crea cuenta. [SUPUESTO]
- S3: Un VOCAL (Pablo) acepta o rechaza; el técnico no entra en el slice L3 de código (sí en docs). [SUPUESTO]
- S4: Sin pago, sin radio planning, sin mapa GIS. [DECISIÓN]
Hipótesis de producto
| ID | Hipótesis | Señal de validación (futura) |
|---|---|---|
| H1 | Una portada de 3 nodos reduce “esto es un operador” | % de visitas a / que terminan en POST con código |
| H2 | El checklist deja claro que no hay técnico mañana | ↓ «pensé que veníais a instalar»; copy visible en acuse |
| H3 | Split lista|ficha basta; no hace falta kanban | 0 columnas Recibido / En curso; Pablo decide sin chat |
No se afirman estadísticas de telecomunicaciones inventadas. Todo lo anterior es razonamiento de diseño etiquetado.
3. Propuesta de valor
| Para | Valor |
|---|---|
| VECINA (Inés Soto) | Ve 3 nodos, marca 4 ítems, escribe planta y nombre, se lleva un código. Nadie le pide cuenta ni cobertura. |
| VOCAL (Pablo Ruiz) | Ve la lista, abre la ficha, acepta o rechaza. No mueve columnas. No instala. |
| TÉCNICA (Nerea Gil) | Fuera del slice de código. El oficio de azotea no entra en v1. |
| Asociación RAMAL | Una superficie mínima: nodos + altas + mesa, sin operador y sin chat. |
No es RAMAL
| Excluido | Por qué |
|---|---|
| Cobertura / speedtest / Mbps | Alta ≠ cobertura del operador |
| Router de casa / Wi-Fi / ONT | Nodo ≠ router de casa |
| Instalación el día 1 | RAMAL no instala hoy |
| Visita técnica programada | Checklist ≠ técnico |
| Movistar / operadora comercial | RAMAL no es el operador |
| Chat de incidencias / kanban | Eso fue PREGÓN (H-CHAT / F-KAN) |
| Quiosco de horas / bloques de 2 h | Eso fue TEMPLE (H-KIOSK / F-BOOK) |
| Wizard de fincas + comparador | Eso fue ZAGUÁN (H-WIZARD / F-CMP) |
| Hero 2-col + 3 cards de «tu fibra» | Anti-patrón AP-12; contradice H-FULL |
| Pago / pasarela / cuota | S4 |
| Radio planning / mapa GIS | S4 |
| App de 40 nodos y NOC | L3 compacto, no OSS/BSS |
4. Objetivos
Negocio / caso de estudio
- Demostrar vertical slice L3 telecomunicaciones / comunidad vecinal con Node + Alta + JWT VOCAL.
- Portfolio coherente: Paper (12 UX + 12 UI) + docs + app runnable.
- Terna R-CL · S-SPLIT · H-FULL · F-ONB frente a TEMPLE (kiosk/horas), PREGÓN (chat/kanban) y MATIZ (bento/editor).
Usuario
| Rol | Objetivo medible en demo |
|---|---|
| VECINA | Ver 3 nodos, completar checklist y ver el código en < 90 s, sin cuenta |
| VOCAL | Entrar a mesa, ver 5 altas, abrir Inés Soto RM-2201 y aceptar en < 45 s |
| TÉCNICA | No encuentra login; el oficio de azotea no está en la app |
No objetivos v1 (explícitos)
- Cobertura, speedtest, Mbps, SLA de operador
- Router de casa, Wi-Fi, ONT, radio planning
- Instalación, visita técnica, cuadrante de Nerea
- Chat, kanban, columnas Recibido / En curso
- Cuenta de vecina, seguimiento con login, push
- Pago, cuota, pasarela
- Mapa GIS / cadastro
- Multi-asociación, multi-vocal
- App nativa ni notificaciones push
- Rol TÉCNICA autenticado
5. Roles y permisos (resumen)
| Acción | Anónimo (vecina) | VOCAL | TÉCNICA |
|---|---|---|---|
| Ver portada / nodos | Sí | Sí | No hay cuenta |
| POST alta | Sí | Sí (no es el job) | No |
Ver acuse /ok/:code | Sí (código en URL) | Sí | — |
| Ver mesa | No (401 / redirect) | Sí | No |
GET /api/mesa | 401 | Sí | — |
| Aceptar / rechazar | No | Sí | No |
| Ver lista de vecinas en portada | No | Sí (mesa) | — |
| Vender cobertura | No (no existe) | No | Fuera |
| Subir a la azotea | Copy: fuera | Copy: fuera | Oficio fuera de app |
[DECISIÓN] Un solo rol autenticado: VOCAL. La vecina no es un usuario del sistema: es autora/autor de un alta. La técnica no es un rol de sistema el día 1.
6. Métricas (modelo, no instrumentadas en v1 salvo base)
| Tipo | Métrica | Definición |
|---|---|---|
| North Star | % de altas que Pablo mueve en 7 días | Oficio de junta, no pageviews |
| Activación vecina | 1.er POST con code | H1 |
| Integridad de expectativa | Reclamaciones «¿cuándo instaláis?» / «¿es Movistar?» / «¿es un speedtest?» | H2 + microcopy |
| Integridad de auth | GET /api/mesa sin token → 401; POST /api/altas sin token → 201 | Portada pública + mesa cerrada |
| Salud | Error rate API nodes / altas | 4xx/5xx |
7. Alcance funcional v1 (L3)
| Módulo | Incluido |
|---|---|
| Portada | GET /api/nodes · 3 nodos (lead + 3 datos) a sangre |
| Checklist | /alta · 4 ítems, una pantalla, no wizard |
| Acuse público | POST /api/altas → RM-XXXX PENDIENTE |
| Auth | POST /api/auth/login → JWT VOCAL |
| Mesa | GET /api/mesa JWT · lista | ficha |
| Decisión | PATCH /api/mesa/:code · ACEPTADA / RECHAZADA |
| Estados UI | Empty (0 nodos / 0 altas), loading skeleton, error de red, 409 ya decidida |
| Mobile | Portada ~390px · checklist · targets ≥44px |
| Seed | 1 VOCAL, 3 nodos, 5 altas (3 PENDIENTE, 2 ACEPTADA) |
| Salud | GET /api/health → { ok: true, service: "ramal-api" } |
8. Criterios de aceptación de producto
/es la portada de 3 nodos a sangre. No pide cuenta. No es un hero 2-col + 3 cards.- Una vecina puede ver 3 nodos en el recorte 16/10 (lead + 3 datos), tocar Azotea Palomeras, completar el checklist y obtener un código
RM-XXXX. /ok/:codedice explícitamente que alta no es cobertura del operador y que RAMAL no instala hoy.- La UI dice explícitamente que nodo no es router de casa y que checklist no es técnico.
- Sin token, GET
/api/mesaresponde 401. POST/api/altasy GET/api/nodesson públicos. - Un POST de alta crea
status=PENDIENTE. Completar no programa una visita. - Un VOCAL inicia sesión y ve 5 altas (3 PENDIENTE, 2 ACEPTADA) en el seed.
- El tablero es un split lista | ficha. No es un kanban de 3 columnas.
- Fotos reales en los 3 nodos seed (assets
nodos/*). - Checklist visible: vivienda · planta · azotea · no instala.
- Chrome S-SPLIT en mesa; portada pública sin top-nav de marketing.
- Rutas solo las del doc 03. No existen
/cobertura,/speedtest,/chat,/turno,/kanban. GET /api/healthdevuelveservice: "ramal-api".- La portada no muestra nombres de otras vecinas; la mesa sí.
9. Stack y artefactos
| Capa | Detalle |
|---|---|
| Frontend | Angular + Tailwind · puerto 4200 |
| Backend | NestJS · puerto 3022 |
| DB | Neon PostgreSQL · project gentle-bonus-75761237 |
| Auth | JWT (VOCAL) |
| Diseño | Paper 01M0M4FJGXPG315PDNT7PCAY3J |
| Repo app | /Users/cristian/orca/ramal-app/ · GitHub Criscode2022/ramal-app |
[COMPROBADO] Puerto, Neon, Paper file ID, repo y path de app constan en el encargo.
El código se escribe en paralelo a esta suite.
10. Riesgos y mitigaciones
| Riesgo | Impacto | Mitigación v1 |
|---|---|---|
| Expectativa de cobertura | «¿Cuándo me ponéis fibra?» | Copy «Alta ≠ cobertura del operador» |
| Expectativa de router | Wi-Fi, ONT, speedtest | Copy «Nodo ≠ router de casa» |
| Expectativa de instalación | Técnico mañana | Copy «RAMAL no instala hoy» |
| Expectativa de kanban | Recibido / En curso | Split lista|ficha, no columnas de estado |
| Pago el día 1 | Alcance L4 disfrazado | S4: sin pasarela |
| Cuenta de vecina | Rompe S2 / H3 | Sin signup; nombre libre en el alta |
| Home percibida como landing de operadora | Pérdida de H-FULL / AP-12 | 3 nodos a sangre; no hero 2-col |
| Confundir con TEMPLE (kiosk + horas) | Expectativa de bloques de 2 h | Portada de nodos; no slots |
| Confundir con PREGÓN (chat + kanban) | Expectativa de parte | Checklist; no hilo |
| Confundir con Movistar | Expectativa de cobertura | Microcopy ×4 |
| Doble decisión de la misma alta | Dos estados | 409 si no está PENDIENTE |
| Enumeración de altas por código | PII de vecina en público | GET mesa JWT; /ok/:code no hidrata ficha ajena |
| 0 nodos | Portada vacía sin oficio | Empty «Aún no hay nodos.» |
| WhatsApp paralelo | H1 no se mide | North star etiquetada como modelo |
11. Glosario
| Término | Definición en RAMAL |
|---|---|
| Ramal | Tramo de red de bloque; el producto, no un operador |
| Nodo | Punto físico (nodes); no es el router de casa |
| Router | Equipo de salón; fuera |
| Alta | POST 201 + código; no significa cobertura |
| Acuse | Código (RM-XXXX); no es un contrato de operador |
| Cobertura | Promesa comercial de Mbps; fuera. RAMAL no es el operador |
| Portada | Superficie H-FULL pública |
| Mesa | Superficie S-SPLIT del VOCAL (lista | ficha) |
| Kanban | Lenguaje de diseño de otros días; no hay columnas aquí |
| VOCAL | Usuario autenticado que opera la mesa |
| VECINA | Autora/autor del alta; no tiene fila en users |
| TÉCNICA | Oficio de azotea; fuera del slice de código |
| Checklist | 4 ítems en una pantalla; no es un técnico |
| Hielo | Ground #E7F0F5 |
| Ink | Mástil #1A2A32 |
| Card | Ficha #FFFFFF |
| Mark | Cobre oxidado #3D7A96 |
| Mute | Galvanizado #5C6B73 |
| Seal | Precinto #B42318, poco |
| Operador | Movistar y similares; fuera |
12. Decisiones de diseño (cierre de brief)
| ID | Decisión | Alternativa descartada |
|---|---|---|
| D1 | Home = portada a sangre + 3 nodos (H-FULL) | Hero 2-col + 3 cards (AP-12) · kiosk (TEMPLE) · chat (PREGÓN) |
| D2 | Shell split en mesa (S-SPLIT) | Rail de estados · bottom tabs · kiosk mínimo · wizard chrome |
| D3 | Registro clásico institucional (R-CL) | Cyan NOC (primer instinto; operador) · vehicle dashboard TEMPLE |
| D4 | Flujo F-ONB (checklist de alta) | F-BOOK, F-KAN, F-EDIT, F-CMP, F-CHAT |
| D5 | Mood ice | Cyan SaaS / NOC (primer instinto), ink dashboard, highlighter |
| D6 | Un rol autenticado VOCAL | Multi-rol técnica / vecina / operador |
| D7 | Status PENDIENTE / ACEPTADA / RECHAZADA | Kanban de 3 columnas; «instalada»; «en cobertura» |
| D8 | Vecina sin cuenta | Signup, seguimiento con login |
| D9 | Portada no lista nombres de otras vecinas | Tablero público enumerable |
| D10 | POST público + checklist 4 ítems | Formulario de DNI + pago + radio planning |
| D11 | Ground G-ICE #E7F0F5 | G-INK, G-FOG, G-CHAPEL, G-CONCRETE, G-GALLERY |
| D12 | Success de acuse en /ok/:code | Toast único sin pantalla |
| D13 | Checklist una pantalla, no wizard | Pasos 1—2—3; chrome F-WIZ |
| D14 | Mesa = split, no kanban | Recibido / En curso / Cerrado |
| D15 | Portada pública + mesa JWT (no auth-first total) | Todo cerrado (rompería S2) · todo abierto (rompería mesa) |
| D16 | Sin pago | Pasarela en alta |
| D17 | 409 si el alta ya no está PENDIENTE | Reabrir «por si acaso» |
13. Relación con el día anterior
| Case | Terna | Por qué RAMAL no lo copia |
|---|---|---|
| TEMPLE (N−1) | S-MIN / H-KIOSK / F-BOOK | No kiosk, no horas, no concierto, no turno |
| PREGÓN (N−2) | S-SIDE / H-CHAT / F-KAN | No admin pública, no chat, no kanban, no 112 |
| MATIZ (N−3) | S-BOTTOM / H-BENTO / F-EDIT | No belleza, no PWA de oficio, no editor de gramos |
| ZAGUÁN (N−4) | S-WIZ / H-WIZARD / F-CMP | No inmobiliaria, no wizard, no comparador |
[COMPROBADO] El brief fija terna R-CL · S-SPLIT · H-FULL · F-ONB. 0 códigos iguales a N−1 (TEMPLE). ≥2 distintos vs N−2 (PREGÓN) y N−3 (MATIZ). Ventana 4 días: R-HY + R-IN + R-HY + R-CL. Ground G-ICE #E7F0F5, no G-INK.
02-ux-research-strategy.md
02 — Estrategia de investigación UX — RAMAL
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 del bloque | Modelado de stakeholders + personas | §3–4 |
| Definir job de «pedir nodo en mi bloque» y de decidir altas | JTBD + stories Must | §5 |
| Mapear fricción WhatsApp / operador / router → portada | Journey + service blueprint | §6–7 |
| Traducir a requisitos L3 portada + mesa | Matriz hallazgo → requisito → feature | §8 |
2. Fuentes y límites
Fuentes admisibles (secundarias / operativas)
- Conocimiento general de redes comunitarias y asociaciones de vecinos (nodo en azotea, alta en junta, técnico que no entra el día 1, distinción red de bloque vs operador comercial).
- Analogía operativa con superficie pública + panel JWT de la serie daily.
- Restricciones ALS-2: no Movistar, no speedtest, no chat, no kanban, no kiosk de horas.
Límites éticos de verdad
| Prohibido | Permitido |
|---|---|
| «El 62% de vecinas de Vallecas pide alta por WhatsApp» sin fuente | «[SUPUESTO] Inés abre la portada desde el salón» |
| Citas de entrevistas ficticias como campo real | Quotes de persona etiquetadas como constructo de diseño |
| Volumen medido «17 altas/semana en Palomeras» como KPI de mercado | Altas de seed de producto, etiquetadas como demo |
| Inventario oficial de una red comunitaria 2026 | Nodos de seed; Inés, Pablo y Nerea son personas de diseño |
| «Inés existe y nos dijo…» | Inés Soto es persona de diseño |
3. Stakeholders
| Stakeholder | Influencia | Interés | Necesidad principal |
|---|---|---|---|
| VECINA (Inés) | Alta en el alta | Muy alta | Pedir nodo en su bloque sin cuenta ni comercial |
| VOCAL (Pablo) | Alta | Muy alta | Ver altas y decidir sin kanban |
| TÉCNICA (Nerea) | Media formal, baja en producto día 1 | Alta | Oficio de azotea; fuera del slice de código |
| Asociación / junta | Alta formal | Alta | Que entren altas; que no se confunda con operador |
| Operador comercial | Competencia / analogía falsa | — | Fuera de alcance; no clonar cobertura |
| Chat / 112 / parte | Canal paralelo / analogía falsa | — | Fuera; no clonar PREGÓN |
| Grupo de WhatsApp del bloque | Canal paralelo | — | No es el backoffice |
Mapa de poder (resumen)
- Decisor de adopción: la junta (quién pone la portada) y, en uso diario, Pablo (quien mira la mesa).
- Usuario frecuente de pedir alta: VECINA (portada + checklist, sin cuenta).
- Usuario frecuente de decisión: VOCAL (mesa).
- Riesgo de rechazo: si el acuse parece un contrato de cobertura; si el nodo parece router; si hay que crear cuenta; si la mesa parece un kanban.
4. Personas
P1 — Inés Soto · VECINA
| Campo | Detalle |
|---|---|
| Edad / contexto | 52 años; vive en Palomeras; 4º B; no quiere «otra cuenta de operadora» |
| Digital | Móvil desde el salón; no llama a un comercial si puede evitarlo |
| Goals | Ver si hay nodo en su bloque; enviar un alta; llevarse un código |
| Pains | El grupo dice «creo que hay fibra»; llama a Movistar; le venden cobertura |
| Quote de diseño | «Solo quiero el nodo del bloque. No me vendas un operador.» |
Escenario: Abre / en el salón, toca Azotea Palomeras, marca los 4 ítems, escribe Inés Soto / 4º B, lee «Alta ≠ cobertura» y el código RM-XXXX.
[COMPROBADO] Nombre, edad y gesto (vecina, 52) salen del encargo. Es constructo de diseño, no entrevistada. [DECISIÓN]
P2 — Pablo Ruiz · VOCAL
| Campo | Detalle |
|---|---|
| Edad / contexto | 38 años; vocal de la asociación; mesa de altas, no de instalación |
| Digital | Fluido en desktop de mesa; la portada pública no es su herramienta |
| Goals | Ver las altas, abrir la ficha, aceptar o rechazar |
| Pains | WhatsApp a deshora; «¿cuándo instaláis?»; un kanban que no es su oficio; speedtest que no es su job |
| Quote de diseño | «Quiero ver las altas y decidir. No un kanban.» |
| Email demo | pablo@ramal.vallecas / password123 |
Escenario: Entra a /login, abre /mesa, toca RM-2201 Inés Soto, lee la ficha, pulsa Aceptar. El copy dice que aceptar no sube a la azotea.
[COMPROBADO] Nombre, edad, email demo y rol salen del encargo.
P3 — Nerea Gil · TÉCNICA (docs)
| Campo | Detalle |
|---|---|
| Edad / contexto | 29 años; técnica de azotea; oficio de mástil, no de mesa |
| Digital | No tiene login en el slice L3 |
| Goals | Subir cuando la junta lo pida (fuera de app) |
| Pains | Que el checklist se lea como una visita suya |
| Quote de diseño | «El alta no me programa. Yo no entro aquí.» |
[COMPROBADO] Nombre, edad y exclusión de login salen del encargo. [DECISIÓN] no hay cuenta TÉCNICA en v1.
5. JTBD
Job principal (vecina)
Cuando quiero saber si hay nodo en mi bloque, quiero pedir alta sin llamar a un comercial, para dejar constancia en la junta sin que eso signifique cobertura ni técnico mañana.
Job principal (vocal)
Cuando hay altas nuevas, quiero ver lista y ficha y decidir, para mover el oficio de la asociación sin un kanban ni un NOC.
Jobs que RAMAL no hace
| Job | Por qué no |
|---|---|
| Contratar cobertura de operador | Alta ≠ cobertura |
| Medir Mbps | No es speedtest |
| Programar visita de Nerea | Checklist ≠ técnico |
| Chat con la junta | ≠ PREGÓN |
| Coger hora de ensayo | ≠ TEMPLE |
6. Stories Must (v1)
| ID | Como | Quiero | Para |
|---|---|---|---|
| US-01 | Vecina | Ver 3 nodos (lead + 3 datos) | Distinguir red de bloque de operador |
| US-02 | Vecina | Completar checklist en una pantalla | Pedir alta sin wizard ni cuenta |
| US-03 | Vecina | Ver un código RM-XXXX | Tener acuse, no cobertura |
| US-04 | Vocal | Entrar con JWT | Abrir la mesa |
| US-05 | Vocal | Ver lista | ficha | Decidir sin kanban |
| US-06 | Vocal | Aceptar o rechazar | Mover PENDIENTE |
Could (fuera de L3): login de Nerea, mapa GIS, radio planning, pago, speedtest, historial de vecina.
7. Journey (resumen)
- Inés abre
/. Ve foto a sangre y 3 nodos. [H1] - Toca Azotea Palomeras →
/alta?nodo=azotea. - Marca vivienda, planta, azotea, no instala. Escribe nombre y 4º B.
- POST →
/ok/RM-XXXX. Lee las cuatro oposiciones. - Pablo entra a
/login→/mesa. Lista a la izquierda. - Abre
/mesa/RM-2201. Acepta. Copy: no sube a la azotea.
Backstage: Nest + Neon + JWT. Nerea no entra. El mástil no se mueve.
8. Matriz hallazgo → requisito → feature
| Hallazgo (etiquetado) | Requisito | Feature v1 |
|---|---|---|
| [HIPÓTESIS H1] Portada de 3 nodos reduce “operador” | 3 nodos en fold, lead + 3 datos | / H-FULL |
| [HIPÓTESIS H2] Checklist evita técnico mañana | 4 ítems + copy irrenunciable | /alta F-ONB |
| [HIPÓTESIS H3] Split basta | Lista | ficha, no columnas | /mesa S-SPLIT |
| [SUPUESTO S2] Sin cuenta de vecina | POST público | POST /api/altas |
| [SUPUESTO S3] Un vocal | Un rol JWT | VOCAL |
| [DECISIÓN S4] Sin pago / GIS | No pasarela, no mapa | Fuera de alcance |
9. Preguntas que este case no responde
- ¿Cuántas altas reales hay en Palomeras en 2026?
- ¿Qué asociación opera un mástil en Sierra Gádor?
- ¿Inés preferiría WhatsApp?
- ¿Nerea necesita un cuadrante?
Esas preguntas piden campo. Aquí hay un vertical slice de diseño, no un estudio.
10. Cierre
Investigación = modelado etiquetado + restricciones ALS-2.
No hay entrevistas inventadas.
Las personas son constructos. Vallecas / Palomeras / Sierra Gádor son [SUPUESTO de catálogo].
03-information-architecture.md
03 — Arquitectura de información — RAMAL
1. Principios de IA
| Principio | Aplicación |
|---|---|
| Portada first | La home son 3 nodos a sangre. No hay landing de marca por delante |
| Público / mesa | La vecina pide alta sin cuenta; Pablo entra con JWT |
| Nodo → checklist → acuse | Profundidad 2 desde la portada hasta el código |
| Mesa = split | Lista a la izquierda, ficha a la derecha; no kanban |
| Lenguaje de dominio | Nodo, alta, acuse, mesa — no «cobertura», «lead», «expediente», «parte» |
| Alta no es cobertura | El acuse no tiene Mbps ni contrato |
| Nodo no es router | El mástil está en la azotea, no en el salón |
| Chrome de oficio | S-SPLIT en mesa; portada sin top-nav de marketing |
2. Sitemap
/ Portada pública (3 nodos) H-FULL
/alta Checklist 4 ítems F-ONB · público
/ok/:code Acuse post-alta público
/login Login JWT VOCAL público
/mesa Split lista | vacío de ficha auth
/mesa/:code Split lista | ficha aceptar/rechazar auth
/** → redirect /
Árbol por audiencia
| Audiencia | Nodos relevantes |
|---|---|
| VECINA | Portada → Alta → Ok. No ve mesa |
| VOCAL | Login → Mesa · (puede leer la portada) |
| TÉCNICA | Ninguno como rol de sistema |
[COMPROBADO] Seis rutas del encargo L3. No se inventan /cobertura, /speedtest, /chat, /turno, /kanban, /tecnico.
El checklist sí es ruta (/alta): una pantalla, no wizard chrome. [DECISIÓN]
La ficha sí es ruta (/mesa/:code) porque S-SPLIT necesita URL compartible para Pablo. [DECISIÓN]
3. Navegación
Portada pública (H-FULL · sin top-nav de marketing)
| Elemento | Destino | Notas |
|---|---|---|
| Wordmark RAMAL | / | Sello sobre la foto; no es hero 2-col |
| Eslogan | — | «La red del bloque, no el operador.» |
| Card nodo | /alta?nodo=:slug | 3 tiles con lead + 3 datos |
| Link Vocal | /login o /mesa si hay token | Secundario, mute, no CTA de marca |
[DECISIÓN] La portada no tiene tabs, no tiene hamburger, no tiene «Cobertura / Tarifas / Área cliente». Un link «Vocal» discreto basta para Pablo.
Chrome de mesa (S-SPLIT autenticado)
| Slot | Destino | Visibilidad | Notas |
|---|---|---|---|
| Wordmark / Mesa | /mesa | Auth | Titular de oficio |
| Lista | /mesa/:code | Auth | Columna izquierda |
| Ficha | — | Auth | Columna derecha |
| Portada | / | Auth | Secundario |
| Salir | limpia token → / | Auth | Cabecera, no rail |
[DECISIÓN] No hay rail S-SIDE (eso fue PREGÓN). No hay kiosk S-MIN (TEMPLE). No hay bottom tabs (MATIZ). La mesa es un split a pantalla.
Chrome de alta (F-ONB)
| Slot | Destino | Notas |
|---|---|---|
| ← Nodos | / | Un paso atrás, no stepper 1—2—3 |
| Formulario | POST /api/altas | Una pantalla |
| — | No hay «Siguiente / Anterior» | El checklist no es wizard |
4. Etiquetas de IA (UI)
| Concepto | Label |
|---|---|
| Home | Portada / Nodos (no «Inicio», no «Cobertura») |
| Alta | Pedir alta / Pedir nodo en el bloque |
| Success | Alta enviada / código |
| Staff | Mesa (no «Backoffice», no «Turno», no «Kanban») |
| Auth | Vocal (no «Área cliente») |
5. Contenido de cada nodo de IA
| Ruta | Qué hay | Qué no hay |
|---|---|---|
/ | Foto a sangre, 3 nodos, lead + 3 datos | Precios, Mbps, login de vecina |
/alta | Nodo, nombre, vivienda, 4 checks | DNI, pago, fecha de visita |
/ok/:code | Código + 4 oposiciones | Contrato, cobertura, «ya está instalado» |
/login | Email / clave VOCAL | Signup de vecina |
/mesa | Lista de 5 altas | Columnas Recibido / En curso |
/mesa/:code | Ficha + Aceptar / Rechazar | Cuadrante de Nerea |
6. Error / empty / loading (sitio)
Viven en las mismas rutas, no en URLs extra.
| Estado | Superficie |
|---|---|
| Loading | Skeletons de 3 nodos / 5 filas / ficha |
| Empty portada | «Aún no hay nodos.» |
| Empty mesa | «No hay altas.» |
| Error red | «La red no responde» + Reintentar |
| 404 alta | «Esa alta no está» en ficha |
| 409 | «Esa alta ya no está pendiente» |
7. Profundidad y búsqueda
Sin buscador en v1. 3 nodos y 5 altas caben en una lista. [DECISIÓN]
Pablo ordena por PENDIENTE primero, no filtra por nodo. Could L+1.
04-user-flows.md
04 — Flujos de usuario — RAMAL
1. Convenciones
| Token | Significado |
|---|---|
| F-ONB | Checklist de alta, una pantalla |
| Mesa | Split VOCAL, JWT |
| 201 | Alta creada PENDIENTE |
| 401 | Mesa sin token |
| 409 | Alta ya decidida |
2. F1 — Vecina pide alta (Must)
/ → GET /api/nodes
→ tap nodo → /alta?nodo=azotea
→ marca 4 ítems + nombre + vivienda
→ POST /api/altas
→ 201 { code: RM-XXXX, status: PENDIENTE }
→ /ok/RM-XXXX
Entrada: portada, sin cuenta.
Salida: código visible + copy «alta ≠ cobertura».
Errores: red (reintentar), checklist incompleto (400, no POST), 0 nodos (empty).
AC
- 4 checks obligatorios. El botón permanece disabled hasta completar.
- No hay paso 2. No hay «Siguiente».
- POST no cobra, no crea user, no llama a Nerea.
3. F2 — Vocal entra a mesa (Must)
/login → POST /api/auth/login
→ JWT VOCAL
→ /mesa
→ GET /api/mesa
→ tap RM-2201 → /mesa/RM-2201
→ GET /api/mesa/RM-2201
AC
- Sin token,
/mesaredirige a/login?next=/mesa. - GET
/api/mesasin Bearer → 401. - Lista a la izquierda, ficha a la derecha (desktop). Mobile: lista o ficha, no las dos a la vez si no cabe.
4. F3 — Vocal acepta / rechaza (Must)
ficha PENDIENTE
→ PATCH /api/mesa/:code { status: ACEPTADA | RECHAZADA }
→ 200 ficha actualizada
→ lista refleja el status
Si ya no está PENDIENTE → 409. Copy: «Aceptar no sube a la azotea.»
AC
- Aceptar no cambia el nodo, no crea visita, no envía email.
- Rechazar no es un parte municipal.
- Los botones desaparecen cuando el status no es PENDIENTE.
5. F4 — Estados de sistema
| Estado | Trigger | UI |
|---|---|---|
| Loading portada | GET nodes en vuelo | 3 skeletons |
| Empty portada | 200 [] | «Aún no hay nodos.» |
| Error portada | 5xx / red | Reintentar |
| Loading mesa | GET mesa en vuelo | 5 skeletons + ficha |
| Empty mesa | 200 [] | «No hay altas.» |
| Loading ficha | GET :code | Skeleton de ficha |
| 404 ficha | código inexistente | «Esa alta no está.» |
6. Flujos que no existen
| Flujo | Por qué |
|---|---|
| Signup vecina | S2 |
| Pago | S4 |
| Speedtest | Alta ≠ cobertura |
| Chat | ≠ PREGÓN |
| Coger hora | ≠ TEMPLE |
| Wizard 1—2—3 | F-ONB es una pantalla |
| Login Nerea | Slice L3 |
7. Tiempos de demo (modelo)
| Actor | Recorrido | Techo |
|---|---|---|
| Inés | Portada → alta → código | < 90 s |
| Pablo | Login → mesa → aceptar RM-2201 | < 45 s |
No son KPIs medidos. Son techos de diseño para el guión. [HIPÓTESIS]
05-data-model.md
05 — Modelo de datos — RAMAL
1. Visión general
Dominio L3 de portada de nodos + mesa autenticada:
| Entidad | Tabla SQL | Propósito |
|---|---|---|
| User | users | Identidad de oficio (rol VOCAL) |
| Session | sessions | Sesión JWT / token persistido |
| Node | nodes | Nodo de bloque (azotea / bloque / arqueta) |
| Alta | altas | Petición de vecina (código RM-XXXX) |
Base: PostgreSQL (Neon project gentle-bonus-75761237) · IDs: serial + texto estable (azotea, RM-2201).
[DECISIÓN] ensureSchema es válido si el contrato de tablas se cumple; el brief de stack no exige Prisma.
Contrato de producto: nodes + altas + JWT VOCAL. No rooms / bookings / bandos / reports.
2. Enums (aplicación; persistidos como TEXT + check)
Role
| Valor | Descripción |
|---|---|
VOCAL | Vocal de la mesa; único rol autenticado v1 |
AltaStatus
| Valor | Descripción |
|---|---|
PENDIENTE | Alta enviada; no implica cobertura ni visita |
ACEPTADA | Pablo aceptó; no implica instalación |
RECHAZADA | Pablo rechazó; no es un parte |
NodeKind
azotea | bloque | arqueta
3. Diagrama ER (texto)
users
id, email, password_hash, name, role(VOCAL)
created_at
1 ──< sessions
sessions
id, user_id → users
expires_at, revoked_at
created_at
nodes
id
slug (unique)
name, kind, street, lead
data_1, data_2, data_3
cover
sort_order
created_at, updated_at
1 ──< altas
altas
id
code (unique, RM-XXXX)
node_id → nodes
neighbor_name
dwelling
status (PENDIENTE|ACEPTADA|RECHAZADA)
created_at, decided_at, updated_at
v1: un solo VOCAL opera sobre el conjunto global.
No hay FK de vecina-usuaria: la vecina no se loguea.
altasno apunta ausers: el alta es anónima de cuenta.
4. Tablas
users
| Columna | Tipo | Notas |
|---|---|---|
| id | BIGSERIAL PK | |
| TEXT UNIQUE | pablo@ramal.vallecas | |
| password_hash | TEXT | bcrypt |
| name | TEXT | Pablo Ruiz |
| role | TEXT | VOCAL |
| created_at | TIMESTAMPTZ |
sessions
| Columna | Tipo | Notas |
|---|---|---|
| id | UUID PK | sid del JWT |
| user_id | BIGINT FK | |
| expires_at | TIMESTAMPTZ | |
| revoked_at | TIMESTAMPTZ | logout |
| created_at | TIMESTAMPTZ |
nodes
| Columna | Tipo | Notas |
|---|---|---|
| id | BIGSERIAL PK | |
| slug | TEXT UNIQUE | azotea / bloque / arqueta |
| name | TEXT | Azotea Palomeras |
| kind | TEXT | azotea | bloque | arqueta |
| street | TEXT | Palomeras |
| lead | TEXT | Una frase. Nodo ≠ router. |
| data_1 | TEXT | Dato 1 del tile |
| data_2 | TEXT | Dato 2 |
| data_3 | TEXT | Dato 3 |
| cover | TEXT | /assets/nodos/azotea.jpg |
| sort_order | INTEGER | 1, 2, 3 |
| created_at / updated_at | TIMESTAMPTZ |
altas
| Columna | Tipo | Notas |
|---|---|---|
| id | BIGSERIAL PK | |
| code | TEXT UNIQUE | RM-2201 |
| node_id | BIGINT FK | |
| neighbor_name | TEXT | Inés Soto |
| dwelling | TEXT | 4º B |
| status | TEXT | PENDIENTE por defecto |
| created_at | TIMESTAMPTZ | |
| decided_at | TIMESTAMPTZ | null hasta PATCH |
| updated_at | TIMESTAMPTZ |
Índices: nodes(slug), altas(code), altas(status), altas(created_at DESC), users(email).
5. Seed
VOCAL
pablo@ramal.vallecas / password123 · Pablo Ruiz · VOCAL
Nodos
| slug | name | kind | 3 datos |
|---|---|---|---|
| azotea | Azotea Palomeras | azotea | Mástil · Palomeras · 18 viviendas al alcance |
| bloque | Bloque Sierra Gádor | bloque | Patio · Sierra Gádor · 24 viviendas |
| arqueta | Arqueta Pablo Neruda | arqueta | Acera · Pablo Neruda · 2 bloques |
Altas
| code | neighbor | node | dwelling | status |
|---|---|---|---|---|
| RM-2201 | Inés Soto | azotea | 4º B | PENDIENTE |
| RM-2202 | Luis Herrero | bloque | 2º A | PENDIENTE |
| RM-2203 | Carmen Vidal | arqueta | 1º C | PENDIENTE |
| RM-2204 | Rosa Méndez | azotea | 6º A | ACEPTADA |
| RM-2205 | Toni Casas | bloque | 3º D | ACEPTADA |
[SUPUESTO de catálogo] Nombres de vecinas secundarias, calles y conteos de viviendas son atrezzo.
6. JSON (camelCase)
API: neighborName, nodeSlug, nodeName, nodeKind, sortOrder, createdAt, decidedAt, data1, data2, data3.
SQL: neighbor_name, node_id, sort_order, created_at, decided_at, data_1.
7. Generador de código
SELECT max(code) numérico + 1, formateado RM- + 4 dígitos. Seed máximo 2205 → siguiente POST = RM-2206 (si no hay altas nuevas). Si se agota 9999, L+1 amplia a 5 dígitos; no es Must.
8. Qué no se modela
| Fuera | Motivo |
|---|---|
| Tabla technicians | Nerea no entra |
| Tabla payments | S4 |
| Tabla coverage / speedtests | Alta ≠ cobertura |
| GIS / lat-lng | S4 |
| rooms / bookings | Dominio de otro día; no es el contrato |
06-tech-stack.md
06 — Stack tecnológico — RAMAL
1. Visión
| Capa | Tecnología | Notas |
|---|---|---|
| Frontend | Angular standalone + signals | Puerto 4200 · web responsive |
| Estilos | Tailwind CSS | Tokens ice (hielo, ink, card, mark, mute, seal) |
| Backend | NestJS TypeScript | Puerto 3022, prefijo /api |
| Persistencia | PostgreSQL (ensureSchema) | Tablas users, nodes, altas, sessions |
| DB | Neon PostgreSQL | project gentle-bonus-75761237 |
| Auth | JWT | Rol VOCAL (mesa); portada y POST altas públicos |
| Diseño | Paper | 01M0M4FJGXPG315PDNT7PCAY3J |
| Repo | /Users/cristian/orca/ramal-app/ | GitHub Criscode2022/ramal-app |
Stack fijo del cron (D-P0-03). No React/Next/Firebase.
[COMPROBADO] Puerto 3022, Neon, Paper y repo constan en el encargo.
El código se escribe en paralelo a esta suite. Este doc es el contrato.
2. Estructura
ramal-app/
├── apps/api/ # Nest (paquete independiente)
├── apps/web/ # Angular + Tailwind (paquete independiente)
└── package.json # scripts api / start · apps independientes
D-P1-03: npm --prefix o pnpm --filter. Prohibido npm workspaces que rompen Angular (AP-06).
Scripts raíz esperados:
| Script | Acción |
|---|---|
| api | Arranca Nest en :3022 |
| start / web | Arranca Angular en :4200 |
3. API pública vs JWT
| Método | Ruta | Auth |
|---|---|---|
| GET | /api/health | Público · { ok: true, service: "ramal-api" } |
| GET | /api/nodes | Público |
| GET | /api/nodes/:slug | Público |
| POST | /api/altas | Público |
| POST | /api/auth/login | Público |
| GET | /api/mesa | JWT VOCAL |
| GET | /api/mesa/:code | JWT VOCAL |
| PATCH | /api/mesa/:code | JWT VOCAL |
CORS: origen http://localhost:4200 en local.
[COMPROBADO] Superficie API del encargo. No se añaden Must extra (/cobertura, /speedtest, /reports).
4. Justificación
| Elección | Razón |
|---|---|
| Angular + Nest | Alineación con la serie daily y el handoff del estudio |
| Neon serverless | Volumen bajo (S1: 4–8 altas/semana; demo 3 nodos × 5 altas); no cola Redis |
| JWT VOCAL | D-P1-05: mesa interna cerrada; la portada sí es pública |
| Auth en servidor para mesa | Source of truth = guard; el cliente no «esconde» nombres |
| Puerto 3022 | Evitar colisión con TEMPLE :3021 / PREGÓN :3020 / MATIZ :3019 |
| Web (no PWA Must) | S2: salón + mesa desktop; install Could L+1 |
code RM-XXXX | Acuse humano, no UUID en el salón |
| 409 si no PENDIENTE | Un alta no se decide dos veces |
5. Variables
.env local (nunca git):
| Variable | Servicio | Descripción |
|---|---|---|
DATABASE_URL | API | Neon gentle-bonus-75761237 |
JWT_SECRET | API | Firma tokens |
PORT | API | opcional, 3022 |
SESSION_TTL_SECONDS | API | opcional (default 86400) |
.env.example solo placeholders.
Web: URL de API en ApiService (default http://localhost:3022/api). Proxy Angular proxy.conf.json → :3022.
6. Módulos API
health · auth · users · nodes · altas · mesa · database · common.
Health: HealthService.name = 'ramal-api'.
main.ts PORT default 3022.
7. Frontend
Páginas: HomePage / · AltaPage /alta · OkPage /ok/:code · LoginPage /login · MesaPage /mesa y /mesa/:code.
Guard JWT en mesa. Interceptor Bearer. Token key ramal.accessToken.
8. Lo que el stack no es
No es un NOC. No es un OSS/BSS. No es un speedtest. No es Prisma obligatorio. No es un leftover de horas de ensayo: el contrato es nodes + altas + VOCAL.
07-creative-direction.md
07 — Dirección creativa — RAMAL
1. Concepto
RAMAL es una portada de red de bloque con escarcha en la teja: se entra a tres nodos y se sale con un alta, no una landing de «tu operadora». El ground es hielo de las ocho. El cobre oxidado es el mástil. El precinto sella lo urgente, poco.
Mood elegido: ice.
Primer instinto descartado: cyan SaaS / NOC. Una red de bloque se ve desde la azotea un martes, no desde un centro de operaciones.
[COMPROBADO] Mood, ground y paleta salen de docs/00-day-brief.md.
2. Territorio visual
| Eje | Sí | No |
|---|---|---|
| Superficie | Hielo #E7F0F5, ficha #FFFFFF, mástil #1A2A32 | Ink TEMPLE, fog PREGÓN, yeso MATIZ, hormigón ZAGUÁN |
| Tipo | Newsreader display + Figtree UI | Fraunces+Atkinson, Ibarra+Jost, Instrument+Sora |
| Foto | Azotea, bloque, arqueta; mástil, no rack de operador | NOC, speedtest, comercial de fibra, «familia sonriente con router» |
| Grid | Portada a sangre 16/10; 3 nodos con lead + 3 datos; split mesa | Hero 2-col + 3 cards de features (AP-12) |
| Acento | Mark #3D7A96 en CTA, nodo activo, sello | Amber dashboard, lima highlighter, amatista chapel |
| Chrome | Split en mesa; portada sin nav de marketing | Rail de kanban, kiosk mínimo, wizard 1—2—3 |
3. Paleta
| Token | Hex | Rol |
|---|---|---|
| Hielo / G-ICE | #E7F0F5 | Ground de página. Escarcha en teja, no fotocopia. |
| Ink | #1A2A32 | Texto, wordmark, mástil mojado. |
| Card | #FFFFFF | Superficie de ficha / tile / form. Nunca el fondo de página. |
| Mark | #3D7A96 | Cobre oxidado del mástil: CTA Enviar, aceptar, foco |
| Mute | #5C6B73 | Meta, labels, calle secundaria, idle |
| Seal | #B42318 | Precinto secundario, poco: error, no 112-como-envío |
Jerarquía de color: el hielo carga; el mark puntúa; la card encuadra el nodo. Si el mark pinta fondos enteros, deja de ser mástil y se vuelve «brand teal». El seal no decora el header: solo puntual (error). [DECISIÓN]
4. Tipografía
| Uso | Familia | Tamaños de referencia |
|---|---|---|
| Wordmark | Newsreader 600/700 | 24–28 desktop · 22 mobile |
| Titular de portada / mesa | Newsreader 600 | 32–44 |
| Código RM-XXXX | Newsreader 700 | 28–36 |
| Nombre de nodo | Newsreader 600 | 22–28 |
| Cuerpo / disclaimers | Figtree 400 | 16–17 / 26–28 lh |
| UI, chips, form, lista | Figtree 400/500/600 | 14 / 16 |
| Meta (kind, calle, status) | Figtree 600 | 12–13, tracking 0.12em, mark o mute |
Prohibido: Newsreader por debajo de 18px (se rompe). Figtree en el wordmark de portada (se vuelve SaaS).
Fuentes vetadas de la serie reciente: Fraunces / Atkinson Hyperlegible / Ibarra Real Nova / Jost / Libre Baskerville / Nunito / Instrument Serif / Sora / Cormorant / Public Sans / Spectral / Manrope / Zilla / Mulish / Outfit / Lora / Karla.
5. Fotografía
| Asset | Debe | No debe |
|---|---|---|
| hero.jpg | Azotea, teja, mástil, frío de mañana | Rack, NOC, familia con router |
| azotea.jpg | Mástil de bloque | Antena 5G de operador como marca |
| bloque.jpg | Patio / fachada de bloque | Render inmobiliario |
| arqueta.jpg | Arqueta de acera | Armario corporativo de operadora |
Alt: nodo + lugar, no «imagen1». Ver doc 09.
6. Registro R-CL
Institucional de asociación: márgenes serios, tipo con contraste, foto a sangre, ficha blanca. No es un dashboard de red. No es un kiosk de horas. No es un chat municipal.
La innovación está en H-FULL + S-SPLIT + F-ONB, no en un look «telecom cian». [DECISIÓN]
7. Anti-moods
| Mood | Por qué no |
|---|---|
| Cyan NOC | Primer instinto; es operador |
| Vehicle dashboard | Fue TEMPLE |
| Highlighter fog | Fue PREGÓN |
| Chapel amatista | Fue MATIZ |
| Hormigón oxidado | Fue ZAGUÁN |
08-design-system.md
08 — Sistema de diseño — RAMAL
1. Tokens
| Token | Valor | Uso |
|---|---|---|
--ground | #E7F0F5 | Fondo de página |
--ink | #1A2A32 | Texto |
--card | #FFFFFF | Superficie |
--mark | #3D7A96 | CTA, foco, status |
--mute | #5C6B73 | Meta |
--seal | #B42318 | Error |
--font-display | Newsreader | Titulares |
--font-sans | Figtree | UI |
--target | 44×44 min | Toques |
Tailwind: bg-ground, text-ink, bg-card, bg-mark, text-mute, text-seal, font-display.
2. Escala de tipo
| Rol | Size / leading |
|---|---|
| Display portada | clamp(2.4rem, 6vw, 4.4rem) / 0.95 |
| H1 mesa | 3rem / 1 |
| H2 nodo | 28px / 32px |
| Cuerpo | 16–17 / 26 |
| Meta | 12–13 / 16, tracking 0.12em, uppercase |
3. Componentes
Tile de nodo (portada)
Foto 16/10 recortada + kind · calle + nombre + lead + 3 datos. Toda la card es el hit. Destino /alta?nodo=:slug.
Checklist (alta)
4 labels con checkbox ≥20px + texto. Fieldset. Disabled CTA hasta complete.
Acuse
Código Newsreader enorme + bloque mark con nodo y nombre + 4 oposiciones en mute.
Fila de mesa
Código · status · vecina · nodo · vivienda. Ring mark si seleccionada.
Ficha
Código, nombre, dl nodo/vivienda, copy irrenunciable, Aceptar (mark) / Rechazar (ground).
Login
Wordmark + dos inputs card + CTA mark. Ayuda mute con demo.
Skeleton
animate-pulse bg-card (o bg-ground sobre card). aria-busy.
Error
Titular display + cuerpo mute + Reintentar mark.
4. Layouts
| Superficie | Grid |
|---|---|
| Portada | Foto 16/10 a sangre; debajo 3 cols desktop, 1 col 390 |
| Alta | Columna max-w-2xl |
| Ok / login | Columna max-w-lg centrada |
| Mesa | minmax(280px,360px) 1fr · stacked < lg |
Max page 1440px. Padding 20 / 40 / 48.
5. Estados de control
| Estado | Tratamiento |
|---|---|
| Default | Card blanca, ink |
| Hover tile | Sin lift; el oficio no flota |
| Focus | Ring mark 2px |
| Disabled | opacity 50–60 |
| Error | Texto seal, no solo color |
| Selected (split) | Ring mark |
6. Iconografía
Cero iconos decorativos de «wifi / 5G / cohete». El oficio es tipográfico + foto. [DECISIÓN]
7. Motion
120–160ms ease-out. Reduced motion: 0ms. Skeletons pulse; no spinners de operador.
8. Densidad (AP-21)
En el recorte 16/10 de portada: eslogan + 3 nodos con lead + 3 datos. Headline huérfano + foto = card hueca. [COMPROBADO] CRON §5.8.
09-content-guide.md
09 — Guía de contenido — RAMAL
1. Voz
Cercana de bloque y de mesa. Tuteo en UI a la vecina. Segunda persona cuando la interfaz habla. Tercera cuando habla la mesa (Inés Soto ocupa…).
Sin jerga de operador («tu cobertura», «hasta 600 Mb», «contrata ahora»). Sin speedtest. Sin «reserva tu slot». Sin sirena. Sin kanban en UI.
El producto se pide y se decide. No vende cobertura, no es router, no instala hoy, no es técnico.
2. Palabras permitidas / prohibidas
| Usar | No usar |
|---|---|
| Nodo / mástil / azotea | Router de casa, ONT, Wi-Fi (como producto) |
| Alta / pedir alta | Contratar, cobertura, alta de operador |
| Acuse / código | Contrato, línea, SIM |
| Pendiente / aceptada / rechazada | Recibido, En curso, Instalada, En cobertura |
| Mesa | Kanban, NOC, backoffice, turno |
| Vocal | Comercial, operador, conserje |
| Vecina / vivienda / planta | Cliente, lead, abonado |
| RAMAL no instala hoy | Técnico mañana, visita programada |
«Kanban» y «F-ONB» son lenguaje de diseño, no de UI: Inés no lee «onboarding»; Pablo lee Pendiente / Aceptada.
«Alta» en UI es la petición de nodo. Si hay ambigüedad, preferir pedir nodo o código. [DECISIÓN]
3. Microcopy duro (literales irrenunciables)
El brief fija oposiciones. Aparecen donde el usuario puede confundirse.
| Oposición | Dónde | Copy |
|---|---|---|
| Alta ≠ cobertura del operador | Checklist · acuse · pie de portada | «Alta no es cobertura del operador. RAMAL no sustituye a Movistar.» |
| Nodo ≠ router de casa | Tile de nodo · checklist · acuse | «El nodo está en la azotea, no en el router de casa.» |
| RAMAL no instala hoy | Checklist · acuse · ficha · pie de mesa | «RAMAL no instala hoy. Aceptar no sube a la azotea.» |
| Checklist ≠ técnico | Alta · acuse | «Completar el alta no programa una visita.» |
[DECISIÓN] Los cuatro textos son obligatorios en UI. No se diluyen a un tooltip.
Pie de portada puede condensar dos en dos líneas; acuse muestra los cuatro.
4. Microcopy por superficie
| Superficie | Copy |
|---|---|
| Eslogan | La red del bloque, no el operador. |
| Línea de lugar | Vallecas · Palomeras |
| Portada titular | RAMAL |
| Tile dato 1 | Mástil / Patio / Acera |
| Tile dato 2 | Palomeras / Sierra Gádor / Pablo Neruda |
| Tile dato 3 | 18 viviendas al alcance / 24 viviendas / 2 bloques |
| Alta volver | ← Nodos |
| Alta titular | Pedir nodo en el bloque |
| Check 1 | Vivo en el bloque. Esta es mi vivienda. |
| Check 2 | Sé mi planta y portal. |
| Check 3 | El nodo está en la azotea, no en el router de casa. |
| Check 4 | Alta no es cobertura. RAMAL no instala hoy. El checklist no es un técnico. |
| Alta submit | Enviar alta |
| Login titular | Entrar a la mesa |
| Login ayuda | Solo vocal. La vecina pide alta en la portada, sin cuenta. |
| Login submit | Entrar |
| Login error | Email o contraseña no valen. |
| Empty portada | Aún no hay nodos. |
| Empty mesa | No hay altas. |
| Success titular | Alta enviada. |
| Success código | RM-2206 |
| Success cuerpo | Alta no es cobertura del operador. RAMAL no instala hoy. |
| Success nodo | Nodo ≠ router de casa. Checklist ≠ técnico. |
| Success CTA | Volver a los nodos |
| Error | La red no responde. |
| Error retry | Reintentar |
| Error 409 | Esa alta ya no está pendiente. Aceptar no sube a la azotea. |
| Loading portada | Cargando |
| Loading mesa | Cargando |
| 404 nodo | Ese nodo no está. |
| Mesa titular | Mesa |
| Ficha vacía | Elige un alta |
| Aceptar | Aceptar |
| Rechazar | Rechazar |
| Salir | Salir |
| Link Vocal (portada) | Vocal |
5. Nodos y altas seed (tono)
Los nodos demo se escriben como infraestructura de bloque, no como ficha de operador ni como anuncio.
| Pieza | Tono |
|---|---|
| Azotea Palomeras | Mástil. Teja. Sin rack. |
| Bloque Sierra Gádor | Patio. Cuarto de contadores. |
| Arqueta Pablo Neruda | Acera. No armario de marca. |
| RM-2201 Inés Soto | Nombre de vecina; no abonada. |
| Marisa Ortiz (POST) | Vecina de smoke; dos palabras, sin biografía. |
No inventar declaraciones de vecinas reales. Si hay una voz, es de persona de diseño etiquetada.
En ACEPTADA no se reescribe a «ya estáis en cobertura». El cierre es la decisión. [DECISIÓN]
6. Nombres propios del seed
| Nombre | Uso |
|---|---|
| Inés Soto | VECINA; sin cuenta; 4º B, RM-2201 |
| Pablo Ruiz | VOCAL; pablo@ramal.vallecas |
| Nerea Gil | TÉCNICA; sin cuenta; docs |
| Luis Herrero, Carmen Vidal, Rosa Méndez, Toni Casas | Vecinas catálogo |
| Marisa Ortiz | POST de smoke |
| RAMAL · Vallecas | Asociación demo; Palomeras / Sierra Gádor / Pablo Neruda |
No inventar CIF de asociación, teléfono de operador, ni cobertura real como si fuera el producto. [SUPUESTO de catálogo demo]
Vallecas / Palomeras / Sierra Gádor son topónimos reconocibles, no un inventario oficial de una red 2026.
7. Tono por estado
| Status | Frase UI | Frase que Pablo podría decir (fuera de app) |
|---|---|---|
| PENDIENTE | Pendiente | «Está en mesa. No es cobertura.» |
| ACEPTADA | Aceptada | «La acepté. No subo a la azotea hoy.» |
| RECHAZADA | Rechazada | «No entra. No es un parte.» |
La app no envía esos mensajes a la vecina en v1. El copy solo prepara el habla. No hay email de «ya tenéis fibra».
8. Capitalización y números
- Wordmark: RAMAL en versales Newsreader.
- Código:
RM-2201(RM versales, guion, 4 dígitos). - Nombres: Inés Soto, Pablo Ruiz, Nerea Gil.
- Nodos: Azotea Palomeras (mayúscula de kind + lugar).
- Vivienda:
4º Ben UI. - Fecha:
22 agosi hay línea larga. - No mezclar
rm-2201en minúsculas en UI. API puede normalizar a versales.
9. Idioma
es-ES. Fácil lectura. EN no en v1.
No mezclar «onboarding» ni «coverage» en UI. En API sí: nodes, neighborName, status.
«Vecina» en UI cuando hablamos de Inés; «el vocal» para Pablo; «la técnica» solo en docs. [DECISIÓN]
10. Accesibilidad de copy
- No transmitir estado solo con color (el mark no «significa» pendiente él solo).
- Filas llevan texto: Pendiente / Aceptada / Rechazada.
- Aviso de no-cobertura lleva palabras, no solo el seal.
- Alt de fotos: nodo + lugar, no «imagen1».
- hero: «Azotea de un bloque en Vallecas; mástil de red de comunidad, no un operador.»
- azotea: «Mástil en azotea de Palomeras; nodo de bloque.»
- bloque: «Fachada y patio del bloque Sierra Gádor.»
- arqueta: «Arqueta de acera en Pablo Neruda.»
11. Piezas que no se escriben
| No | Motivo |
|---|---|
| «Tu cobertura ha sido activada» | Alta ≠ cobertura |
| «Ya está instalado» | RAMAL no instala hoy |
| «Contratar ahora» / «Mbps» | S4 · no operador |
| «Si es urgente, abre aquí» (como 112) | RAMAL no es 112 |
| Recibido / En curso / Cerrado | No es kanban |
| Ranking de velocidad del bloque | Speedtest, otro producto |
| Lorem / «lorem ipsum nodo» | Prohibido en el case |
| Declaraciones inventadas de una asociación real | Verdad de catálogo |
10-accessibility.md
10 — Accesibilidad — RAMAL
1. Alcance
WCAG 2.2 AA orientada al slice: portada, checklist, acuse, login, mesa split. No es una auditoría certificada. [DECISIÓN]
2. Contraste
| Par | Relación esperada |
|---|---|
Ink #1A2A32 sobre hielo #E7F0F5 | > 7:1 |
Ink sobre card #FFFFFF | > 10:1 |
Card sobre mark #3D7A96 | Texto de CTA ≥ 4.5:1 |
Mute #5C6B73 sobre hielo | Meta ≥ 4.5:1 |
Seal #B42318 sobre card | Error ≥ 4.5:1 |
Texto sobre foto: gradient from-ink/80 bajo el titular. No titular suelto sobre teja clara.
3. Teclado y foco
- Orden: wordmark → Vocal → tiles de nodo → form → mesa lista → ficha.
- Foco visible: ring mark 2px.
- Radio de nodo y checks operables con teclado.
- Aceptar / Rechazar no se activan al pasar el foco.
4. Targets
≥44×44 en CTA, checks, filas de mesa, link Vocal. Checks 20px visual + label amplia.
5. Nombre accesible
| Control | Nombre |
|---|---|
| Tile nodo | Nombre del nodo (h2) |
| Checks | Texto del ítem |
| Enviar alta | Enviar alta |
| Aceptar / Rechazar | Aceptar / Rechazar |
| Salir | Salir |
Fotos: alt de doc 09. Decorative? No: las tres son contenido.
6. Estados no solo por color
Pendiente / Aceptada / Rechazada en texto. Completo del checklist: botón disabled + copy. Error: texto seal + mensaje.
7. Motion
prefers-reduced-motion: sin pulse. Skeletons estáticos.
8. Mobile 390
Portada: foto + 3 nodos apilados. No carousel.
Alta: una columna.
Mesa: lista o ficha (master-detail). Back a lista = /mesa.
9. Autocomplete
Login: username / current-password. Nombre: name. Vivienda: sin autocomplete de dirección de operador.
10. Idioma
html lang="es". Números y códigos leídos como texto (RM-2201).
11-privacy-security.md
11 — Privacidad y seguridad — RAMAL
1. Superficie pública vs cerrada
| Recurso | Auth | PII |
|---|---|---|
| GET nodes | No | No (infra de bloque) |
| POST altas | No | Nombre + vivienda de quien envía |
GET /ok/:code | No (solo código en URL) | No hidrata ficha ajena desde API |
| GET/PATCH mesa | JWT VOCAL | Lista de vecinas |
[DECISIÓN] El acuse no hace GET público por código. Evita enumeración de Inés Soto.
2. Datos de vecina
neighbor_name + dwelling + node_id. Sin email, sin DNI, sin teléfono en v1.
La vecina no tiene fila en users.
3. Auth
- JWT HS256. Claims:
subuserId,sidsession. - Password bcrypt 12. Demo
password123solo en seed local. - Guard: Bearer + sesión no revocada.
- Role VOCAL para mesa. 401 sin token, 403 si no VOCAL.
- Rate limit en login.
4. Secretos
DATABASE_URL y JWT_SECRET en apps/api/.env (gitignore). Nunca en Angular.
5. CORS
Allowlist localhost:4200. Previews Vercel opcionales.
6. Amenazas del slice
| Amenaza | Mitigación |
|---|---|
Enumerar /ok/RM-2201 y ver a Inés | Ok no GET; solo state de navegación |
| POST spam de altas | Volumen demo; rate Could L+1 |
| Reabrir alta aceptada | 409 |
| Token en localStorage | XSS = compromiso; slice L3 acepta el tradeoff de SPA [DECISIÓN] |
7. RGPD (modelo, no dictamen)
Base: interés de la asociación en gestionar altas. Minimización: nombre + vivienda. Sin cesión a operador. Derecho de supresión Could L+1 (Pablo no borra en v1).
Nerea no tiene acceso. No hay logs de «quién subió a la azotea».
8. Lo que no hacemos
No OAuth. No cookies de marketing. No analytics de terceros en v1. No cifrado de dwelling más allá de TLS.
12-analytics.md
12 — Analítica — RAMAL
1. North star
% de altas que Pablo mueve (ACEPTADA o RECHAZADA) en 7 días.
Oficio de junta. No pageviews. No «engagement con el mástil». [DECISIÓN]
2. Métricas de producto (modelo)
| Métrica | Definición | Hipótesis |
|---|---|---|
| Activación vecina | POST /api/altas 201 / visitas a / | H1 |
| Compleción checklist | Submit / inicios de /alta | H2 |
| Tiempo a código | < 90 s en demo | Diseño |
| Tiempo a decisión | PATCH desde GET mesa | H3 |
| Integridad auth | 401 en GET mesa anónimo | Seguridad |
| Expectativa | Tickets «¿cuándo instaláis?» (fuera de app) | H2 |
3. Eventos (si se instrumentara)
No hay tracker en v1. Contrato de nombres por si L+1:
| Evento | Props |
|---|---|
portada_view | nodes_count |
alta_submit | node_slug, code |
mesa_open | code |
alta_decide | code, status |
Prohibido: purchase, coverage_unlocked, speedtest_run.
4. Lo que no medimos
Mbps. Tiempo en azotea. Aperturas de WhatsApp (no hay dato). NPS de operadora.
5. Salud
GET /api/health → { ok: true, service: "ramal-api" }. Error rate 4xx/5xx en nodes/altas/mesa.
13-qa-test-plan.md
13 — Plan de QA — RAMAL
1. Smoke API (pnpm test:api)
- GET
/api/health→ramal-api - GET
/api/nodes→ 3 slugsazotea,bloque,arqueta+ lead + 3 datos - GET
/api/nodes/azotea→ kind azotea - POST
/api/altascompleto →RM-PENDIENTE - POST incompleto → 400
- GET
/api/mesasin JWT → 401 - POST
/api/auth/loginPablo → VOCAL - GET
/api/mesa→ ≥5 e incluye RM-2201…2205 - GET
/api/mesa/RM-2201→ Inés Soto PENDIENTE - PATCH ACEPTADA → 200; segundo PATCH → 409
2. Smoke UI
| # | Pasos | Esperado |
|---|---|---|
| 1 | Abrir / | Foto a sangre, 3 nodos, eslogan |
| 2 | Tap Azotea | /alta?nodo=azotea |
| 3 | Enviar sin checks | CTA disabled |
| 4 | Completar + Marisa 5º C | /ok/RM-XXXX + 4 oposiciones |
| 5 | /login Pablo | /mesa split |
| 6 | Tap RM-2201 | Ficha Inés · Aceptar |
| 7 | Aceptar | Status Aceptada; botones fuera |
| 8 | Logout | Portada; /mesa pide login |
| 9 | / 390 | 3 nodos apilados, no carousel |
| 10 | API down | Error + Reintentar |
3. Regresión de unicidad
- No copy TEMPLE (hora, concierto, conserje, turno, TM-).
- No copy PREGÓN (chat, kanban, 112, parte).
- No Movistar, no Mbps, no speedtest.
- Health no dice
temple-api. - Puerto 3022.
4. Accesibilidad rápida
Tab por tiles y checks. Focus ring. Alt de 4 fotos. Status en texto.
5. Datos
Seed idempotente por email y code. Un POST de smoke no borra RM-2201.
Playwright futuro: esperar Inés Soto, no Lola ni Los Yesos.
6. Criterio de salida
Smoke API verde + guión UI completo + 0 leftover de dominio anterior en UI.
14-dev-handoff.md
14 — Handoff de desarrollo — RAMAL
1. DoD del slice
- Health
{ ok: true, service: "ramal-api" }en :3022 - 3 nodos seed con foto, lead, 3 datos
- POST alta pública → RM-XXXX PENDIENTE
- JWT VOCAL
pablo@ramal.vallecas/password123 - Mesa split lista | ficha · PATCH ACEPTADA / RECHAZADA
- Empty / error / loading en portada y mesa
- Copy ×4 visible
- Rutas
//alta/ok/:code/login/mesa/mesa/:code - Paper 12 UX + 12 UI mapeados
- Docs 01–20 + README + executive
2. Contratos
Ver docs 05 y 20. JSON camelCase. SQL snake_case.
POST /api/altas:
{ nodeSlug, neighborName, dwelling, vivienda, planta, azotea, noInstala }
Los cuatro booleanos deben ser true.
PATCH /api/mesa/:code:
{ status: "ACEPTADA" | "RECHAZADA" }
3. Mapas de pantalla
| Paper | Ruta | Componente |
|---|---|---|
| UI-01 Portada | / | HomePage |
| UI-03 Alta | /alta | AltaPage |
| UI-05 Success | /ok/:code | OkPage |
| UI-06 Login | /login | LoginPage |
| UI-07 Mesa | /mesa | MesaPage |
| UI-08 Ficha | /mesa/:code | MesaPage |
4. Tokens en código
tailwind.config.js + styles.scss. Fuentes Google: Newsreader + Figtree.
5. Assets
Copiar 2026-08-22-ramal/assets/nodos/*.jpg y hero.jpg a apps/web/public/assets/.
6. No hacer
- No reintroducir
rooms/bookingscomo dominio vivo. - No documentar leftover TEMPLE como estado actual.
- No añadir signup de vecina, speedtest, chat, kanban, GIS, pago.
7. Arranque
cd /Users/cristian/orca/ramal-app
# apps/api/.env → DATABASE_URL Neon gentle-bonus-75761237, JWT_SECRET, PORT=3022
pnpm --filter @repo/api start:dev
pnpm --filter @repo/web start 15-roadmap.md
15 — Roadmap — RAMAL
Aquí solo viven ideas de L+1 / L4 y pulido posterior.
El slice L3 del día está cerrado: portada, checklist, acuse, JWT VOCAL, mesa split.
1. Hecho (L3)
- Portada H-FULL · 3 nodos · lead + 3 datos
- Checklist F-ONB · 4 ítems · una pantalla
- Acuse
/ok/:code - Mesa S-SPLIT · aceptar / rechazar
- Empty / error / loading · portada 390
- Neon
gentle-bonus-75761237· API :3022 ·ramal-api
2. L+1 (mismo producto, más oficio)
| Item | Nota |
|---|---|
| Filtro de mesa por nodo | 5 filas no lo necesitan; 40 sí |
| Comentario de rechazo | Una línea, no chat |
| Recibo por email a la vecina | Sigue sin cuenta |
| PWA install | Salón; no es Must |
3. L4 (requiere brief nuevo de diversidad)
- Rol TÉCNICA (Nerea) + cuadrante de azotea
- Radio planning / mapa GIS
- Pago / cuota de asociación
- Multi-vocal / multi-bloque
- Speedtest (otro producto; no es RAMAL)
- Chat / kanban (fue PREGÓN)
4. Fuera para siempre en este nombre
Movistar. Cobertura como promesa. Router de casa como nodo. Instalación el día del alta.
5. Criterio para abrir L4
Solo si un día futuro elige telecom L4 y la diversidad ALS-2 lo permite. No parche silencioso.
16-interaction-specs.md
16 — Especificación de interacción — RAMAL
1. Convenciones
| Token | Valor |
|---|---|
| Duración corta | 120–160ms |
| Easing | ease-out |
| Reduced motion | 0ms |
| Target | ≥44×44 (checks + label) |
| Pending | control disabled + aria-busy |
Loading (UI-11) y empty (UI-09) / error (UI-10) viven con los gestos. [DECISIÓN]
2. Portada (UI-01) + mobile (UI-02) · H-FULL
| Gesto | Resultado |
|---|---|
| Primera carga | Loading: 3 tiles card; aria-busy |
| 200 con 3 | Foto a sangre + 3 nodos. Lead + 3 datos |
| 200 con 0 | UI-09 empty |
| Error 5xx / red | UI-10 + Reintentar (repite GET nodes) |
| Tap tile | /alta?nodo=:slug |
| Tap Vocal (ghost) | /login o /mesa si hay token |
| Hover tile desktop | Ring 2px mark. Sin lift |
AC: nombre, lead y 3 datos visibles sin hover. 3 nodos en el recorte 16/10 (AP-21). Cero botones Cobertura / Mbps / Crear cuenta.
El aviso «RAMAL no instala hoy» es texto, no un CTA de instalación. [DECISIÓN]
390: mismas 3 nodos apilados. No carousel. [DECISIÓN]
3. Alta checklist (UI-03) + mobile (UI-04) · F-ONB
| Gesto | Resultado |
|---|---|
| Primera carga | Loading: form skeleton |
| 200 | 3 radios de nodo + nombre + vivienda + 4 checks |
Query ?nodo= | Preselecciona slug |
| Check incompleto | CTA disabled |
| Submit completo | POST · pending en botón |
| 201 | /ok/:code con state |
| 400 | Copy checklist; no navega |
| Error red | seal + mensaje |
| Tap ← Nodos | / |
AC: una pantalla. Cero stepper. Los 4 checks son el job, no un modal.
4. Acuse (UI-05)
| Gesto | Resultado |
|---|---|
| Con código | Newsreader RM-XXXX + bloque mark + 4 oposiciones |
| Sin código | «No hay ese código» + Pedir alta |
| CTA | / nodos |
No GET público por código. [DECISIÓN] doc 11.
5. Login (UI-06)
| Gesto | Resultado |
|---|---|
| Submit | POST login · pending |
| 200 | /mesa o next seguro |
| 401 | «Email o contraseña no valen.» |
| Ya hay token | Redirect mesa |
6. Mesa split (UI-07) + ficha (UI-08)
| Gesto | Resultado |
|---|---|
| GET mesa loading | 5 skeletons + ficha |
| 200 con altas | Lista izq. PENDIENTE primero |
| 200 [] | UI-09 |
| Error | UI-10 |
| Tap fila | /mesa/:code · ring mark |
| Sin :code | «Elige un alta» |
| Ficha loading | Skeleton derecho |
| 404 | «Esa alta no está» |
| Aceptar | PATCH ACEPTADA · botones fuera |
| Rechazar | PATCH RECHAZADA |
| 409 | seal · ya decidida |
| Salir | logout → / |
Desktop: dos columnas. 390: ficha a pantalla; volver a lista = /mesa. [DECISIÓN]
7. Loading / empty / error (UI-09…11)
Skeletons pulse. Empty usa titular display, no un illustration pack. Error no usa copy de 112.
17-prototype-map.md
17 — Mapa de prototipo — RAMAL
Paper file: 01M0M4FJGXPG315PDNT7PCAY3J
https://app.paper.design/file/01M0M4FJGXPG315PDNT7PCAY3J
UX process → docs
| Paper | Doc |
|---|---|
| UX-00 Cover | README + 01 |
| UX-01 Stakeholders | 02 §3 |
| UX-02 Personas | 02 §4 |
| UX-03 JTBD | 02 §5 |
| UX-04 Stories | 02 §6 |
| UX-05 Journey | 02 §7 · 04 |
| UX-06 Blueprint | 02 · 06 |
| UX-07 Site map | 03 |
| UX-08 Flujos | 04 |
| UX-09 Datos+permisos | 05 · 11 |
| UX-10 Métricas | 12 |
| UX-11 Research | 02 |
UI producto → app
| Paper | Ruta | Componente |
|---|---|---|
| UI-00 Tokens | — | tailwind.config.js + styles.scss |
| UI-01 Portada | / | HomePage |
| UI-02 Portada mobile | / 390 | HomePage |
| UI-03 Alta | /alta | AltaPage |
| UI-04 Alta mobile | /alta 390 | AltaPage |
| UI-05 Success | /ok/:code | OkPage |
| UI-06 Login | /login | LoginPage |
| UI-07 Mesa split | /mesa | MesaPage |
| UI-08 Ficha | /mesa/:code | MesaPage |
| UI-09 Empty | mismas rutas | empty branches |
| UI-10 Error | mismas rutas | error branches |
| UI-11 Loading | mismas rutas | skeletons |
Guión de prototipo
- Cover → Portada (3 nodos).
- Alta checklist Inés.
- Acuse RM-XXXX.
- Login Pablo.
- Mesa · RM-2201 · Aceptar.
- Empty / error / loading.
No hay artboard de speedtest, chat, kiosk de horas ni kanban.
18-completeness-audit.md
18 — Auditoría de cierre — RAMAL
1. Alcance L3
| Pieza | Estado |
|---|---|
| Docs 01–20 + README + executive | OK (este set) |
00-day-brief / 00-paper-reference | Intactos (no tocados) |
| Paper 12 UX + 12 UI | Inventario en 00 |
App path /Users/cristian/orca/ramal-app | OK |
Neon gentle-bonus-75761237 | OK |
API :3022 · health ramal-api | Contrato |
| Código nodes + altas + JWT VOCAL | Contrato paralelo |
| Cierre sin deuda del L3 | OK · roadmap solo L+1 / L4 |
2. Terna y diversidad
| Check | Evidencia |
|---|---|
| R-CL · S-SPLIT · H-FULL · F-ONB | Brief + 01 + 07 |
| 0 códigos = TEMPLE | TEMPLE era R-HY · S-MIN · H-KIOSK · F-BOOK |
| ≥2 vs PREGÓN / MATIZ | Split/Full/ONB vs chat/kanban y bento/edit |
| Ventana 4: R-CL | R-HY + R-IN + R-HY + R-CL |
Ground G-ICE #E7F0F5 | 00 + 07 + Tailwind |
3. Copy irrenunciable
Presente en alta, acuse, ficha, pie de portada. Ver doc 09.
4. Anti-patrones vigilados
| AP | Mitigación |
|---|---|
| AP-12 hero 2-col | H-FULL a sangre |
| AP-21 card hueca | Lead + 3 datos |
| Leftover como estado | Contrato RAMAL documentado; no se anota TEMPLE como producto vivo |
| Entrevistas inventadas | 02 etiquetado |
5. Seed
3 nodos · 5 altas (3 PENDIENTE, 2 ACEPTADA) · Pablo VOCAL · Inés sin cuenta.
6. Huecos conscientes (fuera)
GIS, pago, login Nerea, email transaccional, instrumentación analytics.
7. Veredicto
Slice L3 cerrado a nivel de case. El código se escribe en paralelo con el mismo contrato. Si el repo diverge, gana docs 05 + 20 tras alinear ambos.
19-backlog-completo.md
19 — Backlog — RAMAL
1. Done (Must L3)
| ID | Item |
|---|---|
| D01 | Portada 3 nodos H-FULL |
| D02 | Checklist 4 ítems F-ONB |
| D03 | Acuse /ok/:code |
| D04 | Login JWT VOCAL |
| D05 | Mesa split lista | ficha |
| D06 | PATCH ACEPTADA / RECHAZADA |
| D07 | Empty / error / loading |
| D08 | Portada 390 |
| D09 | Seed 3 nodos + 5 altas |
| D10 | Health ramal-api :3022 |
| D11 | Copy ×4 |
| D12 | Docs 01–20 |
2. Could L+1 (no bloquea cierre)
| ID | Item |
|---|---|
| C01 | Filtro mesa por nodo |
| C02 | Nota de rechazo |
| C03 | Email de acuse |
| C04 | PWA |
| C05 | Rate limit POST altas |
3. Explícito L4 / otro brief
| ID | Item |
|---|---|
| E01 | Login Nerea / cuadrante |
| E02 | GIS / radio planning |
| E03 | Pago / cuota |
| E04 | Multi-asociación |
| E05 | Speedtest |
| E06 | Chat / kanban |
4. No-backlog (rechazado)
Hero 2-col. Signup vecina. leftover TEMPLE como feature. Movistar. «Instalar hoy».
5. Cómo usar este archivo
Done = el día. Could = L+1. E = otro cron. Nada de esta lista es deuda del L3.
20-implementation.md
20 — Implementación — RAMAL
1. Resumen ejecutivo técnico
| Campo | Valor |
|---|---|
| App path | /Users/cristian/orca/ramal-app |
| API | NestJS · puerto 3022 · prefijo /api |
| Web | Angular standalone · puerto 4200 |
| Package | apps independientes (npm --prefix o pnpm --filter) |
| DB | Neon PostgreSQL · project gentle-bonus-75761237 |
| Tablas | users, nodes, altas, sessions |
| Auth | JWT Bearer · role VOCAL (mesa); portada y POST altas públicos |
| Dominio | User, Node, Alta |
| Fecha | 2026-08-22 |
| GitHub | https://github.com/Criscode2022/ramal-app |
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.
El código se escribe en paralelo a esta suite. Contrato: nodes + altas + JWT VOCAL. Puerto 3022. Health ramal-api.
[COMPROBADO] Path, puerto, Neon, Paper y GitHub salen del encargo.
2. Cómo arrancar
cd /Users/cristian/orca/ramal-app
# instalar dependencias por app (no npm workspaces)
# apps/api/.env
# DATABASE_URL= # Neon gentle-bonus-75761237
# JWT_SECRET=
# PORT=3022
# schema + seed
# api → http://localhost:3022
# web → http://localhost:4200
Credenciales
| Rol | Password | |
|---|---|---|
| VOCAL | pablo@ramal.vallecas | password123 |
Vecina / técnica: sin cuenta. Inés pide alta en /alta. Nerea no entra.
3. Schema
Al boot o vía migrate:
CREATE TABLEdeusers,sessions,nodes,altas(doc 05).- Índices:
nodes(slug),altas(code),altas(status),users(email). - Seed idempotente:
- upsert VOCAL
pablo@ramal.vallecas(bcrypt depassword123) - upsert
nodesporslug(azotea,bloque,arqueta) - upsert
altasporcode(RM-2201…RM-2205)
- upsert VOCAL
- Dominio: solo nodes + altas + users. [DECISIÓN]
No borrar altas nuevas de Inés/Marisa en cada boot si ya existen filas distintas al seed (upsert por code no pisa status de altas ya decididas).
Generador de código: SELECT max(code) numérico + 1, formateado RM- + 4 dígitos. Seed máximo 2205 → siguiente POST = RM-2206. Si se agota 9999, L+1 amplia a 5 dígitos; no es Must.
Tablas (recordatorio)
| Tabla | Contenido |
|---|---|
users | VOCAL Pablo |
nodes | 3 nodos |
altas | 5 altas 3 PENDIENTE / 2 ACEPTADA |
sessions | sid + expires_at |
4. Módulos API a implementar
Auth
POST /api/auth/login- Valida email/password; compara bcrypt; emite JWT con
sub, email, role. - Guard JWT +
role === VOCALprotege mesa. - Públicos: health, nodes, nodes/:slug, POST altas, login.
Oficio
| Método | Ruta | Auth | Notas |
|---|---|---|---|
| GET | /api/health | No | { ok: true, service: "ramal-api" } |
| GET | /api/nodes | No | sort_order asc + lead + data1..3 |
| GET | /api/nodes/:slug | No | ficha de nodo |
| POST | /api/altas | No | crea PENDIENTE + code; 400 si checklist incompleto |
| GET | /api/mesa | JWT | lista PENDIENTE primero |
| GET | /api/mesa/:code | JWT | ficha |
| PATCH | /api/mesa/:code | JWT | { status } · 409 si no PENDIENTE |
Health (referencia de implementación)
HealthService.name = 'ramal-api'
main.ts PORT default 3022
Alta (referencia)
require vivienda && planta && azotea && noInstala
node = nodes.bySlug(nodeSlug)
code = next RM-
INSERT PENDIENTE
return Alta camelCase con nodeName
JSON camelCase: neighborName, nodeSlug, nodeName, nodeKind, sortOrder, createdAt, decidedAt, data1.
SQL: neighbor_name, node_id, sort_order, created_at, decided_at, data_1.
POST no cobra, no instala, no llama a Nerea, no mueve kanban.
Módulos de dominio: nodes/, altas/, mesa/, auth/, health.
5. Frontend a implementar
| Página | Ruta | Responsabilidad |
|---|---|---|
HomePage | / | H-FULL · GET nodes · 3 tiles |
AltaPage | /alta | Checklist · POST altas |
OkPage | /ok/:code | Acuse · state de navegación |
LoginPage | /login | JWT VOCAL |
MesaPage | /mesa · /mesa/:code | Split · PATCH |
Estados: loading / ready / empty / error en portada y mesa.
Token key: ramal.accessToken.
Proxy: /api → :3022.
Tokens Tailwind: ground / ink / card / mark / mute / seal. Fuentes Newsreader + Figtree.
6. Seed visible en UI
| Código | Vecina | Nodo | Status |
|---|---|---|---|
| RM-2201 | Inés Soto | Azotea Palomeras | PENDIENTE |
| RM-2202 | Luis Herrero | Bloque Sierra Gádor | PENDIENTE |
| RM-2203 | Carmen Vidal | Arqueta Pablo Neruda | PENDIENTE |
| RM-2204 | Rosa Méndez | Azotea Palomeras | ACEPTADA |
| RM-2205 | Toni Casas | Bloque Sierra Gádor | ACEPTADA |
Playwright: esperar Inés Soto y Azotea Palomeras. No esperar Lola, Los Yesos, turno.
7. Smoke
apps/api/scripts/test-api.mjs · pnpm test:api contra :3022.
Cubre health, 3 nodos, POST alta, 400 checklist, 401 mesa, login VOCAL, lista ≥5, ficha RM-2201, PATCH + 409.
8. DoD técnico
Ver doc 14. Este archivo no lista leftover de otro día como estado del producto. El contrato vivo es RAMAL.