On this page 00 — Day brief · 2026-08-16 · CESTA 0%

00-day-brief.md

00 — Day brief · 2026-08-16 · CESTA

Decisiones ALS-2 BRIEF

CampoValor
Fecha2026-08-16
NombreCESTA
ComplejidadNivel 1 (no L2 consecutivo tras RONDA)
SectorONG / despensa vecinal (en especie)
TipoTabla densa de necesidades + compromiso + inbox STAFF JWT
PlataformaWeb responsive (tabla + rail · desktop staff)
RegistroR-CL — civic/botánico excelente, no feed social
ShellS-RAIL — rail de iconos + panel de tabla
HomeH-TABLE — la tabla es la home
FlujoF-LEAD — compromiso público → cola staff
Por qué no RONDANo S-DOCK / H-FEED / F-BOOK; no gaming; no cronología de eventos
Por qué no DERIVANo S-SPLIT / H-MAP / F-MAP

Terna unicidad

R-CL · S-RAIL · H-TABLE · F-LEAD

0 códigos iguales a N−1 (RONDA). ≥2 distintos vs N−2 (DERIVA). Ventana 4 días ya tenía R-HY e R-IN; hoy R-CL. Experimento §8: tabla densa / ONG.

Mood visual (Paper)

  • Candidatos: charity cream+naranja (primer instinto), chapel, botanical, mineral, bookish
  • Elegido: botanical — no el primer instinto (warm off-white × terracotta, cliché y vetado)
  • Paleta: #E6EFDD hueso · #1C2418 tierra · #3D6B4A musgo · #7A9A6A hoja · #C4B49A saco · #B23A2F ficha
  • Tipo: Spectral 52/36/24 display · Manrope 16/14 UI

Alcance L1 must

  1. Home: tabla de 5 necesidades (foto, ítem, pedido, comprometido, hueco)
  2. Fila → compromiso (nombre, email, cantidad) — compromiso ≠ entrega
  3. Success CE-MMDD-NNN
  4. Login + inbox STAFF JWT (NEW → RECEIVED / PARTIAL / CANCELLED)
  5. Empty / loading / error / mobile tabla apilada
  6. ≥12 UX + ≥10 UI

Supuestos

  • S1: Una despensa de barrio cubre 8–20 líneas activas (demo: 5).
  • S2: El vecino llega con “qué falta”, no a donar dinero.
  • S3: La coordinadora necesita cola con estado, no un CRM de donantes.
  • S4: La entrega se confirma en el local; CESTA no cobra.

Hipótesis

IDSeñal
H1La tabla reduce “¿qué llevo?” vs lista de WhatsApp
H2Copy “compromiso, no entrega” baja no-shows de cajas
H3RECEIVED nunca inventa stock: es acuse, no inventario ERP

00-paper-reference.md

Paper reference · CESTA

URLhttps://app.paper.design/file/01M04P0VFWGX1DQVSCR5TPWM9Z
File ID01M04P0VFWGX1DQVSCR5TPWM9Z

UX-count: 12 UI-count: 11

UX-00 Cover · UX-01 Stakeholders · UX-02 Personas · UX-03 JTBD · UX-04 Stories · UX-05 Journey · UX-06 Blueprint · UX-07 Site map · UX-08 Flujos · UX-09 Datos+permisos · UX-10 Métricas · UX-11 Research UI-00 Tokens · UI-01 Tabla · UI-02 Ficha · UI-03 Compromiso · UI-04 Success · UI-05 Login · UI-06 Inbox · UI-07 Empty · UI-08 Error · UI-09 Loading · UI-10 Mobile tabla

UX process (prefijo UX- · mínimo 12)

IDNombreContenido
UX-00CoverPortada CESTA · eslogan «Lo que falta, en una lista.» · terna R-CL · S-RAIL · H-TABLE · F-LEAD · L1 ONG despensa en especie · mood botanical · fecha 2026-08-16
UX-01StakeholdersVECINO anónimo, STAFF (Inés), despensa de barrio, familias receptoras, donantes en especie — no pasarela de dinero
UX-02PersonasRosa Belmonte (54, vecina, trae aceite el sábado) · Inés Marzal (41, coordinadora, inbox antes de abrir)
UX-03JTBDJob «qué falta y cuánto puedo traer» + job «saber quién se comprometió antes de abrir el local»
UX-04StoriesMust: tabla de 5 necesidades, ficha, form comprometer, código CE-, login STAFF, inbox 5 estados, empty/error/loading, mobile tabla apilada
UX-05JourneyLeer tabla → ficha → compromiso honesto → acuse → contacto STAFF → RECEIVED / PARTIAL
UX-06BlueprintFrontstage rail/tabla/form · backstage mostrador del local · sistemas Nest/Neon/JWT
UX-07Site mapPúblico table-first + auth STAFF; rail Lista · Inbox · Entrar
UX-08FlujosF-LEAD: fila → ficha → POST compromiso → success; F-STAFF inbox + PATCH status
UX-09Datos+permisosNeed · Request · User STAFF · Session; JWT en GET/PATCH requests
UX-10MétricasNorth star % compromisos CONTACTED antes de la franja de entrega; tap fila; RECEIVED no inventa stock
UX-11ResearchComprobado / supuesto / hipótesis / decisión — sin entrevistas de campo inventadas

UI producto (prefijo UI- · mínimo 10)

IDNombreFlujo
UI-00TokensDS botanical: hueso, tierra, musgo, hoja, saco, ficha; Spectral + Manrope
UI-01TablaH-TABLE: tabla densa de necesidades (foto, ítem, pedido, comprometido, hueco) + rail
UI-02FichaFoto real, unidad, pedido / comprometido / hueco, disclaimer compromiso ≠ entrega, CTA
UI-03CompromisoForm público; disclaimer compromiso ≠ entrega confirmada
UI-04SuccessAcuse + código CE-MMDD-NNN + próximos pasos
UI-05LoginAcceso JWT STAFF al inbox
UI-06InboxLista + estados NEW → CONTACTED → RECEIVED / PARTIAL / CANCELLED
UI-07EmptyCero necesidades / inbox vacío
UI-08ErrorFallo de red / API; reintentar
UI-09LoadingSkeleton tabla / inbox
UI-10Mobile tablaHome tabla ~390px apilada + rail compacto

Regla: wizard de N pasos → N boards UI solo para esos pasos, además de success/empty/error/loading/staff. CESTA no es wizard: el núcleo es tabla → ficha → form de 1 pantalla.


1. Mapa de canvas (bandas)

§BandaPropósitoArtboards clave
1UX PROCESSModelo de servicio de despensa vecinalUX-00…UX-11
2DESIGN SYSTEMTokens botanical + rail + tablaUI-00
3PUBLIC TABLECara vecina table-firstUI-01, UI-02, UI-03, UI-04, UI-07, UI-10
4STAFFFlujos autenticadosUI-05, UI-06
5STATESResilienciaUI-08, UI-09

Layout canvas (referencia de diseño): origen (0,0) · gaps ~100–120px · UX en 3×4 · UI pública en fila tabla → ficha → compromiso → success · mobile debajo de la tabla. [DECISIÓN] Bandas jerárquicas D-P0-04 / D-P1-04.


2. Mapeo Paper → Angular

ArtboardRuta appComponente
UI-00 Tokenstokens Tailwind + fuentes
UI-01 Tabla/HomePage (tabla)
UI-02 Ficha/necesidades/:slugNeedPage
UI-03 Compromiso/necesidades/:slug/comprometerCommitPage
UI-04 Success/ok/:codeOkPage
UI-05 Login/loginLoginPage
UI-06 Inbox/inboxInboxPage
UI-07 Empty/ 0 needs · /inbox 0 itemsempty en tabla / inbox
UI-08 Errortabla / inbox / formbanner error + retry
UI-09 Loadingtabla / inboxskeletons de filas
UI-10 Mobile tabla/ viewport 390mismo HomePage + filas apiladas

[COMPROBADO] Rutas del encargo. No hay /inbox/:id, search, mapa, feed ni hero 2-col.


3. Tokens de diseño en Paper

TokenValorUso
Hueso / bg#E6EFDDFondo de página, tabla
Tierra#1C2418Texto fuerte, wordmark, rail activo, CTA ink
Musgo#3D6B4AAcciones positivas, RECEIVED, acento primario
Hoja#7A9A6AHueco cubierto, meta positiva, rail hover
Saco#C4B49ARails, bordes, cabecera de tabla, skeletons
Ficha#B23A2FHueco alto, PARTIAL, urgencia, cancelar
DisplaySpectral 52/36/24Titulares, wordmark, código CE-
UIManrope 16/14Body, labels, form, celdas de tabla

[COMPROBADO] Hex y tipo salen de docs/00-day-brief.md.


4. Checklist de densidad (anti thin-frames)

CriterioUXDSPublic tableSTAFFStates
Jerarquía tipográfica visible
Microcopy real (no lorem)
Tokens botanical aplicados
Datos de seed creíblesPersonas5 necesidades + fotosCódigos CE-Empty realista
Media / iconografíaCoverFotos ítemBadges estadoSkeletons
Rail de iconos presenteSpecUI-01, UI-10UI-06
Tabla densa (no cards feed)SpecUI-01, UI-10

5. Media

  • assets/hero.jpg — atmósfera de despensa / cesta / local vecinal
  • assets/needs/arroz.jpg — Arroz redondo
  • assets/needs/aceite.jpg — Aceite de oliva
  • assets/needs/tomate.jpg — Tomate en conserva
  • assets/needs/panales.jpg — Pañales T3
  • assets/needs/leche.jpg — Leche entera

[COMPROBADO] Los seis archivos existen en el case 2026-08-16-cesta/assets/.


6. Enlaces

01-project-definition.md

01 — Definición de proyecto — CESTA

1. Identidad

CampoValor
NombreCESTA
SignificadoCesta de la despensa vecinal: lo que falta, visible en una lista, no un llamamiento a donar dinero
Eslogan”Lo que falta, en una lista.”
Una fraseTabla densa de necesidades en especie + compromiso público + inbox STAFF JWT.
SectorONG / despensa de barrio (donación en especie, no dinero)
TipoWeb L1 — tabla + compromiso + inbox STAFF — Nivel 1 compacto, craft alto
PlataformaWeb responsive (tabla + rail · móvil vecino + desktop staff)
Mercado demoEspaña · despensa de barrio (local demo, no red multi-sede)
Idiomaes-ES
Fecha caso2026-08-16
TernaR-CL · S-RAIL · H-TABLE · F-LEAD

[COMPROBADO] Nombre, eslogan, terna, nivel y sector salen de docs/00-day-brief.md.

2. Problema

Principal (hipótesis de diseño)

[HIPÓTESIS] Quien quiere ayudar a la despensa no abre un marketplace ni un Bizum: pregunta “qué falta” en un grupo de WhatsApp, un cartel del local o un mensaje que se pierde. [SUPUESTO] La coordinadora confirma cajas por mensaje suelto, sin cola, sin código y sin ver el hueco (pedido − comprometido) en un solo vistazo.

Secundarios

ProblemaQuién lo sufreEfecto
“¿Qué llevo?” enterrado en chats y cartelesVECINO (Rosa)Trae lo que sobra en casa, no lo que falta
Compromiso oral sin acuseAmbosCajas fantasma el sábado; hueco mal contado
Mensaje enviado sin códigoVECINONo sabe si llegó; escribe otra vez
Excel / WhatsApp sin estadoSTAFF (Inés)No sabe a quién ya contestó ni quién entregó
Expectativa de “ya está entregado”AmbosCompromiso se cuenta como stock

Supuestos (no investigación primaria propia)

  • S1: Una despensa de barrio cubre 8–20 líneas activas (demo: 5). [SUPUESTO]
  • S2: El vecino llega con “qué falta”, no a donar dinero. [SUPUESTO]
  • S3: La coordinadora necesita cola con estado, no un CRM de donantes. [SUPUESTO]
  • S4: La entrega se confirma en el local; CESTA no cobra. [SUPUESTO]

Hipótesis de producto

IDHipótesisSeñal de validación (futura)
H1La tabla reduce “¿qué llevo?” vs lista de WhatsApp% sesiones con tap en fila
H2Copy “compromiso, no entrega” baja no-shows de cajas↓ “pensé que ya contaba como entregado”
H3RECEIVED nunca inventa stock: es acuse, no inventario ERPRECEIVED no muta requestedQty; hueco = pedido − comprometido

No se afirman estadísticas de demanda de despensas inventadas. Todo lo anterior es razonamiento de diseño etiquetado.

3. Propuesta de valor

ParaValor
VECINO (Rosa Belmonte)Ve qué falta y cuánto hueco queda; se compromete en un minuto; se lleva un código.
STAFF (Inés Marzal)Ve compromisos nuevos, marca contacto antes de abrir, anota recibido / parcial / cancelado sin WhatsApp.
Despensa / barrioCanal mínimo: lista visible + pipeline de cajas, sin TPV ni crowdfunding.

