00-day-brief.md
00 — Day brief · 2026-08-19 · MATIZ
Decisiones ALS-2 BRIEF
| Campo | Valor |
|---|---|
| Fecha | 2026-08-19 |
| Nombre | MATIZ |
| Complejidad | Nivel 2 (no L1 consecutivo tras ZAGUÁN) |
| Sector | Belleza / oficio de colorista (salón de barrio, no reserva de cita) |
| Tipo | PWA cuaderno de fórmulas + fichas + editor de fórmula + JWT COLORISTA |
| Plataforma | PWA mobile-first (bottom tabs) · editor también en desktop |
| Registro | R-HY — chrome de herramienta (tabs) + receta editorial (capilla) |
| Shell | S-BOTTOM — Hoy · Fichas · Cuenta |
| Home | H-BENTO — sillas de hoy + última fórmula + swatches |
| Flujo | F-EDIT — el núcleo es escribir y guardar una fórmula, no una cola |
| Por qué no ZAGUÁN | No S-WIZ / H-WIZARD / F-CMP; no inmobiliaria; no wizard |
| Por qué no MARGEN | No S-TOP / H-GALLERY / F-STORY; no editorial; no lookbook |
| Por qué no CESTA | No S-RAIL / H-TABLE / F-LEAD; no ONG |
| Por qué no CANTERA | S-BOTTOM se reutiliza a propósito (cooldown 5: CANTERA es N−8); F-EDIT ≠ F-ONB; belleza ≠ parte de obra |
Terna unicidad
R-HY · S-BOTTOM · H-BENTO · F-EDIT
0 códigos iguales a N−1 (ZAGUÁN: R-IN · S-WIZ · H-WIZARD · F-CMP).
≥2 distintos vs N−2 (MARGEN: R-CL · S-TOP · H-GALLERY · F-STORY) y N−3 (CESTA: R-CL · S-RAIL · H-TABLE · F-LEAD).
Ventana 4: R-IN + R-CL + R-CL + R-HY (hay clásico e innovador).
S-BOTTOM no está en los últimos 5 (S-WIZ, S-TOP, S-RAIL, S-DOCK, S-SPLIT). H-BENTO y F-EDIT tampoco.
Ground (CRON §5.6)
- Familia: G-CHAPEL
- Hex:
#E6E4ED - Por qué: yeso de capilla, no hormigón (ZAGUÁN), no blanco de kiosco (MARGEN), no musgo (CESTA), no cromo (RONDA), no agua (DERIVA). El acento es amatista de vidriera, no óxido ni cadmio.
Mood visual (Paper)
- Candidatos: candlelit (primer instinto salón), chapel, bookish, nocturnal, mineral
- Elegido: chapel — no el primer instinto candlelit (ámbar × oxblood de “spa de lujo”). Un salón de Usera no es un spa; es un oficio con luz de vidriera y pizarra.
- Paleta:
#E6E4EDyeso de capilla (ground)#2B2838pizarra del banco#6B4C9Aamatista de vidriera#F7F5FAlino de card#6E6A78peltre#C4A35Ahoja de oro (acento secundario, poco)
- Tipo: Libre Baskerville display · Nunito Sans UI (evita Instrument Serif / Sora / Cormorant / Public Sans / Spectral / Manrope / Zilla / Mulish / Lora / Karla / Newsreader / Outfit)
Alcance L2 must
- Login JWT COLORISTA (Nora Beltrán, Salón Amparo)
- Hoy — bento: 3 sillas de hoy + última fórmula + aviso de oxidante
- Fichas — lista de clientas con último matiz
- Ficha de clienta + historial de fórmulas
- Editor de fórmula (must-have inventivo): líneas de color (marca, tono, gramos) + oxidante 10/20/30 vol + tiempo + notas + “guardar ≠ aplicar”
- Success / empty / error / loading
- Cuenta (salón, rol, cerrar sesión)
- ≥12 UX + ≥10 UI
Microcopy duro
- Fórmula ≠ receta comercial. 6/7 + 20 vol no es “el bote de la caja”.
- Ficha ≠ cita. MATIZ no agenda. La silla de hoy es un asiento, no un slot Booksy.
- Guardado ≠ aplicado. Guardar la fórmula no significa que el color esté en el pelo.
- MATIZ no cobra. El ticket lo hace la caja; el producto no es un TPV.
Supuestos
- S1: Un salón de barrio tiene 1–2 coloristas y 40–80 fichas vivas; la demo tiene 1 COLORISTA + 5 clientas + 8 fórmulas.
- S2: La colorista escribe la fórmula en el móvil, junto al lavacabezas, con las manos aún húmedas: bottom tabs, targets grandes, sin wizard.
- S3: No hay reserva online ni pasarela. El “hoy” lo rellena Nora por la mañana (quién viene), no la clienta.
- S4: La dueña (Inma) no usa el producto el día 1; el JWT es solo COLORISTA.
Hipótesis
| ID | Señal |
|---|---|
| H1 | Un editor de gramos + oxidante reduce “WhatsApp + foto del tazón” |
| H2 | Separar ficha de cita evita que MATIZ se lea como Booksy |
| H3 | JWT solo en colorista basta; la clienta no necesita cuenta |
Anti-patrones evadidos
- AP-12: no hero 2-col + 3 cards + lista badges
- AP-16: ≥10 UI incluyendo cada estado y el editor completo
- AP-20: ground chapel, no beige por inercia
- AP-21: Hoy bento con 3 sillas reales en el fold (nombre, silla, último matiz)
00-paper-reference.md
Paper reference · MATIZ
| URL | https://app.paper.design/file/01M0CDJZPXVWCVYZC5Z5V9ZRQE |
| File ID | 01M0CDJZPXVWCVYZC5Z5V9ZRQE |
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 Login · UI-02 Hoy bento · UI-03 Fichas · UI-04 Ficha · UI-05 Editor móvil · UI-06 Editor desktop · UI-07 Success · UI-08 Empty · UI-09 Error · UI-10 Loading · UI-11 Cuenta
UX process (prefijo UX- · mínimo 12)
| ID | Nombre | Contenido |
|---|---|---|
| UX-00 | Cover | Portada MATIZ · eslogan «La fórmula, no la cita.» · terna R-HY · S-BOTTOM · H-BENTO · F-EDIT · L2 belleza oficio · mood chapel · 2026-08-19 |
| UX-01 | Stakeholders | COLORISTA (Nora), DUEÑA (Inma, no JWT día 1), clientas (Marisol, Lucía, Amina, Pilar), caja — no Booksy |
| UX-02 | Personas | Nora Beltrán 34 colorista · Inma Soto 52 dueña · Marisol Vega 47 clienta (no usa la app) |
| UX-03 | JTBD | Job «escribir los gramos antes de que se me olvide el tazón» + job «ver qué le hice hace 8 semanas» |
| UX-04 | Stories | Must: login, hoy, fichas, ficha, editor, success, empty/error/loading, cuenta |
| UX-05 | Journey | Abrir Hoy → silla de Marisol → última fórmula → nueva fórmula → guardar ≠ aplicar |
| UX-06 | Blueprint | Frontstage móvil junto al lavacabezas · backstage caja y cita de WhatsApp · Nest/Neon/JWT |
| UX-07 | Site map / IA | Auth-first PWA; tabs Hoy · Fichas · Cuenta; editor contextual |
| UX-08 | Flujos | F-EDIT: ficha → editor → POST fórmula; F-DESK: login → hoy |
| UX-09 | Datos + permisos | User COLORISTA · Client · Formula · Sitting; JWT en todo salvo login |
| UX-10 | Métricas | North star: % de sillas de hoy con fórmula guardada el mismo día |
| UX-11 | Research notes | Comprobado / supuesto / hipótesis / decisión — sin entrevistas inventadas |
UI producto (prefijo UI- · mínimo 10)
| ID | Nombre | Flujo |
|---|---|---|
| UI-00 | Tokens | DS chapel: yeso, pizarra, amatista, lino, peltre, oro; Libre Baskerville + Nunito Sans |
| UI-01 | Login | Acceso JWT COLORISTA. nora@matiz.salon / password123 |
| UI-02 | Hoy bento | H-BENTO: 3 sillas + última fórmula + aviso oxidante. Lead + 3 datos en fold |
| UI-03 | Fichas | Lista clientas con último matiz |
| UI-04 | Ficha | Historial + CTA nueva fórmula. Ficha ≠ cita |
| UI-05 | Editor móvil | F-EDIT: líneas de color + oxidante + tiempo + notas |
| UI-06 | Editor desktop | Misma receta en 1440; staff secundario |
| UI-07 | Success | Guardado ≠ aplicado |
| UI-08 | Empty | Sin sillas / sin fichas |
| UI-09 | Error | Red / validación |
| UI-10 | Loading | Skeletons bento |
| UI-11 | Cuenta | Salón Amparo, rol, salir |
Regla: el núcleo F-EDIT ocupa UI-05 + UI-06. Hoy es H-BENTO denso (AP-21).
1. Mapa de canvas (bandas)
| § | Banda | Propósito | Artboards |
|---|---|---|---|
| 1 | UX PROCESS | Modelo de oficio colorista | UX-00…UX-11 |
| 2 | DESIGN SYSTEM | Tokens chapel | UI-00 |
| 3 | PWA COLORISTA | Login, hoy, fichas, ficha, editor, estados | UI-01…05, 07–11 |
| 4 | DESKTOP | Editor 1440 | UI-06 |
Layout canvas: origen (0,0) · gaps ~100–120px · UX 3×4 · móviles en 2 filas · desktop editor debajo. D-P0-04 / D-P1-04.
2. Mapeo Paper → Angular
| Artboard | Ruta app | Componente |
|---|---|---|
| UI-00 Tokens | — | tokens Tailwind + fuentes |
| UI-01 Login | /login | LoginPage |
| UI-02 Hoy | / | HoyPage |
| UI-03 Fichas | /fichas | FichasPage |
| UI-04 Ficha | /fichas/:slug | FichaPage |
| UI-05 Editor | /fichas/:slug/formula | FormulaPage |
| UI-06 Editor desk | misma ruta desktop | FormulaPage |
| UI-07 Success | /ok/:id | OkPage |
| UI-08 Empty | Hoy/Fichas 0 | estados |
| UI-09 Error | toast / page | estados |
| UI-10 Loading | skeletons | estados |
| UI-11 Cuenta | /cuenta | CuentaPage |
01-project-definition.md
01 — Definición de proyecto — MATIZ
1. Identidad
| Campo | Valor |
|---|---|
| Nombre | MATIZ |
| Significado | Matiz es el matiz del color y el matiz del oficio: un tono, no una receta de caja. Ni el Booksy del barrio ni el TPV de la caja. Se entra a escribir gramos; se sale con una fórmula guardada. |
| Eslogan | «La fórmula, no la cita.» |
| Una frase | PWA auth-first: cuaderno de fórmulas + fichas + editor + JWT COLORISTA. |
| Sector | Belleza / oficio de colorista (salón de barrio, no reserva de cita) |
| Tipo | PWA L2 — Hoy bento + fichas + editor de fórmula + cuenta — Nivel 2, herramienta de oficio usable |
| Plataforma | PWA mobile-first (bottom tabs) · editor también en desktop |
| Mercado demo | España · Madrid · Usera · Salón Amparo, Calle Amparo 22 (un solo salón, no cadena) |
| Idioma | es-ES |
| Fecha caso | 2026-08-19 |
| Terna | R-HY · S-BOTTOM · H-BENTO · F-EDIT |
[COMPROBADO] Nombre, terna, nivel, sector, ground y paleta salen de docs/00-day-brief.md.
2. Problema
Principal (hipótesis de diseño)
[HIPÓTESIS] La colorista de un salón de barrio no necesita otro Booksy: necesita escribir los gramos antes de que se le olvide el tazón. Hoy eso vive en WhatsApp + foto del tazón + una libreta que se moja junto al lavacabezas. La ficha se lee como cita, el guardado se lee como «ya está en el pelo» y el producto se lee como TPV. [SUPUESTO] Un salón de barrio tiene 1–2 coloristas y 40–80 fichas vivas; no opera un PMS de cadena ni una pasarela.
Secundarios
| Problema | Quién lo sufre | Efecto |
|---|---|---|
| «WhatsApp + foto del tazón» | COLORISTA (Nora) | El matiz de hace 8 semanas se pierde; se improvisa el siguiente |
| Ficha se lee como cita | COLORISTA + DUEÑA (Inma) | Esperan un slot Booksy que el producto no tiene |
| Guardado se lee como aplicado | COLORISTA + clienta (fuera) | «Ya me lo has puesto» cuando solo se apuntó la fórmula |
| Receta comercial se lee como fórmula | COLORISTA | 6/7 + 20 vol no es «el bote de la caja» |
| La dueña pide caja y agenda el día 1 | Inma | El JWT se diluye; MATIZ se vuelve TPV + Booksy |
Supuestos (no investigación primaria propia)
- S1: Un salón de barrio tiene 1–2 coloristas y 40–80 fichas vivas; la demo tiene 1 COLORISTA + 5 clientas + 8 fórmulas. [SUPUESTO]
- S2: La colorista escribe la fórmula en el móvil, junto al lavacabezas, con las manos aún húmedas: bottom tabs, targets grandes, sin wizard. [SUPUESTO]
- S3: No hay reserva online ni pasarela. El «hoy» lo rellena Nora por la mañana (quién viene), no la clienta. [SUPUESTO]
- S4: La dueña (Inma) no usa el producto el día 1; el JWT es solo COLORISTA. [SUPUESTO]
Hipótesis de producto
| ID | Hipótesis | Señal de validación (futura) |
|---|---|---|
| H1 | Un editor de gramos + oxidante reduce «WhatsApp + foto del tazón» | % de sillas de hoy con fórmula guardada el mismo día |
| H2 | Separar ficha de cita evita que MATIZ se lea como Booksy | ↓ «pensé que ya tenía hora» / cero CTA de agenda |
| H3 | JWT solo en colorista basta; la clienta no necesita cuenta | 0 cuentas de clienta; ficha completa sin login de clienta |
No se afirman estadísticas de salones inventadas. Todo lo anterior es razonamiento de diseño etiquetado.
3. Propuesta de valor
| Para | Valor |
|---|---|
| COLORISTA (Nora Beltrán) | Ve las 3 sillas de hoy; abre la ficha; escribe marca, tono, gramos, oxidante y tiempo; guarda sin fingir que el color ya está en el pelo. |
| DUEÑA (Inma Soto) | El oficio queda escrito. No entra el día 1. No pide caja ni agenda aquí. |
| Clientas (Marisol, Lucía, Amina, Pilar, Diego) | No usan la app. Su matiz vive en la ficha que Nora consulta. |
| Salón Amparo | Cuaderno mínimo: sillas + fichas + fórmulas, sin Booksy ni TPV. |
No es MATIZ
| Excluido | Por qué |
|---|---|
| Reserva / slot / Booksy | Ficha ≠ cita (≠ F-BOOK) |
| TPV / cobro / ticket | MATIZ no cobra; la caja es de Inma, fuera |
| Receta comercial / «el bote de la caja» | Fórmula ≠ receta comercial |
| «Ya está aplicado» al guardar | Guardado ≠ aplicado |
| Lead / cuenta de clienta / «pide cita online» | S3 + H3: la clienta no entra |
| Semanario / lookbook de looks | Eso fue MARGEN (H-GALLERY / F-STORY) |
| Tabla de necesidades / compromiso de caja | Eso fue CESTA (H-TABLE / F-LEAD) |
| Parte de obra / wizard de oficio | Eso fue CANTERA (S-BOTTOM / F-ONB); F-EDIT ≠ F-ONB |
| Wizard de criterios + comparador de pisos | Eso es ZAGUÁN (N−1: S-WIZ / H-WIZARD / F-CMP) |
| Feed de «colores nuevos» | Terna H-BENTO, no H-FEED |
| Hero 2-col + 3 cards de «cómo coloreamos» | Anti-patrón AP-12; contradice H-BENTO |
| Pasarela, suscripción, marketplace de tintes | S3 / S4 |
4. Objetivos
Negocio / caso de estudio
- Demostrar vertical slice L2 belleza oficio con Client + Formula + Sitting + JWT COLORISTA.
- Portfolio coherente: Paper (12 UX + 12 UI) + docs + app runnable.
- Terna R-HY · S-BOTTOM · H-BENTO · F-EDIT frente a ZAGUÁN (wizard/comparador), MARGEN (galería/masthead) y CESTA (tabla/rail).
Usuario
| Rol | Objetivo medible en demo |
|---|---|
| COLORISTA | Abrir Hoy, tocar silla de Marisol, escribir una fórmula y guardarla en < 90 s |
| DUEÑA | No encontrar login propio; no encontrar caja ni agenda |
| Clienta | No existe superficie; no hay CTA «mi cita» |
No objetivos v1 (explícitos)
- Reserva, calendario, slot, confirmación de cita
- Cuenta de clienta, recordatorios, WhatsApp outbound
- TPV, ticket, comisión, pasarela
- Multi-colorista, turnos, comisiones por silla
- Recetario de marca (L’Oréal / Wella como catálogo comercial)
- Foto del resultado como UGC de clienta
- App nativa ni notificaciones push
- Rol DUEÑA autenticado (S4)
5. Roles y permisos (resumen)
| Acción | Anónimo | COLORISTA | DUEÑA / clienta |
|---|---|---|---|
| Ver login | Sí | Sí | No hay cuenta |
| Ver Hoy / fichas / editor | No (401 / redirect) | Sí | No |
GET /me, /hoy, /clients | 401 | Sí | — |
| POST fórmula | 401 | Sí | — |
| PATCH fórmula | 401 | Sí | — |
| Agendar silla | No (no existe UI pública) | Seed / fuera de app (S3) | No |
| Cobrar | No | No | Caja física (fuera) |
| Login JWT | Emite si vale | Sí | No hay fila |
[DECISIÓN] Un solo rol autenticado: COLORISTA. La dueña no es un rol de sistema el día 1. Las clientas no son usuarias: son filas de clients.
6. Métricas (modelo, no instrumentadas en v1 salvo base)
| Tipo | Métrica | Definición |
|---|---|---|
| North Star | % de sillas de hoy con fórmula guardada el mismo día | Oficio escrito, no pageviews |
| Activación colorista | 1.ª fórmula SAVED | H1 |
| Integridad de expectativa | Reclamaciones «ya tenía cita» / «ya está aplicado» / «pásame el TPV» | H2 + microcopy |
| Integridad de auth | GET /hoy y /clients sin token → 401 | Auth-first |
| Salud | Error rate API formulas / hoy | 4xx/5xx |
7. Alcance funcional v1 (L2)
| Módulo | Incluido |
|---|---|
| Auth | POST /api/auth/login → JWT COLORISTA · GET /api/me |
| Hoy | GET /api/hoy · bento: 3 sillas + última fórmula + aviso oxidante |
| Fichas | GET /api/clients · lista con último matiz |
| Ficha | GET /api/clients/:slug · historial + CTA nueva fórmula |
| Editor | POST /api/clients/:slug/formulas · líneas + oxidante 10/20/30 + minutos + notas |
| Fórmula | GET / PATCH /api/formulas/:id |
| Success | /ok/:id · «guardado ≠ aplicado» |
| Cuenta | Salón, rol, cerrar sesión |
| Estados UI | Empty (0 sillas / 0 fichas), loading skeleton, error de red |
| Mobile | Bottom tabs · editor a ~390px · targets ≥44px |
| Seed | 1 COLORISTA, 5 clientas, 8 fórmulas, 3 sittings de hoy |
| Salud | GET /api/health |
8. Criterios de aceptación de producto
- Sin token,
/redirige a/login. No hay cara pública de lead. - Una COLORISTA puede iniciar sesión y ver Hoy con 3 sillas reales (nombre, silla, último matiz) en el fold.
- Una COLORISTA puede abrir una ficha, escribir una fórmula (líneas + oxidante + minutos) y guardarla.
- La UI dice explícitamente que fórmula no es receta comercial, que ficha no es cita, que guardado no es aplicado, que MATIZ no agenda y que MATIZ no cobra.
- Sin token, GET
/api/hoy,/api/clients, POST/PATCH de fórmulas responden 401. - Un POST de fórmula crea
status=SAVED. Guardar no marca la silla como teñida. /ok/:idmuestra «Guardado no es aplicado».- La home es el bento de Hoy: no hay hero 2-col + 3 cards como superficie principal.
- Fotos reales en las 5 fichas (assets del case).
- El editor muestra marca, tono, gramos por línea + chips 10 / 20 / 30 vol + minutos + notas.
- Bottom tabs Hoy · Fichas · Cuenta visibles en autenticado.
- Rutas solo las del doc 03. No existen
/cita,/agenda,/cobrar,/tienda.
9. Stack y artefactos
| Capa | Detalle |
|---|---|
| Frontend | Angular + Tailwind · puerto 4200 |
| Backend | NestJS · puerto 3019 |
| DB | Neon PostgreSQL · project mute-wave-74707210 |
| Auth | JWT (COLORISTA) |
| Diseño | Paper 01M0CDJZPXVWCVYZC5Z5V9ZRQE |
| Repo app | /Users/cristian/orca/matiz-app/ · GitHub Criscode2022/matiz-app |
[COMPROBADO] Puerto, Neon, Paper file ID, repo y path de app constan en el encargo.
10. Riesgos y mitigaciones
| Riesgo | Impacto | Mitigación v1 |
|---|---|---|
| Expectativa de cita confirmada | Reclamaciones «ya tenía hora» | Copy «Ficha ≠ cita»; sin botón Agendar |
| Expectativa de color ya puesto | «Ya me lo has aplicado» | Copy «Guardado ≠ aplicado» en editor y success |
| Expectativa de receta de caja | «Ponle el 6/7 de Wella» | Copy «Fórmula ≠ receta comercial» |
| El producto «agenda» | Expectativa F-BOOK | «MATIZ no agenda. El WhatsApp de Inma agenda.» |
| El producto «cobra» | Expectativa TPV | «MATIZ no cobra. El ticket lo hace la caja.» |
| JWT de dueña el día 1 | Diluye S4 | Un rol COLORISTA; Inma no tiene fila |
| Home percibida como landing de salón | Pérdida de craft / H-BENTO | Hoy es bento; no hero 2-col |
| Confundir con ZAGUÁN (wizard inmobiliario) | Expectativa de comparador | Auth-first; sillas, no fincas |
| Confundir con CANTERA (parte de obra) | Expectativa de «enviar el día» | F-EDIT, no F-ONB; fórmula, no parte |
Leftover listings / compare / mesa en repo | App enseña otro producto | Contrato de esta suite gana; I01 de build |
| PII de clienta expuesta en público | Ficha en la calle | Auth-first; GET clients 401 sin token |
| 0 sillas hoy | Abandono | Empty «Hoy no hay sillas. El hoy lo rellena Nora por la mañana.» |
11. Glosario
| Término | Definición en MATIZ |
|---|---|
| Fórmula | Líneas de color (marca, tono, gramos) + oxidante + minutos + notas; no es una receta comercial |
| Ficha | Registro de clienta (clients); no es una cita |
| Silla / sitting | Asiento de hoy (sittings); no es un slot Booksy |
| Guardar | Persistir fórmula SAVED; no significa que el color esté en el pelo |
| Aplicar | Gesto físico en el lavacabezas; fuera de la app en v1 |
| Oxidante | 10 / 20 / 30 vol; no es «el bote de la caja» |
| Línea | { brand, tone, grams } dentro de lines JSON |
| Matiz | Último tono memorable de la ficha (p. ej. «honey balayage») |
| Hoy | Superficie H-BENTO: sillas + última fórmula + aviso oxidante |
| Cuenta | Salón Amparo, rol COLORISTA, salir |
| COLORISTA | Usuario autenticado que opera el cuaderno |
| DUEÑA | Stakeholder; no es rol de sistema el día 1 |
| Clienta / cliente | Fila de clients; no usa la app |
| Capilla | Superficie de ground #E6E4ED, no el fondo de card |
| Lino | Superficie de card #F7F5FA |
| Pizarra | Texto #2B2838 |
| Amatista | Acento #6B4C9A |
| Bottom tabs | Shell S-BOTTOM: Hoy · Fichas · Cuenta |
12. Decisiones de diseño (cierre de brief)
| ID | Decisión | Alternativa descartada |
|---|---|---|
| D1 | Home = Hoy bento (H-BENTO) | Hero 2-col + 3 cards (AP-12) · galería (MARGEN) · wizard (ZAGUÁN) · tabla (CESTA) |
| D2 | Shell bottom tabs (S-BOTTOM) | Masthead · rail · dock flotante · wizard chrome · sidebar CRM |
| D3 | Registro híbrido chapel (R-HY) | Candlelit / spa ámbar (primer instinto) · rusted ZAGUÁN · gallery |
| D4 | Flujo F-EDIT (ficha → editor → guardar) | F-CMP, F-STORY, F-LEAD, F-ONB, F-BOOK |
| D5 | Mood chapel | Candlelit (vetado como primer instinto), rusted ZAGUÁN, moss CESTA |
| D6 | Un rol autenticado COLORISTA | Multi-rol dueña / clienta / caja / admin |
| D7 | Estados de fórmula solo SAVED | APPLIED / MIXED / BILLED (fingiría aplicación o cobro) |
| D8 | Editor de escritura | Wizard de 4 pasos, recetario de marca, cola de sillas |
| D9 | Clienta sin cuenta | Lead, «mi cita», recordatorio push |
| D10 | PATCH /api/formulas/:id | Subruta /apply como único contrato |
| D11 | Ground G-CHAPEL #E6E4ED | G-CONCRETE, G-GALLERY, G-MOSS, G-CHROME, G-WATER |
| D12 | Success de fórmula en /ok/:id | Toast único sin pantalla |
| D13 | Oxidante = 10 | 20 | 30 | Campo libre de volúmenes o «el de la caja» |
| D14 | Sittings de hoy = seed de mañana, no UI de agenda | Calendario de sillas (rompería H2) |
| D15 | Auth-first; cero cara pública | Landing de salón + «pide cita» |
13. Relación con el día anterior
| Case | Terna | Por qué MATIZ no lo copia |
|---|---|---|
| ZAGUÁN (N−1) | S-WIZ / H-WIZARD / F-CMP | No inmobiliaria, no wizard, no comparador, no mesa pública |
| MARGEN (N−2) | S-TOP / H-GALLERY / F-STORY | No editorial, no lookbook, no masthead, no agenda de crónicas |
| CESTA (N−3) | S-RAIL / H-TABLE / F-LEAD | No ONG, no tabla de necesidades, no compromiso, no rail |
| CANTERA (N−8) | S-BOTTOM / F-ONB | S-BOTTOM se reutiliza (cooldown 5); F-EDIT ≠ F-ONB; belleza ≠ parte de obra |
[COMPROBADO] El brief fija terna R-HY · S-BOTTOM · H-BENTO · F-EDIT. 0 códigos iguales a N−1 (ZAGUÁN). ≥2 distintos vs N−2 (MARGEN) y N−3 (CESTA). Ventana 4 días: R-IN + R-CL + R-CL + R-HY. Ground G-CHAPEL #E6E4ED, no G-CONCRETE.
02-ux-research-strategy.md
02 — Estrategia de investigación UX — MATIZ
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 salón de barrio | Modelado de stakeholders + personas | §3–4 |
| Definir job de «escribir los gramos» y de consultar la ficha | JTBD + stories Must | §5 |
| Mapear fricción WhatsApp / tazón → editor | Journey + service blueprint | §6–7 |
| Traducir a requisitos L2 PWA + editor | Matriz hallazgo → requisito → feature | §8 |
2. Fuentes y límites
Fuentes admisibles (secundarias / operativas)
- Conocimiento general de oficio colorista en salón de barrio (fórmula en gramos, oxidante en volúmenes, tiempo de pose, ficha de clienta).
- Analogía operativa con PWA auth-first + editor + JWT de la serie daily.
- Restricciones ALS-2: no reserva, no TPV, no lookbook editorial, no tabla de necesidades, no parte de obra, no wizard inmobiliario.
Límites éticos de verdad
| Prohibido | Permitido |
|---|---|
| «El 73% de coloristas de Usera fotografían el tazón» sin fuente | «[SUPUESTO] la colorista escribe con las manos húmedas junto al lavacabezas» |
| Citas de entrevistas ficticias como campo real | Quotes de persona etiquetadas como constructo de diseño |
| Ticket medio medido «42 € el balayage» como KPI de mercado | Fórmulas de seed de producto, etiquetadas como demo |
| Horario oficial de un salón concreto en Calle Amparo 22 | Local de seed; Nora e Inma son personas de diseño |
| «Nora existe y nos dijo…» | Nora Beltrán es persona de diseño |
3. Stakeholders
| Stakeholder | Influencia | Interés | Necesidad principal |
|---|---|---|---|
| COLORISTA (Nora) | Alta | Muy alta | Escribir gramos y ver qué hizo hace 8 semanas |
| DUEÑA (Inma) | Alta formal, baja en producto día 1 | Alta | Que el oficio quede escrito; no operar el JWT |
| Clientas (Marisol, Lucía, Amina, Pilar) | Baja formal | Alta (en el pelo, no en la app) | Que Nora acierte el matiz; no usar MATIZ |
| Cliente (Diego) | Baja formal | Alta | Igual: el matiz vive en la ficha |
| Caja del salón | Media | Media | Fuera: MATIZ no cobra |
| Agenda / WhatsApp de Inma | Media | Alta | Fuera: MATIZ no agenda |
| Booksy / Treatwell | Competencia / canal paralelo | — | No clonar la reserva; sí sustituir la foto del tazón |
| Marcas de color (Wella, L’Oréal, Igora) | Contexto de oficio | — | No son un recetario comercial dentro de MATIZ |
Mapa de poder (resumen)
- Decisor de adopción: la colorista (quien escribe) y, en difusión, la dueña (quien decide si hay otra app).
- Usuario frecuente de escritura: COLORISTA (Hoy + ficha + editor).
- Usuario frecuente de lectura: COLORISTA (historial de la ficha).
- Riesgo de rechazo: si Hoy parece una agenda; si Guardar parece Aplicar; si hay un CTA de cobro; si Inma exige login el día 1.
4. Personas
P1 — Nora Beltrán · COLORISTA
| Campo | Detalle |
|---|---|
| Edad / contexto | 34 años; colorista de Salón Amparo, Calle Amparo 22, Usera; 1–2 sillas a la vez |
| Digital | Fluida en móvil; escribe junto al lavacabezas con las manos aún húmedas |
| Goals | Ver las sillas de hoy, abrir la ficha, escribir marca / tono / gramos / oxidante / tiempo, guardar sin fingir que ya está en el pelo |
| Pains | WhatsApp + foto del tazón; la libreta se moja; no recuerda el 7.1 de Diego; Booksy no le sirve para los gramos |
| Quote de diseño | «Quiero los gramos antes de que se me olvide el tazón. No me des otra cita.» |
| Email demo | nora@matiz.salon / password123 |
Escenario: Login → Hoy → silla 1 Marisol → última fórmula honey balayage → Nueva fórmula → 9.3 + 8.3, 20 vol, 25 min → Guardar → «Guardado no es aplicado».
[COMPROBADO] Credencial demo sale del encargo. Nombre, edad y salón son constructo de diseño. [DECISIÓN]
P2 — Inma Soto · DUEÑA
| Campo | Detalle |
|---|---|
| Edad / contexto | 52 años; dueña de Salón Amparo; lleva la caja y el WhatsApp de citas |
| Digital | Media; móvil para WhatsApp; desconfía de «otra app de reservas» |
| Goals | Que Nora no pierda el matiz; que el producto no le robe la caja ni la agenda |
| Pains | Cada herramienta nueva se convierte en Booksy + TPV; le piden un login el día 1 |
| Quote de diseño | «Que apunte el color. Las horas las llevo yo por WhatsApp. La caja es mía.» |
Escenario: No se loguea. Pregunta a Nora «¿está apuntado lo de Marisol?». No encuentra /caja ni /agenda.
[DECISIÓN] Inma no es rol de sistema el día 1 (S4). No hay login DUEÑA en L2.
P3 — Marisol Vega · CLIENTA (no usa la app)
| Campo | Detalle |
|---|---|
| Edad / contexto | 47 años; honey balayage; silla 1 de hoy |
| Digital | No entra en MATIZ; pide hora por WhatsApp a Inma |
| Goals | Que Nora recuerde el honey; que no le «inventen» un 6/7 de caja |
| Pains | «El otro día me lo pusieron más oscuro»; no tiene por qué saber los gramos |
| Quote de diseño | «Que se acuerde de lo de la miel. Yo no abro ninguna app.» |
Escenario: Se sienta en la silla 1. Nora abre su ficha. Marisol no ve MATIZ.
[DECISIÓN] Marisol no es rol de sistema. Las cinco clientas son filas, no cuentas.
Clientas / cliente de seed (no personas primarias)
| Nombre | Edad | Matiz | Slug | Hoy |
|---|---|---|---|---|
| Marisol Vega | 47 | honey balayage | marisol | Silla 1 |
| Lucía Campos | 29 | blue-black | lucia | No |
| Amina El Idrissi | 38 | copper | amina | Silla 2 |
| Pilar Núñez | 61 | silver | pilar | Silla 3 |
| Diego Rivas | 33 | ashy brown | diego | No |
[COMPROBADO] Nombres, edades, matices y slugs salen del encargo.
Anti-personas
| Quién | Por qué no es target v1 |
|---|---|
| Community manager de cadena de salones | S1: 1–2 coloristas; no es PMS |
| Clienta que quiere «pedir cita online» | S3 / H2: no hay reserva |
| Cajera que quiere TPV en el mismo sitio | S4; MATIZ no cobra |
| Formadora de marca que quiere recetario oficial | Fórmula ≠ receta comercial |
| Jefe de producto de Booksy (40 estados de cita) | S3: el hoy lo rellena Nora |
| Buscador de ZAGUÁN que pregunta «tres fincas» | Otro job, otro día |
5. JTBD y user stories
Job principal (COLORISTA)
Cuando tengo el tazón delante y las manos húmedas,
quiero escribir los gramos, el oxidante y el tiempo,
para no depender de una foto de WhatsApp ni de la memoria de hace 8 semanas.
Job secundario (COLORISTA, lectura)
Cuando se sienta Marisol,
quiero ver qué le hice hace 8 semanas,
para no inventar el honey ni copiar el bote de la caja.
Job de la DUEÑA (fuera de app)
Cuando el salón abre,
quiero que Nora apunte el color y yo siga con el WhatsApp y la caja,
para no meter otra reserva ni otro TPV.
Jobs secundarios
| Job | Rol |
|---|---|
| Distinguir guardado de aplicado | COLORISTA |
| Ver las 3 sillas de hoy de un vistazo | COLORISTA |
| Avisar si el último oxidante era 30 vol | COLORISTA |
| No enseñar la ficha a la calle | COLORISTA / Inma |
| Entender que MATIZ no cobra ni agenda | Inma |
| No crear cuenta | Clienta |
Stories Must (v1)
| ID | Story | AC |
|---|---|---|
| US1 | Como colorista, quiero entrar con email/password | JWT + redirect / |
| US2 | Como colorista, quiero ver las sillas de hoy | GET /api/hoy · 3 sillas en el fold |
| US3 | Como colorista, quiero ver la última fórmula y un aviso de oxidante | Tile + aviso si 30 vol |
| US4 | Como colorista, quiero listar fichas con último matiz | GET /api/clients |
| US5 | Como colorista, quiero abrir una ficha y su historial | GET /api/clients/:slug · sin CTA de cita |
| US6 | Como colorista, quiero escribir una fórmula | Editor: líneas + oxidante + minutos + notas |
| US7 | Como colorista, quiero guardar sin aplicar | POST → SAVED · success «guardado ≠ aplicado» |
| US8 | Como colorista, quiero corregir una fórmula | PATCH /api/formulas/:id |
| US9 | Como colorista, quiero ver mi cuenta y salir | /cuenta · salón, rol, logout |
| US10 | Como cualquiera sin token, no quiero ver fichas | 401 / redirect login |
| US11 | Como colorista, quiero empty / error / loading | UI-08, UI-09, UI-10 |
| US12 | Como colorista junto al lavacabezas, quiero targets ≥44px | Bottom tabs + editor 390 |
MoSCoW (v1 L2)
| Prioridad | Ítems |
|---|---|
| Must | Login, Hoy bento, fichas, ficha, editor móvil, success, empty/error/loading, cuenta |
| Should | Editor desktop 1440; aviso oxidante 30 vol; badge SAVED; pie «MATIZ no agenda / no cobra» |
| Could | PATCH notas tras guardar; recuento de fórmulas en ficha; skeleton bento |
| Won’t | Agenda, TPV, cuenta clienta, recetario de marca, rol DUEÑA, aplicar |
6. Journey (COLORISTA · día de Marisol)
| Fase | Actor | Acción | Touchpoint | Emoción [HIPÓTESIS] |
|---|---|---|---|---|
| 1 Entrar | Nora | Login JWT; aterriza en Hoy | /login → / | Oficio, «esto no es Booksy» |
| 2 Mirar el día | Nora | Ve silla 1 Marisol, silla 2 Amina, silla 3 Pilar | / H-BENTO | Control (3 sillas, no 12 slots) |
| 3 Recordar | Nora | Abre ficha de Marisol; lee honey de junio | /fichas/marisol | Alivio («esto le hice») |
| 4 Escribir | Nora | Nueva fórmula: líneas + 20 vol + 25 min | /fichas/marisol/formula | Concentración (manos húmedas) |
| 5 Guardar ≠ aplicar | Nora | Guarda; lee el disclaimer | /ok/:id | Control (H1 + microcopy) |
| 6 No cobrar | Nora | No hay TPV; Inma cobra en caja | Fuera | Oficio, no euforia de ticket |
| 7 Comprobar | Nora | Vuelve a la ficha: la nueva está SAVED | /fichas/marisol | Confianza |
Momentos de verdad
- 3 sillas en el primer viewport — si hay que «descubrir la marca», se rompe H-BENTO / AP-21. [HIPÓTESIS]
- Editor sin wizard — si aparecen 4 pasos de «elige el look», F-EDIT falla.
- Success dice que guardado no es aplicado — evita la mentira del color puesto.
- Cero CTA de cita o cobro — si aparece «Agendar» o «Cobrar», H2 / microcopy fallan.
- Tabs abajo, no arriba — S2: manos húmedas, pulgar. [HIPÓTESIS]
7. Service blueprint (resumen)
| Capa | Elementos |
|---|---|
| Frontstage COLORISTA | Login, Hoy bento, fichas, ficha, editor, success, cuenta |
| Frontstage clienta | No existe. La clienta se sienta; no abre MATIZ |
| Backstage | WhatsApp de Inma (hora), caja física (ticket), tazón y oxidante reales, lavacabezas |
| Sistemas | Nest API :3019, Neon mute-wave-74707210, JWT, Angular PWA |
| Soportes | Seed 1 COLORISTA + 5 fichas + 8 fórmulas + 3 sittings + fotos, Paper, docs |
| Fallos | 0 sillas; 401 sin token; 404 slug; red caída → UI error; validación de gramos |
Fallos de servicio y respuesta de diseño
| Fallo | Evidencia de UI | Recuperación |
|---|---|---|
| 0 sillas hoy | UI-08 Empty Hoy | «Hoy no hay sillas. El hoy lo rellena Nora por la mañana.» |
| 0 fichas | UI-08 Empty Fichas | «Aún no hay fichas.» (seed no debería; útil si se vacía) |
| API caída | UI-09 Error | Reintentar |
| Latencia Hoy | Loading (doc 16) | Skeleton bento (3 tiles + 1 fórmula) |
| Form inválido | Inline field | No navegar a /ok |
| Slug inexistente | 404 | «Esa ficha no está.» + volver a Fichas |
Sin token en / | Redirect | /login |
| Oxidante 30 vol en última de hoy | Aviso | Tile amatista: «Revisa el oxidante: 30 vol no es 20.» |
8. Matriz hallazgo → requisito → feature
| Hallazgo | Tipo | Requisito | Feature v1 |
|---|---|---|---|
| Escribe con manos húmedas | [SUPUESTO] S2 | Bottom tabs + targets ≥44 | S-BOTTOM · UI-05 |
| WhatsApp + foto del tazón | [HIPÓTESIS] H1 | Editor de gramos + oxidante | F-EDIT · POST formulas |
| Ficha se lee como cita | [HIPÓTESIS] H2 | Cero CTA de agenda | UI-04 solo historial |
| Guardado se lee como aplicado | [DECISIÓN] | Copy irrenunciable | UI-07 + editor |
| Receta de caja ≠ fórmula | [DECISIÓN] | Copy irrenunciable | Editor + ficha |
| Dueña no entra día 1 | [SUPUESTO] S4 | Un rol COLORISTA | Login + guards |
| Clienta no necesita cuenta | [HIPÓTESIS] H3 | Auth-first, sin signup público | JWT only |
| Hoy lo rellena Nora | [SUPUESTO] S3 | Sittings seed, no calendario | GET /hoy |
| L2 compacto, no PMS | [DECISIÓN] | 5 fichas + 8 fórmulas + 3 sillas | Sin rol DUEÑA, sin TPV |
9. Preguntas abiertas (no bloquean v1)
| ID | Pregunta | Cómo se resolvería después |
|---|---|---|
| Q1 | ¿APPLIED llega en L+1 o nunca? | [DECISIÓN v1] solo SAVED. Aplicar es gesto físico. Could: toggle «puesta» sin fingir el resultado |
| Q2 | ¿Quién escribe las sillas de mañana? | [DECISIÓN v1] seed / backstage. UI de agenda = L3 y rompe H2 |
| Q3 | ¿Inma tendrá JWT en L+1? | S4 lo deja fuera. L+1 Could: rol DUEÑA de solo lectura |
| Q4 | ¿Las marcas del seed son oficiales? | No. Catálogo demo. [SUPUESTO de catálogo] |
| Q5 | ¿El Salón Amparo de Calle Amparo 22 existe? | No. Atrezzo de persona. No se afirma como negocio existente |
| Q6 | ¿Las fotos de clientas son personas reales identificables? | Assets de case; no se presentan como clientela real. [SUPUESTO de catálogo] |
10. Plan de research futuro (si hubiera campo real)
| Método | Muestra orientativa | Pregunta |
|---|---|---|
| Test de usabilidad editor | 5–6 coloristas 25–45 | ¿Guardan una fórmula en < 90 s con el pulgar? |
| Shadowing junto al lavacabezas | 1 colorista de barrio | ¿El editor sustituye la foto del tazón? |
| Revisión de expectativa de cita | Clics en ficha vs «¿a qué hora?» | Validar H2 |
| Editor mobile 390 con manos ocupadas | 5 sesiones | ¿Los targets de 44px bastan con el pulgar húmedo? |
Estos métodos no se han ejecutado. No se reportan hallazgos como si lo hubieran sido.
11. Síntesis
MATIZ se diseña como cuaderno de oficio que escribe y guarda, no como Booksy, no como TPV y no como recetario de marca.
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 — MATIZ
1. Principios de IA
| Principio | Aplicación |
|---|---|
| Auth first | Sin token no hay Hoy. No hay landing de marca por delante. |
| Hoy → ficha → editor → ok | Profundidad 2 desde la silla hasta la fórmula; no hay modal de cita |
| Solo COLORISTA | Todas las superficies autenticadas salvo login |
| Lenguaje de dominio | Fórmula, ficha, silla, matiz, oxidante — no «cita», «SKU», «lead», «listing» |
| Ficha no es cita | La ficha no tiene slot, hora ni botón Agendar |
| Guardar se mira | Success no tiene «Aplicar» ni «Cobrar» (≠ F-BOOK / ≠ TPV) |
| Bottom tabs siempre a mano | S-BOTTOM: Hoy · Fichas · Cuenta; no masthead, no rail, no wizard chrome |
2. Sitemap
/login Login JWT COLORISTA público
/ Hoy bento (auth) H-BENTO
/fichas Lista de clientas (auth)
/fichas/:slug Ficha + historial (auth)
/fichas/:slug/formula Editor de fórmula (auth) F-EDIT
/ok/:id Success post-guardado (auth)
/cuenta Salón, rol, salir (auth)
/** → redirect / (si hay token) o /login
Árbol por audiencia
| Audiencia | Nodos relevantes |
|---|---|
| COLORISTA | Login → Hoy → Ficha → Editor → Ok · Fichas · Cuenta |
| DUEÑA / clienta | Ninguno. No hay cara pública |
[COMPROBADO] Siete rutas del encargo L2. No se inventan /cita, /agenda, /cobrar, /tienda, /aplicar.
3. Navegación
Bottom tabs (S-BOTTOM)
| Slot | Destino | Visibilidad | Notas |
|---|---|---|---|
| Hoy | / | Auth | Amatista cuando activo |
| Fichas | /fichas | Auth | Lista; no es agenda |
| Cuenta | /cuenta | Auth | Salón + salir |
[DECISIÓN] El bottom tab es la única navegación primaria autenticada. Encaja L2 PWA. No hay masthead (MARGEN), no rail (CESTA), no chrome de pasos (ZAGUÁN).
Desktop ≥960px: tabs pueden subir a una barra inferior fija de 64px o a un rail corto inferior centrado; no se convierten en top-nav de marketing.
Mobile ~390px: tabs 56–64px + safe-area; labels Hoy / Fichas / Cuenta siempre visibles. [DECISIÓN] En 390 no se introduce hamburger.
El editor y el success conservan los tabs (la colorista no se pierde). Un «atrás» contextual vive en el header de la pantalla, no sustituye al tab.
Header contextual (no es nav primaria)
| Superficie | Izquierda | Centro | Derecha |
|---|---|---|---|
| Hoy | Wordmark MATIZ | — | — |
| Fichas | Wordmark | — | — |
| Ficha | ← Fichas | Nombre | Nueva fórmula |
| Editor | ← Ficha | «Nueva fórmula» | — |
| Success | — | — | — |
| Cuenta | Wordmark | — | — |
| Login | Wordmark | — | — |
[DECISIÓN] El wordmark en autenticado lleva a / (Hoy), no reinicia un wizard (no hay wizard).
Login (único público)
| Elemento | Destino | Notas |
|---|---|---|
| Email / password / Entrar | POST login → / | JWT |
| Error | no navega | Inline |
| Pie | no «crear cuenta» | No hay signup |
[DECISIÓN] No hay /registro. No hay «Soy clienta». No hay «Pedir cita».
4. Inventario de contenido
| Pantalla | Contenidos |
|---|---|
Login /login | Wordmark, eslogan, email, password, submit, error |
Hoy / | Chrome S-BOTTOM, 3 tiles de silla (nombre, nº, último matiz), tile última fórmula, aviso oxidante, empty, loading, error |
Fichas /fichas | Lista: foto, nombre, último matiz, fecha de última fórmula, empty, error |
Ficha /fichas/:slug | Foto, nombre, edad/matiz, historial de fórmulas, CTA Nueva fórmula, disclaimer «ficha ≠ cita» |
Editor /fichas/:slug/formula | Nombre de ficha, líneas (marca, tono, gramos, quitar/añadir), oxidante 10/20/30, minutos, notas, Guardar, disclaimer «fórmula ≠ receta» + «guardado ≠ aplicado» |
Success /ok/:id | Mensaje, id/slug, «guardado ≠ aplicado», CTA ver ficha / Hoy |
Cuenta /cuenta | Salón Amparo, Calle Amparo 22, rol COLORISTA, nombre Nora, Salir |
5. Taxonomía
Role
| Status API | Label UI | Semántica |
|---|---|---|
COLORISTA | Colorista | Único rol autenticado v1 |
[DECISIÓN] No hay DUENA / CAJA / CLIENTA.
FormulaStatus
| Status API | Label UI | Semántica |
|---|---|---|
SAVED | Guardada | Persistida; no implica aplicada |
Orden de historial: createdAt descendente (lo último escrito primero).
[DECISIÓN] No hay APPLIED / MIXED / BILLED: S4 + microcopy.
Oxidant
| API | Label UI |
|---|---|
10 | 10 vol |
20 | 20 vol |
30 | 30 vol |
Clients (líneas seed)
| slug | Nombre | Edad | Matiz | Hoy |
|---|---|---|---|---|
marisol | Marisol Vega | 47 | honey balayage | Silla 1 |
lucia | Lucía Campos | 29 | blue-black | — |
amina | Amina El Idrissi | 38 | copper | Silla 2 |
pilar | Pilar Núñez | 61 | silver | Silla 3 |
diego | Diego Rivas | 33 | ashy brown | — |
[COMPROBADO] Cinco fichas con slugs del encargo. [DECISIÓN]
Sittings (hoy)
| chair | Client slug | Semántica |
|---|---|---|
| 1 | marisol | Asiento, no slot |
| 2 | amina | Asiento, no slot |
| 3 | pilar | Asiento, no slot |
[COMPROBADO] Encargo: silla 1 Marisol, 2 Amina, 3 Pilar.
6. Modelo mental vs UI
| Modelo mental | Representación |
|---|---|
| «Quién se sienta hoy» | Hoy, 3 tiles de silla |
| «Qué le hice a Marisol» | Ficha /fichas/marisol + historial |
| «Anoto el tazón» | Editor de líneas + oxidante + minutos |
| «¿Ya está en el pelo?» | No. Copy: guardado ≠ aplicado |
| «¿A qué hora viene?» | No hay UI. Copy: ficha ≠ cita. MATIZ no agenda |
| «¿Cobro desde aquí?» | No hay UI. MATIZ no cobra |
| «¿Es el 6/7 de la caja?» | No. Copy: fórmula ≠ receta comercial |
| «Salgo del cuaderno» | Cuenta → Salir |
7. Query string y estado
| Ruta | Query | Uso |
|---|---|---|
/login | ?next=/fichas/marisol (o /, /fichas, /cuenta, /ok/:id, /fichas/:slug/formula) | Redirect post-login sanitizado |
| Resto | — | Sin ?q= de búsqueda Must |
[DECISIÓN] Sin búsqueda facetada en v1. 5 fichas se recorren a ojo. L+1: filtro por matiz.
El editor no persiste borrador en sessionStorage como Must. Si se sale sin guardar, se pierde. Could L+1: draft local.
8. Rutas API alineadas a IA
| UI | API |
|---|---|
| Login | POST /api/auth/login |
| Cuenta / header | GET /api/me JWT |
| Hoy | GET /api/hoy JWT |
| Lista fichas | GET /api/clients JWT |
| Ficha | GET /api/clients/:slug JWT |
| Guardar fórmula | POST /api/clients/:slug/formulas JWT |
| Leer / editar fórmula | GET / PATCH /api/formulas/:id JWT |
| Salud | GET /api/health |
[COMPROBADO] Contrato del encargo. No se documentan citas, cobros ni POST /api/appointments como Must.
9. Decisiones de IA descartadas
| Idea | Por qué no en L2 v1 |
|---|---|
/explorar looks + grid de color | Rompe H-BENTO / F-EDIT; eso es lookbook |
| Área «mis citas» de clienta | Cuenta de facto; contradice H3 |
/sillas calendario semanal | Rompe H2 / ficha ≠ cita |
| Search como home | H-SEARCH de otros días |
| Feed de «fórmulas nuevas» | H-FEED de RONDA |
| Tabla de clientas como home | H-TABLE de CESTA |
| Lookbook editorial | H-GALLERY de MARGEN |
/cita/:slug | Rompe H2 / microcopy Ficha ≠ cita |
| Wizard de 4 pasos de fórmula | F-ONB de CANTERA; el editor es 1 pantalla |
| Wizard de criterios (zona / tope) | ZAGUÁN; otro oficio |
04-user-flows.md
04 — Flujos de usuario — MATIZ
Convenciones
- Actor: Guest (anónimo) | COLORISTA
- Éxito: resultado observable
- Errores: UI + código HTTP cuando aplica
- Flujo canónico: F-EDIT (ficha → editor → POST fórmula → ok)
- Flujo de escritorio: F-DESK (login → Hoy)
- Dueña / clienta: no tienen flujo de sistema
F0 — Entrada anónima (Guest)
cualquier ruta auth → sin token → /login?next=<path sanitizado>
/login → si hay token válido → / (o next)
| Paso | Acción | Sistema |
|---|---|---|
| 1 | Aterriza sin JWT | Guard Angular |
| 2 | Ve solo el login | UI-01 |
| 3 | No encuentra «pedir cita» ni «crear cuenta» | IA |
Éxito: cero cara pública.
AC: no hay landing, hero ni lead. Auth-first.
F1 — Login JWT (COLORISTA)
/login → POST /api/auth/login { email, password }
→ 200 { accessToken, user } → localStorage → / (o ?next sanitizado)
→ 401 → mensaje error en form
| Campo | Validación cliente | API |
|---|---|---|
| required, email | @IsEmail() | |
| password | required, min 6 | @MinLength(6) |
Éxito: token guardado; user role=COLORISTA, nombre Nora Beltrán.
Credencial demo: nora@matiz.salon / password123.
| Caso | Comportamiento |
|---|---|
| Credenciales inválidas | 401 + mensaje |
| Red caída | Error de red en UI |
| Token caducado en Hoy | 401 en GET → re-login |
next externo / // | Ignorado; va a / |
[COMPROBADO] Credencial del encargo.
F2 — Hoy bento (COLORISTA)
/ (token)
→ GET /api/hoy
→ { sittings[3], lastFormula, oxidantWarning }
→ 3 tiles + última fórmula + aviso | empty | error | skeleton
→ tap silla → /fichas/:slug
→ tap última fórmula → /fichas/:slug (de esa fórmula)
| Paso | Acción | Sistema |
|---|---|---|
| 1 | Lee las 3 sillas (nombre, nº, último matiz) | sittings |
| 2 | Lee la última fórmula guardada | lastFormula |
| 3 | Lee el aviso si alguna silla de hoy trae 30 vol | oxidantWarning |
| 4 | Abre ficha | Router |
Éxito: 3 sillas reales en el fold, o empty accionable.
AC tiles: cada una muestra nombre, silla, último matiz. Cero botones Agendar / Cobrar.
Guión Nora (demo): silla 1 Marisol (honey balayage) · silla 2 Amina (copper) · silla 3 Pilar (silver). Última fórmula: Diego, ashy brown, 2026-08-01. Aviso: Amina trae 30 vol. [SUPUESTO de catálogo] alineado al seed de doc 05.
Errores / estados
| Caso | Comportamiento |
|---|---|
| 0 sittings | UI-08: «Hoy no hay sillas. El hoy lo rellena Nora por la mañana.» |
| Red / 5xx | UI-09 + Reintentar (repite GET) |
| Primera carga | Skeleton bento (doc 16) |
| Sin token | Redirect /login |
| 401 mid-session | Redirect /login |
[DECISIÓN] GET /api/hoy es el Must de producto. No hay POST de silla en v1 (S3).
F3 — Lista de fichas (COLORISTA)
/fichas → GET /api/clients
→ filas: foto, nombre, último matiz, fecha
→ tap fila → /fichas/:slug
| Caso | Comportamiento |
|---|---|
| Lista con items | 5 filas seed |
| Lista vacía | Empty: «Aún no hay fichas.» |
| Fallo red / 401 | Error + reintentar / re-login |
Orden: updatedAt de última fórmula desc; quien no tiene fórmula, al final. [DECISIÓN]
Seed: marisol, lucia, amina, pilar, diego.
F4 — Ficha de clienta (COLORISTA)
/fichas/:slug → GET /api/clients/:slug
→ foto, nombre, matiz, historial de fórmulas
→ “La ficha no es una cita. MATIZ no agenda.”
→ CTA Nueva fórmula → /fichas/:slug/formula
→ tap fórmula → /fichas/:slug/formula?id= (Could) o solo lectura en la misma ficha
→ 404 slug inexistente → mensaje + volver a /fichas
| Paso | Acción | Sistema |
|---|---|---|
| 1 | Lee nombre y matiz | Client |
| 2 | Recorre historial (fecha, oxidante, líneas resumidas) | formulas[] |
| 3 | Nueva fórmula | Router |
| 4 | Lee disclaimer | UI |
Éxito: ficha leíble; disclaimer visible; no hay hora, slot ni cobro.
Error: 404 si slug inexistente.
AC ficha
| # | Criterio |
|---|---|
| 1 | Foto real del cover (assets/clients/:slug.jpg) |
| 2 | Nombre en Libre Baskerville; matiz en Nunito Sans peltre |
| 3 | Historial visible; si 0 fórmulas, empty honesto + CTA |
| 4 | Disclaimer «ficha ≠ cita» visible sin scroll en desktop |
| 5 | Sin CTA de cita, cobro o «te llamamos» |
[DECISIÓN] v1 no tiene /fichas/:slug/formula/:id de detalle. El historial se lee en la ficha; el editor crea nueva. PATCH vive si se reabre Could con ?id=; Must = POST nueva.
F5 — Editor de fórmula (COLORISTA) · F-EDIT
/fichas/:slug/formula
→ si slug 404 → volver a /fichas
→ líneas [ { brand, tone, grams } ] (mín. 1)
→ oxidante 10 | 20 | 30
→ minutes
→ notes
→ disclaimer “Esto es una fórmula, no la receta de la caja.”
→ disclaimer “Guardar no es aplicar.”
→ POST /api/clients/:slug/formulas
→ 201 Formula { id, status: SAVED, ... }
→ /ok/:id
| Campo | Validación cliente (mín.) | API |
|---|---|---|
| lines | ≥1; cada una brand min 2, tone min 1, grams > 0 | @ArrayMinSize(1) |
| oxidant | required, enum 10/20/30 | 10 | 20 | 30 |
| minutes | required, 1–90 | @Min(1) @Max(90) |
| notes | opcional, max 500 | @MaxLength(500) |
| status | no en POST; siempre SAVED | servidor fuerza SAVED |
Éxito: registro status=SAVED + pantalla success.
No hay «Aplicar» ni «Cobrar» en v1.
| Gestos | Resultado |
|---|---|
| Añadir línea | Push { brand:'', tone:'', grams: null } |
| Quitar línea | Splice; no deja 0 líneas (bloquea) |
| Chip oxidante | Radio 10 / 20 / 30 |
| Guardar | POST; pending en botón |
| Atrás | /fichas/:slug; descarta si no hay confirm Must |
Errores
| Caso | Comportamiento |
|---|---|
| Validación DTO | 400 + mensaje de campo |
| Red caída | Error de red en form; no navegar a /ok/… |
| Doble submit | Botón disabled mientras pending |
| lines vacías / grams ≤ 0 | Inline; no POST |
| Sin token | 401 |
| Slug inexistente | 404 |
[DECISIÓN] Crear siempre como SAVED. No existe transición a APPLIED.
[COMPROBADO] Entidad Formula según encargo (lines JSON, oxidant 10|20|30, minutes, notes, status SAVED).
Variante desktop (UI-06)
Misma ruta /fichas/:slug/formula a 1440. El editor ocupa una columna ~720 + staff de ficha a la derecha (nombre, último matiz, disclaimer). No es otra app. [DECISIÓN]
F6 — Success de guardado (COLORISTA)
/ok/:id → GET /api/formulas/:id
→ copy de acuse + clientName / id
→ “Guardado no es aplicado. El color aún no está en el pelo.”
→ CTA ver ficha / volver a Hoy
| Paso | Acción | Sistema |
|---|---|---|
| 1 | Lee acuse | UI |
| 2 | Vuelve a la ficha y comprueba el historial | /fichas/:slug |
| 3 | Sigue con la siguiente silla | / |
AC: el disclaimer es visible sin scroll en desktop.
Si se entra a /ok/algo-que-no-existe: mensaje genérico + volver a Hoy; no se inventa un registro. [DECISIÓN]
F7 — Editar fórmula (COLORISTA) · PATCH
Desde ficha (Could: «corregir»)
→ GET /api/formulas/:id
→ mismos campos que F5
→ PATCH /api/formulas/:id { lines?, oxidant?, minutes?, notes? }
→ 200 Formula
→ /ok/:id o vuelta a la ficha
[DECISIÓN] PATCH no acepta { status: "APPLIED" }. Status permanece SAVED. Corregir gramos no es aplicar.
Must de producto = POST nueva. PATCH es Should (el contrato API existe).
F8 — Cuenta y logout (COLORISTA)
/cuenta → GET /api/me
→ Salón Amparo · Calle Amparo 22, Usera · COLORISTA · Nora Beltrán
→ “MATIZ no agenda. MATIZ no cobra.”
→ Salir → limpia localStorage → /login
| Caso | Comportamiento |
|---|---|
| 200 | Nombre, email, rol, salón |
| 401 | Re-login |
| Salir | Borra matiz_token + matiz_user |
F9 — Health (ops)
GET /api/health → 200 { ok: true } (o { status: 'ok' })
Sin auth. No es una pantalla. Smoke de arranque.
Matriz de errores global
| Código | Cuándo | UI |
|---|---|---|
| 400 | DTO inválido | Mensaje campo / genérico |
| 401 | Sin/mal token | Re-login |
| 404 | slug o id no existe | Mensaje + volver a Fichas / Hoy |
| 5xx / network | API caída | Error + retry |
Flujos fuera de alcance v1
- Registro / recuperación de password de COLORISTA
- Cuenta de clienta / «mi cita» / recordatorios
- Reserva o confirmación de hora
- Cobro / ticket / TPV
- Alta de ficha nueva (seed fijo; Could L+1)
- Alta de silla de mañana (agenda)
- Aplicar / marcar puesta
- Notificación push «Marisol en 10 min»
- Multi-colorista / turnos
05-data-model.md
05 — Modelo de datos — MATIZ
1. Visión general
Dominio L2 de oficio colorista + cuaderno autenticado:
| Entidad | Tabla SQL | Propósito |
|---|---|---|
| User | users | Identidad de oficio (rol COLORISTA) |
| Session | sessions | Sesión JWT / token persistido (Should) |
| Client | clients | Ficha de clienta / cliente |
| Formula | formulas | Fórmula de color (líneas + oxidante + tiempo) |
| Sitting | sittings | Silla de un día (asiento, no cita) |
Base: PostgreSQL (Neon project mute-wave-74707210) · IDs: cuid() o texto estable (usr_nora, cli_marisol).
[DECISIÓN] ensureSchema o Prisma son válidos si el contrato de tablas se cumple; el brief de stack no exige Prisma.
2. Enums (aplicación; persistidos como TEXT + check)
Role
| Valor | Descripción |
|---|---|
COLORISTA | Operadora del cuaderno; único rol autenticado v1 |
FormulaStatus
| Valor | Descripción |
|---|---|
SAVED | Persistida; no implica aplicada |
Oxidant
10 | 20 | 30
3. Diagrama ER (texto)
users
id, email, password_hash, name, role(COLORISTA)
salon_name, salon_address
created_at, updated_at
1 ──< sessions
1 ──< formulas -- author
1 ──< sittings -- who wrote today's chairs (Could)
sessions
id, user_id → users
token_hash, expires_at
created_at
clients
id, slug (unique)
name, age, shade -- shade = último matiz memorable
cover -- filename
created_at, updated_at
1 ──< formulas
1 ──< sittings
formulas
id
client_id → clients
author_id → users
lines (JSONB) -- [{ brand, tone, grams }]
oxidant (10|20|30)
minutes
notes
status (SAVED)
created_at, updated_at
sittings
id
client_id → clients
chair (1|2|3)
day (DATE) -- Europe/Madrid
created_at
v1: una sola COLORISTA opera sobre el conjunto global.
No hay FK de clienta-usuaria: la clienta no se loguea.
4. Tablas
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 'COLORISTA' |
| salon_name | TEXT | NOT NULL DEFAULT 'Salón Amparo' |
| salon_address | TEXT | NOT NULL DEFAULT 'Calle Amparo 22, Usera, Madrid' |
| 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. Si el slice usa JWT stateless, documentar el hueco; D-P1-05 se cumple igual con guard.
clients
| Campo | Tipo | Constraints |
|---|---|---|
| id | TEXT | PK |
| slug | TEXT | UNIQUE NOT NULL, kebab / un solo token (marisol) |
| name | TEXT | NOT NULL |
| age | INTEGER | NOT NULL CHECK > 0 |
| shade | TEXT | NOT NULL · matiz memorable (honey balayage) |
| cover | TEXT | filename (marisol.jpg) |
| created_at / updated_at | TIMESTAMPTZ |
formulas
| Campo | Tipo | Constraints |
|---|---|---|
| id | TEXT | PK |
| client_id | TEXT | NOT NULL FK → clients(id) ON DELETE CASCADE |
| author_id | TEXT | NOT NULL FK → users(id) |
| lines | JSONB | NOT NULL · array { brand: string, tone: string, grams: number } ≥ 1 |
| oxidant | INTEGER | NOT NULL CHECK IN (10, 20, 30) |
| minutes | INTEGER | NOT NULL CHECK BETWEEN 1 AND 90 |
| notes | TEXT | NULL |
| status | TEXT | NOT NULL DEFAULT 'SAVED' |
| created_at / updated_at | TIMESTAMPTZ |
Validación de lines: cada ítem tiene brand (min 2), tone (min 1), grams (> 0, número). [DECISIÓN] JSONB, no tabla formula_lines en v1.
sittings
| Campo | Tipo | Constraints |
|---|---|---|
| id | TEXT | PK |
| client_id | TEXT | NOT NULL FK → clients(id) |
| chair | INTEGER | NOT NULL CHECK IN (1, 2, 3) |
| day | DATE | NOT NULL · día civil Europe/Madrid |
| created_at | TIMESTAMPTZ |
[DECISIÓN] Unique (day, chair) y unique (day, client_id): una silla, una persona; una persona, una silla ese día.
5. Reglas de integridad y negocio
| Regla | Implementación |
|---|---|
| Lectura de Hoy / fichas / fórmulas | JWT COLORISTA → 401 sin token |
| Escritura de fórmulas | JWT COLORISTA → 401 sin token |
| Login / health | Públicos |
| Slug único | seed fijo; alta de clienta no es Must |
| Status default fórmula | SAVED al crear |
| Password | Nunca en claro; solo password_hash |
| Orden clients | última fórmula created_at desc |
| Orden historial | created_at desc |
| Orden sittings | chair asc |
| Guardar Formula | no marca sitting como teñida; no cobra |
| Público nunca lista clients | no hay GET público de fichas |
| Oxidante | solo 10 / 20 / 30 |
| Aviso oxidante en Hoy | true si alguna silla de day=hoy tiene última fórmula con oxidant=30 |
6. Contratos API (resumen)
JSON camelCase hacia el web. [DECISIÓN]
POST /api/auth/login
Body: { email, password }
Response 200: { accessToken, user: { id, email, name, role, salonName, salonAddress } }
401 credenciales inválidas. Response sin hash.
GET /api/me (JWT)
Response 200: { id, email, name, role, salonName, salonAddress }
Sin token → 401.
GET /api/hoy (JWT)
Response 200
{
"day": "2026-08-19",
"sittings": [
{
"id": "sit_1",
"chair": 1,
"client": {
"id": "cli_marisol",
"slug": "marisol",
"name": "Marisol Vega",
"age": 47,
"shade": "honey balayage",
"cover": "marisol.jpg"
},
"lastFormula": {
"id": "frm_marisol_2",
"oxidant": 20,
"minutes": 25,
"status": "SAVED",
"createdAt": "2026-06-24T10:00:00.000Z",
"lines": [
{ "brand": "L'Oréal Dia Light", "tone": "9.3", "grams": 20 },
{ "brand": "L'Oréal Dia Light", "tone": "8.3", "grams": 20 }
]
}
}
],
"lastFormula": {
"id": "frm_diego_1",
"clientSlug": "diego",
"clientName": "Diego Rivas",
"oxidant": 20,
"minutes": 30,
"status": "SAVED",
"createdAt": "2026-08-01T11:00:00.000Z",
"lines": [
{ "brand": "L'Oréal Majirel", "tone": "6.1", "grams": 20 },
{ "brand": "L'Oréal Majirel", "tone": "7.1", "grams": 20 }
]
},
"oxidantWarning": {
"active": true,
"chair": 2,
"clientSlug": "amina",
"oxidant": 30,
"message": "Revisa el oxidante: Amina trae 30 vol. 30 no es 20."
}
}
Si 0 sittings: { day, sittings: [], lastFormula, oxidantWarning: { active: false } } + empty UI-08.
lastFormula puede ser null si no hay ninguna.
Sin token → 401.
GET /api/clients (JWT)
Response 200: ClientCard[]
[
{
"id": "cli_marisol",
"slug": "marisol",
"name": "Marisol Vega",
"age": 47,
"shade": "honey balayage",
"cover": "marisol.jpg",
"lastFormulaAt": "2026-06-24T10:00:00.000Z",
"lastOxidant": 20
}
]
GET /api/clients/:slug (JWT)
Client + formulas[] (historial completo, createdAt desc) o 404.
{
"id": "cli_marisol",
"slug": "marisol",
"name": "Marisol Vega",
"age": 47,
"shade": "honey balayage",
"cover": "marisol.jpg",
"formulas": [
{
"id": "frm_marisol_2",
"lines": [
{ "brand": "L'Oréal Dia Light", "tone": "9.3", "grams": 20 },
{ "brand": "L'Oréal Dia Light", "tone": "8.3", "grams": 20 }
],
"oxidant": 20,
"minutes": 25,
"notes": "Balayage miel. No bajar del 8.",
"status": "SAVED",
"createdAt": "2026-06-24T10:00:00.000Z"
}
]
}
POST /api/clients/:slug/formulas (JWT)
Body
| Campo | Tipo | Req |
|---|---|---|
| lines | { brand, tone, grams }[] | sí · ≥1 |
| oxidant | 10 | 20 | 30 | sí |
| minutes | number 1–90 | sí |
| notes | string | no |
Response 201: Formula + id + status: "SAVED" + clientSlug.
Slug inexistente → 404. Sin token → 401. DTO inválido → 400.
GET /api/formulas/:id (JWT)
Formula completa o 404.
PATCH /api/formulas/:id (JWT)
Body: { lines?, oxidant?, minutes?, notes? }
No acepta status.
Response 200: Formula actualizada. Sin token → 401.
[COMPROBADO] PATCH sobre :id. [DECISIÓN] sin transición de status.
GET /api/health
Response 200: { ok: true }
7. Seed de referencia (2026-08-19)
COLORISTA
| Nombre | Password | Salón | |
|---|---|---|---|
| Nora Beltrán | nora@matiz.salon | password123 | Salón Amparo · Calle Amparo 22, Usera, Madrid |
[COMPROBADO] Credencial del encargo. Nombre de pila es constructo de diseño.
Clients
| slug | Nombre | Edad | Matiz | Cover | Asset |
|---|---|---|---|---|---|
| marisol | Marisol Vega | 47 | honey balayage | marisol.jpg | assets/clients/marisol.jpg |
| lucia | Lucía Campos | 29 | blue-black | lucia.jpg | assets/clients/lucia.jpg |
| amina | Amina El Idrissi | 38 | copper | amina.jpg | assets/clients/amina.jpg |
| pilar | Pilar Núñez | 61 | silver | pilar.jpg | assets/clients/pilar.jpg |
| diego | Diego Rivas | 33 | ashy brown | diego.jpg | assets/clients/diego.jpg |
[COMPROBADO] Slugs, nombres, edades y matices del encargo. Fotos en el case.
Formulas (8 · todas SAVED)
Las marcas, tonos y gramos son catálogo demo, no recetas oficiales de fabricante ni historial clínico real. [SUPUESTO de catálogo]
| id | Client | Fecha (demo) | Oxidante | Min | Líneas | Notas |
|---|---|---|---|---|---|---|
frm_diego_1 | diego | 2026-08-01 | 20 | 30 | Majirel 6.1 20 g + Majirel 7.1 20 g | Ceniza. No calentar. |
frm_lucia_1 | lucia | 2026-07-30 | 10 | 35 | Koleston 2/0 30 g + Koleston 2/8 10 g | Blue-black. Cubrir canas del nacimiento. |
frm_amina_2 | amina | 2026-07-08 | 30 | 35 | Igora 7-77 25 g + Igora 8-77 15 g | Cobre. 30 vol: avisar en Hoy. |
frm_marisol_2 | marisol | 2026-06-24 | 20 | 25 | Dia Light 9.3 20 g + Dia Light 8.3 20 g | Honey. No bajar del 8. |
frm_pilar_2 | pilar | 2026-06-10 | 10 | 20 | Illumina 10/81 20 g + Illumina 9/81 20 g | Plata. Solo matizar. |
frm_amina_1 | amina | 2026-05-20 | 20 | 35 | Igora 7-77 40 g | Primer cobre de la temporada. |
frm_marisol_1 | marisol | 2026-04-15 | 20 | 30 | Dia Light 9.3 30 g + Dia Light 10.3 10 g | Más claro; tiró a paja. |
frm_pilar_1 | pilar | 2026-03-18 | 10 | 15 | Illumina 10/1 40 g | Primera plata. |
lastFormula de Hoy = frm_diego_1 (la más reciente).
oxidantWarning = Amina silla 2, frm_amina_2, 30 vol.
Sittings (hoy = 2026-08-19)
| id | chair | Client | day |
|---|---|---|---|
sit_1 | 1 | marisol | 2026-08-19 |
sit_2 | 2 | amina | 2026-08-19 |
sit_3 | 3 | pilar | 2026-08-19 |
Lucía y Diego no tienen sitting hoy.
Las cinco fichas, las ocho fórmulas y las tres sillas son seed de producto. No se presentan como clientela real de un salón de Usera. [SUPUESTO de catálogo]
8. Evolución posible (no implementada)
| Cambio | Nivel |
|---|---|
| Status APPLIED explícito (gesto, no resultado) | L+1 |
| Alta de clienta + foto | L+1 |
| Rol DUEÑA solo lectura | L+1 |
Segunda colorista + author_id filtrado | L+1 / L3 |
| Soft delete + audit log | L3 |
| Agenda / slots | Fuera (H2) |
| TPV / ticket | Fuera (microcopy) |
| Recetario oficial de marca | Fuera (fórmula ≠ receta) |
06-tech-stack.md
06 — Stack tecnológico — MATIZ
1. Visión
| Capa | Tecnología | Notas |
|---|---|---|
| Frontend | Angular standalone + signals | Puerto 4200 · PWA mobile-first |
| Estilos | Tailwind CSS | Tokens chapel (yeso, pizarra, amatista, lino, peltre, oro) |
| Backend | NestJS TypeScript | Puerto 3019, prefijo /api |
| Persistencia | PostgreSQL (ensureSchema o Prisma) | Tablas users, clients, formulas, sittings (+ sessions Should) |
| DB | Neon PostgreSQL | project mute-wave-74707210 |
| Auth | JWT | Rol COLORISTA |
| Diseño | Paper | 01M0CDJZPXVWCVYZC5Z5V9ZRQE |
| Repo | /Users/cristian/orca/matiz-app/ | GitHub Criscode2022/matiz-app |
Stack fijo del cron (D-P0-03). No React/Next/Firebase.
[COMPROBADO] Puerto 3019, Neon, Paper y repo constan en el encargo.
2. Estructura
matiz-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 :3019 |
| start / web | Arranca Angular en :4200 |
3. API pública vs JWT
| Método | Ruta | Auth |
|---|---|---|
| POST | /api/auth/login | Público |
| GET | /api/health | Público |
| GET | /api/me | JWT COLORISTA |
| GET | /api/hoy | JWT COLORISTA |
| GET | /api/clients | JWT COLORISTA |
| GET | /api/clients/:slug | JWT COLORISTA |
| POST | /api/clients/:slug/formulas | JWT COLORISTA |
| GET | /api/formulas/:id | JWT COLORISTA |
| PATCH | /api/formulas/:id | JWT COLORISTA |
CORS: origen http://localhost:4200 en local.
[COMPROBADO] Superficie API del encargo. No se añaden Must extra (/appointments, /payments, /sittings POST).
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: 40–80 fichas; demo 5 + 8 fórmulas); no cola Redis |
| JWT COLORISTA | D-P1-05: herramienta interna cerrada desde el día 1 |
| Auth-first en servidor | Source of truth = guard; el cliente no «esconde» fichas |
| Puerto 3019 | Evitar colisión con ZAGUÁN :3018 / MARGEN :3017 / CESTA :3016 |
| PWA (no nativa) | S2: móvil junto al lavacabezas; install Could L+1 |
JSONB lines | 1–4 líneas por fórmula; no merece tabla hija en v1 |
5. Variables
.env local (nunca git):
| Variable | Servicio | Descripción |
|---|---|---|
DATABASE_URL | API | Neon mute-wave-74707210 |
JWT_SECRET | API | Firma tokens |
PORT | API | opcional, 3019 |
JWT_EXPIRES | API | opcional (p. ej. 12h) |
.env.example solo placeholders.
Web: URL de API en ApiService (default http://localhost:3019/api).
6. Dependencias de producto (mínimas)
API
@nestjs/common/core/platform-express@nestjs/jwt(o verificación manual)bcrypt(obcryptjs)- cliente Postgres (
pg) o Prisma class-validator+class-transformer
Web
- Angular standalone
- Tailwind 3
- fuentes: Libre Baskerville + Nunito Sans (Google Fonts o self-host)
7. Arranque (contrato)
cd /Users/cristian/orca/matiz-app
# instalar apps independientes (npm --prefix o pnpm --filter)
# apps/api/.env → DATABASE_URL + JWT_SECRET + PORT=3019
# migrate / ensureSchema + seed
# api → http://localhost:3019
# web → http://localhost:4200
8. Lo que este stack no es
| No | Por qué |
|---|---|
| npm workspaces | AP-06 |
| Firebase Auth | Stack fijo JWT + Neon |
| Next.js / React | D-P0-03 |
| Pasarela de pago / TPV | MATIZ no cobra |
| Calendario / Booksy API | MATIZ no agenda |
| Elasticsearch | 5 fichas no lo necesitan |
| PMS de cadena | S1: 1–2 coloristas |
9. Nota sobre el repo existente
[COMPROBADO] Existe /Users/cristian/orca/matiz-app y el remoto previsto Criscode2022/matiz-app.
El scaffold contiene leftover de ZAGUÁN (listings, compare, mesa, modelos listing.ts, páginas wizard / comparar / finca). Esta suite define el contrato MATIZ; el código debe alinearse a las rutas y API de los docs 03–05, no al leftover. [DECISIÓN]
07-creative-direction.md
07 — Dirección creativa — MATIZ
1. Concepto
MATIZ es un cuaderno de capilla en pantalla: se entra a escribir un matiz y se sale con una fórmula, no una landing de «salón digital». El ground es yeso de capilla. Las sillas se apoyan como bancos. La amatista es vidriera, no un botón de marketing.
Mood elegido: chapel.
Primer instinto descartado: candlelit (ámbar × oxblood de «spa de lujo»). Un salón de Usera no es un spa; es un oficio con luz de vidriera y pizarra.
[COMPROBADO] Mood, ground y paleta salen de docs/00-day-brief.md.
2. Territorio visual
| Eje | Sí | No |
|---|---|---|
| Superficie | Yeso #E6E4ED, lino de card, pizarra | Hormigón ZAGUÁN, papel MARGEN, musgo CESTA, cromo RONDA, agua DERIVA |
| Tipo | Libre Baskerville display + Nunito Sans UI | Instrument Serif, Sora, Cormorant, Public Sans, Newsreader, Outfit, Spectral, Manrope, Zilla, Mulish, Lora, Karla |
| Foto | Retrato de ficha, luz quieta, oficio | Stock de spa, «pareja feliz con mechas», lookbook de pasarela |
| Grid | Bento de sillas + editor de receta | Hero 2-col + 3 cards de features (AP-12) |
| Acento | Amatista #6B4C9A en tab activo y Guardar | Óxido inmobiliario, cadmio editorial, ámbar de vela |
| Chrome | Bottom tabs Hoy · Fichas · Cuenta | Masthead de periódico, rail, wizard 1—2—3, sidebar CRM |
3. Paleta
| Token | Hex | Rol |
|---|---|---|
| Yeso / G-CHAPEL | #E6E4ED | Ground de página. Violeta frío, no beige. |
| Pizarra | #2B2838 | Texto, wordmark, cuerpo. Casi negro, no púrpura saturado. |
| Amatista | #6B4C9A | Tab activo, acento, CTA Guardar, aviso 30 vol |
| Lino | #F7F5FA | Solo cards (silla, ficha, línea). Nunca el fondo de página. |
| Peltre | #6E6A78 | Meta, labels, tab idle, matiz, minutos |
| Hoja de oro | #C4A35A | Acento secundario, poco: badge SAVED, filete de success |
Jerarquía de color: el yeso carga; la amatista puntúa; el lino encuadra la ficha. Si la amatista pinta fondos enteros, deja de ser vidriera y se vuelve «brand purple». El oro no decora el header: solo puntual. [DECISIÓN]
4. Tipografía
| Uso | Familia | Tamaños de referencia |
|---|---|---|
| Wordmark | Libre Baskerville 700 | 24–28 desktop · 22 mobile |
| Titular de Hoy / ficha | Libre Baskerville 700 | 32–40 |
| Nombre de clienta | Libre Baskerville 700 | 24–32 desktop · 22 mobile |
| Gramos / oxidante | Nunito Sans 700 | 20–24 |
| Cuerpo de notas | Nunito Sans 400 | 16–17 / 26–28 lh |
| UI, tabs, form, lista | Nunito Sans 400/600/700 | 14 / 16 |
| Meta (matiz, silla, fecha) | Nunito Sans 600 | 12–13, tracking 0.04em, peltre |
Prohibido: Libre Baskerville por debajo de 20px (se rompe). Nunito Sans en el wordmark (se vuelve startup).
Fuentes vetadas de la serie: Instrument Serif / Sora / Cormorant / Public Sans / Spectral / Manrope / Zilla / Mulish / Newsreader / Outfit / Lora / Karla.
5. Fotografía
Las imágenes del case son el sistema, no un moodboard:
| Asset | Lectura visual | Uso |
|---|---|---|
hero.jpg | Capilla / vidriera / oficio quieto | Cover UX · atmósfera, no hero de marketing |
clients/marisol.jpg | Marisol, 47, honey | Ficha + silla 1 |
clients/lucia.jpg | Lucía, 29, blue-black | Ficha |
clients/amina.jpg | Amina, 38, copper | Ficha + silla 2 |
clients/pilar.jpg | Pilar, 61, silver | Ficha + silla 3 |
clients/diego.jpg | Diego, 33, ashy brown | Ficha + tile última fórmula |
Crop: fichas y tiles en 1:1 (retrato). No recorte 16:10 editorial ni 4:3 de fachada. [DECISIÓN] 1:1 de oficio, distinto del 4:3 de ZAGUÁN y del 16:10 de MARGEN.
Tratamiento: saturación real, sin LUT «salon gold», sin piel HDR. La amatista no se superpone como filtro: vive en UI.
Swatches del bento (H-BENTO): círculos 24–32px con hex aproximados de oficio (miel, cobre, plata), no fotografías recortadas en círculo. [DECISIÓN]
6. Layout
Bottom tabs (S-BOTTOM)
┌─────────────────────────────────┐
│ (contenido · yeso) │
├─────────────────────────────────┤
│ Hoy Fichas Cuenta │ 56–64px + safe-area
└─────────────────────────────────┘
Tab activo: Nunito 12 amatista + filete 2px amatista arriba del ítem.
Idle: peltre. Sin iconos outline de tijera si el label basta; si hay icono, trazo 1.5 pizarra/amatista.
Home (H-BENTO)
- Wordmark + fecha civil («Miércoles 19»).
- Lead en el fold: 3 tiles de silla (nombre Libre, silla meta, matiz peltre, swatch).
- Tile de última fórmula (Diego · 6.1 + 7.1 · 20 vol).
- Aviso oxidante si aplica (Amina, 30 vol).
No hay portada fotográfica a 60vh. hero.jpg vive en UX-00 Cover, no como hero de producto. [DECISIÓN] AP-21: 3 sillas reales en el fold.
Editor (F-EDIT)
Una columna. Líneas apiladas (marca · tono · gramos). Chips de oxidante. Minutos. Notas. CTA amatista Guardar. Disclaimers encima del CTA.
Desktop 1440: editor + staff de ficha. Misma receta, no otro producto.
Ficha
Retrato 1:1 a ~160–200px, no full-bleed. Nombre → matiz → historial. Blancos de capilla, no de landing.
Cuenta
Lista densa sobre yeso. No imita el bento. Es trastienda: salón, rol, salir.
7. Motion
| Superficie | Spec |
|---|---|
| Hover tile | Rule pizarra; no elevación 8px ni scale 1.04 |
| Cambio de tab | Fade 160ms; off si reduced-motion |
| Skeleton | Pulso 1.2s en peltre/lino; off si reduced-motion |
| Guardar | Badge oro, no confetti ni «gotas de tinte» |
Prohibido: parallax de vidriera, scroll-jacking, page transitions de 400ms, sticker de «NUEVO» parpadeante, animación de mechas.
8. Anti-clichés (serie ALS-2)
| Cliché | Por qué no |
|---|---|
| Spa candlelit ámbar × oxblood | Primer instinto del brief; vetado |
| Hormigón + óxido (ZAGUÁN) | Finca, no oficio de color |
| Papel + cadmio kiosco (MARGEN) | Editorial, no tazón |
| Botanical / musgo (CESTA) | Despensa |
| Chrome / neón (RONDA) | Gaming |
| «Pareja feliz con mechas» stock | Mentira social del «ya eres otra» |
| Booksy rosa + slot | Rompe S3 / H2 |
| Iconos outline de tijera 2014 | Chrome tipográfico basta |
9. Tono de marca (visual + verbal, puente a doc 09)
Cercano de oficio y de lavacabezas. Tuteo. Segunda persona cuando habla la UI. Tercera cuando habla la ficha.
El producto se escribe y se guarda. No agenda, no cobra, no vende el bote de la caja.
10. Criterios de aceptación creativos
- Ground de página =
#E6E4ED. Cards =#F7F5FA. Nunca al revés. - Amatista en el tab activo y en el CTA, no un header púrpura lleno.
- 3 sillas visibles en el primer viewport mobile (tras el wordmark).
- Ninguna fuente de la lista vetada.
- Home reconocible como bento de Hoy, no como hero 2-col de startup.
- Success no celebra un «color aplicado» ni un cobro.
- Oro solo en badge / filete de success, no en el chrome.
08-design-system.md
08 — Design system — MATIZ
1. Tokens
--color-chapel: #E6E4ED; /* G-CHAPEL ground */
--color-slate: #2B2838;
--color-amethyst: #6B4C9A;
--color-linen: #F7F5FA; /* cards only — lino */
--color-pewter: #6E6A78;
--color-gold: #C4A35A; /* acento secundario */
--color-saved: #C4A35A; /* badge SAVED */
--font-display: "Libre Baskerville", "Iowan Old Style", serif;
--font-ui: "Nunito Sans", system-ui, sans-serif;
--radius-card: 4px; /* lino de ficha, no pill */
--radius-cta: 4px;
--rule-tab: 2px solid var(--color-amethyst);
--space: 4 / 8 / 12 / 16 / 24 / 32 / 48 / 64;
Tailwind (referencia):
| Key | Valor |
|---|---|
colors.chapel | #E6E4ED |
colors.slate | #2B2838 |
colors.amethyst | #6B4C9A |
colors.linen | #F7F5FA |
colors.pewter | #6E6A78 |
colors.gold | #C4A35A |
fontFamily.display | Libre Baskerville |
fontFamily.sans | Nunito Sans |
[COMPROBADO] Hex y tipo = brief.
2. Escala tipográfica
| Token | Familia | Size / lh | Uso |
|---|---|---|---|
display-l | Libre 700 | 40 / 44 | Titular Hoy desktop · nombre ficha desktop |
display-m | Libre 700 | 28 / 32 | Nombre tile · wordmark |
display-s | Libre 700 | 22 / 28 | Nombre 390 |
grams | Nunito 700 | 20 / 24 | Gramos · volúmenes |
body | Nunito 400 | 16 / 26 | Notas, disclaimers |
ui | Nunito 400 | 16 / 24 | Form, lista, editor |
meta | Nunito 600 | 13 / 18 | Silla, matiz, fecha |
tab | Nunito 700 | 12 / 16 · tracking 0.04em | Hoy · Fichas · Cuenta |
3. Componentes
Bottom tabs (S-BOTTOM)
| Spec | Valor |
|---|---|
| Alto 390 | 56px + safe-area |
| Alto desktop | 64px fijo inferior |
| Fondo | Lino / yeso con rule 1px peltre 30% |
| Tab activo | Nunito 12 · amatista + filete 2px amatista arriba |
| Tab idle | Peltre |
| Targets | ≥44×44; 3 columnas iguales |
Slots: Hoy / · Fichas /fichas · Cuenta /cuenta.
Login no lleva tabs.
Tile de silla (Hoy)
┌─────────────────────────────┐
│ ① Marisol Vega │ nº silla meta + nombre Libre
│ honey balayage ● │ matiz peltre + swatch
└─────────────────────────────┘
- Fondo lino, sin sombra.
- Toda la tile es un
<a>a/fichas/:slug. - Hover: rule 1px pizarra. Sin lift.
- Swatch 24px,
aria-hidden(el matiz ya está en texto).
Tile última fórmula
Mismas reglas. Muestra clienta, resumen de líneas (6.1 + 7.1), oxidante, fecha. Enlace a /fichas/:slug.
Aviso oxidante
Banner lino + filete amatista 2px izquierda. Texto Nunito 16. No es alert agresivo; es oficio.
Fila de ficha (lista)
| Columna | Contenido |
|---|---|
| Cover | 48×48 1:1, radius 4 |
| Nombre | Libre 22 / Nunito 16 en 390 |
| Matiz | meta peltre |
| Fecha | meta |
Toda la fila es un <a> a /fichas/:slug.
Ficha de clienta
| Bloque | Spec |
|---|---|
| Columna | max-width 720px |
| Cover | 160–200px 1:1 |
| Nombre | display-l / display-s |
| Matiz + edad | meta |
| Historial | lista: fecha, oxidante, líneas, notas |
| Disclaimer | «La ficha no es una cita. MATIZ no agenda.» |
| CTA | Nueva fórmula · primary amatista |
Editor (F-EDIT)
| Pieza | Spec |
|---|---|
| Línea | 3 campos: marca (flex), tono (8ch), gramos (6ch + «g») |
| Añadir / quitar | Ghost; quitar disabled si queda 1 |
| Oxidante | 3 chips radio 10 / 20 / 30 vol · target ≥44 |
| Minutos | input number + «min» |
| Notas | textarea 4+ filas |
| Guardar | primary amatista, h-44, full-width en 390 |
| Disclaimers | 2 bloques body sobre el CTA |
Desktop 1440: grid 2 col (editor 7 / staff 5). Staff: retrato, nombre, último matiz, disclaimer.
Botones
| Variante | Uso | Spec |
|---|---|---|
| Primary | Guardar, Entrar, Nueva fórmula | Fondo amatista, texto lino, h-44, radius 4 |
| Secondary | Añadir línea, ver ficha | Rule pizarra, texto pizarra, fondo yeso |
| Ghost | Salir, Reintentar, Quitar línea | Texto peltre |
| Danger | — | No hay borrado Must |
Pending: disabled + aria-busy. No spinner de marca.
Form
Labels visibles (no placeholder-only). Inputs h-44, radius 4, rule peltre 40%, foco outline 2px amatista offset 2.
Chips de oxidante: <button aria-pressed>. Selected: amatista texto o rule 2px amatista. Idle: lino + rule.
Estados
| Estado | Receta |
|---|---|
| Empty Hoy | Titular Libre 28 + 1 línea Nunito + sin CTA de agenda |
| Empty Fichas | Titular + (seed no debería; útil si se vacía) |
| Empty historial | «Aún no hay fórmulas.» + CTA Nueva |
| Error | Banner filete amatista + texto + Reintentar |
| Loading | Skeleton bento: 3 tiles + 1 barra; lista: 5 filas |
| 404 ficha | «Esa ficha no está.» + link a Fichas |
4. Grid y breakpoints
| Viewport | Comportamiento |
|---|---|
| ≥1200 | Hoy bento 3+1 tiles; editor 2 col; fichas lista |
| 960–1199 | Bento 2+2; editor 2 col compacto |
| 600–959 | Bento 1 col; editor 1 col; tabs inferiores |
| ~390 (PWA) | 1 col; tiles full; editor full; tabs + safe-area |
Padding horizontal: 24 desktop, 16 mobile.
Main autenticado: padding-bottom: calc(64px + env(safe-area-inset-bottom) + 16px) para no tapar el CTA.
5. Iconografía
Mínima. El cuaderno no es un set de iconos outline de tijera.
| Sitio | Recurso |
|---|---|
| Tabs | Texto. Icono trazo 1.5 de 20px opcional (banco / ficha / persona) |
| Swatch | Círculo de color, no icono |
| Empty | Nada o una rule amatista de 32px |
| Status | Texto de badge SAVED, no semáforo |
Si hace falta un icono (salir, error): trazo 1.5, 20px, pizarra. No pack Remix/Lucide a color.
6. Elevación y borde
| Superficie | Tratamiento |
|---|---|
| Página | Yeso plano |
| Card / tile | Lino plano + rule 1px peltre 30% |
| Lista | Filas separadas por rule |
| Modal | No hay modal Must |
Cero sombras shadow-lg. La capilla no flota.
7. Mapa componente → ruta
| Componente | Rutas |
|---|---|
BottomTabsComponent | /, /fichas, /fichas/:slug, /fichas/:slug/formula, /ok/:id, /cuenta |
ChairTile | / |
LastFormulaTile | / |
OxidantWarning | / |
ClientRow | /fichas |
FichaArticle | /fichas/:slug |
FormulaEditor | /fichas/:slug/formula |
LineRow | editor |
EmptyState / ErrorBanner / SkeletonBento | /, /fichas |
8. Criterios de aceptación DS
- Tokens Tailwind = hex del brief, sin «casi blanco»
#FAFAFAcomo ground. - Cards en lino; página en yeso.
- Primary CTA nunca en peltre sobre yeso (contraste).
- Libre Baskerville no aparece en labels de form ni en tabs.
- Recorte de ficha
aspect-square, no 16:10 editorial ni 4:3 de finca. - Targets de tabs, chips y CTA ≥44px.
- Tabs no tapan el CTA Guardar (padding-bottom).
09-content-guide.md
09 — Guía de contenido — MATIZ
1. Voz
Cercana de oficio y de lavacabezas. Tuteo en UI. Segunda persona cuando la interfaz habla. Tercera cuando habla la ficha.
Sin jerga de spa («experiencia cromática», «tu mejor versión», «no te lo pierdas»). Sin sede electrónica. Sin «reserva tu slot».
El producto se escribe y se guarda. No agenda, no cobra, no vende el bote de la caja.
2. Palabras permitidas / prohibidas
| Usar | No usar |
|---|---|
| Fórmula / líneas / gramos | Receta de caja, «el bote», «kit» |
| Ficha / clienta / cliente | Cita, slot, «appointment», paciente |
| Silla / hoy | Agenda, reserva, Booksy, «próxima hora» |
| Guardar / guardada | Aplicar, «ya está en el pelo», «listo el color» |
| Oxidante 10 / 20 / 30 vol | «El revelador de la caja», «peróxido» en UI |
| Cuaderno / oficio | Inbox, CRM, backoffice, TPV |
| Colorista | Stylist, artist, «beauty pro» |
| MATIZ no agenda / no cobra | Pide cita, pagar ahora, ticket |
3. Microcopy duro (literales irrenunciables)
El brief fija oposiciones. Aparecen donde el usuario puede confundirse.
| Oposición | Dónde | Copy |
|---|---|---|
| Fórmula ≠ receta comercial | Editor sobre las líneas · ficha | «Esto es una fórmula, no la receta de la caja. 6/7 + 20 vol no es el bote.» |
| Ficha ≠ cita | Ficha bajo el nombre · Hoy pie | «La ficha no es una cita. MATIZ no agenda.» |
| Guardado ≠ aplicado | Editor sobre Guardar · success | «Guardado no es aplicado. El color aún no está en el pelo.» |
| MATIZ no agenda | Hoy empty · cuenta · ficha | «MATIZ no agenda. El WhatsApp de Inma agenda.» |
| MATIZ no cobra | Cuenta · success pie | «MATIZ no cobra. El ticket lo hace la caja.» |
[DECISIÓN] Los cinco textos son obligatorios en UI. No se diluyen a un tooltip.
4. Microcopy por superficie
| Superficie | Copy |
|---|---|
| Eslogan | La fórmula, no la cita. |
| Línea de salón | Salón Amparo · Calle Amparo 22, Usera |
| Login titular | Entrar al cuaderno |
| Login ayuda | Solo colorista. No hay cuenta de clienta. |
| Login submit | Entrar |
| Login error | Email o contraseña no valen. |
| Hoy titular | Hoy |
| Hoy fecha | Miércoles 19 de agosto |
| Tile silla | Silla 1 · Marisol Vega · honey balayage |
| Tile última | Última fórmula · Diego Rivas · 6.1 + 7.1 · 20 vol |
| Aviso oxidante | Revisa el oxidante: Amina trae 30 vol. 30 no es 20. |
| Empty Hoy | Hoy no hay sillas. El hoy lo rellena Nora por la mañana. |
| Fichas titular | Fichas |
| Empty Fichas | Aún no hay fichas. |
| Ficha CTA | Nueva fórmula |
| Ficha volver | ← Fichas |
| Ficha disclaimer | La ficha no es una cita. MATIZ no agenda. |
| Empty historial | Aún no hay fórmulas en esta ficha. |
| Editor titular | Nueva fórmula |
| Editor líneas | Marca · Tono · Gramos |
| Añadir línea | Añadir línea |
| Quitar línea | Quitar |
| Oxidante | 10 vol · 20 vol · 30 vol |
| Minutos | Minutos de pose |
| Notas | Notas de oficio (no del ticket) |
| Editor disclaimer 1 | Esto es una fórmula, no la receta de la caja. |
| Editor disclaimer 2 | Guardar no es aplicar. |
| Guardar | Guardar fórmula |
| Success titular | Guardada. |
| Success cuerpo | Guardado no es aplicado. El color aún no está en el pelo. |
| Success CTA | Ver la ficha · Volver a Hoy |
| Error | No hemos podido cargar el cuaderno. |
| Error retry | Reintentar |
| Loading (doc 16) | Abriendo el cuaderno… |
| 404 ficha | Esa ficha no está. |
| Cuenta titular | Cuenta |
| Cuenta rol | Colorista |
| Cuenta pie | MATIZ no agenda. MATIZ no cobra. |
| Salir | Salir |
| Tabs | Hoy · Fichas · Cuenta |
5. Fichas seed (tono)
Las fichas demo se escriben como nota de oficio, no como folleto de marca ni como crónica de lookbook.
| Ficha | Tono |
|---|---|
| Marisol | Observación: honey, no bajar del 8, el 10.3 tiró a paja. Sin «luminoso y dorado». |
| Lucía | Blue-black, nacimiento, 10 vol. Sin «intenso y misterioso». |
| Amina | Cobre, 30 vol en la última: se avisa, no se celebra. |
| Pilar | Plata, solo matizar. Sin «plata de revista». |
| Diego | Ceniza, no calentar. Sin «cool brown editorial». |
No inventar declaraciones de clientas reales. Si hay una voz, es de la nota (neutra) o de persona de diseño etiquetada.
6. Nombres propios del seed
| Nombre | Uso |
|---|---|
| Nora Beltrán | COLORISTA; nora@matiz.salon |
| Inma Soto | DUEÑA; no es cuenta |
| Marisol Vega | Ficha marisol; silla 1; no usa la app |
| Lucía Campos | Ficha lucia |
| Amina El Idrissi | Ficha amina; silla 2 |
| Pilar Núñez | Ficha pilar; silla 3 |
| Diego Rivas | Ficha diego; última fórmula |
| Salón Amparo | Calle Amparo 22, Usera; atrezzo de persona |
No inventar CIF, número de colegiada, ni precios de servicio. [SUPUESTO de catálogo demo]
Marcas seed (Dia Light, Majirel, Koleston, Igora, Illumina) son nombres de oficio reconocibles, no recetas oficiales ni partnership. No se afirman como ficha real 2026.
7. Tono por estado
| Status | Frase UI | Frase que Nora podría decir (fuera de app) |
|---|---|---|
| SAVED | Guardada | «Está apuntada. Aún no te la he puesto.» |
| (aplicar) | — no existe | «Ahora sí: al lavacabezas.» |
La app no envía esos mensajes en v1. El copy solo prepara el habla.
8. Capitalización y números
- Wordmark: MATIZ en versales Libre Baskerville.
- Nombres: Marisol Vega, Amina El Idrissi (respetar partículas).
- Gramos:
20 g(espacio). - Oxidante:
20 vol(no «20 volúmenes» en chip; sí en aviso largo). - Minutos:
25 min. - Edad:
47junto al matiz, no «47 años de edad». - Silla:
Silla 1(mayúscula S). - Fecha:
24 junen lista;miércoles 19 de agostoen Hoy. - No mezclar «20vol» pegado ni «g.» con punto.
9. Idioma
es-ES. Fácil lectura. EN no en v1.
No mezclar «formula» en UI. En API sí: formulas, oxidant, shade.
«Clienta» en UI cuando el seed es mujer; «cliente» para Diego; «fichas» para el conjunto. [DECISIÓN]
10. Accesibilidad de copy
- No transmitir estado solo con color («la amatista significa 30 vol»).
- Badge lleva texto: Guardada.
- Aviso de oxidante lleva nombre y número, no solo el banner púrpura.
- Alt de fotos: nombre + matiz, no «imagen1».
- hero: «Luz de capilla y pizarra; oficio de color, no spa.»
- marisol: «Marisol Vega, ficha honey balayage.»
- lucia: «Lucía Campos, ficha blue-black.»
- amina: «Amina El Idrissi, ficha copper.»
- pilar: «Pilar Núñez, ficha silver.»
- diego: «Diego Rivas, ficha ashy brown.»
11. Piezas que no se escriben
| No | Motivo |
|---|---|
| «Reserva tu hueco» / «Pedir cita» | H2 · Ficha ≠ cita |
| «Ya está aplicado» como hecho de guardar | Guardado ≠ aplicado |
| «6/7 de Wella, como en la caja» | Fórmula ≠ receta comercial |
| «Pagar 42 €» / «Ticket» | MATIZ no cobra |
| Ranking de los looks más vistos | Likes, otro producto |
| Lorem / «lorem ipsum mechas» | Prohibido en el case |
| Declaraciones inventadas de un salón real | Verdad de catálogo |
10-accessibility.md
10 — Accesibilidad — MATIZ
Objetivo: WCAG 2.2 AA. No se declara conformidad legal certificada.
Este documento cubre la UI (login, Hoy, fichas, editor, cuenta). No audita la accesibilidad física del Salón Amparo (lavacabezas, umbral, silla). [SUPUESTO]
1. Decisiones
| Tema | Decisión |
|---|---|
| Tipo UI | Nunito Sans ≥16px body |
| Display | Libre Baskerville no se usa por debajo de 20px |
| Contraste | Pizarra #2B2838 sobre #E6E4ED / #F7F5FA |
| CTA amatista | Texto lino sobre #6B4C9A |
| Badge SAVED | Texto pizarra sobre oro claro o oro sobre lino ≥14px bold |
| Foco | Outline 2px amatista, offset 2px, visible (no outline-none global) |
| Tabs | <nav> + aria-current="page" |
| Editor | Labels visibles; chips aria-pressed; líneas en <fieldset> |
| Imágenes | alt = nombre + matiz |
| Form | Labels visibles, no placeholder-only |
| Errores | Texto, no solo color |
| Mobile | Targets ≥44px (tabs, chips, Guardar) · S2 manos húmedas |
| Movimiento | Fade 160ms; prefers-reduced-motion: reduce → 0 |
| Live regions | aria-live="polite" en empty/error de Hoy y editor |
2. Contraste (comprobación de diseño)
| Par | Uso | Nota |
|---|---|---|
#2B2838 / #E6E4ED | Body sobre yeso | Pasa AA y AAA cuerpo |
#2B2838 / #F7F5FA | Body sobre lino | Pasa AA y AAA |
#F7F5FA / #6B4C9A | CTA / tab activo | Pasa AA para texto ≥14px bold |
#6E6A78 / #E6E4ED | Meta peltre | Verificar ≥13px; no usar en 11px |
#6E6A78 como texto 12px sobre lino | — | Prohibido para body |
| Tab idle peltre 50% | Solo si no es el único indicador | + label textual Hoy / Fichas / Cuenta |
#2B2838 / #C4A35A | Badge SAVED | Verificar bold ≥14; si falla, pizarra sobre lino + rule oro |
3. Teclado
| Flujo | Orden |
|---|---|
| Login | Email → password → submit |
| Hoy | Wordmark → tiles silla 1–3 → última fórmula → aviso |
| Fichas | Filas en orden |
| Ficha | Atrás → Nueva fórmula → historial (lectura) |
| Editor | Atrás → líneas (marca, tono, gramos, quitar) → añadir → oxidante → minutos → notas → Guardar |
| Cuenta | Salir |
| Tabs | Al final del documento o landmark navigation; Tab cicla Hoy / Fichas / Cuenta |
- Enter en tile = navegar a ficha.
- Enter en chip oxidante = selecciona (
aria-pressed). - Escape no cierra el editor (no es modal). Confirm de descarte no es Must.
- Focus trap: no hay modal Must.
4. Semántica
| Pieza | Markup |
|---|---|
| Tabs | <nav aria-label="Cuaderno"> · activo aria-current="page" |
| Hoy | <section aria-label="Sillas de hoy"> |
| Tile silla | un <a> por silla; incluye nombre, silla, matiz en texto |
| Swatch | aria-hidden="true" (decorativo) |
| Aviso oxidante | role="status" + texto completo |
| Oxidante | role="group" + aria-label="Oxidante" |
| Líneas | <fieldset> + <legend> «Líneas de color» |
| Gramos | <label> + input type="number" + texto «gramos» |
| Disclaimer | No es alert; es texto normal + borde |
| 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 |
|---|---|
| Tile | «Silla 1, Marisol Vega, honey balayage» |
| Chip | «20 vol, pulsado» / «30 vol, no pulsado» |
| Empty Hoy | «Hoy no hay sillas. El hoy lo rellena Nora por la mañana.» |
| Error | Anuncio en live region; foco al banner |
| Success | «Guardada. Guardado no es aplicado.» |
| Aviso | «Revisa el oxidante: Amina trae 30 volúmenes. 30 no es 20.» |
6. Producto vs salón físico
| Lo que MATIZ puede hacer | Lo que no afirma |
|---|---|
| Targets 44px junto al lavacabezas | Que el lavacabezas de Amparo 22 sea accesible de hecho |
| Editor usable con teclado y zoom 200% | Que el local tenga rampa |
| Auth-first para no exponer fichas | Que Inma haya hecho un DPIA |
[DECISIÓN] v1 no incluye auditoría a11y de sede. El campo age es dato de ficha demo, no un certificado.
7. Riesgos
| Riesgo | Mitigación |
|---|---|
| Tabs tapan Guardar en 390 | padding-bottom del main |
| Libre Baskerville en meta 12px | Prohibido; meta = Nunito 13 |
| Amatista como único estado de tab | + aria-current="page" + label |
| Foto sin alt | Checklist seed: 5 alts + hero |
| Chip 30 vol leído solo por color | Label «30 vol» siempre |
| Manos húmedas (S2) | Targets ≥44; no gestos swipe; no hover-only |
| Input number sin unidad | «g» y «min» visibles junto al campo |
8. QA a11y (mínimo)
| # | Prueba | Pasa si |
|---|---|---|
| 1 | Teclado login → Hoy → ficha → editor → ok | Sin trampa; foco visible |
| 2 | Teclado tabs Hoy / Fichas / Cuenta | Acciones alcanzables |
| 3 | Zoom 200% móvil 390 | Chips usables; no solapa inputs; tabs no tapan CTA |
| 4 | Lighthouse a11y ≥ 90 en / autenticado | Sin contrast fails de tokens |
| 5 | VoiceOver/NVDA: tile anunciada | Nombre, silla, matiz comprensibles |
| 6 | prefers-reduced-motion | Sin stagger ni slide |
| 7 | Contraste badge SAVED y CTA | Texto bold ≥14 |
9. Criterios de aceptación
- Ningún control de tab, chip o CTA mide menos de 44×44 CSS px.
- El disclaimer no depende del color amatista para ser entendido.
- Las 5 fotos seed + hero tienen
altno vacío y no genérico. - El foco no se pierde al reintentar un error de red.
- En 390 el CTA Guardar queda por encima de los tabs.
- Una ficha no se anuncia en Hoy sin token (no hay HTML público de clientas).
11-privacy-security.md
11 — Privacidad y seguridad — MATIZ
1. Datos que existen
| Dato | Dónde | Base / nota |
|---|---|---|
| Email y hash de la colorista | users | Contrato interno del cuaderno |
| Fichas (nombre, edad, matiz, foto) | clients | Catálogo de producto; seed demo · PII de atrezzo |
| Fórmulas (líneas, oxidante, notas) | formulas | Oficio; no es historial clínico certificado |
| Sillas de un día | sittings | Asiento, no cita médica |
| Sesión JWT | Authorization + sessions? | Solo COLORISTA |
No se pide cuenta a la clienta. No hay teléfono, email ni DNI de Marisol en producto. No hay tracking de menores. Las fotos seed no se presentan como clientela real.
2. Minimización
| Qué no se recoge en v1 | Por qué |
|---|---|
| Teléfono / email de la clienta | H3 · no lead · no recordatorio |
| DNI / alergias / ficha sanitaria | MATIZ no es historial clínico |
| Geolocalización del dispositivo | El salón es uno |
| Hora de la cita | Ficha ≠ cita |
| Importe / tarjeta | MATIZ no cobra |
| Fotos subidas por la clienta | No hay UGC |
Edad en seed es dato de ficha demo, no un requisito de negocio. L+1 podría omitirla. [DECISIÓN]
3. Seguridad L2
| Control | Spec |
|---|---|
| Auth | JWT Bearer · role === COLORISTA |
| Password | bcrypt cost 12; nunca en response |
| Lectura | GET hoy / clients / formulas / me → 401 sin token |
| Escritura | POST / PATCH formulas → 401 sin token |
| Rate limit | /auth/login (anti stuffing; Should si no llega al slice) |
| CORS | Origen http://localhost:4200 en local |
| Secretos | Solo .env (no git) |
| Cover allowlist | Filenames seed; no URL arbitraria |
next post-login | Paths relativos allowlist (/, /fichas, /fichas/:slug, /fichas/:slug/formula, /ok/:id, /cuenta) |
| Auth-first web | Guard Angular en todas las rutas salvo /login |
No se declara cumplimiento RGPD definitivo. Sí: minimización, auth-first y derecho a borrar la cuenta de colorista (manual L2). Las fichas seed se tratan como datos de demo, no como encargo de tratamiento de un salón real.
4. Superficies de riesgo
| Riesgo | Mitigación v1 |
|---|---|
| Enumeración de slugs | 401 sin token; 404 con token si no existe (no 403 que filtre) |
| JWT en localStorage | Aceptable en demo; fichas son atrezzo, no clientela real |
Foto de clienta en public/ | Inevitable para el seed; no hay cara pública que las liste sin auth. HTML autenticado |
Leftover listings / cuentas ZAGUÁN en DB | I01 de build: schema propio, no mezclar |
| Notas de fórmula con PII extra | Copy: «notas de oficio»; no pedir teléfono |
| Inma pide un login | S4: no hay fila; no se crea |
5. Roles y autorización (recordatorio)
| Recurso | Anónimo | COLORISTA |
|---|---|---|
| POST login / GET health | Sí | Sí |
| GET me / hoy / clients / :slug / formulas | 401 | Sí |
| POST / PATCH formulas | 401 | Sí |
| Agenda / cobro | No existe | No existe |
Inma, Marisol, Lucía, Amina, Pilar y Diego no tienen fila en users.
6. Criterios de aceptación
- Response de login no incluye
passwordnipassword_hash. - GET
/api/clientsy GET/api/hoysin Bearer son 401, no 200 []. - GET
/api/clients/marisolsin Bearer es 401, no 200. - No hay formulario público que pida teléfono o email de clienta.
/sin token redirige a/login(no renderiza sillas en HTML anónimo)..envno se commitea.
12-analytics.md
12 — Analítica — MATIZ
North star: % de sillas de hoy con fórmula guardada el mismo día (hoy_view con sittings + formula_save ligado a clientSlug de esas sillas).
Instrumentación modelo. No hay proveedor conectado en v1 salvo que el build añada un logger local. [DECISIÓN] No se inventan cifras de conversión.
1. Eventos
| Evento | Cuándo | Props mín. |
|---|---|---|
colorista_login | JWT ok | — |
hoy_view | Render de / autenticado | sittings (0–3) |
ficha_open | Apertura de ficha | slug |
editor_open | Entrada a /fichas/:slug/formula | slug |
formula_save | POST 201 | id, slug, oxidant, lineCount |
hoy_empty | 0 sittings | — |
error_banner | UI-09 | surface |
2. Qué no se mide
| No evento | Motivo |
|---|---|
signup_click / lead_submit | No existe CTA |
appointment_booked | Ficha ≠ cita |
payment_ok | MATIZ no cobra |
formula_applied | Guardado ≠ aplicado; el evento no existe |
like / favorite | Fuera |
| Identidad de Marisol como usuaria | H3 · no es usuaria |
Hipótesis H2: ficha_open sin appointment_click (el CTA no existe). Si en un test de 5 coloristas alguien busca «hora», se anota en research futuro, no como KPI inventado.
3. Funnel modelo (COLORISTA)
colorista_login → hoy_view → ficha_open → editor_open → formula_save
Activación = primer formula_save.
Completud del día = formula_save cuyo slug ∈ slugs de hoy_view.sittings / nº de sittings.
North star de sesión de demo: de 3 sillas, cuántas tienen un formula_save con createdAt del mismo día civil Europe/Madrid.
4. Integridad (producto, no vanidad)
| Señal | Cómo se observa |
|---|---|
| Auth-first | GET /hoy 401 sin token (QA, no analytics) |
| Expectativa de cita | Comentarios cualitativos; cero evento de reserva |
| Expectativa de aplicado | Cero evento applied; copy en success |
| Salud | 4xx/5xx de hoy / formulas |
5. Privacidad de analítica
Sin PII extra. slug es identificador de seed (marisol), no un email. No se guarda IP como usuario. Ver doc 11.
No se envían notes ni lines completas a un tercero.
6. Criterios de aceptación (si se instrumenta)
- Los 7 eventos de §1 tienen nombre estable.
- No se dispara ningún evento de lead / cita / cobro / aplicado.
- Un bloqueador de cookies no rompe el editor (analítica no es Must de runtime).
13-qa-test-plan.md
13 — Plan de pruebas — MATIZ
1. Smoke obligatorio (D-P1-06)
| # | Acción | Esperado |
|---|---|---|
| 1 | GET /api/health | 200 |
| 2 | POST /api/auth/login nora@matiz.salon / password123 | 200 · accessToken · user.role=COLORISTA |
| 3 | GET /api/hoy con Bearer | 200 · 3 sittings · chairs 1 Marisol, 2 Amina, 3 Pilar |
| 4 | GET /api/clients con Bearer | 200 · 5 slugs marisol, lucia, amina, pilar, diego |
| 5 | ng build o serve web | Web arranca o build OK |
[COMPROBADO] Smoke = health + login JWT + hoy + clients.
2. Casos funcionales
| ID | Caso | Esperado |
|---|---|---|
| S1 | Slugs seed | GET clients incluye los 5 |
| S2 | Hoy sin JWT | 401 (no 200 []) |
| S3 | Clients sin JWT | 401 |
| S4 | Clients/:slug sin JWT | 401 |
| S5 | Slug inexistente (con JWT) | 404 |
| S6 | Hoy UI | / muestra 3 sillas (nombre, nº, matiz) + última fórmula + aviso; cero CTA Agendar / Cobrar |
| S7 | Ficha AC | nombre, matiz, historial, «ficha ≠ cita» |
| S8 | Home es bento | / autenticado es H-BENTO; no hero 2-col |
| S9 | Rutas auth | /, /fichas, /cuenta sin token → /login |
| S10 | POST fórmula | 201 · status=SAVED · lines persistidas |
| S11 | PATCH fórmula | 200 · notes/líneas cambian; status sigue SAVED |
| S12 | PATCH no aplica | Body { status: "APPLIED" } ignorado o 400; no existe APPLIED |
| S13 | Login malo | 401 · mensaje en form |
| S14 | Empty Hoy | 0 sittings → UI-08 |
| S15 | API caída | UI-09 + Reintentar conserva la vista |
| S16 | Loading | Skeleton bento antes del primer paint de Hoy |
| S17 | Mobile 390 | Tabs + editor; CTA ≥44px; Guardar no tapado; no hero 2-col |
| S18 | Fotos seed | 5 covers resuelven a assets/clients/* |
| S19 | Password no viaja de vuelta | login response sin hash |
| S20 | Body POST fórmula | acepta lines[], oxidant 10/20/30, minutes, notes |
| S21 | Success | /ok/:id copy «Guardado no es aplicado» |
| S22 | GET me | 200 · Nora · Salón Amparo · Calle Amparo 22 |
| S23 | Oxidante inválido | POST oxidant 40 → 400 |
| S24 | lines vacías | POST lines: [] → 400 |
| S25 | Microcopy ×5 | fórmula≠receta · ficha≠cita · guardado≠aplicado · no agenda · no cobra |
3. Datos de prueba
| Uso | Valor |
|---|---|
| COLORISTA | nora@matiz.salon / password123 |
| Ficha feliz | marisol |
| Ficha 30 vol | amina |
| Última fórmula | diego · frm_diego_1 |
| Sillas hoy | 1 marisol · 2 amina · 3 pilar |
| POST feliz | { lines:[{brand:"Dia Light",tone:"9.3",grams:20}], oxidant:20, minutes:25 } |
| Empty Hoy | borrar sittings del día (solo en entorno de test) |
4. Regresión de diversidad / craft
| Check | Falla si |
|---|---|
| Home es bento | Hay hero 2-col + 3 cards de marca (AP-12) o wizard (ZAGUÁN) o lookbook (MARGEN) |
| Shell es S-BOTTOM | Hay masthead, rail, chrome de pasos 1—2—3 o command search como nav primaria |
| Copy honesto | Success dice «aplicado» o ficha dice «cita» o editor dice «receta de la caja» o hay «pagar» |
| Paleta | Se cuelan óxido ZAGUÁN / cadmio MARGEN / musgo CESTA / ámbar spa |
| Tipo | Instrument Serif / Sora / Cormorant / Public Sans / Newsreader / Outfit / Spectral / Manrope / Zilla / Mulish / Lora / Karla |
| Rutas | Aparecen /cita, /agenda, /cobrar, /comparar, /mesa, /visita |
| Ground | Página en lino #F7F5FA en vez de yeso #E6E4ED |
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 (editor, Hoy con 3 sillas, disclaimer de cita, ausencia de signup clienta, 401 público).
6. Criterios de salida QA v1
- Smoke §1 en verde.
- S2, S6, S8, S10, S17, S21, S25 en verde.
- Cero blockers de copy deshonesto (cita / aplicar / receta de caja / cobrar / agendar).
- Sin
.enven el repo.
7. Fuera de este plan
Playwright e2e completo, carga, fuzzing, auditoría de un salón real.
14-dev-handoff.md
14 — Handoff desarrollo — MATIZ
1. Arranque
cd /Users/cristian/orca/matiz-app
# apps independientes (npm --prefix o pnpm --filter)
# apps/api/.env
# DATABASE_URL= # Neon mute-wave-74707210
# JWT_SECRET=
# PORT=3019
# migrate / ensureSchema + seed
# api → http://localhost:3019
# web → http://localhost:4200
Demo: nora@matiz.salon / password123.
2. Paridad Paper
| Debe verse en Angular | Artboard |
|---|---|
| Tokens chapel + Libre Baskerville / Nunito Sans | UI-00 |
| Login COLORISTA | UI-01 |
| Hoy bento 3 sillas + última + aviso | UI-02 |
| Fichas | UI-03 |
| Ficha + historial | UI-04 |
| Editor móvil | UI-05 |
| Editor desktop 1440 | UI-06 |
| Success «guardado ≠ aplicado» | UI-07 |
| Empty | UI-08 |
| Error | UI-09 |
| Loading skeletons | UI-10 |
| Cuenta | UI-11 |
No clonar ZAGUÁN (wizard + comparador), MARGEN (lookbook + masthead), CESTA (tabla + rail), RONDA (feed + dock) ni CANTERA (parte + wizard de oficio).
Paper se construye en paralelo; los nombres de artboard son el inventario planificado (doc 00).
3. Contratos
| Tema | Contrato |
|---|---|
| Login | { accessToken, user } camelCase (no access_token) |
| Me | GET /api/me Bearer |
| Hoy | GET /api/hoy → { day, sittings, lastFormula, oxidantWarning } |
| Clients | GET /api/clients · GET /api/clients/:slug |
| Alta fórmula | POST /api/clients/:slug/formulas · body lines, oxidant, minutes, notes |
| Fórmula | GET / PATCH /api/formulas/:id |
| Health | GET /api/health |
| Status enum | SAVED |
| Oxidante enum | 10 | 20 | 30 |
| Role | COLORISTA (no AGENT, no EDITOR) |
Campos JSON en camelCase hacia el web aunque SQL sea snake_case. [DECISIÓN]
4. Mapa de rutas web
| Path | Guard cliente | Página |
|---|---|---|
/login | no (si hay token → /) | Login |
/ | auth | Hoy |
/fichas | auth | Lista |
/fichas/:slug | auth | Ficha |
/fichas/:slug/formula | auth | Editor |
/ok/:id | auth | Success |
/cuenta | auth | Cuenta |
Guard: si no hay token, /login?next=. La API es la autoridad.
[COMPROBADO] Solo estas rutas. No implementar /cita, /agenda, /cobrar, /comparar, /mesa.
5. Assets
Copiar desde el case:
2026-08-19-matiz/assets/hero.jpg
2026-08-19-matiz/assets/clients/marisol.jpg
2026-08-19-matiz/assets/clients/lucia.jpg
2026-08-19-matiz/assets/clients/amina.jpg
2026-08-19-matiz/assets/clients/pilar.jpg
2026-08-19-matiz/assets/clients/diego.jpg
→ apps/web/public/assets/clients/ (hero a public/assets/)
cover del seed = filename (marisol.jpg, …).
Quitar leftover public/assets/listings/* y páginas wizard / comparar / mesa si siguen en el repo.
6. DoD implementación L2
| Check | OK |
|---|---|
| API sirve :3019 | |
| Seed 1 COLORISTA + 5 clients + 8 formulas + 3 sittings | |
| Smoke health + login + hoy + clients | |
| JWT protege hoy / clients / POST / PATCH | |
| Web login + Hoy + fichas + ficha + editor + ok + cuenta | |
| Microcopy ×5 | |
| Tokens Tailwind = paleta brief | |
| Home es bento (no leftover wizard / comparador) | |
| Bottom tabs Hoy · Fichas · Cuenta | |
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 cita, aplicación o cobro | Microcopy duro / AP-09 |
Estado APPLIED / SEATED / PUBLISHED / DRAFT | Vocabulario de otros días / mentira de aplicado |
Role AGENT / EDITOR | Eso es ZAGUÁN / MARGEN |
| Search bar o wizard como home | Rompe H-BENTO |
| Hero 2-col + 3 cards | AP-12 |
| Lookbook + masthead | MARGEN |
| Comparador 3 cols | ZAGUÁN |
| Parte de obra | CANTERA |
image_gen como captura de producto | D-P0-09 |
Body POST con zona / maxRent / listing | Leftover |
8. Contactos de diseño (artefactos)
| Artefacto | Path |
|---|---|
| Brief | ux-projects/2026-08-19-matiz/docs/00-day-brief.md |
| Paper | https://app.paper.design/file/01M0CDJZPXVWCVYZC5Z5V9ZRQE |
| 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 — MATIZ
Este documento no es un cajón de trabajo L2 a medias.
El vertical slice L2 del brief está especificado como done-scope (docs 01, 18, 19).
Aquí solo viven ideas de L+1 / L3 y pulido posterior.
1. Hecho en L2 (esta ejecución documental)
| Entrega | Evidencia |
|---|---|
| Definición producto + terna | docs 00-brief, 01 |
| Personas Nora / Inma / Marisol, JTBD, journey | docs 02 |
| IA auth-first + chrome S-BOTTOM | docs 03 |
| Flujos F-EDIT + Hoy + cuenta | docs 04 |
| Modelo users / clients / formulas / sittings + seed | docs 05 |
| Stack Angular/Nest/Neon/JWT :3019 | docs 06, 20 |
| Dirección chapel + DS | docs 07, 08 |
| Copy Fórmula ≠ receta · Ficha ≠ cita · Guardado ≠ aplicado · no agenda · no cobra | docs 09 |
| Paper ref 12 UX + 12 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 matiz-app. El alcance L2 ya está cerrado en spec; el código alinea leftover (listings / compare / mesa) al contrato.
Eso no se reabre aquí como «próximo trimestre de producto».
3. L+1 (misma familia, subida de pulido)
| Ítem | Notas |
|---|---|
| Alta de ficha nueva + cover allowlist | Sigue sin cuenta de clienta |
PATCH desde historial (?id=) | Status sigue SAVED |
| Gesto «puesta» (no resultado) | Copy: no es el color en el pelo |
| Rol DUEÑA solo lectura | S4 se relaja con brief corto |
| Rate limit login | Anti stuffing |
| Playwright S1–S25 | CI |
| API URL por environment | Deploy |
prefers-reduced-motion verificado | a11y |
| PWA install / manifest | S2 |
| Borrador local del editor | sessionStorage; no es SAVED |
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 L+1.
4. L3 (requiere brief nuevo de diversidad)
- Multi-colorista / turnos / comisiones
- Agenda / slots / F-BOOK
- TPV / ticket / pasarela
- Cuenta de clienta y «mi cita»
- Recetario oficial de marca
- Foto del resultado como UGC
- Historial clínico / alergias
- Cadena de salones
5. Explícitamente fuera (no backlog disfrazado)
| Ítem | Por qué |
|---|---|
| Reserva / slot / confirmación de cita | H2 · ≠ F-BOOK |
| Lookbook editorial / agenda de looks | ≠ MARGEN |
| Tabla de necesidades / compromiso | ≠ CESTA |
| Parte de obra / wizard de oficio | ≠ CANTERA |
| Wizard + comparador de fincas | ≠ ZAGUÁN |
| App nativa | Fuera de stack daily |
| Hero 2-col + 3 cards | AP-12 |
6. Orden de ataque si hay continuidad de código
- Contratos 05–06 + seed 05 + smoke 13 (alinear leftover)
- Web paridad UI-00…11
- Alta de ficha + PATCH historial (L+1)
- Rate limit si hay tráfico
- Evaluar brief L3 (no parche silencioso)
16-interaction-specs.md
16 — Especificación de interacción — MATIZ
1. Convenciones
| Token | Valor |
|---|---|
| Duración corta | 120–160ms |
| Easing | ease-out |
| Reduced motion | 0ms |
| Target | ≥44×44 |
| Pending | control disabled + aria-busy |
Loading (skeleton de UI-02 / UI-03) y editor desktop (UI-06) viven en este doc junto a los gestos; el inventario Paper ya reserva UI-06 y UI-10. [DECISIÓN]
2. Bottom tabs (S-BOTTOM)
| Gesto | Resultado |
|---|---|
| Tap Hoy | / · aria-current="page" |
| Tap Fichas | /fichas |
| Tap Cuenta | /cuenta |
| Tap tab actual | Scroll to top (Should); no recarga dura |
| Teclado | Tab al landmark; Enter navega |
| Scroll de contenido | Tabs fijos; no hide-on-scroll v1 |
Estado activo: label amatista + filete 2px amatista arriba.
Estado default: peltre.
390: 56px + env(safe-area-inset-bottom).
Desktop: 64px inferior centrado o full-bleed; no se convierte en top-nav.
Login no muestra tabs.
3. Login (UI-01)
| Gesto | Resultado |
|---|---|
| Submit | POST login; pending en botón |
| 200 | Guarda token + user; navega next sanitizado o / |
| 401 | Texto bajo el form; password no se limpia |
| Enter | Submit |
| Primera carga | Form visible; no skeleton de marca |
next permitido: paths que empiezan por / y no // ni http. Allowlist: /, /fichas, /fichas/:slug, /fichas/:slug/formula, /ok/:id, /cuenta.
4. Hoy bento (UI-02) + loading
| Gesto | Resultado |
|---|---|
| Primera carga | Loading: 3 tiles lino + 1 barra peltre; aria-busy |
| 200 con 3 | tiles + última fórmula + aviso si aplica |
| 200 con 0 | UI-08 empty · sin CTA Agendar |
| Error 5xx / red | UI-09 + Reintentar (repite GET) |
| Tap silla | /fichas/:slug (tile completa clicable) |
| Tap última fórmula | /fichas/:slug de esa fórmula |
| Hover desktop | Rule 1px pizarra; no elevación |
| Pull-to-refresh | No en v1 |
AC: nombre, silla y último matiz visibles en cada tile sin hover. 3 sillas en el fold a 390 (AP-21). Cero botones Agendar / Cobrar.
Aviso 30 vol: visible bajo las tiles; no es dismissible Must (Could: «entendido» solo oculta en sesión).
5. Fichas (UI-03)
| Gesto | Resultado |
|---|---|
| Tap fila | /fichas/:slug |
| Empty | Titular + 1 línea |
| Error | Banner + Reintentar |
| Loading | 5 filas skeleton |
Sin swipe-to-archive. Sin búsqueda Must (5 filas).
6. Ficha (UI-04)
| Gesto | Resultado |
|---|---|
| Tap ← Fichas | /fichas |
| Tap Nueva fórmula | /fichas/:slug/formula |
| Scroll historial | Lectura normal; sin scroll-jacking |
| Tap una fórmula vieja | [DECISIÓN] no navega en Must; se lee in situ. Could: ?id= al editor |
| Foto | No lightbox v1 |
Sin «llamar ahora». Sin WhatsApp click-to-chat. Sin «agendar».
7. Editor móvil (UI-05) · F-EDIT
| Campo | Interacción |
|---|---|
| brand | text |
| tone | text (8ch) |
| grams | number > 0 |
| oxidant | 3 chips radio |
| minutes | number 1–90 |
| notes | textarea |
| Gesto | Resultado |
|---|---|
| Añadir línea | Nueva fila al final; foco al brand |
| Quitar línea | Splice; disabled si queda 1 |
| Chip oxidante | aria-pressed único |
| Guardar | POST; pending; no navegar si inválido |
| Atrás | /fichas/:slug; descarta sin confirm Must |
| Submit inválido | Foco al primer campo error; no POST |
| Error red | Botón Reintentar; no limpia campos |
| Doble tap Guardar | Ignorado mientras pending |
Submit deshabilitado si 0 líneas, grams ≤ 0, oxidant vacío o minutes vacío.
[DECISIÓN] Un solo botón primario: Guardar fórmula. No existe «Aplicar» ni «Guardar y aplicar».
390: CTA full-width h-44 por encima de los tabs (padding-bottom). Teclado iOS no debe tapar el input activo: scroll-margin en campos.
8. Editor desktop (UI-06)
Misma ruta, viewport ≥1200.
| Gesto | Resultado |
|---|---|
| Layout | Editor 7 / staff 5 |
| Staff | Retrato, nombre, matiz, disclaimer; no es otra navegación |
| Guardar | Mismo POST; CTA no full-width (min 200px) |
No hay atajos de teclado extra Must. Cmd+Enter Could L+1.
9. Success (UI-07)
| Gesto | Resultado |
|---|---|
| CTA primario | Ver la ficha /fichas/:slug |
| CTA secundario | Volver a Hoy / |
| Copy | Visible «Guardado no es aplicado» |
Sin confetti. Sin «¡ya está en el pelo!». Sin «cobrar».
10. Empty (UI-08) · Error (UI-09) · Loading (UI-10)
| Estado | Interacción |
|---|---|
| Empty Hoy | Titular + 1 línea. Sin CTA de agenda |
| Empty Fichas | Titular + 1 línea |
| Empty historial | Titular + CTA Nueva fórmula |
| Error | Banner + Reintentar; foco al banner; no pierde form |
| Loading Hoy | 3 tiles + 1 barra; no spinner centrado de marca |
| Loading Fichas | 5 filas lino |
| Loading editor (GET ficha) | 2 líneas skeleton |
11. Cuenta (UI-11)
| Gesto | Resultado |
|---|---|
| Sin token | Redirect login |
| Tap Salir | Limpia storage → /login |
| Error 401 mid-session | Redirect login |
No hay edición de perfil Must. No hay «cambiar password».
12. Motion
| Superficie | Spec |
|---|---|
| Cambio de tab / ruta | Fade 160ms (off si reduced-motion) |
| Tiles Hoy | Fade 160ms, stagger ≤30ms (off si reduced-motion) |
| Badge SAVED | Crossfade 120ms |
| Tabs | Sin hide-on-scroll v1 |
| Skeletons | Estáticos o pulso 1.2s opacity 0.6–1; off si reduced-motion |
Prohibido: scroll-jacking, page transitions de 400ms, parallax de vidriera, like animation, gotas de tinte, mechas que crecen.
13. Criterios de aceptación de interacción
- Una COLORISTA en 390px llega de Hoy a guardar una fórmula en ≤ 4 taps (silla → nueva → … → Guardar).
- Ningún submit permite doble POST.
- Los tabs no cubren el H1 ni el CTA Guardar (spacer).
- Reduced-motion elimina stagger y slide.
- La home no es hero 2-col (AP-12).
- En 390 las 3 sillas son visibles sin scroll de «marca» (AP-21).
/y/fichas/:slugno presentan ningún control de cita ni cobro.- El loading de Hoy es skeleton bento, no un logo.
- El editor muestra «fórmula, no receta» y «guardar no es aplicar» sin scroll en desktop.
- Manos húmedas: cero gestos que exijan hover o swipe horizontal como único camino.
17-prototype-map.md
17 — Mapa de prototipo — MATIZ
Paper no es clicable vía MCP. El prototipo vivo es la app Angular.
Los artboards de Paper se construyen en paralelo; esta tabla es el contrato de paridad.
| Ruta | Paper | Rol |
|---|---|---|
/login | UI-01 | COLORISTA |
/ | UI-02, UI-08, UI-09, UI-10 | COLORISTA |
/fichas | UI-03 (+ empty/error) | COLORISTA |
/fichas/:slug | UI-04 | COLORISTA |
/fichas/:slug/formula | UI-05 (390) · UI-06 (1440) | COLORISTA |
/ok/:id | UI-07 | COLORISTA |
/cuenta | UI-11 | COLORISTA |
Proceso: UX-00…UX-11. Tokens: UI-00.
Empty, error y loading se prototipan como estados / variantes de UI-02 / UI-03 además de sus boards propios. Ver doc 16.
Recorrido demo (guión)
- Abrir
/sin token — redirect/login. Leer eslogan La fórmula, no la cita. - Entrar
nora@matiz.salon/password123. /— Hoy bento: Silla 1 Marisol honey · Silla 2 Amina copper · Silla 3 Pilar silver. Última fórmula Diego 6.1+7.1 20 vol. Aviso: Amina trae 30 vol.- Tocar Marisol Vega — ficha, historial de junio, «La ficha no es una cita.»
- Nueva fórmula — líneas Dia Light 9.3 20 g + 8.3 20 g, 20 vol, 25 min. Leer «Esto es una fórmula, no la receta de la caja.» y «Guardar no es aplicar.»
- Guardar fórmula →
/ok/:id— «Guardado no es aplicado.» - Volver a la ficha: la nueva está SAVED.
- Tab Fichas — 5 filas. Abrir Diego (no está hoy).
- Tab Cuenta — Salón Amparo, Calle Amparo 22, COLORISTA. Leer «MATIZ no agenda. MATIZ no cobra.»
- Comprobar que no hay botón Agendar, Cobrar ni Aplicar.
- Salir →
/login.
Estados en el mismo prototipo
| Estado | Cómo provocarlo |
|---|---|
| Empty Hoy | 0 filas en sittings del día (entorno de test) |
| Empty Fichas | lista vacía autenticada |
| Empty historial | ficha nueva (L+1) o mock |
| Error | API caída / 5xx |
| Loading | throttle de red en GET /hoy |
| 404 ficha | /fichas/no-existe con token |
| 401 | / sin token → login |
| Editor 390 | 390 × 844 en /fichas/marisol/formula |
| Editor 1440 | 1440 × 900 misma ruta (UI-06) |
| Aviso 30 vol | seed Amina silla 2 |
Fuera del prototipo clicable
- Paper artboards son estáticos.
- WhatsApp de Inma (hora) / caja física (ticket).
- El gesto de aplicar el color en el lavacabezas.
- Reserva de cita (no existe).
- TPV / cobro (no existe).
- Cuenta de clienta.
- Recetario oficial de marca.
18-completeness-audit.md
18 — Auditoría de completitud — MATIZ (2026-08-19)
1. Alcance auditado
Vertical slice L2: PWA auth-first de cuaderno de fórmulas + fichas + editor + JWT COLORISTA, con docs, referencia Paper, contratos Angular + Nest + Neon.
Este encargo cubre la suite documental. Paper hi-fi y el repo matiz-app pueden existir en paralelo; la auditoría marca lo que esta suite cierra. El day brief y el paper-reference no se han sobrescrito.
2. Checklist CRON / ALS-2
| Requisito | Estado | Evidencia |
|---|---|---|
| Diversidad sector/tipo/nivel | OK | Belleza oficio L2; no inmobiliaria; no galería; no tabla; no parte de obra |
| Terna S/H/F 0 códigos = N−1 · ≥2 vs N−2 | OK | S-BOTTOM · H-BENTO · F-EDIT vs ZAGUÁN S-WIZ · H-WIZARD · F-CMP |
| Day brief + anti-patrones | OK | docs/00-day-brief.md (no sobrescrito) |
| Paper ≥12 UX + ≥10 UI | OK | UX-count 12 · UI-count 12 · file 01M0CDJZPXVWCVYZC5Z5V9ZRQE |
| Docs 00–20 | OK | suite en docs/ + executive + README |
| JWT COLORISTA | OK | Role COLORISTA · D-P1-05 · auth-first |
| API + seed + Neon | OK | contratos mute-wave-74707210, port 3019 |
| Web tokens | OK | Libre Baskerville + Nunito Sans · paleta chapel |
| Bento-first (no AP-12 / AP-21) | OK | UI-02 / docs 03, 07, 08 |
| Hipótesis no fake field | OK | etiquetas en doc 02 |
| Copy ×5 irrenunciable | OK | docs 01, 09, 16 |
| Ground G-CHAPEL | OK | #E6E4ED · no G-CONCRETE / G-GALLERY / G-MOSS |
3. Cobertura funcional
| Feature brief | Spec | UI Paper | API | Docs |
|---|---|---|---|---|
| Login COLORISTA | Sí | UI-01 | POST login | 04, 06 |
| Hoy bento 3 sillas + última + aviso | Sí | UI-02 | GET hoy | 03, 04, 16 |
| Fichas | Sí | UI-03 | GET clients | 04, 05 |
| Ficha + historial | Sí | UI-04 | GET :slug | 05, 09 |
| Editor móvil | Sí | UI-05 | POST formulas | 04, 16 |
| Editor desktop | Sí | UI-06 | misma API | 04, 16 |
| Success | Sí | UI-07 | GET formula | 04, 09 |
| Empty | Sí | UI-08 | 200 [] | 16 |
| Error | Sí | UI-09 | 5xx/red | 16 |
| Loading | Sí | UI-10 | pending | 16, 17 |
| Cuenta | Sí | UI-11 | GET me | 04 |
| Design system | Sí | UI-00 | — | 08 |
| Seed 1 + 5 + 8 + 3 | Sí | — | seed | 05 |
4. Cobertura Paper (literales)
| 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 | Login | Sí |
| UI-02 | Hoy bento | Sí |
| UI-03 | Fichas | Sí |
| UI-04 | Ficha | Sí |
| UI-05 | Editor móvil | Sí |
| UI-06 | Editor desktop | Sí |
| UI-07 | Success | Sí |
| UI-08 | Empty | Sí |
| UI-09 | Error | Sí |
| UI-10 | Loading | Sí |
| UI-11 | Cuenta | Sí |
[COMPROBADO] UX-count: 12 y UI-count: 12 literales para el gate check-paper-reference.mjs. Paper hi-fi en construcción paralela; inventario planificado escrito.
5. Rúbrica de calidad (auto SCORE orientativo · docs)
| Eje | Score 1–5 | Comentario |
|---|---|---|
| Diversidad | 5 | PWA oficio vs wizard ZAGUÁN, galería MARGEN y tabla CESTA; L2 no L1 |
| Craft visual (spec) | 4–5 | Chapel + Libre/Nunito + fotos de ficha documentadas |
| Densidad UX docs | 5 | Suite L2: tablas, AC, etiquetas S/H/D |
| Completitud código L2 | — | App fuera o en paralelo; leftover scaffold anotado |
| Authz | 5 | Contrato: guard en todo salvo login; 401 público |
| Verdad investigación | 5 | Sin entrevistas ni stats de campo falsas |
6. Huecos aceptados (no regresiones de cierre documental)
| Hueco | Clasificación |
|---|---|
| Agenda / TPV / cuenta clienta / recetario | Fuera L2 |
| e2e automatizado | L+1 |
| Alta de ficha nueva | L+1 |
| Gesto APPLIED | Fuera o L+1 con copy; no Must |
Leftover listings / compare / mesa en repo | Build; contrato de esta suite gana |
| Paper/app no necesariamente tocados aquí | Encargo = documentación |
7. Veredicto
COMPLETO para entrega documental del caso 2026-08-19.
La suite define producto L2 usable (tablas, AC, riesgos, contratos) sin presentar investigación de campo como hecho.
Cualquier ampliación agenda / TPV / clienta-login / recetario / lookbook requiere nuevo brief de diversidad, no parche silencioso.
19-backlog-completo.md
19 — Backlog completo — MATIZ
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-19)
| ID | Ítem | Capa |
|---|---|---|
| D01 | Definición producto MATIZ + eslogan | Docs |
| D02 | Day brief diversidad belleza L2 | Docs (preexistente, no pisado) |
| D03 | Terna R-HY · S-BOTTOM · H-BENTO · F-EDIT | Docs |
| D04 | Personas Nora Beltrán / Inma Soto / Marisol Vega | Docs + Paper ref |
| D05 | JTBD + stories Must | Docs |
| D06 | IA auth-first + chrome S-BOTTOM | Docs |
| D07 | Flujos F0–F9 + AC | Docs |
| D08 | Modelo users / clients / formulas / sittings | Docs |
| D09 | Contratos API :3019 | Docs |
| D10 | Auth JWT COLORISTA | Docs |
| D11 | Status SAVED + 401 público | Docs |
| D12 | Editor líneas + oxidante 10/20/30 + minutos | Docs |
| D13 | Microcopy ×5 (receta / cita / aplicado / agenda / cobra) | Docs |
| D14 | North star sillas con fórmula el mismo día | Docs |
| D15 | Tokens chapel + Libre Baskerville / Nunito Sans | Docs |
| D16 | Paper UX-00…11 + UI-00…11 | Docs |
| D17 | Suite docs 00–20 + README + executive | Docs |
| D18 | Seed 1 COLORISTA + 5 fichas + 8 fórmulas + 3 sillas | Docs |
| D19 | Assets listados (hero + 5 clientas) | README |
| D20 | QA smoke + S1–S25 | Docs |
| D21 | Neon mute-wave-74707210 | Docs |
| D22 | Hoy bento + editor 390/1440 especificados | Docs |
| D23 | Ground G-CHAPEL #E6E4ED | Docs |
| D24 | Rutas reales solo las 7 del encargo | Docs |
Los must-have del brief (login, Hoy, fichas, ficha, editor, success, empty/error/loading, cuenta, ≥12 UX + ≥10 UI) están dentro de D01–D24, no como leftover.
2. Backlog L2 implementación (repo matiz-app)
Alineación de código al contrato ya cerrado. No es alcance documental pendiente.
| ID | Ítem | Prioridad | Notas |
|---|---|---|---|
| I01 | Alinear apps/api + apps/web al contrato MATIZ | P0 | quitar leftover listings/compare/mesa |
| I02 | Schema users / clients / formulas / sittings | P0 | |
| I03 | Seed Nora + 5 clients + 8 formulas + 3 sittings | P0 | |
| I04 | GET hoy / clients / clients/:slug / formulas/:id | P0 | 401 sin token |
| I05 | POST clients/:slug/formulas + PATCH formulas/:id | P0 | status SAVED |
| I06 | POST login JWT + GET me | P0 | nora@matiz.salon |
| I07 | GET health | P0 | |
| I08 | Auth guard Angular en todo salvo login | P0 | |
| I09 | Hoy / fichas / ficha / editor / ok / cuenta / login | P0 | |
| I10 | Tokens Tailwind + fotos + bottom tabs | P0 | |
| I11 | Smoke curl + build | P0 | D-P1-06 |
| I12 | Puerto API 3019 | P0 |
3. Backlog L+1 (polish)
| ID | Ítem | Prioridad | Notas |
|---|---|---|---|
| B01 | Alta de ficha nueva | P1 | sin cuenta clienta |
| B02 | PATCH desde historial | P2 | status SAVED |
| B03 | Rate limit login | P1 | anti stuffing |
| B04 | Playwright smoke | P1 | CI |
| B05 | Skeleton UI-10 en código | P2 | |
| B06 | PWA manifest / install | P2 | S2 |
| B07 | Borrador local del editor | P3 | no es SAVED |
| B08 | API URL por environment | P1 | deploy |
| B09 | Página 404 amigable | P3 | |
| B10 | Sessions invalidables | P2 | |
| B11 | Gesto «puesta» con copy | P2 | no fingir resultado |
| B12 | Rol DUEÑA solo lectura | P3 | brief corto |
4. Backlog L3 (requiere brief nuevo)
| ID | Ítem | Dependencia |
|---|---|---|
| C01 | Multi-colorista / turnos | users + membership |
| C02 | Cuenta CLIENTA + «mi cita» | authz · rompe H3 |
| C03 | Calendario de sillas | H2 se rompe · brief nuevo |
| C04 | TPV / ticket | microcopy se rompe |
| C05 | Recetario oficial de marca | fórmula ≠ receta se rompe |
| C06 | Cadena de salones | S1 se rompe |
| C07 | Historial clínico / alergias | otro producto |
5. Backlog explícitamente fuera
| ID | Ítem |
|---|---|
| E01 | Reserva / slot / confirmación de cita |
| E02 | Lookbook editorial / agenda de looks |
| E03 | Tabla de necesidades / compromiso de caja |
| E04 | Parte de obra / wizard de oficio |
| E05 | Wizard + comparador de fincas |
| E06 | App nativa |
| E07 | Hero 2-col + 3 cards de marca |
6. Explicitamente no-backlog
| Idea | Razón |
|---|---|
| Hero 2-col + 3 cards | AP-12 / rompe H-BENTO |
| Masthead de periódico | S-TOP de MARGEN |
| Chrome de pasos 1—2—3 | S-WIZ de ZAGUÁN |
| Sidebar CRM | S-SIDE |
| Rail de iconos | S-RAIL de CESTA |
| Form «pide cita» | H2 |
| «Pagar ahora» | MATIZ no cobra |
| Status APPLIED como hecho | Guardado ≠ aplicado |
7. Orden de ataque recomendado (continuidad de código)
- I01–I12 (vertical slice runnable, sin leftover)
- B03 + B04 + B08
- B01 + B02
- B06 + B11 si hay campo
- Evaluar brief L3 — no parche silencioso
8. Trazabilidad
| Origen | Ítems |
|---|---|
| Day brief must-have | D01–D24, I01–I12 |
| Doc 15 L+1 | B01–B12 |
| Doc 15 L3 / fuera | C01–C07, E01–E07 |
20-implementation.md
20 — Implementación — MATIZ
1. Resumen ejecutivo técnico
| Campo | Valor |
|---|---|
| App path | /Users/cristian/orca/matiz-app |
| API | NestJS · puerto 3019 · prefijo /api |
| Web | Angular standalone · puerto 4200 |
| Package | apps independientes (npm --prefix o pnpm --filter) |
| DB | Neon PostgreSQL · project mute-wave-74707210 |
| Tablas | users, clients, formulas, sittings (+ sessions Should) |
| Auth | JWT Bearer · role COLORISTA |
| Dominio | User, Client, Formula, Sitting |
| Fecha | 2026-08-19 |
| GitHub | https://github.com/Criscode2022/matiz-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.
[COMPROBADO] Path, puerto, Neon, Paper y GitHub salen del encargo.
[COMPROBADO] El repo existe; contiene leftover de ZAGUÁN (listings, compare, mesa, páginas wizard / finca). El contrato de esta suite es MATIZ.
2. Cómo arrancar
cd /Users/cristian/orca/matiz-app
# instalar dependencias por app (no npm workspaces)
# apps/api/.env
# DATABASE_URL= # Neon mute-wave-74707210
# JWT_SECRET=
# PORT=3019
# schema + seed
# api → http://localhost:3019
# web → http://localhost:4200
Credenciales
| Rol | Password | |
|---|---|---|
| COLORISTA | nora@matiz.salon | password123 |
3. Schema
Al boot o vía migrate:
CREATE TABLE/ migrate deusers,clients,formulas,sittings(doc 05). Opcionalsessions.- Índices:
clients(slug),formulas(client_id, created_at DESC),sittings(day, chair),users(email). Unique(day, chair)y(day, client_id). - Seed idempotente:
- upsert COLORISTA
nora@matiz.salon(bcrypt depassword123) - upsert
clientsporslug - upsert
formulasporid - upsert
sittingsdel2026-08-19(o delCURRENT_DATEEurope/Madrid si se quiere que «hoy» sea siempre el día de demo)
- upsert COLORISTA
[DECISIÓN] Para la demo diaria, el seed de sittings puede usar CURRENT_DATE en Europe/Madrid en lugar de la fecha fija 2026-08-19, de modo que Hoy nunca aparezca vacío al reabrir el case. Documentar en README del repo. Las fórmulas conservan fechas históricas del seed.
No borrar fórmulas nuevas de Nora en cada boot si ya existen filas distintas al seed.
Tablas (recordatorio)
| Tabla | Contenido |
|---|---|
users | COLORISTA Nora |
clients | 5 fichas |
formulas | 8 SAVED |
sittings | 3 sillas de hoy |
sessions | Should: sid + token_hash + expires_at |
4. Módulos API a implementar
Auth
POST /api/auth/loginGET /api/me- Valida email/password; compara bcrypt; emite JWT con
sub, email, role. - Guard JWT +
role === COLORISTAprotege todo salvo login y health.
Oficio
| Método | Ruta | Auth | Notas |
|---|---|---|---|
| GET | /api/health | No | { ok: true } |
| GET | /api/hoy | JWT | sittings del día + lastFormula + oxidantWarning |
| GET | /api/clients | JWT | cards + lastFormulaAt |
| GET | /api/clients/:slug | JWT | ficha + formulas[] |
| POST | /api/clients/:slug/formulas | JWT | crea SAVED |
| GET | /api/formulas/:id | JWT | |
| PATCH | /api/formulas/:id | JWT | lines / oxidant / minutes / notes · no status |
Hoy (referencia de implementación)
day = DATE Europe/Madrid
sittings = WHERE day = today ORDER BY chair ASC
+ join client
+ lastFormula = latest formula of that client
lastFormula = latest formula globally
oxidantWarning.active = any sitting.lastFormula.oxidant === 30
[DECISIÓN] Timezone Europe/Madrid para day de sittings y para labels de UI. JSON transporta ISO UTC. Gramos y minutos son números.
Guardar no toca sittings ni genera cobro.
5. Frontend a implementar
| Página | Ruta | Responsabilidad |
|---|---|---|
| LoginPage | /login | form → login → Hoy |
| HoyPage | / | GET hoy, bento, empty/loading/error |
| FichasPage | /fichas | GET clients |
| FichaPage | /fichas/:slug | GET slug, historial, CTA |
| FormulaPage | /fichas/:slug/formula | editor 390 + 1440, POST |
| OkPage | /ok/:id | GET formula, «guardado ≠ aplicado» |
| CuentaPage | /cuenta | GET me, salir |
Shell autenticada: BottomTabsComponent. Login: sin tabs.
ApiService centraliza base URL http://localhost:3019/api, token storage (matiz_token, matiz_user), métodos tipados (Client, Formula, HoyResponse, User).
AuthGuard en todas las rutas salvo /login. Interceptor Authorization: Bearer.
6. Decisiones de implementación
| Decisión | Razón |
|---|---|
| Puerto API 3019 | Evitar colisión con ZAGUÁN 3018 |
Tablas clients / formulas / sittings | Dominio de oficio, no leftover listings |
Role COLORISTA | Encargo; no AGENT |
| Soft auth en páginas | Simple; API es autoridad |
| Auth-first web | Encargo; cero cara pública |
JSONB lines | 1–4 líneas; no tabla hija |
| Status solo SAVED | Guardado ≠ aplicado |
| Home no usa hero 2-col | AP-12 / H-BENTO |
| Sittings seed = hoy civil | Demo usable cualquier día |
| JSON camelCase | Convenio web serie daily |
PATCH :id sin status | Contrato del encargo |
Success /ok/:id | Board UI-07 |
| Cover allowlist | Evitar URL arbitraria |
| Apps independientes | AP-06 |
7. Variables de entorno
| Variable | Servicio | Descripción |
|---|---|---|
DATABASE_URL | API | Neon mute-wave-74707210 |
JWT_SECRET | API | Firma tokens |
PORT | API | opcional, 3019 |
JWT_EXPIRES | API | opcional, default 12h |
Web: URL de API en ApiService (default localhost:3019).
8. Smoke de implementación (mínimo)
- Schema OK (tablas existen).
GET /api/health200.POST /api/auth/login200 +accessToken+role=COLORISTA.GET /api/hoyBearer: 3 sittings, chairs 1–2–3 = marisol / amina / pilar.GET /api/hoysin token 401.GET /api/clientsincluye 5 slugs.GET /api/clients/marisolhistorial ≥ 2 fórmulas.POST /api/clients/marisol/formulas201status=SAVED.- Web: bento dominante, tabs, disclaimers ×5, fotos 1:1.
9. Estructura de ficheros clave (objetivo)
matiz-app/
├── package.json
├── apps/api/
│ ├── src/
│ │ ├── main.ts
│ │ ├── app.module.ts
│ │ ├── auth/
│ │ ├── clients/
│ │ ├── formulas/
│ │ ├── hoy/
│ │ └── health.controller.ts
│ └── .env.example
├── apps/web/
│ ├── tailwind.config.js
│ └── src/app/
│ ├── app.routes.ts
│ ├── core/api.service.ts
│ ├── shell/bottom-tabs.component.ts
│ └── pages/
│ ├── login/
│ ├── hoy/
│ ├── fichas/
│ ├── ficha/
│ ├── formula/
│ ├── ok/
│ └── cuenta/
└── README.md
Nombres de fichero orientativos; el contrato es de rutas y tablas, no de filenames exactos.
Quitar módulos leftover listings/, compare/, mesa/ y páginas wizard/, comparar/, finca/.
10. Seed — checklist de implementación
| # | Check |
|---|---|
| 1 | User Nora Beltrán / nora@matiz.salon / password bcrypt de password123 |
| 2 | 5 clients con slugs y covers de doc 05 |
| 3 | 8 formulas SAVED con ids frm_* |
| 4 | 3 sittings hoy: chair 1 marisol, 2 amina, 3 pilar |
| 5 | Amina última fórmula oxidant 30 (aviso Hoy) |
| 6 | lastFormula global = diego 2026-08-01 |
| 7 | Idempotencia: upsert por email / slug / formula id |
11. Alineación case ↔ app
| Doc | Evidencia esperada en código |
|---|---|
| 05 data model | columnas y enums |
| 03–04 IA/flows | routes + controllers |
| 08 DS | tailwind colors + fontFamily Libre/Nunito |
| 09 content | strings en templates (microcopy ×5) |
| 00 paper | URLs en README case |
| 11 security | Guard en todo salvo login; 401 público |
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 (alta de ficha, e2e, rate limit, PWA install) viven en backlog L+1, no como deuda silenciosa.
- Si el repo aún muestra
listings/compare/mesa/ wizard de zona, no está alineado: I01 es el primer ítem de build.