No es CESTA

ExcluidoPor qué
Donación de dinero / Bizum / TPVS2 + S4: en especie; no cobra
Feed / cronología de “historias”Terna H-TABLE, no H-FEED (eso fue RONDA)
Mapa de despensasTerna no es H-MAP (eso fue DERIVA)
Search-first / catálogo facetadoTerna no es H-SEARCH (eso fue PIZARRA)
CRM de donantes / sociasS3: cola con estado, no ficha de donante
Inventario ERP / almacénH3: RECEIVED es acuse, no stock
Hero 2-col + 3 cards de “cómo donar”Anti-patrón AP-12; contradice H-TABLE

4. Objetivos

Negocio / caso de estudio

  • Demostrar vertical slice L1 ONG table-first con Need + Request + JWT STAFF.
  • Portfolio coherente: Paper (12 UX + 11 UI) + docs + app runnable.
  • Terna R-CL · S-RAIL · H-TABLE · F-LEAD frente a RONDA (feed) y DERIVA (mapa).

Usuario

RolObjetivo medible en demo
VECINOVer el hueco en el primer viewport y enviar compromiso en < 2 min
STAFFMarcar CONTACTED / RECEIVED / PARTIAL en < 3 taps desde el inbox

No objetivos v1 (explícitos)

  • Pagos, Bizum, socio mensual, certificado fiscal
  • Cuenta de vecino / “mis compromisos”
  • Chat, matching o reputación de donantes
  • Mapa de despensas / multi-sede
  • Notificación email / WhatsApp transaccional
  • Inventario de almacén (entradas/salidas/lote)
  • CMS de necesidades para editoras
  • Multi-staff con roles editoriales

5. Roles y permisos (resumen)

AcciónPúblico (VECINO)STAFF
Ver tabla de necesidades
Ver ficha de necesidad
POST compromisoSí (mismo form)
Login JWTNo (no cuenta vecino)
Listar compromisosNo (401)
Cambiar statusNo
Crear / editar NeedNo (seed)No en v1

[DECISIÓN] Un solo rol autenticado: STAFF. El vecino es anónimo en captura.

6. Métricas (modelo, no instrumentadas en v1 salvo base)

TipoMétricaDefinición
North Star% compromisos válidos CONTACTED antes de la franja de entrega (sábado demo)Calidad operativa del contacto, no “kg recaudados”
Activación1.er compromiso públicoPOST create 2xx
Tabla% sesiones con tap en filaH1
ExpectativaReclamaciones “ya estaba entregado”H2 (cualitativa)
IntegridadRECEIVED no muta requestedQtyH3
SaludError rate API needs/create/list4xx/5xx

7. Alcance funcional v1 (L1)

MóduloIncluido
Home tabla5 necesidades (foto, ítem, pedido, comprometido, hueco) + rail
FichaUnidad, cantidades, disclaimer, CTA comprometer
CompromisoForm público + POST /api/requests { needSlug, name, email, quantity, note }
SuccessCódigo CE-MMDD-NNN en /ok/:code + próximos pasos
AuthPOST /api/auth/login → JWT STAFF
InboxGET list, PATCH :id { status }
Estados UIEmpty, loading skeleton, error de red
Mobile tablaMisma home a ~390px, filas apiladas
Seed1 STAFF, 5 needs, compromisos de ejemplo

8. Criterios de aceptación de producto

  1. Un visitante puede ver la tabla densa sin autenticación y abrir una ficha.
  2. Un visitante puede enviar un compromiso sin cuenta y recibir un código CE-….
  3. La UI dice explícitamente que el compromiso no confirma entrega.
  4. Sin token, GET/PATCH /api/requests* responden 401.
  5. GET /api/needs devuelve las 5 necesidades seed.
  6. Un STAFF puede iniciar sesión y ver el inbox ordenado (createdAt desc).
  7. El inbox permite transicionar NEW | CONTACTED | RECEIVED | PARTIAL | CANCELLED.
  8. La home es la tabla: no hay hero 2-col + 3 cards como superficie principal.
  9. Slugs cubiertos en seed: arroz, aceite, tomate, panales, leche.
  10. Fotos de ítem reales en filas y fichas (assets del case).
  11. Hueco visible = pedido − comprometido; no se vende como stock ERP.
  12. Rutas públicas solo las seis del encargo (doc 03).

9. Stack y artefactos

CapaDetalle
FrontendAngular + Tailwind · puerto 4200
BackendNestJS · puerto 3016
DBNeon PostgreSQL · project jolly-night-99671179
AuthJWT (STAFF)
DiseñoPaper 01M04P0VFWGX1DQVSCR5TPWM9Z
Repo app/Users/cristian/orca/cesta-app/ · GitHub Criscode2022/cesta-app

[COMPROBADO] Puerto, Neon, Paper file ID, repo y path de app constan en el encargo.

10. Riesgos y mitigaciones

RiesgoImpactoMitigación v1
Spam en form públicoInbox ruidosoValidación server; honeypot / rate-limit en L1+
Expectativa de entrega instantáneaCajas fantasmaCopy “compromiso ≠ entrega”; RECEIVED solo STAFF
Contar compromiso como stockHueco mentirosoH3: RECEIVED es acuse; no muta pedido
Confundir con crowdfunding / BizumExpectativa de pagoCopy “en especie”; sin precio ni IBAN
PII de vecinos en compromisosPrivacidadSolo STAFF lista; doc 11
Tabla vacía un martesAbandonoEmpty state “ahora mismo no hay líneas abiertas”
Home percibida como landing de ONG genéricaPérdida de craftRail + tabla como superficie; no hero 2-col
Confusión “ya doné online”Expectativa de dineroNo hay checkout; hay compromiso de caja

11. Glosario

TérminoDefinición en CESTA
Necesidadneeds: ítem, unidad, pedido, comprometido, hueco
Compromisorequests; no es entrega confirmada
Entrega / recibidoSolo cuando STAFF marca RECEIVED
ParcialPARTIAL: llegó menos de lo comprometido
PedidoCantidad que pide la despensa (requestedQty)
ComprometidoSuma de quantity de requests no CANCELLED
Huecomax(0, pedido − comprometido)
STAFFUsuario autenticado que opera el inbox
VECINOVisitante anónimo que se compromete
Código CE-Identificador corto oral (CE-0816-001)
RailShell S-RAIL: iconos + panel de tabla
En especieAlimento / higiene / producto; nunca dinero

12. Decisiones de diseño (cierre de brief)

IDDecisiónAlternativa descartada
D1Home = tabla densa (H-TABLE)Hero 2-col + 3 cards (AP-12) · feed (RONDA) · mapa (DERIVA) · search (PIZARRA)
D2Shell rail de iconos (S-RAIL)Top-nav sticky · dock flotante · sidebar CRM · command search
D3Registro clásico cívico-botánico (R-CL)Charity cream+naranja (primer instinto, cliché) · chapel · mineral
D4Flujo F-LEAD (compromiso → cola)Wizard, kanban, search, mapa, booking
D5Mood botanicalWarm off-white × terracotta (vetado), chapel, bookish
D6Un rol autenticado STAFFMulti-rol vecino / tesorería / admin
D7Estados RECEIVED / PARTIAL (no SEATED / ENROLLED)Vocabulario de mesa o municipal
D8CESTA no cobraTPV / Bizum / socio / certificado fiscal
D9Success en /ok/:codeQuery ?code= (otros días de la serie)
D10PATCH /api/requests/:id { status }Subruta /status (PIZARRA/RONDA)

13. Relación con el día anterior

CaseTernaPor qué CESTA no lo copia
RONDA (N−1)S-DOCK / H-FEED / F-BOOKNo gaming, no cronología, no dock, no sillas
DERIVA (N−2)S-SPLIT / H-MAP / F-MAPNo turismo, no plano, no split mapa

[COMPROBADO] El brief exige 0 códigos iguales a N−1 y ≥2 distintos vs N−2. Terna de hoy: R-CL · S-RAIL · H-TABLE · F-LEAD. Ventana 4 días ya tenía R-HY e R-IN; hoy R-CL.

02-ux-research-strategy.md

02 — Estrategia de investigación UX — CESTA

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)

ObjetivoMétodo en este casoSalida
Entender actores de la despensa vecinalModelado de stakeholders + personas§3–4
Definir job de “qué falta” y de huecoJTBD + stories Must§5
Mapear fricción chat → cajaJourney + service blueprint§6–7
Traducir a requisitos L1 table-firstMatriz hallazgo → requisito → feature§8

2. Fuentes y límites

Fuentes admisibles (secundarias / operativas)

  • Conocimiento general de despensas de barrio, bancos de alimentos de proximidad y grupos de WhatsApp de donación en especie.
  • Analogía operativa con tablas densas + form público + inbox JWT de la serie daily.
  • Restricciones ALS-2: no feed, no mapa, no search-first, no pagos, no CRM de donantes.

Límites éticos de verdad

ProhibidoPermitido
“El 64% de vecinos abandona el grupo de WhatsApp” sin fuente[SUPUESTO] el vecino llega con ‘qué falta’”
Citas de entrevistas ficticias como campo realQuotes de persona etiquetadas como constructo de diseño
SLA medido “contacto en 2 h el viernes” como KPI realNorth star modelo “CONTACTED antes de la franja de entrega”
Stock real de una despensa concretaPedidos de seed de producto
“Inés existe y nos dijo…”Inés Marzal es persona de diseño

3. Stakeholders

StakeholderInfluenciaInterésNecesidad principal
VECINO / vecina que trae cajaBaja formalMuy altaVer qué falta y dejar compromiso con acuse
STAFF / coordinadora (Inés)AltaMuy altaCola con estado y código oral antes de abrir
Familias receptorasAlta misiónAltaQue llegue lo que falta, no lo que sobra
Voluntariado de mostradorMediaAltaLista del sábado sin cajas fantasma
Junta / entidad anfitrionaAlta adopciónMediaCanal mínimo, sin TPV ni campaña de dinero
Banco de alimentos / gran ONGBaja v1BajaFuera: CESTA es despensa de barrio, no red logística
WhatsApp / cartel del localCompetencia / canal paraleloNo clonar el chat; sí sustituir la lista informal

Mapa de poder (resumen)

  • Decisor de adopción: coordinadora de la despensa (quien abre el local).
  • Usuario frecuente de captura: VECINO (tabla + form) y STAFF (inbox del día).
  • Riesgo de rechazo: si la home no enseña el hueco en el primer viewport, o si el form implica “ya está entregado”.

4. Personas

P1 — Rosa Belmonte · VECINO

CampoDetalle
Edad / contexto54 años; vecina del barrio; el sábado puede pasar por el súper y dejar aceite
DigitalMedia; móvil grande; usa WhatsApp; evita “portales de donación” con certificado
GoalsVer qué falta de verdad, comprometer una cantidad concreta, guardar un código
PainsListas de chat desactualizadas, no saber si “traigo aceite” ya cuenta, llevar lo que sobra
Quote de diseño“Dime qué falta y cuántos litros. El sábado paso yo.”
Seed demorosa.belmonte@example.com · compromiso aceite CE-0816-001

Escenario: Abre CESTA, ve Aceite de oliva con hueco, entra a la ficha, compromete 3 L, guarda CE-0816-001.

[DECISIÓN] Rosa no tiene cuenta. Crear usuario de vecina sería fricción y no aporta al job de captura.

P2 — Inés Marzal · STAFF

CampoDetalle
Edad / contexto41 años; coordinadora de la despensa; mira el inbox antes de abrir el local
DigitalMedia–alta; desktop para la cola; móvil para devolver llamadas
GoalsVer NEW del día, contactar, marcar RECEIVED o PARTIAL en mostrador sin Excel
PainsMensajes sueltos, no sabe quién ya fue avisado, compromiso contado como stock
Quote de diseño“Si está en NEW, es mía. Si está RECEIVED, la caja está aquí. Si no, no invento kilos.”
Email demoines@cesta.barrio / password123

Escenario: Login antes de abrir → inbox → Rosa NEW → escribe / llama → CONTACTED → el sábado RECEIVED (o PARTIAL si trajo menos).

[COMPROBADO] Edad, rol y credencial salen del encargo / brief.

Anti-personas

QuiénPor qué no es target v1
Donante que quiere certificado fiscal / BizumEso es tesorería, no despensa en especie
Logística de banco de alimentos regionalOtro producto (almacén, rutas, lotes)
Familia receptora que pide cestaCESTA no es mostrador de reparto a familias
Community manager que quiere feed de “historias de impacto”Rompe H-TABLE

5. JTBD y user stories

Job principal (VECINO)

Cuando quiero ayudar a la despensa del barrio,
quiero ver qué falta y dejar una cantidad con mi nombre,
para que coordinación me contacte sin perder el mensaje y sin creer que ya entregué.

Job principal (STAFF)

Cuando se acercan el día de entrega y empiezan a llegar compromisos,
quiero verlos con ítem, cantidad y estado,
para contactar antes de abrir y anotar recibido o parcial sin inventar stock.

Jobs secundarios

JobRol
Distinguir pedido, comprometido y huecoVECINO
Referir un compromiso por código corto en el mostradorAmbos
Ver cuántas NEW hay antes de abrirSTAFF
Marcar entrega parcial si llegó menosSTAFF
Cancelar duplicado o no-showSTAFF

Stories Must (v1)

IDStoryAC
US1Como vecina, quiero ver las necesidades en una tablaGET /api/needs; columnas foto, ítem, pedido, comprometido, hueco
US2Como vecina, quiero abrir la ficha con unidad y huecoGET :slug; foto real; CTA
US3Como vecina, quiero enviar compromiso sin cuentaPOST 201 + redirect /ok/:code
US4Como vecina, quiero un código y saber que no he entregado todavíacode en /ok/:code + copy disclaimer
US5Como STAFF, quiero entrar con email/passwordJWT + redirect /inbox
US6Como STAFF, quiero listar compromisosGET list JWT
US7Como STAFF, quiero cambiar estado incl. RECEIVED y PARTIALPATCH { status }
US8Como cualquiera, quiero ver empty / error / loadingUI-07, UI-08, UI-09
US9Como vecina en móvil, quiero la tabla usable a ~390pxUI-10; filas apiladas; targets ≥44px

MoSCoW (v1 L1)

PrioridadÍtems
MustTabla home, ficha, form, success, login, inbox, 5 estados, empty/loading/error, mobile tabla + rail
ShouldHueco visible en cada fila; labels ES de enums; rail compacto en 390
CouldCopiar código al portapapeles; click-to-call; persistir scroll de tabla
Won’tPagos, cuenta vecino, mapa, search, email transaccional, inventario ERP

6. Journey (VECINO → STAFF)

FaseActorAcciónTouchpointEmoción [HIPÓTESIS]
1 MirarRosaAbre CESTA; ve la tablaHome tabla + railClaridad, baja fricción
2 ElegirRosaToca Aceite de oliva 24 LFila → fichaControl
3 EntenderRosaLee unidad L, hueco, “el sábado”/necesidades/aceiteConfianza o duda de hueco
4 ComprometerRosaForm + disclaimer/necesidades/aceite/comprometerPrudencia
5 AcuseRosaVe CE-0816-001/ok/CE-0816-001Alivio (no euforia de “ya entregué”)
6 ContactoInésVe NEW, escribeInboxControl operativo
7 CierreInésRECEIVED o PARTIALPATCHCierre honesto

Momentos de verdad

  1. Tabla en el primer viewport — si hay que “descubrir la marca”, se rompe S2. [HIPÓTESIS]
  2. Disclaimer en form y success — si falta, H2 falla.
  3. Código CE- — prueba de “ha llegado” para el mostrador y el mensaje.
  4. RECEIVED / PARTIAL visibles — evita la mentira social del “ya cuenta como stock”.
  5. Rail no esconde Inbox — Inés antes de abrir no busca un menú hamburger. [HIPÓTESIS]

7. Service blueprint (resumen)

CapaElementos
Frontstage VECINORail, tabla, ficha, form, success
Frontstage STAFFLogin, inbox, acciones de estado
BackstageMensaje / llamada / caja física en el local (fuera de app)
SistemasNest API :3016, Neon jolly-night-99671179, JWT, Angular tabla
SoportesSeed 5 necesidades + fotos, Paper, docs
Fallos0 líneas; 401 sin token; 404 slug; red caída → UI error

Fallos de servicio y respuesta de diseño

FalloEvidencia de UIRecuperación
Sin líneas abiertasUI-07 Empty tabla“Ahora mismo no hay líneas abiertas.”
Inbox vacíoUI-07 Empty inbox“Cuando alguien se comprometa, aparece aquí.”
API caídaUI-08 ErrorReintentar
Latencia tablaUI-09 LoadingSkeleton de 5 filas
Form inválidoInline fieldNo navegar a success
Trajo menos (ops)STAFF marca PARTIALNo se inventa el kilo que no llegó [H3]

8. Matriz hallazgo → requisito → feature

HallazgoTipoRequisitoFeature v1
Llega con “qué falta”[SUPUESTO] S2Tabla es la homeUI-01 / H-TABLE
Chat no muestra hueco[HIPÓTESIS] H1Columnas pedido / comprometido / huecoGET needs con agregados
Sin acuse[HIPÓTESIS]Código oralSuccess + CE-
Confunden compromiso con entrega[HIPÓTESIS] H2Copy irrenunciableDisclaimer form/success
Compromiso contado como stock[HIPÓTESIS] H3RECEIVED es acuseEnum + no mutar pedido
STAFF opera antes de abrir[SUPUESTO]Rail + inbox densoS-RAIL / UI-06
PII de vecinos[DECISIÓN]Auth JWT STAFFLogin + guards API
L1 compacto, no CRM[DECISIÓN]Un rol STAFF; needs seedSin cuenta VECINO, sin CMS

9. Preguntas abiertas (no bloquean v1)

IDPreguntaCómo se resolvería después
Q1¿Una cola por despensa o varias sedes?venueId + scope de User L2+
Q2¿Email automático “hemos recibido tu compromiso”?Hook post-create; copy ya promete contacto
Q3¿Checkbox RGPD + política?Legal L1+; minimización ya aplicada
Q4¿El hueco a 0 bloquea el POST?[DECISIÓN v1] no; STAFF decide; copy avisa
Q5¿PARTIAL ajusta quantity o solo el badge?Badge + nota; ajuste numérico = L1+
Q6¿Franja de entrega es solo sábado?[SUPUESTO de demo] sábado 10–13; no es horario oficial de un local real

10. Plan de research futuro (si hubiera campo real)

MétodoMuestra orientativaPregunta
Test de usabilidad tabla5–6 vecinas 40–70¿Leen el hueco en < 20 s?
Shadowing STAFF 1 sábado1 despensa¿El inbox sustituye el WhatsApp del mostrador?
Revisión de no-showsCompromisos RECEIVED vs NEWValidar H2 (cajas fantasma)
Card sort de unidades6–8 participantes¿kg / L / ud / packs se entienden?

Estos métodos no se han ejecutado. No se reportan hallazgos como si lo hubieran sido.

11. Síntesis

CESTA se diseña como lista de lo que falta con cola de compromiso, no como crowdfunding, no como inventario y no como feed de impacto.
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 — CESTA

1. Principios de IA

PrincipioAplicación
Table firstLa home es la tabla de necesidades. No hay landing de marca por delante.
Fila → ficha → compromisoProfundidad 2 desde la tabla hasta el form
Público vs STAFFTabla y form abiertos; inbox solo autenticado
Lenguaje de dominioNecesidad, hueco, compromiso, entrega — no “SKU”, “lead CRM”, “donación online”
Código visibleCE-… en success e inbox para referencia oral / mostrador
Honestidad de estadoCompromiso ≠ entrega; RECEIVED / PARTIAL son ciudadanos de primera
Rail siempre a manoS-RAIL: iconos + panel; no mega-menú

2. Sitemap

/                                      Tabla de necesidades (público)  H-TABLE
/necesidades/:slug                     Ficha de necesidad (público)
/necesidades/:slug/comprometer         Formulario de compromiso (público)
/ok/:code                              Success post-compromiso (público)
/login                                 Login JWT STAFF
/inbox                                 Lista de compromisos (auth STAFF)
/**                                    → redirect /

Árbol por audiencia

AudienciaNodos relevantes
VECINOTabla → Ficha → Comprometer → Ok
STAFFLogin → Inbox (y tabla pública si consulta hueco)
AmbosWordmark → / (STAFF autenticada: rail Inbox)

[COMPROBADO] Seis rutas del encargo. No se inventan /inbox/:id, /buscar, /mapa, /donar.

3. Navegación

Rail de iconos (S-RAIL)

SlotDestinoVisibilidadNotas
Lista/TodosIcono tabla; estado activo en home
Inbox/inboxSTAFF; si guest → /login?next=/inboxCola operativa
Entrar/login si guest; acción salir si authTodosAcceso STAFF

[DECISIÓN] El rail es la única navegación primaria. Tres destinos: encaja L1 compacto. No hay top-nav de 7 ítems, no dock flotante (RONDA), no command search (PIZARRA).

Desktop ≥960px: rail vertical izquierdo (~56–64px) + panel de tabla.
Mobile ~390px: rail compacto inferior o superior de iconos; la tabla se apila (UI-10). [DECISIÓN] En 390 el rail no compite con un hamburger.

Pública (además del rail)

ElementoDestinoNotas
Wordmark CESTA/Spectral; no “fundación + hero”
Fila de necesidad/necesidades/:slugToda la fila es el enlace
Comprometer/necesidades/:slug/comprometerPrimary en ficha
Entrada STAFF discreta/loginTambién vía rail

STAFF (autenticado)

ElementoDestinoVisibilidad
Inbox/inboxSTAFF
Badge rolSTAFF
NombreInés MarzalSTAFF
Salirlimpia token → /loginSTAFF
Acciones de estadoen la propia lista /inboxSTAFF · no hay ruta detalle

[DECISIÓN] L1: el PATCH vive en la fila del inbox. No se añade /inbox/:id.

4. Inventario de contenido

PantallaContenidos
Tabla /Wordmark, eslogan, tabla (foto, ítem, pedido, comprometido, hueco), empty, loading, error, rail
FichaHero foto, título, unidad, pedido / comprometido / hueco, nota de entrega, disclaimer, CTA
ComprometerTítulo ítem, disclaimer, campos (nombre, email, cantidad, nota), submit
SuccessMensaje, código CE-, “no es entrega”, siguiente paso (sábado demo)
LoginTítulo panel despensa, email, password, submit, error
InboxLista densa: código, nombre, ítem, cantidad, status, acciones, empty, error

5. Taxonomía

RequestStatus

Status APILabel UISemántica
NEWNuevoAcaba de llegar; sin contacto
CONTACTEDContactadoSTAFF inició llamada / mensaje
RECEIVEDRecibidoLa caja está en el local (acuse, no stock ERP)
PARTIALParcialLlegó menos de lo comprometido
CANCELLEDCanceladoNo-show, duplicado, desiste

Orden de inbox: createdAt descendente (más reciente primero).
[DECISIÓN] No hay WAITLIST: el hueco es numérico, no una silla.

NeedUnit

APILabel UISeed
kgkgarroz
LLaceite, leche
ududtomate
packspackspañales

Need (líneas seed)

slugTítuloPedidoUnidad
arrozArroz redondo40kg
aceiteAceite de oliva24L
tomateTomate en conserva36ud
panalesPañales T312packs
lecheLeche entera48L

[COMPROBADO] Slugs y cantidades salen del encargo.

6. Modelo mental vs UI

Modelo mentalRepresentación
“¿Qué falta?”Tabla home, columna hueco
“¿Cuánto aceite?”Ficha: pedido / comprometido / hueco + unidad L
“Traigo el sábado”Form /comprometer + disclaimer
“Me dieron un número”code CE-
“Lista del día de gente nueva”Inbox + badge NEW
“Ya le llamé”CONTACTED
“La caja está aquí”RECEIVED
“Trajo menos”PARTIAL
“No vino”CANCELLED

7. Query string

L1 table-first no usa facetas de catálogo.
Único query admitido: /login?next=/inbox (redirect post-login).

[DECISIÓN] Sin ?q= ni chips. Eso sería H-SEARCH (PIZARRA).

8. Rutas API alineadas a IA

UIAPI
TablaGET /api/needs
FichaGET /api/needs/:slug
Submit compromisoPOST /api/requests { needSlug, name, email, quantity, note }
LoginPOST /api/auth/login
Inbox listGET /api/requests JWT
Cambiar estadoPATCH /api/requests/:id { status }

[COMPROBADO] Contrato del encargo. No se documentan /api/requests/stats ni POST /api/needs como Must.

9. Decisiones de IA descartadas

IdeaPor qué no en L1 v1
/explorar editorial + tabla secundariaRompe H-TABLE
Área “mis compromisos” por email mágicoCuenta de facto; authz delicada
/inbox/:idL1 compacto; acciones en lista
Search / chips de categoríaH-SEARCH de PIZARRA
Mapa de despensasH-MAP de DERIVA
Feed de entregasH-FEED de RONDA
Wizard de 4 pasos de donaciónF-ONB; el form es 1 pantalla
/donar con importeRompe S2 / S4

04-user-flows.md

04 — Flujos de usuario — CESTA

Convenciones

  • Actor: Guest (VECINO) | STAFF
  • Éxito: resultado observable
  • Errores: UI + código HTTP cuando aplica
  • Flujo canónico: F-LEAD (compromiso público → cola staff)

F1 — Descubrimiento por tabla (VECINO)

/  → tabla de necesidades (H-TABLE)
   → GET /api/needs
   → filas densas  |  empty  |  error  |  skeleton
   → click fila → /necesidades/:slug
   → rail Inbox / Entrar → /login  (si guest)
PasoAcciónSistema
1Aterriza; la primera fila (foto + hueco) es el primer focoRender home
2Recorre ítem, pedido, comprometido, huecoGET needs
3Abre fichaRouter
4Rail permanece visibleShell S-RAIL

Éxito: al menos una ficha alcanzable, o empty accionable.
AC: no hay que hacer scroll de “marca” para llegar a la primera fila.

Errores / estados

CasoComportamiento
0 necesidadesUI-07: “Ahora mismo no hay líneas abiertas.”
Red / 5xxUI-08 + Reintentar
Primera cargaUI-09 skeleton de 5 filas

F2 — Entender necesidad (VECINO)

/necesidades/:slug → GET /api/needs/:slug
                   → foto, unidad, pedido, comprometido, hueco, disclaimer
                   → CTA "Comprometer" → /necesidades/:slug/comprometer
                   → 404 slug → mensaje + volver a la tabla
PasoAcciónSistema
1Lee qué falta y en qué unidadNeed
2Contrasta huecorequestedQty − committedQty
3Decide comprometer o volver a la tablaRouter

Éxito: CTA visible; disclaimer “comprometer no confirma entrega” visible antes del form.
Error: 404 si slug inexistente.

AC ficha

#Criterio
1Foto real del imageKey (no color sólido)
2Título + unidad (kg / L / ud / packs)
3Pedido, comprometido y hueco visibles
4Disclaimer visible sobre el fold en desktop
5CTA “Comprometer” (aunque hueco = 0: copy avisa)

[DECISIÓN] Hueco 0 no oculta el CTA: el vecino puede comprometer de más; STAFF decide. Copy: “El hueco puede haberse cubierto; te contactamos igual.”


F3 — Comprometer (VECINO, público)

/necesidades/:slug/comprometer
  → validación cliente
  → POST /api/requests {
        needSlug,
        name,
        email,
        quantity,
        note
     }
  → 201 Request { code, status: NEW, ... }
  → /ok/:code
CampoValidación cliente (mín.)API
needSlugrequired (de la ruta)existe en DB
namerequired, min 2@MinLength(2)
emailrequired, email@IsEmail()
quantityrequired, number > 0@IsNumber() @Min(0.01) entero si unidad ud/packs
noteoptionaldefault "" / @MaxLength(400)

Éxito: registro status=NEW, código CE-MMDD-NNN, pantalla success.
No hay cuenta de vecino en v1.

Errores

CasoComportamiento
Validación DTO400 + mensaje de campo
needSlug inexistente400 / 404
Red caídaError de red en form; no navegar a /ok/…
Email mal formado400
Doble submitBotón disabled mientras pending
quantity ≤ 0400

[DECISIÓN] No se bloquea el POST aunque hueco sea 0. Bloquear en cliente mentiría sobre el mostrador (Q4).

[COMPROBADO] Body del POST = { needSlug, name, email, quantity, note } según encargo.


F4 — Success (VECINO)

/ok/:code → copy de acuse + code
          → “Esto no confirma la entrega”
          → CTA volver a la tabla / ir a la ficha
PasoAcciónSistema
1Lee acuse y código (param de ruta)UI
2Conserva el código (foto, papel, nota)Fuera de app
3Espera llamada / mensaje de coordinaciónFuera de app

AC: el código es seleccionable; el disclaimer es visible sin scroll en desktop.
Si se entra a /ok/algo-que-no-existe sin haber POST: se muestra el código de la URL + copy genérico; no se inventa un registro. [DECISIÓN] Success es acuse de UI, no lookup público (lookup = L2+).


F5 — Login JWT (STAFF)

/login → POST /api/auth/login { email, password }
      → 200 { accessToken, user } → localStorage → /inbox  (o ?next sanitizado)
      → 401 → mensaje error en form
CampoValidación clienteAPI
emailrequired, email@IsEmail()
passwordrequired, min 6@MinLength(6)

Éxito: token guardado; user role=STAFF, nombre Inés Marzal.
Credencial demo: ines@cesta.barrio / password123.

CasoComportamiento
Credenciales inválidas401 + mensaje
Red caídaError de red en UI
Token caducado en inbox401 en GET → re-login

F6 — Inbox (STAFF)

/inbox (token en cliente)
  → GET /api/requests
  → Render filas + badges + acciones
CasoComportamiento
Lista con itemsFilas: code, nombre, ítem, quantity + unidad, status, createdAt
Lista vacíaEmpty: “Nadie se ha comprometido todavía.”
Fallo red / 401Error + reintentar / re-login

[DECISIÓN] Sin stats endpoint en v1 (no está en el contrato API). Recuentos, si se muestran, se derivan del array.


F7 — Cambio de estado (STAFF, en la lista)

/inbox
  → PATCH /api/requests/:id { status }
  → 200 Request actualizado

Transiciones típicas (máquina simple L1)

DesdeHaciaIntención
NEWCONTACTEDInés inició contacto
CONTACTEDRECEIVEDLa caja está en el local
CONTACTEDPARTIALLlegó menos de lo comprometido
NEWRECEIVEDLlegó sin aviso previo (mostrador)
*CANCELLEDNo-show, duplicado, desiste
PARTIALRECEIVEDCompletó después
RECEIVEDCANCELLEDCorrección (devolución / error)
**Corrección operativa (API acepta enum)

Éxito: badge actualizado en la fila.
Errores: 404 id; 401 sin token; 400 status inválido.

Regla de integridad H3 (ops, no trigger DB v1): RECEIVED / PARTIAL no mutan requestedQty del Need. El hueco se calcula por suma de quantity no CANCELLED. Ajuste fino de kilos = L1+.

[COMPROBADO] PATCH body = { status } sobre /api/requests/:id.


F8 — Logout (STAFF)

Click "Salir" (rail) → limpia localStorage (token + user) → /login

Matriz de errores global

CódigoCuándoUI
400DTO inválidoMensaje campo / genérico
401Sin/mal tokenRe-login
404id o slug no existeMensaje + volver
5xx / networkAPI caídaError + retry

Flujos fuera de alcance v1

  • Registro / recuperación de password de STAFF
  • “Mis compromisos” por código público
  • Notificación email automática
  • Filtros multi-criterio en inbox
  • Detalle /inbox/:id
  • Pago / Bizum
  • Alta de Need por UI

05-data-model.md

05 — Modelo de datos — CESTA

1. Visión general

Dominio L1 de tabla de necesidades + cola de compromiso:

EntidadTabla SQLPropósito
UserusersIdentidad de coordinación (rol STAFF)
SessionsessionsSesión JWT / token persistido (Should)
NeedneedsLínea de la despensa (oferta de lo que falta)
RequestrequestsCompromiso ligado a una necesidad

Base: PostgreSQL (Neon project jolly-night-99671179) · IDs: cuid() o entero serial.
[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

ValorDescripción
STAFFOperadora del inbox; único rol autenticado v1

RequestStatus

ValorDescripción
NEWRecién creado por el form público
CONTACTEDSTAFF ha iniciado contacto
RECEIVEDEntrega confirmada en el local (acuse, no stock ERP)
PARTIALLlegó menos de lo comprometido
CANCELLEDAnulado

NeedUnit

kg | L | ud | packs

3. Diagrama ER (texto)

users
  id, email, password_hash, name, role(STAFF)
  created_at, updated_at
  1 ──< sessions

sessions
  id, user_id → users
  token_hash, expires_at
  created_at

needs
  id, slug (unique), title, summary, description
  unit, requested_qty, image_key
  created_at, updated_at
  1 ──< requests

requests
  id, code (unique)
  need_id → needs          -- o need_slug
  name, email
  quantity
  note
  status (default NEW)
  created_at, updated_at

No hay FK entre requests y users en v1: el form es anónimo. STAFF opera sobre el conjunto global.

4. Tablas

users

CampoTipoConstraints
idTEXTPK
emailTEXTUNIQUE NOT NULL
password_hashTEXTNOT NULL (bcrypt)
nameTEXTNOT NULL
roleTEXTNOT NULL DEFAULT 'STAFF'
created_atTIMESTAMPTZDEFAULT now()
updated_atTIMESTAMPTZDEFAULT now()

sessions

CampoTipoConstraints
idTEXTPK
user_idTEXTNOT NULL FK → users(id) ON DELETE CASCADE
token_hashTEXTNOT NULL
expires_atTIMESTAMPTZNOT NULL
created_atTIMESTAMPTZDEFAULT 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.

needs

CampoTipoConstraints
idTEXTPK
slugTEXTUNIQUE NOT NULL, kebab-case
titleTEXTNOT NULL
summaryTEXTcard / fila (~140)
descriptionTEXTficha; default ''
unitTEXTNOT NULL (kg | L | ud | packs)
requested_qtyNUMERIC> 0 · pedido de la despensa
image_keyTEXTfilename (arroz.jpg …)
created_at / updated_atTIMESTAMPTZ

committed_qty y gap no se persisten: se calculan. [DECISIÓN]

requests

CampoTipoConstraints
idTEXT o INTPK
codeTEXTUNIQUE, formato CE-MMDD-NNN
need_idTEXTFK Need (o need_slug)
nameTEXTNOT NULL
emailTEXTNOT NULL
quantityNUMERIC> 0
noteTEXTdefault ''
statusTEXTdefault NEW
created_at / updated_atTIMESTAMPTZ

5. Reglas de integridad y negocio

ReglaImplementación
Create públicoPOST /api/requests sin guard
List / patchJWT → 401 sin token
Catálogo públicoGET /api/needs y :slug sin guard
Código únicoCE-MMDD- + secuencia 001–999 del día [DECISIÓN] Europe/Madrid (fallback +1 si colisión)
Status defaultNEW al crear
PasswordNunca en claro; solo password_hash
NoteCoalesce a "" si omitida
Orden listado requestscreated_at desc
Orden tabla needsestable: seed order o title asc (no “destacados”)
ComprometidoSUM(quantity) WHERE status <> 'CANCELLED'
HuecoGREATEST(0, requested_qty − comprometido)
RECEIVED vs pedidoNo muta requested_qty (H3)
POST con hueco 0Permitido (Q4)

6. Contratos API (resumen)

GET /api/needs (público)

Response 200: Need[] con campos de fila + agregados:

{
  "slug": "aceite",
  "title": "Aceite de oliva",
  "unit": "L",
  "requestedQty": 24,
  "committedQty": 3,
  "gap": 21,
  "imageKey": "aceite.jpg",
  "summary": "…"
}

JSON camelCase hacia el web. [DECISIÓN]

GET /api/needs/:slug (público)

Need o 404. Incluye description.

POST /api/requests (público)

Body

CampoTipoReq
needSlugstring
namestring
emailstring
quantitynumber
notestringno

Response 201: Request + need embebido mínimo (title, slug, unit) + code + status: "NEW".

[COMPROBADO] Campos del body = encargo.

POST /api/auth/login

Body: { email, password }
Response 200: { accessToken, user: { id, email, name, role } }

GET /api/requests (JWT)

Array Request + need, createdAt desc. Sin token → 401.

PATCH /api/requests/:id (JWT)

Body: { status: RequestStatus }
Response: Request actualizado. Sin token → 401.

[COMPROBADO] PATCH sobre :id con { status }, no /status.

7. Seed de referencia (2026-08-16)

STAFF

NombreEmailPassword
Inés Marzalines@cesta.barriopassword123

[COMPROBADO] Credencial del encargo.

Needs

slugTítuloUnidadPedidoAsset
arrozArroz redondokg40arroz.jpg
aceiteAceite de olivaL24aceite.jpg
tomateTomate en conservaud36tomate.jpg
panalesPañales T3packs12panales.jpg
lecheLeche enteraL48leche.jpg

[COMPROBADO] Cinco líneas del encargo.
Textos de summary / description son contenido de producto demo, no inventario de una despensa real. [SUPUESTO de catálogo]

slugSummary demo
arrozBase de la cesta semanal. Preferible redondo, paquete cerrado.
aceitePara cocinar. Garrafa o botellas; anotar litros.
tomateLatas o botes. Cuenta por unidad.
panalesTalla 3. Pack cerrado.
lecheEntera, brick. Anotar litros.

Requests

CódigoNombreNeedQtyEstadoNota demo
CE-0816-001Rosa Belmonteaceite3NEW“Paso el sábado por la mañana.”
CE-0816-002Luis Arandaarroz5CONTACTED
CE-0816-003Marta Gilleche6RECEIVEDEntregado viernes
CE-0816-004Youssef Amranitomate8PARTIALTrajo 4; queda pendiente
CE-0816-005Pilar Sotopanales2CANCELLEDNo puede este sábado

El sufijo de fecha del código de seed es el día del caso; la generación runtime usa la fecha Europe/Madrid del servidor.

8. Evolución posible (no implementada)

CambioNivel
venueId + scope de STAFFL2
Ajuste numérico de quantity en PARTIALL1+
Check DB / transacción de huecoL2
CMS Need autenticadoL2 / L3
User VECINO con historialL3
Soft delete + audit log de statusL2

06-tech-stack.md

06 — Stack tecnológico — CESTA

1. Visión

CapaTecnologíaNotas
FrontendAngular standalone + signalsPuerto 4200
EstilosTailwind CSSTokens botanical (hueso/tierra/musgo/hoja/saco/ficha)
BackendNestJS TypeScriptPuerto 3016, prefijo /api
PersistenciaPostgreSQL (ensureSchema o Prisma)Tablas users, sessions?, needs, requests
DBNeon PostgreSQLproject jolly-night-99671179
AuthJWTRol STAFF
DiseñoPaper01M04P0VFWGX1DQVSCR5TPWM9Z
Repo/Users/cristian/orca/cesta-app/GitHub Criscode2022/cesta-app

Stack fijo del cron (D-P0-03). No React/Next/Firebase.
[COMPROBADO] Puerto 3016, Neon, Paper y repo constan en el encargo.

2. Estructura

cesta-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:

ScriptAcción
apiArranca Nest en :3016
start / webArranca Angular en :4200

3. API pública vs JWT

MétodoRutaAuth
GET/api/needsPúblico
GET/api/needs/:slugPúblico
POST/api/requestsPúblico
POST/api/auth/loginPúblico
GET/api/requestsJWT STAFF
PATCH/api/requests/:idJWT STAFF

CORS: origen http://localhost:4200 en local.

[COMPROBADO] Superficie API del encargo. No se añaden Must extra (/stats, POST /needs).

4. Justificación

ElecciónRazón
Angular + NestAlineación con la serie daily y el handoff del estudio
Neon serverlessVolumen bajo (S1: 8–20 líneas; demo 5); no cola Redis
JWT STAFFD-P1-05: panel interno cerrado desde el día 1
Agregados de hueco en servidorSource of truth = SQL SUM; la tabla no inventa stock
Puerto 3016Evitar colisión con RONDA :3015 / DERIVA :3014 / PIZARRA :3013

5. Variables

.env local (nunca git):

VariableServicioDescripción
DATABASE_URLAPINeon jolly-night-99671179
JWT_SECRETAPIFirma tokens
PORTAPIopcional, 3016
JWT_EXPIRESAPIopcional (p. ej. 12h)

.env.example solo placeholders.
Web: URL de API en ApiService (default http://localhost:3016/api).

6. Dependencias de producto (mínimas)

API

  • @nestjs/common / core / platform-express
  • @nestjs/jwt (o verificación manual)
  • bcrypt (o bcryptjs)
  • cliente Postgres (pg) o Prisma
  • class-validator + class-transformer

Web

  • Angular standalone
  • Tailwind 3
  • fuentes: Spectral + Manrope (Google Fonts o self-host)

7. Arranque (contrato)

cd /Users/cristian/orca/cesta-app
# instalar apps independientes (npm --prefix o pnpm --filter)
# apps/api/.env → DATABASE_URL + JWT_SECRET + PORT=3016
# migrate / ensureSchema + seed
# api → http://localhost:3016
# web → http://localhost:4200

8. Lo que este stack no es

NoPor qué
npm workspacesAP-06
Firebase AuthStack fijo JWT + Neon
Next.js / ReactD-P0-03
Pasarela de pagoS4
Elasticsearch5 filas no lo necesitan

9. Nota sobre el repo existente

[COMPROBADO] Existe /Users/cristian/orca/cesta-app y el remoto Criscode2022/cesta-app.
El scaffold puede contener restos de un producto anterior (rutas /mesas, GET /tables). Esta suite define el contrato CESTA; 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 · CESTA

Mood: botanical (musgo wash #E6EFDD × soil). No charity naranja. Superficie = tabla, no hero 2-col.

Spectral display + Manrope UI. Rail 72px tierra. Hueco en #B23A2F. Fotos de despensa reales por línea.

08-design-system.md

08 — Design system · CESTA

Tokens: bone #E6EFDD soil #1C2418 moss #3D6B4A leaf #7A9A6A sack #C4B49A alert #B23A2F.

Componentes: rail, tabla (5 columnas fijas), fila con foto 56×40, CTA suelo, estados empty/loading/error.

09-content-guide.md

09 — Guía de contenido — CESTA

1. Voz

Cercana de barrio, concreta, de mostrador. Tuteo. Segunda persona.
Sin jerga de fundraising (“impacto premium”), sin sede electrónica, sin “haz tu donación ahora”.

El producto informa y recoge un compromiso, no vende una causa.

2. Palabras permitidas / prohibidas

UsarNo usar
Compromiso / comprometerReserva, donación online, “ya está entregado”
Coordinación te escribe“Caja garantizada”, “ticket emitido”
HuecoStock, inventario, SKU
Necesidad / líneaProducto, item shop, campaña
En especie / traerBizum, socio, certificado fiscal
Lo que falta, en una lista“Descubre nuestro ecosistema de impacto”
Código CE-Referencia UUID, localizador
Recibido / RECEIVEDInscrito, sentado, enrolled
Coordinadora / STAFFCommunity manager, fundraiser

3. Microcopy clave (literales de producto)

SuperficieCopy
EsloganLo que falta, en una lista.
HomePedido, comprometido y hueco de esta semana.
Fila CTA implícitoToda la fila es el enlace; no “Ver más”
Disclaimer fichaComprometer no confirma la entrega. La caja se cuenta cuando llega al local.
Form títuloComprometer {título}
Form submitEnviar compromiso
Form disclaimerEsto es un compromiso, no una entrega confirmada.
Success titularCompromiso anotado.
Success cuerpoGuarda este código. Todavía no has entregado. Inés te contacta antes de abrir.
Success códigoCE-0816-001 (seleccionable)
Empty tablaAhora mismo no hay líneas abiertas.
Empty inboxNadie se ha comprometido todavía.
Error tablaNo hemos podido cargar la lista.
Error retryReintentar
Login titularCoordinación
Login submitEntrar
Login errorEmail o contraseña no valen.
Inbox titularCompromisos
PATCH CONTACTEDMarcar contactado
PATCH RECEIVEDRecibido
PATCH PARTIALParcial
PATCH CANCELLEDCancelar
Rail ListaLista
Rail InboxInbox
SalirSalir

[DECISIÓN] El disclaimer aparece tres veces: ficha, form, success. No es opcional.

4. Códigos

Formato CE-MMDD-NNN. Se lee en voz alta en el mostrador: “ce, ocho dieciséis, cero cero uno”.
No UUID. No QR obligatorio en v1.

5. Nombres propios del seed

NombreUso
Rosa BelmonteVECINO; compromiso aceite
Inés MarzalSTAFF; copy de “te escribe Inés” en success de seed

No inventar reseñas, CIF ni horarios oficiales de una despensa real. [SUPUESTO de catálogo demo]
Franja demo: sábado 10–13. No se afirma como horario de un local existente.

6. Tono por estado

StatusFrase inbox (STAFF)Frase que Inés podría mandar (fuera de app)
NEWSin contacto
CONTACTEDEscrita / llamada“He visto tu CE-0816-001. ¿Traes los 3 L el sábado?”
RECEIVEDCaja aquí“Recibido. Gracias. Trae el código por si acaso.”
PARTIALLlegó menos“Anotamos parcial. Si puedes completar, avísame.”
CANCELLEDFuera“Cancelamos este compromiso.”

La app no envía esos mensajes en v1. El copy solo prepara el habla.

7. Capitalización y números

  • Wordmark: CESTA en versales.
  • Cantidades: 40 kg, 24 L, 36 ud, 12 packs (espacio fino entre número y unidad).
  • Hueco: 21 L (misma unidad que el pedido).
  • No mezclar “litros” en UI si la unidad es L; en ficha se puede expandir “litros”.

8. Idioma

es-ES. Fácil lectura. EN no en v1.
No mezclar “need” en UI. En API sí: needSlug, needs.

9. Accesibilidad de copy

  • No transmitir estado solo con color (“el rojo significa parcial”).
  • Badge lleva texto: Nuevo, Contactado, Recibido, Parcial, Cancelado.
  • Alt de fotos: “Botellas de aceite de oliva”, no “imagen1”.

10. Piezas que no se escriben

NoMotivo
Importes, IBAN, “dona 10 €”CESTA no cobra
“Entrega confirmada al instante”Rompe H2
Ranking de donantesReputación, otro producto
Lorem / “lorem ipsum aceite”Prohibido en el case

10-accessibility.md

10 — Accesibilidad — CESTA

Objetivo: WCAG 2.2 AA. No se declara conformidad legal certificada.
Este documento cubre la UI (tabla, form, rail, inbox). No audita la accesibilidad física del local demo. [SUPUESTO]

1. Decisiones

TemaDecisión
Tipo UIManrope ≥16px body
DisplaySpectral no se usa por debajo de 24px
ContrasteTierra #1C2418 sobre #E6EFDD / #C4B49A
CTA musgoTexto hueso sobre #3D6B4A
Ficha / PARTIALTexto hueso ≥14px bold
FocoOutline 2px musgo, offset 2px, visible (no outline-none global)
Rail3 botones reales (<a> / <button>), no divs clicables
Tabla desktop<table> + <caption> o aria-label; cabeceras <th scope="col">
Tabla móvilLista de <article> apilados, no tabla de 5 columnas ilegible
Imágenesalt descriptivo del ítem
FormLabels visibles, no placeholder-only
ErroresTexto, no solo color
MobileTargets ≥44px (rail 48×48, CTA h-44)
MovimientoFade 160ms; prefers-reduced-motion: reduce → 0
Live regionsaria-live="polite" en empty/error de la tabla

2. Contraste (comprobación de diseño)

ParUsoNota
#1C2418 / #E6EFDDBodyPasa AA y AAA cuerpo
#1C2418 / #C4B49AThead / rail idlePasa AA
#E6EFDD / #3D6B4ACTA / RECEIVEDPasa AA para texto ≥14px bold
#E6EFDD / #1C2418Rail activoPasa AA
#E6EFDD / #B23A2FBadge PARTIALVerificar ≥14px bold; no usar en 12px
#7A9A6A como texto 13px sobre huesoProhibido para body

3. Teclado

FlujoOrden
TablaWordmark → primera fila → … → rail (Lista, Inbox, Entrar)
FichaBack → CTA → rail
FormNombre → email → cantidad → nota → submit → rail
InboxLista (cada fila / acciones) → rail
LoginEmail → password → submit
  • Enter en fila = navegar a ficha.
  • Escape no cierra el rail (no es modal).
  • Focus trap solo si hubiera modal; v1 no tiene modal salvo confirmación CANCELLED (Should).

4. Semántica

PiezaMarkup
Tabla desktop<table> con caption “Necesidades de la despensa”; fila = enlace único o tr + un a que cubre
Tabla móvil<ul> de cards; un <a> por ítem
Cifraspedido, comprometido, hueco anunciados con unidad
Rail<nav aria-label="Principal">
Slot activoaria-current="page"
Form<form> + <label for>
DisclaimerNo es alert; es texto normal + borde
Success código<p><code> seleccionable
Badgestexto visible; no solo icono
Skeletonsaria-busy="true" en el contenedor; aria-hidden en placeholders

5. Lector de pantalla — copy

SituaciónAnuncio
Fila“Aceite de oliva, pedido 24 litros, comprometido 3, hueco 21”
CTA“Comprometer aceite de oliva”
Success“Compromiso anotado. Código ce ocho dieciséis cero cero uno. Todavía no has entregado.”
ErrorAnuncio en live region; foco al banner

6. Producto vs local físico

Lo que CESTA puede hacerLo que no afirma
Unidad, pedido, hueco, código oralRampa, mostrador a altura, bucle magnético del local
Form usable con teclado y zoom 200%Que el local demo sea accesible

[DECISIÓN] v1 no incluye faceta a11y de sede (eso era PIZARRA). El campo note permite “voy con carrito” sin fingir auditoría.

7. Riesgos

RiesgoMitigación
Tabla de 5 columnas en 390UI-10: apilar; no scroll horizontal ciego
Rail tapa el CTA en móvilPadding-bottom del main ≥ 72px
Spectral en meta 12pxProhibido; meta = Manrope 14
Musgo como único estado activo+ aria-current + label “Lista”
Foto sin altChecklist seed: 5 alts
Status bar iOS en PaperNo es producto

8. QA a11y (mínimo)

#PruebaPasa si
1Teclado tabla → ficha → form → successSin trampa; foco visible
2Teclado login → inbox → PATCHAcciones alcanzables
3Zoom 200% móvil 390Rail usable; no solapa inputs
4Lighthouse a11y ≥ 90 en /Sin contrast fails de tokens
5VoiceOver/NVDA: hueco anunciadoFila comprensible
6prefers-reduced-motionSin stagger
7Contraste badges PARTIAL / RECEIVEDTexto hueso bold ≥14

9. Criterios de aceptación

  1. Ningún control del rail o CTA mide menos de 44×44 CSS px.
  2. El disclaimer no depende del color musgo para ser entendido.
  3. Las 5 fotos seed tienen alt no vacío y no genérico.
  4. El foco no se pierde al reintentar un error de red.
  5. En 390 la tabla no obliga a scroll horizontal para leer el hueco.

11-privacy-security.md

11 — Privacidad y seguridad — CESTA

No es un dictamen legal ni un DPIA. Es el contrato de producto para el vertical slice.

1. Datos

DatoTablaClasificaciónUso
Nombre, emailrequestsPIIContactar compromiso
NotarequestsPII opcionalContexto STAFF
quantityrequestsOperativoHueco
Need, estado, códigorequests + needsOperativoCola y acuse
Email STAFF, password hashusersCredencialSolo STAFF
token_hash, expires_atsessionsSecreto de sesiónLogout / caducidad

Sin datos de salud, menores con tutor, geolocalización, ni pagos.

[DECISIÓN] El vecino no crea cuenta. Menos superficie de credenciales.

2. Base (supuesto de diseño)

[SUPUESTO] Interés de la despensa en coordinar entregas en especie + consentimiento de contacto al enviar el form.
El texto del form debe mencionarlo:

Usaremos tu nombre y email solo para escribirte por este compromiso.

No se cede a redes de fundraising, no se usa para marketing de terceros, no se pide IBAN.

3. Minimización

RecogemosNo recogemos en v1
name, emailDNI, fecha de nacimiento
note libreDirección de casa
quantityFoto de perfil, DNI fiscal
Teléfono (no está en el contrato del POST)

[COMPROBADO] Body público = { needSlug, name, email, quantity, note }. Sin phone.

4. Retención

DatoHipótesis operativa
Compromisos12 meses desde created_at; luego borrado o anonimización (name → “—”, email hash)
SessionsHasta expires_at o logout
NeedsMientras la línea esté abierta (archivo = L1+)
Demo NeonNo es producción; se puede resetear

[HIPÓTESIS] 12 meses cubre una temporada de despensa y reclamaciones de “yo escribí”. No es plazo legal afirmado.

5. Autorización

RecursoPúblicoSTAFF JWT
GET needs / :slug
POST requests
GET / PATCH requests401
Password hashesNunca en responseNunca

Guards en API son la autoridad. La UI solo esconde.
[COMPROBADO] D-P1-05: panel interno cerrado desde el día 1.

JWT

  1. Login válido → bcrypt compare → emite JWT (sub, email, role).
  2. Request protegida → verifica firma.
  3. Logout → limpia cliente; si hay sessions, se invalida el sid.

[DECISIÓN] Bearer header, no cookie de primer partido en v1 (demo localhost). XSS sigue siendo el riesgo (no innerHTML de notas sin escape).

6. Seguridad v1

ControlDetalle
TLS hacia NeonConnection string sslmode
Validación DTOsclass-validator
Passwordbcrypt cost ≥ 10
SecretosFuera de git; .env local
Público no lista PIIGET needs sin emails de requests
Rate limit POSTL1+; documentado como hueco
HoneypotL1+
CORSOrigen web conocido

7. Amenazas y respuesta

AmenazaImpactoRespuesta v1
Enumeración de inboxPII de vecinos401 sin token
Spam de compromisosCola inútilValidación; rate-limit L1+
Token robado en localStorageSuplantación STAFFExpiración corta
Overposting statusCaos de huecoEnum cerrado; H3 operativa
IDOR :idVer PII ajenaUn solo STAFF en v1; igual exigir JWT
Seed en producciónCredencial conocidaRotar JWT_SECRET y password si se publica
Lookup /ok/:code como ficha PIIFiltraciónSuccess no lista email; no hay GET público por código

8. Credencial demo

CampoValor
Emailines@cesta.barrio
Passwordpassword123

[COMPROBADO] Encargo. No reutilizar en un deploy público sin rotar.

9. Riesgos residuales (aceptados L1)

  • No hay checkbox RGPD formal (Q3).
  • No hay cifrado de columna PII (TLS en tránsito + Neon en reposo).
  • localStorage XSS.
  • Un STAFF ve todas las solicitudes (cola única de demo).

10. Criterios de aceptación

  1. GET /api/requests sin Authorization401.
  2. PATCH /api/requests/:id sin token → 401.
  3. GET /api/needs no incluye emails de vecinos.
  4. Response de login no incluye password_hash.
  5. .env no se commitea.
  6. /ok/:code no revela email ni nota.

12-analytics.md

12 — Analítica — CESTA

Instrumentación modelo. v1 puede no emitir eventos reales; este doc es el contrato para cuando se encienda.
No se presentan tasas de conversión inventadas.

1. North star

% de compromisos válidos (nombre + email + need existente) que pasan a CONTACTED antes de la franja de entrega (sábado demo).

Por qué no “kilos recaudados” ni “pageviews”:

Métrica vanidosaPor qué no es éxito
Visitas a la tablaUn cartel de WhatsApp también se mira
Hueco 0Puede ser compromiso fantasma (falla H2)
Cuentas creadasEl vecino no tiene cuenta
€ donadosCESTA no cobra

La north star mide contacto honesto a tiempo, alineada a S3/H2.

2. Hipótesis ↔ señales

IDHipótesisEvento / invariante
H1La tabla reduce “¿qué llevo?” vs WhatsApp% sesiones con need_opened
H2Copy “compromiso ≠ entrega” baja no-shows↓ tickets “pensé que ya contaba”; ratio RECEIVED / NEW
H3RECEIVED nunca inventa stockrequested_qty inmutable; hueco = pedido − comprometido

3. Eventos

EventoDóndeProps (sin PII en claro)
table_viewed/result_count
need_openedFila / fichaslug
commit_startedFormslug
commit_submittedPOST 201code, slug, quantity
commit_failedPOST errorreason (validation/network/4xx)
staff_loginLogin 200
staff_login_failed401
inbox_viewed/inboxcount
status_changedPATCH 200from, to, slug

[DECISIÓN] No enviar email a analytics. El code es el identificador.

4. Funnel

Land tabla → need_opened → commit_started → commit_submitted
           → staff_login → status_changed(CONTACTED) ≤ franja
           → status_changed(RECEIVED)  |  PARTIAL
PasoDefinición
Landtable_viewed
Interésneed_opened / table_viewed → H1
Intencióncommit_started / need_opened
Capturacommit_submitted / commit_started
OpsCONTACTED antes de la franja → North star
CierreRECEIVED o PARTIAL; pedido inmutable → H3

5. Invariantes (salud, no vanity)

CheckQuery conceptualAlerta
H3requested_qty cambió tras un RECEIVEDCualquier need
Códigos únicosduplicate code> 0
401 inesperadosGET needs 401No debe ocurrir
5xxcualquier ruta> 1%

6. Dimensiones

DimensiónValores
slugarroz, aceite, tomate, panales, leche
unitkg, L, ud, packs
viewportmobile ≤480, desktop

Sin user-id de vecino.

7. Privacidad de analítica

  • Sin fingerprinting.
  • Sin email en claro.
  • IP no se guarda en el plano de producto.
  • Eventos de demo pueden quedarse en consola.

8. Qué no medimos en v1

NoMotivo
HeatmapsCraft, no research de campo fingido
NPSMuestra nula
“Kilos en almacén”No hay inventario ERP
Followers de la ONGNo es red social
Importe donadoNo hay dinero

9. Criterios de aceptación (si se implementa tracker)

  1. Cada evento de la tabla §3 tiene nombre estable snake_case.
  2. commit_submitted incluye code con prefijo CE-.
  3. Ningún payload de analytics incluye email.
  4. El dashboard interno (si existe) no es Must del L1.

13-qa-test-plan.md

13 — Plan de pruebas — CESTA

1. Smoke obligatorio (D-P1-06)

#AcciónEsperado
1GET /api/needs200 · array length ≥ 5 · slugs seed presentes
2POST /api/requests { needSlug, name, email, quantity, note }201 · code con prefijo CE- · status=NEW
3POST /api/auth/login ines@cesta.barrio / password123200 · accessToken · user.role=STAFF
4GET /api/requests con Bearer200 · incluye el POST reciente
5ng build o serve webWeb arranca o build OK

[COMPROBADO] Smoke = GET needs + POST request + login JWT.

2. Casos funcionales

IDCasoEsperado
S1Slugs seedGET needs incluye arroz, aceite, tomate, panales, leche
S2AceiteGET /api/needs/aceite 200 · unit L · requestedQty 24
S3Slug inexistente404
S4Compromiso email inválido400; form no navega a /ok/…
S5Compromiso quantity ≤ 0400
S6SuccessCódigo visible en /ok/:code; copy “todavía no has entregado”
S7Inbox sin JWT401 / redirect login
S8PATCH CONTACTED200 · badge Contactado
S9PATCH RECEIVED200 · requestedQty del need no cambia
S10PATCH PARTIAL200
S11PATCH CANCELLED200 · comprometido deja de sumar esa quantity
S12Login malo401 · mensaje en form
S13Empty tabla (DB sin needs)UI-07
S14API caídaUI-08 + Reintentar conserva la vista
S15LoadingUI-09 skeletons antes del primer paint
S16Mobile 390Tabla apilada; CTA ≥44px; no hero 2-col
S17Fotos seed5 image_key resuelven a assets
S18Código formatoregex ^CE-\d{4}-\d{3}$
S19GET requests no filtra PII al públicosin token 401 (no 200 [])
S20Password no viaja de vueltalogin response sin hash
S21Body POSTacepta needSlug, name, email, quantity, note
S22Huecogap === max(0, requestedQty - committedQty)

3. Datos de prueba

UsoValor
STAFFines@cesta.barrio / password123
VECINORosa Belmonte · rosa.belmonte@example.com
Need felizaceite
Need parcial demotomate (seed PARTIAL)
Código seedCE-0816-001

4. Regresión de diversidad / craft

CheckFalla si
Home es tablaHay hero 2-col + 3 cards de marca (AP-12)
Shell es railHay dock flotante, sidebar CRM o command search como nav primaria
Copy honestoSuccess dice “entrega confirmada” o “donación recibida”
PaletaSe cuelan naranja ONG / cromo RONDA / chalky PIZARRA
TipoZilla / Mulish / Newsreader / Atkinson / Outfit
RutasAparecen /mesas, /buscar, /donar, /inbox/:id

5. Hipótesis (no se “prueban” en la demo)

H1, H2, H3 requieren uso real. El QA verifica que existen las superficies que permitirían medirlas (tap en fila, disclaimer, badge RECEIVED + pedido inmutable).

6. Criterios de salida QA v1

  • Smoke §1 en verde.
  • S2, S6, S7, S9, S16, S18, S21 en verde.
  • Cero blockers de copy deshonesto.
  • Sin .env en el repo.

7. Fuera de este plan

Playwright e2e completo, carga, fuzzing, auditoría del local físico.

14-dev-handoff.md

14 — Handoff desarrollo — CESTA

1. Arranque

cd /Users/cristian/orca/cesta-app

# apps independientes (npm --prefix o pnpm --filter)
# apps/api/.env
#   DATABASE_URL=  # Neon jolly-night-99671179
#   JWT_SECRET=
#   PORT=3016

# migrate / ensureSchema + seed
# api → http://localhost:3016
# web → http://localhost:4200

Demo: ines@cesta.barrio / password123.

2. Paridad Paper

Debe verse en AngularArtboard
Tokens botanical + Spectral/ManropeUI-00
Tabla densa + railUI-01, UI-10
Ficha con foto realUI-02
Compromiso + disclaimerUI-03
Success CE- en /ok/:codeUI-04
Login STAFFUI-05
Inbox estadosUI-06
Empty / error / loadingUI-07…09

No clonar RONDA (feed + dock), DERIVA (mapa split) ni PIZARRA (command search).

3. Contratos

TemaContrato
Login{ accessToken, user } camelCase (no access_token)
TablaGET /api/needs → array con requestedQty, committedQty, gap
FichaGET /api/needs/:slug
CompromisoPOST /api/requests { needSlug, name, email, quantity, note }
InboxGET /api/requests Bearer
StatusPATCH /api/requests/:id { status }
CódigoCE-MMDD-NNN Europe/Madrid
Status enumNEW | CONTACTED | RECEIVED | PARTIAL | CANCELLED

Campos JSON en camelCase hacia el web aunque SQL sea snake_case. [DECISIÓN]

4. Mapa de rutas web

PathGuard clientePágina
/noTabla
/necesidades/:slugnoFicha
/necesidades/:slug/comprometernoForm
/ok/:codenoSuccess
/loginnoLogin
/inboxsoft (redirect)Inbox

Soft guard: si no hay token, /login?next=. La API es la autoridad.

[COMPROBADO] Solo estas seis rutas. No implementar /inbox/:id, /mesas, /donar.

5. Assets

Copiar desde el case:

2026-08-16-cesta/assets/hero.jpg
2026-08-16-cesta/assets/needs/*.jpg
  → apps/web/public/assets/needs/   (o src/assets/needs/)

image_key del seed = filename (arroz.jpg, …).

6. DoD implementación L1

CheckOK
API sirve :3016
Seed 1 STAFF + 5 needs
Smoke GET + POST + login
JWT protege GET/PATCH requests
Web tabla + rail + ficha + form + success + inbox
Disclaimer ×3
Tokens Tailwind = paleta brief
ng build o equivalente OK
README del repo app con credencial
Home es tabla (no leftover feed)

7. No hacer

ProhibidoMotivo
npm workspacesAP-06
Commit de .envSecreto
Prometer entrega en copyH2 / AP-09
Estado SEATED / ENROLLED / WAITLISTVocabulario de otros días
Search bar o mapa como homeRompe H-TABLE
Hero 2-col + 3 cardsAP-12
image_gen como captura de productoD-P0-09
Body POST con tableSlug / partySizeLeftover RONDA

8. Contactos de diseño (artefactos)

ArtefactoPath
Briefux-projects/2026-08-16-cesta/docs/00-day-brief.md
Paperhttps://app.paper.design/file/01M04P0VFWGX1DQVSCR5TPWM9Z
Datosdocs/05-data-model.md
Flujosdocs/04-user-flows.md
IXdocs/16-interaction-specs.md
Builddocs/20-implementation.md

15-roadmap.md

15 — Roadmap — CESTA

1. Hecho en L1 (esta ejecución documental)

EntregaEvidencia
Definición producto + ternadocs 00-brief, 01
Personas Rosa / Inés, JTBD, journeydocs 02
IA table + raildocs 03
Flujos F-LEAD + STAFFdocs 04
Modelo needs / requests / users + seed 5docs 05
Stack Angular/Nest/Neon/JWT :3016docs 06, 20
Dirección botanical + DSdocs 07, 08
Copy honesto compromiso ≠ entregadocs 09
Paper ref 12 UX + 11 UIdocs 00-paper-reference
Suite 00–20 + README + executivecase folder

2. Implementación L1 (misma complejidad, código)

Contrato en docs 14 y 20. No es deuda silenciosa del case documental: es el build del repo cesta-app.

ÍtemPrioridad
Schema + seed 5 needsP0
Endpoints Must + smokeP0
Web tabla/ficha/form/success/login/inboxP0
Rail + tokens botanicalP0
Sustituir leftover /mesas / /tablesP0
Guards Angular canActivateP1

3. L1+ (misma complejidad, polish)

ÍtemNotas
Rate limit + honeypot POST requestsAnti-spam
Checkbox privacidadQ3
Ajuste numérico en PARTIALQ5
Filtros de status en inboxQuery API
Playwright S1–S22CI
Copy code clipboardCould
mailto en inboxOps
API URL por environmentDeploy
prefers-reduced-motion verificadoa11y

Estas son subidas de pulido, no parches del L1 documental.

[DECISIÓN] No dejar backlog del vertical slice como “mañana”. Lo de arriba es polish o subida de nivel.

4. L2 / L3 (requiere brief nuevo de diversidad)

  • Multi-sede / varias despensas
  • Cuenta VECINO y “mis compromisos”
  • Email / WhatsApp transaccional
  • CMS de necesidades
  • Inventario ERP (lotes, caducidad)
  • Check DB transaccional de hueco
  • Audit log
  • Search o mapa como home (rompería terna)

5. Explícitamente fuera (no backlog disfrazado)

ÍtemPor qué
Pagos / Bizum / socio / certificado fiscalS2 / S4
Feed de impactoTerna ≠ RONDA
Mapa de despensasTerna ≠ DERIVA
Search-first homeTerna ≠ PIZARRA
Kanban≠ PRIMA
CRM de donantesS3
App nativaFuera de stack daily

6. Orden de ataque si hay continuidad de código

  1. Contratos 05–06 + seed 05 + smoke 13
  2. Web paridad UI-00…10 (sustituir leftover)
  3. Guards + mailto + clipboard
  4. Rate limit si hay tráfico
  5. Evaluar brief L2 (no parche silencioso)

16-interaction-specs.md

16 — Especificación de interacción — CESTA

1. Convenciones

TokenValor
Duración corta120–160ms
Easingease-out
Reduced motion0ms
Target≥44×44
Pendingcontrol disabled + aria-busy

2. Rail (S-RAIL)

GestoResultado
Tap slotNavega; aria-current se mueve
Tap Inbox sin sesión/login?next=/inbox
Scroll de la tablaRail no se esconde en v1
TecladoTab a los slots al final (desktop: rail es el primer landmark o el último; [DECISIÓN] rail primero en desktop, último en 390)
Rotación landscape 390Rail compacto; main padding-bottom 72

Estado activo: disco tierra + icono hueso.
Estado default: tierra sobre saco/hueso.

Slots: Lista / · Inbox /inbox · Entrar /login (o Salir si auth).


3. Tabla home

GestoResultado
Tap fila/necesidades/:slug (fila completa clicable, un solo enlace)
Pull-to-refreshNo en v1; Reintentar solo en error
Primera carga5 skeletons de fila; no spinner centrado de marca
0 resultadosEmpty UI-07
ErrorBanner fijo bajo wordmark + Reintentar (refetch)
Hover desktopFondo saco/20; no elevación de card

AC: la primera fila (foto + hueco) está en el viewport inicial a 390 y a 1280 (sin hero de 60vh).

Orden visual = seed / title ASC. No “destacados”.

Columnas desktop: foto · ítem · pedido · comprometido · hueco.
Numéricos: tabular-nums, alineados a la derecha, unidad visible.

390 (UI-10): pila — [foto 56] [título] + fila de tres metas pedido / comprometido / hueco.


4. Ficha

GestoResultado
CTA“Comprometer” → /necesidades/:slug/comprometer
Hueco 0Mismo CTA; copy avisa
Back / wordmarkTabla /
FotoNo lightbox v1
DisclaimerVisible antes del CTA (no debajo del fold en desktop)

5. Form compromiso

CampoInteracción
nameAutocomplete name
emailtype=email, autocomplete email
quantitytype=number · min > 0 · step 1 si ud/packs; 0.5 o 1 si kg/L
notetextarea 3 filas
GestoResultado
Submit inválidoFoco al primer campo error; no POST
Submit válidoBotón disabled; POST; 201 → /ok/:code
Error redBotón “Reintentar”; no limpia campos
Doble tapIgnorado mientras pending

Submit deshabilitado si nombre < 2, email inválido o quantity ≤ 0.


6. Success

GestoResultado
Long-press / selectCódigo seleccionable
Copiar (Could)navigator.clipboard; toast 1.5s “Código copiado”
CTA primarioVolver a la tabla /
CTA secundarioVolver a la ficha

Sin animación de confetti. Sin “¡entrega confirmada!”.

El :code de la ruta se pinta tal cual. No lookup PII.


7. Login

GestoResultado
SubmitPOST login; pending en botón
200Guarda token + user; navega next sanitizado o /inbox
401Texto bajo el form; password no se limpia
EnterSubmit

next permitido: paths que empiezan por / y no // ni http.


8. Inbox

GestoResultado
Sin tokenRedirect login
AccionesEn la fila: Contactado / Recibido / Parcial / Cancelar
EmptyTitular + 1 línea
Error 401 mid-sessionRedirect login
Error redBanner + Reintentar

No swipe-to-archive. No navegación a /inbox/:id.


9. Estados (acciones en lista)

AcciónConfirmaciónResultado
ContactadoNoPATCH CONTACTED
RecibidoNoPATCH RECEIVED
ParcialNoPATCH PARTIAL
Cancelar (“¿Cancelar CE-…?”)PATCH CANCELLED

Optimistic UI: badge cambia al tap; si PATCH falla, rollback + toast error.

RECEIVED no anima un “stock +N”. H3.


10. Motion

SuperficieSpec
Filas tablaFade 160ms, stagger ≤30ms (off si reduced-motion)
Cambio de badgeCrossfade 120ms
RailSin hide; sin bounce
SkeletonsEstáticos o pulso 1.2s opacity 0.6–1; off si reduced-motion

Prohibido: scroll-jacking, page transitions de 400ms, parallax en foto.


11. Criterios de aceptación de interacción

  1. Un VECINO en 390px llega de tabla a success en ≤ 4 taps (fila, CTA, submit → success).
  2. Un STAFF en 390px marca CONTACTED en ≤ 3 taps desde inbox (acción en fila).
  3. Ningún submit permite doble POST.
  4. El rail no cubre el CTA de ficha (padding).
  5. Reduced-motion elimina stagger.
  6. La home no es hero 2-col (AP-12).
  7. En 390 el hueco se lee sin scroll horizontal.

17-prototype-map.md

17 — Mapa de prototipo — CESTA

Paper no es clicable vía MCP. El prototipo vivo es la app Angular.

RutaPaperRol
/UI-01, UI-07, UI-08, UI-09, UI-10VECINO
/necesidades/:slugUI-02VECINO
/necesidades/:slug/comprometerUI-03VECINO
/ok/:codeUI-04VECINO
/loginUI-05STAFF
/inboxUI-06STAFF

Proceso: UX-00…UX-11. Tokens: UI-00.

Recorrido demo (guión)

  1. Abrir / — tabla de 5 líneas, rail Lista activo.
  2. Tocar Aceite de oliva — ficha + disclaimer.
  3. Comprometer — form 3 L, Rosa Belmonte.
  4. Success /ok/CE-… — código + “todavía no has entregado”.
  5. /loginines@cesta.barrio / password123.
  6. /inbox — marcar Contactado / Recibido / Parcial.

Estados en el mismo prototipo

EstadoCómo provocarlo
Empty tablaAPI 200 []
Empty inboxlista vacía autenticada
ErrorAPI caída / 5xx
Loadingthrottle de red
Mobile tabla390 × 844

Fuera del prototipo clicable

  • Paper artboards son estáticos.
  • WhatsApp / llamada de Inés (backstage).
  • Entrega física del sábado.
  • Lookup público de PII por código.

18-completeness-audit.md

18 — Auditoría de completitud — CESTA (2026-08-16)

1. Alcance auditado

Vertical slice L1: tabla densa de necesidades de despensa vecinal + compromiso público + panel STAFF JWT, con docs, referencia Paper, contratos Angular + Nest + Neon.

Este encargo cubre la suite documental. Paper hi-fi y el repo cesta-app pueden existir en paralelo; la auditoría marca lo que esta suite cierra. El day brief no se ha sobrescrito.

2. Checklist CRON / ALS-2

RequisitoEstadoEvidencia
Diversidad sector/tipo/nivelOKONG despensa L1; no feed; no mapa; no search; no pagos
Terna 0 códigos = N−1 · ≥2 vs N−2OKR-CL · S-RAIL · H-TABLE · F-LEAD (brief)
Day brief + anti-patronesOKdocs/00-day-brief.md (no sobrescrito)
Paper ≥12 UX + ≥10 UIOKUX-count 12 · UI-count 11 · file 01M04P0VFWGX1DQVSCR5TPWM9Z
Docs 00–20OKsuite en docs/ + executive + README
JWT STAFFOKRole STAFF · D-P1-05
API + seed + NeonOKcontratos jolly-night-99671179, port 3016
Web tokensOKSpectral + Manrope · paleta botanical
Table-first (no AP-12)OKUI-01 / docs 03, 07, 08
Hipótesis no fake fieldOKetiquetas en doc 02
Copy compromiso ≠ entregaOKdocs 01, 09, 16

3. Cobertura funcional

Feature briefSpecUI PaperAPIDocs
Home tabla 5 necesidadesUI-01, UI-10GET needs03, 04, 16
Ficha + fotoUI-02GET :slug05, 09
Form compromisoUI-03POST requests04, 05
Success + CE-UI-04code · /ok/:code04, 09
Login STAFFUI-05POST login04, 06
Inbox 5 estadosUI-06GET + PATCH04, 05
EmptyUI-07200 []16
ErrorUI-085xx/red16
LoadingUI-09pending16
Design systemUI-0008
Seed 5 needsseed05
RailUI-01, UI-1003, 08

4. Cobertura Paper (literales del brief)

IDNombreEn 00-paper-reference
UX-00Cover
UX-01Stakeholders
UX-02Personas
UX-03JTBD
UX-04Stories
UX-05Journey
UX-06Blueprint
UX-07Site map
UX-08Flujos
UX-09Datos+permisos
UX-10Métricas
UX-11Research
UI-00Tokens
UI-01Tabla
UI-02Ficha
UI-03Compromiso
UI-04Success
UI-05Login
UI-06Inbox
UI-07Empty
UI-08Error
UI-09Loading
UI-10Mobile tabla

[COMPROBADO] Nombres coinciden con el encargo. UX-count: 12 y UI-count: 11 literales para el gate check-paper-reference.mjs.

5. Rúbrica de calidad (auto SCORE orientativo · docs)

EjeScore 1–5Comentario
Diversidad5Tabla+rail vs feed RONDA y mapa DERIVA; L1 no L2
Craft visual (spec)4–5Botanical + Spectral/Manrope + fotos ítem documentadas
Densidad UX docs5Suite L1 alto: tablas, AC, etiquetas
Completitud código L1App fuera o en paralelo; leftover scaffold anotado
Authz5Contrato: guard en list/patch
Verdad investigación5Sin entrevistas ni stats de campo falsas

6. Huecos aceptados (no regresiones de cierre documental)

HuecoClasificación
Pagos / feed / mapa / search-homeFuera L1
e2e automatizadoL1+
Route guards formales AngularL1+
Check DB hueco / ajuste PARTIALL1+ / L2
Checkbox RGPDL1+ documentado
Leftover /mesas en repoBuild; contrato de esta suite gana
Paper/app no necesariamente tocados aquíEncargo = documentación

7. Veredicto

COMPLETO para entrega documental del caso 2026-08-16.
La suite define producto L1 usable (tablas, AC, riesgos, contratos) sin presentar investigación de campo como hecho.
Cualquier ampliación mapa / search-first / pagos / feed requiere nuevo brief de diversidad, no parche silencioso.

19-backlog-completo.md

19 — Backlog completo — CESTA

Inventario de ítems. Los del alcance L1 documental del día están Done.
El resto es opcional / siguiente nivel, no deuda oculta del cierre.

1. Done — L1 case documental (2026-08-16)

IDÍtemCapa
D01Definición producto CESTA + esloganDocs
D02Day brief diversidad ONG L1Docs (preexistente, no pisado)
D03Terna R-CL · S-RAIL · H-TABLE · F-LEADDocs
D04Personas Rosa Belmonte / Inés MarzalDocs + Paper ref
D05JTBD + stories MustDocs
D06IA table-first + railDocs
D07Flujos F1–F8 + ACDocs
D08Modelo users / needs / requestsDocs
D09Contratos API :3016Docs
D10Auth JWT STAFFDocs
D115 estados incl. RECEIVED y PARTIALDocs
D12Código CE-MMDD-NNNDocs
D13Disclaimer compromiso ≠ entregaDocs
D14North star CONTACTED antes de franjaDocs
D15Tokens botanical + Spectral / ManropeDocs
D16Paper UX-00…11 + UI-00…10Docs
D17Suite docs 00–20 + README + executiveDocs
D18Seed 5 necesidades + solicitudes especificadoDocs
D19Assets listadosREADME
D20QA smoke + S1–S22Docs
D21Neon jolly-night-99671179Docs
D22Rutas reales solo las 6 del encargoDocs

2. Backlog L1 implementación (repo cesta-app)

IDÍtemPrioridadNotas
I01Alinear apps/api + apps/web al contrato CESTAP0quitar leftover mesas/tables
I02Schema needs / requests / usersP0
I03Seed Inés + 5 needs + requestsP0
I04GET needs / :slugP0
I05POST requests + código CE-P0body needSlug, name, email, quantity, note
I06POST login JWTP0ines@cesta.barrio
I07GET requests + PATCH :id { status }P0
I08Tabla + rail AngularP0
I09Ficha / comprometer / ok / login / inboxP0
I10Tokens Tailwind + fotosP0
I11Smoke curl + buildP0D-P1-06
I12Puerto API 3016P0

3. Backlog L1+ (polish)

IDÍtemPrioridadNotas
B01canActivate guards AngularP1UX auth
B02Filtros por status en inboxP2Query API
B03Ajuste quantity en PARTIALP1Q5
B04Captcha / rate limit POSTP1anti-spam
B05Checkbox privacidadP1RGPD
B06Playwright smokeP1CI
B07Skeleton UI-09 en códigoP2
B08mailto en inboxP2ops
B09Copy code clipboardP3
B10API URL por environmentP1deploy
B11Página 404 amigableP3
B12Sessions invalidablesP2

4. Backlog L2 / L3 (requiere brief nuevo)

IDÍtemDependencia
C01Multi-sedevenues + membership
C02Cuenta VECINOauthz
C03Email / WhatsApp transaccionalprovider
C04Lookup público por códigoauthz
C05Check DB huecotransacción
C06Audit logEvent
C07CMS NeedRole EDITOR
C08Inventario ERPlotes

5. Backlog explícitamente fuera

IDÍtem
E01Pagos / Bizum / socio / certificado fiscal
E02Feed de impacto
E03Mapa de despensas
E04Search-first como home
E05Kanban de compromisos
E06CRM de donantes
E07App nativa

6. Explicitamente no-backlog

IdeaRazón
Hero 2-col + 3 cardsAP-12 / rompe H-TABLE
Sidebar CRMS-SIDE
Dock flotanteS-DOCK de RONDA
Command search municipalPIZARRA
Split mapaDERIVA
Wizard donación 4 pasosF-ONB

7. Orden de ataque recomendado (continuidad de código)

  1. I01–I12 (vertical slice runnable, sin leftover)
  2. B01 + B06 + B10
  3. B03 + B08
  4. B04 + B05 si hay tráfico
  5. Evaluar brief L2 — no parche silencioso

8. Trazabilidad

OrigenÍtems
Day brief must-haveD01–D22, I01–I12
Doc 15 L1+B01–B12
Doc 15 L2 / fueraC01–C08, E01–E07

20-implementation.md

20 — Implementación — CESTA

1. Resumen ejecutivo técnico

CampoValor
App path/Users/cristian/orca/cesta-app
APINestJS · puerto 3016 · prefijo /api
WebAngular standalone · puerto 4200
Packageapps independientes (npm --prefix o pnpm --filter)
DBNeon PostgreSQL · project jolly-night-99671179
Tablasusers, needs, requests (+ sessions Should)
AuthJWT Bearer · role STAFF
DominioUser, Need, Request
Fecha2026-08-16
GitHubhttps://github.com/Criscode2022/cesta-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; puede contener leftover de otro producto (/mesas, GET /tables). El contrato de esta suite es CESTA.

2. Cómo arrancar

cd /Users/cristian/orca/cesta-app
# instalar dependencias por app (no npm workspaces)

# apps/api/.env
#   DATABASE_URL=   # Neon jolly-night-99671179
#   JWT_SECRET=
#   PORT=3016

# schema + seed
# api → http://localhost:3016
# web → http://localhost:4200

Credenciales

RolEmailPassword
STAFFines@cesta.barriopassword123

3. Schema

Al boot o vía migrate:

  1. CREATE TABLE / migrate de users, needs, requests (doc 05).
  2. Índices: needs(slug), requests(created_at DESC), requests(need_id, status), users(email).
  3. Seed idempotente:
    • upsert STAFF ines@cesta.barrio (bcrypt de password123)
    • upsert 5 needs por slug
    • upsert requests por code

No borrar datos de usuario en cada boot si ya existen filas distintas al seed.

Tablas (recordatorio)

TablaContenido
usersSTAFF Inés
needs5 líneas seed
requestsCompromisos públicos
sessionsShould: sid + token_hash + expires_at

4. Módulos API a implementar

Auth

  • POST /api/auth/login
  • Valida email/password; compara bcrypt; emite JWT con sub, email, role.
  • Guard JWT protege lectura/escritura de requests.

Needs (público)

MétodoRutaAuthNotas
GET/api/needsNoincluye committedQty, gap
GET/api/needs/:slugNo404 si no existe

Requests

MétodoRutaAuthNotas
POST/api/requestsNobody { needSlug, name, email, quantity, note }; status NEW; code CE-
GET/api/requestsJWTlist + need; createdAt desc
PATCH/api/requests/:idJWTbody { status }

Generación de código

CE- + MMDD (Europe/Madrid) + - + NNN

NNN = siguiente entero del día, padded 3. Si unique falla, reintentar NNN+1 (máx. 5).

[DECISIÓN] Timezone Europe/Madrid, no UTC, para que el código coincida con el mostrador.

Agregados

committedQty = SUM(quantity) WHERE status <> 'CANCELLED'
gap          = GREATEST(0, requestedQty - committedQty)

RECEIVED / PARTIAL no actualizan requested_qty.

5. Frontend a implementar

PáginaRutaResponsabilidad
HomePage/GET needs, tabla, empty/loading/error, rail
NeedPage/necesidades/:slugGET slug, foto, disclaimer, CTA
CommitPage/necesidades/:slug/comprometerform create público + disclaimer
OkPage/ok/:codeconfirmación + código + “no es entrega”
LoginPage/loginform → login → inbox
InboxPage/inboxlist + PATCH en fila + empty/error + logout

Shell: RailComponent persistente.
ApiService centraliza base URL http://localhost:3016/api, token storage (cesta_token, cesta_user), métodos tipados (Need, Request, User).

6. Decisiones de implementación

DecisiónRazón
Puerto API 3016Evitar colisión con RONDA 3015
Tablas needs / requestsDominio despensa, no leftover tables
Soft auth en páginasSimple; API es autoridad
Sin FK User–RequestForm anónimo
Código CE-MMDD-NNNReferencia oral corta
Templates standaloneVelocidad daily
No bloquear POST por hueco 0Q4; STAFF decide
Home no usa hero como layoutAP-12 / H-TABLE
RECEIVED no ENROLLED / SEATEDDominio de caja, no municipal ni mesa
JSON camelCaseConvenio web serie daily
PATCH :id no :id/statusContrato del encargo
Success /ok/:codeContrato del encargo

7. Variables de entorno

VariableServicioDescripción
DATABASE_URLAPINeon jolly-night-99671179
JWT_SECRETAPIFirma tokens
PORTAPIopcional, 3016
JWT_EXPIRESAPIopcional, default 12h

Web: URL de API en ApiService (default localhost:3016).

8. Smoke de implementación (mínimo)

  1. Schema OK (tablas existen).
  2. GET /api/needs length ≥ 5, incluye arroz, aceite, tomate, panales, leche.
  3. POST /api/requests 201 + code CE-.
  4. POST /api/auth/login 200 + accessToken.
  5. GET /api/requests Bearer incluye el POST.
  6. GET /api/requests sin token 401.
  7. PATCH /api/requests/:id { status: "RECEIVED" } 200 y requestedQty intacto.
  8. Web: tabla dominante, rail, disclaimer, fotos.

9. Estructura de ficheros clave (objetivo)

cesta-app/
├── package.json
├── apps/api/
│   ├── src/
│   │   ├── main.ts
│   │   ├── app.module.ts
│   │   ├── auth/
│   │   ├── needs/
│   │   └── requests/
│   └── .env.example
├── apps/web/
│   ├── tailwind.config.js
│   └── src/app/
│       ├── app.routes.ts
│       ├── core/api.service.ts
│       ├── shell/rail.component.ts
│       └── pages/
│           ├── home/          # tabla
│           ├── need/
│           ├── commit/
│           ├── ok/
│           ├── login/
│           └── inbox/
└── README.md

Nombres de fichero orientativos; el contrato es de rutas y tablas, no de filenames exactos.

10. Seed — checklist de implementación

#Check
1User Inés Marzal / ines@cesta.barrio / password bcrypt de password123
25 needs con slugs de doc 05 y image_key alineado a assets
3Unidades kg / L / ud / packs y pedidos 40 / 24 / 36 / 12 / 48
4Requests CE-0816-001…005 cubriendo NEW, CONTACTED, RECEIVED, PARTIAL, CANCELLED
5Rosa → aceite 3 L NEW
6Youssef → tomate PARTIAL
7Idempotencia: upsert por email/slug/code

11. Alineación case ↔ app

DocEvidencia esperada en código
05 data modelcolumnas y enums
03–04 IA/flowsroutes + controllers
08 DStailwind colors + fontFamily Spectral/Manrope
09 contentstrings en templates (disclaimer ×3)
00 paperURLs en README case
11 securityGuard en GET/PATCH requests

12. Notas de cierre técnico

  • El case documental L1 está especificado (docs + Paper ref + contratos).
  • Must-have de producto del brief están escritos con AC.
  • Mejoras (guards Angular, e2e, rate limit) viven en backlog L1+, no como deuda silenciosa.
  • Si el repo aún muestra feed de mesas, no está alineado: I01 es el primer ítem de build.