This is the full developer documentation for Terminales Docs # Terminales Solution Docs > The published knowledge base for the terminales POS system — what it is, how it's built, and why. What works today Every user story with three independent statuses — built, covered by the hermetic test suite, and verified against a real backend. The clearest picture of progress: [web stories](/product/user-stories/) · [mobile stories](/product/mobile-user-stories/). Diseño ↔ App Galerías lado a lado del diseño en Figma y la pantalla real, generadas desde capturas: [web](/design/parity/) · [móvil](/design/parity-mobile/). Overview What terminales is, the repositories that make it up, and the shared glossary. Start at the [system overview](/overview/system-overview/). Decisions Architecture, product and technical decision records — with rationale and the alternatives that were rejected. See the [decision index](/decisions/). Diagrams System context, data model, and the migration epic graph, rendered from Mermaid. See the [system context diagram](/diagrams/system-context/). Where the programme stands The migration is complete and the production-cutover gate is crossed; the work now is verification toward go-live. See the [milestones](/progress/milestones/) — or the [roadmap](/progress/roadmap/) for how it got here. For AI agents This site is agent-consumable: [/llms.txt](/llms.txt) for an index, [/llms-full.txt](/llms-full.txt) for the full corpus, and every page as raw markdown at `.md`. # ADR 0001 — Appwrite integration architecture > the single authoritative record of the cross-repo architecture decisions for the Appwrite migration. Every sibling ticket cites this ADR instead of re-deriving or re-litigating the choices > **Purpose:** the single authoritative record of the cross-repo architecture decisions for the Appwrite migration. Every sibling ticket cites this ADR instead of re-deriving or re-litigating the choices. * Status: Accepted * Date: 2026-06-25 * Deciders: terminales engineering + architecture (human sign-off, issue [#101](https://github.com/inspiraCode/terminales-api/issues/101)) * Repos: Multi — `terminales-api` (NestJS), `terminales` (Android/Kotlin), `terminales-web` (Next.js) * Related: `architecture-decisions` (the rationale table this ADR formalizes), `appwrite`, [../business-rules/auth-and-multitenancy.md](/business-rules/auth-and-multitenancy/) ## Context [Section titled “Context”](#context) The migration to **Appwrite** as the primary backend makes several architectural choices that span all three client/service repos. They were decided up front (no spikes) so they could be baked into the backlog tickets rather than rediscovered per ticket. This ADR records each decision, its rationale, and the alternatives that were considered and rejected. Phase context: the system is **pre-production** (no live users), and the **legacy stack stays live until cutover** — both facts shape several decisions below (notably feature flags, migrations, and the legacy stack itself). Decisions are recorded as **Accepted**; do not re-open without a superseding ADR. ## Client access — SDK-primary, no GraphQL [Section titled “Client access — SDK-primary, no GraphQL”](#client-access--sdk-primary-no-graphql) Use the Appwrite **SDKs as the primary access path**: the Web SDK on Next.js and the Kotlin SDK on Android. REST is a fallback only. GraphQL is not used. **Rationale:** the SDKs are first-class and the smallest maintenance surface; this also standardizes the Android configuration, which previously carried both SDK and GraphQL endpoints. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected) * **GraphQL** → dropped. Extra surface to maintain, and it was redundantly dual-configured on Android. * **REST as the primary CRUD path (a server proxy)** → rejected; clients talk to Appwrite directly under RLS (see [Server boundary](#server-boundary--no-crud-proxy)). ## Data strategy — offline-first Android, hybrid Web [Section titled “Data strategy — offline-first Android, hybrid Web”](#data-strategy--offline-first-android-hybrid-web) The **Android POS terminal is offline-first**: a local cache, queued writes, and `clientId`-idempotent sync. The **Web admin is online/hybrid** (built mock-first, then wired to Appwrite). **Rationale:** a POS terminal must keep selling without a network; admin tooling does not have that constraint. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected-1) * **Online-only terminal** → rejected; unacceptable for retail — the terminal cannot stop selling when connectivity drops. * **Offline-first Web admin** → rejected; needless complexity for a tool that is always operated online. ## Auth sessions — JWT (Android/API) + cookie (Web SSR) [Section titled “Auth sessions — JWT (Android/API) + cookie (Web SSR)”](#auth-sessions--jwt-androidapi--cookie-web-ssr) Android mints an Appwrite **JWT**, validated by the NestJS guard for native and server-to-server calls. Next.js uses a **secure cookie session** for SSR. PIN login is the one custom flow: NestJS verifies the PIN and mints an Appwrite **custom token** the client exchanges for a session. **Rationale:** cookies fit server-side rendering; JWTs fit native apps and server-to-server validation. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected-2) * **BetterAuth** → replaced; Appwrite Account now owns identity, sessions, MFA, and recovery. * **Cookie-only sessions** → rejected; do not fit the native Android client. * **TOTP / authenticator MFA** → rejected for now; MFA uses **email/SMS factors only** (SMS needs a configured provider per env). ## Multitenancy — Store = Team, single shared DB, document RLS [Section titled “Multitenancy — Store = Team, single shared DB, document RLS”](#multitenancy--store--team-single-shared-db-document-rls) **Store = an Appwrite Team** (the tenant boundary; membership roles `cashier` / `supervisor` / `manager`). **Merchant = a `merchants` collection** grouping stores via `stores.merchantId`. All data lives in **one shared database**; isolation is enforced by **document-level permissions (RLS)** — `Role.team(storeTeamId[, role])`. **Labels** (`Role.label(...)`) are reserved for cross-merchant platform staff. **Rationale:** Teams give native per-store roles; the merchant is flat organizational data (Appwrite teams cannot nest); RLS is the single enforcement point, so isolation is a data-layer guarantee, not UI filtering. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected-3) * **Database-per-merchant** → rejected; operational cost and it forecloses cross-store reporting. (Portability tiers that *could* split DB/project later without redesign are documented in [../business-rules/roles-permissions-and-labels.md](/business-rules/roles-permissions-and-labels/).) * **Merchant = Team** → rejected; Appwrite teams are flat and cannot nest stores under a merchant. * **`pos_roles` / `pos_permissions` collections** → rejected; the role→permission mapping is a **code matrix** (`src/auth/roles/pos-roles.ts`), not data. See also the [Appwrite multi-tenancy guidance](https://appwrite.io/docs/products/auth/multi-tenancy), which prescribes exactly this Teams + `Role.team(...)` model. ## Functions deploy — CI/CD [Section titled “Functions deploy — CI/CD”](#functions-deploy--cicd) Appwrite Functions are deployed via **CI/CD** (`appwrite push` in GitHub Actions) per environment; the CLI is used for local development. **Rationale:** consistent, reproducible, auditable dev → staging → prod deploys. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected-4) * **Manual console deploys** → rejected; not reproducible or auditable across environments. ## Schema-as-code — `appwrite.json`, no migrations (pre-prod) [Section titled “Schema-as-code — appwrite.json, no migrations (pre-prod)”](#schema-as-code--appwritejson-no-migrations-pre-prod) `appwrite.json` is the **single declaration** of databases, collections, attributes, indexes, relationships, buckets, and functions, applied per environment via `appwrite push`. **Type generation** (`appwrite types`) feeds the API and Web so types never drift from the schema. **No migration scripts before production.** **Rationale:** reproducible environments and drift-free types; with no live data pre-production, `appwrite push` is the right substitute for migrations. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected-5) * **Migration scripts before production** → rejected; there is no live data to migrate yet, so they would be premature ceremony. (Introduced at production cutover.) * **Hand-written types** → rejected; they drift from the schema. ## Feature flags — deferred to production [Section titled “Feature flags — deferred to production”](#feature-flags--deferred-to-production) **No feature flags pre-production.** Every Appwrite ticket ships `feature_flag: ""`. At cutover, the Epic G production-flags ticket introduces **runtime** `appwrite_*` flags (toggled without redeploy, evaluated from a `feature_flags` collection + Realtime) plus a kill-switch. **Rationale:** there are no live users to protect, so gating no-op code behind flags adds cost without benefit. Future agents must **not** “fix” this by adding flags prematurely. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected-6) * **Flags from day one** → rejected; gating code with no users to protect is wasted complexity in the pre-production phase. ## Server boundary — no CRUD proxy [Section titled “Server boundary — no CRUD proxy”](#server-boundary--no-crud-proxy) Clients call **Appwrite directly** for simple reads/writes under RLS. **NestJS is not a CRUD proxy.** NestJS/DBOS keeps only complex, durable, or server-authority logic: settlement, PSP capture/retry, fiscal stamping, token validation, PIN custom-token minting, user/team admin, and Storage token issuance. **Rationale:** less code and lower latency; RLS is the access choke point, so pass-through controllers add nothing. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected-7) * **NestJS pass-through controllers for CRUD** → rejected; they duplicate the SDK, add latency, and bypass nothing because RLS already enforces access. (The generic server “repository abstraction” ticket was removed for the same reason.) See `nestjs-dbos` for the precise server-side surface. ## Legacy stack — kept until cutover [Section titled “Legacy stack — kept until cutover”](#legacy-stack--kept-until-cutover) **TypeORM + BetterAuth stay** in service for legacy (non-POS) domains until the Epic G decommission ticket. The POS `Product` is a **new** Appwrite collection in its own namespace — the legacy loan `Product` is untouched. **Rationale:** migrate safely and reversibly; the migration can be rolled back at cutover because the legacy stacks remain until flags are ramped. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected-8) * **Big-bang migration** → rejected; too risky without a fallback during the transition. * **Renaming legacy `Product` to free the name** → rejected; a risky FK rewrite, made unnecessary because the POS `Product` is a separate Appwrite collection. ## Related decisions & deferrals [Section titled “Related decisions & deferrals”](#related-decisions--deferrals) These are decided and binding but owned by other docs — pointers, not duplicates: * **Roles via team membership; Labels for platform.** POS role = store-team membership; role→permission map is the `pos-roles.ts` code matrix; Labels reserved for platform staff. Full catalog + permission matrix: [../business-rules/roles-permissions-and-labels.md](/business-rules/roles-permissions-and-labels/). * **Employees = profile, not identity.** Identity/credentials/MFA live in Appwrite Auth users; `employees` is a POS profile (pinHash, storeTeamId) linked 1:1 by `userId`. See [../business-rules/auth-and-multitenancy.md](/business-rules/auth-and-multitenancy/). * **2FA via email/SMS factors only** (no TOTP). See [Auth sessions](#auth-sessions--jwt-androidapi--cookie-web-ssr). * **Presence via the Appwrite Presences API** (GA 2026-05-25), not a custom heartbeat collection. * **Deferral — no feature flags until production.** See [Feature flags](#feature-flags--deferred-to-production). * **Deferral — no database migrations until production.** See [Schema-as-code](#schema-as-code--appwritejson-no-migrations-pre-prod). Future agents must not “fix” the two deferrals by adding flags or migration scripts prematurely. Living detail: `open-questions-and-todos`. ## References [Section titled “References”](#references) * `architecture-decisions` — the rationale table + refinements + consequences this ADR formalizes. * `appwrite` — the deep Appwrite platform reference. * `nestjs-dbos` — the server-side boundary. * [../business-rules/auth-and-multitenancy.md](/business-rules/auth-and-multitenancy/) · [../business-rules/pos-domain-model.md](/business-rules/pos-domain-model/) · [../business-rules/roles-permissions-and-labels.md](/business-rules/roles-permissions-and-labels/). * [Appwrite multi-tenancy](https://appwrite.io/docs/products/auth/multi-tenancy). # ADR 0002 — Full Postgres→Appwrite domain migration & TypeORM decommission > record the Wave 3 decision to migrate all remaining domain data off > **Purpose:** record the Wave 3 decision to migrate **all** remaining domain data off Postgres/TypeORM onto Appwrite before the production cutover, and to decommission the TypeORM stack — closing the “legacy kept until cutover” deferral that [ADR-0001](/adr/0001-appwrite-integration/) opened. Every Track B migration ticket cites this ADR. * Status: Accepted * Date: 2026-07-11 * Deciders: Product Owner + terminales engineering (Wave 3 planning session; PO chose “full migration before go-live”) > **Status update (2026-07-12):** the legacy TypeORM **teardown described here is DONE** — all 23 domain entities were dropped in one PR (\[terminales-api#296]). The staged “Bucket 1/2/3” sequencing below is **historical**; several Bucket-2 tables (`deposits`, `charges`, `payments`, `merchant_balances`, `balance_movements`, `providers`, `provider_products`) now **exist**. For current state + the remaining Appwrite *rebuild*, see `wave3-appwrite-migration-resequence`. * Repos: Multi — `terminales-api` (NestJS/TypeORM), `terminales-web` (Next.js), `terminales` (Android/Kotlin) * Related: [0001-appwrite-integration.md](/adr/0001-appwrite-integration/) (supersedes its “Legacy stack — kept until cutover” clause for domain data), [../business-rules/pos-domain-model.md](/business-rules/pos-domain-model/), [../pdr/0001-mvp-golive-scope.md](/pdr/0001-mvp-golive-scope/), `terminales-api/docs/decommission/typeorm-and-betterauth.md`, `wave3-mvp-analysis` §3.2 + Track B ## Context [Section titled “Context”](#context) [ADR-0001](/adr/0001-appwrite-integration/) decided that **Appwrite is the primary backend** and that the legacy TypeORM + BetterAuth stack would be **kept until cutover** so the migration could proceed safely and reversibly. The POS domain was migrated first (40 Appwrite tables live in `terminales-api/infra/appwrite/appwrite.config.json`), and the auth mechanism was swapped off BetterAuth (#143/#148/#150). At the start of Wave 3 the migration is **half done**. An audit (2026-07-11) found: * **\~23 legacy non-POS domains still live in Postgres via TypeORM** — `@Entity` classes under `terminales-api/src/*/entities/*.entity.ts`, wired through `TypeOrmModule.forFeature(...)`, resolved by `src/database/data-source.ts` (`synchronize:true` still permitted in dev/test — auto-DDL against Postgres). * **Six concepts exist twice** — `merchants`, `products`, `terminals`, `payment_intents`, `transactions`, `audit_log` are modeled as **both** a Postgres TypeORM entity **and** an Appwrite table. Ownership is split by surface (legacy admin/fintech vs. new POS), a correctness and data-integrity risk in production. * **GraphQL resolvers are unguarded** after the BetterAuth removal (decommission doc §3a: “do not deploy to production without a GraphQL Appwrite guard”). The Product Owner decided in Wave 3 planning: **complete the migration and decommission TypeORM before go-live** (the most architecturally complete of the options weighed), so the solution ships with a single source of truth — the target architecture, not a mid-migration state. Note: the system is still **pre-production (no live customer data)**, so “migration” is predominantly *schema-as-code + controller repoint + entity removal*, not a large data backfill; the only data to move is seeded/reference/stage data. This ADR records the strategy; the per-domain execution lives in Track B of the Wave 3 analysis. ## Decision — migrate all domain data to Appwrite; Postgres holds only disposable aux [Section titled “Decision — migrate all domain data to Appwrite; Postgres holds only disposable aux”](#decision--migrate-all-domain-data-to-appwrite-postgres-holds-only-disposable-aux) **All business/domain data moves to Appwrite TablesDB (and Appwrite Storage for files).** After Wave 3, Postgres contains **only disposable auxiliary state**: DBOS durable-workflow tables, the TypeORM migration-bookkeeping table (until TypeORM is fully removed), and the Redis query cache is retired or kept as a pure cache. Postgres becomes **rebuildable from nothing** — no backup strategy required for it; the Appwrite data is the persistent, backed-up source of truth (row 76 backup/restore runbook). **Rationale:** the “Appwrite holds all solution data” rule stops being aspirational only when TypeORM no longer owns any domain entity; a split source of truth (six dual-modeled concepts) is a launch-blocking integrity risk; and doing this pre-production — with no live customer data — is far cheaper than a post-launch migration. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected) * **Guard-and-defer (ship mid-migration, track as debt)** → rejected by the PO in favor of a clean architecture at launch; leaving 23 TypeORM domains and six dual-modeled concepts in production was judged too risky and too slow to pay down later. * **Audit-then-decide per-domain (some migrate, some stay)** → rejected; produces a permanent hybrid whose seams (dual writes, `synchronize:true`) are exactly the risk we are removing. ## Domain classification — three migration buckets [Section titled “Domain classification — three migration buckets”](#domain-classification--three-migration-buckets) The 23 legacy domains are not homogeneous; each Track B ticket declares its bucket. * **Bucket 1 — Consolidate onto an existing Appwrite table (the six overlaps).** `merchants`, `products`, `terminals`, `payment_intents`, `transactions`, `audit_log` already have a canonical Appwrite table. Migration = retire the Postgres entity, repoint every legacy controller/resolver onto the Appwrite table as the single source of truth, and reconcile any column the legacy admin surface needs (added additively per [ADR-0001 schema-as-code](/adr/0001-appwrite-integration/)). * **Bucket 2 — VAS / financial-services domains → migrate as the VAS module.** `telecom-providers` (airtime), `gift-cards`, `service-providers`, `deposits`, `bank-account`, `merchant-balance` (+ `balance-movement`), `charges` are the special-categories/financial vertical that [PDR-0001](/pdr/0001-mvp-golive-scope/) brings **into MVP scope**. They are modeled fresh per [value-added-services.md](/business-rules/value-added-services/) and `TDR-0003` rather than lifted-and-shifted — Track C owns their Appwrite schema; Track B only ensures the old TypeORM entities are dropped. * **Bucket 3 — Admin/support domains → migrate or reclassify.** `users` (BetterAuth identity — already export-and-freeze per row 79; identity now lives in Appwrite Auth, `employees` is the POS profile), `api-keys`, `addresses`, `personal-information`, `navigation`, `platforms`, `templates`, `email`, `storage` (legacy Supabase/Postgres object model, superseded by `src/storage/appwrite-storage.service.ts`), `cronjobs` (operational scheduling — a candidate to fold into DBOS scheduled workflows rather than a data table). Each is migrated to an additive Appwrite table, reclassified as Appwrite Auth/Storage/DBOS, or dropped if the audit shows it is dead. A **migration inventory** (per-domain: bucket, target Appwrite table, controllers to repoint, data to move, drop plan) is the first Track B deliverable and the source of the §3.2 table in the Wave 3 analysis. ## GraphQL — guard first, then retire [Section titled “GraphQL — guard first, then retire”](#graphql--guard-first-then-retire) **Sequencing rule: the unguarded GraphQL resolvers are secured before any other Track B work.** ADR-0001 already decided **GraphQL is not used** going forward (SDK-primary). So the end state is **GraphQL removed**, not migrated. But because removal follows the per-domain controller repoints, the interim exposure is closed immediately by putting the resolvers behind the `AppwriteAuthGuard` + store-role scopes (the same layer as row 3). **Rationale:** an unguarded GraphQL surface authenticated-but-not-authorized is a P0 production blocker; guarding is hours, full removal rides the domain migrations. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected-1) * **Migrate the GraphQL resolvers to read Appwrite and keep GraphQL** → rejected; contradicts ADR-0001’s SDK-primary/no-GraphQL decision and preserves a surface we intend to delete. ## Decommission sequencing & the DDL path [Section titled “Decommission sequencing & the DDL path”](#decommission-sequencing--the-ddl-path) 1. **Guard GraphQL** (P0, immediate). 2. **Per-domain migration**, dependency-ordered (identity/`users` freeze and the six overlaps first; VAS/financial via Track C; admin/support last). Each ticket: create/confirm the additive Appwrite schema, move any seeded data, repoint controllers to the Appwrite SDK, delete the TypeORM entity + `forFeature` module + tests. 3. **Drop TypeORM domain wiring** — remove the last `@Entity`/data-source domain globs; set `synchronize:false` everywhere (also the row 1 A05 security fix). 4. **Reintroduce migrations as the only DDL path at M6** (row 77) — for the Appwrite side this means the `appwrite push` + additive-only regime graduates to managed schema changes; for the residual Postgres aux (DBOS), migrations own DDL. **Rationale:** guarding removes the acute risk first; per-domain ordering keeps every step reversible until its entity is deleted; disabling `synchronize` and reintroducing migrations closes the auto-DDL-against-live-data hazard ADR-0001’s pre-prod regime tolerated. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected-2) * **Big-bang cutover of all domains at once** → rejected (same reasoning as ADR-0001): no fallback if a domain’s repoint is wrong; per-domain keeps blast radius small. * **Keep `synchronize:true` for the DBOS/aux Postgres** → rejected; even aux DDL should go through migrations post-cutover so schema change is auditable. ## Consequences [Section titled “Consequences”](#consequences) * ADR-0001’s “Legacy stack — kept until cutover” clause is **closed** for domain data by this ADR; the clause’s *reversibility* guarantee holds only per-domain during step 2. * Postgres is now **disposable**: no Appwrite-grade backup story for it; the money ledger and all domain data are in Appwrite and covered by row 76. * The VAS/financial migration is **not** a Track B lift-and-shift — it is new modeling owned by Track C + `TDR-0003`; Track B only removes the old entities. * Scope risk: this is the largest Wave 3 track; the migration inventory must be produced and sized before committing the go-live date. ## References [Section titled “References”](#references) * [0001-appwrite-integration.md](/adr/0001-appwrite-integration/) — the decision this ADR extends. * `terminales-api/docs/decommission/typeorm-and-betterauth.md` — the authoritative migration state and the unguarded-GraphQL warning. * [../business-rules/pos-domain-model.md](/business-rules/pos-domain-model/) — entity→table mapping. * [../pdr/0001-mvp-golive-scope.md](/pdr/0001-mvp-golive-scope/) — VAS + Clientes in scope. * `0003-vas-fulfillment-dbos` — VAS modeling. # ADR 0003 — Observability telemetry transport: Sentry (frontend) + OTel/LGTM (backend) > settle the telemetry-transport question that Wave 2 left as an open PO > **Purpose:** settle the telemetry-transport question that Wave 2 left as an open PO decision (A09, M6 gate) and that blocks \~10 `todos.tasks` items across web and mobile. Track G observability tickets cite this ADR. * Status: Accepted * Date: 2026-07-11 * Deciders: Product Owner + terminales engineering (Wave 3 planning session) * Repos: Multi — `terminales-web` (Next.js), `terminales` (Android/Kotlin), `terminales-api` (NestJS), infra (`terminales-api/observability/`) * Related: `wave2-mvp-analysis` §3.1 A09 + rows 29/83/84, `observability-plan`, `0002-sentry-sdk-wiring` ## Context [Section titled “Context”](#context) Observability is asymmetric across the stack (audit 2026-07-11): * **API — mature.** OpenTelemetry SDK (`src/instrumentation.ts`: OTLP traces → `:4318`, Prometheus metrics → `:8081`, pino log correlation), nestjs-pino + nestjs-otel, and a full **LGTM stack** provisioned at `terminales-api/observability/` (Grafana, Loki, Tempo, Mimir, Prometheus, Promtail, OpenTelemetry Collector). A `grafana-stage` deployment is live on `ionos` (#215). * **Frontend — absent.** Web declares `NEXT_PUBLIC_SENTRY_DSN` but **installs no Sentry SDK**; every metric is console-only or a no-op sink (\~9 emission points blocked on `@blocker(needs-metrics-pipeline)`). Mobile analytics are log-stubs (no crash/error sink); worse, mobile over-logs sensitive traffic at BODY level (row 15). * **Appwrite — unshipped.** Appwrite Function and database logs go nowhere; there is no dashboard or alert on Appwrite-side health. Wave 2 flagged “pick the web/mobile telemetry transport (Sentry vs OTel collector)” as the blocker and drafted the wiring as rows 83 (web) / 84 (mobile). The PO decided in Wave 3. ## Decision — Sentry for frontend errors, OTel/LGTM for backend + Appwrite logs [Section titled “Decision — Sentry for frontend errors, OTel/LGTM for backend + Appwrite logs”](#decision--sentry-for-frontend-errors-otellgtm-for-backend--appwrite-logs) **Two transports, split by strength:** * **Sentry** owns **frontend error/crash reporting and session tracing** on web (`@sentry/nextjs`) and mobile (`sentry-android` / Kotlin). It is the production visibility surface for the web admin and the Android POS terminal — uncaught errors, crashes, failed auth/tender flows, release health. * **OpenTelemetry → the self-hosted LGTM stack** stays the backend transport: API traces (Tempo), metrics (Mimir/Prometheus), and logs (Loki). It is **extended** to ingest **Appwrite Function and database logs into Loki** and to add POS/Appwrite dashboards + alerts in Grafana. * The two are **correlated by shared ids** (request-id / trace-id propagated from the client Sentry event into the backend OTel trace where feasible) so an incident can be followed across the boundary. **Rationale:** Sentry gives managed, low-effort, best-in-class frontend error triage and release health that the raw OTel collector does not — fastest production value for the two surfaces that have *zero* visibility today. Keeping OTel/LGTM for the backend preserves the mature, already-provisioned pipeline and full data ownership of traces/metrics, and Loki is the natural sink for Appwrite logs. Splitting by strength beats forcing one pipeline to do both jobs poorly. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected) * **OTel collector everywhere (route web/mobile through the LGTM stack, no Sentry)** → rejected; more wiring on the frontend, and no managed error-grouping/release-health UX — the frontend surfaces would get raw spans/logs but not the triage workflow that makes them actionable, delaying the value we need most. * **Sentry everywhere (retire self-hosted LGTM)** → rejected; throws away the mature, provisioned API pipeline and full trace/metric data ownership, and adds cost for backend telemetry that already works well. ## Consequences [Section titled “Consequences”](#consequences) * Unblocks Wave 2 rows **83** (web telemetry wiring) and **84** (mobile telemetry wiring) and the \~10 `@blocker(needs-metrics-pipeline)` todos — those emission points now have a transport (`TDR-0002` specifies the wiring). * New MVP prerequisites: a Sentry project/DSN per environment (web + mobile), PII scrubbing configured (no PIN/PAN/JWT in events — reconciles with the row 15 mobile logging fix), and Appwrite→Loki log shipping. * The consolidated wiring, dashboards, alerts, and Appwrite ingestion are specified in `observability-plan` and drafted as Track G. * Metric-name/event taxonomy must be shared web↔mobile↔api so dashboards line up; the plan owns the canonical event names. ## References [Section titled “References”](#references) * `wave2-mvp-analysis` §3.1 A09, rows 29/83/84, §4.6 telemetry decision. * `0002-sentry-sdk-wiring` — the SDK wiring approach. * `observability-plan` — the full plan. * `terminales-api/observability/` — the provisioned LGTM stack. # Auth & multitenancy > how identity, roles, tenant isolation, and the login flows work on Appwrite > **Purpose:** how identity, roles, tenant isolation, and the login flows work on Appwrite. **Status:** as of 2026-09-26 (2FA: TOTP login works, web #1136; owner-as-operator rule 2026-08-18; terminal-onboarding pairing gate 2026-08-12; core model 2026-06-30). ## Multitenancy model (decided 2026-06-19) [Section titled “Multitenancy model (decided 2026-06-19)”](#multitenancy-model-decided-2026-06-19) * **Store = Appwrite Team.** Each store is its own Appwrite Team — the tenant boundary. Membership roles are the POS roles `cashier` / `supervisor` / `manager` (Appwrite team roles are arbitrary strings). * **Merchant = `merchants` collection.** The merchant is an **organization record** grouping stores (`stores.merchantId`), *not* a team (Appwrite teams are flat). A merchant owner overseeing several stores is simply a `manager` member of each store team. * **Single shared database.** Isolation is by **document-level permissions (RLS)** — read `Role.team(storeTeamId)`, write `Role.team(storeTeamId, )`. A query can never return another store’s rows because the requester isn’t in that team. (No DB-per-merchant, no merchant team.) * **Store-team provisioning** (creating a Team) needs the API key, so it’s a **server** op; routine reads/writes are **client-direct** under RLS. * **Labels** (`Role.label(...)`) are reserved for cross-merchant **platform** admins/support — not normal POS roles. ## Roles [Section titled “Roles”](#roles) > The full role catalog, permission matrix, label usage, tenancy tiers, and the future distributor network are specified in [roles-permissions-and-labels.md](/business-rules/roles-permissions-and-labels/). This section is the summary. * **POS role = store-team membership role** (`cashier` / `supervisor` / `manager`, all assignable). There are **no** `pos_roles`/`pos_permissions` collections. * The **role → permission mapping is a code matrix** (`src/auth/roles/pos-roles.ts`) — the single source of truth for both the `Role.team(storeTeamId, )` writes each collection grants and the server-side `assertStoreRole()` checks. * **Enforcement:** Appwrite **RLS** for all data; a lightweight `assertStoreRole()` only inside the few server endpoints that remain (PIN mint, invites, user/team admin, durable workflows). No `@RequireRole` on CRUD. * Sensitive operations write to `audit_log`. ## Sessions: JWT vs cookie [Section titled “Sessions: JWT vs cookie”](#sessions-jwt-vs-cookie) | Client | Mechanism | Why | | -------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | | **Android terminal** | Appwrite **JWT** (minted from the session) | Native app; token also validated by NestJS for server-to-server calls. | | **Next.js web** | Secure **cookie session** (SSR) | Cookies fit server-side rendering. | | **NestJS API** | Validates the incoming Appwrite token (JWT/session) via a custom guard | Replaces the BetterAuth guard + passport strategies in `terminales-api/src/auth/auth.guard.ts`. | ## Login & account flows [Section titled “Login & account flows”](#login--account-flows) All built on **Appwrite Account**. These replace the BetterAuth equivalents. | Flow | Where | Notes | | ---------------------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Email + password login** | Web, Mobile | Standard Account session. | | **Magic link login** | Web, Mobile | Passwordless email link. | | **Forgot / reset password** | Web, Mobile | Account recovery. | | **2FA (MFA)** | Web, Mobile | **Authenticator app (TOTP), email or SMS.** Enrol the app (QR / setup key → verify) or verify email/phone → enable MFA → challenge on login. The web picks the enrolled factor **totp → email → phone** (`pickChallengeFactor`, terminales-web #1136, R9 M15 — before it a TOTP-only account could not sign in); a TOTP challenge has no resend. MFA copy never names an authenticator brand. SMS needs a configured provider per env. | | **PIN login** (cashier fast-login) | API mints, Web + Mobile consume | NestJS validates the employee PIN, then mints an Appwrite **custom token** the client exchanges for a session. Re-targets the legacy PIN-login work. | | **User management** | API endpoints + Web UI | Create/list/update users; assign roles. | | **Invite users** | API flow + Web UI | Invite with role into a team. | | **Presence** | Web | Online-users via the Appwrite **Presences API** (GA 2026-05-25) — presence records + `Channel.presences()` + auto-expiry; scoped to the store team. | ### Implemented (2026-06-30, first wave) [Section titled “Implemented (2026-06-30, first wave)”](#implemented-2026-06-30-first-wave) | Flow | Real paths | | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | API token guard | `terminales-api/src/auth/appwrite-auth.guard.ts` (+ spec) — validates the Appwrite token, replaces the BetterAuth guard (#114). | | Web email/password + magic link | `terminales-web/src/app/login/{LoginForm.tsx,login.api.ts}` + `callback/MagicLinkCallback.tsx`; SSR cookie helper `src/core/api/session.ts` (#101). | | Mobile session/JWT | `terminales/app/.../auth/{SessionManager,EncryptedSessionStorage,JwtAuthInterceptor,SessionAccountClient,SessionClock}.kt` — session lifecycle, encrypted local storage, JWT minted + attached to NestJS calls (#119). | | Mobile 2FA + recovery | `terminales/app/.../auth/{AppwriteMfaRepository,MfaViewModel,AppwriteRecoveryRepository,RecoveryViewModel}.kt` — email/SMS MFA factors + forgot/reset password (#120). | PIN login (custom token) and user-management/invite UIs remain to land. ## PIN login (the one custom flow) [Section titled “PIN login (the one custom flow)”](#pin-login-the-one-custom-flow) Cashiers don’t type full credentials at the terminal. Instead: 1. Client sends store + PIN to NestJS. 2. NestJS verifies the PIN hash against `employees`, checks store/role. 3. NestJS mints an Appwrite **custom token** for that user. 4. Client exchanges the custom token for an Appwrite session (cookie on Web, JWT on Android). This keeps PIN verification server-side while still producing a normal Appwrite session. **Owners sell too (PO 2026-08-18).** Every person who operates the POS — the merchant owner included — needs an operator profile: an `employees` row with a scrypt PIN, created via `POST /pos/employees` (manager-min caller; links an EXISTING auth user, never mutates the membership). There is **no separate `owner` POS role** — the owner’s membership resolves to **manager** rank on the POS plane. Runbook until the web “Personal” UI ships: `terminales-api/scripts/provision-employee.sh` (self-provisioning supported); the e2e seed now creates the owner’s operator profile (PIN in `seed-info.md`). ## Terminal onboarding — the official operator sequence (decided 2026-08-12) [Section titled “Terminal onboarding — the official operator sequence (decided 2026-08-12)”](#terminal-onboarding--the-official-operator-sequence-decided-2026-08-12) A mobile terminal is only **operational** after two distinct provisioning steps, populated by two different mechanisms. Email/password sign-in is a **bootstrap**, not provisioning: it establishes an Appwrite session and stamps the offline tenant, but it does **not** pair a terminal and does **not** populate the operational session. Getting these wrong leaves a device inside the POS but with no store (“Sin tienda asignada”), no terminal (“Terminal: —”), and an empty catalog. | Provisioning step | Created by | Populates | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | **Terminal pairing** | claim a pairing code → `POST /pos/terminals/claim` → `TerminalIdentity{terminalId, storeId, deviceSerial}` in `TerminalIdentityStore` | `isPaired`, the PIN gate, the shift’s terminal, catalog/tenant scope | | **PIN login** | `POST /pos/pin-login` → `{ token, employee{storeId,storeTeamId}, shift }` → `TerminalSessionStore.unlock()` | the operational session: employee row, store, open shift (the shift/sale context resolvers) | **Canonical sequence:** (1) a **manager pairs the terminal once** — a supervisor issues the pairing code from the **web admin** (`/admin/pos/terminals → Vincular terminal`), the operator types it on the device; (2) **cashier PIN login** every shift; (3) select store (multi-store); (4) **Abrir turno**; (5) sell. Pairing codes are 8-char, 5-min TTL (`terminal-pairing.constants.ts`). **Decision (PO, 2026-08-12) — pairing is a required onboarding gate.** This closes design gap #36 (“M05b”): pairing was previously only an in-shell redirect on the sale tabs, so an authenticated email/password + store-pick device slipped into the shell unpaired and the PIN gate (`requiresPin = isPaired && !isUnlocked`) was silently skipped. The gate now lives in `MainActivity`: an authenticated operator whose device has no `TerminalIdentity` (`canEnterPos == false`) is routed to `TerminalSetupScreen` **before** the shell — so no device can operate unpaired, and PIN login always runs. `canEnterPos` stays harness-overridable (`BuildConfig.DEBUG && HarnessSeed.enabled`) so tests keep pre-registering. ## What stays in NestJS [Section titled “What stays in NestJS”](#what-stays-in-nestjs) Token validation guard, PIN verification + custom-token minting, user-management/invite endpoints that need server authority, and anything touching PSP/secrets. See `nestjs-dbos`. ## Deferred until production [Section titled “Deferred until production”](#deferred-until-production) No feature flags gate these flows pre-production (no live users). At cutover, the production-flags ticket (Epic G) introduces `appwrite_auth` gating and a kill-switch. See `open-questions-and-todos`. # Card settlement — the model (AngelPay reconciliation, Option X) > the domain truth for how a card sale's money flows from the terminal to a withdrawable merchant balance — what the system must uphold, independent of how it's built > **Purpose:** the domain truth for how a **card sale’s money** flows from the terminal to a withdrawable merchant balance — what the system must uphold, independent of how it’s built. **Audience:** AI agents (and the humans reviewing them) building the `CardSettlement` sub-project. **Status:** as of 2026-09-21 — design-complete, ready to draft; PO-approved 2026-09-20. Source decision log: `../../backlog/ideation/api-card-settlement-angelpay-reconciliation.md`. Operator flow: `../runbooks/card-settlement-reconciliation.md`. **Related:** [pos-domain-model.md](/business-rules/pos-domain-model/) · [reporting.md](/business-rules/reporting/) · [runtime-settings.md](/business-rules/runtime-settings/) · `terminales-api/src/core/money.ts` (`MINOR_SCALE`). ## Why this exists [Section titled “Why this exists”](#why-this-exists) A card charge on the N86 (the AngelPay SDK) does **not** land in Axios’s account at sale time — the money lands in **AngelPay’s** account. AngelPay settles it to the Axios affiliation (`9613149`) at **T+1**, automatically, paying **net** (it deducts its agreed commission before paying Axios); Fri–Sun settle Monday, cutoff 10:59 pm. Axios then pays merchants via **liquidaciones** (money-out). So at the moment a merchant sees a card sale credited to their wallet, that money is **virtual** — promised by AngelPay, not yet confirmed to Axios. This model records which card money is virtual vs confirmed, without changing what the merchant can already do with a fresh sale. The “bank statement” the system imports to reconcile is the **AngelPay movements export** (an xlsx from the AngelPay portal), **not** a bank CSV. The siafrac-style bank-transfer / per-merchant-reference deposit reconciliation is a **separate later wave** (see **Deferred**, below). ## The core rule — Option X (PO, 2026-09-20) [Section titled “The core rule — Option X (PO, 2026-09-20)”](#the-core-rule--option-x-po-2026-09-20) **Keep the at-sale gross wallet credit; only withdrawals to a bank require settlement.** This is the live path today and it does **not** change: * A card sale credits the merchant wallet its **gross** amount at sale time, `AVAILABLE`, exactly as it does now. * That balance stays **immediately usable** for VAS, merchant→merchant transfers, and top-ups — those check the gross `availableBalance` snapshot and are unaffected by settlement. * Only a **withdrawal to a bank** (a liquidación / money-out) is gated on confirmed money — on `settlement_reports.availableAmount` (see the three money figures below). A per-merchant X/Y runtime toggle (some merchants gross-at-sale, some settle-first) was **rejected** — it forks ledger semantics. There is one path: gross at sale, settlement gates only the withdrawal. ## The AngelPay import is the source of truth for settlement [Section titled “The AngelPay import is the source of truth for settlement”](#the-angelpay-import-is-the-source-of-truth-for-settlement) The nightly rollup that computed settlement reports from terminales’ own records (`terminales-api/src/wallet/settlements/settlement-rollup.scheduler.ts`, crontab `30 6 * * *`) is **disabled, not deleted** — gated behind a new realm setting **`settlement.source`** with values `angelpay_import | computed_rollup`, **default `angelpay_import`**. The rollup is kept as a fallback for a future PSP that cannot provide a settlement export. See [runtime-settings.md](/business-rules/runtime-settings/) for the resolver; the on-demand fallback and the kill switch are in the reconciliation runbook. The import is a **second writer** of `availableAmount` (the first is `applyReportAvailable()` in `settlement-requests.service.ts`); the two must coexist, selected by `settlement.source`. ## The three money figures — kept separate [Section titled “The three money figures — kept separate”](#the-three-money-figures--kept-separate) A card sale carries three distinct amounts. Conflating them is the classic settlement bug; the model keeps all three: | Figure | What it is | Source | Used for | | ------------------------------- | ---------------------------------------------------------------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------- | | **Gross** | The full sale amount, credited to the wallet at sale time | AngelPay `Monto` | The at-sale wallet credit (Option X) — the `availableBalance` snapshot VAS/transfers/top-ups spend against | | **AngelPay net** | Gross − **AngelPay’s** commission (what AngelPay actually deposits to Axios) | AngelPay `Total Depositar` | The Axios↔AngelPay commissions report (what the processor cost us) | | **Merchant net (withdrawable)** | Gross of **confirmed** sales − **Axios’s** effective-dated commission | Computed at report time | `settlement_reports.availableAmount` — what the merchant may withdraw | Note the two commissions are different: AngelPay’s (baked into `Total Depositar`) and Axios’s (the effective-dated rule, below). Axios may absorb AngelPay’s and/or waive its own. ## `settlementStatus` — a new, orthogonal column [Section titled “settlementStatus — a new, orthogonal column”](#settlementstatus--a-new-orthogonal-column) A new **`settlementStatus`** on the card deposit tracks confirmation, **independent** of the existing `status`: * Values: **`pending_settlement` → `settled` | `rejected`**. * It does **not** overload `status`. `status` controls **usability** and stays `AVAILABLE` under Option X (the money is spendable from the moment of sale). `settlementStatus` answers a different question — “is this card money virtual or confirmed?” * Target: the `deposits` table (`terminales-api/src/wallet/deposits/deposits.constants.ts`; generated types `appwrite-row-types.generated.ts`; schema `terminales-api/infra/appwrite/appwrite.config.json`). Channel-E card deposits are written `AVAILABLE` today in `deposits.service.ts` `register()`; `salesOrderId` is the link back to the sale. The import **flips** an existing card deposit’s `settlementStatus` — under Option X the deposit already exists from the sale, so the import never creates or credits. ## `unsettledBalance` — how much of the balance is virtual [Section titled “unsettledBalance — how much of the balance is virtual”](#unsettledbalance--how-much-of-the-balance-is-virtual) **`unsettledBalance`** = Σ of a merchant’s `pending_settlement` card deposits, computed from the deposits table (indexed by `merchantId`), exposed for reporting. It answers “how much of this balance is still virtual right now” — the credit-side view. (It does **not** answer “did this specific debit spend virtual money” — that per-debit attribution is **deferred**, below.) ## Effective-dated commission (Axios’s own) [Section titled “Effective-dated commission (Axios’s own)”](#effective-dated-commission-axioss-own) Axios’s commission is a set of **effective-dated rules** (datetime ranges), **global with a per-merchant cascade**, and each rule may be **`$0.00`** (Axios can absorb AngelPay’s commission and/or waive its own). It applies **only** when a settlement report’s withdrawable amount is computed — never in the cart. **No commission is shown at the POS** (the cart is unchanged). Today’s single rate resolves via `RuntimeSettingsService.inspect` (`merchant → realm → default`) in `settlement-rollup.scheduler.ts`; the effective-dated rules generalise that. ## Idempotency and the join [Section titled “Idempotency and the join”](#idempotency-and-the-join) * **Per-ROW idempotency, not per-file.** Operators re-export and overlap date ranges, so the AngelPay **`ID Transacción`** is the dedup key. It is stored as a **settlement-reference** column on the deposit — **not** as the deposit `idempotencyId` (that field is the tender `clientId` that drives the wallet-credit dedup, a separate concern). * **Join key** — AngelPay row (`Autorización` | `Referencia`/`ID Transacción` | `Folio`) → `payment_intents` (`authCode` / `integratorReference` / `folio`) → `salesOrderId` → the `CARD_TERMINAL` deposit. A multi-tender sale shares one `salesOrderId`, so disambiguate by amount. Terminal serial → `terminals.serialNumber` → `storeTeamId` → merchant. ## Money math [Section titled “Money math”](#money-math) All money is integer **minor units, ×10000** (`terminales-api/src/core/money.ts` `MINOR_SCALE = 10_000`) — see AI\_CONTEXT hard-rule #9. Commission math uses **one rounding rule (half-up)** and the result is compared against AngelPay’s reported figure. ## No vendor names in the UI [Section titled “No vendor names in the UI”](#no-vendor-names-in-the-ui) The operator screens and i18n say **“cobros con tarjeta”** / **“procesador de pagos”** — never the brand. “AngelPay” appears in code, docs, and column mappings only, never in merchant- or operator-facing copy. ## Edge cases [Section titled “Edge cases”](#edge-cases) * **Rejected / unmatched already-credited sale** → flag it in the **exceptions queue** and **leave the wallet credit in place** for an operator to decide. **No auto-reversal in MVP** (see Deferred). Under Option X the money was already spendable; clawing it back is a deliberate later “money-backwards” ticket. * **Overlapping / re-exported rows** → deduped by `ID Transacción` (per-row idempotency); a row already `settled` is a no-op. * **`settlement.source = computed_rollup`** → the import is bypassed; reports come from the nightly rollup. This is the kill switch. ## Deferred — do not forget [Section titled “Deferred — do not forget”](#deferred--do-not-forget) These were explicitly deferred by the PO on 2026-09-20 and are recorded here so they are not lost. The runbook repeats them; `../progress/open-questions-and-todos.md` is the live tracker. * **Per-debit “used virtual money” flag.** Exact per-transaction attribution — stamping a VAS/transfer debit with how much of it was funded by `pending_settlement` money — is a follow-up. This wave ships only the credit-side `settlementStatus` + `unsettledBalance`, which answer “which money is virtual,” not “this specific debit spent virtual money.” Revisit when per-debit reporting is needed. * **Bank-transfer wallet-deposit reconciliation** (the siafrac model: per-merchant reference, staged bank-statement import). This was the original sub-project 4, deferred by “scope A” on 2026-09-20. Its design bullets remain in `backlog/ideation/multi-platform-roles-admin-operator.md`. * **Auto-reversal of a rejected already-credited card sale.** MVP flags the exception only; reversal is a later deliberate money-backwards ticket. # Client → API gateway (NestJS is the mandatory path) > **Status:** as of 2026-09-08. Binding architecture decision (PO/Alfredo, 2026-09-08; ref terminales-api #264). **Scope:** how the web and mobile clients reach backend data/services. **Audience:** all client (web/mobile) + API developers, and anyone reasoning about observability, security, or scaling. ## The rule [Section titled “The rule”](#the-rule) **All client requests route through the NestJS API (`terminales-api`). Clients do NOT talk to Appwrite directly.** Reads and writes from web and mobile go to the Nest gateway, which then talks to Appwrite (and the rest of the backend) server-side. ### Why (the rationale that makes this binding) [Section titled “Why (the rationale that makes this binding)”](#why-the-rationale-that-makes-this-binding) * **Observability** — one choke point where *every* client request is traced, logged, and metered. Metrics/logs/traces are collected in NestJS. If clients hit Appwrite directly, that traffic is invisible to our monitoring; centralizing it makes debugging, capacity planning, and future decisions tractable. * **Security** — a single guarded gateway (auth guards + rate-limiting + audit log) is the enforcement boundary, with Appwrite RLS as defense-in-depth rather than the only line. One surface to reason about and harden. * **Control** — one place to shape, cache, version, and evolve traffic; scaling and behavioral changes happen server-side without touching every client. The trade-offs (extra hop latency, NestJS carries read volume, large Storage blobs proxy through the API) were weighed and accepted — NestJS scales horizontally, and centralized observability/security is the priority. ## The single sanctioned exception: Appwrite Realtime [Section titled “The single sanctioned exception: Appwrite Realtime”](#the-single-sanctioned-exception-appwrite-realtime) **Appwrite Realtime** is a client-direct WebSocket (client → Appwrite) and **cannot be proxied through NestJS**. It is the **one** allowed direct-to-Appwrite path, because live POS sync (a sale on one terminal reflecting on another) needs a push channel and polling is a poor substitute. Even here: * **Data *writes* still flow through NestJS** (and are therefore observable); only the live *push notification* is direct. * The Realtime leg is paired with an **always-on poll backstop** — `useRealtimePolledJob` (`terminales-web/src/core/appwrite/useRealtimePolledJob.ts`) runs the Appwrite Realtime subscription **and** a `refetchInterval` poll concurrently, both writing the same cache idempotently, so a dropped/reconnecting socket never strands a job. * It is gated by the **`appwrite_realtime`** runtime flag (seeded **ON/global** in prod; **OFF** = emergency kill-switch that removes the WS leg, leaving the poll backstop to carry updates at interval latency — no redeploy). This is the only remaining runtime feature flag; see the flag register: `terminales-api/docs/flags/appwrite-rollout.md`. > Note: mobile does not yet have a shared equivalent of `useRealtimePolledJob` — the offline-first Realtime+poll reconcile is specified but not yet built as a shared primitive (possible follow-up). ## Consequences [Section titled “Consequences”](#consequences) * The former `appwrite_client_direct` feature flag is **retired** — “clients direct to Appwrite” is permanently off by architecture, not a toggle. (One of the 7 flags retired in the #264 reconcile.) * New client features consume backend data via the NestJS API, not the Appwrite SDK directly — the sole exception being subscribing to Appwrite Realtime for live updates (behind `appwrite_realtime`). ## References [Section titled “References”](#references) * `terminales-api/docs/flags/appwrite-rollout.md` — the reconciled 1-flag register + kill-switch/observability contract. * terminales-api #264 (rollout-ramp reconcile, closed 2026-09-08); `appwrite_client_direct` retirement. # Merchant verification policy — what an unverified merchant may do > the domain truth for what merchant verification gates. This is the authoritative page; any other doc that describes verification gating must agree with it > **Purpose:** the domain truth for what merchant verification gates. This is the authoritative page; any other doc that describes verification gating must agree with it. **Audience:** AI agents. **Status:** as of 2026-09-26. PO decision 2026-09-25 (“B plus the switch”), as implemented on `terminales-api` `origin/main` (`6172310`). This supersedes the 2026-09-19 “money-out-only” version of this page (formerly `merchant-verification-money-out-only.md`), which is now **policy A** — still available, but only behind the realm switch below. ## The rule, in one line [Section titled “The rule, in one line”](#the-rule-in-one-line) **By default (policy B) an unverified merchant can set up but cannot operate:** it may build its catalog and appearance, hold **one store** and pair **one terminal**, but it cannot sell, open shifts, register cash movements or invite staff until it is verified. Money-out always needs verification. A platform admin can verify on the merchant’s behalf, and can flip the whole realm to policy A (unverified merchants operate; only money-out waits) with the runtime switch `merchant.unverifiedCanOperate`. ## Source of truth in code [Section titled “Source of truth in code”](#source-of-truth-in-code) * **Policy point:** `terminales-api/src/pos/merchant-capabilities/merchant-capabilities.service.ts` — `MerchantCapabilitiesService`. Every gated operation calls its `assertCan*` methods instead of re-deriving the rule; the pure `resolve(merchant, storeCount, policy)` is the policy itself, and `policy` defaults to `{ unverifiedCanOperate: false }` (policy B) so a caller that forgets the switch never grants operating rights. * **Constants (same file):** `UNVERIFIED_MAX_STORES = 1`, `UNVERIFIED_MAX_TERMINALS = 1`, `UNVERIFIED_DEVICE_REQUEST_LIMIT = 1`, `UNVERIFIED_CAN_OPERATE_SETTING = 'merchant.unverifiedCanOperate'`. * **The switch:** `terminales-api/src/core/runtime-settings/settings-registry.ts` — key `merchant.unverifiedCanOperate`, `type: boolean`, `default: false`, `scopes: 'realm'`, `editableBy: 'admin'`, `clientVisible: false`; es-MX label «Comercios sin verificar pueden operar». Read via `RuntimeSettingsService`; any read failure or malformed value resolves to the default, i.e. **fails closed to policy B**. * **Coded 403s:** `terminales-api/src/pos/merchant-capabilities/merchant-unverified.ts` — body `{ statusCode, code, gate, message }` with `code` ∈ `merchant_unverified | merchant_suspended | merchant_store_mismatch` and `gate` ∈ `sell | open_shift | cash_movement | invite_staff | add_store | pair_terminal | request_devices | withdraw | transfer_out`. * **Client read:** `GET /pos/merchants/:id/capabilities` (`merchant-capabilities.controller.ts`; the merchant owner or a `platformAdmin`) returns the resolved `MerchantCapabilitiesDto`. Clients mirror it; they never read the switch. ## Capability table [Section titled “Capability table”](#capability-table) “Verified” = `merchants.isVerified === true` (the composite checklist: owner KYC approved **and** verified bank account, plus the other required checks — `merchant-verification-spine-kyc-integration-design` §6). “Active” = `isActive !== false` **and** `status !== 'suspended'` (fail-closed when the row is missing). | Capability (`MerchantCapabilitiesDto`) | Unverified · **policy B** (default) | Unverified · policy A (switch on) | Verified | Suspended | | -------------------------------------------------------------------------- | ----------------------------------- | --------------------------------- | --------- | --------------- | | Sell — cash, card, VAS (`canSell`) | ❌ | ✅ | ✅ | ❌ | | Open / close shift (`canOpenShift`) | ❌ | ✅ | ✅ | ❌ | | Register cash movement (`canRegisterCashMovement`) | ❌ | ✅ | ✅ | ❌ | | Invite staff — issue, resend **and** accept (`canInviteStaff`) | ❌ | ✅ | ✅ | ❌ | | Stores (`maxStores`) | 1 | unlimited | unlimited | ❌ (active gate) | | Paired terminals, counted across all its stores (`maxTerminals`) | 1 | unlimited | unlimited | ❌ (active gate) | | Handheld device requests (`maxRequestedDevices`) | 1 | 1 | unlimited | 0 | | **Liquidación** (`canWithdraw`) | ❌ | ❌ | ✅ | ❌ | | **Merchant→merchant transfer / wallet→platform top-up** (`canTransferOut`) | ❌ | ❌ | ✅ | ❌ | | Create another merchant (`canCreateMerchant`) | ❌ | ❌ | ✅ | — | | Catalog, store appearance, notifications, the verification flow | ✅ | ✅ | ✅ | ❌ | * Under policy B the four operating capabilities are `false` and the growth caps are 1/1; under policy A they follow `isActive` and the caps are unlimited. The device-request cap and money-out ignore the switch. * **Card and VAS still have their own operational prerequisites** once a merchant may sell (an admin-assigned payment credential on the terminal; a funded wallet or connected platform balance). Those are readiness, not verification. ## Where each gate is enforced (api) [Section titled “Where each gate is enforced (api)”](#where-each-gate-is-enforced-api) | Gate | Call site | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `sell` | `src/pos/sales/sales.service.ts`, `src/pos/sales/settlement.service.ts`, `src/payment-intents/payment-intents.service.ts` (`assertCanSellForStore`); `src/vas/sale/vas-sale.service.ts` (`assertCanSell`) | | `open_shift` | `src/pos/staff/shift.service.ts` (`assertCanOpenShiftForStore`) | | `cash_movement` | `src/pos/staff/cash-movement.service.ts` (`assertCanRegisterCashMovement`) | | `invite_staff` | `src/auth/invites/invites.service.ts` (issue, resend, both accept paths — checked **before** any account is created); `src/auth/user-management/user-management.service.ts` | | `add_store` | `src/pos/foundations/store-provisioning.service.ts` (`assertCanAddStore`) | | `pair_terminal` | `src/pos/terminals/terminal-pairing.service.ts` (`assertCanAddTerminalForStore`; partner/PSP terminals count) | | `request_devices` | `src/device-requests/device-request-allowance.service.ts` (`assertCanRequestDevices`; over-cap → 422 `device_request_unverified_limit`, re-checked at approve) | | `withdraw` | `src/wallet/settlements/settlement-requests.service.ts` (`assertCanWithdraw`) | | `transfer_out` | `src/wallet/wallet.controller.ts`, `src/platform-connections/platform-topup.service.ts` (`assertCanTransferOut`) | The **owner invite** (platform provisioning a merchant’s owner) is not gated. Onboarding refuses a second merchant while the owner already has an unverified one (409 «Ya tienes un comercio pendiente de verificación. Complétala antes de crear otro.», `src/pos/onboarding/onboarding.service.ts`). Denials are counted in `merchant_gate_denied_total{action,reason=suspended|unverified|store_mismatch}`. ## Opening the gate: verify, or verify on behalf [Section titled “Opening the gate: verify, or verify on behalf”](#opening-the-gate-verify-or-verify-on-behalf) * **The merchant verifies** through `/admin/verificacion` (hosted KYC + the checklist) — see `merchant-verification-ux-design`. * **A platform admin verifies on the merchant’s behalf:** `POST /pos/admin/merchants/:id/verification/attest` (`src/verification/http/admin/merchant-verification-admin.controller.ts`; `platformAdmin` only; reason ≥ 10 chars). Attestable checks: `contactEmail`, `contactPhone`, `identity`, `business`, `aml` (when enabled), `bank` (confirms the account the merchant already registered). `terms` is never attestable — the owner accepts them. Web: merchant detail → tab *Verificación* → «Verificar en nombre del comercio». * **Revoke** (`…/verification/revoke`, `platformAdmin`): the merchant goes `lapsed` (`manual_revoke`); only what the platform attested is undone (an attested bank account returns to `PENDING`), what the merchant completed itself is kept; the reason is audit-only. The merchant falls back under the unverified policy. * Provisioning (`provision-on-behalf`) and the bulk import can attest the same checks at creation. ## The switch — who and how [Section titled “The switch — who and how”](#the-switch--who-and-how) * **Realm-only, on purpose.** There is **no per-merchant override**: a merchant-scoped write of `merchant.unverifiedCanOperate` is rejected (422) and ignored at read. A per-merchant escape hatch is exactly what the retired `allowUnverifiedSales` column was. * **Platform admin only.** Written via `PUT /platform/settings/:key` (`src/core/runtime-settings/runtime-settings.controller.ts`), audited, reason required (≥ 8 chars in the web). Platform operators can read it. Web: ADMINISTRACIÓN › *Configuración* (`/admin/platform/settings`), section *Verificación*. * **Default `false`** = policy B. Flipping it is a realm-wide product decision, not an operational fix for one merchant — to unblock a single merchant, verify it (or attest on its behalf). ## Suspension — the kill-switch (both policies) [Section titled “Suspension — the kill-switch (both policies)”](#suspension--the-kill-switch-both-policies) * **Who:** platform **admin** only (merchant lifecycle — create / suspend / reactivate / delete; see [roles-permissions-and-labels.md](/business-rules/roles-permissions-and-labels/) § Platform roles). Web: merchant detail header «Suspender comercio» / «Reactivar comercio» (reason ≥ 8 chars). * **What:** the merchant is inactive/suspended; every gate throws `merchant_suspended` — no selling, operating, growth or money-out, verified or not. * **Reactivate:** restores whatever verification state it held (and therefore whichever policy row applies). * **Audited:** every suspend/reactivate writes an `audit_log` row (actor, before/after, reason). ## Rationale [Section titled “Rationale”](#rationale) * **Set up freely, operate once verified (policy B).** A new merchant can prepare everything it needs (catalog, look, a store, a terminal) while verification runs, so it is ready the moment it is approved — without trading, taking cash or growing a team under an unverified identity. * **Money-out is always gated.** Risk concentrates when value leaves the platform; payouts, transfers and wallet-funded top-ups require verification in both policies. * **One realm-level lever, no per-merchant hatch.** If the business wants day-one selling again, it flips the realm to policy A; it never opens one merchant by hand (the un-audited `allowUnverifiedSales` rows on `manual`, 2026-09-12 — `decisions` — are the failure this avoids). * **Fail closed.** The switch defaults to `false`, a read failure resolves to `false`, and `resolve()` defaults to policy B. ## Edge cases [Section titled “Edge cases”](#edge-cases) * **Card / VAS sale by an unverified merchant** → policy B: denied (`sell`). Policy A: allowed if the operational prerequisite is met. * **Staff invited before the merchant verifies** → the invite may exist, but accepting is refused (`invite_staff`) until the merchant verifies; the invitee sees «La tienda no puede sumar personal por ahora». * **Merchant created by the platform** → born unverified like any other, except for the checks the admin attested at provisioning. * **Verified merchant is later revoked or its KYC/bank lapses** → it returns to the unverified row of the active policy (under B: selling, shifts, cash movements and invites stop; existing stores/terminals stay, but no new ones beyond the cap). * **Bank account edited after verification** → the bank check lapses (edit ⇒ re-verify, W49b), money-out re-gates; under policy B operating re-gates too. This holds on **every** edit: since terminales-api #965 each bank write gets its own recompute (`@<$updatedAt>` event key; before, a constant workflow id meant only the first bank event ever ran, so a CLABE edit after an attestation did not lapse the merchant). A merchant left stuck `lapsed` or wrongly verified is recovered with `POST /pos/admin/merchants/:id/verification/resync` (operator). * **Verified merchant is suspended** → everything stops until an admin reactivates it; verification state is retained. ## Open questions [Section titled “Open questions”](#open-questions) * None for the rule. Web/api copy for the same gate differs (web `merchantVerification.i18n.ts` click-time sentences vs the api `message`) — cosmetic, tracked from the 2026-09-26 docs sweep. **Related:** `merchant-verification-ux-design` · `merchant-verification-spine-kyc-integration-design` · [roles-permissions-and-labels.md](/business-rules/roles-permissions-and-labels/) · [runtime-settings.md](/business-rules/runtime-settings/) · `merchant-onboarding-and-verification-analysis` · `multi-platform-roles-admin-operator` · `decisions` · human manuals: manual técnico §4.3, manual de usuario §2.2.5 # Offline-first reads, tender-scoped offline sales & idempotency > the rules for what the terminal does without network — reads always serve from cache; cash sales may complete offline and sync later; card sales require connectivity — plus the clientId idempotency contract that protects online retries, server replays, and offline-sale sync > **Purpose:** the rules for what the terminal does without network — reads always serve from cache; **cash** sales may complete offline and sync later; **card** sales require connectivity — plus the `clientId` idempotency contract that protects online retries, server replays, and offline-sale sync. **Status:** as of 2026-08-22 (supersedes the 2026-07-06 “no offline sales ever” policy — see Decision history). ## The policy — by tender [Section titled “The policy — by tender”](#the-policy--by-tender) **Offline behavior now depends on how the customer pays (PO decision, 2026-08-22 — binding):** | Tender | Offline? | Why | | ------------------------ | --------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | **Cash** | **Allowed offline.** The sale is captured and finalized on the device and **synced when connectivity returns**. | Cash needs no external authorization — the money is physically in the drawer, so the record can be reconciled later. | | **Card** | **Online required.** The UI **blocks** a card payment while offline. | A card charge needs live authorization from the PSP (AngelPay) — it is impossible to authorize a card without the network. | | **Mixed (any card leg)** | **Online required.** | If any portion is card, the card leg forces the whole sale online. | Reads are always **offline-first** (catalog/product lookups, cached reports/history, notifications, theming) from a local store that is the source of truth for reads. > **Open edges to confirm with the PO/eng before implementation** (do not assume): > > * Offline cash sales assume an **already-open shift** — shift open/close, cash movements and approvals are **not** covered by this decision and remain online for now. Confirm whether a cashier may also open/close a shift or record cash movements offline. > * Confirm the **mixed-payment** ruling above (any card leg ⇒ online) is the intended behavior. {/\* TODO(manual): @area(terminales) @kind(flow) offline cash-sale queue: device-side capture + finalize for cash tender, drain+dedup on reconnect; card path stays online-blocked. Needs eng design ticket. \*/} ## Read path (Android) — unchanged [Section titled “Read path (Android) — unchanged”](#read-path-android--unchanged) 1. Reads serve from a **local cache** (Room/SQLite), hydrated from Appwrite `Databases.listDocuments`. 2. While online, a **Realtime** subscription pushes document changes that refresh the cache. 3. With the network disabled, reads continue from the cache unchanged. This is exactly what shipped in mobile #125 (Room read cache + Realtime refresh) and it continues as-is. ## Cash sales offline — the write path [Section titled “Cash sales offline — the write path”](#cash-sales-offline--the-write-path) 1. **When offline, a cash sale is captured and finalized locally** and enqueued for sync with its `clientId` (see below). The UI clearly indicates it is pending sync. 2. **On reconnect, the queue drains** to the server, which **dedups on `clientId`** so a sale is never double-recorded. 3. **Card / mixed-card sales are blocked offline** — a clear “Sin conexión — el cobro con tarjeta requiere conexión” state (banner + disabled card CTA + reintentar). Cash remains available. 4. **Server-side resiliency still applies for online sales.** The card charge and its settlement side-effects run as **NestJS/DBOS durable workflows** (see `nestjs-dbos`); the client makes one online call and the server owns retries/compensation. > Implementation status: this is a **decided policy, not yet built**. The current code still blocks *all* offline sales (the 2026-07-06 design). Enabling offline cash sales is a mobile feature change tracked separately — until it ships, the terminal blocks every offline sale. ## `clientId` idempotency [Section titled “clientId idempotency”](#clientid-idempotency) * `clientId` is a **client-generated UUID** stamped on every terminal-originated write (notably sales/orders). * It is the idempotency key: the same `clientId` arriving twice produces one record, not two — whether the duplicate comes from an online timeout→retry, a DBOS workflow replay, **or an offline cash-sale re-sync**. * Sales tables (`sales_orders`, `transactions`, etc.) carry `clientId` **from the start** (unique index). * Idempotency depends on the server honoring `clientId` dedup; clients must send it on every money-path call. ## Conflict policy [Section titled “Conflict policy”](#conflict-policy) | Data | Policy | | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Catalog reads (products/prices) | Last-write-wins; the cache is refreshed from server/Realtime. | | Sales (`sales_orders`, `transactions`, `stock_movements`) | **Append-only** — new immutable records; concurrent online terminals, DBOS replays, and offline cash-sale sync all reconcile through `clientId` dedup. | | Stock levels | Derived from the append-only `stock_movements` ledger; decrements applied server-side by the sale’s durable workflow (offline cash sales reconcile stock on sync). | ## Durable / must-succeed flows [Section titled “Durable / must-succeed flows”](#durable--must-succeed-flows) Anything that must not be left half-done — multi-tender settlement, stock decrement on sale, PSP capture + retry, fiscal stamping — runs as a **NestJS/DBOS durable workflow**, not a best-effort client call. See `nestjs-dbos` and [pos-domain-model.md](/business-rules/pos-domain-model/). ## Tenant scoping still applies offline [Section titled “Tenant scoping still applies offline”](#tenant-scoping-still-applies-offline) The local cache holds **only the session’s tenant (and store) data** — never cross-merchant rows. The cache is populated through the authenticated session, which can only read permitted documents. See [auth-and-multitenancy.md](/business-rules/auth-and-multitenancy/). ## Decision history [Section titled “Decision history”](#decision-history) * **2026-08-22 (PO, binding):** offline sales are re-enabled **for cash tender only** — cash sales may complete offline and sync idempotently on reconnect; **card sales require connectivity** (PSP authorization can’t happen offline). This **supersedes the 2026-07-06 “no offline sales ever” policy** for the cash path. The `clientId` idempotency contract now also protects offline cash-sale re-sync. Implementation (device-side cash-sale queue) is a pending mobile feature; until it ships, the terminal still blocks all offline sales. * **2026-07-06 (PO):** operational writes went from “queue offline, sync later” to **online-only** — the server had to hold the sales record at the moment of sale (settlement, stock and PSP capture are server-owned via DBOS). *Now partially superseded (cash) by the 2026-08-22 decision above; the online path for card and the DBOS ownership of server-side resiliency remain.* * **≤ 2026-06-19:** an earlier design described a general offline sales queue for all tenders; superseded by the tender-scoped policy above. # Operator rejection policy — curated reasons & internal notes > the domain truth for what an operator may say to a merchant when a queue item is rejected — a curated, merchant-facing reason, never the operator's free text. Independent of how any single queue is built > **Purpose:** the domain truth for what an operator may say to a merchant when a queue item is **rejected** — a curated, merchant-facing reason, never the operator’s free text. Independent of how any single queue is built. **Audience:** AI agents (and the platform operators the runbook trains). **Status:** as of 2026-09-20. Approved by the PO 2026-09-19 as part of Operator Queues (sub-project 2 — `multi-platform-roles-admin-operator`, § “Sub-project 2 — Operator queues”). Companion daily flow: `operator-queues`. ## Why this exists [Section titled “Why this exists”](#why-this-exists) Every reject action across the operator queues used to have the same trap: the operator typed a free-text `reason` and — on the liquidaciones side — **that free text was shown to the merchant verbatim** (the finding that motivated this policy). On the bank-account side the opposite failure applied: the operator’s `reason` was audit-only, so a rejected merchant saw only the generic “tu cuenta fue rechazada, vuelve a capturar” (web #911) and re-entered the same wrong account because nobody told them what was actually wrong. Both failures are fixed the same way: split what the operator records into two fields with two different audiences. ## Rules [Section titled “Rules”](#rules) * **Every reject is `reasonCode` + optional `internalNote`.** The operator picks a **curated `reasonCode`** from the queue’s catalogue (a `` mirror them. ### Cuentas bancarias — bank-account verification [Section titled “Cuentas bancarias — bank-account verification”](#cuentas-bancarias--bank-account-verification) | `reasonCode` | Merchant-facing es-MX copy | | -------------------- | ------------------------------------------------------------------------------------------- | | `holder_mismatch` | El titular de la cuenta no coincide con el comercio. Sube una cuenta a nombre del comercio. | | `clabe_invalid` | La CLABE no es válida. Verifica los 18 dígitos y vuelve a capturarla. | | `bank_not_supported` | El banco de la cuenta no está admitido. Registra una cuenta de un banco admitido. | | `illegible_document` | El documento no es legible o está incompleto. Sube una carátula clara y completa. | | `statement_mismatch` | La carátula no corresponde a la cuenta capturada. Sube la carátula de esa misma cuenta. | | `other` | Contacta a soporte para más información. *(nota interna obligatoria)* | ### Liquidaciones — settlement request [Section titled “Liquidaciones — settlement request”](#liquidaciones--settlement-request) | `reasonCode` | Merchant-facing es-MX copy | | ------------------ | ------------------------------------------------------------------------------------------------ | | `bank_rejected` | El banco rechazó la cuenta destino. Revisa los datos de tu cuenta y vuelve a solicitar. | | `account_outdated` | Los datos de la cuenta están desactualizados. Actualiza tu cuenta bancaria y vuelve a solicitar. | | `under_review` | El movimiento está en revisión. No necesitas hacer nada por ahora. | | `other` | Contacta a soporte para más información. *(nota interna obligatoria)* | ### Datos fiscales — business / tax verification [Section titled “Datos fiscales — business / tax verification”](#datos-fiscales--business--tax-verification) | `reasonCode` | Merchant-facing es-MX copy | | ------------------- | --------------------------------------------------------------------------- | | `rfc_name_mismatch` | El RFC no coincide con la razón social. Corrige tus datos fiscales. | | `rfc_inactive` | El RFC no está vigente. Verifica tu situación fiscal y actualiza tus datos. | | `fiscal_incomplete` | Tus datos fiscales están incompletos. Completa la información faltante. | | `other` | Contacta a soporte para más información. *(nota interna obligatoria)* | ## Edge cases [Section titled “Edge cases”](#edge-cases) * **Operator submits `other` with no note →** the queue rejects the submission (validation error). “Otro” without a note is never persisted. * **Operator writes an `internalNote` but picks a real curated code →** allowed and encouraged; the note is stored, the merchant still sees only the curated copy, never the note. * **A rejection reason exists in the model but not in this catalogue →** treat it as unmapped and render the generic `other` copy to the merchant rather than leaking a raw slug (same defensive posture as the verification `_fallback` mapping, decisions 2026-09-02). Then file a docs change to add the code. * **Identity checks are locked to Admin.** Operators reject *business/fiscal* checks (this `Datos fiscales` catalogue); identity revoke/unlock/attest is an Admin action and out of this policy’s operator scope — see the runbook’s escalation section. ## Open questions [Section titled “Open questions”](#open-questions) * Final wording of each es-MX string is PO-confirmable; the codes are fixed. Any change is a docs PR against this file. Mirror unresolved items in `../progress/open-questions-and-todos.md`. # POS domain model → Appwrite > the authoritative entity list and how each maps onto Appwrite collections, including the decisions Appwrite changed vs. the original TypeORM design > **Purpose:** the authoritative entity list and how each maps onto Appwrite collections, including the decisions Appwrite changed vs. the original TypeORM design. **Status:** as of 2026-09-26 (R9 B4, api #967: `POST /pos/deposits` refuses channel E — `CARD_TERMINAL` — from any client with a coded 400, closing a hole that credited the merchant wallet with no card sale behind it; channel E is written only internally. R9 second wave: approval freshness — void/refund/discount/shift-variance now expire in 10 minutes like the cash-out approval; VAS single-connection debit + crash-safe; VAS `CANCELLED` order status; product `costPrice` wired end-to-end. R9 first wave: shift gate admin-only + coded refusals, cashier cash-out approval verified server-side, VAS not voidable; shift-close expected-cash rule 2026-09-09, gap #18/Q-n86-4; shift-required-to-sell rule 2026-08-12; wallet domain group 2026-07-11 per PDR-0003/TDR-0004; core model 2026-06-30). **Source:** `terminales-api/docs/pos/{1.README, 2.MVP_ROADMAP, 3.REUSE_ANALYSIS}.md` (REUSE/EXTEND/RENAME/NEW matrix), re-interpreted for Appwrite. ## How Appwrite changes the original design [Section titled “How Appwrite changes the original design”](#how-appwrite-changes-the-original-design) The original POS plan extended TypeORM entities and split queries (GraphQL) from commands (REST). Appwrite changes several of those decisions: | Original (TypeORM/BetterAuth era) | Appwrite-era treatment | | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Legacy `Product` → rename to `LoanProduct` to free `Product` for POS SKU (risky migration) | **Conflict dissolves.** POS `Product` is a **new** Appwrite collection in its own namespace; the legacy loan `Product` stays in Postgres/TypeORM untouched until its own later migration. No rename, no FK rewrite. | | Single `Transaction` ledger extended with `SALE`/`VOID` | **New** Appwrite `transactions` collection (POS ledger) with `salesOrderId/shiftId/terminalId/employeeId` + `clientId`. Legacy financial `Transaction` stays in TypeORM until migrated. | | Reuse `PaymentIntent` (extend) | **New** Appwrite `payment_intents` collection (PSP-agnostic shape); the PSP adapter stays NestJS code. | | Reuse `AuditLog` table | **New** Appwrite `audit_log` collection (action/entity/entityId/before/after JSON). | | `Merchant` → `Store` operational scope | **Store = an Appwrite Team** (tenant boundary); **Merchant = a `merchants` collection** grouping stores (`stores.merchantId`). Document permissions scope by store team. *(Updated 2026-06-19: store=team, not merchant=team.)* | | BetterAuth org role ↔ per-store POS role bridge | **POS role = store-team membership role** (`cashier`/`supervisor`/`manager`); role→permission map is a **code matrix**. **No `pos_roles`/`pos_permissions` collections.** *(Updated 2026-06-19.)* | | “GraphQL for queries, REST for commands” | **Changed:** simple reads/writes = Appwrite SDK **directly from clients under RLS** (no NestJS CRUD proxy); complex/durable commands = **NestJS REST/DBOS**. GraphQL dropped. *(Updated 2026-06-19: clarified no-proxy.)* | | Reuse `dbos` for durable flows | **Unchanged** — settlement, fiscal stamping, PSP retries stay DBOS workflows in NestJS. | | Offline mode post-MVP via `clientId` | **Promoted** — offline-first from the start; `clientId` idempotency baked into sales collections. | ## Collections by domain [Section titled “Collections by domain”](#collections-by-domain) These are the AppwriteData epic’s domain groups (one ticket per group). See [../diagrams/data-model.md](/diagrams/data-model/) for the ER view. ### catalog [Section titled “catalog”](#catalog) `categories`, `products` (POS SKU), `variants` (single-axis), `barcodes`, `tax_rates`. ### foundations [Section titled “foundations”](#foundations) `merchants` (org), `merchant_settings` (per-merchant prefs — notifications/biometric/language/currency), `merchant_themes` (receipt/branding theme), `stores` (carry `storeTeamId` + `merchantId`), `terminals` (storeId / kind / pairingCode / lastSeenAt / currentShiftId), `audit_log`. ### inventory [Section titled “inventory”](#inventory) `stock_levels` ((variantId, storeId) → qty), `stock_movements` (append-only ledger). ### sales [Section titled “sales”](#sales) `carts`, `line_items`, `discounts`, `sales_orders`, `customers` (customer directory — `sales_orders.customerId`), `order_payments`, `transactions` (POS ledger), `payment_intents`, `receipts` (thin artifact). ### staff [Section titled “staff”](#staff) `employees` (POS profile linked 1:1 to the Auth user: userId + storeTeamId + pinHash), `shifts`, `cash_movements`, `approvals`. *(2026-06-19: `pos_roles`/`pos_permissions` dropped — roles are store-team memberships + a code matrix.)* ### reporting [Section titled “reporting”](#reporting) `shift_reports` (frozen Z-report snapshot per closed shift), `daily_sales_rollups` (incremental per-store/day aggregate). *(2026-06-24: added for the hybrid reporting strategy — see [reporting.md](/business-rules/reporting/).)* Both are **derived & reconcilable** from the `transactions` ledger, never the source of truth, and key on the **store-local business date** (`stores.timezone`). ### wallet (merchant balance) *(2026-07-11: added per [PDR-0003](/pdr/0003-vas-connectivity-phasing-and-wallet-visibility/) / `TDR-0004`)* [Section titled “wallet (merchant balance) (2026-07-11: added per PDR-0003 / TDR-0004)”](#wallet-merchant-balance-2026-07-11-added-per-pdr-0003--tdr-0004) `merchant_balances` (one snapshot row per merchant — availableBalance/pendingBalance/totalEarnings/totalWithdrawals; **owners-only** visibility, owner = the additive **`merchants.ownerId`** column decided 2026-07-12), `balance_movements` (append-only ledger: type CREDIT/DEBIT/COMMISSION/WITHDRAWAL/DEPOSIT/TRANSFER\_IN/TRANSFER\_OUT/REFUND, `balanceAfter`, `undoFor`), plus the **Deposit/Charge/Payment trio**: `deposits` (customer “payments”/top-ups, channels A–E, status `pending → declined | available → applied → canceling | canceled | refunded`; **client-registerable only for A–D** since api #967 — `POST /pos/deposits` refuses channel E, `CARD_TERMINAL`, with a coded 400 `deposit_channel_not_allowed` regardless of caller role; E is written only internally, in-process, by sale finalize / settlement-open / the card-charge workflow — see [card-settlement-model.md](/business-rules/card-settlement-model/)), `charges` (**reference existing `line_items`** — additive, no cart refactor), `payments` (Deposit↔Charge application records; split payments = N deposits × M charges of one cart). The trio coexists with the shipped `carts → sales_orders → order_payments/transactions` flow. All wallet mutations are serialized **per merchant** through the DBOS `wallet_processing` partitioned queue — never written directly (TDR-0004). The snapshot is a cache of the ledger; a recompute job asserts Σ movements == balance. ### value\_added\_services *(partly built — see [value-added-services.md](/business-rules/value-added-services/))* [Section titled “value\_added\_services (partly built — see value-added-services.md)”](#value_added_services-partly-built--see-value-added-servicesmd) `providers` (carriers/billers/PIN brands mirror), `provider_products` (denomination/price mirror), `serialized_units` (chips/SIM inventory) are **live**. `service_fulfillments` (external-call record) and `pin_vouchers` (PIN custody) are **still to build**, and the `line_items`/`transactions` VAS field additions are not yet applied. VAS product kinds surface through the unified POS flow (a `kind` discriminator on `products`/`line_items`). ### notifications [Section titled “notifications”](#notifications) `notifications` (type/title/body/refType/refId/audience/contextJson), `notification_reads` (per-user read/dismiss). ### config / platform [Section titled “config / platform”](#config--platform) `meta` (`_meta`, global key/value config), `feature_flags` (rollout flags). ### Reference diagram — with caveats [Section titled “Reference diagram — with caveats”](#reference-diagram--with-caveats) `../diagrams/POS Conceptual Model.png` (PO-drawn system overview) is a useful relationship map (Merchant → Balance → Movements + Transfer, the polymorphic cart line — TAE/PIN/Service/Product, Client ⇢ Cart, Cancel Order → Approval). **Caveats (PO, 2026-07-12) — do not implement these parts as drawn:** * The auth cluster (Users → Auth → Members/Passkeys/Sessions/Accounts → Teams/Organizations) still reflects the **decommissioned BetterAuth model**; identity is Appwrite Auth (see [auth-and-multitenancy.md](/business-rules/auth-and-multitenancy/)). * **“Pay thru Balance” is NOT a general Order Payment path in MVP** — the wallet debit is scoped to VAS fulfillment only ([PDR-0003](/pdr/0003-vas-connectivity-phasing-and-wallet-visibility/), Track K wiring). * **Payment intents are not generalized to all three tender paths** — `payment_intents` stays card-specific; cash tenders keep the shipped `order_payments`/`transactions` path. * The deposit channels A–E (PDR-0003 Decision 3) are not drawn yet — Balance Movement is fed by Transfer only in the diagram. ## Money units — integer minor units (×10000) [Section titled “Money units — integer minor units (×10000)”](#money-units--integer-minor-units-10000) Every monetary value in the system — Appwrite `integer` columns (prices, `openingFloat`, `countedCash`, `expectedCash`, `variance`, `cash_movements.amount`, tender/settlement amounts, commissions, wallet balances), the NestJS DTOs, both clients, the wire payloads (request **and** response), and every test fixture — is an **integer in minor units**: `MINOR_SCALE = 10_000`, so `$1.00 = 10000` and `$0.05 = 500`. Rationale: integers only, never floats, so money math can’t drift. * **Convert only at the human edge** — parse pesos→minor on input, format minor→pesos on render. Nothing in storage or on the wire is ever a peso/major-unit or decimal value. * **Per repo:** web brands the type **`Minor`** (`terminales-web/src/core/money.ts`) so a peso-vs-minor mix-up is a compile error; API money DTO fields are **`@IsInt`** (they reject decimals at the boundary); Android holds money as **cents** internally (`…Cents: Long`) and crosses to minor units **only at the wire boundary** (`ShiftAmount.centsToMinor` / `minorToCents`) — never as a major-unit `Double`. * **Sole exception:** `denominationCounts` **keys** (shift close) are major-unit face values (`"500"`, `"0.5"`) which the server converts with `toMinor`. Rates/ratios (`taxRate` `0.16`) are fractional, not money amounts. * **Cross-repo contract, learned the hard way:** a stale “all money is major units” docstring on the API’s shift-close snapshot led the Android client to serialize shift money as major-unit `Double`s — a 10000× mis-scale that also 400’d `@IsInt` open-shift. Postmortem + the three-repo fix map: `terminales-web/docs/pos/shift-model-c.md` §8. This rule is also stated in `../AI_CONTEXT.md` (hard rule 9) so every agent session loads it. ## Atomicity & consistency [Section titled “Atomicity & consistency”](#atomicity--consistency) * Simple multi-attribute updates use **Appwrite transactions** where sufficient. * Multi-step, must-succeed flows (stock decrement on sale, multi-tender settlement, PSP capture + retry, fiscal stamping) stay **NestJS/DBOS durable workflows**. See `nestjs-dbos`. * Offline writes carry a **`clientId`**; the server dedups so re-sync never double-applies. See [offline-and-idempotency.md](/business-rules/offline-and-idempotency/). ## Reporting-driven field additions [Section titled “Reporting-driven field additions”](#reporting-driven-field-additions) To make reports correct and cheap **without later backfills**, several sales/staff/catalog collections carry extra fields beyond the minimal checkout contract. Historical reports must reflect **point-in-time** values, so reporting-relevant attributes are **snapshotted at write time** rather than joined from mutable catalog rows. Authoritative field lists live in [reporting.md](/business-rules/reporting/) §“Data-model adjustments”; the headline additions: * **`line_items`**: `productId`, `categoryId`, `productName`, `sku`, `unitCost` (margin without joining catalog). * **`sales_orders`**: `shiftId`, `terminalId`, `finalizedAt`, `businessDate`, `voidedAt`, `voidedBy`, `voidReason`, `refundedAmount`, `rolledUpAt` (R9 C7, api #970 — stamped once a delivered VAS sale’s contribution lands in `daily_sales_rollups`, so a backfill or workflow replay never double-counts). * **`transactions`**: `commission`, `netAmount`, `paymentMethod`, `provider`, `entryMode`, `cardBrand`, `completedAt`, status `REFUNDED`. * **`shifts`**: `storeId`, `variance` (counted − expected). * **`discounts`**: `appliedBy`, `approvalId` (loss-prevention / exceptions report). * **`products`**: `costPrice` — **wired end-to-end since R9 C3 (api #968).** `POST /pos/products` / `PATCH /pos/products/:id` accept it (whole centavos, minor units, not negative) and persist it; before #968 the field was silently refused and never stored, so the web’s «Costo» input (§14.8.1 of the technical manual) had no effect. Used for margin and inventory valuation. ## Same-day void (supervisor-approved reversal) [Section titled “Same-day void (supervisor-approved reversal)”](#same-day-void-supervisor-approved-reversal) A finalized ticket can be reversed **within the current shift** — a *same-day void*, **distinct from a refund** (refunds are post-MVP). Rules (web-terminal `#61`, api `same-day-void` + `staff-approval-audit`): * **Supervisor-approved.** Every void requires a **supervisor PIN**, sent to the backend for **server-side verification only** — never compared in the browser. The PIN authorizes an `approvals` row (`type: 'void'`, amount) that the backend **re-verifies** before applying the void. * **Free-text reason required** (bounded length); it lands on `sales_orders.voidReason` with `voidedAt`/`voidedBy`. * **CARD tickets additionally cancel the linked `payment_intents`** record as part of the void. * **VAS sales are never voided or refunded** (PO, R9 2026-09-26): 409 `vas_sale_not_voidable` — see [value-added-services.md](/business-rules/value-added-services/) § VAS and the shift drawer. * **A failed/reversed VAS sale’s order ends `CANCELLED`, not `FINALIZED`** (PO ruling, R9 C8; terminales-api #971): `sales_orders.status` gains a fourth value, `CANCELLED` — a sale that never happened, distinct from `VOID` (which reverses a sale that *did* complete and *did* move money). The fulfillment workflow sets it idempotently as the last step of its failure path; `void`/`refund` still answer `vas_sale_not_voidable` (409) exactly as before. Clients show it as **«No completada»** (web #1146, Android #502), excluded from revenue the same way a `VOID` is. Backfill: `pnpm backfill:vas-failed-cancelled` (dry-run by default) moves pre-#971 failed VAS orders off `FINALIZED`. * Endpoint shape: approve via the POS approvals flow, then `POST pos/sales-orders/:id/void` with `{ reason, supervisorPin }`. Built mock-first on web (`src/app/terminal/voids/`), to be re-validated against the generated DTOs when `pnpm api-types` runs. ## Shift required to sell (turno gates the sale) [Section titled “Shift required to sell (turno gates the sale)”](#shift-required-to-sell-turno-gates-the-sale) **Decision (PO, 2026-08-12):** finalizing a sale **requires an open shift** (turno) — for **all sales, cash *and* card** — gated behind `feature_flag_pos_staff_shifts`. This resolves the previously open design question in `../design/screens/35-fab-acceso-rapido.md` (“qué acciones se ocultan/deshabilitan sin turno”): the sale is one of them. * **Why.** The shift is the cash-drawer accountability envelope (opening float → `cash_movements` → **corte Z** `variance = counted − expected`). A cash sale with no drawer session cannot be reconciled at close, and a same-day void is already **bound to the current shift** (§ above) — a shiftless sale would produce an unbindable void. Applying the rule to card sales too keeps one mental model and complete per-shift reporting (the `transactions` ledger stays the source of truth; the shift is the accountability overlay). * **Enforcement** — add where `shiftId` already flows (today `@IsOptional` on every sale DTO): * **api** — a guard in the `pos/orders/finalize` preamble (mirroring the VAS guards in `sales.service.ts`) rejects a finalize when the store has no open shift; `held-orders` (`create-held-order.dto.ts`, required `shiftId`) is the precedent. * **web** — `TotalsPanel.tsx` `canTender` includes `shift != null`. * **mobile** — the checkout path guards on `shiftSessionStore.currentShift`. * **UX — never a dead-end.** With no open shift the sale CTA/checkout **routes into the open-shift flow** instead of showing an advisory banner over an enabled “Cobrar”. On **web** the open-shift step is a split-window screen (blue brand panel + right card, mirroring the PIN “Acceso rápido” layout) shown **after PIN login when `shift === null`**. * When the flag is **off** (a merchant not running shifts) sales proceed unguarded; per Hard rule 1 (all flags ON for MVP) the guard is **active in MVP**. * **Update 2026-09-26 (PO ruling, R9 A2; terminales-api #964):** a POS sale **always** requires an open shift. The flag is now the runtime setting `pos.shiftRequiredToSell` (default `true`), **admin-only** (`editableBy: 'admin'`) — an owner can no longer switch it off. The key stays registered only because the web and Android still read it; removing it (unconditional guard) is a cross-repo follow-up under feature-flag minimalism. The refusal is coded: **400 `sale_requires_open_shift`** («La venta requiere un turno abierto; abre un turno para continuar») on finalize and settlement-open, and **400 `sale_shift_terminal_mismatch`** («La venta usa un turno de otra terminal») under `pos.shiftTerminalStrict`. The web raises the open-shift gate over the kept cart from «Cobrar» (web #1130/#1133); Android routes a cash refusal to «Abrir turno» (terminales #500). VAS sales follow the same rule ([value-added-services.md](/business-rules/value-added-services/)). ## Cash-out approval (Salida) — verified server-side [Section titled “Cash-out approval (Salida) — verified server-side”](#cash-out-approval-salida--verified-server-side) **Decision (PO, R9 A1, 2026-09-26; terminales-api #964).** A cash-out (`PAY_OUT` / `DROP`) recorded by a caller **below supervisor** needs a supervisor approval that the **api re-checks** from the `approvals` row: action `cash_movement`, `refId` = the shift (same store), minted within the last **10 minutes**, `authorizedAmount` ≥ the movement amount, and **unused** (the movement is written under `cma_`, so a replay or a concurrent second use collides and is refused). Any failure → **403 `cash_movement_approval_required`** («La salida de efectivo requiere la autorización de un supervisor.») and nothing is written. Supervisors/managers need none (one they send is still verified). Cash-ins (`PAY_IN`, `PICKUP`) and the in-process cash-direct deposit need none. Before #964 the controller dropped `approvalId`, so any cashier could lower the drawer. Clients: web #1133 sends `authorizedAmount` and re-asks the PIN on the 403; Android #500 asks the supervisor PIN on every Salida. Staff PINs are 4–6 digits on every keypad. ## Supervisor approval freshness — 10 minutes, per action (R9 C6) [Section titled “Supervisor approval freshness — 10 minutes, per action (R9 C6)”](#supervisor-approval-freshness--10-minutes-per-action-r9-c6) **Decision (PO ruling in the R9 findings; terminales-api #969).** The freshness rule the cash-out approval already used (§ above — minted within the last 10 minutes, `approvals` row re-checked server-side) now covers every other supervisor approval that gates a POS action: **void** (`SALE_VOID`, including cancel of an open order, `POST /pos/orders/:id/cancel`), **refund** (`SALE_REFUND`), **discount over threshold** (`DISCOUNT_OVER_THRESHOLD`) and **shift-variance close** (`SHIFT_VARIANCE`). Before #969 none of these four had an age check — an approval minted hours earlier still worked. A void approval is also **single-use**: its ledger row is keyed on the `approvalId`, so a second sale attempting to reuse the same approval collides and is refused, mirroring the cash-out’s `cma_` key. Each refusal is a **coded 403** (not a generic rejection), carrying the same es-MX sentence the action already used: | Action | Code | | ---------------------------------- | ---------------------------------- | | Void (and cancel of an open order) | `sale_void_approval_required` | | Refund | `sale_refund_approval_required` | | Discount over threshold | `discount_approval_required` | | Shift-variance close | `shift_variance_approval_required` | Clients (web #1144, Android #501) react the same way to any of these four codes: re-open the supervisor PIN sheet for the **same** action (same order/sale/shift, same amount/reason) and retry once a fresh approval is granted — never a dead-end error, which is what happened before (e.g. a stale void approval answered «Autorizado pero la anulación falló. Reconcilia.»). ## Shift close — expected cash (corte Z) [Section titled “Shift close — expected cash (corte Z)”](#shift-close--expected-cash-corte-z) **Decision (Alfredo, 2026-09-09; gap #18 / Q-n86-4).** At close the drawer’s **expected cash** is the textbook drawer expectation, in integer minor units (×10000): > **expected cash = opening float + cash pay-ins − cash pay-outs + cash tenders of FINALIZED non-voided orders on this shift − cash change given.** Voids reverse their cash (a voided order contributes nothing). * **The sales term is not optional.** A prior gap (#18) computed expected cash as `opening float + net(cash_movements)` only — omitting the shift’s cash sales — so a finalized cash sale was silently missing from the close (N86/qa-design: a $3 cash sale did not appear; the close read `500 + 200 − 125 = $575` instead of `$578`). The corrected figure **includes the shift’s CASH tenders**. * **Source of truth = the same aggregation the Z-report uses.** Collect the shift’s orders by `sales_orders.shiftId`, drop VOID orders, and sum the **CASH** `order_payments` (joined by `salesOrderId`; `order_payments` carries no `shiftId` by design — the shift link lives on `sales_orders`). `sales_orders.shiftId` is stamped by **both** the legacy finalize path and the unified settlement path, so both are counted. The close derivation and the Z-report must read the **one** shared formula so they cannot drift. * **Change given is already netted in the tender amount** on the unified path (the recorded `order_payments.amount` is the applied/net cash that stayed in the drawer); the “− cash change given” clause must be honoured **without double-counting** — only subtract `changeGiven` where the stored `amount` is the gross tendered figure. * **Minor units throughout** — opening float, `cash_movements.amount`, and `order_payments.amount` are all minor units; never re-scale (`toMinor`) an already-minor value. Cf. Hard rule 9 + the shift-model-c postmortem (§ Money above). * **One figure, many readers.** `variance = countedCash − expectedCash`, the frozen `shift_reports` (corte Z) snapshot, and the web “Cerrar turno” preview (`pos/shifts/:id/close-snapshot`) all read this single derived value; the API recomputes it authoritatively at close (the client preview is advisory only). > Nuance (PO, 2026-09-10, api PR #697): the sales term counts NON-VOID orders exactly as the Z report does — including an in-flight order whose cash leg is already recorded at the close instant — so the close snapshot and the Z report can never disagree; the cash is physically in the drawer. ## Out of immediate scope (later track) [Section titled “Out of immediate scope (later track)”](#out-of-immediate-scope-later-track) Migrating the **legacy non-POS** entities (lending/deposits/transfers/merchant-balance, etc.) to Appwrite. The active backlog is POS; TypeORM keeps serving legacy domains until the Epic G decommission ticket’s blockers are all done. > **Value-added services (TAE / Pines / Services / Chips):** *no longer “deferred, undecided”.* As of 2026-06-24 these are slated to migrate into the Appwrite POS model via the **unified POS flow** (with a `kind` discriminator + a fulfillment collection; chips as serialized physical inventory). The recommended entities, fields and orchestration are specified in [value-added-services.md](/business-rules/value-added-services/) and the legacy contracts in `apitae-webservice`. Collections are added to the authoritative lists above only when their tickets are created. ## Tenant & store scoping (summary) [Section titled “Tenant & store scoping (summary)”](#tenant--store-scoping-summary) Every tenant-owned document carries the owning team (merchant) and, where relevant, a store attribute. **Document-level permissions** enforce that a user only reads/writes their team’s data, and cashiers are further scoped to their store. Details in [auth-and-multitenancy.md](/business-rules/auth-and-multitenancy/). # Reporting & Analytics > the authoritative catalog of POS reports, their requirements (data, filters, format) > **Purpose:** the authoritative catalog of POS reports, their requirements (data, filters, format), the compute architecture, and the data-model fields/collections each report depends on. Defined in the plan phase so the Appwrite collections capture the right data from day one. **Status:** as of 2026-07-09. Source decisions confirmed with product owner; MVP suite now implemented (see below). **Related:** [data-model.md](/diagrams/data-model/) · [pos-domain-model.md](/business-rules/pos-domain-model/) · mobile screen `design/screens/25-reportes.md` · web flow `design/web-screens/flow-11-reportes.md` · backlog: `api-appwrite-reporting-daily-sales-report.md`, `api-appwrite-reporting-end-of-day-z-report.md`. ## Why this exists [Section titled “Why this exists”](#why-this-exists) Reporting turns raw POS events into business decisions (what sells, who sells it, where cash leaks, what the card processor costs). The legacy `ReportService` aggregated a single `transactions` table and had no product-, employee-, store-, or shift-level breakdown. As we build the Appwrite POS collections, we lock in the fields and indexes those reports need **before** the append-only ledgers and snapshot rows exist — refactoring them later means data backfills, not just code changes. ## Implementation status (2026-07-09, `terminales-api`) [Section titled “Implementation status (2026-07-09, terminales-api)”](#implementation-status-2026-07-09-terminales-api) The MVP report suite is **built** on the API, matching the decisions below: * **Endpoints** live under `src/reports/pos/mvp-reports.controller.ts` (#178): daily sales, sales **by employee** / **by product** / **by payment-method**, **voids & discounts**, and **cash-movements** — each with its own `dto/*-query.dto.ts` (date-range + filters). * **CSV export** via `src/reports/pos/report-csv.util.ts`; **shared pagination** via `report-pagination.ts`; money formatting via `report-money.ts`. CSV is still the only export format (per the v1 decision). * **Rollups:** the `daily_sales_rollups` writer (`src/pos/sales/rollup-writer.ts`) is written by the durable finalize workflow (`src/pos/sales/finalize-workflow.ts`), so long-range reports read snapshots. * Schema is pinned by offline specs (`rollups-schema.spec.ts`, `sales-schema.spec.ts`). ## Decisions [Section titled “Decisions”](#decisions) * **Compute strategy — hybrid.** Live Appwrite queries for short ranges / drill-downs; pre-aggregated `daily_sales_rollups` + frozen `shift_reports` snapshots for fast long-range and historical reports. * **Z-report — frozen immutable snapshot** written by the shift-close flow (not recomputed on demand). * **Export — CSV only for v1.** PDF, share-sheet, and email are planned future formats (annotated, not built). ## Compute architecture (hybrid) [Section titled “Compute architecture (hybrid)”](#compute-architecture-hybrid) | Layer | Used for | Mechanism | | ------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | | **Live query** | Today / current shift, drill-downs, ticket lookup | Appwrite Query API client-direct under RLS; NestJS read where a cross-collection join is needed | | **Frozen snapshot** | Z-reports / shift close | `shift_reports` document written by the shift-close DBOS workflow | | **Pre-aggregated rollup** | Long ranges, trends, multi-store comparison, dashboards | `daily_sales_rollups` maintained incrementally by a DBOS workflow on order finalize / void | **Business-date rule:** rollups and Z-reports key on the **store-local calendar date** (`stores.timezone`, default `America/Mexico_City`), **not UTC** — otherwise “today’s sales” splits across the wrong day. Each derived row stores both `businessDate` (local `YYYY-MM-DD`) and the UTC range (`utcFrom`/`utcTo`) it covers. **Source-of-truth rule:** rollups and snapshots are **derived and reconcilable** — never authoritative. The append-only ledgers (`transactions`, `stock_movements`) remain the source of truth and can rebuild any rollup by replay. Voids/refunds must **decrement** the affected rollup, never be silently dropped. **Offline rule:** reporting is **online-only**; the offline terminal does not compute aggregates. ## Role scope (applies to every report) [Section titled “Role scope (applies to every report)”](#role-scope-applies-to-every-report) RLS scopes all reads to the store team. Within that: | Role | Sees | | ------------------- | --------------------------------------------- | | **cashier** | own shift / own sales only | | **supervisor** | the whole store | | **manager / owner** | the store **and** multiple stores (portfolio) | Common filters available everywhere: **date range** (`from`/`to`) and **store** (multi-store roles only). *** ## Must-have reports (MVP) [Section titled “Must-have reports (MVP)”](#must-have-reports-mvp) ### 1. Daily Sales Summary (Corte Diario) [Section titled “1. Daily Sales Summary (Corte Diario)”](#1-daily-sales-summary-corte-diario) * **Purpose:** end-of-day business pulse per store. * **Data:** gross sales, net sales (gross − commissions), tax collected, discounts, refunds/voids, transaction count, average ticket, units sold; sales-by-tender breakdown; sales-by-hour series. * **Filters:** date range, store, terminal, employee. * **Source:** `daily_sales_rollups` (range); live fallback from `sales_orders` + `transactions` + `order_payments`. * **Format:** KPI cards + line/bar chart + summary table; CSV export. ### 2. Shift Z-Report & Cortes de caja history (mobile Corte Z / web W58) [Section titled “2. Shift Z-Report & Cortes de caja history (mobile Corte Z / web W58)”](#2-shift-z-report--cortes-de-caja-history-mobile-corte-z--web-w58) * **Purpose:** cash reconciliation and audit per shift. * **Data (frozen at close):** cashier, terminal, opened/closed at, opening float, expected vs counted cash, **variance**, sales by tender, cash movements, commissions, txn count, totals. History list shows corte #, cajero, fecha, vendido, diferencia, estado. * **Filters:** date range, store, employee, status (open/closed), variance threshold (e.g. shortages only). * **Source:** `shift_reports` (frozen snapshot); `shifts` for still-open shifts. * **Format:** reprintable detail + history table; CSV export. ### 3. Sales by Product / Category [Section titled “3. Sales by Product / Category”](#3-sales-by-product--category) * **Purpose:** what sells; merchandising and margin decisions. * **Data:** per product/variant/category — units sold, gross, net, **margin** (revenue − cost), % of total, avg price; top-N ranking. * **Filters:** date range, store, category, employee, dimension toggle (product/category), top-N. * **Source:** `line_items` with denormalized `productId` / `categoryId` / `unitCost` snapshot (see adjustments). * **Format:** ranked table + bar chart; CSV export. ### 4. Sales by Payment Method (tender mix) [Section titled “4. Sales by Payment Method (tender mix)”](#4-sales-by-payment-method-tender-mix) * **Purpose:** understand cash vs card mix and card cost. * **Data:** per method/provider — amount, count, % of sales, commissions, net, effective commission rate. * **Filters:** date range, store, employee, method, provider. * **Source:** `order_payments` + `transactions` (`commission` / `netAmount`). * **Format:** summary table + donut/bar; CSV export. ### 5. Sales by Employee (cashier performance) [Section titled “5. Sales by Employee (cashier performance)”](#5-sales-by-employee-cashier-performance) * **Purpose:** staff productivity and loss-prevention attribution. * **Data:** per employee — gross/net sales, txn count, avg ticket, units, discounts given, voids/refunds, cash variance from their shifts. * **Filters:** date range, store, role. * **Source:** `sales_orders.employeeId`, `transactions.employeeId`, `shift_reports`, `discounts`. * **Format:** comparison table; CSV export. ### 6. Commissions Report (web W57) [Section titled “6. Commissions Report (web W57)”](#6-commissions-report-web-w57) * **Purpose:** reconcile PSP / platform fees. * **Data:** by provider/method/concept — operations count, volume, commission, net, effective rate, margin. * **Filters:** date range, store, provider, method. * **Source:** `transactions.commission` / `netAmount`, `payment_intents.provider`. * **Format:** breakdown table; CSV export. ### 7. Cash Movements Report (Movimientos de efectivo) [Section titled “7. Cash Movements Report (Movimientos de efectivo)”](#7-cash-movements-report-movimientos-de-efectivo) * **Purpose:** track non-sale cash in/out for reconciliation. * **Data:** per movement — kind (PAY\_IN/PAY\_OUT, plus DROP/PETTY\_CASH if adopted), amount, reason, employee, shift, time; totals by kind. * **Filters:** date range, store, employee, kind. * **Source:** `cash_movements`. * **Format:** ledger table; CSV export. ### 8. Voids & Discounts (exceptions / loss prevention) [Section titled “8. Voids & Discounts (exceptions / loss prevention)”](#8-voids--discounts-exceptions--loss-prevention) * **Purpose:** surface overrides that erode revenue and flag abuse. * **Data:** voided orders and applied discounts — amount, reason, who performed it, who authorized (approval), original order ref, time. * **Filters:** date range, store, employee, approver, type (void/discount). * **Source:** `transactions(type=VOID)`, `sales_orders` void fields, `discounts`, `approvals`, `audit_log`. * **Format:** exception table; CSV export. *** ## Additional useful reports (later phases) [Section titled “Additional useful reports (later phases)”](#additional-useful-reports-later-phases) | Report | Purpose | Key data | Main source | Notes / gap | | --------------------------------- | ------------------------------- | ----------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------- | | **Inventory / Stock** | On-hand, low-stock, shrinkage | qty on hand, value, movements by type, ADJUSTMENT shrinkage | `stock_levels`, `stock_movements` | Needs `costPrice` for valuation | | **Sales by Terminal/Device** | Per-device performance & health | sales, txn count, last seen, app/os version | `transactions.terminalId`, `terminals` | | | **Multi-store Comparison** | Owner/manager portfolio view | sales/net/margin per store, ranking, period delta | `daily_sales_rollups` | Rollups make this cheap | | **Tax / Fiscal (CFDI)** | Tax collected for filing | tax by rate, taxable base, exempt | `line_items.taxAmount`, `tax_rates` | Aligns with later CFDI epic | | **Merchant Balance / Settlement** | Wallet/payout reconciliation | credits, withdrawals, commissions, balance trend | legacy `balance_movements`, `merchant_balances` | Legacy TypeORM until migrated | | **Refunds Report** | Refund volume & reasons | refunded amount, reason, original order | `transactions`, `payment_intents` | Model refund explicitly (see adjustments) | | **Audit Trail** | Security/compliance review | who changed what, before/after | `audit_log` | | | **Sales Trends / ABC** | Forecasting, fast/slow movers | period-over-period, ABC class | `daily_sales_rollups` | Future analytics | | **Customer reports** | Repeat/loyalty analytics | — | — | **Blocked:** no customer entity today; decide if needed | *** ## Data-model adjustments (what the collections must capture) [Section titled “Data-model adjustments (what the collections must capture)”](#data-model-adjustments-what-the-collections-must-capture) > These extend the contracts defined in the AppwriteData backlog tickets. Field names below are additions on top of each ticket’s current contract. ### A. New collections (reporting persistence) [Section titled “A. New collections (reporting persistence)”](#a-new-collections-reporting-persistence) **`shift_reports`** — frozen Z-report snapshot, written by the shift-close DBOS workflow; immutable. * `shiftId`, `storeId`, `storeTeamId`, `merchantId`, `terminalId`, `employeeId` * `businessDate` (store-local), `openedAt`, `closedAt` * `openingFloat`, `expectedCash`, `countedCash`, `variance` * `grossSales`, `netSales`, `taxTotal`, `discountTotal`, `refundTotal`, `txnCount`, `unitsSold` * `salesByTender` (JSON: method → `{amount, count}`), `commissionsTotal` * `cashMovementsSummary` (JSON: kind → `{amount, count}`) * `reportNumber` (sequential per store). CSV-exportable. **`daily_sales_rollups`** — incremental aggregate, maintained by a DBOS workflow on finalize/void. * `storeId`, `storeTeamId`, `merchantId`, `businessDate` (store-local `YYYY-MM-DD`), `utcFrom`, `utcTo` * `grossSales`, `netSales`, `taxTotal`, `discountTotal`, `refundTotal`, `txnCount`, `unitsSold`, `avgTicket` * `salesByTender` (JSON), `salesByHour` (JSON array\[24]), `commissionsTotal` * Unique index `(storeId, businessDate)`; reconcilable by replaying the day’s ledger. * **Start with store/day.** Add companion rollups (per-employee/day, per-product/day, per-category/day) only if live drill-down proves too slow. ### B. Field additions / denormalization on existing collections [Section titled “B. Field additions / denormalization on existing collections”](#b-field-additions--denormalization-on-existing-collections) > **Rationale:** append-only ledgers and historical reports must reflect **point-in-time** values. If a row only stores FKs, later price/cost/category/name edits corrupt historical reports. Snapshot the reporting-relevant fields at write time. * **`line_items`** — add snapshot fields `productId`, `categoryId`, `productName`, `sku`, `unitCost` (copied from `products.costPrice` at sale → margin without joining the mutable catalog). Keep existing `unitPrice`, `taxAmount`, `discountAmount`, `qty`. * **`sales_orders`** — add `shiftId`, `terminalId`, `finalizedAt`, `businessDate`; void/refund fields `voidedAt`, `voidedBy`, `voidReason`, `refundedAmount`. * **`transactions`** — add `commission`, `netAmount`, `paymentMethod`, `provider`, `entryMode`, `cardBrand`, `completedAt`; extend status with `REFUNDED`. (Already has `shiftId`/`employeeId`/`terminalId`.) * **`order_payments`** — add `provider`; consider `tipAmount`. (Already has `method`, `amount`, `changeGiven`.) * **`shifts`** — add `storeId` (+ `storeTeamId`/`merchantId` for scoping), `variance`, explicit `openedAt`/`closedAt` if base timestamps aren’t sufficient. (Already has `openingFloat`, `expectedCash`, `countedCash`, `status`, `terminalId`, `employeeId`.) * **`discounts`** — add `appliedBy`, `approvalId` (links to `approvals` for the exceptions report). (Already has `scope`, `kind`, `value`, `refId`.) * **`products`** — confirm `costPrice` is captured (required for margin/inventory valuation). Currently optional. * **`cash_movements`** — confirm `kind`, `amount`, `reason`, `shiftId`. Consider extending the enum with `DROP`/`PETTY_CASH` if operations need drawer drops distinct from pay-outs. ### C. Indexes for reporting performance [Section titled “C. Indexes for reporting performance”](#c-indexes-for-reporting-performance) | Collection | Indexes | | --------------------- | ------------------------------------------------------------------------------ | | `sales_orders` | `(storeId, finalizedAt)`, `(employeeId, finalizedAt)`, `(shiftId)`, `(status)` | | `transactions` | `(storeId, completedAt)`, `(type, completedAt)`, `(shiftId)`, `(employeeId)` | | `line_items` | `(productId)`, `(categoryId)` (+ join key to order / business date) | | `shift_reports` | `(storeId, businessDate)`, `(employeeId, closedAt)` | | `daily_sales_rollups` | unique `(storeId, businessDate)` | | `cash_movements` | `(shiftId)`, `(storeId, createdAt)` | ## Export [Section titled “Export”](#export) * **v1: CSV only** for every report listed above. * **Future (annotated, not yet built):** PDF (formatted Z-report / daily cut), share-sheet (mobile), email delivery. # Roles→permissions matrix — web ↔ api sync & drift analysis > **Status:** 2026-09-09 (merchants TL, canvas-review row 94); updated 2026-09-19 (PlatformRoles foundation — added [§5 Platform roles](#5-platform-roles-admin--operator)). Companion to [`roles-permissions-and-labels.md`](/business-rules/roles-permissions-and-labels/). This note answers Alfredo’s W40 review item: *“analyze whether the roles/permissions matrix is correctly implemented and reflects the actual user capabilities.”* Read-only analysis against `origin/main` of both repos — no code change. ## TL;DR [Section titled “TL;DR”](#tldr) 1. The web W40 matrix (`terminales-web/src/components/PosRoles/posRoles.ts`) and the api canonical (`terminales-api/src/auth/roles/pos-roles.ts` — `ROLE_PERMISSIONS`) are **currently identical** — no drift. 2. But `ROLE_PERMISSIONS` is a **descriptive/documentation matrix**, not the runtime gate. The api enforces with **role-rank** minimums (`assertStoreRole(ctx, storeTeamId, minRole)` → `roleSatisfies`/`ROLE_RANK`), chosen per endpoint by hand — and there is a **third** hand-mirror (`role-scopes.ts`). So “actual capabilities” are the per-endpoint `minRole` gates, which are *aligned to* the matrix, not *derived from* it. 3. Three copies of one truth (api `ROLE_PERMISSIONS`, web `posRoles.ts`, api `role-scopes.ts`) + N hand-chosen `assertStoreRole(minRole)` call-sites = a standing drift risk. **Recommendation: generate the web mirror from the api canonical (shared package / codegen), and add a contract test that each endpoint’s `minRole` matches `ROLE_PERMISSIONS`.** Details in §4. ## 1. The two matrices are identical (no drift today) [Section titled “1. The two matrices are identical (no drift today)”](#1-the-two-matrices-are-identical-no-drift-today) Both define the same roles, the same 14-permission union, and the same cumulative composition: | Role | Permissions (cumulative) | | -------------- | ------------------------------------------------------------------------------------------------------------------ | | **cashier** | `catalog:read`, `inventory:read`, `sales:read`, `sales:create`, `shifts:open`, `shifts:close`, `cash:movement` (7) | | **supervisor** | + `sales:void`, `inventory:write`, `reports:read`, `employees:read` (11) | | **manager** | + `catalog:write`, `employees:write`, `store:manage` (14) | * api: `terminales-api/src/auth/roles/pos-roles.ts` — `CASHIER_PERMISSIONS` / `SUPERVISOR_PERMISSIONS` / `MANAGER_PERMISSIONS` → `ROLE_PERMISSIONS` (`pos-roles.ts:60-94`). * web: `terminales-web/src/components/PosRoles/posRoles.ts` — the same three arrays → `ROLE_PERMISSIONS` (`posRoles.ts:60-108`), explicitly documented as a **hand-copied mirror** of the api (see its header comment). Verified line-by-line on `origin/main` 2026-09-09: **no divergence**. The W40 screen therefore renders the documented capability model correctly, and the web `posRoles.spec` pins the exact per-role set as a web-side guardrail (AC2 of #299). ## 2. …but `ROLE_PERMISSIONS` is not what the API enforces [Section titled “2. …but ROLE\_PERMISSIONS is not what the API enforces”](#2-but-role_permissions-is-not-what-the-api-enforces) The runtime authorization gate is **role-rank**, not permission-set membership: * `terminales-api/src/auth/roles/assert-store-role.ts:49-58` — `assertStoreRole(ctx, storeTeamId, minRole)` passes iff `roleSatisfies(role, minRole)` (i.e. `ROLE_RANK[role] >= ROLE_RANK[minRole]`). * Controllers/services call it with a hand-chosen `minRole`, e.g. `invites.service.ts:366,563` (`assertStoreRole(ctx, storeTeamId, 'manager')`). * `roleHasPermission` / `ROLE_PERMISSIONS` is consulted only by **`role-scopes.ts`** (whose comment says its scopes *“mirror `ROLE_PERMISSIONS`”* — `role-scopes.ts:8,26`) and referenced in **`shift.service.ts:84,286`** (a comment about *“reconciling this gate with `ROLE_PERMISSIONS` (issue #288)”* + `CLOSE_MIN_ROLE`). No guard does `ROLE_PERMISSIONS[role].includes(permission)` at request time. **Implication:** the matrix is consistent with enforcement *today* because every capability’s real gate is a rank minimum and the matrix is cumulative-by-rank — but each endpoint’s `minRole` is an independent hand decision. Nothing mechanically guarantees, say, that the endpoint behind `sales:void` actually requires `supervisor` (the matrix’s claim). The #288 reference in `shift.service.ts` is evidence such a reconciliation has already been needed once. ## 3. Row-94 sub-items (W40 screen) [Section titled “3. Row-94 sub-items (W40 screen)”](#3-row-94-sub-items-w40-screen) * **“Reflects actual capabilities?”** — Yes for the documented model, with the caveat in §2: it mirrors `ROLE_PERMISSIONS`, and enforcement is rank-based and hand-aligned to it. No mismatch found today. * **“Remove `Ver documentación` button (does nothing)”** and **“canvas checkmark colors”** — pure web UI, not part of this analysis; they belong in the W40 parity slice (`web-parity-staff-roles-invite`, FD-7 draft). ## 4. Recommendation [Section titled “4. Recommendation”](#4-recommendation) Ranked, cheapest-first: 1. **Contract test (do first, cheap):** a test that asserts the web `ROLE_PERMISSIONS` equals the api `ROLE_PERMISSIONS` verbatim. Today they live in separate repos, so this needs the api matrix reachable to the web test — which motivates (2). As an interim, keep the existing `posRoles.spec` pin. 2. **Single source (recommended): generate/share the matrix.** Publish `pos-roles.ts`’s `ROLE_PERMISSIONS` (+ `POS_ROLES`, `PosPermission`) from a **shared package** (or a codegen step the api emits and the web imports), and **delete the web hand-mirror** + reduce `role-scopes.ts` to a derivation. This removes three copies down to one. The static matrix does **not** warrant a runtime **read-endpoint** (it is compile-time-known; an endpoint adds a network round-trip and a cache to display a constant) — prefer the shared package unless a future need to toggle permissions at runtime appears. 3. **Enforcement-alignment test (closes the §2 gap):** derive or test each endpoint’s `assertStoreRole(minRole)` against `ROLE_PERMISSIONS`, so the *gate* provably matches the *matrix* (not just the two matrices matching each other). This is the deeper guarantee row 94 is really asking for. **No code shipped from this note** (row 94 said “no code unless drift is found”; no matrix drift found). Items (1)–(3) are proposals for the PO to route as follow-up slices; (2) is the durable fix and would let the web `posRoles.ts` mirror be deleted. ## 5. Platform roles (admin / operator) [Section titled “5. Platform roles (admin / operator)”](#5-platform-roles-admin--operator) The sections above cover the **store plane** (POS `cashier < supervisor < manager`). The **platform plane** — InspiraCode staff granted via Appwrite **Labels** — has, as of the 2026-09-18 PO brainstorm, exactly **two** roles (see the companion [`roles-permissions-and-labels.md` § Platform roles](/business-rules/roles-permissions-and-labels/#platform-roles-labels) and `../../backlog/ideation/multi-platform-roles-admin-operator.md`): | Role | Label | Scope | | ------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Operator** | `platformOperator` | Day-to-day platform queues (verification, deposits, liquidaciones incl. rollup, device diagnosis, merchant directory read-mostly, VAS catalog sync). | | **Admin** | `platformAdmin` | **Operator ⊇** everything Operator can, **plus** dangerous/structural powers (grant/revoke roles, config & feature flags, PSP credentials, overrides, verification revoke/unlock, merchant lifecycle). | Support / finance / developer roles are **deferred**, and impersonation is **post-MVP**. Unlike the store matrix, the platform gate is **not** rank-vs-permission-set: one guard, `assertPlatformRole(labels, ...allowed)`, admits a caller carrying any allowed label, and `platformAdmin` satisfies every operator route. **Drift note (why this belongs here):** the full **platform route matrix** — which route requires `operator` vs `admin` — is decided in the API ticket [inspiraCode/terminales-api#767](https://github.com/inspiraCode/terminales-api/issues/767) and mirrored in the companion doc’s [§ Platform route matrix](/business-rules/roles-permissions-and-labels/#platform-route-matrix-operator-vs-admin). To avoid a third hand-copy of one truth (the §4 lesson), it is **not** re-tabulated here — cite the companion doc and the API ticket, not a copy. **Navigation audience:** the `ADMINISTRACIÓN` group is platform-staff-only (admins + operators); merchant-plane roles use the separate `MI NEGOCIO` group. Resolves the `TODO(human)` in `terminales-api/src/navigation/navigation-seed.ts`. ## References [Section titled “References”](#references) * api canonical: `terminales-api/src/auth/roles/pos-roles.ts` (`ROLE_PERMISSIONS`, `ROLE_RANK`, `roleSatisfies`). * api enforcement: `terminales-api/src/auth/roles/assert-store-role.ts`; `role-scopes.ts`; `shift.service.ts` (#288). * web mirror + screen: `terminales-web/src/components/PosRoles/posRoles.ts` (+ `posRoles.i18n.ts`), `src/core/auth/roles.ts`. * [`roles-permissions-and-labels.md`](/business-rules/roles-permissions-and-labels/) — the role/label model this matrix serves. # Roles, permissions & labels > the authoritative what of authorization — every role, the permission matrix, label usage, and when to use each Appwrite auth feature. The why and the login/account flows live in auth-and-multitenancy.md; this doc is the structured reference that sits beside it > **Purpose:** the authoritative *what* of authorization — every role, the permission matrix, label usage, and when to use each Appwrite auth feature. The *why* and the login/account flows live in [auth-and-multitenancy.md](/business-rules/auth-and-multitenancy/); this doc is the structured reference that sits beside it. **Status:** as of 2026-09-26 (verification `resync` recovery + cleanup-script rule, terminales-api #965/#966). ## The three planes [Section titled “The three planes”](#the-three-planes) Authorization splits into **two planes**; a future **third plane** is commercial, not authorization. Keeping them separate is the load-bearing idea of this doc. | Plane | Appwrite mechanism | Audience | Governs | | -------------------- | ------------------------------------------ | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- | | **Platform** | **Labels** (`Role.label(...)`) | InspiraCode staff (Admin / Operator) | Cross-merchant operations & support | | **Store** (tenant) | **Teams + memberships** (`Role.team(...)`) | Merchant owner + employees | Per-store operational data (RLS) | | **Network** (future) | **Graph data** (not Teams, not Labels) | Distributors | Recruitment & commission relationships — see [§ Network / distributor plane](#network--distributor-plane-forward-compatible) | **Merchant straddles platform and store:** identified at the platform plane by `merchants.ownerId`, it operates at the store plane via `manager` membership on each owned store team. It is deliberately **not** a label (see [§ Merchant](#merchant-the-owner)). ## Platform roles (Labels) [Section titled “Platform roles (Labels)”](#platform-roles-labels) Platform staff are cross-merchant and are granted via Appwrite **Labels** — never store-team memberships. Labels are project-scoped (see [§ Tenancy isolation tiers](#tenancy-isolation-tiers--portability)). Every platform action is audited. There are **two** platform roles, and only two (PO brainstorm 2026-09-18 — `multi-platform-roles-admin-operator`; `decisions`). **Admin ⊇ Operator:** an admin can do everything an operator can, plus the dangerous and structural powers. The single server guard is `assertPlatformRole(labels, ...allowed)` and `platformAdmin` satisfies any operator-allowed route. | Role | Label | Can do | Notes | | ------------ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Operator** | `platformOperator` | The day-to-day queues: merchant & bank-account verification, deposit review, liquidaciones (incl. the on-demand rollup), device-enablement diagnosis, read-mostly merchant directory / basic assistance, VAS catalog sync. | Read-mostly on the merchant directory; the dangerous/structural actions in the Admin row are withheld. Every action audited. | | **Admin** | `platformAdmin` | Everything Operator can, **plus** the dangerous & structural powers: granting/revoking platform roles, platform config & feature flags (including the realm switch `merchant.unverifiedCanOperate`), PSP credentials, verification attest (verify on behalf) / revoke / unlock, merchant lifecycle (create / **suspend** / reactivate / delete — suspension is the kill-switch since `allowUnverifiedSales` was retired, see [merchant-verification-policy.md](/business-rules/merchant-verification-policy/)). | “Manage only *their own* merchants” is **not** a blanket label — scope it via `merchants.ownerId` / an explicit admin↔merchant assignment, checked server-side. A bare `platformAdmin` label is global; ownership scoping is data, not a role. | **Deferred — not roles today.** A support role (`platformSupport`), a finance role, and a developer role (`platformDeveloper`) are **explicitly out of scope** for now — the 2026-09-18 decision is two roles only. **Impersonation is post-MVP** — no platform role impersonates a user today. These are listed so the model stays forward-compatible, not because anything exercises them. **Granting is a product action, not a script.** Platform roles are granted and revoked **from the product by an admin** (no script-only path), and every grant/revoke is written to `audit_log`. The kill switch for any platform capability is revoking the label from the user. **Labels-only — the known restore caveat.** Platform authorization is labels-only (Option A). The one accepted caveat: **Appwrite backup restore does not replay user labels**, so a restore can silently drop platform grants — tracked and owned by `api-backup-restore-does-not-replay-user-labels-authz`. ### Platform route matrix (operator vs admin) [Section titled “Platform route matrix (operator vs admin)”](#platform-route-matrix-operator-vs-admin) Every platform route resolves through `assertPlatformRole`. **operator** = the caller carries `platformOperator` **or** `platformAdmin`; **admin** = `platformAdmin` only. The source of truth for the code change is the API ticket ([inspiraCode/terminales-api#767](https://github.com/inspiraCode/terminales-api/issues/767)); this table mirrors its decided target. | Route | Required role | | --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | `GET /pos/settlements/ops/requests`, `GET /pos/settlements/ops/bank-accounts` | operator | | `POST /pos/settlements/ops/rollup` | operator | | `PATCH /pos/settlements/requests/:id/status` | operator | | `GET /pos/settlements/bank-account/admin`, `PATCH /pos/settlements/bank-account/:id/verification` | operator | | `POST /pos/admin/merchants/:id/verification/confirm-business`, `.../verification/resync` | operator | | `POST /pos/admin/merchants/:id/verification/attest`, `.../verification/revoke`, `.../verification/unlock` | **admin** (the old `.../override` route is gone — `merchant-verification-admin.controller.spec.ts` asserts it is not registered) | | `GET /platform/settings` | operator (read) | | `PUT /platform/settings/:key` (incl. `merchant.unverifiedCanOperate`) | **admin** | | `POST /vas/admin/catalog/sync` | operator | | `GET /platform/merchants*`, `GET /platform/summaries/*` (cross-tenant read) | operator | | `GET /workflows/:id`, `/steps` (any-workflow read) | operator | | `POST\|PATCH\|GET /admin/psp-credentials` | **admin** | | `POST /pos/merchants`, `DELETE /pos/merchants/:id`, merchant `status` edits | **admin** | | `POST /platform/impersonations` | **admin** (post-MVP; untouched) | **`…/verification/resync` is the recovery path.** It re-derives the merchant by calling `recomputeMerchant` directly — not through a DBOS workflow id — so it always runs. Use it for a merchant stuck `lapsed` or wrongly verified, and after a recompute trigger exhausted its 10 retries (`verification.recompute.retries_exhausted`, terminales-api #966). `attest` answering 409 on an already-VERIFIED bank account is expected; resync is the path. **Cleanup scripts that delete or rewrite bank-account rows directly in the database must call `resync` for each affected merchant afterwards** — a direct write bypasses the bank service, so no per-event recompute (terminales-api #965) fires and the merchant’s `isVerified` goes stale. Service-level re-asserts must match the controller’s role — a controller widened to operator while its service still asserts admin is a silent 403. ### Navigation audience [Section titled “Navigation audience”](#navigation-audience) The **`ADMINISTRACIÓN`** navigation group is for **platform staff only** — admins and operators. Merchant-plane roles (owner / manager / supervisor / cashier) use a separate group, **`MI NEGOCIO`**. A user carrying only a platform label sees `ADMINISTRACIÓN`; a merchant user never does. This resolves the `TODO(human)` in `terminales-api/src/navigation/navigation-seed.ts`. ## Merchant (the owner) [Section titled “Merchant (the owner)”](#merchant-the-owner) The **Merchant** is an organization owner, modeled as a record, not a label: * Owns a `merchants` record; groups stores via `stores.merchantId` (see [pos-domain-model.md](/business-rules/pos-domain-model/) § foundations). * Operates each store by holding the **`manager`** team membership on that store’s team — the same role a salaried store manager holds. This is what makes store-data RLS “just work” for the owner; no special-case code. * Manages stores, employees (invites/roles), and white-label themes (screens W48–W54 in `frontend-context`). * **Why a record, not a label:** the owner anchors the future distributor network graph (`sponsorId` hangs off this record). A flat label could not express ownership scope or recruitment relationships. ## Store-team membership roles [Section titled “Store-team membership roles”](#store-team-membership-roles) ### Active roles [Section titled “Active roles”](#active-roles) The POS role **is** the Appwrite team-membership role — `manager` / `supervisor` / `cashier` (team roles are arbitrary strings). These three are the real, assignable staff roles, grounded in the built product: the employee role enum is `cashier | supervisor | manager` (`EPosRoleType` in `pos-role.entity.ts`; `PosEmployeeDto` in the web generated types), the PIN badge shows `role` (screens M02/W04), W38 assigns a role, and W40 renders the role matrix. | Role | Granted by | RLS expression | Scope | Approvals | | -------------- | -------------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------- | | **manager** | Merchant/Admin (and held by the owner) | `Role.team(storeTeamId, 'manager')` | Full control of the store (catalog, inventory, staff, stores/terminals, reports). | Approves everything a supervisor can. | | **supervisor** | manager | `Role.team(storeTeamId, 'supervisor')` | Manage inventory, products, services, store staff. Whole-store reporting. | **Approves** cashier discounts, refunds, returns. | | **cashier** | manager/supervisor | `Role.team(storeTeamId, 'cashier')` | Sales & transactions; own profile, own shift, own receipts. | **Requests** approvals from a supervisor. | Report scopes (cashier = own shift, supervisor = store, manager/owner = portfolio) are defined in [reporting.md](/business-rules/reporting/) and must match this table. ### Reserved roles (not yet active) [Section titled “Reserved roles (not yet active)”](#reserved-roles-not-yet-active) The 88 designed screens are entirely staff-facing POS. “Customer” exists today only as a **data record** (`customers` collection + `sales_orders.customerId`, assigned to a sale by a cashier — the Clientes module, `frontend-context` §5), and there is **no public storefront / anonymous flow**. These two roles are documented so the model stays forward-compatible, but they are **not active — no screens exercise them today**: * **customer** — an authenticated Account with **no** team membership; reaches only its own rows via `Role.user(id)`. Activated by a future customer portal. * **guest** — anonymous/unauthenticated session; public reads only via `Role.guests`. Activated by a future public storefront. ## Permission matrix [Section titled “Permission matrix”](#permission-matrix) Rows = collection groups ([pos-domain-model.md](/business-rules/pos-domain-model/)); columns = active store roles. `W` = read+write, `R` = read, `—` = none. Each write maps to a `Role.team(storeTeamId, )` grant on the row and the corresponding `AuthScope` in `auth.scopes.ts`. | Domain / collections | manager | supervisor | cashier | Scope (`AuthScope`) | | -------------------------------------------------------------------------------- | ------------- | ----------- | ------------- | ---------------------------------------- | | **catalog** — `categories,products,variants,barcodes,tax_rates` | W | W | R | `POS_CATALOG_READ/WRITE` | | **inventory** — `stock_levels,stock_movements` | W | W | R | `POS_INVENTORY_READ/WRITE` | | **sales** — `carts,line_items,sales_orders,order_payments,transactions,receipts` | W | W | W | `POS_SALES_READ/WRITE`, `TRANSACTIONS_*` | | **discounts / approvals** — `discounts,approvals` | W (approve) | W (approve) | R + request | `POS_SALES_*` | | **shifts** — `shifts,cash_movements` | W | W | W (own) | `POS_SHIFTS_READ/WRITE` | | **staff** — `employees` | W | R | R (own) | `POS_EMPLOYEES_READ/WRITE` | | **stores** — `stores,terminals` | W | R | R | `POS_STORES_READ/WRITE`, `TERMINALS_*` | | **reporting** — `shift_reports,daily_sales_rollups` | R (portfolio) | R (store) | R (own shift) | `POS_REPORTS_READ` | | **themes** — `merchant_themes` (planned, BG-7) | W | R | R | — | | **audit** — `audit_log` | R (scoped) | R (scoped) | — | system-written | > Reserved roles: **customer** reaches only its own future-portal rows (`Role.user`); **guest** gets public reads only (`Role.guests`). Neither appears in the active matrix. ## Approval workflow [Section titled “Approval workflow”](#approval-workflow) Cashiers cannot self-approve exceptions. A cashier **requests** and a supervisor (or manager) **approves**: * Discount on a line or cart. * Refund of a line or cart. * Return of a line or cart. Each approval is an `approvals` row (linked from `discounts.approvalId`, [pos-domain-model.md](/business-rules/pos-domain-model/)) and is written to `audit_log`. Screens: M+1/M+2 (mobile), W13/W31/W36 (web). ## Impersonation & audit [Section titled “Impersonation & audit”](#impersonation--audit) * **Impersonation is post-MVP** — no platform role impersonates a user today (see [§ Platform roles](#platform-roles-labels)). This section records the design so that audit already accounts for it when it lands. * **Every** sensitive action — including future impersonated ones — writes to the `audit_log` collection (`action`, `entity`, `entityId`, `before`, `after`, `userId`; already implemented in `terminales-api/src/audit-log`). * Audit is the accountability backstop for impersonation: an impersonated write must record both the acting identity and the impersonated subject. ## White-labeling access [Section titled “White-labeling access”](#white-labeling-access) `merchant_themes` (white-label branding: colors, typography, `logoFileId`, dark mode — planned BG-7) is **manager/Merchant-editable**; supervisor and cashier are **read-only**. A platform **Operator** may assist with theme setup as part of basic merchant assistance. Runtime application is covered in `theming`; editor screen is W54. ## When to use each Appwrite auth feature [Section titled “When to use each Appwrite auth feature”](#when-to-use-each-appwrite-auth-feature) | Feature | Use it for | Don’t use it for | | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- | | **Account** | The logged-in user’s own session, profile, MFA, recovery — self-service flows. | Server-authority admin operations. | | **Users (server)** | Admin user CRUD, PIN custom-token minting, invites — anything needing the API key. | Routine reads/writes (those go client-direct under RLS). | | **Teams + Memberships** | The **store** tenant boundary; membership role = POS role; invites add a user to a store team. | Cross-merchant platform staff (use Labels). Project-scoped: does **not** cross Appwrite projects. | | **Labels** | Cross-merchant platform staff (`platformAdmin` / `platformOperator`) and the future `distributor` capability flag. | Per-store POS roles. Project-scoped: does **not** cross Appwrite projects. | | **Permissions** | Per-row document permissions (read/write) granting `Role.team/user/guests`. | Coarse, app-wide gates. | | **Roles (`Role.*` helpers)** | The expression language inside Permissions: `team`, `member`, `user`, `label`, `guests`. | — | | **Impersonation** | Post-MVP platform debugging — always audited (deferred; no role impersonates today). | Routine access; never silent. | | **Preferences** | Non-authorization user settings (locale, UI prefs, notification toggles). | **Roles or permissions** — prefs are not a security boundary. | | **Row-Level Security** (document permissions) | **Default** for all tenant data — the primary isolation mechanism, portable across every tenancy tier. | — | | **Table/Collection-Level Security** | Collections where every row shares identical access (global reference data, e.g. shared `tax_rates` templates). | Tenant-owned data — that needs row-level scoping. | > **Account / Users / Teams / Labels are all project-scoped.** A project-per-merchant split (tier T2 below) therefore fragments platform-staff identity and needs a control-plane project. ## Tenancy isolation tiers & portability [Section titled “Tenancy isolation tiers & portability”](#tenancy-isolation-tiers--portability) The role model must survive a later split into separate databases or projects (for DB growth or billing isolation) **without redesign**. [Appwrite’s multi-tenancy guidance](https://appwrite.io/docs/products/auth/multi-tenancy) prescribes the Teams + `Role.team(...)` model we use but is silent on single-vs-separate DB/project — that is our architectural call. | Tier | Shape | Isolation | Trade-off | | ---------------- | --------------------------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **T0** (current) | one project, one shared database, RLS by team | `Role.team(...)` document permissions | Cheapest; cross-merchant reporting trivial; platform Labels/impersonation reach everything. | | **T1** | one project, **database-per-merchant** | per-`databaseId` separation, same Teams/Labels | Size/migrate a heavy tenant easily; cross-merchant queries now span DBs. | | **T2** | **project-per-merchant** | full billing/quota/blast-radius isolation | Teams/Labels/Users/Accounts are project-scoped → platform identity, impersonation, cross-merchant admin **fragment**; needs a **control-plane project** + identity replication + a routing map. | **Portability rules to follow now (even at T0):** * **Never hard-code** a single `databaseId`/`projectId` in role or RLS logic. Resolve them per-merchant through a **`merchants` routing record** (`merchants.databaseId`, future `merchants.projectId`) so a tenant relocates by config, not code. * Keep collection / team / label **names stable** across tiers. * A tier migration is a **data move + re-grant of the same `Role.team`/`Role.label` templates** — not a model change. * **RLS is the one isolation primitive portable across all tiers.** ## Network / distributor plane (forward compatible) [Section titled “Network / distributor plane (forward compatible)”](#network--distributor-plane-forward-compatible) The system must be forward-compatible with a future MLM-style network where a user becomes a **distributor** that recruits others for benefits/commissions (rules TBD). This is the **third plane** — commercial, not authorization — and the current model accommodates it additively. * **Distributor capability** = a platform-grantable Label `distributor` (sits alongside the platform roles above). It gates *who may recruit*, not *who can read which store data*. * **The network is graph data**, not Teams or Labels (both are flat and cannot nest): a `sponsorId`/`uplineId` adjacency on the merchant/distributor record, plus a closure table or materialized path for N-level upline/downline rollups. New collection(s) TBD (`distributors` / `network_edges` / `network_closure`) — not built now. The Merchant-as-record decision is what makes this possible. * **Hard isolation rule:** downline visibility grants read on **commission/network-summary** collections scoped by graph position **only** — **never** `Role.team(downlineStore)` on operational store data. A distributor sees money/relationship aggregates, never their recruits’ sales rows. * **Enrollment** is a recruit-invite flow distinct from the store-team employee invite: it creates/links a **new merchant/distributor node under the inviter’s downline**, not a team membership. Sensitive → `audit_log`. * **Commissions** will be a future append-only ledger (reusing the [offline-and-idempotency.md](/business-rules/offline-and-idempotency/) ledger pattern); existing balance-movement/transfers are the money-movement precedent. Out of scope to define here. * **Tenancy interaction:** commission rollups need cross-merchant reads → keep network/commission data in a **shared control-plane database**, regardless of how store data is later partitioned. * **Open question:** eligibility — open to any merchant vs platform-granted vs sponsored-only. Recommendation: platform-grantable label + sponsorship-driven enrollment; defer the benefit/commission rules. ## Enforcement model [Section titled “Enforcement model”](#enforcement-model) * **RLS first** for all CRUD: Appwrite document permissions are the enforcement point. No `@RequireRole` on CRUD. * A lightweight **`assertStoreRole()`** runs only inside the few server endpoints that remain (PIN mint, invites, user/team admin, durable workflows). * **Single source of truth** for the role→permission mapping is the code matrix `src/auth/roles/pos-roles.ts` — **not yet created**. Today roles live in `EPosRoleType` and scopes in `auth.scopes.ts`; the explicit mapping between them is still to be written (the current BetterAuth `auth-access-control.ts` covers only system roles, not POS roles). * Server code resolves `{databaseId, projectId}` from the merchant routing record (see tiers above), **never** a constant. ## Open questions & cross-links [Section titled “Open questions & cross-links”](#open-questions--cross-links) * **BetterAuth → Appwrite gap:** the `pos-roles.ts` matrix file does not exist yet; this doc describes the target. * **T2 control-plane** design (identity replication / SSO across projects) is unspecified. * **Distributor eligibility & commission rules** are deferred (see network plane above). **Related:** [auth-and-multitenancy.md](/business-rules/auth-and-multitenancy/) · [pos-domain-model.md](/business-rules/pos-domain-model/) · [reporting.md](/business-rules/reporting/) · [offline-and-idempotency.md](/business-rules/offline-and-idempotency/) · `appwrite` · `architecture-decisions` · [../overview/glossary.md](/overview/glossary/) · `theming` · [Appwrite multi-tenancy](https://appwrite.io/docs/products/auth/multi-tenancy). # Runtime settings — the key catalogue > the authoritative catalogue of every runtime setting key — its scope, default, who may edit it, and whether it is a money rule. This is the domain page; the mechanism, resolution order and audit live in the runbook ../runbooks/runtime-settings.md > **Purpose:** the authoritative catalogue of every runtime setting key — its scope, default, who may edit it, and whether it is a money rule. This is the domain page; the mechanism, resolution order and audit live in the runbook `../runbooks/runtime-settings.md`. **Audience:** AI agents. **Status:** as of 2026-09-26 — catalogue re-diffed against the api registry on `origin/main` `171898a4` (PO 2026-09-26: the code is the decision); agreed split: PO 2026-09-19 — `../../backlog/ideation/multi-runtime-settings-flags-inventory.md`). ## How to read this page [Section titled “How to read this page”](#how-to-read-this-page) Every key is declared in the typed registry `terminales-api/src/core/runtime-settings/settings-registry.ts` (built by api #797 + its `RuntimeSettings` siblings). **This catalogue is generated from that file — regenerate it whenever a key is added, removed, or its shape changes.** The registry is the source of truth for the exact leaf-key spelling and validating schema; unknown keys are rejected everywhere. * **Scope** — `realm` = one value per realm; `realm + merchant` = a realm default that a per-merchant row may override (resolution: merchant override → realm value → code default). * **Default** — the code default, which **equals today’s deployed behaviour**, so an empty `runtime_settings` table changes nothing. * **editableBy** — `admin` (platform operator/admin screens) or `owner` (merchant owner settings; the owner write admits only `editableBy: 'owner'` keys, any other → 403). * **moneyRule** — `yes` if the value is a monetary amount stored and served in integer minor units (×10000). Rates/ratios are exempt even when they govern money. * **clientVisible** — `yes` if the resolved value may be handed to ordinary store staff (cashiers/supervisors) through the POS effective-settings read; the registry defaults to `no`. ## Catalogue [Section titled “Catalogue”](#catalogue) Diffed key-by-key against `terminales-api/src/core/runtime-settings/settings-registry.ts` on `origin/main` `171898a4` (2026-09-26; PO ruling: **the code is the decision** where this page and the registry disagreed). 27 keys. `clientVisible` = the resolved value may reach ordinary store staff through the POS effective-settings read (default `false`). ### Reconciliation [Section titled “Reconciliation”](#reconciliation) | Key | Scope | Default | editableBy | moneyRule | clientVisible | Notes | | ----------------------------------- | ----- | --------------------- | ---------- | --------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `reconciliation.allowManualLineFix` | realm | `false` | admin | no | no | Manual fix of an unreadable bank-statement line; consumer ships with the bank-statement import (sub-project 4). The seed key that proves the mechanism. | | `reconciliation.concentratorClabe` | realm | `''` (not configured) | admin | no | **yes** | The single house (concentrador) CLABE every merchant wires deposits to (#852, deposit model “Option A”); merchants are told apart by `merchants.depositReference` in the concepto. A non-empty value must be an 18-digit CLABE with a valid control digit, else the write is rejected. Client-visible so the deposit-instructions screen can show it. | ### POS behaviour (← migrated from `FEATURE_FLAG_POS_*` / refunds) [Section titled “POS behaviour (← migrated from FEATURE\_FLAG\_POS\_\* / refunds)”](#pos-behaviour--migrated-from-feature_flag_pos_--refunds) | Key | Scope | Default | editableBy | moneyRule | clientVisible | Notes | | --------------------------------- | ---------------- | --------------------------------- | ---------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `pos.shiftRequiredToSell` | realm + merchant | `true` | admin | no | yes | ← `FEATURE_FLAG_POS_STAFF_SHIFTS`. Admin-only in code since terminales-api #964 (R9 A2, 2026-09-26: a POS sale always requires an open shift; the owner cannot turn it off). Kept only because web + Android still read it; remove it (unconditional guard) under feature-flag minimalism. | | `pos.shiftTerminalStrict` | realm + merchant | `true` | **owner** | no | yes | ← `FEATURE_FLAG_POS_SHIFT_TERMINAL_STRICT`. ON → a sale on another terminal’s shift is a 400 `sale_shift_terminal_mismatch`; OFF → warn-only. | | `pos.drawerLockdown` | realm + merchant | `true` | **owner** | no | yes | ← `FEATURE_FLAG_POS_SHIFT_DRAWER_LOCKDOWN`. | | `pos.refundsBeyondSameDay` | realm + merchant | `true` | **owner** | no | yes | ← `FEATURE_FLAG_REFUNDS_BEYOND_SAME_DAY`. Also fixes the old plane mismatch (api ON, web profile omitted it). | | `pos.shiftCloseVarianceTolerance` | realm + merchant | `toMinor(50)` = `500000` ($50.00) | **owner** | **yes** | yes | Integer minor units (×10000). ← the hardcoded $50.00 in `shift-variance.ts`. A store-preference dial, so owner-editable. | | `onboarding.selfSignupOpen` | realm | `true` | admin | no | yes | ← `FEATURE_FLAG_MERCHANT_ONBOARDING`. A realm that only provisions merchants centrally turns it off. Realm-only (a signing-up merchant has no row to override). | ### VAS [Section titled “VAS”](#vas) | Key | Scope | Default | editableBy | moneyRule | clientVisible | Notes | | ----------------------------------- | ---------------- | ----------------------------- | ---------- | --------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `vas.billPayDebtLookup` | realm + merchant | `true` | **owner** | no | yes | ← `FEATURE_FLAG_VAS_ADEUDO_LOOKUP`. Gates `GET /vas/adeudo`; OFF → the legacy flag-off response, no upstream call. | | `vas.fraud.maxAmountPerTransaction` | realm + merchant | `50_000_000` ($5,000.00) | admin | **yes** | no | ← `VAS_FRAUD_*` (mirrors `DEFAULT_VAS_FRAUD_LIMITS`). `0` disables the dimension. | | `vas.fraud.windowMinutes` | realm + merchant | `60` | admin | no | no | Window for the store/cashier count + amount limits. | | `vas.fraud.maxCountPerStore` | realm + merchant | `100` | admin | no | no | `0` disables. | | `vas.fraud.maxAmountPerStore` | realm + merchant | `1_000_000_000` ($100,000.00) | admin | **yes** | no | `0` disables. | | `vas.fraud.maxCountPerCashier` | realm + merchant | `50` | admin | no | no | `0` disables. | | `vas.fraud.maxAmountPerCashier` | realm + merchant | `500_000_000` ($50,000.00) | admin | **yes** | no | `0` disables. | | `vas.vendorFloatLowThreshold` | realm | `0` (alert off) | admin | no | no | Low vendor-float alert threshold, in the **vendor’s native MXN float unit** (pesos as the provider reports it), not ledger minor units — hence not a money rule. The key **and any UI label must not name the vendor**. Realm-only. | ### Settlements [Section titled “Settlements”](#settlements) | Key | Scope | Default | editableBy | moneyRule | clientVisible | Notes | | --------------------------- | ---------------- | ----------------- | ---------- | --------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `settlement.commissionRate` | realm + merchant | `0.02` | admin | no | no | Fractional rate in `0–1` (0.02 = 2 %). ← `SETTLEMENT_COMMISSION_RATE`. **Not** a money rule in the registry: it is a ratio, not a minor-units amount (rates are exempt), even though it governs money. Not effective-dated: a report keeps the rate it was computed with; a change affects only the next computation. Negotiated per merchant. | | `settlement.source` | realm | `angelpay_import` | admin | no | no | Enum `angelpay_import` \| `computed_rollup` (#828): which pipeline is authoritative for card settlements. Registered **inert** — nothing reads it yet; the nightly rollup keeps running until the import can replace it. es-MX admin label «Fuente de liquidaciones». | ### Verification [Section titled “Verification”](#verification) | Key | Scope | Default | editableBy | moneyRule | clientVisible | Notes | | ---------------------------------- | -------------- | ----------- | ---------- | --------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `verification.amlEnabled` | realm | `false` | admin | no | no | ← `VERIFICATION_AML_ENABLED`. Whether the `aml` check counts toward the required set. | | `verification.currentTermsVersion` | realm | `'2026-08'` | admin | no | no | ← `CURRENT_TERMS_VERSION` (mirrors `DEFAULT_TERMS_VERSION`). Bumping it makes merchants re-accept (`terms → lapsed` on the next recompute). | | `verification.validityMonths` | realm | `24` | admin | no | no | Identity-mirror validity window, in calendar months. | | `verification.expiringWindowDays` | realm | `30` | admin | no | no | Days ahead the daily sweep flags an identity as expiring soon. | | `merchant.unverifiedCanOperate` | realm **only** | `false` | admin | no | no | The unverified-merchant policy switch. `false` = **policy B** (set up, 1 store / 1 terminal, no selling/shifts/cash movements/invites); `true` = policy A (operate from day one; money-out still gated). A merchant-scoped write is rejected (422) and ignored at read; an unreadable value fails closed to `false`. Clients read `GET /pos/merchants/:id/capabilities` instead. See [merchant-verification-policy.md](/business-rules/merchant-verification-policy/). | ### Module enablement (← per-merchant commercial package / staged onboarding) [Section titled “Module enablement (← per-merchant commercial package / staged onboarding)”](#module-enablement--per-merchant-commercial-package--staged-onboarding) | Key | Scope | Default | editableBy | moneyRule | clientVisible | Notes | | ---------------------- | ---------------- | ------- | ---------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `modules.wallet` | realm + merchant | `true` | admin | no | no | Wallet + deposits + transfers. Disabled ⇒ new ops refused `403 module_disabled` (`ModuleEnablementService`); history reads stay allowed; in-flight work is not cancelled. | | `modules.vas` | realm + merchant | `true` | admin | no | no | VAS sales. | | `modules.cardPayments` | realm + merchant | `true` | admin | no | no | Card tender / payment intents. | | `modules.settlements` | realm + merchant | `true` | admin | no | no | Liquidaciones. | ## Not in this catalogue [Section titled “Not in this catalogue”](#not-in-this-catalogue) * **Deploy-time env** (integration wiring, build/dev toggles, security constants) — see the runbook §“What stays deploy-time env”. These are not editable at runtime. * **Removed flags** — always-ON flags hard-wired ON (e.g. `MERCHANT_WALLET`, `SETTLEMENT_UNIFIED`, web `WRITE_GATEWAY`) and dead/zero-reader flags deleted outright. `FEATURE_FLAG_MERCHANT_WALLET_DEPOSITS` is the one survivor (switchable until money-in reconciliation ships) and is **not** a runtime setting. # Security baseline — OWASP Top 10 > the durable, always-current security baseline for the solution: OWASP Top-10 (2021) A01–A10 status per repo (terminales-api / terminales-web / terminales[mobile]), the accepted-risk register, and the pre-prod security gates. Every audit UPDATES this file — it is a living register, not a one-off report. It records status + owner + expiry; it does not re-argue decisions > **Purpose:** the durable, always-current security baseline for the solution: OWASP Top-10 (2021) A01–A10 status **per repo** (terminales-api / terminales-web / terminales\[mobile]), the accepted-risk register, and the pre-prod security gates. Every audit UPDATES this file — it is a **living register, not a one-off report**. It records status + owner + expiry; it does not re-argue decisions. **Status:** as of 2026-09-08. First living cut, produced by a fresh origin/main posture scan (below). It **supersedes** the point-in-time Wave-2 audit snapshot in `wave2-mvp-analysis` §3.1 for A01–A10 status — that snapshot (2026-07-06) predates the SecurityBaseline hardening wave and now reads as history. **Related:** `wave2-mvp-analysis` (§3.1 seed + §4.1 row 20 rationale + §3.4 M6 gate list) · `production-cutover` (the cutover-day gate table this register feeds) · [../progress/milestones.md](/progress/milestones/) (M6 closed 2026-07-24, M11 go-live blockers) · [./auth-and-multitenancy.md](/business-rules/auth-and-multitenancy/) · [./roles-permissions-and-labels.md](/business-rules/roles-permissions-and-labels/) · `AI_CONTEXT` ## Scope & how to read this [Section titled “Scope & how to read this”](#scope--how-to-read-this) This baseline covers the three code repos and the Appwrite backend. Fulfils terminales-api issue **#266** (“Multi DOCS SecurityBaseline OWASP Baseline Document”), whose deliverable belongs here in `terminales-solution/docs/` rather than the api repo. The nine sibling **SecurityBaseline** tickets own the actual remediation code; this file is the registry they hang their status, residuals, and gates on. Statuses are **covered** / **partial** / **missing** / **n/a**, judged against the *current merged code*, not intended fixes. A `partial`/`missing` cell names the ticket that owns closing it. The **accepted-risk register** below is the part the Product Owner (Alfredo) triages — every row is a **candidate** with `Owner: TODO(human)` and a suggested expiry, waiting for him to assign an owner, set an expiry, and confirm the accept-vs-fix recommendation. This is a security document: no control is claimed present unless it was seen in the code, and anything that a scan cannot fully verify is marked **“unverified — needs review”** rather than asserted. **Scan basis (origin/main, 2026-09-08):** terminales-api @ `e5aa9ef` · terminales-web @ `ac048de4` · terminales (mobile) @ `09160e07`. Working trees were on feature branches and were deliberately NOT used. `pnpm audit` figures ran against the working-tree dependency sets and are approximate (transitive advisories move daily). ## A01–A10 status [Section titled “A01–A10 status”](#a01a10-status) | # | terminales-api | terminales-web | terminales (mobile) | Fix owner (ticket) | | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- | | **A01 Broken Access Control** | **covered** — `AppwriteAuthGuard` re-validates identity server-side every request (`src/auth/appwrite-auth.guard.ts`), `AppwriteScopeGuard` + `@RequireScopes` enforce store-team role scopes with 403 (`src/auth/appwrite-scope.guard.ts`, #157); `PlatformAdminGuard`/`assertPlatformAdmin` gate the admin-override endpoint (`src/verification/http/admin/…-admin.controller.ts`, #674); default-**deny** capability gate (`merchant-capabilities.service.ts`, #630); `cache.controller.ts` now `@UseGuards(AppwriteScopeGuard)`. Caveat: authn is opt-in per controller (no global `APP_GUARD` — the throttler is the only `APP_GUARD`), mitigated by CI guard/scope-coverage sweeps that fail the build on an unguarded route (`src/auth/guard-coverage.spec.ts`, #443). | **partial** — edge gate `src/proxy.ts` (Next 16 renamed `middleware.ts`→`proxy.ts`) is a cookie-**presence** check by design; real validation is server-side `getServerSession()` (`src/core/auth/serverSession.ts`, `account.get()`) with real consumers `requireServerSession`/`requireAdminServerSession`/`requirePlatformAdminServerSession`/`requireWalletOwnerSession` (#175). The old unscoped Voids `limit:1000` cross-store leak is **fixed** — server-scoped by `storeId`+open-shift `since`, query disabled until `storeId` known (`terminal/voids/VoidsView.tsx`, #247). Residual: `appwrite.json` client-side `create("users")` perms (see A08). | **partial (by design)** — client-side defence-in-depth only; server RLS is the real boundary. Tenant scoping `CatalogDao` `WHERE tenantId=:tenantId` + `SessionTenantProvider` (returns the real persisted tenant in prod, degrades to empty cache — never another tenant’s rows); `DefaultUploadAuthorizer.canUpload()` **fails closed** (manager-role only), documented as UX gate (#100). | web #608 (scoping-contract gaps), api #428 (cross-store authz sweep) — residual polish only | | **A02 Cryptographic Failures** | **covered** — AES-256-GCM for stored PSP/connection secrets (`psp-credentials/psp-credentials.crypto.ts`, `platform-connections/…crypto.ts`; random IV + auth tag; keys `PSP_CREDENTIALS_ENCRYPTION_KEY`/`PLATFORM_CONNECTIONS_ENCRYPTION_KEY` from env, presence-checked at boot, never logged); PIN hashing scrypt + `timingSafeEqual` with a decoy-hash timing equalizer for the empty-find path (`auth/pin-login/pin-login.service.ts`). | **covered** — session cookie `httpOnly`+`sameSite=lax`+ **transport-aware** `Secure` (`core/auth/sessionCookie.ts` `buildSessionCookieOptions`/`isSecureRequest`, honors `x-forwarded-proto`, PR #804); `APPWRITE_API_KEY` behind `import 'server-only'` (`core/appwrite/appwriteServer.ts`); JWT/session secret only in the httpOnly cookie, never in the client bundle. | **covered** (with one accepted gap) — `EncryptedSharedPreferences` AES256-GCM master key (`auth/EncryptedSessionStorage.kt`), JWT `@Volatile` memory-only (`auth/SessionManager.kt`), PIN never stored; `allowBackup="false"` + backup/data-extraction rules exclude the DB, session prefs, datastore. **Room DB is unencrypted (no SQLCipher)** and there is **no TLS cert pinning** — see register. | mobile #198 (Room at-rest encryption evaluation) for the residual | | **A03 Injection** | **covered** — global `ValidationPipe` `{whitelist, forbidNonWhitelisted, transform}` (`src/main.ts`); Appwrite `Query.*` builders throughout; Postgres remainder uses `$1/$2` bind params (`reports/services/report.service.ts`); raw SQL only in migration DDL. | **covered** — zero `dangerouslySetInnerHTML`/`eval`/`new Function` in `src`; React escaping intact; typed SDK/OpenAPI queries. | **covered** — all Room `@Query` parameterized (`:tenantId`/`:id`/`:clientId`), typed SDK ops, kotlinx.serialization JSON. | — (zero unmitigated gaps; keep the pattern — enforced by this baseline) | | **A04 Insecure Design** | **covered** — money-path races handled by DBOS durable workflows keyed on `clientId` as the dedupe id: `pos/sales/finalize-workflow.ts`, `settlement-workflow.service.ts`, `card-charge-workflow.ts`, `vas-fulfillment-workflow.service.ts`; `assertClientId` + `(storeTeamId, clientId)` unique index (`payment-intents/angelpay-card.adapter.ts`). The July TOCTOU/stock-decrement races are the ones these workflows close. | **covered** — POS `finalizeOrder` carries a `clientId` UUID idempotency key (`terminal/_cart/finalizeOrder.ts`); coarse errors, PIN never logged. | **partial** — idempotent write path (`clientId` = documentId, 409 swallowed as no-op; `RealtimePlusPoll` de-dups). Gap: PIN lockout is **server-side only** (client renders the server’s 429/423 blocked state; no local attempt counter/backstop). | register (mobile client-side PIN backstop — low) | | **A05 Security Misconfiguration** | **covered** (one pre-go-live flag to close) — helmet + fail-closed **allowlist** CORS + `SWAGGER_ENABLED`-gated docs (`bootstrap/http-hardening.ts`, #183); TypeORM `synchronize` OFF in prod/stage + `migrationsRun:false` (`database/database.options.ts`). Residual: `AUTO_DDL_ENVS={'development'}` leaves auto-DDL ON in dev with an explicit “REMOVE before go-live” code comment. `FORCE_HTTPS` — **unverified** in-app (TLS terminates at the shared traefik per infra). | **covered** — central `core/security/securityHeaders.ts` wired at `next.config.ts` for every route: enforcing CSP (`default-src 'self'`; `object-src 'none'`; `base-uri 'self'`; `form-action 'self'`; **`frame-ancestors 'none'`**; `upgrade-insecure-requests`), HSTS `max-age=63072000; includeSubDomains`, `X-Frame-Options: DENY`, `X-Content-Type-Options`, `Referrer-Policy`, `Permissions-Policy` (#176). Safari-compat PR #804 omits `upgrade-insecure-requests`+HSTS **in dev only**. CSP uses `'unsafe-inline'` (MUI/Emotion) — accepted-risk, see register; not nonce-based, not report-only. | **covered** — the July “`HttpLoggingInterceptor` at `Level.BODY` unconditionally” finding is **fixed**: `Level.BODY` only when `BuildConfig.DEBUG`, else `NONE`, test-guarded (`core/graphql/ApolloGraphQLClient.kt`, #195); release `isMinifyEnabled=true`+`isShrinkResources=true`; `targetSdk=36`. No `network_security_config.xml` — **unverified**, relies on API-28+ cleartext-off default (register). | web #176 (nonce-CSP follow-up), api #183 (dev auto-DDL removal) — residuals in register | | **A06 Vulnerable & Outdated Components** | **partial** — `pnpm audit --prod` (approx): 7 transitive vulns (4 high `brace-expansion` DoS, moderate `qs`, `uuid`) — no direct-dep criticals; `better-auth` confirmed removed. Dependabot on (`.github/dependabot.yml`); **no CodeQL**; gitleaks on PR+push. | **partial** — `pnpm audit --prod` (approx): 4 vulns (2 moderate/2 low) all `dompurify` via `@monaco-editor/react` (admin editor); no direct-dep vulns. Dependabot on; **no renovate, no CodeQL**; gitleaks present but **non-blocking** (`continue-on-error`, main-only). | **covered** — `renovate.json` (weekly, security-labeled, patch/digest automerge) + gitleaks CLI; vendored blobs (angelpaySDK `.aar`, Vanstone JARs) **checksummed** by `config/vendored-binaries.sha256` + `verifyVendoredBinaries` Gradle task, CI-enforced (#154, #147). | web #174 (dep-scanning rollout), api #249 (dependabot rollout — landed), mobile #154 (done) | | **A07 Identification & Authentication** | **covered** — identity re-validated server-side every request; global per-IP throttler **Redis-backed** (`rate-limit/rate-limit.module.ts`, `RedisThrottlerStorage`, #182); PIN-login failure limiter + lockout Redis-backed in prod (`auth/pin-login/pin-attempt-store.ts`: 5 fails/60s window, lockout after 3 windows for 15 min). | **covered** — `POST /api/auth/session` calls `checkSessionRateLimit` before any Appwrite call (10/15min, IP+`sha256(email)` keyed, oracle-free, 429+`Retry-After`; #177). Caveat: default store is an in-memory `Map` (per-serverless-instance) — Redis store is a follow-up behind the same interface (register). | **covered** — 15-min JWT TTL + 60s early-refresh, mint serialized under a mutex, retry-once-on-401 (`auth/SessionManager.kt`, `JwtAuthInterceptor.kt`); magic-link/recovery now on **https App Links** (`autoVerify=true`, `assetlinks.json` committed) with `terminales://` kept only as fallback (#263/#187). | web #177 (Redis-backed limiter); register (assetlinks must be hosted at prod domain) | | **A08 Software & Data Integrity** | **covered** (typecheck gap) — `test-gate.yml` runs `pnpm test`+`test:e2e` on every PR+push (frozen lockfile, #175); schema-as-code validated (`config-validate.yml`, #539) + migrations-only DDL in prod; `clientId` idempotency required. Minor: no standalone typecheck step (`nest build` not run in the gate). | **partial** — `finalizeOrder` `clientId` idempotency + CI quality/type/e2e gates. **Regression:** `appwrite.json` declares client-side `create("users")` perms on some tables (the known terminals/customers client-create issue) — verify per-table intent, treat as a prod blocker. | **covered** — `verifyAppwriteModels` drift gate is a required CI check; release signing built only from CI secrets (`signingConfig=null` otherwise, no committed keystore); vendored-binary checksum gate (A06). | web #608 + terminals/customers client-create regression (prod blocker); api typecheck step (register — low) | | **A09 Security Logging & Monitoring** | **covered** — pino + OTel (`instrumentation.ts`, `observability/otel/*`) + Prometheus/Grafana; append-only `audit_log` with exhaustiveness-tested sensitive-op coverage (`pos/audit/*`, `SENSITIVE_OPS_INVENTORY.md`, #184/#103); impersonation audited; secret redaction test-proven. | **covered (runtime-gated)** — `@sentry/nextjs` genuinely installed + configured (client/server/edge configs, `withSentryConfig`); DSN-gated + flag `observability_sentry_web`; structured `web_session_*` logs. The **transport sink is a pending PO decision** (§4.6) — Sentry is wired, the org-wide transport is not chosen. | **covered (runtime-gated)** — Sentry a real dep, gated by a non-blank `SENTRY_DSN` (blank on CI/fresh checkout, #195); over-log risk closed by the A05 DEBUG gate; upload authz logs roles only, never userId/tenantId. | Telemetry-transport gate (§4.6 / ADR-0003) — see gates + register | | **A10 SSRF** | **covered** — all outbound URLs are fixed-config (env/secret-file), not request-supplied: Mantarys `MANTARYS_WS_URL`, SMS-OTP endpoint, Telegram, Loki. Minor: Mantarys `endpointOverride` (#484) can come from a platform-connection row set by a platform admin (audited, not user-supplied) — a config-trust boundary, in register. | **n/a** — no server route does user-URL-driven `fetch`; server fetches hit fixed Appwrite/API origins. | **n/a** — endpoints are build-time `BuildConfig` constants, never user-controlled. | register (api Mantarys config trust boundary — low) | **Categories with zero unmitigated gaps across all three repos:** A03 (Injection). All others carry at least one residual tracked in the register or gates below. ## Accepted-risk register [Section titled “Accepted-risk register”](#accepted-risk-register) Every row is a **CANDIDATE** for the PO (Alfredo) to triage: assign a real Owner, set/confirm the Expiry, and confirm the accept-vs-fix call. `Owner: TODO(human)` and expiries are suggestions, not decisions. Ordered by severity. | Risk | Repo | Severity | Owner | Expiry / re-evaluate | Recommendation | Removal path / notes | | ---------------------------------------------------------------------------------------------------------------------------------- | ------ | ---------- | ----------- | ----------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `appwrite.json` grants client-side `create("users")`/`read("users")` on some tables (terminals/customers client-create regression) | web | **High** | TODO(human) | **before first prod deploy** | **fix-before-prod** | Re-scope to server-authored writes; verify each table’s `$permissions` + `rowSecurity` against the canonical api schema. Owner ticket: web #608 (scoping contracts) + the standing regression. **Confirm intent per table before go-live.** | | Web `/api/auth/session` rate-limiter is an in-memory `Map` (per-serverless-instance on Vercel) — no shared store | web | Medium | TODO(human) | before real traffic / ≥2 instances | **fix-before-prod** | Swap in the Redis-backed store behind the existing `sessionRateLimit` interface (web #177 follow-up). Same class of gap as the api limiter, which is already Redis-backed (#182). | | Web CSP uses `'unsafe-inline'` for `script-src` and `style-src` (MUI/Emotion runtime styles) | web | Medium | TODO(human) | before public launch (nonce follow-up) | **accept (MVP)** | Removal = nonce-based CSP + CSP reporting; documented in `securityHeaders.ts`. Enforcing CSP with `frame-ancestors 'none'` already ships (#176). | | Mobile: no TLS certificate pinning (`CertificatePinner`) for a payments client | mobile | Medium | TODO(human) | before prod (evaluate) | **fix-before-prod (evaluate)** | **Unverified whether intentional — needs review.** Evaluate pinning the Appwrite + Nest origins; weigh against cert-rotation ops. New finding (not in the July audit). | | No CodeQL / SAST workflow in any of the three repos (dependency scanning exists, static analysis does not) | multi | Medium | TODO(human) | before public launch | **accept (MVP) → fix post-launch** | Add CodeQL (or equivalent) to CI. Dependency advisories are covered (dependabot/renovate); source SAST is the gap. | | API auth is opt-in per controller (no global `APP_GUARD`); a guard-less controller is a build failure, not a runtime deny | api | Medium | TODO(human) | ongoing (keep CI sweep green) | **accept** | Mitigated by reflection-based `guard-coverage.spec.ts`/`scope-coverage.spec.ts` (#443) that fail CI on any unguarded/unscoped route. Re-evaluate if a global guard becomes cheap. | | Room DB unencrypted (no SQLCipher) on the mobile device | mobile | Low–Medium | TODO(human) | revisit before any PII/PAN lands in cache | **accept** | Mitigated: `allowBackup=false` + backup/data-extraction excludes; cache holds catalog + `sync_queue` only. Owner: mobile #198. Flip to fix the moment sensitive data is cached. | | API `AUTO_DDL_ENVS` includes `development` — TypeORM auto-DDL ON in dev | api | Low | TODO(human) | before go-live | **fix-before-prod** | Code comment already says “REMOVE before go-live.” Prod/stage are OFF; this is a dev-only cleanup so prod cannot accidentally inherit it. Owner: api #183 residual. | | API `pnpm audit`: 7 transitive advisories (4 high `brace-expansion` DoS, `qs`, `uuid`) | api | Low–Medium | TODO(human) | next dependabot cycle | **accept** | All transitive, no direct-dep criticals; dependabot will bump. Re-check at each audit. Owner: api #249/dependabot. | | Web `dompurify` transitive vulns (2 moderate/2 low) via `@monaco-editor/react` | web | Low | TODO(human) | next dep-bump cycle | **accept** | Admin-only Monaco editor; patched in `dompurify ≥3.4.12`, needs a monaco bump. Owner: web #174/dependabot. | | Web gitleaks is non-blocking (`continue-on-error`, push-to-main only, no PR trigger) | web | Low | TODO(human) | before prod | **fix-before-prod (cheap)** | Make the scan blocking and add the PR trigger (api runs it on PR+push). Owner: web #174. | | API `test-gate` has no explicit typecheck step (`nest build` not run in the gate) | api | Low | TODO(human) | before go-live | **fix-before-prod (cheap)** | Add a `tsc --noEmit`/`nest build` step so a type error fails CI, not just tests. | | Mobile PIN throttle is server-only (no client-side attempt backstop) | mobile | Low | TODO(human) | post-MVP | **accept** | Client renders the server’s 429/423 lockout; server is the authority. A local counter is defence-in-depth only. | | Mobile: no explicit `network_security_config.xml` | mobile | Low | TODO(human) | before prod (add explicit config) | **accept** | `targetSdk=36` disables cleartext by default; an explicit config is hardening, not a fix. | | Mobile App Links `assetlinks.json` must be hosted at the production domain (custom-scheme `terminales://` remains as fallback) | mobile | Low | TODO(human) | before go-live | **accept** | App Links landed (#263/#187) with `assetlinks.json` committed; the go-live task is hosting it at the prod domain and confirming `autoVerify`. | | API Mantarys `endpointOverride` (#484) — outbound URL can come from a platform-connection row set by a platform admin | api | Low | TODO(human) | document + restrict to `platform_admin` | **accept** | Admin-configured + audited, not request-supplied → not classic user-SSRF, but a config-trust boundary worth stating. | | Web session cookie 30-day `maxAge` fallback (carried from the Wave-2 A02 caveat) | web | Low | TODO(human) | verify current value | **accept** | **Unverified against current code — needs review.** Cookie is now transport-aware `Secure`+`httpOnly`+`lax` (#804); confirm the effective `maxAge` matches policy. | ### Recently mitigated — dropped off the register since the Wave-2 audit (2026-07-06) [Section titled “Recently mitigated — dropped off the register since the Wave-2 audit (2026-07-06)”](#recently-mitigated--dropped-off-the-register-since-the-wave-2-audit-2026-07-06) Recorded so the PO can see what closed (each was a July `partial`/`missing`/accepted-risk, now covered on origin/main): API `verifyPin` timing oracle → decoy-hash equalizer (was api todo #90); API helmet/CORS-allowlist/Swagger-gate/`synchronize:false` (#183); API Redis rate-limit + PIN lockout (#182); mobile BODY-level HTTP logging → DEBUG-gated (#195); mobile vendored-AAR checksums → `verifyVendoredBinaries` + `vendored-binaries.sha256` (#154/#147); mobile magic-link deep links → https App Links + committed `assetlinks.json` (#263/#187); web security headers/CSP/HSTS shipped (#176, Safari-compat #804); web Voids cross-store `limit:1000` leak → server-scoped (#247); web SSR session-guard consumers wired (`requireServerSession` et al., #175). ## Pre-prod security gates [Section titled “Pre-prod security gates”](#pre-prod-security-gates) The checks that must pass before go-live. These feed the cutover-day gate table in `production-cutover` — that runbook is the execution sequencer; this list is the security subset with current state. `[ ]` = open, `[~]` = substantially met with a residual, `[x]` = met. * [ ] **Telemetry transport chosen and wired (web + mobile)** — **pending PO decision (§4.6).** Sentry SDK is genuinely installed and runtime-gated on **both** clients (web `@sentry/nextjs`, mobile `sentry-android`), but the org-wide transport/sink is not chosen — the structured logs land in no-op sinks today. ADR-0003 (Sentry frontend + OTel/LGTM backend) may already close this; the PO must confirm it formally closes the §4.6 row (cutover runbook gate 13 `TODO(human)`). **Recorded as an open gate, never as decided.** * \[\~] **Dependency scanning live in all three repos** — api: dependabot ✅ (#249); mobile: renovate + gitleaks + vendored-AAR checksums ✅ (#154/#147); web: dependabot ✅ but **no renovate, no CodeQL, gitleaks non-blocking** (web #174). Residual: no CodeQL/SAST in any repo (register). * \[\~] **GitHub environment protection provisioned** — prod `appwrite push` gated by a required (human) reviewer. Cutover-runbook gate 1 records this as **CLOSED via #163** (verify: `gh api repos/inspiraCode/terminales-api/environments/production --jq '.protection_rules'` → non-empty). This supersedes the Wave-2 §3.1 reference to api #97; **verify, don’t redo.** * [x] **Vendored-binary checksum policy (mobile AARs)** — met: `config/vendored-binaries.sha256` + the `verifyVendoredBinaries` Gradle task, CI-enforced (#154/#147). * [ ] **Web `appwrite.json` client-create regression closed** — the `create("users")` client-side permissions (A08) must be re-scoped and verified per-table before first prod deploy (web #608). Prod blocker. * [ ] **Web session rate-limiter moved to a shared store** — before real traffic / ≥2 serverless instances (web #177). * [ ] **API dev auto-DDL flag removed** — drop `development` from `AUTO_DDL_ENVS` before go-live (api #183 residual). * [ ] **Feature-flag production ramp + kill-switch proven** — Multi #264 (open, human-reserved); cutover-runbook gate 5. Not strictly a code control, but the go-live security-relevant switch. * [ ] **Cutover runbook executed** — the full ordered sequence in `production-cutover` (final backup, schema reconcile, decommission verify, deploy, flag ramp, post-cutover smoke) plus its two pending §4.6 PO decisions (telemetry transport, deploy-pipeline shape). ## Maintenance contract [Section titled “Maintenance contract”](#maintenance-contract) This file is **living**. Update it — and refresh the `Status:` date in the header — on any of: * **A new security audit / posture scan** (per wave, or ad-hoc): re-run the per-repo scan against origin/main, update the A01–A10 cells and the scan-basis SHAs, and move anything newly fixed into “Recently mitigated.” Do not create a separate one-off report — this file *is* the report. * **A wave closes**: reconcile every `partial`/`missing` cell against what merged; flip cells and retire register rows whose fix landed. * **An accepted-risk expiry passes**: the row re-opens as a decision — the PO either extends the expiry or the risk becomes fix-before-prod. * **A gate changes state**: update the pre-prod gates checklist and cross-check the cutover runbook’s gate table. Keep the house style: long-line prose, no markdownlint reflow (per the repo docs convention). Cite real file paths only; mark anything a scan cannot confirm as “unverified — needs review” rather than asserting a control exists. ## Sources [Section titled “Sources”](#sources) * Posture scans of origin/main (2026-09-08): terminales-api `e5aa9ef`, terminales-web `ac048de4`, terminales `09160e07`. * `wave2-mvp-analysis` §3.1 (the seed A01–A10 table + living-baseline proposal), §4.1 row 20 (this doc’s rationale), §3.4 (M6 gate list). * `production-cutover` — the cutover-day preconditions gate table (gates 1, 5, 13, 14 map to the gates above). * [../progress/milestones.md](/progress/milestones/) — M6 closed 2026-07-24; M11 go-live blockers include this doc (#266). * SecurityBaseline sibling tickets: api #182 (Redis rate-limit + lockout), #183 (runtime misconfig hardening), #184 (sensitive-ops membership validation), #157 (store role-scope authz), #428 (cross-store authz sweep), #249 (dependency scanning), #181 (critical dependency bumps), #443 (guard-coverage sweep), #630 (default-deny), #674 (admin-override); web #176 (security headers/CSP), #177 (session-route rate limit), #174 (dependency scanning), #175 (enforce server-session validation), #247 (admin data scoping), #608 (scoping-contract gaps); mobile #155 (release logging/build hardening), #154 (dependency scanning + AAR checksums), #198 (Room at-rest encryption evaluation), #195 (Sentry frontend wiring / logging level). * [../decisions/README.md](/decisions/readme/) → [../adr/0003-observability-telemetry-transport.md](/adr/0003-observability-telemetry-transport/) (telemetry transport — the §4.6 open gate). # Value-Added Services — TAE, Pines, Services, Chips > the recommended terminales domain model for the four value-added-services (VAS) verticals > **Purpose:** the recommended terminales domain model for the four value-added-services (VAS) verticals migrating from the legacy stack — what entities/fields/enums to add so the Appwrite collections capture the right data the first time. This is a **plan-phase recommendation**; backlog tickets are created separately. **Status:** as of 2026-07-30 — **the model below is built.** The Appwrite tables, the DBOS fulfillment workflow, the wallet debit/compensation, PIN custody, the adeudo endpoint and the VAS screens on web and mobile all shipped during sprints 33–34. The one layer still missing is the **real aggregator transport**. (Connectivity phasing + wallet rule amended per [PDR-0003](/pdr/0003-vas-connectivity-phasing-and-wallet-visibility/); original model 2026-06-24. 2026-09-26: a VAS sale records a CASH tender on the shift, requires an open shift and is not voidable — § VAS and the shift drawer. Same day, second wave: a platform-funded sale debits only the connection and is crash-safe, a delivered sale counts in the daily rollup, and a failed/reversed sale’s order ends `CANCELLED` — § VAS fulfillment funding & sale outcome.) > > **The aggregator is Mantarys.** What this document calls “the VB webservice” in the abstract has a name and a contract: see `manual-integracion-mantarys` (WSDL, five methods, the 32-entry response-code table, a 13-case certification matrix) and the sequenced implementation plan. Until that transport lands, `VbAggregatorAdapter` is a deliberate **fail-closed 503 stub** — so a VAS sale does not silently half-succeed, and the VAS live E2E specs are **gated, not failing**. > > **Money-safety rule (PO, 2026-07-26):** Mantarys `Confirmation` code **`17` is TERMINAL → FAILED** — reverse the wallet debit, never poll. This is deliberately fail-safe and **overrides** the legacy documentation’s softer reading of that code. > > **Companions:** the aggregator contract in `mantarys` · legacy contracts in `apitae-webservice` · POS model in [./pos-domain-model.md](/business-rules/pos-domain-model/) · reporting in [./reporting.md](/business-rules/reporting/) · per-story status in [../product/user-stories.md](/product/user-stories/) (§ VAS) and [../product/mobile-user-stories.md](/product/mobile-user-stories/). ## Scope & decisions [Section titled “Scope & decisions”](#scope--decisions) The four verticals (mobile **airtime / TAE**, **gift-card PINs**, **service/bill payment**, **SIM chips**) were previously parked as “legacy TypeORM, decision deferred”. This doc records the decision to bring them into the Appwrite POS model: * **Unified POS flow.** TAE/Pines/Services sell through the **same** `cart → sales_orders → transactions` pipeline as physical goods — one POS, one shift/Z-report, one tender/cash model, one reporting model — distinguished by a `kind` discriminator, with a fulfillment collection for the external call. * **Chips = physical serialized inventory.** SIM chips are real stocked goods, modeled with the existing `products`/`variants` + `stock_levels`/`stock_movements`, plus serialized units (ICCID/IMEI/NIP/MSISDN). No external webservice call to sell a chip. * **Provider catalog = local Appwrite mirror, synced** from the Mantarys `pos_prices_products` endpoint. ⚠ **Open question:** under Phase 1 there is **one shared webService user**, so that endpoint returns one flat catalog with no tenant dimension — while `providers` / `provider_products` rows are `storeTeamId`-scoped and the adeudo guard treats that scope as a **security boundary**. Where the sync job writes is a **PO decision** (fan-out per store team vs. a shared sentinel); engineering recommends fan-out. See `open-questions-and-todos`. * **The aggregator (Mantarys) stays the upstream money-movement source of truth** for TAE/Pines/Services; terminales orchestrates via a **NestJS aggregator adapter + DBOS** durable workflow (debit wallet → write pending → call provider → reconcile or reverse). terminales does not call apitae-sales. ## Per-feature concepts [Section titled “Per-feature concepts”](#per-feature-concepts) | Vertical | `kind` | Recipient ref | Amount | Fulfillment | Notes | | ------------ | ----------------------- | --------------------------------- | ------------------------------------- | ----------------------------------------------------- | ----------------------------------------- | | **TAE** | `TAE_AIRTIME` | phone (per-carrier validation) | fixed denomination | VB `Request_Transaction` | 24h same-number dedup | | **Pines** | `GIFT_PIN` | email/phone (delivery) | fixed denomination/plan (title, days) | VB call → **PIN code** custody + delivery + resend | code is sensitive | | **Services** | `SERVICE_PAYMENT` | account/reference (often barcode) | looked-up (adeudo) or entered | VB call; pre-check via `check_service_pending_amount` | CFE may carry a `CUR`; CFE commission cap | | **Chips** | `PHYSICAL` (serialized) | — | item price | none (local inventory) | bulk Excel import; activation lifecycle | ## Recommended data model [Section titled “Recommended data model”](#recommended-data-model) ### Catalog — extend `products`, add provider catalog [Section titled “Catalog — extend products, add provider catalog”](#catalog--extend-products-add-provider-catalog) **`products` additions:** * `kind` enum `[PHYSICAL, TAE_AIRTIME, GIFT_PIN, SERVICE_PAYMENT]` * `providerId?` (→ `providers`) * `isVirtual` (true for TAE/PIN/SERVICE → **skip stock decrement**) * `amountMode` enum `[FIXED, OPEN, LOOKUP]` (LOOKUP ⇒ adeudo before sale) * `referenceType` enum `[PHONE, ACCOUNT, EMAIL, NONE]` * `validationPattern?` (per-provider reference/phone format) * `isSerialized` (true for chips) **New `providers`** — local mirror, synced from `pos_prices_products`: `kind` `[TAE_CARRIER, BILL_PROVIDER, PIN_BRAND]`, `name`, `externalProviderCode`, `logoFileId`, `checkAmount` (adeudo support), `validationRules` (JSON), `commissionRule` (JSON: `FIXED|PERCENT` + value + caps, e.g. CFE ≤ 5%), `isActive`, `syncedAt`, + base. **New `provider_products`** — denomination/price mirror of `pos_prices_products`: `providerId`, `externalSku`, `title`, `amount`, `cost`, `message`, `days?`, `productType`, `isActive`, `syncedAt`, + base. ### Sales — extend existing collections, add fulfillment + custody [Section titled “Sales — extend existing collections, add fulfillment + custody”](#sales--extend-existing-collections-add-fulfillment--custody) **`line_items` additions** (VAS lines; `variantId` becomes optional): `kind`, `providerId?`, `providerProductId?`, `recipientRef?` (phone/account/email), `denomination?`, `serviceReference?`. Physical fields (`unitPrice`, `unitCost`, tax/discount, `qty`) unchanged. **`transactions` additions:** * extend `type` with `REFUND` / `REVERSAL` (keep `SALE`/`VOID`) * extend `status` with `PENDING_EXTERNAL` * add `externalProviderId?`, `externalFolio?` (VB `Folio`), `externalStatusCode?` (VB `Confirmation`) * reuses already-planned reporting fields (`commission`, `netAmount`, `provider`). **New `service_fulfillments`** — the external-call record / reconciliation, written by the DBOS workflow: `salesOrderId`, `lineItemId`, `transactionId`, `providerId`, `kind`, `recipientRef`, `requestPayload` (JSON), `responsePayload` (JSON), `externalFolio`, `confirmationCode`, `status` `[PENDING, DELIVERED, FAILED, REVERSED]`, `folioPos` (idempotency = `${salesOrderId}${seq}`), `retryCount`, `polledAt?`, `completedAt?`, `reversedAt?`, + base. Store-team scoped; append-mostly. This is the terminales replacement for the legacy Mongo `Transactions` audit. **New `pin_vouchers`** — PIN custody & delivery: `salesOrderId`, `fulfillmentId`, `providerId`, `brand`, `denomination`, `pinCode` (**sensitive — masked in logs/audit, access-controlled, show-once + controlled reprint**), `serial?`, `expiresAt?`, `deliveryMethod` `[ON_SCREEN, EMAIL, SMS]`, `deliveryStatus`, `recipient`, `redeemedAt?`, + base. ### Chips — physical serialized inventory [Section titled “Chips — physical serialized inventory”](#chips--physical-serialized-inventory) Reuse `products` (`kind=PHYSICAL`, `isSerialized=true`) + `stock_levels` / `stock_movements`. **New `serialized_units`** (generalized; chips are the first consumer): `productId`/`variantId`, `storeId`, `storeTeamId`, `merchantId`, `msisdn`, `carrier`, `nip`, `iccidImei`, `status` `[IN_STOCK, SOLD, ACTIVATED, VOID]`, `registrationDate`, `applicationDate?`, `soldOrderId?`, + base. * **Unique indexes:** `(storeTeamId, iccidImei)`, `(storeTeamId, nip)`. * **Bulk Excel import** (legacy columns `TELEFONO / MONTO / COMPAÑIA / NIP / IMEI`), with per-row validation. * Selling a chip links a unit to the order, sets `status=SOLD` + `soldOrderId`, and writes a stock movement; activation flips `status=ACTIVATED` + `applicationDate`. ## Orchestration (NestJS + DBOS) [Section titled “Orchestration (NestJS + DBOS)”](#orchestration-nestjs--dbos) * **Aggregator adapter** parallel to the PSP adapter (`board/backlog/api-appwrite-payments-psp-adapter.md`), wrapping the VB methods (`Request_Transaction`, `check_transaction`, `check_service_pending_amount`, `pin_resend`, `Check_Balance`). See `apitae-webservice`. * **Durable sale workflow:** finalize order → `Request_Transaction` (idempotent on `folioPos`) → on `24`/`17` durably **poll** `check_transaction` → write `service_fulfillments` (+ `pin_vouchers` for PIN) → set `transactions.status` (`COMPLETED`/`FAILED`). Replaces the legacy cron leader-lock + in-process poll loop. * **Reversal workflow** for failures/timeouts. * **Adeudo endpoint:** NestJS read calling `check_service_pending_amount` when `amountMode=LOOKUP` (cached, not persisted). * **Provider-catalog sync job:** periodically refresh `providers` / `provider_products` from `pos_prices_products`. ## Provider connectivity — three phases (decided 2026-07-11, [PDR-0003](/pdr/0003-vas-connectivity-phasing-and-wallet-visibility/)) [Section titled “Provider connectivity — three phases (decided 2026-07-11, PDR-0003)”](#provider-connectivity--three-phases-decided-2026-07-11-pdr-0003) | Phase | Connectivity | Credentials | Scope | | ----------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | | **1 (MVP)** | The client’s existing system — the **VB webService directly** (not `apitae-sales`), same SQL Server + provider connections | **One shared webService user** managed in the terminales backend (`VB_WS_URL/USER/PASSWORD` env); terminales transacts **on behalf of all merchants**. Temporary by design. | In MVP | | **2** | Same VB webService | **Each merchant connects their own webService user**; resolved per call through the `resolveVbCredentials(merchantId)` seam the Phase-1 adapter ships | Post-MVP | | **3** | **Direct provider integration** (TAE, services, …) | Per-provider **certification** of the terminales solution | Post-MVP (future) | The external VB balance is **never displayed in any phase** — see the wallet rule below. **⚠ Amended 2026-08-03 (PDR-0003 Amendment):** a merchant may now **connect one or more external platforms** (Mantarys and its instances VentaMóvil / Sureste — same aggregator, different backend URLs — plus a generic “Otra plataforma”) with **their own username/password credential** (Phase-2 seam brought forward as a product feature), and the **balance of a connected platform IS displayed, owner-only**. See the wallet rule below and the Wave-3 design module `figma-wave3-external-platforms-prompts`. ## Cross-cutting constraints [Section titled “Cross-cutting constraints”](#cross-cutting-constraints) * **VAS is online-only.** TAE/Pines/Services need a live external call and **cannot be offline-queued** — the offline-first model (`offline-and-idempotency.md`) applies to physical/cash sales only. Chips, being local inventory, can sell offline like any physical good. * **Reporting:** extend `daily_sales_rollups` and the report catalog to break down by `kind`/`provider` (TAE by carrier, Pines by brand, Services by biller, commission by provider). See [./reporting.md](/business-rules/reporting/). * **Security:** PIN codes masked everywhere; carry over fraud controls (recipient blacklist, velocity/anomaly detection, MFA step-up above an amount threshold). * **Independent ledgers (2026-06-24, amended 2026-07-11 and again 2026-08-03 by [PDR-0003](/pdr/0003-vas-connectivity-phasing-and-wallet-visibility/)):** * **VB provider float (shared Phase-1 user)** — the upstream supplier wallet reached through the single backend-managed credential. terminales reads it (`Check_Balance`) **internally only** (ops metric / low-float platform alert); it is **not displayed** and **not** a terminales ledger. * **Connected external platform balances (NEW 2026-08-03)** — when a merchant **connects a platform with their own credential** (Mantarys / VentaMóvil / Sureste / Otra), that platform’s balance **IS displayed, owner-only** (same `merchants.ownerId` guard as the wallet), and **can fund the VAS portion of a sale** as a funding source distinct from and additional to the merchant-wallet debit. Each connection keeps its **own balance/usage ledger**, still never mirrored into `balance_movements`. Auth = username + password (not Keycloak). Insufficient platform balance → blocked at selection (no negatives). Online-only. Data model, credential encryption and the per-platform ledger are still to build (design gaps #46–#49). * **Adding balance to a connected platform (NEW 2026-08-04, wave 3.1)** — the merchant can **top up** a platform balance (or terminales’ own wallet) from the handheld, making the per-platform ledger **bidirectional** (credits from top-ups/deposits + debits from VAS). Three funding sources: (1) **card on the handheld** (AngelPay bridge, immediate); (2) **transfer from the terminales merchant wallet** (`merchant_balances` → platform, immediate); (3) a **deposit** (bank channels A–C, **delayed — an owner/manager validates it**; cash channel D immediate) — the deposit form gains a **`target`** field (merchant wallet **or** a connected platform). **Vendor gap:** Mantarys exposes **no inbound-credit method** today (`Check_Balance` only reads; sales only debit), so the card-charge top-up is forward-looking — terminales records the credit against the per-platform ledger and reconciles the shown balance against `Check_Balance`; the real provider-side crediting depends on a **new Mantarys integration** (design gaps #50/#51). No negatives; online-only. * **terminales merchant wallet** — the platform-managed wallet (`merchant_balances` / `balance_movements`), shown to **merchant owners only**, funded by the deposit channels (PDR-0003 Decision 3) and the **default funding source for VAS operations** — in Phase 1 terminales pays the provider on the merchant’s behalf, so the sale is blocked before the provider call if funds are insufficient (unless a connected platform balance is chosen instead), with a compensating credit on reverso. Integrity model: `TDR-0004` (DBOS per-merchant serialization). * The *store cash balance* (shifts / cash movements / Z-reports) remains the separate, employee-visible cash-control domain — cash paid directly to the merchant (channel D) credits store cash, never the wallet. * Keep the ledgers independent: never mirror VB movements into `balance_movements` — a VAS debit is our own movement, not a copy of VB’s. ## Suggested future Epic (tickets created separately) [Section titled “Suggested future Epic (tickets created separately)”](#suggested-future-epic-tickets-created-separately) VAS schema (catalog + sales additions + new collections) · provider-catalog sync · aggregator adapter · VAS sale/fulfillment DBOS flow · PIN custody + delivery + resend · chips serialized inventory + Excel import · adeudo lookup endpoint · VAS reporting dimensions · fraud/MFA controls · merchant-wallet debit wiring ([PDR-0003](/pdr/0003-vas-connectivity-phasing-and-wallet-visibility/) / `TDR-0004` — the ledgers themselves are never reconciled). Ticketed 2026-07-11 as Wave 3 Tracks C + K. ## VAS and the shift drawer — cash tender, open shift, not voidable (R9, 2026-09-26) [Section titled “VAS and the shift drawer — cash tender, open shift, not voidable (R9, 2026-09-26)”](#vas-and-the-shift-drawer--cash-tender-open-shift-not-voidable-r9-2026-09-26) **Supersedes** the 2026-09-03 “Known limitation — VAS bypasses the shared tender (MVP)”. Source: terminales-api #964 (R9 A3, A7; PO rulings in the R9 findings), clients web #1133/#1135 and Android #500. VAS sales (airtime, service/bill payment, gift-PIN) still do **not** flow through the shared POS cart or `finalizeOrder`: a VAS sale is a self-contained checkout that debits the merchant wallet (or a connected platform balance), calls the aggregator and writes its own fulfillment + custody records. What changed is how it meets the **cash drawer**: * **The customer pays cash, and that cash is recorded on the shift.** The customer hands the cashier the «Total a cobrar»; neither the web nor the Android screen has a tender step. Once the cashier is told SUCCESS, the api writes **one CASH `order_payments` row** for the total (deterministic id `vcash_`) and stamps the order’s `shiftId` / `terminalId`, in one transaction. The close’s expected cash and the Corte Z (both read the CASH tenders of the shift’s finalized orders) therefore count it **exactly once**. A replay of the same `clientId` never adds it twice (and repairs a missing one); a failed or reversed sale records nothing. A platform-funded sale records the cash too — the customer still paid the cashier. * **A VAS sale requires an open shift.** `POST /vas/sale` takes optional `shiftId` / `terminalId` and resolves the drawer **before any charge**: a sent `shiftId` must be an OPEN shift of the store; otherwise the store’s open shift on `terminalId`, then the cashier’s own open shift, then the store’s only open shift. No drawer → **400 `sale_requires_open_shift`**, nothing charged or written (same code as a POS sale — see [pos-domain-model.md](/business-rules/pos-domain-model/) § Shift required to sell). Another terminal’s shift under strict mode → 400 `sale_shift_terminal_mismatch`. * **A VAS sale is not voidable or refundable.** `POST /pos/orders/:id/void` and `/refund` refuse a VAS order (a `vas-` synthetic cart or any `service_fulfillments` row) before any write: **409 `vas_sale_not_voidable`** — «Esta recarga ya se entregó y no se puede anular.» (delivered) or «Esta recarga no se completó; no hay cobro que anular.» (failed, reversed or still pending — a pending sale may still deliver). Without this, a same-day void would have handed the customer’s cash back out of the drawer (the CASH tender above) while the airtime stayed delivered. The clients hide «Anular»/«Reembolsar» on a VAS sale and drop it from «Anular venta»; the 409 is the backstop. **Superseded (rollup):** the paragraph below said a VAS sale makes no contribution to `daily_sales_rollups`. As of the same day (terminales-api #970, R9 C7 — see the next section) a **delivered** VAS sale now adds exactly one contribution to the rollup behind the Inicio KPI tiles. What’s still true: a VAS sale has **no** unified sale receipt, and its revenue/commission **detail** (by kind/provider) is still read from `service_fulfillments` and the commissions report (`pos/reports/commissions`), not from the rollup — the rollup only carries the aggregate transaction-count/revenue figures the Inicio tiles need. The new CASH tender makes VAS cash visible to the **drawer** (close, Corte Z cash reconciliation) independently of the rollup. Physical goods and SIM chips are unaffected — they use the normal cart/tender path. ## VAS fulfillment funding & sale outcome — single debit, crash-safe, in the rollup, CANCELLED on failure (R9 second wave, 2026-09-26) [Section titled “VAS fulfillment funding & sale outcome — single debit, crash-safe, in the rollup, CANCELLED on failure (R9 second wave, 2026-09-26)”](#vas-fulfillment-funding--sale-outcome--single-debit-crash-safe-in-the-rollup-cancelled-on-failure-r9-second-wave-2026-09-26) Four more R9 findings, all in terminales-api: **#968** (C1 single debit, C3 product cost), **#969** (C5, the same PR as the approval-freshness fix — see [pos-domain-model.md](/business-rules/pos-domain-model/) § Supervisor approval freshness), and **#970**/**#972**/**#973** + **#971** (C7 rollup, C8 CANCELLED). * **A platform-funded sale is charged once (C1, api #968).** Before, a VAS sale funded from a connected platform balance *also* debited the merchant wallet — a double charge. The funding source now travels in the durable fulfillment workflow’s own input: a platform-funded sale debits and reverses **only** the connection; a wallet-funded sale debits and reverses **only** the wallet — including on replay and on crash recovery. The sale still refuses an insufficient balance (402) or a cross-tenant connection (403) before writing anything; the response contract is unchanged. * **That debit is crash-safe (C5, api #969).** The connection debit, its link to the delivered fulfillment, and its reversal on failure now run as **idempotent steps inside the durable fulfillment workflow**, keyed per sale. After a crash mid-sale, DBOS recovery replays the workflow and posts **exactly one** debit and, if the purchase then fails, **exactly one** reversal — never a duplicate of either. * **A delivered VAS sale counts in the daily rollup (C7, api #970/#972/#973).** Behind the Inicio KPI tiles, `daily_sales_rollups` now gets exactly one contribution per delivered VAS sale: one transaction, its total as revenue, **0 units**, cash tender. A failed or reversed sale contributes nothing (see C8 below), and the report’s live-scan fallback applies the same rule. New additive column `sales_orders.rolledUpAt` marks a sale already counted, so a re-run of the backfill or a workflow replay never double-counts. Two follow-ups keep the contribution on the right store: a VAS order whose provider names no store now resolves the team’s real `stores` row (`#972`, so it lands on the store’s existing rollup row instead of a second one keyed on the store-team id that Inicio never reads) and gets that store’s folio; `#973`’s backfill repairs `sales_orders.storeId` on older orders the same way, for teams with exactly one store (ambiguous teams are listed and left alone). * **A failed/reversed sale’s order ends `CANCELLED`, not `FINALIZED` (C8, api #971).** A VAS sale whose purchase failed or was reversed is a sale that never happened — see [pos-domain-model.md](/business-rules/pos-domain-model/) § Same-day void for the new `sales_orders.status` value and how it differs from `VOID`. It has no cash tender and makes no rollup contribution; `void`/`refund` still answer `vas_sale_not_voidable` (409, «Esta recarga no se completó; no hay cobro que anular.») exactly as before the reorder needed to keep that message. Clients show it as **«No completada»** (web #1146, Android #502) — distinct from a `VOID`’d sale — and hide reprint/share on it (Android). * **Backfills (all dry-run by default, idempotent; `-- --apply` writes):** * `pnpm backfill:vas-rollup` — adds past delivered VAS sales the rollup missed (pre-#970). * `pnpm backfill:vas-order-storeid` — repairs `sales_orders.storeId` on older VAS orders (pre-#972/#973). * `pnpm backfill:vas-failed-cancelled` — moves older failed/reversed VAS orders from `FINALIZED` to `CANCELLED` (pre-#971). # Feature-flag minimalism — born-in-prod, no legacy scaffolding **Decided:** 2026-08-24 · **By:** PO (Alfredo Pacheco) · **Status:** binding **Eventual home:** user/tech manual (terminales-docs). This file is the interim record so we don’t lose it. ## Principle [Section titled “Principle”](#principle) We have **not** shipped to prod/live yet. Until we do, there is **no legacy path to preserve**. 1. **Build only the final path.** Do not add or retain scaffolding whose only job is to support a superseded path — dual-shape adapters, rollback branches, “not-yet” fallbacks — beyond the short window needed to land a change safely. 2. **Born in prod with only strictly-necessary flags.** A feature flag is *strictly necessary* only if it gates something that must be **independently toggled in production** — a genuinely optional capability or a kill-switch for a risky integration. A flag that merely stages a migration we have **not shipped yet** is not necessary; it is transitional and must be collapsed before launch. 3. **Sweep the agentic-workflow flags.** The pipeline introduced many `FEATURE_FLAG_*` / `feature_flag_*` flags. Before the prod cutover, audit every one across **api / web / mobile** and either justify it as strictly-necessary or delete it (flag + both branches). > Superseding note: the earlier “all feature flags ON for MVP” directive (2026-07-14) turned the ramp off. This is the next step — the flags themselves come **out** for prod birth, not just switched on. ## Applies now — settlement consolidation [Section titled “Applies now — settlement consolidation”](#applies-now--settlement-consolidation) * **`feature_flag_settlement_unified`** (env `FEATURE_FLAG_SETTLEMENT_UNIFIED`, api) is a **migration flag, not a product flag.** Unified is THE settlement path from day one. Before prod, collapse to unified-only: remove the flag, the legacy settlement path, and the per-order `settlementModel=legacy` branch. The `settlementModel` lock exists to make the *migration* safe — once there is no legacy path, it too can go. * **Mobile client** dual-shape handling is transitional scaffolding: the legacy `Captured` branch, and the “el cobro se registró; finaliza desde Ventas” guards in the Manual/Split card view-models. Once unified is the default and the legacy tail is deleted server-side, simplify the client to the **settling path only**, and finish full settling-observe for Manual/Split (drop the interim guard). ## Why [Section titled “Why”](#why) Carrying dual paths and migration flags into prod is cost with **no** pre-launch benefit: it doubles the surface to test and maintain and invites the exact class of drift — project-id, stale env, path divergence — that has already cost us time. Pre-launch, the cheapest correct system is the one that only ever had one path. ## How to apply [Section titled “How to apply”](#how-to-apply) * New work targets the final path directly. Add a flag only for an independently-toggleable prod capability or a real kill-switch. * When a migration flag *is* needed to land a change safely, file its **removal** as a same-wave follow-up, never a permanent fixture. * Before prod cutover: `grep -rn "FEATURE_FLAG_\|feature_flag_"` across api, web, mobile → justify each survivor or delete it. # Decision records — ADR · PDR · TDR > the durable, append-only record of the decisions that shape the solution > **Purpose:** the durable, append-only record of the decisions that shape the solution, so agents and humans cite a decision instead of re-deriving or re-litigating it. Every ticket that implements a decision links its record; every record links the tickets it spawned. Established 2026-07-11 as part of Wave 3 (see `wave3-mvp-analysis`). **Status:** as of 2026-07-30 — index current; the ten records below are all still `Accepted`, and all four Wave-3 decisions have since been **executed** (see the outcome column). ## The three record types [Section titled “The three record types”](#the-three-record-types) The repo distinguishes decisions by **what kind of question they answer**, because the audiences and the “who signs off” differ. A single wave usually produces some of each. | Type | Answers | Owner (signs off) | Lives in | | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -------- | | **ADR** — Architecture Decision Record | *Structural/architecture* choices that span repos or bind the shape of the system for a long time (data platform, auth model, deploy topology, migration strategy). | Engineering + architecture | `adr` | | **PDR** — Product Decision Record | *Product/scope/UX* choices: what is in or out of a release, what a feature must do, which reports must exist, pricing/policy calls. | Product Owner | `pdr` | | **TDR** — Technical Decision Record | *Implementation-level* technical choices, narrower than an ADR: how to wire a library, which test strategy, a service-internal pattern. Reversible with local blast radius. | The implementing engineer(s) | `tdr` | Rule of thumb: **if reversing it later would force a cross-repo rewrite → ADR; if it changes what the client can do at launch → PDR; if it only changes how one repo builds something → TDR.** When unsure, prefer the broader type (ADR > PDR > TDR). ## Format [Section titled “Format”](#format) All three share the [ADR-0001](/adr/0001-appwrite-integration/) skeleton so they read the same: * Front-matter: **Status** (`Proposed` / `Accepted` / `Superseded by …`), **Date**, **Deciders**, **Repos**, **Related** (links). * **Context** — the situation and forces. * One `##` section per decision, each with a **Rationale** and an **### Alternatives rejected** subsection. * Supersede, never edit history: a reversed decision gets a new record whose Status points back, and the old record’s Status becomes `Superseded by `. Numbering is zero-padded and per-type: `adr/000N-*.md`, `pdr/000N-*.md`, `tdr/000N-*.md`. ## Index [Section titled “Index”](#index) ### ADR — Architecture [Section titled “ADR — Architecture”](#adr--architecture) | # | Title | Status | Date | Outcome (2026-07-30) | | ------------------------------------------------------- | --------------------------------------------------------------------------- | -------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | [0001](/adr/0001-appwrite-integration/) | Appwrite integration architecture | Accepted | 2026-06-25 | ✅ Built — Epics A–F merged sprint 32 | | [0002](/adr/0002-full-appwrite-migration-decommission/) | Full Postgres→Appwrite domain migration & TypeORM decommission | Accepted | 2026-07-11 | ✅ Executed — BetterAuth gone (#149, 07-04), frozen tables dropped (#248, 07-12); TypeORM **migrations machinery deliberately retained** (#180) | | [0003](/adr/0003-observability-telemetry-transport/) | Observability telemetry transport — Sentry (frontend) + OTel/LGTM (backend) | Accepted | 2026-07-11 | ✅ Wired — `observability-{validate,stage,prod}.yml` + grafana-stage dashboards | ### PDR — Product [Section titled “PDR — Product”](#pdr--product) | # | Title | Status | Date | Outcome (2026-07-30) | | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------- | | [0001](/pdr/0001-mvp-golive-scope/) | MVP go-live scope — VAS + Clientes in; QR / cobro-rápido / POS-interaction / KPI-drill-down deferred | Accepted | 2026-07-11 | ✅ In scope and built on all three clients; deferrals held | | [0002](/pdr/0002-mvp-reports-catalog/) | MVP reports catalog | Accepted | 2026-07-11 | ✅ All 8 built + CSV export; live verification partly blocked by the `report-store-select` drift | | [0003](/pdr/0003-vas-connectivity-phasing-and-wallet-visibility/) | VAS provider connectivity phasing (1: shared VB user · 2: per-merchant · 3: direct) & wallet visibility — VB balance never displayed | Accepted | 2026-07-11 | 🟡 Phase 1 in progress — the shared-user model is what makes the Mantarys catalog-tenancy question open (see open questions) | ### TDR — Technical [Section titled “TDR — Technical”](#tdr--technical) | # | Title | Status | Date | Outcome (2026-07-30) | | ------ | ----------------------------------------------------------------------------------------- | ------------------------------ | ---------- | ---------------------------------------------------------------------------------------------------------------------- | | `0001` | Wave 3 verification strategy — stories + parity audit + E2E | Accepted | 2026-07-11 | ✅ All three legs running: story boards, parity galleries (web + mobile), mock + live Playwright suites | | `0002` | Sentry SDK wiring — web (Next.js) + mobile (Compose) | Accepted | 2026-07-11 | ✅ Web wired behind `FEATURE_FLAG_OBSERVABILITY_SENTRY_WEB` (now ON) | | `0003` | VAS fulfillment via DBOS durable workflows + `products.kind` discriminator | Accepted (amended by PDR-0003) | 2026-07-11 | ✅ Workflow, ledger, wallet debit and PIN custody built; the **aggregator transport** is the open half — see `Mantarys` | | `0004` | Merchant wallet integrity — DBOS per-merchant serialization (Lendbit pattern) on Appwrite | Accepted | 2026-07-11 | ✅ Serialized ops + integrity job merged (api #374/#375); wallet flags enabled on stage 2026-07-30 | ## Standing directives without a record (yet) [Section titled “Standing directives without a record (yet)”](#standing-directives-without-a-record-yet) Binding calls made in review sessions that have **not** been written up as ADR/PDR/TDR. They are listed here so they are citable; promoting them into real records is outstanding work. | Directive | Date | Type it should be | Where it currently lives | | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | -------------------------------------- | ------------------------------------------------------------------------------------- | | **All feature flags ON for MVP** — the ramp is over; nothing ships dark | 2026-07-14 | PDR (supersedes the flag-ramp posture) | `AI_CONTEXT.md` hard rule 1; production ramp ticket api #264 | | **Sales are online-only, forever** — no offline queueing of operational writes; the offline cache is non-operational reads only; DBOS owns resiliency | 2026-07-06 | ADR | [business-rules/offline-and-idempotency.md](/business-rules/offline-and-idempotency/) | | **Mantarys Confirmation code `17` is terminal → FAILED** (reverse, never poll) — fail-safe, overrides the legacy documentation | 2026-07-26 | TDR | `mantarys-integration-plan` | | **M6 cutover: migrations are the only DDL path; no console/`psql` DDL** | 2026-07-11 | ADR | `terminales-api/src/database/MIGRATIONS.md` (api #180) | | **`terminales-solution` commits direct to `main`; code repos keep PRs** | — | convention, not a decision record | `AI_CONTEXT.md` hard rule 7 | ## Relationship to existing docs [Section titled “Relationship to existing docs”](#relationship-to-existing-docs) * `architecture-decisions` — the informal rationale table that predates this system; ADR-0001/0002 formalize it. New architecture decisions go through an ADR, not that table. * `open-questions-and-todos` — live deferrals and “do not fix prematurely” notes; when an open question is *settled*, it becomes a record here and is struck from that list. # Parity Figma ↔ App (web) > Galería lado a lado del diseño en Figma y la pantalla real de terminales-web, organizada por historia de usuario. > **Qué es esto:** una galería lado a lado del **diseño en Figma** y la **pantalla real** de `terminales-web`, organizada por [historia de usuario](https://github.com/inspiraCode/terminales-solution/blob/main/docs/product/user-stories.md) y, al final, por capa del design system (átomos · moléculas · organismos). > > **Para qué sirve:** > > 1. Ver de un vistazo **en qué difiere la app del diseño**, para poder priorizar los ajustes. > 2. **Mostrar avance al cliente** — cada historia de usuario con sus pantallas reales. > 3. Tener un lugar donde **anotar** observaciones por pantalla y por componente. > > **Plan de remediación:** el orden de ataque para cerrar estos gaps con el mínimo de trabajo está en [remediation-plan.md](/design/parity/remediation-plan/) — se arregla por capas (tokens → theme → átomos → patrones), no pantalla por pantalla. > > **Desviaciones intencionales:** no toda diferencia es un defecto. Cuando la implementación difiere del frame **a propósito** (mejor UX, o convención del design system / web) se registra en la fila **Desviación (propuesta)** de la tabla y en [intentional-deviations.md](/design/parity/intentional-deviations/), en vez de abrir un issue de ajuste. Una desviación aceptada convierte un ❌/⚠️ de *Notas* en un ✅ aceptado — por eso, **antes de crear un draft de UI polish, revisá si la diferencia ya está registrada como desviación**. Una fila con `—` no tiene desviación: la diferencia (si la hay) debe corregirse para coincidir con Figma. > > **Estado:** las celdas marcadas `⚠️ Falta …` señalan trabajo pendiente: o falta exportar el render de Figma, o falta capturar la pantalla. ## Fuentes [Section titled “Fuentes”](#fuentes) | Qué | De dónde | | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | Renders de Figma (`figma/`) | `terminales-solution/docs/design/web/{screens,atomic,molecules,organisms}/` — archivo Figma “Terminales - Claude” `ndebIq7cqexBCf9eKVU0Fm` | | Capturas de la app (`screenshots/`) | worktree `351-admin-shell`, `src/app/**/screenshot.png` (59 páginas), renombradas por ruta | | Historias de usuario | `terminales-solution/docs/product/user-stories.md` | | Auditoría previa por pantalla | `docs/progress/design-parity-matrix.md` (W01–W65, estado ✅/🟡/⚠) | ## Convenciones [Section titled “Convenciones”](#convenciones) * **Nombre de captura** = la ruta del App Router con `/` → `-`, sin route-groups ni corchetes. `src/app/admin/pos/stock-levels/adjust/screenshot.png` → `screenshots/admin-pos-stock-levels-adjust.png`. * **Nombre de render** = `figma-.png`, tal como se exporta desde `design/web/`. * **`⚠️ Falta diseño`** = no hay render de Figma exportado para ese frame. * **`⚠️ Falta captura`** = la pantalla no se capturó (es un modal/estado, o la ruta no existe). ## Cómo actualizar esta galería [Section titled “Cómo actualizar esta galería”](#cómo-actualizar-esta-galería) Dos comandos, en este orden. Ambos son idempotentes: se pueden re-correr cuando cambie la app. ```bash pnpm screenshots # recaptura docs/design-parity/screenshots/ (~5 min) pnpm docs:parity # reconstruye este README con lo que haya en disco ``` `pnpm screenshots` levanta **su propio** servidor de producción en el puerto **4000** (`next build && next start` sobre un `distDir` propio, `.next-screenshots`), con `NEXT_PUBLIC_USE_MOCKS=true` y **todos** los feature flags en ON. Eso significa que: * no pisa un `pnpm dev` que ya tengas abierto en el 3000; * ninguna ruta cae en `notFound()` por un flag apagado, así que la galería no tiene huecos; * los datos son los fixtures mock, iguales en cada corrida. Subconjuntos: `pnpm screenshots --grep admin-pos` (el filtro matchea el nombre del archivo). Para reusar un servidor que ya levantaste: `E2E_BASE_URL=http://localhost:4000 pnpm screenshots`. ### Las notas se escriben aquí y se conservan [Section titled “Las notas se escriben aquí y se conservan”](#las-notas-se-escriben-aquí-y-se-conservan) La sección **Notas** de cada pantalla es para escribir a mano — es el objetivo del documento. Hay dos formas de hacerlo, y las dos terminan en el mismo markdown: 1. **En este archivo**, editando la celda directamente. 2. **Desde el navegador**, con el sitio de documentación corriendo en local (`cd terminales-solution/docs-site && pnpm dev`): cada bloque de Notas muestra un botón **✏️ Editar nota** que abre un textarea y, al guardar, **escribe en este archivo**. Es la forma cómoda de anotar, porque tenés las capturas a tamaño grande al lado mientras escribís. Sólo existe en `pnpm dev` — el sitio publicado es estático y de sólo lectura. En ambos casos `pnpm docs:parity` **conserva** lo que escribas: antes de reescribir lee este archivo y recupera toda nota que no sea el placeholder, reinsertándola bajo el mismo encabezado. El ciclo es: 1. `pnpm screenshots` → cambian las imágenes 2. editás la celda **Notas** → tu observación, en markdown 3. `pnpm docs:parity` → se reconstruyen las tablas, tus notas sobreviven Una nota se identifica por su encabezado `####`. Si renombrás un encabezado, su nota queda huérfana: el script **la imprime en consola** al terminar en vez de perderla en silencio. ### Publicación en el sitio de documentación [Section titled “Publicación en el sitio de documentación”](#publicación-en-el-sitio-de-documentación) Este documento se publica en el sitio Starlight (`terminales-solution/docs-site`) como **/design/parity/**. El sitio lo lee *desde este repo* —no hay copia— vía `externalSources` en `docs-site/publish.config.mjs`: copia el markdown y sus imágenes a `public/design/parity/` y reescribe las rutas relativas a absolutas. O sea que el markdown de acá sigue siendo la única fuente de verdad, y las notas que escribas aparecen en el HTML en el próximo `pnpm build` del sitio. Para ver las capturas en grande en el sitio hay dos ayudas: * **⛶ Modo amplio** — un botón arriba del todo que oculta la navegación y el panel derecho; las imágenes pasan de \~345 px a \~610 px de ancho. La preferencia se recuerda. * **Click en cualquier captura** — abre el PNG original a tamaño completo (también funciona en GitHub). **Qué captura** — la lista vive en `e2e/screenshots/targets.ts`, una entrada por ruta más un array `dialogs` para los modales de cada pantalla (varios frames de Figma —W21, W30, W33, W39— son diálogos en la app, no páginas, y un barrido solo-de-rutas los dejaría siempre vacíos). Los componentes del design system salen de `/playground` vía `e2e/screenshots/playground.spec.ts`, que lee el registro de showcases, así que **agregar un showcase lo captura solo**. Agregar una pantalla = agregar una entrada en `targets.ts`; nada más hay que tocar. **Sobre el determinismo:** en dos corridas independientes, 157 de 167 capturas salieron byte-idénticas. Las demás cambian unos pocos bytes con las mismas dimensiones (el showcase de `Skeleton` anima con `animation="wave"` de MUI, cuyos keyframes CSS ignoran la emulación de `prefers-reduced-motion`; el resto es *antialiasing* de fuentes). O sea: `git diff` sobre esta carpeta es buena señal, pero antes de concluir que la UI cambió, mirá la imagen. ## Hallazgos sistémicos [Section titled “Hallazgos sistémicos”](#hallazgos-sistémicos) > Auditoría automatizada que comparó cada render de Figma contra su captura de la app, midiendo los colores con un muestreador de píxeles. **Son defectos de sistema: se arreglan una vez en el token o en el componente base y desaparecen de decenas de pantallas.** > **Estado (2026-07-23) — remediación fases 0–2 aplicada y re-auditada.** Se cableó DESIGN.md como fuente de verdad (\`scripts/generate-tokens.mjs\`), se reescribió el theme MUI con \~15 overrides, se corrigieron los átomos y se promovieron a props reales los hacks \`sx\` de los showcases (BalanceCard tone, IconButton, ProviderTile, ListRow, SummaryRow, Toast, geometría del Checkbox, default del Icon). La **re-auditoría contra las capturas nuevas** mueve el veredicto de **96 ❌ / 7 ⚠️ / 0 ✅** (pre-fix) a **37 ❌ / 58 ⚠️ / 6 ✅** — la mayoría de las pantallas pasó de “importante” a “menor”. Las notas por pantalla de abajo YA reflejan el estado post-remediación. ### Residuo tras la remediación (backlog para la próxima pasada) [Section titled “Residuo tras la remediación (backlog para la próxima pasada)”](#residuo-tras-la-remediación-backlog-para-la-próxima-pasada) > **Estado (2026-07-23, re-verificado contra el código).** La lista original de 11 residuos sistémicos está **casi toda resuelta**: la remediación de fases 0–2 más los batches atómicos cablearon los fixes en el theme/átomos y **cascadearon a las pantallas**. Cada ítem se re-verificó con grep del archivo culpable citado (ver playbook §7, las notas caducan entre batches). Estado real, con evidencia: **✅ Resueltos y verificados en código** 1. **Patrón de selección** — Chip activo = tinte primary.light #E6EEFA + texto primary.main (src/ui/atoms/Chip/index.tsx:58-61); SegmentedControl usa thumb blanco background.paper sobre track (SegmentedControl/index.tsx:83-89), patrón correcto del diseño. Cerrado. 2. **Badges de estado** — StatusBadge (soft por rol) adoptado en 37 puntos de src/app; los culpables citados ya migraron: SalesOrderDetail.tsx:86-87 y ProductList.tsx:95-96 usan StatusBadge (los Chip restantes en ProductList son contadores numéricos). Cerrado. 3. **Nav/lista seleccionado** — theme.ts:84 fija action.selected = primary.soft (#E6EEFA) y SideNav lo consume en su estado Mui-selected. Cerrado. 4. **Borde del Button outlined** — el slot outlined existe en MuiButton (theme.ts:225), 1px sólido primary.main. Cerrado. 5. **Paradigma de etiqueta** — FormField renderiza label estático arriba (Typography como label, FormField/index.tsx:36-49) + placeholder gris, no floating label. Cerrado. 6. **CTA tonal/soft** — HeldOrderCard usa Button variant tonal (HeldOrderCard/index.tsx:118-119), no contained sólido. Cerrado. 7. **Fecha en español** — src/core/utils.ts:20 llama dayjs.locale(es) y el formateo per-fecha usa .locale(es) (utils.ts:307). Cerrado. 8. **Iconos de adorno** — theme.ts:74-76 apunta action.active al gris frío #9CA3AF, no al #757575 default. Cerrado. 9. **White-label del avatar** — WhiteLabelThemeProvider pasa light = lighten(main, 0.9) explícito a augmentColor, que para #0052CC resuelve a #E6EEFA (WhiteLabelThemeProvider.tsx:58-62). Cerrado. **Abierto (residuo real)** 3. **Migración de tablas admin** — \~90% hecho: 31 pantallas ya usan el DataTable compartido (toolbar FilterBar + Pagination numerada + selección por fila) y 0 usan ya TablePagination de MUI. Quedan 4 archivos con tabla MUI cruda: PosStock/StockLevelList.tsx, PosStock/StockMovementList.tsx, Reports/ZHistoryTable.tsx, Reports/ReportView\.tsx — candidatos a migrar (algunas vistas de reporte pueden querer layout propio; evaluar caso por caso). 4. **Iconografía divergente** (cosmético) — glifos outlined de MUI y chevrons donde el diseño usa glifos rellenos/triángulos, y glifos desnudos donde el diseño los envuelve en contenedor cuadrado tintado #F0F4F8/#E6EEFA — Banner, Toast, KPI Stat Card, Empty State, FAB, TenderPicker. Baja prioridad. > **Implicación:** varias notas por pantalla de abajo se escribieron antes de que estos fixes sistémicos cascadearan y pueden estar sobre-estimadas. Al re-auditar una pantalla, verificar primero contra este estado antes de re-reportar un residuo ya cerrado. ### Fixes confirmados en las capturas [Section titled “Fixes confirmados en las capturas”](#fixes-confirmados-en-las-capturas) * Azul canónico #0052CC medido exacto en fills, links, focus rings e iconos activos (con hover/pressed #003D99, disabled #E5E7EB+#9CA3AF) en casi todos los pares. * Tinte de marca #E6EEFA (palette.primary.light) ahora existe y se usa en Avatar, IconButton, Pagination, Balance Card CTA, Customer Row, chip de BulkActionBar, fila activa de Command Palette y filas no-leídas de Notificaciones. * Avatar corregido: relleno tinte #E6EEFA + iniciales #0052CC (antes solid primary invertido), diámetros exactos 32/40/56px. * Fondos soft por rol confirmados en Alert/Banner/StatusBadge/Toast: info #EFF6FF, success #ECFDF5, warning #FFFBEB, danger #FEF2F2, con textos #3B82F6/#10B981/#F59E0B/#EF4444. * Campos de formulario: relleno blanco, borde hairline #E5E7EB, focus #0052CC 2px, error #EF4444, disabled bg #E5E7EB, alto \~40px — medidos en FormField, Textarea, Select, Denomination Row y campos POS. * Controles de selección: unchecked #D1D5DB, checked/indeterminate #0052CC en Checkbox y Radio; PIN Dot lleno #0052CC; Switch track ON #0052CC / OFF #D1D5DB con thumb blanco y geometría 44×24/thumb18/inset3. * Superficies de tabla y tarjeta: cabecera #F7F9FC, divisores/hairline #E5E7EB, cards planas sin sombra. * Colores de chart: serie 1 #0052CC, serie 2 #3B82F6 (info), serie 3 #10B981, serie 4 #F59E0B (useChartColors.ts). * Superficie dark de Toast y Tooltip medida #1F2933 opaca, igual al asset de Figma. * Tokens de texto: primario #1F2933, secundario #6B7280, disabled #9CA3AF — consistentes en títulos, labels y valores. ## Capturas que no corresponden a su diseño [Section titled “Capturas que no corresponden a su diseño”](#capturas-que-no-corresponden-a-su-diseño) > La auditoría verifica primero que cada par muestre la misma pantalla. En estos 12 casos **no** coinciden, así que su celda de Notas describe el desajuste en vez de listar diferencias de estilo. El patrón dominante: el frame de Figma diseña una pantalla completa y la app la resuelve como diálogo sobre el listado, o la captura tomó otro paso del flujo — son decisiones de producto (diálogo-vs-página) o gaps de implementación, no desviaciones de estilo. * **W03b · Nueva contraseña** — La captura de la app muestra el estado de error “enlace inválido” de /reset-password (sin query params userId/secret), no el formulario de nueva contraseña que diseña el Figma. * **Categoría — alta** — El frame de Figma (figma-w23-categorias.png) es el LISTADO de Categorías (tabla + filtros + paginación, sin ningún formulario), mientras que la captura de la app muestra el drawer “Nueva categoría” (formulario de alta) abierto sobre el listado. No existe en el par un frame de Figma del formulario de * **Impuesto — alta** — La captura de Figma emparejada (figma-w24-impuestos.png) es la pantalla de LISTA de Impuestos (tabla de tasas + botón “Nueva tasa”); no contiene ningún formulario de alta. La app sí muestra el drawer “Nuevo Impuesto”. Falta exportar/emparejar el frame de Figma del formulario de alta (si existe), o e * **W33 · Detalle de turno / Corte Z** — El panel FIGMA diseña una PÁGINA completa de “Corte Z” de un turno CERRADO; el panel APP muestra un DIÁLOGO de detalle de un turno ABIERTO (sin corte todavía). Distinto paso del flujo, distinta composición (página vs diálogo) y distinto estado del turno. * **W35 · Cerrar turno** — El Figma diseña una página de resumen “Cerrar turno · Corte Z” dentro del shell admin (sidebar + topbar), mientras que la app captura una pantalla de terminal (shell POS, sin sidebar) de conteo de efectivo por denominación. Son pasos distintos del flujo de cierre. * **W36b · Movimiento de caja (terminal)** — El manifest reutiliza el Figma de W36 (página admin de administración) para W36b, pero la captura de la app es la pantalla de terminal (formulario de entrada con teclado numérico). Son dos pantallas distintas: lista/tabla admin vs. formulario de captura de un movimiento en terminal. * **W38 · Empleado crear / editar** — Figma = pagina completa “Empleado” (header + secciones Datos personales/Acceso + sticky save bar). App = drawer lateral derecho sobre la lista de Empleados, con set de campos distinto. * **W56 · Reporte de ventas** — El panel APP no muestra la pantalla del diseño. FIGMA dibuja el “Reporte de ventas · Desglose del periodo” (tabs Por producto/categoría/empleado/método + tabla de líneas Producto·SKU·Categoría·Ingresos·Unidades·Tendencia con checkboxes, botón Exportar y paginación 1–5 de 248). La captura APP muestra * **W58 · Cortes de caja (Z)** — El Figma diseña el HISTÓRICO/lista de cortes Z (tabla con filas Z-088…Z-084); la app captura el DETALLE de un solo corte Z (tarjeta con desglose Ventas/Formas de Pago/Conciliación). Son dos vistas distintas del mismo flujo. * **W42 · Tiempo Aire (TAE)** — Figma diseña la página compuesta de recarga (proveedor como chips + grilla de montos + captura de teléfono/total); la app muestra solo el paso “Selecciona el proveedor” con tarjetas grandes y un “Resumen de compra” vacío, sin montos ni campo de teléfono. * **W46b · Registrar depósito** — El frame que estaba pareado era la **lista** de depósitos (W46), no el **formulario** que captura la app; se despareó la lista para no comparar dos pantallas distintas. Falta exportar el frame del formulario «Registrar depósito» de Figma (ver TODO en la celda de Figma de W46b) para tener un par comparable. * **W07 · Dashboard** — El sidebar coincide (misma app, ruta “Inicio” activa), pero el panel de contenido principal es un diseño completamente distinto. Figma diseña un dashboard analítico de retail: saludo “Hola, Victoria”, 4 KPIs (Ventas hoy $8,450, Transacciones 128, Ticket promedio $66.01, Productos vendidos 342), gráf ## Índice [Section titled “Índice”](#índice) * [Hallazgos sistémicos](#hallazgos-sist%C3%A9micos) * [Capturas que no corresponden a su diseño](#capturas-que-no-corresponden-a-su-dise%C3%B1o) * [Pantallas por historia de usuario](#pantallas-por-historia-de-usuario) * [ACC — Acceso y onboarding](#acc--acceso-y-onboarding) * [POS — Venta en punto de venta](#pos--venta-en-punto-de-venta) * [VENTAS / VOID — Historial de ventas, cancelación y reembolso](#ventas--void--historial-de-ventas-cancelaci%C3%B3n-y-reembolso) * [CAT — Catálogo](#cat--cat%C3%A1logo) * [INV — Inventario](#inv--inventario) * [SHIFT — Turnos y caja](#shift--turnos-y-caja) * [STAFF — Personal y control de acceso](#staff--personal-y-control-de-acceso) * [REP — Reportes](#rep--reportes) * [VAS — Servicios de valor agregado](#vas--servicios-de-valor-agregado) * [BAL — Wallet del comercio](#bal--wallet-del-comercio) * [CONEXIONES — Plataformas de saldo (W66–W72)](#conexiones--plataformas-de-saldo-w66w72) * [CLI — Clientes](#cli--clientes) * [TERM / WL — Comercios, tiendas, terminales y white-label](#term--wl--comercios-tiendas-terminales-y-white-label) * [ACCT — Cuenta y ajustes](#acct--cuenta-y-ajustes) * [NOTIF — Notificaciones](#notif--notificaciones) * [SEARCH — Búsqueda global](#search--b%C3%BAsqueda-global) * [PLAT — Administración de plataforma](#plat--administraci%C3%B3n-de-plataforma) * [Sin historia de usuario — rutas técnicas y de desarrollo](#sin-historia-de-usuario--rutas-t%C3%A9cnicas-y-de-desarrollo) * [Design system](#design-system) * [Átomos](#%C3%A1tomos) * [Moléculas](#mol%C3%A9culas) * [Organismos](#organismos) *** ## Pantallas por historia de usuario [Section titled “Pantallas por historia de usuario”](#pantallas-por-historia-de-usuario) ### ACC — Acceso y onboarding [Section titled “ACC — Acceso y onboarding”](#acc--acceso-y-onboarding) Flujo W-01. Historias `US-ACC-1` … `US-ACC-6`. #### W01 · Iniciar sesión [Section titled “W01 · Iniciar sesión”](#w01--iniciar-sesión) **Ruta:** `/login` · **Figma:** [`107:358`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=107-358) · **Historia:** US-ACC-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------- | | [![figma W01](/design/parity/figma/screens/figma-w01-login.png)](/design/parity/figma/screens/figma-w01-login.png "figma W01 — abrir a tamaño completo") | [![app W01](/design/parity/screenshots/login.png)](/design/parity/screenshots/login.png "app W01 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes** — la capa sistémica de color está firme; persisten overrides locales de tamaño y falta contenido del diseño.- **Color:** ✅ Azul canónico #0052CC en panel de marca, botón Entrar, focus ring y links; fondo #F5F7FA y heading #1F2933 coinciden; hairlines #E5E7EB. - **Sombras:** 👍 En la recaptura el botón Entrar se ve **plano** — ya no proyecta la sombra dura (#DFDFDF→#F2F2F2) que reportaba la nota previa; el override de `PrimaryButton.tsx` parece removido/neutralizado. La tarjeta conserva una sombra suave equivalente al Figma (reverificar el `boxShadow` del Paper en `src/ui/templates/AuthSplit/index.tsx`). - **Espaciado:** ❌ Sigue igual — campos \~56px de alto vs \~38px del Figma (`src/ui/Inputs/TextInput.tsx` hardcodea `padding: '18px 22px'`); botón Entrar \~70px vs 52px del Figma (`PrimaryButton.tsx` padding sm). - **Tipografía:** etiqueta del botón sobredimensionada vs 14px/600 del sistema (`PrimaryButton.tsx fontSize sm 18`). - **Contenido:** ❌ Sigue faltando el botón outlined “Acceso rápido con PIN” (existe en Figma con borde azul; sin rastro en `LoginForm.tsx` ni `login.i18n.ts`). El divisor “— o —” del Figma se reemplaza por el texto “O recibe un enlace de acceso por correo”; “Enviar enlace mágico” es link de texto azul en Figma pero botón outlined neutro en la app (`SecondaryButton` en `LoginForm.tsx`). - **Layout:** split 50/50 (`AuthSplit flex '1 1 50%'`) vs \~43/57 del Figma; “¿Olvidaste tu contraseña?” alineado a la derecha en la app, a la izquierda en Figma. - **Estados:** la captura muestra el email en autofocus (ring 2px #0052CC), asteriscos rojos de requerido y toggle de visibilidad oscuro que el Figma no dibuja — decidir si se ocultan o se actualiza el diseño. - Sistémico: label estático sobre el campo, paradigma ya adoptado ✓. | | | Desviación (propuesta) | | | 🔶 **Propuesta** — el campo de contraseña agrega un **toggle de visibilidad (ojo)** ausente en el frame. *Figma:* agregar el icono trailing mostrar/ocultar al frame `107:358`. *(El tamaño de campos/botón NO es desviación: es bug de overrides `sx` — va como ❌.)* [ver desviaciones](/design/parity/intentional-deviations/) | | #### W02 · Verificación 2FA [Section titled “W02 · Verificación 2FA”](#w02--verificación-2fa) **Ruta:** `/login (MfaChallengeForm)` · **Figma:** [`110:376`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=110-376) · **Historia:** US-ACC-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------- | | [![figma W02](/design/parity/figma/screens/figma-w02-2fa.png)](/design/parity/figma/screens/figma-w02-2fa.png "figma W02 — abrir a tamaño completo") | ⚠️ **Falta captura** — es un estado dentro de `/login`, no una ruta propia; hay que capturarlo con Playwright forzando el reto MFA | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | | Desviación (propuesta) | | | — | | #### W03 · Recuperar contraseña [Section titled “W03 · Recuperar contraseña”](#w03--recuperar-contraseña) **Ruta:** `/forgot-password` · **Figma:** [`110:407`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=110-407) · **Historia:** US-ACC-3 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W03](/design/parity/figma/screens/figma-w03-recuperar.png)](/design/parity/figma/screens/figma-w03-recuperar.png "figma W03 — abrir a tamaño completo") | [![app W03](/design/parity/screenshots/forgot-password.png)](/design/parity/screenshots/forgot-password.png "app W03 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** (recaptura post-remediación; misma pantalla, split W03 con card centrada)- **Color:** Fixes sistémicos confirmados por medición: panel izquierdo, botón, badge y focus ring del campo todos en **#0052CC** canónico; título #1F2933; subtítulo #6B7280. Residuo: el link “Volver a iniciar sesión” renderiza **#232D37** (texto plano) en vez del azul de marca semibold del diseño — es un `next/link` desnudo en `src/app/forgot-password/ForgotPasswordForm.tsx:182-187`. - **Iconos:** El badge del “?” es un cuadrado **sólido #0052CC** con glifo blanco; Figma lo diseña como cuadrado en tinte **#E6EEFA** (medido) con círculo azul — `ForgotPasswordForm.tsx:89-104` usa `bgcolor: 'primary.main'` en lugar de `primary.light`. - **Contenido:** Falta el hint informativo persistente de Figma (“Si el correo existe, recibirás un enlace en unos minutos.”, tinte #EFF6FF verificado en el diseño); la app solo muestra un Banner **después** de enviar y con otro copy (`ForgotPasswordForm.tsx:132-136`). Copys divergentes en `recovery.i18n.ts`: subtítulo recortado (“Te enviaremos…” vs “Ingresa tu correo y te enviaremos…”), botón “Enviar enlace de recuperación” vs “Enviar enlace”, placeholder “” vs “”. - **Contenido:** El panel de marca reutiliza `LoginBrand` con el tagline genérico del login; Figma W03 pide el tagline propio “Recupera el acceso a tu cuenta en unos pasos.” (`src/app/forgot-password/page.tsx:22`). - **Layout:** CTA medido a **\~40px** (ramp medium) donde Figma usa el botón grande **\~52px**; falta `size` en el Button del form. - **Estados:** La captura tiene el campo con `autoFocus` (ring azul 2px #0052CC correcto); el borde hairline #E5E7EB en reposo queda como “verificar”. - **Bordes/Sombras:** Card radio y sombra suave equivalentes en ambos paneles; botón sin sombra ✓. - Sistémico conocido (una línea): label flotante MUI con asterisco vs label estático sobre el campo del diseño — patrón de formulario pendiente de decisión de producto. | | | Desviación (propuesta) | | | — | | #### W03b · Nueva contraseña [Section titled “W03b · Nueva contraseña”](#w03b--nueva-contraseña) **Ruta:** `/reset-password` · **Figma:** [`110:407`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=110-407) · **Historia:** US-ACC-3 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W03b](/design/parity/figma/screens/figma-w03b-nueva-contrasena.png)](/design/parity/figma/screens/figma-w03b-nueva-contrasena.png "figma W03b — abrir a tamaño completo") | [![app W03b](/design/parity/screenshots/reset-password.png)](/design/parity/screenshots/reset-password.png "app W03b — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Panel FIGMA:** el paso de formulario del flujo de recuperación — icono de candado en tinte, título “Crea tu nueva contraseña”, campos “Nueva contraseña” y “Confirmar contraseña”, checklist de requisitos y botón “Guardar contraseña”. - **Panel APP:** el estado de error de la misma ruta — card “Restablecer contraseña” con alerta “El enlace de recuperación es inválido o expiró.” y link “Volver a iniciar sesión”, sin ningún campo (recaptura sigue en esta rama). - **Causa:** `src/app/reset-password/ResetPasswordForm.tsx` exige `?userId=…&secret=…` en la URL (`hasParams`, líneas 54–56/121); la captura se tomó sin esos params y cayó en la rama de enlace inválido. - **Acción de captura:** recapturar con `/reset-password?userId=dummy&secret=dummy` (o mockeando `useSearchParams`) para renderizar el formulario y poder auditar la paridad. - **Dato aprovechable:** el azul de marca ya mide **#0052CC** en la captura y el fondo #F5F7FA coincide — el fix sistémico sí llegó a esta ruta, pero el residuo del formulario queda **pendiente de verificar**. | | | Desviación (propuesta) | | | — | | #### W04 · Acceso rápido (PIN) [Section titled “W04 · Acceso rápido (PIN)”](#w04--acceso-rápido-pin) **Ruta:** `/terminal/lock` · **Figma:** [`110:481`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=110-481) · **Historia:** US-ACC-4 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W04](/design/parity/figma/screens/figma-w04-pin.png)](/design/parity/figma/screens/figma-w04-pin.png "figma W04 — abrir a tamaño completo") | [![app W04](/design/parity/screenshots/terminal-lock.png)](/design/parity/screenshots/terminal-lock.png "app W04 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** — recaptura post-#538: el panel de marca azul ya está presente y su tagline coincide con el Figma (“Cambia de cajero en segundos con un PIN.”).- **Layout:** 👍 Mejora frente a la nota previa — ahora es split con panel de marca (antes era columna única centrada). Persisten diferencias de composición: el Figma abre con avatar “JP” + “Juan Pérez” + “Cajero · Tienda Centro”; la app solo muestra el título “PIN de Cajero Demo” sin avatar ni rol. El link “Cambiar usuario” está **arriba** (bajo el título) en la app vs **abajo** del teclado en el Figma. - **Teclado:** la app dibuja cada tecla como caja con hairline y añade una tecla **“C”** (limpiar) + botón explícito **“Entrar”** (deshabilitado); el Figma usa numerales sin caja, sin “C” ni “Entrar”, solo backspace en chip tintado. - **Dots de progreso:** con el PIN vacío la app no muestra los 4 dots (aparecen al teclear) mientras el Figma los dibuja siempre; el estilo del ring vacío hereda del residuo de \[PIN Dot]. - **Color:** azul canónico #0052CC verificado en ambos paneles. | | | Desviación (propuesta) | | | — | | #### W05 · Seleccionar comercio / tienda [Section titled “W05 · Seleccionar comercio / tienda”](#w05--seleccionar-comercio--tienda) **Ruta:** `/select-store` · **Figma:** [`112:406`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=112-406) · **Historia:** US-ACC-5 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W05](/design/parity/figma/screens/figma-w05-seleccionar-comercio.png)](/design/parity/figma/screens/figma-w05-seleccionar-comercio.png "figma W05 — abrir a tamaño completo") | [![app W05](/design/parity/screenshots/select-store.png)](/design/parity/screenshots/select-store.png "app W05 — abrir a tamaño completo") | | Notas | | | **✅ Capturado (#614) · veredicto best-effort.** Roster multi-tienda del harness (`/select-store?mockStores=multi`; el roster mock por defecto tiene una sola membresía y auto-salta al dashboard — seam mock-only en `useMembershipStoreTeams`). Divergencia estructural (no defecto de token): la app usa el shell **AuthSplit** y lista las tiendas como **filas verticales** sin dirección/estado ni botón «Continuar»; el frame las dibuja como **tarjetas horizontales**. El estado vacío **M6** (node `559-3186`) → `blocked-report` (sin export de Figma; MCP sin auth). Evidencia + notas por rúbrica: `docs/ui-evidence/614/`. | | | Desviación (propuesta) | | | — | | #### W06 · Onboarding — crear comercio [Section titled “W06 · Onboarding — crear comercio”](#w06--onboarding--crear-comercio) **Ruta:** `— (sin ruta)` · **Figma:** [`112:443`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=112-443) · **Historia:** US-ACC-6 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W06](/design/parity/figma/screens/figma-w06-onboarding.png)](/design/parity/figma/screens/figma-w06-onboarding.png "figma W06 — abrir a tamaño completo") | [![app W06](/design/parity/screenshots/onboarding.png)](/design/parity/screenshots/onboarding.png "app W06 — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Azul canónico verificado: botón Continuar y paso activo del stepper = #0052CC en ambos paneles; título #1F2933, subtítulo #6B7280 — fixes sistémicos presentes en esta captura. - **Bordes:** Campos con hairline #E5E7EB y relleno blanco en app y Figma (medido en el primer campo). - **Espaciado:** Alturas idénticas medidas: campos \~40px (app 341→380 vs Figma 339→378), botón grande \~52px (app 655→706 vs Figma 657→708). Composición, radios y densidad de la tarjeta equivalentes. - **Layout:** Misma estructura: stepper 1-2-3 + “Paso 1 de 3”, título, subtítulo, 4 campos, CTA full-width. - **Contenido:** Divergencia **intencional y documentada** (comentario en `src/app/onboarding/onboarding.i18n.ts`): el alcance US-ACC-6 es merchant-create only, así que “Nombre de la tienda” del Figma se reemplaza por “Usuario propietario (ID)” + helper, el subtítulo cambia (“Registra el comercio y designa a su propietario.” vs “Configura tu organización y tu primera tienda.”) y “Dirección” pasa a “(opcional)”. Es el Figma el que quedó desactualizado respecto al contrato — actualizar el frame, no el código. | | | Desviación (propuesta) | | | — | | #### Cierre de sesión [Section titled “Cierre de sesión”](#cierre-de-sesión) **Ruta:** `/sign-out` · **Historia:** — | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — pantalla técnica sin frame en Figma | [![app Cierre de sesión](/design/parity/screenshots/sign-out.png)](/design/parity/screenshots/sign-out.png "app Cierre de sesión — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** No hay nodo exportado para `/sign-out`. Dato de la recaptura: la captura de la app es en realidad la pantalla de **`/login`** — el cierre de sesión redirige ahí (idéntica a W01), no tiene pantalla propia. **Siguiente paso:** confirmar que `/sign-out` es solo redirección; si es así, su paridad ya queda cubierta por W01 y no requiere frame propio. Si se espera una pantalla de transición/confirmación, exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`. | | | Desviación (propuesta) | | | — | | #### Callback de login (OAuth / magic link) [Section titled “Callback de login (OAuth / magic link)”](#callback-de-login-oauth--magic-link) **Ruta:** `/login/callback` · **Historia:** — (sin frame de diseño) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — pantalla técnica sin frame en Figma | [![app Callback de login (OAuth / magic link)](/design/parity/screenshots/login-callback.png)](/design/parity/screenshots/login-callback.png "app Callback de login (OAuth / magic link) — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | | Desviación (propuesta) | | | — | | ### POS — Venta en punto de venta [Section titled “POS — Venta en punto de venta”](#pos--venta-en-punto-de-venta) Flujo W-03. Historias `US-POS-1` … `US-POS-9`. #### W10 · POS Venta (two-pane) [Section titled “W10 · POS Venta (two-pane)”](#w10--pos-venta-two-pane) **Ruta:** `/terminal/home` · **Figma:** [`114:715`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=114-715) · **Historia:** US-POS-1 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W10](/design/parity/figma/screens/figma-w10-pos-venta.png)](/design/parity/figma/screens/figma-w10-pos-venta.png "figma W10 — abrir a tamaño completo") | [![app W10](/design/parity/screenshots/terminal-home.png)](/design/parity/screenshots/terminal-home.png "app W10 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** (captura recapturada por PR #538; el existingNote de 2026-07-23 quedó parcialmente obsoleto)- **Layout:** El two-pane (catálogo izq. / carrito der.) coincide con Figma. ✓ - **Contenido (mejora post-#538):** Los tabs de catálogo **Productos / Servicios / TAE / Pines** ahora SÍ están presentes (el existingNote los marcaba ausentes); “Productos” activo en azul con ícono de carrito, igual que el diseño. ✓ - **Color (residuo del Chip resuelto en esta pantalla):** La chip de categoría seleccionada (“Todas”) ahora usa **tinte suave #E6EEFA con texto azul**, ya no sólida #0052CC con texto blanco — coincide con Figma; el residuo previo del componente \[Chip] no aplica aquí. - **Layout:** El campo de búsqueda sigue siendo Paper blanco outlined (\~40px) con botón de lupa azul a la derecha, vs Figma relleno **#F0F4F8** sin borde — hereda del residuo de \[Search Field] (`picker/SearchInput.tsx`). - **Contenido:** Las tarjetas de producto siguen sin badge de existencias (“24 pzs” verde / “Agotado” rojo del diseño) — `picker/ProductGrid.tsx` sólo renderiza nombre + precio. - **Contenido / Header (cambiado por PR #538):** El header de la app difiere del diseño: app = “Punto de venta” sin subtítulo + chip “Turno abierto” + nombre **“Cajero Demo”** con avatar CD + botón outlined **“Bloquear ahora”** (lock); Figma = “Punto de venta / Tienda Centro · Caja 1” + **“Orden #1042”** + avatar SR (sin nombre de cajero ni Bloquear). Divergencia de composición/producto, no de estilo. - **Estados:** Captura con **carrito vacío** (“El carrito está vacío / Agrega productos…”, Cobrar deshabilitado #E5E7EB+#9CA3AF, Subtotal/IVA/Total $0.00) vs Figma con **venta activa** (Venta actual, “Vaciar” rojo, 3 ítems con steppers −/+, Subtotal $372, Descuento, IVA $51, TOTAL $372, “Cobrar · $372.00” azul). Distinto estado: steppers de cantidad, totales y Cobrar activo quedan sin comparar; re-verificar con carrito poblado. - **Extras de la app (no en Figma):** FAB azul “+” abajo-izq., botón outlined “Asignar cliente”, bloque de estado de terminal (“En línea · WT-0001 · Terminal seleccionada automáticamente”), “Guardar en espera” y “Ventas en espera” — divergencias de producto conocidas/pendientes. - **Color:** Precios en **#0052CC**, disabled #E5E7EB+#9CA3AF, tarjetas planas blancas con hairline #E5E7EB — fixes sistémicos confirmados en la captura fresca. | | | Desviación (propuesta) | | | 🔶 **Aceptada** — el **FAB speed-dial** abajo-izquierda (Movimiento de caja / Cerrar turno / Anular venta, solo con turno abierto) se reubicó fuera del header a propósito. **Decisión de producto: se mantiene** — no filar issue para quitarlo. *Figma:* documentar el FAB de navegación en el frame W10 (`255:3399`). [ver desviaciones](/design/parity/intentional-deviations/) | | #### W11 · POS Resultados de búsqueda [Section titled “W11 · POS Resultados de búsqueda”](#w11--pos-resultados-de-búsqueda) **Ruta:** `/terminal/home (estado de búsqueda)` · **Figma:** [`131:8968`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=131-8968) · **Historia:** US-POS-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W11](/design/parity/figma/screens/figma-w11-resultados.png)](/design/parity/figma/screens/figma-w11-resultados.png "figma W11 — abrir a tamaño completo") | [![app W11](/design/parity/screenshots/terminal-home--busqueda.png)](/design/parity/screenshots/terminal-home--busqueda.png "app W11 — abrir a tamaño completo") | | Notas | | | **✅ Recapturado — la búsqueda ahora se ejecuta y el grid queda filtrado.**- **FIGMA:** “4 resultados para ‘coca’”, grid con 4 variantes de Coca-Cola (600 ml, 2 L, Light 600 ml, Sin Azúcar 2 L) con badge de stock (24/18/12/9 pzs), carrito con 3 ítems y “Cobrar · $372.00” en #0052CC. Chips de tipo: Todos/Productos/Servicios/Tiempo Aire/Tarjetas. - **APP:** “coca” ejecutado (ENTER) → grid filtrado. El `open` del diálogo `terminal-home--busqueda` ahora confirma la búsqueda y **espera a que el grid quede filtrado** antes de capturar; la captura anterior disparaba antes del `useDebounce` (500 ms) del hook de la lista y congelaba el catálogo completo de W10. - **Diferencia de contenido (no de estado):** el catálogo mock sólo tiene **un** producto “coca” (Coca-Cola 600 ml), así que la app muestra **1** resultado frente a los **4** del diseño. - **Pendiente verificado en código (no cambia al recapturar):** no existe label “N resultados para …” en `src/app/terminal/home/picker/`; los chips de la app son categorías (CategoryRail: Todas/Bebidas/Alimentos/…) y no el filtro por tipo del diseño; `ProductGrid.tsx` no muestra badge de stock; el carrito está vacío (el diseño lo muestra con 3 ítems). - **Fixes sistémicos medidos (OK):** azul canónico #0052CC en chip seleccionado y precios; disabled #E5E7EB; tarjetas blancas con hairline #E5E7EB. | | | Desviación (propuesta) | | | — | | #### W12 · POS Categoría especial (VAS) [Section titled “W12 · POS Categoría especial (VAS)”](#w12--pos-categoría-especial-vas) **Ruta:** `— (sin ruta)` · **Figma:** [`131:9039`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=131-9039) · **Historia:** US-VAS-4 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el nodo existe pero no se exportó a `design/web/screens/` | ⚠️ **Falta captura** — no hay ruta VAS dentro del POS del terminal | | Notas | | | **⛔ Comparación no posible — faltan las dos imágenes.** No hay render de Figma exportado ni captura de la app, así que no hay nada que comparar. El motivo de cada gap está en las celdas de arriba. Primero hay que resolverlo (exportar el frame y/o construir la pantalla); recién ahí tiene sentido auditar paridad. | | | Desviación (propuesta) | | | — | | #### W13 · POS Descuento [Section titled “W13 · POS Descuento”](#w13--pos-descuento) **Ruta:** `— (hoja modal)` · **Figma:** [`115:805`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=115-805) · **Historia:** US-POS-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------- | | [![figma W13](/design/parity/figma/screens/figma-w13-descuento.png)](/design/parity/figma/screens/figma-w13-descuento.png "figma W13 — abrir a tamaño completo") | ⚠️ **Falta captura** — la hoja de descuento no está cableada en el POS | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | | Desviación (propuesta) | | | — | | #### W14 · POS Cobrar (método) [Section titled “W14 · POS Cobrar (método)”](#w14--pos-cobrar-método) **Ruta:** `— (diálogo)` · **Figma:** [`115:1004`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=115-1004) · **Historia:** US-POS-3 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------- | | [![figma W14](/design/parity/figma/screens/figma-w14-cobrar.png)](/design/parity/figma/screens/figma-w14-cobrar.png "figma W14 — abrir a tamaño completo") | ⚠️ **Falta captura** — diálogo (`TenderMethodDialog`), requiere captura con carrito activo | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | | Desviación (propuesta) | | | — | | #### W15 · POS Efectivo [Section titled “W15 · POS Efectivo”](#w15--pos-efectivo) **Ruta:** `— (diálogo)` · **Figma:** [`115:1126`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=115-1126) · **Historia:** US-POS-3 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------- | | [![figma W15](/design/parity/figma/screens/figma-w15-efectivo.png)](/design/parity/figma/screens/figma-w15-efectivo.png "figma W15 — abrir a tamaño completo") | ⚠️ **Falta captura** — diálogo (`CashTenderDialog`) | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | | Desviación (propuesta) | | | — | | #### W16 · POS Tarjeta — procesando [Section titled “W16 · POS Tarjeta — procesando”](#w16--pos-tarjeta--procesando) **Ruta:** `— (diálogo)` · **Figma:** [`117:1335`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=117-1335) · **Historia:** US-POS-4 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------- | | [![figma W16](/design/parity/figma/screens/figma-w16-tarjeta.png)](/design/parity/figma/screens/figma-w16-tarjeta.png "figma W16 — abrir a tamaño completo") | ⚠️ **Falta captura** — diálogo (`RemoteCardTenderDialog`) | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | | Desviación (propuesta) | | | — | | #### W17 · POS Pago mixto (split) [Section titled “W17 · POS Pago mixto (split)”](#w17--pos-pago-mixto-split) **Ruta:** `— (diálogo)` · **Figma:** [`115:1234`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=115-1234) · **Historia:** US-POS-5 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------- | | [![figma W17](/design/parity/figma/screens/figma-w17-mixto.png)](/design/parity/figma/screens/figma-w17-mixto.png "figma W17 — abrir a tamaño completo") | ⚠️ **Falta captura** — superficie no cableada | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño (`115:1234`) existe pero el harness no capturó el diálogo de pago mixto. **Siguiente paso:** si la superficie existe, agregar su entrada en `e2e/screenshots/targets.ts` (estado del sheet de tender/split) y correr `pnpm screenshots`; si no está construida, es gap de implementación, no de paridad. Nota: cuando se capture, cotejar contra los residuos abiertos de **POS — Tender Picker** y **POS — Discount Sheet** del ledger. | | | Desviación (propuesta) | | | — | | #### W18 · POS Recibo [Section titled “W18 · POS Recibo”](#w18--pos-recibo) **Ruta:** `— (documento de impresión)` · **Figma:** [`133:10777`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=133-10777) · **Historia:** US-POS-7 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------ | | [![figma W18](/design/parity/figma/screens/figma-w18-recibo.png)](/design/parity/figma/screens/figma-w18-recibo.png "figma W18 — abrir a tamaño completo") | ⚠️ **Falta captura** — el recibo es solo impresión (`ReceiptDocument.tsx`), no hay pantalla | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño (`133:10777`) existe pero no hay documento de impresión capturado (es un render de recibo, no una ruta navegable). **Siguiente paso:** si existe una vista/preview de recibo, agregar target en `e2e/screenshots/targets.ts`; si el recibo sólo se materializa al imprimir, documentar que la paridad de este artefacto no es auditable por screenshot. | | | Desviación (propuesta) | | | — | | #### W19 · POS Venta exitosa [Section titled “W19 · POS Venta exitosa”](#w19--pos-venta-exitosa) **Ruta:** `— (modal)` · **Figma:** [`117:1417`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=117-1417) · **Historia:** US-POS-6 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- | | [![figma W19](/design/parity/figma/screens/figma-w19-exito.png)](/design/parity/figma/screens/figma-w19-exito.png "figma W19 — abrir a tamaño completo") | ⚠️ **Falta captura** — no existe pantalla/modal de confirmación | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño del modal de éxito (`117:1417`) existe pero el harness no alcanzó el estado post-cobro. **Siguiente paso:** agregar el target del modal de confirmación en `e2e/screenshots/targets.ts` (flujo que dispara la venta exitosa) y correr `pnpm screenshots`; si no está construido, es gap de implementación, no de paridad. | | | Desviación (propuesta) | | | — | | #### W64 · Ventas en espera [Section titled “W64 · Ventas en espera”](#w64--ventas-en-espera) **Ruta:** `/admin/pos/held-orders` · **Figma:** [`574:14716`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=574-14716) · **Historia:** US-POS-8 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W64](/design/parity/figma/screens/figma-w64-en-espera.png)](/design/parity/figma/screens/figma-w64-en-espera.png "figma W64 — abrir a tamaño completo") | [![app W64](/design/parity/screenshots/admin-pos-held-orders.png)](/design/parity/screenshots/admin-pos-held-orders.png "app W64 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** — misma pantalla; la captura fresca confirma que varios residuos ya cerraron y el remanente es la anatomía de una molécula más un bug de dato.- **Estados (cerrado):** 👍 “Reanudar venta” ahora es **tonal soft** (bg azul claro, etiqueta azul), igual que el diseño — se resolvió el `variant="contained"` sólido que se reportaba antes en `HeldOrderCard`. - **Layout segmented (cerrado):** 👍 el control “Mis carritos / Todos” ahora es **pill blanco sobre track gris `#F0F4F8`** con texto oscuro, conforme a Figma; el patrón invertido (pill azul sólido) del atom `SegmentedControl` ya no aparece. - **i18n (cerrado):** 👍 el tiempo relativo ya sale en español (“hace un mes”), no en inglés — `dayjs.locale('es')` quedó activo. El texto “hace un mes” vs “Hace 4 min” del diseño es ahora **dato viejo** (`heldAt` de `heldOrder.mocks.ts`), no bug de i18n. - **Layout (residuo real):** composición de la tarjeta aún difiere del diseño — hereda del residuo de **HeldOrderCard**: id “H-2271” sin “#” (Figma “#H-2271”); tiempo y “En espera por…” como **Chips outlined** (Figma: texto gris con mini-avatar inline); avatar “SR” arriba-derecha (Figma: inline junto al holder); precio abajo-derecha bajo los chips (Figma: esquina superior derecha). - **Contenido:** ❌ importes ×100 persiste: `$37,200.00` vs `$372.00` del diseño para el mismo carrito — el mock guarda centavos y `HeldOrdersList.tsx` formatea el crudo (`heldOrder.mocks.ts`, aclarar unidad en `types.ts`). Bug de dato, no de estilo. - **Estados:** la app muestra **“Todos”** activo; Figma muestra “Mis carritos”. Default `'all'` deliberado en `HeldOrdersList.tsx` — confirmar con producto. - **Color:** ✅ CTA/segmento activo miden azul canónico; borde de tarjeta hairline `#E5E7EB`; los avatares (“SR”, switcher “Tienda demo”) ahora leen en **tinte de marca soft**, sin el `lighten(#0052CC,.2)`=`#3374D6` que antes pisaba el tinte canónico en superficies white-label — aparente remediación. - **Nav (IA, registrar 1 vez):** el sidebar vivo diverge del de Figma — bajo ADMINISTRACIÓN sólo aparecen **Comercios** + Notificaciones, mientras el diseño lista “Comercio y tiendas / Terminales / Ajustes”. Reorganización de nav post-#538 (`src/components/AdminShell/adminNav.ts`); Figma probablemente desactualizado (🎨). Shell compartido — no se re-lista en las demás pantallas /admin. - **Contenido (menor):** subtítulo “…de la sucursal en el turno abierto” vs “…compartidos por tienda en este turno” del diseño — copy, `heldOrder.i18n.ts`. | | | Desviación (propuesta) | | | — | | #### W64b · Ventas en espera — desde el POS [Section titled “W64b · Ventas en espera — desde el POS”](#w64b--ventas-en-espera--desde-el-pos) **Ruta:** `/terminal/home (diálogo)` · **Figma:** [`574:14716`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=574-14716) · **Historia:** US-POS-8 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W64b](/design/parity/figma/screens/figma-w64-en-espera.png)](/design/parity/figma/screens/figma-w64-en-espera.png "figma W64b — abrir a tamaño completo") | [![app W64b](/design/parity/screenshots/terminal-home--dialog-ventas-en-espera.png)](/design/parity/screenshots/terminal-home--dialog-ventas-en-espera.png "app W64b — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** — variante en diálogo sobre el POS; la captura fresca confirma que los residuos de botón e i18n ya cerraron y sólo queda la anatomía de la tarjeta más un dato de mock.- **Estados (cerrado):** 👍 “Reanudar venta” se renderiza **tonal soft** (bg azul claro, etiqueta azul), conforme al diseño — resuelto el `variant="contained"` sólido que se flageaba en `HeldOrderCard/index.tsx`. - **i18n (cerrado):** 👍 el chip de tiempo ahora sale en español (“hace un mes”), no “17 days ago” — `formatRelativeTime`/dayjs ya usa locale `es`. El delta vs “Hace 4 min” del diseño es **dato viejo** (`heldAt` en `heldOrder.mocks.ts`), no i18n. - **Layout (residuo real):** anatomía de tarjeta distinta a Figma — hereda del residuo de **HeldOrderCard**: id “H-2271” sin “#”; tiempo y “En espera por Sofía R.” como **Chips outlined** (Figma: texto gris + mini-avatar inline); avatar “SR” arriba-derecha (Figma: inline); precio a la derecha de los chips (Figma: esquina superior derecha). - **Contenido:** montos `$37,200.00` / `$8,450.00` vs `$372.00` del diseño — mismo bug ×100 de centavos crudos (mock), no defecto de paridad. - **Bordes/Sombras:** ✅ tarjetas planas con hairline `#E5E7EB`, radio del diálogo conforme; “Cerrar” como link azul abajo-derecha. - **Decisión de producto (no se re-lista):** diálogo-vs-página y ausencia de tabs “Mis carritos/Todos” en el terminal (sólo “mine”) es comportamiento documentado. | | | Desviación (propuesta) | | | — | | #### POS — índice admin [Section titled “POS — índice admin”](#pos--índice-admin) **Ruta:** `/admin/pos` · **Historia:** — | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — índice de navegación, sin frame | [![app POS — índice admin](/design/parity/screenshots/admin-pos.png)](/design/parity/screenshots/admin-pos.png "app POS — índice admin — abrir a tamaño completo") | | Notas | | | **✅ Copy corregido en #548 (sin diseño Figma).** La ruta `/admin/pos` sigue siendo un panel índice/scaffold (aún sin páginas de entidad montadas), pero el texto ya está **en español**: título “POS” y cuerpo “Panel de administración de POS. Las páginas de cada entidad (Sucursales, Productos, Terminales…) se mostrarán aquí en sus respectivos tickets.” — se eliminó el placeholder en inglés que antes era visible al usuario. Sin frame de Figma exportado: cuando exista, exportarlo a `docs/design/web/` y re-correr `pnpm docs:parity`. La auditoría de paridad de layout queda pendiente hasta que se monten las páginas de entidad. | | | Desviación (propuesta) | | | — | | #### Intentos de pago (payment intents) [Section titled “Intentos de pago (payment intents)”](#intentos-de-pago-payment-intents) **Ruta:** `/admin/pos/payment-intents` · **Historia:** US-POS-4 (soporte) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — pantalla de diagnóstico, sin frame | [![app Intentos de pago (payment intents)](/design/parity/screenshots/admin-pos-payment-intents.png)](/design/parity/screenshots/admin-pos-payment-intents.png "app Intentos de pago (payment intents) — abrir a tamaño completo") | | Notas | | | **✅ Corregido en #547 (sin diseño Figma).** La pantalla migró de la tabla MUI cruda a la **DataTable compartida**. Verificado en la captura fresca:- **StatusBadge soft-by-role:** todos los estados ahora son suaves (fondo tenue + texto tintado): Aprobado verde, Requiere Captura ámbar, Procesando azul, Fallido rojo, Cancelado gris, Requiere Método de Pago gris — ya no son sólidos saturados; alineado con el fix del ledger. - **Fechas es:** columna “Fecha de Creación” en formato legible (`20 jun 2026`), ya no `mm/dd/yyyy` US. - **Filtros:** botón “Filtros” outlined 1px primary (los inputs Desde/Hasta nativos en formato US ya no se muestran inline; el residuo de date-range queda dentro del panel de filtros). - **Tabla:** header gris suave, montos con `MXN`, tarjeta enmascarada (•••• 4242), paginación “Mostrando 1–7 de 8”.Sin frame de Figma: para veredicto formal de paridad, exportar el nodo a `docs/design/web/` y re-correr `pnpm docs:parity`. | | | Desviación (propuesta) | | | — | | #### Aprobaciones de supervisor [Section titled “Aprobaciones de supervisor”](#aprobaciones-de-supervisor) **Ruta:** `/admin/pos/approvals` · **Historia:** US-POS-2 / US-VOID-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el diseño vive como organismo **POS Approval Modal**, no como pantalla | [![app Aprobaciones de supervisor](/design/parity/screenshots/admin-pos-approvals.png)](/design/parity/screenshots/admin-pos-approvals.png "app Aprobaciones de supervisor — abrir a tamaño completo") | | Notas | | | **✅ Corregido en #548 (sin diseño Figma).** Verificado en la captura fresca:- **Fechas es:** columna “Fecha” ahora legible (`20 jun 2026`, `21 jun 2026`…), ya no ISO crudo (`2026-06-20T15:12:00Z`). - **Sucursal:** valores legibles (“Centro”, “Norte”, “Sur”), ya no slugs (`store-centro` / `store-norte`). - **Estilos sistémicos conformes:** “Filtros” es botón outlined 1px primary; chips de Acción en gris soft; header de tabla gris claro; paginación “Mostrando 1–7 de 7”.Sin frame de Figma: para veredicto formal de paridad, exportar el nodo a `docs/design/web/` y re-correr `pnpm docs:parity`. | | | Desviación (propuesta) | | | — | | ### VENTAS / VOID — Historial de ventas, cancelación y reembolso [Section titled “VENTAS / VOID — Historial de ventas, cancelación y reembolso”](#ventas--void--historial-de-ventas-cancelación-y-reembolso) Flujo W-06. Historias `US-VOID-1` … `US-VOID-3`. #### W29 · Ventas [Section titled “W29 · Ventas”](#w29--ventas) **Ruta:** `/admin/pos/sales-orders` · **Figma:** [`119:1820`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=119-1820) · **Historia:** US-VOID-1 (entrada) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W29](/design/parity/figma/screens/figma-w29-ventas.png)](/design/parity/figma/screens/figma-w29-ventas.png "figma W29 — abrir a tamaño completo") | [![app W29](/design/parity/screenshots/admin-pos-sales-orders.png)](/design/parity/screenshots/admin-pos-sales-orders.png "app W29 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `best-effort`** — ⚠️ Mejor esfuerzo — diferencias menores medidas, ninguna bloqueante. Evidencia: referencia Figma `119:1820` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados).**⚠️ Diferencias menores** *(re-auditado con recaptura; gran build-out desde la nota previa — ya NO es una pantalla reducida)*- **Contenido (resuelto):** la toolbar rica del diseño ya está implementada — subtítulo KPI (“6 ventas · $2,691.20”), botón **Exportar**, búsqueda local (“Buscar por folio o cliente…”), tabs Todas/Pagadas/Canceladas y select. Columnas ahora Folio/Cliente/Fecha/Total/Artículos/Estado + editar por fila, con avatar de cliente. Pager numerado (“Mostrando 1–6 de 6”). - **Color/estilo (OK):** badges de estado **soft por rol** (Pagada verde, Pago Parcial ámbar, Abierta info, Anulada/Cancelada rojo, Borrador neutro); nav “Ventas” en tinte #E6EEFA + texto azul; cabecera de tabla #F7F9FC. - **Residuos menores:** “Exportar” es outlined (borde azul + icono descarga) vs Figma primario sólido “+ Exportar”; tabs en patrón underline vs pill segmentado del diseño; avatares gris “M” (todos Mostrador — dato) vs iniciales de color; falta la columna checkbox de selección que Figma sí muestra. - **Datos:** cifras y folios distintos (mock A-100x vs #104x del diseño) — esperado. | | | Desviación (propuesta) | | | — | | #### W30 · Detalle de venta [Section titled “W30 · Detalle de venta”](#w30--detalle-de-venta) **Ruta:** `/admin/pos/sales-orders/[id]` · **Figma:** [`125:6743`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=125-6743) · **Historia:** US-POS-7 / US-VOID-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W30](/design/parity/figma/screens/figma-w30-detalle-venta.png)](/design/parity/figma/screens/figma-w30-detalle-venta.png "figma W30 — abrir a tamaño completo") | [![app W30](/design/parity/screenshots/admin-pos-sales-order-detail.png)](/design/parity/screenshots/admin-pos-sales-order-detail.png "app W30 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** *(re-auditado; los bugs de la nota previa están CERRADOS)* — el par compara distinto registro (app **A-1001** vs Figma **#1042**), esperado por mocks.- **Cerrado:** ahora es **página dedicada** con breadcrumb (Ventas/Historial), no diálogo; layout de **dos tarjetas** (Artículos + Información) como el diseño; montos **con “$”** (formatCurrencyWithSymbol aplicado); badge “Pagada” en **verde soft** (ya no Chip sólido); iconos/labels sin literal negro. - **Información (OK):** Cliente/Cajero/Método/Recibido/Cambio/Folio fiscal — coincide con el diseño. - **Residuo (acciones):** el diseño muestra **Reimprimir** (azul outlined activo) + **Cancelar / Reembolsar** (rojo sólido); la app muestra Reimprimir **deshabilitado** + **Asignar cliente** (azul outlined) y no expone el botón rojo en la captura (el flujo sí existe — ver W31). Verificar gating de RefundAction con status PAID. - **Artículos (👍 impl más rica):** la app usa tabla con encabezados (Producto/Cant./Precio Unit./Importe) — más informativa que la lista simple del diseño; añade línea Descuento en el resumen. - **Bordes:** botón outlined hereda residuo de \[Button] (azul \~50%) — no re-listar. | | | Desviación (propuesta) | | | 🔶 **Propuesta** — la **tabla de Artículos es más completa** que el frame (columnas/totales extra), en el mismo layout de dos tarjetas. *Figma:* enriquecer la tabla de artículos del frame W30. [ver desviaciones](/design/parity/intentional-deviations/) | | #### Ventas — filtros [Section titled “Ventas — filtros”](#ventas--filtros) **Ruta:** `/admin/pos/sales-orders (diálogo)` · **Historia:** US-REP-6 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el diseño vive como organismo **Filter Bar**, no como pantalla | [![app Ventas — filtros](/design/parity/screenshots/admin-pos-sales-orders--dialog-filtros.png)](/design/parity/screenshots/admin-pos-sales-orders--dialog-filtros.png "app Ventas — filtros — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada (diálogo de filtros) pero no hay diseño exportado contra el cual compararla. **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es decisión de diseño pendiente, no desviación de la implementación. | | | Desviación (propuesta) | | | — | | #### W31 · Cancelar / Reembolsar [Section titled “W31 · Cancelar / Reembolsar”](#w31--cancelar--reembolsar) **Ruta:** `/terminal/voids (modal · render de referencia en /playground)` · **Figma:** [`131:8774`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=131-8774) · **Historia:** US-VOID-1 / US-VOID-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W31](/design/parity/figma/screens/figma-w31-cancelar-reembolsar.png)](/design/parity/figma/screens/figma-w31-cancelar-reembolsar.png "figma W31 — abrir a tamaño completo") | [![app W31](/design/parity/screenshots/playground/w31-cancelar-reembolsar.png)](/design/parity/screenshots/playground/w31-cancelar-reembolsar.png "app W31 — abrir a tamaño completo") | | Notas | | | **✅ Re-emparejado — el par compara el mismo estado (modal de autorización).** Verificado en la recaptura del render de referencia.- **FIGMA:** modal “Autorización requerida” con PIN-pad de 4 dígitos (Cancelar / Autorizar) sobre el detalle de **Venta #1042**, con sidebar y topbar del back office. - **APP (`/playground`, `VentasVoidScreenShowcase`):** misma composición — detalle de Venta #1042 + modal de autorización con teclado numérico, icono de candado en tinte ámbar y Cancelar/Autorizar. Coincide. - **Diferencias esperadas (estado + entorno):** el render de playground no lleva el shell admin (sidebar + topbar) que sí muestra el frame; los puntos del PIN están **vacíos** (Autorizar deshabilitado) porque no hay dígitos capturados, vs los 2 puntos azules del frame (estado a media captura). El color del anillo del punto vacío hereda del residuo de \[PIN Dot] — no re-listar. - **Por qué playground y no la ruta:** bajo los mocks del harness `/terminal/voids` sólo renderiza su estado vacío (pedidos anulables del turno abierto no coinciden con los fixtures) — mismo fallback de referencia del DS usado para W47/W57/W60. | | | Desviación (propuesta) | | | — | | ### CAT — Catálogo [Section titled “CAT — Catálogo”](#cat--catálogo) Flujo W-04. Historias `US-CAT-1` … `US-CAT-3`. #### W20 · Productos [Section titled “W20 · Productos”](#w20--productos) **Ruta:** `/admin/pos/catalog` · **Figma:** [`118:1529`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=118-1529) · **Historia:** US-CAT-1 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W20](/design/parity/figma/screens/figma-w20-productos.png)](/design/parity/figma/screens/figma-w20-productos.png "figma W20 — abrir a tamaño completo") | [![app W20](/design/parity/screenshots/admin-pos-catalog.png)](/design/parity/screenshots/admin-pos-catalog.png "app W20 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `best-effort`** — ⚠️ Mejor esfuerzo — diferencias menores medidas, ninguna bloqueante. Evidencia: referencia Figma `118:1529` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados).**⚠️ Diferencias menores** *(re-auditado; el bug de categoría y los gaps estructurales de la nota previa están RESUELTOS)*- **Cerrado (bug):** la columna Categoría ya muestra el **nombre** (Bebidas, Alimentos, Limpieza…), no el ID numérico. - **Cerrado (contenido/layout):** subtítulo “6 productos · 7 categorías”; búsqueda + chips Todos/Activos/Bajo stock + Filtros + select; columnas ahora **Producto (avatar iniciales en tinte) / SKU / Categoría / Precio / Stock / Estado + editar por fila**; pager numerado; botón “**+ Nuevo producto**” con icono y texto completo. - **Color (OK):** tab/indicador y CTA #0052CC; nav “Productos” en tinte de marca #E6EEFA + azul (el pill gris ya no está); cabecera #F7F9FC; badges “Activo” verde soft (StatusBadge, ya no Chip sólido). - **Residuos menores:** tabs underline vs pill segmentado del diseño; fila seleccionada (checkbox azul + #E6EEFA) no ejercitada en la captura; Estado todo “Activo” por datos vs Bajo/Agotado del frame. | | | Desviación (propuesta) | | | — | | #### W21 · Producto crear / editar [Section titled “W21 · Producto crear / editar”](#w21--producto-crear--editar) **Ruta:** `/admin/pos/catalog/products/{new,[id]}` · **Figma:** [`120:2657`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=120-2657) · **Historia:** US-CAT-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W21](/design/parity/figma/screens/figma-w21-producto-edit.png)](/design/parity/figma/screens/figma-w21-producto-edit.png "figma W21 — abrir a tamaño completo") | [![app W21](/design/parity/screenshots/admin-pos-catalog-products-new.png)](/design/parity/screenshots/admin-pos-catalog-products-new.png "app W21 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `match`** — ✅ Coincide. Evidencia: referencia Figma `120:2657` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados).**✅ Paridad alcanzada (#468)** — confirmada en la recaptura.Página completa: `PageHeader` (“Nuevo producto” + subtítulo) + tarjetas `Paper` + `StickySaveBar`, con grid de dos columnas (izquierda: Información — Nombre/SKU/Categoría select/Descripción/Precio de venta/Costo/Código de barras; derecha: Imagen dropzone + Inventario con Stock inicial, switch Alerta de stock bajo y Umbral).- **Layout/Contenido:** ✅ coincide con el contenedor full-page del frame; barra sticky “Tienes cambios sin guardar · Descartar/Guardar”. - **Color:** ✅ títulos/labels resuelven a tokens del tema (#1F2933); CTA Guardar #0052CC. - **⚠️ Divergencias intencionales (marcar el frame, no fix):** hint de Imagen “PNG o JPG · hasta 10 MB” (el frame dice “CSV, PNG o JPG”, inconsistente para una imagen); glyph del dropzone “+” + botón “Seleccionar archivos” vs glifo de compartir del frame — menor. | | | Desviación (propuesta) | | | — | | #### W22 · Detalle de producto [Section titled “W22 · Detalle de producto”](#w22--detalle-de-producto) **Ruta:** `— (drawer)` · **Figma:** [`133:10120`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=133-10120) · **Historia:** US-CAT-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------- | | [![figma W22](/design/parity/figma/screens/figma-w22-detalle-producto.png)](/design/parity/figma/screens/figma-w22-detalle-producto.png "figma W22 — abrir a tamaño completo") | ⚠️ **Falta captura** — no hay pantalla de detalle dedicada | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño (drawer de detalle de producto) existe pero no hay implementación capturada. **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es gap de implementación, no de paridad. | | | Desviación (propuesta) | | | — | | #### W23 · Categorías [Section titled “W23 · Categorías”](#w23--categorías) **Ruta:** `/admin/pos/catalog/categories` · **Figma:** [`122:2840`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-2840) · **Historia:** US-CAT-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W23](/design/parity/figma/screens/figma-w23-categorias.png)](/design/parity/figma/screens/figma-w23-categorias.png "figma W23 — abrir a tamaño completo") | [![app W23](/design/parity/screenshots/admin-pos-catalog-categories.png)](/design/parity/screenshots/admin-pos-catalog-categories.png "app W23 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `best-effort`** — ⚠️ Mejor esfuerzo — diferencias menores medidas, ninguna bloqueante. Evidencia: referencia Figma `122:2840` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados).**⚠️ Diferencias menores** *(re-auditado; avatar, badge soft, toolbar y pager ya llegaron)* — datasets distintos (app 7 vs Figma 12 categorías), esperado.- **Cerrado:** encabezado dedicado “Categorías · 7 categorías” + CTA “**+ Nueva categoría**”; **avatar de iniciales en tinte** por fila; **StatusBadge soft** (Activa verde / Inactiva gris) — ya no el icono booleano monocromo; toolbar con Buscar + chips Todas/Activas/Vacías + Filtros; pager numerado (“Mostrando 1–7 de 7”). - **Color (OK):** CTA y elementos activos #0052CC; cabecera de tabla #F7F9FC; divisores #E5E7EB. - **Residuo (contenido):** el esquema del diseño trae columnas de analítica **SKUs / Margen / Ventas mes** además de Productos/Estado; la app sólo muestra Categoría/Productos/Estado — faltan las tres métricas y el select derecho. - **Estado (semántica):** Figma distingue Activa/**Vacía**; la app usa Activa/**Inactiva** (categorías con 0 productos siguen “Activa”) — taxonomía a alinear con producto. | | | Desviación (propuesta) | | | — | | #### Categoría — alta [Section titled “Categoría — alta”](#categoría--alta) **Ruta:** `/admin/pos/catalog/categories (diálogo)` · **Figma:** [`122:2840`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-2840) · **Historia:** US-CAT-2 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Categoría — alta](/design/parity/figma/screens/figma-w23-categorias.png)](/design/parity/figma/screens/figma-w23-categorias.png "figma Categoría — alta — abrir a tamaño completo") | [![app Categoría — alta](/design/parity/screenshots/admin-pos-catalog-categories--dialog-nueva-categoria.png)](/design/parity/screenshots/admin-pos-catalog-categories--dialog-nueva-categoria.png "app Categoría — alta — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla** (re-verificado contra recaptura fresca).- **Panel FIGMA** (`figma-w23-categorias.png`): pantalla de **listado de Categorías** — “Categorías / 12 categorías”, botón “+ Nueva categoría”, buscador, chips Todas/Activas/Vacías, tabla (Categoría, SKUs, Margen, Ventas mes, Productos, Estado) y paginación. No contiene formulario de alta. - **Panel APP** (`admin-pos-catalog-categories--dialog-nueva-categoria.png`): **drawer lateral “Nueva categoría”** abierto sobre el listado, con campos **Categoría\*** (enfocado, “Ingrese el nombre de la categoría”), Identificador, Descripción, Categoría Padre, Sucursal, Orden, Color y acciones Cancelar/Guardar. - **Causa probable:** el manifest emparejó el frame del *listado* con la captura del *formulario de alta*; falta el frame Figma del formulario. - **Acción:** exportar/conseguir el frame “Nueva categoría” y re-auditar. Sin veredicto de paridad posible. - Informativo (medido): el azul canónico **#0052CC** sí llegó al drawer (botón Guardar y borde de foco del primer campo). | | | Desviación (propuesta) | | | 🔶 **Propuesta** — el alta se resuelve como **drawer lateral** sobre el listado en vez de la página completa del frame, preservando tabla/filtros/scroll. *Figma:* documentar el patrón drawer-sobre-lista para altas (o confirmar la página completa caso por caso). [ver desviaciones](/design/parity/intentional-deviations/) | | #### W24 · Impuestos [Section titled “W24 · Impuestos”](#w24--impuestos) **Ruta:** `/admin/pos/tax-rates` · **Figma:** [`122:3134`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-3134) · **Historia:** US-CAT-3 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W24](/design/parity/figma/screens/figma-w24-impuestos.png)](/design/parity/figma/screens/figma-w24-impuestos.png "figma W24 — abrir a tamaño completo") | [![app W24](/design/parity/screenshots/admin-pos-tax-rates.png)](/design/parity/screenshots/admin-pos-tax-rates.png "app W24 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `match`** — ✅ Coincide. Evidencia: referencia Figma `122:3134` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados).**✅ Coincide** — la pantalla fue remediada respecto a la auditoría previa; la recaptura fresca ya alinea con Figma (la nota anterior estaba obsoleta).- **Estado (bug resuelto):** los estados ya renderizan como **StatusBadge soft** (Activa verde, Inactiva gris). El bug previo de iconos check/x forzados a #1F2933 quedó corregido. - **Columnas:** ahora coinciden con el diseño — Tasa (avatar+nombre), **Tipo** (IVA/IEPS), Porcentaje, Productos, Estado. Se eliminaron las columnas ajenas (Impuesto Incluido / Fecha de creación). - **Toolbar:** presentes búsqueda inline + chips **Todas/Activas/Inactivas** + Filtros; primario “+ Nueva tasa” con icono. Icono de lápiz de edición por fila (antes ausente). - **Residuos menores:** paginación/volumen difiere por datos (app “1–6 de 6” vs Figma “1–5 de 6 … 10”); Figma incluye una columna monetaria sin rótulo (“—”, valores $) que la app omite —la app queda más limpia. El nav lateral post-#538 diverge del Figma (se registra una sola vez fuera de este lote). | | | Desviación (propuesta) | | | — | | #### Impuesto — alta [Section titled “Impuesto — alta”](#impuesto--alta) **Ruta:** `/admin/pos/tax-rates (diálogo)` · **Figma:** [`122:3134`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-3134) · **Historia:** US-CAT-3 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Impuesto — alta](/design/parity/figma/screens/figma-w24-impuestos.png)](/design/parity/figma/screens/figma-w24-impuestos.png "figma Impuesto — alta — abrir a tamaño completo") | [![app Impuesto — alta](/design/parity/screenshots/admin-pos-tax-rates--dialog-nuevo-impuesto.png)](/design/parity/screenshots/admin-pos-tax-rates--dialog-nuevo-impuesto.png "app Impuesto — alta — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla** (re-verificado).- **Panel FIGMA** (`figma-w24-impuestos.png`): pantalla de **lista** de Impuestos (header, chips Todas/Activas/Inactivas, tabla y paginación, botón “Nueva tasa”). No hay formulario de alta. - **Panel APP** (`admin-pos-tax-rates--dialog-nuevo-impuesto.png`): **drawer “Nuevo Impuesto”** sobre la lista: campos **Tasa\*** (enfocado, “Ingrese el nombre de la tasa”), **Porcentaje\***, Región, switches **Impuesto Incluido** (off) y **Estado** (on), acciones Cancelar/Guardar. - **Causa probable:** el manifest empareja el alta con el frame de la lista; falta exportar el frame Figma del formulario (si existe). - Informativo (medido): fixes sistémicos presentes — Guardar #0052CC, borde de campo enfocado #0052CC, track del switch en azul canónico. Decisión producto diálogo-vs-drawer conocida. - **Acción:** conseguir la referencia Figma del alta y re-auditar el par. | | | Desviación (propuesta) | | | 🔶 **Propuesta** — el alta se resuelve como **drawer lateral** sobre el listado en vez de la página completa del frame, preservando tabla/filtros/scroll. *Figma:* documentar el patrón drawer-sobre-lista para altas (o confirmar la página completa caso por caso). [ver desviaciones](/design/parity/intentional-deviations/) | | ### INV — Inventario [Section titled “INV — Inventario”](#inv--inventario) Flujo W-05. Historias `US-INV-1` … `US-INV-3`. #### W25 · Niveles de stock [Section titled “W25 · Niveles de stock”](#w25--niveles-de-stock) **Ruta:** `/admin/pos/stock-levels` · **Figma:** [`122:3426`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-3426) · **Historia:** US-INV-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W25](/design/parity/figma/screens/figma-w25-stock.png)](/design/parity/figma/screens/figma-w25-stock.png "figma W25 — abrir a tamaño completo") | [![app W25](/design/parity/screenshots/admin-pos-stock-levels.png)](/design/parity/screenshots/admin-pos-stock-levels.png "app W25 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `match`** — ✅ Coincide. Evidencia: referencia Figma `122:3426` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados).**✅ Coincide** — reconstrucción completa: lo que la auditoría previa marcó como esqueleto/placeholder ya está implementado y alinea con Figma.- **Estructura:** título “Niveles de stock” + subtítulo “Existencias por tienda”, sub-tabs **Niveles/Movimientos/Transferencias/Ajustar**, búsqueda, chips **Todos/Bajo stock/Agotados**, Filtros + select. Todo presente. - **Tabla:** columnas **Producto** (avatar+nombre), **SKU**, **Tienda**, **Costo**, Existencias, **Estado** — coinciden con el diseño; datos reales (Coca-Cola 600ml, SKU 750105530, Tienda Centro) en vez de mock; checkbox de selección e icono de lápiz por fila. - **Estados:** badges soft En stock (verde) / Bajo (ámbar) / Agotado (rojo). - **Componente:** aunque el ledger marca **StockLevelList** como tabla MUI cruda, la recaptura renderiza alineada al sistema (cabecera tenue, hairlines, badges soft, avatares) — sin defecto visible. El bug previo del label del Select ya estaba corregido. - **Residuo menor:** paginación/volumen por datos (app “1–5 de 5” vs Figma “1–5 de 248 … 10”). | | | Desviación (propuesta) | | | — | | #### W26 · Ajuste de inventario [Section titled “W26 · Ajuste de inventario”](#w26--ajuste-de-inventario) **Ruta:** `/admin/pos/stock-levels/adjust` · **Figma:** [`124:5630`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=124-5630) · **Historia:** US-INV-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W26](/design/parity/figma/screens/figma-w26-ajuste.png)](/design/parity/figma/screens/figma-w26-ajuste.png "figma W26 — abrir a tamaño completo") | [![app W26](/design/parity/screenshots/admin-pos-stock-levels-adjust.png)](/design/parity/screenshots/admin-pos-stock-levels-adjust.png "app W26 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `best-effort`** — ⚠️ Mejor esfuerzo — diferencias menores medidas, ninguna bloqueante. Evidencia: referencia Figma `124:5630` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados).**⚠️ Diferencias menores** — la composición fue remediada: el layout que la auditoría previa marcó como divergente ya coincide con Figma.- **Layout (resuelto):** ahora hay **H1 “Ajuste de inventario”**, botón superior **“+ Guardar”**, **grid de 2 columnas** (Producto full-width; Tienda\|Tipo, Cantidad\|Motivo) y **barra sticky inferior** con Descartar/Guardar. Antes era 1 sola columna sin header ni barra. - **Controles:** la app usa selects/dropdowns para Producto/Variante, Sucursal, Tipo y Motivo; Figma usa inputs de texto con placeholders — diferencia de control (comportamiento real de la app), no de estilo. - **Rótulos:** Producto→“Producto / Variante”, Tienda→“Sucursal”, Nota→“Notas”; subtítulo “Ajusta las existencias” vs Figma “Registra un ajuste de existencias”; card “Ajuste de inventario” vs “Ajuste”. - **Barra sticky:** falta el texto **“Tienes cambios sin guardar”** a la izquierda (Figma lo muestra); la app solo deja Descartar/Guardar. | | | Desviación (propuesta) | | | — | | #### W27 · Movimientos de inventario [Section titled “W27 · Movimientos de inventario”](#w27--movimientos-de-inventario) **Ruta:** `/admin/pos/stock-movements` · **Figma:** [`122:3717`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-3717) · **Historia:** US-INV-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W27](/design/parity/figma/screens/figma-w27-movimientos.png)](/design/parity/figma/screens/figma-w27-movimientos.png "figma W27 — abrir a tamaño completo") | [![app W27](/design/parity/screenshots/admin-pos-stock-movements.png)](/design/parity/screenshots/admin-pos-stock-movements.png "app W27 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `match`** — ✅ Coincide. Evidencia: referencia Figma `122:3717` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados).**✅ Coincide** — reconstrucción completa; la vista incompleta/divergente de la auditoría previa ya está alineada con Figma.- **Estructura:** título “Movimientos de inventario” + subtítulo “Entradas y salidas (ledger)”, búsqueda, tabs **Todos/Entradas/Salidas**, Filtros + select y acción primaria única **“+ Nuevo movimiento”** (ya no aparecen los botones de transferencia previos montados en esta ruta). - **Tabla:** columnas \[checkbox] **Producto** (avatar+nombre), **Tipo**, **Motivo**, **Cantidad ($)**, **Saldo**, **Fecha**, lápiz de edición — coinciden con el diseño. Motivo ahora es texto (Compra/Venta/Merma), ya no pill sólido. - **Fecha:** pills soft por recencia (verde/ámbar/rojo) como en Figma. - **Componente:** **StockMovementList** figura en el ledger como tabla MUI cruda, pero la recaptura renderiza alineada al sistema — sin defecto visible. - **Residuo menor:** paginación/volumen por datos (app “1–5 de 5” vs Figma “1–5 de 1,024 … 10”); conviene verificar la semántica de color de las pills de Fecha vs el diseño. | | | Desviación (propuesta) | | | — | | #### W28 · Transferencia entre tiendas [Section titled “W28 · Transferencia entre tiendas”](#w28--transferencia-entre-tiendas) **Ruta:** `/admin/pos/stock-movements/transfer` · **Figma:** [`124:5847`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=124-5847) · **Historia:** US-INV-3 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W28](/design/parity/figma/screens/figma-w28-transferencia.png)](/design/parity/figma/screens/figma-w28-transferencia.png "figma W28 — abrir a tamaño completo") | [![app W28](/design/parity/screenshots/admin-pos-stock-movements-transfer.png)](/design/parity/screenshots/admin-pos-stock-movements-transfer.png "app W28 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `best-effort`** — ⚠️ Mejor esfuerzo — diferencias menores medidas, ninguna bloqueante. Evidencia: referencia Figma `124:5847` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados).**⚠️ Diferencias menores** — re-verificado; el bug de Fecha quedó **corregido en #548**.- **Fecha (resuelto):** ✅ ahora usa el date picker localizado del DS (placeholder es ‘DD MMMM YYYY’ + icono de calendario), en vez del `` nativo con ‘mm/dd/yyyy’ y calendario del navegador. - **Controles:** selects para Sucursal de origen/destino y Producto/Variante vs inputs de texto en Figma — diferencia de control, no de estilo. - **Barra sticky:** sigue faltando el texto **‘Tienes cambios sin guardar’** que muestra Figma; la app deja solo Descartar/Guardar. - **Rótulo:** app ‘Notas’ vs Figma ‘Nota’. - **Layout:** H1 + subtítulo, botón ‘+ Guardar’ superior, grid de 2 columnas y barra sticky inferior coinciden. | | | Desviación (propuesta) | | | — | | #### Recepción de transferencia [Section titled “Recepción de transferencia”](#recepción-de-transferencia) **Ruta:** `/admin/pos/stock-movements/receive` · **Historia:** US-INV-3 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el flujo de recepción está sin diseñar (gap #16) | [![app Recepción de transferencia](/design/parity/screenshots/admin-pos-stock-movements-receive.png)](/design/parity/screenshots/admin-pos-stock-movements-receive.png "app Recepción de transferencia — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla.- **APP** (`admin-pos-stock-movements-receive.png`): tarjeta simple centrada **“Recepción de transferencia”** + subtítulo “Confirma la recepción de una transferencia entrante”, un único campo **Referencia de la transferencia\*** y botón **“Confirmar recepción”** azul sólido (visualmente #0052CC) a todo el ancho de la tarjeta. Implementación mínima de un paso. - **Siguiente paso:** exportar el nodo del flujo de recepción a `docs/design/web/` y re-correr `pnpm docs:parity`; si no existe frame, es decisión de diseño pendiente, no desviación de implementación. | | | Desviación (propuesta) | | | — | | ### SHIFT — Turnos y caja [Section titled “SHIFT — Turnos y caja”](#shift--turnos-y-caja) Flujo W-07. Historias `US-SHIFT-1` … `US-SHIFT-5`. #### W32 · Turnos [Section titled “W32 · Turnos”](#w32--turnos) **Ruta:** `/admin/pos/shifts` · **Figma:** [`119:2117`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=119-2117) · **Historia:** US-SHIFT-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W32](/design/parity/figma/screens/figma-w32-turnos.png)](/design/parity/figma/screens/figma-w32-turnos.png "figma W32 — abrir a tamaño completo") | [![app W32](/design/parity/screenshots/admin-pos-shifts.png)](/design/parity/screenshots/admin-pos-shifts.png "app W32 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `best-effort`** — ⚠️ Mejor esfuerzo — diferencias menores medidas, ninguna bloqueante. Evidencia: referencia Figma `119:2117` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados). **Decisión columna “Dif.” (AC8):** el frame **sí** aplica tinte semántico — la varianza negativa (`−$20` en el frame) se dibuja como **píldora tintada**, el resto queda texto plano. La app renderizaba todo en gris (incluido el faltante `−$150.00`), perdiendo ese realce → **se restauró** el `StatusBadge` en la lista reutilizando `varianceStatusVariant` (`src/components/PosShifts/ShiftList.tsx`), la misma regla que ya usa el detalle W33: `success` → texto muted, `danger`/`warning` → píldora soft. La captura fresca lo muestra (`−$150.00` en badge danger). **Residuo menor:** el frame tiñe el faltante en ámbar mientras la app mapea negativo→`danger` (rojo) vía el helper compartido (fuente única de la semántica lista↔detalle); diferencia de matiz registrada en `intentional-deviations.md`. Test: `ShiftList.test.tsx` (tinte del faltante / texto plano del cuadrado).**⚠️ Diferencias menores**La pantalla se rehízo (post-#538) y ahora replica el diseño casi 1:1; la nota previa (tabla genérica de turnos por empleado) quedó **obsoleta**.- **Estructura:** ya presentes subtítulo “Historial de cortes de caja”, tabs (Turnos / Mov. efectivo / Cerrar turno), CTA azul **Abrir turno**, buscador, chips Todos/Abiertos/Cerrados y **Filtros** + select “Selecciona una opción”. Columnas alineadas al Figma: Turno (avatar+#) · Cajero · Apertura · Vendido · Dif. · Estado, con checkbox de selección e icono de editar por fila. - **Paginación:** ahora numerada custom (“Mostrando 1–5 de 7 · 1 2 ›”), coincide con el patrón Figma (ya no es TablePagination de MUI). - **Estados:** badge “Abierto” en verde soft y “Cerrado” en rojo soft, alineados al design system (se corrigió el azul sólido `#3B82F6` anterior). - **Residuo — chips de filtro:** Todos/Abiertos/Cerrados se renderiza como control segmentado (contenedor gris, píldora blanca activa) en vez de las píldoras sueltas del Figma (Todos en azul-soft). Mismo patrón en W36/W37. - **Residuo — select:** hereda del residuo de \[Select] (sin tinte de opción seleccionada, hairline en estado abierto). - **Datos:** columna Dif. en formato moneda ($0.00 / −$150.00) vs enteros del Figma; Apertura con fecha absoluta (“29 jun 2026”) vs relativa (“Hoy 08:00”). Solo datos. - **Nav / topbar (sistémico, registrado aparte):** la sidebar admin (grupo ADMINISTRACIÓN reducido a Comercios/Notificaciones) y el topbar (sin cluster de avatares JP MA LR, store “Tienda demo”) divergen del Figma por el cambio de nav-IA de #538; no se re-reporta en cada pantalla admin. | | | Desviación (propuesta) | | | — | | #### W33 · Detalle de turno / Corte Z [Section titled “W33 · Detalle de turno / Corte Z”](#w33--detalle-de-turno--corte-z) **Ruta:** `— (ver W58)` · **Figma:** [`132:9833`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=132-9833) · **Historia:** US-SHIFT-5 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W33](/design/parity/figma/screens/figma-w33-detalle-z.png)](/design/parity/figma/screens/figma-w33-detalle-z.png "figma W33 — abrir a tamaño completo") | [![app W33](/design/parity/screenshots/admin-pos-shifts--dialog-detalle.png)](/design/parity/screenshots/admin-pos-shifts--dialog-detalle.png "app W33 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**Re-verificado con la captura fresca (mismatch conocido).- **FIGMA:** página completa **“Turno #88 · Corte Z”** de un turno **cerrado** — tarjeta Resumen (Cajero Sofía Ramírez, Apertura Hoy 08:00, Cierre Hoy 16:00, Fondo inicial $1,500, Ventas efectivo $4,200, Retiros −$2,000, Efectivo esperado $3,700, Diferencia $0.00 en azul) y botón outline **“Reimprimir corte”**. Reporte Z ya emitido. - **APP:** **diálogo** de detalle de un turno **abierto** (Ana Gómez · Sucursal Centro·term-01, badge **“Abierto”**, Cierre “Pendiente de cierre”, Conciliación de Efectivo con Esperado/Contado en “—” y Diferencia “Pendiente de cierre”, Movimientos “Sin movimientos de efectivo”, Notas “Turno matutino en curso”). - **Desajuste de flujo:** turno de la app sigue abierto (no hay Corte Z que renderizar) y es diálogo, no página. Para auditar paridad hay que recapturar la vista de Corte Z de un turno cerrado (ver W58). No se listan diferencias de estilo por ser vistas distintas. | | | Desviación (propuesta) | | | — | | #### W34 · Abrir turno [Section titled “W34 · Abrir turno”](#w34--abrir-turno) **Ruta:** `/terminal/shift/open` · **Figma:** [`132:9488`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=132-9488) · **Historia:** US-SHIFT-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W34](/design/parity/figma/screens/figma-w34-abrir-turno.png)](/design/parity/figma/screens/figma-w34-abrir-turno.png "figma W34 — abrir a tamaño completo") | [![app W34](/design/parity/screenshots/terminal-shift-open.png)](/design/parity/screenshots/terminal-shift-open.png "app W34 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Misma pantalla (Abrir turno con keypad de fondo de caja). El Figma la maqueta como tarjeta dentro del chrome admin; la app la renderiza como página fullscreen de terminal (con “Bloquear ahora” y “Cancelar”) — decisión sistémica página-vs-tarjeta, pendiente de producto.- **Contenido:** la app **omite el bloque de identidad** Terminal `POS-01` / Cajero `Sofía R.` que el Figma coloca bajo el título; en su lugar muestra input “Fondo de caja”, monto $0.00, enlace “Cancelar” y pie “Gerente Demo”. - **Estados:** “Abrir turno” deshabilitado en gris (bg #E5E7EB, texto #9CA3AF) por monto $0.00; en Figma aparece azul activo por tener $1,500.00 — diferencia de estado esperada. El azul de “Cancelar”/“Bloquear ahora”/foco del input lee consistente con el canónico #0052CC. - **Tipografía / bordes:** jerarquía correcta (título oscuro `#1F2933` / subtítulo “Cuenta el fondo de caja inicial” gris) y hairlines #E5E7EB en input y teclas — fixes aplicados. - **Iconos:** teclas del keypad en cajas outlined redondeadas; en Figma los dígitos van planos (solo backspace enmarcado) — residuo menor del KeypadKey. - **Layout:** columna angosta centrada con mucho whitespace lateral, propio de la página fullscreen. | | | Desviación (propuesta) | | | — | | #### W35 · Cerrar turno [Section titled “W35 · Cerrar turno”](#w35--cerrar-turno) **Ruta:** `/terminal/shift/close` · **Figma:** [`132:9670`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=132-9670) · **Historia:** US-SHIFT-3 / US-SHIFT-4 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W35](/design/parity/figma/screens/figma-w35-cerrar-turno.png)](/design/parity/figma/screens/figma-w35-cerrar-turno.png "figma W35 — abrir a tamaño completo") | [![app W35](/design/parity/screenshots/terminal-shift-close.png)](/design/parity/screenshots/terminal-shift-close.png "app W35 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**Mismatch conocido, re-verificado.- **FIGMA:** resumen **“Cerrar turno · Corte Z”** dentro del shell admin — tarjeta “Conteo de efectivo” (esperado $5,800.00 / contado $5,780.00 / Diferencia −$20.00), tarjeta “Ventas por método” (Efectivo, Tarjeta, Saldo, Total vendido $8,250.00) y acciones “Imprimir corte (Z)” (outline) + “Cerrar turno” (primario). Es la vista de resumen/corte Z ya calculado. - **APP:** pantalla de **terminal** “Cerrar turno” / “Cuenta el efectivo por denominación”: desglose por denominación ($1,000→$0.50) con inputs en 0, Total contado $0.00, Efectivo esperado $2,300.00, Diferencia −$2,300.00, campo Notas y botón deshabilitado. Es el paso de captura por denominación, no el resumen — ese contenido corresponde a W65. - **⚠️ Captura OBSOLETA (pre-#538):** el harness no pudo refrescar este shot (testid `nav-close-shift` eliminado) — targets.ts necesita actualización de nav-testid; re-mapear W35 al resumen/corte Z correcto antes de auditar estilo. | | | Desviación (propuesta) | | | — | | #### W36 · Movimientos de caja (admin) [Section titled “W36 · Movimientos de caja (admin)”](#w36--movimientos-de-caja-admin) **Ruta:** `/admin/pos/cash-movements` · **Figma:** [`123:5405`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-5405) · **Historia:** US-SHIFT-2 / US-BAL-3 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W36](/design/parity/figma/screens/figma-w36-movimientos-caja.png)](/design/parity/figma/screens/figma-w36-movimientos-caja.png "figma W36 — abrir a tamaño completo") | [![app W36](/design/parity/screenshots/admin-pos-cash-movements.png)](/design/parity/screenshots/admin-pos-cash-movements.png "app W36 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Pantalla reconstruida (post-#538); la nota previa (falta CTA, toolbar recortada, columnas divergentes) quedó **obsoleta** — ahora coincide casi 1:1.- **Estructura:** ya presente el CTA azul relleno **+ Nuevo movimiento**; toolbar con buscador, control Todos/Entradas/Salidas y **Filtros**. Columnas alineadas al Figma: checkbox · Movimiento (avatar+nombre) · Cajero · Motivo · Monto · Autoriza · Estado · editar. - **Estados:** StatusBadge de Estado presente (Registrado verde-soft / Autorizado ámbar-soft), coincide con el diseño. - **Paginación:** numerada custom (“Mostrando 1–5 de 7 · 1 2 ›”). - **Residuo — select:** falta el select “Selecciona una opción” junto a Filtros que sí trae el Figma. - **Residuo — chips:** control segmentado vs píldoras sueltas del Figma (igual que W32). - **Color / datos:** montos coloreados verde/rojo (+$1,500.00 / −$2,000.00) — mejora semántica frente a los montos neutros del Figma; formato con decimales “.00” vs enteros del Figma. Solo criterio/datos. | | | Desviación (propuesta) | | | — | | #### W36b · Movimiento de caja (terminal) [Section titled “W36b · Movimiento de caja (terminal)”](#w36b--movimiento-de-caja-terminal) **Ruta:** `/terminal/shift/cash-movement` · **Figma:** [`123:5405`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-5405) · **Historia:** US-SHIFT-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W36b](/design/parity/figma/screens/figma-w36-movimientos-caja.png)](/design/parity/figma/screens/figma-w36-movimientos-caja.png "figma W36b — abrir a tamaño completo") | [![app W36b](/design/parity/screenshots/terminal-shift-cash-movement.png)](/design/parity/screenshots/terminal-shift-cash-movement.png "app W36b — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**Mismatch conocido, re-verificado.- **FIGMA:** página **admin** “Movimientos de caja” (la misma de W36; se reutilizó por falta de diseño del formulario de terminal): sidebar, tabla Movimiento · Cajero · Motivo · Monto · Autoriza · Estado y paginación. - **APP:** **formulario de terminal** “Movimiento de efectivo” / “Registra una entrada o salida de efectivo”: control segmentado Entrada/Salida/Depósito/Retiro, campo **Monto** ($0.00), teclado numérico, textarea “Motivo” (0/240), botón “Registrar” (deshabilitado) y enlace “Cancelar”. - **⚠️ Captura OBSOLETA (pre-#538):** shot no refrescado (deriva de nav de terminal) — targets.ts necesita actualización. Además no existe Figma del formulario de terminal: conseguir/generar el diseño de referencia o reasignar la captura antes de auditar color/tipografía/espaciado. | | | Desviación (propuesta) | | | — | | #### W65 · Cerrar turno — desglose por denominación [Section titled “W65 · Cerrar turno — desglose por denominación”](#w65--cerrar-turno--desglose-por-denominación) **Ruta:** `/admin/pos/shifts/close?shiftId=…` · **Figma:** [`574:14732`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=574-14732) · **Historia:** US-SHIFT-3 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W65](/design/parity/figma/screens/figma-w65-cerrar-denominacion.png)](/design/parity/figma/screens/figma-w65-cerrar-denominacion.png "figma W65 — abrir a tamaño completo") | [![app W65](/design/parity/screenshots/admin-pos-shifts-close.png)](/design/parity/screenshots/admin-pos-shifts-close.png "app W65 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Misma pantalla; el **fix principal llegó** y los dos residuos previos están resueltos: el desglose ahora va en **dos columnas** compactas (antes una sola columna full-width) y el KPI “Efectivo esperado” se pinta en **azul de realce**.- **KPIs:** tres tarjetas Ventas brutas $12,450.00 / Efectivo esperado $4,500.00 (azul) / Pagos con tarjeta $7,950.00 coinciden con el Figma. - **Totalizador:** Total contado, Efectivo esperado y Diferencia (faltante), junto con el botón “Cerrar turno (Z-Report)”, ya son visibles; no quedan bajo el fold. - **Residuo — color de diferencia:** en el Figma la “Diferencia (faltante)” va en **rojo** (−$15.00); en la app (con conteo en cero) el faltante $4,500.00 se muestra en texto oscuro — verificar que el faltante aplique el rojo de error con datos reales. - **Formato / datos:** etiquetas de denominación con decimales “$1,000.00” vs “$1,000” del Figma; conteos prellenados del Figma vs ceros en la app son solo datos. - **Título:** app con título + subtítulo (“Cuenta el efectivo por denominación”) vs título unificado del Figma — reordenamiento menor, aceptable. | | | Desviación (propuesta) | | | — | | ### STAFF — Personal y control de acceso [Section titled “STAFF — Personal y control de acceso”](#staff--personal-y-control-de-acceso) Flujo W-08. Historias `US-STAFF-1` … `US-STAFF-4`. #### W37 · Empleados [Section titled “W37 · Empleados”](#w37--empleados) **Ruta:** `/admin/pos/employees` · **Figma:** [`119:2409`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=119-2409) · **Historia:** US-STAFF-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W37](/design/parity/figma/screens/figma-w37-empleados.png)](/design/parity/figma/screens/figma-w37-empleados.png "figma W37 — abrir a tamaño completo") | [![app W37](/design/parity/screenshots/admin-pos-employees.png)](/design/parity/screenshots/admin-pos-employees.png "app W37 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `best-effort`** — ⚠️ Mejor esfuerzo — diferencias menores medidas, ninguna bloqueante. Evidencia: referencia Figma `119:2409` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados).**⚠️ Diferencias menores** — la migración a DataTable ya aterrizó y el residuo de IDs crudos quedó resuelto; solo resta la terminología del nombre de tienda.- **Encabezado:** ✅ Coincide — título **Personal** + contador **‘5 empleados · 3 roles’**. - **CTA:** ✅ Coincide — **‘Invitar usuario’** con icono **+** en el encabezado. - **Toolbar:** ✅ Coincide — buscador, segmentado **Todos/Activos/Invitados**, botón **Filtros** y select **‘Selecciona una opción’**. - **Columnas:** ✅ Coincide — **Empleado (avatar iniciales) · Rol · Tienda · Ventas · Turnos · Estado**. - **Filas:** ✅ Coincide — checkbox de selección + acción **editar (lápiz)** por fila. - **Estado:** ✅ Coincide — badge de tinte suave 3-estados (Activo/Invitado/Inactivo). - **Tienda:** ⚠️ ya muestra un **nombre legible** (corregido: sin ‘store-1/store-2’), pero usa el prefijo **‘Sucursal …’** (Sucursal Centro/Norte) en lugar de **‘Tienda …’** como en Figma. | | | Desviación (propuesta) | | | — | | #### W38 · Empleado crear / editar [Section titled “W38 · Empleado crear / editar”](#w38--empleado-crear--editar) **Ruta:** `/admin/pos/employees (formulario)` · **Figma:** [`124:6018`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=124-6018) · **Historia:** US-STAFF-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W38](/design/parity/figma/screens/figma-w38-empleado-edit.png)](/design/parity/figma/screens/figma-w38-empleado-edit.png "figma W38 — abrir a tamaño completo") | [![app W38](/design/parity/screenshots/admin-pos-employees--dialog-nuevo-empleado.png)](/design/parity/screenshots/admin-pos-employees--dialog-nuevo-empleado.png "app W38 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `blocked-report`** — ⛔ Bloqueado — el par actual no compara la misma pantalla/estado. Evidencia: referencia Figma `124:6018` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados). **Bloqueo:** la captura pareada es el *drawer* de alta rápida de la lista (`admin-pos-employees--dialog-nuevo-empleado`), no la **página** completa `/admin/pos/employees/new` que diseña el frame; existe una captura de página (`admin-pos-employees-new.png`) pero re-aparearla exige tocar la declaración del generador (`scripts/build-design-parity-doc.mjs`, fuera del alcance de este ticket). Seguimiento en `todos.tasks`.**❌ Las imágenes no corresponden a la misma pantalla.** (Desajuste conocido W38.)- **Panel FIGMA:** página completa de edición. Header “Empleado / Datos, rol y acceso” con botón *Guardar* superior; dos tarjetas planas — **Datos personales** (Nombre completo, Correo, Teléfono) y **Acceso** (Rol, Tienda, PIN, Estado) — y una *sticky save bar* inferior (“Tienes cambios sin guardar” · Descartar · Guardar). - **Panel APP:** no es esa página, sino un **drawer lateral derecho** montado sobre la lista de Empleados (se ven Filtros y las filas Ana López, Bruno Martínez… detrás). Campos: **Sucursal, Usuario, Rol, PIN** (todos con `*` requerido) más un switch **Activo**, con acciones *Cancelar / Guardar* al pie. - **Desajuste de composición Y contenido:** página vs. drawer, y además distinto set de campos — la app no tiene Nombre completo/Correo/Teléfono/Tienda/Estado, y añade Sucursal, Usuario (select) y el toggle Activo que la página Figma no muestra. - **Tokens (medidos en el drawer, sí llegaron):** azul de marca **#0052CC** en *Guardar*; switch activo azul; *Cancelar* como botón secundario blanco con borde 1px. Azul canónico correcto. - **Acción:** recapturar la **página** `/empleados/nuevo` (o `/[id]/editar`) que corresponde a este diseño, no el drawer de alta rápida de la lista. Sin esa captura no puede auditarse la paridad real de W38. | | | Desviación (propuesta) | | | 🔶 **Propuesta** — el alta se resuelve como **drawer lateral** sobre el listado en vez de la página completa del frame, preservando tabla/filtros/scroll. *Figma:* documentar el patrón drawer-sobre-lista para altas (o confirmar la página completa caso por caso). [ver desviaciones](/design/parity/intentional-deviations/) | | #### W39 · Invitar usuario [Section titled “W39 · Invitar usuario”](#w39--invitar-usuario) **Ruta:** `/admin/users` · **Figma:** [`131:8930`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=131-8930) · **Historia:** US-STAFF-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W39](/design/parity/figma/screens/figma-w39-invitar.png)](/design/parity/figma/screens/figma-w39-invitar.png "figma W39 — abrir a tamaño completo") | [![app W39](/design/parity/screenshots/admin-users.png)](/design/parity/screenshots/admin-users.png "app W39 — abrir a tamaño completo") | | Notas | | | **✅ Paridad (FD-7 · #926)** — 👍 la pantalla se rehízo y coincide con el diseño en lo esencial; los residuos que quedan son **heredados** (Search Field blanco+hairline) o divergencias de IA de navegación con Figma desactualizado (🎨), no bloqueantes y rastreados aparte. Comparo la página lista; el diálogo y sus estados se auditan en W39b.- **Color:** OK. Azul canónico #0052CC en *Invitar usuario* y *Filtros*; header de tabla #F7F9FC; status badges soft (Activo verde, Invitado ámbar, Inactivo rojo). - **Layout — corregido:** *Invitar usuario* es ahora un **botón compacto en el header superior-derecho** (antes banner full-width). Ya no compiten varios primarios rellenos: *Filtros* es outline. - **Contenido — corregido:** aparecen el **page header** (“Personal” + subtítulo “5 empleados · 4 roles”), los **filter chips** Todos/Activos/Invitados (segmented), y los **avatares con iniciales** (AL, BM, CN…) en la celda Empleado. - **Estados — corregido:** la columna Estado usa **StatusBadge soft** (Activo/Invitado/Inactivo), no los círculos negros anteriores. - **Columnas:** ahora Empleado/Rol/Tienda/Ventas/Turnos/Estado, alineadas con Figma. La sección “Invitaciones pendientes” es un añadido funcional válido no presente en Figma. - **Residuos menores:** campo *Buscar* blanco+hairline vs. soft-fill del diseño → hereda del residuo de \[Search Field]; el select derecho trunca a “Selecciona una …”. - **Nav lateral (registro único para todo el shell POS):** el grupo **ADMINISTRACIÓN** de la app muestra solo **Comercios / Notificaciones**, mientras el Figma lista **Comercio y tiendas / Terminales / Ajustes / Notificaciones**. Divergencia de IA de navegación (probable reorganización post-#538); Figma parece desactualizado (🎨). No se repite en las demás pantallas admin de este lote. | | | Desviación (propuesta) | | | — | | #### W39b · Invitar usuario — diálogo [Section titled “W39b · Invitar usuario — diálogo”](#w39b--invitar-usuario--diálogo) **Ruta:** `/admin/users (diálogo)` · **Figma:** [`131:8930`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=131-8930) · **Historia:** US-STAFF-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W39b](/design/parity/figma/screens/figma-w39-invitar.png)](/design/parity/figma/screens/figma-w39-invitar.png "figma W39b — abrir a tamaño completo") | [![app W39b](/design/parity/screenshots/admin-users--dialog-invitar.png)](/design/parity/screenshots/admin-users--dialog-invitar.png "app W39b — abrir a tamaño completo") | | Notas | | | **✅ Paridad (FD-7 · #926)** — el diálogo compone el `Dialog` DS + tokens y ahora construye **todos los estados diseñados** del frame W39: *idle/vacío*, *cargando/enviando*, *éxito* (invitación enviada), y los frames de **error** (correo duplicado, correo inválido en línea, error de red). Cada estado usa la copia es-MX verbatim de `user.i18n` y no expone marcas de proveedor. El showcase `PersonalInvitarScreenShowcase` refleja los seis frames lado a lado; specs `InviteUserDialog.test` + `PersonalScreensShowcase.test` en verde.- **Color:** OK. Borde de campos #E5E7EB, botón deshabilitado #E5E7EB / texto #9CA3AF, subtítulo #6B7280, título \~#1F2933. Scrim plano. - **Bordes/Sombras:** campos con hairline y radio suave; diálogo con esquinas redondeadas; sin halo de sombra dura. - **Estados — construidos:** además del formulario *idle*, el diálogo renderiza el panel *cargando* (spinner + “Enviando invitación…”), *éxito* (“Invitación enviada” + “Invitar a otra persona”/“Listo”), y error (correo ya con acceso · genérico/red · inválido en línea). *Enviar invitación* se deshabilita mientras el formulario está incompleto o en envío. - **Layout (residuo de plantilla de diálogo):** título **centrado** (Figma a la izquierda), **sin botón X** de cierre, y acciones como **dos botones a todo el ancho** en lugar de compactas a la derecha. Más aire vertical que el diálogo compacto de Figma → hereda del residuo de \[Dialog / Modal]. - **Contenido:** la app muestra un **subtítulo** (“Se enviará un correo…”) en vez del “Texto de ayuda” bajo cada campo; el campo **Rol** es un **select con `*` requerido** vs. campo de texto “Cajero” en Figma. Decisiones de producto.Recomendación: alinear la plantilla del diálogo (título a la izquierda + X, acciones compactas a la derecha). | | | Desviación (propuesta) | | | — | | #### W40 · Roles y permisos [Section titled “W40 · Roles y permisos”](#w40--roles-y-permisos) **Ruta:** `/admin/pos/employees/roles` · **Figma:** [`130:8533`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=130-8533) · **Historia:** US-STAFF-3 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W40](/design/parity/figma/screens/figma-w40-roles.png)](/design/parity/figma/screens/figma-w40-roles.png "figma W40 — abrir a tamaño completo") | [![app W40](/design/parity/screenshots/admin-pos-employees-roles.png)](/design/parity/screenshots/admin-pos-employees-roles.png "app W40 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (FD-7 · #926): ✅** — la matriz de solo lectura compone el `DataTable` DS + `PageTemplate` + tokens de tema, con las columnas rol (Cajero · Supervisor · Gerente), el check neutro de concesión, el guion de denegación y los chips de **Ámbito** (Tienda/Comercio) — todos derivados de `src/components/PosRoles/posRoles.ts` (concesiones acumulativas, verificadas por `posRoles.spec`). Permanece **read-only** y alcanzable W37→W40 vía `PersonalTabs`. Evidencia: referencia Figma `130:8533` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados).**✅ Paridad** — 👍 mejora importante frente a la nota previa; varios faltantes ya resueltos.- **Color:** OK. Header #1F2933, subtítulo gris, header de tabla #F7F9FC, divisores #E5E7EB, botón *Ver documentación* azul #0052CC. Tab activo underline #0052CC. - **Contenido — corregido:** ahora existe la columna **Ámbito** con chips soft (Tienda verde, Comercio rojo), presente el **Alert informativo** “Matriz de solo lectura” completo (ya no reducido a subtítulo), y la **paginación** (“14 permisos · 3 roles”, 1 2 3). - **Iconos/Estados — corregido:** los checks son ahora **neutros oscuros ✓** (antes verdes #32AE60), coincidiendo con el criterio del Figma; guion “—” para deshabilitado. - **Residuo de toolbar:** falta el botón **Filtros** y el select “Selecciona una opción” que Figma coloca a la derecha; la app deja solo Buscar + chips Todos/Cajero/Supervisor/Gerente. - **Layout:** la app añade **tabs Empleados / Roles y permisos** que el Figma no plantea; filas de permisos con distinto contenido (Ver catálogo, Editar catálogo, Ver inventario… vs Registrar venta, Aplicar descuento…) — decisión de datos, no de estilo. | | | Desviación (propuesta) | | | — | | ### REP — Reportes [Section titled “REP — Reportes”](#rep--reportes) Flujo W-11. Historias `US-REP-1` … `US-REP-6`. #### W55 · Reportes (overview) [Section titled “W55 · Reportes (overview)”](#w55--reportes-overview) **Ruta:** `/admin/pos/reports` · **Figma:** [`125:6381`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=125-6381) · **Historia:** US-REP-1 / US-REP-6 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W55](/design/parity/figma/screens/figma-w55-reportes-overview.png)](/design/parity/figma/screens/figma-w55-reportes-overview.png "figma W55 — abrir a tamaño completo") | [![app W55](/design/parity/screenshots/admin-pos-reports.png)](/design/parity/screenshots/admin-pos-reports.png "app W55 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `best-effort`** — ⚠️ Mejor esfuerzo — diferencias menores medidas, ninguna bloqueante. Evidencia: referencia Figma `125:6381` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados).**⚠️ Diferencias menores** — misma pantalla (Reportes → Resumen). Los fixes sistémicos llegaron.- **Color:** OK. Barras y glifo del icono KPI en #0052CC; tint de tab activa ≈#E6EEFA; deltas en verde. Cards planas con hairline #E5E7EB, sin sombra. - **Layout (culpable — control de rango):** el rango de fechas usa **`` nativos** (“08/01/2026 → 08/07/2026”, fuente del sistema + glifo de calendario del navegador) en lugar del dropdown único estilizado “1 Oct – 24 Oct, 2025” del diseño → rompe la paridad tipográfica → hereda del residuo de \[Date-range Picker]. - **Contenido (KPIs):** el set difiere — app: *Venta bruta / Venta neta / Transacciones / Ticket promedio*; Figma: *Ventas del periodo / Transacciones / Ticket promedio / Comisiones*. Falta la card **Comisiones** (config de reporte). - **Contenido (delta):** Figma muestra el % real (“▲ 8% vs periodo”); la app muestra el placeholder “▲ Periodo seleccionado” sin porcentaje en las 4 cards. El chevron del delta es fino → hereda del residuo de \[KPI Stat Card] (Figma usa ▲/▼ macizos). - **Gráficos:** “Ventas por hora” (línea/área azul) y “Por método de pago” (barras azules) coinciden; esquinas de barra rectas → hereda del residuo de \[Chart — Bar]. Tabla “Top productos” con columnas Producto/SKU/Categoría/Ingresos/Unidades/Tendencia alineadas. | | | Desviación (propuesta) | | | — | | #### W56 · Reporte de ventas [Section titled “W56 · Reporte de ventas”](#w56--reporte-de-ventas) **Ruta:** `/admin/pos/reports/daily-sales` · **Figma:** [`130:7959`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=130-7959) · **Historia:** US-REP-1 … US-REP-4 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W56](/design/parity/figma/screens/figma-w56-reporte-ventas.png)](/design/parity/figma/screens/figma-w56-reporte-ventas.png "figma W56 — abrir a tamaño completo") | [![app W56](/design/parity/screenshots/admin-pos-reports-daily-sales.png)](/design/parity/screenshots/admin-pos-reports-daily-sales.png "app W56 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `blocked-report`** — ⛔ Bloqueado — el par actual no compara la misma pantalla/estado. Evidencia: referencia Figma `130:7959` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados). **Bloqueo:** la captura pareada apunta a `daily-sales` (dashboard “Ventas del Día”), no al desglose por producto con tabs (Por producto/categoría/empleado/método) y Exportar que diseña el frame W56; re-aparear la ruta correcta exige tocar la declaración del generador (fuera de alcance). Seguimiento en `todos.tasks`.**❌ Las imágenes no corresponden a la misma pantalla.** (Desajuste conocido W56; ruta `daily-sales` tocada por #538 — revisada, sigue siendo otro reporte.)- **FIGMA (diseño):** “Reporte de ventas · Desglose del periodo” — tabla de líneas de producto con tabs (Por producto / Por categoría / Por empleado / Por método), columnas Producto · SKU · Categoría · Ingresos · Unidades · Tendencia, selección por checkbox, botón **Exportar** y paginación “Mostrando 1–5 de 248”. - **APP (implementación):** “Ventas del Día · Resumen diario del punto de venta” — otro reporte: filtros **Fecha** (06/08/2026) / **Sucursal** (Centro), tarjetas KPI (Venta Bruta $16,826.22 · Venta Neta $13,961.95 · Transacciones 160 · Ticket Promedio $87.26), gráfico de barras **Ventas por Hora** y sección **Productos Más Vendidos**. - **Ruta a corregir:** la captura apunta al dashboard diario, no al desglose de ventas por producto que diseña W56. Recapturar la ruta correcta (tabla con tabs y Exportar) antes de auditar paridad de estilos. - No se listan diferencias de color/tipografía porque no hay pantalla equivalente que comparar. | | | Desviación (propuesta) | | | — | | #### W57 · Comisiones [Section titled “W57 · Comisiones”](#w57--comisiones) **Ruta:** `— (sin ruta)` · **Figma:** [`130:8242`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=130-8242) · **Historia:** US-REP-5 / US-VAS-6 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W57](/design/parity/figma/screens/figma-w57-comisiones.png)](/design/parity/figma/screens/figma-w57-comisiones.png "figma W57 — abrir a tamaño completo") | [![app W57](/design/parity/screenshots/playground/w57-comisiones.png)](/design/parity/screenshots/playground/w57-comisiones.png "app W57 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `best-effort`** — ⚠️ Mejor esfuerzo — diferencias menores medidas, ninguna bloqueante. Evidencia: referencia Figma `130:8242` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados).**⚠️ Diferencias menores** — 👍 el hallazgo principal previo (chip *Todas* como botón sólido azul) está corregido.- **Color:** OK. Azul #0052CC en *Exportar*, checkbox de fila y select-all, tinte de avatar con inicial primary. Chips de estado soft reales (Activa verde #ECFDF5, ámbar, rojo) que coinciden con Figma. - **Filtros — corregido:** *Todas / Servicios / Tarjeta* es ahora un **segmented control** (Todas como segmento blanco elevado sobre contenedor gris; Servicios/Tarjeta en texto gris), en vez del botón sólido azul anterior. Figma usa tint #E6EEFA + texto azul para el activo y pills grises para el resto → diferencia menor de tono, ya no de patrón. - **Estados:** el checkbox de cabecera muestra **indeterminado** (correcto por la selección parcial de 1 fila); Figma lo dibuja vacío — la app es más correcta. - **Bordes:** header de tabla #F7F9FC, fila seleccionada azul muy claro, divisores hairline #E5E7EB — alineados. - **Residuos menores:** avatares de **una sola letra** (C/S/P/L/A) vs. dos letras (CO/SA/PA…) en Figma; el select derecho trunca a “Selecciona una …”; los botones de paginación van con **borde azul** (1 relleno, 2/3/10 outline) vs. números planos en Figma. - **Layout:** captura solo-contenido (sin sidebar/AppBar) — diferencia de captura conocida, no de pantalla. 5 filas, mismos importes y “Mostrando 1-5 de 18”. | | | Desviación (propuesta) | | | — | | #### W58 · Cortes de caja (Z) [Section titled “W58 · Cortes de caja (Z)”](#w58--cortes-de-caja-z) **Ruta:** `/admin/pos/reports/z-report` · **Figma:** [`123:4823`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-4823) · **Historia:** US-SHIFT-5 / US-REP-6 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W58](/design/parity/figma/screens/figma-w58-cortes-z.png)](/design/parity/figma/screens/figma-w58-cortes-z.png "figma W58 — abrir a tamaño completo") | [![app W58](/design/parity/screenshots/admin-pos-reports-z-report.png)](/design/parity/screenshots/admin-pos-reports-z-report.png "app W58 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.** (Desajuste conocido W58.)- **Panel FIGMA:** *Cortes de caja (Z) · Histórico de cortes Z* — **lista/tabla** con columnas Corte · Cajero · Fecha · Vendido · Diferencia · Estado, chips de filtro (Todos / Cuadrados / Con diferencia), buscador, botón **Filtros**, botón **Exportar**, fila seleccionada con checkbox, StatusBadges (Cuadrado/Diferencia), avatares con iniciales y paginación “Mostrando 1–5 de 96”. - **Panel APP:** vista de **detalle de un único corte Z** — selector *Turno 2 · cerrado 28 jun 2026*, botón **Imprimir**, y una tarjeta con el desglose: **Ventas** (Venta Bruta $24,125.00 / Neta $20,243.39 / Impuestos / Descuentos), **Formas de Pago**, **Cancelaciones** y **Conciliación de Efectivo** (Diferencia −$20.25). No hay tabla ni listado. - **Desajuste:** la captura corresponde al reporte individual que se abre *desde* la lista, no al histórico que diseña el Figma. Recapturar la ruta de la lista de cortes Z para poder auditar paridad. - No se listan diferencias de color/tipografía porque no es la misma pantalla. | | | Desviación (propuesta) | | | — | | #### Reportes (ruta legacy) [Section titled “Reportes (ruta legacy)”](#reportes-ruta-legacy) **Ruta:** `/admin/reports` · **Historia:** US-REP-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — ruta legacy duplicada de W55; decidir si se retira | [![app Reportes (ruta legacy)](/design/parity/screenshots/admin-reports.png)](/design/parity/screenshots/admin-reports.png "app Reportes (ruta legacy) — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada (dashboard de Reportes con KPIs, tarjetas de tipos de reporte y panel “Reportes Detallados”) pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | | Desviación (propuesta) | | | — | | ### VAS — Servicios de valor agregado [Section titled “VAS — Servicios de valor agregado”](#vas--servicios-de-valor-agregado) Flujo W-09. Historias `US-VAS-1` … `US-VAS-6`. #### W41 · Pago de servicios [Section titled “W41 · Pago de servicios”](#w41--pago-de-servicios) **Ruta:** `/admin/service-payments` · **Figma:** [`127:6816`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=127-6816) · **Historia:** US-VAS-3 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W41](/design/parity/figma/screens/figma-w41-servicios.png)](/design/parity/figma/screens/figma-w41-servicios.png "figma W41 — abrir a tamaño completo") | [![app W41](/design/parity/screenshots/admin-service-payments.png)](/design/parity/screenshots/admin-service-payments.png "app W41 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes** — la captura fresca **no muestra la unificación de CTA de #548**; el botón sigue divergiendo y persisten las brechas estructurales.- **CTA:** app ‘Continuar’ (deshabilitado) vs Figma ‘Agregar a la venta’ — la copia no coincide visualmente. - **Layout:** sigue faltando por completo la **barra de pestañas** del diseño (Servicios / Tiempo aire / Pines / Transferir / Depósitos / Wallet); la app va directa al contenido. - **Contenido:** la tarjeta izquierda mantiene otro paradigma: app = **grid de proveedores** (CFE, JMAS Juárez, Telmex, Gas Natural) con encabezado ‘Selecciona el proveedor’; Figma = **chips de Categoría** (Luz/Agua/Gas/Internet/Teléfono/TV, Luz seleccionada) + fila de Proveedor único (CFE). Es la mayor divergencia visible. - **Estados:** app en estado vacío (Importe ‘Consultar adeudo’, Total $0.00, Continuar disabled) vs Figma lleno ($688.00, botón activo). - **Título:** app ‘Pago de Servicios’ vs Figma ‘Pago de servicios’ (capitalización). - **Color / nav:** azul de marca correcto; item Servicios activo con tinte #E6EEFA — ya conforme. | | | Desviación (propuesta) | | | 🔶 **Propuesta** — los servicios VAS son **rutas del sidebar** (grupo Finanzas), no una barra de pestañas in-page; la CTA está unificada en **«Continuar»** en todo VAS (#548). Probable Figma desactualizado. *Figma:* adoptar la navegación por sidebar en los frames VAS (o confirmar la barra in-page como canónica). [ver desviaciones](/design/parity/intentional-deviations/) | | #### W42 · Tiempo Aire (TAE) [Section titled “W42 · Tiempo Aire (TAE)”](#w42--tiempo-aire-tae) **Ruta:** `/admin/airtime` · **Figma:** [`127:7043`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=127-7043) · **Historia:** US-VAS-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W42](/design/parity/figma/screens/figma-w42-tae.png)](/design/parity/figma/screens/figma-w42-tae.png "figma W42 — abrir a tamaño completo") | [![app W42](/design/parity/screenshots/admin-airtime.png)](/design/parity/screenshots/admin-airtime.png "app W42 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes siguen mostrando pasos distintos del flujo** — no procede auditoría de estilo lado a lado.- **Panel FIGMA:** página compuesta — subtítulo “Recargas de saldo”, sección **Compañía** con proveedores como chips pequeños con avatar de letra en color (Telcel, Movistar, AT\&T, Unefon, Bait), grilla **Monto** ($10–$500) con $50 seleccionado, y panel derecho **Captura** (“Número de teléfono”, “Total (incl. comisión) $51.00”, botón “Agregar a la venta”). - **Panel APP:** solo el **primer paso** (selección de proveedor) — subtítulo “Telcel, AT\&T, Movistar y más”, “Selecciona el proveedor” con grid de tarjetas grandes con tile de letra en gris (Telcel, AT\&T, Movistar, Unefon, Virgin Mobile, OXXO Cel), y panel derecho **Resumen de compra** con Monto/Comisión/Total a cobrar en $0.00.**Post-fix #548:** el CTA de la app es “Continuar” (deshabilitado), ya unificado en todas las pantallas VAS. Es el mismo copy que W42 ya mostraba, así que no hubo cambio visible aquí — la divergencia de fondo persiste (lista de proveedores distinta, chips con avatar de color vs tarjetas grandes grises, montos/captura ausentes en la app). Azul de “Vaciar” #0052CC canónico.Para re-auditar paridad hace falta recapturar la app en el mismo paso compuesto que diseña el Figma, o alinear el Figma con el asistente por pasos de la app. | | | Desviación (propuesta) | | | 🔶 **Propuesta** — TAE usa un **flujo por pasos** (proveedor → monto/captura) en vez de la página compuesta del frame, reutilizando el patrón VAS compartido. *Figma:* documentar el flujo por pasos, o confirmar la página compuesta (implicaría rediseñar `VasSaleScreen`). [ver desviaciones](/design/parity/intentional-deviations/) | | #### W43 · Pines / tarjetas de regalo [Section titled “W43 · Pines / tarjetas de regalo”](#w43--pines--tarjetas-de-regalo) **Ruta:** `/admin/giftcards` · **Figma:** [`127:7220`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=127-7220) · **Historia:** US-VAS-2 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W43](/design/parity/figma/screens/figma-w43-pines.png)](/design/parity/figma/screens/figma-w43-pines.png "figma W43 — abrir a tamaño completo") | [![app W43](/design/parity/screenshots/admin-giftcards.png)](/design/parity/screenshots/admin-giftcards.png "app W43 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** — misma pantalla; los fixes sistémicos siguen presentes, el residuo es copy y estado de captura.- **Etiqueta de botón (post-fix #548):** el CTA de la app ahora es **“Continuar”** (antes “Agregar a la venta”), ya **unificado** con el resto de las pantallas VAS. Como efecto, ahora **diverge del Figma** (“Agregar a la venta”) pero gana consistencia interna entre W41/W42/W43. Divergencia intencional de producto, no defecto de estilo. - **Color:** azul de marca correcto (Vaciar #0052CC, Total (incl. comisión) #0052CC, deshabilitado bg gris). Glifos de marca (Netflix rojo, Spotify verde, GP azul) correctos. - **Estados:** la captura de la app está en estado vacío (ninguna marca, Total $0.00, CTA deshabilitado); el Figma muestra estado lleno (Netflix + $200). Estados distintos del mismo flujo, no defecto. Por eso la grilla “Denominación” no aparece en la app (se renderiza tras elegir marca). - **Contenido:** copy divergente — título “Tarjetas de Regalo” (app) vs “Tarjetas de regalo”; subtítulo “iTunes, Google Play, Spotify y más” vs “Pines digitales”. Decisión de producto. - **Layout:** estructura de dos columnas (selector izquierda + Captura derecha) coincide con el Figma. | | | Desviación (propuesta) | | | — | | #### W44 · Transferir saldo [Section titled “W44 · Transferir saldo”](#w44--transferir-saldo) **Ruta:** `/admin/transfers` · **Figma:** [`132:9984`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=132-9984) · **Historia:** US-VAS-5 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W44](/design/parity/figma/screens/figma-w44-transferir.png)](/design/parity/figma/screens/figma-w44-transferir.png "figma W44 — abrir a tamaño completo") | [![app W44](/design/parity/screenshots/admin-transfers.png)](/design/parity/screenshots/admin-transfers.png "app W44 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Misma pantalla: formulario “Transferir saldo” (Destinatario / Monto / Concepto / Saldo disponible / Continuar) en ambos paneles. La remediación sistémica llegó bien a esta vista.- **Color:** Azul de marca canónico confirmado — el valor del saldo mide `#0052CC`. Texto de etiquetas/heading oscuro `#1F2933`. Todo correcto. - **Estados:** Único residuo real. En Figma el botón **Continuar** aparece como CTA azul primario (estado de reposo del diseño); en la app está **deshabilitado** — bg `#E5E7EB` + texto `#9CA3AF`. Coherente con el estilo `disabled` del sistema y con la regla de negocio (form vacío), pero la captura no muestra el CTA azul del diseño. Diferencia de estado, no de estilo. - **Bordes:** Campos con hairline `#E5E7EB` y relleno blanco — coincide con el fix de form fields. La tarjeta contenedora muestra una sombra suave en la esquina inferior en vez del hairline plano puro del diseño; residuo menor de elevación. - **Contenido:** El placeholder de Destinatario difiere: Figma usa “Comercio Norte”, la app “ID del comercio”. Wording, no estructura. - **Color (tinte):** La tarjeta “Saldo disponible” mide `#EBF1FB`, un pelín más saturada que el info-soft `#EFF6FF` de la spec; diferencia imperceptible. - **Layout / Tipografía:** Composición, orden de campos, jerarquía y densidad coinciden. Sin sombra en botones. Sin hallazgos.Nota: la geometría diálogo-vs-página no aplica aquí (ambas son página completa). | | | Desviación (propuesta) | | | — | | #### W45 · Confirmar transferencia / comprobante [Section titled “W45 · Confirmar transferencia / comprobante”](#w45--confirmar-transferencia--comprobante) **Ruta:** `— (paso dentro de /admin/transfers)` · **Figma:** [`132:10125`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=132-10125) · **Historia:** US-VAS-5 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------- | | [![figma W45](/design/parity/figma/screens/figma-w45-confirmar-transferencia.png)](/design/parity/figma/screens/figma-w45-confirmar-transferencia.png "figma W45 — abrir a tamaño completo") | ⚠️ **Falta captura** — paso de confirmación, requiere captura del flujo completo | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe (comprobante “Transferencia exitosa” con check verde, monto, filas De/Para/Concepto/Folio y botones Compartir comprobante / Listo) pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | | Desviación (propuesta) | | | — | | ### BAL — Wallet del comercio [Section titled “BAL — Wallet del comercio”](#bal--wallet-del-comercio) Historias `US-BAL-1` … `US-BAL-8`. #### W47 · Saldo / Wallet [Section titled “W47 · Saldo / Wallet”](#w47--saldo--wallet) **Ruta:** `/admin/wallet` · **Figma:** [`133:10306`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=133-10306) · **Historia:** US-BAL-1 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W47](/design/parity/figma/screens/figma-w47-wallet.png)](/design/parity/figma/screens/figma-w47-wallet.png "figma W47 — abrir a tamaño completo") | [![app W47](/design/parity/screenshots/playground/w47-saldo-wallet.png)](/design/parity/screenshots/playground/w47-saldo-wallet.png "app W47 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** — misma pantalla (Saldo del comercio, layout de dos columnas + lista de movimientos). Los fixes sistémicos llegaron y están medidos; el residuo es de contenido/composición, probablemente ligado al modelo de datos real.- **Color:** Azul de marca **#0052CC** confirmado por muestreo en las tres tarjetas de saldo (default/cargando/vacío). Botón Depositar en clara #EBF1FB con texto azul, igual que Figma. Página #F5F7FA, tarjetas blancas con hairline. Sin regresiones de tema. - **Contenido (principal):** Figma muestra **“Cuentas bancarias”** — lista de bancos vinculados (BBVA ••••4821, Santander ••••9930) con filas de navegación (chevron). La app la reemplaza por **“Resumen de la cuenta”** (Total abonado / Total retirado / Saldo pendiente). Es una sección/feature distinta; la de cuentas bancarias no está presente. Verificar con producto si es intencional. - **Estados:** La captura es un showcase con tres estados (con datos, cargando con skeleton #F0F4F8, y vacío “Aún no hay movimientos”). Todos correctos; Figma solo diagrama el estado con datos. - **Color (montos):** Los importes de la lista se pintan con **verde/rojo semántico** en la app; en Figma los montos son **neutros #1F2933**. Divergencia menor de criterio — decidir si el color semántico se adopta como estándar. - **Contenido (tarjeta de saldo):** La app agrega el enlace secundario **“Retirar · Próximamente”** bajo Depositar, ausente en Figma. - **Tipografía/Bordes:** Jerarquía y hairlines #E5E7EB consistentes; encabezado de resumen y filas con divisores correctos. | | | Desviación (propuesta) | | | — | | #### W46 · Depósitos [Section titled “W46 · Depósitos”](#w46--depósitos) **Ruta:** `/admin/deposits` · **Figma:** [`123:4532`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-4532) · **Historia:** US-BAL-2 / US-VAS-5 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W46](/design/parity/figma/screens/figma-w46-depositos.png)](/design/parity/figma/screens/figma-w46-depositos.png "figma W46 — abrir a tamaño completo") | [![app W46](/design/parity/screenshots/admin-deposits.png)](/design/parity/screenshots/admin-deposits.png "app W46 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Misma pantalla; los fixes sistémicos llegaron. El residuo es propio de la tabla (columna de acciones extra y densidad).- **Color:** Azul de marca canónico verificado — #0052CC medido en botón “Nuevo depósito”, tab activo “Todos” y outline de “Filtros”. Badge “Pendiente” sobre tinte soft amarillo. Botón verde de acción #10B981 y “Rechazar” con outline rojo (tokens de éxito/error, no comparables contra Figma porque el diseño no tiene esos botones). - **Estados:** Tratamiento de la columna Estado **inconsistente**: solo “Pendiente” es badge con tinte; “Disponible/Aplicado/Rechazado” salen como texto plano de color. Figma usa texto de color plano para todos. Unificar a StatusBadge sobre tokens \*-soft en la celda Estado del listado. - **Contenido:** La app agrega una columna **“Acciones”** con botones “Marcar disponible” / “Rechazar” para filas pendientes, ausente en Figma (que solo tiene el lápiz de edición). Es una función de producto; confirmar si debe vivir en la tabla o migrar a menú/acciones por fila para no romper la paridad. - **Layout:** La columna extra comprime el ancho y hace que el **Folio se parta en dos líneas** (“DEP-\n1042”) como link azul; Figma lo muestra en una línea, texto oscuro. Ajustar min-width de la columna Folio y/o `whiteSpace: nowrap`. - **Bordes:** Card plana con hairline, header de tabla #F7F9FC/#F5F7FA y botones-icono con borde #E5E7EB — coinciden con el diseño. - **Estados (fila):** Figma muestra la primera fila seleccionada (checkbox + realce); la app no. Diferencia de estado de captura, no de estilo. - Diferencias de datos (fechas “01 feb 2024” vs “24 Oct”, paginación “de 5” vs “de 84”, avatar en tinte azul vs iniciales neutras) no son defectos. | | | Desviación (propuesta) | | | — | | #### W46b · Registrar depósito [Section titled “W46b · Registrar depósito”](#w46b--registrar-depósito) **Ruta:** `/admin/deposits/new` · **Historia:** US-BAL-2 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el frame que estaba pareado (`123:4532`, `figma-w46-depositos.png`) es la **lista** de depósitos (W46), no el **formulario** de alta. Falta exportar el frame del formulario «Registrar depósito». | [![app W46b](/design/parity/screenshots/admin-deposits-new.png)](/design/parity/screenshots/admin-deposits-new.png "app W46b — abrir a tamaño completo") | | Notas | | | **⚠️ Falta el frame de Figma del formulario — se despareó la lista.**- **Panel APP:** pantalla de *formulario* “Registrar Depósito” (W46b) — alerta informativa, campos Canal de depósito, Banco de Depósito, Fecha, Número de Referencia, Monto Depositado (MXN), Cuenta de Origen y Notas Adicionales, tarjeta lateral “Comprobante” con carga de archivo (deshabilitada, “disponible próximamente”), y barra de acciones fija Cancelar/Continuar. Es la pantalla correcta del título. - **Panel FIGMA:** el frame que estaba pareado (`123:4532`) es la **lista** de depósitos (W46), no el **formulario**. Se despareó para no comparar dos pantallas distintas; la celda de Figma queda como hueco documentado con un `TODO(human)` para exportar el frame del formulario «Registrar depósito» y volver a emparejarlo. - Confirmado en la captura fresca: el botón Continuar mide **#0052CC** (azul canónico llegó) y el select de “Canal de depósito” hereda del residuo de **\[Select]** (hairline vs 2px azul). Sin diseño no hay auditoría de paridad. | | | Desviación (propuesta) | | | 🔶 **Propuesta** — el alta se resuelve como **drawer lateral** sobre el listado en vez de la página completa del frame, preservando tabla/filtros/scroll. *Figma:* documentar el patrón drawer-sobre-lista para altas (o confirmar la página completa caso por caso). [ver desviaciones](/design/parity/intentional-deviations/) | | ### CONEXIONES — Plataformas de saldo (W66–W72) [Section titled “CONEXIONES — Plataformas de saldo (W66–W72)”](#conexiones--plataformas-de-saldo-w66w72) Flujo 14 (issues #571 · #570). Tab Conexiones + conectar + detalle (con Movimientos) + Agregar saldo (modal + comprobante). Las capturas son la referencia del design system en `/playground`: la superficie real vive bajo `/admin/settings/connections` (owner/admin) y es owner/admin-only. #### W66 · Conexiones (lista) [Section titled “W66 · Conexiones (lista)”](#w66--conexiones-lista) **Ruta:** `/admin/settings?tab=connections` · **Figma:** [`1112:11288`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=1112-11288) · **Historia:** US-CONN-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------- | | [![figma W66](/design/parity/figma/screens/figma-w66-conexiones-lista.png)](/design/parity/figma/screens/figma-w66-conexiones-lista.png "figma W66 — abrir a tamaño completo") | ⚠️ **Falta captura** | | Notas | | | *Pendiente de anotar* | | | Desviación (propuesta) | | | — | | #### W67 · Conectar plataforma (elegir) [Section titled “W67 · Conectar plataforma (elegir)”](#w67--conectar-plataforma-elegir) **Ruta:** `/admin/settings/connections/new` · **Figma:** [`1115:11409`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=1115-11409) · **Historia:** US-CONN-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------- | | [![figma W67](/design/parity/figma/screens/figma-w67-conectar-elegir.png)](/design/parity/figma/screens/figma-w67-conectar-elegir.png "figma W67 — abrir a tamaño completo") | ⚠️ **Falta captura** | | Notas | | | *Pendiente de anotar* | | | Desviación (propuesta) | | | — | | #### W68 · Conectar plataforma (credenciales) [Section titled “W68 · Conectar plataforma (credenciales)”](#w68--conectar-plataforma-credenciales) **Ruta:** `/admin/settings/connections/new` · **Figma:** [`1115:11593`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=1115-11593) · **Historia:** US-CONN-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------- | | [![figma W68](/design/parity/figma/screens/figma-w68-conectar-credenciales.png)](/design/parity/figma/screens/figma-w68-conectar-credenciales.png "figma W68 — abrir a tamaño completo") | ⚠️ **Falta captura** | | Notas | | | *Pendiente de anotar* | | | Desviación (propuesta) | | | — | | #### W69 · Detalle de plataforma [Section titled “W69 · Detalle de plataforma”](#w69--detalle-de-plataforma) **Ruta:** `/admin/settings/connections/[id]` · **Figma:** [`1115:11777`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=1115-11777) · **Historia:** US-CONN-3 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------- | | [![figma W69](/design/parity/figma/screens/figma-w69-detalle-plataforma.png)](/design/parity/figma/screens/figma-w69-detalle-plataforma.png "figma W69 — abrir a tamaño completo") | ⚠️ **Falta captura** | | Notas | | | *Pendiente de anotar* | | | Desviación (propuesta) | | | — | | #### W71 · Agregar saldo (modal) [Section titled “W71 · Agregar saldo (modal)”](#w71--agregar-saldo-modal) **Ruta:** `/admin/settings/connections/[id]` · **Figma:** [`1157:11929`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=1157-11929) · **Historia:** US-CONN-4 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------- | | [![figma W71](/design/parity/figma/screens/figma-w71-agregar-saldo.png)](/design/parity/figma/screens/figma-w71-agregar-saldo.png "figma W71 — abrir a tamaño completo") | ⚠️ **Falta captura** | | Notas | | | *Pendiente de anotar* | | | Desviación (propuesta) | | | — | | #### W72 · Agregar saldo (comprobante) [Section titled “W72 · Agregar saldo (comprobante)”](#w72--agregar-saldo-comprobante) **Ruta:** `/admin/settings/connections/[id]` · **Figma:** [`1158:11935`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=1158-11935) · **Historia:** US-CONN-4 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------- | | [![figma W72](/design/parity/figma/screens/figma-w72-agregar-saldo-comprobante.png)](/design/parity/figma/screens/figma-w72-agregar-saldo-comprobante.png "figma W72 — abrir a tamaño completo") | ⚠️ **Falta captura** | | Notas | | | *Pendiente de anotar* | | | Desviación (propuesta) | | | — | | ### CLI — Clientes [Section titled “CLI — Clientes”](#cli--clientes) Flujo W-13 (v2). Historias `US-CLI-1`, `US-CLI-2`. #### Clientes — directorio [Section titled “Clientes — directorio”](#clientes--directorio) **Ruta:** `/admin/customers` · **Figma:** [`231:3`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=231-3) · **Historia:** US-CLI-2 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el nodo `231:3` existe en Figma pero no se exportó a `design/web/screens/` | [![app Clientes — directorio](/design/parity/screenshots/admin-customers.png)](/design/parity/screenshots/admin-customers.png "app Clientes — directorio — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada (directorio de clientes: filtros, columnas Cliente / Teléfono / Correo / Compras / Total gastado / Última visita / Saldo, paginación “Mostrando 1–7 de 9”) y luce consistente con los tokens sistémicos, pero no hay diseño exportado (nodo `231:3` sin exportar) contra el cual medir paridad. **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | | Desviación (propuesta) | | | — | | #### Cliente — detalle [Section titled “Cliente — detalle”](#cliente--detalle) **Ruta:** `/admin/customers/[id]` · **Figma:** [`232:157`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=232-157) · **Historia:** US-CLI-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — nodo `232:157` sin exportar | [![app Cliente — detalle](/design/parity/screenshots/admin-customers-id.png)](/design/parity/screenshots/admin-customers-id.png "app Cliente — detalle — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada (detalle de cliente: cabecera con avatar + acciones Editar/Nueva venta, KPIs Total gastado / Compras / Ticket promedio / Saldo, pestañas Compras / Datos fiscales (CFDI) / Crédito · fiado, tabla de compras) y aplica los tokens sistémicos, pero no hay diseño exportado (nodo `232:157`) contra el cual comparar. **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y correr `pnpm docs:parity`; si no existe frame, es decisión de diseño pendiente, no desviación de implementación. | | | Desviación (propuesta) | | | — | | #### Nuevo cliente — diálogo [Section titled “Nuevo cliente — diálogo”](#nuevo-cliente--diálogo) **Ruta:** `/admin/customers (diálogo)` · **Figma:** [`233:383`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=233-383) · **Historia:** US-CLI-1 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — nodo `233:383` sin exportar | [![app Nuevo cliente — diálogo](/design/parity/screenshots/admin-customers--dialog-nuevo-cliente.png)](/design/parity/screenshots/admin-customers--dialog-nuevo-cliente.png "app Nuevo cliente — diálogo — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada (drawer lateral “Nuevo cliente” con campos Nombre completo\* / Teléfono / Correo (opcional) / RFC (opcional, para CFDI) y acciones Cancelar/Guardar) pero no hay diseño exportado (nodo `233:383`) contra el cual medir paridad. **Siguiente paso:** exportar el nodo y correr `pnpm docs:parity`; si no existe frame, es decisión de diseño pendiente, no desviación de implementación. | | | Desviación (propuesta) | | | — | | #### Asignar cliente a la venta [Section titled “Asignar cliente a la venta”](#asignar-cliente-a-la-venta) **Ruta:** `— (modal en el POS)` · **Figma:** [`233:279`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=233-279) · **Historia:** US-CLI-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — nodo `233:279` sin exportar | ⚠️ **Falta captura** — modal, requiere captura desde el POS | | Notas | | | **⛔ Comparación no posible — faltan las dos imágenes.** No hay render de Figma exportado (nodo `233:279`) ni captura de la app, así que no hay nada que comparar. Primero hay que resolverlo (exportar el frame y/o construir/capturar la pantalla); recién ahí tiene sentido auditar paridad. | | | Desviación (propuesta) | | | — | | #### Nuevo cliente (alta rápida) [Section titled “Nuevo cliente (alta rápida)”](#nuevo-cliente-alta-rápida) **Ruta:** `— (modal en el POS)` · **Figma:** [`233:383`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=233-383) · **Historia:** US-CLI-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :---------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — nodo `233:383` sin exportar | ⚠️ **Falta captura** — modal, requiere captura desde el POS | | Notas | | | **⛔ Comparación no posible — faltan las dos imágenes.** No hay render de Figma exportado (nodo `233:383`) ni captura de la app del modal de alta rápida en el POS. Primero hay que resolverlo (exportar el frame y/o capturar la pantalla); recién ahí tiene sentido auditar paridad. | | | Desviación (propuesta) | | | — | | ### TERM / WL — Comercios, tiendas, terminales y white-label [Section titled “TERM / WL — Comercios, tiendas, terminales y white-label”](#term--wl--comercios-tiendas-terminales-y-white-label) Flujo W-10. Historias `US-TERM-1`, `US-TERM-2`, `US-WL-1`. #### W48 · Comercios [Section titled “W48 · Comercios”](#w48--comercios) **Ruta:** `/admin/merchants` · **Figma:** [`130:7665`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=130-7665) · **Historia:** US-TERM-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W48](/design/parity/figma/screens/figma-w48-comercios.png)](/design/parity/figma/screens/figma-w48-comercios.png "figma W48 — abrir a tamaño completo") | [![app W48](/design/parity/screenshots/admin-merchants.png)](/design/parity/screenshots/admin-merchants.png "app W48 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `match`** — ✅ Coincide. Evidencia: referencia Figma `130:7665` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados). **Chequeo de fixtures (filas 4–5):** verificadas **verbatim** contra el frame — `comerciosParityData.ts` filas 4/5 (`Leche Lala 1L` · RFC 750100912 · Lácteos · $24.50 · 0 · Suspendido; `Aceite 123 1L` · RFC 750103345 · Abarrotes · $38.00 · 31 · Activo) reproducen 1:1 lo que dibuja el frame `130:7665` (el propio diseño usa esos nombres de producto como placeholders del roster de comercios). **No divergen → sin cambio de datos.** El showcase `/playground?component=w48-comercios` renderiza las mismas filas.**✅ Coincide** — la composición ya reproduce el layout del Figma; los fixes de #547 aterrizaron íntegros.- **Encabezado:** ✅ título de página **Comercios** + subtítulo **‘Organizaciones que administras’** (corregido). - **Tabs:** ✅ **Comercios / Tiendas / Apariencia** presentes (corregido). - **Filtros:** ✅ buscador + pills **Todos/Activos/Suspendidos** + botón **Filtros** + select de orden (corregido). - **CTA:** ✅ un único primario compacto **‘+ Nuevo comercio’** arriba a la derecha; ya no están los dos botones sólidos sobredimensionados (corregido). - **Tabla — afordancias:** ✅ checkboxes de selección, **icono editar por fila** y **pie de paginación numerada** (‘Mostrando 1–7 de 9’ + pager 1/2) (corregido). - **Columnas:** ✅ **Comercio (avatar) · RFC · Tiendas · Saldo · Empleados · Estado**, alineadas con el diseño. - **Estados:** ✅ StatusBadge de tinte suave (Activo verde, Pendiente ámbar, Inactivo gris, Suspendido rojo). | | | Desviación (propuesta) | | | — | | #### W49 · Perfil del comercio [Section titled “W49 · Perfil del comercio”](#w49--perfil-del-comercio) **Ruta:** `/admin/merchants (formulario)` · **Figma:** [`124:6197`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=124-6197) · **Historia:** US-TERM-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------- | | [![figma W49](/design/parity/figma/screens/figma-w49-perfil-comercio.png)](/design/parity/figma/screens/figma-w49-perfil-comercio.png "figma W49 — abrir a tamaño completo") | ⚠️ **Falta captura** — formulario, requiere captura con un comercio abierto | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe (Figma `124:6197`: formulario “Perfil del comercio” / “Datos de tu organización” con secciones Comercio —Nombre comercial, RFC, Régimen fiscal, Dirección fiscal— y Contacto —Correo, Teléfono—, primario `+ Guardar` y barra inferior “Tienes cambios sin guardar” · Descartar/Guardar) pero no hay implementación capturada. **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | | Desviación (propuesta) | | | — | | #### W50 · Tiendas [Section titled “W50 · Tiendas”](#w50--tiendas) **Ruta:** `/admin/pos/stores` · **Figma:** [`122:4009`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-4009) · **Historia:** US-TERM-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W50](/design/parity/figma/screens/figma-w50-tiendas.png)](/design/parity/figma/screens/figma-w50-tiendas.png "figma W50 — abrir a tamaño completo") | [![app W50](/design/parity/screenshots/admin-pos-stores.png)](/design/parity/screenshots/admin-pos-stores.png "app W50 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** *(antes ❌; recaptura limpia)*- **Layout:** ✅ coincide — título **Tiendas**, subtítulo **6 tiendas · 2 comercios**, tabs **Todas/Abiertas/Cerradas**, buscador, columnas **Tienda** (avatar) · **Dirección** · **Terminales** · **Ventas mes** · **Empleados** · **Estado**, edición por fila y paginación numerada. - **Estados:** ✅ StatusBadge soft por estado (Abierta verde, Sin turno ámbar, Cerrada gris). - **Residuo menor:** falta el `Select` de orden («Selecciona una opción») del frame; la CTA **Nueva tienda** va en la fila de toolbar en vez del encabezado; los nombres de fixture leen «Sucursal …» (dato del mock) vs «Tienda …» del diseño. - **Nota de captura:** la lista tarda \~500 ms (mock) en poblar; una captura anterior tomó el esqueleto vacío — la recaptura confirma 6 filas. | | | Desviación (propuesta) | | | — | | #### W51 · Tienda crear / editar [Section titled “W51 · Tienda crear / editar”](#w51--tienda-crear--editar) **Ruta:** `/admin/pos/stores (formulario)` · **Figma:** [`124:6370`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=124-6370) · **Historia:** US-TERM-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------- | | [![figma W51](/design/parity/figma/screens/figma-w51-tienda-edit.png)](/design/parity/figma/screens/figma-w51-tienda-edit.png "figma W51 — abrir a tamaño completo") | ⚠️ **Falta captura** — formulario, requiere captura con una tienda abierta | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | | Desviación (propuesta) | | | — | | #### W52 · Terminales [Section titled “W52 · Terminales”](#w52--terminales) **Ruta:** `/admin/pos/terminals` · **Figma:** [`123:4238`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-4238) · **Historia:** US-TERM-1 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W52](/design/parity/figma/screens/figma-w52-terminales.png)](/design/parity/figma/screens/figma-w52-terminales.png "figma W52 — abrir a tamaño completo") | [![app W52](/design/parity/screenshots/admin-pos-terminals.png)](/design/parity/screenshots/admin-pos-terminals.png "app W52 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** — el texto del CTA y el residuo de IDs crudos ya se corrigieron; resta la terminología del nombre de tienda y detalles de toolbar.- **Encabezado:** ✅ Coincide — título **Terminales** + subtítulo **‘5 terminales vinculadas’**. - **CTA principal:** ✅ ahora rotula **‘Vincular terminal’** (corregido; antes ‘Nuevo’). - **Columnas:** ✅ Coincide — **Terminal (avatar iniciales) · Tienda · Última conexión · Versión · Estado**. - **Tabs / buscador:** ✅ Coincide — **Todas / En línea / Fuera de línea** + buscador **‘Buscar…’** + **Filtros**. - **Estado:** ✅ Coincide — StatusBadge de tinte suave (En línea verde / Fuera de línea rojo). - **Filas:** ✅ Coincide — **editar (lápiz)** + acción **‘Vincular terminal’** por fila (intencional). - **Tienda:** ⚠️ ya muestra **nombre** (corregido: sin ‘store-1/2/3’), pero usa el prefijo **‘Sucursal …’** (Sucursal Centro/Norte/Sur) en lugar de **‘Tienda …’** de Figma. - **Toolbar:** ⚠️ falta el **select de orden** (‘Selecciona una opción’) y el CTA queda en la fila del toolbar en lugar del encabezado top-right (menor). | | | Desviación (propuesta) | | | — | | #### W53 · Vincular terminal (pairing) [Section titled “W53 · Vincular terminal (pairing)”](#w53--vincular-terminal-pairing) **Ruta:** `/admin/pos/terminals (diálogo)` · **Figma:** [`133:10490`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=133-10490) · **Historia:** US-TERM-2 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W53](/design/parity/figma/screens/figma-w53-vincular-pairing.png)](/design/parity/figma/screens/figma-w53-vincular-pairing.png "figma W53 — abrir a tamaño completo") | [![app W53](/design/parity/screenshots/playground/w53-vincular-pairing.png)](/design/parity/screenshots/playground/w53-vincular-pairing.png "app W53 — abrir a tamaño completo") | | Notas | | | **✅ Paridad (FD-9 · #927).** El overlay «Vincular terminal» (`TerminalPairingDialog`, `/admin/pos/terminals`) compone la tarjeta DS + tokens de tema y el **chip de código mono azul-marca** (`PairingCodeChip`, gap #24) — reemplazando el `Box` ad-hoc con relleno `action.hover`. Los estados **inválido/expirado/éxito** (E7/E8/S4, nodos `552-2758` / `553-2880` / `554-3002`) — y los `denied`/`not-found` que comparten el mismo layout de feedback — están **construidos** por la máquina de estados `TerminalPairingContent` y se muestran lado a lado en el showcase `w53-vincular-pairing` del playground, cada uno con la copia es-MX verbatim de `pos-terminal.i18n` y sin marcas de proveedor. El código/cuenta varían por corrida. Sin cambio de comportamiento ni de contrato de pairing (US-TERM-2 sigue en verde). Evidencia: `docs/ui-evidence/614/` + `docs/design-parity/screenshots/playground/w53-vincular-pairing.png`. | | | Desviación (propuesta) | | | — | | #### W54 · Apariencia white-label + recibo [Section titled “W54 · Apariencia white-label + recibo”](#w54--apariencia-white-label--recibo) **Ruta:** `/admin/merchants/appearance` · **Figma:** [`128:7274`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=128-7274) · **Historia:** US-WL-1 / US-PLAT-3 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W54](/design/parity/figma/screens/figma-w54-apariencia.png)](/design/parity/figma/screens/figma-w54-apariencia.png "figma W54 — abrir a tamaño completo") | [![app W54](/design/parity/screenshots/admin-merchants-appearance.png)](/design/parity/screenshots/admin-merchants-appearance.png "app W54 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Re-auditoría del `ThemeEditorForm` tras PR #538. El layout base del editor (tarjeta **Tema** con Color primario / Color de acento + lápiz de edición, dropzone, select **Tipografía: Inter**, switch **Tema oscuro disponible**, y tarjeta **Vista previa** con banner ‘Tu Comercio’ + Saldo disponible + Cobrar) se mantiene fiel al diseño.- **Color:** Azul canónico verificado — Guardar / lápices de edición (en chip cuadrado tinte #E6EEFA) / swatch primario / banner ‘Tu Comercio’ / Cobrar = **#0052CC**. El acento diverge por **dato de mock**: Figma azul #1A73E8 vs app teal #00A3A3; no es defecto de estilo. - **Layout (nuevo, PR #538):** La app ahora añade en el header **dos acciones extra que Figma no muestra**: **`Restablecer estilos`** (botón outlined en rojo/coral) y **`Cancelar`** (botón de texto), junto al `+ Guardar`. Figma solo trae `+ Guardar`. Divergencia de composición nueva a validar contra el diseño (o actualizar Figma). - **Estados:** El switch ‘Tema oscuro disponible’ aparece **OFF** (gris) mientras Figma lo muestra **ON** (azul) — diferencia de dato/estado, ambos renderizan bien. - **Dropzone:** La app suma icono ‘+’ y botón outlined **‘Seleccionar archivos’** (Figma solo icono compartir); hint distinto (Figma ‘CSV, PNG o JPG · hasta 10 MB’ vs app ‘PNG, JPG, WEBP o GIF · hasta 10 MB’) y colocado bajo el dropzone. Composición menor. - **Contenido extra (👍):** La app incluye la sección **‘Recibo / Encabezado del recibo’** (parte ‘+ recibo’ del alcance) que el crop de Figma no muestra — contenido correcto de más. - **Sistémico (1 línea):** sidebar con chip ‘Tienda demo’ e ítem ‘Comercios’ vs ‘Tienda Centro’ / ‘Comercio y tiendas’ de Figma — cambio de nav-IA global (PR #538), no propio de la pantalla.Residuo restante = dato de mock (acento/switch), composición menor del dropzone y las nuevas acciones de header (Restablecer/Cancelar) por conciliar con Figma. | | | Desviación (propuesta) | | | 🔶 **Propuesta** — el encabezado del editor agrega **Restablecer estilos** (outlined) y **Cancelar** junto a Guardar. *Figma:* agregar esas acciones al encabezado del frame W54. [ver desviaciones](/design/parity/intentional-deviations/) | | ### ACCT — Cuenta y ajustes [Section titled “ACCT — Cuenta y ajustes”](#acct--cuenta-y-ajustes) Flujo W-12. Historias `US-ACCT-1` … `US-ACCT-3`. #### W59 · Mi cuenta [Section titled “W59 · Mi cuenta”](#w59--mi-cuenta) **Ruta:** `/admin/my-account` · **Figma:** [`128:7503`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=128-7503) · **Historia:** US-ACCT-1 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W59](/design/parity/figma/screens/figma-w59-mi-cuenta.png)](/design/parity/figma/screens/figma-w59-mi-cuenta.png "figma W59 — abrir a tamaño completo") | [![app W59](/design/parity/screenshots/admin-my-account.png)](/design/parity/screenshots/admin-my-account.png "app W59 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Misma pantalla (perfil “Mi cuenta”). Los fixes sistémicos están presentes y medidos; el residuo es de contenido/estado, no de estilo.- **Color:** Verificado. Botón `#0052CC`, chip Supervisor texto `#0052CC` sobre tinte `#E6EEFA`, avatar tinte `#EEF4FF`, badge “Sesión actual” verde soft `#E8F5E9`. Todo canónico. - **Tipografía:** Título/labels `#212B35` ≈ `#1F2933`. Sin observaciones. - **Bordes/Sombras:** Hairline `#E5E7EB` en tarjetas y campos; Correo deshabilitado con relleno gris; tarjetas y AppBar planas. Correcto. - **Iconos:** El botón Figma trae glifo “+” (“+ Guardar”); la app muestra solo “Guardar” sin icono. - **Contenido:** La app añade **“Sesiones activas”** (2 tarjetas Chrome/Safari con IP y fecha) y la línea **“Miembro desde: ene 2026”** en la tarjeta de perfil — no existen en Figma. Contenido de más, no falta. - **Estados:** Figma muestra los campos como placeholders vacíos; la app muestra Nombre/Teléfono con valor y **Correo deshabilitado**. Diferencia de estado. - **Layout:** Las tarjetas de la app ocupan casi todo el ancho; en Figma la tarjeta “Datos personales” es más angosta. Diferencia de anchura contenida, aceptable. | | | Desviación (propuesta) | | | — | | #### W60 · Seguridad [Section titled “W60 · Seguridad”](#w60--seguridad) **Ruta:** `/admin/settings (tab)` · **Figma:** [`128:7681`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=128-7681) · **Historia:** US-ACCT-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W60](/design/parity/figma/screens/figma-w60-seguridad.png)](/design/parity/figma/screens/figma-w60-seguridad.png "figma W60 — abrir a tamaño completo") | [![app W60](/design/parity/screenshots/playground/w60-seguridad.png)](/design/parity/screenshots/playground/w60-seguridad.png "app W60 — abrir a tamaño completo") | | Notas | | | **✅ Coincide**Re-auditoría post-remediación: la pantalla de Seguridad quedó alineada con el diseño; los fixes sistémicos se miden en esta captura.- **Color:** Azul canónico verificado — FAB `#0052CC`, track del Switch 2FA `#0052CC`, botones outline `Cambiar`/`Cambiar PIN` con label/borde `#0052CC` y enlace `Cerrar`. Verde de éxito `Esta sesión` `#10B981`, igual que Figma. - **Tipografía:** Títulos de tarjeta y filas (Acceso, Contraseña, 2FA, PIN de cajero) en `#1F2933`; subtítulos/metadatos en gris secundario. Coincide. - **Bordes/Sombras:** Tarjetas planas blancas con hairline `#E5E7EB` sobre fondo `#F5F7FA`; divisor entre filas de sesiones presente; sin sombra en tarjetas ni FAB. Correcto. - **Estados:** Switch 2FA activo (thumb blanco sobre track azul), geometría correcta. - **Layout/Contenido:** Composición idéntica — header + subtítulo, tarjeta *Acceso* (Contraseña/2FA/PIN) y tarjeta *Sesiones activas* con las dos sesiones y sus acciones; FAB `+` arriba a la derecha. Todos los textos correctos.Único desajuste esperado y no imputable a la pantalla: el encuadre del playground recorta el sidebar y la AppBar (buscador, *Turno abierto*, *Sincronizado*, avatares) que sí figuran en Figma. Nada accionable. | | | Desviación (propuesta) | | | — | | #### Re-autenticación por contraseña [Section titled “Re-autenticación por contraseña”](#re-autenticación-por-contraseña) **Ruta:** `/admin/my-account (diálogo)` · **Historia:** US-ACCT-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — sin frame; es un requisito de Appwrite (cambiar teléfono exige contraseña) | [![app Re-autenticación por contraseña](/design/parity/screenshots/admin-my-account--dialog-contrasena.png)](/design/parity/screenshots/admin-my-account--dialog-contrasena.png "app Re-autenticación por contraseña — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | | Desviación (propuesta) | | | — | | #### W61 · Ajustes generales [Section titled “W61 · Ajustes generales”](#w61--ajustes-generales) **Ruta:** `/admin/settings` · **Figma:** [`133:10622`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=133-10622) · **Historia:** US-ACCT-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W61](/design/parity/figma/screens/figma-w61-ajustes.png)](/design/parity/figma/screens/figma-w61-ajustes.png "figma W61 — abrir a tamaño completo") | [![app W61](/design/parity/screenshots/admin-settings.png)](/design/parity/screenshots/admin-settings.png "app W61 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** — re-verificado sobre la captura fresca; el H1 quedó **corregido en #548**.- **H1 (resuelto):** ✅ la app ya renderiza el título **‘Ajustes’** encima de las pestañas (General/Seguridad/Bitácora/Mi cuenta), como en Figma. - **Layout:** las tarjetas de formulario siguen ocupando todo el ancho del contenido; en Figma están constreñidas a una columna de lectura (\~455px). Considerar `maxWidth`. - **Color / Estados / Tipografía / Bordes:** coinciden — switch encendido #0052CC con thumb blanco, select ‘Español (MX)’, botón Guardar azul de marca plano, tarjetas planas con hairline y jerarquía correcta. | | | Desviación (propuesta) | | | — | | #### W62 · Bitácora / Audit log [Section titled “W62 · Bitácora / Audit log”](#w62--bitácora--audit-log) **Ruta:** `/admin/audit-log` · **Figma:** [`123:5114`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-5114) · **Historia:** US-ACCT-3 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W62](/design/parity/figma/screens/figma-w62-bitacora.png)](/design/parity/figma/screens/figma-w62-bitacora.png "figma W62 — abrir a tamaño completo") | [![app W62](/design/parity/screenshots/admin-audit-log.png)](/design/parity/screenshots/admin-audit-log.png "app W62 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `best-effort`** — ⚠️ Mejor esfuerzo — diferencias menores medidas, ninguna bloqueante. Evidencia: referencia Figma `123:5114` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados). **Suplantación (#440) — sin frame Figma propio:** las filas de impersonación `audit-7`/`audit-8` **no tienen frame Figma** (se integran en la tabla W62 existente). Evidencia capturada aparte, en la página 2 (donde se ordenan a propósito para que la página 1 quede byte-a-byte comparable con el frame):[![W62 impersonación — audit-7/audit-8](/design/parity/screenshots/admin-audit-log--impersonacion.png)](/design/parity/screenshots/admin-audit-log--impersonacion.png)Se ve el badge **Suplantación** + doble atribución (operador **Diego S.** / *en nombre de* Ana P. en `audit-7`; `audit-8` cae al id crudo del sujeto por no traer snapshot `after`). Fuente: `AuditLogList.tsx` (`audit-log-impersonation-badge-*`) + `auditLog.impersonation.ts`.**⚠️ Diferencias menores** — re-verificado; el formato de Fecha quedó **corregido en #548**.- **Fecha (resuelto):** ✅ la columna muestra ahora fechas es-formateadas (‘18 jul 2026’, ‘17 jul 2026’), con la cabecera ‘Fecha’ limpia y sin el em-dash suelto. - **Layout:** persiste la omisión de toda la **columna de selección** del Figma (checkbox select-all, checkbox por fila y primera fila en estado seleccionado). Falta la selección masiva en `AuditLogTable`. - **Iconos:** el glifo de acción sigue como lápiz relleno azul primary vs lápiz-en-recuadro contorneado gris del Figma. - **Estados/Contenido:** ‘Editó precio’ muestra ‘Rechazado’ (rojo) en app vs ‘OK’ en Figma; conteos/páginas (8 / 2 vs 3,210 / 10) son datos mock. - **Color / cabecera / toolbar:** coinciden (Exportar y paginación activa #0052CC, cabecera #F7F9FC, Buscar + Todas/Hoy/Esta semana + Filtros). | | | Desviación (propuesta) | | | — | | #### Bitácora — filtros [Section titled “Bitácora — filtros”](#bitácora--filtros) **Ruta:** `/admin/audit-log (diálogo)` · **Historia:** US-ACCT-3 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — organismo **Filter Bar**, sin frame de pantalla | [![app Bitácora — filtros](/design/parity/screenshots/admin-audit-log--dialog-filtros.png)](/design/parity/screenshots/admin-audit-log--dialog-filtros.png "app Bitácora — filtros — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma** (nodo sin exportar o frame sin diseñar; el motivo va en la celda izquierda). La app SÍ captura un drawer de filtros funcional y coherente: chips ‘Filtrar por Acción’ (Anulación de venta, Ajuste de descuento, Movimiento de efectivo, Cambio de rol, Reinicio de PIN, Reembolso, Actuó como usuario) y ‘Filtrar por Entidad’ (Venta, Pago, Movimiento de efectivo, Membresía, Empleado, Usuario), campo ‘Usuario’, ‘Rango de fechas’ y botones ‘Limpiar Todo’ (outlined) + ‘Aplicar’ (azul). Residuo heredado del **\[Date-range Picker]**: ‘Rango de fechas’ usa dos inputs Desde/Hasta separados. **Siguiente paso:** exportar el nodo a `docs/design/web/` y re-correr `pnpm docs:parity`; si no existe frame, es decisión de diseño pendiente. | | | Desviación (propuesta) | | | — | | ### NOTIF — Notificaciones [Section titled “NOTIF — Notificaciones”](#notif--notificaciones) Flujo W-02. Historia `US-NOTIF-1`. #### W08 · Notificaciones [Section titled “W08 · Notificaciones”](#w08--notificaciones) **Ruta:** `/admin/notifications` · **Figma:** [`113:430`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=113-430) · **Historia:** US-NOTIF-1 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W08](/design/parity/figma/screens/figma-w08-notificaciones.png)](/design/parity/figma/screens/figma-w08-notificaciones.png "figma W08 — abrir a tamaño completo") | [![app W08](/design/parity/screenshots/admin-notifications.png)](/design/parity/screenshots/admin-notifications.png "app W08 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** — los dos fixes estructurales de #547 ya aparecen; solo restan diferencias finas de glifo.- **Agrupación:** ✅ Coincide — la lista ya se agrupa por día en secciones **‘Hoy’** y **‘Ayer’** con divisores (corregido; antes todo caía en ‘Anteriores’). - **Badges de icono:** ✅ Coincide — los glifos ya **no van todos en círculo de tinte**; solo la fila tipo avatar (‘Nuevo empleado’) conserva el círculo (corregido). - **Color / Layout / Tipografía:** ✅ botón #0052CC, tinte de fila no-leída ≈ #E6EEFA, tabs Todas/No leídas/Ventas/Stock con activo azul subrayado, hora a la derecha. - **Glifos específicos:** ⚠️ persisten diferencias de icono: venta = recibo en la app vs **carrito** en Figma; transferencia = flecha simple vs **doble flecha**; corte de caja = reloj vs **documento**. Residuo cosmético menor. | | | Desviación (propuesta) | | | — | | #### W63 · Detalle de notificación [Section titled “W63 · Detalle de notificación”](#w63--detalle-de-notificación) **Ruta:** `/admin/notifications/[id]` · **Figma:** [`163:10716`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=163-10716) · **Historia:** US-NOTIF-1 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W63](/design/parity/figma/screens/figma-w63-detalle-notificacion.png)](/design/parity/figma/screens/figma-w63-detalle-notificacion.png "figma W63 — abrir a tamaño completo") | [![app W63](/design/parity/screenshots/admin-notifications-id.png)](/design/parity/screenshots/admin-notifications-id.png "app W63 — abrir a tamaño completo") | | Notas | | | **✅ Coincide** — misma plantilla, confirmado sobre captura nueva. El contenido difiere solo por el mock (Figma: alerta de inventario ‘Stock bajo’; app: ‘Venta completada #1029’).- **Color:** Botón primario **#0052CC**; rol ‘Sistema’ y enlaces de acción en azul. Fondo #F5F7FA, tarjeta blanca. - **Tipografía:** Título 600 oscuro, meta gris, cuerpo #1F2933; etiquetas de campo (Tienda/total) en gris con valor oscuro. Jerarquía correcta. - **Botones / Bordes:** Ramp correcto — primario relleno (‘Ver detalle’) + outlined azul (‘Realizar ajuste’), sin sombra; enlaces de texto ‘Marcar como no leída’ / ‘Descartar’ a la derecha; breadcrumb con enlace azul + ‘Detalle’ oscuro; tarjeta plana con hairline. - **Iconos:** Glifo de tipo (recibo) azul dentro de contenedor en tinte claro; en el diseño el rol Inventario usa triángulo ámbar por ser otro tipo. Correcto. - **Layout:** Estructura idéntica; la fila de meta muestra 2 columnas (app) vs 4 (Figma) por el mock distinto, no es residuo. - Menor: la fecha se muestra ‘18 jul 2026, 9:32 am’ (mes es-abreviado OK; ‘am’ en minúscula) vs ‘Hoy · 10:42’ del Figma — dato mock. | | | Desviación (propuesta) | | | — | | ### SEARCH — Búsqueda global [Section titled “SEARCH — Búsqueda global”](#search--búsqueda-global) Flujo W-02. Historia `US-SEARCH-1`. #### W09 · Búsqueda global (⌘K) [Section titled “W09 · Búsqueda global (⌘K)”](#w09--búsqueda-global-k) **Ruta:** `— (overlay global)` · **Figma:** [`113:651`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=113-651) · **Historia:** US-SEARCH-1 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W09](/design/parity/figma/screens/figma-w09-busqueda-global.png)](/design/parity/figma/screens/figma-w09-busqueda-global.png "figma W09 — abrir a tamaño completo") | [![app W09](/design/parity/screenshots/admin-dashboard--palette-busqueda-global.png)](/design/parity/screenshots/admin-dashboard--palette-busqueda-global.png "app W09 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** — misma paleta global ⌘K. La app captura el **estado vacío** (‘Escribe al menos 2 caracteres para buscar’) mientras Figma muestra el estado poblado (query ‘coca’ con resultados agrupados), así que filas, section-headers y tint de fila activa no son auditables aquí.- **Color:** Fix sistémico CONFIRMADO — el anillo de foco del input mide **#0052CC**. Diálogo #FFFFFF, scrim gris detrás correcto. - **Layout / Estados:** Residuo propio — el buscador se renderiza como **TextField MUI con caja redondeada y anillo de foco azul 2px**, mientras Figma lo diseña como **barra de comando sin borde** (icono + texto con divisor a todo el ancho, sin caja). Alinear al estilo command-bar (ver residuo \[Command Palette]). - **Contenido:** Falta el pill **‘ESC’** arriba a la derecha que sí trae Figma. - **Espaciado:** Diálogo anclado más alto (pisa el contenido) y algo más angosto que el diseño centrado. - **Cobertura:** para cerrar la paridad hace falta recapturar el estado poblado y validar allí tint de fila activa (#E6EEFA), tiles de icono en tinte y tipografía de títulos/subtítulos. | | | Desviación (propuesta) | | | — | | ### PLAT — Administración de plataforma [Section titled “PLAT — Administración de plataforma”](#plat--administración-de-plataforma) Historias `US-PLAT-1` … `US-PLAT-3`. #### W07 · Dashboard [Section titled “W07 · Dashboard”](#w07--dashboard) **Ruta:** `/admin/dashboard` · **Figma:** [`106:2`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=106-2) · **Historia:** US-PLAT-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W07](/design/parity/figma/screens/figma-w07-dashboard.png)](/design/parity/figma/screens/figma-w07-dashboard.png "figma W07 — abrir a tamaño completo") | [![app W07](/design/parity/screenshots/admin-dashboard.png)](/design/parity/screenshots/admin-dashboard.png "app W07 — abrir a tamaño completo") | | Notas | | | **Veredicto de paridad (#613): `best-effort`** — ⚠️ Mejor esfuerzo — diferencias menores medidas, ninguna bloqueante. Evidencia: referencia Figma `106:2` + captura de la app (arriba), rúbrica agentic-ui-parity (layout · spacing · tokens · tipografía · estados). **Shell admin (AC6):** esta captura (`admin-dashboard.png`) ES el shell montado (drawer + top bar sobre `/admin/dashboard`); su referencia de navegación no-Figma vive en `docs/design/admin-shell/admin-shell-reference.png`. Layout/spacing/tokens/tipografía coinciden; los **estados activos** del drawer (ítem “Inicio” resaltado, grupos OPERACIÓN/CATÁLOGO/ADMINISTRACIÓN) coinciden con la referencia. La única divergencia de nav es la **IA reorganizada en #538** (Comercios/Tiendas/Apariencia), una **desviación aceptada** (Figma desactualizado) ya registrada en `intentional-deviations.md`. Residuos restantes = componentes compartidos (Date-range Picker, KPI Stat Card, Chart Donut), no propios de W07.**🎨 Figma desactualizado (navegación)** — con una novedad importante de paridad.- **Resuelto:** la captura fresca **YA corresponde a la misma pantalla** que el Figma. El desajuste previo (‘pantalla equivocada’ — dashboard de billetera/pagos) quedó atrás: ahora ambos muestran el dashboard analítico de retail — saludo ‘Hola, Victoria’, 4 KPIs, gráfico ‘Ventas por hora’, donut ‘Categorías’ y tabla ‘Ventas recientes’. Contenido y layout coinciden (KPIs $8,450 +12.5% · 128 +6% · $66.01 −2% · 342 +9%; tabla con checkbox+avatar+SKU+badges Activo/Bajo y primera fila seleccionada con tinte). - **Nav (motivo del marcador, registrado UNA sola vez aquí):** el drawer real de AdminShell cambió con **PR #538** (`src/components/AdminShell/adminNav.ts`). El grupo ADMINISTRACIÓN se reorganizó — split comercio↔tienda (**Comercios / Tiendas**), item white-label **‘Apariencia’** añadido, más **Terminales / Ajustes**. La app encabeza el grupo con **‘Comercios’** donde el Figma dibuja ‘Comercio y tiendas / Terminales / Ajustes’. Es un cambio de IA intencional; el **Figma de navegación quedó desactualizado** — actualizar el diseño, no el código. Esta barra aparece en TODAS las pantallas admin; no se repite en las demás. - **Residuos de componente heredados (no recontar):** selector de fecha = dos inputs nativos Desde/Hasta (08/01/2026–08/07/2026) vs el pill único combinado ‘Hoy / 1 Oct–24 Oct’ del Figma (\[Date-range Picker]); delta de KPI con chevron fino ^/v vs triángulo ▲/▼ relleno (\[KPI Stat Card]); donut con gaps de padAngle vs segmentos que se tocan (\[Chart — Donut]). - Menor: el emoji 👋 del saludo se renderiza como caja (tofu) en la captura — probable falta de fuente de emoji en el entorno headless, no defecto de UI. Usuario/tienda (Sofía Ramírez/Tienda demo vs Ada Victoria/Tienda Centro) son mocks de nav. | | | Desviación (propuesta) | | | 🔶 **Propuesta** — el nav lateral de admin se reorganizó en #538: **Comercios / Tiendas / Apariencia / Terminales / Ajustes** (se separó comercio↔tienda y se agregó white-label). *Figma:* actualizar la navegación del frame `97:35` a la IA nueva. [ver desviaciones](/design/parity/intentional-deviations/) | | #### Actividad reciente [Section titled “Actividad reciente”](#actividad-reciente) **Ruta:** `/admin/recent-activities` · **Historia:** US-PLAT-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — bloque del dashboard W07 convertido en ruta; sin frame propio | [![app Actividad reciente](/design/parity/screenshots/admin-recent-activities.png)](/design/parity/screenshots/admin-recent-activities.png "app Actividad reciente — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(vista implementada en #549 — antes stub «Hola Mundo»)*- **Layout:** la ruta `/admin/recent-activities` ya renderiza la vista real: título «Actividades recientes» + subtítulo + **DataTable** compartida (Fecha · Actividad · Detalle · Monto · Estado) con paginación numerada («Mostrando 1–5 de 5»). - **Estado:** badges soft «Completado» (StatusBadge por rol), montes alineados a la derecha, fechas en español. - **Nota:** sin par de Figma (ruta operativa). Se captura con el flag `recent_activities_web` ON (añadido al `playwright.screenshots.config.ts`). | | | Desviación (propuesta) | | | — | | #### Tickets de soporte [Section titled “Tickets de soporte”](#tickets-de-soporte) **Ruta:** `/admin/tickets` · **Historia:** US-PLAT-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — pantalla legacy sin frame | [![app Tickets de soporte](/design/parity/screenshots/admin-tickets.png)](/design/parity/screenshots/admin-tickets.png "app Tickets de soporte — abrir a tamaño completo") | | Notas | | | Herramienta interna, sin diseño Figma. La app renderiza correctamente la pantalla “Buscar Tickets”: columna izquierda con “Búsqueda Rápida” (Último ticket, Tickets de hoy / esta semana / del mes) y tarjeta “Estadísticas Rápidas” (127 / 892 / $45.2K); panel principal con toggle Por Número / Por Fecha, campo de número de ticket, botón “Buscar Ticket” y “Filtros Avanzados” (Estado, Tipo de operación, Monto mínimo/máximo). Se muestra dentro del AdminShell con el sidebar de tienda. Sin desviaciones que reportar; los inputs de rango de monto y los Select heredan del residuo de \[Select] y \[Search Field]. Sin diseño Figma (herramienta interna). | | | Desviación (propuesta) | | | — | | ### Sin historia de usuario — rutas técnicas y de desarrollo [Section titled “Sin historia de usuario — rutas técnicas y de desarrollo”](#sin-historia-de-usuario--rutas-técnicas-y-de-desarrollo) Rutas capturadas que no pertenecen a ninguna historia: raíz, playground y preview del design system. #### Playground del design system [Section titled “Playground del design system”](#playground-del-design-system) **Ruta:** `/admin/playground` · **Historia:** — (herramienta de parity) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — herramienta interna; es la superficie desde la que se capturan los átomos/moléculas/organismos de abajo | [![app Playground del design system](/design/parity/screenshots/admin-playground.png)](/design/parity/screenshots/admin-playground.png "app Playground del design system — abrir a tamaño completo") | | Notas | | | Herramienta interna, sin diseño Figma. La captura muestra un estado de error: el contenido de `/admin/playground` falla con “Error: Failed to fetch” (solo se ve el sidebar del AdminShell). Es un fallo de carga de datos/entorno al capturar, no una desviación de diseño. Recapturar en un entorno con backend disponible. Sin diseño Figma (herramienta interna). | | | Desviación (propuesta) | | | — | | #### Playground del design system (ruta pública) [Section titled “Playground del design system (ruta pública)”](#playground-del-design-system-ruta-pública) **Ruta:** `/playground` · **Historia:** — (herramienta de parity) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — herramienta interna | [![app Playground del design system (ruta pública)](/design/parity/screenshots/playground.png)](/design/parity/screenshots/playground.png "app Playground del design system (ruta pública) — abrir a tamaño completo") | | Notas | | | Herramienta interna de parity, sin diseño Figma. Renderiza correctamente el índice “Web Design System — Playground” con secciones ATOMS / MOLECULES / ORGANISMS y enlaces a cada componente y pantalla (W07, W25–W65, etc.). Es la página de navegación del catálogo de componentes; no aplica comparación visual. Sin diseño Figma (herramienta interna). | | | Desviación (propuesta) | | | — | | #### UI preview [Section titled “UI preview”](#ui-preview) **Ruta:** `/ui-preview` · **Historia:** — (herramienta de parity) | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — herramienta interna | [![app UI preview](/design/parity/screenshots/ui-preview.png)](/design/parity/screenshots/ui-preview.png "app UI preview — abrir a tamaño completo") | | Notas | | | Herramienta interna de preview, sin diseño Figma. Muestra un componente aislado sobre scrim: el modal de cobro con tarjeta “Inserta, desliza o acerca la tarjeta” / “Esperando la tarjeta del cliente…”, ícono de tarjeta en círculo tinte #E6EEFA, bloque “Monto $372.00” y acción “Cancelar” en primary. Render correcto; sirve para inspección de un solo componente, no comparación de pantalla completa. Sin diseño Figma (herramienta interna). | | | Desviación (propuesta) | | | — | | *** ## Design system [Section titled “Design system”](#design-system) Los renders de Figma vienen de `design/web/{atomic,molecules,organisms}/`. Las capturas reales **todavía no existen**: la superficie para tomarlas es `/admin/playground` (dev-only, mock data), que renderiza cada componente del DS en todos sus estados. Ese es el mismo enfoque bottom-up que se usó en móvil (átomos → moléculas → organismos → pantallas) antes de auditar las pantallas. ### Átomos [Section titled “Átomos”](#átomos) Código en `src/ui/atoms/**`. #### Avatar [Section titled “Avatar”](#avatar) **Código:** `src/ui/atoms/Avatar/index.tsx` · **Figma:** [`45:90`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=45-90) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Avatar](/design/parity/figma/atoms/figma-avatar.png)](/design/parity/figma/atoms/figma-avatar.png "figma Avatar — abrir a tamaño completo") | [![app Avatar](/design/parity/screenshots/playground/avatar.png)](/design/parity/screenshots/playground/avatar.png "app Avatar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Tinte del avatar de iniciales **#E6EEFA** medido en la app — idéntico al de Figma (#E6EEFA). Iniciales “JP” en azul de marca (núcleo \~#1761D1→#0052CC sobre el tinte); coincide con las iniciales primary del diseño. - **Contenido:** El avatar “con imagen” resuelve con círculo sólido **#0052CC** (azul canónico) + glifo de persona blanco — el fix de brand-blue llegó a esta pantalla. - **Estados:** La rampa de tamaños S/M/L está presente y crece proporcionalmente, como en Figma (que muestra tres tamaños de iniciales). - **Tipografía:** Iniciales centradas, peso alto, legibles en los tres tamaños; consistente con el diseño. - **Layout:** Figma solo muestra las tres variantes de iniciales; la app añade “con imagen” y “fallback” como showcase — no es un desajuste, es superset del catálogo.Residuo: ninguno accionable. El avatar “con imagen” muestra placeholder de glifo en lugar de foto real, pero es contenido de showcase, no defecto de estilo. | | | Desviación (propuesta) | | | — | | #### Banner / Inline Alert [Section titled “Banner / Inline Alert”](#banner--inline-alert) **Código:** `src/ui/atoms/Banner/index.tsx` | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Banner / Inline Alert](/design/parity/figma/atoms/figma-banner.png)](/design/parity/figma/atoms/figma-banner.png "figma Banner / Inline Alert — abrir a tamaño completo") | [![app Banner / Inline Alert](/design/parity/screenshots/playground/banner.png)](/design/parity/screenshots/playground/banner.png "app Banner / Inline Alert — abrir a tamaño completo") | | Notas | | | **✅ Coincide**Misma pantalla: los 4 banners (info / success / warning / danger) están en el mismo orden y con el mismo contenido en ambos paneles. Los fixes sistémicos de tinte llegaron a esta atom y están medidos.- **Color:** Tintes soft idénticos al hex — info `#EFF6FF`, success `#ECFDF5` (Figma `#ECFCF5`), warning `#FFFBEB`, danger `#FEF2F2`. Coinciden con Figma píxel a píxel. - **Iconos:** Rol/color por variante correcto: info azul `#3B82F6` (círculo relleno), success verde `#25BF8C`, warning ámbar `#F5A720`, danger rojo `#F05454/#EF4444`. Coincide con la paleta canónica. - **Tipografía:** Jerarquía correcta — título en el color del rol (peso semibold) + cuerpo en texto oscuro. Igual que Figma. - **Layout:** Estructura idéntica — icono a la izquierda, título+cuerpo, cierre arriba a la derecha. Estructura y densidad concuerdan (escalas distintas, no comparo px absolutos). - **Residuo (menor, no bloqueante):** El glifo de cierre difiere — Figma usa un icono estilo tecla “borrar/backspace” en gris redondeado; la app usa una `×` simple. Diferencia de icono trivial, misma posición/función. - **Nota de captura:** Las etiquetas en versalitas (INFO/SUCCESS/WARNING/DANGER) son del showcase/playground, no de la atom — no es defecto del componente. | | | Desviación (propuesta) | | | — | | #### Button [Section titled “Button”](#button) **Código:** `src/ui/atoms/Button/index.tsx` · **Figma:** [`21:50`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=21-50) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Button](/design/parity/figma/atoms/figma-button.png)](/design/parity/figma/atoms/figma-button.png "figma Button — abrir a tamaño completo") | [![app Button](/design/parity/screenshots/playground/button.png)](/design/parity/screenshots/playground/button.png "app Button — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Filled `#0052CC` (default), `#003D99` (hover y pressed) — medidos idénticos en Figma y app. Tonal `#E6EEFA` con texto `#0052CC`. Disabled `#E5E7EB` en ambos. Todo cableado por `palette.primary`. - **Bordes:** Outlined con borde azul 1px sólido; tarjetas/estados con radio redondeado consistente entre ambos. - **Iconos:** El showcase usa un ícono de caja registradora (POS) donde Figma muestra un carrito — diferencia de ejemplo del showcase, no defecto. - **Estados:** Las 4 variantes (Filled/Tonal/Outlined/Text) cubren default/hover/pressed/disabled/loading; el spinner de loading aparece en todas. Figma organiza loading como fila-variante y la app como columna-estado — decisión de layout del showcase, cobertura funcional equivalente. - **Layout:** Tonal pressed mide levemente más azulado en la app (`#BAD0EF`) que en Figma (tinte gris `#E5E7EB`); residuo menor de tinte de estado, dentro de tolerancia. | | | Desviación (propuesta) | | | — | | #### Checkbox [Section titled “Checkbox”](#checkbox) **Código:** `src/ui/atoms/Checkbox/index.tsx` · **Figma:** [`90:8`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=90-8) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Checkbox](/design/parity/figma/atoms/figma-checkbox.png)](/design/parity/figma/atoms/figma-checkbox.png "figma Checkbox — abrir a tamaño completo") | [![app Checkbox](/design/parity/screenshots/playground/checkbox-radio-switch.png)](/design/parity/screenshots/playground/checkbox-radio-switch.png "app Checkbox — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Azul de marca canónico confirmado — el glifo indeterminate mide **#0052CC** exacto; el checked comparte el mismo relleno (muestreo #548BDD por la mezcla con el check blanco central). Figma también dominado por #0052CC. Sin residuo de azul. - **Bordes:** Unchecked con trazo hairline **#D5D9DE** (spec \~#D1D5DB) — variación despreciable. Geometría de esquina redondeada custom del checkbox presente en los tres estados, igual que Figma. - **Iconos:** Check blanco y guion (indeterminate) blancos, centrados, coinciden con el diseño. - **Estados:** La app añade disabled (relleno atenuado + texto #9CA3AF) y error (texto rojo de ayuda) que Figma no muestra — es un showcase superset; los tres estados canónicos del átomo casan. - **Layout:** Figma solo diseña los 3 glifos del checkbox; el panel app los expone dentro del playground junto a Radio/Switch. El átomo en sí coincide; sin acción pendiente propia de esta pantalla. | | | Desviación (propuesta) | | | — | | #### Chip / Category Chip [Section titled “Chip / Category Chip”](#chip--category-chip) **Código:** `src/ui/atoms/Chip/index.tsx` · **Figma:** [`35:31`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=35-31) | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Chip / Category Chip](/design/parity/figma/atoms/figma-chip.png)](/design/parity/figma/atoms/figma-chip.png "figma Chip / Category Chip — abrir a tamaño completo") | [![app Chip / Category Chip](/design/parity/screenshots/playground/chip.png)](/design/parity/screenshots/playground/chip.png "app Chip / Category Chip — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color (activo):** El chip *activo* está clavado — bg tinte de marca **#E6EEFA** y etiqueta **#0052CC** (azul canónico), idéntico a Figma. Fix sistémico verificado en esta captura. - **Color (inactivo):** bg medido **\~#EBEDF0** (gris neutro MUI) vs **#F0F4F8** de Figma (gris levemente más frío); diferencia de tinte imperceptible. Texto **#6B7280** (secondary), correcto. - **Bordes:** Píldora totalmente redondeada, filled sin borde en ambos estados — coincide. - **Estados:** Se representan los dos estados que diseña Figma (inactivo/activo). La app los organiza con rótulos INACTIVO/ACTIVO; Figma los muestra lado a lado — mismo contenido y jerarquía. - **Tipografía:** Etiqueta “Categoría”, peso medio; consistente entre paneles. - **Sombras:** Plano en ambos — sin residuo.Sin hallazgos accionables propios de la pantalla; el matiz del inactivo es opcional (alinear tinte a #F0F4F8 si se busca paridad exacta, pero es cosmético). | | | Desviación (propuesta) | | | — | | #### Date-range Picker [Section titled “Date-range Picker”](#date-range-picker) **Código:** `src/ui/atoms/DateRangePicker/index.tsx` · **Figma:** [`103:116`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=103-116) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Date-range Picker](/design/parity/figma/atoms/figma-date-range-picker.png)](/design/parity/figma/atoms/figma-date-range-picker.png "figma Date-range Picker — abrir a tamaño completo") | [![app Date-range Picker](/design/parity/screenshots/playground/date-range-picker.png)](/design/parity/screenshots/playground/date-range-picker.png "app Date-range Picker — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Coincide. Campos blancos, texto de valor #1F2933, hairline medido #E5E7EB/#E6E8EC, icono de adorno de calendario #9CA3AF y estado disabled #E5E7EB — todos alineados con la paleta remediada. - **Tipografía:** Coincide. Label estático arriba (Desde/Hasta \~#666D75 ≈ secundario), secciones CLOSED/OPEN/DISABLED en gris; valores en dark. La única diferencia textual es el uso de ejemplo, no defecto. - **Bordes:** Coincide. Campos blanco + hairline redondeado, alto \~40. El pill de Figma usa radio mayor (pastilla), pero es consecuencia del patrón, no un token distinto. - **Iconos:** Coincide. Icono de calendario gris #9CA3AF (adorno); en Figma también aparece el chevron gris #9CA3AF que la app no muestra (ver Layout). - **Estados:** Coincide. Se muestran closed / open (placeholder DD/MM/YYYY) / disabled con el patrón de deshabilitado correcto (#E5E7EB + texto atenuado). - **Layout:** Residuo real. Figma define **un único control combinado**: `[icono calendario] "1 Oct – 24 Oct, 2025" [chevron]` que despliega el calendario. La app lo implementa como **dos inputs de fecha nativos separados** (Desde/Hasta) sin pill combinado, sin resumen de rango en una sola línea y sin chevron de despliegue. Es una diferencia de patrón/estructura, no de tokens. Verificar el componente de showcase en `src/ui` (playground DateRangePicker) si se desea converger al pill único de Figma. | | | Desviación (propuesta) | | | 🔶 **Propuesta** — dos campos explícitos **Desde / Hasta** en vez del pill combinado con chevron; se puede teclear cada extremo sin abrir el popover. *Figma:* decidir adoptar los dos campos o mantener el pill. [ver desviaciones](/design/parity/intentional-deviations/) | | #### FAB [Section titled “FAB”](#fab) **Código:** `src/ui/atoms/Fab/index.tsx` | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma FAB](/design/parity/figma/atoms/figma-fab.png)](/design/parity/figma/atoms/figma-fab.png "figma FAB — abrir a tamaño completo") | [![app FAB](/design/parity/screenshots/playground/fab.png)](/design/parity/screenshots/playground/fab.png "app FAB — abrir a tamaño completo") | | Notas | | | **✅ Coincide**El playground muestra el mismo átomo que diseña Figma: el FAB en sus dos estados (collapsed con glifo `+`, expanded con glifo `×`), que son exactamente los dos círculos del panel Figma.- **Color:** medido #0052CC en los dos círculos Figma y en ambos FAB de la app (collapsed 985,278 → #0253CC; expanded × 1204,300 → #0152CB). Azul de marca canónico confirmado post-remediación; el mini-FAB de acción también en #0047C8/#0052CC. - **Iconos:** glifo blanco (`+` / `×`) centrado, proporción correcta (\~20px); coincide con el spec. - **Bordes:** círculo perfecto, sin borde; correcto. - **Sombras:** el FAB lleva una sombra suave que se corresponde con el glow difuso del diseño Figma; sin residuo. - **Estados:** collapsed→expanded cablea la transición `+`→`×` tal como muestran los dos estados del diseño. - **Contenido:** el backdrop gris y las mini-acciones (Cobrar / Buscar producto / Imprimir) del estado expanded son composición del SpeedDial, no aparecen en el crop atómico de Figma; no es defecto de paridad.Sin residuo propio de la pantalla que remediar. | | | Desviación (propuesta) | | | — | | #### File Upload [Section titled “File Upload”](#file-upload) **Código:** `src/ui/atoms/FileUpload/index.tsx` · **Figma:** [`103:126`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=103-126) | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma File Upload](/design/parity/figma/atoms/figma-file-upload.png)](/design/parity/figma/atoms/figma-file-upload.png "figma File Upload — abrir a tamaño completo") | [![app File Upload](/design/parity/screenshots/playground/file-upload.png)](/design/parity/screenshots/playground/file-upload.png "app File Upload — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Contenedor circular del icono en `#E6EEFA` (palette.primary.light) medido igual en ambos paneles; glifo en azul de marca `#0052CC` (Figma y app). `src/ui/atoms/FileUpload/index.tsx` usa `primary.light`/`primary.main`. Coincide. - **Tipografía:** Título en negrita oscuro (`subtitle2`, text.primary) y subtítulo/hint en gris tenue (`caption`, text.disabled `#9CA3AF`) según diseño. Coincide. - **Bordes:** Zona con borde `2px dashed` sobre `theme.palette.divider` (#E5E7EB); estado ERROR conmuta a `error.main` `#EF4444` medido. Botón outlined 1px sólido de marca. Coincide. - **Iconos:** El glifo difiere (app usa `Plus`, Figma un icono de compartir), pero el contenedor tintado + color de marca coinciden — icono de adorno, no defecto. Papelera del chip en `#0052CC` como adorno. - **Layout:** El chip de archivo es plano con hairline (`divider`) y papelera a la derecha, como el patrón de tarjeta plana. El showcase expone estados EMPTY/HOVER-DRAG/WITH-FILE/ERROR. - **Estados:** El subtítulo de formatos/tamaño (“CSV, PNG o JPG · hasta 10 MB”) existe vía prop `hint` (text.disabled); el showcase simplemente no lo pasa — decisión del showcase, el componente lo soporta. Sin residuo real. | | | Desviación (propuesta) | | | — | | #### Form Field [Section titled “Form Field”](#form-field) **Código:** `src/ui/atoms/FormField/index.tsx` · **Figma:** [`91:23`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=91-23) | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Form Field](/design/parity/figma/atoms/figma-form-field.png)](/design/parity/figma/atoms/figma-form-field.png "figma Form Field — abrir a tamaño completo") | [![app Form Field](/design/parity/screenshots/playground/form-field.png)](/design/parity/screenshots/playground/form-field.png "app Form Field — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Borde focus medido `#0052CC` en app = azul de marca de Figma. Relleno disabled `#E5E7EB` idéntico. Estado error en rojo en ambos (Figma ≈`#EE3030`, app `#F05252` — misma familia, sin residuo real). Ayuda gris \~`#6D7482` (≈`#6B7280`). - **Tipografía:** Label estático en negrita arriba del campo, ayuda y mensaje de error en tamaño menor, coincide en ambos paneles. - **Bordes:** Campos blancos con hairline y esquinas redondeadas en los cuatro estados; anillo/borde de focus y borde de error consistentes con Figma. - **Estados:** Default / focus (borde azul) / error (borde+texto rojo) / disabled (relleno gris `#E5E7EB` + texto atenuado) todos presentes y correctos. - **Layout:** El showcase apila los estados en vertical con etiquetas DEFAULT/FOCUS/ERROR/DISABLED mientras Figma los muestra en fila horizontal — decisión del showcase, no defecto. Estructura interna del campo idéntica. | | | Desviación (propuesta) | | | — | | #### Icon [Section titled “Icon”](#icon) **Código:** `src/ui/atoms/Icon/index.tsx` | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Icon](/design/parity/figma/atoms/figma-icon.png)](/design/parity/figma/atoms/figma-icon.png "figma Icon — abrir a tamaño completo") | [![app Icon](/design/parity/screenshots/playground/icon.png)](/design/parity/screenshots/playground/icon.png "app Icon — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Iconos medidos en `#0052CC` exacto en ambos paneles (Plus, Gear, Info, muestras de tamaño). Azul de marca canónico correcto. - **Iconos:** FontAwesome Solid, fill-based, ligados a `Color`, nombres = registro `TerminalesIcons` — paridad con Figma y con Kotlin/`react-icons/fa6`. - **Espaciado:** Grilla de registro con celdas uniformes y etiquetas centradas debajo; escala de tamaños sm 16 / md 20 / lg 24 / xl 32 / xxl 40 legible. - **Estados:** Swatches de la sección Colors mapean a tokens canónicos medidos: primary `#0052CC`, success `#10B981`, warning `#F59E0B`, error `#EF4444`, text `#1F2933`. - **Layout:** Las secciones Registry / Sizes / Colors son estructura del showcase de `/playground`; el set de iconos de ejemplo distinto respecto a Figma es decisión del showcase, no residuo. - Sin residuo real del componente. | | | Desviación (propuesta) | | | — | | #### Icon Button [Section titled “Icon Button”](#icon-button) **Código:** `src/ui/atoms/IconButton/index.tsx` · **Figma:** [`34:73`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=34-73) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Icon Button](/design/parity/figma/atoms/figma-icon-button.png)](/design/parity/figma/atoms/figma-icon-button.png "figma Icon Button — abrir a tamaño completo") | [![app Icon Button](/design/parity/screenshots/playground/icon-button.png)](/design/parity/screenshots/playground/icon-button.png "app Icon Button — abrir a tamaño completo") | | Notas | | | **✅ Coincide**Ambos paneles muestran el mismo átomo (IconButton con glifo de tienda) en sus tres variantes × tres estados. El Figma los presenta en una fila continua de 9 tiles; la app los organiza en una matriz etiquetada **Standard / Outlined / Ghost × Default / Pressed / Disabled** — misma cobertura semántica, solo cambia el andamiaje del showcase (no es defecto del componente).- **Color:** Todos los tokens post-remediación están presentes y medidos en la captura nueva: * Pressed sólido = **#0052CC** (idéntico al azul de marca del Figma, medido en ambos). * Tinte de fondo default/pressed-suave = **#E6EEFA** (coincide con el tinte del Figma). * Disabled: fondo **#E5E7EB** + glifo **#9CA3AF** (tokens de disabled correctos). * Icono activo azul en Outlined/Ghost default y pressed = **#0052CC**. - **Estados:** Standard=fondo tinte→sólido, Outlined=blanco con hairline→tinte, Ghost=transparente→tinte. La progresión default→pressed→disabled es consistente con las tres columnas del Figma. - **Iconos:** El glifo (store) coincide; en Standard pressed va en blanco sobre el sólido, igual que el tile azul del Figma. - **Bordes:** Outlined en la app usa hairline muy tenue (\~#E5E7EB) sobre blanco; concuerda con el tile blanco-con-borde del Figma. Diferencia de contraste marginal, no accionable. - **Layout:** Único residuo — el Figma es una tira lineal y la app una grilla con headers; decisión de presentación del playground, no del átomo.Sin hallazgos accionables; la remediación sistémica (azul #0052CC, tinte #E6EEFA, disabled #E5E7EB/#9CA3AF) llegó completa a esta pantalla. | | | Desviación (propuesta) | | | — | | #### Keypad Key [Section titled “Keypad Key”](#keypad-key) **Código:** `src/ui/atoms/KeypadKey/index.tsx` · **Figma:** [`37:45`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=37-45) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Keypad Key](/design/parity/figma/atoms/figma-keypad-key.png)](/design/parity/figma/atoms/figma-keypad-key.png "figma Keypad Key — abrir a tamaño completo") | [![app Keypad Key](/design/parity/screenshots/playground/keypad-key.png)](/design/parity/screenshots/playground/keypad-key.png "app Keypad Key — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Estado pressed medido con tinte de selección **#E6EEFA** y glifo/dígito en azul de marca **#0052CC** — patrón de selección con tinte correcto (idéntico en fila Dígito y Acción). Default blanco **#FFFFFF** con dígito **#1F2933**; disabled con dígito **#9CA3AF**. Todo alineado con la remediación (palette.primary.light + azul marca). - **Tipografía:** Dígito en peso bold, coherente con Figma. - **Bordes:** Tecla con esquinas redondeadas suaves, igual que el diseño; default con hairline sutil sobre fondo claro. - **Iconos:** Fila Acción (backspace) replica los tres estados con el mismo esquema de color que la fila Dígito. - **Estados:** default / pressed / disabled presentes y consistentes con el patrón de tinte canónico. - **Layout:** El showcase agrega la fila Acción y etiquetas de estado (DEFAULT/PRESSED/DISABLED) — decisión del showcase, no defecto. Figma muestra las variantes en horizontal sin etiquetas; sin residuo del componente. | | | Desviación (propuesta) | | | — | | #### PIN Dot [Section titled “PIN Dot”](#pin-dot) **Código:** `src/ui/atoms/PinDot/index.tsx` · **Figma:** [`47:52`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=47-52) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma PIN Dot](/design/parity/figma/atoms/figma-pin-dot.png)](/design/parity/figma/atoms/figma-pin-dot.png "figma PIN Dot — abrir a tamaño completo") | [![app PIN Dot](/design/parity/screenshots/playground/pin-dot.png)](/design/parity/screenshots/playground/pin-dot.png "app PIN Dot — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Bordes:** Residuo cerrado — el anillo del dot **vacío** ya mide el gris frío `#D1D5DB` (corregido en #546, `palette.grey[300]`), igualando el anillo vacío de Figma. Mismo tono en la fila “PIN ROW”. - **Color:** Dot **lleno** en `#0052CC` (azul de marca) y estado **error** en `#EF4444` en ambos paneles. - **Iconos:** Anillo 2px sólido + círculo perfecto (`borderRadius: '50%'`, `boxSizing: border-box`) coincide. - **Layout:** Las filas extra “PIN ROW”/“ERROR” son composición del showcase, no defecto. - **Estados:** Lleno/vacío/error bien diferenciados con transición de borde/fondo. | | | Desviación (propuesta) | | | — | | #### QR Code [Section titled “QR Code”](#qr-code) **Código:** `src/ui/atoms/QrCode/index.tsx` | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma QR Code](/design/parity/figma/atoms/figma-qr-code.png)](/design/parity/figma/atoms/figma-qr-code.png "figma QR Code — abrir a tamaño completo") | [![app QR Code](/design/parity/screenshots/playground/qr-code.png)](/design/parity/screenshots/playground/qr-code.png "app QR Code — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Módulos medidos en #1F2933 (tinta oscura canónica), igual que el Figma (#1F2933 medido). Quiet zone blanco #FFFFFF en ambos. Sin negro puro fuera de token. - **Layout:** App muestra el atom en tres tamaños (S/M/L) sobre tarjeta blanca con esquinas redondeadas y fondo #F5F7FA del playground; el panel Figma es un crop ampliado de un solo código, por eso no se compara contenedor a escala. - **Bordes:** Tarjeta contenedora con radio redondeado y hairline suave, coherente con el patrón de tarjetas planas. - **Iconos:** El patrón de módulos difiere (dato codificado y estilo de módulo distinto) — es contenido de ejemplo del generador, no defecto. - **Estados:** N/A para este atom.Sin residuo real; el componente cumple la paridad tras la remediación. | | | Desviación (propuesta) | | | — | | #### Radio [Section titled “Radio”](#radio) **Código:** `src/ui/atoms/Radio/index.tsx` · **Figma:** [`90:14`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=90-14) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Radio](/design/parity/figma/atoms/figma-radio.png)](/design/parity/figma/atoms/figma-radio.png "figma Radio — abrir a tamaño completo") | [![app Radio](/design/parity/screenshots/playground/checkbox-radio-switch.png)](/design/parity/screenshots/playground/checkbox-radio-switch.png "app Radio — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Punto seleccionado medido `#0052CC`, idéntico al de Figma (`#0052CC`). Anillo no-seleccionado gris claro `~#D1D5DB`, coincide con el ring canónico de Figma. - **Bordes:** Geometría redondeada correcta en ambos: no-seleccionado = anillo gris con relleno blanco; seleccionado = anillo azul + hueco blanco + punto azul central. Igual que Figma. - **Estados:** DEFAULT, SELECTED y DISABLED reproducen el patrón de Figma (disabled atenuado `#9CA3AF`). El estado ERROR (label roja + helper “Selecciona un método”) es un extra del showcase — Figma solo ilustra default/seleccionado; no es defecto. - **Layout:** Radio + label alineados, mismo patrón de selección con tinte/relleno de marca. Sin residuo. | | | Desviación (propuesta) | | | — | | #### Search Field [Section titled “Search Field”](#search-field) **Código:** `src/ui/atoms/SearchField/index.tsx` · **Figma:** [`37:35`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=37-35) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Search Field](/design/parity/figma/atoms/figma-search-field.png)](/design/parity/figma/atoms/figma-search-field.png "figma Search Field — abrir a tamaño completo") | [![app Search Field](/design/parity/screenshots/playground/search-field.png)](/design/parity/screenshots/playground/search-field.png "app Search Field — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** El único residuo medible es la superficie del campo. Figma muestra el search bar con **relleno suave #F0F4F8 y sin borde** (estilo *filled*); el app lo renderiza como **#FFFFFF + hairline #E5E7EB** (`src/ui/atoms/SearchField/index.tsx` usa `TextField` con el tema canónico). Esta es la decisión de campo-de-formulario blanco+hairline ya estandarizada en la remediación, por lo que probablemente el #F0F4F8 de Figma sea el lienzo/estilo filled y no un defecto; queda como diferencia de tratamiento (filled vs outlined), no de token. - **Iconos:** Lupa de adorno **#9CA3AF** en ambos paneles. ✓ - **Tipografía:** Placeholder gris muted (\~#9CA3AF) coincide; texto escrito (TYPING) en oscuro #1F2933. ✓ - **Bordes:** App con radio redondeado y hairline #E5E7EB; alto \~40. ✓ - **Estados:** DISABLED del app = relleno **#E5E7EB** (coincide con remediación). Botón limpiar (×) aparece al escribir. ✓ - **Layout:** Icono a la izquierda + placeholder “Buscar cliente…” coinciden; el showcase añade TYPING y DISABLED (decisión de showcase, no defecto). | | | Desviación (propuesta) | | | 🔶 **Propuesta** — relleno **outlined blanco + hairline** `#E5E7EB` (coherente con el resto de inputs del DS) en vez del soft-fill `#F0F4F8` del frame. *Figma:* unificar el search field al tratamiento outlined, o marcar el soft-fill como excepción. [ver desviaciones](/design/parity/intentional-deviations/) | | #### Segmented Control [Section titled “Segmented Control”](#segmented-control) **Código:** `src/ui/atoms/SegmentedControl/index.tsx` · **Figma:** [`90:24`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=90-24) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Segmented Control](/design/parity/figma/atoms/figma-segmented-control.png)](/design/parity/figma/atoms/figma-segmented-control.png "figma Segmented Control — abrir a tamaño completo") | [![app Segmented Control](/design/parity/screenshots/playground/segmented-control.png)](/design/parity/screenshots/playground/segmented-control.png "app Segmented Control — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Track gris claro medido en app (#EBEDF0 / #F0F4F8) alineado con Figma (#F0F4F8). Thumb del segmento activo blanco puro (#FFFFFF) en ambos. - **Bordes:** Thumb con esquinas redondeadas sobre track redondeado — geometría de selección correcta tras la remediación. - **Estados:** Patrón de selección = thumb blanco (no relleno primary), tal como el canon. El estado DISABLED conserva thumb blanco en el activo y atenúa los labels inactivos; consistente con Figma. - **Tipografía:** Label seleccionado en tono oscuro y no-seleccionados en gris; jerarquía correcta. - **Layout:** Tres segmentos (Día/Semana/Mes) en fila dentro del contenedor tintado; misma estructura que el diseño. - Nota: el panel APP añade un segundo grupo DISABLED como showcase de /playground (decisión del showcase, no defecto). | | | Desviación (propuesta) | | | — | | #### Select [Section titled “Select”](#select) **Código:** `src/ui/atoms/Select/index.tsx` · **Figma:** [`91:45`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=91-45) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Select](/design/parity/figma/atoms/figma-select.png)](/design/parity/figma/atoms/figma-select.png "figma Select — abrir a tamaño completo") | [![app Select](/design/parity/screenshots/playground/select.png)](/design/parity/screenshots/playground/select.png "app Select — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Estados (abierto):** Residuo cerrado — el select **ABIERTO** ya dibuja el borde azul **#0052CC** \~2px (corregido en #546), igual que Figma. El campo cerrado conserva relleno blanco + hairline **#E5E7EB**. - **Estados (opción seleccionada):** Afordancia implementada — la fila “Manzana” muestra tinte **#E6EEFA**, etiqueta **azul** y **check #0052CC** a la derecha, replicando el patrón del Figma (“Bebidas”). - **Color:** Texto de opciones **#1F2933**, superficie del popper blanca con hairline; paridad de tokens. - **Contenido:** Los estados extra No-results/Loading (spinner) son del showcase, no defecto. | | | Desviación (propuesta) | | | — | | #### Skeleton [Section titled “Skeleton”](#skeleton) **Código:** `src/ui/atoms/Skeleton/index.tsx` | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Skeleton](/design/parity/figma/atoms/figma-skeleton.png)](/design/parity/figma/atoms/figma-skeleton.png "figma Skeleton — abrir a tamaño completo") | [![app Skeleton](/design/parity/screenshots/playground/skeleton.png)](/design/parity/screenshots/playground/skeleton.png "app Skeleton — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Relleno medido en app #F0F4F8 / #F1F5F8 / #F2F5F8 = surface-variant canónico #F0F4F8 de Figma. Cableado en `theme.ts:407-410` (`MuiSkeleton.root.backgroundColor = scheme.surface.variant`), fix “Parity finding #16” verificado. - **Bordes:** Barras y avatar con esquinas redondeadas / círculo, igual que el diseño. - **Layout:** El showcase muestra las primitivas etiquetadas (LINE/BLOCK/ROW/CARD/TABLE); Figma muestra una composición de página (avatar+líneas, card+líneas). Es la misma primitiva de skeleton — la composición vs. catálogo es decisión del showcase, no defecto. - **Estados:** La app añade un shimmer/wave animado (gradiente #E8ECF0→#F2F5F8 en BLOCK); es la animación de carga por defecto de MUI, esperada en un skeleton; el screenshot de Figma es estático. Sin residuo. - **Tipografía:** Etiquetas de sección del showcase (#6B7280), no forman parte del componente. | | | Desviación (propuesta) | | | — | | #### Status Badge [Section titled “Status Badge”](#status-badge) **Código:** `src/ui/atoms/StatusBadge/index.tsx` · **Figma:** [`33:26`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=33-26) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Status Badge](/design/parity/figma/atoms/figma-status-badge.png)](/design/parity/figma/atoms/figma-status-badge.png "figma Status Badge — abrir a tamaño completo") | [![app Status Badge](/design/parity/screenshots/playground/status-badge.png)](/design/parity/screenshots/playground/status-badge.png "app Status Badge — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Tintes \*-soft verificados por muestreo y coinciden con Figma: success `#ECFDF5`≈`#ECFCF5`, warning `#FFFBEB`, danger `#FEF2F2`, info `#EFF6FF`. Textos por rol correctos: success `#10B981`, warning `#F59E0B`, danger `#EF4444`, info azul. Fix de variantes soft confirmado. - **Bordes:** Pill totalmente redondeado con relleno tintado en las 4 variantes (sin borde duro), igual que Figma. - **Layout:** El showcase apila vertical con etiqueta de rol (SUCCESS/WARNING/DANGER/INFO) sobre cada badge; Figma los muestra en fila horizontal. Es organización del playground, no defecto del componente. - **Estados:** Este showcase no incluye la variante `neutral` gris (Figma tampoco la muestra aquí); nada que reportar. - Salvedad: el histograma global marca 99% fondo porque los badges ocupan <1% del canvas 1440×900; los recortes confirman los pills tintados. | | | Desviación (propuesta) | | | — | | #### Switch [Section titled “Switch”](#switch) **Código:** `src/ui/atoms/Switch/index.tsx` · **Figma:** [`90:21`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=90-21) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Switch](/design/parity/figma/atoms/figma-switch.png)](/design/parity/figma/atoms/figma-switch.png "figma Switch — abrir a tamaño completo") | [![app Switch](/design/parity/screenshots/playground/checkbox-radio-switch.png)](/design/parity/screenshots/playground/checkbox-radio-switch.png "app Switch — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Track ON medido **#0052CC** en la app, idéntico al azul de marca del Figma; track OFF **#D1D5DB** en ambos paneles; thumb blanco **#FFFFFF** verificado. Sin desviación. - **Bordes:** Forma pill totalmente redondeada con thumb circular blanco, igual que el diseño. - **Layout:** Thumb a la izquierda en OFF y desplazado a la derecha sobre track azul en ON — reproduce ambos estados del Figma. - **Estados:** El panel Figma solo muestra OFF y ON; la app añade **DISABLED** (decisión del showcase), con track gris tenue + thumb azul claro medido **#9EBDEB** — atenuación coherente con el patrón disabled. - **Tipografía:** Etiquetas OFF/ON/DISABLED en caps grises y “Notificaciones” en texto oscuro, consistentes con el resto de átomos. Sin residuo real del componente. | | | Desviación (propuesta) | | | — | | #### Textarea [Section titled “Textarea”](#textarea) **Código:** `src/ui/atoms/Textarea/index.tsx` · **Figma:** [`103:110`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=103-110) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Textarea](/design/parity/figma/atoms/figma-textarea.png)](/design/parity/figma/atoms/figma-textarea.png "figma Textarea — abrir a tamaño completo") | [![app Textarea](/design/parity/screenshots/playground/textarea.png)](/design/parity/screenshots/playground/textarea.png "app Textarea — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Fondo blanco `#FFFFFF` con borde hairline `#E5E7EB` medido en el estado default — coincide con el token canónico. - **Bordes:** Esquinas redondeadas y borde 1px sólido; radios consistentes con Figma. - **Estados:** Focus con borde azul de marca `#0052CC` (medido), error con borde `#EF4444` + texto de ayuda rojo, disabled con tinte gris y texto atenuado — todos correctos. - **Tipografía:** Label estático arriba en tono oscuro, placeholder gris — patrón de campo de formulario correcto. - **Layout:** Figma solo muestra el estado default; el showcase de /playground agrega FOCUS/ERROR/DISABLED (decisión del showcase). El texto de ejemplo distinto es decisión del showcase, no defecto. | | | Desviación (propuesta) | | | — | | #### Toast / Snackbar [Section titled “Toast / Snackbar”](#toast--snackbar) **Código:** `src/ui/atoms/Toast/index.tsx` · **Figma:** [`102:142`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=102-142) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Toast / Snackbar](/design/parity/figma/atoms/figma-toast.png)](/design/parity/figma/atoms/figma-toast.png "figma Toast / Snackbar — abrir a tamaño completo") | [![app Toast / Snackbar](/design/parity/screenshots/playground/toast.png)](/design/parity/screenshots/playground/toast.png "app Toast / Snackbar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color (acción):** Residuo cerrado — la acción **“Deshacer”** ya se pinta en azul claro legible **#7FB0FF** sobre la superficie oscura (corregido en #546), igual que Figma; antes leía casi blanco (`primary.light`). - **Color:** Superficie del toast **#1F2933** opaca y texto del mensaje blanco en ambos paneles. - **Layout:** Estructura idéntica — icono de estado + mensaje (flex-grow) + acción a la derecha. - **Estados:** Las 4 variantes (info/success/warning/danger) con sus roles de color correctos. - **Iconos:** Badges circulares por estado (decisión del showcase); colores de rol correctos. | | | Desviación (propuesta) | | | — | | #### Tooltip [Section titled “Tooltip”](#tooltip) **Código:** `src/ui/atoms/Tooltip/index.tsx` · **Figma:** [`102:150`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=102-150) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Tooltip](/design/parity/figma/atoms/figma-tooltip.png)](/design/parity/figma/atoms/figma-tooltip.png "figma Tooltip — abrir a tamaño completo") | [![app Tooltip](/design/parity/screenshots/playground/tooltip.png)](/design/parity/screenshots/playground/tooltip.png "app Tooltip — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Fondo del tooltip **#1F2933** medido en ambos paneles (Figma 66% del pixel-count; app muestreado en la píldora “Arriba” → #1F2933). Opaco, sin transparencia. Texto blanco. Fix de tooltip opaco confirmado. - **Iconos:** Caret/flecha direccional presente y correctamente orientado en las cuatro variantes (top/bottom/left/right). - **Layout:** El showcase de /playground demuestra las 4 posiciones alrededor de un botón “Objetivo” (outlined, borde primary) — decisión del showcase, no defecto. El Figma solo muestra un tooltip individual con texto de ejemplo “Editar”. - **Tipografía/Espaciado/Bordes:** Píldora redondeada con padding compacto, sin borde, coherente con el diseño. Sin residuo real detectado. | | | Desviación (propuesta) | | | — | | ### Moléculas [Section titled “Moléculas”](#moléculas) Código en `src/ui/molecules/**`. #### Balance Card [Section titled “Balance Card”](#balance-card) **Código:** `src/ui/molecules/BalanceCard/index.tsx` | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Balance Card](/design/parity/figma/molecules/figma-balance-card-context.png)](/design/parity/figma/molecules/figma-balance-card-context.png "figma Balance Card — abrir a tamaño completo") | [![app Balance Card](/design/parity/screenshots/playground/balance-card.png)](/design/parity/screenshots/playground/balance-card.png "app Balance Card — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Variante primary medida en #0052CC (fondo) con botón tonal #E6EEFA + texto azul — igual que la card azul de Figma. Cards planas blancas para el resto de variantes. - **Bordes:** Hairline de tarjeta plana en #E5E7EB, radios redondeados; sin sombras. Correcto. - **Iconos:** Botón “actualizar” con icono de refresco en brand #0052CC; adorno discreto arriba-derecha, coherente con Figma. - **Tipografía:** “Saldo disponible” en gris secundario, monto en jerarquía fuerte (#1F2933 en cards blancas / blanco sobre primary), sufijo MXN atenuado (#9CA3AF). Sin desvíos. - **Estados:** Skeleton “cargando” con barra pálida \~#F6F9FB (≈#F0F4F8); estado primary destacado. Todos presentes. - **Layout:** El panel APP muestra variantes extra de ejemplo (con moneda, crédito $8,000, cargando) que no aparecen en el contexto de Figma — decisión del showcase, no defecto. La estructura label→monto→acción replica la card de Figma. | | | Desviación (propuesta) | | | — | | #### Cart Bar [Section titled “Cart Bar”](#cart-bar) **Código:** `src/ui/molecules/CartBar/index.tsx` · **Figma:** [`44:80`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=44-80) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Cart Bar](/design/parity/figma/molecules/figma-cart-bar.png)](/design/parity/figma/molecules/figma-cart-bar.png "figma Cart Bar — abrir a tamaño completo") | [![app Cart Bar](/design/parity/screenshots/playground/cart-bar.png)](/design/parity/screenshots/playground/cart-bar.png "app Cart Bar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Barra medida `#0052CC` en ambos paneles (Figma 52.5% de píxeles, APP 5.5% por ser captura de página completa); azul de marca canónico correcto. Fondo del showcase `#F5F7FA`. - **Tipografía:** Bloque apilado “N artículos” (secundario, más claro) sobre “$monto” (blanco, peso fuerte) reproduce la jerarquía de Figma. - **Bordes:** Esquinas redondeadas (\~8px) en ambos; barra plana sin sombra. Coincide. - **Iconos:** Carrito relleno blanco a la izquierda en ambos paneles. Coincide. - **Layout:** Icono + texto a la izquierda, CTA a la derecha. Estructura idéntica a Figma. - **Estados:** El CTA difiere por texto de ejemplo (“Ver carrito” en Figma vs “Cobrar” en la app) — decisión del showcase, no defecto. La app añade una variante “vacío · deshabilitado” (sin referencia en Figma) que atenúa el texto del CTA a gris pero mantiene la barra en azul pleno; comportamiento razonable para el estado, sin residuo del componente. | | | Desviación (propuesta) | | | — | | #### Customer Row [Section titled “Customer Row”](#customer-row) **Código:** `src/ui/molecules/CustomerRow/index.tsx` | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Customer Row](/design/parity/figma/molecules/figma-customer-row.png)](/design/parity/figma/molecules/figma-customer-row.png "figma Customer Row — abrir a tamaño completo") | [![app Customer Row](/design/parity/screenshots/playground/customer-row.png)](/design/parity/screenshots/playground/customer-row.png "app Customer Row — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Avatar con tinte `#E6EEFA` medido en ambos paneles (canónico `palette.primary.light`) e iniciales en azul de marca. Nombre `#1F2933` (medido `#454E56` en bordes por antialias), teléfono gris secundario, chevron `#6B7280`. Todo alineado. - **Iconos:** Chevron de acción en gris, coincide con Figma. - **Layout:** Avatar + bloque nombre/teléfono + chevron a la derecha; misma estructura y proporciones que el diseño. - **Estados:** El panel APP añade una variante `SELECTED` con fondo tintado (`#E1E9F6`, familia primary.light) y esquinas redondeadas — es un estado del showcase que sigue el patrón de selección con tinte, no un defecto. Figma solo muestra el estado default. - Sin residuo real del componente. | | | Desviación (propuesta) | | | — | | #### Denomination Row [Section titled “Denomination Row”](#denomination-row) **Código:** `src/ui/molecules/DenominationRow/index.tsx` | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Denomination Row](/design/parity/figma/molecules/figma-denomination-row.png)](/design/parity/figma/molecules/figma-denomination-row.png "figma Denomination Row — abrir a tamaño completo") | [![app Denomination Row](/design/parity/screenshots/playground/denomination-row.png)](/design/parity/screenshots/playground/denomination-row.png "app Denomination Row — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado — residuo de subtotal cerrado)*- **Color:** El subtotal ahora coincide. En la captura fresca el `$0.00` de la fila VACÍO se pinta en gris atenuado (\~#6B7280, `text.secondary`), claramente más claro que el label `$500.00` — igual que el gris del `$0.00` de Figma. Los subtotales con valor (`$4,000.00`, etc.) van en oscuro `#1F2933`. Cerrada la diferencia que la auditoría 07-23 dejaba como residuo aceptado. - **Iconos/Layout:** Separadores `×` y `=` como glifos grises `text.secondary`, igual que Figma. ✅ - **Bordes:** Input blanco con hairline #E5E7EB y esquinas redondeadas. ✅ - **Layout:** Fila flex `label · × · input · = · subtotal`, subtotal alineado a la derecha (`ml:auto`), anchos de label e input consistentes. ✅ - **Tipografía:** `subtitle2` label/subtotal, `body2` separadores. La diferencia de formato `$500` (Figma) vs `$500.00` (app) es dato de ejemplo del showcase, no defecto. | | | Desviación (propuesta) | | | — | | #### Empty State [Section titled “Empty State”](#empty-state) **Código:** `src/ui/molecules/EmptyState/index.tsx` · **Figma:** [`99:151`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=99-151) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Empty State](/design/parity/figma/molecules/figma-empty-state.png)](/design/parity/figma/molecules/figma-empty-state.png "figma Empty State — abrir a tamaño completo") | [![app Empty State](/design/parity/screenshots/playground/empty-state.png)](/design/parity/screenshots/playground/empty-state.png "app Empty State — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Tinte del contenedor de icono medido `#F0F4F8` en Figma y app; glifo de adorno `#9CA3AF` en ambos. Título `#1F2933`, descripción gris medio (`#6B7280`, lectura clareada por antialias del texto fino). Botón relleno `#0052CC` idéntico en los dos paneles; botón outlined con borde y texto azul de marca. - **Bordes:** Contenedor de icono con esquinas redondeadas + botón outlined con borde sólido 1px; botón relleno sin borde. Coincide. - **Iconos:** Contenedor tintado con icono de adorno centrado, patrón correcto. El icono de ejemplo difiere (regalo en Figma / etiqueta en la variante “con acciones” del showcase) — decisión del showcase, no defecto. - **Layout:** Estructura vertical centrada contenedor-de-icono → título → descripción → fila de acciones (outlined + relleno), reproducida fielmente. Las variantes adicionales del showcase (búsqueda vacía, sin descripción) son ejemplos del playground. - **Estados:** Botones primario/outlined con el patrón cromático correcto; sin residuo detectable. | | | Desviación (propuesta) | | | — | | #### Held-order Card [Section titled “Held-order Card”](#held-order-card) **Código:** `src/ui/molecules/HeldOrderCard/index.tsx` | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Held-order Card](/design/parity/figma/molecules/figma-held-order-card.png)](/design/parity/figma/molecules/figma-held-order-card.png "figma Held-order Card — abrir a tamaño completo") | [![app Held-order Card](/design/parity/screenshots/playground/held-order-card.png)](/design/parity/screenshots/playground/held-order-card.png "app Held-order Card — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación)*- **Color:** Botón `Reanudar venta` tonal medido #E6EEFA con texto azul de marca #0052CC. Avatar `SR` fill #E6EEFA + iniciales #0052CC. Folio `#H-2271` en azul de marca. Nombre/meta en grises canónicos, precio en negrita oscuro. Coincide. - **Bordes:** Tarjetas planas con hairline #E5E7EB y radio redondeado; chips (`Hace 4 min`, `En espera por…`) con borde neutro tipo StatusBadge. Sin sombras pesadas. - **Estados:** Variante `Reanudar deshabilitado` con fill/texto disabled gris (#E5E7EB/#9CA3AF) correctamente aplicada; variantes Default y Con artículos consistentes. - **Layout:** El showcase presenta tarjetas compactas (avatar arriba-derecha, chips en fila, meta como badges) frente a la tarjeta expandida de Figma; es la decisión de composición del showcase, no residuo del componente. Estructura, roles de color y patrón de botón tonal coinciden. - **Iconos/Tipografía:** Textos de ejemplo (`Carlos Méndez`, `6 art.`, `LR`) distintos por ser datos del showcase; no computa como defecto.Sin residuo real del componente. | | | Desviación (propuesta) | | | — | | #### KPI Stat Card [Section titled “KPI Stat Card”](#kpi-stat-card) **Código:** `src/ui/molecules/KpiStatCard/index.tsx` · **Figma:** [`99:63`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=99-63) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma KPI Stat Card](/design/parity/figma/molecules/figma-kpi-stat-card.png)](/design/parity/figma/molecules/figma-kpi-stat-card.png "figma KPI Stat Card — abrir a tamaño completo") | [![app KPI Stat Card](/design/parity/screenshots/playground/kpi-stat-card.png)](/design/parity/screenshots/playground/kpi-stat-card.png "app KPI Stat Card — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Iconos (delta):** Residuo cerrado — el indicador de delta ya usa **triángulo relleno ▲/▼** (corregido en #546), igualando el glifo de Figma; antes era un chevron fino. - **Color:** Contenedor de icono tintado **#E6EEFA** (primary.light), valor **#1F2933**, label gris de adorno; success verde ≈ **#10B981** y danger **#EF4444** con tinte -soft en el contenedor. - **Layout:** label + icono tintado arriba, valor grande tabular, delta con flecha+% abajo, tarjeta plana con hairline. - **Salvedad:** Las etiquetas/valores por variante son decisión del showcase, no defecto. | | | Desviación (propuesta) | | | — | | #### List Row [Section titled “List Row”](#list-row) **Código:** `src/ui/molecules/ListRow/index.tsx` · **Figma:** [`41:60`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=41-60) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma List Row](/design/parity/figma/molecules/figma-list-row.png)](/design/parity/figma/molecules/figma-list-row.png "figma List Row — abrir a tamaño completo") | [![app List Row](/design/parity/screenshots/playground/list-row.png)](/design/parity/screenshots/playground/list-row.png "app List Row — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación)*- **Color:** Contenedor de icono con tinte `#E6EEFA` y glifo `#0052CC` medidos idénticos en ambos paneles. Badge Info con fill `#EFF6FF` (rol info) + texto azul, igual que Figma. - **Iconos:** Residuo del chevron **cerrado** — ahora se pinta en azul de marca (`primary.main`, `src/ui/molecules/ListRow/index.tsx:145`). En el showcase el chevron es de trazo algo más fino que el de Figma (grosor de icono), diferencia visual menor. - **Estados:** Pressed = tinte `#E6EEFA`; Selected = `#E1E9F6` (tinte info algo más profundo para diferenciar del pressed) — patrón de selección con tinte redondeado correcto. Los estados `pressed`/`selected` extra y las etiquetas de sección son decisión del showcase. - **Layout:** Estructura icono-tintado + título/subtítulo + slot de trailing (chevron / valor / badge) consistente con el diseño. Las filas del showcase van sobre el fondo `#F5F7FA` en vez de tarjetas blancas con hairline; es el encuadre del playground, no del componente. - **Tipografía:** Título en oscuro, secundario en gris y valor `$1,234.00` en oscuro — coinciden. | | | Desviación (propuesta) | | | — | | #### Product Card [Section titled “Product Card”](#product-card) **Código:** `src/ui/molecules/ProductCard/index.tsx` · **Figma:** [`44:50`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=44-50) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Product Card](/design/parity/figma/molecules/figma-product-card.png)](/design/parity/figma/molecules/figma-product-card.png "figma Product Card — abrir a tamaño completo") | [![app Product Card](/design/parity/screenshots/playground/product-card.png)](/design/parity/screenshots/playground/product-card.png "app Product Card — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** *(re-auditado tras remediación — commit `d515d29`)*- **Color:** Precio azul de marca correcto — medido **#0052CC** en ambos paneles. Título ≈#1F2933 y hairline #E5E7EB confirmados. Residuo de precio **cerrado**. - **Estados (stock disponible):** Residuo **cerrado**. “24 en stock” ahora se pinta como **StatusBadge success** (pill verde soft), en vez de caption gris plano (`src/ui/molecules/ProductCard/index.tsx:106-118`, ``), reproduciendo la pill verde de Figma. Stock bajo cae a la variante `warning` (ámbar). - **Estados (Agotado):** Residuos remanentes (menores, no bloqueantes): la app resuelve “sin stock” con StatusBadge danger en la **esquina superior-izquierda sobre la imagen** (`index.tsx:85-89`) más **card a opacity 0.6** (`index.tsx:64`), mientras Figma pone la pill roja **debajo del precio** a opacidad plena. La atenuación global lava el precio azul. Diferencia de tratamiento/posición, no de token; se deja como residuo aceptado. - **Bordes/Layout:** Tarjeta plana + hairline, placeholder 1:1 y título coinciden. El borde azul del estado “Seleccionado” es un estado extra del showcase (no en Figma), correcto. - **Iconos:** Botón “+” relleno sólido azul de marca `#0052CC` correcto (feature POS `onAdd`; Figma no lo dibuja); glifo placeholder `#9CA3AF` es decisión del showcase. - **Residuo remanente (aceptado):** posición de la pill Agotado (sobre imagen vs bajo precio) y opacity 0.6 de la tarjeta agotada. | | | Desviación (propuesta) | | | 🔶 **Propuesta** — el estado **Agotado atenúa la tarjeta a alpha 0.6** (y la vuelve no-interactiva). *Figma:* añadir un estado OutOfStock a 60% de opacidad a la variante. *(Espejo de la desviación aceptada en móvil.)* [ver desviaciones](/design/parity/intentional-deviations/) | | #### Profile Card [Section titled “Profile Card”](#profile-card) **Código:** `src/ui/molecules/ProfileCard/index.tsx` | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Profile Card](/design/parity/figma/molecules/figma-profile-card-context.png)](/design/parity/figma/molecules/figma-profile-card-context.png "figma Profile Card — abrir a tamaño completo") | [![app Profile Card](/design/parity/screenshots/playground/profile-card.png)](/design/parity/screenshots/playground/profile-card.png "app Profile Card — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Avatar con tinte `#E6EEFA` e iniciales `#0052CC` (medido) — patrón avatar tinte+iniciales canónico. Nombre `#1F2933`, rol gris \~`#6B7280` (medido). Tarjeta default blanca `#FFFFFF`. - **Bordes:** Estado `selected` con borde azul de marca `#0052CC` 1px (medido), sin relleno; default plano con hairline claro. Correcto. - **Iconos:** N/A — el avatar usa iniciales “AV”, no icono. - **Layout:** Avatar circular a la izquierda + bloque nombre/rol; dos estados (default / selected) en el showcase. Estructura coincide con el patrón de selección con borde. - **Estados:** default y selected verificados; el borde marca la selección (no tinte de fondo), consistente con list-row/tarjeta seleccionable.*Salvedad: la referencia Figma del par es la pantalla “Selecciona tu tienda” (tarjetas de tienda); el panel APP es el showcase del molecule ProfileCard con datos de ejemplo “Ana Vega / Gerente de tienda” — el contenido de ejemplo es decisión del showcase, no defecto. Sin residuo real del componente.* | | | Desviación (propuesta) | | | — | | #### Provider Tile [Section titled “Provider Tile”](#provider-tile) **Código:** `src/ui/molecules/ProviderTile/index.tsx` · **Figma:** [`48:63`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=48-63) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Provider Tile](/design/parity/figma/molecules/figma-provider-tile.png)](/design/parity/figma/molecules/figma-provider-tile.png "figma Provider Tile — abrir a tamaño completo") | [![app Provider Tile](/design/parity/screenshots/playground/provider-tile.png)](/design/parity/screenshots/playground/provider-tile.png "app Provider Tile — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Estado Activo medido en app — borde `#004FCB ≈ #0052CC` (primary.main, 2px sólido) y fondo `#E6EEFA` (palette.primary.light); Figma activo mide borde `#0043C8` / fondo `#E6EEFA`. Glifo y label recoloreados a primary.main. Default = superficie blanca + hairline `divider`. Todo cableado por rol en `src/ui/molecules/ProviderTile/index.tsx`. - **Layout:** Estructura idéntica a Figma — contenedor de icono redondeado tintado (56×56, radius.md) apilado sobre label centrado; tile 112px min, radius.lg. No es una fila-badge horizontal. - **Estados:** Default (tinte neutral `surface.variant` en el contenedor, glifo `text.secondary`) y Activo (tinte primary.light + anillo 2px + recoloreado a primary) reproducen las variantes Figma “Default”/“Activo”. - **Iconos:** El showcase usa un glifo tipo tarjeta mientras Figma muestra un teléfono — decisión de icono de ejemplo del showcase, no defecto (`icon` es prop). - **Bordes:** Figma dibuja el tile Default con una sombra suave elevada; la app lo aplana a blanco + hairline, coherente con la remediación “tarjetas planas+hairline” — intención de DS, no residuo. | | | Desviación (propuesta) | | | — | | #### Quantity Stepper [Section titled “Quantity Stepper”](#quantity-stepper) **Código:** `src/ui/molecules/QuantityStepper/index.tsx` · **Figma:** [`44:69`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=44-69) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Quantity Stepper](/design/parity/figma/molecules/figma-quantity-stepper.png)](/design/parity/figma/molecules/figma-quantity-stepper.png "figma Quantity Stepper — abrir a tamaño completo") | [![app Quantity Stepper](/design/parity/screenshots/playground/quantity-stepper.png)](/design/parity/screenshots/playground/quantity-stepper.png "app Quantity Stepper — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Fondo de botones tintado `#E6EEFA` (palette.primary.light) e iconos −/+ en azul de marca `#0052CC` (primary.main), medidos en el panel APP; el dígito central va en tinta oscura. Coincide con el diseño. El origen `src/ui/molecules/QuantityStepper/index.tsx` cablea `primary.light` / `primary.main` correctamente. - **Estados:** Fila “editable” con ambos botones tintados y “en mínimo” con el − deshabilitado en gris (`action.disabledBackground` `#E5E7EB` + `text.disabled` `#9CA3AF`) y el + activo azul; replica exactamente las dos filas de Figma (habilitado / − deshabilitado en gris). - **Bordes:** Botones cuadrado-redondeado usando `theme.shape.borderRadius` (canónico). Coincide. - **Iconos:** −/+ con trazo azul de marca; sin defecto. - **Layout:** Estructura \[−] \[valor] \[+] con separación uniforme; igual que Figma. - **Nota de medición:** La fuente Figma (176 px) diluye el azul al reescalar (lecturas \~`#145FD0`/`#F0F4F8`), pero es artefacto de resolución, no discrepancia; el APP usa los tokens canónicos correctos. | | | Desviación (propuesta) | | | — | | #### Summary Row [Section titled “Summary Row”](#summary-row) **Código:** `src/ui/molecules/SummaryRow/index.tsx` · **Figma:** [`43:66`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=43-66) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Summary Row](/design/parity/figma/molecules/figma-summary-row.png)](/design/parity/figma/molecules/figma-summary-row.png "figma Summary Row — abrir a tamaño completo") | [![app Summary Row](/design/parity/screenshots/playground/summary-row.png)](/design/parity/screenshots/playground/summary-row.png "app Summary Row — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación — commit `d515d29`)*- **Color:** El TOTAL standalone mide exactamente **#0052CC** en ambos paneles (azul de marca canónico). El acento se aplica vía `tone="primary"` en el showcase — **no** vía `emphasis`. Decisión de API deliberada: `emphasis` controla solo peso/tamaño; el color lo decide `tone` (`default` = #1F2933, `success` = #10B981, `primary` = #0052CC). Así el mismo componente pinta el TOTAL en azul aquí pero **oscuro** en el POS Cash Tender (cuyo frame lo quiere en tinta oscura), sin regresión cruzada. - **Tipografía:** Coincide — TOTAL en negrita/mayor (h5) y label en subtitle1; Subtotal en body2/subtitle2. Jerarquía correcta. - **Bordes:** Coincide — divisor hairline #E5E7EB sobre la fila del total. - **Layout:** Coincide — label a la izquierda, valor a la derecha, alineación baseline, `space-between`. - **Estados (showcase):** La fila “Descuento” en verde #10B981 (`tone="success"`) y el ejemplo negativo −$50.00 son ejemplos del showcase (no en el panel Figma); no son defecto. | | | Desviación (propuesta) | | | — | | ### Organismos [Section titled “Organismos”](#organismos) Código en `src/ui/organisms/**`. #### Breadcrumbs [Section titled “Breadcrumbs”](#breadcrumbs) **Código:** `src/ui/organisms/Breadcrumbs/Breadcrumbs.tsx` · **Figma:** [`94:21`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=94-21) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Breadcrumbs](/design/parity/figma/organisms/figma-breadcrumbs.png)](/design/parity/figma/organisms/figma-breadcrumbs.png "figma Breadcrumbs — abrir a tamaño completo") | [![app Breadcrumbs](/design/parity/screenshots/playground/breadcrumbs.png)](/design/parity/screenshots/playground/breadcrumbs.png "app Breadcrumbs — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación)*- **Color:** Enlaces intermedios `#6B7280` (medido) = Figma `#6B7280` ✓. Último nivel resaltado `#1F2933` = Figma ✓. Fondo showcase `#F5F7FA` es la superficie del /playground, no defecto. - **Iconos:** Residuo del chevron **cerrado** — el separador ahora fija `color: 'text.disabled'` (#9CA3AF, gris de adorno) en `src/ui/organisms/Breadcrumbs/Breadcrumbs.tsx:37`, igual que Figma. - **Tipografía:** Último nivel en peso mayor (bold) tal como Figma; enlaces navegables en regular. Coincide. - **Layout/Estados:** Patrón trail con separador chevron, último nivel resaltado y `aria-current="page"` correcto. El showcase muestra dos ejemplos (ruta corta + ruta profunda), decisión del showcase. | | | Desviación (propuesta) | | | — | | #### Bulk Action Bar [Section titled “Bulk Action Bar”](#bulk-action-bar) **Código:** `src/ui/organisms/DataTable/BulkActionBar.tsx` · **Figma:** [`99:122`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=99-122) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Bulk Action Bar](/design/parity/figma/organisms/figma-bulk-action-bar.png)](/design/parity/figma/organisms/figma-bulk-action-bar.png "figma Bulk Action Bar — abrir a tamaño completo") | [![app Bulk Action Bar](/design/parity/screenshots/playground/bulk-action-bar.png)](/design/parity/screenshots/playground/bulk-action-bar.png "app Bulk Action Bar — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación — commit `d515d29`)*- **Color:** Tint de fondo idéntico — Figma #E6EEFA vs App #E6EEFA (medido; `theme.palette.action.selected` en `BulkActionBar.tsx:54`). Editar/Exportar en primary #0052CC, Eliminar en rojo. Coincide. - **Tipografía:** El contador “3 seleccionados” ya se pinta en **azul de marca + bold** (`primary.main`, `BulkActionBar.tsx:72`), igual que Figma. Residuo cerrado. - **Bordes:** Residuo cerrado — se añadió `border: 1px solid primary.main` (#0052CC) alrededor de la barra tonal (`BulkActionBar.tsx`), reproduciendo el contorno azul de Figma en los cuatro cantos. - **Iconos:** El elemento líder difiere por diseño: Figma muestra un **checkbox relleno azul con check** (indicador de selección); la App muestra un **icono X** (afordancia `onClear`/Xmark, decisión de interacción del componente, no defecto de estilo). - **Layout:** Estructura coincide — líder + contador (flex:1) + acciones a la derecha, radio redondeado. La segunda fila (“selección amplia”, 12 seleccionados) es variante de demostración del showcase.Neto: paridad de color, borde y estructura; el único delta es la afordancia líder (X de limpiar vs checkbox), decisión de interacción deliberada. | | | Desviación (propuesta) | | | — | | #### Chart — Bar [Section titled “Chart — Bar”](#chart--bar) **Código:** `src/ui/organisms/charts/*` · **Figma:** [`101:101`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=101-101) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Chart — Bar](/design/parity/figma/organisms/figma-chart-bar.png)](/design/parity/figma/organisms/figma-chart-bar.png "figma Chart — Bar — abrir a tamaño completo") | [![app Chart — Bar](/design/parity/screenshots/playground/chart-bar.png)](/design/parity/screenshots/playground/chart-bar.png "app Chart — Bar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Bordes:** Residuo cerrado — las barras ya salen con **radio superior redondeado** (\~8-10px, corregido en #546 vía `borderRadius` en la serie), igual que Figma; antes tenían esquinas rectas. - **Color:** Barras y swatch de leyenda en `#0052CC` (azul de marca), fondo de card blanco. - **Layout:** Ejes X/Y, gridlines, ticks y leyenda “Ventas” son la variante rotulada “BARRAS CON EJES Y LEYENDA” del showcase, no defecto. - **Tipografía:** Título `#1F2933`, labels de eje en gris tenue — consistente con los tokens. - **Estados:** N/A (chart estático). | | | Desviación (propuesta) | | | — | | #### Chart — Donut [Section titled “Chart — Donut”](#chart--donut) **Código:** `src/ui/organisms/charts/*` · **Figma:** [`101:114`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=101-114) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Chart — Donut](/design/parity/figma/organisms/figma-chart-donut.png)](/design/parity/figma/organisms/figma-chart-donut.png "figma Chart — Donut — abrir a tamaño completo") | [![app Chart — Donut](/design/parity/screenshots/playground/chart-donut.png)](/design/parity/screenshots/playground/chart-donut.png "app Chart — Donut — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Layout:** Residuo cosmético cerrado — los arcos ya se **tocan sin separación** (`padAngle` eliminado, corregido en #546), igual que Figma. Dona con leyenda vertical a la derecha y grosor de anillo equivalente. - **Color:** Serie idéntica en ambos paneles — Bebidas `#0052CC`, Abarrotes `#3B82F6`, Botanas `#10B981`, Otros `#F59E0B`, en el mismo orden. - **Tipografía:** Título “Categorías” `#1F2933`, labels de leyenda en gris; total central “9,500” en bold + “Total” en gris. - **Iconos:** Marcadores de leyenda como puntos rellenos del color de serie. - **Salvedad:** El total central es la variante del showcase, no defecto. | | | Desviación (propuesta) | | | — | | #### Chart — Line [Section titled “Chart — Line”](#chart--line) **Código:** `src/ui/organisms/charts/*` · **Figma:** [`101:91`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=101-91) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Chart — Line](/design/parity/figma/organisms/figma-chart-line.png)](/design/parity/figma/organisms/figma-chart-line.png "figma Chart — Line — abrir a tamaño completo") | [![app Chart — Line](/design/parity/screenshots/playground/chart-line.png)](/design/parity/screenshots/playground/chart-line.png "app Chart — Line — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado: relleno de área ahora presente)*- **Color:** ✅ Trazo de serie #0052CC (marca) en ambos paneles. Relleno de área bajo la curva medido #E6EEFA (primary.light). - **Layout:** ✅ Residuo cerrado — la app **ya renderiza el área rellena** (#E6EEFA bajo la curva) igual que Figma; ya no es línea pura. `LineChart` activa el fill. - **Iconos/Ejes:** ✅ Ejes (8–16), gridlines (0–80) y leyenda “Ventas” son de la variante del showcase (“línea con ejes, gridlines y leyenda”), no defecto vs la miniatura de Figma. - **Estados:** ✅ Variantes Vacío (“Sin datos para mostrar”) y Cargando (skeleton #F0F4F8) presentes y correctas, sin equivalente en Figma. - **Tipografía/Bordes:** ✅ Título “Ventas por hora” #1F2933, tarjeta plana + hairline; eje/grid en tokens de texto/#E5E7EB. | | | Desviación (propuesta) | | | — | | #### Command Palette [Section titled “Command Palette”](#command-palette) **Código:** `src/ui/organisms/CommandPalette/index.tsx` · **Figma:** [`113:651`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=113-651) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Command Palette](/design/parity/figma/organisms/figma-command-palette-context.png)](/design/parity/figma/organisms/figma-command-palette-context.png "figma Command Palette — abrir a tamaño completo") | [![app Command Palette](/design/parity/screenshots/playground/command-palette.png)](/design/parity/screenshots/playground/command-palette.png "app Command Palette — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Iconos:** Residuo cerrado — cada icono de fila ya va dentro de un **chip redondeado tintado #F0F4F8** (corregido en #546), igual que Figma; antes el icono quedaba desnudo sobre blanco. - **Tipografía:** Encabezados de sección ya en estilo **overline** (MAYÚSCULAS + tracking, gris): “PRODUCTOS/VENTAS/EMPLEADOS”; antes eran Title Case grandes. - **Layout:** Filas de **dos líneas** (título + metadata: “SKU 7501 · $18.00”, “12 ago · $540.00 · Pagado”), replicando Figma. - **Estados:** **Chip ESC** presente a la derecha del campo de búsqueda. - **Color:** Tint de selección `#E5EDFA` ≈ `primary.light`, tarjeta blanca + hairline, campo con foco azul. - **Salvedad:** El glifo de producto (etiqueta) y la consulta escrita son decisiones del showcase. | | | Desviación (propuesta) | | | — | | #### Confirm Dialog [Section titled “Confirm Dialog”](#confirm-dialog) **Código:** `src/ui/organisms/ConfirmDialog/index.tsx` · **Figma:** [`102:122`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=102-122) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Confirm Dialog](/design/parity/figma/organisms/figma-confirm-dialog.png)](/design/parity/figma/organisms/figma-confirm-dialog.png "figma Confirm Dialog — abrir a tamaño completo") | [![app Confirm Dialog](/design/parity/screenshots/playground/confirm-dialog.png)](/design/parity/screenshots/playground/confirm-dialog.png "app Confirm Dialog — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Icono de peligro con contenedor tintado red-soft `#FEF2F2` y triángulo `#EF4444` medidos idénticos en ambos paneles. Botón Eliminar en relleno rojo sólido `#EF4444` (app y Figma iguales). Botón Cancelar outlined con azul de marca (`#0556CD` app / `#0153CC` Figma ≈ `#0052CC`). Título oscuro y cuerpo gris coinciden. - **Iconos:** Patrón de contenedor tintado + glifo relleno (triángulo de advertencia) correcto y consistente con el resto de organismos remediados. - **Bordes:** Tarjeta plana blanca con esquinas redondeadas; Cancelar con borde 1px sólido azul, Eliminar sin borde. Coincide. - **Layout:** Dos botones a ancho completo lado a lado (\~50/50), Cancelar izquierda / acción destructiva derecha. Estructura idéntica. - **Estados:** Botón destructivo en rol danger, secundario outlined — patrón correcto. - **Salvedad:** Los dos paneles están a escalas muy distintas (Figma 448×308 recorte; app dialog pequeño centrado sobre overlay). La sombra sutil del modal en la app es propia del componente overlay y no es defecto. Sin residuo real que reportar. | | | Desviación (propuesta) | | | — | | #### Data Table [Section titled “Data Table”](#data-table) **Código:** `src/ui/organisms/DataTable/DataTable.tsx` · **Figma:** [`95:59`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=95-59) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Data Table](/design/parity/figma/organisms/figma-data-table.png)](/design/parity/figma/organisms/figma-data-table.png "figma Data Table — abrir a tamaño completo") | [![app Data Table](/design/parity/screenshots/playground/data-table.png)](/design/parity/screenshots/playground/data-table.png "app Data Table — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Estados (selección masiva):** Residuo cerrado — presente la **columna de checkbox de selección** con checkbox de encabezado (select-all + estado **indeterminado** ⊟), checkboxes por fila y **BulkActionBar** (“2 seleccionados” · Editar/Exportar/Eliminar) sobre las filas seleccionadas (corregido en #546). Checkbox seleccionado = relleno sólido `#0052CC` con check blanco. - **Color:** Fila seleccionada con tinte `#E5EDFA` ≈ `#E6EEFA`; divisores `#E5E7EB`; StatusBadges como píldoras tinte-soft (Activo verde, Bajo amarillo, Agotado rojo) coinciden en patrón y tono. - **Layout:** Columnas (avatar+iniciales / Producto / SKU / Categoría / Precio / Stock / Estado / acción) y header hairline coinciden; estados Cargando/skeleton y Vacío son composiciones del showcase. - **Iconos:** El icono de editar como `IconButton` outlined por fila es decisión del showcase, no defecto del organismo. | | | Desviación (propuesta) | | | — | | #### Dialog / Modal [Section titled “Dialog / Modal”](#dialog--modal) **Código:** `src/ui/organisms/Dialog/index.tsx` · **Figma:** [`102:91`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=102-91) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Dialog / Modal](/design/parity/figma/organisms/figma-dialog.png)](/design/parity/figma/organisms/figma-dialog.png "figma Dialog / Modal — abrir a tamaño completo") | [![app Dialog / Modal](/design/parity/screenshots/playground/dialog.png)](/design/parity/screenshots/playground/dialog.png "app Dialog / Modal — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Layout:** Residuo cerrado — el **divisor sobre el footer** (línea arriba de Cancelar/Guardar cambios) ya está presente (corregido en #546), además del divisor de header; ambos hairlines coinciden con Figma. - **Color:** Guardar cambios `#0052CC` sólido + texto blanco; Cancelar outlined con borde/texto azul; título `#1F2933`; ✕ gris; scrim gris. - **Bordes:** Campos con relleno blanco + hairline `#E5E7EB` redondeado, label estático arriba. - **Estados:** El foco del 2º campo en Figma es decisión de estado del showcase, no defecto. - **Iconos/Tipografía:** ✕ de cierre gris a la derecha del título; título/labels bold, helper en gris. | | | Desviación (propuesta) | | | — | | #### Filter Bar [Section titled “Filter Bar”](#filter-bar) **Código:** `src/ui/organisms/DataTable/FilterBar.tsx` · **Figma:** [`99:73`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=99-73) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Filter Bar](/design/parity/figma/organisms/figma-filter-bar.png)](/design/parity/figma/organisms/figma-filter-bar.png "figma Filter Bar — abrir a tamaño completo") | [![app Filter Bar](/design/parity/screenshots/playground/filter-bar.png)](/design/parity/screenshots/playground/filter-bar.png "app Filter Bar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Trigger “Filtros” medido en #0052CC (borde + texto + icono) en ambos paneles; enlace “Quitar todos” #0052CC. Coincide con azul de marca. - **Bordes:** Campo de búsqueda blanco #FFFFFF con hairline y botón outlined 1px sólido; dropdown “Selecciona una opción” con hairline. Alineado con canon. - **Estados:** Badge contador = círculo relleno SÓLIDO #0052CC + “2” blanco (excepción correcta de relleno para contadores). Chips de “Filtros aplicados” con outline hairline y close en gris adorno (\~#9CA3AF). - **Iconos:** El showcase añade icono de hamburguesa antes de “Filtros” (Figma sin icono) — decisión del showcase, no defecto. - **Layout:** Figma muestra segmented (Todos/Activos/Bajo stock); el showcase lo sustituye por dos filas de búsqueda + fila de chips “Filtros aplicados” — contenido de showcase, no residuo del componente. - **Color (nota, no defecto):** El campo de búsqueda en Figma trae relleno tintado #F0F4F8; la app lo pinta blanco #FFFFFF, consistente con la decisión canónica de campos blancos+hairline (fix verificado). | | | Desviación (propuesta) | | | — | | #### Page Header [Section titled “Page Header”](#page-header) **Código:** `src/ui/organisms/PageHeader/PageHeader.tsx` · **Figma:** [`94:31`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=94-31) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Page Header](/design/parity/figma/organisms/figma-page-header.png)](/design/parity/figma/organisms/figma-page-header.png "figma Page Header — abrir a tamaño completo") | [![app Page Header](/design/parity/screenshots/playground/page-header.png)](/design/parity/screenshots/playground/page-header.png "app Page Header — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Botón primario medido **#0052CC** en Figma y app; fondo de sección #F5F7FA / #F5F5F5 (escalas distintas, equivalente); subtítulo gris \~#868C98 acorde a #6B7280. Azul de marca ya remediado y verificado. - **Tipografía:** Título en peso alto y color oscuro (navy \~#1F2933), subtítulo secundario en gris, eyebrow en gris tenue mayúsculas — jerarquía idéntica. - **Layout:** Patrón título + subtítulo + acción primaria alineada a la derecha coincide. El panel APP muestra además las variantes *solo título* y *con breadcrumbs* — decisión del showcase de /playground, no defecto del componente. - **Bordes:** Botón con esquinas redondeadas y texto blanco `+ Nuevo producto` igual en ambos. - **Iconos:** Icono `+` de adorno dentro del botón presente en ambos paneles. - **Estados:** Sin residuo medible. Coincide. | | | Desviación (propuesta) | | | — | | #### Pagination [Section titled “Pagination”](#pagination) **Código:** `src/ui/organisms/DataTable/Pagination.tsx` · **Figma:** [`94:43`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=94-43) | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Pagination](/design/parity/figma/organisms/figma-pagination.png)](/design/parity/figma/organisms/figma-pagination.png "figma Pagination — abrir a tamaño completo") | [![app Pagination](/design/parity/screenshots/playground/pagination.png)](/design/parity/screenshots/playground/pagination.png "app Pagination — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Página activa con relleno SÓLIDO primary — medido **#0052CC** tanto en app (`205,134`) como en Figma (`65,17`), con texto blanco. Es la excepción al patrón de tinte y está bien aplicada. - **Estados:** Tiles inactivos planos blancos con hairline (#E5E7EB) y numeral oscuro; activo destaca por el relleno azul. Correcto. - **Bordes:** Esquinas redondeadas en los tiles coinciden; chevrons ‹ › como botones ghost sin relleno. - **Layout:** El showcase muestra 3 variantes (página actual / primera / última) — decisión del showcase, no defecto. Figma solo muestra el patrón base. - **Tipografía:** Numerales centrados, peso consistente entre ambos. - Sin residuo real del componente. | | | Desviación (propuesta) | | | — | | #### POS — Approval Modal [Section titled “POS — Approval Modal”](#pos--approval-modal) **Código:** `src/ui/organisms/pos/ApprovalModal/index.tsx` · **Figma:** [`104:161`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=104-161) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Approval Modal](/design/parity/figma/organisms/figma-pos-approval-modal.png)](/design/parity/figma/organisms/figma-pos-approval-modal.png "figma POS — Approval Modal — abrir a tamaño completo") | [![app POS — Approval Modal](/design/parity/screenshots/playground/pos-approval-modal.png)](/design/parity/screenshots/playground/pos-approval-modal.png "app POS — Approval Modal — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Marca #0052CC verificado (PIN dots en Figma, glifo de backspace en ambos). Lock tile warning-soft `#FFFBEB` + glifo `#F59E0B` coinciden exacto. Error `#EF4444` correcto. - **Color (residuo):** El tile de la tecla borrar usa `theme.palette.action.hover` → renderiza `#F5F5F5`, cuando el diseño pide surface-variant/skeleton `#F0F4F8` (ligero tinte azulado). Diferencia casi imperceptible pero real. Culpable: `src/ui/organisms/pos/ApprovalModal/index.tsx` L253 (`backgroundColor: theme.palette.action.hover`) — el propio comentario dice `--color-surface-variant`; conviene cablearlo al token skeleton/surface-variant. - **Layout:** Figma muestra el par Cancelar (outlined) + Autorizar (filled); la app renderiza solo el link de texto Cancelar. **No es defecto**: el organismo soporta ese par vía `requireConfirm`; el showcase elige a propósito la variante auto-submit shipped (`ApprovalModalShowcase.tsx` L43-49, sin `requireConfirm`). - **Iconos:** Lock relleno naranja y glifo backspace primary coinciden. - **Estados:** Default (dots vacíos, sin PIN tecleado) y PIN incorrecto (dots rojos + mensaje) correctos; el default de Figma con 2 dots llenos es solo un estado de entrada distinto del showcase. - **Tipografía/Bordes:** Título, subtítulo y hairline de campos sin discrepancias medibles. | | | Desviación (propuesta) | | | — | | #### POS — Cash Tender [Section titled “POS — Cash Tender”](#pos--cash-tender) **Código:** `src/ui/organisms/pos/CashTender/index.tsx` · **Figma:** [`105:200`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=105-200) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Cash Tender](/design/parity/figma/organisms/figma-pos-cash-tender.png)](/design/parity/figma/organisms/figma-pos-cash-tender.png "figma POS — Cash Tender — abrir a tamaño completo") | [![app POS — Cash Tender](/design/parity/screenshots/playground/pos-cash-tender.png)](/design/parity/screenshots/playground/pos-cash-tender.png "app POS — Cash Tender — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación — commit `d515d29`)*- **Color:** Residuo del **Total** cerrado. El monto “Total $406.00” ahora se pinta en **tinta oscura bold (#1F2933)**, no azul — se desacopló el color de `emphasis` en `SummaryRow` (color lo decide `tone`, no el énfasis), así el POS conserva el total oscuro que muestra su frame mientras el SummaryRow standalone sigue en azul vía `tone="primary"`. - **Color:** Callout **Cambio** con tinte de éxito correcto — app `#ECFDF5` vs Figma `#ECFCF5` (`success.light`), label + monto en `success.main` (`CashTender/index.tsx`). Coincide. - **Color:** Fondo de los chips de monto `#F5F5F5` coincide con Figma. - **Estados:** El botón `Confirmar` disabled = `#E5E7EB` + texto `#9CA3AF` (input vacío en el showcase); al habilitarse usa `variant="filled"` (azul sólido, como Figma). Diferencia de estado, no defecto. Ningún chip aparece seleccionado (input vacío) — no evaluable en esta captura. - **Bordes/Iconos:** Input blanco + hairline + label estático arriba; ✕ de cierre gris en ambos. Coincide. - **Datos del showcase (no defectos):** título “Pago en efectivo”/label “Recibido” vs. “Efectivo”/“Monto recibido” del Figma, y chips `$406/$500/$1000` — copy y props por defecto del showcase. | | | Desviación (propuesta) | | | — | | #### POS — Discount Sheet [Section titled “POS — Discount Sheet”](#pos--discount-sheet) **Código:** `src/ui/organisms/pos/DiscountSheet/index.tsx` · **Figma:** [`104:214`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=104-214) | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Discount Sheet](/design/parity/figma/organisms/figma-pos-discount-sheet.png)](/design/parity/figma/organisms/figma-pos-discount-sheet.png "figma POS — Discount Sheet — abrir a tamaño completo") | [![app POS — Discount Sheet](/design/parity/screenshots/playground/pos-discount-sheet.png)](/design/parity/screenshots/playground/pos-discount-sheet.png "app POS — Discount Sheet — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** *(re-auditado tras remediación)*- **Color:** Verificado y coincide. Thumb del segmented blanco `#FFFFFF` sobre track `#F5F5F5`; banner de total con tinte verde `#ECFDF5` + cifra esmeralda (residuo del highlight success **cerrado**); botón disabled `#E5E7EB` + texto `#9CA3AF`; texto principal `#2D363F`. Todos los fixes de la fase 2 se sostienen. - **Layout (feature-gap):** Falta el caption de autorización que Figma muestra bajo el campo (`Máx. 20% sin autorización`). El showcase no lo renderiza — verificar si el organism expone esa prop de límite/ayuda. - **Layout (feature-gap):** Falta el selector de **Motivo** (Figma: dropdown `Motivo: Cliente frecuente`). La app omite el campo de razón del descuento; es un elemento funcional del sheet, no solo texto de ejemplo. - **Estados:** El botón `Aplicar descuento` aparece en estado **disabled** (campo vacío en el showcase), por lo que no se compara directo el azul sólido del CTA de Figma; la geometría y el tono disabled sí coinciden. - **Bordes/Iconos:** Coinciden — card plana con hairline, campo blanco con borde suave, icono de cierre `×` en gris de adorno. - **Tipografía:** Coincide. Diferencias de etiqueta (`%/Monto/Cupón` vs `Porcentaje/Monto fijo/Cupón`, `Nuevo total` vs `Total con descuento`) son decisión del showcase, no defecto.Residuos remanentes = **feature-gaps** (selector de Motivo + caption de autorización), no defectos de token/estilo; se difieren al flujo de checkout. | | | Desviación (propuesta) | | | — | | #### POS — Result Modal [Section titled “POS — Result Modal”](#pos--result-modal) **Código:** `src/ui/organisms/pos/ResultModal/index.tsx` | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Result Modal](/design/parity/figma/organisms/figma-pos-result-modal-context.png)](/design/parity/figma/organisms/figma-pos-result-modal-context.png "figma POS — Result Modal — abrir a tamaño completo") | [![app POS — Result Modal](/design/parity/screenshots/playground/pos-result-modal.png)](/design/parity/screenshots/playground/pos-result-modal.png "app POS — Result Modal — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Azul de marca del CTA “Nueva venta” medido **#0052CC** en Figma y app (idéntico). Check de éxito **#10B981** en ambos. Halo/icono de error usan rol error **#EF4444 + #FDE8E8** (tinte \*-soft), correcto. - **Tipografía:** Título “¡Venta completada!” #1F2933 en los dos; subtítulo folio en gris secundario; pesos y jerarquía coinciden. - **Espaciado:** Estructura vertical (check → título → folio → bloque resumen → fila de acciones → CTA) idéntica en proporción. - **Bordes:** Tarjeta modal plana con esquinas redondeadas; botones outlined con borde 1px sólido azul; bloque-resumen tintado (#F7F9FC Figma / #F5F7FA app, equivalentes). - **Iconos:** Check relleno blanco sobre círculo verde igual; en la variante error, `!` blanco sobre círculo rojo con halo tenue. - **Layout:** Tres botones outlined (Imprimir/Compartir/Email) en fila + CTA sólido a ancho completo debajo, igual que Figma. - **Estados:** El panel APP añade la variante **ERROR — Reintentar** que el contexto Figma no incluye (solo muestra éxito). Es decisión del showcase para exhibir el estado de error, no un defecto del componente. | | | Desviación (propuesta) | | | — | | #### POS — Split Payment [Section titled “POS — Split Payment”](#pos--split-payment) **Código:** `src/ui/organisms/pos/SplitPayment/index.tsx` · **Figma:** [`105:253`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=105-253) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Split Payment](/design/parity/figma/organisms/figma-pos-split-payment.png)](/design/parity/figma/organisms/figma-pos-split-payment.png "figma POS — Split Payment — abrir a tamaño completo") | [![app POS — Split Payment](/design/parity/screenshots/playground/pos-split-payment.png)](/design/parity/screenshots/playground/pos-split-payment.png "app POS — Split Payment — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** *(re-auditado tras remediación)*Todos los tokens remediados verifican; el residuo es de **estado/composición del showcase**, no de color.- **Color:** Sin defecto. Medido en ambos paneles: azul de marca **#0052CC** (Total / borde “Agregar pago”), banner Restante **#FFFBEB** + texto **#F59E0B** (idéntico Figma↔app), segmented thumb **#FFFFFF** sobre contenedor **#F5F5F5**, CTA deshabilitado gris. Coincide (el tinte *warning* del Restante ya está aplicado). - **Layout:** Los paneles renderizan **estados distintos** del organism: Figma muestra el estado lleno (lista de pagos Efectivo/Tarjeta con filas tintadas #F7F9FC, íconos y X por fila, botón outlined “Agregar pago”, CTA “Finalizar venta”); la app muestra el estado de **entrada** (Total + selector segmented + input Monto + botón “Agregar” + Restante). No es defecto de remediación: es otra pantalla del mismo flujo. - **Estados:** El botón “Confirmar”/“Agregar” aparece deshabilitado (Monto vacío) con relleno tonal gris, consistente con el “Finalizar venta” deshabilitado del Figma. - **Tipografía:** Título “Pago dividido” vs “Pago mixto” — texto de ejemplo del showcase, no defecto. - **Pendiente de verificar:** con el showcase en estado vacío no se confirma en esta captura el estilo de las **filas de pago** (fondo #F7F9FC, ícono, X de eliminar) ni el botón **outlined “Agregar pago”** (borde #0052CC 1px). Se recomienda una variante del showcase con pagos ya agregados para cerrar la paridad estructural. | | | Desviación (propuesta) | | | — | | #### POS — Tender Picker [Section titled “POS — Tender Picker”](#pos--tender-picker) **Código:** `src/ui/organisms/pos/TenderPicker/index.tsx` · **Figma:** [`105:151`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=105-151) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Tender Picker](/design/parity/figma/organisms/figma-pos-tender-picker.png)](/design/parity/figma/organisms/figma-pos-tender-picker.png "figma POS — Tender Picker — abrir a tamaño completo") | [![app POS — Tender Picker](/design/parity/screenshots/playground/pos-tender-picker.png)](/design/parity/screenshots/playground/pos-tender-picker.png "app POS — Tender Picker — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** *(remediación estructural CERRADA — antes ❌)*El residuo estructural del ❌ previo está **resuelto**: el shot fresco ya no es una lista de navegación con chevron sino un selector de método idéntico en patrón al Figma.- **Layout ✅:** Presente el chip **“Total a cobrar $406.00”** con tinte #E6EEFA y monto azul bold, y el CTA **“Continuar”** relleno #0052CC al pie. Ambos coincidían faltando antes. - **Estados ✅:** Modelo de **selección** con radios — fila activa (Efectivo) con borde azul + tinte #E6EEFA + check relleno azul; filas inactivas con radio vacío. Coincide con Figma (ya no hay ChevronRight de drill-in). - **Tipografía ✅:** Subtítulos por opción presentes (“Pago en efectivo + cambio”, “Inserta, desliza o acerca”, “Saldo del comercio”, “Combina varios métodos”). - **Iconos ⚠️ (residuo conocido, baja prioridad):** Contenedor tintado #F0F4F8 sí aplicado (fix presente), pero los **glifos y su color divergen** — Figma usa iconos a color por método (billetera verde, tarjeta azul, flechas azules, `+`); la app usa glifos en gris frío de adorno (billete azul solo en el seleccionado; tarjeta/billetera/capas en gris). Iconografía divergente ya catalogada. - **Color ✅:** Canon respetado.Culpable previo (`src/ui/organisms/pos/TenderPicker/index.tsx`, sin total/selección/subtítulo/CTA) **remediado**; residuo restante = solo iconografía. | | | Desviación (propuesta) | | | — | | #### Scrim / Overlay [Section titled “Scrim / Overlay”](#scrim--overlay) **Código:** `src/ui/organisms/Scrim/index.tsx` | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — sin render exportado en `design/web/organisms/` | [![app Scrim / Overlay](/design/parity/screenshots/playground/scrim.png)](/design/parity/screenshots/playground/scrim.png "app Scrim / Overlay — abrir a tamaño completo") | | Notas | | | **ℹ️ Sin render de Figma — verificación del shot de la app.** No hay diseño exportado contra el cual medir paridad de token (opacidad exacta no verificable).- **Layout:** El shot demuestra el patrón scrim+surface: overlay negro semitransparente (\~55-60%) atenúa la página de productos, y la card modal “Editar producto” permanece **nítida** por encima. Comportamiento correcto y esperado del componente. - **Siguiente paso:** exportar el nodo de Figma para confirmar la opacidad canónica del scrim; si no existe frame, es decisión de diseño pendiente, no desviación de implementación. | | | Desviación (propuesta) | | | — | | #### Impersonation Banner [Section titled “Impersonation Banner”](#impersonation-banner) **Código:** `src/ui/organisms/ImpersonationBanner/index.tsx` | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — sin render exportado en `design/web/organisms/` | [![app Impersonation Banner](/design/parity/screenshots/playground/impersonation-banner.png)](/design/parity/screenshots/playground/impersonation-banner.png "app Impersonation Banner — abrir a tamaño completo") | | Notas | | | **ℹ️ Sin render de Figma — verificación del shot de la app.** No hay diseño exportado contra el cual comparar paridad.- **Color:** Banner con tinte **warning #FFFBEB**; título “Estás actuando como X” en ámbar/naranja bold (\~#F59E0B); texto secundario en gris; icono de usuarios en ámbar. Coherente con el rol warning del sistema. - **Estados:** 3 variantes en el showcase — sesión activa con comercio (incluye “Grupo Comercial MX”), sin comercio, y **saliendo** (spinner en el botón). Consistentes. - **Botón:** “Salir de la suplantación” outlined con borde y texto en azul de marca #0052CC. - **Siguiente paso:** exportar el frame de Figma para poder auditar paridad de tinte/tipografía/espaciado. | | | Desviación (propuesta) | | | — | | #### Side Nav [Section titled “Side Nav”](#side-nav) **Código:** `src/ui/organisms/SideNav/SideNav.tsx` · **Figma:** [`97:35`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=97-35) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Side Nav](/design/parity/figma/organisms/figma-side-nav.png)](/design/parity/figma/organisms/figma-side-nav.png "figma Side Nav — abrir a tamaño completo") | [![app Side Nav](/design/parity/screenshots/playground/side-nav.png)](/design/parity/screenshots/playground/side-nav.png "app Side Nav — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(showcase vs Figma) — pero OJO con adminNav.ts (#538)*- **Color:** Ítem activo con tinte de marca **#E6EEFA** + icono/texto **#0052CC** en los 3 estados (expandido Inicio, activo por módulo, riel colapsado). Encabezados de sección en gris neutro (\~#9CA3AF); inactivos en neutro oscuro. Coincide. - **Espaciado/Layout:** Agrupación OPERACIÓN / CATÁLOGO / EQUIPO / FINANZAS / ADMINISTRACIÓN con el mismo orden e ítems que Figma. El showcase omite logo “Terminales”, selector de tienda y footer de usuario que Figma sí muestra — fuera del alcance del showcase. - **Bordes/Tipografía:** Píldora activa redondeada sin borde; encabezados en mayúsculas/tracking; label activo semibold azul. Coincide. - **Iconos:** Algunos glifos difieren de Figma (Punto de venta = caja registradora vs carrito; Productos = etiqueta vs regalo; Servicios = factura vs flechas; Reportes = línea vs barras) — íconos de ejemplo, no defecto. Sin cambios desde la nota previa. - **⚠️ Nota #538 (fuera del par showcase↔Figma):** El SideNav del /playground usa datos de ejemplo y **sigue coincidiendo con este Figma**. Pero PR #538 cambió el drawer REAL de la app (`src/components/AdminShell/adminNav.ts`): la sección ADMINISTRACIÓN ahora es **Comercios / Tiendas / Apariencia / Terminales / Ajustes** (antes “Comercio y tiendas”), es decir se separó el nivel comercio↔tienda y se añadió “Apariencia” (white-label). Ese drawer real **diverge del Figma actual**; conviene re-exportar el frame de la IA de admin y auditar `AdminShell` por separado. El organism SideNav en sí no cambió. | | | Desviación (propuesta) | | | — | | #### Sticky Save Bar [Section titled “Sticky Save Bar”](#sticky-save-bar) **Código:** `src/ui/organisms/StickySaveBar/StickySaveBar.tsx` · **Figma:** [`103:134`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=103-134) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Sticky Save Bar](/design/parity/figma/organisms/figma-sticky-save-bar.png)](/design/parity/figma/organisms/figma-sticky-save-bar.png "figma Sticky Save Bar — abrir a tamaño completo") | [![app Sticky Save Bar](/design/parity/screenshots/playground/sticky-save-bar.png)](/design/parity/screenshots/playground/sticky-save-bar.png "app Sticky Save Bar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Botón *Guardar* con relleno sólido #0052CC medido idéntico en Figma y app. Botón de texto *Descartar* en azul de marca (trazos medidos #4681DA/#82AAE6, antialiasing de texto primary) en ambos. - **Iconos:** El icono de información es un círculo relleno neutro #6B7280 en ambos paneles (medido exacto), con la “i” blanca. - **Tipografía:** Mensaje “Tienes cambios sin guardar” en gris neutro; jerarquía y peso equivalentes. - **Layout:** Barra plana con hairline; a la izquierda icono + mensaje, a la derecha *Descartar* (botón de texto) + *Guardar* (botón relleno primary). Orden y alineación coinciden. - **Estados:** El patrón de botón relleno primary (excepción de relleno sólido) se respeta en *Guardar*; *Descartar* como acción secundaria de texto. - **Salvedad showcase:** El contenedor con borde y el área de vista previa tenue por encima de la barra son marco del showcase de /playground, no defecto del componente. | | | Desviación (propuesta) | | | — | | #### Tabs [Section titled “Tabs”](#tabs) **Código:** `src/ui/organisms/Tabs/Tabs.tsx` · **Figma:** [`94:6`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=94-6) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Tabs](/design/parity/figma/organisms/figma-tabs.png)](/design/parity/figma/organisms/figma-tabs.png "figma Tabs — abrir a tamaño completo") | [![app Tabs](/design/parity/screenshots/playground/tabs.png)](/design/parity/screenshots/playground/tabs.png "app Tabs — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Estados:** Residuo cerrado — la pestaña activa ya usa el **tratamiento de pill** (relleno azul claro **#E6EEFA** con etiqueta azul **#0052CC**, corregido en #546), replicando el pill de Figma; visible en el showcase “PESTAÑA ACTIVA EN PILL PRIMARY”. - **Color:** Etiqueta activa en `#0052CC`, inactivas en gris neutro; jerarquía idéntica. - **Layout:** Las filas extra (activa / hover / disabled) son del showcase; hover destaca “Historial” y disabled atenúa, coherentes con los tokens. - **Espaciado:** El fondo del lienzo del playground (`#F5F7FA`) es del showcase, no del componente.Sin residuo real del componente. | | | Desviación (propuesta) | | | — | | #### Top Bar [Section titled “Top Bar”](#top-bar) **Código:** `src/ui/organisms/TopBar/TopBar.tsx` · **Figma:** [`98:51`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=98-51) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Top Bar](/design/parity/figma/organisms/figma-top-bar.png)](/design/parity/figma/organisms/figma-top-bar.png "figma Top Bar — abrir a tamaño completo") | [![app Top Bar](/design/parity/screenshots/playground/top-bar.png)](/design/parity/screenshots/playground/top-bar.png "app Top Bar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Marca #0052CC medido en el logo del row *Punto de venta* y en el ícono del avatar; tinte de avatar #E6EEFA (palette.primary.light) confirmado. Badge de notificación #EF4444 (error). Todo canónico. - **Tipografía:** Título #1F2933, secundario “Bienvenido” en gris, jerarquía correcta. - **Iconos:** Campana de adorno #6B7280/#9CA3AF (no azul), correcta; ícono de avatar relleno sobre contenedor tintado, patrón esperado. - **Bordes:** Tarjetas planas con hairline claro; divisor vertical sutil entre acciones y menú de usuario. - **Layout:** El panel APP es un showcase con 3 variantes (base sin badge / con logo+badge+menú / con navegación atrás), mientras Figma muestra una única barra con buscador + chips de estado (Turno abierto, Sincronizado) + grupo de avatares. Esa diferencia de contenido de ejemplo es decisión del showcase, no defecto — los tokens y estados subyacentes coinciden. - **Estados:** Menú de usuario con avatar tintado + iniciales/ícono y badge numérico correctos; sin residuo de paridad detectable. | | | Desviación (propuesta) | | | — | | *** ## Apéndice — otras vistas de `/playground` [Section titled “Apéndice — otras vistas de /playground”](#apéndice--otras-vistas-de-playground) El registro de `/playground` también incluye **composiciones de pantalla** hechas con el design system y algunos tableros de flujo completo. Cuando la pantalla ya tiene ruta real, arriba se muestra la captura de la app; estas son las que quedan. Sirven como *referencia de implementación* — cómo se vería la pantalla armada solo con componentes del DS — y son un buen punto de partida cuando toque construir la pantalla de verdad. #### cuenta-flow [Section titled “cuenta-flow”](#cuenta-flow) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app cuenta-flow](/design/parity/screenshots/playground/cuenta-flow.png)](/design/parity/screenshots/playground/cuenta-flow.png "app cuenta-flow — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### reportes-flow [Section titled “reportes-flow”](#reportes-flow) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app reportes-flow](/design/parity/screenshots/playground/reportes-flow.png)](/design/parity/screenshots/playground/reportes-flow.png "app reportes-flow — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### ventas-flow [Section titled “ventas-flow”](#ventas-flow) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app ventas-flow](/design/parity/screenshots/playground/ventas-flow.png)](/design/parity/screenshots/playground/ventas-flow.png "app ventas-flow — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w07-dashboard [Section titled “w07-dashboard”](#w07-dashboard) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w07-dashboard](/design/parity/screenshots/playground/w07-dashboard.png)](/design/parity/screenshots/playground/w07-dashboard.png "app w07-dashboard — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w25-stock [Section titled “w25-stock”](#w25-stock) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w25-stock](/design/parity/screenshots/playground/w25-stock.png)](/design/parity/screenshots/playground/w25-stock.png "app w25-stock — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w26-ajuste [Section titled “w26-ajuste”](#w26-ajuste) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w26-ajuste](/design/parity/screenshots/playground/w26-ajuste.png)](/design/parity/screenshots/playground/w26-ajuste.png "app w26-ajuste — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w27-movimientos [Section titled “w27-movimientos”](#w27-movimientos) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w27-movimientos](/design/parity/screenshots/playground/w27-movimientos.png)](/design/parity/screenshots/playground/w27-movimientos.png "app w27-movimientos — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w28-transferencia [Section titled “w28-transferencia”](#w28-transferencia) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w28-transferencia](/design/parity/screenshots/playground/w28-transferencia.png)](/design/parity/screenshots/playground/w28-transferencia.png "app w28-transferencia — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w29-ventas [Section titled “w29-ventas”](#w29-ventas) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w29-ventas](/design/parity/screenshots/playground/w29-ventas.png)](/design/parity/screenshots/playground/w29-ventas.png "app w29-ventas — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w30-detalle-venta [Section titled “w30-detalle-venta”](#w30-detalle-venta) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w30-detalle-venta](/design/parity/screenshots/playground/w30-detalle-venta.png)](/design/parity/screenshots/playground/w30-detalle-venta.png "app w30-detalle-venta — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w31-rechazo-pin [Section titled “w31-rechazo-pin”](#w31-rechazo-pin) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w31-rechazo-pin](/design/parity/screenshots/playground/w31-rechazo-pin.png)](/design/parity/screenshots/playground/w31-rechazo-pin.png "app w31-rechazo-pin — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w31-reembolso-parcial [Section titled “w31-reembolso-parcial”](#w31-reembolso-parcial) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w31-reembolso-parcial](/design/parity/screenshots/playground/w31-reembolso-parcial.png)](/design/parity/screenshots/playground/w31-reembolso-parcial.png "app w31-reembolso-parcial — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w32-turnos [Section titled “w32-turnos”](#w32-turnos) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w32-turnos](/design/parity/screenshots/playground/w32-turnos.png)](/design/parity/screenshots/playground/w32-turnos.png "app w32-turnos — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w33-detalle-z [Section titled “w33-detalle-z”](#w33-detalle-z) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w33-detalle-z](/design/parity/screenshots/playground/w33-detalle-z.png)](/design/parity/screenshots/playground/w33-detalle-z.png "app w33-detalle-z — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w34-abrir-turno [Section titled “w34-abrir-turno”](#w34-abrir-turno) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w34-abrir-turno](/design/parity/screenshots/playground/w34-abrir-turno.png)](/design/parity/screenshots/playground/w34-abrir-turno.png "app w34-abrir-turno — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w35-cerrar-turno [Section titled “w35-cerrar-turno”](#w35-cerrar-turno) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w35-cerrar-turno](/design/parity/screenshots/playground/w35-cerrar-turno.png)](/design/parity/screenshots/playground/w35-cerrar-turno.png "app w35-cerrar-turno — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w36-movimientos-caja [Section titled “w36-movimientos-caja”](#w36-movimientos-caja) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w36-movimientos-caja](/design/parity/screenshots/playground/w36-movimientos-caja.png)](/design/parity/screenshots/playground/w36-movimientos-caja.png "app w36-movimientos-caja — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w37-empleados [Section titled “w37-empleados”](#w37-empleados) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w37-empleados](/design/parity/screenshots/playground/w37-empleados.png)](/design/parity/screenshots/playground/w37-empleados.png "app w37-empleados — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w38-empleado-edit [Section titled “w38-empleado-edit”](#w38-empleado-edit) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w38-empleado-edit](/design/parity/screenshots/playground/w38-empleado-edit.png)](/design/parity/screenshots/playground/w38-empleado-edit.png "app w38-empleado-edit — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w39-invitar [Section titled “w39-invitar”](#w39-invitar) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w39-invitar](/design/parity/screenshots/playground/w39-invitar.png)](/design/parity/screenshots/playground/w39-invitar.png "app w39-invitar — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w40-roles [Section titled “w40-roles”](#w40-roles) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w40-roles](/design/parity/screenshots/playground/w40-roles.png)](/design/parity/screenshots/playground/w40-roles.png "app w40-roles — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w48-comercios [Section titled “w48-comercios”](#w48-comercios) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w48-comercios](/design/parity/screenshots/playground/w48-comercios.png)](/design/parity/screenshots/playground/w48-comercios.png "app w48-comercios — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w55-reportes-overview [Section titled “w55-reportes-overview”](#w55-reportes-overview) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w55-reportes-overview](/design/parity/screenshots/playground/w55-reportes-overview.png)](/design/parity/screenshots/playground/w55-reportes-overview.png "app w55-reportes-overview — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w56-reporte-ventas [Section titled “w56-reporte-ventas”](#w56-reporte-ventas) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w56-reporte-ventas](/design/parity/screenshots/playground/w56-reporte-ventas.png)](/design/parity/screenshots/playground/w56-reporte-ventas.png "app w56-reporte-ventas — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w58-cortes-z [Section titled “w58-cortes-z”](#w58-cortes-z) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w58-cortes-z](/design/parity/screenshots/playground/w58-cortes-z.png)](/design/parity/screenshots/playground/w58-cortes-z.png "app w58-cortes-z — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w59-mi-cuenta [Section titled “w59-mi-cuenta”](#w59-mi-cuenta) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w59-mi-cuenta](/design/parity/screenshots/playground/w59-mi-cuenta.png)](/design/parity/screenshots/playground/w59-mi-cuenta.png "app w59-mi-cuenta — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w61-ajustes [Section titled “w61-ajustes”](#w61-ajustes) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w61-ajustes](/design/parity/screenshots/playground/w61-ajustes.png)](/design/parity/screenshots/playground/w61-ajustes.png "app w61-ajustes — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w62-bitacora [Section titled “w62-bitacora”](#w62-bitacora) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w62-bitacora](/design/parity/screenshots/playground/w62-bitacora.png)](/design/parity/screenshots/playground/w62-bitacora.png "app w62-bitacora — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w65-cerrar-denominacion [Section titled “w65-cerrar-denominacion”](#w65-cerrar-denominacion) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w65-cerrar-denominacion](/design/parity/screenshots/playground/w65-cerrar-denominacion.png)](/design/parity/screenshots/playground/w65-cerrar-denominacion.png "app w65-cerrar-denominacion — abrir a tamaño completo") | | Notas | | | **Showcase de composición** — captura del design system en `/playground` (sin par de Figma en esta entrada del apéndice). Es una referencia de *implementación* de la pantalla, útil para inspeccionar la composición de átomos/moléculas/organismos aislada de datos reales. La auditoría de paridad Figma ↔ app de esta pantalla vive en su entrada principal del índice por historia de usuario (cuando existe frame de Figma). | | | Desviación (propuesta) | | | — | | #### w75-reporte-detalle [Section titled “w75-reporte-detalle”](#w75-reporte-detalle) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w75-reporte-detalle](/design/parity/screenshots/playground/w75-reporte-detalle.png)](/design/parity/screenshots/playground/w75-reporte-detalle.png "app w75-reporte-detalle — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | | Desviación (propuesta) | | | — | | #### w76-disponer-saldo [Section titled “w76-disponer-saldo”](#w76-disponer-saldo) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w76-disponer-saldo](/design/parity/screenshots/playground/w76-disponer-saldo.png)](/design/parity/screenshots/playground/w76-disponer-saldo.png "app w76-disponer-saldo — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | | Desviación (propuesta) | | | — | | #### w76-enviando-solicitud [Section titled “w76-enviando-solicitud”](#w76-enviando-solicitud) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w76-enviando-solicitud](/design/parity/screenshots/playground/w76-enviando-solicitud.png)](/design/parity/screenshots/playground/w76-enviando-solicitud.png "app w76-enviando-solicitud — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | | Desviación (propuesta) | | | — | | #### w76-sin-cuenta-bancaria [Section titled “w76-sin-cuenta-bancaria”](#w76-sin-cuenta-bancaria) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w76-sin-cuenta-bancaria](/design/parity/screenshots/playground/w76-sin-cuenta-bancaria.png)](/design/parity/screenshots/playground/w76-sin-cuenta-bancaria.png "app w76-sin-cuenta-bancaria — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | | Desviación (propuesta) | | | — | | #### w76-solicitud-enviada [Section titled “w76-solicitud-enviada”](#w76-solicitud-enviada) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w76-solicitud-enviada](/design/parity/screenshots/playground/w76-solicitud-enviada.png)](/design/parity/screenshots/playground/w76-solicitud-enviada.png "app w76-solicitud-enviada — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | | Desviación (propuesta) | | | — | | #### w76-split-no-cuadra [Section titled “w76-split-no-cuadra”](#w76-split-no-cuadra) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w76-split-no-cuadra](/design/parity/screenshots/playground/w76-split-no-cuadra.png)](/design/parity/screenshots/playground/w76-split-no-cuadra.png "app w76-split-no-cuadra — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | | Desviación (propuesta) | | | — | | # Parity Figma ↔ App (móvil) > Galería lado a lado del diseño en Figma y la pantalla real de la app Android (Jetpack Compose), por capa de diseño atómico. > Galería lado a lado del diseño en Figma y la implementación real en la app Android (Jetpack Compose), capturada con Maestro en el Playground. Método: el `design-parity-playbook.md` del repo `terminales-solution`. Las notas se **miden** (muestreo de píxel), no se estiman a ojo; la columna **Notas** es escrita a mano y se preserva entre regeneraciones. Desviaciones intencionales (donde la app mejora al diseño) se registran en [`intentional-deviations.md`](/design/parity-mobile/intentional-deviations/). **Veredicto:** ✅ 40 · ⚠️ 15 · ❌ 0 · *Actualizado desde los assets en disco.* ## Átomos [Section titled “Átomos”](#átomos) #### ✅ Button [Section titled “✅ Button”](#-button) Nodo Figma [`21:50`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=21-50) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/atoms/TerminalesButton.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/atomic/button/figma-button.png)](/design/parity-mobile/figma/atomic/button/figma-button.png) | [![](/design/parity-mobile/screenshots/atomic/button.png)](/design/parity-mobile/screenshots/atomic/button.png) | ✅ Corregido. El contenedor Tonal ahora usa primary-soft #E6EEFA (TerminalesColors.PrimarySubtle) con contenido azul #0052CC, igual que el frame 21:50 (antes SecondaryContainer #E3F2FD). Filled #0052CC, disabled #E5E7EB y radio md correctos. El estado Pressed se comunica con ripple/state-layer de M3 (no intercambia a #003D99) — ver desviación. | 🔶 **Propuesta** — El Pressed no oscurece a #003D99; usa el state-layer/ripple de Material 3. *Figma:* n/a platform [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ Icon Button [Section titled “✅ Icon Button”](#-icon-button) Nodo Figma [`34:73`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=34-73) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/atoms/TerminalesIconButton.kt` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/atomic/icon-button/figma-icon-button.png)](/design/parity-mobile/figma/atomic/icon-button/figma-icon-button.png) | [![](/design/parity-mobile/screenshots/atomic/icon-button.png)](/design/parity-mobile/screenshots/atomic/icon-button.png) | ✅ Corregido. Las tres variantes (Standard/Outlined/Ghost) pintan el glifo en azul primary #0052CC por defecto, igual que el frame 34:73 (antes Outlined/Ghost usaban TextPrimary oscuro). Standard = squircle con relleno tonal; Outlined/Ghost mantienen la forma circular de M3 por convención de plataforma — ver desviación. Callers sobre fondos de color pasan contentColor explícito. | 🔶 **Propuesta** — Outlined/Ghost usan la forma circular de M3 (OutlinedIconButton/IconButton) en vez del squircle del frame; Standard sí es squircle. *Figma:* decidir: aceptar circular (plataforma) o unificar Standard a circular en el frame 34:73. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ Text Field [Section titled “✅ Text Field”](#-text-field) Nodo Figma [`36:35`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=36-35) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/atoms/TerminalesTextField.kt` | Figma | App | Notas | Desviación (propuesta) | | --------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/atomic/text-field/figma-text-field.png)](/design/parity-mobile/figma/atomic/text-field/figma-text-field.png) | [![](/design/parity-mobile/screenshots/atomic/text-field.png)](/design/parity-mobile/screenshots/atomic/text-field.png) | ✅ Default/Error/Disabled coinciden (relleno claro, borde error #EF4444, placeholder #9CA3AF). El estado Focus renderiza el aro azul con foco real (mismo mecanismo M3 verificado en Search Field); el seed estático no lo dispara — limitación de captura, no defecto. TerminalesTextField.kt cablea focusedBorderColor #2196F3 correctamente. | — | #### ✅ Search Field [Section titled “✅ Search Field”](#-search-field) Nodo Figma [`37:35`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=37-35) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/atoms/TerminalesSearchField.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/atomic/search-field/figma-search-field.png)](/design/parity-mobile/figma/atomic/search-field/figma-search-field.png) | [![](/design/parity-mobile/screenshots/atomic/search-field.png)](/design/parity-mobile/screenshots/atomic/search-field.png) | ✅ Estado Active verificado con FOCO REAL (Maestro tap): borde y lupa azules (#2196F3 InputBorderFocused ≈ primary), relleno blanco #FFFFFF, texto Telcel y botón limpiar ✕ — coincide con el frame 37:35. Default gris #F7F9FC también coincide. La ❌ previa era una limitación de captura estática (el seed programático no dispara el visual de foco de M3), no un defecto del componente. | 🔶 **Propuesta** — El campo agrega un botón limpiar (✕) con área táctil \~48dp cuando hay texto, ausente en el Figma Active. *Figma:* Agregar un icono trailing de limpiar (✕) al variant Active del nodo 37:35 para reflejar el affordance implementado. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ Status Badge [Section titled “✅ Status Badge”](#-status-badge) Nodo Figma [`33:26`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=33-26) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/atoms/StatusBadge.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/atomic/status-badge/figma-status-badge.png)](/design/parity-mobile/figma/atomic/status-badge/figma-status-badge.png) | [![](/design/parity-mobile/screenshots/atomic/status-badge.png)](/design/parity-mobile/screenshots/atomic/status-badge.png) | ✅ Paridad exacta. Fill soft y color de texto por estado coinciden con Figma y con los tokens DS: Success fill medido #ECFCF5 (SuccessSubtle) · texto #10B981; Warning #FFFBEB (WarningSubtle) · #F59E0B; Danger #FEF2F2 (ErrorSubtle) · #EF4444; Info #EFF6FF (InfoSubtle) · #3B82F6 — idénticos en ambos PNG. Es tinte soft real, no alpha(main,0.12). Píldora full-round (TerminalesRadius.full 999dp), 12sp SemiBold, sin punto guía (igual que Figma). Los 4 estados (Success/Warning/Danger/Info) presentes. | — | #### ✅ Category Chip [Section titled “✅ Category Chip”](#-category-chip) Nodo Figma [`35:31`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=35-31) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/atoms/CategoryChip.kt` | Figma | App | Notas | Desviación (propuesta) | | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/atomic/category-chip/figma-category-chip.png)](/design/parity-mobile/figma/atomic/category-chip/figma-category-chip.png) | [![](/design/parity-mobile/screenshots/atomic/category-chip.png)](/design/parity-mobile/screenshots/atomic/category-chip.png) | ✅ Paridad exacta. Inactive fill medido #F0F4F8 (SurfaceVariant) con texto gris #6B7280 (TextSecondary); Active fill #E6EEFA (PrimarySubtle) con texto azul #0052CC (Primary) — ambos igualan Figma pixel a pixel (Figma: inactive #F0F4F8, active #E6EEFA). Tinte soft real, no alpha(primary,0.12). Píldora full-round (TerminalesRadius.full), 14sp SemiBold. Ambos estados Inactive/Active del frame presentes; el estilo Solid legacy (fill primario sólido + texto blanco) no aparece en Figma y se conserva solo para el no-op del catálogo POS, no es defecto de paridad. | — | #### ✅ PIN Dot [Section titled “✅ PIN Dot”](#-pin-dot) Nodo Figma [`47:52`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=47-52) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/atoms/PinDots.kt` | Figma | App | Notas | Desviación (propuesta) | | --------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- | | [![](/design/parity-mobile/figma/atomic/pin-dot/figma-pin-dot.png)](/design/parity-mobile/figma/atomic/pin-dot/figma-pin-dot.png) | [![](/design/parity-mobile/screenshots/atomic/pin-dot.png)](/design/parity-mobile/screenshots/atomic/pin-dot.png) | ✅ Paridad correcta. Punto lleno en app = #0052CC (TerminalesColors.Primary), coincide con el azul del Figma #0050C0 (quantizado; = primary #0052CC de DESIGN.md). Punto vacío: contorno con TerminalesColors.Border #D1D5DB (stroke 1.5dp, CircleShape), que casa con el outline gris del Figma #D0D0D0 mejor que el token #E5E7EB de DESIGN.md. Diámetro 16dp uniforme para lleno y vacío, proporción idéntica a la del frame (dot ≈16px de alto). El conteo de 6 dots es uso del showcase (length=6); el frame solo muestra 2 dots de muestra (vacío+lleno). Estado isError reañade la rampa danger (#EF4444). Sin culpable. | — | #### ✅ Keypad Key [Section titled “✅ Keypad Key”](#-keypad-key) Nodo Figma [`37:45`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=37-45) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/atoms/KeypadKey.kt` | Figma | App | Notas | Desviación (propuesta) | | --------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [![](/design/parity-mobile/figma/atomic/keypad-key/figma-keypad-key.png)](/design/parity-mobile/figma/atomic/keypad-key/figma-keypad-key.png) | [![](/design/parity-mobile/screenshots/atomic/keypad-key.png)](/design/parity-mobile/screenshots/atomic/keypad-key.png) | ✅ Paridad correcta en el estado base. Tecla dígito Default: fondo blanco app #FFFFFF (TerminalesColors.KeypadKey) + dígito #1F2933 (TextPrimary), coincide con el Figma (tecla blanca + dígito oscuro #304040). Radio RoundedCornerShape(TerminalesRadius.md=12dp). El estado Presionado del Figma (fondo lavanda #E0E0F0 + dígito azul) se entrega en la app vía ripple de Material sobre la .clickable() en vez de reproducir el fill lavanda estático — decisión idiomática de Android (ver deviation\_candidate). Nota: la tecla de Acción del screenshot se renderiza en azul (icono #1762D0 sobre fondo #F0F4F8), mientras el código actual usa tokens danger rojos (KeypadDelete #FEE2E2 / KeypadDeleteIcon #EF4444) en KeypadKey.kt — el screenshot parece anterior a ese cambio de token; el nodo Figma 37:45 no muestra tecla de acción, así que no es verificable contra el frame. | 🔶 **Propuesta** — La tecla usa ripple de Material (.clickable) y target táctil ≥48dp en lugar del fill lavanda estático del estado Pressed del Figma. *Figma:* Actualizar el nodo 37:45: documentar el estado Pressed como ripple/overlay de Material (no un fill lavanda fijo) y anotar el target táctil mínimo de 48dp para las teclas. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ Avatar [Section titled “✅ Avatar”](#-avatar) Nodo Figma [`45:90`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=45-90) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/atoms/TerminalesAvatar.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/atomic/avatar/figma-avatar.png)](/design/parity-mobile/figma/atomic/avatar/figma-avatar.png) | [![](/design/parity-mobile/screenshots/atomic/avatar.png)](/design/parity-mobile/screenshots/atomic/avatar.png) | ✅ Variante soft correcta y NO invertida: relleno del círculo #E6EEFA (TerminalesColors.PrimarySubtle) e iniciales #0052CC (TerminalesColors.Primary), medidos idénticos en Figma (fill #E6EEFA, iniciales azul primary) y en la app. Forma círculo completo (CircleShape) y pasos de tamaño Small 32 / Medium 40 / Large 56 dp coinciden con la referencia. | — | #### ✅ Inline Alert [Section titled “✅ Inline Alert”](#-inline-alert) Nodo Figma [`47:78`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=47-78) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/atoms/InlineAlert.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- | | [![](/design/parity-mobile/figma/atomic/inline-alert/figma-inline-alert.png)](/design/parity-mobile/figma/atomic/inline-alert/figma-inline-alert.png) | [![](/design/parity-mobile/screenshots/atomic/inline-alert.png)](/design/parity-mobile/screenshots/atomic/inline-alert.png) | ✅ Corregido. Radio ahora TerminalesRadius.sm (8dp) según DESIGN.md (antes md/12dp). Fondos soft por estado exactos (info #EFF6FF, warning #FFFBEB, danger #FEF2F2), glifos con color main correcto, texto #1F2933. | — | ## Moléculas [Section titled “Moléculas”](#moléculas) #### ✅ Card [Section titled “✅ Card”](#-card) Nodo Figma [`43:44`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=43-44) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/molecules/TerminalesCards.kt` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/molecule/card/figma-card.png)](/design/parity-mobile/figma/molecule/card/figma-card.png) | [![](/design/parity-mobile/screenshots/molecule/card.png)](/design/parity-mobile/screenshots/molecule/card.png) | ✅ Paridad. Superficie blanca CardBackground medida #FFFFFF en ambos lados; borde hairline en la variante plana (transición outer #F5F7FA → #EEEFF2 → #FFFFFF, token CardBorder #E5E7EB) igual que el trazo tenue de Figma; radio lg y estructura título oscuro / cuerpo gris coinciden. El eje Elevation Flat/Raised de las notas está cubierto: la app muestra ‘Plana’ (borde 1dp, level0) y ‘Elevada’ (sombra level2, sin borde), y TerminalesCard mapea exactamente esas dos variantes. Único matiz: el fondo de pantalla app es #F5F7FA vs #F5F5F5 de Figma (superficie, no la tarjeta). | — | #### ✅ Product Card [Section titled “✅ Product Card”](#-product-card) Nodo Figma [`44:50`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=44-50) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/molecules/ProductCard.kt` | Figma | App | Notas | Desviación (propuesta) | | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/molecule/product-card/figma-product-card.png)](/design/parity-mobile/figma/molecule/product-card/figma-product-card.png) | [![](/design/parity-mobile/screenshots/molecule/product-card.png)](/design/parity-mobile/screenshots/molecule/product-card.png) | ✅ Paridad de color y estructura. Placeholder de imagen medido #F0F4F8 en ambos lados (token SurfaceVariant exacto); nombre #1F2933 (TextPrimary exacto); precio medido #0D5BCF ≈ Primary #0052CC (aclarado por antialias); pill de stock verde-suave fondo #ECFCF5 con texto verde #43C99C, y pill ‘Agotado’ rojo-suave fondo #FEF7F7 con texto #FCD8D8 (danger #EF4444). Ambos estados Default / OutOfStock presentes y ProductCard.kt los cablea (badge success ‘N en stock’ vs danger ‘Agotado’, umbral low-stock). Diferencia medible: el estado OutOfStock de la app baja toda la tarjeta a alpha 0.6 (nombre/precio atenuados), mientras que en Figma la tarjeta agotada mantiene opacidad plena; se trata como mejora de UX (ver deviation\_candidate). | 🔶 **Propuesta** — OutOfStock atenúa la tarjeta completa a alpha 0.6 y la vuelve no-clickable; Figma la deja a opacidad plena. *Figma:* Añadir un estado OutOfStock con 60% de opacidad (y aspecto no-interactivo) a la variante del componente en Figma para reflejar el comportamiento del código. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ KPI Tile [Section titled “✅ KPI Tile”](#-kpi-tile) Nodo Figma [`43:57`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=43-57) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/molecules/KpiTile.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/molecule/kpi-tile/figma-kpi-tile.png)](/design/parity-mobile/figma/molecule/kpi-tile/figma-kpi-tile.png) | [![](/design/parity-mobile/screenshots/molecule/kpi-tile.png)](/design/parity-mobile/screenshots/molecule/kpi-tile.png) | ✅ Paridad. Superficie blanca medida #FFFFFF con borde hairline (transición #EEEFF2 → #FFFFFF, token CardBorder #E5E7EB) igual que Figma; etiqueta gris medida #969BA5 (TextSecondary #6B7280) sobre valor oscuro tabular (Figma valor #222C36 ≈ TextPrimary #1F2933). El eje Trend Up/Down está bien tintado: delta ascendente verde medido #0F9A6F/#169D73 (SuccessText #059669, familia success #10B981) y delta descendente rojo medido #F7A3A3 (danger Error #EF4444), coincidiendo con ‘+12.5% vs ayer’ verde y ‘-4.2% vs ayer’ rojo del diseño. KpiTile.kt usa figuras tabulares (tnum) y kpiTrendColor mapea los tokens correctos. | — | #### ✅ List Row [Section titled “✅ List Row”](#-list-row) Nodo Figma [`41:60`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=41-60) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/molecules/ListRow.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/molecule/list-row/figma-list-row.png)](/design/parity-mobile/figma/molecule/list-row/figma-list-row.png) | [![](/design/parity-mobile/screenshots/molecule/list-row.png)](/design/parity-mobile/screenshots/molecule/list-row.png) | ✅ Corregido. El contenedor del icono guía ahora es círculo (CircleShape) con tinte primary-soft #E6EEFA e icono azul, igual que el frame 41:60 (antes cuadro redondeado + SurfaceVariant). El chevron se conserva gris (text-tertiary) como convención de plataforma — ver desviación. Título/subtítulo/badge correctos. | 🔶 **Propuesta** — Chevron gris (text.tertiary) en lugar del azul primary de Figma *Figma:* Re-tintar el chevron del frame list-row a text.tertiary gris (#9CA3AF) para alinear con la convención de Android; el azul se reserva para acciones. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ Summary Row [Section titled “✅ Summary Row”](#-summary-row) Nodo Figma [`43:66`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=43-66) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/molecules/SummaryRow.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/molecule/summary-row/figma-summary-row.png)](/design/parity-mobile/figma/molecule/summary-row/figma-summary-row.png) | [![](/design/parity-mobile/screenshots/molecule/summary-row.png)](/design/parity-mobile/screenshots/molecule/summary-row.png) | ✅ Corregido. La etiqueta TOTAL ahora usa TextPrimary #1F2933 cuando isTotal (antes TextSecondary), separando énfasis de color; el valor TOTAL azul #0052CC como opt-in. Subtotal y divisor correctos. | — | #### ✅ Receipt Row [Section titled “✅ Receipt Row”](#-receipt-row) Nodo Figma [`47:60`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=47-60) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/molecules/ReceiptRow.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/molecule/receipt-row/figma-receipt-row.png)](/design/parity-mobile/figma/molecule/receipt-row/figma-receipt-row.png) | [![](/design/parity-mobile/screenshots/molecule/receipt-row.png)](/design/parity-mobile/screenshots/molecule/receipt-row.png) | ✅ Coincide con Figma: descripción en gris secondary #A0A0B0≈#6B7280 (no cae en el bug de usar primary), monto derecho en primary #101020≈#1F2933 con peso Medium, y divisor punteado presente en ambos (Canvas dashPathEffect, hairline \~1dp sobre #F5F7FA). Estructura de dos columnas idéntica. Sin culpable. | — | #### ✅ Cart Bar [Section titled “✅ Cart Bar”](#-cart-bar) Nodo Figma [`44:80`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=44-80) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/molecules/CartBar.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/molecule/cart-bar/figma-cart-bar.png)](/design/parity-mobile/figma/molecule/cart-bar/figma-cart-bar.png) | [![](/design/parity-mobile/screenshots/molecule/cart-bar.png)](/design/parity-mobile/screenshots/molecule/cart-bar.png) | ✅ Corregido. El glifo del badge ahora es un carrito (TerminalesIcons.Cart) en vez de tarjeta de crédito, igual que Figma; barra azul #0052CC y badge translúcido correctos. La CTA Ver carrito se conserva como píldora blanca (afordancia mejor) — ver desviación. | 🔶 **Propuesta** — La app dibuja ‘Ver carrito’ como píldora blanca (#FFFFFF) con chevron en vez del texto blanco plano del Figma. *Figma:* Actualizar el frame 44:80 para renderizar ‘Ver carrito’ como botón/píldora blanca con chevron, o confirmar el texto plano como canónico. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ Quantity Stepper [Section titled “✅ Quantity Stepper”](#-quantity-stepper) Nodo Figma [`44:69`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=44-69) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/molecules/QuantityStepper.kt` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- | | [![](/design/parity-mobile/figma/molecule/quantity-stepper/figma-quantity-stepper.png)](/design/parity-mobile/figma/molecule/quantity-stepper/figma-quantity-stepper.png) | [![](/design/parity-mobile/screenshots/molecule/quantity-stepper.png)](/design/parity-mobile/screenshots/molecule/quantity-stepper.png) | ✅ Corregido. Los botones +/- ahora usan la variante Standard (FilledTonal) con relleno primary-soft #E6EEFA e iconos azules #0052CC, igual que Figma (antes Outlined gris). Geometría 36dp y − deshabilitado en el mínimo correctos. | — | #### ✅ Provider Tile [Section titled “✅ Provider Tile”](#-provider-tile) Nodo Figma [`48:63`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=48-63) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/molecules/ProviderTile.kt` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/molecule/provider-tile/figma-provider-tile.png)](/design/parity-mobile/figma/molecule/provider-tile/figma-provider-tile.png) | [![](/design/parity-mobile/screenshots/molecule/provider-tile.png)](/design/parity-mobile/screenshots/molecule/provider-tile.png) | ✅ Bug de label invisible CORREGIDO (label seleccionado azul #0052CC, visible). El badge usa el color de marca del proveedor (Telcel azul, AT\&T, Movistar) con la inicial — DESVIACIÓN INTENCIONAL ACEPTADA (2026-07-26): el color de marca ayuda al reconocimiento rápido en la cuadrícula. El aro de selección va en el badge. | ✅ **Aceptada** — Badge con color de marca del proveedor (Telcel azul, etc.) + inicial, en vez del badge gris/soft genérico del frame. *Figma:* actualizar el nodo 48:63 a la variante con color de marca. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | ## Organismos [Section titled “Organismos”](#organismos) #### ⚠️ Top App Bar [Section titled “⚠️ Top App Bar”](#️-top-app-bar) Nodo Figma [`40:45`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=40-45) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/organisms/TerminalesTopBars.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/organism/top-app-bar/figma-top-app-bar.png)](/design/parity-mobile/figma/organism/top-app-bar/figma-top-app-bar.png) | [![](/design/parity-mobile/screenshots/organism/top-app-bar.png)](/design/parity-mobile/screenshots/organism/top-app-bar.png) | ⚠️ Re-auditado 2026-08-05: el componente TerminalesTopBar sigue correcto (barra blanca #FFFFFF, título oscuro #1F2933, divisor hairline; igual a 40:45) y se usa aún en pantallas secundarias/auth. CAMBIO de esta iteración: las top bars fueron REMOVIDAS de las 5 pantallas principales del shell (Inicio/Vender/Carrito/Reportes/Cuenta) — ahora sin barra; las notificaciones se movieron al FAB de navegación. Ver desviación. | 🔶 **Propuesta** — Top bars removidas de las 5 pantallas principales del shell; notificaciones movidas al FAB de navegación. *Figma:* En los frames M06/M11/M13/M24/M25 quitar la top bar; documentar notificaciones en el FAB. Conservar 40:45 para pantallas secundarias. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ⚠️ Bottom Nav Item [Section titled “⚠️ Bottom Nav Item”](#️-bottom-nav-item) Nodo Figma [`45:53`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=45-53) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/organisms/BottomNav.kt` | Figma | App | Notas | Desviación (propuesta) | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/organism/bottom-nav-item/figma-bottom-nav-item.png)](/design/parity-mobile/figma/organism/bottom-nav-item/figma-bottom-nav-item.png) | [![](/design/parity-mobile/screenshots/organism/bottom-nav-item.png)](/design/parity-mobile/screenshots/organism/bottom-nav-item.png) | ⚠️ Re-auditado 2026-08-05 (online, sin banner). El item conserva paridad de estados (activo #0052CC / inactivo #9CA3AF), pero su contenedor (Bottom Nav Bar) fue reemplazado por el FAB de navegación en el shell. Se mantiene solo como referencia del DS. Ver desviación (bottom-nav → FAB). | 🔶 **Propuesta** — El item de bottom-nav queda como referencia del DS; su contenedor fue reemplazado por el FAB de navegación global. *Figma:* Deprecar el item 45:53 junto con el bottom-nav 45:56; mapear sus estados al FAB de navegación. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ⚠️ Bottom Nav Bar [Section titled “⚠️ Bottom Nav Bar”](#️-bottom-nav-bar) Nodo Figma [`45:56`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=45-56) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/organisms/BottomNav.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [![](/design/parity-mobile/figma/organism/bottom-nav-bar/figma-bottom-nav-bar.png)](/design/parity-mobile/figma/organism/bottom-nav-bar/figma-bottom-nav-bar.png) | [![](/design/parity-mobile/screenshots/organism/bottom-nav-bar.png)](/design/parity-mobile/screenshots/organism/bottom-nav-bar.png) | ⚠️ Re-auditado 2026-08-05 (online, sin banner). El componente BottomNav sigue en el DS (ahora con 6 destinos incl. «Más…») pero fue REEMPLAZADO en el shell por el FAB de navegación global (abajo-izquierda) — la bottom-nav ya NO se monta en producción. La captura muestra el showcase del DS. Ver desviación. | 🔶 **Propuesta** — La bottom-nav fue reemplazada por un FAB de navegación global speed-dial (Inicio/Vender/Carrito/Reportes/Cuenta/Más/Notificaciones). *Figma:* Deprecar el bottom-nav 45:56 en el diseño y crear el componente «FAB de navegación (speed-dial)». [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ Bottom Sheet [Section titled “✅ Bottom Sheet”](#-bottom-sheet) Nodo Figma [`48:66`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=48-66) · código `app/src/main/java/com/axios/terminales/ui/components/terminales/organisms/BottomSheetHost.kt` | Figma | App | Notas | Desviación (propuesta) | | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [![](/design/parity-mobile/figma/organism/bottom-sheet/figma-bottom-sheet.png)](/design/parity-mobile/figma/organism/bottom-sheet/figma-bottom-sheet.png) | [![](/design/parity-mobile/screenshots/organism/bottom-sheet.png)](/design/parity-mobile/screenshots/organism/bottom-sheet.png) | ✅ Tokens núcleo correctos (superficie #FFFFFF, radio superior \~24, título #1F2933, CTA #0052CC, scrim oscuro #626364). El drag handle y la ✕ usan tonos M3/oscuros más VISIBLES que el gris muy claro de Figma (#E3E6E9/#A6ACB7, bajo contraste) — se conservan por afordancia/contraste (ver desviación). Presencia de scrim + drag handle + cierre por gesto = convención M3. | 🔶 **Propuesta** — Hoja modal M3: scrim oscuro, drag handle y ✕ con tono más visible que el gris muy claro de Figma; cierre por gesto. *Figma:* actualizar el prototipo 48:66 para mostrar scrim + handle/✕ con tono representativo de M3. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | ## Pantallas [Section titled “Pantallas”](#pantallas) #### ⚠️ M01 · Iniciar sesión [Section titled “⚠️ M01 · Iniciar sesión”](#️-m01--iniciar-sesión) Nodo Figma [`52:27`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=52-27) · código `app/src/main/java/com/axios/terminales/auth/LoginScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/screen/01-iniciar-sesion/figma-01-iniciar-sesion.png)](/design/parity-mobile/figma/screen/01-iniciar-sesion/figma-01-iniciar-sesion.png) | [![](/design/parity-mobile/screenshots/screen/01-iniciar-sesion.png)](/design/parity-mobile/screenshots/screen/01-iniciar-sesion.png) | ⚠️ Misma pantalla de login: layout, proporción y tokens base coinciden — fondo #F5F7FA en ambos, tile de marca + wordmark bold + tagline gris, campos con relleno claro (#F5F7FA/#F7F9FC) y borde #E5E7EB, enlace ¿Olvidaste tu contraseña? azul #1862D1≈#0052CC, y el botón Entrar es el DS primario #0052CC en el frame. Único gap de STYLE: el botón secundario ‘Acceso rápido con PIN’ se renderiza con borde gris #E5E7EB + texto oscuro #1F2933 (variante DS Outlined neutral), mientras el frame lo dibuja como botón azul (borde + label #0052CC). No cuentan contra parity: el Entrar en gris #E5E7EB y el ‘Enviar enlace mágico’ en gris #9CA3AF son artefactos de captura del estado vacío/deshabilitado (formulario sin datos) — el frame muestra el estado activo; el wordmark ‘terminales’ vs ‘Terminales’ es copy/branding (feature) del recurso app\_name; y el ojo de mostrar/ocultar contraseña es un extra del app. | 🔶 **Propuesta** — El secundario ‘Acceso rápido con PIN’ usa la variante DS Outlined neutral (borde #E5E7EB + texto #1F2933) en vez del outline azul del frame (#0052CC). *Figma:* Actualizar el frame 52:27 para que el botón secundario adopte el outline neutro (borde #E5E7EB + label #1F2933) en lugar del azul #0052CC. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ⚠️ M02 · Acceso rápido (PIN) [Section titled “⚠️ M02 · Acceso rápido (PIN)”](#️-m02--acceso-rápido-pin) Nodo Figma [`300:4256`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=300-4256) · código `app/src/main/java/com/axios/terminales/auth/PinLoginScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/screen/02-acceso-rapido-pin/figma-02-acceso-rapido-pin.png)](/design/parity-mobile/figma/screen/02-acceso-rapido-pin/figma-02-acceso-rapido-pin.png) | [![](/design/parity-mobile/screenshots/screen/02-acceso-rapido-pin.png)](/design/parity-mobile/screenshots/screen/02-acceso-rapido-pin.png) | ⚠️ Backspace CORREGIDO: la tecla de acción ahora usa soft-primary (#E6EEFA + icono #0052CC) en vez del token danger rojo — intención neutra igual que el frame 300:4256. Barra blanca + título a la izquierda confirmados; perfiles (JP/AM/LR), PIN dots y teclas de dígito coinciden. Residual menor de STYLE: el gap vertical entre teclas está colapsado (las teclas se leen como columnas conectadas) vs celdas separadas del frame — ajuste de spacing del teclado pendiente. El botón Desbloquear explícito es una desviación propuesta (submit accesible). (Nota: la tecla de borrar del teclado ya se corrigió a neutro soft-primary.) | 🔶 **Propuesta** — La app añade un botón primario ‘Desbloquear’ bajo el teclado (submit explícito) que el frame 300:4256 no incluye. *Figma:* Añadir un botón primario ‘Desbloquear’ entre el teclado y el enlace ‘Cambiar usuario’ en el frame 300:4256, con estados enabled/disabled/loading. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ M03 · Verificación 2FA [Section titled “✅ M03 · Verificación 2FA”](#-m03--verificación-2fa) Nodo Figma [`55:59`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=55-59) · código `app/src/main/java/com/axios/terminales/auth/MfaScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- | | [![](/design/parity-mobile/figma/screen/03-verificacion-2fa/figma-03-verificacion-2fa.png)](/design/parity-mobile/figma/screen/03-verificacion-2fa/figma-03-verificacion-2fa.png) | [![](/design/parity-mobile/screenshots/screen/03-verificacion-2fa.png)](/design/parity-mobile/screenshots/screen/03-verificacion-2fa.png) | ✅ Re-auditado 2026-08-07 (online, N86 720×1280). RESUELTO (#325/S1): el input OTP de 6 celdas segmentadas (`TerminalesOtpInput`), el contador «Reenviar código en M:SS» (`ResendCode`, 45s) y el CTA «Verificar» ya existen en código (`MfaScreen.kt` → `ChallengeCodeForm`). La captura muestra el selector de factor porque el medio-frame de entrada de código sólo se renderiza tras `challengeId != null` (cuando Appwrite emite el reto); el harness offline no tiene sesión para emitirlo (al tocar «Recibir código» surge «Algo salió mal»), así que ese estado es *verificado en código, no capturable estáticamente*. Tokens correctos y medidos (candado #E6EEFA, CTA/link #0052CC, fondo #F5F7FA): el defecto era la AUSENCIA de la feature, ahora presente. | — | #### ✅ M04 · Recuperar contraseña [Section titled “✅ M04 · Recuperar contraseña”](#-m04--recuperar-contraseña) Nodo Figma [`56:88`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=56-88) · código `app/src/main/java/com/axios/terminales/auth/RecoveryScreens.kt` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/screen/04-recuperar-contrasena/figma-04-recuperar-contrasena.png)](/design/parity-mobile/figma/screen/04-recuperar-contrasena/figma-04-recuperar-contrasena.png) | [![](/design/parity-mobile/screenshots/screen/04-recuperar-contrasena.png)](/design/parity-mobile/screenshots/screen/04-recuperar-contrasena.png) | ✅ Mismo screen (recuperar contraseña) y los tokens coinciden medidos. TOP BAR: confirmado el flip a blanco — barra #FFFFFF con título oscuro alineado a la izquierda “Recuperar contraseña” (≈ #1F2933) y flecha de regreso; igual que el Figma. Tile de ayuda azul claro + círculo de interrogación azul sólido #0052CC (idéntico Figma), headline bold oscuro “¿Olvidaste tu contraseña?”, subtítulo gris, label “Correo Electrónico” y TextField DS con borde claro (#E5E7EB ≈ Figma #F3F5F8) y valor oscuro. STYLE: paleta correcta, no hay tokens mal aplicados. Residuales, todos de ESTADO/captura (no defectos): (1) el CTA “Enviar enlace” aparece en estado DESHABILITADO — relleno gris medido #E5E7EB con texto #B7BBC3 — porque canSendRecovery=false al capturar; el Figma lo dibuja activo azul #0052CC, que es el token correcto del TerminalesButton cuando está enabled; (2) el banner informativo azul “Si el correo existe…” del Figma no aparece en la captura porque recoverySent=false, pero el código sí lo implementa (InlineAlert Info). FEATURE: el Figma incluye una campana de notificaciones al extremo derecho de la top bar que la app omite (AuthTopBar sólo lleva back+título); omisión deliberada y correcta en un flujo pre-login → ver deviation\_candidate. Sin culpable de token. | 🔶 **Propuesta** — La app omite la campana de notificaciones que el Figma dibuja en la top bar de recuperar contraseña *Figma:* Eliminar el icono de campana del trailing de la top bar en el frame 56:88 (y demás frames del flujo auth) para reflejar una barra auth con solo flecha de regreso + título. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ M05 · Seleccionar tienda/terminal [Section titled “✅ M05 · Seleccionar tienda/terminal”](#-m05--seleccionar-tiendaterminal) Nodo Figma [`57:85`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=57-85) · código `app/src/main/java/com/axios/terminales/auth/StoreSelectionScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/screen/05-seleccionar-tienda-terminal/figma-05-seleccionar-tienda-terminal.png)](/design/parity-mobile/figma/screen/05-seleccionar-tienda-terminal/figma-05-seleccionar-tienda-terminal.png) | [![](/design/parity-mobile/screenshots/screen/05-seleccionar-tienda-terminal.png)](/design/parity-mobile/screenshots/screen/05-seleccionar-tienda-terminal.png) | ✅ Misma pantalla (selección de tienda/terminal). Layout idéntico: header con back + título “Selecciona tu tienda”, subtítulo, tres tarjetas de tienda (glifo circular + nombre/dirección + badge de estado) y CTA “Continuar”. Tokens medidos coinciden: CTA #0052CC en ambos (Figma #0052CC / app #0052CC), borde de tarjeta seleccionada = primary #0052CC, superficie de tarjeta #FFFFFF, fondo #F5F7FA idéntico, círculo del glifo seleccionado con tinte primary (Figma #A0BFEC / app #AAC5EE) y badges verde (Abierta) / ámbar (Sin turno). Afordancia de selección correcta: borde 2dp primary + relleno primary-subtle. STYLE: sin desviaciones de token/componente. FEATURE: única diferencia — el header de Figma lleva un icono de campana (notificaciones) arriba a la derecha que la app no renderiza; es una omisión menor en una pantalla pre-sesión, no rompe la paridad. | 🔶 **Propuesta** — La app omite el icono de campana (notificaciones) del header que sí aparece en el frame de Figma. *Figma:* Eliminar el icono de campana del header del frame 57:85 para alinearlo con el AuthTopBar (solo back + título). [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ M06 · Inicio (Dashboard) [Section titled “✅ M06 · Inicio (Dashboard)”](#-m06--inicio-dashboard) Nodo Figma [`300:4317`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=300-4317) · código `app/src/main/java/com/axios/terminales/ui/screens/home/HomeScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/screen/06-inicio-dashboard/figma-06-inicio-dashboard.png)](/design/parity-mobile/figma/screen/06-inicio-dashboard/figma-06-inicio-dashboard.png) | [![](/design/parity-mobile/screenshots/screen/06-inicio-dashboard.png)](/design/parity-mobile/screenshots/screen/06-inicio-dashboard.png) | ✅ Re-auditado 2026-08-07 (online + sembrado, N86 720×1280). RESUELTO (#322/F1): `AppShell` reserva inset inferior = alto del nav-FAB; al hacer scroll al fondo la última fila de «Actividad reciente» ($89.00 · 01:40 p.m.) queda completamente por encima del FAB de navegación — ya no se recorta contenido. Saludo inline «Hola, Ana Cajera» + fecha, KPIs sembrados (Ventas hoy $184,320.00 · Transacciones 1284), CTA «Nueva venta» y grid de accesos. La divergencia vs el frame 300:4317 (top bar + bottom-nav) sigue siendo intencional → ver desviación. (Artefacto de harness, no defecto: FAB llave inglesa «Playground (dev)» abajo-derecha, gated a dashboard bajo `startPlayground`.) | 🔶 **Propuesta** — Dashboard sin top bar (saludo inline) y sin bottom-nav: navegación global vía FAB speed-dial (Inicio/Vender/Carrito/Reportes/Cuenta/Más/Notificaciones). *Figma:* Rehacer M06 300:4317 sin top bar (encabezado inline «Hola, {nombre}») y sin bottom-nav; documentar el FAB de navegación global. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ⚠️ M11 · Terminal POS / Catálogo [Section titled “⚠️ M11 · Terminal POS / Catálogo”](#️-m11--terminal-pos--catálogo) Nodo Figma [`67:303`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=67-303) · código `app/src/main/java/com/axios/terminales/ui/screens/pos/CatalogScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/screen/11-terminal-pos-catalogo/figma-11-terminal-pos-catalogo.png)](/design/parity-mobile/figma/screen/11-terminal-pos-catalogo/figma-11-terminal-pos-catalogo.png) | [![](/design/parity-mobile/screenshots/screen/11-terminal-pos-catalogo.png)](/design/parity-mobile/screenshots/screen/11-terminal-pos-catalogo.png) | ⚠️ Re-auditado 2026-08-07 (online + sembrado, N86). RESUELTO (#324/F5): el FAB de caja (kiosco) ya NO colisiona con la barra CTA inferior — el «+» abajo-izquierda queda despejado del bloque azul «$77.00 · Ver carrito». Encabezado ligero «Punto de venta» presente (#323/S10) y campo de búsqueda con lupa inline (F2). Residual (se conserva ⚠️): (a) la fila de chips de categoría sigue clippeando «Tiempo A…» al borde derecho sin afordancia de fade (S12, no aterrizado); (b) CAPTURA — bajo el harness de seed actual el grid de productos aparece VACÍO (el carrito sí siembra 4 artículos $77.00, pero el listado del catálogo no); es una regresión de siembra del catálogo revelada por la recaptura, NO un defecto de estilo → seguimiento en #350 (no se arregla en esta barrera). | 🔶 **Propuesta** — POS en modo kiosco: sin top bar y sin FAB de navegación; FAB de operaciones de caja (Nueva venta/En espera/Mov. de caja/Asignar cliente/Cerrar turno/Anular venta), espejo del terminal/home web. *Figma:* Actualizar M11 67:303 a layout kiosco (sin top bar, sin bottom-nav) con el FAB de caja; reconciliar el set de acciones con M35 300:4289. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ M13 · Carrito / Venta actual [Section titled “✅ M13 · Carrito / Venta actual”](#-m13--carrito--venta-actual) Nodo Figma [`300:4196`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=300-4196) · código `app/src/main/java/com/axios/terminales/ui/screens/pos/CartScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/screen/13-carrito-venta-actual/figma-13-carrito-venta-actual.png)](/design/parity-mobile/figma/screen/13-carrito-venta-actual/figma-13-carrito-venta-actual.png) | [![](/design/parity-mobile/screenshots/screen/13-carrito-venta-actual.png)](/design/parity-mobile/screenshots/screen/13-carrito-venta-actual.png) | ✅ Re-auditado 2026-08-07 (online + sembrado, N86). RESUELTO (#324/F5): el FAB de caja ya no colisiona con la barra «Cobrar · $77.00» — el «+» abajo-izquierda queda despejado. Carrito sembrado (Coca-Cola ×2, Sabritas ×1, Leche Lala ×1), stepper de cantidad soft-blue, resumen Subtotal/Descuento/IVA/TOTAL $77.00 con TOTAL en primary. Encabezado «Venta actual» presente (#323/S10). Paridad con el frame 300:4196 en modo kiosco. | 🔶 **Propuesta** — Carrito en modo kiosco: sin top bar y sin FAB de navegación; FAB de operaciones de caja. *Figma:* Actualizar M13 300:4196 a layout kiosco (sin top bar, sin bottom-nav) con el FAB de caja. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ M20 · Pago de servicio [Section titled “✅ M20 · Pago de servicio”](#-m20--pago-de-servicio) Nodo Figma [`76:503`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=76-503) · código `app/src/main/java/com/axios/terminales/ui/screens/vas/VasServicePaymentScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- | | [![](/design/parity-mobile/figma/screen/20-pago-de-servicio/figma-20-pago-de-servicio.png)](/design/parity-mobile/figma/screen/20-pago-de-servicio/figma-20-pago-de-servicio.png) | [![](/design/parity-mobile/screenshots/screen/20-pago-de-servicio.png)](/design/parity-mobile/screenshots/screen/20-pago-de-servicio.png) | ✅ Misma pantalla (Pago de servicio) — captura ahora poblada (flag VAS on + VasMocks). Layout idéntico: chips Categoría (Luz/Agua/Gas/Internet/Teléfono, 5 iguales al diseño), ProviderTile CFE, Número de referencia, SummaryRow Importe/Comisión/Total ($688.00) y CTA. Tokens medidos coinciden: fondo #F5F7FA=#F5F7FA, botón/Total en primary #0052CC. FEATURE: paridad completa — VasMocks.kt sirve las mismas 5 categorías + proveedor único CFE (Comisión Federal de Electricidad), sin carencias vs diseño. STYLE: sin desviación de token/componente. Única diferencia de ESTADO (no de token): la app deshabilita el CTA en gris #D0D3DA mientras la referencia obligatoria está vacía; Figma lo muestra azul #0052CC habilitado como snapshot lleno. Es gating de validación correcto (confirmado por M22, donde el campo opcional deja el CTA habilitado), no un defecto. | — | #### ✅ M24 · Mi cuenta [Section titled “✅ M24 · Mi cuenta”](#-m24--mi-cuenta) Nodo Figma [`83:647`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=83-647) · código `app/src/main/java/com/axios/terminales/account/MyAccountScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/screen/24-mi-cuenta/figma-24-mi-cuenta.png)](/design/parity-mobile/figma/screen/24-mi-cuenta/figma-24-mi-cuenta.png) | [![](/design/parity-mobile/screenshots/screen/24-mi-cuenta.png)](/design/parity-mobile/screenshots/screen/24-mi-cuenta.png) | ✅ Re-auditado 2026-08-07 (online + sembrado, N86). RESUELTO (#329/S9): las filas (Datos personales, Dispositivos y terminales, Seguridad, Apariencia, Soporte) se renderizan habilitadas con contraste adecuado — título en TextPrimary oscuro + subtítulo secundario gris + chevron, con ícono en círculo soft; ya no se leen como «cargando». Card de perfil (AC · Ana Cajera · Empleado). «Cerrar sesión» en outline danger. «Sin tienda asignada» es gap de seed, no defecto. | 🔶 **Propuesta** — Mi cuenta sin top bar: el encabezado es la ProfileCard; navegación y notificaciones viven en el FAB global. *Figma:* Quitar la top bar (y su campana) del frame 24 83:647; el encabezado pasa a ser la ProfileCard. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ M25 · Reportes [Section titled “✅ M25 · Reportes”](#-m25--reportes) Nodo Figma [`85:699`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=85-699) · código `app/src/main/java/com/axios/terminales/reporting/ReportsScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/screen/25-reportes/figma-25-reportes.png)](/design/parity-mobile/figma/screen/25-reportes/figma-25-reportes.png) | [![](/design/parity-mobile/screenshots/screen/25-reportes.png)](/design/parity-mobile/screenshots/screen/25-reportes.png) | ✅ Re-auditado 2026-08-07 (online + sembrado, N86). RESUELTO (#322/F1 + #323/S10): encabezado ligero «Reportes» presente y el inset inferior del nav-FAB aplica — al hacer scroll al fondo, «Corte Z», «Ventas del día» y el botón «Exportar / Compartir» quedan por encima del FAB. Sembrado: «Ventas del periodo $184,320.00», Transacciones 1284, Ventas por método (Efectivo $92,160 · Tarjeta $80,160 · Saldo $12,000). «Comisiones —» es gap de seed. La divergencia por top-bar removida es intencional → desviación. | 🔶 **Propuesta** — Reportes sin top bar: título omitido; navegación en el FAB global. *Figma:* Quitar la top bar del frame 25 85:699; opcionalmente añadir un encabezado inline «Reportes». [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ M30 · Clientes — Lista [Section titled “✅ M30 · Clientes — Lista”](#-m30--clientes--lista) Nodo Figma [`300:4041`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=300-4041) · código `app/src/main/java/com/axios/terminales/ui/screens/customers/ClientesListaScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- | | [![](/design/parity-mobile/figma/screen/30-clientes-lista/figma-30-clientes-lista.png)](/design/parity-mobile/figma/screen/30-clientes-lista/figma-30-clientes-lista.png) | [![](/design/parity-mobile/screenshots/screen/30-clientes-lista.png)](/design/parity-mobile/screenshots/screen/30-clientes-lista.png) | ✅ Re-auditado 2026-08-07 (online, N86). RESUELTO (#333/F2): el campo «Buscar cliente…» usa lupa inline sin botón azul trailing. Lista sembrada (María González, Juan Pérez, Sofía Ramírez, Carlos López, Ana Torres, Cliente general) con avatar de iniciales + chevron. Encabezado «Clientes» con back. Paridad con 300:4041. | — | #### ✅ M31 · Clientes — Detalle [Section titled “✅ M31 · Clientes — Detalle”](#-m31--clientes--detalle) Nodo Figma [`300:4052`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=300-4052) · código `app/src/main/java/com/axios/terminales/ui/screens/customers/ClienteDetalleScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/screen/31-clientes-detalle/figma-31-clientes-detalle.png)](/design/parity-mobile/figma/screen/31-clientes-detalle/figma-31-clientes-detalle.png) | [![](/design/parity-mobile/screenshots/screen/31-clientes-detalle.png)](/design/parity-mobile/screenshots/screen/31-clientes-detalle.png) | ✅ Re-captura correcta: ahora sí es el detalle del cliente. Paridad de tokens completa. Header avatar #E6EEFA + iniciales azules, nombre oscuro y subtítulo teléfono·email gris coinciden. Grid 2×2 de KpiTiles blancos sobre fondo #F5F7FA idéntico. Tokens medidos exactos: Total gastado en Primary azul (app #0040C0/#0050C0 vs figma #0050C0, ≈ #0052CC), Saldo (fiado) en Success verde (app #10B980 vs figma #10B080/#00B070, ≈ #10B981), Compras/Ticket prom. en neutro oscuro sobre blanco. Sección ‘Compras recientes’ con las mismas 3 filas mock (24 Oct #1042 $372.00 / 22 Oct #1019 $640.00 / 20 Oct #0987 $120.00), fecha·folio a la izquierda y monto a la derecha. CTA ‘Nueva venta’ medido #0052CC exacto en ambos lados. No hay desviación de STYLE ni de FEATURE en el contenido. Nota menor de chrome compartida: el ícono de campana del top app bar presente en Figma (gris #607080) está ausente en el app (igual que en M30); es shell global del app, no un defecto de contenido de esta pantalla. | — | #### ⚠️ M07 · Notificaciones [Section titled “⚠️ M07 · Notificaciones”](#️-m07--notificaciones) Nodo Figma [`60:177`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=60-177) · código `app/src/main/java/com/axios/terminales/notifications/NotificationsScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- | | [![](/design/parity-mobile/figma/screen/07-notificaciones/figma-07-notificaciones.png)](/design/parity-mobile/figma/screen/07-notificaciones/figma-07-notificaciones.png) | [![](/design/parity-mobile/screenshots/screen/07-notificaciones.png)](/design/parity-mobile/screenshots/screen/07-notificaciones.png) | ⚠️ Misma pantalla, ahora poblada (3 notifs sembradas agrupadas por día). TopAppBar blanca #FFFFFF ‘Notificaciones’ + back correctas. Divergencias de ESTILO medidas: (1) Indicador de no-leído — la app pinta un punto ROJO #EF4444 a la DERECHA de la fila, mientras Figma usa punto AZUL (medido #CCDCF5 sobre tarjeta tintada #E6EEFA) a la IZQUIERDA + fondo de tarjeta azul claro para los no-leídos; token semántico equivocado (danger vs primary). (2) Contenedor de ícono — la app usa cuadro redondeado (squircle) tintado (glifo #0052CC), Figma usa CÍRCULO de color por tipo. (3) Cabecera — la app movió ‘marcar todas como leídas’ a un ícono de check en la app bar y OMITE la fila contadora ‘N sin leer’ + el link de texto azul ‘Marcar todas como leídas’ que dibuja Figma. Diferencia de formato (no estilo): agrupación por fecha absoluta ‘25 Jul 2025 / 24 Jul 2025’ (NotificationDayGrouper.kt) vs ‘Hoy / Ayer’ del Figma; y las filas muestran fecha como tercera línea en vez de hora HH:MM. Contenido distinto por datos sembrados (esperado). Culpable: NotificationRow/NotificationsScreen (forma de ícono + indicador no-leído + cabecera contadora). | — | #### ⚠️ M08 · Abrir turno [Section titled “⚠️ M08 · Abrir turno”](#️-m08--abrir-turno) Nodo Figma [`61:212`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=61-212) · código `app/src/main/java/com/axios/terminales/shifts/ (grep-confirm)` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/screen/08-abrir-turno/figma-08-abrir-turno.png)](/design/parity-mobile/figma/screen/08-abrir-turno/figma-08-abrir-turno.png) | [![](/design/parity-mobile/screenshots/screen/08-abrir-turno.png)](/design/parity-mobile/screenshots/screen/08-abrir-turno.png) | ⚠️ Misma pantalla (encabezado resumen + keypad numérico + monto + botón ‘Abrir turno’). STYLE: (1) el encabezado ShiftSummaryCard renderiza SummaryRow apiladas a sangre completa sobre Surface blanco sin card/radio (Terminal —, Cajero —, y una fila extra Fondo de caja $0.00) en vez de la Card blanca insertada de DOS columnas Terminal | 🔶 **Propuesta** — Tecla borrar del keypad en rojo (#F58181, token KeypadDelete + ícono X) en vez del neutro azul del Figma (#F0F4F8 / ⌫ #83AAE4). *Figma:* Actualizar el keypad del DS en Figma para que la tecla borrar use el fondo/ícono danger, alineando el diseño con NumericKeypad.KeypadDeleteButton. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ⚠️ M09 · Cerrar turno (Corte Z) [Section titled “⚠️ M09 · Cerrar turno (Corte Z)”](#️-m09--cerrar-turno-corte-z) Nodo Figma [`300:4229`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=300-4229) · código `app/src/main/java/com/axios/terminales/shifts/ (grep-confirm)` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- | | [![](/design/parity-mobile/figma/screen/09-cerrar-turno/figma-09-cerrar-turno.png)](/design/parity-mobile/figma/screen/09-cerrar-turno/figma-09-cerrar-turno.png) | [![](/design/parity-mobile/screenshots/screen/09-cerrar-turno.png)](/design/parity-mobile/screenshots/screen/09-cerrar-turno.png) | ⚠️ Misma pantalla, poblada. Divergencia estructural ACEPTADA (carve-out §4.6): la app usa 3 KpiTile + ‘Efectivo contado’ con NumericKeypad + ‘Diferencia’ + InlineAlert ‘Descuadre detectado’, EN LUGAR del desglose ‘Conteo por denominación’ + botón ‘Imprimir corte (Z)’ del Figma — NO es defecto. Tokens compartidos correctos y medidos: KpiTile fondo blanco #FFFFFF; tecla de borrado del keypad ahora en soft-primary (relleno azul #1F67D2 con glifo claro #D0DFF6, según DS, ya NO roja como en la nota vieja de M08); InlineAlert danger y ‘Diferencia -$92,160.00’ en rojo (verificado visualmente contra el rojo del Figma). RESIDUAL CONOCIDO (menor) confirmado con datos: los 3 KpiTile en fila truncan valores largos de dinero — se leen ‘$184,3…’, ‘$92,16…’, ‘$80,16…’ en vez del monto completo. Culpable del ⚠️: el ancho de KpiTile en fila de 3 (truncamiento), no la estructura del carve-out. | — | #### ✅ M10 · Movimientos de efectivo [Section titled “✅ M10 · Movimientos de efectivo”](#-m10--movimientos-de-efectivo) Nodo Figma [`63:258`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=63-258) · código `app/src/main/java/com/axios/terminales/shifts/ (grep-confirm)` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/screen/10-movimientos-de-efectivo/figma-10-movimientos-de-efectivo.png)](/design/parity-mobile/figma/screen/10-movimientos-de-efectivo/figma-10-movimientos-de-efectivo.png) | [![](/design/parity-mobile/screenshots/screen/10-movimientos-de-efectivo.png)](/design/parity-mobile/screenshots/screen/10-movimientos-de-efectivo.png) | ✅ Re-auditado 2026-08-07 (online + sembrado, N86). RESUELTO (#328/S2): (1) el FAB «+» ahora tiene tamaño acotado (píldora soft-blue abajo-derecha), ya no gigante cubriendo la lista; (2) el subtítulo muestra hora formateada «Entrada · 08:00 / Salida · 11:30 / Entrada · 13:15», no el timestamp ISO crudo; (3) cada fila lleva círculo de dirección por tipo (Entrada = «+» verde #41C59D en círculo soft; Salida = flecha roja) y las filas se agrupan en un Card. Montos medidos: −$500.00 rojo #F58888 (Error), +montos verde (Success) — coinciden con Figma 63:258. Cabecera «Movimientos de caja» con back. | — | #### ⚠️ M12 · Detalle de producto [Section titled “⚠️ M12 · Detalle de producto”](#️-m12--detalle-de-producto) Nodo Figma [`68:344`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=68-344) · código `app/src/main/java/com/axios/terminales/ui/screens/pos/ProductDetailScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/screen/12-detalle-de-producto/figma-12-detalle-de-producto.png)](/design/parity-mobile/figma/screen/12-detalle-de-producto/figma-12-detalle-de-producto.png) | [![](/design/parity-mobile/screenshots/screen/12-detalle-de-producto.png)](/design/parity-mobile/screenshots/screen/12-detalle-de-producto.png) | ⚠️ Re-capturado 2026-08-05 (online + sembrado, sin banner) — AHORA POBLADO (Coca-Cola 600ml, $18.00, «Refresco», Cantidad stepper, «Agregar al carrito · $18.00»); resuelve el 🔄 previo (estado loading). Top App Bar «Detalle» + back conservada (pantalla secundaria). Observaciones: «Existencias no disponibles» (stock 0 sembrado) y el nav FAB del shell se solapa con el CTA «Agregar al carrito» (mismo patrón de inset que F1). | — | #### ✅ M14 · Cobrar / Método de pago [Section titled “✅ M14 · Cobrar / Método de pago”](#-m14--cobrar--método-de-pago) Nodo Figma [`70:389`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=70-389) · código `app/src/main/java/com/axios/terminales/ui/screens/pos/CheckoutScreens.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/screen/14-cobrar-metodo-de-pago/figma-14-cobrar-metodo-de-pago.png)](/design/parity-mobile/figma/screen/14-cobrar-metodo-de-pago/figma-14-cobrar-metodo-de-pago.png) | [![](/design/parity-mobile/screenshots/screen/14-cobrar-metodo-de-pago.png)](/design/parity-mobile/screenshots/screen/14-cobrar-metodo-de-pago.png) | ✅ Misma pantalla y paridad de tokens sólida. Título “Cobrar” + flecha atrás en ambos, Top App Bar blanca, fondo #F5F7FA. Tarjeta “Total a cobrar” con primario medido #0052CC == #0052CC (app). Lista de métodos idéntica en orden y copy: Efectivo (“Pago en efectivo + cambio”, seleccionado), Tarjeta (“Inserta, desliza o acerca”), Saldo (“Saldo del comercio”), Pago mixto (“Combina varios métodos”); tarjeta seleccionada con tinte azul claro #DCE4F0 (Figma) vs #E6EEFA (app) — misma familia, diferencia imperceptible. Botón “Continuar” azul primario en ambos. DIFERENCIAS solo de FEATURE/ESTADO, no de token: (1) monto $406.00 (frame) vs $77.00 (app) = dato del carrito; (2) la app deshabilita/atenúa la fila Saldo (CheckoutScreens.kt:133, fuera de alcance MVP) mientras el frame la dibuja activa; (3) la app añade el link “Más acciones” (SharedActionsSheet, CheckoutScreens.kt:69) y el bottom nav, ausentes en el frame aislado; (4) iconos de tender levemente distintos (Figma: cartera verde para Efectivo; app: billete en tile azul), ambos dentro del lenguaje azul del DS. STYLE: sin culpable de token. FEATURE: adiciones de la app (Más acciones, nav) y Saldo deshabilitado. | — | #### ⚠️ M15 · Cobro manual con tarjeta [Section titled “⚠️ M15 · Cobro manual con tarjeta”](#️-m15--cobro-manual-con-tarjeta) Nodo Figma [`71:403`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=71-403) · código `app/src/main/java/com/axios/terminales/ui/screens/pos/ (grep-confirm CardCheckoutScreens.kt)` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- | | [![](/design/parity-mobile/figma/screen/15-cobro-manual-con-tarjeta/figma-15-cobro-manual-con-tarjeta.png)](/design/parity-mobile/figma/screen/15-cobro-manual-con-tarjeta/figma-15-cobro-manual-con-tarjeta.png) | [![](/design/parity-mobile/screenshots/screen/15-cobro-manual-con-tarjeta.png)](/design/parity-mobile/screenshots/screen/15-cobro-manual-con-tarjeta.png) | ⚠️ Misma pantalla (título “Cobro con tarjeta” + flecha atrás, label “Monto a cobrar”, chips de preajuste $100/$200/$500/$1,000, NumericKeypad 3×4 y “Continuar” en ambos; NOTA: este frame es de chrome BLANCO, no lleva el blue-chrome de la barra). Fondo #F5F7FA vs #F5F7FA y teclas del keypad blancas #FFFFFF vs #FFFFFF coinciden. PERO hay dos desviaciones de STYLE reales (CardCheckoutScreens.kt / AmountChipButton.kt): (1) CHIPS de preajuste — Figma son PÍLDORAS grises sin borde ni sombra, relleno medido #F0F4F8, etiqueta entera “$100”…“$1,000”; la app renderiza TARJETAS blancas #FFFFFF con borde + sombra 2dp en un Row weight(1f) de ancho igual, y la etiqueta usa TerminalesFormat.moneyCents → agrega “.00” (“$100.00”), de modo que “$1,000.00” NO cabe y se PARTE en 3 líneas (“$1,0 / 00.0 / 0”) — defecto de formato+ancho. (2) TECLA BORRAR — Figma es azul (chrome del flujo de tarjeta), fondo medido #3D7BD7 con icono azul; la app la pinta ROJA: KeypadDelete #FEE2E2 / KeypadDeleteIcon #EF4444 (medido bg #F26363), desviándose del token azul del frame. DIFERENCIA de ESTADO (no defecto): monto $1,250.00 (frame) vs $0.00 (app, sin captura) y por eso “Continuar” sale deshabilitado en gris #E5E7EB. STYLE culpable: AmountChipButton (tarjeta blanca+sombra en vez de píldora gris; label moneyCents con .00 que provoca wrap) y token KeypadDelete rojo vs azul del frame. (Nota: la tecla de borrar del teclado ya se corrigió a neutro soft-primary.) | — | #### ✅ M23 · Transferir saldo [Section titled “✅ M23 · Transferir saldo”](#-m23--transferir-saldo) Nodo Figma [`82:631`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=82-631) · código `app/src/main/java/com/axios/terminales/ (grep-confirm terminales_transfer)` | Figma | App | Notas | Desviación (propuesta) | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/screen/23-transferir-saldo/figma-23-transferir-saldo.png)](/design/parity-mobile/figma/screen/23-transferir-saldo/figma-23-transferir-saldo.png) | [![](/design/parity-mobile/screenshots/screen/23-transferir-saldo.png)](/design/parity-mobile/screenshots/screen/23-transferir-saldo.png) | ✅ Re-auditado 2026-08-07 (online + sembrado, N86). RESUELTO (#333/F2): el campo «Buscar comercio o folio…» usa lupa inline (`TerminalesDebouncedSearchField`), sin el botón azul trailing extra que marcaba el defecto. Chips de recientes (Norte/Sur/Plaza/Mayoreo), «Monto a transferir $2,000.00» y CTA «Continuar» primary. Paridad con 82:631. | — | #### ✅ M26 · Historial / Buscar ticket [Section titled “✅ M26 · Historial / Buscar ticket”](#-m26--historial--buscar-ticket) Nodo Figma [`86:740`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=86-740) · código `app/src/main/java/com/axios/terminales/reporting/ (grep-confirm)` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/screen/26-historial-buscar-ticket/figma-26-historial-buscar-ticket.png)](/design/parity-mobile/figma/screen/26-historial-buscar-ticket/figma-26-historial-buscar-ticket.png) | [![](/design/parity-mobile/screenshots/screen/26-historial-buscar-ticket.png)](/design/parity-mobile/screenshots/screen/26-historial-buscar-ticket.png) | ✅ Misma pantalla, AHORA POBLADA con las 3 filas sembradas (A-1042 $540.00 Pagado / A-1041 $128.50 Cancelado / A-1040 $89.00 Reembolsado). Andamiaje y tokens correctos y medidos: Top App Bar blanca ‘Historial’ + back; control segmentado ‘Por folio / Por fecha’ y chips ‘Hoy / Semana / Mes’ con pastilla azul en el activo; filas en Card con ícono de recibo circular. StatusBadge con tokens correctos por estado: Pagado en verde (fondo medido #A7E7D1, success), Cancelado en rojo (danger), Reembolsado en ámbar (warning) — cada uno como pastilla StatusBadge coincidente con el DS. Diferencias de ESTADO (no de estilo, no penalizan): la app abre por defecto en la pestaña ‘Por fecha’ (TicketHistoryViewModel init DATE) mientras el frame ilustra ‘Por folio’ seleccionada con el campo ‘Número de folio…’; y la app añade badge de no-leídas ‘2’ rojo donde Figma pone campana. El ícono de fila es recibo para las 3 (datos tipo ticket) vs la variación cart/recibo/teléfono del ejemplo Figma — diferencia de datos, esperado. | — | #### ✅ M29 · Agregar movimiento de caja [Section titled “✅ M29 · Agregar movimiento de caja”](#-m29--agregar-movimiento-de-caja) Nodo Figma [`291:951`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=291-951) · código `app/src/main/java/com/axios/terminales/ (grep-confirm terminales_shift_add_movement)` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/screen/29-agregar-movimiento-caja/figma-29-agregar-movimiento-caja.png)](/design/parity-mobile/figma/screen/29-agregar-movimiento-caja/figma-29-agregar-movimiento-caja.png) | [![](/design/parity-mobile/screenshots/screen/29-agregar-movimiento-caja.png)](/design/parity-mobile/screenshots/screen/29-agregar-movimiento-caja.png) | ✅ Re-auditado 2026-08-07 (online + sembrado, N86). RESUELTO (#326/S3): control segmentado «Entrada | 🔶 **Propuesta** — La app captura el monto con un NumericKeypad bajo el display en lugar del campo de texto ‘Monto’ de Figma. *Figma:* Actualizar el frame 291:951 para colocar el NumericKeypad del DS bajo el display de monto, conservando el campo Monto solo como display de solo lectura. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ M34 · Ventas en espera [Section titled “✅ M34 · Ventas en espera”](#-m34--ventas-en-espera) Nodo Figma [`300:4185`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=300-4185) · código `app/src/main/java/com/axios/terminales/ui/screens/customers/VentasEnEsperaScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/screen/34-ventas-en-espera/figma-34-ventas-en-espera.png)](/design/parity-mobile/figma/screen/34-ventas-en-espera/figma-34-ventas-en-espera.png) | [![](/design/parity-mobile/screenshots/screen/34-ventas-en-espera.png)](/design/parity-mobile/screenshots/screen/34-ventas-en-espera.png) | ✅ Re-auditado 2026-08-07 (online + sembrado, N86). Sembrado (#335): tabs «Mis carritos | — | #### ⚠️ M19 · Detalle de ticket [Section titled “⚠️ M19 · Detalle de ticket”](#️-m19--detalle-de-ticket) Nodo Figma [`75:475`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=75-475) · código `app/src/main/java/com/axios/terminales/reporting/TicketDetailScreen.kt (grep-confirm)` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [![](/design/parity-mobile/figma/screen/19-detalle-de-ticket/figma-19-detalle-de-ticket.png)](/design/parity-mobile/figma/screen/19-detalle-de-ticket/figma-19-detalle-de-ticket.png) | [![](/design/parity-mobile/screenshots/screen/19-detalle-de-ticket.png)](/design/parity-mobile/screenshots/screen/19-detalle-de-ticket.png) | ⚠️ Misma pantalla, AHORA POBLADA (ticket o-1 / Folio A-1042). Alta paridad estructural: Card con SummaryRows, StatusBadge ‘Pagado’ verde (fondo medido #A7E7D0, success), divisor punteado, ‘Total’ en negrita, InlineAlert ámbar ‘Cancelar o reembolsar requiere autorización de un supervisor’ (fondo medido #FFFBEB, warning) y botón outline DANGER ‘Cancelar / Reembolsar’ con texto/borde rojo (medido #F18A8B) — todos correctos vs Figma. Divergencia de ESTILO medida: los botones secundarios ‘Reimprimir’ / ‘Compartir’ usan outline NEUTRO (texto gris medido #60676F, borde gris claro) en lugar del outline PRIMARIO azul del Figma (texto azul, medido antialias #74A0E2). Divergencia de COPY: la app bar dice ‘Detalle de ticket’ vs ‘Ticket #1029’ del Figma (además sin campana). La app AGREGA fila ‘Subtotal’ y línea de tienda ‘Tienda Demo’ que el Figma no muestra (más detalle → candidato de desviación). Culpable del ⚠️: la variante de botón secundario neutro donde el DS/Figma pide primario-outline azul. | 🔶 **Propuesta** — La app muestra desglose ‘Subtotal’ + ‘IVA’ + ‘Total’ y una línea de tienda (‘Tienda Demo’) en el detalle de ticket; el Figma sólo lista ‘IVA incluido’ + ‘TOTAL’ sin subtotal ni tienda. *Figma:* Actualizar el frame 75:475 para incluir un SummaryRow ‘Subtotal’ y una línea de tienda emisora en el encabezado del ticket. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ⚠️ M28 · Detalle de notificación [Section titled “⚠️ M28 · Detalle de notificación”](#️-m28--detalle-de-notificación) Nodo Figma [`164:937`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=164-937) · código `app/src/main/java/com/axios/terminales/notifications/NotificationDetailScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/screen/28-detalle-notificacion/figma-28-detalle-notificacion.png)](/design/parity-mobile/figma/screen/28-detalle-notificacion/figma-28-detalle-notificacion.png) | [![](/design/parity-mobile/screenshots/screen/28-detalle-notificacion.png)](/design/parity-mobile/screenshots/screen/28-detalle-notificacion.png) | ⚠️ Misma pantalla, AHORA POBLADA (notif n-1 ‘Venta completada’, tipo transacción — por eso el contenido difiere del ejemplo ‘Stock bajo/Inventario’ del Figma; diferencia de datos, esperado). Andamiaje correcto: TopAppBar + cabecera (ícono + categoría + fecha) + título + cuerpo + Card ‘Detalles’ con SummaryRow + botones de texto. Botón ‘Marcar como no leída’ en azul (medido antialias #95BEE8, primary ✓). Divergencias de ESTILO medidas: (1) el contenedor del ícono es cuadro redondeado azul (glifo #0052CC) en vez del CÍRCULO de color del Figma — misma divergencia que M07; (2) ‘Descartar’ se pinta en ROJO danger (medido #F2A5A7) mientras el Figma lo usa en AZUL primary (medido antialias #B9CEEF). Divergencia de FEATURE: la app NO renderiza los botones de acción contextual — primario ‘Ver producto’ (azul, medido #95B7EA) + secundario outline ‘Realizar ajuste’ — que trae el Figma; parcialmente explicable porque la notif sembrada es transacción sin deep-link contextual, pero el andamiaje de CTA está ausente. Copy: ‘Notificaciones’ (plural) vs ‘Notificación’ del Figma. Culpable: forma de ícono (cuadro vs círculo) + token de ‘Descartar’ (rojo vs azul) + ausencia de CTA contextual. | — | #### ⚠️ M18 · Confirmar transferencia [Section titled “⚠️ M18 · Confirmar transferencia”](#️-m18--confirmar-transferencia) Nodo Figma [`74:456`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=74-456) · código `app/src/main/java/com/axios/terminales/ui/screens/terminales/TransferReceiptScreen.kt` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [![](/design/parity-mobile/figma/screen/18-confirmar-transferencia-comprobante/figma-18-confirmar-transferencia-comprobante.png)](/design/parity-mobile/figma/screen/18-confirmar-transferencia-comprobante/figma-18-confirmar-transferencia-comprobante.png) | [![](/design/parity-mobile/screenshots/screen/18-confirmar-transferencia-comprobante.png)](/design/parity-mobile/screenshots/screen/18-confirmar-transferencia-comprobante.png) | ⚠️ Same-screen OK: ambos muestran el comprobante de transferencia exitosa. Layout, copy y estructura idénticos: check verde (figma #68D3AF ≈ app #81DABD), título ‘Transferencia exitosa’, monto en azul (app #0052CC), tarjeta con De/Para/Concepto/Folio/Monto y botón primario ‘Listo’ (figma #3E7CD8 ≈ app #3C7BD8, ambos primary #0052CC). STYLE: el botón secundario ‘Compartir comprobante’ difiere — Figma usa variante outlined con texto y borde AZUL (#0052CC, scan #82AAE4), mientras la app usa TerminalesButtonVariant.Outlined con texto neutro oscuro (scan #A8ADB3) + borde gris claro y AGREGA un ícono Share (leadingIcon = TerminalesIcons.Share). No hay diferencia de FEATURE: mismos campos y textos. Diferencia menor de token en un botón. | 🔶 **Propuesta** — El botón ‘Compartir comprobante’ incluye un ícono Share que refuerza el affordance de compartir. *Figma:* Agregar un ícono Share como leadingIcon al botón secundario ‘Compartir comprobante’ en el frame 74:456. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ⚠️ M35 · FAB · Acciones rápidas [Section titled “⚠️ M35 · FAB · Acciones rápidas”](#️-m35--fab--acciones-rápidas) Nodo Figma [`300:4289`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=300-4289) · código `app/src/main/java/com/axios/terminales/ui/screens/customers/PosQuickActionsScaffold.kt` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![](/design/parity-mobile/figma/screen/35-fab-acceso-rapido/figma-35-fab-acceso-rapido.png)](/design/parity-mobile/figma/screen/35-fab-acceso-rapido/figma-35-fab-acceso-rapido.png) | [![](/design/parity-mobile/screenshots/screen/35-fab-acceso-rapido.png)](/design/parity-mobile/screenshots/screen/35-fab-acceso-rapido.png) | ⚠️ Re-auditado 2026-08-07 (online + sembrado, N86). Speed-dial del FAB de caja (kiosco) con el set actualizado: Nueva venta · En espera · Mov. de caja · Asignar cliente · Cerrar turno · Anular venta — espejo del `terminal/home` web. Divergencia intencional vs M35 300:4289 (set de caja del rediseño de navegación) → ver desviación; no es defecto. | 🔶 **Propuesta** — El FAB de caja añade «Cerrar turno» y «Anular venta» y quita «Abrir menú» respecto al set previo; íconos distintivos por acción. *Figma:* Actualizar M35 300:4289 al set de 6 acciones (Nueva venta/En espera/Mov. de caja/Asignar cliente/Cerrar turno/Anular venta) con íconos por acción. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ M32 · Asignar cliente (sheet) [Section titled “✅ M32 · Asignar cliente (sheet)”](#-m32--asignar-cliente-sheet) Nodo Figma [`300:4086`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=300-4086) · código `app/src/main/java/com/axios/terminales/ui/screens/customers/AssignCustomerSheet.kt` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/screen/32-asignar-cliente-sheet/figma-32-asignar-cliente-sheet.png)](/design/parity-mobile/figma/screen/32-asignar-cliente-sheet/figma-32-asignar-cliente-sheet.png) | [![](/design/parity-mobile/screenshots/screen/32-asignar-cliente-sheet.png)](/design/parity-mobile/screenshots/screen/32-asignar-cliente-sheet.png) | ✅ Re-auditado 2026-08-07 (online + sembrado, N86). RESUELTO (#333/F2): dentro del bottom-sheet «Asignar cliente», el campo «Buscar cliente…» usa lupa inline sin botón azul trailing. Botón «Agregar nuevo cliente» (dashed) + link «Ver todos los clientes». Scrim/handle M3. Alcanzado vía FAB de caja → «Asignar cliente». | — | #### ✅ M33 · Alta rápida cliente (sheet) [Section titled “✅ M33 · Alta rápida cliente (sheet)”](#-m33--alta-rápida-cliente-sheet) Nodo Figma [`300:4129`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=300-4129) · código `app/src/main/java/com/axios/terminales/ui/screens/customers/QuickAddCustomerSheet.kt` | Figma | App | Notas | Desviación (propuesta) | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [![](/design/parity-mobile/figma/screen/33-alta-rapida-cliente-sheet/figma-33-alta-rapida-cliente-sheet.png)](/design/parity-mobile/figma/screen/33-alta-rapida-cliente-sheet/figma-33-alta-rapida-cliente-sheet.png) | [![](/design/parity-mobile/screenshots/screen/33-alta-rapida-cliente-sheet.png)](/design/parity-mobile/screenshots/screen/33-alta-rapida-cliente-sheet.png) | ✅ Same-screen OK: ambos son el bottom-sheet ‘Nuevo cliente’ sobre la ‘Venta actual’. FEATURE completo y en paridad: título ‘Nuevo cliente’, los 4 mismos campos en el mismo orden (Nombre completo, Teléfono, Correo (opcional), RFC (opcional, CFDI)) con idénticos placeholders (María González, 55 0000 0000, , XAXX010101000) y la fila de botones Cancelar (outlined azul, figma texto #83ABE6) + Guardar (primary #3B7AD8), confirmados en QuickAddCustomerSheet.kt:57-111 (los botones quedan bajo el fold en el screenshot de la app pero existen en código). App usa drag handle DS (#374151). STYLE menor: el fondo de los campos es un relleno gris claro estándar de la app (#F7F9FC) vs blanco del Figma (#FFFFFF), y la app añade asterisco rojo de requerido (scan #F9BBBB) en Nombre y Teléfono que el Figma no muestra — mejora, no regresión. | 🔶 **Propuesta** — La app marca los campos obligatorios (Nombre completo, Teléfono) con un asterisco rojo de requerido. *Figma:* Agregar el marcador de requerido (asterisco) a las etiquetas ‘Nombre completo’ y ‘Teléfono’ del frame 300:4129. [ver desviaciones](/design/parity-mobile/intentional-deviations/) | #### ✅ M21 · Tiempo Aire (TAE) [Section titled “✅ M21 · Tiempo Aire (TAE)”](#-m21--tiempo-aire-tae) Nodo Figma [`80:545`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=80-545) · código `app/src/main/java/com/axios/terminales/ui/screens/vas/VasCaptureComponents.kt (grep-confirm VasAirtimeRoute)` | Figma | App | Notas | Desviación (propuesta) | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/screen/21-tiempo-aire-tae/figma-21-tiempo-aire-tae.png)](/design/parity-mobile/figma/screen/21-tiempo-aire-tae/figma-21-tiempo-aire-tae.png) | [![](/design/parity-mobile/screenshots/screen/21-tiempo-aire-tae.png)](/design/parity-mobile/screenshots/screen/21-tiempo-aire-tae.png) | ✅ Misma pantalla (Tiempo Aire). Layout idéntico: chips Compañía (Telcel/Movistar/AT\&T/Unefon/Bait, 5 iguales al diseño), Número de teléfono y grid Monto de 6 AmountChipButton ($10/$20/$50/$100/$200/$500) con $50 seleccionado. Tokens medidos coinciden exacto: chip seleccionado fill primary-soft #E6EEFA=#E6EEFA, fondo #F5F7FA, label activo en primary. FEATURE: paridad completa — VasMocks.kt aporta las mismas 5 compañías y las 6 denominaciones, sin carencias vs diseño. STYLE: sin desviación de token/componente. Única diferencia de ESTADO: CTA deshabilitado en gris #D0D3DA (teléfono obligatorio vacío) vs figma azul habilitado; es gating de validación correcto, no defecto de token. | — | #### ✅ M22 · Tarjeta de regalo / Pines [Section titled “✅ M22 · Tarjeta de regalo / Pines”](#-m22--tarjeta-de-regalo--pines) Nodo Figma [`81:589`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=81-589) · código `app/src/main/java/com/axios/terminales/ui/screens/vas/ (grep-confirm VasGiftCardRoute)` | Figma | App | Notas | Desviación (propuesta) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | | [![](/design/parity-mobile/figma/screen/22-tarjeta-de-regalo-pines/figma-22-tarjeta-de-regalo-pines.png)](/design/parity-mobile/figma/screen/22-tarjeta-de-regalo-pines/figma-22-tarjeta-de-regalo-pines.png) | [![](/design/parity-mobile/screenshots/screen/22-tarjeta-de-regalo-pines.png)](/design/parity-mobile/screenshots/screen/22-tarjeta-de-regalo-pines.png) | ✅ Misma pantalla (Tarjeta de regalo). Paridad casi perfecta: chips Marca (Netflix/Spotify/PlayStation/Xbox/Amazon, 5 iguales al diseño), grid Denominación de 6 chips ($100/$200/$300/$500/$1,000/$1,500) con $200 seleccionado, campo Correo del destinatario (opcional), banner info ‘Se emitirá un código PIN…’ y CTA. Tokens medidos coinciden exacto: chip seleccionado primary-soft #E6EEFA, banner azul suave (#EFF6FF app), y CTA primary #0052CC=#0052CC habilitado en ambos (correo opcional no bloquea). FEATURE: paridad completa vía VasMocks.kt (mismas 5 marcas + 6 denominaciones), sin carencias. STYLE: sin desviación de token/componente. | — | # Desviaciones intencionales — Móvil (App mejor que Figma) > **Propósito.** El objetivo de la paridad de diseño es que la app coincida con el diseño en Figma. Pero no toda diferencia es un defecto: a veces la implementación actual es **deliberadamente mejor** que el frame de Figma — más usable, más eficiente, o más consistente con el design system / la plataforma Android. Cuando ese es el caso, **conservamos la implementación** y la registramos aquí con su razón, en lugar de degradarla para igualar el pixel del diseño. > > Esto complementa la galería [`README.md`](/design/parity-mobile/): una entrada aquí convierte un ❌/⚠️ de la galería en un **✅ (desviación aceptada)** — la nota de la galería debe enlazar a la entrada de aquí. ## Cómo se decide una desviación [Section titled “Cómo se decide una desviación”](#cómo-se-decide-una-desviación) Una diferencia se registra como desviación intencional (no como bug) solo si cumple **al menos una** de estas y **ninguna** regla de negocio/accesibilidad la contradice: 1. **Más usuario-amigable** — mejora la usabilidad real en un dispositivo táctil (área de toque ≥48dp, afordancia más clara, menos pasos). 2. **Más eficiente / consistente con el design system** — usa un token, un componente Material 3 o un patrón ya establecido en la app, evitando un valor único del frame. 3. **Más consistente con la plataforma** — respeta convenciones de Android/Material (ripple, back gesture, insets, teclado) que el frame —pensado de forma genérica— no refleja. Requisitos de la entrada: **evidencia medida** (no “se ve mejor”), el archivo Kotlin, y —si aplica— una nota para que **el diseño en Figma se actualice** a la implementación (para que el frame deje de ser la “verdad” divergente). ## Estado [Section titled “Estado”](#estado) * **Abierto 2026-07-25.** Auditados átomos (10), moléculas (9) y organismos (4). Veredicto galería acumulado: **✅ 23 · ⚠️ 0 · ❌ 0** (23 componentes) — capa de componentes en verde. Correcciones: Top App Bar (azul→blanco), Button (Tonal → primary-soft), Inline Alert (radio sm), Summary Row (TOTAL primary), List Row (contenedor círculo+primary-soft), Cart Bar (icono carrito), Quantity Stepper (FilledTonal), Provider Tile (label visible), Icon Button (glifo azul), y Search/Text Field verificados con foco real. Cada ✅ o iguala el frame o lleva una desviación documentada. * **9 candidatas** de desviación: **1 aceptada** (Provider Tile — badge de marca, 2026-07-26) y **8 propuestas** pendientes de visto bueno. Se listan con la regla que cumplen, la evidencia medida y una recomendación. ## Candidatas propuestas (pendientes de aceptación) [Section titled “Candidatas propuestas (pendientes de aceptación)”](#candidatas-propuestas-pendientes-de-aceptación) > Estas surgieron de la comparación Figma↔App. Cada una es un caso donde la app difiere del frame de forma potencialmente mejor. **Decisión pendiente**: aceptar (conservar la app + actualizar Figma) o rechazar (igualar el frame). ### Keypad Key — press con ripple de Material + target táctil ≥48dp (en vez del fill lavanda estático) [Section titled “Keypad Key — press con ripple de Material + target táctil ≥48dp (en vez del fill lavanda estático)”](#keypad-key--press-con-ripple-de-material--target-táctil-48dp-en-vez-del-fill-lavanda-estático) * **Capa / nodo Figma:** atom · `37:45` * **Código:** `app/src/main/java/com/axios/terminales/ui/components/terminales/atoms/KeypadKey.kt` * **Diseño dice:** estado Pressed = fill lavanda estático `#E0E0F0` + dígito azul. * **App hace:** tecla 72×48dp (aspectRatio 1.5) con ripple de Material animado sobre `.clickable()`. * **Por qué se conserva:** reglas **1 (usabilidad)** y **3 (plataforma)** — cumple el mínimo táctil de 48dp de Material y comunica el press con feedback animado, práctica idiomática de Compose. * **Acción en Figma:** documentar el Pressed del nodo `37:45` como ripple/overlay + anotar target ≥48dp. * **Recomendación:** **aceptar.** · **Aceptada por:** *(pendiente)* · **Fecha:** — ### Search Field — botón limpiar (✕) de \~48dp cuando hay texto (ausente en el frame) [Section titled “Search Field — botón limpiar (✕) de \~48dp cuando hay texto (ausente en el frame)”](#search-field--botón-limpiar--de-48dp-cuando-hay-texto-ausente-en-el-frame) * **Capa / nodo Figma:** atom · `37:35` * **Código:** `app/src/main/java/com/axios/terminales/ui/components/terminales/atoms/TerminalesSearchField.kt` * **Diseño dice:** el variant Active solo muestra el placeholder, sin affordance de limpiar. * **App hace:** `IconButton` M3 (\~48dp) con ✕ (`TextSecondary #6F7684`) para limpiar en un toque (issue #217 AC2). * **Por qué se conserva:** reglas **1 (usabilidad)** y **2 (DS/consistencia)** — convención estándar de búsqueda; limpiar en un toque. * **Acción en Figma:** agregar un icono trailing ✕ al variant Active del nodo `37:35`. * **Recomendación:** **aceptar.** · **Aceptada por:** *(pendiente)* · **Fecha:** — ### Button (Filled) — Pressed vía state-layer/ripple de M3 (no intercambia a `#003D99`) [Section titled “Button (Filled) — Pressed vía state-layer/ripple de M3 (no intercambia a #003D99)”](#button-filled--pressed-vía-state-layerripple-de-m3-no-intercambia-a-003d99) * **Capa / nodo Figma:** atom · `21:50` * **Código:** `app/src/main/java/com/axios/terminales/ui/components/terminales/atoms/TerminalesButton.kt` * **Diseño dice:** Pressed = relleno `#003D99` (primary-strong). * **App hace:** el relleno se mantiene en `#0052CC`; el press se comunica con ripple/state-layer de M3. * **Por qué se conserva (parcial):** regla **3 (plataforma)** — M3 usa overlay de ripple. **Pero** el token `ButtonPrimaryPressed #003D99` existe y está sin cablear, y el frame sí muestra el color; un state-layer sobre el token oscuro daría lo mejor de ambos. * **Acción en Figma:** n/a (plataforma) — o mejor: **cablear el token** y dejar el ripple encima. * **Recomendación:** **decisión pendiente** (se inclina a *arreglar*: cablear `#003D99` + conservar ripple). ### Icon Button (Outlined/Ghost) — forma circular de M3 (no squircle como Standard/Figma) [Section titled “Icon Button (Outlined/Ghost) — forma circular de M3 (no squircle como Standard/Figma)”](#icon-button-outlinedghost--forma-circular-de-m3-no-squircle-como-standardfigma) * **Capa / nodo Figma:** atom · `34:73` * **Código:** `app/src/main/java/com/axios/terminales/ui/components/terminales/atoms/TerminalesIconButton.kt` * **Diseño dice:** las 3 variantes son squircles (radio md) con glifo azul `#0052CC`. * **App hace:** Standard = squircle azul ✓; Outlined/Ghost = círculo de M3 con glifo `TextPrimary #1F2933`. * **Por qué se conserva (parcial):** regla **3 (plataforma)** — el `OutlinedIconButton`/`IconButton` de M3 es circular por convención. **Pero** rompe la consistencia interna con Standard (squircle) y el color del glifo **ya se corrigió** (ahora azul primary en las tres variantes). * **Acción en Figma:** n/a para la forma (circular = convención M3); el glifo ya iguala el frame (azul). * **Recomendación:** **decisión pendiente solo para la forma** (aceptar circular M3, o unificar Standard a circular en el frame). El color del glifo ya está corregido → gallery ✅. ### Product Card — estado “Agotado” atenúa la tarjeta a 60% y la vuelve no-interactiva [Section titled “Product Card — estado “Agotado” atenúa la tarjeta a 60% y la vuelve no-interactiva”](#product-card--estado-agotado-atenúa-la-tarjeta-a-60-y-la-vuelve-no-interactiva) * **Capa / nodo Figma:** molecule · `44:50` * **Código:** `app/src/main/java/com/axios/terminales/ui/components/terminales/molecules/ProductCard.kt` * **Diseño dice:** la tarjeta agotada mantiene opacidad plena (nombre `#1F2933`, precio `#0052CC`). * **App hace:** baja toda la tarjeta a `alpha 0.6` y deshabilita el toque. * **Por qué se conserva:** regla **1 (usabilidad)** — atenuar + bloquear el toque de un producto no disponible evita agregar al carrito un artículo agotado; convención de e-commerce. * **Acción en Figma:** añadir un estado OutOfStock al 60% (aspecto no-interactivo) al componente `44:50`. * **Recomendación:** **aceptar.** · **Aceptada por:** *(pendiente)* · **Fecha:** — ### List Row — chevron de navegación gris (text-tertiary) en vez del azul de Figma [Section titled “List Row — chevron de navegación gris (text-tertiary) en vez del azul de Figma”](#list-row--chevron-de-navegación-gris-text-tertiary-en-vez-del-azul-de-figma) * **Capa / nodo Figma:** molecule · `41:60` * **Código:** `app/src/main/java/com/axios/terminales/ui/components/terminales/molecules/ListRow.kt` * **Diseño dice:** chevron azul primary `#0052CC`. * **App hace:** chevron gris `TextTertiary #9CA3AF`. * **Por qué se conserva:** regla **3 (plataforma)** — el indicador de disclosure gris/neutro (`onSurfaceVariant`) es la convención de Material en Android; el azul se reserva para acciones. * **Acción en Figma:** re-tintar el chevron del frame `41:60` a text-tertiary gris. * **Recomendación:** **aceptar.** · **Aceptada por:** *(pendiente)* · **Fecha:** — * **Nota:** el contenedor del icono guía (antes cuadrado+SurfaceVariant) **ya se corrigió** a círculo+primary-soft, igualando el frame; solo el chevron gris se conserva como esta desviación. ### Cart Bar — CTA “Ver carrito” como píldora blanca (no texto plano) [Section titled “Cart Bar — CTA “Ver carrito” como píldora blanca (no texto plano)”](#cart-bar--cta-ver-carrito-como-píldora-blanca-no-texto-plano) * **Capa / nodo Figma:** molecule · `44:80` * **Código:** `app/src/main/java/com/axios/terminales/ui/components/terminales/molecules/CartBar.kt` * **Diseño dice:** “Ver carrito” es texto blanco plano sobre la barra azul. * **App hace:** lo envuelve en una píldora blanca `#FFFFFF` con chevron azul `#0052CC`. * **Por qué se conserva (parcial):** regla **1 (usabilidad)** — la píldora da un blanco de toque mayor y una afordancia de botón más clara para la acción primaria de la barra de carrito. * **Acción en Figma:** actualizar `44:80` a botón/píldora, o confirmar el texto plano como canónico. * **Recomendación:** **decisión pendiente** (afordancia mejor, pero conviene confirmar con diseño). ### Bottom Sheet — convenciones M3 (scrim oscuro, drag handle, cierre por gesto) [Section titled “Bottom Sheet — convenciones M3 (scrim oscuro, drag handle, cierre por gesto)”](#bottom-sheet--convenciones-m3-scrim-oscuro-drag-handle-cierre-por-gesto) * **Capa / nodo Figma:** organism · `48:66` * **Código:** `app/src/main/java/com/axios/terminales/ui/components/terminales/organisms/BottomSheetHost.kt` * **Diseño dice:** frame aislado sobre fondo plano `#F5F5F5`, sin scrim. * **App hace:** scrim oscuro medido `#626364`, drag handle visible y cierre por gesto (M3 `ModalBottomSheet`). * **Por qué se conserva:** regla **3 (plataforma)** — scrim + drag handle + gesto son la anatomía estándar de una hoja modal de Material; mejoran afordancia y accesibilidad. * **Acción en Figma:** actualizar el prototipo `48:66` para mostrar scrim + handle representativo. * **Recomendación:** **aceptar.** · **Aceptada por:** *(pendiente)* · **Fecha:** — * **Nota sobre el tono:** el gris muy claro de Figma (handle `#E3E6E9`, ✕ `#A6ACB7`) es de bajo contraste; los tonos M3 por defecto (más oscuros/visibles) mejoran afordancia y accesibilidad, así que el tono **también** se conserva como parte de esta desviación (no se degrada al gris claro). ### Provider Tile — badge con color de marca sólido (no el gris/soft genérico del frame) [Section titled “Provider Tile — badge con color de marca sólido (no el gris/soft genérico del frame)”](#provider-tile--badge-con-color-de-marca-sólido-no-el-grissoft-genérico-del-frame) * **Capa / nodo Figma:** molecule · `48:63` * **Código:** `app/src/main/java/com/axios/terminales/ui/components/terminales/molecules/ProviderTile.kt` * **Diseño dice:** badge gris neutro (default) / primary-soft (selected) con icono de teléfono. * **App hace:** badge con el **color de marca** del proveedor (Telcel azul, AT\&T azul, Movistar verde) y la inicial de la marca; el aro de selección va en el badge. * **Por qué se conserva:** regla **2 (DS/consistencia)** — el color de marca ayuda al reconocimiento rápido del proveedor en la cuadrícula; es una decisión deliberada del componente (`brandColor` por marca). * **Acción en Figma:** actualizar el nodo `48:63` a la variante con color de marca. * **✅ ACEPTADA — Aceptada por:** usuario/PO · **Fecha:** 2026-07-26 · el color de marca se conserva. * **Nota:** el label seleccionado invisible (blanco sobre blanco) **ya fue corregido** → ahora Primary azul. ## Rediseño de navegación — re-auditoría 2026-08-05 (nivel pantalla/navegación) [Section titled “Rediseño de navegación — re-auditoría 2026-08-05 (nivel pantalla/navegación)”](#rediseño-de-navegación--re-auditoría-2026-08-05-nivel-pantallanavegación) > Estas surgieron de la re-auditoría del **rediseño de navegación** de esta iteración (FAB de navegación global en vez de bottom-nav, remoción de top bars en las 5 pantallas principales, modo kiosco en POS, y el nuevo hub «Más opciones»). A diferencia de las candidatas de arriba (nivel componente), estas son a nivel de **pantalla/navegación**. Capturas: `audit/re-audit-2026-08-05/` y resumen en `RE-AUDIT-2026-08-05.md`. **Decisión pendiente de PO.** ### Navegación — FAB speed-dial global reemplaza la bottom-nav bar [Section titled “Navegación — FAB speed-dial global reemplaza la bottom-nav bar”](#navegación--fab-speed-dial-global-reemplaza-la-bottom-nav-bar) * **Capa / nodo Figma:** organism · `45:56` (Bottom Nav Bar) + `45:53` (Bottom Nav Item) * **Código:** `app/src/main/java/com/axios/terminales/ui/components/terminales/organisms/NavFab.kt`, `.../templates/AppShell.kt` (bottomBar removido), `MainActivity.kt` (montaje del FAB) * **Diseño dice:** barra de pestañas inferior con 5 destinos (Inicio/Vender/Carrito/Reportes/Cuenta). * **App hace:** un **FAB de navegación global** abajo-izquierda (speed-dial) con 7 entradas (Inicio/Vender/Carrito/Reportes/Cuenta/Más…/Notificaciones); el destino activo se muestra colapsado y las no-leídas como badge. * **Por qué se conserva:** rediseño de navegación aprobado en producto — un FAB global unifica navegación y notificaciones, libera la franja inferior y habilita el modo kiosco en POS. * **Acción en Figma:** deprecar el bottom-nav `45:56`/`45:53` y crear el componente **«FAB de navegación (speed-dial)»** con estado colapsado=destino activo + badge de no-leídas. * **Recomendación:** **aceptar** (cambio intencional de esta iteración). · **Aceptada por:** *(pendiente)* · **Fecha:** — ### Top App Bar — removida de las 5 pantallas principales del shell [Section titled “Top App Bar — removida de las 5 pantallas principales del shell”](#top-app-bar--removida-de-las-5-pantallas-principales-del-shell) * **Capa / nodo Figma:** organism · `40:45` (en M06 `300:4317`, M11 `67:303`, M13 `300:4196`, M24 `83:647`, M25 `85:699`) * **Código:** `TerminalesTopBars.kt`, `HomeScreen.kt`, `CatalogScreen.kt`, `CartScreen.kt`, `ReportsScreen.kt`, `MyAccountScreen.kt` * **Diseño dice:** cada pantalla principal lleva una top bar con título (y campana de notificaciones). * **App hace:** **sin top bar** en las 5 pantallas; el encabezado es inline (Inicio: «Hola, {nombre}»; Cuenta: ProfileCard) o ausente (Vender/Carrito/Reportes); la campana pasó al FAB de navegación. * **Por qué se conserva:** libera altura vertical (relevante en N86) y unifica navegación/notificaciones en el FAB. El componente `40:45` sigue vigente para pantallas secundarias/auth. * **Acción en Figma:** quitar la top bar de los 5 frames; documentar notificaciones en el FAB; conservar `40:45` para secundarias. * **Recomendación:** **aceptar** — con seguimiento del hallazgo de polish #3 (encabezados inconsistentes en Vender/Carrito/Reportes). · **Aceptada por:** *(pendiente)* · **Fecha:** — * **Actualización (NavShell #441):** el rediseño de navegación **restituye la top bar por nivel** en los **roots de área** del drawer: **Reportes**, **Cuenta**, **Clientes** y **Saldo** ahora llevan la barra root (`TerminalesMainTopBar`-style: hamburguesa que abre el drawer + título, sin flecha atrás). Se conserva el encabezado inline solo en **Inicio** (M06, propiedad del ticket Home-extras) y el modo **kiosco** (Vender/Carrito) sigue sin top bar. **Notificaciones** queda pendiente (mantiene su flecha atrás porque es también el destino de la campana desde toda la app y su back-stack unwinding está fijado por un test de navegación) — a resolver con diseño. ### POS en modo kiosco — FAB de operaciones de caja, sin navegación global [Section titled “POS en modo kiosco — FAB de operaciones de caja, sin navegación global”](#pos-en-modo-kiosco--fab-de-operaciones-de-caja-sin-navegación-global) * **Capa / nodo Figma:** screen · M11 `67:303` + M13 `300:4196` (+ FAB M35 `300:4289`) * **Código:** `app/src/main/java/com/axios/terminales/ui/screens/pos/` (PosQuickActionsScaffold, Fab.kt) * **Diseño dice:** catálogo/carrito con top bar y navegación estándar. * **App hace:** **modo kiosco** — sin top bar, **sin FAB de navegación**, y un **FAB de operaciones de caja** (Nueva venta · En espera · Mov. de caja · Asignar cliente · Cerrar turno · Anular venta), espejo del `terminal/home` web. * **Por qué se conserva:** paridad con el kiosco web; el cajero opera la venta sin navegación global, evitando salidas accidentales durante el cobro. * **Acción en Figma:** actualizar M11/M13 a layout kiosco y reconciliar el set de acciones del FAB de caja con M35 `300:4289` (definir cuál es canónico). * **Recomendación:** **aceptar** — con seguimiento del polish #2 (el FAB colapsado se solapa con la barra CTA). · **Aceptada por:** *(pendiente)* · **Fecha:** — ### Dashboard — encabezado inline «Hola, {nombre}» en vez de top bar [Section titled “Dashboard — encabezado inline «Hola, {nombre}» en vez de top bar”](#dashboard--encabezado-inline-hola-nombre-en-vez-de-top-bar) * **Capa / nodo Figma:** screen · M06 `300:4317` * **Código:** `app/src/main/java/com/axios/terminales/ui/screens/home/HomeScreen.kt` * **Diseño dice:** dashboard con top bar (azul en la implementación previa). * **App hace:** sin top bar; saludo inline «Hola, {nombre}» + fecha como encabezado; FAB de navegación presente. * **Por qué se conserva:** encabezado inline más cálido/personal y consistente con la remoción de top bars. * **Acción en Figma:** rehacer M06 `300:4317` con encabezado inline (sin top bar) y el FAB de navegación. * **Recomendación:** **aceptar.** · **Aceptada por:** *(pendiente)* · **Fecha:** — ### Notificaciones — entrada dentro del FAB de navegación (antes campana en top bar) [Section titled “Notificaciones — entrada dentro del FAB de navegación (antes campana en top bar)”](#notificaciones--entrada-dentro-del-fab-de-navegación-antes-campana-en-top-bar) * **Capa / nodo Figma:** organism · `40:45` (campana de la top bar) * **Código:** `MainActivity.kt` (`onNotificationsClick` → drawer), `.../organisms/NavFab.kt` (entrada + badge) * **Diseño dice:** campana de notificaciones en la top bar de cada pantalla. * **App hace:** entrada «Notificaciones» dentro del FAB de navegación, con badge de no-leídas; abre el drawer. * **Por qué se conserva:** al remover las top bars, el FAB es el punto único de navegación/estado; el badge conserva la señal de no-leídas. * **Acción en Figma:** reflejar el nuevo punto de entrada (Notificaciones en el FAB) en el sistema de navegación del diseño. * **Recomendación:** **aceptar.** · **Aceptada por:** *(pendiente)* · **Fecha:** — ### «Más opciones» — nuevo hub de navegación (sucesor del action-hub), sin nodo en Figma [Section titled “«Más opciones» — nuevo hub de navegación (sucesor del action-hub), sin nodo en Figma”](#más-opciones--nuevo-hub-de-navegación-sucesor-del-action-hub-sin-nodo-en-figma) * **Capa / nodo Figma:** screen · *(sin nodo — pantalla nueva)* * **Código:** `app/src/main/java/com/axios/terminales/ui/screens/more/MoreScreen.kt` * **Diseño dice:** no existe. * **App hace:** hub «Más opciones» con secciones (Turno de caja / Cobros y pagos / Clientes / Terminal / Reportes / Otros) y filas flag-gated que se ocultan; alcanzable desde el FAB → «Más…». * **Por qué se conserva:** alberga los destinos que no caben en el FAB de navegación; sucede al action-hub al remover la bottom-nav. * **Acción en Figma:** **crear un frame nuevo** para el hub «Más opciones» con sus secciones y filas. * **Recomendación:** **aceptar** — con seguimiento del polish #5 (densidad de lista holgada). · **Aceptada por:** *(pendiente)* · **Fecha:** — ## Registro de desviaciones [Section titled “Registro de desviaciones”](#registro-de-desviaciones) Plantilla — copiar por cada desviación aceptada: ```md ### Componente — resumen de la desviación en una línea - **Capa / nodo Figma:** atom · `NN:NN` - **Código:** `app/src/main/java/.../Foo.kt` - **Diseño dice:** lo que muestra el frame, medido: hex/dp/estructura - **App hace:** lo que hace la app, medido - **Por qué se conserva:** regla 1/2/3 + evidencia - **Acción en Figma:** actualizar el frame `NN:NN` a la implementación · o · n/a (plataforma) - **Aceptada por:** PO/diseño · **Fecha:** YYYY-MM-DD ``` ## MerchantVerification (#418) — diseñado en canvas, sin frame de Figma [Section titled “MerchantVerification (#418) — diseñado en canvas, sin frame de Figma”](#merchantverification-418--diseñado-en-canvas-sin-frame-de-figma) > Las tres superficies de verificación de comercio (banner de estado, diálogo de acción bloqueada y pantalla de progreso) **no tienen frames de Figma** (spec D2 / survey §3): su diseño canónico es el canvas aprobado `docs/design/canvases/merchant-verification-ux/` (artboards `MobileBanner*`, `MobileBlockedDialog*`, `MobileProgress*`, PO 2026-09-02). Se registra aquí como desviación intencional siguiendo el precedente del gap #55: la “verdad” de diseño es el canvas, no un nodo de Figma, por lo que la auditoría de paridad debe citar los **nombres de artboard**, nunca un `node-id`. ### Banner / Diálogo / Progreso de verificación — anatomía del design system sobre canvas [Section titled “Banner / Diálogo / Progreso de verificación — anatomía del design system sobre canvas”](#banner--diálogo--progreso-de-verificación--anatomía-del-design-system-sobre-canvas) * **Capa / nodo Figma:** *(sin nodo — diseñado en canvas, artboards `MobileBanner*` / `MobileBlockedDialog*` / `MobileProgress*`)* * **Código:** `app/src/main/java/com/axios/terminales/ui/components/verification/{MerchantVerificationBanner,MerchantVerificationBlockedDialog}.kt`, `app/src/main/java/com/axios/terminales/ui/screens/verification/MerchantVerificationScreen.kt` * **Diseño dice:** canvas (no Figma) — banner tipo `OfflineBanner`, diálogo `AlertDialog` del DS, pantalla con filas + badges + step markers + InlineAlert por estado. * **App hace:** reutiliza la anatomía del DS (`OfflineBanner`, `AlertDialog` con tokens, `StatusBadge`, `InlineAlert`, `TerminalesButton`, `TerminalesTopBar`) ligada a `TerminalesColors`. * **Por qué se conserva:** regla **2 (DS/consistencia)** — no hay frame que igualar; el canvas define la intención y el DS provee la anatomía shippeada. * **Acción en Figma:** n/a — si se crean frames, deben derivarse del canvas aprobado. * **Nota — copy fuera de Appendix A:** el badge por-fila del estado “Completado” (`merchant_verification_check_completed`) no aparece en la tabla Appendix A (que solo lista labels de estado global); se agregó como único label de fila-completada porque el canvas muestra un badge en cada fila (AC4). La fila `aml` no se renderiza mientras el contrato envía `not_required` y Appendix A no trae su copy (AC4). El retorno por deep-link del Custom Tab está diferido (spec §15): la pantalla refresca en foreground y ofrece `Actualizar estado`. * **Recomendación:** **aceptar** (diseñado en canvas, sin frame). · **Aceptada por:** *(pendiente)* · **Fecha:** — ## S13 “N86 fit” — desviaciones deliberadas para el Nexgo N86 (360×640 dp) [Section titled “S13 “N86 fit” — desviaciones deliberadas para el Nexgo N86 (360×640 dp)”](#s13-n86-fit--desviaciones-deliberadas-para-el-nexgo-n86-360640-dp) > Sesión del PO en dispositivo (2026-09-09, evidencia `docs/ui-evidence/n86-s4-2026-09-09/` y `docs/ui-evidence/s13-n86-fit/`). Los frames de Figma están dibujados a 390×844; en el handheld de 640dp de alto varios CTAs quedaban bajo el pliegue. Se registran aquí las diferencias que la app conserva **a propósito** frente al frame; todas cumplen la regla **1 (usabilidad)** y no contradicen ninguna regla de negocio. ### M27d Pago en efectivo — chips enteros en UNA fila, teclado compacto y CTA fijo (UX-2 / UX-4) [Section titled “M27d Pago en efectivo — chips enteros en UNA fila, teclado compacto y CTA fijo (UX-2 / UX-4)”](#m27d-pago-en-efectivo--chips-enteros-en-una-fila-teclado-compacto-y-cta-fijo-ux-2--ux-4) * **Capa / nodo Figma:** screen · M27d (Cobro efectivo) · **Código:** `app/src/main/java/com/axios/terminales/ui/screens/pos/CheckoutScreens.kt` (`CashSheetContent`, `QuickAmountRow`), `.../atoms/AmountChipButton.kt` (`AmountChipStyle.Quick`), `.../atoms/KeypadKey.kt` + `.../molecules/NumericKeypad.kt` (`compact`). * **Diseño dice:** chips de monto de 100dp con dos líneas (“$50.00”), teclado 1.5:1, botones Confirmar + Cancelar al final de una columna que scrollea. * **App hace:** 4 chips de 48dp en una sola fila con etiqueta entera (“$50”, “$100”, “$200”) y “Exacto” con el monto exacto como línea secundaria pequeña; el chip cuyo valor coincide con el monto recibido se marca (PrimarySubtle + texto primary + anillo 1.5dp, `selected` real en a11y); teclado compacto de teclas 44dp; encabezado de hoja con × (reemplaza el botón “Cancelar”); **CTA Confirmar fijo al pie** sobre la barra de navegación (medido: todo cabe en los 520dp de contenido de la hoja sin scroll — `CashSheetLayoutTest`). * **Por qué se conserva:** regla **1** — en el N86 las etiquetas “$50.00” partían a mitad de palabra (“Exac / to”, “$50. / 00”) y el CTA quedaba fuera de pantalla (D4-01); ahora el cobro se completa sin scroll. * **Acción en Figma:** actualizar M27d a chips de una fila con enteros y CTA anclado. * **Aceptada por:** PO (ask directo 2026-09-09) · **Fecha:** 2026-09-10 ### M17 Venta exitosa — Imprimir / Compartir / Nueva venta fijos al pie (UX-3) [Section titled “M17 Venta exitosa — Imprimir / Compartir / Nueva venta fijos al pie (UX-3)”](#m17-venta-exitosa--imprimir--compartir--nueva-venta-fijos-al-pie-ux-3) * **Capa / nodo Figma:** screen · `73:439` · **Código:** `app/src/main/java/com/axios/terminales/ui/screens/pos/PosCheckoutFlowContent.kt` (`CheckoutReceiptContent`). * **Diseño dice:** una sola columna: medallón, tarjeta de recibo y las acciones al final. * **App hace:** la tarjeta scrollea en su propia banda (`RECEIPT_SCROLL_TAG`) y las tres acciones son un pie fijo (`RECEIPT_ACTIONS_TAG`) sobre la barra de navegación — `ReceiptActionsLayoutTest` mide que con 14 líneas “Nueva venta” sigue visible sin scroll. * **Por qué se conserva:** regla **1** — en D4-02 había que scrollear para llegar a “Nueva venta”. * **Acción en Figma:** anotar las acciones de `73:439` como footer fijo. * **Aceptada por:** PO · **Fecha:** 2026-09-10 ### M27b Autorización de supervisor — abre totalmente expandida, teclas compactas (UX-6 / UX-7) [Section titled “M27b Autorización de supervisor — abre totalmente expandida, teclas compactas (UX-6 / UX-7)”](#m27b-autorización-de-supervisor--abre-totalmente-expandida-teclas-compactas-ux-6--ux-7) * **Capa / nodo Figma:** organism · Approval Sheet · **Código:** `app/src/main/java/com/axios/terminales/approvals/ApprovalSheetHost.kt` (`rememberApprovalSheetState`, `skipPartiallyExpanded = true`), `.../organisms/pos/ApprovalSheet.kt` (`compact = true`). * **Diseño dice:** hoja modal M3 estándar (parada a media altura). * **App hace:** sin parada parcial — teclado y fila \[Cancelar | Autorizar] visibles al abrir (D5-06 los ocultaba hasta arrastrar); teclas 44dp para que el cuerpo mida ≤520dp; se conserva el × y el cierre por scrim/gesto/back. Copy: “Ingresa el PIN de **supervisor**” (rol aprobador según `roles-permissions-and-labels`), ya no “gerente”. * **Por qué se conserva:** reglas **1** y **3**. * **Aceptada por:** PO · **Fecha:** 2026-09-10 ### Banner global “Sin conexión” — se colapsa a 28dp cuando la pantalla ya muestra su alerta (UX-8) [Section titled “Banner global “Sin conexión” — se colapsa a 28dp cuando la pantalla ya muestra su alerta (UX-8)”](#banner-global-sin-conexión--se-colapsa-a-28dp-cuando-la-pantalla-ya-muestra-su-alerta-ux-8) * **Capa / nodo Figma:** organism · Offline banner · **Código:** `app/src/main/java/com/axios/terminales/ui/components/network/OfflineBanner.kt` (`compact`), `OfflineBannerPolicy.kt` (`offlineBannerPresentation`, `RegisterScreenOfflineAlert`), `MainActivity.kt`. * **Diseño dice:** banner rojo de 56dp siempre que no hay red. * **App hace:** si la pantalla actual monta su propia alerta accionable (carrito: InlineAlert + Reintentar; selector de tender), el banner global baja a una franja de una línea de 28dp; la alerta de pantalla conserva la acción. Los dos ya no apilan \~170dp en el N86 (D7-01). * **Por qué se conserva:** regla **1**. * **Aceptada por:** PO · **Fecha:** 2026-09-10 ### Provider Tile — seleccionado = fondo PrimarySubtle + anillo 2dp + check (UX-10) [Section titled “Provider Tile — seleccionado = fondo PrimarySubtle + anillo 2dp + check (UX-10)”](#provider-tile--seleccionado--fondo-primarysubtle--anillo-2dp--check-ux-10) * **Capa / nodo Figma:** molecule · `48:63` · **Código:** `.../molecules/ProviderTile.kt`. * **Diseño dice:** el frame marca la selección solo con el label acentuado. * **App hace:** relleno PrimarySubtle en todo el tile, anillo primary 2dp, check pequeño arriba a la derecha (`PROVIDER_TILE_SELECTED_CD`), label primary; el badge de color de marca se conserva (desviación aceptada 2026-07-26). * **Por qué se conserva:** regla **1** — en D9-01 la selección pasaba desapercibida. * **Aceptada por:** PO · **Fecha:** 2026-09-10 ### M28 Detalle de notificación — claves del payload localizadas + “Ver más” (UX-12) [Section titled “M28 Detalle de notificación — claves del payload localizadas + “Ver más” (UX-12)”](#m28-detalle-de-notificación--claves-del-payload-localizadas--ver-más-ux-12) * **Código:** `app/src/main/java/com/axios/terminales/notifications/NotificationPayloadPresentation.kt`, `NotificationDetailScreen.kt`. * **App hace:** `productName`→Producto, `onHand`/`quantity`→Existencia, `threshold`/`minStock`→Mínimo, `folio`→Folio, `amount`→Monto (formateado como dinero desde unidades menores canónicas); las claves desconocidas se ocultan tras “Ver más”. Antes imprimía las claves crudas (D11-02). * **Aceptada por:** PO · **Fecha:** 2026-09-10 ### M12 Placeholder de producto — glifo neutro de mercancía (UX-13) [Section titled “M12 Placeholder de producto — glifo neutro de mercancía (UX-13)”](#m12-placeholder-de-producto--glifo-neutro-de-mercancía-ux-13) * **Código:** `app/src/main/java/com/axios/terminales/ui/screens/pos/PosImages.kt`, `TerminalesIcons.Product` (`BoxOpen`). * **App hace:** el producto sin imagen usa una caja abierta, no el regalo de Pines (D11-04). * **Aceptada por:** PO · **Fecha:** 2026-09-10 ## Registro de desviaciones aceptadas [Section titled “Registro de desviaciones aceptadas”](#registro-de-desviaciones-aceptadas) ### Provider Tile — badge con color de marca del proveedor (2026-07-26) [Section titled “Provider Tile — badge con color de marca del proveedor (2026-07-26)”](#provider-tile--badge-con-color-de-marca-del-proveedor-2026-07-26) * **Capa / nodo Figma:** molecule · `48:63` · **Código:** `.../molecules/ProviderTile.kt` * **Qué se conserva:** el badge usa el color de marca del proveedor (Telcel azul, AT\&T, Movistar) con la inicial, en vez del badge gris/soft genérico del frame. * **Por qué:** regla 2 — el color de marca ayuda al reconocimiento rápido del proveedor en la cuadrícula. * **Acción en Figma:** actualizar el nodo `48:63` a la variante con color de marca. * **Aceptada por:** usuario/PO · **Fecha:** 2026-07-26 # Parity — snapshot 2026-07-24 > Copia congelada de la galería Figma ↔ App al 2026-07-24 (solo lectura). > 📸 **Snapshot congelado del 2026-07-24.** Copia de solo lectura de la galería de paridad en esa fecha: las capturas de la app están congeladas; los frames de Figma se comparten con la galería viva. La versión que evoluciona está en [/design/parity/](/design/parity/). > **Qué es esto:** una galería lado a lado del **diseño en Figma** y la **pantalla real** de `terminales-web`, organizada por [historia de usuario](https://github.com/inspiraCode/terminales-solution/blob/main/docs/product/user-stories.md) y, al final, por capa del design system (átomos · moléculas · organismos). > > **Para qué sirve:** > > 1. Ver de un vistazo **en qué difiere la app del diseño**, para poder priorizar los ajustes. > 2. **Mostrar avance al cliente** — cada historia de usuario con sus pantallas reales. > 3. Tener un lugar donde **anotar** observaciones por pantalla y por componente. > > **Plan de remediación:** el orden de ataque para cerrar estos gaps con el mínimo de trabajo está en `remediation-plan` — se arregla por capas (tokens → theme → átomos → patrones), no pantalla por pantalla. > > **Estado:** las columnas *Notas* están **vacías a propósito** — la anotación de diferencias es el siguiente paso. Las celdas marcadas `⚠️ Falta …` señalan trabajo pendiente: o falta exportar el render de Figma, o falta capturar la pantalla. ## Fuentes [Section titled “Fuentes”](#fuentes) | Qué | De dónde | | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | Renders de Figma (`figma/`) | `terminales-solution/docs/design/web/{screens,atomic,molecules,organisms}/` — archivo Figma “Terminales - Claude” `ndebIq7cqexBCf9eKVU0Fm` | | Capturas de la app (`screenshots/`) | worktree `351-admin-shell`, `src/app/**/screenshot.png` (59 páginas), renombradas por ruta | | Historias de usuario | `terminales-solution/docs/product/user-stories.md` | | Auditoría previa por pantalla | `docs/progress/design-parity-matrix.md` (W01–W65, estado ✅/🟡/⚠) | ## Convenciones [Section titled “Convenciones”](#convenciones) * **Nombre de captura** = la ruta del App Router con `/` → `-`, sin route-groups ni corchetes. `src/app/admin/pos/stock-levels/adjust/screenshot.png` → `screenshots/admin-pos-stock-levels-adjust.png`. * **Nombre de render** = `figma-.png`, tal como se exporta desde `design/web/`. * **`⚠️ Falta diseño`** = no hay render de Figma exportado para ese frame. * **`⚠️ Falta captura`** = la pantalla no se capturó (es un modal/estado, o la ruta no existe). ## Cómo actualizar esta galería [Section titled “Cómo actualizar esta galería”](#cómo-actualizar-esta-galería) Dos comandos, en este orden. Ambos son idempotentes: se pueden re-correr cuando cambie la app. ```bash pnpm screenshots # recaptura docs/design-parity/screenshots/ (~5 min) pnpm docs:parity # reconstruye este README con lo que haya en disco ``` `pnpm screenshots` levanta **su propio** servidor de producción en el puerto **4000** (`next build && next start` sobre un `distDir` propio, `.next-screenshots`), con `NEXT_PUBLIC_USE_MOCKS=true` y **todos** los feature flags en ON. Eso significa que: * no pisa un `pnpm dev` que ya tengas abierto en el 3000; * ninguna ruta cae en `notFound()` por un flag apagado, así que la galería no tiene huecos; * los datos son los fixtures mock, iguales en cada corrida. Subconjuntos: `pnpm screenshots --grep admin-pos` (el filtro matchea el nombre del archivo). Para reusar un servidor que ya levantaste: `E2E_BASE_URL=http://localhost:4000 pnpm screenshots`. ### Las notas se escriben aquí y se conservan [Section titled “Las notas se escriben aquí y se conservan”](#las-notas-se-escriben-aquí-y-se-conservan) La sección **Notas** de cada pantalla es para escribir a mano — es el objetivo del documento. Hay dos formas de hacerlo, y las dos terminan en el mismo markdown: 1. **En este archivo**, editando la celda directamente. 2. **Desde el navegador**, con el sitio de documentación corriendo en local (`cd terminales-solution/docs-site && pnpm dev`): cada bloque de Notas muestra un botón **✏️ Editar nota** que abre un textarea y, al guardar, **escribe en este archivo**. Es la forma cómoda de anotar, porque tenés las capturas a tamaño grande al lado mientras escribís. Sólo existe en `pnpm dev` — el sitio publicado es estático y de sólo lectura. En ambos casos `pnpm docs:parity` **conserva** lo que escribas: antes de reescribir lee este archivo y recupera toda nota que no sea el placeholder, reinsertándola bajo el mismo encabezado. El ciclo es: 1. `pnpm screenshots` → cambian las imágenes 2. editás la celda **Notas** → tu observación, en markdown 3. `pnpm docs:parity` → se reconstruyen las tablas, tus notas sobreviven Una nota se identifica por su encabezado `####`. Si renombrás un encabezado, su nota queda huérfana: el script **la imprime en consola** al terminar en vez de perderla en silencio. ### Publicación en el sitio de documentación [Section titled “Publicación en el sitio de documentación”](#publicación-en-el-sitio-de-documentación) Este documento se publica en el sitio Starlight (`terminales-solution/docs-site`) como **/design/parity/**. El sitio lo lee *desde este repo* —no hay copia— vía `externalSources` en `docs-site/publish.config.mjs`: copia el markdown y sus imágenes a `public/design/parity/` y reescribe las rutas relativas a absolutas. O sea que el markdown de acá sigue siendo la única fuente de verdad, y las notas que escribas aparecen en el HTML en el próximo `pnpm build` del sitio. Para ver las capturas en grande en el sitio hay dos ayudas: * **⛶ Modo amplio** — un botón arriba del todo que oculta la navegación y el panel derecho; las imágenes pasan de \~345 px a \~610 px de ancho. La preferencia se recuerda. * **Click en cualquier captura** — abre el PNG original a tamaño completo (también funciona en GitHub). **Qué captura** — la lista vive en `e2e/screenshots/targets.ts`, una entrada por ruta más un array `dialogs` para los modales de cada pantalla (varios frames de Figma —W21, W30, W33, W39— son diálogos en la app, no páginas, y un barrido solo-de-rutas los dejaría siempre vacíos). Los componentes del design system salen de `/playground` vía `e2e/screenshots/playground.spec.ts`, que lee el registro de showcases, así que **agregar un showcase lo captura solo**. Agregar una pantalla = agregar una entrada en `targets.ts`; nada más hay que tocar. **Sobre el determinismo:** en dos corridas independientes, 157 de 167 capturas salieron byte-idénticas. Las demás cambian unos pocos bytes con las mismas dimensiones (el showcase de `Skeleton` anima con `animation="wave"` de MUI, cuyos keyframes CSS ignoran la emulación de `prefers-reduced-motion`; el resto es *antialiasing* de fuentes). O sea: `git diff` sobre esta carpeta es buena señal, pero antes de concluir que la UI cambió, mirá la imagen. ## Hallazgos sistémicos [Section titled “Hallazgos sistémicos”](#hallazgos-sistémicos) > Auditoría automatizada que comparó cada render de Figma contra su captura de la app, midiendo los colores con un muestreador de píxeles. **Son defectos de sistema: se arreglan una vez en el token o en el componente base y desaparecen de decenas de pantallas.** > **Estado (2026-07-23) — remediación fases 0–2 aplicada y re-auditada.** Se cableó DESIGN.md como fuente de verdad (\`scripts/generate-tokens.mjs\`), se reescribió el theme MUI con \~15 overrides, se corrigieron los átomos y se promovieron a props reales los hacks \`sx\` de los showcases (BalanceCard tone, IconButton, ProviderTile, ListRow, SummaryRow, Toast, geometría del Checkbox, default del Icon). La **re-auditoría contra las capturas nuevas** mueve el veredicto de **96 ❌ / 7 ⚠️ / 0 ✅** (pre-fix) a **37 ❌ / 58 ⚠️ / 6 ✅** — la mayoría de las pantallas pasó de “importante” a “menor”. Las notas por pantalla de abajo YA reflejan el estado post-remediación. ### Residuo tras la remediación (backlog para la próxima pasada) [Section titled “Residuo tras la remediación (backlog para la próxima pasada)”](#residuo-tras-la-remediación-backlog-para-la-próxima-pasada) > **Estado (2026-07-23, re-verificado contra el código).** La lista original de 11 residuos sistémicos está **casi toda resuelta**: la remediación de fases 0–2 más los batches atómicos cablearon los fixes en el theme/átomos y **cascadearon a las pantallas**. Cada ítem se re-verificó con grep del archivo culpable citado (ver playbook §7, las notas caducan entre batches). Estado real, con evidencia: **✅ Resueltos y verificados en código** 1. **Patrón de selección** — Chip activo = tinte primary.light #E6EEFA + texto primary.main (src/ui/atoms/Chip/index.tsx:58-61); SegmentedControl usa thumb blanco background.paper sobre track (SegmentedControl/index.tsx:83-89), patrón correcto del diseño. Cerrado. 2. **Badges de estado** — StatusBadge (soft por rol) adoptado en 37 puntos de src/app; los culpables citados ya migraron: SalesOrderDetail.tsx:86-87 y ProductList.tsx:95-96 usan StatusBadge (los Chip restantes en ProductList son contadores numéricos). Cerrado. 3. **Nav/lista seleccionado** — theme.ts:84 fija action.selected = primary.soft (#E6EEFA) y SideNav lo consume en su estado Mui-selected. Cerrado. 4. **Borde del Button outlined** — el slot outlined existe en MuiButton (theme.ts:225), 1px sólido primary.main. Cerrado. 5. **Paradigma de etiqueta** — FormField renderiza label estático arriba (Typography como label, FormField/index.tsx:36-49) + placeholder gris, no floating label. Cerrado. 6. **CTA tonal/soft** — HeldOrderCard usa Button variant tonal (HeldOrderCard/index.tsx:118-119), no contained sólido. Cerrado. 7. **Fecha en español** — src/core/utils.ts:20 llama dayjs.locale(es) y el formateo per-fecha usa .locale(es) (utils.ts:307). Cerrado. 8. **Iconos de adorno** — theme.ts:74-76 apunta action.active al gris frío #9CA3AF, no al #757575 default. Cerrado. 9. **White-label del avatar** — WhiteLabelThemeProvider pasa light = lighten(main, 0.9) explícito a augmentColor, que para #0052CC resuelve a #E6EEFA (WhiteLabelThemeProvider.tsx:58-62). Cerrado. **Abierto (residuo real)** 3. **Migración de tablas admin** — \~90% hecho: 31 pantallas ya usan el DataTable compartido (toolbar FilterBar + Pagination numerada + selección por fila) y 0 usan ya TablePagination de MUI. Quedan 4 archivos con tabla MUI cruda: PosStock/StockLevelList.tsx, PosStock/StockMovementList.tsx, Reports/ZHistoryTable.tsx, Reports/ReportView\.tsx — candidatos a migrar (algunas vistas de reporte pueden querer layout propio; evaluar caso por caso). 4. **Iconografía divergente** (cosmético) — glifos outlined de MUI y chevrons donde el diseño usa glifos rellenos/triángulos, y glifos desnudos donde el diseño los envuelve en contenedor cuadrado tintado #F0F4F8/#E6EEFA — Banner, Toast, KPI Stat Card, Empty State, FAB, TenderPicker. Baja prioridad. > **Implicación:** varias notas por pantalla de abajo se escribieron antes de que estos fixes sistémicos cascadearan y pueden estar sobre-estimadas. Al re-auditar una pantalla, verificar primero contra este estado antes de re-reportar un residuo ya cerrado. ### Fixes confirmados en las capturas [Section titled “Fixes confirmados en las capturas”](#fixes-confirmados-en-las-capturas) * Azul canónico #0052CC medido exacto en fills, links, focus rings e iconos activos (con hover/pressed #003D99, disabled #E5E7EB+#9CA3AF) en casi todos los pares. * Tinte de marca #E6EEFA (palette.primary.light) ahora existe y se usa en Avatar, IconButton, Pagination, Balance Card CTA, Customer Row, chip de BulkActionBar, fila activa de Command Palette y filas no-leídas de Notificaciones. * Avatar corregido: relleno tinte #E6EEFA + iniciales #0052CC (antes solid primary invertido), diámetros exactos 32/40/56px. * Fondos soft por rol confirmados en Alert/Banner/StatusBadge/Toast: info #EFF6FF, success #ECFDF5, warning #FFFBEB, danger #FEF2F2, con textos #3B82F6/#10B981/#F59E0B/#EF4444. * Campos de formulario: relleno blanco, borde hairline #E5E7EB, focus #0052CC 2px, error #EF4444, disabled bg #E5E7EB, alto \~40px — medidos en FormField, Textarea, Select, Denomination Row y campos POS. * Controles de selección: unchecked #D1D5DB, checked/indeterminate #0052CC en Checkbox y Radio; PIN Dot lleno #0052CC; Switch track ON #0052CC / OFF #D1D5DB con thumb blanco y geometría 44×24/thumb18/inset3. * Superficies de tabla y tarjeta: cabecera #F7F9FC, divisores/hairline #E5E7EB, cards planas sin sombra. * Colores de chart: serie 1 #0052CC, serie 2 #3B82F6 (info), serie 3 #10B981, serie 4 #F59E0B (useChartColors.ts). * Superficie dark de Toast y Tooltip medida #1F2933 opaca, igual al asset de Figma. * Tokens de texto: primario #1F2933, secundario #6B7280, disabled #9CA3AF — consistentes en títulos, labels y valores. ## Capturas que no corresponden a su diseño [Section titled “Capturas que no corresponden a su diseño”](#capturas-que-no-corresponden-a-su-diseño) > La auditoría verifica primero que cada par muestre la misma pantalla. En estos 15 casos **no** coinciden, así que su celda de Notas describe el desajuste en vez de listar diferencias de estilo. El patrón dominante: el frame de Figma diseña una pantalla completa y la app la resuelve como diálogo sobre el listado, o la captura tomó otro paso del flujo — son decisiones de producto (diálogo-vs-página) o gaps de implementación, no desviaciones de estilo. * **W03b · Nueva contraseña** — La captura de la app muestra el estado de error “enlace inválido” de /reset-password (sin query params userId/secret), no el formulario de nueva contraseña que diseña el Figma. * **W04 · Acceso rápido (PIN)** — Figma diseña el paso 2 del flujo (teclado PIN con usuario ya seleccionado); la app fue capturada en el paso 1 (selección de usuario en “Terminal bloqueado”). Recapturar tras hacer clic en un usuario. * **W31 · Cancelar / Reembolsar** — Figma muestra el modal de autorización con PIN-pad sobre el detalle de Venta #1042; la app capturó /terminal/voids en estado vacío (“No hay tickets que se puedan anular en este turno”) — el diálogo de PIN nunca llegó a abrirse. * **Categoría — alta** — El frame de Figma (figma-w23-categorias.png) es el LISTADO de Categorías (tabla + filtros + paginación, sin ningún formulario), mientras que la captura de la app muestra el drawer “Nueva categoría” (formulario de alta) abierto sobre el listado. No existe en el par un frame de Figma del formulario de * **Impuesto — alta** — La captura de Figma emparejada (figma-w24-impuestos.png) es la pantalla de LISTA de Impuestos (tabla de tasas + botón “Nueva tasa”); no contiene ningún formulario de alta. La app sí muestra el drawer “Nuevo Impuesto”. Falta exportar/emparejar el frame de Figma del formulario de alta (si existe), o e * **W11 · POS Resultados de búsqueda** — Misma ruta (/terminal/home) pero estado equivocado: el FIGMA diseña los resultados de búsqueda (“4 resultados para ‘coca’”, grid filtrado a 4 Coca-Colas, carrito con 3 ítems); la APP muestra el estado pre-búsqueda de W10 — “coca” está tecleado pero el grid exhibe el catálogo completo (6 productos, s * **W33 · Detalle de turno / Corte Z** — El panel FIGMA diseña una PÁGINA completa de “Corte Z” de un turno CERRADO; el panel APP muestra un DIÁLOGO de detalle de un turno ABIERTO (sin corte todavía). Distinto paso del flujo, distinta composición (página vs diálogo) y distinto estado del turno. * **W35 · Cerrar turno** — El Figma diseña una página de resumen “Cerrar turno · Corte Z” dentro del shell admin (sidebar + topbar), mientras que la app captura una pantalla de terminal (shell POS, sin sidebar) de conteo de efectivo por denominación. Son pasos distintos del flujo de cierre. * **W36b · Movimiento de caja (terminal)** — El manifest reutiliza el Figma de W36 (página admin de administración) para W36b, pero la captura de la app es la pantalla de terminal (formulario de entrada con teclado numérico). Son dos pantallas distintas: lista/tabla admin vs. formulario de captura de un movimiento en terminal. * **W38 · Empleado crear / editar** — Figma = pagina completa “Empleado” (header + secciones Datos personales/Acceso + sticky save bar). App = drawer lateral derecho sobre la lista de Empleados, con set de campos distinto. * **W56 · Reporte de ventas** — El panel APP no muestra la pantalla del diseño. FIGMA dibuja el “Reporte de ventas · Desglose del periodo” (tabs Por producto/categoría/empleado/método + tabla de líneas Producto·SKU·Categoría·Ingresos·Unidades·Tendencia con checkboxes, botón Exportar y paginación 1–5 de 248). La captura APP muestra * **W58 · Cortes de caja (Z)** — El Figma diseña el HISTÓRICO/lista de cortes Z (tabla con filas Z-088…Z-084); la app captura el DETALLE de un solo corte Z (tarjeta con desglose Ventas/Formas de Pago/Conciliación). Son dos vistas distintas del mismo flujo. * **W42 · Tiempo Aire (TAE)** — Figma diseña la página compuesta de recarga (proveedor como chips + grilla de montos + captura de teléfono/total); la app muestra solo el paso “Selecciona el proveedor” con tarjetas grandes y un “Resumen de compra” vacío, sin montos ni campo de teléfono. * **W46b · Registrar depósito** — El panel FIGMA muestra la lista de Depósitos (tabla de folios DEP-xxxx con Tienda/Fecha/Monto/Método/Estado, botón “Nuevo depósito”, filtros y paginación). El panel APP muestra el formulario “Registrar Depósito” (campos Canal de depósito, Banco, Fecha, Número de Referencia, Monto Depositado, Cuenta * **W07 · Dashboard** — El sidebar coincide (misma app, ruta “Inicio” activa), pero el panel de contenido principal es un diseño completamente distinto. Figma diseña un dashboard analítico de retail: saludo “Hola, Victoria”, 4 KPIs (Ventas hoy $8,450, Transacciones 128, Ticket promedio $66.01, Productos vendidos 342), gráf ## Índice [Section titled “Índice”](#índice) * [Hallazgos sistémicos](#hallazgos-sist%C3%A9micos) * [Capturas que no corresponden a su diseño](#capturas-que-no-corresponden-a-su-dise%C3%B1o) * [Pantallas por historia de usuario](#pantallas-por-historia-de-usuario) * [ACC — Acceso y onboarding](#acc--acceso-y-onboarding) * [POS — Venta en punto de venta](#pos--venta-en-punto-de-venta) * [VENTAS / VOID — Historial de ventas, cancelación y reembolso](#ventas--void--historial-de-ventas-cancelaci%C3%B3n-y-reembolso) * [CAT — Catálogo](#cat--cat%C3%A1logo) * [INV — Inventario](#inv--inventario) * [SHIFT — Turnos y caja](#shift--turnos-y-caja) * [STAFF — Personal y control de acceso](#staff--personal-y-control-de-acceso) * [REP — Reportes](#rep--reportes) * [VAS — Servicios de valor agregado](#vas--servicios-de-valor-agregado) * [BAL — Wallet del comercio](#bal--wallet-del-comercio) * [CLI — Clientes](#cli--clientes) * [TERM / WL — Comercios, tiendas, terminales y white-label](#term--wl--comercios-tiendas-terminales-y-white-label) * [ACCT — Cuenta y ajustes](#acct--cuenta-y-ajustes) * [NOTIF — Notificaciones](#notif--notificaciones) * [SEARCH — Búsqueda global](#search--b%C3%BAsqueda-global) * [PLAT — Administración de plataforma](#plat--administraci%C3%B3n-de-plataforma) * [Sin historia de usuario — rutas técnicas y de desarrollo](#sin-historia-de-usuario--rutas-t%C3%A9cnicas-y-de-desarrollo) * [Design system](#design-system) * [Átomos](#%C3%A1tomos) * [Moléculas](#mol%C3%A9culas) * [Organismos](#organismos) *** ## Pantallas por historia de usuario [Section titled “Pantallas por historia de usuario”](#pantallas-por-historia-de-usuario) ### ACC — Acceso y onboarding [Section titled “ACC — Acceso y onboarding”](#acc--acceso-y-onboarding) Flujo W-01. Historias `US-ACC-1` … `US-ACC-6`. #### W01 · Iniciar sesión [Section titled “W01 · Iniciar sesión”](#w01--iniciar-sesión) **Ruta:** `/login` · **Figma:** [`107:358`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=107-358) · **Historia:** US-ACC-1 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W01](/design/parity/figma/screens/figma-w01-login.png)](/design/parity/figma/screens/figma-w01-login.png "figma W01 — abrir a tamaño completo") | [![app W01](/design/parity/history/2026-07-24/screenshots/login.png)](/design/parity/history/2026-07-24/screenshots/login.png "app W01 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes** — la capa de color sistémica llegó, pero overrides locales de `sx` anulan los fixes de tamaño/sombra y falta contenido del diseño.- **Color:** ✅ Resuelto — azul canónico **#0052CC medido** en panel de marca, botón Entrar, focus ring y links (ambos paneles); fondo #F5F7FA y heading #1F2933 coinciden; hairlines de campos y botón secundario = **#E5E7EB** exacto. - **Sombras:** ❌ El botón Entrar proyecta sombra visible (gradiente #DFDFDF→#F2F2F2 medido bajo el botón); en Figma no hay ninguna. Culpable: `src/ui/Buttons/PrimaryButton.tsx` hardcodea `boxShadow: '0px 10px 15px…'` en `sx`, pisando el fix del theme. La tarjeta también lleva sombra explícita (`src/ui/templates/AuthSplit/index.tsx`, `boxShadow` en el Paper) mientras el Figma muestra borde nítido sin sombra. - **Espaciado:** ❌ Campos de \~60px de alto (borde 406→468 medido) vs \~38px en Figma — `src/ui/Inputs/TextInput.tsx` hardcodea `padding: '18px 22px'`, anulando el fix de campos \~40px. Botón Entrar \~67px (526→593) vs 52px del Figma — `PrimaryButton.tsx` hardcodea `padding: sm '20px'`. - **Tipografía:** Etiqueta del botón a 18px (`fontSize: {sm: 18}` en `PrimaryButton.tsx`) vs 14px/600 del sistema. - **Contenido:** ❌ Falta el botón outlined “**Acceso rápido con PIN**” (existe en Figma con borde azul #0052CC; sin rastro en `LoginForm.tsx` ni `login.i18n.ts`). El divisor “— o —” del Figma se reemplazó por el texto “O recibe un enlace de acceso por correo”. “Enviar enlace mágico” es link de texto azul en Figma pero botón outlined neutro en la app (`SecondaryButton` en `LoginForm.tsx:256`). - **Layout:** Split 50/50 (`flex: '1 1 50%'` en `AuthSplit/index.tsx`) vs \~43/57 medido en Figma (azul termina en x≈616/1440 vs x≈720). “¿Olvidaste tu contraseña?” alineado a la derecha en app, a la izquierda en Figma. - **Estados:** La captura muestra el email con autofocus (ring azul 2px #0052CC) y asteriscos rojos de requerido (\~#D84F4F medido) que el Figma no dibuja; el campo contraseña muestra toggle de visibilidad oscuro sin equivalente en Figma — decidir si se ocultan asteriscos/adorno o se actualiza el diseño. - **Verificar:** si “Acceso rápido con PIN” quedó fuera por decisión de producto, documentarlo; si no, es contenido faltante. | | #### W02 · Verificación 2FA [Section titled “W02 · Verificación 2FA”](#w02--verificación-2fa) **Ruta:** `/login (MfaChallengeForm)` · **Figma:** [`110:376`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=110-376) · **Historia:** US-ACC-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------- | | [![figma W02](/design/parity/figma/screens/figma-w02-2fa.png)](/design/parity/figma/screens/figma-w02-2fa.png "figma W02 — abrir a tamaño completo") | ⚠️ **Falta captura** — es un estado dentro de `/login`, no una ruta propia; hay que capturarlo con Playwright forzando el reto MFA | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W03 · Recuperar contraseña [Section titled “W03 · Recuperar contraseña”](#w03--recuperar-contraseña) **Ruta:** `/forgot-password` · **Figma:** [`110:407`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=110-407) · **Historia:** US-ACC-3 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W03](/design/parity/figma/screens/figma-w03-recuperar.png)](/design/parity/figma/screens/figma-w03-recuperar.png "figma W03 — abrir a tamaño completo") | [![app W03](/design/parity/history/2026-07-24/screenshots/forgot-password.png)](/design/parity/history/2026-07-24/screenshots/forgot-password.png "app W03 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** (recaptura post-remediación; misma pantalla, split W03 con card centrada)- **Color:** Fixes sistémicos confirmados por medición: panel izquierdo, botón, badge y focus ring del campo todos en **#0052CC** canónico; título #1F2933; subtítulo #6B7280. Residuo: el link “Volver a iniciar sesión” renderiza **#232D37** (texto plano) en vez del azul de marca semibold del diseño — es un `next/link` desnudo en `src/app/forgot-password/ForgotPasswordForm.tsx:182-187`. - **Iconos:** El badge del “?” es un cuadrado **sólido #0052CC** con glifo blanco; Figma lo diseña como cuadrado en tinte **#E6EEFA** (medido) con círculo azul — `ForgotPasswordForm.tsx:89-104` usa `bgcolor: 'primary.main'` en lugar de `primary.light`. - **Contenido:** Falta el hint informativo persistente de Figma (“Si el correo existe, recibirás un enlace en unos minutos.”, tinte #EFF6FF verificado en el diseño); la app solo muestra un Banner **después** de enviar y con otro copy (`ForgotPasswordForm.tsx:132-136`). Copys divergentes en `recovery.i18n.ts`: subtítulo recortado (“Te enviaremos…” vs “Ingresa tu correo y te enviaremos…”), botón “Enviar enlace de recuperación” vs “Enviar enlace”, placeholder “” vs “”. - **Contenido:** El panel de marca reutiliza `LoginBrand` con el tagline genérico del login; Figma W03 pide el tagline propio “Recupera el acceso a tu cuenta en unos pasos.” (`src/app/forgot-password/page.tsx:22`). - **Layout:** CTA medido a **\~40px** (ramp medium) donde Figma usa el botón grande **\~52px**; falta `size` en el Button del form. - **Estados:** La captura tiene el campo con `autoFocus` (ring azul 2px #0052CC correcto); el borde hairline #E5E7EB en reposo queda como “verificar”. - **Bordes/Sombras:** Card radio y sombra suave equivalentes en ambos paneles; botón sin sombra ✓. - Sistémico conocido (una línea): label flotante MUI con asterisco vs label estático sobre el campo del diseño — patrón de formulario pendiente de decisión de producto. | | #### W03b · Nueva contraseña [Section titled “W03b · Nueva contraseña”](#w03b--nueva-contraseña) **Ruta:** `/reset-password` · **Figma:** [`110:407`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=110-407) · **Historia:** US-ACC-3 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W03b](/design/parity/figma/screens/figma-w03b-nueva-contrasena.png)](/design/parity/figma/screens/figma-w03b-nueva-contrasena.png "figma W03b — abrir a tamaño completo") | [![app W03b](/design/parity/history/2026-07-24/screenshots/reset-password.png)](/design/parity/history/2026-07-24/screenshots/reset-password.png "app W03b — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Panel FIGMA:** el paso de formulario del flujo de recuperación — icono de candado en tinte, título “Crea tu nueva contraseña”, campos “Nueva contraseña” y “Confirmar contraseña”, checklist de requisitos y botón “Guardar contraseña”. - **Panel APP:** el estado de error de la misma ruta — card “Restablecer contraseña” con alerta “El enlace de recuperación es inválido o expiró.” y link “Volver a iniciar sesión”, sin ningún campo. - **Causa:** `src/app/reset-password/ResetPasswordForm.tsx` exige `?userId=…&secret=…` en la URL (`hasParams`, líneas 54–56/121); la captura se tomó sin esos params y cayó en la rama de enlace inválido. - **Acción de captura:** recapturar con `/reset-password?userId=dummy&secret=dummy` (o mockeando `useSearchParams`) para renderizar el formulario y poder auditar la paridad. - **Dato aprovechable:** el azul de marca ya mide **#0052CC** en la captura (dominante 47.7%, igual al Figma) y el fondo #F5F7FA coincide — el fix sistémico sí llegó a esta ruta, pero el residuo del formulario queda **pendiente de verificar**. | | #### W04 · Acceso rápido (PIN) [Section titled “W04 · Acceso rápido (PIN)”](#w04--acceso-rápido-pin) **Ruta:** `/terminal/lock` · **Figma:** [`110:481`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=110-481) · **Historia:** US-ACC-4 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W04](/design/parity/figma/screens/figma-w04-pin.png)](/design/parity/figma/screens/figma-w04-pin.png "figma W04 — abrir a tamaño completo") | [![app W04](/design/parity/history/2026-07-24/screenshots/terminal-lock.png)](/design/parity/history/2026-07-24/screenshots/terminal-lock.png "app W04 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **FIGMA:** paso 2 del acceso rápido — layout dividido con panel de marca azul “Terminales” a la izquierda y tarjeta de PIN a la derecha: avatar “JP”, “Juan Pérez / Cajero · Tienda Centro”, “Ingresa tu PIN” con 4 dots de progreso, teclado numérico 1–9/0 + backspace y link “Cambiar usuario”. - **APP:** paso 1 del mismo flujo — pantalla “Terminal bloqueado / Selecciona tu usuario” con tres tiles de usuario (CD Cajero Demo, SD Supervisor Demo, GD Gerente Demo). El teclado PIN nunca aparece en la captura (`screenshots/terminal-lock.png`). - **Acción:** recapturar la app DESPUÉS de hacer clic en un usuario, para llegar al paso de ingreso de PIN, y re-auditar contra este Figma. - **Dato medido (no es auditoría de estilo):** el azul canónico #0052CC se midió en ambos paneles (avatares de la app y panel/avatar del Figma) — el fix sistémico de azul sí llegó a esta ruta. | | #### W05 · Seleccionar comercio / tienda [Section titled “W05 · Seleccionar comercio / tienda”](#w05--seleccionar-comercio--tienda) **Ruta:** `/select-store` · **Figma:** [`112:406`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=112-406) · **Historia:** US-ACC-5 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W05](/design/parity/figma/screens/figma-w05-seleccionar-comercio.png)](/design/parity/figma/screens/figma-w05-seleccionar-comercio.png "figma W05 — abrir a tamaño completo") | ⚠️ **Falta captura** — **la app salta esta pantalla**: `SelectStoreView` auto-selecciona cuando el usuario tiene una sola membresía (“AC2 — a single membership needs no click”), y el roster mock tiene exactamente una. Para capturar el selector hace falta un fixture con ≥2 tiendas. | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W06 · Onboarding — crear comercio [Section titled “W06 · Onboarding — crear comercio”](#w06--onboarding--crear-comercio) **Ruta:** `— (sin ruta)` · **Figma:** [`112:443`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=112-443) · **Historia:** US-ACC-6 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W06](/design/parity/figma/screens/figma-w06-onboarding.png)](/design/parity/figma/screens/figma-w06-onboarding.png "figma W06 — abrir a tamaño completo") | [![app W06](/design/parity/history/2026-07-24/screenshots/onboarding.png)](/design/parity/history/2026-07-24/screenshots/onboarding.png "app W06 — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Azul canónico verificado: botón Continuar y paso activo del stepper = #0052CC en ambos paneles; título #1F2933, subtítulo #6B7280 — fixes sistémicos presentes en esta captura. - **Bordes:** Campos con hairline #E5E7EB y relleno blanco en app y Figma (medido en el primer campo). - **Espaciado:** Alturas idénticas medidas: campos \~40px (app 341→380 vs Figma 339→378), botón grande \~52px (app 655→706 vs Figma 657→708). Composición, radios y densidad de la tarjeta equivalentes. - **Layout:** Misma estructura: stepper 1-2-3 + “Paso 1 de 3”, título, subtítulo, 4 campos, CTA full-width. - **Contenido:** Divergencia **intencional y documentada** (comentario en `src/app/onboarding/onboarding.i18n.ts`): el alcance US-ACC-6 es merchant-create only, así que “Nombre de la tienda” del Figma se reemplaza por “Usuario propietario (ID)” + helper, el subtítulo cambia y “Dirección” pasa a “(opcional)”. Es el Figma el que quedó desactualizado respecto al contrato — actualizar el frame, no el código. | | #### Cierre de sesión [Section titled “Cierre de sesión”](#cierre-de-sesión) **Ruta:** `/sign-out` · **Historia:** — | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — pantalla técnica sin frame en Figma | [![app Cierre de sesión](/design/parity/history/2026-07-24/screenshots/sign-out.png)](/design/parity/history/2026-07-24/screenshots/sign-out.png "app Cierre de sesión — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Callback de login (OAuth / magic link) [Section titled “Callback de login (OAuth / magic link)”](#callback-de-login-oauth--magic-link) **Ruta:** `/login/callback` · **Historia:** — (sin frame de diseño) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — pantalla técnica sin frame en Figma | [![app Callback de login (OAuth / magic link)](/design/parity/history/2026-07-24/screenshots/login-callback.png)](/design/parity/history/2026-07-24/screenshots/login-callback.png "app Callback de login (OAuth / magic link) — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | ### POS — Venta en punto de venta [Section titled “POS — Venta en punto de venta”](#pos--venta-en-punto-de-venta) Flujo W-03. Historias `US-POS-1` … `US-POS-9`. #### W10 · POS Venta (two-pane) [Section titled “W10 · POS Venta (two-pane)”](#w10--pos-venta-two-pane) **Ruta:** `/terminal/home` · **Figma:** [`114:715`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=114-715) · **Historia:** US-POS-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W10](/design/parity/figma/screens/figma-w10-pos-venta.png)](/design/parity/figma/screens/figma-w10-pos-venta.png "figma W10 — abrir a tamaño completo") | [![app W10](/design/parity/history/2026-07-24/screenshots/terminal-home.png)](/design/parity/history/2026-07-24/screenshots/terminal-home.png "app W10 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Azul canónico verificado por medición: chip activa **#0052CC**, precios **#0052CC**, texto “Asignar cliente” **#0052CC**; Cobrar deshabilitado **#E5E7EB** bg + **#9CA3AF** texto — los fixes sistémicos sí llegaron a esta pantalla. - **Color:** La chip de categoría seleccionada difiere del diseño: app = sólida #0052CC con texto blanco; Figma = tinte **#E6EEFA** con texto #0052CC (medido). El tinte ya está en `palette.primary.light`; culpable: `src/ui/atoms/Chip/index.tsx` (líneas 50–56, `selected ? primary.main : …`). - **Bordes:** Hairline **#E5E7EB** medido en campo de búsqueda y en tarjetas de producto; tarjetas planas blancas ✓. - **Layout:** Campo de búsqueda: Figma lo diseña relleno **#F0F4F8** sin borde (\~40px de alto); la app usa Paper outlined blanco (\~45px) — `src/app/terminal/home/picker/SearchInput.tsx`. - **Contenido:** Faltan los tabs de catálogo del diseño (Productos / Servicios / TAE / Pines) — el picker solo muestra productos. - **Contenido:** Las tarjetas de producto no muestran el badge de existencias (“24 pzs” verde / “Agotado” rojo, texto \~#1EBE89 medido en Figma) — `picker/ProductGrid.tsx` solo renderiza nombre + precio. - **Estados:** Captura con carrito vacío vs venta activa en Figma: mismo screen y layout two-pane, pero steppers de cantidad, “Vaciar”, IVA incluido y Cobrar activo quedan sin comparar; re-verificar con carrito poblado. - **Estados:** Switch “Escáner”: track off **#D1D5DB**, alto 24px, thumb blanco ✓ (el color de track no estaba especificado en el fix; verificar contra token). - **Layout:** Composición del header (AppBar de marca con Orden #/chip de turno/avatar vs saludo + acciones de turno) y extras del carrito (Asignar cliente, estado de terminal, Guardar en espera): divergencias de producto conocidas/pendientes, no de estilo. | | #### W11 · POS Resultados de búsqueda [Section titled “W11 · POS Resultados de búsqueda”](#w11--pos-resultados-de-búsqueda) **Ruta:** `/terminal/home (estado de búsqueda)` · **Figma:** [`131:8968`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=131-8968) · **Historia:** US-POS-1 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W11](/design/parity/figma/screens/figma-w11-resultados.png)](/design/parity/figma/screens/figma-w11-resultados.png "figma W11 — abrir a tamaño completo") | [![app W11](/design/parity/history/2026-07-24/screenshots/terminal-home--busqueda.png)](/design/parity/history/2026-07-24/screenshots/terminal-home--busqueda.png "app W11 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.** (misma ruta, estado equivocado — se necesita recaptura)- **FIGMA:** estado de resultados de W11 — campo con “coca”, label “4 resultados para ‘coca’”, grid filtrado (4 Coca-Colas con badge de stock “24 pzs”), carrito con 3 ítems y Cobrar $372.00 en #0052CC. - **APP:** estado por defecto de W10 — “coca” tecleado pero grid con el catálogo completo (6 productos, solo 1 coincide), sin label de conteo, carrito vacío y Cobrar deshabilitado. - **Causa (captura):** `e2e/screenshots/targets.ts` (dialog `terminal-home--busqueda`) llena el input pero `ready: testId('product-picker')` ya se cumple antes del debounce de 300ms de `SearchInput.tsx` + refetch; el ready debe esperar a que el grid quede filtrado (o al response de la query) antes de disparar la captura. - **Pendiente verificado en código (aparecerá igual tras recapturar):** no existe ningún label “N resultados para …” en `src/app/terminal/home/picker/`; los chips de la app son categorías (CategoryRail) y no el filtro por tipo del diseño (Todos/Productos/Servicios/Tiempo Aire/Tarjetas); `ProductGrid.tsx` no muestra badge de stock (“24 pzs” verde soft #1EBE89/#ECFCF5 en Figma). Además el Chip selected de la app es filled #0052CC mientras el diseño usa tinte #E6EEFA + texto azul (`src/ui/atoms/Chip/index.tsx:45-53`) — a confirmar en la re-auditoría. - **Fixes sistémicos medidos en esta captura (OK):** azul canónico #0052CC en chip seleccionado y precios; disabled #E5E7EB; tarjetas blancas con hairline #E5E7EB. - **Acción:** corregir el `ready`/espera en targets.ts, recapturar y re-auditar W11. | | #### W12 · POS Categoría especial (VAS) [Section titled “W12 · POS Categoría especial (VAS)”](#w12--pos-categoría-especial-vas) **Ruta:** `— (sin ruta)` · **Figma:** [`131:9039`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=131-9039) · **Historia:** US-VAS-4 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el nodo existe pero no se exportó a `design/web/screens/` | ⚠️ **Falta captura** — no hay ruta VAS dentro del POS del terminal | | Notas | | | **⛔ Comparación no posible — faltan las dos imágenes.** No hay render de Figma exportado ni captura de la app, así que no hay nada que comparar. El motivo de cada gap está en las celdas de arriba. Primero hay que resolverlo (exportar el frame y/o construir la pantalla); recién ahí tiene sentido auditar paridad. | | #### W13 · POS Descuento [Section titled “W13 · POS Descuento”](#w13--pos-descuento) **Ruta:** `— (hoja modal)` · **Figma:** [`115:805`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=115-805) · **Historia:** US-POS-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------- | | [![figma W13](/design/parity/figma/screens/figma-w13-descuento.png)](/design/parity/figma/screens/figma-w13-descuento.png "figma W13 — abrir a tamaño completo") | ⚠️ **Falta captura** — la hoja de descuento no está cableada en el POS | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W14 · POS Cobrar (método) [Section titled “W14 · POS Cobrar (método)”](#w14--pos-cobrar-método) **Ruta:** `— (diálogo)` · **Figma:** [`115:1004`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=115-1004) · **Historia:** US-POS-3 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------- | | [![figma W14](/design/parity/figma/screens/figma-w14-cobrar.png)](/design/parity/figma/screens/figma-w14-cobrar.png "figma W14 — abrir a tamaño completo") | ⚠️ **Falta captura** — diálogo (`TenderMethodDialog`), requiere captura con carrito activo | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W15 · POS Efectivo [Section titled “W15 · POS Efectivo”](#w15--pos-efectivo) **Ruta:** `— (diálogo)` · **Figma:** [`115:1126`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=115-1126) · **Historia:** US-POS-3 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------- | | [![figma W15](/design/parity/figma/screens/figma-w15-efectivo.png)](/design/parity/figma/screens/figma-w15-efectivo.png "figma W15 — abrir a tamaño completo") | ⚠️ **Falta captura** — diálogo (`CashTenderDialog`) | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W16 · POS Tarjeta — procesando [Section titled “W16 · POS Tarjeta — procesando”](#w16--pos-tarjeta--procesando) **Ruta:** `— (diálogo)` · **Figma:** [`117:1335`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=117-1335) · **Historia:** US-POS-4 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------- | | [![figma W16](/design/parity/figma/screens/figma-w16-tarjeta.png)](/design/parity/figma/screens/figma-w16-tarjeta.png "figma W16 — abrir a tamaño completo") | ⚠️ **Falta captura** — diálogo (`RemoteCardTenderDialog`) | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W17 · POS Pago mixto (split) [Section titled “W17 · POS Pago mixto (split)”](#w17--pos-pago-mixto-split) **Ruta:** `— (diálogo)` · **Figma:** [`115:1234`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=115-1234) · **Historia:** US-POS-5 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------- | | [![figma W17](/design/parity/figma/screens/figma-w17-mixto.png)](/design/parity/figma/screens/figma-w17-mixto.png "figma W17 — abrir a tamaño completo") | ⚠️ **Falta captura** — superficie no cableada | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W18 · POS Recibo [Section titled “W18 · POS Recibo”](#w18--pos-recibo) **Ruta:** `— (documento de impresión)` · **Figma:** [`133:10777`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=133-10777) · **Historia:** US-POS-7 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------ | | [![figma W18](/design/parity/figma/screens/figma-w18-recibo.png)](/design/parity/figma/screens/figma-w18-recibo.png "figma W18 — abrir a tamaño completo") | ⚠️ **Falta captura** — el recibo es solo impresión (`ReceiptDocument.tsx`), no hay pantalla | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W19 · POS Venta exitosa [Section titled “W19 · POS Venta exitosa”](#w19--pos-venta-exitosa) **Ruta:** `— (modal)` · **Figma:** [`117:1417`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=117-1417) · **Historia:** US-POS-6 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- | | [![figma W19](/design/parity/figma/screens/figma-w19-exito.png)](/design/parity/figma/screens/figma-w19-exito.png "figma W19 — abrir a tamaño completo") | ⚠️ **Falta captura** — no existe pantalla/modal de confirmación | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W64 · Ventas en espera [Section titled “W64 · Ventas en espera”](#w64--ventas-en-espera) **Ruta:** `/admin/pos/held-orders` · **Figma:** [`574:14716`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=574-14716) · **Historia:** US-POS-8 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W64](/design/parity/figma/screens/figma-w64-en-espera.png)](/design/parity/figma/screens/figma-w64-en-espera.png "figma W64 — abrir a tamaño completo") | [![app W64](/design/parity/history/2026-07-24/screenshots/admin-pos-held-orders.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-held-orders.png "app W64 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes** — misma pantalla; los fixes sistémicos de color SÍ llegaron (medidos), pero hay una regresión de tema, dos bugs de contenido visibles y la tarjeta difiere en composición.- **Color:** ✅ verificado: CTA y segmento activo `#0052CC` exacto, texto principal `#1F2933`, borde de tarjeta hairline `#E5E7EB`, fondo `#F5F7FA`. ❌ Avatar de tarjeta y header: bg medido `#3374D6` = `lighten(#0052CC, .2)` — `mergeMerchantTheme` pasa por `augmentColor` y recalcula `primary.light`, pisando el tinte canónico `#E6EEFA` del fix de Avatar en superficies white-label (`src/ui/theme/WhiteLabelThemeProvider.tsx`; derivar ahí el soft como en `theme.ts` en vez del lighten de MUI). - **Contenido:** ❌ importes ×100: el mock guarda centavos (`total: 37200`) y `HeldOrdersList.tsx:167` formatea el valor crudo → “$37,200.00” vs “$372.00” del diseño para el mismo carrito (`heldOrder.mocks.ts`, aclarar unidad en `types.ts:22`). - **Contenido:** ❌ “17 days ago” en inglés: `src/core/utils.ts` importa `dayjs/locale/es` pero nunca activa `dayjs.locale('es')` (Figma: “Hace 4 min”). Los `heldAt` del mock (2026-07-06) además envejecen la demo — refrescarlos a offsets relativos. - **Estados:** app captura “Todos” activo; Figma muestra “Mis carritos”. El default `'all'` es deliberado en código (`HeldOrdersList.tsx:55`) — confirmar con producto cuál es el default correcto. - **Layout:** patrón del segmented invertido: Figma = pill blanco sobre track gris `#F0F4F8` con texto oscuro; app = pill azul sólido sobre track blanco (`src/ui/atoms/SegmentedControl/index.tsx`, atom compartido — afecta otras pantallas). - **Layout:** composición de `HeldOrderCard` distinta al diseño: Figma pone id azul “#H-2271” + precio arriba-derecha, “Hace X min” como caption bajo el nombre y holder como avatar chico inline + texto plano; app pone “H-2271” oscuro sin “#”, avatar sólido arriba-derecha, tiempo/holder como chips outlined y precio abajo (`src/ui/molecules/HeldOrderCard/index.tsx`). - **Bordes/Sombras:** ✅ tarjeta plana con hairline, sin sombra; botón sin sombra, label 14/600. - **Estados:** CTA “Reanudar venta” es `contained` sólido; Figma lo diseña tonal soft (bg `#E6EEFA` medido, label primary) — requiere variante tonal de Button o `ButtonProps` en la tarjeta (`HeldOrderCard/index.tsx:115`). - **Contenido:** copy del subtítulo difiere (“compartidos por tienda en este turno” vs “de la sucursal en el turno abierto”) — menor, `heldOrder.i18n.ts`; “Pantalla completa” arriba-derecha no existe en el diseño (shell compartido, una línea: igual que el resto de /admin). | | #### W64b · Ventas en espera — desde el POS [Section titled “W64b · Ventas en espera — desde el POS”](#w64b--ventas-en-espera--desde-el-pos) **Ruta:** `/terminal/home (diálogo)` · **Figma:** [`574:14716`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=574-14716) · **Historia:** US-POS-8 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W64b](/design/parity/figma/screens/figma-w64-en-espera.png)](/design/parity/figma/screens/figma-w64-en-espera.png "figma W64b — abrir a tamaño completo") | [![app W64b](/design/parity/history/2026-07-24/screenshots/terminal-home--dialog-ventas-en-espera.png)](/design/parity/history/2026-07-24/screenshots/terminal-home--dialog-ventas-en-espera.png "app W64b — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**La captura nueva confirma que la remediación sistémica llegó: azul canónico, hairlines y tinte de marca medidos. El residuo es propio de la tarjeta (variante de botón, anatomía) más un bug de i18n en el tiempo relativo.- **Color:** ✅ Verificado — “Reanudar venta” y “Cerrar” miden **#0052CC**; borde de tarjeta **#E5E7EB**; avatar SR en tinte de marca (\~#E1EAF9) con iniciales azules. - **Estados:** Figma usa botón **tonal** para “Reanudar venta” (fill medido exactamente **#E6EEFA**, etiqueta azul); la app renderiza `variant="contained"` sólido #0052CC → jerarquía más pesada que el diseño. Culpable: `src/ui/molecules/HeldOrderCard/index.tsx:115`. - **Contenido:** chip “**17 days ago**” en inglés dentro de UI en español (Figma: “Hace 4 min”). `HeldOrdersDialog.tsx:142` llama `formatRelativeTime` → dayjs `.fromNow()` sin locale `es` (`src/core/utils.ts:401`). Bug de i18n real, no de estilo. - **Layout:** anatomía de tarjeta distinta al Figma: id “#H-2271” azul vs “H-2271” oscuro sin “#”; tiempo y “En espera por…” como Chips outlined en vez de texto gris con mini-avatar inline; precio junto a los chips (Figma: esquina superior derecha); avatar arriba a la derecha (Figma: inline). Todo en `HeldOrderCard`. - **Tipografía:** jerarquía interna invertida vs Figma (Figma: nombre en bold, id secundario azul; app: id en bold, nombre gris) — mismo culpable. - **Bordes/Sombras:** ✅ tarjetas planas con hairline #E5E7EB, sin sombra; radio del diálogo conforme. - **Contenido (dato):** montos/fechas difieren por mock data ($37,200 vs $372; “17 days” por `heldAt` viejo en `heldOrder.mocks.ts`) — no es defecto de paridad. - Diálogo-vs-página y tabs “Mis carritos/Todos” ausentes: decisión de producto conocida y documentada (el terminal sólo muestra “mine”; “Todos” es superficie admin) — no se re-lista. | | #### POS — índice admin [Section titled “POS — índice admin”](#pos--índice-admin) **Ruta:** `/admin/pos` · **Historia:** — | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — índice de navegación, sin frame | [![app POS — índice admin](/design/parity/history/2026-07-24/screenshots/admin-pos.png)](/design/parity/history/2026-07-24/screenshots/admin-pos.png "app POS — índice admin — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Intentos de pago (payment intents) [Section titled “Intentos de pago (payment intents)”](#intentos-de-pago-payment-intents) **Ruta:** `/admin/pos/payment-intents` · **Historia:** US-POS-4 (soporte) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — pantalla de diagnóstico, sin frame | [![app Intentos de pago (payment intents)](/design/parity/history/2026-07-24/screenshots/admin-pos-payment-intents.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-payment-intents.png "app Intentos de pago (payment intents) — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Aprobaciones de supervisor [Section titled “Aprobaciones de supervisor”](#aprobaciones-de-supervisor) **Ruta:** `/admin/pos/approvals` · **Historia:** US-POS-2 / US-VOID-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el diseño vive como organismo **POS Approval Modal**, no como pantalla | [![app Aprobaciones de supervisor](/design/parity/history/2026-07-24/screenshots/admin-pos-approvals.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-approvals.png "app Aprobaciones de supervisor — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | ### VENTAS / VOID — Historial de ventas, cancelación y reembolso [Section titled “VENTAS / VOID — Historial de ventas, cancelación y reembolso”](#ventas--void--historial-de-ventas-cancelación-y-reembolso) Flujo W-06. Historias `US-VOID-1` … `US-VOID-3`. #### W29 · Ventas [Section titled “W29 · Ventas”](#w29--ventas) **Ruta:** `/admin/pos/sales-orders` · **Figma:** [`119:1820`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=119-1820) · **Historia:** US-VOID-1 (entrada) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W29](/design/parity/figma/screens/figma-w29-ventas.png)](/design/parity/figma/screens/figma-w29-ventas.png "figma W29 — abrir a tamaño completo") | [![app W29](/design/parity/history/2026-07-24/screenshots/admin-pos-sales-orders.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-sales-orders.png "app W29 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes (de contenido/feature, NO de estilo)** *(re-auditado 2026-07-23 con recaptura)* — los tokens sistémicos se confirman todos en la recaptura; el residuo es una implementación reducida de la pantalla.- **Color (fix-gaps CERRADOS en la recaptura):** los badges de estado ya son **soft por rol** — Pagada verde soft, Pago Parcial ámbar soft, Abierta info soft, Anulada/Cancelada rojo soft, Borrador neutro (`StatusBadge`, no `` crudo). El ítem de nav activo (“Ventas”) ya va en tinte de marca **#E6EEFA** + texto azul (no gris). La paginación es el **pager numerado** (“Mostrando 1–6 de 6” + chip “1” azul), no `TablePagination`. Los tres residuos de estilo que esta nota marcaba están cerrados. - **Color (OK):** azul canónico #0052CC en Filtros/nav; cabecera de tabla #F7F9FC, divisores #E5E7EB, título #1F2933, chip “Turno abierto” soft #ECFDF5 — coinciden. - **Contenido (residuo real):** falta la toolbar rica del diseño: subtítulo KPI (“1,284 ventas · $184,320 este mes”), botón primario **Exportar**, búsqueda local, tabs segmentadas Todas/Pagadas/Canceladas y el select. La app añade un botón “Pantalla completa” que no está en Figma. - **Layout (residuo real):** esquema de tabla distinto — Figma: checkbox de selección + avatar de cliente + Folio/Cliente/Fecha/Total/Artículos/Estado + editar por fila; app: Ticket/Estado/Sucursal/Empleado/Fecha/Total/Por Cobrar, sin selección ni acción por fila. Definir con producto el esquema vigente. - **Tipografía:** encabezado “Órdenes de Venta” en jerarquía menor que el H grande “Ventas” de Figma, y sin subtítulo.Conclusión: **estilo/tokens alineados** (badges soft, nav tint, pager numerado, header, azul). El ❌ restante es **alcance de feature** — columnas/toolbar/selección/métricas y datos reales — no desviación de estilo. Tratarlo como build-out de pantalla, no como pulido de paridad. | | #### W30 · Detalle de venta [Section titled “W30 · Detalle de venta”](#w30--detalle-de-venta) **Ruta:** `— (drawer en la lista)` · **Figma:** [`125:6743`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=125-6743) · **Historia:** US-POS-7 / US-VOID-1 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W30](/design/parity/figma/screens/figma-w30-detalle-venta.png)](/design/parity/figma/screens/figma-w30-detalle-venta.png "figma W30 — abrir a tamaño completo") | [![app W30](/design/parity/history/2026-07-24/screenshots/admin-pos-sales-orders--dialog-detalle.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-sales-orders--dialog-detalle.png "app W30 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**- **Contenedor:** diálogo sobre la lista vs página dedicada del diseño — gap conocido #240 (pendiente de producto); la versión fiel existe solo en `parity/VentasDetailScreen.tsx` (playground). No se re-lista como defecto. - **Color:** azul canónico OK — etiqueta de “Asignar cliente” medida **#0052CC** ✓; cabecera de tabla **#F7F9FC** ✓; divisor bajo cabecera **#E5E7EB** ✓; valores **#1F2933** ✓, labels ≈#6B7280 ✓. - **Color:** badge “Pagada” es Chip sólido **#10B981 con texto blanco**; Figma usa tinte soft (#ECFCF5 + texto verde). El fix “StatusBadge sobre tokens \*-soft” NO llegó: `SalesOrderDetail.tsx:87` usa `Chip color="success"` crudo en vez de `atoms/StatusBadge`. - **Iconos:** el icono de “Asignar cliente” es **negro** junto a etiqueta azul: `src/ui/FontAwesomeIcon.tsx:25` (default `color='black'`) + `AssignCustomerButton.tsx:57` no pasa color — debería heredar `currentColor`. - **Contenido:** montos sin “$” (“540.00” vs “$372.00” del diseño): `formatCurrency` (`src/core/utils.ts:49`) no antepone símbolo; existe `formatCurrencyWithSymbol` sin usar en `SalesOrderDetail.tsx`. - **Contenido:** faltan “Reimprimir” (solo existe en el showcase), “Folio fiscal”, “Recibido”/“Cambio” (la tabla de pagos solo trae Método/Tarjeta/Estado/Importe) y el nombre del Cliente (solo botón de asignar). “Cancelar / Reembolsar” (`RefundAction`) existe en código pero no es visible en la captura — verificar gating con status PAID. - **Layout:** summary como pila vertical de 8 pares label/valor a ancho completo — densidad muy inferior a las dos cards (“Artículos” + “Información”) en dos columnas del diseño; el contenido desborda el viewport y la tabla queda cortada. - **Bordes:** borde del botón outlined = azul al \~50% (medido #7FA8E5, default MUI) vs borde sólido #0052CC del diseño — menor. - **Tipografía/Contenido:** título “A-1001” (ticketNumber crudo) vs “Venta #1042” formateado — menor, dato de mock. | | #### Ventas — filtros [Section titled “Ventas — filtros”](#ventas--filtros) **Ruta:** `/admin/pos/sales-orders (diálogo)` · **Historia:** US-REP-6 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el diseño vive como organismo **Filter Bar**, no como pantalla | [![app Ventas — filtros](/design/parity/history/2026-07-24/screenshots/admin-pos-sales-orders--dialog-filtros.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-sales-orders--dialog-filtros.png "app Ventas — filtros — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### W31 · Cancelar / Reembolsar [Section titled “W31 · Cancelar / Reembolsar”](#w31--cancelar--reembolsar) **Ruta:** `/terminal/voids` · **Figma:** [`131:8774`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=131-8774) · **Historia:** US-VOID-1 / US-VOID-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W31](/design/parity/figma/screens/figma-w31-cancelar-reembolsar.png)](/design/parity/figma/screens/figma-w31-cancelar-reembolsar.png "figma W31 — abrir a tamaño completo") | [![app W31](/design/parity/history/2026-07-24/screenshots/terminal-voids.png)](/design/parity/history/2026-07-24/screenshots/terminal-voids.png "app W31 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **FIGMA:** modal “Autorización requerida” con PIN-pad de 4 dígitos (Cancelar / Autorizar) superpuesto al detalle de **Venta #1042** en el back office (Ventas > Historial), con sidebar y topbar completos. - **APP:** página POS `/terminal/voids` (“Anular Tickets”) en **estado vacío**: buscador “Buscar por número de ticket…”, mensaje “No hay tickets que se puedan anular en este turno” y botón “Bloquear ahora”. El diálogo de PIN nunca se abrió. - **Causa:** la captura se tomó en un turno sin tickets anulables, así que el paso diseñado (autorización con PIN) es inalcanzable en esa sesión. El diálogo sí existe en código: `src/app/terminal/voids/_dialog/` (strings en `src/app/terminal/voids/voids.i18n.ts:63-68`, “Autorización de supervisor”). - **Recaptura necesaria:** sembrar un ticket anulable (mock) en el turno, abrir el diálogo de PIN y recapturar ese estado. - **Verificación sistémica parcial (lo visible):** “Bloquear ahora” mide **#0052CC** y el título **#1F2933** — la remediación sí llegó a esta ruta; queda pendiente medir el diálogo real. - **Divergencia de IA conocida (pendiente de producto, no estilo):** diseño = flujo desde detalle de venta del back office; app = página POS dedicada con búsqueda de ticket. | | ### CAT — Catálogo [Section titled “CAT — Catálogo”](#cat--catálogo) Flujo W-04. Historias `US-CAT-1` … `US-CAT-3`. #### W20 · Productos [Section titled “W20 · Productos”](#w20--productos) **Ruta:** `/admin/pos/catalog` · **Figma:** [`118:1529`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=118-1529) · **Historia:** US-CAT-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W20](/design/parity/figma/screens/figma-w20-productos.png)](/design/parity/figma/screens/figma-w20-productos.png "figma W20 — abrir a tamaño completo") | [![app W20](/design/parity/history/2026-07-24/screenshots/admin-pos-catalog.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-catalog.png "app W20 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes** — los fixes sistémicos de color sí llegaron; el residuo es estructural y de contenido, más un bug de datos.- **Color:** ✅ Azul canónico verificado: tab activa + indicador **#0052CC**, botón «Nuevo» **#0052CC**, «Filtros» y «Pantalla completa» **#0052CC**; título **#1F2933**, cabecera de tabla **#F7F9FC**, divisores **#E5E7EB**. ❌ Chip «Activo» sólido **#10B981** con texto blanco; Figma usa StatusBadge soft (bg **#D0F4E6**, texto verde) — `ProductList.tsx` usa `Chip color="success"` en vez de StatusBadge. - **Color:** Ítem seleccionado del sidebar con pill gris **#EBEBEB**; en Figma es tinte de marca **#E6EEFA** (el tinte `primary.light` no llegó al nav del AppShell). - **Contenido (bug):** La columna Categoría muestra el **ID numérico** («1», «3», «5») en vez del nombre («Bebidas», «Panadería»…) — `src/app/components/PosCatalog/ProductList.tsx` (render de `field: 'category'` devuelve `row.category` crudo; falta resolver el nombre). - **Contenido:** Faltan del diseño: subtítulo «248 productos · 12 categorías», búsqueda dentro de la tab, chips segmentados Todos/Activos/Bajo stock y el select; solo existe el botón «Filtros» (además a la izquierda; en Figma va a la derecha). - **Layout:** Set de columnas distinto: Figma = avatar iniciales + Producto, SKU, Categoría, Precio, Stock, Estado, editar por fila; app = Nombre, Categoría, Precio, Variantes, Códigos de barras, Activo — sin SKU, sin Stock, sin acción de edición ni avatar (el Avatar tinte+iniciales promovido no llegó a esta tabla). - **Layout:** Paginación MUI («Filas por página», «1–6 de 6») vs paginación numerada del diseño («Mostrando 1–5 de 248», páginas 1…10). - **Estados:** Figma muestra fila seleccionada (checkbox azul + bg **#E6EEFA**); estado no ejercitado en la captura — verificar hover/selección de fila. - **Tipografía:** Botón dice «Nuevo» sin icono; Figma «+ Nuevo producto». Texto de filas renderiza oscuro (px medidos \~#0D1823–#28313B, consistente con #1F2933) ✅. - **Bordes:** Tabla con contenedor hairline y radios correctos ✅; tabs con patrón underline MUI vs pill segmentado con tinte del diseño (decisión de patrón pendiente, una línea y sigo). - **Iconos:** «Pantalla completa» no existe en el diseño (affordance de la app); topbar difiere del diseño (grupo de avatares JP/MJ/LR ausente) — sistémico del shell, no propio de esta pantalla. | | #### W21 · Producto crear / editar [Section titled “W21 · Producto crear / editar”](#w21--producto-crear--editar) **Ruta:** `/admin/pos/catalog (formulario)` · **Figma:** [`120:2657`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=120-2657) · **Historia:** US-CAT-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W21](/design/parity/figma/screens/figma-w21-producto-edit.png)](/design/parity/figma/screens/figma-w21-producto-edit.png "figma W21 — abrir a tamaño completo") | [![app W21](/design/parity/history/2026-07-24/screenshots/admin-pos-catalog--dialog-nuevo-producto.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-catalog--dialog-nuevo-producto.png "app W21 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Mismo paso del flujo (crear producto), pero diálogo lateral en la app vs página completa en Figma — decisión de producto pendiente ya registrada; no se re-audita la geometría derivada de eso.- **Color:** fixes sistémicos confirmados por medición: #0052CC en botón Guardar, borde de foco (Categoría) y track del switch Activo; borde de campo #E5E7EB con relleno blanco; placeholder #9CA3AF — todo igual al Figma. - **Color:** residuo real — títulos (“Información básica”, “Variantes y códigos de barras”) y labels de campo miden **#000000 puro** vs #1F2933 del Figma. Culpable: `src/app/components/PosCatalog/ProductForm.tsx` usa `color="black"` literal en `Title`/`Text` (líneas 59, 78, 88, 97, 151, 158, 166, 175, 184); el mapeo de tokens no aplica sobre “black” explícito. - **Estados:** switch on con azul canónico y thumb blanco, geometría \~44×23 medida — fix aplicado. - **Espaciado:** etiqueta pegada al switch (gap ≈2 px entre track y “Activo”/“Principal”); Figma separa switch y label claramente. - **Contenido:** faltan vs Figma: SKU de producto, Costo, dropzone de **Imagen**, sección **Inventario** completa (stock inicial, alerta de stock bajo, umbral); Descripción sin placeholder (“Escribe una descripción del producto…”). - **Contenido:** Categoría es input de texto libre; en Figma es select con chevron (opciones de catálogo). - **Layout:** Variantes siempre visibles con una fila vacía; en Figma son opt-in con switch “Tiene variantes” y campo “Eje de variante”. Modelo de código de barras distinto (por variante con simbología/Principal en la app vs campo único de producto en Figma) — verificar si es decisión de datos deliberada. - **Contenido:** footer del diálogo “Cancelar/Guardar” vs barra sticky “Tienes cambios sin guardar · Descartar/Guardar” — ligado a la decisión diálogo-vs-página. | | #### W22 · Detalle de producto [Section titled “W22 · Detalle de producto”](#w22--detalle-de-producto) **Ruta:** `— (drawer)` · **Figma:** [`133:10120`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=133-10120) · **Historia:** US-CAT-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------- | | [![figma W22](/design/parity/figma/screens/figma-w22-detalle-producto.png)](/design/parity/figma/screens/figma-w22-detalle-producto.png "figma W22 — abrir a tamaño completo") | ⚠️ **Falta captura** — no hay pantalla de detalle dedicada | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W23 · Categorías [Section titled “W23 · Categorías”](#w23--categorías) **Ruta:** `/admin/pos/catalog/categories` · **Figma:** [`122:2840`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-2840) · **Historia:** US-CAT-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W23](/design/parity/figma/screens/figma-w23-categorias.png)](/design/parity/figma/screens/figma-w23-categorias.png "figma W23 — abrir a tamaño completo") | [![app W23](/design/parity/history/2026-07-24/screenshots/admin-pos-catalog-categories.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-catalog-categories.png "app W23 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes** — el estilo (post-fix) ya coincide; el residuo es de composición y contenido propio de la pantalla.- **Color:** Fixes verificados en esta captura: botón primario “Nuevo” **#0052CC** (medido, = Figma), indicador y label de tab activa **#0052CC**, botón “Filtros” con icono **#0052CC**, cabecera de tabla **#F7F9FC**, divisores **#E5E7EB**, checkbox con trazo **#D1D5DB** y esquinas redondeadas, fondo **#F5F7FA**. El azul canónico llegó a toda la pantalla. - **Contenido:** El esquema de la tabla no es el del diseño: Figma pide Categoría (avatar de iniciales en tinte) / SKUs / Margen / Ventas mes / Productos / Estado + acción de edición; la app muestra Nombre+slug / Descripción / Orden / Activa / Fecha de Creación, sin columna de acciones ni avatar (el fix “Avatar en tinte + iniciales” no llegó aquí). - **Contenido:** Falta la toolbar de filtrado del diseño: campo “Buscar…”, chips segmentados Todas/Activas/Vacías y select “Selecciona una opción”; la app sólo tiene “Filtros”. - **Estados:** “Estado” en Figma es badge soft verde/rojo (Activa/Vacía); la app usa icono booleano monocromo **#1F2933** — el StatusBadge sobre tokens \*-soft no se aplicó a esta tabla. - **Layout:** Paginación distinta: Figma numerada con “Mostrando 1–5 de 12”; app usa TablePagination estándar (“Filas por página / 1–7 de 7”). Botón “Pantalla completa” extra en la app, ausente en Figma. - **Layout:** (sistémico conocido, 1 línea) tab “Categorías” dentro de Productos/Categorías/Impuestos vs página dedicada con encabezado “Categorías · 12 categorías” y CTA “+ Nueva categoría” — decisión de producto pendiente; la etiqueta “Nuevo” sin icono + deriva de esto. - **Estados:** Fila seleccionada con tinte #E6EEFA del diseño: sin fila seleccionada en la captura — verificar (el tinte existe en palette.primary.light). - **Tipografía / Bordes / Sombras:** Sin hallazgos: jerarquía nombre 600 + slug secundario, tarjeta plana con hairline, sin sombras — conforme. | | #### Categoría — alta [Section titled “Categoría — alta”](#categoría--alta) **Ruta:** `/admin/pos/catalog/categories (diálogo)` · **Figma:** [`122:2840`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-2840) · **Historia:** US-CAT-2 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Categoría — alta](/design/parity/figma/screens/figma-w23-categorias.png)](/design/parity/figma/screens/figma-w23-categorias.png "figma Categoría — alta — abrir a tamaño completo") | [![app Categoría — alta](/design/parity/history/2026-07-24/screenshots/admin-pos-catalog-categories--dialog-nueva-categoria.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-catalog-categories--dialog-nueva-categoria.png "app Categoría — alta — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Panel FIGMA** (`figma/screens/figma-w23-categorias.png`): pantalla de **listado de Categorías** — título “Categorías / 12 categorías”, botón “+ Nueva categoría”, buscador, chips Todas/Activas/Vacías, tabla (Categoría, SKUs, Margen, Ventas mes, Productos, Estado) y paginación. El frame **no contiene ningún formulario de alta** (ni modal ni drawer). - **Panel APP** (`screenshots/admin-pos-catalog-categories--dialog-nueva-categoria.png`): **drawer lateral “Nueva categoría”** abierto sobre el listado, con campos Nombre\*, Identificador, Descripción, Categoría Padre, Sucursal, Orden, Color y acciones Cancelar/Guardar. - **Causa probable:** el manifest emparejó el frame del *listado* (que corresponde al heading de listado de categorías) con la captura del *formulario de alta*. No hay frame de Figma del formulario en este par. - **Acción:** conseguir/exportar el frame de Figma del formulario “Nueva categoría” y re-auditar; mientras tanto no se puede emitir veredicto de paridad para esta pantalla. - Dato informativo (medido, no un veredicto): el azul canónico **#0052CC** sí llegó al drawer (botón Guardar y borde de foco de Nombre). | | #### W24 · Impuestos [Section titled “W24 · Impuestos”](#w24--impuestos) **Ruta:** `/admin/pos/tax-rates` · **Figma:** [`122:3134`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-3134) · **Historia:** US-CAT-3 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W24](/design/parity/figma/screens/figma-w24-impuestos.png)](/design/parity/figma/screens/figma-w24-impuestos.png "figma W24 — abrir a tamaño completo") | [![app W24](/design/parity/history/2026-07-24/screenshots/admin-pos-tax-rates.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-tax-rates.png "app W24 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes** — los tokens sistémicos llegaron (azul, bordes, textos, cabecera de tabla medidos y correctos), pero el residuo propio de la pantalla es sustancial: la columna Estado está rota de color y faltan piezas de la toolbar y del modelo de columnas.- **Color:** verificado en captura: botón “Nuevo”, tab activa e indicador = **#0052CC** ✓; cabecera de tabla **#F7F9FC** ✓; bordes/divisores **#E5E7EB** ✓; texto primario **#1F2933** / secundario \~#6B7280 ✓; checkbox con trazo **#D1D5DB** y geometría redondeada ✓. - **Estados (bug):** los iconos de “Activa” renderizan ambos en **#1F2933** (medido en check y en x) aunque el código pide `color="success"/"error"` — `src/app/components/PosTaxRates/PosTaxRateList.tsx:75` pasa claves de palette a `src/ui/FontAwesomeIcon.tsx:47`, que las reenvía como color CSS inválido y el SVG hereda `currentColor`. Figma pide chips soft (Activa #10B981 sobre #ECFCF5, Inactiva roja): reemplazar el icono por **StatusBadge** (\*-soft), que ya existe promovido. - **Contenido:** faltan columnas del diseño: **Tipo** (IVA/IEPS), monto ($) y conteo de **Productos**; el app muestra en cambio Impuesto Incluido y Fecha de Creación. Verificar contra el modelo de datos real. - **Contenido:** Figma trae búsqueda inline + chips rápidos **Todas/Activas/Inactivas** + select en la toolbar; el app solo tiene “Filtros” (búsqueda escondida en el diálogo). Falta el filtro rápido de estado (`FilterBar` en PosTaxRateList.tsx:177). - **Iconos:** sin acción de edición visible por fila (Figma tiene icono de lápiz); el app depende de `onRowClick`, affordance invisible. - **Iconos:** el botón primario dice “Nuevo” sin icono +; Figma: “Nueva tasa” con +. - **Layout:** Figma no incluye avatar de iniciales en app (CO/SA…) — el app usa nombre + región secundaria; menor. - **Layout:** paginación “Filas por página 7” + flechas vs numerada del diseño (sistémico de DataTable; el default 7 es raro). - **Contenido:** título “Tasas de Impuesto” (i18n) + subnav de catálogo vs página standalone “Impuestos” con subtítulo — composición en tabs es decisión de producto (#408), y el botón “Pantalla completa” no existe en el diseño; una línea, no se re-audita. | | #### Impuesto — alta [Section titled “Impuesto — alta”](#impuesto--alta) **Ruta:** `/admin/pos/tax-rates (diálogo)` · **Figma:** [`122:3134`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-3134) · **Historia:** US-CAT-3 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Impuesto — alta](/design/parity/figma/screens/figma-w24-impuestos.png)](/design/parity/figma/screens/figma-w24-impuestos.png "figma Impuesto — alta — abrir a tamaño completo") | [![app Impuesto — alta](/design/parity/history/2026-07-24/screenshots/admin-pos-tax-rates--dialog-nuevo-impuesto.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-tax-rates--dialog-nuevo-impuesto.png "app Impuesto — alta — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Panel FIGMA:** muestra la pantalla de **lista** de Impuestos (header “Impuestos”, filtros Todas/Activas/Inactivas, tabla de 5 tasas, paginación, botón “Nueva tasa”). No hay formulario de alta en ninguna parte del frame (`figma/screens/figma-w24-impuestos.png`, 1440×900 completo verificado). - **Panel APP:** muestra el **drawer “Nuevo Impuesto”** abierto sobre la lista: campos Nombre (enfocado), Tasa, Región, switches “Impuesto Incluido” (off) y “Activa” (on), acciones Cancelar/Guardar (`screenshots/admin-pos-tax-rates--dialog-nuevo-impuesto.png`). - **Causa probable:** el manifest empareja el alta con el frame de la lista — falta exportar el frame Figma del formulario de alta (si existe en el archivo) o corregir el pairing. - **Informativo (medido, no es auditoría de estilo):** los fixes sistémicos sí llegaron a esta captura — Guardar `#0052CC`, borde de campo enfocado `#0052CC`, track del switch Activa `#0051CA` (≈ azul canónico). Queda pendiente la decisión de producto diálogo-vs-drawer ya conocida. - **Acción:** conseguir la referencia Figma del alta y re-auditar el par. | | ### INV — Inventario [Section titled “INV — Inventario”](#inv--inventario) Flujo W-05. Historias `US-INV-1` … `US-INV-3`. #### W25 · Niveles de stock [Section titled “W25 · Niveles de stock”](#w25--niveles-de-stock) **Ruta:** `/admin/pos/stock-levels` · **Figma:** [`122:3426`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-3426) · **Historia:** US-INV-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W25](/design/parity/figma/screens/figma-w25-stock.png)](/design/parity/figma/screens/figma-w25-stock.png "figma W25 — abrir a tamaño completo") | [![app W25](/design/parity/history/2026-07-24/screenshots/admin-pos-stock-levels.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-stock-levels.png "app W25 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Misma pantalla conceptual (Inventario / niveles de stock), pero la app es una implementación mínima tipo placeholder que omite casi toda la estructura y el contenido del diseño. No es redirect ni otro paso del flujo. (No pude medir hex: el clasificador de Bash estuvo caído toda la sesión — los juicios de color van por lectura visual y marcados como *verificar*.)- **Contenido:** Faltan casi todos los bloques diseñados: título “Niveles de stock” + subtítulo “Existencias por tienda”, sub-tabs (Niveles / Movimientos / Transferencias / Ajustar), campo de búsqueda, chips de filtro (Todos / Bajo stock / Agotados), botón Filtros, badges de **Estado** (En stock / Bajo / Agotado), columnas **SKU** y **Costo**, avatares de producto con iniciales, checkboxes de selección de fila y paginación (“Mostrando 1–5 de 248”). Ninguno aparece en la app. - **Contenido/Datos:** La app muestra datos mock (`var-1001`, `store-1`) en vez de nombres/SKU reales; agrega una columna **Variante** (vacía) que no existe en Figma, y le faltan **Tienda**, **Costo** y **Estado** del diseño. - **Layout:** Figma coloca la tabla dentro de una card con borde redondeado; la app la deja casi a sangre con una columna de acción sin encabezado (botón **Ajustar** por fila). El header de la app sí lleva el tinte claro (\~#F7F9FC visual) y separadores hairline — consistentes con el sistema. - **Estados / Bug propio de pantalla:** ~~El select “Todas las sucursales” renderiza el label superpuesto~~ **Corregido** — se fijó `inputLabel.shrink` en el filtro (`StockLevelList.tsx`), el label “Sucursal” ahora va estático arriba y el valor debajo, sin superposición. Queda menor: el placeholder del buscador global aparece recortado (“Buscar productos, venta…”). - **Color (verificar):** El botón primario “Ajuste de inventario” se ve como azul de marca sólido, aparentemente el canónico #0052CC; “Ajustar”/“Pantalla completa” son outlined con texto azul sobre blanco y borde hairline — visualmente alineado con la remediación. Sin medición por Bash caído. - **Tipografía:** Etiqueta de botón bold blanca \~14/600 y encabezados de tabla en gris — consistentes con los fixes sistémicos.Conclusión (re-auditado 2026-07-23): los fixes sistémicos se confirman en la recaptura — SideNav con item activo en tinte de marca #E6EEFA + texto azul, header de tabla #F7F9FC + hairlines, CTA azul sólido y botones outlined con borde azul. El bug del label del Select está **corregido**. El residuo restante es **de alcance funcional, no de estilo**: la pantalla es un esqueleto frente al diseño (faltan sub-tabs Niveles/Movimientos/Transferencias, toolbar de filtros con chips, columnas SKU/Costo/Estado, avatares, selección por fila y paginación, además de datos reales en vez de mock). Recomendación: tratarlo como épica de feature (build-out de columnas/filtros + adaptador de datos), no como pulido de paridad — el theme y los átomos ya están alineados. | | #### W26 · Ajuste de inventario [Section titled “W26 · Ajuste de inventario”](#w26--ajuste-de-inventario) **Ruta:** `/admin/pos/stock-levels/adjust` · **Figma:** [`124:5630`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=124-5630) · **Historia:** US-INV-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W26](/design/parity/figma/screens/figma-w26-ajuste.png)](/design/parity/figma/screens/figma-w26-ajuste.png "figma W26 — abrir a tamaño completo") | [![app W26](/design/parity/history/2026-07-24/screenshots/admin-pos-stock-levels-adjust.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-stock-levels-adjust.png "app W26 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Misma pantalla (formulario “Ajuste de inventario”), y la remediación sistémica se ve reflejada: el azul de marca (nav activo “Inventario” sobre tinte, botón “Pantalla completa”, chip “Tienda demo”, logo) es visualmente consistente con #0052CC; textos #1F2933/#6B7280, bordes hairline #E5E7EB y campos de relleno blanco con alto \~40px. El residuo es propio de la pantalla, en composición y contenido:- **Layout:** Figma organiza los campos en una card con **grid de 2 columnas** (Tienda+Tipo, Cantidad+Motivo); la app los **apila en 1 sola columna** full-width. Divergencia estructural fuerte. - **Contenido/Controles:** Figma usa **inputs de texto con placeholders** (Coca-Cola 600ml, Tienda Centro, Entrada, +24, Compra a proveedor, Recepción #4521); la app usa **selects/dropdowns** para Producto/Variante, Sucursal, Tipo y Motivo — cambia el tipo de campo, no solo el estilo. - **Jerarquía/Layout:** la app **no tiene** el H1 de página “Ajuste de inventario” + subtítulo “Registra un ajuste de existencias”, ni el **botón azul “+ Guardar” superior**, ni la **barra sticky inferior** “Tienes cambios sin guardar” con Descartar/Guardar. En su lugar el título vive dentro de la card y arriba a la derecha hay “Pantalla completa”. - **Contenido:** etiquetas divergentes — Producto→“Producto / Variante”, Tienda→“Sucursal”, Nota→“Notas”; la app añade **asteriscos rojos de requerido** ausentes en Figma. - **Color:** hex exacto de los azules quedó **por verificar** (herramienta de muestreo caída durante la auditoría); visualmente coincide con el canónico.Culpable probable: pantalla `admin/pos/stock-levels/adjust` (composición del form) — falta el header de página con acción Guardar y la barra de cambios sin guardar; alinear grid a 2 columnas y revisar labels/controles vs. diseño. | | #### W27 · Movimientos de inventario [Section titled “W27 · Movimientos de inventario”](#w27--movimientos-de-inventario) **Ruta:** `/admin/pos/stock-movements` · **Figma:** [`122:3717`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-3717) · **Historia:** US-INV-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W27](/design/parity/figma/screens/figma-w27-movimientos.png)](/design/parity/figma/screens/figma-w27-movimientos.png "figma W27 — abrir a tamaño completo") | [![app W27](/design/parity/history/2026-07-24/screenshots/admin-pos-stock-movements.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-stock-movements.png "app W27 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Misma pantalla conceptual (movimientos de inventario), pero la app implementa una vista muy distinta y más incompleta que el diseño. Los fixes sistémicos parecen presentes (botón azul de marca sólido sin sombra, cabecera de tabla en tinte claro, badges con tono), pero el residuo propio de la pantalla es grande.- **Layout:** Falta el encabezado de página completo — no hay título **“Movimientos de inventario”** ni subtítulo **“Entradas y salidas (ledger)”**. La app arranca directo en la tabla. - **Contenido:** Ausente toda la barra de herramientas del diseño: **buscador**, **tabs Todos/Entradas/Salidas**, botón **Filtros** y **dropdown** “Selecciona una opción”. La app no ofrece ninguna de estas. - **Contenido:** Acción primaria distinta. Figma: un único **“+ Nuevo movimiento”**. App: **“Recibir transferencia”** + **“Transferencia”** + **“Pantalla completa”** — es otra intención de flujo (transferencias) montada sobre esta ruta. - **Contenido:** Columnas divergentes. Figma = `[✓] Producto (avatar+nombre) · Tipo · Motivo · Cantidad ($) · Saldo · Fecha · editar`. App = `Fecha · Producto (var-1001) · Variante (vacía) · Motivo · Ajuste (+50/-1/…)`. Sin checkbox de selección, sin avatar de producto y sin icono de editar por fila. - **Estados:** Sin fila seleccionada resaltada (en Figma la primera fila va marcada con check azul y fondo tenue). - **Contenido:** Sin **paginación**. Figma muestra “Mostrando 1–5 de 1,024” con paginador `1 2 3 … 10`. - **Color/Estados:** El “Motivo” se renderiza como **badge sólido** (verde Recepción, rojo Venta/Ajuste/Traspaso). El diseño usa etiquetas de texto para Tipo (Entrada/Salida) y Motivo (Compra/Venta/Merma), no pills de color. Revisar el mapeo StatusBadge para esta tabla. - **Color:** El azul del botón “Transferencia” y de la cabecera de tabla se leen sobre tokens de marca (no se pudo medir en pixel este run: el clasificador de Bash estaba caído; **verificar** #0052CC exacto).Fixes sistémicos: botón plano 14px/600, cabecera de tabla en tinte claro y separadores hairline se ven aplicados; el grueso del hallazgo es composición y contenido faltante propios de W27, no residuo de tokens. | | #### W28 · Transferencia entre tiendas [Section titled “W28 · Transferencia entre tiendas”](#w28--transferencia-entre-tiendas) **Ruta:** `/admin/pos/stock-movements/transfer` · **Figma:** [`124:5847`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=124-5847) · **Historia:** US-INV-3 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W28](/design/parity/figma/screens/figma-w28-transferencia.png)](/design/parity/figma/screens/figma-w28-transferencia.png "figma W28 — abrir a tamaño completo") | [![app W28](/design/parity/history/2026-07-24/screenshots/admin-pos-stock-movements-transfer.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-stock-movements-transfer.png "app W28 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Ambos paneles son el formulario de transferencia entre tiendas (misma pantalla). Los arreglos sistémicos SÍ llegaron aquí (medidos), pero la composición y el contenido divergen del diseño.- **Color:** Correcto y sobre token. Botón primario “Registrar transferencia” = #0052CC exacto (etiqueta blanca); relleno de campo #FFFFFF; borde hairline #E5E7EB; label #1F2933; lienzo #F5F7FA. Asterisco de requerido en rojo (#D74A4A), aceptable. - **Layout:** Divergencia principal. La app es una sola tarjeta angosta centrada con el título dentro; Figma es página ancha con título H1 “Transferencia entre tiendas” + subtítulo “Mueve existencias entre tiendas” y la tarjeta rotulada “Transferencia”. La app no tiene el encabezado de página. - **Layout:** Figma dispone los campos en rejilla de 2 columnas (Origen\|Destino, Cantidad\|Fecha); la app los apila en una sola columna. - **Contenido:** Falta el campo **Fecha** en la app. Renombres: Origen/Destino → “Sucursal de origen/destino”, Producto → “Producto / Variante”, Nota → “Notas”. - **Estados:** Figma usa inputs de texto con placeholders (Tienda Centro, Coca-Cola 600ml, 12, 24 Oct 2025); la app usa **selects/dropdowns** para producto y sucursales y campos vacíos con asterisco de requerido. Divergencia de control, no solo de estilo. - **Contenido:** Patrón de guardado distinto. Figma tiene “+ Guardar” arriba-derecha y **barra sticky** inferior (“Tienes cambios sin guardar” + Descartar + Guardar); la app tiene un único botón full-width “Registrar transferencia” dentro de la tarjeta y arriba-derecha muestra “Pantalla completa”. Falta la barra de acción persistente.Nota: la elección tarjeta-angosta-vs-página y el patrón de acción son decisiones de composición/producto; el residuo accionable propio de la pantalla es la ausencia del campo Fecha, la falta del encabezado de página y la falta de la barra sticky de guardado. | | #### Recepción de transferencia [Section titled “Recepción de transferencia”](#recepción-de-transferencia) **Ruta:** `/admin/pos/stock-movements/receive` · **Historia:** US-INV-3 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el flujo de recepción está sin diseñar (gap #16) | [![app Recepción de transferencia](/design/parity/history/2026-07-24/screenshots/admin-pos-stock-movements-receive.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-stock-movements-receive.png "app Recepción de transferencia — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | ### SHIFT — Turnos y caja [Section titled “SHIFT — Turnos y caja”](#shift--turnos-y-caja) Flujo W-07. Historias `US-SHIFT-1` … `US-SHIFT-5`. #### W32 · Turnos [Section titled “W32 · Turnos”](#w32--turnos) **Ruta:** `/admin/pos/shifts` · **Figma:** [`119:2117`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=119-2117) · **Historia:** US-SHIFT-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W32](/design/parity/figma/screens/figma-w32-turnos.png)](/design/parity/figma/screens/figma-w32-turnos.png "figma W32 — abrir a tamaño completo") | [![app W32](/design/parity/history/2026-07-24/screenshots/admin-pos-shifts.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-shifts.png "app W32 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Es la misma pantalla (ruta Turnos), no un redirect ni un diálogo, pero la implementación está muy por debajo del diseño: la app es una tabla genérica de turnos por empleado mientras el Figma es el historial de cortes de caja con toda su barra de acciones.- **Contenido:** faltan subtítulo “Historial de cortes de caja”, tabs (Turnos / Mov. efectivo / Cerrar turno), CTA primario **Abrir turno**, buscador, chips de filtro (Todos / Abiertos / Cerrados) y el dropdown “Selecciona una opción”. La app solo trae el botón **Filtros** (y un **Pantalla completa** que no está en el diseño). - **Layout / tabla:** el Figma tiene columna de selección (checkbox), avatares por fila y acción de editar; la app no tiene ninguna. Columnas distintas: Figma = Turno# / Cajero / Apertura / Vendido / Dif. / Estado; app = Empleado / Sucursal / Estado / Apertura / Diferencia. Distinto modelo de datos (corte por turno vs turno por empleado). - **Estados:** badge “Abierto” diverge en color y estilo — Figma = chip verde soft (`#70D6B4` sobre tinte); app = relleno **azul sólido `#3B82F6`** (rol info). Convendría alinear “Abierto” al tratamiento soft del design system. - **Color (marca):** el azul primario del Figma es `#0052CC`; en la app los controles outline (Filtros / Pantalla completa) leen un azul más brillante (\~`#3978D6`/`#5288DB` antialias), no el `#0052CC` canónico. Verificar que el botón use `primary.main`. - **Bien resuelto:** título `#1F2933`, cabecera de tabla `#F7F9FC`, divisores hairline `#E5E7EB`, badge neutro `#EBEBEB` y badges de diferencia con tokens \*-soft correctos (success `#10B981`, warning `#F59E0B`, error `#EF4444`) — los fixes sistémicos llegaron. - **Paginación:** Figma usa paginación numerada custom (“Mostrando 1–5 de 96”, 1 2 3 … 10); la app usa el TablePagination de MUI (“Filas por página / 1–7 de 7”). Diferencia de patrón, probablemente decisión de producto. | | #### W33 · Detalle de turno / Corte Z [Section titled “W33 · Detalle de turno / Corte Z”](#w33--detalle-de-turno--corte-z) **Ruta:** `— (ver W58)` · **Figma:** [`132:9833`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=132-9833) · **Historia:** US-SHIFT-5 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W33](/design/parity/figma/screens/figma-w33-detalle-z.png)](/design/parity/figma/screens/figma-w33-detalle-z.png "figma W33 — abrir a tamaño completo") | [![app W33](/design/parity/history/2026-07-24/screenshots/admin-pos-shifts--dialog-detalle.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-shifts--dialog-detalle.png "app W33 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **FIGMA:** página completa **“Turno #88 · Corte Z”** de un turno **cerrado** — tarjeta Resumen (Cajero Sofía Ramírez, Apertura Hoy 08:00, Cierre Hoy 16:00, Fondo inicial $1,500, Ventas efectivo $4,200, Retiros −$2,000, Efectivo esperado $3,700, Diferencia $0.00) y botón **“Reimprimir corte”**. Es el reporte Z ya emitido. - **APP:** **diálogo/panel** de detalle de un turno **abierto** (badge **“Abierto”**, “Pendiente de cierre”) superpuesto a la tabla de turnos — Ana Gómez · Sucursal Centro·term-01, Conciliación de Efectivo con Efectivo Esperado/Contado en **“—”** y Diferencia “Pendiente de cierre”, Movimientos “Sin movimientos”, Notas “Turno matutino en curso”. - **Desajuste de flujo:** el turno de la app **sigue abierto**, por lo que no existe un Corte Z que renderizar; además es diálogo, no página. Para auditar paridad hay que **recapturar un turno cerrado abriendo su vista de Corte Z** (la página con Resumen + Reimprimir corte). - No listo diferencias de estilo por tratarse de pantallas distintas. Nota lateral: el azul de marca sí aparece aplicado en la app (badge, “Pantalla completa”, nav activo), pero no es comparable aquí. | | #### W34 · Abrir turno [Section titled “W34 · Abrir turno”](#w34--abrir-turno) **Ruta:** `/terminal/shift/open` · **Figma:** [`132:9488`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=132-9488) · **Historia:** US-SHIFT-1 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W34](/design/parity/figma/screens/figma-w34-abrir-turno.png)](/design/parity/figma/screens/figma-w34-abrir-turno.png "figma W34 — abrir a tamaño completo") | [![app W34](/design/parity/history/2026-07-24/screenshots/terminal-shift-open.png)](/design/parity/history/2026-07-24/screenshots/terminal-shift-open.png "app W34 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Ambos paneles muestran el flujo *Abrir turno* con el teclado de fondo de caja: es la misma pantalla. El Figma la maqueta como tarjeta centrada dentro del chrome admin (sidebar + top bar); la app la renderiza como página fullscreen de terminal (sin sidebar, con “Bloquear ahora” y “Cancelar”). Es la conocida decisión sistémica página-vs-card embebida — pendiente de producto, no la repito como defecto de estilo.- **Color:** El azul de “Cancelar”, “Bloquear ahora” y el borde de foco del input se ve consistente con el canónico #0052CC; no pude medir el pixel (herramienta de muestreo caída por outage del clasificador) — dejar como *verificar medición*. - **Tipografía:** Título “Abrir turno” en gris oscuro (~~#1F2933) y subtítulo “Cuenta el fondo de caja inicial” en gris medio (~~#6B7280): jerarquía correcta, fix aplicado. - **Bordes:** Teclas del keypad y el input con hairline claro (\~#E5E7EB): coincide con el fix de bordes/campos. - **Estados:** Botón “Abrir turno” deshabilitado en gris (bg #E5E7EB + texto #9CA3AF) porque el monto es $0.00 — el fix de disabled se ve aplicado; en Figma aparece activo/azul por estar con $1,500.00 (diferencia de estado, esperada). - **Contenido:** La app **omite el bloque de identidad** Terminal `POS-01` / Cajero `Sofía R.` que el Figma coloca bajo el título; en su lugar la app muestra input editable “Fondo de caja”, enlace “Cancelar” y pie “Gerente Demo”. Evaluar si se agrega la meta Terminal/Cajero en la vista de terminal (terminal-shift-open). - **Iconos:** Teclas del keypad en la app van en cajas outlined redondeadas; en Figma los dígitos son planos (solo backspace enmarcado). Residuo menor de estilo del KeypadKey en esta pantalla. - **Layout:** Columna de contenido angosta centrada en viewport muy ancho (mucho whitespace lateral); propio de la página fullscreen de terminal, no de la tarjeta compacta del Figma. | | #### W35 · Cerrar turno [Section titled “W35 · Cerrar turno”](#w35--cerrar-turno) **Ruta:** `/terminal/shift/close` · **Figma:** [`132:9670`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=132-9670) · **Historia:** US-SHIFT-3 / US-SHIFT-4 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W35](/design/parity/figma/screens/figma-w35-cerrar-turno.png)](/design/parity/figma/screens/figma-w35-cerrar-turno.png "figma W35 — abrir a tamaño completo") | [![app W35](/design/parity/history/2026-07-24/screenshots/terminal-shift-close.png)](/design/parity/history/2026-07-24/screenshots/terminal-shift-close.png "app W35 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Panel FIGMA:** página “Cerrar turno · Corte Z” renderizada dentro del shell administrativo completo (sidebar “Terminales” + topbar con búsqueda y perfil). Contenido: tarjeta “Conteo de efectivo” (Efectivo esperado $5,800.00 / Efectivo contado $5,780.00 / Diferencia −$20.00), tarjeta “Ventas por método” (Efectivo, Tarjeta, Saldo, Total vendido $8,250.00) y acciones “Imprimir corte (Z)” (outline) + “Cerrar turno” (primario). Es una vista de **resumen / corte Z** ya calculado. - **Panel APP:** pantalla de **terminal** (shell POS, sin sidebar; botón “Bloquear ahora” arriba a la derecha) titulada “Cerrar turno” con subtítulo “Cuenta el efectivo por denominación”. Contenido: “Desglose de efectivo contado” con tabla de denominaciones ($1,000 → $0.50), cada una con input en 0 y subtotal $0.00; Total contado $0.00, Efectivo esperado $2,300.00, Diferencia −$2,300.00; campo “Notas (opcional)” y botón “Cerrar turno” deshabilitado. Es el **paso de captura por denominación**, no el resumen. - **Causa:** el par asocia el diseño de resumen Corte Z (admin) con la captura `screenshots/terminal-shift-close.png` (terminal, conteo por denominación). Ese contenido de denominaciones corresponde más bien a W65 (`admin-pos-shifts-close`). Para re-auditar W35 hay que recapturar la vista de resumen/corte Z equivalente, o re-mapear W35 a la pantalla de terminal correcta. No se listan diferencias de estilo porque los paneles no muestran la misma vista. | | #### W36 · Movimientos de caja (admin) [Section titled “W36 · Movimientos de caja (admin)”](#w36--movimientos-de-caja-admin) **Ruta:** `/admin/pos/cash-movements` · **Figma:** [`123:5405`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-5405) · **Historia:** US-SHIFT-2 / US-BAL-3 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W36](/design/parity/figma/screens/figma-w36-movimientos-caja.png)](/design/parity/figma/screens/figma-w36-movimientos-caja.png "figma W36 — abrir a tamaño completo") | [![app W36](/design/parity/history/2026-07-24/screenshots/admin-pos-cash-movements.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-cash-movements.png "app W36 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Es la misma pantalla (tabla admin de Movimientos de caja), no hay redirect ni paso distinto del flujo. Los fixes sistémicos SÍ llegaron a esta captura, el residuo es de composición/contenido propio de la pantalla.- **Color:** Verificado en la captura: azul de marca #0555CD (≈#0052CC) en badge de campana y botón Filtros; verde éxito #10B981 y rojo error en montos; cabecera de tabla #F7F9FC. Sin residuo de color. - **Contenido:** Falta el CTA primario azul relleno ‘+ Nuevo movimiento’ (la app solo tiene ‘Pantalla completa’ outline) — no hay acción de crear movimiento. - **Contenido:** Toolbar recortada: faltan campo de búsqueda, control segmentado Todos/Entradas/Salidas y el select ‘Selecciona una opción’; solo queda ‘Filtros’. - **Layout:** Columnas divergentes. Figma: checkbox · Movimiento (avatar+nombre) · Cajero · Motivo · Monto · Autoriza · Estado · editar. App: Tipo · Turno (+emp) · Monto · Motivo · Fecha. Faltan selección por fila, avatar, columna Cajero, columna Autoriza e icono de editar. - **Estados:** Sin StatusBadge de Estado (Registrado/Autorizado soft) en la app; el chip ‘Tipo’ es un pill neutro outline (#C3C3C3), no un status badge tintado. - **Componentes:** Paginación distinta — Figma numerada custom (‘1–5 de 42’, 1 2 3 … 10) vs TablePagination de MUI (‘Filas por página 7’, ‘1–7 de 7’). Decisión de componente pendiente. - **Color:** Montos coloreados verde/rojo en la app (mejora semántica razonable) frente a montos en negro con +/- en Figma; alinear criterio.Resumen: tokens sistémicos correctos; el gap es de paridad estructural/funcional (CTA, filtros, columnas de estado/cajero, paginación). | | #### W36b · Movimiento de caja (terminal) [Section titled “W36b · Movimiento de caja (terminal)”](#w36b--movimiento-de-caja-terminal) **Ruta:** `/terminal/shift/cash-movement` · **Figma:** [`123:5405`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-5405) · **Historia:** US-SHIFT-2 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W36b](/design/parity/figma/screens/figma-w36-movimientos-caja.png)](/design/parity/figma/screens/figma-w36-movimientos-caja.png "figma W36b — abrir a tamaño completo") | [![app W36b](/design/parity/history/2026-07-24/screenshots/terminal-shift-cash-movement.png)](/design/parity/history/2026-07-24/screenshots/terminal-shift-cash-movement.png "app W36b — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Panel Figma:** es la página **admin** de “Movimientos de caja” (la misma usada en W36): sidebar de navegación a la izquierda, barra de búsqueda superior, título “Movimientos de caja / Entradas y salidas del turno”, botón “Nuevo movimiento”, chips Todos/Entradas/Salidas + Filtros, y una **tabla** (Movimiento · Cajero · Motivo · Monto · Autoriza · Estado) con badges de estado y paginación “Mostrando 1-5 de 42”. - **Panel App:** es el **formulario de terminal** “Movimiento de efectivo”: subtítulo “Registra una entrada o salida de efectivo”, control segmentado Entrada/Salida/Depósito/Retiro, campo **Monto** ($0.00), **teclado numérico**, textarea “Motivo”, botón “Registrar” (deshabilitado) y enlace “Cancelar”. - **Causa:** el manifest empareja `figma-w36-movimientos-caja.png` (diseño admin) con `screenshots/terminal-shift-cash-movement.png` (pantalla de terminal). No hay un diseño Figma del formulario de terminal; se reutilizó el de W36. Sin diseño de referencia correcto, no procede auditar color/tipografía/espaciado. - **Acción:** conseguir/generar el Figma del formulario de terminal (teclado + Monto) para W36b, o reasignar esta captura. La verificación de los fixes sistémicos queda pendiente hasta tener el par correcto. - *Nota:* no se pudieron tomar mediciones de color (bash bloqueado por clasificador no disponible); el veredicto se apoya en la estructura del composite, que es inequívoca. | | #### W65 · Cerrar turno — desglose por denominación [Section titled “W65 · Cerrar turno — desglose por denominación”](#w65--cerrar-turno--desglose-por-denominación) **Ruta:** `/admin/pos/shifts/close?shiftId=…` · **Figma:** [`574:14732`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=574-14732) · **Historia:** US-SHIFT-3 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W65](/design/parity/figma/screens/figma-w65-cerrar-denominacion.png)](/design/parity/figma/screens/figma-w65-cerrar-denominacion.png "figma W65 — abrir a tamaño completo") | [![app W65](/design/parity/history/2026-07-24/screenshots/admin-pos-shifts-close.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-shifts-close.png "app W65 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Misma pantalla (Cerrar turno / Corte Z, desglose por denominación). Los tres KPIs coinciden en valores ($12,450.00 / $4,500.00 / $7,950.00) y los fixes sistémicos de color llegaron a esta vista.- **Color:** Azul de marca correcto en nav activo (“Turnos y caja”), chip “Tienda demo” y botón “Pantalla completa” (#0052CC). Residuo: el valor “Efectivo esperado” va en azul de realce en Figma pero la app lo pinta en texto oscuro #1F2933 como los otros KPIs — falta el tinte. - **Layout:** Diferencia principal — Figma empaqueta las 11 denominaciones en **dos columnas** compactas; la app las apila en **una sola columna** full-width, lo que baja mucho la densidad y empuja el totalizador y el botón fuera de pantalla. Revisar el grid del componente de cierre de turno. - **Espaciado / Composición:** Cada fila de denominación en la app se extiende a todo el ancho (monto izq. · input centro · resultado $0.00 extremo derecho) con divisor hairline; Figma usa el formato inline compacto “$1,000 × 2 = $2,000.00”. - **Contenido:** Título unificado en Figma (“Cerrar turno · Corte Z”) vs. título + subtítulo en la app (“Cuenta el efectivo por denominación (Corte Z)”) — reordenamiento menor, aceptable. Los conteos prellenados de Figma vs. ceros en la app son solo datos. - **Verificar:** Total contado / Diferencia (−$15.00) y botón “Cerrar turno (Z-Report)” quedaron bajo el fold en la captura; no auditables aquí. Recapturar a mayor alto. - **Nota herramientas:** imgtool/Bash no disponibles esta sesión; colores evaluados por lectura visual de ambos paneles, no muestreados en px. | | ### STAFF — Personal y control de acceso [Section titled “STAFF — Personal y control de acceso”](#staff--personal-y-control-de-acceso) Flujo W-08. Historias `US-STAFF-1` … `US-STAFF-4`. #### W37 · Empleados [Section titled “W37 · Empleados”](#w37--empleados) **Ruta:** `/admin/pos/employees` · **Figma:** [`119:2409`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=119-2409) · **Historia:** US-STAFF-1 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W37](/design/parity/figma/screens/figma-w37-empleados.png)](/design/parity/figma/screens/figma-w37-empleados.png "figma W37 — abrir a tamaño completo") | [![app W37](/design/parity/history/2026-07-24/screenshots/admin-pos-employees.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-employees.png "app W37 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**- **Color:** Fix sistémico confirmado — medí #0052CC en el botón “Nuevo”, en el subrayado del tab activo y en los chips de rol; sin rastro del azul viejo (#0057B8). - **Contenido (título):** App titula “Empleados” sin subtítulo; Figma titula “Personal” con contador “8 empleados · 3 roles”. Falta el contador. - **Contenido (toolbar):** Faltan el campo de búsqueda, el control segmentado Todos/Activos/Invitados y el select “Selecciona una opción”; la app solo expone “Filtros”. - **Contenido (CTA):** App “Nuevo” vs Figma “Invitar usuario” con icono +. Falta el glifo y la etiqueta correcta. - **Contenido (columnas):** App = Usuario/Sucursal/Rol/Activo/Fecha de Creación; Figma = Empleado/Rol/Tienda/Ventas/Turnos/Estado. Faltan **Ventas** y **Turnos**; el **Estado** (StatusBadge Activo/Invitado/Inactivo) se reemplazó por un icono booleano “Activo”; se añadió “Fecha de Creación” que no está en el diseño. - **Iconos (filas):** Faltan los avatares con iniciales por fila y la acción de editar (lápiz) por fila que sí aparecen en Figma. - **Estados:** El Rol se pinta como chip azul (Gerente/Supervisor) en la app; en Figma es texto plano — decoración de más respecto al diseño. - **Contenido (datos):** Sucursal muestra IDs crudos “store-1/store-2” en lugar de nombres legibles (“Tienda Centro/Norte/Sur”); mock por revisar. - **Layout (paginación):** App usa TablePagination de MUI (“Filas por página 7 · 1–5 de 5”) frente a la paginación numerada del diseño (“Mostrando 1–5 de 8 · 1 2 3 … 10”); además “7 filas/página” con solo 5 filas y total 5 es inconsistente. - **Bordes:** Cabecera de tabla medida en blanco (#FFFFFF) en vez del gris tokenizado #F7F9FC; los hairlines #E5E7EB de separación sí están presentes. | | #### W38 · Empleado crear / editar [Section titled “W38 · Empleado crear / editar”](#w38--empleado-crear--editar) **Ruta:** `/admin/pos/employees (formulario)` · **Figma:** [`124:6018`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=124-6018) · **Historia:** US-STAFF-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W38](/design/parity/figma/screens/figma-w38-empleado-edit.png)](/design/parity/figma/screens/figma-w38-empleado-edit.png "figma W38 — abrir a tamaño completo") | [![app W38](/design/parity/history/2026-07-24/screenshots/admin-pos-employees--dialog-nuevo-empleado.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-employees--dialog-nuevo-empleado.png "app W38 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Panel FIGMA:** página completa de edición de empleado. Header “Empleado / Datos, rol y acceso” con botón *Guardar* superior; dos tarjetas planas — **Datos personales** (Nombre completo, Correo, Teléfono) y **Acceso** (Rol, Tienda, PIN, Estado) — y una *sticky save bar* inferior (“Tienes cambios sin guardar” · Descartar · Guardar). - **Panel APP:** la captura no es esa página, sino un **drawer lateral derecho** montado sobre la lista de Empleados (se ven Filtros y las filas Ana López, Bruno Martínez, etc. detrás). Sus campos son **Sucursal, Usuario, Rol, PIN** más un switch **Activo**, con acciones *Cancelar / Guardar* al pie del drawer. - **Desajuste de composición:** página vs. drawer. No es solo el conocido debate diálogo-vs-página: además difiere el contenido — la app no tiene Nombre completo, Correo, Teléfono, Tienda ni Estado, y añade Sucursal, Usuario (select) y el toggle Activo que la página Figma no muestra. - **Fixes sistémicos (medidos en el drawer, sí llegaron):** azul de marca **#0052CC** en *Guardar*; switch activo **#0051CA**; *Cancelar* como botón secundario blanco con borde. El azul canónico está aplicado. - **Acción:** recapturar la **página** `/empleados/nuevo` (o `/[id]/editar`) que corresponde a este diseño Figma, en vez del drawer de alta rápida de la lista de Empleados. Sin esa captura no puede auditarse la paridad real de la pantalla W38. | | #### W39 · Invitar usuario [Section titled “W39 · Invitar usuario”](#w39--invitar-usuario) **Ruta:** `/admin/users` · **Figma:** [`131:8930`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=131-8930) · **Historia:** US-STAFF-2 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W39](/design/parity/figma/screens/figma-w39-invitar.png)](/design/parity/figma/screens/figma-w39-invitar.png "figma W39 — abrir a tamaño completo") | [![app W39](/design/parity/history/2026-07-24/screenshots/admin-users.png)](/design/parity/history/2026-07-24/screenshots/admin-users.png "app W39 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**El fix sistémico de color SÍ llegó a esta pantalla; el residuo es de composición/jerarquía propio de la lista de usuarios. (El diálogo de invitación que diseña el Figma se audita en W39b — aquí comparo la página lista.)- **Color:** Verificado OK — azul canónico #0052CC en botón Invitar usuario, Filtrar, Nuevo y pills Propietario/Administrador; pill gris #EBEBEB, lápiz #757575, papelera #EF4444, header de tabla #F7F9FC. Sin residuo de color. - **Layout:** El botón **Invitar usuario es un banner azul full-width** enorme, mientras Figma lo tiene como botón compacto en el header superior-derecho. Rehacer como acción de header. - **Jerarquía:** Tres primarios azules rellenos compiten en la misma vista (Invitar usuario + Filtrar + Nuevo). Figma tiene UN solo primario; “Filtros” es un botón secundario/outline. Degradar Filtrar/Nuevo a secundario. - **Contenido:** Falta el **page header** (título de página + subtítulo con conteo, p.ej. “8 empleados · 3 roles”) que abre la pantalla en Figma; la app salta del top bar directo al banner. - **Contenido:** Faltan los **filter chips** (Todos / Activos / Invitados) que Figma coloca sobre la tabla. - **Espaciado:** El bloque “Buscar” (label + campo vacío) queda desconectado a la izquierda mientras Filtrar/Nuevo viven al extremo derecho; Figma integra búsqueda y filtros en una sola toolbar. - **Estados:** La columna **Activo** usa círculos negros rellenos (check/x) — muy pesados y fuera del lenguaje; Figma usa status chips soft (Activo/Invitado/Inactivo). Migrar a StatusBadge sobre tokens \*-soft. - **Iconos:** Las filas no muestran **avatar/iniciales** en la celda Nombre; Figma sí (CO, SA, PA…). Añadir Avatar en tinte con iniciales primary. - **Contenido:** Columnas distintas (app: Nombre/Correo/Rol/Sucursal/Activo/Fecha/Acciones vs Figma: Empleado/Rol/Tienda/Ventas/Turnos/Estado) — decisión de modelo de datos; la sección “Invitaciones pendientes” es un añadido funcional válido no presente en Figma. | | #### W39b · Invitar usuario — diálogo [Section titled “W39b · Invitar usuario — diálogo”](#w39b--invitar-usuario--diálogo) **Ruta:** `/admin/users (diálogo)` · **Figma:** [`131:8930`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=131-8930) · **Historia:** US-STAFF-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W39b](/design/parity/figma/screens/figma-w39-invitar.png)](/design/parity/figma/screens/figma-w39-invitar.png "figma W39b — abrir a tamaño completo") | [![app W39b](/design/parity/history/2026-07-24/screenshots/admin-users--dialog-invitar.png)](/design/parity/history/2026-07-24/screenshots/admin-users--dialog-invitar.png "app W39b — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Los fixes sistémicos llegaron y los medí en esta captura: nada de regresiones de token.- **Color:** OK. Borde de campos #E5E7EB, fondo del botón deshabilitado #E5E7EB (exacto), subtítulo #6B7280, título \~#1F2933. El banner azul de fondo mide #002966 = #0052CC atenuado por el scrim → azul de marca canónico correcto. - **Bordes:** OK. Campos con hairline #E5E7EB y radio suave; diálogo con esquinas redondeadas (radio 16 aparente). - **Sombras:** OK. Scrim plano #7F7F7F sin halo de sombra dura en el borde del diálogo. - **Estados:** El botón **Enviar invitación** está deshabilitado (#E5E7EB / texto #9CA3AF) porque la captura es con el formulario vacío; en Figma aparece habilitado (azul, con “Cajero” cargado). Es la diferencia de estado por el momento de captura, no un defecto de estilo. - **Layout:** Residuo propio de la pantalla — la app usa una plantilla de diálogo distinta a la de Figma: título **centrado** (Figma lo alinea a la izquierda), **sin botón X** de cierre, y acciones como **dos botones a todo el ancho** en lugar de compactas alineadas a la derecha. El diálogo es bastante más grande y con más aire vertical que el compacto de Figma. - **Contenido:** La app muestra un **subtítulo** (“Se enviará un correo…”) en lugar de los “Texto de ayuda” bajo cada campo del diseño. El campo **Rol** es un **select con asterisco requerido `*`**; en Figma es un campo de texto con valor “Cajero” y sin marca de requerido. - **Tipografía / Espaciado:** Sin regresiones de token; el mayor aire vertical viene de la plantilla de diálogo genérica (padding de acciones amplio), no de un valor roto.Recomendación: alinear la plantilla del diálogo (título a la izquierda + X, acciones compactas a la derecha) y decidir a nivel producto helper-text vs subtítulo y select-vs-texto para Rol. | | #### W40 · Roles y permisos [Section titled “W40 · Roles y permisos”](#w40--roles-y-permisos) **Ruta:** `/admin/pos/employees/roles` · **Figma:** [`130:8533`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=130-8533) · **Historia:** US-STAFF-3 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W40](/design/parity/figma/screens/figma-w40-roles.png)](/design/parity/figma/screens/figma-w40-roles.png "figma W40 — abrir a tamaño completo") | [![app W40](/design/parity/history/2026-07-24/screenshots/admin-pos-employees-roles.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-employees-roles.png "app W40 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Misma pantalla (matriz de roles Cajero/Supervisor/Gerente). Los tokens sistémicos están correctos; el residuo es de composición y contenido faltante propio de esta vista.- **Color:** Fixes confirmados por muestreo — tab activo y underline #0052CC (450,228), icono de nav activo #0052CC, título #1F2933 (292,144), subtítulo \~#767D8B, cabecera de tabla #F7F9FC, borde/divisor #E5E7EB. Nada que corregir aquí. - **Contenido:** Falta la columna **Ámbito** del diseño (etiquetas Tienda/Comercio con color) — pérdida semántica real, no solo estética. - **Contenido:** Falta toda la **barra de herramientas** del diseño: buscador, chips de filtro (Todos/Cajero/Supervisor), botón **Filtros** y el select “Selecciona una opción”. - **Contenido:** Falta el **Alert informativo** “Matriz de solo lectura” (la app lo reduce al subtítulo “Solo lectura”) y la **paginación** (“12 permisos · 3 roles”, páginas 1…10). - **Contenido:** El botón primario **“Ver documentación”** del diseño se sustituyó por “Pantalla completa”; la app además introduce tabs Empleados/Roles que el Figma no plantea. - **Iconos/Estados:** Los checks de la app son verdes #32AE60 (854,328) frente a los checks neutros del Figma; el guion “—” (deshabilitado) coincide en gris. Diferencia de criterio, probablemente aceptable pero no fiel al diseño. - **Layout:** La app muestra más filas de capacidades (lista larga con scroll) en lugar de la tabla paginada de 5 permisos del diseño; densidad y jerarquía de tabla por lo demás consistentes con los tokens. | | ### REP — Reportes [Section titled “REP — Reportes”](#rep--reportes) Flujo W-11. Historias `US-REP-1` … `US-REP-6`. #### W55 · Reportes (overview) [Section titled “W55 · Reportes (overview)”](#w55--reportes-overview) **Ruta:** `/admin/pos/reports` · **Figma:** [`125:6381`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=125-6381) · **Historia:** US-REP-1 / US-REP-6 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W55](/design/parity/figma/screens/figma-w55-reportes-overview.png)](/design/parity/figma/screens/figma-w55-reportes-overview.png "figma W55 — abrir a tamaño completo") | [![app W55](/design/parity/history/2026-07-24/screenshots/admin-pos-reports.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-reports.png "app W55 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Misma pantalla (Reportes → Resumen): tabs Resumen/Ventas/Comisiones/Cortes Z, subtabs Ventas/Productos/Empleados, 4 KPI cards, gráfico “Ventas por hora” (línea), “Por método de pago” (barras) y tabla “Top productos”. Los fixes sistémicos llegaron a esta pantalla.- **Color:** Azul canónico verificado — barras y glifo del icono KPI en **#0052CC**; tint de tab activa **#E1E9F6** (≈ #E6EEFA); delta en verde. Correcto. - **Tipografía:** Label KPI en gris #6B7280, valor en #1F2933 oscuro. OK. - **Bordes:** Cards planas con hairline #E5E7EB, sin sombra. OK. - **Contenido (KPIs):** El set de métricas difiere del diseño — app muestra *Venta bruta / Venta neta / Transacciones / Ticket promedio*; Figma diseña *Ventas del periodo / Transacciones / Ticket promedio / Comisiones*. Falta la card **Comisiones**. Dato/config de reporte. - **Contenido (delta):** Figma muestra el % real (“▲ 8% vs periodo”); la app muestra el placeholder “▲ Periodo seleccionado” sin porcentaje en las 4 cards. - **Layout/Estados:** El control de rango de fechas usa **inputs `` nativos** (dígitos en fuente del sistema + glifo de calendario del navegador) dentro de un pill, en vez del dropdown único estilizado “1 Oct – 24 Oct, 2025” del diseño → rompe la paridad tipográfica del control. Culpable: componente de rango de fechas de Reportes. - **Layout:** La app agrega un selector “Sucursal Centro” y un botón outlined “Pantalla completa” que no están en el Figma (adiciones de la implementación, no defecto de estilo). - **Iconos:** El cuadro del icono KPI va en tint de marca con glifo #0052CC; en Figma se ve más neutro/gris. Diferencia mínima. | | #### W56 · Reporte de ventas [Section titled “W56 · Reporte de ventas”](#w56--reporte-de-ventas) **Ruta:** `/admin/pos/reports/daily-sales` · **Figma:** [`130:7959`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=130-7959) · **Historia:** US-REP-1 … US-REP-4 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W56](/design/parity/figma/screens/figma-w56-reporte-ventas.png)](/design/parity/figma/screens/figma-w56-reporte-ventas.png "figma W56 — abrir a tamaño completo") | [![app W56](/design/parity/history/2026-07-24/screenshots/admin-pos-reports-daily-sales.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-reports-daily-sales.png "app W56 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **FIGMA (diseño):** “Reporte de ventas · Desglose del periodo” — tabla de líneas de producto con tabs (Por producto / Por categoría / Por empleado / Por método), columnas Producto · SKU · Categoría · Ingresos · Unidades · Tendencia, selección por checkbox, botón **Exportar** y paginación (Mostrando 1–5 de 248). - **APP (implementación):** “Ventas del Día · Resumen diario del punto de venta” — otro reporte del módulo: filtros **Fecha** / **Sucursal**, tarjetas KPI (Venta Bruta $22,691.98 · Venta Neta $18,494.51 · Transacciones 179 · Ticket Promedio $103.32), gráfico de barras **Ventas por Hora** y sección **Productos Más Vendidos**. Incluye botón “Pantalla completa”. - **Ruta a corregir:** la captura apunta a la vista de dashboard diario, no al desglose de ventas por producto que diseña W56. Hay que recapturar la ruta correcta (tabla con tabs y Exportar) antes de poder auditar paridad de estilos. - No se listan diferencias de color/tipografía/bordes porque no hay pantalla equivalente que comparar. | | #### W57 · Comisiones [Section titled “W57 · Comisiones”](#w57--comisiones) **Ruta:** `— (sin ruta)` · **Figma:** [`130:8242`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=130-8242) · **Historia:** US-REP-5 / US-VAS-6 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W57](/design/parity/figma/screens/figma-w57-comisiones.png)](/design/parity/figma/screens/figma-w57-comisiones.png "figma W57 — abrir a tamaño completo") | [![app W57](/design/parity/history/2026-07-24/screenshots/playground/w57-comisiones.png)](/design/parity/history/2026-07-24/screenshots/playground/w57-comisiones.png "app W57 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Los fixes sistémicos llegaron a esta pantalla (medidos sobre la captura nueva). El único residuo real es el estilo del control de filtros.- **Color:** azul de marca correcto en todo — Exportar #0052CC, checkbox de fila y select-all #0052CC, tinte de avatar con inicial primary. Chips de estado en pills soft reales (verde #ECFDF5, ámbar, rojo) que coinciden con Figma. - **Estados:** tabs de filtro divergen. Figma → chip activo en tinte `#E6EEFA` + texto azul, y `Servicios`/`Tarjeta` como pills grises `#F0F4F8`. App → `Todas` como botón sólido `#0052CC` con texto blanco, y `Servicios`/`Tarjeta` como texto pelado sin pill. Es el hallazgo principal; probable ToggleButtonGroup/Tabs con variante equivocada. - **Estados:** el checkbox select-all de la cabecera muestra indeterminado (correcto por la selección parcial de 1 fila); Figma lo dibuja vacío. Matiz menor, la app es más correcta. - **Bordes:** cabecera de tabla `#F7F9FC`, fila seleccionada `#EBF1FB`, divisores hairline `#E5E7EB` — todo alineado. - **Contenido:** el dropdown derecho trunca a “Selecciona una …” vs. el placeholder completo en Figma; el ícono de editar va dentro de un icon-button con borde sutil vs. glifo pelado en Figma. Ambos menores. - **Layout:** 5 filas, mismos importes, “Mostrando 1-5 de 18” y paginación `1 2 3 … 10` idénticos. La app se captura solo-contenido (sin sidebar/AppBar) — diferencia de captura conocida, no de pantalla. | | #### W58 · Cortes de caja (Z) [Section titled “W58 · Cortes de caja (Z)”](#w58--cortes-de-caja-z) **Ruta:** `/admin/pos/reports/z-report` · **Figma:** [`123:4823`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-4823) · **Historia:** US-SHIFT-5 / US-REP-6 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W58](/design/parity/figma/screens/figma-w58-cortes-z.png)](/design/parity/figma/screens/figma-w58-cortes-z.png "figma W58 — abrir a tamaño completo") | [![app W58](/design/parity/history/2026-07-24/screenshots/admin-pos-reports-z-report.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-reports-z-report.png "app W58 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Panel Figma:** vista *Cortes de caja (Z)* — “Histórico de cortes Z”. Es una **lista/tabla** con columnas Corte · Cajero · Fecha · Vendido · Diferencia · Estado, chips de filtro (Todos / Cuadrados / Con diferencia), buscador, botón **Filtros**, botón **Exportar**, fila seleccionada con checkbox, StatusBadges (Cuadrado/Diferencia), avatares con iniciales y paginación “Mostrando 1–5 de 96”. - **Panel App:** vista de **detalle de un único corte Z** — cabecera con selector *Turno 2 · cerrado*, botones **Imprimir** y **Pantalla completa**, y una tarjeta con el desglose del reporte: **Ventas** (Venta Bruta/Neta, Impuestos, Descuentos), **Formas de Pago**, **Cancelaciones** y **Conciliación de Efectivo**. No hay tabla ni listado. - **Desajuste:** la captura corresponde a un paso distinto del flujo (el reporte individual que se abre *desde* la lista), no al histórico que diseña el Figma. Hay que recapturar la ruta de la lista de cortes Z (probablemente Reportes → Cortes de caja Z) para poder auditar paridad. - No listo diferencias de color/tipografía/espaciado porque no son la misma pantalla; cualquier medición sería engañosa. | | #### Reportes (ruta legacy) [Section titled “Reportes (ruta legacy)”](#reportes-ruta-legacy) **Ruta:** `/admin/reports` · **Historia:** US-REP-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — ruta legacy duplicada de W55; decidir si se retira | [![app Reportes (ruta legacy)](/design/parity/history/2026-07-24/screenshots/admin-reports.png)](/design/parity/history/2026-07-24/screenshots/admin-reports.png "app Reportes (ruta legacy) — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | ### VAS — Servicios de valor agregado [Section titled “VAS — Servicios de valor agregado”](#vas--servicios-de-valor-agregado) Flujo W-09. Historias `US-VAS-1` … `US-VAS-6`. #### W41 · Pago de servicios [Section titled “W41 · Pago de servicios”](#w41--pago-de-servicios) **Ruta:** `/admin/service-payments` · **Figma:** [`127:6816`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=127-6816) · **Historia:** US-VAS-3 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W41](/design/parity/figma/screens/figma-w41-servicios.png)](/design/parity/figma/screens/figma-w41-servicios.png "figma W41 — abrir a tamaño completo") | [![app W41](/design/parity/history/2026-07-24/screenshots/admin-service-payments.png)](/design/parity/history/2026-07-24/screenshots/admin-service-payments.png "app W41 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Misma pantalla (Pago de servicios), no es captura equivocada. Los arreglos de estilo llegaron bien; el residuo es de composición/contenido.- **Color:** Azul de marca correcto y medido: botón **Vaciar** y **Total** = `#0052CC` exacto; ícono JMAS en info `#3B82F6`; disabled del botón Continuar `#E5E7EB` bg + `#9CA3AF` texto. Todo conforme a la remediación. - **Estados:** El item de nav **Servicios** activo usa gris neutro `#EBEBEB` en lugar del tinte de marca `#E6EEFA` de Figma — el `palette.primary.light` no llegó al `selected` del ListItem del shell (sidebar compartido). Finding real aunque sea shell-level. - **Layout:** Falta por completo la **barra de pestañas** del diseño (Servicios / Tiempo aire / Pines / Transferir / Depósitos / Wallet). La app va directa al contenido de Servicios. - **Contenido:** La tarjeta izquierda difiere de paradigma: Figma = **Categoría** (chips Luz/Agua/Gas/Internet/Teléfono/TV) + fila de **Proveedor** único (CFE); la app = **grid de proveedores** (CFE, JMAS Juárez, Telmex, Gas Natural) con íconos de color, sin chips de categoría. Decisión de producto pendiente, pero es la mayor divergencia visible. - **Estados (captura):** La app está en estado vacío (Importe placeholder “Consultar adeudo”, Total `$0.00`, Continuar disabled) mientras Figma muestra estado lleno (`$688.00`, botón activo “Agregar a la venta”). Diferencia de estado + etiqueta de botón (**Continuar** vs **Agregar a la venta**). - **Bordes/Sombras:** Tarjetas y campos con hairline `#E5E7EB` y sin sombra — coincide. - **Tipografía:** Título oscuro y subtítulo `~#6B7280` — coincide; misma jerarquía de encabezado. | | #### W42 · Tiempo Aire (TAE) [Section titled “W42 · Tiempo Aire (TAE)”](#w42--tiempo-aire-tae) **Ruta:** `/admin/airtime` · **Figma:** [`127:7043`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=127-7043) · **Historia:** US-VAS-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W42](/design/parity/figma/screens/figma-w42-tae.png)](/design/parity/figma/screens/figma-w42-tae.png "figma W42 — abrir a tamaño completo") | [![app W42](/design/parity/history/2026-07-24/screenshots/admin-airtime.png)](/design/parity/history/2026-07-24/screenshots/admin-airtime.png "app W42 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**Ambos paneles son la ruta “Tiempo Aire”, pero muestran **pasos distintos del flujo**, así que no procede una auditoría de estilo lado a lado.- **Panel FIGMA:** página única de recarga ya compuesta — subtítulo “Recargas de saldo”, sección **Compañia** con proveedores como *chips cuadrados pequeños con avatar de letra* (Telcel, Movistar, AT\&T, Unefon, Bait), grilla **Monto** ($10–$500) con $50 seleccionado, y panel derecho **Captura** con “Número de teléfono”, “Total (incl. comisión) $51.00” y botón “Agregar a la venta”. - **Panel APP:** solo el **primer paso** del asistente — subtítulo “Telcel, AT\&T, Movistar y más”, encabezado “Selecciona el proveedor” con *tarjetas grandes con ícono de teléfono y subtítulo “México”* (Telcel, AT\&T, Movistar, Unefon, Virgin Mobile, OXXO Cel), y panel derecho **Resumen de compra** con Monto/Comisión/Total a cobrar en $0.00 y botón “Continuar” (deshabilitado). No hay grilla de montos ni captura de teléfono en esta vista — presumiblemente aparecen en un paso posterior.**Diferencias de fondo (no de estilo):** lista de proveedores distinta (Bait vs Virgin Mobile/OXXO Cel), proveedor como chip vs tarjeta, montos y captura presentes en Figma pero ausentes en la app, “Captura/Agregar a la venta” vs “Resumen de compra/Continuar”.**Fix sistémico verificado en esta captura:** el azul de marca del botón “Vaciar” mide **#0052CC** (canónico) — la remediación llegó a esta pantalla.Para re-auditar paridad hace falta recapturar la app en el mismo paso que diseña el Figma (proveedor + monto + captura en una sola vista) o alinear el diseño de Figma con el asistente por pasos de la app. | | #### W43 · Pines / tarjetas de regalo [Section titled “W43 · Pines / tarjetas de regalo”](#w43--pines--tarjetas-de-regalo) **Ruta:** `/admin/giftcards` · **Figma:** [`127:7220`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=127-7220) · **Historia:** US-VAS-2 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W43](/design/parity/figma/screens/figma-w43-pines.png)](/design/parity/figma/screens/figma-w43-pines.png "figma W43 — abrir a tamaño completo") | [![app W43](/design/parity/history/2026-07-24/screenshots/admin-giftcards.png)](/design/parity/history/2026-07-24/screenshots/admin-giftcards.png "app W43 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** — misma pantalla; los fixes sistémicos llegaron, el residuo es copy y estado de captura.- **Color:** Azul de marca correcto y medido: botón Vaciar #0052CC, texto Total (incl. comisión) #0052CC, deshabilitado bg #E5E7EB. Glifos de marca (Netflix #E50914, Spotify verde, GP azul) son tintes propios de cada brand, correctos. - **Estados:** La captura de la app está en estado vacío por defecto (ninguna marca seleccionada, Total $0.00, botón “Agregar a la venta” deshabilitado). El Figma muestra estado lleno (Netflix + $200 seleccionados). No es un defecto, son estados distintos del mismo flujo. - **Contenido:** La grilla “Denominación” ($100–$1,500) NO está ausente por bug: `VasSaleScreen.tsx:720` la renderiza solo tras elegir marca. Como la captura no tiene marca seleccionada, no aparece — verificar su estilo/selección requiere una captura con marca elegida. - **Tipografía/Contenido:** Copy divergente vs Figma — título “Tarjetas de Regalo” (app) vs “Tarjetas de regalo”; subtítulo “iTunes, Google Play, Spotify y más” vs “Pines digitales” del mock. Definido en `vas.i18n.ts:118-119`; parece decisión de producto, no defecto de estilo. - **Bordes/Sombras:** Tarjetas Marca y Captura planas con hairline, sin sombra pesada; coincide con la remediación. - **Layout:** Estructura de dos columnas (selector izquierda + panel Captura derecha) coincide con el Figma. La fila “Total” usa un contenedor gris suave #F9FAFB (el Figma lo deja sobre blanco) — diferencia mínima de énfasis, aceptable. | | #### W44 · Transferir saldo [Section titled “W44 · Transferir saldo”](#w44--transferir-saldo) **Ruta:** `/admin/transfers` · **Figma:** [`132:9984`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=132-9984) · **Historia:** US-VAS-5 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W44](/design/parity/figma/screens/figma-w44-transferir.png)](/design/parity/figma/screens/figma-w44-transferir.png "figma W44 — abrir a tamaño completo") | [![app W44](/design/parity/history/2026-07-24/screenshots/admin-transfers.png)](/design/parity/history/2026-07-24/screenshots/admin-transfers.png "app W44 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Misma pantalla: formulario “Transferir saldo” (Destinatario / Monto / Concepto / Saldo disponible / Continuar) en ambos paneles. La remediación sistémica llegó bien a esta vista.- **Color:** Azul de marca canónico confirmado — el valor del saldo mide `#0052CC` (sample 935,505). Texto de etiquetas/heading `#1F2933`. Todo correcto. - **Estados:** Único residuo real. En Figma el botón **Continuar** aparece como CTA azul primario (estado de reposo del diseño); en la app está **deshabilitado** — bg `#E5E7EB` + texto `#9CA3AF` (sample 760,577/595). Es coherente con el nuevo estilo `disabled` del sistema y con la regla de negocio (form vacío), pero la captura no muestra el CTA azul del diseño. Diferencia de estado, no de estilo. - **Bordes:** Campos con hairline `#E5E7EB` y relleno blanco, alto tipo `size small` — coincide con el fix de form fields. La tarjeta contenedora muestra una sombra suave en la esquina inferior en vez del hairline plano puro del diseño; residuo menor de elevación. - **Contenido:** El placeholder de Destinatario difiere: Figma usa el ejemplo “Comercio Norte”, la app “ID del comercio”. Wording, no estructura. - **Color (tinte):** La tarjeta “Saldo disponible” mide `#EBF1FB`, un pelín más saturada que el info-soft `#EFF6FF` de la spec; diferencia imperceptible. - **Layout / Tipografía:** Composición, orden de campos, jerarquía y densidad coinciden. Sin sombra en botones. Sin hallazgos.Nota: la geometría diálogo-vs-página no aplica aquí (ambas son página completa). | | #### W45 · Confirmar transferencia / comprobante [Section titled “W45 · Confirmar transferencia / comprobante”](#w45--confirmar-transferencia--comprobante) **Ruta:** `— (paso dentro de /admin/transfers)` · **Figma:** [`132:10125`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=132-10125) · **Historia:** US-VAS-5 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------- | | [![figma W45](/design/parity/figma/screens/figma-w45-confirmar-transferencia.png)](/design/parity/figma/screens/figma-w45-confirmar-transferencia.png "figma W45 — abrir a tamaño completo") | ⚠️ **Falta captura** — paso de confirmación, requiere captura del flujo completo | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | ### BAL — Wallet del comercio [Section titled “BAL — Wallet del comercio”](#bal--wallet-del-comercio) Historias `US-BAL-1` … `US-BAL-8`. #### W47 · Saldo / Wallet [Section titled “W47 · Saldo / Wallet”](#w47--saldo--wallet) **Ruta:** `/admin/wallet` · **Figma:** [`133:10306`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=133-10306) · **Historia:** US-BAL-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W47](/design/parity/figma/screens/figma-w47-wallet.png)](/design/parity/figma/screens/figma-w47-wallet.png "figma W47 — abrir a tamaño completo") | [![app W47](/design/parity/history/2026-07-24/screenshots/playground/w47-saldo-wallet.png)](/design/parity/history/2026-07-24/screenshots/playground/w47-saldo-wallet.png "app W47 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** — misma pantalla (Saldo del comercio, layout de dos columnas + lista de movimientos). Los fixes sistémicos llegaron y están medidos; el residuo es de contenido/composición, probablemente ligado al modelo de datos real.- **Color:** Azul de marca **#0052CC** confirmado por muestreo en las tres tarjetas de saldo (default/cargando/vacío). Botón Depositar en clara #EBF1FB con texto azul, igual que Figma. Página #F5F7FA, tarjetas blancas con hairline. Sin regresiones de tema. - **Contenido (principal):** Figma muestra **“Cuentas bancarias”** — lista de bancos vinculados (BBVA ••••4821, Santander ••••9930) con filas de navegación (chevron). La app la reemplaza por **“Resumen de la cuenta”** (Total abonado / Total retirado / Saldo pendiente). Es una sección/feature distinta; la de cuentas bancarias no está presente. Verificar con producto si es intencional. - **Estados:** La captura es un showcase con tres estados (con datos, cargando con skeleton #F0F4F8, y vacío “Aún no hay movimientos”). Todos correctos; Figma solo diagrama el estado con datos. - **Color (montos):** Los importes de la lista se pintan con **verde/rojo semántico** en la app; en Figma los montos (−$2,000.00 / +$10,000.00) son **neutros #1F2933**. Divergencia menor de criterio — decidir si el color semántico se adopta como estándar. - **Contenido (tarjeta de saldo):** La app agrega el enlace secundario **“Retirar · Próximamente”** bajo Depositar, ausente en Figma. - **Tipografía/Bordes:** Jerarquía y hairlines #E5E7EB consistentes; encabezado de resumen y filas con divisores correctos. | | #### W46 · Depósitos [Section titled “W46 · Depósitos”](#w46--depósitos) **Ruta:** `/admin/deposits` · **Figma:** [`123:4532`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-4532) · **Historia:** US-BAL-2 / US-VAS-5 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W46](/design/parity/figma/screens/figma-w46-depositos.png)](/design/parity/figma/screens/figma-w46-depositos.png "figma W46 — abrir a tamaño completo") | [![app W46](/design/parity/history/2026-07-24/screenshots/admin-deposits.png)](/design/parity/history/2026-07-24/screenshots/admin-deposits.png "app W46 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Misma pantalla; los fixes sistémicos llegaron. El residuo es propio de la tabla (columna de acciones extra y densidad).- **Color:** Azul de marca canónico verificado — #0052CC medido en botón “Nuevo depósito”, tab activo “Todos” y outline de “Filtros”. Badge “Pendiente” sobre tinte soft #FFFBEB (rol success/warning suave presente). Botón verde de acción #10B981 y “Rechazar” con outline rojo #F48282 (tokens de éxito/error, no comparables contra Figma porque el diseño no tiene esos botones). - **Estados:** Tratamiento de la columna Estado **inconsistente**: solo “Pendiente” es badge con tinte; “Disponible/Aplicado/Rechazado” salen como texto plano de color. Figma usa texto de color plano para todos. Unificar a StatusBadge sobre tokens \*-soft en la celda Estado del listado. - **Contenido:** La app agrega una columna **“Acciones”** con botones “Marcar disponible” / “Rechazar” para filas pendientes, ausente en Figma (que solo tiene el lápiz de edición). Es una función de producto; confirmar si debe vivir en la tabla o migrar a menú/acciones por fila para no romper la paridad. - **Layout:** La columna extra comprime el ancho y hace que el **Folio se parta en dos líneas** (“DEP-\n1042”) como link azul; Figma lo muestra en una línea, texto oscuro. Ajustar min-width de la columna Folio y/o `whiteSpace: nowrap`. - **Bordes:** Card plana con hairline, header de tabla #F7F9FC/#F5F7FA y botones-icono con borde #E5E7EB — coinciden con el diseño. - **Estados (fila):** Figma muestra la primera fila seleccionada (checkbox + realce); la app no. Diferencia de estado de captura, no de estilo. - Diferencias de datos (fechas “01 feb 2024” vs “24 Oct”, paginación “de 5” vs “de 84”, avatar en tinte azul vs iniciales neutras) no son defectos. | | #### W46b · Registrar depósito [Section titled “W46b · Registrar depósito”](#w46b--registrar-depósito) **Ruta:** `/admin/deposits/new` · **Figma:** [`123:4532`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-4532) · **Historia:** US-BAL-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W46b](/design/parity/figma/screens/figma-w46-depositos.png)](/design/parity/figma/screens/figma-w46-depositos.png "figma W46b — abrir a tamaño completo") | [![app W46b](/design/parity/history/2026-07-24/screenshots/admin-deposits-new.png)](/design/parity/history/2026-07-24/screenshots/admin-deposits-new.png "app W46b — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Panel FIGMA:** pantalla de *lista* de Depósitos (W46) — encabezado “Depósitos”, tabla con folios DEP-1038…1042 (columnas Folio/Tienda/Fecha/Monto/Método/Estado), tabs Todos/Confirmados/Pendientes, botón “+ Nuevo depósito”, buscador, filtros y paginación “Mostrando 1–5 de 84”. - **Panel APP:** pantalla de *formulario* “Registrar Depósito” (W46b) — alerta informativa, campos Canal de depósito, Banco de Depósito, Fecha, Número de Referencia, Monto Depositado (MXN), Cuenta de Origen y Notas Adicionales, columna “Comprobante” con carga de archivo, y barra de acciones Cancelar/Continuar. - La captura de la app SÍ es la pantalla esperada por el título “Registrar depósito”; lo que no corresponde es la imagen de referencia Figma pareada (`figma/screens/figma-w46-depositos.png` es la LISTA, no el formulario). Hace falta reexportar el frame del formulario de registro desde Figma para poder auditar la paridad. - Nota lateral (no bloqueante): el botón Continuar de la app mide **#0052CC**, confirmando que el azul canónico llegó a esta pantalla. No se listan más diferencias de estilo porque los paneles no son comparables. | | ### CLI — Clientes [Section titled “CLI — Clientes”](#cli--clientes) Flujo W-13 (v2). Historias `US-CLI-1`, `US-CLI-2`. #### Clientes — directorio [Section titled “Clientes — directorio”](#clientes--directorio) **Ruta:** `/admin/customers` · **Figma:** [`231:3`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=231-3) · **Historia:** US-CLI-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el nodo `231:3` existe en Figma pero no se exportó a `design/web/screens/` | [![app Clientes — directorio](/design/parity/history/2026-07-24/screenshots/admin-customers.png)](/design/parity/history/2026-07-24/screenshots/admin-customers.png "app Clientes — directorio — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Cliente — detalle [Section titled “Cliente — detalle”](#cliente--detalle) **Ruta:** `/admin/customers/[id]` · **Figma:** [`232:157`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=232-157) · **Historia:** US-CLI-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — nodo `232:157` sin exportar | [![app Cliente — detalle](/design/parity/history/2026-07-24/screenshots/admin-customers-id.png)](/design/parity/history/2026-07-24/screenshots/admin-customers-id.png "app Cliente — detalle — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Nuevo cliente — diálogo [Section titled “Nuevo cliente — diálogo”](#nuevo-cliente--diálogo) **Ruta:** `/admin/customers (diálogo)` · **Figma:** [`233:383`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=233-383) · **Historia:** US-CLI-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — nodo `233:383` sin exportar | [![app Nuevo cliente — diálogo](/design/parity/history/2026-07-24/screenshots/admin-customers--dialog-nuevo-cliente.png)](/design/parity/history/2026-07-24/screenshots/admin-customers--dialog-nuevo-cliente.png "app Nuevo cliente — diálogo — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Asignar cliente a la venta [Section titled “Asignar cliente a la venta”](#asignar-cliente-a-la-venta) **Ruta:** `— (modal en el POS)` · **Figma:** [`233:279`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=233-279) · **Historia:** US-CLI-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — nodo `233:279` sin exportar | ⚠️ **Falta captura** — modal, requiere captura desde el POS | | Notas | | | **⛔ Comparación no posible — faltan las dos imágenes.** No hay render de Figma exportado ni captura de la app, así que no hay nada que comparar. El motivo de cada gap está en las celdas de arriba. Primero hay que resolverlo (exportar el frame y/o construir la pantalla); recién ahí tiene sentido auditar paridad. | | #### Nuevo cliente (alta rápida) [Section titled “Nuevo cliente (alta rápida)”](#nuevo-cliente-alta-rápida) **Ruta:** `— (modal en el POS)` · **Figma:** [`233:383`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=233-383) · **Historia:** US-CLI-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — nodo `233:383` sin exportar | ⚠️ **Falta captura** — modal, requiere captura desde el POS | | Notas | | | **⛔ Comparación no posible — faltan las dos imágenes.** No hay render de Figma exportado ni captura de la app, así que no hay nada que comparar. El motivo de cada gap está en las celdas de arriba. Primero hay que resolverlo (exportar el frame y/o construir la pantalla); recién ahí tiene sentido auditar paridad. | | ### TERM / WL — Comercios, tiendas, terminales y white-label [Section titled “TERM / WL — Comercios, tiendas, terminales y white-label”](#term--wl--comercios-tiendas-terminales-y-white-label) Flujo W-10. Historias `US-TERM-1`, `US-TERM-2`, `US-WL-1`. #### W48 · Comercios [Section titled “W48 · Comercios”](#w48--comercios) **Ruta:** `/admin/merchants` · **Figma:** [`130:7665`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=130-7665) · **Historia:** US-TERM-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W48](/design/parity/figma/screens/figma-w48-comercios.png)](/design/parity/figma/screens/figma-w48-comercios.png "figma W48 — abrir a tamaño completo") | [![app W48](/design/parity/history/2026-07-24/screenshots/admin-merchants.png)](/design/parity/history/2026-07-24/screenshots/admin-merchants.png "app W48 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Misma pantalla (listado de Comercios). Los fixes de color sistémicos llegaron, pero queda residuo de composición y un badge sin tokenizar.- **Color:** Azul de marca correcto — botones medidos en #0052CC (1050,135 / 1250,135). Cabecera de tabla #F7F9FC y grises tokenizados presentes. ✓ - **Estados:** Los badges de Estado son píldoras de **relleno sólido saturado** con texto blanco (verde #10B981, ámbar #F59E0B, rojo Suspendido). El fix de StatusBadge sobre tokens `*-soft` NO alcanzó esta columna; deberían ser tinte suave, no fill pleno. Culpable probable: el badge del listado de merchants no usa `StatusBadge`/tokens soft. - **Espaciado/Bordes:** Botones sobredimensionados — miden \~70px de alto (y≈110→180), muy por encima del ramp canónico (\~52 large). Además hay **dos** botones grandes (Filtrar + Nuevo) donde Figma lleva un único primario compacto `+ Nuevo comercio`. - **Layout:** Falta el encabezado de página `Comercios` + subtítulo “Organizaciones que administras” (la app solo muestra el label “Buscar”). Faltan las sub-tabs `Comercios / Tiendas / Apariencia` y la fila de pills de filtro `Todos / Activos / Suspendidos` con botón `Filtros`. - **Contenido:** Faltan checkboxes de selección de fila, el icono de editar por fila y el estado de fila seleccionada (tinte azul) que sí trae el Figma; tampoco aparece el pie de paginación (“Mostrando 1–3 de 3” + pager). - **Contenido (dato/producto):** Columnas distintas — app: Nombre / Razón Social / Estado / Categoría / Balance / Fecha de Creación; Figma: Comercio / RFC / Tiendas / Saldo / Empleados / Estado. Probable decisión de modelo de datos, no de estilo; se señala pero no bloquea. Los chips de Categoría (outlined) están correctos. | | #### W49 · Perfil del comercio [Section titled “W49 · Perfil del comercio”](#w49--perfil-del-comercio) **Ruta:** `/admin/merchants (formulario)` · **Figma:** [`124:6197`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=124-6197) · **Historia:** US-TERM-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------- | | [![figma W49](/design/parity/figma/screens/figma-w49-perfil-comercio.png)](/design/parity/figma/screens/figma-w49-perfil-comercio.png "figma W49 — abrir a tamaño completo") | ⚠️ **Falta captura** — formulario, requiere captura con un comercio abierto | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W50 · Tiendas [Section titled “W50 · Tiendas”](#w50--tiendas) **Ruta:** `/admin/pos/stores` · **Figma:** [`122:4009`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-4009) · **Historia:** US-TERM-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W50](/design/parity/figma/screens/figma-w50-tiendas.png)](/design/parity/figma/screens/figma-w50-tiendas.png "figma W50 — abrir a tamaño completo") | [![app W50](/design/parity/history/2026-07-24/screenshots/admin-pos-stores.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-stores.png "app W50 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Es la misma ruta (Administración › Comercio y tiendas), y los fixes sistémicos SÍ llegaron a esta pantalla — el residuo es de contenido/composición, no de tokens.- **Color:** Correcto. Botón primario medido #0052CC; cabecera de tabla #F7F9FC; título de fila #1F2933; página #F5F7FA. Coincide con la remediación. - **Bordes:** Chips (MXN/USD) y contenedores con hairline limpio; sin sombras. OK. - **Contenido:** El modelo de tabla es otro. Figma lista **Tienda · Dirección · Terminales · Ventas mes · Empleados · Estado**; la app lista **Nombre · Moneda · Zona Horaria · Fecha de Creación**. Faltan por completo las métricas de negocio (terminales, ventas, empleados) y la acción de editar por fila. - **Layout:** Faltan controles del diseño: tabs **Todas/Abiertas/Cerradas**, buscador inline y el **Select** de la barra de filtros (la app solo tiene “Filtros”). Falta el subtítulo contador (“3 tiendas · 1 comercio”). Aparece un botón **Pantalla completa** que no está en Figma. - **Iconos:** Faltan los **avatares con iniciales** por fila (CO/SA/PA…) que el Figma usa como columna Tienda. - **Estados:** Faltan los **StatusBadge** de estado (Abierta verde / Sin turno rojo). La app no expresa el estado de la tienda. - **Tipografía:** Terminología divergente: la app dice **Sucursales / Crear Sucursal**; Figma dice **Tiendas / Nueva tienda**. - **Espaciado:** Paginación estilo MUI (“Filas por página · 1–6 de 6” con flechas) en vez de la paginación numerada (1 2 3 … 10) del diseño.La mayor parte de este residuo es decisión de producto/datos (qué columnas y controles expone la vista de tiendas), no de estilo; conviene alinear el modelo de tabla y los controles con el Figma. | | #### W51 · Tienda crear / editar [Section titled “W51 · Tienda crear / editar”](#w51--tienda-crear--editar) **Ruta:** `/admin/pos/stores (formulario)` · **Figma:** [`124:6370`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=124-6370) · **Historia:** US-TERM-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------- | | [![figma W51](/design/parity/figma/screens/figma-w51-tienda-edit.png)](/design/parity/figma/screens/figma-w51-tienda-edit.png "figma W51 — abrir a tamaño completo") | ⚠️ **Falta captura** — formulario, requiere captura con una tienda abierta | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W52 · Terminales [Section titled “W52 · Terminales”](#w52--terminales) **Ruta:** `/admin/pos/terminals` · **Figma:** [`123:4238`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-4238) · **Historia:** US-TERM-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W52](/design/parity/figma/screens/figma-w52-terminales.png)](/design/parity/figma/screens/figma-w52-terminales.png "figma W52 — abrir a tamaño completo") | [![app W52](/design/parity/history/2026-07-24/screenshots/admin-pos-terminals.png)](/design/parity/history/2026-07-24/screenshots/admin-pos-terminals.png "app W52 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Misma pantalla (listado **Terminales**), pero la composición de la app se aleja bastante del diseño. Los tokens sistémicos sí llegaron; el residuo es propio de esta pantalla (estructura y contenido).- **Color:** Verificado — botón `Nuevo` de la app = **#0052CC**, idéntico al `Vincular terminal` de Figma (#0052CC). Tinte de cabecera de tabla presente (\~#F7F9FC). El azul de marca canónico está bien cableado aquí. - **Contenido:** El modelo de datos difiere fuerte. Figma: `Terminal` (nombre POS-01 + avatar iniciales), `Tienda`, `Última conexión`, `Versión`, `Estado`. App: `Número de Serie` (+ modelo), `Tipo`, `Sucursal`, `Activa`, `Última Conexión`, `Vincular terminal`. Faltan avatares de iniciales por fila y el subtítulo **“5 terminales vinculadas”**. - **Layout:** Toolbar de filtros incompleta — la app solo renderiza `Filtros`. Faltan el input `Buscar...`, los tabs segmentados (**Todas / En línea / Fuera de línea**) y el select `Selecciona una opción`. - **Estados:** Falta la columna **Estado** con `StatusBadge` sobre tinte suave (Figma: En línea #ECFCF5 verde, Fuera de línea rojo, amarillo). La app la sustituye por un único icono check/x en `Activa`, perdiendo la semántica de color por estado. - **Layout (CTA):** Composición distinta — Figma pone `Vincular terminal` como CTA principal arriba; la app usa `Nuevo` arriba y mueve `Vincular terminal` a un botón outlined por fila. Además la app añade un botón extra `Pantalla completa` que no está en el diseño. - **Layout (paginación):** Figma usa paginador numerado (`Mostrando 1–5 de 5` + `1 2 3 … 10`, página activa en azul relleno). La app usa `TablePagination` de MUI (`Filas por página 7 · 1–5 de 5 · flechas`) — patrón diferente al diseñado. - **Acción de fila:** Figma tiene icono de edición por fila; la app tiene botón `Vincular terminal` outlined por fila.Nota: la disyuntiva componente-MUI-vs-diseño-custom (paginación, chips de estado) es en parte decisión de producto; el residuo accionable aquí es reponer el buscador + tabs de estado + columna Estado con badges de tinte suave y los avatares/subtítulo. | | #### W53 · Vincular terminal (pairing) [Section titled “W53 · Vincular terminal (pairing)”](#w53--vincular-terminal-pairing) **Ruta:** `— (sin ruta)` · **Figma:** [`133:10490`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=133-10490) · **Historia:** US-TERM-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------- | | [![figma W53](/design/parity/figma/screens/figma-w53-vincular-pairing.png)](/design/parity/figma/screens/figma-w53-vincular-pairing.png "figma W53 — abrir a tamaño completo") | ⚠️ **Falta captura** — no hay UI de pairing en web | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W54 · Apariencia white-label + recibo [Section titled “W54 · Apariencia white-label + recibo”](#w54--apariencia-white-label--recibo) **Ruta:** `/admin/merchants/appearance` · **Figma:** [`128:7274`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=128-7274) · **Historia:** US-WL-1 / US-PLAT-3 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W54](/design/parity/figma/screens/figma-w54-apariencia.png)](/design/parity/figma/screens/figma-w54-apariencia.png "figma W54 — abrir a tamaño completo") | [![app W54](/design/parity/history/2026-07-24/screenshots/admin-merchants-appearance.png)](/design/parity/history/2026-07-24/screenshots/admin-merchants-appearance.png "app W54 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Azul de marca canónico verificado en esta captura — Guardar/icon-buttons/swatch primario/banner ‘Tu Comercio’ = **#0052CC**, Cobrar #0354CC (≈ igual). Acento diverge por **dato de mock**: Figma azul #1A73E8 vs app teal #00A3A3; no es defecto de estilo. - **Tipografía:** $12,450.00 en #1F2933, ‘Saldo disponible’ en gris secundario. Sin residuo. - **Bordes:** Hairline de tarjetas #E5E7EB confirmado; campos y dropzone con borde correcto. - **Estados:** Switch ‘Tema oscuro disponible’ aparece OFF (gris, thumb blanco 18px) mientras Figma lo muestra ON (azul) — diferencia de estado/dato, ambos renderizan bien. - **Layout/Contenido:** Dropzone: la app suma icono ‘+’ y botón outlined ‘Seleccionar archivos’ (Figma sólo icono compartir); hint distinto (Figma ‘CSV, PNG o JPG · hasta 10 MB’ vs app ‘PNG, JPG, WEBP o GIF’). Composición menor. La app además incluye la sección **‘Recibo / Encabezado del recibo’** (parte ‘+ recibo’ del alcance) que el crop de Figma no muestra — contenido extra correcto. - **Sistémico (1 línea):** sidebar con chip ‘Tienda demo’ vs selector ‘Tienda Centro’ de Figma — patrón global, no propio de la pantalla.Los fixes de la remediación llegaron a esta pantalla; el residuo restante es sobre todo dato de mock (acento/switch) y composición menor del dropzone. | | ### ACCT — Cuenta y ajustes [Section titled “ACCT — Cuenta y ajustes”](#acct--cuenta-y-ajustes) Flujo W-12. Historias `US-ACCT-1` … `US-ACCT-3`. #### W59 · Mi cuenta [Section titled “W59 · Mi cuenta”](#w59--mi-cuenta) **Ruta:** `/admin/my-account` · **Figma:** [`128:7503`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=128-7503) · **Historia:** US-ACCT-1 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W59](/design/parity/figma/screens/figma-w59-mi-cuenta.png)](/design/parity/figma/screens/figma-w59-mi-cuenta.png "figma W59 — abrir a tamaño completo") | [![app W59](/design/parity/history/2026-07-24/screenshots/admin-my-account.png)](/design/parity/history/2026-07-24/screenshots/admin-my-account.png "app W59 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Misma pantalla (perfil “Mi cuenta”). Los fixes sistémicos están presentes y medidos en esta captura; el residuo es propio de contenido/estado, no de estilo.- **Color:** Verificado. Botón `#0052CC`, chip Supervisor texto `#0052CC` sobre tinte `#E6EEFA`, avatar tinte `#EEF4FF`, badge “Sesión actual” verde soft `#E8F5E9`. Todo canónico. - **Tipografía:** Título/labels `#212B35` ≈ `#1F2933` canónico. Sin observaciones. - **Bordes:** Hairline `#E5E7EB` en tarjetas y campos; campo Correo deshabilitado con relleno `#E5E7EB`. Correcto. - **Sombras:** Tarjetas y AppBar planas. Correcto. - **Iconos:** El botón Figma trae glifo “+” (“+ Guardar”); la app solo muestra “Guardar” sin icono. - **Contenido:** La app añade **“Sesiones activas”** (2 tarjetas Chrome/Safari con IP y fecha) y la línea **“Miembro desde: ene 2026”** en la tarjeta de perfil — ninguno existe en Figma. Es contenido de más, no falta. - **Estados:** Figma muestra los tres campos como placeholders vacíos; la app muestra Nombre/Teléfono con valor y **Correo deshabilitado** (gris). Diferencia de estado, no de token. - **Layout:** Las tarjetas de la app ocupan casi todo el ancho del contenido; en Figma la tarjeta “Datos personales” es más angosta. Diferencia de anchura contenida, aceptable. | | #### W60 · Seguridad [Section titled “W60 · Seguridad”](#w60--seguridad) **Ruta:** `/admin/settings (tab)` · **Figma:** [`128:7681`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=128-7681) · **Historia:** US-ACCT-1 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W60](/design/parity/figma/screens/figma-w60-seguridad.png)](/design/parity/figma/screens/figma-w60-seguridad.png "figma W60 — abrir a tamaño completo") | [![app W60](/design/parity/history/2026-07-24/screenshots/playground/w60-seguridad.png)](/design/parity/history/2026-07-24/screenshots/playground/w60-seguridad.png "app W60 — abrir a tamaño completo") | | Notas | | | **✅ Coincide**Re-auditoría post-remediación: la pantalla de Seguridad quedó alineada con el diseño. Los fixes sistémicos llegaron y se miden en esta captura.- **Color:** Azul canónico verificado — FAB `#0052CC`, track del Switch 2FA `#0052CC`, botones outline y enlace *Cerrar* con label/borde `#0052CC`. Verde de éxito *Esta sesión* `#10B981`, igual que Figma. Sin rastro del viejo `#0057B8`. - **Tipografía:** Títulos de tarjeta y filas (Acceso, Contraseña, PIN de cajero) en `#1F2933`; subtítulos/metadatos en gris secundario. Coincide con Figma. - **Bordes:** Tarjetas planas blancas con hairline `#E5E7EB`/`#EAECEF` sobre fondo `#F5F7FA`; divisor entre filas de sesiones presente. Correcto. - **Sombras:** Sin sombra en tarjetas ni FAB — coincide. - **Estados:** Switch 2FA activo (thumb blanco 18px sobre track azul), geometría 44×24 correcta. - **Layout:** Composición idéntica — header + subtítulo, tarjeta *Acceso* (Contraseña/2FA/PIN) y tarjeta *Sesiones activas* con las dos sesiones y sus acciones. FAB `+` arriba a la derecha. - **Contenido:** Todos los textos presentes y correctos.Único desajuste, esperado y no imputable a la pantalla: el panel APP muestra solo el área de contenido y recorta el sidebar de navegación y la AppBar (buscador, *Turno abierto*, *Sincronizado*, avatares) que sí figuran en Figma — es el encuadre del playground, no un residuo de estilo. Nada accionable en esta pantalla. | | #### Re-autenticación por contraseña [Section titled “Re-autenticación por contraseña”](#re-autenticación-por-contraseña) **Ruta:** `/admin/my-account (diálogo)` · **Historia:** US-ACCT-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — sin frame; es un requisito de Appwrite (cambiar teléfono exige contraseña) | [![app Re-autenticación por contraseña](/design/parity/history/2026-07-24/screenshots/admin-my-account--dialog-contrasena.png)](/design/parity/history/2026-07-24/screenshots/admin-my-account--dialog-contrasena.png "app Re-autenticación por contraseña — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### W61 · Ajustes generales [Section titled “W61 · Ajustes generales”](#w61--ajustes-generales) **Ruta:** `/admin/settings` · **Figma:** [`133:10622`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=133-10622) · **Historia:** US-ACCT-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W61](/design/parity/figma/screens/figma-w61-ajustes.png)](/design/parity/figma/screens/figma-w61-ajustes.png "figma W61 — abrir a tamaño completo") | [![app W61](/design/parity/history/2026-07-24/screenshots/admin-settings.png)](/design/parity/history/2026-07-24/screenshots/admin-settings.png "app W61 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Los fixes sistémicos llegaron a esta pantalla y están medidos, no supuestos.- **Color:** Switch encendido = **#0052CC**, thumb blanco; switch apagado con track gris y thumb blanco. Botón **Guardar** azul de marca plano. Todo coincide con la remediación. - **Tipografía:** Encabezados de tarjeta (‘Preferencias’, ‘Impresión’) y filas en #1F2933; subtítulos en gris. Pestaña activa ‘General’ en azul con subrayado. Coincide. - **Sombras:** Botón y tarjetas sin sombra dura; tarjetas planas con hairline. Coincide. - **Contenido:** APP **no renderiza el título de página ‘Ajustes’** (H1) que el Figma coloca encima de las pestañas — va directo de la AppBar a las tabs. Añadir el heading. - **Layout:** Las tarjetas de formulario en APP ocupan todo el ancho del contenido; en Figma están constreñidas a una columna de lectura angosta (\~455px), lo que separa mucho el control (switch/select) de su etiqueta. Considerar un `maxWidth` en el contenedor del formulario para acercar la densidad. - **Bordes:** Radio de esquina de las tarjetas en APP se ve algo más redondeado (\~16) que el casi recto del Figma; residuo menor. - **Estados:** Campo ‘Idioma’ → select ‘Español (MX)’ presente y alineado a la derecha; coincide en función, con leve diferencia de tratamiento vs. el campo de Figma. - **Iconos:** El botón **Guardar** aparece en APP y no se ve en el recorte de Figma (posible fuera de encuadre en el diseño); no es defecto de estilo.Nota: diferencias de la barra lateral (items/usuario, chip ‘Tienda demo’ vs dropdown ‘Tienda Centro’) son de la nav/mocks, no propias de esta pantalla. | | #### W62 · Bitácora / Audit log [Section titled “W62 · Bitácora / Audit log”](#w62--bitácora--audit-log) **Ruta:** `/admin/audit-log` · **Figma:** [`123:5114`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-5114) · **Historia:** US-ACCT-3 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W62](/design/parity/figma/screens/figma-w62-bitacora.png)](/design/parity/figma/screens/figma-w62-bitacora.png "figma W62 — abrir a tamaño completo") | [![app W62](/design/parity/history/2026-07-24/screenshots/admin-audit-log.png)](/design/parity/history/2026-07-24/screenshots/admin-audit-log.png "app W62 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Fixes sistémicos confirmados por muestreo — Exportar y paginación activa #0052CC, cabecera de tabla #F7F9FC, divisores #E5E7EB, avatar en tinte #E5EDFA con iniciales primary. Todo canónico. - **Estados:** Badges soft reales y correctos — Autorizado #ECFDF5 (verde), Rechazado #FEF0F0 (rojo), Creó producto #FFFBEB (ámbar). StatusBadge sobre tokens \*-soft OK. - **Layout:** La app omite por completo la columna de selección que el Figma diseña: checkbox de encabezado (select-all), checkbox por fila y la primera fila en estado seleccionado (checkbox azul + tinte de fila). Es la diferencia estructural principal — falta la selección masiva (`AuditLogTable`). - **Iconos:** El glifo de acción difiere: Figma usa un lápiz-en-recuadro contorneado en gris neutro; la app usa lápiz relleno en azul primary. Alinear glifo y tono. - **Contenido:** Menores — la columna Fecha del Figma prefija con em-dash “— Hoy 09:31” (app sin él); “Editó precio” muestra “Rechazado” en app vs “OK” en Figma (el texto de la app es más coherente). Conteo y páginas (8 / 2 vs 3,210 / 10) son datos mock, esperado. - Nota sistémica ya conocida: acciones de fila como ícono suelto en vez de botón etiquetado — decisión de producto pendiente, no se recuenta aquí. | | #### Bitácora — filtros [Section titled “Bitácora — filtros”](#bitácora--filtros) **Ruta:** `/admin/audit-log (diálogo)` · **Historia:** US-ACCT-3 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — organismo **Filter Bar**, sin frame de pantalla | [![app Bitácora — filtros](/design/parity/history/2026-07-24/screenshots/admin-audit-log--dialog-filtros.png)](/design/parity/history/2026-07-24/screenshots/admin-audit-log--dialog-filtros.png "app Bitácora — filtros — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | ### NOTIF — Notificaciones [Section titled “NOTIF — Notificaciones”](#notif--notificaciones) Flujo W-02. Historia `US-NOTIF-1`. #### W08 · Notificaciones [Section titled “W08 · Notificaciones”](#w08--notificaciones) **Ruta:** `/admin/notifications` · **Figma:** [`113:430`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=113-430) · **Historia:** US-NOTIF-1 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W08](/design/parity/figma/screens/figma-w08-notificaciones.png)](/design/parity/figma/screens/figma-w08-notificaciones.png "figma W08 — abrir a tamaño completo") | [![app W08](/design/parity/history/2026-07-24/screenshots/admin-notifications.png)](/design/parity/history/2026-07-24/screenshots/admin-notifications.png "app W08 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Botón primario medido en **#0052CC** exacto — el fix sistémico del azul llegó a esta pantalla. Tinte de fila no-leída **#E5EDFA** ≈ **#E6EEFA** de Figma; filas leídas en blanco; fondo de página **#F5F7FA** idéntico. Punto/dot de no-leído en azul de marca. Sin residuo de color. - **Iconos:** Residuo propio. La app envuelve **todos** los glifos en un badge circular de tinte suave; en Figma solo la fila de avatar (‘Nuevo empleado’) lleva círculo y las de venta/stock muestran el icono **desnudo** sobre la fila. Además hay glifos distintos: venta carrito→recibo, transferencia doble-flecha→flecha simple, corte de caja documento→reloj. Revisar el mapa icono↔tipo de notificación de la pantalla. - **Contenido:** Figma agrupa por **‘Hoy’ / ‘Ayer’** (dos secciones con divisor); la app colapsa todo en una sola sección **‘Anteriores’**. Es diferencia de datos/timestamps del mock, no de layout — verificar que la lógica de agrupación por día se ejerza con datos reales. - **Layout / Tipografía / Bordes:** Coinciden — título + subtítulo, tabs ‘Todas/No leídas/Ventas/Stock’ con activo azul y subrayado, filas con badge+título+subtítulo y hora alineada a la derecha, hairline #E5E7EB entre filas y tarjeta plana. Sin sombras espurias. - **Estados:** El destacado de no-leído (tinte + dot) se reproduce en las dos primeras filas tal como en Figma. | | #### W63 · Detalle de notificación [Section titled “W63 · Detalle de notificación”](#w63--detalle-de-notificación) **Ruta:** `/admin/notifications/[id]` · **Figma:** [`163:10716`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=163-10716) · **Historia:** US-NOTIF-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W63](/design/parity/figma/screens/figma-w63-detalle-notificacion.png)](/design/parity/figma/screens/figma-w63-detalle-notificacion.png "figma W63 — abrir a tamaño completo") | [![app W63](/design/parity/history/2026-07-24/screenshots/admin-notifications-id.png)](/design/parity/history/2026-07-24/screenshots/admin-notifications-id.png "app W63 — abrir a tamaño completo") | | Notas | | | **✅ Coincide**Misma pantalla (detalle de notificación) y remediación sistémica confirmada sobre la captura nueva. El contenido difiere solo por el mock (Figma muestra una alerta de inventario “Stock bajo”, la app una “Venta completada #1029”): es la misma plantilla, no un desajuste de paridad.- **Color:** Botón primario medido **#0052CC** (canónico). Rol “Sistema” y enlaces de acción en azul (#0857CD/#0052CC). Fondo de página #F5F7FA, tarjeta blanca. Todo dentro del sistema. - **Tipografía:** Título 600 oscuro, meta gris, cuerpo #1F2933; etiquetas de campo (Tienda/total) en gris con valor en oscuro. Jerarquía correcta. - **Bordes:** Tarjeta plana con hairline; breadcrumb con enlace azul + “Detalle” en oscuro. Botón outlined con borde azul y relleno blanco. - **Botones:** Ramp y estilo correctos — primario relleno + outlined con etiqueta 600, sin sombra; enlaces de texto (“Marcar como no leída”, “Descartar”) a la derecha, como en el diseño. - **Iconos:** Glifo de tipo (recibo) azul dentro de un avatar en tinte claro; en el diseño el rol Inventario usa un triángulo ámbar (por ser otro tipo de notificación). El contenedor del avatar se ve ligeramente ovalado en el crop — verificar que sea círculo perfecto. - **Layout / Contenido:** Estructura idéntica (breadcrumb → tarjeta con icono+título+meta+cuerpo → fila de meta → acciones). La fila de meta muestra 2 columnas vs 4 en Figma por el mock distinto; no es residuo de la pantalla. | | ### SEARCH — Búsqueda global [Section titled “SEARCH — Búsqueda global”](#search--búsqueda-global) Flujo W-02. Historia `US-SEARCH-1`. #### W09 · Búsqueda global (⌘K) [Section titled “W09 · Búsqueda global (⌘K)”](#w09--búsqueda-global-k) **Ruta:** `— (overlay global)` · **Figma:** [`113:651`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=113-651) · **Historia:** US-SEARCH-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W09](/design/parity/figma/screens/figma-w09-busqueda-global.png)](/design/parity/figma/screens/figma-w09-busqueda-global.png "figma W09 — abrir a tamaño completo") | [![app W09](/design/parity/history/2026-07-24/screenshots/admin-dashboard--palette-busqueda-global.png)](/design/parity/history/2026-07-24/screenshots/admin-dashboard--palette-busqueda-global.png "app W09 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** — misma pantalla (paleta global ⌘K). Figma muestra el estado con consulta escrita (“coca”) y resultados agrupados; la app captura el estado vacío (“Escribe al menos 2 caracteres para buscar”), así que las filas de resultado, section-headers y el tint de fila activa no son auditables aquí.- **Color:** Fix sistémico CONFIRMADO en esta captura — el anillo de foco del input mide **#0052CC** exacto (canónico). Fondo del diálogo #FFFFFF, scrim de dim gris detrás correcto. - **Bordes:** El divisor hairline bajo el input y el radio del diálogo están presentes. OK. - **Layout / Estados:** Residuo propio de la pantalla — el buscador se renderiza como **TextField MUI con caja redondeada y anillo de foco azul de 2px**, mientras que Figma lo diseña como **barra de comando sin borde** (solo icono + texto con divisor a todo el ancho debajo, sin caja ni anillo). Alinear el input de la paleta al estilo command-bar. - **Contenido:** Falta el pill **“ESC”** arriba a la derecha del input que sí trae Figma. - **Espaciado:** Diálogo anclado más alto (pisa el banner de promo) y algo más angosto que el diseño, que lo centra y le da más ancho. Ajuste menor de posición/ancho. - **Nota de cobertura:** para cerrar la paridad hace falta recapturar el estado poblado (con query) y validar ahí el tint de fila activa (#E6EEFA), los tiles de icono en tinte y la tipografía de títulos/subtítulos. | | ### PLAT — Administración de plataforma [Section titled “PLAT — Administración de plataforma”](#plat--administración-de-plataforma) Historias `US-PLAT-1` … `US-PLAT-3`. #### W07 · Dashboard [Section titled “W07 · Dashboard”](#w07--dashboard) **Ruta:** `/admin/dashboard` · **Figma:** [`106:2`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=106-2) · **Historia:** US-PLAT-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W07](/design/parity/figma/screens/figma-w07-dashboard.png)](/design/parity/figma/screens/figma-w07-dashboard.png "figma W07 — abrir a tamaño completo") | [![app W07](/design/parity/history/2026-07-24/screenshots/admin-dashboard.png)](/design/parity/history/2026-07-24/screenshots/admin-dashboard.png "app W07 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Figma (diseño):** dashboard analítico de retail/POS — saludo “Hola, Victoria 👋”, selector de fecha “Hoy”, 4 tarjetas KPI (Ventas hoy $8,450.00, Transacciones 128, Ticket promedio $66.01, Productos vendidos 342 con deltas vs ayer), gráfico de línea “Ventas por hora”, donut “Categorías” y tabla “Ventas recientes” con checkboxes, SKU, precio, stock y badges de estado. - **App (implementación):** dashboard de pagos/billetera — banner promocional azul “0% Comisión en Pagos”, sección “Acciones Rápidas” (Tiempo Aire, Tarjetas de Regalo, Pago de Servicios, Transferir saldo), tarjeta lateral “Saldo Disponible $24,567.50 MXN” con Retirar Saldo, “Resumen Semanal” y “Más Opciones”. - Comparten únicamente el sidebar (misma app, ítem “Inicio” activo, mismos grupos OPERACIÓN/CATÁLOGO/EQUIPO/FINANZAS/ADMINISTRACIÓN). El área de contenido no tiene correspondencia alguna. - No listo diferencias de color/tipografía/espaciado: al ser pantallas distintas, cualquier comparación de estilo sería ruido. Se necesita recapturar la ruta correcta del dashboard analítico (o confirmar qué pantalla debe vivir en “Inicio”). | | #### Actividad reciente [Section titled “Actividad reciente”](#actividad-reciente) **Ruta:** `/admin/recent-activities` · **Historia:** US-PLAT-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — bloque del dashboard W07 convertido en ruta; sin frame propio | [![app Actividad reciente](/design/parity/history/2026-07-24/screenshots/admin-recent-activities.png)](/design/parity/history/2026-07-24/screenshots/admin-recent-activities.png "app Actividad reciente — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Tickets de soporte [Section titled “Tickets de soporte”](#tickets-de-soporte) **Ruta:** `/admin/tickets` · **Historia:** US-PLAT-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — pantalla legacy sin frame | [![app Tickets de soporte](/design/parity/history/2026-07-24/screenshots/admin-tickets.png)](/design/parity/history/2026-07-24/screenshots/admin-tickets.png "app Tickets de soporte — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | ### Sin historia de usuario — rutas técnicas y de desarrollo [Section titled “Sin historia de usuario — rutas técnicas y de desarrollo”](#sin-historia-de-usuario--rutas-técnicas-y-de-desarrollo) Rutas capturadas que no pertenecen a ninguna historia: raíz, playground y preview del design system. #### Playground del design system [Section titled “Playground del design system”](#playground-del-design-system) **Ruta:** `/admin/playground` · **Historia:** — (herramienta de parity) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — herramienta interna; es la superficie desde la que se capturan los átomos/moléculas/organismos de abajo | [![app Playground del design system](/design/parity/history/2026-07-24/screenshots/admin-playground.png)](/design/parity/history/2026-07-24/screenshots/admin-playground.png "app Playground del design system — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Playground del design system (ruta pública) [Section titled “Playground del design system (ruta pública)”](#playground-del-design-system-ruta-pública) **Ruta:** `/playground` · **Historia:** — (herramienta de parity) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — herramienta interna | [![app Playground del design system (ruta pública)](/design/parity/history/2026-07-24/screenshots/playground.png)](/design/parity/history/2026-07-24/screenshots/playground.png "app Playground del design system (ruta pública) — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### UI preview [Section titled “UI preview”](#ui-preview) **Ruta:** `/ui-preview` · **Historia:** — (herramienta de parity) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — herramienta interna | [![app UI preview](/design/parity/history/2026-07-24/screenshots/ui-preview.png)](/design/parity/history/2026-07-24/screenshots/ui-preview.png "app UI preview — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | *** ## Design system [Section titled “Design system”](#design-system) Los renders de Figma vienen de `design/web/{atomic,molecules,organisms}/`. Las capturas reales **todavía no existen**: la superficie para tomarlas es `/admin/playground` (dev-only, mock data), que renderiza cada componente del DS en todos sus estados. Ese es el mismo enfoque bottom-up que se usó en móvil (átomos → moléculas → organismos → pantallas) antes de auditar las pantallas. ### Átomos [Section titled “Átomos”](#átomos) Código en `src/ui/atoms/**`. #### Avatar [Section titled “Avatar”](#avatar) **Código:** `src/ui/atoms/Avatar/index.tsx` · **Figma:** [`45:90`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=45-90) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Avatar](/design/parity/figma/atoms/figma-avatar.png)](/design/parity/figma/atoms/figma-avatar.png "figma Avatar — abrir a tamaño completo") | [![app Avatar](/design/parity/history/2026-07-24/screenshots/playground/avatar.png)](/design/parity/history/2026-07-24/screenshots/playground/avatar.png "app Avatar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Tinte del avatar de iniciales **#E6EEFA** medido en la app — idéntico al de Figma (#E6EEFA). Iniciales “JP” en azul de marca (núcleo \~#1761D1→#0052CC sobre el tinte); coincide con las iniciales primary del diseño. - **Contenido:** El avatar “con imagen” resuelve con círculo sólido **#0052CC** (azul canónico) + glifo de persona blanco — el fix de brand-blue llegó a esta pantalla. - **Estados:** La rampa de tamaños S/M/L está presente y crece proporcionalmente, como en Figma (que muestra tres tamaños de iniciales). - **Tipografía:** Iniciales centradas, peso alto, legibles en los tres tamaños; consistente con el diseño. - **Layout:** Figma solo muestra las tres variantes de iniciales; la app añade “con imagen” y “fallback” como showcase — no es un desajuste, es superset del catálogo.Residuo: ninguno accionable. El avatar “con imagen” muestra placeholder de glifo en lugar de foto real, pero es contenido de showcase, no defecto de estilo. | | #### Banner / Inline Alert [Section titled “Banner / Inline Alert”](#banner--inline-alert) **Código:** `src/ui/atoms/Banner/index.tsx` | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Banner / Inline Alert](/design/parity/figma/atoms/figma-banner.png)](/design/parity/figma/atoms/figma-banner.png "figma Banner / Inline Alert — abrir a tamaño completo") | [![app Banner / Inline Alert](/design/parity/history/2026-07-24/screenshots/playground/banner.png)](/design/parity/history/2026-07-24/screenshots/playground/banner.png "app Banner / Inline Alert — abrir a tamaño completo") | | Notas | | | **✅ Coincide**Misma pantalla: los 4 banners (info / success / warning / danger) están en el mismo orden y con el mismo contenido en ambos paneles. Los fixes sistémicos de tinte llegaron a esta atom y están medidos.- **Color:** Tintes soft idénticos al hex — info `#EFF6FF`, success `#ECFDF5` (Figma `#ECFCF5`), warning `#FFFBEB`, danger `#FEF2F2`. Coinciden con Figma píxel a píxel. - **Iconos:** Rol/color por variante correcto: info azul `#3B82F6` (círculo relleno), success verde `#25BF8C`, warning ámbar `#F5A720`, danger rojo `#F05454/#EF4444`. Coincide con la paleta canónica. - **Tipografía:** Jerarquía correcta — título en el color del rol (peso semibold) + cuerpo en texto oscuro. Igual que Figma. - **Layout:** Estructura idéntica — icono a la izquierda, título+cuerpo, cierre arriba a la derecha. Estructura y densidad concuerdan (escalas distintas, no comparo px absolutos). - **Residuo (menor, no bloqueante):** El glifo de cierre difiere — Figma usa un icono estilo tecla “borrar/backspace” en gris redondeado; la app usa una `×` simple. Diferencia de icono trivial, misma posición/función. - **Nota de captura:** Las etiquetas en versalitas (INFO/SUCCESS/WARNING/DANGER) son del showcase/playground, no de la atom — no es defecto del componente. | | #### Button [Section titled “Button”](#button) **Código:** `src/ui/atoms/Button/index.tsx` · **Figma:** [`21:50`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=21-50) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Button](/design/parity/figma/atoms/figma-button.png)](/design/parity/figma/atoms/figma-button.png "figma Button — abrir a tamaño completo") | [![app Button](/design/parity/history/2026-07-24/screenshots/playground/button.png)](/design/parity/history/2026-07-24/screenshots/playground/button.png "app Button — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Filled `#0052CC` (default), `#003D99` (hover y pressed) — medidos idénticos en Figma y app. Tonal `#E6EEFA` con texto `#0052CC`. Disabled `#E5E7EB` en ambos. Todo cableado por `palette.primary`. - **Bordes:** Outlined con borde azul 1px sólido; tarjetas/estados con radio redondeado consistente entre ambos. - **Iconos:** El showcase usa un ícono de caja registradora (POS) donde Figma muestra un carrito — diferencia de ejemplo del showcase, no defecto. - **Estados:** Las 4 variantes (Filled/Tonal/Outlined/Text) cubren default/hover/pressed/disabled/loading; el spinner de loading aparece en todas. Figma organiza loading como fila-variante y la app como columna-estado — decisión de layout del showcase, cobertura funcional equivalente. - **Layout:** Tonal pressed mide levemente más azulado en la app (`#BAD0EF`) que en Figma (tinte gris `#E5E7EB`); residuo menor de tinte de estado, dentro de tolerancia. | | #### Checkbox [Section titled “Checkbox”](#checkbox) **Código:** `src/ui/atoms/Checkbox/index.tsx` · **Figma:** [`90:8`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=90-8) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Checkbox](/design/parity/figma/atoms/figma-checkbox.png)](/design/parity/figma/atoms/figma-checkbox.png "figma Checkbox — abrir a tamaño completo") | [![app Checkbox](/design/parity/history/2026-07-24/screenshots/playground/checkbox-radio-switch.png)](/design/parity/history/2026-07-24/screenshots/playground/checkbox-radio-switch.png "app Checkbox — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Azul de marca canónico confirmado — el glifo indeterminate mide **#0052CC** exacto; el checked comparte el mismo relleno (muestreo #548BDD por la mezcla con el check blanco central). Figma también dominado por #0052CC. Sin residuo de azul. - **Bordes:** Unchecked con trazo hairline **#D5D9DE** (spec \~#D1D5DB) — variación despreciable. Geometría de esquina redondeada custom del checkbox presente en los tres estados, igual que Figma. - **Iconos:** Check blanco y guion (indeterminate) blancos, centrados, coinciden con el diseño. - **Estados:** La app añade disabled (relleno atenuado + texto #9CA3AF) y error (texto rojo de ayuda) que Figma no muestra — es un showcase superset; los tres estados canónicos del átomo casan. - **Layout:** Figma solo diseña los 3 glifos del checkbox; el panel app los expone dentro del playground junto a Radio/Switch. El átomo en sí coincide; sin acción pendiente propia de esta pantalla. | | #### Chip / Category Chip [Section titled “Chip / Category Chip”](#chip--category-chip) **Código:** `src/ui/atoms/Chip/index.tsx` · **Figma:** [`35:31`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=35-31) | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Chip / Category Chip](/design/parity/figma/atoms/figma-chip.png)](/design/parity/figma/atoms/figma-chip.png "figma Chip / Category Chip — abrir a tamaño completo") | [![app Chip / Category Chip](/design/parity/history/2026-07-24/screenshots/playground/chip.png)](/design/parity/history/2026-07-24/screenshots/playground/chip.png "app Chip / Category Chip — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color (activo):** El chip *activo* está clavado — bg tinte de marca **#E6EEFA** y etiqueta **#0052CC** (azul canónico), idéntico a Figma. Fix sistémico verificado en esta captura. - **Color (inactivo):** bg medido **\~#EBEDF0** (gris neutro MUI) vs **#F0F4F8** de Figma (gris levemente más frío); diferencia de tinte imperceptible. Texto **#6B7280** (secondary), correcto. - **Bordes:** Píldora totalmente redondeada, filled sin borde en ambos estados — coincide. - **Estados:** Se representan los dos estados que diseña Figma (inactivo/activo). La app los organiza con rótulos INACTIVO/ACTIVO; Figma los muestra lado a lado — mismo contenido y jerarquía. - **Tipografía:** Etiqueta “Categoría”, peso medio; consistente entre paneles. - **Sombras:** Plano en ambos — sin residuo.Sin hallazgos accionables propios de la pantalla; el matiz del inactivo es opcional (alinear tinte a #F0F4F8 si se busca paridad exacta, pero es cosmético). | | #### Date-range Picker [Section titled “Date-range Picker”](#date-range-picker) **Código:** `src/ui/atoms/DateRangePicker/index.tsx` · **Figma:** [`103:116`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=103-116) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Date-range Picker](/design/parity/figma/atoms/figma-date-range-picker.png)](/design/parity/figma/atoms/figma-date-range-picker.png "figma Date-range Picker — abrir a tamaño completo") | [![app Date-range Picker](/design/parity/history/2026-07-24/screenshots/playground/date-range-picker.png)](/design/parity/history/2026-07-24/screenshots/playground/date-range-picker.png "app Date-range Picker — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Coincide. Campos blancos, texto de valor #1F2933, hairline medido #E5E7EB/#E6E8EC, icono de adorno de calendario #9CA3AF y estado disabled #E5E7EB — todos alineados con la paleta remediada. - **Tipografía:** Coincide. Label estático arriba (Desde/Hasta \~#666D75 ≈ secundario), secciones CLOSED/OPEN/DISABLED en gris; valores en dark. La única diferencia textual es el uso de ejemplo, no defecto. - **Bordes:** Coincide. Campos blanco + hairline redondeado, alto \~40. El pill de Figma usa radio mayor (pastilla), pero es consecuencia del patrón, no un token distinto. - **Iconos:** Coincide. Icono de calendario gris #9CA3AF (adorno); en Figma también aparece el chevron gris #9CA3AF que la app no muestra (ver Layout). - **Estados:** Coincide. Se muestran closed / open (placeholder DD/MM/YYYY) / disabled con el patrón de deshabilitado correcto (#E5E7EB + texto atenuado). - **Layout:** Residuo real. Figma define **un único control combinado**: `[icono calendario] "1 Oct – 24 Oct, 2025" [chevron]` que despliega el calendario. La app lo implementa como **dos inputs de fecha nativos separados** (Desde/Hasta) sin pill combinado, sin resumen de rango en una sola línea y sin chevron de despliegue. Es una diferencia de patrón/estructura, no de tokens. Verificar el componente de showcase en `src/ui` (playground DateRangePicker) si se desea converger al pill único de Figma. | | #### FAB [Section titled “FAB”](#fab) **Código:** `src/ui/atoms/Fab/index.tsx` | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma FAB](/design/parity/figma/atoms/figma-fab.png)](/design/parity/figma/atoms/figma-fab.png "figma FAB — abrir a tamaño completo") | [![app FAB](/design/parity/history/2026-07-24/screenshots/playground/fab.png)](/design/parity/history/2026-07-24/screenshots/playground/fab.png "app FAB — abrir a tamaño completo") | | Notas | | | **✅ Coincide**El playground muestra el mismo átomo que diseña Figma: el FAB en sus dos estados (collapsed con glifo `+`, expanded con glifo `×`), que son exactamente los dos círculos del panel Figma.- **Color:** medido #0052CC en los dos círculos Figma y en ambos FAB de la app (collapsed 985,278 → #0253CC; expanded × 1204,300 → #0152CB). Azul de marca canónico confirmado post-remediación; el mini-FAB de acción también en #0047C8/#0052CC. - **Iconos:** glifo blanco (`+` / `×`) centrado, proporción correcta (\~20px); coincide con el spec. - **Bordes:** círculo perfecto, sin borde; correcto. - **Sombras:** el FAB lleva una sombra suave que se corresponde con el glow difuso del diseño Figma; sin residuo. - **Estados:** collapsed→expanded cablea la transición `+`→`×` tal como muestran los dos estados del diseño. - **Contenido:** el backdrop gris y las mini-acciones (Cobrar / Buscar producto / Imprimir) del estado expanded son composición del SpeedDial, no aparecen en el crop atómico de Figma; no es defecto de paridad.Sin residuo propio de la pantalla que remediar. | | #### File Upload [Section titled “File Upload”](#file-upload) **Código:** `src/ui/atoms/FileUpload/index.tsx` · **Figma:** [`103:126`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=103-126) | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma File Upload](/design/parity/figma/atoms/figma-file-upload.png)](/design/parity/figma/atoms/figma-file-upload.png "figma File Upload — abrir a tamaño completo") | [![app File Upload](/design/parity/history/2026-07-24/screenshots/playground/file-upload.png)](/design/parity/history/2026-07-24/screenshots/playground/file-upload.png "app File Upload — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Contenedor circular del icono en `#E6EEFA` (palette.primary.light) medido igual en ambos paneles; glifo en azul de marca `#0052CC` (Figma y app). `src/ui/atoms/FileUpload/index.tsx` usa `primary.light`/`primary.main`. Coincide. - **Tipografía:** Título en negrita oscuro (`subtitle2`, text.primary) y subtítulo/hint en gris tenue (`caption`, text.disabled `#9CA3AF`) según diseño. Coincide. - **Bordes:** Zona con borde `2px dashed` sobre `theme.palette.divider` (#E5E7EB); estado ERROR conmuta a `error.main` `#EF4444` medido. Botón outlined 1px sólido de marca. Coincide. - **Iconos:** El glifo difiere (app usa `Plus`, Figma un icono de compartir), pero el contenedor tintado + color de marca coinciden — icono de adorno, no defecto. Papelera del chip en `#0052CC` como adorno. - **Layout:** El chip de archivo es plano con hairline (`divider`) y papelera a la derecha, como el patrón de tarjeta plana. El showcase expone estados EMPTY/HOVER-DRAG/WITH-FILE/ERROR. - **Estados:** El subtítulo de formatos/tamaño (“CSV, PNG o JPG · hasta 10 MB”) existe vía prop `hint` (text.disabled); el showcase simplemente no lo pasa — decisión del showcase, el componente lo soporta. Sin residuo real. | | #### Form Field [Section titled “Form Field”](#form-field) **Código:** `src/ui/atoms/FormField/index.tsx` · **Figma:** [`91:23`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=91-23) | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Form Field](/design/parity/figma/atoms/figma-form-field.png)](/design/parity/figma/atoms/figma-form-field.png "figma Form Field — abrir a tamaño completo") | [![app Form Field](/design/parity/history/2026-07-24/screenshots/playground/form-field.png)](/design/parity/history/2026-07-24/screenshots/playground/form-field.png "app Form Field — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Borde focus medido `#0052CC` en app = azul de marca de Figma. Relleno disabled `#E5E7EB` idéntico. Estado error en rojo en ambos (Figma ≈`#EE3030`, app `#F05252` — misma familia, sin residuo real). Ayuda gris \~`#6D7482` (≈`#6B7280`). - **Tipografía:** Label estático en negrita arriba del campo, ayuda y mensaje de error en tamaño menor, coincide en ambos paneles. - **Bordes:** Campos blancos con hairline y esquinas redondeadas en los cuatro estados; anillo/borde de focus y borde de error consistentes con Figma. - **Estados:** Default / focus (borde azul) / error (borde+texto rojo) / disabled (relleno gris `#E5E7EB` + texto atenuado) todos presentes y correctos. - **Layout:** El showcase apila los estados en vertical con etiquetas DEFAULT/FOCUS/ERROR/DISABLED mientras Figma los muestra en fila horizontal — decisión del showcase, no defecto. Estructura interna del campo idéntica. | | #### Icon [Section titled “Icon”](#icon) **Código:** `src/ui/atoms/Icon/index.tsx` | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Icon](/design/parity/figma/atoms/figma-icon.png)](/design/parity/figma/atoms/figma-icon.png "figma Icon — abrir a tamaño completo") | [![app Icon](/design/parity/history/2026-07-24/screenshots/playground/icon.png)](/design/parity/history/2026-07-24/screenshots/playground/icon.png "app Icon — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Iconos medidos en `#0052CC` exacto en ambos paneles (Plus, Gear, Info, muestras de tamaño). Azul de marca canónico correcto. - **Iconos:** FontAwesome Solid, fill-based, ligados a `Color`, nombres = registro `TerminalesIcons` — paridad con Figma y con Kotlin/`react-icons/fa6`. - **Espaciado:** Grilla de registro con celdas uniformes y etiquetas centradas debajo; escala de tamaños sm 16 / md 20 / lg 24 / xl 32 / xxl 40 legible. - **Estados:** Swatches de la sección Colors mapean a tokens canónicos medidos: primary `#0052CC`, success `#10B981`, warning `#F59E0B`, error `#EF4444`, text `#1F2933`. - **Layout:** Las secciones Registry / Sizes / Colors son estructura del showcase de `/playground`; el set de iconos de ejemplo distinto respecto a Figma es decisión del showcase, no residuo. - Sin residuo real del componente. | | #### Icon Button [Section titled “Icon Button”](#icon-button) **Código:** `src/ui/atoms/IconButton/index.tsx` · **Figma:** [`34:73`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=34-73) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Icon Button](/design/parity/figma/atoms/figma-icon-button.png)](/design/parity/figma/atoms/figma-icon-button.png "figma Icon Button — abrir a tamaño completo") | [![app Icon Button](/design/parity/history/2026-07-24/screenshots/playground/icon-button.png)](/design/parity/history/2026-07-24/screenshots/playground/icon-button.png "app Icon Button — abrir a tamaño completo") | | Notas | | | **✅ Coincide**Ambos paneles muestran el mismo átomo (IconButton con glifo de tienda) en sus tres variantes × tres estados. El Figma los presenta en una fila continua de 9 tiles; la app los organiza en una matriz etiquetada **Standard / Outlined / Ghost × Default / Pressed / Disabled** — misma cobertura semántica, solo cambia el andamiaje del showcase (no es defecto del componente).- **Color:** Todos los tokens post-remediación están presentes y medidos en la captura nueva: * Pressed sólido = **#0052CC** (idéntico al azul de marca del Figma, medido en ambos). * Tinte de fondo default/pressed-suave = **#E6EEFA** (coincide con el tinte del Figma). * Disabled: fondo **#E5E7EB** + glifo **#9CA3AF** (tokens de disabled correctos). * Icono activo azul en Outlined/Ghost default y pressed = **#0052CC**. - **Estados:** Standard=fondo tinte→sólido, Outlined=blanco con hairline→tinte, Ghost=transparente→tinte. La progresión default→pressed→disabled es consistente con las tres columnas del Figma. - **Iconos:** El glifo (store) coincide; en Standard pressed va en blanco sobre el sólido, igual que el tile azul del Figma. - **Bordes:** Outlined en la app usa hairline muy tenue (\~#E5E7EB) sobre blanco; concuerda con el tile blanco-con-borde del Figma. Diferencia de contraste marginal, no accionable. - **Layout:** Único residuo — el Figma es una tira lineal y la app una grilla con headers; decisión de presentación del playground, no del átomo.Sin hallazgos accionables; la remediación sistémica (azul #0052CC, tinte #E6EEFA, disabled #E5E7EB/#9CA3AF) llegó completa a esta pantalla. | | #### Keypad Key [Section titled “Keypad Key”](#keypad-key) **Código:** `src/ui/atoms/KeypadKey/index.tsx` · **Figma:** [`37:45`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=37-45) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Keypad Key](/design/parity/figma/atoms/figma-keypad-key.png)](/design/parity/figma/atoms/figma-keypad-key.png "figma Keypad Key — abrir a tamaño completo") | [![app Keypad Key](/design/parity/history/2026-07-24/screenshots/playground/keypad-key.png)](/design/parity/history/2026-07-24/screenshots/playground/keypad-key.png "app Keypad Key — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Estado pressed medido con tinte de selección **#E6EEFA** y glifo/dígito en azul de marca **#0052CC** — patrón de selección con tinte correcto (idéntico en fila Dígito y Acción). Default blanco **#FFFFFF** con dígito **#1F2933**; disabled con dígito **#9CA3AF**. Todo alineado con la remediación (palette.primary.light + azul marca). - **Tipografía:** Dígito en peso bold, coherente con Figma. - **Bordes:** Tecla con esquinas redondeadas suaves, igual que el diseño; default con hairline sutil sobre fondo claro. - **Iconos:** Fila Acción (backspace) replica los tres estados con el mismo esquema de color que la fila Dígito. - **Estados:** default / pressed / disabled presentes y consistentes con el patrón de tinte canónico. - **Layout:** El showcase agrega la fila Acción y etiquetas de estado (DEFAULT/PRESSED/DISABLED) — decisión del showcase, no defecto. Figma muestra las variantes en horizontal sin etiquetas; sin residuo del componente. | | #### PIN Dot [Section titled “PIN Dot”](#pin-dot) **Código:** `src/ui/atoms/PinDot/index.tsx` · **Figma:** [`47:52`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=47-52) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma PIN Dot](/design/parity/figma/atoms/figma-pin-dot.png)](/design/parity/figma/atoms/figma-pin-dot.png "figma PIN Dot — abrir a tamaño completo") | [![app PIN Dot](/design/parity/history/2026-07-24/screenshots/playground/pin-dot.png)](/design/parity/history/2026-07-24/screenshots/playground/pin-dot.png "app PIN Dot — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** El dot **lleno** mide `#0052CC` en ambos paneles — azul de marca correcto (verificado, no re-reportado). El estado **error** es `#EF4444` (`palette.error.main`), consistente. - **Bordes:** Único residuo real — el borde del dot **vacío** mide `#E5E7EB` en la app (usa `theme.palette.divider`) vs `#D1D5DB` en Figma, un paso de gris más oscuro (el mismo tono que el borde de checkbox/radio sin marcar). Confirmado en `src/ui/atoms/PinDot/index.tsx:54` (`2px solid ${theme.palette.divider}`). Cambiar a `palette.grey[300]`/`#D1D5DB` para paridad exacta del anillo vacío. - **Iconos:** Anillo 2px sólido + círculo perfecto (`borderRadius: '50%'`, `boxSizing: border-box`) coincide con Figma. - **Layout:** Figma solo muestra vacío + lleno; la app añade filas “PIN ROW” y “ERROR” — decisión del showcase, no defecto. - **Estados:** Lleno/vacío/error bien diferenciados; transición de `border-color`/`background-color` presente. | | #### QR Code [Section titled “QR Code”](#qr-code) **Código:** `src/ui/atoms/QrCode/index.tsx` | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma QR Code](/design/parity/figma/atoms/figma-qr-code.png)](/design/parity/figma/atoms/figma-qr-code.png "figma QR Code — abrir a tamaño completo") | [![app QR Code](/design/parity/history/2026-07-24/screenshots/playground/qr-code.png)](/design/parity/history/2026-07-24/screenshots/playground/qr-code.png "app QR Code — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Módulos medidos en #1F2933 (tinta oscura canónica), igual que el Figma (#1F2933 medido). Quiet zone blanco #FFFFFF en ambos. Sin negro puro fuera de token. - **Layout:** App muestra el atom en tres tamaños (S/M/L) sobre tarjeta blanca con esquinas redondeadas y fondo #F5F7FA del playground; el panel Figma es un crop ampliado de un solo código, por eso no se compara contenedor a escala. - **Bordes:** Tarjeta contenedora con radio redondeado y hairline suave, coherente con el patrón de tarjetas planas. - **Iconos:** El patrón de módulos difiere (dato codificado y estilo de módulo distinto) — es contenido de ejemplo del generador, no defecto. - **Estados:** N/A para este atom.Sin residuo real; el componente cumple la paridad tras la remediación. | | #### Radio [Section titled “Radio”](#radio) **Código:** `src/ui/atoms/Radio/index.tsx` · **Figma:** [`90:14`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=90-14) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Radio](/design/parity/figma/atoms/figma-radio.png)](/design/parity/figma/atoms/figma-radio.png "figma Radio — abrir a tamaño completo") | [![app Radio](/design/parity/history/2026-07-24/screenshots/playground/checkbox-radio-switch.png)](/design/parity/history/2026-07-24/screenshots/playground/checkbox-radio-switch.png "app Radio — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Punto seleccionado medido `#0052CC`, idéntico al de Figma (`#0052CC`). Anillo no-seleccionado gris claro `~#D1D5DB`, coincide con el ring canónico de Figma. - **Bordes:** Geometría redondeada correcta en ambos: no-seleccionado = anillo gris con relleno blanco; seleccionado = anillo azul + hueco blanco + punto azul central. Igual que Figma. - **Estados:** DEFAULT, SELECTED y DISABLED reproducen el patrón de Figma (disabled atenuado `#9CA3AF`). El estado ERROR (label roja + helper “Selecciona un método”) es un extra del showcase — Figma solo ilustra default/seleccionado; no es defecto. - **Layout:** Radio + label alineados, mismo patrón de selección con tinte/relleno de marca. Sin residuo. | | #### Search Field [Section titled “Search Field”](#search-field) **Código:** `src/ui/atoms/SearchField/index.tsx` · **Figma:** [`37:35`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=37-35) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Search Field](/design/parity/figma/atoms/figma-search-field.png)](/design/parity/figma/atoms/figma-search-field.png "figma Search Field — abrir a tamaño completo") | [![app Search Field](/design/parity/history/2026-07-24/screenshots/playground/search-field.png)](/design/parity/history/2026-07-24/screenshots/playground/search-field.png "app Search Field — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** El único residuo medible es la superficie del campo. Figma muestra el search bar con **relleno suave #F0F4F8 y sin borde** (estilo *filled*); el app lo renderiza como **#FFFFFF + hairline #E5E7EB** (`src/ui/atoms/SearchField/index.tsx` usa `TextField` con el tema canónico). Esta es la decisión de campo-de-formulario blanco+hairline ya estandarizada en la remediación, por lo que probablemente el #F0F4F8 de Figma sea el lienzo/estilo filled y no un defecto; queda como diferencia de tratamiento (filled vs outlined), no de token. - **Iconos:** Lupa de adorno **#9CA3AF** en ambos paneles. ✓ - **Tipografía:** Placeholder gris muted (\~#9CA3AF) coincide; texto escrito (TYPING) en oscuro #1F2933. ✓ - **Bordes:** App con radio redondeado y hairline #E5E7EB; alto \~40. ✓ - **Estados:** DISABLED del app = relleno **#E5E7EB** (coincide con remediación). Botón limpiar (×) aparece al escribir. ✓ - **Layout:** Icono a la izquierda + placeholder “Buscar cliente…” coinciden; el showcase añade TYPING y DISABLED (decisión de showcase, no defecto). | | #### Segmented Control [Section titled “Segmented Control”](#segmented-control) **Código:** `src/ui/atoms/SegmentedControl/index.tsx` · **Figma:** [`90:24`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=90-24) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Segmented Control](/design/parity/figma/atoms/figma-segmented-control.png)](/design/parity/figma/atoms/figma-segmented-control.png "figma Segmented Control — abrir a tamaño completo") | [![app Segmented Control](/design/parity/history/2026-07-24/screenshots/playground/segmented-control.png)](/design/parity/history/2026-07-24/screenshots/playground/segmented-control.png "app Segmented Control — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Track gris claro medido en app (#EBEDF0 / #F0F4F8) alineado con Figma (#F0F4F8). Thumb del segmento activo blanco puro (#FFFFFF) en ambos. - **Bordes:** Thumb con esquinas redondeadas sobre track redondeado — geometría de selección correcta tras la remediación. - **Estados:** Patrón de selección = thumb blanco (no relleno primary), tal como el canon. El estado DISABLED conserva thumb blanco en el activo y atenúa los labels inactivos; consistente con Figma. - **Tipografía:** Label seleccionado en tono oscuro y no-seleccionados en gris; jerarquía correcta. - **Layout:** Tres segmentos (Día/Semana/Mes) en fila dentro del contenedor tintado; misma estructura que el diseño. - Nota: el panel APP añade un segundo grupo DISABLED como showcase de /playground (decisión del showcase, no defecto). | | #### Select [Section titled “Select”](#select) **Código:** `src/ui/atoms/Select/index.tsx` · **Figma:** [`91:45`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=91-45) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Select](/design/parity/figma/atoms/figma-select.png)](/design/parity/figma/atoms/figma-select.png "figma Select — abrir a tamaño completo") | [![app Select](/design/parity/history/2026-07-24/screenshots/playground/select.png)](/design/parity/history/2026-07-24/screenshots/playground/select.png "app Select — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** fixes sistémicos verificados en el campo cerrado: relleno blanco, borde hairline **#E5E7EB** (medido arriba/abajo), texto de opciones **#1F2933** (medido). El tinte de selección del Figma es **#E6EEFA** con check **#0052CC** (medidos en el diseño). - **Estados:** el select ABIERTO de la captura conserva borde **#E5E7EB** mientras el Figma muestra borde azul **#0052CC** \~2px. El fix existe en código (`src/ui/theme/theme.ts:260` → `.Mui-focused` = primary), pero `SelectShowcase.tsx` abre el popper con la prop `open` sin enfocar el input, así que la captura no lo ejercita — agregar foco (o un estado “Focused”) al showcase para poder verificarlo. - **Estados:** el showcase no tiene ejemplo con valor seleccionado; la afordancia de opción seleccionada del Figma (fila con tinte #E6EEFA + etiqueta azul + **check #0052CC** a la derecha) **no está implementada**: no hay override `MuiAutocomplete` en el theme ni `renderOption` con check en `src/ui/atoms/Select/index.tsx` (el default MUI daría solo tinte rgba primario 8%, sin check ni texto azul). - **Iconos:** chevron del campo en **#757575** (action.active default de MUI, medido) vs. gris del sistema (#6B7280) del diseño — mapear el popupIcon al gris frío. - **Tipografía:** la etiqueta “Fruta” dentro del campo mide ~~#6B7280 vs. placeholder del Figma más claro (~~#9CA3AF); además MUI usa label flotante en el borde al llenar el campo, patrón que el Figma no dibuja — verificar con un estado lleno. - **Layout:** en la captura el popper del ejemplo OPEN se solapa con el campo del ejemplo “No results” (los poppers portalan al body) — artefacto del showcase, no del atom. - **Contenido:** la app muestra además estados No-results y Loading (spinner visible, medido en captura) que el frame de Figma no dibuja — extra correcto, no es defecto. | | #### Skeleton [Section titled “Skeleton”](#skeleton) **Código:** `src/ui/atoms/Skeleton/index.tsx` | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Skeleton](/design/parity/figma/atoms/figma-skeleton.png)](/design/parity/figma/atoms/figma-skeleton.png "figma Skeleton — abrir a tamaño completo") | [![app Skeleton](/design/parity/history/2026-07-24/screenshots/playground/skeleton.png)](/design/parity/history/2026-07-24/screenshots/playground/skeleton.png "app Skeleton — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Relleno medido en app #F0F4F8 / #F1F5F8 / #F2F5F8 = surface-variant canónico #F0F4F8 de Figma. Cableado en `theme.ts:407-410` (`MuiSkeleton.root.backgroundColor = scheme.surface.variant`), fix “Parity finding #16” verificado. - **Bordes:** Barras y avatar con esquinas redondeadas / círculo, igual que el diseño. - **Layout:** El showcase muestra las primitivas etiquetadas (LINE/BLOCK/ROW/CARD/TABLE); Figma muestra una composición de página (avatar+líneas, card+líneas). Es la misma primitiva de skeleton — la composición vs. catálogo es decisión del showcase, no defecto. - **Estados:** La app añade un shimmer/wave animado (gradiente #E8ECF0→#F2F5F8 en BLOCK); es la animación de carga por defecto de MUI, esperada en un skeleton; el screenshot de Figma es estático. Sin residuo. - **Tipografía:** Etiquetas de sección del showcase (#6B7280), no forman parte del componente. | | #### Status Badge [Section titled “Status Badge”](#status-badge) **Código:** `src/ui/atoms/StatusBadge/index.tsx` · **Figma:** [`33:26`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=33-26) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Status Badge](/design/parity/figma/atoms/figma-status-badge.png)](/design/parity/figma/atoms/figma-status-badge.png "figma Status Badge — abrir a tamaño completo") | [![app Status Badge](/design/parity/history/2026-07-24/screenshots/playground/status-badge.png)](/design/parity/history/2026-07-24/screenshots/playground/status-badge.png "app Status Badge — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Tintes \*-soft verificados por muestreo y coinciden con Figma: success `#ECFDF5`≈`#ECFCF5`, warning `#FFFBEB`, danger `#FEF2F2`, info `#EFF6FF`. Textos por rol correctos: success `#10B981`, warning `#F59E0B`, danger `#EF4444`, info azul. Fix de variantes soft confirmado. - **Bordes:** Pill totalmente redondeado con relleno tintado en las 4 variantes (sin borde duro), igual que Figma. - **Layout:** El showcase apila vertical con etiqueta de rol (SUCCESS/WARNING/DANGER/INFO) sobre cada badge; Figma los muestra en fila horizontal. Es organización del playground, no defecto del componente. - **Estados:** Este showcase no incluye la variante `neutral` gris (Figma tampoco la muestra aquí); nada que reportar. - Salvedad: el histograma global marca 99% fondo porque los badges ocupan <1% del canvas 1440×900; los recortes confirman los pills tintados. | | #### Switch [Section titled “Switch”](#switch) **Código:** `src/ui/atoms/Switch/index.tsx` · **Figma:** [`90:21`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=90-21) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Switch](/design/parity/figma/atoms/figma-switch.png)](/design/parity/figma/atoms/figma-switch.png "figma Switch — abrir a tamaño completo") | [![app Switch](/design/parity/history/2026-07-24/screenshots/playground/checkbox-radio-switch.png)](/design/parity/history/2026-07-24/screenshots/playground/checkbox-radio-switch.png "app Switch — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Track ON medido **#0052CC** en la app, idéntico al azul de marca del Figma; track OFF **#D1D5DB** en ambos paneles; thumb blanco **#FFFFFF** verificado. Sin desviación. - **Bordes:** Forma pill totalmente redondeada con thumb circular blanco, igual que el diseño. - **Layout:** Thumb a la izquierda en OFF y desplazado a la derecha sobre track azul en ON — reproduce ambos estados del Figma. - **Estados:** El panel Figma solo muestra OFF y ON; la app añade **DISABLED** (decisión del showcase), con track gris tenue + thumb azul claro medido **#9EBDEB** — atenuación coherente con el patrón disabled. - **Tipografía:** Etiquetas OFF/ON/DISABLED en caps grises y “Notificaciones” en texto oscuro, consistentes con el resto de átomos. Sin residuo real del componente. | | #### Textarea [Section titled “Textarea”](#textarea) **Código:** `src/ui/atoms/Textarea/index.tsx` · **Figma:** [`103:110`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=103-110) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Textarea](/design/parity/figma/atoms/figma-textarea.png)](/design/parity/figma/atoms/figma-textarea.png "figma Textarea — abrir a tamaño completo") | [![app Textarea](/design/parity/history/2026-07-24/screenshots/playground/textarea.png)](/design/parity/history/2026-07-24/screenshots/playground/textarea.png "app Textarea — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Fondo blanco `#FFFFFF` con borde hairline `#E5E7EB` medido en el estado default — coincide con el token canónico. - **Bordes:** Esquinas redondeadas y borde 1px sólido; radios consistentes con Figma. - **Estados:** Focus con borde azul de marca `#0052CC` (medido), error con borde `#EF4444` + texto de ayuda rojo, disabled con tinte gris y texto atenuado — todos correctos. - **Tipografía:** Label estático arriba en tono oscuro, placeholder gris — patrón de campo de formulario correcto. - **Layout:** Figma solo muestra el estado default; el showcase de /playground agrega FOCUS/ERROR/DISABLED (decisión del showcase). El texto de ejemplo distinto es decisión del showcase, no defecto. | | #### Toast / Snackbar [Section titled “Toast / Snackbar”](#toast--snackbar) **Código:** `src/ui/atoms/Toast/index.tsx` · **Figma:** [`102:142`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=102-142) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Toast / Snackbar](/design/parity/figma/atoms/figma-toast.png)](/design/parity/figma/atoms/figma-toast.png "figma Toast / Snackbar — abrir a tamaño completo") | [![app Toast / Snackbar](/design/parity/history/2026-07-24/screenshots/playground/toast.png)](/design/parity/history/2026-07-24/screenshots/playground/toast.png "app Toast / Snackbar — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Superficie del toast correcta y opaca: medí **#1F2933** en Figma y en la app (rol `text.primary`, patrón “dark” verificado). Texto del mensaje blanco en ambos. ✅ - **Color (acción):** Único residuo real. La acción “Deshacer” en Figma es azul claro medido **#7FB0FF**; la app la pinta en **#E6EEFA** (`primary.light`, `src/ui/atoms/Toast/index.tsx` → `color: dark ? 'primary.light' : 'primary.main'`). Ese tinte está pensado como *relleno* y sobre fondo oscuro lee casi blanco en vez de azul legible. Considerar un rol de azul claro (p. ej. `primary.200`/un token on-dark) para el CTA sobre superficie oscura. - **Iconos:** Figma muestra el check de éxito como glifo verde simple (sin círculo); la app usa badges circulares rellenos por estado (rojo/naranja/verde/azul). Es decisión del showcase (variantes de ejemplo), no defecto — colores de rol correctos. - **Bordes:** Ambos con esquinas redondeadas; el radio de Figma es algo más tipo píldora (\~½ alto) y el de la app un rect redondeado un poco menos pronunciado. Diferencia de proporción menor, dentro de tolerancia. - **Layout:** Estructura idéntica — icono de estado + mensaje (flex-grow) + acción a la derecha. ✅ - **Estados:** Las 4 variantes (info/success/warning/danger) presentes con sus roles de color correctos. ✅ | | #### Tooltip [Section titled “Tooltip”](#tooltip) **Código:** `src/ui/atoms/Tooltip/index.tsx` · **Figma:** [`102:150`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=102-150) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Tooltip](/design/parity/figma/atoms/figma-tooltip.png)](/design/parity/figma/atoms/figma-tooltip.png "figma Tooltip — abrir a tamaño completo") | [![app Tooltip](/design/parity/history/2026-07-24/screenshots/playground/tooltip.png)](/design/parity/history/2026-07-24/screenshots/playground/tooltip.png "app Tooltip — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Fondo del tooltip **#1F2933** medido en ambos paneles (Figma 66% del pixel-count; app muestreado en la píldora “Arriba” → #1F2933). Opaco, sin transparencia. Texto blanco. Fix de tooltip opaco confirmado. - **Iconos:** Caret/flecha direccional presente y correctamente orientado en las cuatro variantes (top/bottom/left/right). - **Layout:** El showcase de /playground demuestra las 4 posiciones alrededor de un botón “Objetivo” (outlined, borde primary) — decisión del showcase, no defecto. El Figma solo muestra un tooltip individual con texto de ejemplo “Editar”. - **Tipografía/Espaciado/Bordes:** Píldora redondeada con padding compacto, sin borde, coherente con el diseño. Sin residuo real detectado. | | ### Moléculas [Section titled “Moléculas”](#moléculas) Código en `src/ui/molecules/**`. #### Balance Card [Section titled “Balance Card”](#balance-card) **Código:** `src/ui/molecules/BalanceCard/index.tsx` | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Balance Card](/design/parity/figma/molecules/figma-balance-card-context.png)](/design/parity/figma/molecules/figma-balance-card-context.png "figma Balance Card — abrir a tamaño completo") | [![app Balance Card](/design/parity/history/2026-07-24/screenshots/playground/balance-card.png)](/design/parity/history/2026-07-24/screenshots/playground/balance-card.png "app Balance Card — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Variante primary medida en #0052CC (fondo) con botón tonal #E6EEFA + texto azul — igual que la card azul de Figma. Cards planas blancas para el resto de variantes. - **Bordes:** Hairline de tarjeta plana en #E5E7EB, radios redondeados; sin sombras. Correcto. - **Iconos:** Botón “actualizar” con icono de refresco en brand #0052CC; adorno discreto arriba-derecha, coherente con Figma. - **Tipografía:** “Saldo disponible” en gris secundario, monto en jerarquía fuerte (#1F2933 en cards blancas / blanco sobre primary), sufijo MXN atenuado (#9CA3AF). Sin desvíos. - **Estados:** Skeleton “cargando” con barra pálida \~#F6F9FB (≈#F0F4F8); estado primary destacado. Todos presentes. - **Layout:** El panel APP muestra variantes extra de ejemplo (con moneda, crédito $8,000, cargando) que no aparecen en el contexto de Figma — decisión del showcase, no defecto. La estructura label→monto→acción replica la card de Figma. | | #### Cart Bar [Section titled “Cart Bar”](#cart-bar) **Código:** `src/ui/molecules/CartBar/index.tsx` · **Figma:** [`44:80`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=44-80) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Cart Bar](/design/parity/figma/molecules/figma-cart-bar.png)](/design/parity/figma/molecules/figma-cart-bar.png "figma Cart Bar — abrir a tamaño completo") | [![app Cart Bar](/design/parity/history/2026-07-24/screenshots/playground/cart-bar.png)](/design/parity/history/2026-07-24/screenshots/playground/cart-bar.png "app Cart Bar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Barra medida `#0052CC` en ambos paneles (Figma 52.5% de píxeles, APP 5.5% por ser captura de página completa); azul de marca canónico correcto. Fondo del showcase `#F5F7FA`. - **Tipografía:** Bloque apilado “N artículos” (secundario, más claro) sobre “$monto” (blanco, peso fuerte) reproduce la jerarquía de Figma. - **Bordes:** Esquinas redondeadas (\~8px) en ambos; barra plana sin sombra. Coincide. - **Iconos:** Carrito relleno blanco a la izquierda en ambos paneles. Coincide. - **Layout:** Icono + texto a la izquierda, CTA a la derecha. Estructura idéntica a Figma. - **Estados:** El CTA difiere por texto de ejemplo (“Ver carrito” en Figma vs “Cobrar” en la app) — decisión del showcase, no defecto. La app añade una variante “vacío · deshabilitado” (sin referencia en Figma) que atenúa el texto del CTA a gris pero mantiene la barra en azul pleno; comportamiento razonable para el estado, sin residuo del componente. | | #### Customer Row [Section titled “Customer Row”](#customer-row) **Código:** `src/ui/molecules/CustomerRow/index.tsx` | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Customer Row](/design/parity/figma/molecules/figma-customer-row.png)](/design/parity/figma/molecules/figma-customer-row.png "figma Customer Row — abrir a tamaño completo") | [![app Customer Row](/design/parity/history/2026-07-24/screenshots/playground/customer-row.png)](/design/parity/history/2026-07-24/screenshots/playground/customer-row.png "app Customer Row — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Avatar con tinte `#E6EEFA` medido en ambos paneles (canónico `palette.primary.light`) e iniciales en azul de marca. Nombre `#1F2933` (medido `#454E56` en bordes por antialias), teléfono gris secundario, chevron `#6B7280`. Todo alineado. - **Iconos:** Chevron de acción en gris, coincide con Figma. - **Layout:** Avatar + bloque nombre/teléfono + chevron a la derecha; misma estructura y proporciones que el diseño. - **Estados:** El panel APP añade una variante `SELECTED` con fondo tintado (`#E1E9F6`, familia primary.light) y esquinas redondeadas — es un estado del showcase que sigue el patrón de selección con tinte, no un defecto. Figma solo muestra el estado default. - Sin residuo real del componente. | | #### Denomination Row [Section titled “Denomination Row”](#denomination-row) **Código:** `src/ui/molecules/DenominationRow/index.tsx` | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Denomination Row](/design/parity/figma/molecules/figma-denomination-row.png)](/design/parity/figma/molecules/figma-denomination-row.png "figma Denomination Row — abrir a tamaño completo") | [![app Denomination Row](/design/parity/history/2026-07-24/screenshots/playground/denomination-row.png)](/design/parity/history/2026-07-24/screenshots/playground/denomination-row.png "app Denomination Row — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** *(re-auditado tras remediación)*- **Color:** El subtotal difiere. En Figma el `$0.00` se renderiza en gris atenuado (medido \~#62696F/#6B7280, tipo `text.secondary`), claramente más claro que el label `$500`. En la app el subtotal es oscuro `#1F2933` (`src/ui/molecules/DenominationRow/index.tsx:85` usa `color="text.primary"`). Diferencia real pero menor/ambigua (la única muestra en Figma es el estado cero). Se deja como residuo aceptado. - **Iconos/Layout:** Residuo de operadores **cerrado** — los separadores `×` (entre denom e input) y `=` (entre input y subtotal) ahora se renderizan como glifos `text.secondary` grises (`index.tsx:51-58,74-81`), igual que Figma. - **Color:** Resto coincide — label denominación `text.primary` oscuro, `×` y `=` en `text.secondary` gris, en ambos paneles. - **Bordes:** Input blanco con hairline `#E5E7EB` medido (canónico) y esquinas redondeadas — coincide. - **Layout:** Fila flex `label · × · input · = · subtotal`, subtotal alineado a la derecha (`ml:auto`), ancho de label e input \~96px en ambos. - **Tipografía:** `subtitle2` para label/subtotal, `body2` para separadores; coincide con Figma. La diferencia de formato `$500` (Figma) vs `$500.00` (app) es dato de ejemplo del showcase, no defecto. | | #### Empty State [Section titled “Empty State”](#empty-state) **Código:** `src/ui/molecules/EmptyState/index.tsx` · **Figma:** [`99:151`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=99-151) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Empty State](/design/parity/figma/molecules/figma-empty-state.png)](/design/parity/figma/molecules/figma-empty-state.png "figma Empty State — abrir a tamaño completo") | [![app Empty State](/design/parity/history/2026-07-24/screenshots/playground/empty-state.png)](/design/parity/history/2026-07-24/screenshots/playground/empty-state.png "app Empty State — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Tinte del contenedor de icono medido `#F0F4F8` en Figma y app; glifo de adorno `#9CA3AF` en ambos. Título `#1F2933`, descripción gris medio (`#6B7280`, lectura clareada por antialias del texto fino). Botón relleno `#0052CC` idéntico en los dos paneles; botón outlined con borde y texto azul de marca. - **Bordes:** Contenedor de icono con esquinas redondeadas + botón outlined con borde sólido 1px; botón relleno sin borde. Coincide. - **Iconos:** Contenedor tintado con icono de adorno centrado, patrón correcto. El icono de ejemplo difiere (regalo en Figma / etiqueta en la variante “con acciones” del showcase) — decisión del showcase, no defecto. - **Layout:** Estructura vertical centrada contenedor-de-icono → título → descripción → fila de acciones (outlined + relleno), reproducida fielmente. Las variantes adicionales del showcase (búsqueda vacía, sin descripción) son ejemplos del playground. - **Estados:** Botones primario/outlined con el patrón cromático correcto; sin residuo detectable. | | #### Held-order Card [Section titled “Held-order Card”](#held-order-card) **Código:** `src/ui/molecules/HeldOrderCard/index.tsx` | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Held-order Card](/design/parity/figma/molecules/figma-held-order-card.png)](/design/parity/figma/molecules/figma-held-order-card.png "figma Held-order Card — abrir a tamaño completo") | [![app Held-order Card](/design/parity/history/2026-07-24/screenshots/playground/held-order-card.png)](/design/parity/history/2026-07-24/screenshots/playground/held-order-card.png "app Held-order Card — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación)*- **Color:** Botón `Reanudar venta` tonal medido #E6EEFA con texto azul de marca #0052CC. Avatar `SR` fill #E6EEFA + iniciales #0052CC. Folio `#H-2271` en azul de marca. Nombre/meta en grises canónicos, precio en negrita oscuro. Coincide. - **Bordes:** Tarjetas planas con hairline #E5E7EB y radio redondeado; chips (`Hace 4 min`, `En espera por…`) con borde neutro tipo StatusBadge. Sin sombras pesadas. - **Estados:** Variante `Reanudar deshabilitado` con fill/texto disabled gris (#E5E7EB/#9CA3AF) correctamente aplicada; variantes Default y Con artículos consistentes. - **Layout:** El showcase presenta tarjetas compactas (avatar arriba-derecha, chips en fila, meta como badges) frente a la tarjeta expandida de Figma; es la decisión de composición del showcase, no residuo del componente. Estructura, roles de color y patrón de botón tonal coinciden. - **Iconos/Tipografía:** Textos de ejemplo (`Carlos Méndez`, `6 art.`, `LR`) distintos por ser datos del showcase; no computa como defecto.Sin residuo real del componente. | | #### KPI Stat Card [Section titled “KPI Stat Card”](#kpi-stat-card) **Código:** `src/ui/molecules/KpiStatCard/index.tsx` · **Figma:** [`99:63`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=99-63) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma KPI Stat Card](/design/parity/figma/molecules/figma-kpi-stat-card.png)](/design/parity/figma/molecules/figma-kpi-stat-card.png "figma KPI Stat Card — abrir a tamaño completo") | [![app KPI Stat Card](/design/parity/history/2026-07-24/screenshots/playground/kpi-stat-card.png)](/design/parity/history/2026-07-24/screenshots/playground/kpi-stat-card.png "app KPI Stat Card — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Coincide. Contenedor de icono tintado medido en **#E6EEFA** en ambos paneles (primary.light). Valor en **#1F2933**, label gris de adorno (\~#9499A4 ≈ #9CA3AF), divisor/borde hairline correcto. - **Iconos:** Único residuo real — el indicador de delta usa un **chevron fino** (`^`/`v`) en la app, mientras Figma dibuja un **triángulo relleno ▲/▼**. Diferencia de glifo, no de color. Culpable probable: el icono elegido en `src/ui/**` para el componente KPI (revisar el render del delta/trend). - **Estados:** success verde ≈ **#17BB84** (app) vs **#10B981** (Figma) — mismo rol, tono casi idéntico; danger **#EF4444** con tinte \*-soft en el contenedor de icono coincide. - **Layout:** Estructura correcta — label + icono tintado arriba, valor grande tabular, delta con flecha+% abajo. Tarjeta plana con hairline. - **Salvedad:** las etiquetas/valores de ejemplo distintos por variante (Órdenes hoy / Ingresos del mes / Devoluciones) son decisión del showcase, no defecto. | | #### List Row [Section titled “List Row”](#list-row) **Código:** `src/ui/molecules/ListRow/index.tsx` · **Figma:** [`41:60`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=41-60) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma List Row](/design/parity/figma/molecules/figma-list-row.png)](/design/parity/figma/molecules/figma-list-row.png "figma List Row — abrir a tamaño completo") | [![app List Row](/design/parity/history/2026-07-24/screenshots/playground/list-row.png)](/design/parity/history/2026-07-24/screenshots/playground/list-row.png "app List Row — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación)*- **Color:** Contenedor de icono con tinte `#E6EEFA` y glifo `#0052CC` medidos idénticos en ambos paneles. Badge Info con fill `#EFF6FF` (rol info) + texto azul, igual que Figma. - **Iconos:** Residuo del chevron **cerrado** — ahora se pinta en azul de marca (`primary.main`, `src/ui/molecules/ListRow/index.tsx:145`). En el showcase el chevron es de trazo algo más fino que el de Figma (grosor de icono), diferencia visual menor. - **Estados:** Pressed = tinte `#E6EEFA`; Selected = `#E1E9F6` (tinte info algo más profundo para diferenciar del pressed) — patrón de selección con tinte redondeado correcto. Los estados `pressed`/`selected` extra y las etiquetas de sección son decisión del showcase. - **Layout:** Estructura icono-tintado + título/subtítulo + slot de trailing (chevron / valor / badge) consistente con el diseño. Las filas del showcase van sobre el fondo `#F5F7FA` en vez de tarjetas blancas con hairline; es el encuadre del playground, no del componente. - **Tipografía:** Título en oscuro, secundario en gris y valor `$1,234.00` en oscuro — coinciden. | | #### Product Card [Section titled “Product Card”](#product-card) **Código:** `src/ui/molecules/ProductCard/index.tsx` · **Figma:** [`44:50`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=44-50) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Product Card](/design/parity/figma/molecules/figma-product-card.png)](/design/parity/figma/molecules/figma-product-card.png "figma Product Card — abrir a tamaño completo") | [![app Product Card](/design/parity/history/2026-07-24/screenshots/playground/product-card.png)](/design/parity/history/2026-07-24/screenshots/playground/product-card.png "app Product Card — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** *(re-auditado tras remediación — commit `d515d29`)*- **Color:** Precio azul de marca correcto — medido **#0052CC** en ambos paneles. Título ≈#1F2933 y hairline #E5E7EB confirmados. Residuo de precio **cerrado**. - **Estados (stock disponible):** Residuo **cerrado**. “24 en stock” ahora se pinta como **StatusBadge success** (pill verde soft), en vez de caption gris plano (`src/ui/molecules/ProductCard/index.tsx:106-118`, ``), reproduciendo la pill verde de Figma. Stock bajo cae a la variante `warning` (ámbar). - **Estados (Agotado):** Residuos remanentes (menores, no bloqueantes): la app resuelve “sin stock” con StatusBadge danger en la **esquina superior-izquierda sobre la imagen** (`index.tsx:85-89`) más **card a opacity 0.6** (`index.tsx:64`), mientras Figma pone la pill roja **debajo del precio** a opacidad plena. La atenuación global lava el precio azul. Diferencia de tratamiento/posición, no de token; se deja como residuo aceptado. - **Bordes/Layout:** Tarjeta plana + hairline, placeholder 1:1 y título coinciden. El borde azul del estado “Seleccionado” es un estado extra del showcase (no en Figma), correcto. - **Iconos:** Botón “+” relleno sólido azul de marca `#0052CC` correcto (feature POS `onAdd`; Figma no lo dibuja); glifo placeholder `#9CA3AF` es decisión del showcase. - **Residuo remanente (aceptado):** posición de la pill Agotado (sobre imagen vs bajo precio) y opacity 0.6 de la tarjeta agotada. | | #### Profile Card [Section titled “Profile Card”](#profile-card) **Código:** `src/ui/molecules/ProfileCard/index.tsx` | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Profile Card](/design/parity/figma/molecules/figma-profile-card-context.png)](/design/parity/figma/molecules/figma-profile-card-context.png "figma Profile Card — abrir a tamaño completo") | [![app Profile Card](/design/parity/history/2026-07-24/screenshots/playground/profile-card.png)](/design/parity/history/2026-07-24/screenshots/playground/profile-card.png "app Profile Card — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Avatar con tinte `#E6EEFA` e iniciales `#0052CC` (medido) — patrón avatar tinte+iniciales canónico. Nombre `#1F2933`, rol gris \~`#6B7280` (medido). Tarjeta default blanca `#FFFFFF`. - **Bordes:** Estado `selected` con borde azul de marca `#0052CC` 1px (medido), sin relleno; default plano con hairline claro. Correcto. - **Iconos:** N/A — el avatar usa iniciales “AV”, no icono. - **Layout:** Avatar circular a la izquierda + bloque nombre/rol; dos estados (default / selected) en el showcase. Estructura coincide con el patrón de selección con borde. - **Estados:** default y selected verificados; el borde marca la selección (no tinte de fondo), consistente con list-row/tarjeta seleccionable.*Salvedad: la referencia Figma del par es la pantalla “Selecciona tu tienda” (tarjetas de tienda); el panel APP es el showcase del molecule ProfileCard con datos de ejemplo “Ana Vega / Gerente de tienda” — el contenido de ejemplo es decisión del showcase, no defecto. Sin residuo real del componente.* | | #### Provider Tile [Section titled “Provider Tile”](#provider-tile) **Código:** `src/ui/molecules/ProviderTile/index.tsx` · **Figma:** [`48:63`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=48-63) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Provider Tile](/design/parity/figma/molecules/figma-provider-tile.png)](/design/parity/figma/molecules/figma-provider-tile.png "figma Provider Tile — abrir a tamaño completo") | [![app Provider Tile](/design/parity/history/2026-07-24/screenshots/playground/provider-tile.png)](/design/parity/history/2026-07-24/screenshots/playground/provider-tile.png "app Provider Tile — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Estado Activo medido en app — borde `#004FCB ≈ #0052CC` (primary.main, 2px sólido) y fondo `#E6EEFA` (palette.primary.light); Figma activo mide borde `#0043C8` / fondo `#E6EEFA`. Glifo y label recoloreados a primary.main. Default = superficie blanca + hairline `divider`. Todo cableado por rol en `src/ui/molecules/ProviderTile/index.tsx`. - **Layout:** Estructura idéntica a Figma — contenedor de icono redondeado tintado (56×56, radius.md) apilado sobre label centrado; tile 112px min, radius.lg. No es una fila-badge horizontal. - **Estados:** Default (tinte neutral `surface.variant` en el contenedor, glifo `text.secondary`) y Activo (tinte primary.light + anillo 2px + recoloreado a primary) reproducen las variantes Figma “Default”/“Activo”. - **Iconos:** El showcase usa un glifo tipo tarjeta mientras Figma muestra un teléfono — decisión de icono de ejemplo del showcase, no defecto (`icon` es prop). - **Bordes:** Figma dibuja el tile Default con una sombra suave elevada; la app lo aplana a blanco + hairline, coherente con la remediación “tarjetas planas+hairline” — intención de DS, no residuo. | | #### Quantity Stepper [Section titled “Quantity Stepper”](#quantity-stepper) **Código:** `src/ui/molecules/QuantityStepper/index.tsx` · **Figma:** [`44:69`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=44-69) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Quantity Stepper](/design/parity/figma/molecules/figma-quantity-stepper.png)](/design/parity/figma/molecules/figma-quantity-stepper.png "figma Quantity Stepper — abrir a tamaño completo") | [![app Quantity Stepper](/design/parity/history/2026-07-24/screenshots/playground/quantity-stepper.png)](/design/parity/history/2026-07-24/screenshots/playground/quantity-stepper.png "app Quantity Stepper — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Fondo de botones tintado `#E6EEFA` (palette.primary.light) e iconos −/+ en azul de marca `#0052CC` (primary.main), medidos en el panel APP; el dígito central va en tinta oscura. Coincide con el diseño. El origen `src/ui/molecules/QuantityStepper/index.tsx` cablea `primary.light` / `primary.main` correctamente. - **Estados:** Fila “editable” con ambos botones tintados y “en mínimo” con el − deshabilitado en gris (`action.disabledBackground` `#E5E7EB` + `text.disabled` `#9CA3AF`) y el + activo azul; replica exactamente las dos filas de Figma (habilitado / − deshabilitado en gris). - **Bordes:** Botones cuadrado-redondeado usando `theme.shape.borderRadius` (canónico). Coincide. - **Iconos:** −/+ con trazo azul de marca; sin defecto. - **Layout:** Estructura \[−] \[valor] \[+] con separación uniforme; igual que Figma. - **Nota de medición:** La fuente Figma (176 px) diluye el azul al reescalar (lecturas \~`#145FD0`/`#F0F4F8`), pero es artefacto de resolución, no discrepancia; el APP usa los tokens canónicos correctos. | | #### Summary Row [Section titled “Summary Row”](#summary-row) **Código:** `src/ui/molecules/SummaryRow/index.tsx` · **Figma:** [`43:66`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=43-66) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Summary Row](/design/parity/figma/molecules/figma-summary-row.png)](/design/parity/figma/molecules/figma-summary-row.png "figma Summary Row — abrir a tamaño completo") | [![app Summary Row](/design/parity/history/2026-07-24/screenshots/playground/summary-row.png)](/design/parity/history/2026-07-24/screenshots/playground/summary-row.png "app Summary Row — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación — commit `d515d29`)*- **Color:** El TOTAL standalone mide exactamente **#0052CC** en ambos paneles (azul de marca canónico). El acento se aplica vía `tone="primary"` en el showcase — **no** vía `emphasis`. Decisión de API deliberada: `emphasis` controla solo peso/tamaño; el color lo decide `tone` (`default` = #1F2933, `success` = #10B981, `primary` = #0052CC). Así el mismo componente pinta el TOTAL en azul aquí pero **oscuro** en el POS Cash Tender (cuyo frame lo quiere en tinta oscura), sin regresión cruzada. - **Tipografía:** Coincide — TOTAL en negrita/mayor (h5) y label en subtitle1; Subtotal en body2/subtitle2. Jerarquía correcta. - **Bordes:** Coincide — divisor hairline #E5E7EB sobre la fila del total. - **Layout:** Coincide — label a la izquierda, valor a la derecha, alineación baseline, `space-between`. - **Estados (showcase):** La fila “Descuento” en verde #10B981 (`tone="success"`) y el ejemplo negativo −$50.00 son ejemplos del showcase (no en el panel Figma); no son defecto. | | ### Organismos [Section titled “Organismos”](#organismos) Código en `src/ui/organisms/**`. #### Breadcrumbs [Section titled “Breadcrumbs”](#breadcrumbs) **Código:** `src/ui/organisms/Breadcrumbs/Breadcrumbs.tsx` · **Figma:** [`94:21`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=94-21) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Breadcrumbs](/design/parity/figma/organisms/figma-breadcrumbs.png)](/design/parity/figma/organisms/figma-breadcrumbs.png "figma Breadcrumbs — abrir a tamaño completo") | [![app Breadcrumbs](/design/parity/history/2026-07-24/screenshots/playground/breadcrumbs.png)](/design/parity/history/2026-07-24/screenshots/playground/breadcrumbs.png "app Breadcrumbs — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación)*- **Color:** Enlaces intermedios `#6B7280` (medido) = Figma `#6B7280` ✓. Último nivel resaltado `#1F2933` = Figma ✓. Fondo showcase `#F5F7FA` es la superficie del /playground, no defecto. - **Iconos:** Residuo del chevron **cerrado** — el separador ahora fija `color: 'text.disabled'` (#9CA3AF, gris de adorno) en `src/ui/organisms/Breadcrumbs/Breadcrumbs.tsx:37`, igual que Figma. - **Tipografía:** Último nivel en peso mayor (bold) tal como Figma; enlaces navegables en regular. Coincide. - **Layout/Estados:** Patrón trail con separador chevron, último nivel resaltado y `aria-current="page"` correcto. El showcase muestra dos ejemplos (ruta corta + ruta profunda), decisión del showcase. | | #### Bulk Action Bar [Section titled “Bulk Action Bar”](#bulk-action-bar) **Código:** `src/ui/organisms/DataTable/BulkActionBar.tsx` · **Figma:** [`99:122`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=99-122) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Bulk Action Bar](/design/parity/figma/organisms/figma-bulk-action-bar.png)](/design/parity/figma/organisms/figma-bulk-action-bar.png "figma Bulk Action Bar — abrir a tamaño completo") | [![app Bulk Action Bar](/design/parity/history/2026-07-24/screenshots/playground/bulk-action-bar.png)](/design/parity/history/2026-07-24/screenshots/playground/bulk-action-bar.png "app Bulk Action Bar — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación — commit `d515d29`)*- **Color:** Tint de fondo idéntico — Figma #E6EEFA vs App #E6EEFA (medido; `theme.palette.action.selected` en `BulkActionBar.tsx:54`). Editar/Exportar en primary #0052CC, Eliminar en rojo. Coincide. - **Tipografía:** El contador “3 seleccionados” ya se pinta en **azul de marca + bold** (`primary.main`, `BulkActionBar.tsx:72`), igual que Figma. Residuo cerrado. - **Bordes:** Residuo cerrado — se añadió `border: 1px solid primary.main` (#0052CC) alrededor de la barra tonal (`BulkActionBar.tsx`), reproduciendo el contorno azul de Figma en los cuatro cantos. - **Iconos:** El elemento líder difiere por diseño: Figma muestra un **checkbox relleno azul con check** (indicador de selección); la App muestra un **icono X** (afordancia `onClear`/Xmark, decisión de interacción del componente, no defecto de estilo). - **Layout:** Estructura coincide — líder + contador (flex:1) + acciones a la derecha, radio redondeado. La segunda fila (“selección amplia”, 12 seleccionados) es variante de demostración del showcase.Neto: paridad de color, borde y estructura; el único delta es la afordancia líder (X de limpiar vs checkbox), decisión de interacción deliberada. | | #### Chart — Bar [Section titled “Chart — Bar”](#chart--bar) **Código:** `src/ui/organisms/charts/*` · **Figma:** [`101:101`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=101-101) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Chart — Bar](/design/parity/figma/organisms/figma-chart-bar.png)](/design/parity/figma/organisms/figma-chart-bar.png "figma Chart — Bar — abrir a tamaño completo") | [![app Chart — Bar](/design/parity/history/2026-07-24/screenshots/playground/chart-bar.png)](/design/parity/history/2026-07-24/screenshots/playground/chart-bar.png "app Chart — Bar — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Barras medidas en `#0052CC` (azul de marca) en Figma y app; el swatch de la leyenda también `#0052CC`. Fondo de card blanco. Serie primaria correcta — sin residuo de color. - **Bordes:** Único residuo real del componente: las barras de la app salen con esquinas superiores **rectas**, mientras Figma las dibuja con **radio superior redondeado** (\~8-10px). `BarChart/index.tsx` usa `@mui/x-charts` sin pasar `borderRadius` a la serie; agregar `borderRadius` al `` cerraría la brecha. - **Layout:** El panel Figma es un recorte mínimo (solo título + 5 barras). La app muestra ejes X/Y, gridlines horizontales, ticks y leyenda “Ventas” — pero el propio frame del showcase se rotula “BARRAS CON EJES Y LEYENDA”, así que esa estructura extra es decisión del showcase, no defecto. - **Tipografía:** Título “Por método de pago” en gris oscuro `#1F2933`, labels de eje en gris tenue — consistente con los tokens de texto. - **Estados:** No aplican estados interactivos en esta captura. | | #### Chart — Donut [Section titled “Chart — Donut”](#chart--donut) **Código:** `src/ui/organisms/charts/*` · **Figma:** [`101:114`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=101-114) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Chart — Donut](/design/parity/figma/organisms/figma-chart-donut.png)](/design/parity/figma/organisms/figma-chart-donut.png "figma Chart — Donut — abrir a tamaño completo") | [![app Chart — Donut](/design/parity/history/2026-07-24/screenshots/playground/chart-donut.png)](/design/parity/history/2026-07-24/screenshots/playground/chart-donut.png "app Chart — Donut — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Serie medida idéntica en ambos paneles — Bebidas `#0052CC` (primary de marca), Abarrotes `#3B82F6` (info/serie2), Botanas `#10B981`, Otros `#F59E0B`. Paridad exacta en las 4 cuñas y en el mismo orden. - **Tipografía:** Título “Categorías” en gris tinta oscuro (#1F2933) y labels de leyenda en gris; total central “9,500” en bold + “Total” en gris secundario. Consistente con la escala tipográfica. - **Layout:** Dona (donut) con leyenda vertical a la derecha, marcador circular + etiqueta por serie; misma relación de grosor de anillo. Estructura equivalente. - **Iconos:** Marcadores de leyenda como puntos rellenos del color de serie — coincide. - **Estados:** N/A (chart estático). - **Salvedad (no defecto):** El **total central “9,500”** es la variante del showcase (su título es “Dona con leyenda y total central”); Figma muestra la dona sin total central. Decisión del showcase. - **Residuo menor (cosmético):** La app renderiza `padAngle` (pequeño gap entre arcos) mientras que en Figma los segmentos se tocan sin separación. Diferencia mínima de estilo del donut; no altera color, serie ni jerarquía. | | #### Chart — Line [Section titled “Chart — Line”](#chart--line) **Código:** `src/ui/organisms/charts/*` · **Figma:** [`101:91`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=101-91) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Chart — Line](/design/parity/figma/organisms/figma-chart-line.png)](/design/parity/figma/organisms/figma-chart-line.png "figma Chart — Line — abrir a tamaño completo") | [![app Chart — Line](/design/parity/history/2026-07-24/screenshots/playground/chart-line.png)](/design/parity/history/2026-07-24/screenshots/playground/chart-line.png "app Chart — Line — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Trazo de la serie medido = **#0052CC** en ambos paneles (marca), coincide. Relleno de área en Figma medido = **#E6EEFA** (primary.light) ✅ como tinte, pero **ausente en la app**. - **Layout:** Único residuo real — Figma muestra la línea sobre un **área rellena** (#E6EEFA bajo la curva); la app renderiza **línea pura sin relleno**. `src/ui/organisms/charts/LineChart/index.tsx` no pasa `area: true` a la serie de `@mui/x-charts`. Fix: activar el relleno con el tinte de marca (alpha de primary.light). - **Iconos/Ejes:** Ejes (8–16), gridlines horizontales (0–80) y leyenda “Ventas” son de la **variante del showcase** (“línea con ejes, gridlines y leyenda”), no defecto vs Figma (que es una miniatura sin ejes). - **Estados:** Variantes Vacío (“Sin datos para mostrar”) y Cargando (skeleton #F0F4F8) presentes y correctas, sin equivalente en la miniatura de Figma. - **Tipografía/Bordes:** Título “Ventas por hora” #1F2933 y tarjeta plana + hairline coinciden; eje/grid usan text.secondary/#E5E7EB (tokens), OK. | | #### Command Palette [Section titled “Command Palette”](#command-palette) **Código:** `src/ui/organisms/CommandPalette/index.tsx` · **Figma:** [`113:651`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=113-651) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Command Palette](/design/parity/figma/organisms/figma-command-palette-context.png)](/design/parity/figma/organisms/figma-command-palette-context.png "figma Command Palette — abrir a tamaño completo") | [![app Command Palette](/design/parity/history/2026-07-24/screenshots/playground/command-palette.png)](/design/parity/history/2026-07-24/screenshots/playground/command-palette.png "app Command Palette — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** El tint de selección coincide — app **#E5EDFA** ≈ `palette.primary.light` **#E6EEFA** (Figma **#E6EEFA**). Scrim, tarjeta blanca plana + hairline `divider` y campo de búsqueda blanco con foco azul: OK. - **Iconos:** Residuo real — en Figma cada icono de fila va dentro de un contenedor redondeado tintado **#F0F4F8**; en la app el icono queda desnudo sobre blanco (**#FFFFFF** medido alrededor). Falta el chip de icono. Culpable: `src/ui/organisms/CommandPalette/index.tsx` L231–235 (`ListItemIcon` + `Icon` sin wrapper tintado). El adorno gris #9CA3AF sí está, pero sin el contenedor. - **Tipografía:** Los encabezados de sección difieren — Figma usa estilo *overline* (MAYÚSCULAS + tracking, \~11px, gris **#8C9097**: “PRODUCTOS”); la app los muestra en Title Case y más grandes (gris **#7A7B7D**: “Productos”). Causa: `ListSubheader` sin override (no hay `MuiListSubheader` en `theme.ts`; usa el default MUI \~16px sin `text-transform`). L217. - **Layout:** Filas de una sola línea en la app vs. dos líneas en Figma (título + metadata: “SKU 750105530 · $18.00”, “24 Oct · $406.00 · Pagado”). El tipo `CommandItem` no tiene campo secundario y `ListItemText` sólo pinta `primary` (L236) — limitación del componente, no sólo dato del showcase. - **Estados:** Falta el chip **ESC** dentro del campo de búsqueda (Figma lo muestra a la derecha); el `SearchField` no lleva ese `endAdornment` (L196–203). Afordancia menor. - **Salvedad:** El icono de producto (etiqueta en app vs. caja de regalo en Figma) y la consulta escrita “coca” vs. placeholder son decisiones del showcase, no defectos. | | #### Confirm Dialog [Section titled “Confirm Dialog”](#confirm-dialog) **Código:** `src/ui/organisms/ConfirmDialog/index.tsx` · **Figma:** [`102:122`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=102-122) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Confirm Dialog](/design/parity/figma/organisms/figma-confirm-dialog.png)](/design/parity/figma/organisms/figma-confirm-dialog.png "figma Confirm Dialog — abrir a tamaño completo") | [![app Confirm Dialog](/design/parity/history/2026-07-24/screenshots/playground/confirm-dialog.png)](/design/parity/history/2026-07-24/screenshots/playground/confirm-dialog.png "app Confirm Dialog — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Icono de peligro con contenedor tintado red-soft `#FEF2F2` y triángulo `#EF4444` medidos idénticos en ambos paneles. Botón Eliminar en relleno rojo sólido `#EF4444` (app y Figma iguales). Botón Cancelar outlined con azul de marca (`#0556CD` app / `#0153CC` Figma ≈ `#0052CC`). Título oscuro y cuerpo gris coinciden. - **Iconos:** Patrón de contenedor tintado + glifo relleno (triángulo de advertencia) correcto y consistente con el resto de organismos remediados. - **Bordes:** Tarjeta plana blanca con esquinas redondeadas; Cancelar con borde 1px sólido azul, Eliminar sin borde. Coincide. - **Layout:** Dos botones a ancho completo lado a lado (\~50/50), Cancelar izquierda / acción destructiva derecha. Estructura idéntica. - **Estados:** Botón destructivo en rol danger, secundario outlined — patrón correcto. - **Salvedad:** Los dos paneles están a escalas muy distintas (Figma 448×308 recorte; app dialog pequeño centrado sobre overlay). La sombra sutil del modal en la app es propia del componente overlay y no es defecto. Sin residuo real que reportar. | | #### Data Table [Section titled “Data Table”](#data-table) **Código:** `src/ui/organisms/DataTable/DataTable.tsx` · **Figma:** [`95:59`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=95-59) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Data Table](/design/parity/figma/organisms/figma-data-table.png)](/design/parity/figma/organisms/figma-data-table.png "figma Data Table — abrir a tamaño completo") | [![app Data Table](/design/parity/history/2026-07-24/screenshots/playground/data-table.png)](/design/parity/history/2026-07-24/screenshots/playground/data-table.png "app Data Table — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Fila seleccionada con tinte `#E6EEFA` medido en Figma vs `#E5EDFA` en app (palette.primary.light) — coincide. Divisores `#E5E7EB`, textos muted grises — OK. - **Estados:** StatusBadges renderizados como píldoras con tinte-soft: Activo `#ECFDF5` (Figma `#ECFCF5`), Agotado `#FEF2F2`, Bajo amarillo-soft — coinciden en patrón y tono. Checkbox seleccionado = relleno sólido `#0052CC` con check blanco y esquinas redondeadas. - **Layout:** Estructura de columnas (avatar+iniciales / Producto / SKU / Categoría / Precio / Stock / Estado / acción), header hairline y avatar con tinte+iniciales azules coinciden. Estados extra del panel app (Cargando/skeleton `#F0F4F8`, Vacío con contenedor de icono tintado, Filas seleccionadas + BulkActionBar) son composiciones del showcase, no en el frame de Figma. - **Iconos:** Salvedad de showcase — el icono de editar se pinta como `IconButton variant="outlined"` (caja redondeada con borde `#E5E7EB`) en cada fila, mientras Figma muestra el glyph sin contenedor (la caja sólo aparece en la fila seleccionada/hover). Es decisión del showcase (`DataTableShowcase.tsx:150`), no defecto del componente DataTable. Sin residuo real del organismo. | | #### Dialog / Modal [Section titled “Dialog / Modal”](#dialog--modal) **Código:** `src/ui/organisms/Dialog/index.tsx` · **Figma:** [`102:91`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=102-91) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Dialog / Modal](/design/parity/figma/organisms/figma-dialog.png)](/design/parity/figma/organisms/figma-dialog.png "figma Dialog / Modal — abrir a tamaño completo") | [![app Dialog / Modal](/design/parity/history/2026-07-24/screenshots/playground/dialog.png)](/design/parity/history/2026-07-24/screenshots/playground/dialog.png "app Dialog / Modal — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Confirmado tras remediación — Guardar cambios `#0052CC` sólido + texto blanco; Cancelar outlined con borde y texto azul de marca sobre blanco; título `#1F2933`; ✕ gris (`text.secondary`); scrim gris. Coincide. - **Bordes:** Campos con relleno blanco + hairline `#E5E7EB` redondeado, alto \~40 con label estático arriba. Coincide. - **Layout:** Residuo real — Figma dibuja un **divisor hairline bajo el header** y otro **sobre el footer**; la app los omite (header/body/footer fluyen sin líneas). Culpable: `src/ui/organisms/Dialog/index.tsx` usa `DialogTitle`/`DialogActions` sin `borderBottom`/`borderTop` (`dividers`). - **Estados:** En Figma el 2º campo aparece **enfocado** (aro azul); en la app está sin foco con valor “Texto activo”. Es decisión de estado del showcase (`DialogShowcase.tsx`), **no defecto**. - **Iconos:** ✕ de cierre con `size={18}`, color gris, alineado a la derecha del título. Coincide. - **Tipografía:** Título bold, labels bold, helper y placeholder en gris. Coincide.Estructura, tokens y botones ya conformes; único pendiente accionable = añadir los divisores de header/footer. | | #### Filter Bar [Section titled “Filter Bar”](#filter-bar) **Código:** `src/ui/organisms/DataTable/FilterBar.tsx` · **Figma:** [`99:73`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=99-73) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Filter Bar](/design/parity/figma/organisms/figma-filter-bar.png)](/design/parity/figma/organisms/figma-filter-bar.png "figma Filter Bar — abrir a tamaño completo") | [![app Filter Bar](/design/parity/history/2026-07-24/screenshots/playground/filter-bar.png)](/design/parity/history/2026-07-24/screenshots/playground/filter-bar.png "app Filter Bar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Trigger “Filtros” medido en #0052CC (borde + texto + icono) en ambos paneles; enlace “Quitar todos” #0052CC. Coincide con azul de marca. - **Bordes:** Campo de búsqueda blanco #FFFFFF con hairline y botón outlined 1px sólido; dropdown “Selecciona una opción” con hairline. Alineado con canon. - **Estados:** Badge contador = círculo relleno SÓLIDO #0052CC + “2” blanco (excepción correcta de relleno para contadores). Chips de “Filtros aplicados” con outline hairline y close en gris adorno (\~#9CA3AF). - **Iconos:** El showcase añade icono de hamburguesa antes de “Filtros” (Figma sin icono) — decisión del showcase, no defecto. - **Layout:** Figma muestra segmented (Todos/Activos/Bajo stock); el showcase lo sustituye por dos filas de búsqueda + fila de chips “Filtros aplicados” — contenido de showcase, no residuo del componente. - **Color (nota, no defecto):** El campo de búsqueda en Figma trae relleno tintado #F0F4F8; la app lo pinta blanco #FFFFFF, consistente con la decisión canónica de campos blancos+hairline (fix verificado). | | #### Page Header [Section titled “Page Header”](#page-header) **Código:** `src/ui/organisms/PageHeader/PageHeader.tsx` · **Figma:** [`94:31`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=94-31) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Page Header](/design/parity/figma/organisms/figma-page-header.png)](/design/parity/figma/organisms/figma-page-header.png "figma Page Header — abrir a tamaño completo") | [![app Page Header](/design/parity/history/2026-07-24/screenshots/playground/page-header.png)](/design/parity/history/2026-07-24/screenshots/playground/page-header.png "app Page Header — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Botón primario medido **#0052CC** en Figma y app; fondo de sección #F5F7FA / #F5F5F5 (escalas distintas, equivalente); subtítulo gris \~#868C98 acorde a #6B7280. Azul de marca ya remediado y verificado. - **Tipografía:** Título en peso alto y color oscuro (navy \~#1F2933), subtítulo secundario en gris, eyebrow en gris tenue mayúsculas — jerarquía idéntica. - **Layout:** Patrón título + subtítulo + acción primaria alineada a la derecha coincide. El panel APP muestra además las variantes *solo título* y *con breadcrumbs* — decisión del showcase de /playground, no defecto del componente. - **Bordes:** Botón con esquinas redondeadas y texto blanco `+ Nuevo producto` igual en ambos. - **Iconos:** Icono `+` de adorno dentro del botón presente en ambos paneles. - **Estados:** Sin residuo medible. Coincide. | | #### Pagination [Section titled “Pagination”](#pagination) **Código:** `src/ui/organisms/DataTable/Pagination.tsx` · **Figma:** [`94:43`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=94-43) | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Pagination](/design/parity/figma/organisms/figma-pagination.png)](/design/parity/figma/organisms/figma-pagination.png "figma Pagination — abrir a tamaño completo") | [![app Pagination](/design/parity/history/2026-07-24/screenshots/playground/pagination.png)](/design/parity/history/2026-07-24/screenshots/playground/pagination.png "app Pagination — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Página activa con relleno SÓLIDO primary — medido **#0052CC** tanto en app (`205,134`) como en Figma (`65,17`), con texto blanco. Es la excepción al patrón de tinte y está bien aplicada. - **Estados:** Tiles inactivos planos blancos con hairline (#E5E7EB) y numeral oscuro; activo destaca por el relleno azul. Correcto. - **Bordes:** Esquinas redondeadas en los tiles coinciden; chevrons ‹ › como botones ghost sin relleno. - **Layout:** El showcase muestra 3 variantes (página actual / primera / última) — decisión del showcase, no defecto. Figma solo muestra el patrón base. - **Tipografía:** Numerales centrados, peso consistente entre ambos. - Sin residuo real del componente. | | #### POS — Approval Modal [Section titled “POS — Approval Modal”](#pos--approval-modal) **Código:** `src/ui/organisms/pos/ApprovalModal/index.tsx` · **Figma:** [`104:161`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=104-161) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Approval Modal](/design/parity/figma/organisms/figma-pos-approval-modal.png)](/design/parity/figma/organisms/figma-pos-approval-modal.png "figma POS — Approval Modal — abrir a tamaño completo") | [![app POS — Approval Modal](/design/parity/history/2026-07-24/screenshots/playground/pos-approval-modal.png)](/design/parity/history/2026-07-24/screenshots/playground/pos-approval-modal.png "app POS — Approval Modal — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Marca #0052CC verificado (PIN dots en Figma, glifo de backspace en ambos). Lock tile warning-soft `#FFFBEB` + glifo `#F59E0B` coinciden exacto. Error `#EF4444` correcto. - **Color (residuo):** El tile de la tecla borrar usa `theme.palette.action.hover` → renderiza `#F5F5F5`, cuando el diseño pide surface-variant/skeleton `#F0F4F8` (ligero tinte azulado). Diferencia casi imperceptible pero real. Culpable: `src/ui/organisms/pos/ApprovalModal/index.tsx` L253 (`backgroundColor: theme.palette.action.hover`) — el propio comentario dice `--color-surface-variant`; conviene cablearlo al token skeleton/surface-variant. - **Layout:** Figma muestra el par Cancelar (outlined) + Autorizar (filled); la app renderiza solo el link de texto Cancelar. **No es defecto**: el organismo soporta ese par vía `requireConfirm`; el showcase elige a propósito la variante auto-submit shipped (`ApprovalModalShowcase.tsx` L43-49, sin `requireConfirm`). - **Iconos:** Lock relleno naranja y glifo backspace primary coinciden. - **Estados:** Default (dots vacíos, sin PIN tecleado) y PIN incorrecto (dots rojos + mensaje) correctos; el default de Figma con 2 dots llenos es solo un estado de entrada distinto del showcase. - **Tipografía/Bordes:** Título, subtítulo y hairline de campos sin discrepancias medibles. | | #### POS — Cash Tender [Section titled “POS — Cash Tender”](#pos--cash-tender) **Código:** `src/ui/organisms/pos/CashTender/index.tsx` · **Figma:** [`105:200`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=105-200) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Cash Tender](/design/parity/figma/organisms/figma-pos-cash-tender.png)](/design/parity/figma/organisms/figma-pos-cash-tender.png "figma POS — Cash Tender — abrir a tamaño completo") | [![app POS — Cash Tender](/design/parity/history/2026-07-24/screenshots/playground/pos-cash-tender.png)](/design/parity/history/2026-07-24/screenshots/playground/pos-cash-tender.png "app POS — Cash Tender — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación — commit `d515d29`)*- **Color:** Residuo del **Total** cerrado. El monto “Total $406.00” ahora se pinta en **tinta oscura bold (#1F2933)**, no azul — se desacopló el color de `emphasis` en `SummaryRow` (color lo decide `tone`, no el énfasis), así el POS conserva el total oscuro que muestra su frame mientras el SummaryRow standalone sigue en azul vía `tone="primary"`. - **Color:** Callout **Cambio** con tinte de éxito correcto — app `#ECFDF5` vs Figma `#ECFCF5` (`success.light`), label + monto en `success.main` (`CashTender/index.tsx`). Coincide. - **Color:** Fondo de los chips de monto `#F5F5F5` coincide con Figma. - **Estados:** El botón `Confirmar` disabled = `#E5E7EB` + texto `#9CA3AF` (input vacío en el showcase); al habilitarse usa `variant="filled"` (azul sólido, como Figma). Diferencia de estado, no defecto. Ningún chip aparece seleccionado (input vacío) — no evaluable en esta captura. - **Bordes/Iconos:** Input blanco + hairline + label estático arriba; ✕ de cierre gris en ambos. Coincide. - **Datos del showcase (no defectos):** título “Pago en efectivo”/label “Recibido” vs. “Efectivo”/“Monto recibido” del Figma, y chips `$406/$500/$1000` — copy y props por defecto del showcase. | | #### POS — Discount Sheet [Section titled “POS — Discount Sheet”](#pos--discount-sheet) **Código:** `src/ui/organisms/pos/DiscountSheet/index.tsx` · **Figma:** [`104:214`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=104-214) | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Discount Sheet](/design/parity/figma/organisms/figma-pos-discount-sheet.png)](/design/parity/figma/organisms/figma-pos-discount-sheet.png "figma POS — Discount Sheet — abrir a tamaño completo") | [![app POS — Discount Sheet](/design/parity/history/2026-07-24/screenshots/playground/pos-discount-sheet.png)](/design/parity/history/2026-07-24/screenshots/playground/pos-discount-sheet.png "app POS — Discount Sheet — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** *(re-auditado tras remediación)*- **Color:** Verificado y coincide. Thumb del segmented blanco `#FFFFFF` sobre track `#F5F5F5`; banner de total con tinte verde `#ECFDF5` + cifra esmeralda (residuo del highlight success **cerrado**); botón disabled `#E5E7EB` + texto `#9CA3AF`; texto principal `#2D363F`. Todos los fixes de la fase 2 se sostienen. - **Layout (feature-gap):** Falta el caption de autorización que Figma muestra bajo el campo (`Máx. 20% sin autorización`). El showcase no lo renderiza — verificar si el organism expone esa prop de límite/ayuda. - **Layout (feature-gap):** Falta el selector de **Motivo** (Figma: dropdown `Motivo: Cliente frecuente`). La app omite el campo de razón del descuento; es un elemento funcional del sheet, no solo texto de ejemplo. - **Estados:** El botón `Aplicar descuento` aparece en estado **disabled** (campo vacío en el showcase), por lo que no se compara directo el azul sólido del CTA de Figma; la geometría y el tono disabled sí coinciden. - **Bordes/Iconos:** Coinciden — card plana con hairline, campo blanco con borde suave, icono de cierre `×` en gris de adorno. - **Tipografía:** Coincide. Diferencias de etiqueta (`%/Monto/Cupón` vs `Porcentaje/Monto fijo/Cupón`, `Nuevo total` vs `Total con descuento`) son decisión del showcase, no defecto.Residuos remanentes = **feature-gaps** (selector de Motivo + caption de autorización), no defectos de token/estilo; se difieren al flujo de checkout. | | #### POS — Result Modal [Section titled “POS — Result Modal”](#pos--result-modal) **Código:** `src/ui/organisms/pos/ResultModal/index.tsx` | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Result Modal](/design/parity/figma/organisms/figma-pos-result-modal-context.png)](/design/parity/figma/organisms/figma-pos-result-modal-context.png "figma POS — Result Modal — abrir a tamaño completo") | [![app POS — Result Modal](/design/parity/history/2026-07-24/screenshots/playground/pos-result-modal.png)](/design/parity/history/2026-07-24/screenshots/playground/pos-result-modal.png "app POS — Result Modal — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Azul de marca del CTA “Nueva venta” medido **#0052CC** en Figma y app (idéntico). Check de éxito **#10B981** en ambos. Halo/icono de error usan rol error **#EF4444 + #FDE8E8** (tinte \*-soft), correcto. - **Tipografía:** Título “¡Venta completada!” #1F2933 en los dos; subtítulo folio en gris secundario; pesos y jerarquía coinciden. - **Espaciado:** Estructura vertical (check → título → folio → bloque resumen → fila de acciones → CTA) idéntica en proporción. - **Bordes:** Tarjeta modal plana con esquinas redondeadas; botones outlined con borde 1px sólido azul; bloque-resumen tintado (#F7F9FC Figma / #F5F7FA app, equivalentes). - **Iconos:** Check relleno blanco sobre círculo verde igual; en la variante error, `!` blanco sobre círculo rojo con halo tenue. - **Layout:** Tres botones outlined (Imprimir/Compartir/Email) en fila + CTA sólido a ancho completo debajo, igual que Figma. - **Estados:** El panel APP añade la variante **ERROR — Reintentar** que el contexto Figma no incluye (solo muestra éxito). Es decisión del showcase para exhibir el estado de error, no un defecto del componente. | | #### POS — Split Payment [Section titled “POS — Split Payment”](#pos--split-payment) **Código:** `src/ui/organisms/pos/SplitPayment/index.tsx` · **Figma:** [`105:253`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=105-253) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Split Payment](/design/parity/figma/organisms/figma-pos-split-payment.png)](/design/parity/figma/organisms/figma-pos-split-payment.png "figma POS — Split Payment — abrir a tamaño completo") | [![app POS — Split Payment](/design/parity/history/2026-07-24/screenshots/playground/pos-split-payment.png)](/design/parity/history/2026-07-24/screenshots/playground/pos-split-payment.png "app POS — Split Payment — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** *(re-auditado tras remediación)*Todos los tokens remediados verifican; el residuo es de **estado/composición del showcase**, no de color.- **Color:** Sin defecto. Medido en ambos paneles: azul de marca **#0052CC** (Total / borde “Agregar pago”), banner Restante **#FFFBEB** + texto **#F59E0B** (idéntico Figma↔app), segmented thumb **#FFFFFF** sobre contenedor **#F5F5F5**, CTA deshabilitado gris. Coincide (el tinte *warning* del Restante ya está aplicado). - **Layout:** Los paneles renderizan **estados distintos** del organism: Figma muestra el estado lleno (lista de pagos Efectivo/Tarjeta con filas tintadas #F7F9FC, íconos y X por fila, botón outlined “Agregar pago”, CTA “Finalizar venta”); la app muestra el estado de **entrada** (Total + selector segmented + input Monto + botón “Agregar” + Restante). No es defecto de remediación: es otra pantalla del mismo flujo. - **Estados:** El botón “Confirmar”/“Agregar” aparece deshabilitado (Monto vacío) con relleno tonal gris, consistente con el “Finalizar venta” deshabilitado del Figma. - **Tipografía:** Título “Pago dividido” vs “Pago mixto” — texto de ejemplo del showcase, no defecto. - **Pendiente de verificar:** con el showcase en estado vacío no se confirma en esta captura el estilo de las **filas de pago** (fondo #F7F9FC, ícono, X de eliminar) ni el botón **outlined “Agregar pago”** (borde #0052CC 1px). Se recomienda una variante del showcase con pagos ya agregados para cerrar la paridad estructural. | | #### POS — Tender Picker [Section titled “POS — Tender Picker”](#pos--tender-picker) **Código:** `src/ui/organisms/pos/TenderPicker/index.tsx` · **Figma:** [`105:151`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=105-151) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Tender Picker](/design/parity/figma/organisms/figma-pos-tender-picker.png)](/design/parity/figma/organisms/figma-pos-tender-picker.png "figma POS — Tender Picker — abrir a tamaño completo") | [![app POS — Tender Picker](/design/parity/history/2026-07-24/screenshots/playground/pos-tender-picker.png)](/design/parity/history/2026-07-24/screenshots/playground/pos-tender-picker.png "app POS — Tender Picker — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes** — el patrón del componente diverge del diseño. Figma es un *selector de método* de una sola pantalla (chip Total, radios de selección con estado activo, subtítulos, iconos en contenedor tintado, CTA Continuar); la app es una *lista de navegación con chevron* que solo emite intención y hace drill-in a subflujos. Los tokens que sí están presentes coinciden con el canon; el residuo es estructural.- **Color:** Lo medido coincide — Figma: chip Total y fila seleccionada `#E6EEFA` (primary.light), Continuar `#0052CC`, contenedor de icono `#F0F4F8`. App: card blanca, borde `#E7E9EC`≈`#E5E7EB`, texto oscuro. No hay defecto de color en lo renderizado. - **Layout:** Falta el **chip “Total a cobrar”** superior (tinte `#E6EEFA` + monto en primary bold). No existe en `TenderPicker/index.tsx`. - **Layout:** Falta el **botón “Continuar”** (relleno sólido `#0052CC`, texto blanco) al pie del diálogo. - **Estados:** No hay **modelo de selección** — Figma usa radios/checkbox con fila activa (borde primary + tinte `#E6EEFA` + check relleno); la app usa `ChevronRight` de drill-in y `onSelect` sin estado seleccionado (líneas 96–98). - **Tipografía:** Faltan los **subtítulos** por opción (“Pago en efectivo + cambio”, etc.); la app solo muestra `label` (línea 93–95). - **Iconos:** Iconos **sin contenedor tintado** — heredan `text.primary` (#1F2933) bare (línea 92), mientras Figma los envuelve en cuadro redondeado `#F0F4F8` (y verde para Efectivo). El fix “contenedor tintado de iconos en ListRow” no está aplicado aquí. - **Salvedad:** el chevron vs radio no es “texto de ejemplo del showcase” sino un modelo de interacción distinto → residuo real del componente, no decisión del showcase. - **Culpable:** `src/ui/organisms/pos/TenderPicker/index.tsx` (no contempla total, subtítulo, selección ni CTA). | | #### Scrim / Overlay [Section titled “Scrim / Overlay”](#scrim--overlay) **Código:** `src/ui/organisms/Scrim/index.tsx` | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — sin render exportado en `design/web/organisms/` | [![app Scrim / Overlay](/design/parity/history/2026-07-24/screenshots/playground/scrim.png)](/design/parity/history/2026-07-24/screenshots/playground/scrim.png "app Scrim / Overlay — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Impersonation Banner [Section titled “Impersonation Banner”](#impersonation-banner) **Código:** `src/ui/organisms/ImpersonationBanner/index.tsx` | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — sin render exportado en `design/web/organisms/` | [![app Impersonation Banner](/design/parity/history/2026-07-24/screenshots/playground/impersonation-banner.png)](/design/parity/history/2026-07-24/screenshots/playground/impersonation-banner.png "app Impersonation Banner — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Side Nav [Section titled “Side Nav”](#side-nav) **Código:** `src/ui/organisms/SideNav/SideNav.tsx` · **Figma:** [`97:35`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=97-35) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Side Nav](/design/parity/figma/organisms/figma-side-nav.png)](/design/parity/figma/organisms/figma-side-nav.png "figma Side Nav — abrir a tamaño completo") | [![app Side Nav](/design/parity/history/2026-07-24/screenshots/playground/side-nav.png)](/design/parity/history/2026-07-24/screenshots/playground/side-nav.png "app Side Nav — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Ítem activo con tinte de marca **#E6EEFA** e ícono/texto **#0052CC** (medido en los 3 estados del showcase). Riel colapsado reproduce el mismo par tinte+azul en el ítem activo. Encabezados de sección en gris neutro (\~#9CA3AF). Ítems inactivos en neutro oscuro sobre blanco. - **Tipografía:** Encabezados de sección en mayúsculas/tracking; label de ítem activo en peso semibold azul; inactivos en neutro. Jerarquía equivalente al diseño. - **Espaciado:** Agrupación por secciones (OPERACIÓN, CATÁLOGO, EQUIPO, FINANZAS, ADMINISTRACIÓN) con el mismo orden y densidad de ítems que Figma. - **Bordes:** Píldora activa con esquinas redondeadas, sin borde; consistente con el patrón de selección tintado. - **Iconos:** Íconos de ítems inactivos en neutro oscuro (estilo relleno). Algunos glifos difieren respecto a Figma (p. ej. Punto de venta = caja registradora vs carrito; Productos = etiqueta vs regalo; Servicios = factura vs flechas de intercambio; Reportes = línea vs barras) — **decisión del showcase (íconos de ejemplo), no defecto**. - **Layout:** El showcase de /playground muestra 3 estados (expandido activo, activo por módulo, colapsado) y omite el logo “Terminales”, el selector de tienda y el footer de usuario que sí aparecen en Figma — **fuera del alcance del showcase, no residuo del componente**. - **Estados:** Activo, inactivo y colapsado-activo verificados con color medido; todos siguen el patrón canónico tinte #E6EEFA + #0052CC. | | #### Sticky Save Bar [Section titled “Sticky Save Bar”](#sticky-save-bar) **Código:** `src/ui/organisms/StickySaveBar/StickySaveBar.tsx` · **Figma:** [`103:134`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=103-134) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Sticky Save Bar](/design/parity/figma/organisms/figma-sticky-save-bar.png)](/design/parity/figma/organisms/figma-sticky-save-bar.png "figma Sticky Save Bar — abrir a tamaño completo") | [![app Sticky Save Bar](/design/parity/history/2026-07-24/screenshots/playground/sticky-save-bar.png)](/design/parity/history/2026-07-24/screenshots/playground/sticky-save-bar.png "app Sticky Save Bar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Botón *Guardar* con relleno sólido #0052CC medido idéntico en Figma y app. Botón de texto *Descartar* en azul de marca (trazos medidos #4681DA/#82AAE6, antialiasing de texto primary) en ambos. - **Iconos:** El icono de información es un círculo relleno neutro #6B7280 en ambos paneles (medido exacto), con la “i” blanca. - **Tipografía:** Mensaje “Tienes cambios sin guardar” en gris neutro; jerarquía y peso equivalentes. - **Layout:** Barra plana con hairline; a la izquierda icono + mensaje, a la derecha *Descartar* (botón de texto) + *Guardar* (botón relleno primary). Orden y alineación coinciden. - **Estados:** El patrón de botón relleno primary (excepción de relleno sólido) se respeta en *Guardar*; *Descartar* como acción secundaria de texto. - **Salvedad showcase:** El contenedor con borde y el área de vista previa tenue por encima de la barra son marco del showcase de /playground, no defecto del componente. | | #### Tabs [Section titled “Tabs”](#tabs) **Código:** `src/ui/organisms/Tabs/Tabs.tsx` · **Figma:** [`94:6`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=94-6) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Tabs](/design/parity/figma/organisms/figma-tabs.png)](/design/parity/figma/organisms/figma-tabs.png "figma Tabs — abrir a tamaño completo") | [![app Tabs](/design/parity/history/2026-07-24/screenshots/playground/tabs.png)](/design/parity/history/2026-07-24/screenshots/playground/tabs.png "app Tabs — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Pestaña activa medida en **#0052CC** tanto en Figma (texto `30,18`) como en la app (texto `58,97`); el indicador inferior también mide **#0052CC** en la app (`80,118`). Azul de marca correcto. - **Bordes:** Divisor/hairline bajo la tira de pestañas medido en **#E5E7EB** en ambos (`70,120` app). Coincide. - **Tipografía:** Pestaña activa en semibold azul + inactivas en gris neutro; jerarquía idéntica a Figma. - **Estados:** El patrón de selección (texto primary + subrayado sólido primary) está correctamente aplicado; MUI Tabs con `indicator` en primary. - **Layout:** El panel APP es un showcase de /playground con tres filas extra (activa+indicador / hover / disabled) que Figma no muestra — decisión del showcase, no defecto. El estado hover destaca “Historial” y el disabled atenúa las pestañas, coherentes con los tokens. - **Espaciado:** Fondo del contenedor difiere (Figma #F5F5F5 neutro vs playground #F5F7FA) pero es el lienzo del showcase, no el componente.Sin residuo real del componente. | | #### Top Bar [Section titled “Top Bar”](#top-bar) **Código:** `src/ui/organisms/TopBar/TopBar.tsx` · **Figma:** [`98:51`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=98-51) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Top Bar](/design/parity/figma/organisms/figma-top-bar.png)](/design/parity/figma/organisms/figma-top-bar.png "figma Top Bar — abrir a tamaño completo") | [![app Top Bar](/design/parity/history/2026-07-24/screenshots/playground/top-bar.png)](/design/parity/history/2026-07-24/screenshots/playground/top-bar.png "app Top Bar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Marca #0052CC medido en el logo del row *Punto de venta* y en el ícono del avatar; tinte de avatar #E6EEFA (palette.primary.light) confirmado. Badge de notificación #EF4444 (error). Todo canónico. - **Tipografía:** Título #1F2933, secundario “Bienvenido” en gris, jerarquía correcta. - **Iconos:** Campana de adorno #6B7280/#9CA3AF (no azul), correcta; ícono de avatar relleno sobre contenedor tintado, patrón esperado. - **Bordes:** Tarjetas planas con hairline claro; divisor vertical sutil entre acciones y menú de usuario. - **Layout:** El panel APP es un showcase con 3 variantes (base sin badge / con logo+badge+menú / con navegación atrás), mientras Figma muestra una única barra con buscador + chips de estado (Turno abierto, Sincronizado) + grupo de avatares. Esa diferencia de contenido de ejemplo es decisión del showcase, no defecto — los tokens y estados subyacentes coinciden. - **Estados:** Menú de usuario con avatar tintado + iniciales/ícono y badge numérico correctos; sin residuo de paridad detectable. | | *** ## Apéndice — otras vistas de `/playground` [Section titled “Apéndice — otras vistas de /playground”](#apéndice--otras-vistas-de-playground) El registro de `/playground` también incluye **composiciones de pantalla** hechas con el design system y algunos tableros de flujo completo. Cuando la pantalla ya tiene ruta real, arriba se muestra la captura de la app; estas son las que quedan. Sirven como *referencia de implementación* — cómo se vería la pantalla armada solo con componentes del DS — y son un buen punto de partida cuando toque construir la pantalla de verdad. #### cuenta-flow [Section titled “cuenta-flow”](#cuenta-flow) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app cuenta-flow](/design/parity/history/2026-07-24/screenshots/playground/cuenta-flow.png)](/design/parity/history/2026-07-24/screenshots/playground/cuenta-flow.png "app cuenta-flow — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### reportes-flow [Section titled “reportes-flow”](#reportes-flow) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app reportes-flow](/design/parity/history/2026-07-24/screenshots/playground/reportes-flow.png)](/design/parity/history/2026-07-24/screenshots/playground/reportes-flow.png "app reportes-flow — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### ventas-flow [Section titled “ventas-flow”](#ventas-flow) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app ventas-flow](/design/parity/history/2026-07-24/screenshots/playground/ventas-flow.png)](/design/parity/history/2026-07-24/screenshots/playground/ventas-flow.png "app ventas-flow — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w25-stock [Section titled “w25-stock”](#w25-stock) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w25-stock](/design/parity/history/2026-07-24/screenshots/playground/w25-stock.png)](/design/parity/history/2026-07-24/screenshots/playground/w25-stock.png "app w25-stock — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w26-ajuste [Section titled “w26-ajuste”](#w26-ajuste) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w26-ajuste](/design/parity/history/2026-07-24/screenshots/playground/w26-ajuste.png)](/design/parity/history/2026-07-24/screenshots/playground/w26-ajuste.png "app w26-ajuste — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w27-movimientos [Section titled “w27-movimientos”](#w27-movimientos) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w27-movimientos](/design/parity/history/2026-07-24/screenshots/playground/w27-movimientos.png)](/design/parity/history/2026-07-24/screenshots/playground/w27-movimientos.png "app w27-movimientos — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w28-transferencia [Section titled “w28-transferencia”](#w28-transferencia) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w28-transferencia](/design/parity/history/2026-07-24/screenshots/playground/w28-transferencia.png)](/design/parity/history/2026-07-24/screenshots/playground/w28-transferencia.png "app w28-transferencia — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w29-ventas [Section titled “w29-ventas”](#w29-ventas) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w29-ventas](/design/parity/history/2026-07-24/screenshots/playground/w29-ventas.png)](/design/parity/history/2026-07-24/screenshots/playground/w29-ventas.png "app w29-ventas — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w30-detalle-venta [Section titled “w30-detalle-venta”](#w30-detalle-venta) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w30-detalle-venta](/design/parity/history/2026-07-24/screenshots/playground/w30-detalle-venta.png)](/design/parity/history/2026-07-24/screenshots/playground/w30-detalle-venta.png "app w30-detalle-venta — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w31-cancelar-reembolsar [Section titled “w31-cancelar-reembolsar”](#w31-cancelar-reembolsar) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w31-cancelar-reembolsar](/design/parity/history/2026-07-24/screenshots/playground/w31-cancelar-reembolsar.png)](/design/parity/history/2026-07-24/screenshots/playground/w31-cancelar-reembolsar.png "app w31-cancelar-reembolsar — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w31-rechazo-pin [Section titled “w31-rechazo-pin”](#w31-rechazo-pin) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w31-rechazo-pin](/design/parity/history/2026-07-24/screenshots/playground/w31-rechazo-pin.png)](/design/parity/history/2026-07-24/screenshots/playground/w31-rechazo-pin.png "app w31-rechazo-pin — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w31-reembolso-parcial [Section titled “w31-reembolso-parcial”](#w31-reembolso-parcial) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w31-reembolso-parcial](/design/parity/history/2026-07-24/screenshots/playground/w31-reembolso-parcial.png)](/design/parity/history/2026-07-24/screenshots/playground/w31-reembolso-parcial.png "app w31-reembolso-parcial — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w32-turnos [Section titled “w32-turnos”](#w32-turnos) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w32-turnos](/design/parity/history/2026-07-24/screenshots/playground/w32-turnos.png)](/design/parity/history/2026-07-24/screenshots/playground/w32-turnos.png "app w32-turnos — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w33-detalle-z [Section titled “w33-detalle-z”](#w33-detalle-z) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w33-detalle-z](/design/parity/history/2026-07-24/screenshots/playground/w33-detalle-z.png)](/design/parity/history/2026-07-24/screenshots/playground/w33-detalle-z.png "app w33-detalle-z — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w34-abrir-turno [Section titled “w34-abrir-turno”](#w34-abrir-turno) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w34-abrir-turno](/design/parity/history/2026-07-24/screenshots/playground/w34-abrir-turno.png)](/design/parity/history/2026-07-24/screenshots/playground/w34-abrir-turno.png "app w34-abrir-turno — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w35-cerrar-turno [Section titled “w35-cerrar-turno”](#w35-cerrar-turno) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w35-cerrar-turno](/design/parity/history/2026-07-24/screenshots/playground/w35-cerrar-turno.png)](/design/parity/history/2026-07-24/screenshots/playground/w35-cerrar-turno.png "app w35-cerrar-turno — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w36-movimientos-caja [Section titled “w36-movimientos-caja”](#w36-movimientos-caja) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w36-movimientos-caja](/design/parity/history/2026-07-24/screenshots/playground/w36-movimientos-caja.png)](/design/parity/history/2026-07-24/screenshots/playground/w36-movimientos-caja.png "app w36-movimientos-caja — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w37-empleados [Section titled “w37-empleados”](#w37-empleados) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w37-empleados](/design/parity/history/2026-07-24/screenshots/playground/w37-empleados.png)](/design/parity/history/2026-07-24/screenshots/playground/w37-empleados.png "app w37-empleados — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w38-empleado-edit [Section titled “w38-empleado-edit”](#w38-empleado-edit) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w38-empleado-edit](/design/parity/history/2026-07-24/screenshots/playground/w38-empleado-edit.png)](/design/parity/history/2026-07-24/screenshots/playground/w38-empleado-edit.png "app w38-empleado-edit — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w39-invitar [Section titled “w39-invitar”](#w39-invitar) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w39-invitar](/design/parity/history/2026-07-24/screenshots/playground/w39-invitar.png)](/design/parity/history/2026-07-24/screenshots/playground/w39-invitar.png "app w39-invitar — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w40-roles [Section titled “w40-roles”](#w40-roles) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w40-roles](/design/parity/history/2026-07-24/screenshots/playground/w40-roles.png)](/design/parity/history/2026-07-24/screenshots/playground/w40-roles.png "app w40-roles — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w55-reportes-overview [Section titled “w55-reportes-overview”](#w55-reportes-overview) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w55-reportes-overview](/design/parity/history/2026-07-24/screenshots/playground/w55-reportes-overview.png)](/design/parity/history/2026-07-24/screenshots/playground/w55-reportes-overview.png "app w55-reportes-overview — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w56-reporte-ventas [Section titled “w56-reporte-ventas”](#w56-reporte-ventas) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w56-reporte-ventas](/design/parity/history/2026-07-24/screenshots/playground/w56-reporte-ventas.png)](/design/parity/history/2026-07-24/screenshots/playground/w56-reporte-ventas.png "app w56-reporte-ventas — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w58-cortes-z [Section titled “w58-cortes-z”](#w58-cortes-z) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w58-cortes-z](/design/parity/history/2026-07-24/screenshots/playground/w58-cortes-z.png)](/design/parity/history/2026-07-24/screenshots/playground/w58-cortes-z.png "app w58-cortes-z — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w59-mi-cuenta [Section titled “w59-mi-cuenta”](#w59-mi-cuenta) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w59-mi-cuenta](/design/parity/history/2026-07-24/screenshots/playground/w59-mi-cuenta.png)](/design/parity/history/2026-07-24/screenshots/playground/w59-mi-cuenta.png "app w59-mi-cuenta — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w61-ajustes [Section titled “w61-ajustes”](#w61-ajustes) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w61-ajustes](/design/parity/history/2026-07-24/screenshots/playground/w61-ajustes.png)](/design/parity/history/2026-07-24/screenshots/playground/w61-ajustes.png "app w61-ajustes — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w62-bitacora [Section titled “w62-bitacora”](#w62-bitacora) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w62-bitacora](/design/parity/history/2026-07-24/screenshots/playground/w62-bitacora.png)](/design/parity/history/2026-07-24/screenshots/playground/w62-bitacora.png "app w62-bitacora — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | #### w65-cerrar-denominacion [Section titled “w65-cerrar-denominacion”](#w65-cerrar-denominacion) | Diseño Figma | Captura de la app | | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | *Vista de referencia del design system — sin par en Figma.* | [![app w65-cerrar-denominacion](/design/parity/history/2026-07-24/screenshots/playground/w65-cerrar-denominacion.png)](/design/parity/history/2026-07-24/screenshots/playground/w65-cerrar-denominacion.png "app w65-cerrar-denominacion — abrir a tamaño completo") | | Notas | | | *Pendiente de anotar* | | # Parity — snapshot 2026-08-07 > Copia congelada de la galería Figma ↔ App al 2026-08-07 (solo lectura). > 📸 **Snapshot congelado del 2026-08-07.** Copia de solo lectura de la galería de paridad en esa fecha: las capturas de la app están congeladas; los frames de Figma se comparten con la galería viva. La versión que evoluciona está en [/design/parity/](/design/parity/). > **Qué es esto:** una galería lado a lado del **diseño en Figma** y la **pantalla real** de `terminales-web`, organizada por [historia de usuario](https://github.com/inspiraCode/terminales-solution/blob/main/docs/product/user-stories.md) y, al final, por capa del design system (átomos · moléculas · organismos). > > **Para qué sirve:** > > 1. Ver de un vistazo **en qué difiere la app del diseño**, para poder priorizar los ajustes. > 2. **Mostrar avance al cliente** — cada historia de usuario con sus pantallas reales. > 3. Tener un lugar donde **anotar** observaciones por pantalla y por componente. > > **Plan de remediación:** el orden de ataque para cerrar estos gaps con el mínimo de trabajo está en `remediation-plan` — se arregla por capas (tokens → theme → átomos → patrones), no pantalla por pantalla. > > **Estado:** las columnas *Notas* están **vacías a propósito** — la anotación de diferencias es el siguiente paso. Las celdas marcadas `⚠️ Falta …` señalan trabajo pendiente: o falta exportar el render de Figma, o falta capturar la pantalla. ## Fuentes [Section titled “Fuentes”](#fuentes) | Qué | De dónde | | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | Renders de Figma (`figma/`) | `terminales-solution/docs/design/web/{screens,atomic,molecules,organisms}/` — archivo Figma “Terminales - Claude” `ndebIq7cqexBCf9eKVU0Fm` | | Capturas de la app (`screenshots/`) | worktree `351-admin-shell`, `src/app/**/screenshot.png` (59 páginas), renombradas por ruta | | Historias de usuario | `terminales-solution/docs/product/user-stories.md` | | Auditoría previa por pantalla | `docs/progress/design-parity-matrix.md` (W01–W65, estado ✅/🟡/⚠) | ## Convenciones [Section titled “Convenciones”](#convenciones) * **Nombre de captura** = la ruta del App Router con `/` → `-`, sin route-groups ni corchetes. `src/app/admin/pos/stock-levels/adjust/screenshot.png` → `screenshots/admin-pos-stock-levels-adjust.png`. * **Nombre de render** = `figma-.png`, tal como se exporta desde `design/web/`. * **`⚠️ Falta diseño`** = no hay render de Figma exportado para ese frame. * **`⚠️ Falta captura`** = la pantalla no se capturó (es un modal/estado, o la ruta no existe). ## Cómo actualizar esta galería [Section titled “Cómo actualizar esta galería”](#cómo-actualizar-esta-galería) Dos comandos, en este orden. Ambos son idempotentes: se pueden re-correr cuando cambie la app. ```bash pnpm screenshots # recaptura docs/design-parity/screenshots/ (~5 min) pnpm docs:parity # reconstruye este README con lo que haya en disco ``` `pnpm screenshots` levanta **su propio** servidor de producción en el puerto **4000** (`next build && next start` sobre un `distDir` propio, `.next-screenshots`), con `NEXT_PUBLIC_USE_MOCKS=true` y **todos** los feature flags en ON. Eso significa que: * no pisa un `pnpm dev` que ya tengas abierto en el 3000; * ninguna ruta cae en `notFound()` por un flag apagado, así que la galería no tiene huecos; * los datos son los fixtures mock, iguales en cada corrida. Subconjuntos: `pnpm screenshots --grep admin-pos` (el filtro matchea el nombre del archivo). Para reusar un servidor que ya levantaste: `E2E_BASE_URL=http://localhost:4000 pnpm screenshots`. ### Las notas se escriben aquí y se conservan [Section titled “Las notas se escriben aquí y se conservan”](#las-notas-se-escriben-aquí-y-se-conservan) La sección **Notas** de cada pantalla es para escribir a mano — es el objetivo del documento. Hay dos formas de hacerlo, y las dos terminan en el mismo markdown: 1. **En este archivo**, editando la celda directamente. 2. **Desde el navegador**, con el sitio de documentación corriendo en local (`cd terminales-solution/docs-site && pnpm dev`): cada bloque de Notas muestra un botón **✏️ Editar nota** que abre un textarea y, al guardar, **escribe en este archivo**. Es la forma cómoda de anotar, porque tenés las capturas a tamaño grande al lado mientras escribís. Sólo existe en `pnpm dev` — el sitio publicado es estático y de sólo lectura. En ambos casos `pnpm docs:parity` **conserva** lo que escribas: antes de reescribir lee este archivo y recupera toda nota que no sea el placeholder, reinsertándola bajo el mismo encabezado. El ciclo es: 1. `pnpm screenshots` → cambian las imágenes 2. editás la celda **Notas** → tu observación, en markdown 3. `pnpm docs:parity` → se reconstruyen las tablas, tus notas sobreviven Una nota se identifica por su encabezado `####`. Si renombrás un encabezado, su nota queda huérfana: el script **la imprime en consola** al terminar en vez de perderla en silencio. ### Publicación en el sitio de documentación [Section titled “Publicación en el sitio de documentación”](#publicación-en-el-sitio-de-documentación) Este documento se publica en el sitio Starlight (`terminales-solution/docs-site`) como **/design/parity/**. El sitio lo lee *desde este repo* —no hay copia— vía `externalSources` en `docs-site/publish.config.mjs`: copia el markdown y sus imágenes a `public/design/parity/` y reescribe las rutas relativas a absolutas. O sea que el markdown de acá sigue siendo la única fuente de verdad, y las notas que escribas aparecen en el HTML en el próximo `pnpm build` del sitio. Para ver las capturas en grande en el sitio hay dos ayudas: * **⛶ Modo amplio** — un botón arriba del todo que oculta la navegación y el panel derecho; las imágenes pasan de \~345 px a \~610 px de ancho. La preferencia se recuerda. * **Click en cualquier captura** — abre el PNG original a tamaño completo (también funciona en GitHub). **Qué captura** — la lista vive en `e2e/screenshots/targets.ts`, una entrada por ruta más un array `dialogs` para los modales de cada pantalla (varios frames de Figma —W21, W30, W33, W39— son diálogos en la app, no páginas, y un barrido solo-de-rutas los dejaría siempre vacíos). Los componentes del design system salen de `/playground` vía `e2e/screenshots/playground.spec.ts`, que lee el registro de showcases, así que **agregar un showcase lo captura solo**. Agregar una pantalla = agregar una entrada en `targets.ts`; nada más hay que tocar. **Sobre el determinismo:** en dos corridas independientes, 157 de 167 capturas salieron byte-idénticas. Las demás cambian unos pocos bytes con las mismas dimensiones (el showcase de `Skeleton` anima con `animation="wave"` de MUI, cuyos keyframes CSS ignoran la emulación de `prefers-reduced-motion`; el resto es *antialiasing* de fuentes). O sea: `git diff` sobre esta carpeta es buena señal, pero antes de concluir que la UI cambió, mirá la imagen. ## Hallazgos sistémicos [Section titled “Hallazgos sistémicos”](#hallazgos-sistémicos) > Auditoría automatizada que comparó cada render de Figma contra su captura de la app, midiendo los colores con un muestreador de píxeles. **Son defectos de sistema: se arreglan una vez en el token o en el componente base y desaparecen de decenas de pantallas.** > **Estado (2026-07-23) — remediación fases 0–2 aplicada y re-auditada.** Se cableó DESIGN.md como fuente de verdad (\`scripts/generate-tokens.mjs\`), se reescribió el theme MUI con \~15 overrides, se corrigieron los átomos y se promovieron a props reales los hacks \`sx\` de los showcases (BalanceCard tone, IconButton, ProviderTile, ListRow, SummaryRow, Toast, geometría del Checkbox, default del Icon). La **re-auditoría contra las capturas nuevas** mueve el veredicto de **96 ❌ / 7 ⚠️ / 0 ✅** (pre-fix) a **37 ❌ / 58 ⚠️ / 6 ✅** — la mayoría de las pantallas pasó de “importante” a “menor”. Las notas por pantalla de abajo YA reflejan el estado post-remediación. ### Residuo tras la remediación (backlog para la próxima pasada) [Section titled “Residuo tras la remediación (backlog para la próxima pasada)”](#residuo-tras-la-remediación-backlog-para-la-próxima-pasada) > **Estado (2026-07-23, re-verificado contra el código).** La lista original de 11 residuos sistémicos está **casi toda resuelta**: la remediación de fases 0–2 más los batches atómicos cablearon los fixes en el theme/átomos y **cascadearon a las pantallas**. Cada ítem se re-verificó con grep del archivo culpable citado (ver playbook §7, las notas caducan entre batches). Estado real, con evidencia: **✅ Resueltos y verificados en código** 1. **Patrón de selección** — Chip activo = tinte primary.light #E6EEFA + texto primary.main (src/ui/atoms/Chip/index.tsx:58-61); SegmentedControl usa thumb blanco background.paper sobre track (SegmentedControl/index.tsx:83-89), patrón correcto del diseño. Cerrado. 2. **Badges de estado** — StatusBadge (soft por rol) adoptado en 37 puntos de src/app; los culpables citados ya migraron: SalesOrderDetail.tsx:86-87 y ProductList.tsx:95-96 usan StatusBadge (los Chip restantes en ProductList son contadores numéricos). Cerrado. 3. **Nav/lista seleccionado** — theme.ts:84 fija action.selected = primary.soft (#E6EEFA) y SideNav lo consume en su estado Mui-selected. Cerrado. 4. **Borde del Button outlined** — el slot outlined existe en MuiButton (theme.ts:225), 1px sólido primary.main. Cerrado. 5. **Paradigma de etiqueta** — FormField renderiza label estático arriba (Typography como label, FormField/index.tsx:36-49) + placeholder gris, no floating label. Cerrado. 6. **CTA tonal/soft** — HeldOrderCard usa Button variant tonal (HeldOrderCard/index.tsx:118-119), no contained sólido. Cerrado. 7. **Fecha en español** — src/core/utils.ts:20 llama dayjs.locale(es) y el formateo per-fecha usa .locale(es) (utils.ts:307). Cerrado. 8. **Iconos de adorno** — theme.ts:74-76 apunta action.active al gris frío #9CA3AF, no al #757575 default. Cerrado. 9. **White-label del avatar** — WhiteLabelThemeProvider pasa light = lighten(main, 0.9) explícito a augmentColor, que para #0052CC resuelve a #E6EEFA (WhiteLabelThemeProvider.tsx:58-62). Cerrado. **Abierto (residuo real)** 3. **Migración de tablas admin** — \~90% hecho: 31 pantallas ya usan el DataTable compartido (toolbar FilterBar + Pagination numerada + selección por fila) y 0 usan ya TablePagination de MUI. Quedan 4 archivos con tabla MUI cruda: PosStock/StockLevelList.tsx, PosStock/StockMovementList.tsx, Reports/ZHistoryTable.tsx, Reports/ReportView\.tsx — candidatos a migrar (algunas vistas de reporte pueden querer layout propio; evaluar caso por caso). 4. **Iconografía divergente** (cosmético) — glifos outlined de MUI y chevrons donde el diseño usa glifos rellenos/triángulos, y glifos desnudos donde el diseño los envuelve en contenedor cuadrado tintado #F0F4F8/#E6EEFA — Banner, Toast, KPI Stat Card, Empty State, FAB, TenderPicker. Baja prioridad. > **Implicación:** varias notas por pantalla de abajo se escribieron antes de que estos fixes sistémicos cascadearan y pueden estar sobre-estimadas. Al re-auditar una pantalla, verificar primero contra este estado antes de re-reportar un residuo ya cerrado. ### Fixes confirmados en las capturas [Section titled “Fixes confirmados en las capturas”](#fixes-confirmados-en-las-capturas) * Azul canónico #0052CC medido exacto en fills, links, focus rings e iconos activos (con hover/pressed #003D99, disabled #E5E7EB+#9CA3AF) en casi todos los pares. * Tinte de marca #E6EEFA (palette.primary.light) ahora existe y se usa en Avatar, IconButton, Pagination, Balance Card CTA, Customer Row, chip de BulkActionBar, fila activa de Command Palette y filas no-leídas de Notificaciones. * Avatar corregido: relleno tinte #E6EEFA + iniciales #0052CC (antes solid primary invertido), diámetros exactos 32/40/56px. * Fondos soft por rol confirmados en Alert/Banner/StatusBadge/Toast: info #EFF6FF, success #ECFDF5, warning #FFFBEB, danger #FEF2F2, con textos #3B82F6/#10B981/#F59E0B/#EF4444. * Campos de formulario: relleno blanco, borde hairline #E5E7EB, focus #0052CC 2px, error #EF4444, disabled bg #E5E7EB, alto \~40px — medidos en FormField, Textarea, Select, Denomination Row y campos POS. * Controles de selección: unchecked #D1D5DB, checked/indeterminate #0052CC en Checkbox y Radio; PIN Dot lleno #0052CC; Switch track ON #0052CC / OFF #D1D5DB con thumb blanco y geometría 44×24/thumb18/inset3. * Superficies de tabla y tarjeta: cabecera #F7F9FC, divisores/hairline #E5E7EB, cards planas sin sombra. * Colores de chart: serie 1 #0052CC, serie 2 #3B82F6 (info), serie 3 #10B981, serie 4 #F59E0B (useChartColors.ts). * Superficie dark de Toast y Tooltip medida #1F2933 opaca, igual al asset de Figma. * Tokens de texto: primario #1F2933, secundario #6B7280, disabled #9CA3AF — consistentes en títulos, labels y valores. ## Capturas que no corresponden a su diseño [Section titled “Capturas que no corresponden a su diseño”](#capturas-que-no-corresponden-a-su-diseño) > La auditoría verifica primero que cada par muestre la misma pantalla. En estos 12 casos **no** coinciden, así que su celda de Notas describe el desajuste en vez de listar diferencias de estilo. El patrón dominante: el frame de Figma diseña una pantalla completa y la app la resuelve como diálogo sobre el listado, o la captura tomó otro paso del flujo — son decisiones de producto (diálogo-vs-página) o gaps de implementación, no desviaciones de estilo. * **W03b · Nueva contraseña** — La captura de la app muestra el estado de error “enlace inválido” de /reset-password (sin query params userId/secret), no el formulario de nueva contraseña que diseña el Figma. * **Categoría — alta** — El frame de Figma (figma-w23-categorias.png) es el LISTADO de Categorías (tabla + filtros + paginación, sin ningún formulario), mientras que la captura de la app muestra el drawer “Nueva categoría” (formulario de alta) abierto sobre el listado. No existe en el par un frame de Figma del formulario de * **Impuesto — alta** — La captura de Figma emparejada (figma-w24-impuestos.png) es la pantalla de LISTA de Impuestos (tabla de tasas + botón “Nueva tasa”); no contiene ningún formulario de alta. La app sí muestra el drawer “Nuevo Impuesto”. Falta exportar/emparejar el frame de Figma del formulario de alta (si existe), o e * **W33 · Detalle de turno / Corte Z** — El panel FIGMA diseña una PÁGINA completa de “Corte Z” de un turno CERRADO; el panel APP muestra un DIÁLOGO de detalle de un turno ABIERTO (sin corte todavía). Distinto paso del flujo, distinta composición (página vs diálogo) y distinto estado del turno. * **W35 · Cerrar turno** — El Figma diseña una página de resumen “Cerrar turno · Corte Z” dentro del shell admin (sidebar + topbar), mientras que la app captura una pantalla de terminal (shell POS, sin sidebar) de conteo de efectivo por denominación. Son pasos distintos del flujo de cierre. * **W36b · Movimiento de caja (terminal)** — El manifest reutiliza el Figma de W36 (página admin de administración) para W36b, pero la captura de la app es la pantalla de terminal (formulario de entrada con teclado numérico). Son dos pantallas distintas: lista/tabla admin vs. formulario de captura de un movimiento en terminal. * **W38 · Empleado crear / editar** — Figma = pagina completa “Empleado” (header + secciones Datos personales/Acceso + sticky save bar). App = drawer lateral derecho sobre la lista de Empleados, con set de campos distinto. * **W56 · Reporte de ventas** — El panel APP no muestra la pantalla del diseño. FIGMA dibuja el “Reporte de ventas · Desglose del periodo” (tabs Por producto/categoría/empleado/método + tabla de líneas Producto·SKU·Categoría·Ingresos·Unidades·Tendencia con checkboxes, botón Exportar y paginación 1–5 de 248). La captura APP muestra * **W58 · Cortes de caja (Z)** — El Figma diseña el HISTÓRICO/lista de cortes Z (tabla con filas Z-088…Z-084); la app captura el DETALLE de un solo corte Z (tarjeta con desglose Ventas/Formas de Pago/Conciliación). Son dos vistas distintas del mismo flujo. * **W42 · Tiempo Aire (TAE)** — Figma diseña la página compuesta de recarga (proveedor como chips + grilla de montos + captura de teléfono/total); la app muestra solo el paso “Selecciona el proveedor” con tarjetas grandes y un “Resumen de compra” vacío, sin montos ni campo de teléfono. * **W46b · Registrar depósito** — El frame que estaba pareado era la **lista** de depósitos (W46), no el **formulario** que captura la app; se despareó la lista para no comparar dos pantallas distintas. Falta exportar el frame del formulario «Registrar depósito» de Figma (ver TODO en la celda de Figma de W46b) para tener un par comparable. * **W07 · Dashboard** — El sidebar coincide (misma app, ruta “Inicio” activa), pero el panel de contenido principal es un diseño completamente distinto. Figma diseña un dashboard analítico de retail: saludo “Hola, Victoria”, 4 KPIs (Ventas hoy $8,450, Transacciones 128, Ticket promedio $66.01, Productos vendidos 342), gráf ## Índice [Section titled “Índice”](#índice) * [Hallazgos sistémicos](#hallazgos-sist%C3%A9micos) * [Capturas que no corresponden a su diseño](#capturas-que-no-corresponden-a-su-dise%C3%B1o) * [Pantallas por historia de usuario](#pantallas-por-historia-de-usuario) * [ACC — Acceso y onboarding](#acc--acceso-y-onboarding) * [POS — Venta en punto de venta](#pos--venta-en-punto-de-venta) * [VENTAS / VOID — Historial de ventas, cancelación y reembolso](#ventas--void--historial-de-ventas-cancelaci%C3%B3n-y-reembolso) * [CAT — Catálogo](#cat--cat%C3%A1logo) * [INV — Inventario](#inv--inventario) * [SHIFT — Turnos y caja](#shift--turnos-y-caja) * [STAFF — Personal y control de acceso](#staff--personal-y-control-de-acceso) * [REP — Reportes](#rep--reportes) * [VAS — Servicios de valor agregado](#vas--servicios-de-valor-agregado) * [BAL — Wallet del comercio](#bal--wallet-del-comercio) * [CLI — Clientes](#cli--clientes) * [TERM / WL — Comercios, tiendas, terminales y white-label](#term--wl--comercios-tiendas-terminales-y-white-label) * [ACCT — Cuenta y ajustes](#acct--cuenta-y-ajustes) * [NOTIF — Notificaciones](#notif--notificaciones) * [SEARCH — Búsqueda global](#search--b%C3%BAsqueda-global) * [PLAT — Administración de plataforma](#plat--administraci%C3%B3n-de-plataforma) * [Sin historia de usuario — rutas técnicas y de desarrollo](#sin-historia-de-usuario--rutas-t%C3%A9cnicas-y-de-desarrollo) * [Design system](#design-system) * [Átomos](#%C3%A1tomos) * [Moléculas](#mol%C3%A9culas) * [Organismos](#organismos) *** ## Pantallas por historia de usuario [Section titled “Pantallas por historia de usuario”](#pantallas-por-historia-de-usuario) ### ACC — Acceso y onboarding [Section titled “ACC — Acceso y onboarding”](#acc--acceso-y-onboarding) Flujo W-01. Historias `US-ACC-1` … `US-ACC-6`. #### W01 · Iniciar sesión [Section titled “W01 · Iniciar sesión”](#w01--iniciar-sesión) **Ruta:** `/login` · **Figma:** [`107:358`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=107-358) · **Historia:** US-ACC-1 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W01](/design/parity/figma/screens/figma-w01-login.png)](/design/parity/figma/screens/figma-w01-login.png "figma W01 — abrir a tamaño completo") | [![app W01](/design/parity/history/2026-08-07/screenshots/login.png)](/design/parity/history/2026-08-07/screenshots/login.png "app W01 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes** — la capa de color sistémica llegó, pero overrides locales de `sx` anulan los fixes de tamaño/sombra y falta contenido del diseño.- **Color:** ✅ Resuelto — azul canónico **#0052CC medido** en panel de marca, botón Entrar, focus ring y links (ambos paneles); fondo #F5F7FA y heading #1F2933 coinciden; hairlines de campos y botón secundario = **#E5E7EB** exacto. - **Sombras:** ❌ El botón Entrar proyecta sombra visible (gradiente #DFDFDF→#F2F2F2 medido bajo el botón); en Figma no hay ninguna. Culpable: `src/ui/Buttons/PrimaryButton.tsx` hardcodea `boxShadow: '0px 10px 15px…'` en `sx`, pisando el fix del theme. La tarjeta también lleva sombra explícita (`src/ui/templates/AuthSplit/index.tsx`, `boxShadow` en el Paper) mientras el Figma muestra borde nítido sin sombra. - **Espaciado:** ❌ Campos de \~60px de alto (borde 406→468 medido) vs \~38px en Figma — `src/ui/Inputs/TextInput.tsx` hardcodea `padding: '18px 22px'`, anulando el fix de campos \~40px. Botón Entrar \~67px (526→593) vs 52px del Figma — `PrimaryButton.tsx` hardcodea `padding: sm '20px'`. - **Tipografía:** Etiqueta del botón a 18px (`fontSize: {sm: 18}` en `PrimaryButton.tsx`) vs 14px/600 del sistema. - **Contenido:** ❌ Falta el botón outlined “**Acceso rápido con PIN**” (existe en Figma con borde azul #0052CC; sin rastro en `LoginForm.tsx` ni `login.i18n.ts`). El divisor “— o —” del Figma se reemplazó por el texto “O recibe un enlace de acceso por correo”. “Enviar enlace mágico” es link de texto azul en Figma pero botón outlined neutro en la app (`SecondaryButton` en `LoginForm.tsx:256`). - **Layout:** Split 50/50 (`flex: '1 1 50%'` en `AuthSplit/index.tsx`) vs \~43/57 medido en Figma (azul termina en x≈616/1440 vs x≈720). “¿Olvidaste tu contraseña?” alineado a la derecha en app, a la izquierda en Figma. - **Estados:** La captura muestra el email con autofocus (ring azul 2px #0052CC) y asteriscos rojos de requerido (\~#D84F4F medido) que el Figma no dibuja; el campo contraseña muestra toggle de visibilidad oscuro sin equivalente en Figma — decidir si se ocultan asteriscos/adorno o se actualiza el diseño. - **Verificar:** si “Acceso rápido con PIN” quedó fuera por decisión de producto, documentarlo; si no, es contenido faltante. | | #### W02 · Verificación 2FA [Section titled “W02 · Verificación 2FA”](#w02--verificación-2fa) **Ruta:** `/login (MfaChallengeForm)` · **Figma:** [`110:376`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=110-376) · **Historia:** US-ACC-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------- | | [![figma W02](/design/parity/figma/screens/figma-w02-2fa.png)](/design/parity/figma/screens/figma-w02-2fa.png "figma W02 — abrir a tamaño completo") | ⚠️ **Falta captura** — es un estado dentro de `/login`, no una ruta propia; hay que capturarlo con Playwright forzando el reto MFA | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W03 · Recuperar contraseña [Section titled “W03 · Recuperar contraseña”](#w03--recuperar-contraseña) **Ruta:** `/forgot-password` · **Figma:** [`110:407`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=110-407) · **Historia:** US-ACC-3 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W03](/design/parity/figma/screens/figma-w03-recuperar.png)](/design/parity/figma/screens/figma-w03-recuperar.png "figma W03 — abrir a tamaño completo") | [![app W03](/design/parity/history/2026-08-07/screenshots/forgot-password.png)](/design/parity/history/2026-08-07/screenshots/forgot-password.png "app W03 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** (recaptura post-remediación; misma pantalla, split W03 con card centrada)- **Color:** Fixes sistémicos confirmados por medición: panel izquierdo, botón, badge y focus ring del campo todos en **#0052CC** canónico; título #1F2933; subtítulo #6B7280. Residuo: el link “Volver a iniciar sesión” renderiza **#232D37** (texto plano) en vez del azul de marca semibold del diseño — es un `next/link` desnudo en `src/app/forgot-password/ForgotPasswordForm.tsx:182-187`. - **Iconos:** El badge del “?” es un cuadrado **sólido #0052CC** con glifo blanco; Figma lo diseña como cuadrado en tinte **#E6EEFA** (medido) con círculo azul — `ForgotPasswordForm.tsx:89-104` usa `bgcolor: 'primary.main'` en lugar de `primary.light`. - **Contenido:** Falta el hint informativo persistente de Figma (“Si el correo existe, recibirás un enlace en unos minutos.”, tinte #EFF6FF verificado en el diseño); la app solo muestra un Banner **después** de enviar y con otro copy (`ForgotPasswordForm.tsx:132-136`). Copys divergentes en `recovery.i18n.ts`: subtítulo recortado (“Te enviaremos…” vs “Ingresa tu correo y te enviaremos…”), botón “Enviar enlace de recuperación” vs “Enviar enlace”, placeholder “” vs “”. - **Contenido:** El panel de marca reutiliza `LoginBrand` con el tagline genérico del login; Figma W03 pide el tagline propio “Recupera el acceso a tu cuenta en unos pasos.” (`src/app/forgot-password/page.tsx:22`). - **Layout:** CTA medido a **\~40px** (ramp medium) donde Figma usa el botón grande **\~52px**; falta `size` en el Button del form. - **Estados:** La captura tiene el campo con `autoFocus` (ring azul 2px #0052CC correcto); el borde hairline #E5E7EB en reposo queda como “verificar”. - **Bordes/Sombras:** Card radio y sombra suave equivalentes en ambos paneles; botón sin sombra ✓. - Sistémico conocido (una línea): label flotante MUI con asterisco vs label estático sobre el campo del diseño — patrón de formulario pendiente de decisión de producto. | | #### W03b · Nueva contraseña [Section titled “W03b · Nueva contraseña”](#w03b--nueva-contraseña) **Ruta:** `/reset-password` · **Figma:** [`110:407`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=110-407) · **Historia:** US-ACC-3 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W03b](/design/parity/figma/screens/figma-w03b-nueva-contrasena.png)](/design/parity/figma/screens/figma-w03b-nueva-contrasena.png "figma W03b — abrir a tamaño completo") | [![app W03b](/design/parity/history/2026-08-07/screenshots/reset-password.png)](/design/parity/history/2026-08-07/screenshots/reset-password.png "app W03b — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Panel FIGMA:** el paso de formulario del flujo de recuperación — icono de candado en tinte, título “Crea tu nueva contraseña”, campos “Nueva contraseña” y “Confirmar contraseña”, checklist de requisitos y botón “Guardar contraseña”. - **Panel APP:** el estado de error de la misma ruta — card “Restablecer contraseña” con alerta “El enlace de recuperación es inválido o expiró.” y link “Volver a iniciar sesión”, sin ningún campo. - **Causa:** `src/app/reset-password/ResetPasswordForm.tsx` exige `?userId=…&secret=…` en la URL (`hasParams`, líneas 54–56/121); la captura se tomó sin esos params y cayó en la rama de enlace inválido. - **Acción de captura:** recapturar con `/reset-password?userId=dummy&secret=dummy` (o mockeando `useSearchParams`) para renderizar el formulario y poder auditar la paridad. - **Dato aprovechable:** el azul de marca ya mide **#0052CC** en la captura (dominante 47.7%, igual al Figma) y el fondo #F5F7FA coincide — el fix sistémico sí llegó a esta ruta, pero el residuo del formulario queda **pendiente de verificar**. | | #### W04 · Acceso rápido (PIN) [Section titled “W04 · Acceso rápido (PIN)”](#w04--acceso-rápido-pin) **Ruta:** `/terminal/lock` · **Figma:** [`110:481`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=110-481) · **Historia:** US-ACC-4 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W04](/design/parity/figma/screens/figma-w04-pin.png)](/design/parity/figma/screens/figma-w04-pin.png "figma W04 — abrir a tamaño completo") | [![app W04](/design/parity/history/2026-08-07/screenshots/terminal-lock.png)](/design/parity/history/2026-08-07/screenshots/terminal-lock.png "app W04 — abrir a tamaño completo") | | Notas | | | **✅ Recapturado — ahora ambos paneles muestran el paso 2 (teclado PIN).**- **FIGMA:** layout dividido — panel de marca azul “Terminales / Cambia de cajero en segundos con un PIN” a la izquierda y tarjeta de PIN a la derecha (avatar “JP”, “Juan Pérez / Cajero · Tienda Centro”, “Ingresa tu PIN”, 4 dots de progreso, teclado 1–9/0 + backspace y link “Cambiar usuario”). - **APP:** teclado PIN de “Cajero Demo” tras seleccionar el usuario — “Terminal bloqueado / PIN de Cajero Demo / Cambiar usuario / Ingresa tu PIN para iniciar sesión”, teclado 1–9 + C/0/backspace y botón “Entrar”. El `setup` de `terminal-lock` ahora hace clic en el perfil de cajero antes de capturar (helper `revealPinKeypad` en `e2e/screenshots/targets.ts`). - **Diferencias reales (no de estado):** la app es una sola columna centrada (sin el panel de marca azul del diseño); su teclado añade una tecla “C” (limpiar) y un botón explícito “Entrar”; los 4 dots de progreso de la app aparecen al teclear (aquí el PIN está vacío). - **Dato medido:** el azul canónico #0052CC se midió en ambos paneles — el fix sistémico de azul llegó a esta ruta. | | #### W05 · Seleccionar comercio / tienda [Section titled “W05 · Seleccionar comercio / tienda”](#w05--seleccionar-comercio--tienda) **Ruta:** `/select-store` · **Figma:** [`112:406`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=112-406) · **Historia:** US-ACC-5 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W05](/design/parity/figma/screens/figma-w05-seleccionar-comercio.png)](/design/parity/figma/screens/figma-w05-seleccionar-comercio.png "figma W05 — abrir a tamaño completo") | ⚠️ **Falta captura** — **la app salta esta pantalla**: `SelectStoreView` auto-selecciona cuando el usuario tiene una sola membresía (“AC2 — a single membership needs no click”), y el roster mock tiene exactamente una. Para capturar el selector hace falta un fixture con ≥2 tiendas. | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W06 · Onboarding — crear comercio [Section titled “W06 · Onboarding — crear comercio”](#w06--onboarding--crear-comercio) **Ruta:** `— (sin ruta)` · **Figma:** [`112:443`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=112-443) · **Historia:** US-ACC-6 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W06](/design/parity/figma/screens/figma-w06-onboarding.png)](/design/parity/figma/screens/figma-w06-onboarding.png "figma W06 — abrir a tamaño completo") | [![app W06](/design/parity/history/2026-08-07/screenshots/onboarding.png)](/design/parity/history/2026-08-07/screenshots/onboarding.png "app W06 — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Azul canónico verificado: botón Continuar y paso activo del stepper = #0052CC en ambos paneles; título #1F2933, subtítulo #6B7280 — fixes sistémicos presentes en esta captura. - **Bordes:** Campos con hairline #E5E7EB y relleno blanco en app y Figma (medido en el primer campo). - **Espaciado:** Alturas idénticas medidas: campos \~40px (app 341→380 vs Figma 339→378), botón grande \~52px (app 655→706 vs Figma 657→708). Composición, radios y densidad de la tarjeta equivalentes. - **Layout:** Misma estructura: stepper 1-2-3 + “Paso 1 de 3”, título, subtítulo, 4 campos, CTA full-width. - **Contenido:** Divergencia **intencional y documentada** (comentario en `src/app/onboarding/onboarding.i18n.ts`): el alcance US-ACC-6 es merchant-create only, así que “Nombre de la tienda” del Figma se reemplaza por “Usuario propietario (ID)” + helper, el subtítulo cambia y “Dirección” pasa a “(opcional)”. Es el Figma el que quedó desactualizado respecto al contrato — actualizar el frame, no el código. | | #### Cierre de sesión [Section titled “Cierre de sesión”](#cierre-de-sesión) **Ruta:** `/sign-out` · **Historia:** — | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — pantalla técnica sin frame en Figma | [![app Cierre de sesión](/design/parity/history/2026-08-07/screenshots/sign-out.png)](/design/parity/history/2026-08-07/screenshots/sign-out.png "app Cierre de sesión — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Callback de login (OAuth / magic link) [Section titled “Callback de login (OAuth / magic link)”](#callback-de-login-oauth--magic-link) **Ruta:** `/login/callback` · **Historia:** — (sin frame de diseño) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — pantalla técnica sin frame en Figma | [![app Callback de login (OAuth / magic link)](/design/parity/history/2026-08-07/screenshots/login-callback.png)](/design/parity/history/2026-08-07/screenshots/login-callback.png "app Callback de login (OAuth / magic link) — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | ### POS — Venta en punto de venta [Section titled “POS — Venta en punto de venta”](#pos--venta-en-punto-de-venta) Flujo W-03. Historias `US-POS-1` … `US-POS-9`. #### W10 · POS Venta (two-pane) [Section titled “W10 · POS Venta (two-pane)”](#w10--pos-venta-two-pane) **Ruta:** `/terminal/home` · **Figma:** [`114:715`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=114-715) · **Historia:** US-POS-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W10](/design/parity/figma/screens/figma-w10-pos-venta.png)](/design/parity/figma/screens/figma-w10-pos-venta.png "figma W10 — abrir a tamaño completo") | [![app W10](/design/parity/history/2026-08-07/screenshots/terminal-home.png)](/design/parity/history/2026-08-07/screenshots/terminal-home.png "app W10 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Azul canónico verificado por medición: chip activa **#0052CC**, precios **#0052CC**, texto “Asignar cliente” **#0052CC**; Cobrar deshabilitado **#E5E7EB** bg + **#9CA3AF** texto — los fixes sistémicos sí llegaron a esta pantalla. - **Color:** La chip de categoría seleccionada difiere del diseño: app = sólida #0052CC con texto blanco; Figma = tinte **#E6EEFA** con texto #0052CC (medido). El tinte ya está en `palette.primary.light`; culpable: `src/ui/atoms/Chip/index.tsx` (líneas 50–56, `selected ? primary.main : …`). - **Bordes:** Hairline **#E5E7EB** medido en campo de búsqueda y en tarjetas de producto; tarjetas planas blancas ✓. - **Layout:** Campo de búsqueda: Figma lo diseña relleno **#F0F4F8** sin borde (\~40px de alto); la app usa Paper outlined blanco (\~45px) — `src/app/terminal/home/picker/SearchInput.tsx`. - **Contenido:** Faltan los tabs de catálogo del diseño (Productos / Servicios / TAE / Pines) — el picker solo muestra productos. - **Contenido:** Las tarjetas de producto no muestran el badge de existencias (“24 pzs” verde / “Agotado” rojo, texto \~#1EBE89 medido en Figma) — `picker/ProductGrid.tsx` solo renderiza nombre + precio. - **Estados:** Captura con carrito vacío vs venta activa en Figma: mismo screen y layout two-pane, pero steppers de cantidad, “Vaciar”, IVA incluido y Cobrar activo quedan sin comparar; re-verificar con carrito poblado. - **Estados:** Switch “Escáner”: track off **#D1D5DB**, alto 24px, thumb blanco ✓ (el color de track no estaba especificado en el fix; verificar contra token). - **Layout:** Composición del header (AppBar de marca con Orden #/chip de turno/avatar vs saludo + acciones de turno) y extras del carrito (Asignar cliente, estado de terminal, Guardar en espera): divergencias de producto conocidas/pendientes, no de estilo. | | #### W11 · POS Resultados de búsqueda [Section titled “W11 · POS Resultados de búsqueda”](#w11--pos-resultados-de-búsqueda) **Ruta:** `/terminal/home (estado de búsqueda)` · **Figma:** [`131:8968`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=131-8968) · **Historia:** US-POS-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W11](/design/parity/figma/screens/figma-w11-resultados.png)](/design/parity/figma/screens/figma-w11-resultados.png "figma W11 — abrir a tamaño completo") | [![app W11](/design/parity/history/2026-08-07/screenshots/terminal-home--busqueda.png)](/design/parity/history/2026-08-07/screenshots/terminal-home--busqueda.png "app W11 — abrir a tamaño completo") | | Notas | | | **✅ Recapturado — la búsqueda ahora se ejecuta y el grid queda filtrado.**- **FIGMA:** “4 resultados para ‘coca’”, grid con 4 variantes de Coca-Cola (600 ml, 2 L, Light 600 ml, Sin Azúcar 2 L) con badge de stock (24/18/12/9 pzs), carrito con 3 ítems y “Cobrar · $372.00” en #0052CC. Chips de tipo: Todos/Productos/Servicios/Tiempo Aire/Tarjetas. - **APP:** “coca” ejecutado (ENTER) → grid filtrado. El `open` del diálogo `terminal-home--busqueda` ahora confirma la búsqueda y **espera a que el grid quede filtrado** antes de capturar; la captura anterior disparaba antes del `useDebounce` (500 ms) del hook de la lista y congelaba el catálogo completo de W10. - **Diferencia de contenido (no de estado):** el catálogo mock sólo tiene **un** producto “coca” (Coca-Cola 600 ml), así que la app muestra **1** resultado frente a los **4** del diseño. - **Pendiente verificado en código (no cambia al recapturar):** no existe label “N resultados para …” en `src/app/terminal/home/picker/`; los chips de la app son categorías (CategoryRail: Todas/Bebidas/Alimentos/…) y no el filtro por tipo del diseño; `ProductGrid.tsx` no muestra badge de stock; el carrito está vacío (el diseño lo muestra con 3 ítems). - **Fixes sistémicos medidos (OK):** azul canónico #0052CC en chip seleccionado y precios; disabled #E5E7EB; tarjetas blancas con hairline #E5E7EB. | | #### W12 · POS Categoría especial (VAS) [Section titled “W12 · POS Categoría especial (VAS)”](#w12--pos-categoría-especial-vas) **Ruta:** `— (sin ruta)` · **Figma:** [`131:9039`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=131-9039) · **Historia:** US-VAS-4 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el nodo existe pero no se exportó a `design/web/screens/` | ⚠️ **Falta captura** — no hay ruta VAS dentro del POS del terminal | | Notas | | | **⛔ Comparación no posible — faltan las dos imágenes.** No hay render de Figma exportado ni captura de la app, así que no hay nada que comparar. El motivo de cada gap está en las celdas de arriba. Primero hay que resolverlo (exportar el frame y/o construir la pantalla); recién ahí tiene sentido auditar paridad. | | #### W13 · POS Descuento [Section titled “W13 · POS Descuento”](#w13--pos-descuento) **Ruta:** `— (hoja modal)` · **Figma:** [`115:805`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=115-805) · **Historia:** US-POS-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------- | | [![figma W13](/design/parity/figma/screens/figma-w13-descuento.png)](/design/parity/figma/screens/figma-w13-descuento.png "figma W13 — abrir a tamaño completo") | ⚠️ **Falta captura** — la hoja de descuento no está cableada en el POS | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W14 · POS Cobrar (método) [Section titled “W14 · POS Cobrar (método)”](#w14--pos-cobrar-método) **Ruta:** `— (diálogo)` · **Figma:** [`115:1004`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=115-1004) · **Historia:** US-POS-3 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------- | | [![figma W14](/design/parity/figma/screens/figma-w14-cobrar.png)](/design/parity/figma/screens/figma-w14-cobrar.png "figma W14 — abrir a tamaño completo") | ⚠️ **Falta captura** — diálogo (`TenderMethodDialog`), requiere captura con carrito activo | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W15 · POS Efectivo [Section titled “W15 · POS Efectivo”](#w15--pos-efectivo) **Ruta:** `— (diálogo)` · **Figma:** [`115:1126`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=115-1126) · **Historia:** US-POS-3 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------- | | [![figma W15](/design/parity/figma/screens/figma-w15-efectivo.png)](/design/parity/figma/screens/figma-w15-efectivo.png "figma W15 — abrir a tamaño completo") | ⚠️ **Falta captura** — diálogo (`CashTenderDialog`) | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W16 · POS Tarjeta — procesando [Section titled “W16 · POS Tarjeta — procesando”](#w16--pos-tarjeta--procesando) **Ruta:** `— (diálogo)` · **Figma:** [`117:1335`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=117-1335) · **Historia:** US-POS-4 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------- | | [![figma W16](/design/parity/figma/screens/figma-w16-tarjeta.png)](/design/parity/figma/screens/figma-w16-tarjeta.png "figma W16 — abrir a tamaño completo") | ⚠️ **Falta captura** — diálogo (`RemoteCardTenderDialog`) | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W17 · POS Pago mixto (split) [Section titled “W17 · POS Pago mixto (split)”](#w17--pos-pago-mixto-split) **Ruta:** `— (diálogo)` · **Figma:** [`115:1234`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=115-1234) · **Historia:** US-POS-5 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------- | | [![figma W17](/design/parity/figma/screens/figma-w17-mixto.png)](/design/parity/figma/screens/figma-w17-mixto.png "figma W17 — abrir a tamaño completo") | ⚠️ **Falta captura** — superficie no cableada | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W18 · POS Recibo [Section titled “W18 · POS Recibo”](#w18--pos-recibo) **Ruta:** `— (documento de impresión)` · **Figma:** [`133:10777`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=133-10777) · **Historia:** US-POS-7 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------ | | [![figma W18](/design/parity/figma/screens/figma-w18-recibo.png)](/design/parity/figma/screens/figma-w18-recibo.png "figma W18 — abrir a tamaño completo") | ⚠️ **Falta captura** — el recibo es solo impresión (`ReceiptDocument.tsx`), no hay pantalla | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W19 · POS Venta exitosa [Section titled “W19 · POS Venta exitosa”](#w19--pos-venta-exitosa) **Ruta:** `— (modal)` · **Figma:** [`117:1417`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=117-1417) · **Historia:** US-POS-6 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- | | [![figma W19](/design/parity/figma/screens/figma-w19-exito.png)](/design/parity/figma/screens/figma-w19-exito.png "figma W19 — abrir a tamaño completo") | ⚠️ **Falta captura** — no existe pantalla/modal de confirmación | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W64 · Ventas en espera [Section titled “W64 · Ventas en espera”](#w64--ventas-en-espera) **Ruta:** `/admin/pos/held-orders` · **Figma:** [`574:14716`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=574-14716) · **Historia:** US-POS-8 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W64](/design/parity/figma/screens/figma-w64-en-espera.png)](/design/parity/figma/screens/figma-w64-en-espera.png "figma W64 — abrir a tamaño completo") | [![app W64](/design/parity/history/2026-08-07/screenshots/admin-pos-held-orders.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-held-orders.png "app W64 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes** — misma pantalla; los fixes sistémicos de color SÍ llegaron (medidos), pero hay una regresión de tema, dos bugs de contenido visibles y la tarjeta difiere en composición.- **Color:** ✅ verificado: CTA y segmento activo `#0052CC` exacto, texto principal `#1F2933`, borde de tarjeta hairline `#E5E7EB`, fondo `#F5F7FA`. ❌ Avatar de tarjeta y header: bg medido `#3374D6` = `lighten(#0052CC, .2)` — `mergeMerchantTheme` pasa por `augmentColor` y recalcula `primary.light`, pisando el tinte canónico `#E6EEFA` del fix de Avatar en superficies white-label (`src/ui/theme/WhiteLabelThemeProvider.tsx`; derivar ahí el soft como en `theme.ts` en vez del lighten de MUI). - **Contenido:** ❌ importes ×100: el mock guarda centavos (`total: 37200`) y `HeldOrdersList.tsx:167` formatea el valor crudo → “$37,200.00” vs “$372.00” del diseño para el mismo carrito (`heldOrder.mocks.ts`, aclarar unidad en `types.ts:22`). - **Contenido:** ❌ “17 days ago” en inglés: `src/core/utils.ts` importa `dayjs/locale/es` pero nunca activa `dayjs.locale('es')` (Figma: “Hace 4 min”). Los `heldAt` del mock (2026-07-06) además envejecen la demo — refrescarlos a offsets relativos. - **Estados:** app captura “Todos” activo; Figma muestra “Mis carritos”. El default `'all'` es deliberado en código (`HeldOrdersList.tsx:55`) — confirmar con producto cuál es el default correcto. - **Layout:** patrón del segmented invertido: Figma = pill blanco sobre track gris `#F0F4F8` con texto oscuro; app = pill azul sólido sobre track blanco (`src/ui/atoms/SegmentedControl/index.tsx`, atom compartido — afecta otras pantallas). - **Layout:** composición de `HeldOrderCard` distinta al diseño: Figma pone id azul “#H-2271” + precio arriba-derecha, “Hace X min” como caption bajo el nombre y holder como avatar chico inline + texto plano; app pone “H-2271” oscuro sin “#”, avatar sólido arriba-derecha, tiempo/holder como chips outlined y precio abajo (`src/ui/molecules/HeldOrderCard/index.tsx`). - **Bordes/Sombras:** ✅ tarjeta plana con hairline, sin sombra; botón sin sombra, label 14/600. - **Estados:** CTA “Reanudar venta” es `contained` sólido; Figma lo diseña tonal soft (bg `#E6EEFA` medido, label primary) — requiere variante tonal de Button o `ButtonProps` en la tarjeta (`HeldOrderCard/index.tsx:115`). - **Contenido:** copy del subtítulo difiere (“compartidos por tienda en este turno” vs “de la sucursal en el turno abierto”) — menor, `heldOrder.i18n.ts`; “Pantalla completa” arriba-derecha no existe en el diseño (shell compartido, una línea: igual que el resto de /admin). | | #### W64b · Ventas en espera — desde el POS [Section titled “W64b · Ventas en espera — desde el POS”](#w64b--ventas-en-espera--desde-el-pos) **Ruta:** `/terminal/home (diálogo)` · **Figma:** [`574:14716`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=574-14716) · **Historia:** US-POS-8 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W64b](/design/parity/figma/screens/figma-w64-en-espera.png)](/design/parity/figma/screens/figma-w64-en-espera.png "figma W64b — abrir a tamaño completo") | [![app W64b](/design/parity/history/2026-08-07/screenshots/terminal-home--dialog-ventas-en-espera.png)](/design/parity/history/2026-08-07/screenshots/terminal-home--dialog-ventas-en-espera.png "app W64b — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**La captura nueva confirma que la remediación sistémica llegó: azul canónico, hairlines y tinte de marca medidos. El residuo es propio de la tarjeta (variante de botón, anatomía) más un bug de i18n en el tiempo relativo.- **Color:** ✅ Verificado — “Reanudar venta” y “Cerrar” miden **#0052CC**; borde de tarjeta **#E5E7EB**; avatar SR en tinte de marca (\~#E1EAF9) con iniciales azules. - **Estados:** Figma usa botón **tonal** para “Reanudar venta” (fill medido exactamente **#E6EEFA**, etiqueta azul); la app renderiza `variant="contained"` sólido #0052CC → jerarquía más pesada que el diseño. Culpable: `src/ui/molecules/HeldOrderCard/index.tsx:115`. - **Contenido:** chip “**17 days ago**” en inglés dentro de UI en español (Figma: “Hace 4 min”). `HeldOrdersDialog.tsx:142` llama `formatRelativeTime` → dayjs `.fromNow()` sin locale `es` (`src/core/utils.ts:401`). Bug de i18n real, no de estilo. - **Layout:** anatomía de tarjeta distinta al Figma: id “#H-2271” azul vs “H-2271” oscuro sin “#”; tiempo y “En espera por…” como Chips outlined en vez de texto gris con mini-avatar inline; precio junto a los chips (Figma: esquina superior derecha); avatar arriba a la derecha (Figma: inline). Todo en `HeldOrderCard`. - **Tipografía:** jerarquía interna invertida vs Figma (Figma: nombre en bold, id secundario azul; app: id en bold, nombre gris) — mismo culpable. - **Bordes/Sombras:** ✅ tarjetas planas con hairline #E5E7EB, sin sombra; radio del diálogo conforme. - **Contenido (dato):** montos/fechas difieren por mock data ($37,200 vs $372; “17 days” por `heldAt` viejo en `heldOrder.mocks.ts`) — no es defecto de paridad. - Diálogo-vs-página y tabs “Mis carritos/Todos” ausentes: decisión de producto conocida y documentada (el terminal sólo muestra “mine”; “Todos” es superficie admin) — no se re-lista. | | #### POS — índice admin [Section titled “POS — índice admin”](#pos--índice-admin) **Ruta:** `/admin/pos` · **Historia:** — | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — índice de navegación, sin frame | [![app POS — índice admin](/design/parity/history/2026-08-07/screenshots/admin-pos.png)](/design/parity/history/2026-08-07/screenshots/admin-pos.png "app POS — índice admin — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Intentos de pago (payment intents) [Section titled “Intentos de pago (payment intents)”](#intentos-de-pago-payment-intents) **Ruta:** `/admin/pos/payment-intents` · **Historia:** US-POS-4 (soporte) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — pantalla de diagnóstico, sin frame | [![app Intentos de pago (payment intents)](/design/parity/history/2026-08-07/screenshots/admin-pos-payment-intents.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-payment-intents.png "app Intentos de pago (payment intents) — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Aprobaciones de supervisor [Section titled “Aprobaciones de supervisor”](#aprobaciones-de-supervisor) **Ruta:** `/admin/pos/approvals` · **Historia:** US-POS-2 / US-VOID-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el diseño vive como organismo **POS Approval Modal**, no como pantalla | [![app Aprobaciones de supervisor](/design/parity/history/2026-08-07/screenshots/admin-pos-approvals.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-approvals.png "app Aprobaciones de supervisor — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | ### VENTAS / VOID — Historial de ventas, cancelación y reembolso [Section titled “VENTAS / VOID — Historial de ventas, cancelación y reembolso”](#ventas--void--historial-de-ventas-cancelación-y-reembolso) Flujo W-06. Historias `US-VOID-1` … `US-VOID-3`. #### W29 · Ventas [Section titled “W29 · Ventas”](#w29--ventas) **Ruta:** `/admin/pos/sales-orders` · **Figma:** [`119:1820`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=119-1820) · **Historia:** US-VOID-1 (entrada) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W29](/design/parity/figma/screens/figma-w29-ventas.png)](/design/parity/figma/screens/figma-w29-ventas.png "figma W29 — abrir a tamaño completo") | [![app W29](/design/parity/history/2026-08-07/screenshots/admin-pos-sales-orders.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-sales-orders.png "app W29 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes (de contenido/feature, NO de estilo)** *(re-auditado 2026-07-23 con recaptura)* — los tokens sistémicos se confirman todos en la recaptura; el residuo es una implementación reducida de la pantalla.- **Color (fix-gaps CERRADOS en la recaptura):** los badges de estado ya son **soft por rol** — Pagada verde soft, Pago Parcial ámbar soft, Abierta info soft, Anulada/Cancelada rojo soft, Borrador neutro (`StatusBadge`, no `` crudo). El ítem de nav activo (“Ventas”) ya va en tinte de marca **#E6EEFA** + texto azul (no gris). La paginación es el **pager numerado** (“Mostrando 1–6 de 6” + chip “1” azul), no `TablePagination`. Los tres residuos de estilo que esta nota marcaba están cerrados. - **Color (OK):** azul canónico #0052CC en Filtros/nav; cabecera de tabla #F7F9FC, divisores #E5E7EB, título #1F2933, chip “Turno abierto” soft #ECFDF5 — coinciden. - **Contenido (residuo real):** falta la toolbar rica del diseño: subtítulo KPI (“1,284 ventas · $184,320 este mes”), botón primario **Exportar**, búsqueda local, tabs segmentadas Todas/Pagadas/Canceladas y el select. La app añade un botón “Pantalla completa” que no está en Figma. - **Layout (residuo real):** esquema de tabla distinto — Figma: checkbox de selección + avatar de cliente + Folio/Cliente/Fecha/Total/Artículos/Estado + editar por fila; app: Ticket/Estado/Sucursal/Empleado/Fecha/Total/Por Cobrar, sin selección ni acción por fila. Definir con producto el esquema vigente. - **Tipografía:** encabezado “Órdenes de Venta” en jerarquía menor que el H grande “Ventas” de Figma, y sin subtítulo.Conclusión: **estilo/tokens alineados** (badges soft, nav tint, pager numerado, header, azul). El ❌ restante es **alcance de feature** — columnas/toolbar/selección/métricas y datos reales — no desviación de estilo. Tratarlo como build-out de pantalla, no como pulido de paridad. | | #### W30 · Detalle de venta [Section titled “W30 · Detalle de venta”](#w30--detalle-de-venta) **Ruta:** `/admin/pos/sales-orders/[id]` · **Figma:** [`125:6743`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=125-6743) · **Historia:** US-POS-7 / US-VOID-1 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W30](/design/parity/figma/screens/figma-w30-detalle-venta.png)](/design/parity/figma/screens/figma-w30-detalle-venta.png "figma W30 — abrir a tamaño completo") | [![app W30](/design/parity/history/2026-08-07/screenshots/admin-pos-sales-order-detail.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-sales-order-detail.png "app W30 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**- **Contenedor:** diálogo sobre la lista vs página dedicada del diseño — gap conocido #240 (pendiente de producto); la versión fiel existe solo en `parity/VentasDetailScreen.tsx` (playground). No se re-lista como defecto. - **Color:** azul canónico OK — etiqueta de “Asignar cliente” medida **#0052CC** ✓; cabecera de tabla **#F7F9FC** ✓; divisor bajo cabecera **#E5E7EB** ✓; valores **#1F2933** ✓, labels ≈#6B7280 ✓. - **Color:** badge “Pagada” es Chip sólido **#10B981 con texto blanco**; Figma usa tinte soft (#ECFCF5 + texto verde). El fix “StatusBadge sobre tokens \*-soft” NO llegó: `SalesOrderDetail.tsx:87` usa `Chip color="success"` crudo en vez de `atoms/StatusBadge`. - **Iconos:** el icono de “Asignar cliente” es **negro** junto a etiqueta azul: `src/ui/FontAwesomeIcon.tsx:25` (default `color='black'`) + `AssignCustomerButton.tsx:57` no pasa color — debería heredar `currentColor`. - **Contenido:** montos sin “$” (“540.00” vs “$372.00” del diseño): `formatCurrency` (`src/core/utils.ts:49`) no antepone símbolo; existe `formatCurrencyWithSymbol` sin usar en `SalesOrderDetail.tsx`. - **Contenido:** faltan “Reimprimir” (solo existe en el showcase), “Folio fiscal”, “Recibido”/“Cambio” (la tabla de pagos solo trae Método/Tarjeta/Estado/Importe) y el nombre del Cliente (solo botón de asignar). “Cancelar / Reembolsar” (`RefundAction`) existe en código pero no es visible en la captura — verificar gating con status PAID. - **Layout:** summary como pila vertical de 8 pares label/valor a ancho completo — densidad muy inferior a las dos cards (“Artículos” + “Información”) en dos columnas del diseño; el contenido desborda el viewport y la tabla queda cortada. - **Bordes:** borde del botón outlined = azul al \~50% (medido #7FA8E5, default MUI) vs borde sólido #0052CC del diseño — menor. - **Tipografía/Contenido:** título “A-1001” (ticketNumber crudo) vs “Venta #1042” formateado — menor, dato de mock. | | #### Ventas — filtros [Section titled “Ventas — filtros”](#ventas--filtros) **Ruta:** `/admin/pos/sales-orders (diálogo)` · **Historia:** US-REP-6 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el diseño vive como organismo **Filter Bar**, no como pantalla | [![app Ventas — filtros](/design/parity/history/2026-08-07/screenshots/admin-pos-sales-orders--dialog-filtros.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-sales-orders--dialog-filtros.png "app Ventas — filtros — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### W31 · Cancelar / Reembolsar [Section titled “W31 · Cancelar / Reembolsar”](#w31--cancelar--reembolsar) **Ruta:** `/terminal/voids (modal · render de referencia en /playground)` · **Figma:** [`131:8774`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=131-8774) · **Historia:** US-VOID-1 / US-VOID-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W31](/design/parity/figma/screens/figma-w31-cancelar-reembolsar.png)](/design/parity/figma/screens/figma-w31-cancelar-reembolsar.png "figma W31 — abrir a tamaño completo") | [![app W31](/design/parity/history/2026-08-07/screenshots/playground/w31-cancelar-reembolsar.png)](/design/parity/history/2026-08-07/screenshots/playground/w31-cancelar-reembolsar.png "app W31 — abrir a tamaño completo") | | Notas | | | **✅ Re-emparejado — el par compara el mismo estado (modal de autorización).**- **FIGMA:** modal “Autorización requerida” con PIN-pad de 4 dígitos (Cancelar / Autorizar) sobre el detalle de **Venta #1042**, con sidebar y topbar del back office. - **APP (render de referencia):** la misma composición desde `/playground` (`VentasVoidScreenShowcase`, `screenshots/playground/w31-cancelar-reembolsar.png`) — detalle de Venta #1042 + modal de autorización con teclado numérico y Cancelar/Autorizar. - **Por qué el render de playground y no la ruta:** bajo los mocks del harness `/terminal/voids` sólo puede renderizar su estado vacío — `selectVoidableOrders` conserva pedidos del turno abierto (`shift_001`, 2026-06-29) de la tienda del cajero, pero los pedidos mock son de junio 20–24 y de otra tienda, así que la lista de anulables queda vacía y no hay venta sobre la cual abrir el modal. Sembrar un pedido anulable del día exige tocar fixtures de producto (fuera de alcance de este ticket). Es el mismo fallback de referencia del DS que la galería ya usa para W47/W57/W60. - **Diferencia esperada:** el render de playground no lleva el shell admin (sidebar + topbar) que sí muestra el frame. | | ### CAT — Catálogo [Section titled “CAT — Catálogo”](#cat--catálogo) Flujo W-04. Historias `US-CAT-1` … `US-CAT-3`. #### W20 · Productos [Section titled “W20 · Productos”](#w20--productos) **Ruta:** `/admin/pos/catalog` · **Figma:** [`118:1529`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=118-1529) · **Historia:** US-CAT-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W20](/design/parity/figma/screens/figma-w20-productos.png)](/design/parity/figma/screens/figma-w20-productos.png "figma W20 — abrir a tamaño completo") | [![app W20](/design/parity/history/2026-08-07/screenshots/admin-pos-catalog.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-catalog.png "app W20 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes** — los fixes sistémicos de color sí llegaron; el residuo es estructural y de contenido, más un bug de datos.- **Color:** ✅ Azul canónico verificado: tab activa + indicador **#0052CC**, botón «Nuevo» **#0052CC**, «Filtros» y «Pantalla completa» **#0052CC**; título **#1F2933**, cabecera de tabla **#F7F9FC**, divisores **#E5E7EB**. ❌ Chip «Activo» sólido **#10B981** con texto blanco; Figma usa StatusBadge soft (bg **#D0F4E6**, texto verde) — `ProductList.tsx` usa `Chip color="success"` en vez de StatusBadge. - **Color:** Ítem seleccionado del sidebar con pill gris **#EBEBEB**; en Figma es tinte de marca **#E6EEFA** (el tinte `primary.light` no llegó al nav del AppShell). - **Contenido (bug):** La columna Categoría muestra el **ID numérico** («1», «3», «5») en vez del nombre («Bebidas», «Panadería»…) — `src/app/components/PosCatalog/ProductList.tsx` (render de `field: 'category'` devuelve `row.category` crudo; falta resolver el nombre). - **Contenido:** Faltan del diseño: subtítulo «248 productos · 12 categorías», búsqueda dentro de la tab, chips segmentados Todos/Activos/Bajo stock y el select; solo existe el botón «Filtros» (además a la izquierda; en Figma va a la derecha). - **Layout:** Set de columnas distinto: Figma = avatar iniciales + Producto, SKU, Categoría, Precio, Stock, Estado, editar por fila; app = Nombre, Categoría, Precio, Variantes, Códigos de barras, Activo — sin SKU, sin Stock, sin acción de edición ni avatar (el Avatar tinte+iniciales promovido no llegó a esta tabla). - **Layout:** Paginación MUI («Filas por página», «1–6 de 6») vs paginación numerada del diseño («Mostrando 1–5 de 248», páginas 1…10). - **Estados:** Figma muestra fila seleccionada (checkbox azul + bg **#E6EEFA**); estado no ejercitado en la captura — verificar hover/selección de fila. - **Tipografía:** Botón dice «Nuevo» sin icono; Figma «+ Nuevo producto». Texto de filas renderiza oscuro (px medidos \~#0D1823–#28313B, consistente con #1F2933) ✅. - **Bordes:** Tabla con contenedor hairline y radios correctos ✅; tabs con patrón underline MUI vs pill segmentado con tinte del diseño (decisión de patrón pendiente, una línea y sigo). - **Iconos:** «Pantalla completa» no existe en el diseño (affordance de la app); topbar difiere del diseño (grupo de avatares JP/MJ/LR ausente) — sistémico del shell, no propio de esta pantalla. | | #### W21 · Producto crear / editar [Section titled “W21 · Producto crear / editar”](#w21--producto-crear--editar) **Ruta:** `/admin/pos/catalog/products/{new,[id]}` · **Figma:** [`120:2657`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=120-2657) · **Historia:** US-CAT-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W21](/design/parity/figma/screens/figma-w21-producto-edit.png)](/design/parity/figma/screens/figma-w21-producto-edit.png "figma W21 — abrir a tamaño completo") | [![app W21](/design/parity/history/2026-08-07/screenshots/admin-pos-catalog-products-new.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-catalog-products-new.png "app W21 — abrir a tamaño completo") | | Notas | | | **✅ Paridad alcanzada (#468)**W21 migrado de diálogo a página completa: `PageHeader` + tarjetas `Paper` + `StickySaveBar`, coincidiendo con el contenedor full-page del frame. Grid de dos columnas (izquierda: información + variantes; derecha: imagen + inventario).- **Layout:** ✅ página completa (antes diálogo lateral) — resuelve la decisión producto diálogo-vs-página a favor de la página, como el frame. - **Contenido:** ✅ añadidos SKU de producto, Costo, dropzone de **Imagen**, sección **Inventario** (stock inicial, alerta de stock bajo, umbral) y Descripción con placeholder (“Escribe una descripción del producto…”). - **Contenido:** ✅ Categoría ahora es select con opciones del catálogo (antes input de texto libre). - **Variantes:** ✅ opt-in con switch “Tiene variantes” (antes siempre visibles), y **reconstruida como la tabla del frame**: campo **Eje de variante** + columnas **Valor · SKU · Código de barras · Precio · Stock**, una fila por variante. El código primario se edita en la fila; un expansor revela el sub-editor multi-código (simbología/Principal) para no perder capacidad. Código de barras único a nivel producto en modo sin variantes. - **Barra sticky:** ✅ “Tienes cambios sin guardar · Descartar/Guardar” (antes footer de diálogo Cancelar/Guardar). - **Color:** ✅ literales `color="black"` eliminados de `ProductForm.tsx` — títulos y labels ahora resuelven a tokens del tema (#1F2933). - **⚠️ Divergencia intencional (bug del diseño):** el hint de Imagen es **“PNG o JPG · hasta 10 MB”**; el frame dice “CSV, PNG o JPG” pero un CSV no puede ser la imagen de un producto. Se mantiene la corrección y se marca el frame para actualizar. - **⚠️ Divergencia intencional (función > frame):** el frame no dibuja un control de estado, pero la lista filtra/insigna por `isActive` y ningún otro punto de la UI puede cambiarlo; se mantiene el switch **“Estado/Activo”** en la tarjeta de información. Marcar el frame para añadir el control. - **Seguimiento (no bloquea el merge):** la imagen seleccionada no se persiste a storage aún; `initialStock`/`minStock` **y el Stock por variante** se recogen en el payload pero su escritura a `StockLevels` es diferida (mismo patrón documentado). | | #### W22 · Detalle de producto [Section titled “W22 · Detalle de producto”](#w22--detalle-de-producto) **Ruta:** `— (drawer)` · **Figma:** [`133:10120`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=133-10120) · **Historia:** US-CAT-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------- | | [![figma W22](/design/parity/figma/screens/figma-w22-detalle-producto.png)](/design/parity/figma/screens/figma-w22-detalle-producto.png "figma W22 — abrir a tamaño completo") | ⚠️ **Falta captura** — no hay pantalla de detalle dedicada | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W23 · Categorías [Section titled “W23 · Categorías”](#w23--categorías) **Ruta:** `/admin/pos/catalog/categories` · **Figma:** [`122:2840`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-2840) · **Historia:** US-CAT-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W23](/design/parity/figma/screens/figma-w23-categorias.png)](/design/parity/figma/screens/figma-w23-categorias.png "figma W23 — abrir a tamaño completo") | [![app W23](/design/parity/history/2026-08-07/screenshots/admin-pos-catalog-categories.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-catalog-categories.png "app W23 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes** — el estilo (post-fix) ya coincide; el residuo es de composición y contenido propio de la pantalla.- **Color:** Fixes verificados en esta captura: botón primario “Nuevo” **#0052CC** (medido, = Figma), indicador y label de tab activa **#0052CC**, botón “Filtros” con icono **#0052CC**, cabecera de tabla **#F7F9FC**, divisores **#E5E7EB**, checkbox con trazo **#D1D5DB** y esquinas redondeadas, fondo **#F5F7FA**. El azul canónico llegó a toda la pantalla. - **Contenido:** El esquema de la tabla no es el del diseño: Figma pide Categoría (avatar de iniciales en tinte) / SKUs / Margen / Ventas mes / Productos / Estado + acción de edición; la app muestra Nombre+slug / Descripción / Orden / Activa / Fecha de Creación, sin columna de acciones ni avatar (el fix “Avatar en tinte + iniciales” no llegó aquí). - **Contenido:** Falta la toolbar de filtrado del diseño: campo “Buscar…”, chips segmentados Todas/Activas/Vacías y select “Selecciona una opción”; la app sólo tiene “Filtros”. - **Estados:** “Estado” en Figma es badge soft verde/rojo (Activa/Vacía); la app usa icono booleano monocromo **#1F2933** — el StatusBadge sobre tokens \*-soft no se aplicó a esta tabla. - **Layout:** Paginación distinta: Figma numerada con “Mostrando 1–5 de 12”; app usa TablePagination estándar (“Filas por página / 1–7 de 7”). Botón “Pantalla completa” extra en la app, ausente en Figma. - **Layout:** (sistémico conocido, 1 línea) tab “Categorías” dentro de Productos/Categorías/Impuestos vs página dedicada con encabezado “Categorías · 12 categorías” y CTA “+ Nueva categoría” — decisión de producto pendiente; la etiqueta “Nuevo” sin icono + deriva de esto. - **Estados:** Fila seleccionada con tinte #E6EEFA del diseño: sin fila seleccionada en la captura — verificar (el tinte existe en palette.primary.light). - **Tipografía / Bordes / Sombras:** Sin hallazgos: jerarquía nombre 600 + slug secundario, tarjeta plana con hairline, sin sombras — conforme. | | #### Categoría — alta [Section titled “Categoría — alta”](#categoría--alta) **Ruta:** `/admin/pos/catalog/categories (diálogo)` · **Figma:** [`122:2840`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-2840) · **Historia:** US-CAT-2 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Categoría — alta](/design/parity/figma/screens/figma-w23-categorias.png)](/design/parity/figma/screens/figma-w23-categorias.png "figma Categoría — alta — abrir a tamaño completo") | [![app Categoría — alta](/design/parity/history/2026-08-07/screenshots/admin-pos-catalog-categories--dialog-nueva-categoria.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-catalog-categories--dialog-nueva-categoria.png "app Categoría — alta — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Panel FIGMA** (`figma/screens/figma-w23-categorias.png`): pantalla de **listado de Categorías** — título “Categorías / 12 categorías”, botón “+ Nueva categoría”, buscador, chips Todas/Activas/Vacías, tabla (Categoría, SKUs, Margen, Ventas mes, Productos, Estado) y paginación. El frame **no contiene ningún formulario de alta** (ni modal ni drawer). - **Panel APP** (`screenshots/admin-pos-catalog-categories--dialog-nueva-categoria.png`): **drawer lateral “Nueva categoría”** abierto sobre el listado, con campos Nombre\*, Identificador, Descripción, Categoría Padre, Sucursal, Orden, Color y acciones Cancelar/Guardar. - **Causa probable:** el manifest emparejó el frame del *listado* (que corresponde al heading de listado de categorías) con la captura del *formulario de alta*. No hay frame de Figma del formulario en este par. - **Acción:** conseguir/exportar el frame de Figma del formulario “Nueva categoría” y re-auditar; mientras tanto no se puede emitir veredicto de paridad para esta pantalla. - Dato informativo (medido, no un veredicto): el azul canónico **#0052CC** sí llegó al drawer (botón Guardar y borde de foco de Nombre). | | #### W24 · Impuestos [Section titled “W24 · Impuestos”](#w24--impuestos) **Ruta:** `/admin/pos/tax-rates` · **Figma:** [`122:3134`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-3134) · **Historia:** US-CAT-3 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W24](/design/parity/figma/screens/figma-w24-impuestos.png)](/design/parity/figma/screens/figma-w24-impuestos.png "figma W24 — abrir a tamaño completo") | [![app W24](/design/parity/history/2026-08-07/screenshots/admin-pos-tax-rates.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-tax-rates.png "app W24 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes** — los tokens sistémicos llegaron (azul, bordes, textos, cabecera de tabla medidos y correctos), pero el residuo propio de la pantalla es sustancial: la columna Estado está rota de color y faltan piezas de la toolbar y del modelo de columnas.- **Color:** verificado en captura: botón “Nuevo”, tab activa e indicador = **#0052CC** ✓; cabecera de tabla **#F7F9FC** ✓; bordes/divisores **#E5E7EB** ✓; texto primario **#1F2933** / secundario \~#6B7280 ✓; checkbox con trazo **#D1D5DB** y geometría redondeada ✓. - **Estados (bug):** los iconos de “Activa” renderizan ambos en **#1F2933** (medido en check y en x) aunque el código pide `color="success"/"error"` — `src/app/components/PosTaxRates/PosTaxRateList.tsx:75` pasa claves de palette a `src/ui/FontAwesomeIcon.tsx:47`, que las reenvía como color CSS inválido y el SVG hereda `currentColor`. Figma pide chips soft (Activa #10B981 sobre #ECFCF5, Inactiva roja): reemplazar el icono por **StatusBadge** (\*-soft), que ya existe promovido. - **Contenido:** faltan columnas del diseño: **Tipo** (IVA/IEPS), monto ($) y conteo de **Productos**; el app muestra en cambio Impuesto Incluido y Fecha de Creación. Verificar contra el modelo de datos real. - **Contenido:** Figma trae búsqueda inline + chips rápidos **Todas/Activas/Inactivas** + select en la toolbar; el app solo tiene “Filtros” (búsqueda escondida en el diálogo). Falta el filtro rápido de estado (`FilterBar` en PosTaxRateList.tsx:177). - **Iconos:** sin acción de edición visible por fila (Figma tiene icono de lápiz); el app depende de `onRowClick`, affordance invisible. - **Iconos:** el botón primario dice “Nuevo” sin icono +; Figma: “Nueva tasa” con +. - **Layout:** Figma no incluye avatar de iniciales en app (CO/SA…) — el app usa nombre + región secundaria; menor. - **Layout:** paginación “Filas por página 7” + flechas vs numerada del diseño (sistémico de DataTable; el default 7 es raro). - **Contenido:** título “Tasas de Impuesto” (i18n) + subnav de catálogo vs página standalone “Impuestos” con subtítulo — composición en tabs es decisión de producto (#408), y el botón “Pantalla completa” no existe en el diseño; una línea, no se re-audita. | | #### Impuesto — alta [Section titled “Impuesto — alta”](#impuesto--alta) **Ruta:** `/admin/pos/tax-rates (diálogo)` · **Figma:** [`122:3134`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-3134) · **Historia:** US-CAT-3 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Impuesto — alta](/design/parity/figma/screens/figma-w24-impuestos.png)](/design/parity/figma/screens/figma-w24-impuestos.png "figma Impuesto — alta — abrir a tamaño completo") | [![app Impuesto — alta](/design/parity/history/2026-08-07/screenshots/admin-pos-tax-rates--dialog-nuevo-impuesto.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-tax-rates--dialog-nuevo-impuesto.png "app Impuesto — alta — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Panel FIGMA:** muestra la pantalla de **lista** de Impuestos (header “Impuestos”, filtros Todas/Activas/Inactivas, tabla de 5 tasas, paginación, botón “Nueva tasa”). No hay formulario de alta en ninguna parte del frame (`figma/screens/figma-w24-impuestos.png`, 1440×900 completo verificado). - **Panel APP:** muestra el **drawer “Nuevo Impuesto”** abierto sobre la lista: campos Nombre (enfocado), Tasa, Región, switches “Impuesto Incluido” (off) y “Activa” (on), acciones Cancelar/Guardar (`screenshots/admin-pos-tax-rates--dialog-nuevo-impuesto.png`). - **Causa probable:** el manifest empareja el alta con el frame de la lista — falta exportar el frame Figma del formulario de alta (si existe en el archivo) o corregir el pairing. - **Informativo (medido, no es auditoría de estilo):** los fixes sistémicos sí llegaron a esta captura — Guardar `#0052CC`, borde de campo enfocado `#0052CC`, track del switch Activa `#0051CA` (≈ azul canónico). Queda pendiente la decisión de producto diálogo-vs-drawer ya conocida. - **Acción:** conseguir la referencia Figma del alta y re-auditar el par. | | ### INV — Inventario [Section titled “INV — Inventario”](#inv--inventario) Flujo W-05. Historias `US-INV-1` … `US-INV-3`. #### W25 · Niveles de stock [Section titled “W25 · Niveles de stock”](#w25--niveles-de-stock) **Ruta:** `/admin/pos/stock-levels` · **Figma:** [`122:3426`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-3426) · **Historia:** US-INV-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W25](/design/parity/figma/screens/figma-w25-stock.png)](/design/parity/figma/screens/figma-w25-stock.png "figma W25 — abrir a tamaño completo") | [![app W25](/design/parity/history/2026-08-07/screenshots/admin-pos-stock-levels.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-stock-levels.png "app W25 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Misma pantalla conceptual (Inventario / niveles de stock), pero la app es una implementación mínima tipo placeholder que omite casi toda la estructura y el contenido del diseño. No es redirect ni otro paso del flujo. (No pude medir hex: el clasificador de Bash estuvo caído toda la sesión — los juicios de color van por lectura visual y marcados como *verificar*.)- **Contenido:** Faltan casi todos los bloques diseñados: título “Niveles de stock” + subtítulo “Existencias por tienda”, sub-tabs (Niveles / Movimientos / Transferencias / Ajustar), campo de búsqueda, chips de filtro (Todos / Bajo stock / Agotados), botón Filtros, badges de **Estado** (En stock / Bajo / Agotado), columnas **SKU** y **Costo**, avatares de producto con iniciales, checkboxes de selección de fila y paginación (“Mostrando 1–5 de 248”). Ninguno aparece en la app. - **Contenido/Datos:** La app muestra datos mock (`var-1001`, `store-1`) en vez de nombres/SKU reales; agrega una columna **Variante** (vacía) que no existe en Figma, y le faltan **Tienda**, **Costo** y **Estado** del diseño. - **Layout:** Figma coloca la tabla dentro de una card con borde redondeado; la app la deja casi a sangre con una columna de acción sin encabezado (botón **Ajustar** por fila). El header de la app sí lleva el tinte claro (\~#F7F9FC visual) y separadores hairline — consistentes con el sistema. - **Estados / Bug propio de pantalla:** ~~El select “Todas las sucursales” renderiza el label superpuesto~~ **Corregido** — se fijó `inputLabel.shrink` en el filtro (`StockLevelList.tsx`), el label “Sucursal” ahora va estático arriba y el valor debajo, sin superposición. Queda menor: el placeholder del buscador global aparece recortado (“Buscar productos, venta…”). - **Color (verificar):** El botón primario “Ajuste de inventario” se ve como azul de marca sólido, aparentemente el canónico #0052CC; “Ajustar”/“Pantalla completa” son outlined con texto azul sobre blanco y borde hairline — visualmente alineado con la remediación. Sin medición por Bash caído. - **Tipografía:** Etiqueta de botón bold blanca \~14/600 y encabezados de tabla en gris — consistentes con los fixes sistémicos.Conclusión (re-auditado 2026-07-23): los fixes sistémicos se confirman en la recaptura — SideNav con item activo en tinte de marca #E6EEFA + texto azul, header de tabla #F7F9FC + hairlines, CTA azul sólido y botones outlined con borde azul. El bug del label del Select está **corregido**. El residuo restante es **de alcance funcional, no de estilo**: la pantalla es un esqueleto frente al diseño (faltan sub-tabs Niveles/Movimientos/Transferencias, toolbar de filtros con chips, columnas SKU/Costo/Estado, avatares, selección por fila y paginación, además de datos reales en vez de mock). Recomendación: tratarlo como épica de feature (build-out de columnas/filtros + adaptador de datos), no como pulido de paridad — el theme y los átomos ya están alineados. | | #### W26 · Ajuste de inventario [Section titled “W26 · Ajuste de inventario”](#w26--ajuste-de-inventario) **Ruta:** `/admin/pos/stock-levels/adjust` · **Figma:** [`124:5630`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=124-5630) · **Historia:** US-INV-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W26](/design/parity/figma/screens/figma-w26-ajuste.png)](/design/parity/figma/screens/figma-w26-ajuste.png "figma W26 — abrir a tamaño completo") | [![app W26](/design/parity/history/2026-08-07/screenshots/admin-pos-stock-levels-adjust.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-stock-levels-adjust.png "app W26 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Misma pantalla (formulario “Ajuste de inventario”), y la remediación sistémica se ve reflejada: el azul de marca (nav activo “Inventario” sobre tinte, botón “Pantalla completa”, chip “Tienda demo”, logo) es visualmente consistente con #0052CC; textos #1F2933/#6B7280, bordes hairline #E5E7EB y campos de relleno blanco con alto \~40px. El residuo es propio de la pantalla, en composición y contenido:- **Layout:** Figma organiza los campos en una card con **grid de 2 columnas** (Tienda+Tipo, Cantidad+Motivo); la app los **apila en 1 sola columna** full-width. Divergencia estructural fuerte. - **Contenido/Controles:** Figma usa **inputs de texto con placeholders** (Coca-Cola 600ml, Tienda Centro, Entrada, +24, Compra a proveedor, Recepción #4521); la app usa **selects/dropdowns** para Producto/Variante, Sucursal, Tipo y Motivo — cambia el tipo de campo, no solo el estilo. - **Jerarquía/Layout:** la app **no tiene** el H1 de página “Ajuste de inventario” + subtítulo “Registra un ajuste de existencias”, ni el **botón azul “+ Guardar” superior**, ni la **barra sticky inferior** “Tienes cambios sin guardar” con Descartar/Guardar. En su lugar el título vive dentro de la card y arriba a la derecha hay “Pantalla completa”. - **Contenido:** etiquetas divergentes — Producto→“Producto / Variante”, Tienda→“Sucursal”, Nota→“Notas”; la app añade **asteriscos rojos de requerido** ausentes en Figma. - **Color:** hex exacto de los azules quedó **por verificar** (herramienta de muestreo caída durante la auditoría); visualmente coincide con el canónico.Culpable probable: pantalla `admin/pos/stock-levels/adjust` (composición del form) — falta el header de página con acción Guardar y la barra de cambios sin guardar; alinear grid a 2 columnas y revisar labels/controles vs. diseño. | | #### W27 · Movimientos de inventario [Section titled “W27 · Movimientos de inventario”](#w27--movimientos-de-inventario) **Ruta:** `/admin/pos/stock-movements` · **Figma:** [`122:3717`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-3717) · **Historia:** US-INV-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W27](/design/parity/figma/screens/figma-w27-movimientos.png)](/design/parity/figma/screens/figma-w27-movimientos.png "figma W27 — abrir a tamaño completo") | [![app W27](/design/parity/history/2026-08-07/screenshots/admin-pos-stock-movements.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-stock-movements.png "app W27 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Misma pantalla conceptual (movimientos de inventario), pero la app implementa una vista muy distinta y más incompleta que el diseño. Los fixes sistémicos parecen presentes (botón azul de marca sólido sin sombra, cabecera de tabla en tinte claro, badges con tono), pero el residuo propio de la pantalla es grande.- **Layout:** Falta el encabezado de página completo — no hay título **“Movimientos de inventario”** ni subtítulo **“Entradas y salidas (ledger)”**. La app arranca directo en la tabla. - **Contenido:** Ausente toda la barra de herramientas del diseño: **buscador**, **tabs Todos/Entradas/Salidas**, botón **Filtros** y **dropdown** “Selecciona una opción”. La app no ofrece ninguna de estas. - **Contenido:** Acción primaria distinta. Figma: un único **“+ Nuevo movimiento”**. App: **“Recibir transferencia”** + **“Transferencia”** + **“Pantalla completa”** — es otra intención de flujo (transferencias) montada sobre esta ruta. - **Contenido:** Columnas divergentes. Figma = `[✓] Producto (avatar+nombre) · Tipo · Motivo · Cantidad ($) · Saldo · Fecha · editar`. App = `Fecha · Producto (var-1001) · Variante (vacía) · Motivo · Ajuste (+50/-1/…)`. Sin checkbox de selección, sin avatar de producto y sin icono de editar por fila. - **Estados:** Sin fila seleccionada resaltada (en Figma la primera fila va marcada con check azul y fondo tenue). - **Contenido:** Sin **paginación**. Figma muestra “Mostrando 1–5 de 1,024” con paginador `1 2 3 … 10`. - **Color/Estados:** El “Motivo” se renderiza como **badge sólido** (verde Recepción, rojo Venta/Ajuste/Traspaso). El diseño usa etiquetas de texto para Tipo (Entrada/Salida) y Motivo (Compra/Venta/Merma), no pills de color. Revisar el mapeo StatusBadge para esta tabla. - **Color:** El azul del botón “Transferencia” y de la cabecera de tabla se leen sobre tokens de marca (no se pudo medir en pixel este run: el clasificador de Bash estaba caído; **verificar** #0052CC exacto).Fixes sistémicos: botón plano 14px/600, cabecera de tabla en tinte claro y separadores hairline se ven aplicados; el grueso del hallazgo es composición y contenido faltante propios de W27, no residuo de tokens. | | #### W28 · Transferencia entre tiendas [Section titled “W28 · Transferencia entre tiendas”](#w28--transferencia-entre-tiendas) **Ruta:** `/admin/pos/stock-movements/transfer` · **Figma:** [`124:5847`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=124-5847) · **Historia:** US-INV-3 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W28](/design/parity/figma/screens/figma-w28-transferencia.png)](/design/parity/figma/screens/figma-w28-transferencia.png "figma W28 — abrir a tamaño completo") | [![app W28](/design/parity/history/2026-08-07/screenshots/admin-pos-stock-movements-transfer.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-stock-movements-transfer.png "app W28 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Ambos paneles son el formulario de transferencia entre tiendas (misma pantalla). Los arreglos sistémicos SÍ llegaron aquí (medidos), pero la composición y el contenido divergen del diseño.- **Color:** Correcto y sobre token. Botón primario “Registrar transferencia” = #0052CC exacto (etiqueta blanca); relleno de campo #FFFFFF; borde hairline #E5E7EB; label #1F2933; lienzo #F5F7FA. Asterisco de requerido en rojo (#D74A4A), aceptable. - **Layout:** Divergencia principal. La app es una sola tarjeta angosta centrada con el título dentro; Figma es página ancha con título H1 “Transferencia entre tiendas” + subtítulo “Mueve existencias entre tiendas” y la tarjeta rotulada “Transferencia”. La app no tiene el encabezado de página. - **Layout:** Figma dispone los campos en rejilla de 2 columnas (Origen\|Destino, Cantidad\|Fecha); la app los apila en una sola columna. - **Contenido:** Falta el campo **Fecha** en la app. Renombres: Origen/Destino → “Sucursal de origen/destino”, Producto → “Producto / Variante”, Nota → “Notas”. - **Estados:** Figma usa inputs de texto con placeholders (Tienda Centro, Coca-Cola 600ml, 12, 24 Oct 2025); la app usa **selects/dropdowns** para producto y sucursales y campos vacíos con asterisco de requerido. Divergencia de control, no solo de estilo. - **Contenido:** Patrón de guardado distinto. Figma tiene “+ Guardar” arriba-derecha y **barra sticky** inferior (“Tienes cambios sin guardar” + Descartar + Guardar); la app tiene un único botón full-width “Registrar transferencia” dentro de la tarjeta y arriba-derecha muestra “Pantalla completa”. Falta la barra de acción persistente.Nota: la elección tarjeta-angosta-vs-página y el patrón de acción son decisiones de composición/producto; el residuo accionable propio de la pantalla es la ausencia del campo Fecha, la falta del encabezado de página y la falta de la barra sticky de guardado. | | #### Recepción de transferencia [Section titled “Recepción de transferencia”](#recepción-de-transferencia) **Ruta:** `/admin/pos/stock-movements/receive` · **Historia:** US-INV-3 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el flujo de recepción está sin diseñar (gap #16) | [![app Recepción de transferencia](/design/parity/history/2026-08-07/screenshots/admin-pos-stock-movements-receive.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-stock-movements-receive.png "app Recepción de transferencia — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | ### SHIFT — Turnos y caja [Section titled “SHIFT — Turnos y caja”](#shift--turnos-y-caja) Flujo W-07. Historias `US-SHIFT-1` … `US-SHIFT-5`. #### W32 · Turnos [Section titled “W32 · Turnos”](#w32--turnos) **Ruta:** `/admin/pos/shifts` · **Figma:** [`119:2117`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=119-2117) · **Historia:** US-SHIFT-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W32](/design/parity/figma/screens/figma-w32-turnos.png)](/design/parity/figma/screens/figma-w32-turnos.png "figma W32 — abrir a tamaño completo") | [![app W32](/design/parity/history/2026-08-07/screenshots/admin-pos-shifts.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-shifts.png "app W32 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Es la misma pantalla (ruta Turnos), no un redirect ni un diálogo, pero la implementación está muy por debajo del diseño: la app es una tabla genérica de turnos por empleado mientras el Figma es el historial de cortes de caja con toda su barra de acciones.- **Contenido:** faltan subtítulo “Historial de cortes de caja”, tabs (Turnos / Mov. efectivo / Cerrar turno), CTA primario **Abrir turno**, buscador, chips de filtro (Todos / Abiertos / Cerrados) y el dropdown “Selecciona una opción”. La app solo trae el botón **Filtros** (y un **Pantalla completa** que no está en el diseño). - **Layout / tabla:** el Figma tiene columna de selección (checkbox), avatares por fila y acción de editar; la app no tiene ninguna. Columnas distintas: Figma = Turno# / Cajero / Apertura / Vendido / Dif. / Estado; app = Empleado / Sucursal / Estado / Apertura / Diferencia. Distinto modelo de datos (corte por turno vs turno por empleado). - **Estados:** badge “Abierto” diverge en color y estilo — Figma = chip verde soft (`#70D6B4` sobre tinte); app = relleno **azul sólido `#3B82F6`** (rol info). Convendría alinear “Abierto” al tratamiento soft del design system. - **Color (marca):** el azul primario del Figma es `#0052CC`; en la app los controles outline (Filtros / Pantalla completa) leen un azul más brillante (\~`#3978D6`/`#5288DB` antialias), no el `#0052CC` canónico. Verificar que el botón use `primary.main`. - **Bien resuelto:** título `#1F2933`, cabecera de tabla `#F7F9FC`, divisores hairline `#E5E7EB`, badge neutro `#EBEBEB` y badges de diferencia con tokens \*-soft correctos (success `#10B981`, warning `#F59E0B`, error `#EF4444`) — los fixes sistémicos llegaron. - **Paginación:** Figma usa paginación numerada custom (“Mostrando 1–5 de 96”, 1 2 3 … 10); la app usa el TablePagination de MUI (“Filas por página / 1–7 de 7”). Diferencia de patrón, probablemente decisión de producto. | | #### W33 · Detalle de turno / Corte Z [Section titled “W33 · Detalle de turno / Corte Z”](#w33--detalle-de-turno--corte-z) **Ruta:** `— (ver W58)` · **Figma:** [`132:9833`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=132-9833) · **Historia:** US-SHIFT-5 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W33](/design/parity/figma/screens/figma-w33-detalle-z.png)](/design/parity/figma/screens/figma-w33-detalle-z.png "figma W33 — abrir a tamaño completo") | [![app W33](/design/parity/history/2026-08-07/screenshots/admin-pos-shifts--dialog-detalle.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-shifts--dialog-detalle.png "app W33 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **FIGMA:** página completa **“Turno #88 · Corte Z”** de un turno **cerrado** — tarjeta Resumen (Cajero Sofía Ramírez, Apertura Hoy 08:00, Cierre Hoy 16:00, Fondo inicial $1,500, Ventas efectivo $4,200, Retiros −$2,000, Efectivo esperado $3,700, Diferencia $0.00) y botón **“Reimprimir corte”**. Es el reporte Z ya emitido. - **APP:** **diálogo/panel** de detalle de un turno **abierto** (badge **“Abierto”**, “Pendiente de cierre”) superpuesto a la tabla de turnos — Ana Gómez · Sucursal Centro·term-01, Conciliación de Efectivo con Efectivo Esperado/Contado en **“—”** y Diferencia “Pendiente de cierre”, Movimientos “Sin movimientos”, Notas “Turno matutino en curso”. - **Desajuste de flujo:** el turno de la app **sigue abierto**, por lo que no existe un Corte Z que renderizar; además es diálogo, no página. Para auditar paridad hay que **recapturar un turno cerrado abriendo su vista de Corte Z** (la página con Resumen + Reimprimir corte). - No listo diferencias de estilo por tratarse de pantallas distintas. Nota lateral: el azul de marca sí aparece aplicado en la app (badge, “Pantalla completa”, nav activo), pero no es comparable aquí. | | #### W34 · Abrir turno [Section titled “W34 · Abrir turno”](#w34--abrir-turno) **Ruta:** `/terminal/shift/open` · **Figma:** [`132:9488`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=132-9488) · **Historia:** US-SHIFT-1 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W34](/design/parity/figma/screens/figma-w34-abrir-turno.png)](/design/parity/figma/screens/figma-w34-abrir-turno.png "figma W34 — abrir a tamaño completo") | [![app W34](/design/parity/history/2026-08-07/screenshots/terminal-shift-open.png)](/design/parity/history/2026-08-07/screenshots/terminal-shift-open.png "app W34 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Ambos paneles muestran el flujo *Abrir turno* con el teclado de fondo de caja: es la misma pantalla. El Figma la maqueta como tarjeta centrada dentro del chrome admin (sidebar + top bar); la app la renderiza como página fullscreen de terminal (sin sidebar, con “Bloquear ahora” y “Cancelar”). Es la conocida decisión sistémica página-vs-card embebida — pendiente de producto, no la repito como defecto de estilo.- **Color:** El azul de “Cancelar”, “Bloquear ahora” y el borde de foco del input se ve consistente con el canónico #0052CC; no pude medir el pixel (herramienta de muestreo caída por outage del clasificador) — dejar como *verificar medición*. - **Tipografía:** Título “Abrir turno” en gris oscuro (~~#1F2933) y subtítulo “Cuenta el fondo de caja inicial” en gris medio (~~#6B7280): jerarquía correcta, fix aplicado. - **Bordes:** Teclas del keypad y el input con hairline claro (\~#E5E7EB): coincide con el fix de bordes/campos. - **Estados:** Botón “Abrir turno” deshabilitado en gris (bg #E5E7EB + texto #9CA3AF) porque el monto es $0.00 — el fix de disabled se ve aplicado; en Figma aparece activo/azul por estar con $1,500.00 (diferencia de estado, esperada). - **Contenido:** La app **omite el bloque de identidad** Terminal `POS-01` / Cajero `Sofía R.` que el Figma coloca bajo el título; en su lugar la app muestra input editable “Fondo de caja”, enlace “Cancelar” y pie “Gerente Demo”. Evaluar si se agrega la meta Terminal/Cajero en la vista de terminal (terminal-shift-open). - **Iconos:** Teclas del keypad en la app van en cajas outlined redondeadas; en Figma los dígitos son planos (solo backspace enmarcado). Residuo menor de estilo del KeypadKey en esta pantalla. - **Layout:** Columna de contenido angosta centrada en viewport muy ancho (mucho whitespace lateral); propio de la página fullscreen de terminal, no de la tarjeta compacta del Figma. | | #### W35 · Cerrar turno [Section titled “W35 · Cerrar turno”](#w35--cerrar-turno) **Ruta:** `/terminal/shift/close` · **Figma:** [`132:9670`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=132-9670) · **Historia:** US-SHIFT-3 / US-SHIFT-4 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W35](/design/parity/figma/screens/figma-w35-cerrar-turno.png)](/design/parity/figma/screens/figma-w35-cerrar-turno.png "figma W35 — abrir a tamaño completo") | [![app W35](/design/parity/history/2026-08-07/screenshots/terminal-shift-close.png)](/design/parity/history/2026-08-07/screenshots/terminal-shift-close.png "app W35 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Panel FIGMA:** página “Cerrar turno · Corte Z” renderizada dentro del shell administrativo completo (sidebar “Terminales” + topbar con búsqueda y perfil). Contenido: tarjeta “Conteo de efectivo” (Efectivo esperado $5,800.00 / Efectivo contado $5,780.00 / Diferencia −$20.00), tarjeta “Ventas por método” (Efectivo, Tarjeta, Saldo, Total vendido $8,250.00) y acciones “Imprimir corte (Z)” (outline) + “Cerrar turno” (primario). Es una vista de **resumen / corte Z** ya calculado. - **Panel APP:** pantalla de **terminal** (shell POS, sin sidebar; botón “Bloquear ahora” arriba a la derecha) titulada “Cerrar turno” con subtítulo “Cuenta el efectivo por denominación”. Contenido: “Desglose de efectivo contado” con tabla de denominaciones ($1,000 → $0.50), cada una con input en 0 y subtotal $0.00; Total contado $0.00, Efectivo esperado $2,300.00, Diferencia −$2,300.00; campo “Notas (opcional)” y botón “Cerrar turno” deshabilitado. Es el **paso de captura por denominación**, no el resumen. - **Causa:** el par asocia el diseño de resumen Corte Z (admin) con la captura `screenshots/terminal-shift-close.png` (terminal, conteo por denominación). Ese contenido de denominaciones corresponde más bien a W65 (`admin-pos-shifts-close`). Para re-auditar W35 hay que recapturar la vista de resumen/corte Z equivalente, o re-mapear W35 a la pantalla de terminal correcta. No se listan diferencias de estilo porque los paneles no muestran la misma vista. | | #### W36 · Movimientos de caja (admin) [Section titled “W36 · Movimientos de caja (admin)”](#w36--movimientos-de-caja-admin) **Ruta:** `/admin/pos/cash-movements` · **Figma:** [`123:5405`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-5405) · **Historia:** US-SHIFT-2 / US-BAL-3 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W36](/design/parity/figma/screens/figma-w36-movimientos-caja.png)](/design/parity/figma/screens/figma-w36-movimientos-caja.png "figma W36 — abrir a tamaño completo") | [![app W36](/design/parity/history/2026-08-07/screenshots/admin-pos-cash-movements.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-cash-movements.png "app W36 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Es la misma pantalla (tabla admin de Movimientos de caja), no hay redirect ni paso distinto del flujo. Los fixes sistémicos SÍ llegaron a esta captura, el residuo es de composición/contenido propio de la pantalla.- **Color:** Verificado en la captura: azul de marca #0555CD (≈#0052CC) en badge de campana y botón Filtros; verde éxito #10B981 y rojo error en montos; cabecera de tabla #F7F9FC. Sin residuo de color. - **Contenido:** Falta el CTA primario azul relleno ‘+ Nuevo movimiento’ (la app solo tiene ‘Pantalla completa’ outline) — no hay acción de crear movimiento. - **Contenido:** Toolbar recortada: faltan campo de búsqueda, control segmentado Todos/Entradas/Salidas y el select ‘Selecciona una opción’; solo queda ‘Filtros’. - **Layout:** Columnas divergentes. Figma: checkbox · Movimiento (avatar+nombre) · Cajero · Motivo · Monto · Autoriza · Estado · editar. App: Tipo · Turno (+emp) · Monto · Motivo · Fecha. Faltan selección por fila, avatar, columna Cajero, columna Autoriza e icono de editar. - **Estados:** Sin StatusBadge de Estado (Registrado/Autorizado soft) en la app; el chip ‘Tipo’ es un pill neutro outline (#C3C3C3), no un status badge tintado. - **Componentes:** Paginación distinta — Figma numerada custom (‘1–5 de 42’, 1 2 3 … 10) vs TablePagination de MUI (‘Filas por página 7’, ‘1–7 de 7’). Decisión de componente pendiente. - **Color:** Montos coloreados verde/rojo en la app (mejora semántica razonable) frente a montos en negro con +/- en Figma; alinear criterio.Resumen: tokens sistémicos correctos; el gap es de paridad estructural/funcional (CTA, filtros, columnas de estado/cajero, paginación). | | #### W36b · Movimiento de caja (terminal) [Section titled “W36b · Movimiento de caja (terminal)”](#w36b--movimiento-de-caja-terminal) **Ruta:** `/terminal/shift/cash-movement` · **Figma:** [`123:5405`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-5405) · **Historia:** US-SHIFT-2 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W36b](/design/parity/figma/screens/figma-w36-movimientos-caja.png)](/design/parity/figma/screens/figma-w36-movimientos-caja.png "figma W36b — abrir a tamaño completo") | [![app W36b](/design/parity/history/2026-08-07/screenshots/terminal-shift-cash-movement.png)](/design/parity/history/2026-08-07/screenshots/terminal-shift-cash-movement.png "app W36b — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Panel Figma:** es la página **admin** de “Movimientos de caja” (la misma usada en W36): sidebar de navegación a la izquierda, barra de búsqueda superior, título “Movimientos de caja / Entradas y salidas del turno”, botón “Nuevo movimiento”, chips Todos/Entradas/Salidas + Filtros, y una **tabla** (Movimiento · Cajero · Motivo · Monto · Autoriza · Estado) con badges de estado y paginación “Mostrando 1-5 de 42”. - **Panel App:** es el **formulario de terminal** “Movimiento de efectivo”: subtítulo “Registra una entrada o salida de efectivo”, control segmentado Entrada/Salida/Depósito/Retiro, campo **Monto** ($0.00), **teclado numérico**, textarea “Motivo”, botón “Registrar” (deshabilitado) y enlace “Cancelar”. - **Causa:** el manifest empareja `figma-w36-movimientos-caja.png` (diseño admin) con `screenshots/terminal-shift-cash-movement.png` (pantalla de terminal). No hay un diseño Figma del formulario de terminal; se reutilizó el de W36. Sin diseño de referencia correcto, no procede auditar color/tipografía/espaciado. - **Acción:** conseguir/generar el Figma del formulario de terminal (teclado + Monto) para W36b, o reasignar esta captura. La verificación de los fixes sistémicos queda pendiente hasta tener el par correcto. - *Nota:* no se pudieron tomar mediciones de color (bash bloqueado por clasificador no disponible); el veredicto se apoya en la estructura del composite, que es inequívoca. | | #### W65 · Cerrar turno — desglose por denominación [Section titled “W65 · Cerrar turno — desglose por denominación”](#w65--cerrar-turno--desglose-por-denominación) **Ruta:** `/admin/pos/shifts/close?shiftId=…` · **Figma:** [`574:14732`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=574-14732) · **Historia:** US-SHIFT-3 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W65](/design/parity/figma/screens/figma-w65-cerrar-denominacion.png)](/design/parity/figma/screens/figma-w65-cerrar-denominacion.png "figma W65 — abrir a tamaño completo") | [![app W65](/design/parity/history/2026-08-07/screenshots/admin-pos-shifts-close.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-shifts-close.png "app W65 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Misma pantalla (Cerrar turno / Corte Z, desglose por denominación). Los tres KPIs coinciden en valores ($12,450.00 / $4,500.00 / $7,950.00) y los fixes sistémicos de color llegaron a esta vista.- **Color:** Azul de marca correcto en nav activo (“Turnos y caja”), chip “Tienda demo” y botón “Pantalla completa” (#0052CC). Residuo: el valor “Efectivo esperado” va en azul de realce en Figma pero la app lo pinta en texto oscuro #1F2933 como los otros KPIs — falta el tinte. - **Layout:** Diferencia principal — Figma empaqueta las 11 denominaciones en **dos columnas** compactas; la app las apila en **una sola columna** full-width, lo que baja mucho la densidad y empuja el totalizador y el botón fuera de pantalla. Revisar el grid del componente de cierre de turno. - **Espaciado / Composición:** Cada fila de denominación en la app se extiende a todo el ancho (monto izq. · input centro · resultado $0.00 extremo derecho) con divisor hairline; Figma usa el formato inline compacto “$1,000 × 2 = $2,000.00”. - **Contenido:** Título unificado en Figma (“Cerrar turno · Corte Z”) vs. título + subtítulo en la app (“Cuenta el efectivo por denominación (Corte Z)”) — reordenamiento menor, aceptable. Los conteos prellenados de Figma vs. ceros en la app son solo datos. - **Verificar:** Total contado / Diferencia (−$15.00) y botón “Cerrar turno (Z-Report)” quedaron bajo el fold en la captura; no auditables aquí. Recapturar a mayor alto. - **Nota herramientas:** imgtool/Bash no disponibles esta sesión; colores evaluados por lectura visual de ambos paneles, no muestreados en px. | | ### STAFF — Personal y control de acceso [Section titled “STAFF — Personal y control de acceso”](#staff--personal-y-control-de-acceso) Flujo W-08. Historias `US-STAFF-1` … `US-STAFF-4`. #### W37 · Empleados [Section titled “W37 · Empleados”](#w37--empleados) **Ruta:** `/admin/pos/employees` · **Figma:** [`119:2409`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=119-2409) · **Historia:** US-STAFF-1 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W37](/design/parity/figma/screens/figma-w37-empleados.png)](/design/parity/figma/screens/figma-w37-empleados.png "figma W37 — abrir a tamaño completo") | [![app W37](/design/parity/history/2026-08-07/screenshots/admin-pos-employees.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-employees.png "app W37 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**- **Color:** Fix sistémico confirmado — medí #0052CC en el botón “Nuevo”, en el subrayado del tab activo y en los chips de rol; sin rastro del azul viejo (#0057B8). - **Contenido (título):** App titula “Empleados” sin subtítulo; Figma titula “Personal” con contador “8 empleados · 3 roles”. Falta el contador. - **Contenido (toolbar):** Faltan el campo de búsqueda, el control segmentado Todos/Activos/Invitados y el select “Selecciona una opción”; la app solo expone “Filtros”. - **Contenido (CTA):** App “Nuevo” vs Figma “Invitar usuario” con icono +. Falta el glifo y la etiqueta correcta. - **Contenido (columnas):** App = Usuario/Sucursal/Rol/Activo/Fecha de Creación; Figma = Empleado/Rol/Tienda/Ventas/Turnos/Estado. Faltan **Ventas** y **Turnos**; el **Estado** (StatusBadge Activo/Invitado/Inactivo) se reemplazó por un icono booleano “Activo”; se añadió “Fecha de Creación” que no está en el diseño. - **Iconos (filas):** Faltan los avatares con iniciales por fila y la acción de editar (lápiz) por fila que sí aparecen en Figma. - **Estados:** El Rol se pinta como chip azul (Gerente/Supervisor) en la app; en Figma es texto plano — decoración de más respecto al diseño. - **Contenido (datos):** Sucursal muestra IDs crudos “store-1/store-2” en lugar de nombres legibles (“Tienda Centro/Norte/Sur”); mock por revisar. - **Layout (paginación):** App usa TablePagination de MUI (“Filas por página 7 · 1–5 de 5”) frente a la paginación numerada del diseño (“Mostrando 1–5 de 8 · 1 2 3 … 10”); además “7 filas/página” con solo 5 filas y total 5 es inconsistente. - **Bordes:** Cabecera de tabla medida en blanco (#FFFFFF) en vez del gris tokenizado #F7F9FC; los hairlines #E5E7EB de separación sí están presentes. | | #### W38 · Empleado crear / editar [Section titled “W38 · Empleado crear / editar”](#w38--empleado-crear--editar) **Ruta:** `/admin/pos/employees (formulario)` · **Figma:** [`124:6018`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=124-6018) · **Historia:** US-STAFF-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W38](/design/parity/figma/screens/figma-w38-empleado-edit.png)](/design/parity/figma/screens/figma-w38-empleado-edit.png "figma W38 — abrir a tamaño completo") | [![app W38](/design/parity/history/2026-08-07/screenshots/admin-pos-employees--dialog-nuevo-empleado.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-employees--dialog-nuevo-empleado.png "app W38 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Panel FIGMA:** página completa de edición de empleado. Header “Empleado / Datos, rol y acceso” con botón *Guardar* superior; dos tarjetas planas — **Datos personales** (Nombre completo, Correo, Teléfono) y **Acceso** (Rol, Tienda, PIN, Estado) — y una *sticky save bar* inferior (“Tienes cambios sin guardar” · Descartar · Guardar). - **Panel APP:** la captura no es esa página, sino un **drawer lateral derecho** montado sobre la lista de Empleados (se ven Filtros y las filas Ana López, Bruno Martínez, etc. detrás). Sus campos son **Sucursal, Usuario, Rol, PIN** más un switch **Activo**, con acciones *Cancelar / Guardar* al pie del drawer. - **Desajuste de composición:** página vs. drawer. No es solo el conocido debate diálogo-vs-página: además difiere el contenido — la app no tiene Nombre completo, Correo, Teléfono, Tienda ni Estado, y añade Sucursal, Usuario (select) y el toggle Activo que la página Figma no muestra. - **Fixes sistémicos (medidos en el drawer, sí llegaron):** azul de marca **#0052CC** en *Guardar*; switch activo **#0051CA**; *Cancelar* como botón secundario blanco con borde. El azul canónico está aplicado. - **Acción:** recapturar la **página** `/empleados/nuevo` (o `/[id]/editar`) que corresponde a este diseño Figma, en vez del drawer de alta rápida de la lista de Empleados. Sin esa captura no puede auditarse la paridad real de la pantalla W38. | | #### W39 · Invitar usuario [Section titled “W39 · Invitar usuario”](#w39--invitar-usuario) **Ruta:** `/admin/users` · **Figma:** [`131:8930`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=131-8930) · **Historia:** US-STAFF-2 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W39](/design/parity/figma/screens/figma-w39-invitar.png)](/design/parity/figma/screens/figma-w39-invitar.png "figma W39 — abrir a tamaño completo") | [![app W39](/design/parity/history/2026-08-07/screenshots/admin-users.png)](/design/parity/history/2026-08-07/screenshots/admin-users.png "app W39 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**El fix sistémico de color SÍ llegó a esta pantalla; el residuo es de composición/jerarquía propio de la lista de usuarios. (El diálogo de invitación que diseña el Figma se audita en W39b — aquí comparo la página lista.)- **Color:** Verificado OK — azul canónico #0052CC en botón Invitar usuario, Filtrar, Nuevo y pills Propietario/Administrador; pill gris #EBEBEB, lápiz #757575, papelera #EF4444, header de tabla #F7F9FC. Sin residuo de color. - **Layout:** El botón **Invitar usuario es un banner azul full-width** enorme, mientras Figma lo tiene como botón compacto en el header superior-derecho. Rehacer como acción de header. - **Jerarquía:** Tres primarios azules rellenos compiten en la misma vista (Invitar usuario + Filtrar + Nuevo). Figma tiene UN solo primario; “Filtros” es un botón secundario/outline. Degradar Filtrar/Nuevo a secundario. - **Contenido:** Falta el **page header** (título de página + subtítulo con conteo, p.ej. “8 empleados · 3 roles”) que abre la pantalla en Figma; la app salta del top bar directo al banner. - **Contenido:** Faltan los **filter chips** (Todos / Activos / Invitados) que Figma coloca sobre la tabla. - **Espaciado:** El bloque “Buscar” (label + campo vacío) queda desconectado a la izquierda mientras Filtrar/Nuevo viven al extremo derecho; Figma integra búsqueda y filtros en una sola toolbar. - **Estados:** La columna **Activo** usa círculos negros rellenos (check/x) — muy pesados y fuera del lenguaje; Figma usa status chips soft (Activo/Invitado/Inactivo). Migrar a StatusBadge sobre tokens \*-soft. - **Iconos:** Las filas no muestran **avatar/iniciales** en la celda Nombre; Figma sí (CO, SA, PA…). Añadir Avatar en tinte con iniciales primary. - **Contenido:** Columnas distintas (app: Nombre/Correo/Rol/Sucursal/Activo/Fecha/Acciones vs Figma: Empleado/Rol/Tienda/Ventas/Turnos/Estado) — decisión de modelo de datos; la sección “Invitaciones pendientes” es un añadido funcional válido no presente en Figma. | | #### W39b · Invitar usuario — diálogo [Section titled “W39b · Invitar usuario — diálogo”](#w39b--invitar-usuario--diálogo) **Ruta:** `/admin/users (diálogo)` · **Figma:** [`131:8930`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=131-8930) · **Historia:** US-STAFF-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W39b](/design/parity/figma/screens/figma-w39-invitar.png)](/design/parity/figma/screens/figma-w39-invitar.png "figma W39b — abrir a tamaño completo") | [![app W39b](/design/parity/history/2026-08-07/screenshots/admin-users--dialog-invitar.png)](/design/parity/history/2026-08-07/screenshots/admin-users--dialog-invitar.png "app W39b — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Los fixes sistémicos llegaron y los medí en esta captura: nada de regresiones de token.- **Color:** OK. Borde de campos #E5E7EB, fondo del botón deshabilitado #E5E7EB (exacto), subtítulo #6B7280, título \~#1F2933. El banner azul de fondo mide #002966 = #0052CC atenuado por el scrim → azul de marca canónico correcto. - **Bordes:** OK. Campos con hairline #E5E7EB y radio suave; diálogo con esquinas redondeadas (radio 16 aparente). - **Sombras:** OK. Scrim plano #7F7F7F sin halo de sombra dura en el borde del diálogo. - **Estados:** El botón **Enviar invitación** está deshabilitado (#E5E7EB / texto #9CA3AF) porque la captura es con el formulario vacío; en Figma aparece habilitado (azul, con “Cajero” cargado). Es la diferencia de estado por el momento de captura, no un defecto de estilo. - **Layout:** Residuo propio de la pantalla — la app usa una plantilla de diálogo distinta a la de Figma: título **centrado** (Figma lo alinea a la izquierda), **sin botón X** de cierre, y acciones como **dos botones a todo el ancho** en lugar de compactas alineadas a la derecha. El diálogo es bastante más grande y con más aire vertical que el compacto de Figma. - **Contenido:** La app muestra un **subtítulo** (“Se enviará un correo…”) en lugar de los “Texto de ayuda” bajo cada campo del diseño. El campo **Rol** es un **select con asterisco requerido `*`**; en Figma es un campo de texto con valor “Cajero” y sin marca de requerido. - **Tipografía / Espaciado:** Sin regresiones de token; el mayor aire vertical viene de la plantilla de diálogo genérica (padding de acciones amplio), no de un valor roto.Recomendación: alinear la plantilla del diálogo (título a la izquierda + X, acciones compactas a la derecha) y decidir a nivel producto helper-text vs subtítulo y select-vs-texto para Rol. | | #### W40 · Roles y permisos [Section titled “W40 · Roles y permisos”](#w40--roles-y-permisos) **Ruta:** `/admin/pos/employees/roles` · **Figma:** [`130:8533`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=130-8533) · **Historia:** US-STAFF-3 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W40](/design/parity/figma/screens/figma-w40-roles.png)](/design/parity/figma/screens/figma-w40-roles.png "figma W40 — abrir a tamaño completo") | [![app W40](/design/parity/history/2026-08-07/screenshots/admin-pos-employees-roles.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-employees-roles.png "app W40 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Misma pantalla (matriz de roles Cajero/Supervisor/Gerente). Los tokens sistémicos están correctos; el residuo es de composición y contenido faltante propio de esta vista.- **Color:** Fixes confirmados por muestreo — tab activo y underline #0052CC (450,228), icono de nav activo #0052CC, título #1F2933 (292,144), subtítulo \~#767D8B, cabecera de tabla #F7F9FC, borde/divisor #E5E7EB. Nada que corregir aquí. - **Contenido:** Falta la columna **Ámbito** del diseño (etiquetas Tienda/Comercio con color) — pérdida semántica real, no solo estética. - **Contenido:** Falta toda la **barra de herramientas** del diseño: buscador, chips de filtro (Todos/Cajero/Supervisor), botón **Filtros** y el select “Selecciona una opción”. - **Contenido:** Falta el **Alert informativo** “Matriz de solo lectura” (la app lo reduce al subtítulo “Solo lectura”) y la **paginación** (“12 permisos · 3 roles”, páginas 1…10). - **Contenido:** El botón primario **“Ver documentación”** del diseño se sustituyó por “Pantalla completa”; la app además introduce tabs Empleados/Roles que el Figma no plantea. - **Iconos/Estados:** Los checks de la app son verdes #32AE60 (854,328) frente a los checks neutros del Figma; el guion “—” (deshabilitado) coincide en gris. Diferencia de criterio, probablemente aceptable pero no fiel al diseño. - **Layout:** La app muestra más filas de capacidades (lista larga con scroll) en lugar de la tabla paginada de 5 permisos del diseño; densidad y jerarquía de tabla por lo demás consistentes con los tokens. | | ### REP — Reportes [Section titled “REP — Reportes”](#rep--reportes) Flujo W-11. Historias `US-REP-1` … `US-REP-6`. #### W55 · Reportes (overview) [Section titled “W55 · Reportes (overview)”](#w55--reportes-overview) **Ruta:** `/admin/pos/reports` · **Figma:** [`125:6381`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=125-6381) · **Historia:** US-REP-1 / US-REP-6 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W55](/design/parity/figma/screens/figma-w55-reportes-overview.png)](/design/parity/figma/screens/figma-w55-reportes-overview.png "figma W55 — abrir a tamaño completo") | [![app W55](/design/parity/history/2026-08-07/screenshots/admin-pos-reports.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-reports.png "app W55 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Misma pantalla (Reportes → Resumen): tabs Resumen/Ventas/Comisiones/Cortes Z, subtabs Ventas/Productos/Empleados, 4 KPI cards, gráfico “Ventas por hora” (línea), “Por método de pago” (barras) y tabla “Top productos”. Los fixes sistémicos llegaron a esta pantalla.- **Color:** Azul canónico verificado — barras y glifo del icono KPI en **#0052CC**; tint de tab activa **#E1E9F6** (≈ #E6EEFA); delta en verde. Correcto. - **Tipografía:** Label KPI en gris #6B7280, valor en #1F2933 oscuro. OK. - **Bordes:** Cards planas con hairline #E5E7EB, sin sombra. OK. - **Contenido (KPIs):** El set de métricas difiere del diseño — app muestra *Venta bruta / Venta neta / Transacciones / Ticket promedio*; Figma diseña *Ventas del periodo / Transacciones / Ticket promedio / Comisiones*. Falta la card **Comisiones**. Dato/config de reporte. - **Contenido (delta):** Figma muestra el % real (“▲ 8% vs periodo”); la app muestra el placeholder “▲ Periodo seleccionado” sin porcentaje en las 4 cards. - **Layout/Estados:** El control de rango de fechas usa **inputs `` nativos** (dígitos en fuente del sistema + glifo de calendario del navegador) dentro de un pill, en vez del dropdown único estilizado “1 Oct – 24 Oct, 2025” del diseño → rompe la paridad tipográfica del control. Culpable: componente de rango de fechas de Reportes. - **Layout:** La app agrega un selector “Sucursal Centro” y un botón outlined “Pantalla completa” que no están en el Figma (adiciones de la implementación, no defecto de estilo). - **Iconos:** El cuadro del icono KPI va en tint de marca con glifo #0052CC; en Figma se ve más neutro/gris. Diferencia mínima. | | #### W56 · Reporte de ventas [Section titled “W56 · Reporte de ventas”](#w56--reporte-de-ventas) **Ruta:** `/admin/pos/reports/daily-sales` · **Figma:** [`130:7959`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=130-7959) · **Historia:** US-REP-1 … US-REP-4 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W56](/design/parity/figma/screens/figma-w56-reporte-ventas.png)](/design/parity/figma/screens/figma-w56-reporte-ventas.png "figma W56 — abrir a tamaño completo") | [![app W56](/design/parity/history/2026-08-07/screenshots/admin-pos-reports-daily-sales.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-reports-daily-sales.png "app W56 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **FIGMA (diseño):** “Reporte de ventas · Desglose del periodo” — tabla de líneas de producto con tabs (Por producto / Por categoría / Por empleado / Por método), columnas Producto · SKU · Categoría · Ingresos · Unidades · Tendencia, selección por checkbox, botón **Exportar** y paginación (Mostrando 1–5 de 248). - **APP (implementación):** “Ventas del Día · Resumen diario del punto de venta” — otro reporte del módulo: filtros **Fecha** / **Sucursal**, tarjetas KPI (Venta Bruta $22,691.98 · Venta Neta $18,494.51 · Transacciones 179 · Ticket Promedio $103.32), gráfico de barras **Ventas por Hora** y sección **Productos Más Vendidos**. Incluye botón “Pantalla completa”. - **Ruta a corregir:** la captura apunta a la vista de dashboard diario, no al desglose de ventas por producto que diseña W56. Hay que recapturar la ruta correcta (tabla con tabs y Exportar) antes de poder auditar paridad de estilos. - No se listan diferencias de color/tipografía/bordes porque no hay pantalla equivalente que comparar. | | #### W57 · Comisiones [Section titled “W57 · Comisiones”](#w57--comisiones) **Ruta:** `— (sin ruta)` · **Figma:** [`130:8242`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=130-8242) · **Historia:** US-REP-5 / US-VAS-6 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W57](/design/parity/figma/screens/figma-w57-comisiones.png)](/design/parity/figma/screens/figma-w57-comisiones.png "figma W57 — abrir a tamaño completo") | [![app W57](/design/parity/history/2026-08-07/screenshots/playground/w57-comisiones.png)](/design/parity/history/2026-08-07/screenshots/playground/w57-comisiones.png "app W57 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Los fixes sistémicos llegaron a esta pantalla (medidos sobre la captura nueva). El único residuo real es el estilo del control de filtros.- **Color:** azul de marca correcto en todo — Exportar #0052CC, checkbox de fila y select-all #0052CC, tinte de avatar con inicial primary. Chips de estado en pills soft reales (verde #ECFDF5, ámbar, rojo) que coinciden con Figma. - **Estados:** tabs de filtro divergen. Figma → chip activo en tinte `#E6EEFA` + texto azul, y `Servicios`/`Tarjeta` como pills grises `#F0F4F8`. App → `Todas` como botón sólido `#0052CC` con texto blanco, y `Servicios`/`Tarjeta` como texto pelado sin pill. Es el hallazgo principal; probable ToggleButtonGroup/Tabs con variante equivocada. - **Estados:** el checkbox select-all de la cabecera muestra indeterminado (correcto por la selección parcial de 1 fila); Figma lo dibuja vacío. Matiz menor, la app es más correcta. - **Bordes:** cabecera de tabla `#F7F9FC`, fila seleccionada `#EBF1FB`, divisores hairline `#E5E7EB` — todo alineado. - **Contenido:** el dropdown derecho trunca a “Selecciona una …” vs. el placeholder completo en Figma; el ícono de editar va dentro de un icon-button con borde sutil vs. glifo pelado en Figma. Ambos menores. - **Layout:** 5 filas, mismos importes, “Mostrando 1-5 de 18” y paginación `1 2 3 … 10` idénticos. La app se captura solo-contenido (sin sidebar/AppBar) — diferencia de captura conocida, no de pantalla. | | #### W58 · Cortes de caja (Z) [Section titled “W58 · Cortes de caja (Z)”](#w58--cortes-de-caja-z) **Ruta:** `/admin/pos/reports/z-report` · **Figma:** [`123:4823`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-4823) · **Historia:** US-SHIFT-5 / US-REP-6 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W58](/design/parity/figma/screens/figma-w58-cortes-z.png)](/design/parity/figma/screens/figma-w58-cortes-z.png "figma W58 — abrir a tamaño completo") | [![app W58](/design/parity/history/2026-08-07/screenshots/admin-pos-reports-z-report.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-reports-z-report.png "app W58 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Panel Figma:** vista *Cortes de caja (Z)* — “Histórico de cortes Z”. Es una **lista/tabla** con columnas Corte · Cajero · Fecha · Vendido · Diferencia · Estado, chips de filtro (Todos / Cuadrados / Con diferencia), buscador, botón **Filtros**, botón **Exportar**, fila seleccionada con checkbox, StatusBadges (Cuadrado/Diferencia), avatares con iniciales y paginación “Mostrando 1–5 de 96”. - **Panel App:** vista de **detalle de un único corte Z** — cabecera con selector *Turno 2 · cerrado*, botones **Imprimir** y **Pantalla completa**, y una tarjeta con el desglose del reporte: **Ventas** (Venta Bruta/Neta, Impuestos, Descuentos), **Formas de Pago**, **Cancelaciones** y **Conciliación de Efectivo**. No hay tabla ni listado. - **Desajuste:** la captura corresponde a un paso distinto del flujo (el reporte individual que se abre *desde* la lista), no al histórico que diseña el Figma. Hay que recapturar la ruta de la lista de cortes Z (probablemente Reportes → Cortes de caja Z) para poder auditar paridad. - No listo diferencias de color/tipografía/espaciado porque no son la misma pantalla; cualquier medición sería engañosa. | | #### Reportes (ruta legacy) [Section titled “Reportes (ruta legacy)”](#reportes-ruta-legacy) **Ruta:** `/admin/reports` · **Historia:** US-REP-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — ruta legacy duplicada de W55; decidir si se retira | [![app Reportes (ruta legacy)](/design/parity/history/2026-08-07/screenshots/admin-reports.png)](/design/parity/history/2026-08-07/screenshots/admin-reports.png "app Reportes (ruta legacy) — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | ### VAS — Servicios de valor agregado [Section titled “VAS — Servicios de valor agregado”](#vas--servicios-de-valor-agregado) Flujo W-09. Historias `US-VAS-1` … `US-VAS-6`. #### W41 · Pago de servicios [Section titled “W41 · Pago de servicios”](#w41--pago-de-servicios) **Ruta:** `/admin/service-payments` · **Figma:** [`127:6816`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=127-6816) · **Historia:** US-VAS-3 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W41](/design/parity/figma/screens/figma-w41-servicios.png)](/design/parity/figma/screens/figma-w41-servicios.png "figma W41 — abrir a tamaño completo") | [![app W41](/design/parity/history/2026-08-07/screenshots/admin-service-payments.png)](/design/parity/history/2026-08-07/screenshots/admin-service-payments.png "app W41 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Misma pantalla (Pago de servicios), no es captura equivocada. Los arreglos de estilo llegaron bien; el residuo es de composición/contenido.- **Color:** Azul de marca correcto y medido: botón **Vaciar** y **Total** = `#0052CC` exacto; ícono JMAS en info `#3B82F6`; disabled del botón Continuar `#E5E7EB` bg + `#9CA3AF` texto. Todo conforme a la remediación. - **Estados:** El item de nav **Servicios** activo usa gris neutro `#EBEBEB` en lugar del tinte de marca `#E6EEFA` de Figma — el `palette.primary.light` no llegó al `selected` del ListItem del shell (sidebar compartido). Finding real aunque sea shell-level. - **Layout:** Falta por completo la **barra de pestañas** del diseño (Servicios / Tiempo aire / Pines / Transferir / Depósitos / Wallet). La app va directa al contenido de Servicios. - **Contenido:** La tarjeta izquierda difiere de paradigma: Figma = **Categoría** (chips Luz/Agua/Gas/Internet/Teléfono/TV) + fila de **Proveedor** único (CFE); la app = **grid de proveedores** (CFE, JMAS Juárez, Telmex, Gas Natural) con íconos de color, sin chips de categoría. Decisión de producto pendiente, pero es la mayor divergencia visible. - **Estados (captura):** La app está en estado vacío (Importe placeholder “Consultar adeudo”, Total `$0.00`, Continuar disabled) mientras Figma muestra estado lleno (`$688.00`, botón activo “Agregar a la venta”). Diferencia de estado + etiqueta de botón (**Continuar** vs **Agregar a la venta**). - **Bordes/Sombras:** Tarjetas y campos con hairline `#E5E7EB` y sin sombra — coincide. - **Tipografía:** Título oscuro y subtítulo `~#6B7280` — coincide; misma jerarquía de encabezado. | | #### W42 · Tiempo Aire (TAE) [Section titled “W42 · Tiempo Aire (TAE)”](#w42--tiempo-aire-tae) **Ruta:** `/admin/airtime` · **Figma:** [`127:7043`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=127-7043) · **Historia:** US-VAS-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W42](/design/parity/figma/screens/figma-w42-tae.png)](/design/parity/figma/screens/figma-w42-tae.png "figma W42 — abrir a tamaño completo") | [![app W42](/design/parity/history/2026-08-07/screenshots/admin-airtime.png)](/design/parity/history/2026-08-07/screenshots/admin-airtime.png "app W42 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**Ambos paneles son la ruta “Tiempo Aire”, pero muestran **pasos distintos del flujo**, así que no procede una auditoría de estilo lado a lado.- **Panel FIGMA:** página única de recarga ya compuesta — subtítulo “Recargas de saldo”, sección **Compañia** con proveedores como *chips cuadrados pequeños con avatar de letra* (Telcel, Movistar, AT\&T, Unefon, Bait), grilla **Monto** ($10–$500) con $50 seleccionado, y panel derecho **Captura** con “Número de teléfono”, “Total (incl. comisión) $51.00” y botón “Agregar a la venta”. - **Panel APP:** solo el **primer paso** del asistente — subtítulo “Telcel, AT\&T, Movistar y más”, encabezado “Selecciona el proveedor” con *tarjetas grandes con ícono de teléfono y subtítulo “México”* (Telcel, AT\&T, Movistar, Unefon, Virgin Mobile, OXXO Cel), y panel derecho **Resumen de compra** con Monto/Comisión/Total a cobrar en $0.00 y botón “Continuar” (deshabilitado). No hay grilla de montos ni captura de teléfono en esta vista — presumiblemente aparecen en un paso posterior.**Diferencias de fondo (no de estilo):** lista de proveedores distinta (Bait vs Virgin Mobile/OXXO Cel), proveedor como chip vs tarjeta, montos y captura presentes en Figma pero ausentes en la app, “Captura/Agregar a la venta” vs “Resumen de compra/Continuar”.**Fix sistémico verificado en esta captura:** el azul de marca del botón “Vaciar” mide **#0052CC** (canónico) — la remediación llegó a esta pantalla.Para re-auditar paridad hace falta recapturar la app en el mismo paso que diseña el Figma (proveedor + monto + captura en una sola vista) o alinear el diseño de Figma con el asistente por pasos de la app. | | #### W43 · Pines / tarjetas de regalo [Section titled “W43 · Pines / tarjetas de regalo”](#w43--pines--tarjetas-de-regalo) **Ruta:** `/admin/giftcards` · **Figma:** [`127:7220`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=127-7220) · **Historia:** US-VAS-2 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W43](/design/parity/figma/screens/figma-w43-pines.png)](/design/parity/figma/screens/figma-w43-pines.png "figma W43 — abrir a tamaño completo") | [![app W43](/design/parity/history/2026-08-07/screenshots/admin-giftcards.png)](/design/parity/history/2026-08-07/screenshots/admin-giftcards.png "app W43 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** — misma pantalla; los fixes sistémicos llegaron, el residuo es copy y estado de captura.- **Color:** Azul de marca correcto y medido: botón Vaciar #0052CC, texto Total (incl. comisión) #0052CC, deshabilitado bg #E5E7EB. Glifos de marca (Netflix #E50914, Spotify verde, GP azul) son tintes propios de cada brand, correctos. - **Estados:** La captura de la app está en estado vacío por defecto (ninguna marca seleccionada, Total $0.00, botón “Agregar a la venta” deshabilitado). El Figma muestra estado lleno (Netflix + $200 seleccionados). No es un defecto, son estados distintos del mismo flujo. - **Contenido:** La grilla “Denominación” ($100–$1,500) NO está ausente por bug: `VasSaleScreen.tsx:720` la renderiza solo tras elegir marca. Como la captura no tiene marca seleccionada, no aparece — verificar su estilo/selección requiere una captura con marca elegida. - **Tipografía/Contenido:** Copy divergente vs Figma — título “Tarjetas de Regalo” (app) vs “Tarjetas de regalo”; subtítulo “iTunes, Google Play, Spotify y más” vs “Pines digitales” del mock. Definido en `vas.i18n.ts:118-119`; parece decisión de producto, no defecto de estilo. - **Bordes/Sombras:** Tarjetas Marca y Captura planas con hairline, sin sombra pesada; coincide con la remediación. - **Layout:** Estructura de dos columnas (selector izquierda + panel Captura derecha) coincide con el Figma. La fila “Total” usa un contenedor gris suave #F9FAFB (el Figma lo deja sobre blanco) — diferencia mínima de énfasis, aceptable. | | #### W44 · Transferir saldo [Section titled “W44 · Transferir saldo”](#w44--transferir-saldo) **Ruta:** `/admin/transfers` · **Figma:** [`132:9984`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=132-9984) · **Historia:** US-VAS-5 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W44](/design/parity/figma/screens/figma-w44-transferir.png)](/design/parity/figma/screens/figma-w44-transferir.png "figma W44 — abrir a tamaño completo") | [![app W44](/design/parity/history/2026-08-07/screenshots/admin-transfers.png)](/design/parity/history/2026-08-07/screenshots/admin-transfers.png "app W44 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Misma pantalla: formulario “Transferir saldo” (Destinatario / Monto / Concepto / Saldo disponible / Continuar) en ambos paneles. La remediación sistémica llegó bien a esta vista.- **Color:** Azul de marca canónico confirmado — el valor del saldo mide `#0052CC` (sample 935,505). Texto de etiquetas/heading `#1F2933`. Todo correcto. - **Estados:** Único residuo real. En Figma el botón **Continuar** aparece como CTA azul primario (estado de reposo del diseño); en la app está **deshabilitado** — bg `#E5E7EB` + texto `#9CA3AF` (sample 760,577/595). Es coherente con el nuevo estilo `disabled` del sistema y con la regla de negocio (form vacío), pero la captura no muestra el CTA azul del diseño. Diferencia de estado, no de estilo. - **Bordes:** Campos con hairline `#E5E7EB` y relleno blanco, alto tipo `size small` — coincide con el fix de form fields. La tarjeta contenedora muestra una sombra suave en la esquina inferior en vez del hairline plano puro del diseño; residuo menor de elevación. - **Contenido:** El placeholder de Destinatario difiere: Figma usa el ejemplo “Comercio Norte”, la app “ID del comercio”. Wording, no estructura. - **Color (tinte):** La tarjeta “Saldo disponible” mide `#EBF1FB`, un pelín más saturada que el info-soft `#EFF6FF` de la spec; diferencia imperceptible. - **Layout / Tipografía:** Composición, orden de campos, jerarquía y densidad coinciden. Sin sombra en botones. Sin hallazgos.Nota: la geometría diálogo-vs-página no aplica aquí (ambas son página completa). | | #### W45 · Confirmar transferencia / comprobante [Section titled “W45 · Confirmar transferencia / comprobante”](#w45--confirmar-transferencia--comprobante) **Ruta:** `— (paso dentro de /admin/transfers)` · **Figma:** [`132:10125`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=132-10125) · **Historia:** US-VAS-5 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------- | | [![figma W45](/design/parity/figma/screens/figma-w45-confirmar-transferencia.png)](/design/parity/figma/screens/figma-w45-confirmar-transferencia.png "figma W45 — abrir a tamaño completo") | ⚠️ **Falta captura** — paso de confirmación, requiere captura del flujo completo | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | ### BAL — Wallet del comercio [Section titled “BAL — Wallet del comercio”](#bal--wallet-del-comercio) Historias `US-BAL-1` … `US-BAL-8`. #### W47 · Saldo / Wallet [Section titled “W47 · Saldo / Wallet”](#w47--saldo--wallet) **Ruta:** `/admin/wallet` · **Figma:** [`133:10306`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=133-10306) · **Historia:** US-BAL-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W47](/design/parity/figma/screens/figma-w47-wallet.png)](/design/parity/figma/screens/figma-w47-wallet.png "figma W47 — abrir a tamaño completo") | [![app W47](/design/parity/history/2026-08-07/screenshots/playground/w47-saldo-wallet.png)](/design/parity/history/2026-08-07/screenshots/playground/w47-saldo-wallet.png "app W47 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** — misma pantalla (Saldo del comercio, layout de dos columnas + lista de movimientos). Los fixes sistémicos llegaron y están medidos; el residuo es de contenido/composición, probablemente ligado al modelo de datos real.- **Color:** Azul de marca **#0052CC** confirmado por muestreo en las tres tarjetas de saldo (default/cargando/vacío). Botón Depositar en clara #EBF1FB con texto azul, igual que Figma. Página #F5F7FA, tarjetas blancas con hairline. Sin regresiones de tema. - **Contenido (principal):** Figma muestra **“Cuentas bancarias”** — lista de bancos vinculados (BBVA ••••4821, Santander ••••9930) con filas de navegación (chevron). La app la reemplaza por **“Resumen de la cuenta”** (Total abonado / Total retirado / Saldo pendiente). Es una sección/feature distinta; la de cuentas bancarias no está presente. Verificar con producto si es intencional. - **Estados:** La captura es un showcase con tres estados (con datos, cargando con skeleton #F0F4F8, y vacío “Aún no hay movimientos”). Todos correctos; Figma solo diagrama el estado con datos. - **Color (montos):** Los importes de la lista se pintan con **verde/rojo semántico** en la app; en Figma los montos (−$2,000.00 / +$10,000.00) son **neutros #1F2933**. Divergencia menor de criterio — decidir si el color semántico se adopta como estándar. - **Contenido (tarjeta de saldo):** La app agrega el enlace secundario **“Retirar · Próximamente”** bajo Depositar, ausente en Figma. - **Tipografía/Bordes:** Jerarquía y hairlines #E5E7EB consistentes; encabezado de resumen y filas con divisores correctos. | | #### W46 · Depósitos [Section titled “W46 · Depósitos”](#w46--depósitos) **Ruta:** `/admin/deposits` · **Figma:** [`123:4532`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-4532) · **Historia:** US-BAL-2 / US-VAS-5 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W46](/design/parity/figma/screens/figma-w46-depositos.png)](/design/parity/figma/screens/figma-w46-depositos.png "figma W46 — abrir a tamaño completo") | [![app W46](/design/parity/history/2026-08-07/screenshots/admin-deposits.png)](/design/parity/history/2026-08-07/screenshots/admin-deposits.png "app W46 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Misma pantalla; los fixes sistémicos llegaron. El residuo es propio de la tabla (columna de acciones extra y densidad).- **Color:** Azul de marca canónico verificado — #0052CC medido en botón “Nuevo depósito”, tab activo “Todos” y outline de “Filtros”. Badge “Pendiente” sobre tinte soft #FFFBEB (rol success/warning suave presente). Botón verde de acción #10B981 y “Rechazar” con outline rojo #F48282 (tokens de éxito/error, no comparables contra Figma porque el diseño no tiene esos botones). - **Estados:** Tratamiento de la columna Estado **inconsistente**: solo “Pendiente” es badge con tinte; “Disponible/Aplicado/Rechazado” salen como texto plano de color. Figma usa texto de color plano para todos. Unificar a StatusBadge sobre tokens \*-soft en la celda Estado del listado. - **Contenido:** La app agrega una columna **“Acciones”** con botones “Marcar disponible” / “Rechazar” para filas pendientes, ausente en Figma (que solo tiene el lápiz de edición). Es una función de producto; confirmar si debe vivir en la tabla o migrar a menú/acciones por fila para no romper la paridad. - **Layout:** La columna extra comprime el ancho y hace que el **Folio se parta en dos líneas** (“DEP-\n1042”) como link azul; Figma lo muestra en una línea, texto oscuro. Ajustar min-width de la columna Folio y/o `whiteSpace: nowrap`. - **Bordes:** Card plana con hairline, header de tabla #F7F9FC/#F5F7FA y botones-icono con borde #E5E7EB — coinciden con el diseño. - **Estados (fila):** Figma muestra la primera fila seleccionada (checkbox + realce); la app no. Diferencia de estado de captura, no de estilo. - Diferencias de datos (fechas “01 feb 2024” vs “24 Oct”, paginación “de 5” vs “de 84”, avatar en tinte azul vs iniciales neutras) no son defectos. | | #### W46b · Registrar depósito [Section titled “W46b · Registrar depósito”](#w46b--registrar-depósito) **Ruta:** `/admin/deposits/new` · **Historia:** US-BAL-2 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el frame que estaba pareado (`123:4532`, `figma-w46-depositos.png`) es la **lista** de depósitos (W46), no el **formulario** de alta. Falta exportar el frame del formulario «Registrar depósito». | [![app W46b](/design/parity/history/2026-08-07/screenshots/admin-deposits-new.png)](/design/parity/history/2026-08-07/screenshots/admin-deposits-new.png "app W46b — abrir a tamaño completo") | | Notas | | | **⚠️ Falta el frame de Figma del formulario — se despareó la lista.**- **Panel APP:** pantalla de *formulario* “Registrar Depósito” (W46b) — alerta informativa, campos Canal de depósito, Banco de Depósito, Fecha, Número de Referencia, Monto Depositado (MXN), Cuenta de Origen y Notas Adicionales, columna “Comprobante” con carga de archivo, y barra de acciones Cancelar/Continuar. Es la pantalla correcta del título. - **Panel FIGMA:** el frame que estaba pareado (`123:4532`, `figma/screens/figma-w46-depositos.png`) es la **lista** de depósitos (W46), no el **formulario**. Se despareó para no comparar dos pantallas distintas; la celda de Figma queda como hueco documentado con un `TODO(human)` para exportar el frame del formulario «Registrar depósito» y volver a emparejarlo. - Nota lateral (no bloqueante): el botón Continuar de la app mide **#0052CC**, confirmando que el azul canónico llegó a esta pantalla. | | ### CLI — Clientes [Section titled “CLI — Clientes”](#cli--clientes) Flujo W-13 (v2). Historias `US-CLI-1`, `US-CLI-2`. #### Clientes — directorio [Section titled “Clientes — directorio”](#clientes--directorio) **Ruta:** `/admin/customers` · **Figma:** [`231:3`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=231-3) · **Historia:** US-CLI-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — el nodo `231:3` existe en Figma pero no se exportó a `design/web/screens/` | [![app Clientes — directorio](/design/parity/history/2026-08-07/screenshots/admin-customers.png)](/design/parity/history/2026-08-07/screenshots/admin-customers.png "app Clientes — directorio — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Cliente — detalle [Section titled “Cliente — detalle”](#cliente--detalle) **Ruta:** `/admin/customers/[id]` · **Figma:** [`232:157`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=232-157) · **Historia:** US-CLI-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — nodo `232:157` sin exportar | [![app Cliente — detalle](/design/parity/history/2026-08-07/screenshots/admin-customers-id.png)](/design/parity/history/2026-08-07/screenshots/admin-customers-id.png "app Cliente — detalle — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Nuevo cliente — diálogo [Section titled “Nuevo cliente — diálogo”](#nuevo-cliente--diálogo) **Ruta:** `/admin/customers (diálogo)` · **Figma:** [`233:383`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=233-383) · **Historia:** US-CLI-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — nodo `233:383` sin exportar | [![app Nuevo cliente — diálogo](/design/parity/history/2026-08-07/screenshots/admin-customers--dialog-nuevo-cliente.png)](/design/parity/history/2026-08-07/screenshots/admin-customers--dialog-nuevo-cliente.png "app Nuevo cliente — diálogo — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Asignar cliente a la venta [Section titled “Asignar cliente a la venta”](#asignar-cliente-a-la-venta) **Ruta:** `— (modal en el POS)` · **Figma:** [`233:279`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=233-279) · **Historia:** US-CLI-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — nodo `233:279` sin exportar | ⚠️ **Falta captura** — modal, requiere captura desde el POS | | Notas | | | **⛔ Comparación no posible — faltan las dos imágenes.** No hay render de Figma exportado ni captura de la app, así que no hay nada que comparar. El motivo de cada gap está en las celdas de arriba. Primero hay que resolverlo (exportar el frame y/o construir la pantalla); recién ahí tiene sentido auditar paridad. | | #### Nuevo cliente (alta rápida) [Section titled “Nuevo cliente (alta rápida)”](#nuevo-cliente-alta-rápida) **Ruta:** `— (modal en el POS)` · **Figma:** [`233:383`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=233-383) · **Historia:** US-CLI-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — nodo `233:383` sin exportar | ⚠️ **Falta captura** — modal, requiere captura desde el POS | | Notas | | | **⛔ Comparación no posible — faltan las dos imágenes.** No hay render de Figma exportado ni captura de la app, así que no hay nada que comparar. El motivo de cada gap está en las celdas de arriba. Primero hay que resolverlo (exportar el frame y/o construir la pantalla); recién ahí tiene sentido auditar paridad. | | ### TERM / WL — Comercios, tiendas, terminales y white-label [Section titled “TERM / WL — Comercios, tiendas, terminales y white-label”](#term--wl--comercios-tiendas-terminales-y-white-label) Flujo W-10. Historias `US-TERM-1`, `US-TERM-2`, `US-WL-1`. #### W48 · Comercios [Section titled “W48 · Comercios”](#w48--comercios) **Ruta:** `/admin/merchants` · **Figma:** [`130:7665`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=130-7665) · **Historia:** US-TERM-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W48](/design/parity/figma/screens/figma-w48-comercios.png)](/design/parity/figma/screens/figma-w48-comercios.png "figma W48 — abrir a tamaño completo") | [![app W48](/design/parity/history/2026-08-07/screenshots/admin-merchants.png)](/design/parity/history/2026-08-07/screenshots/admin-merchants.png "app W48 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Misma pantalla (listado de Comercios). Los fixes de color sistémicos llegaron, pero queda residuo de composición y un badge sin tokenizar.- **Color:** Azul de marca correcto — botones medidos en #0052CC (1050,135 / 1250,135). Cabecera de tabla #F7F9FC y grises tokenizados presentes. ✓ - **Estados:** Los badges de Estado son píldoras de **relleno sólido saturado** con texto blanco (verde #10B981, ámbar #F59E0B, rojo Suspendido). El fix de StatusBadge sobre tokens `*-soft` NO alcanzó esta columna; deberían ser tinte suave, no fill pleno. Culpable probable: el badge del listado de merchants no usa `StatusBadge`/tokens soft. - **Espaciado/Bordes:** Botones sobredimensionados — miden \~70px de alto (y≈110→180), muy por encima del ramp canónico (\~52 large). Además hay **dos** botones grandes (Filtrar + Nuevo) donde Figma lleva un único primario compacto `+ Nuevo comercio`. - **Layout:** Falta el encabezado de página `Comercios` + subtítulo “Organizaciones que administras” (la app solo muestra el label “Buscar”). Faltan las sub-tabs `Comercios / Tiendas / Apariencia` y la fila de pills de filtro `Todos / Activos / Suspendidos` con botón `Filtros`. - **Contenido:** Faltan checkboxes de selección de fila, el icono de editar por fila y el estado de fila seleccionada (tinte azul) que sí trae el Figma; tampoco aparece el pie de paginación (“Mostrando 1–3 de 3” + pager). - **Contenido (dato/producto):** Columnas distintas — app: Nombre / Razón Social / Estado / Categoría / Balance / Fecha de Creación; Figma: Comercio / RFC / Tiendas / Saldo / Empleados / Estado. Probable decisión de modelo de datos, no de estilo; se señala pero no bloquea. Los chips de Categoría (outlined) están correctos. | | #### W49 · Perfil del comercio [Section titled “W49 · Perfil del comercio”](#w49--perfil-del-comercio) **Ruta:** `/admin/merchants (formulario)` · **Figma:** [`124:6197`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=124-6197) · **Historia:** US-TERM-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------- | | [![figma W49](/design/parity/figma/screens/figma-w49-perfil-comercio.png)](/design/parity/figma/screens/figma-w49-perfil-comercio.png "figma W49 — abrir a tamaño completo") | ⚠️ **Falta captura** — formulario, requiere captura con un comercio abierto | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W50 · Tiendas [Section titled “W50 · Tiendas”](#w50--tiendas) **Ruta:** `/admin/pos/stores` · **Figma:** [`122:4009`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=122-4009) · **Historia:** US-TERM-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W50](/design/parity/figma/screens/figma-w50-tiendas.png)](/design/parity/figma/screens/figma-w50-tiendas.png "figma W50 — abrir a tamaño completo") | [![app W50](/design/parity/history/2026-08-07/screenshots/admin-pos-stores.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-stores.png "app W50 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Es la misma ruta (Administración › Comercio y tiendas), y los fixes sistémicos SÍ llegaron a esta pantalla — el residuo es de contenido/composición, no de tokens.- **Color:** Correcto. Botón primario medido #0052CC; cabecera de tabla #F7F9FC; título de fila #1F2933; página #F5F7FA. Coincide con la remediación. - **Bordes:** Chips (MXN/USD) y contenedores con hairline limpio; sin sombras. OK. - **Contenido:** El modelo de tabla es otro. Figma lista **Tienda · Dirección · Terminales · Ventas mes · Empleados · Estado**; la app lista **Nombre · Moneda · Zona Horaria · Fecha de Creación**. Faltan por completo las métricas de negocio (terminales, ventas, empleados) y la acción de editar por fila. - **Layout:** Faltan controles del diseño: tabs **Todas/Abiertas/Cerradas**, buscador inline y el **Select** de la barra de filtros (la app solo tiene “Filtros”). Falta el subtítulo contador (“3 tiendas · 1 comercio”). Aparece un botón **Pantalla completa** que no está en Figma. - **Iconos:** Faltan los **avatares con iniciales** por fila (CO/SA/PA…) que el Figma usa como columna Tienda. - **Estados:** Faltan los **StatusBadge** de estado (Abierta verde / Sin turno rojo). La app no expresa el estado de la tienda. - **Tipografía:** Terminología divergente: la app dice **Sucursales / Crear Sucursal**; Figma dice **Tiendas / Nueva tienda**. - **Espaciado:** Paginación estilo MUI (“Filas por página · 1–6 de 6” con flechas) en vez de la paginación numerada (1 2 3 … 10) del diseño.La mayor parte de este residuo es decisión de producto/datos (qué columnas y controles expone la vista de tiendas), no de estilo; conviene alinear el modelo de tabla y los controles con el Figma. | | #### W51 · Tienda crear / editar [Section titled “W51 · Tienda crear / editar”](#w51--tienda-crear--editar) **Ruta:** `/admin/pos/stores (formulario)` · **Figma:** [`124:6370`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=124-6370) · **Historia:** US-TERM-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------- | | [![figma W51](/design/parity/figma/screens/figma-w51-tienda-edit.png)](/design/parity/figma/screens/figma-w51-tienda-edit.png "figma W51 — abrir a tamaño completo") | ⚠️ **Falta captura** — formulario, requiere captura con una tienda abierta | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W52 · Terminales [Section titled “W52 · Terminales”](#w52--terminales) **Ruta:** `/admin/pos/terminals` · **Figma:** [`123:4238`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-4238) · **Historia:** US-TERM-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W52](/design/parity/figma/screens/figma-w52-terminales.png)](/design/parity/figma/screens/figma-w52-terminales.png "figma W52 — abrir a tamaño completo") | [![app W52](/design/parity/history/2026-08-07/screenshots/admin-pos-terminals.png)](/design/parity/history/2026-08-07/screenshots/admin-pos-terminals.png "app W52 — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes**Misma pantalla (listado **Terminales**), pero la composición de la app se aleja bastante del diseño. Los tokens sistémicos sí llegaron; el residuo es propio de esta pantalla (estructura y contenido).- **Color:** Verificado — botón `Nuevo` de la app = **#0052CC**, idéntico al `Vincular terminal` de Figma (#0052CC). Tinte de cabecera de tabla presente (\~#F7F9FC). El azul de marca canónico está bien cableado aquí. - **Contenido:** El modelo de datos difiere fuerte. Figma: `Terminal` (nombre POS-01 + avatar iniciales), `Tienda`, `Última conexión`, `Versión`, `Estado`. App: `Número de Serie` (+ modelo), `Tipo`, `Sucursal`, `Activa`, `Última Conexión`, `Vincular terminal`. Faltan avatares de iniciales por fila y el subtítulo **“5 terminales vinculadas”**. - **Layout:** Toolbar de filtros incompleta — la app solo renderiza `Filtros`. Faltan el input `Buscar...`, los tabs segmentados (**Todas / En línea / Fuera de línea**) y el select `Selecciona una opción`. - **Estados:** Falta la columna **Estado** con `StatusBadge` sobre tinte suave (Figma: En línea #ECFCF5 verde, Fuera de línea rojo, amarillo). La app la sustituye por un único icono check/x en `Activa`, perdiendo la semántica de color por estado. - **Layout (CTA):** Composición distinta — Figma pone `Vincular terminal` como CTA principal arriba; la app usa `Nuevo` arriba y mueve `Vincular terminal` a un botón outlined por fila. Además la app añade un botón extra `Pantalla completa` que no está en el diseño. - **Layout (paginación):** Figma usa paginador numerado (`Mostrando 1–5 de 5` + `1 2 3 … 10`, página activa en azul relleno). La app usa `TablePagination` de MUI (`Filas por página 7 · 1–5 de 5 · flechas`) — patrón diferente al diseñado. - **Acción de fila:** Figma tiene icono de edición por fila; la app tiene botón `Vincular terminal` outlined por fila.Nota: la disyuntiva componente-MUI-vs-diseño-custom (paginación, chips de estado) es en parte decisión de producto; el residuo accionable aquí es reponer el buscador + tabs de estado + columna Estado con badges de tinte suave y los avatares/subtítulo. | | #### W53 · Vincular terminal (pairing) [Section titled “W53 · Vincular terminal (pairing)”](#w53--vincular-terminal-pairing) **Ruta:** `— (sin ruta)` · **Figma:** [`133:10490`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=133-10490) · **Historia:** US-TERM-2 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------- | | [![figma W53](/design/parity/figma/screens/figma-w53-vincular-pairing.png)](/design/parity/figma/screens/figma-w53-vincular-pairing.png "figma W53 — abrir a tamaño completo") | ⚠️ **Falta captura** — no hay UI de pairing en web | | Notas | | | **⛔ Comparación no posible — falta la captura de la app.** El diseño existe pero no hay implementación capturada; el motivo está en la celda de la derecha (pantalla no construida, estado/modal que el harness no alcanza, o ruta inexistente). **Siguiente paso:** si la pantalla existe y el harness no llega, agregar su entrada en `e2e/screenshots/targets.ts` y correr `pnpm screenshots`; si no está construida, es un gap de implementación, no de paridad. | | #### W54 · Apariencia white-label + recibo [Section titled “W54 · Apariencia white-label + recibo”](#w54--apariencia-white-label--recibo) **Ruta:** `/admin/merchants/appearance` · **Figma:** [`128:7274`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=128-7274) · **Historia:** US-WL-1 / US-PLAT-3 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W54](/design/parity/figma/screens/figma-w54-apariencia.png)](/design/parity/figma/screens/figma-w54-apariencia.png "figma W54 — abrir a tamaño completo") | [![app W54](/design/parity/history/2026-08-07/screenshots/admin-merchants-appearance.png)](/design/parity/history/2026-08-07/screenshots/admin-merchants-appearance.png "app W54 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Azul de marca canónico verificado en esta captura — Guardar/icon-buttons/swatch primario/banner ‘Tu Comercio’ = **#0052CC**, Cobrar #0354CC (≈ igual). Acento diverge por **dato de mock**: Figma azul #1A73E8 vs app teal #00A3A3; no es defecto de estilo. - **Tipografía:** $12,450.00 en #1F2933, ‘Saldo disponible’ en gris secundario. Sin residuo. - **Bordes:** Hairline de tarjetas #E5E7EB confirmado; campos y dropzone con borde correcto. - **Estados:** Switch ‘Tema oscuro disponible’ aparece OFF (gris, thumb blanco 18px) mientras Figma lo muestra ON (azul) — diferencia de estado/dato, ambos renderizan bien. - **Layout/Contenido:** Dropzone: la app suma icono ‘+’ y botón outlined ‘Seleccionar archivos’ (Figma sólo icono compartir); hint distinto (Figma ‘CSV, PNG o JPG · hasta 10 MB’ vs app ‘PNG, JPG, WEBP o GIF’). Composición menor. La app además incluye la sección **‘Recibo / Encabezado del recibo’** (parte ‘+ recibo’ del alcance) que el crop de Figma no muestra — contenido extra correcto. - **Sistémico (1 línea):** sidebar con chip ‘Tienda demo’ vs selector ‘Tienda Centro’ de Figma — patrón global, no propio de la pantalla.Los fixes de la remediación llegaron a esta pantalla; el residuo restante es sobre todo dato de mock (acento/switch) y composición menor del dropzone. | | ### ACCT — Cuenta y ajustes [Section titled “ACCT — Cuenta y ajustes”](#acct--cuenta-y-ajustes) Flujo W-12. Historias `US-ACCT-1` … `US-ACCT-3`. #### W59 · Mi cuenta [Section titled “W59 · Mi cuenta”](#w59--mi-cuenta) **Ruta:** `/admin/my-account` · **Figma:** [`128:7503`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=128-7503) · **Historia:** US-ACCT-1 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W59](/design/parity/figma/screens/figma-w59-mi-cuenta.png)](/design/parity/figma/screens/figma-w59-mi-cuenta.png "figma W59 — abrir a tamaño completo") | [![app W59](/design/parity/history/2026-08-07/screenshots/admin-my-account.png)](/design/parity/history/2026-08-07/screenshots/admin-my-account.png "app W59 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Misma pantalla (perfil “Mi cuenta”). Los fixes sistémicos están presentes y medidos en esta captura; el residuo es propio de contenido/estado, no de estilo.- **Color:** Verificado. Botón `#0052CC`, chip Supervisor texto `#0052CC` sobre tinte `#E6EEFA`, avatar tinte `#EEF4FF`, badge “Sesión actual” verde soft `#E8F5E9`. Todo canónico. - **Tipografía:** Título/labels `#212B35` ≈ `#1F2933` canónico. Sin observaciones. - **Bordes:** Hairline `#E5E7EB` en tarjetas y campos; campo Correo deshabilitado con relleno `#E5E7EB`. Correcto. - **Sombras:** Tarjetas y AppBar planas. Correcto. - **Iconos:** El botón Figma trae glifo “+” (“+ Guardar”); la app solo muestra “Guardar” sin icono. - **Contenido:** La app añade **“Sesiones activas”** (2 tarjetas Chrome/Safari con IP y fecha) y la línea **“Miembro desde: ene 2026”** en la tarjeta de perfil — ninguno existe en Figma. Es contenido de más, no falta. - **Estados:** Figma muestra los tres campos como placeholders vacíos; la app muestra Nombre/Teléfono con valor y **Correo deshabilitado** (gris). Diferencia de estado, no de token. - **Layout:** Las tarjetas de la app ocupan casi todo el ancho del contenido; en Figma la tarjeta “Datos personales” es más angosta. Diferencia de anchura contenida, aceptable. | | #### W60 · Seguridad [Section titled “W60 · Seguridad”](#w60--seguridad) **Ruta:** `/admin/settings (tab)` · **Figma:** [`128:7681`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=128-7681) · **Historia:** US-ACCT-1 | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W60](/design/parity/figma/screens/figma-w60-seguridad.png)](/design/parity/figma/screens/figma-w60-seguridad.png "figma W60 — abrir a tamaño completo") | [![app W60](/design/parity/history/2026-08-07/screenshots/playground/w60-seguridad.png)](/design/parity/history/2026-08-07/screenshots/playground/w60-seguridad.png "app W60 — abrir a tamaño completo") | | Notas | | | **✅ Coincide**Re-auditoría post-remediación: la pantalla de Seguridad quedó alineada con el diseño. Los fixes sistémicos llegaron y se miden en esta captura.- **Color:** Azul canónico verificado — FAB `#0052CC`, track del Switch 2FA `#0052CC`, botones outline y enlace *Cerrar* con label/borde `#0052CC`. Verde de éxito *Esta sesión* `#10B981`, igual que Figma. Sin rastro del viejo `#0057B8`. - **Tipografía:** Títulos de tarjeta y filas (Acceso, Contraseña, PIN de cajero) en `#1F2933`; subtítulos/metadatos en gris secundario. Coincide con Figma. - **Bordes:** Tarjetas planas blancas con hairline `#E5E7EB`/`#EAECEF` sobre fondo `#F5F7FA`; divisor entre filas de sesiones presente. Correcto. - **Sombras:** Sin sombra en tarjetas ni FAB — coincide. - **Estados:** Switch 2FA activo (thumb blanco 18px sobre track azul), geometría 44×24 correcta. - **Layout:** Composición idéntica — header + subtítulo, tarjeta *Acceso* (Contraseña/2FA/PIN) y tarjeta *Sesiones activas* con las dos sesiones y sus acciones. FAB `+` arriba a la derecha. - **Contenido:** Todos los textos presentes y correctos.Único desajuste, esperado y no imputable a la pantalla: el panel APP muestra solo el área de contenido y recorta el sidebar de navegación y la AppBar (buscador, *Turno abierto*, *Sincronizado*, avatares) que sí figuran en Figma — es el encuadre del playground, no un residuo de estilo. Nada accionable en esta pantalla. | | #### Re-autenticación por contraseña [Section titled “Re-autenticación por contraseña”](#re-autenticación-por-contraseña) **Ruta:** `/admin/my-account (diálogo)` · **Historia:** US-ACCT-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — sin frame; es un requisito de Appwrite (cambiar teléfono exige contraseña) | [![app Re-autenticación por contraseña](/design/parity/history/2026-08-07/screenshots/admin-my-account--dialog-contrasena.png)](/design/parity/history/2026-08-07/screenshots/admin-my-account--dialog-contrasena.png "app Re-autenticación por contraseña — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### W61 · Ajustes generales [Section titled “W61 · Ajustes generales”](#w61--ajustes-generales) **Ruta:** `/admin/settings` · **Figma:** [`133:10622`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=133-10622) · **Historia:** US-ACCT-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W61](/design/parity/figma/screens/figma-w61-ajustes.png)](/design/parity/figma/screens/figma-w61-ajustes.png "figma W61 — abrir a tamaño completo") | [![app W61](/design/parity/history/2026-08-07/screenshots/admin-settings.png)](/design/parity/history/2026-08-07/screenshots/admin-settings.png "app W61 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**Los fixes sistémicos llegaron a esta pantalla y están medidos, no supuestos.- **Color:** Switch encendido = **#0052CC**, thumb blanco; switch apagado con track gris y thumb blanco. Botón **Guardar** azul de marca plano. Todo coincide con la remediación. - **Tipografía:** Encabezados de tarjeta (‘Preferencias’, ‘Impresión’) y filas en #1F2933; subtítulos en gris. Pestaña activa ‘General’ en azul con subrayado. Coincide. - **Sombras:** Botón y tarjetas sin sombra dura; tarjetas planas con hairline. Coincide. - **Contenido:** APP **no renderiza el título de página ‘Ajustes’** (H1) que el Figma coloca encima de las pestañas — va directo de la AppBar a las tabs. Añadir el heading. - **Layout:** Las tarjetas de formulario en APP ocupan todo el ancho del contenido; en Figma están constreñidas a una columna de lectura angosta (\~455px), lo que separa mucho el control (switch/select) de su etiqueta. Considerar un `maxWidth` en el contenedor del formulario para acercar la densidad. - **Bordes:** Radio de esquina de las tarjetas en APP se ve algo más redondeado (\~16) que el casi recto del Figma; residuo menor. - **Estados:** Campo ‘Idioma’ → select ‘Español (MX)’ presente y alineado a la derecha; coincide en función, con leve diferencia de tratamiento vs. el campo de Figma. - **Iconos:** El botón **Guardar** aparece en APP y no se ve en el recorte de Figma (posible fuera de encuadre en el diseño); no es defecto de estilo.Nota: diferencias de la barra lateral (items/usuario, chip ‘Tienda demo’ vs dropdown ‘Tienda Centro’) son de la nav/mocks, no propias de esta pantalla. | | #### W62 · Bitácora / Audit log [Section titled “W62 · Bitácora / Audit log”](#w62--bitácora--audit-log) **Ruta:** `/admin/audit-log` · **Figma:** [`123:5114`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=123-5114) · **Historia:** US-ACCT-3 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W62](/design/parity/figma/screens/figma-w62-bitacora.png)](/design/parity/figma/screens/figma-w62-bitacora.png "figma W62 — abrir a tamaño completo") | [![app W62](/design/parity/history/2026-08-07/screenshots/admin-audit-log.png)](/design/parity/history/2026-08-07/screenshots/admin-audit-log.png "app W62 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Fixes sistémicos confirmados por muestreo — Exportar y paginación activa #0052CC, cabecera de tabla #F7F9FC, divisores #E5E7EB, avatar en tinte #E5EDFA con iniciales primary. Todo canónico. - **Estados:** Badges soft reales y correctos — Autorizado #ECFDF5 (verde), Rechazado #FEF0F0 (rojo), Creó producto #FFFBEB (ámbar). StatusBadge sobre tokens \*-soft OK. - **Layout:** La app omite por completo la columna de selección que el Figma diseña: checkbox de encabezado (select-all), checkbox por fila y la primera fila en estado seleccionado (checkbox azul + tinte de fila). Es la diferencia estructural principal — falta la selección masiva (`AuditLogTable`). - **Iconos:** El glifo de acción difiere: Figma usa un lápiz-en-recuadro contorneado en gris neutro; la app usa lápiz relleno en azul primary. Alinear glifo y tono. - **Contenido:** Menores — la columna Fecha del Figma prefija con em-dash “— Hoy 09:31” (app sin él); “Editó precio” muestra “Rechazado” en app vs “OK” en Figma (el texto de la app es más coherente). Conteo y páginas (8 / 2 vs 3,210 / 10) son datos mock, esperado. - Nota sistémica ya conocida: acciones de fila como ícono suelto en vez de botón etiquetado — decisión de producto pendiente, no se recuenta aquí. | | #### Bitácora — filtros [Section titled “Bitácora — filtros”](#bitácora--filtros) **Ruta:** `/admin/audit-log (diálogo)` · **Historia:** US-ACCT-3 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — organismo **Filter Bar**, sin frame de pantalla | [![app Bitácora — filtros](/design/parity/history/2026-08-07/screenshots/admin-audit-log--dialog-filtros.png)](/design/parity/history/2026-08-07/screenshots/admin-audit-log--dialog-filtros.png "app Bitácora — filtros — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | ### NOTIF — Notificaciones [Section titled “NOTIF — Notificaciones”](#notif--notificaciones) Flujo W-02. Historia `US-NOTIF-1`. #### W08 · Notificaciones [Section titled “W08 · Notificaciones”](#w08--notificaciones) **Ruta:** `/admin/notifications` · **Figma:** [`113:430`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=113-430) · **Historia:** US-NOTIF-1 | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W08](/design/parity/figma/screens/figma-w08-notificaciones.png)](/design/parity/figma/screens/figma-w08-notificaciones.png "figma W08 — abrir a tamaño completo") | [![app W08](/design/parity/history/2026-08-07/screenshots/admin-notifications.png)](/design/parity/history/2026-08-07/screenshots/admin-notifications.png "app W08 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Botón primario medido en **#0052CC** exacto — el fix sistémico del azul llegó a esta pantalla. Tinte de fila no-leída **#E5EDFA** ≈ **#E6EEFA** de Figma; filas leídas en blanco; fondo de página **#F5F7FA** idéntico. Punto/dot de no-leído en azul de marca. Sin residuo de color. - **Iconos:** Residuo propio. La app envuelve **todos** los glifos en un badge circular de tinte suave; en Figma solo la fila de avatar (‘Nuevo empleado’) lleva círculo y las de venta/stock muestran el icono **desnudo** sobre la fila. Además hay glifos distintos: venta carrito→recibo, transferencia doble-flecha→flecha simple, corte de caja documento→reloj. Revisar el mapa icono↔tipo de notificación de la pantalla. - **Contenido:** Figma agrupa por **‘Hoy’ / ‘Ayer’** (dos secciones con divisor); la app colapsa todo en una sola sección **‘Anteriores’**. Es diferencia de datos/timestamps del mock, no de layout — verificar que la lógica de agrupación por día se ejerza con datos reales. - **Layout / Tipografía / Bordes:** Coinciden — título + subtítulo, tabs ‘Todas/No leídas/Ventas/Stock’ con activo azul y subrayado, filas con badge+título+subtítulo y hora alineada a la derecha, hairline #E5E7EB entre filas y tarjeta plana. Sin sombras espurias. - **Estados:** El destacado de no-leído (tinte + dot) se reproduce en las dos primeras filas tal como en Figma. | | #### W63 · Detalle de notificación [Section titled “W63 · Detalle de notificación”](#w63--detalle-de-notificación) **Ruta:** `/admin/notifications/[id]` · **Figma:** [`163:10716`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=163-10716) · **Historia:** US-NOTIF-1 | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W63](/design/parity/figma/screens/figma-w63-detalle-notificacion.png)](/design/parity/figma/screens/figma-w63-detalle-notificacion.png "figma W63 — abrir a tamaño completo") | [![app W63](/design/parity/history/2026-08-07/screenshots/admin-notifications-id.png)](/design/parity/history/2026-08-07/screenshots/admin-notifications-id.png "app W63 — abrir a tamaño completo") | | Notas | | | **✅ Coincide**Misma pantalla (detalle de notificación) y remediación sistémica confirmada sobre la captura nueva. El contenido difiere solo por el mock (Figma muestra una alerta de inventario “Stock bajo”, la app una “Venta completada #1029”): es la misma plantilla, no un desajuste de paridad.- **Color:** Botón primario medido **#0052CC** (canónico). Rol “Sistema” y enlaces de acción en azul (#0857CD/#0052CC). Fondo de página #F5F7FA, tarjeta blanca. Todo dentro del sistema. - **Tipografía:** Título 600 oscuro, meta gris, cuerpo #1F2933; etiquetas de campo (Tienda/total) en gris con valor en oscuro. Jerarquía correcta. - **Bordes:** Tarjeta plana con hairline; breadcrumb con enlace azul + “Detalle” en oscuro. Botón outlined con borde azul y relleno blanco. - **Botones:** Ramp y estilo correctos — primario relleno + outlined con etiqueta 600, sin sombra; enlaces de texto (“Marcar como no leída”, “Descartar”) a la derecha, como en el diseño. - **Iconos:** Glifo de tipo (recibo) azul dentro de un avatar en tinte claro; en el diseño el rol Inventario usa un triángulo ámbar (por ser otro tipo de notificación). El contenedor del avatar se ve ligeramente ovalado en el crop — verificar que sea círculo perfecto. - **Layout / Contenido:** Estructura idéntica (breadcrumb → tarjeta con icono+título+meta+cuerpo → fila de meta → acciones). La fila de meta muestra 2 columnas vs 4 en Figma por el mock distinto; no es residuo de la pantalla. | | ### SEARCH — Búsqueda global [Section titled “SEARCH — Búsqueda global”](#search--búsqueda-global) Flujo W-02. Historia `US-SEARCH-1`. #### W09 · Búsqueda global (⌘K) [Section titled “W09 · Búsqueda global (⌘K)”](#w09--búsqueda-global-k) **Ruta:** `— (overlay global)` · **Figma:** [`113:651`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=113-651) · **Historia:** US-SEARCH-1 | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W09](/design/parity/figma/screens/figma-w09-busqueda-global.png)](/design/parity/figma/screens/figma-w09-busqueda-global.png "figma W09 — abrir a tamaño completo") | [![app W09](/design/parity/history/2026-08-07/screenshots/admin-dashboard--palette-busqueda-global.png)](/design/parity/history/2026-08-07/screenshots/admin-dashboard--palette-busqueda-global.png "app W09 — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** — misma pantalla (paleta global ⌘K). Figma muestra el estado con consulta escrita (“coca”) y resultados agrupados; la app captura el estado vacío (“Escribe al menos 2 caracteres para buscar”), así que las filas de resultado, section-headers y el tint de fila activa no son auditables aquí.- **Color:** Fix sistémico CONFIRMADO en esta captura — el anillo de foco del input mide **#0052CC** exacto (canónico). Fondo del diálogo #FFFFFF, scrim de dim gris detrás correcto. - **Bordes:** El divisor hairline bajo el input y el radio del diálogo están presentes. OK. - **Layout / Estados:** Residuo propio de la pantalla — el buscador se renderiza como **TextField MUI con caja redondeada y anillo de foco azul de 2px**, mientras que Figma lo diseña como **barra de comando sin borde** (solo icono + texto con divisor a todo el ancho debajo, sin caja ni anillo). Alinear el input de la paleta al estilo command-bar. - **Contenido:** Falta el pill **“ESC”** arriba a la derecha del input que sí trae Figma. - **Espaciado:** Diálogo anclado más alto (pisa el banner de promo) y algo más angosto que el diseño, que lo centra y le da más ancho. Ajuste menor de posición/ancho. - **Nota de cobertura:** para cerrar la paridad hace falta recapturar el estado poblado (con query) y validar ahí el tint de fila activa (#E6EEFA), los tiles de icono en tinte y la tipografía de títulos/subtítulos. | | ### PLAT — Administración de plataforma [Section titled “PLAT — Administración de plataforma”](#plat--administración-de-plataforma) Historias `US-PLAT-1` … `US-PLAT-3`. #### W07 · Dashboard [Section titled “W07 · Dashboard”](#w07--dashboard) **Ruta:** `/admin/dashboard` · **Figma:** [`106:2`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=106-2) · **Historia:** US-PLAT-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma W07](/design/parity/figma/screens/figma-w07-dashboard.png)](/design/parity/figma/screens/figma-w07-dashboard.png "figma W07 — abrir a tamaño completo") | [![app W07](/design/parity/history/2026-08-07/screenshots/admin-dashboard.png)](/design/parity/history/2026-08-07/screenshots/admin-dashboard.png "app W07 — abrir a tamaño completo") | | Notas | | | **❌ Las imágenes no corresponden a la misma pantalla.**- **Figma (diseño):** dashboard analítico de retail/POS — saludo “Hola, Victoria 👋”, selector de fecha “Hoy”, 4 tarjetas KPI (Ventas hoy $8,450.00, Transacciones 128, Ticket promedio $66.01, Productos vendidos 342 con deltas vs ayer), gráfico de línea “Ventas por hora”, donut “Categorías” y tabla “Ventas recientes” con checkboxes, SKU, precio, stock y badges de estado. - **App (implementación):** dashboard de pagos/billetera — banner promocional azul “0% Comisión en Pagos”, sección “Acciones Rápidas” (Tiempo Aire, Tarjetas de Regalo, Pago de Servicios, Transferir saldo), tarjeta lateral “Saldo Disponible $24,567.50 MXN” con Retirar Saldo, “Resumen Semanal” y “Más Opciones”. - Comparten únicamente el sidebar (misma app, ítem “Inicio” activo, mismos grupos OPERACIÓN/CATÁLOGO/EQUIPO/FINANZAS/ADMINISTRACIÓN). El área de contenido no tiene correspondencia alguna. - No listo diferencias de color/tipografía/espaciado: al ser pantallas distintas, cualquier comparación de estilo sería ruido. Se necesita recapturar la ruta correcta del dashboard analítico (o confirmar qué pantalla debe vivir en “Inicio”). | | #### Actividad reciente [Section titled “Actividad reciente”](#actividad-reciente) **Ruta:** `/admin/recent-activities` · **Historia:** US-PLAT-1 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — bloque del dashboard W07 convertido en ruta; sin frame propio | [![app Actividad reciente](/design/parity/history/2026-08-07/screenshots/admin-recent-activities.png)](/design/parity/history/2026-08-07/screenshots/admin-recent-activities.png "app Actividad reciente — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Tickets de soporte [Section titled “Tickets de soporte”](#tickets-de-soporte) **Ruta:** `/admin/tickets` · **Historia:** US-PLAT-2 | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — pantalla legacy sin frame | [![app Tickets de soporte](/design/parity/history/2026-08-07/screenshots/admin-tickets.png)](/design/parity/history/2026-08-07/screenshots/admin-tickets.png "app Tickets de soporte — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | ### Sin historia de usuario — rutas técnicas y de desarrollo [Section titled “Sin historia de usuario — rutas técnicas y de desarrollo”](#sin-historia-de-usuario--rutas-técnicas-y-de-desarrollo) Rutas capturadas que no pertenecen a ninguna historia: raíz, playground y preview del design system. #### Playground del design system [Section titled “Playground del design system”](#playground-del-design-system) **Ruta:** `/admin/playground` · **Historia:** — (herramienta de parity) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — herramienta interna; es la superficie desde la que se capturan los átomos/moléculas/organismos de abajo | [![app Playground del design system](/design/parity/history/2026-08-07/screenshots/admin-playground.png)](/design/parity/history/2026-08-07/screenshots/admin-playground.png "app Playground del design system — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Playground del design system (ruta pública) [Section titled “Playground del design system (ruta pública)”](#playground-del-design-system-ruta-pública) **Ruta:** `/playground` · **Historia:** — (herramienta de parity) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — herramienta interna | [![app Playground del design system (ruta pública)](/design/parity/history/2026-08-07/screenshots/playground.png)](/design/parity/history/2026-08-07/screenshots/playground.png "app Playground del design system (ruta pública) — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### UI preview [Section titled “UI preview”](#ui-preview) **Ruta:** `/ui-preview` · **Historia:** — (herramienta de parity) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — herramienta interna | [![app UI preview](/design/parity/history/2026-08-07/screenshots/ui-preview.png)](/design/parity/history/2026-08-07/screenshots/ui-preview.png "app UI preview — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | *** ## Design system [Section titled “Design system”](#design-system) Los renders de Figma vienen de `design/web/{atomic,molecules,organisms}/`. Las capturas reales **todavía no existen**: la superficie para tomarlas es `/admin/playground` (dev-only, mock data), que renderiza cada componente del DS en todos sus estados. Ese es el mismo enfoque bottom-up que se usó en móvil (átomos → moléculas → organismos → pantallas) antes de auditar las pantallas. ### Átomos [Section titled “Átomos”](#átomos) Código en `src/ui/atoms/**`. #### Avatar [Section titled “Avatar”](#avatar) **Código:** `src/ui/atoms/Avatar/index.tsx` · **Figma:** [`45:90`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=45-90) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Avatar](/design/parity/figma/atoms/figma-avatar.png)](/design/parity/figma/atoms/figma-avatar.png "figma Avatar — abrir a tamaño completo") | [![app Avatar](/design/parity/history/2026-08-07/screenshots/playground/avatar.png)](/design/parity/history/2026-08-07/screenshots/playground/avatar.png "app Avatar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Tinte del avatar de iniciales **#E6EEFA** medido en la app — idéntico al de Figma (#E6EEFA). Iniciales “JP” en azul de marca (núcleo \~#1761D1→#0052CC sobre el tinte); coincide con las iniciales primary del diseño. - **Contenido:** El avatar “con imagen” resuelve con círculo sólido **#0052CC** (azul canónico) + glifo de persona blanco — el fix de brand-blue llegó a esta pantalla. - **Estados:** La rampa de tamaños S/M/L está presente y crece proporcionalmente, como en Figma (que muestra tres tamaños de iniciales). - **Tipografía:** Iniciales centradas, peso alto, legibles en los tres tamaños; consistente con el diseño. - **Layout:** Figma solo muestra las tres variantes de iniciales; la app añade “con imagen” y “fallback” como showcase — no es un desajuste, es superset del catálogo.Residuo: ninguno accionable. El avatar “con imagen” muestra placeholder de glifo en lugar de foto real, pero es contenido de showcase, no defecto de estilo. | | #### Banner / Inline Alert [Section titled “Banner / Inline Alert”](#banner--inline-alert) **Código:** `src/ui/atoms/Banner/index.tsx` | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Banner / Inline Alert](/design/parity/figma/atoms/figma-banner.png)](/design/parity/figma/atoms/figma-banner.png "figma Banner / Inline Alert — abrir a tamaño completo") | [![app Banner / Inline Alert](/design/parity/history/2026-08-07/screenshots/playground/banner.png)](/design/parity/history/2026-08-07/screenshots/playground/banner.png "app Banner / Inline Alert — abrir a tamaño completo") | | Notas | | | **✅ Coincide**Misma pantalla: los 4 banners (info / success / warning / danger) están en el mismo orden y con el mismo contenido en ambos paneles. Los fixes sistémicos de tinte llegaron a esta atom y están medidos.- **Color:** Tintes soft idénticos al hex — info `#EFF6FF`, success `#ECFDF5` (Figma `#ECFCF5`), warning `#FFFBEB`, danger `#FEF2F2`. Coinciden con Figma píxel a píxel. - **Iconos:** Rol/color por variante correcto: info azul `#3B82F6` (círculo relleno), success verde `#25BF8C`, warning ámbar `#F5A720`, danger rojo `#F05454/#EF4444`. Coincide con la paleta canónica. - **Tipografía:** Jerarquía correcta — título en el color del rol (peso semibold) + cuerpo en texto oscuro. Igual que Figma. - **Layout:** Estructura idéntica — icono a la izquierda, título+cuerpo, cierre arriba a la derecha. Estructura y densidad concuerdan (escalas distintas, no comparo px absolutos). - **Residuo (menor, no bloqueante):** El glifo de cierre difiere — Figma usa un icono estilo tecla “borrar/backspace” en gris redondeado; la app usa una `×` simple. Diferencia de icono trivial, misma posición/función. - **Nota de captura:** Las etiquetas en versalitas (INFO/SUCCESS/WARNING/DANGER) son del showcase/playground, no de la atom — no es defecto del componente. | | #### Button [Section titled “Button”](#button) **Código:** `src/ui/atoms/Button/index.tsx` · **Figma:** [`21:50`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=21-50) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Button](/design/parity/figma/atoms/figma-button.png)](/design/parity/figma/atoms/figma-button.png "figma Button — abrir a tamaño completo") | [![app Button](/design/parity/history/2026-08-07/screenshots/playground/button.png)](/design/parity/history/2026-08-07/screenshots/playground/button.png "app Button — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Filled `#0052CC` (default), `#003D99` (hover y pressed) — medidos idénticos en Figma y app. Tonal `#E6EEFA` con texto `#0052CC`. Disabled `#E5E7EB` en ambos. Todo cableado por `palette.primary`. - **Bordes:** Outlined con borde azul 1px sólido; tarjetas/estados con radio redondeado consistente entre ambos. - **Iconos:** El showcase usa un ícono de caja registradora (POS) donde Figma muestra un carrito — diferencia de ejemplo del showcase, no defecto. - **Estados:** Las 4 variantes (Filled/Tonal/Outlined/Text) cubren default/hover/pressed/disabled/loading; el spinner de loading aparece en todas. Figma organiza loading como fila-variante y la app como columna-estado — decisión de layout del showcase, cobertura funcional equivalente. - **Layout:** Tonal pressed mide levemente más azulado en la app (`#BAD0EF`) que en Figma (tinte gris `#E5E7EB`); residuo menor de tinte de estado, dentro de tolerancia. | | #### Checkbox [Section titled “Checkbox”](#checkbox) **Código:** `src/ui/atoms/Checkbox/index.tsx` · **Figma:** [`90:8`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=90-8) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Checkbox](/design/parity/figma/atoms/figma-checkbox.png)](/design/parity/figma/atoms/figma-checkbox.png "figma Checkbox — abrir a tamaño completo") | [![app Checkbox](/design/parity/history/2026-08-07/screenshots/playground/checkbox-radio-switch.png)](/design/parity/history/2026-08-07/screenshots/playground/checkbox-radio-switch.png "app Checkbox — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Azul de marca canónico confirmado — el glifo indeterminate mide **#0052CC** exacto; el checked comparte el mismo relleno (muestreo #548BDD por la mezcla con el check blanco central). Figma también dominado por #0052CC. Sin residuo de azul. - **Bordes:** Unchecked con trazo hairline **#D5D9DE** (spec \~#D1D5DB) — variación despreciable. Geometría de esquina redondeada custom del checkbox presente en los tres estados, igual que Figma. - **Iconos:** Check blanco y guion (indeterminate) blancos, centrados, coinciden con el diseño. - **Estados:** La app añade disabled (relleno atenuado + texto #9CA3AF) y error (texto rojo de ayuda) que Figma no muestra — es un showcase superset; los tres estados canónicos del átomo casan. - **Layout:** Figma solo diseña los 3 glifos del checkbox; el panel app los expone dentro del playground junto a Radio/Switch. El átomo en sí coincide; sin acción pendiente propia de esta pantalla. | | #### Chip / Category Chip [Section titled “Chip / Category Chip”](#chip--category-chip) **Código:** `src/ui/atoms/Chip/index.tsx` · **Figma:** [`35:31`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=35-31) | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Chip / Category Chip](/design/parity/figma/atoms/figma-chip.png)](/design/parity/figma/atoms/figma-chip.png "figma Chip / Category Chip — abrir a tamaño completo") | [![app Chip / Category Chip](/design/parity/history/2026-08-07/screenshots/playground/chip.png)](/design/parity/history/2026-08-07/screenshots/playground/chip.png "app Chip / Category Chip — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color (activo):** El chip *activo* está clavado — bg tinte de marca **#E6EEFA** y etiqueta **#0052CC** (azul canónico), idéntico a Figma. Fix sistémico verificado en esta captura. - **Color (inactivo):** bg medido **\~#EBEDF0** (gris neutro MUI) vs **#F0F4F8** de Figma (gris levemente más frío); diferencia de tinte imperceptible. Texto **#6B7280** (secondary), correcto. - **Bordes:** Píldora totalmente redondeada, filled sin borde en ambos estados — coincide. - **Estados:** Se representan los dos estados que diseña Figma (inactivo/activo). La app los organiza con rótulos INACTIVO/ACTIVO; Figma los muestra lado a lado — mismo contenido y jerarquía. - **Tipografía:** Etiqueta “Categoría”, peso medio; consistente entre paneles. - **Sombras:** Plano en ambos — sin residuo.Sin hallazgos accionables propios de la pantalla; el matiz del inactivo es opcional (alinear tinte a #F0F4F8 si se busca paridad exacta, pero es cosmético). | | #### Date-range Picker [Section titled “Date-range Picker”](#date-range-picker) **Código:** `src/ui/atoms/DateRangePicker/index.tsx` · **Figma:** [`103:116`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=103-116) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Date-range Picker](/design/parity/figma/atoms/figma-date-range-picker.png)](/design/parity/figma/atoms/figma-date-range-picker.png "figma Date-range Picker — abrir a tamaño completo") | [![app Date-range Picker](/design/parity/history/2026-08-07/screenshots/playground/date-range-picker.png)](/design/parity/history/2026-08-07/screenshots/playground/date-range-picker.png "app Date-range Picker — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Coincide. Campos blancos, texto de valor #1F2933, hairline medido #E5E7EB/#E6E8EC, icono de adorno de calendario #9CA3AF y estado disabled #E5E7EB — todos alineados con la paleta remediada. - **Tipografía:** Coincide. Label estático arriba (Desde/Hasta \~#666D75 ≈ secundario), secciones CLOSED/OPEN/DISABLED en gris; valores en dark. La única diferencia textual es el uso de ejemplo, no defecto. - **Bordes:** Coincide. Campos blanco + hairline redondeado, alto \~40. El pill de Figma usa radio mayor (pastilla), pero es consecuencia del patrón, no un token distinto. - **Iconos:** Coincide. Icono de calendario gris #9CA3AF (adorno); en Figma también aparece el chevron gris #9CA3AF que la app no muestra (ver Layout). - **Estados:** Coincide. Se muestran closed / open (placeholder DD/MM/YYYY) / disabled con el patrón de deshabilitado correcto (#E5E7EB + texto atenuado). - **Layout:** Residuo real. Figma define **un único control combinado**: `[icono calendario] "1 Oct – 24 Oct, 2025" [chevron]` que despliega el calendario. La app lo implementa como **dos inputs de fecha nativos separados** (Desde/Hasta) sin pill combinado, sin resumen de rango en una sola línea y sin chevron de despliegue. Es una diferencia de patrón/estructura, no de tokens. Verificar el componente de showcase en `src/ui` (playground DateRangePicker) si se desea converger al pill único de Figma. | | #### FAB [Section titled “FAB”](#fab) **Código:** `src/ui/atoms/Fab/index.tsx` | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma FAB](/design/parity/figma/atoms/figma-fab.png)](/design/parity/figma/atoms/figma-fab.png "figma FAB — abrir a tamaño completo") | [![app FAB](/design/parity/history/2026-08-07/screenshots/playground/fab.png)](/design/parity/history/2026-08-07/screenshots/playground/fab.png "app FAB — abrir a tamaño completo") | | Notas | | | **✅ Coincide**El playground muestra el mismo átomo que diseña Figma: el FAB en sus dos estados (collapsed con glifo `+`, expanded con glifo `×`), que son exactamente los dos círculos del panel Figma.- **Color:** medido #0052CC en los dos círculos Figma y en ambos FAB de la app (collapsed 985,278 → #0253CC; expanded × 1204,300 → #0152CB). Azul de marca canónico confirmado post-remediación; el mini-FAB de acción también en #0047C8/#0052CC. - **Iconos:** glifo blanco (`+` / `×`) centrado, proporción correcta (\~20px); coincide con el spec. - **Bordes:** círculo perfecto, sin borde; correcto. - **Sombras:** el FAB lleva una sombra suave que se corresponde con el glow difuso del diseño Figma; sin residuo. - **Estados:** collapsed→expanded cablea la transición `+`→`×` tal como muestran los dos estados del diseño. - **Contenido:** el backdrop gris y las mini-acciones (Cobrar / Buscar producto / Imprimir) del estado expanded son composición del SpeedDial, no aparecen en el crop atómico de Figma; no es defecto de paridad.Sin residuo propio de la pantalla que remediar. | | #### File Upload [Section titled “File Upload”](#file-upload) **Código:** `src/ui/atoms/FileUpload/index.tsx` · **Figma:** [`103:126`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=103-126) | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma File Upload](/design/parity/figma/atoms/figma-file-upload.png)](/design/parity/figma/atoms/figma-file-upload.png "figma File Upload — abrir a tamaño completo") | [![app File Upload](/design/parity/history/2026-08-07/screenshots/playground/file-upload.png)](/design/parity/history/2026-08-07/screenshots/playground/file-upload.png "app File Upload — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Contenedor circular del icono en `#E6EEFA` (palette.primary.light) medido igual en ambos paneles; glifo en azul de marca `#0052CC` (Figma y app). `src/ui/atoms/FileUpload/index.tsx` usa `primary.light`/`primary.main`. Coincide. - **Tipografía:** Título en negrita oscuro (`subtitle2`, text.primary) y subtítulo/hint en gris tenue (`caption`, text.disabled `#9CA3AF`) según diseño. Coincide. - **Bordes:** Zona con borde `2px dashed` sobre `theme.palette.divider` (#E5E7EB); estado ERROR conmuta a `error.main` `#EF4444` medido. Botón outlined 1px sólido de marca. Coincide. - **Iconos:** El glifo difiere (app usa `Plus`, Figma un icono de compartir), pero el contenedor tintado + color de marca coinciden — icono de adorno, no defecto. Papelera del chip en `#0052CC` como adorno. - **Layout:** El chip de archivo es plano con hairline (`divider`) y papelera a la derecha, como el patrón de tarjeta plana. El showcase expone estados EMPTY/HOVER-DRAG/WITH-FILE/ERROR. - **Estados:** El subtítulo de formatos/tamaño (“CSV, PNG o JPG · hasta 10 MB”) existe vía prop `hint` (text.disabled); el showcase simplemente no lo pasa — decisión del showcase, el componente lo soporta. Sin residuo real. | | #### Form Field [Section titled “Form Field”](#form-field) **Código:** `src/ui/atoms/FormField/index.tsx` · **Figma:** [`91:23`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=91-23) | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Form Field](/design/parity/figma/atoms/figma-form-field.png)](/design/parity/figma/atoms/figma-form-field.png "figma Form Field — abrir a tamaño completo") | [![app Form Field](/design/parity/history/2026-08-07/screenshots/playground/form-field.png)](/design/parity/history/2026-08-07/screenshots/playground/form-field.png "app Form Field — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Borde focus medido `#0052CC` en app = azul de marca de Figma. Relleno disabled `#E5E7EB` idéntico. Estado error en rojo en ambos (Figma ≈`#EE3030`, app `#F05252` — misma familia, sin residuo real). Ayuda gris \~`#6D7482` (≈`#6B7280`). - **Tipografía:** Label estático en negrita arriba del campo, ayuda y mensaje de error en tamaño menor, coincide en ambos paneles. - **Bordes:** Campos blancos con hairline y esquinas redondeadas en los cuatro estados; anillo/borde de focus y borde de error consistentes con Figma. - **Estados:** Default / focus (borde azul) / error (borde+texto rojo) / disabled (relleno gris `#E5E7EB` + texto atenuado) todos presentes y correctos. - **Layout:** El showcase apila los estados en vertical con etiquetas DEFAULT/FOCUS/ERROR/DISABLED mientras Figma los muestra en fila horizontal — decisión del showcase, no defecto. Estructura interna del campo idéntica. | | #### Icon [Section titled “Icon”](#icon) **Código:** `src/ui/atoms/Icon/index.tsx` | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Icon](/design/parity/figma/atoms/figma-icon.png)](/design/parity/figma/atoms/figma-icon.png "figma Icon — abrir a tamaño completo") | [![app Icon](/design/parity/history/2026-08-07/screenshots/playground/icon.png)](/design/parity/history/2026-08-07/screenshots/playground/icon.png "app Icon — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Iconos medidos en `#0052CC` exacto en ambos paneles (Plus, Gear, Info, muestras de tamaño). Azul de marca canónico correcto. - **Iconos:** FontAwesome Solid, fill-based, ligados a `Color`, nombres = registro `TerminalesIcons` — paridad con Figma y con Kotlin/`react-icons/fa6`. - **Espaciado:** Grilla de registro con celdas uniformes y etiquetas centradas debajo; escala de tamaños sm 16 / md 20 / lg 24 / xl 32 / xxl 40 legible. - **Estados:** Swatches de la sección Colors mapean a tokens canónicos medidos: primary `#0052CC`, success `#10B981`, warning `#F59E0B`, error `#EF4444`, text `#1F2933`. - **Layout:** Las secciones Registry / Sizes / Colors son estructura del showcase de `/playground`; el set de iconos de ejemplo distinto respecto a Figma es decisión del showcase, no residuo. - Sin residuo real del componente. | | #### Icon Button [Section titled “Icon Button”](#icon-button) **Código:** `src/ui/atoms/IconButton/index.tsx` · **Figma:** [`34:73`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=34-73) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Icon Button](/design/parity/figma/atoms/figma-icon-button.png)](/design/parity/figma/atoms/figma-icon-button.png "figma Icon Button — abrir a tamaño completo") | [![app Icon Button](/design/parity/history/2026-08-07/screenshots/playground/icon-button.png)](/design/parity/history/2026-08-07/screenshots/playground/icon-button.png "app Icon Button — abrir a tamaño completo") | | Notas | | | **✅ Coincide**Ambos paneles muestran el mismo átomo (IconButton con glifo de tienda) en sus tres variantes × tres estados. El Figma los presenta en una fila continua de 9 tiles; la app los organiza en una matriz etiquetada **Standard / Outlined / Ghost × Default / Pressed / Disabled** — misma cobertura semántica, solo cambia el andamiaje del showcase (no es defecto del componente).- **Color:** Todos los tokens post-remediación están presentes y medidos en la captura nueva: * Pressed sólido = **#0052CC** (idéntico al azul de marca del Figma, medido en ambos). * Tinte de fondo default/pressed-suave = **#E6EEFA** (coincide con el tinte del Figma). * Disabled: fondo **#E5E7EB** + glifo **#9CA3AF** (tokens de disabled correctos). * Icono activo azul en Outlined/Ghost default y pressed = **#0052CC**. - **Estados:** Standard=fondo tinte→sólido, Outlined=blanco con hairline→tinte, Ghost=transparente→tinte. La progresión default→pressed→disabled es consistente con las tres columnas del Figma. - **Iconos:** El glifo (store) coincide; en Standard pressed va en blanco sobre el sólido, igual que el tile azul del Figma. - **Bordes:** Outlined en la app usa hairline muy tenue (\~#E5E7EB) sobre blanco; concuerda con el tile blanco-con-borde del Figma. Diferencia de contraste marginal, no accionable. - **Layout:** Único residuo — el Figma es una tira lineal y la app una grilla con headers; decisión de presentación del playground, no del átomo.Sin hallazgos accionables; la remediación sistémica (azul #0052CC, tinte #E6EEFA, disabled #E5E7EB/#9CA3AF) llegó completa a esta pantalla. | | #### Keypad Key [Section titled “Keypad Key”](#keypad-key) **Código:** `src/ui/atoms/KeypadKey/index.tsx` · **Figma:** [`37:45`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=37-45) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Keypad Key](/design/parity/figma/atoms/figma-keypad-key.png)](/design/parity/figma/atoms/figma-keypad-key.png "figma Keypad Key — abrir a tamaño completo") | [![app Keypad Key](/design/parity/history/2026-08-07/screenshots/playground/keypad-key.png)](/design/parity/history/2026-08-07/screenshots/playground/keypad-key.png "app Keypad Key — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Estado pressed medido con tinte de selección **#E6EEFA** y glifo/dígito en azul de marca **#0052CC** — patrón de selección con tinte correcto (idéntico en fila Dígito y Acción). Default blanco **#FFFFFF** con dígito **#1F2933**; disabled con dígito **#9CA3AF**. Todo alineado con la remediación (palette.primary.light + azul marca). - **Tipografía:** Dígito en peso bold, coherente con Figma. - **Bordes:** Tecla con esquinas redondeadas suaves, igual que el diseño; default con hairline sutil sobre fondo claro. - **Iconos:** Fila Acción (backspace) replica los tres estados con el mismo esquema de color que la fila Dígito. - **Estados:** default / pressed / disabled presentes y consistentes con el patrón de tinte canónico. - **Layout:** El showcase agrega la fila Acción y etiquetas de estado (DEFAULT/PRESSED/DISABLED) — decisión del showcase, no defecto. Figma muestra las variantes en horizontal sin etiquetas; sin residuo del componente. | | #### PIN Dot [Section titled “PIN Dot”](#pin-dot) **Código:** `src/ui/atoms/PinDot/index.tsx` · **Figma:** [`47:52`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=47-52) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma PIN Dot](/design/parity/figma/atoms/figma-pin-dot.png)](/design/parity/figma/atoms/figma-pin-dot.png "figma PIN Dot — abrir a tamaño completo") | [![app PIN Dot](/design/parity/history/2026-08-07/screenshots/playground/pin-dot.png)](/design/parity/history/2026-08-07/screenshots/playground/pin-dot.png "app PIN Dot — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** El dot **lleno** mide `#0052CC` en ambos paneles — azul de marca correcto (verificado, no re-reportado). El estado **error** es `#EF4444` (`palette.error.main`), consistente. - **Bordes:** Único residuo real — el borde del dot **vacío** mide `#E5E7EB` en la app (usa `theme.palette.divider`) vs `#D1D5DB` en Figma, un paso de gris más oscuro (el mismo tono que el borde de checkbox/radio sin marcar). Confirmado en `src/ui/atoms/PinDot/index.tsx:54` (`2px solid ${theme.palette.divider}`). Cambiar a `palette.grey[300]`/`#D1D5DB` para paridad exacta del anillo vacío. - **Iconos:** Anillo 2px sólido + círculo perfecto (`borderRadius: '50%'`, `boxSizing: border-box`) coincide con Figma. - **Layout:** Figma solo muestra vacío + lleno; la app añade filas “PIN ROW” y “ERROR” — decisión del showcase, no defecto. - **Estados:** Lleno/vacío/error bien diferenciados; transición de `border-color`/`background-color` presente. | | #### QR Code [Section titled “QR Code”](#qr-code) **Código:** `src/ui/atoms/QrCode/index.tsx` | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma QR Code](/design/parity/figma/atoms/figma-qr-code.png)](/design/parity/figma/atoms/figma-qr-code.png "figma QR Code — abrir a tamaño completo") | [![app QR Code](/design/parity/history/2026-08-07/screenshots/playground/qr-code.png)](/design/parity/history/2026-08-07/screenshots/playground/qr-code.png "app QR Code — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Módulos medidos en #1F2933 (tinta oscura canónica), igual que el Figma (#1F2933 medido). Quiet zone blanco #FFFFFF en ambos. Sin negro puro fuera de token. - **Layout:** App muestra el atom en tres tamaños (S/M/L) sobre tarjeta blanca con esquinas redondeadas y fondo #F5F7FA del playground; el panel Figma es un crop ampliado de un solo código, por eso no se compara contenedor a escala. - **Bordes:** Tarjeta contenedora con radio redondeado y hairline suave, coherente con el patrón de tarjetas planas. - **Iconos:** El patrón de módulos difiere (dato codificado y estilo de módulo distinto) — es contenido de ejemplo del generador, no defecto. - **Estados:** N/A para este atom.Sin residuo real; el componente cumple la paridad tras la remediación. | | #### Radio [Section titled “Radio”](#radio) **Código:** `src/ui/atoms/Radio/index.tsx` · **Figma:** [`90:14`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=90-14) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Radio](/design/parity/figma/atoms/figma-radio.png)](/design/parity/figma/atoms/figma-radio.png "figma Radio — abrir a tamaño completo") | [![app Radio](/design/parity/history/2026-08-07/screenshots/playground/checkbox-radio-switch.png)](/design/parity/history/2026-08-07/screenshots/playground/checkbox-radio-switch.png "app Radio — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Punto seleccionado medido `#0052CC`, idéntico al de Figma (`#0052CC`). Anillo no-seleccionado gris claro `~#D1D5DB`, coincide con el ring canónico de Figma. - **Bordes:** Geometría redondeada correcta en ambos: no-seleccionado = anillo gris con relleno blanco; seleccionado = anillo azul + hueco blanco + punto azul central. Igual que Figma. - **Estados:** DEFAULT, SELECTED y DISABLED reproducen el patrón de Figma (disabled atenuado `#9CA3AF`). El estado ERROR (label roja + helper “Selecciona un método”) es un extra del showcase — Figma solo ilustra default/seleccionado; no es defecto. - **Layout:** Radio + label alineados, mismo patrón de selección con tinte/relleno de marca. Sin residuo. | | #### Search Field [Section titled “Search Field”](#search-field) **Código:** `src/ui/atoms/SearchField/index.tsx` · **Figma:** [`37:35`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=37-35) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Search Field](/design/parity/figma/atoms/figma-search-field.png)](/design/parity/figma/atoms/figma-search-field.png "figma Search Field — abrir a tamaño completo") | [![app Search Field](/design/parity/history/2026-08-07/screenshots/playground/search-field.png)](/design/parity/history/2026-08-07/screenshots/playground/search-field.png "app Search Field — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** El único residuo medible es la superficie del campo. Figma muestra el search bar con **relleno suave #F0F4F8 y sin borde** (estilo *filled*); el app lo renderiza como **#FFFFFF + hairline #E5E7EB** (`src/ui/atoms/SearchField/index.tsx` usa `TextField` con el tema canónico). Esta es la decisión de campo-de-formulario blanco+hairline ya estandarizada en la remediación, por lo que probablemente el #F0F4F8 de Figma sea el lienzo/estilo filled y no un defecto; queda como diferencia de tratamiento (filled vs outlined), no de token. - **Iconos:** Lupa de adorno **#9CA3AF** en ambos paneles. ✓ - **Tipografía:** Placeholder gris muted (\~#9CA3AF) coincide; texto escrito (TYPING) en oscuro #1F2933. ✓ - **Bordes:** App con radio redondeado y hairline #E5E7EB; alto \~40. ✓ - **Estados:** DISABLED del app = relleno **#E5E7EB** (coincide con remediación). Botón limpiar (×) aparece al escribir. ✓ - **Layout:** Icono a la izquierda + placeholder “Buscar cliente…” coinciden; el showcase añade TYPING y DISABLED (decisión de showcase, no defecto). | | #### Segmented Control [Section titled “Segmented Control”](#segmented-control) **Código:** `src/ui/atoms/SegmentedControl/index.tsx` · **Figma:** [`90:24`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=90-24) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Segmented Control](/design/parity/figma/atoms/figma-segmented-control.png)](/design/parity/figma/atoms/figma-segmented-control.png "figma Segmented Control — abrir a tamaño completo") | [![app Segmented Control](/design/parity/history/2026-08-07/screenshots/playground/segmented-control.png)](/design/parity/history/2026-08-07/screenshots/playground/segmented-control.png "app Segmented Control — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Track gris claro medido en app (#EBEDF0 / #F0F4F8) alineado con Figma (#F0F4F8). Thumb del segmento activo blanco puro (#FFFFFF) en ambos. - **Bordes:** Thumb con esquinas redondeadas sobre track redondeado — geometría de selección correcta tras la remediación. - **Estados:** Patrón de selección = thumb blanco (no relleno primary), tal como el canon. El estado DISABLED conserva thumb blanco en el activo y atenúa los labels inactivos; consistente con Figma. - **Tipografía:** Label seleccionado en tono oscuro y no-seleccionados en gris; jerarquía correcta. - **Layout:** Tres segmentos (Día/Semana/Mes) en fila dentro del contenedor tintado; misma estructura que el diseño. - Nota: el panel APP añade un segundo grupo DISABLED como showcase de /playground (decisión del showcase, no defecto). | | #### Select [Section titled “Select”](#select) **Código:** `src/ui/atoms/Select/index.tsx` · **Figma:** [`91:45`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=91-45) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Select](/design/parity/figma/atoms/figma-select.png)](/design/parity/figma/atoms/figma-select.png "figma Select — abrir a tamaño completo") | [![app Select](/design/parity/history/2026-08-07/screenshots/playground/select.png)](/design/parity/history/2026-08-07/screenshots/playground/select.png "app Select — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** fixes sistémicos verificados en el campo cerrado: relleno blanco, borde hairline **#E5E7EB** (medido arriba/abajo), texto de opciones **#1F2933** (medido). El tinte de selección del Figma es **#E6EEFA** con check **#0052CC** (medidos en el diseño). - **Estados:** el select ABIERTO de la captura conserva borde **#E5E7EB** mientras el Figma muestra borde azul **#0052CC** \~2px. El fix existe en código (`src/ui/theme/theme.ts:260` → `.Mui-focused` = primary), pero `SelectShowcase.tsx` abre el popper con la prop `open` sin enfocar el input, así que la captura no lo ejercita — agregar foco (o un estado “Focused”) al showcase para poder verificarlo. - **Estados:** el showcase no tiene ejemplo con valor seleccionado; la afordancia de opción seleccionada del Figma (fila con tinte #E6EEFA + etiqueta azul + **check #0052CC** a la derecha) **no está implementada**: no hay override `MuiAutocomplete` en el theme ni `renderOption` con check en `src/ui/atoms/Select/index.tsx` (el default MUI daría solo tinte rgba primario 8%, sin check ni texto azul). - **Iconos:** chevron del campo en **#757575** (action.active default de MUI, medido) vs. gris del sistema (#6B7280) del diseño — mapear el popupIcon al gris frío. - **Tipografía:** la etiqueta “Fruta” dentro del campo mide ~~#6B7280 vs. placeholder del Figma más claro (~~#9CA3AF); además MUI usa label flotante en el borde al llenar el campo, patrón que el Figma no dibuja — verificar con un estado lleno. - **Layout:** en la captura el popper del ejemplo OPEN se solapa con el campo del ejemplo “No results” (los poppers portalan al body) — artefacto del showcase, no del atom. - **Contenido:** la app muestra además estados No-results y Loading (spinner visible, medido en captura) que el frame de Figma no dibuja — extra correcto, no es defecto. | | #### Skeleton [Section titled “Skeleton”](#skeleton) **Código:** `src/ui/atoms/Skeleton/index.tsx` | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Skeleton](/design/parity/figma/atoms/figma-skeleton.png)](/design/parity/figma/atoms/figma-skeleton.png "figma Skeleton — abrir a tamaño completo") | [![app Skeleton](/design/parity/history/2026-08-07/screenshots/playground/skeleton.png)](/design/parity/history/2026-08-07/screenshots/playground/skeleton.png "app Skeleton — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Relleno medido en app #F0F4F8 / #F1F5F8 / #F2F5F8 = surface-variant canónico #F0F4F8 de Figma. Cableado en `theme.ts:407-410` (`MuiSkeleton.root.backgroundColor = scheme.surface.variant`), fix “Parity finding #16” verificado. - **Bordes:** Barras y avatar con esquinas redondeadas / círculo, igual que el diseño. - **Layout:** El showcase muestra las primitivas etiquetadas (LINE/BLOCK/ROW/CARD/TABLE); Figma muestra una composición de página (avatar+líneas, card+líneas). Es la misma primitiva de skeleton — la composición vs. catálogo es decisión del showcase, no defecto. - **Estados:** La app añade un shimmer/wave animado (gradiente #E8ECF0→#F2F5F8 en BLOCK); es la animación de carga por defecto de MUI, esperada en un skeleton; el screenshot de Figma es estático. Sin residuo. - **Tipografía:** Etiquetas de sección del showcase (#6B7280), no forman parte del componente. | | #### Status Badge [Section titled “Status Badge”](#status-badge) **Código:** `src/ui/atoms/StatusBadge/index.tsx` · **Figma:** [`33:26`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=33-26) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Status Badge](/design/parity/figma/atoms/figma-status-badge.png)](/design/parity/figma/atoms/figma-status-badge.png "figma Status Badge — abrir a tamaño completo") | [![app Status Badge](/design/parity/history/2026-08-07/screenshots/playground/status-badge.png)](/design/parity/history/2026-08-07/screenshots/playground/status-badge.png "app Status Badge — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Tintes \*-soft verificados por muestreo y coinciden con Figma: success `#ECFDF5`≈`#ECFCF5`, warning `#FFFBEB`, danger `#FEF2F2`, info `#EFF6FF`. Textos por rol correctos: success `#10B981`, warning `#F59E0B`, danger `#EF4444`, info azul. Fix de variantes soft confirmado. - **Bordes:** Pill totalmente redondeado con relleno tintado en las 4 variantes (sin borde duro), igual que Figma. - **Layout:** El showcase apila vertical con etiqueta de rol (SUCCESS/WARNING/DANGER/INFO) sobre cada badge; Figma los muestra en fila horizontal. Es organización del playground, no defecto del componente. - **Estados:** Este showcase no incluye la variante `neutral` gris (Figma tampoco la muestra aquí); nada que reportar. - Salvedad: el histograma global marca 99% fondo porque los badges ocupan <1% del canvas 1440×900; los recortes confirman los pills tintados. | | #### Switch [Section titled “Switch”](#switch) **Código:** `src/ui/atoms/Switch/index.tsx` · **Figma:** [`90:21`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=90-21) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Switch](/design/parity/figma/atoms/figma-switch.png)](/design/parity/figma/atoms/figma-switch.png "figma Switch — abrir a tamaño completo") | [![app Switch](/design/parity/history/2026-08-07/screenshots/playground/checkbox-radio-switch.png)](/design/parity/history/2026-08-07/screenshots/playground/checkbox-radio-switch.png "app Switch — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Track ON medido **#0052CC** en la app, idéntico al azul de marca del Figma; track OFF **#D1D5DB** en ambos paneles; thumb blanco **#FFFFFF** verificado. Sin desviación. - **Bordes:** Forma pill totalmente redondeada con thumb circular blanco, igual que el diseño. - **Layout:** Thumb a la izquierda en OFF y desplazado a la derecha sobre track azul en ON — reproduce ambos estados del Figma. - **Estados:** El panel Figma solo muestra OFF y ON; la app añade **DISABLED** (decisión del showcase), con track gris tenue + thumb azul claro medido **#9EBDEB** — atenuación coherente con el patrón disabled. - **Tipografía:** Etiquetas OFF/ON/DISABLED en caps grises y “Notificaciones” en texto oscuro, consistentes con el resto de átomos. Sin residuo real del componente. | | #### Textarea [Section titled “Textarea”](#textarea) **Código:** `src/ui/atoms/Textarea/index.tsx` · **Figma:** [`103:110`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=103-110) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Textarea](/design/parity/figma/atoms/figma-textarea.png)](/design/parity/figma/atoms/figma-textarea.png "figma Textarea — abrir a tamaño completo") | [![app Textarea](/design/parity/history/2026-08-07/screenshots/playground/textarea.png)](/design/parity/history/2026-08-07/screenshots/playground/textarea.png "app Textarea — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Fondo blanco `#FFFFFF` con borde hairline `#E5E7EB` medido en el estado default — coincide con el token canónico. - **Bordes:** Esquinas redondeadas y borde 1px sólido; radios consistentes con Figma. - **Estados:** Focus con borde azul de marca `#0052CC` (medido), error con borde `#EF4444` + texto de ayuda rojo, disabled con tinte gris y texto atenuado — todos correctos. - **Tipografía:** Label estático arriba en tono oscuro, placeholder gris — patrón de campo de formulario correcto. - **Layout:** Figma solo muestra el estado default; el showcase de /playground agrega FOCUS/ERROR/DISABLED (decisión del showcase). El texto de ejemplo distinto es decisión del showcase, no defecto. | | #### Toast / Snackbar [Section titled “Toast / Snackbar”](#toast--snackbar) **Código:** `src/ui/atoms/Toast/index.tsx` · **Figma:** [`102:142`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=102-142) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Toast / Snackbar](/design/parity/figma/atoms/figma-toast.png)](/design/parity/figma/atoms/figma-toast.png "figma Toast / Snackbar — abrir a tamaño completo") | [![app Toast / Snackbar](/design/parity/history/2026-08-07/screenshots/playground/toast.png)](/design/parity/history/2026-08-07/screenshots/playground/toast.png "app Toast / Snackbar — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Superficie del toast correcta y opaca: medí **#1F2933** en Figma y en la app (rol `text.primary`, patrón “dark” verificado). Texto del mensaje blanco en ambos. ✅ - **Color (acción):** Único residuo real. La acción “Deshacer” en Figma es azul claro medido **#7FB0FF**; la app la pinta en **#E6EEFA** (`primary.light`, `src/ui/atoms/Toast/index.tsx` → `color: dark ? 'primary.light' : 'primary.main'`). Ese tinte está pensado como *relleno* y sobre fondo oscuro lee casi blanco en vez de azul legible. Considerar un rol de azul claro (p. ej. `primary.200`/un token on-dark) para el CTA sobre superficie oscura. - **Iconos:** Figma muestra el check de éxito como glifo verde simple (sin círculo); la app usa badges circulares rellenos por estado (rojo/naranja/verde/azul). Es decisión del showcase (variantes de ejemplo), no defecto — colores de rol correctos. - **Bordes:** Ambos con esquinas redondeadas; el radio de Figma es algo más tipo píldora (\~½ alto) y el de la app un rect redondeado un poco menos pronunciado. Diferencia de proporción menor, dentro de tolerancia. - **Layout:** Estructura idéntica — icono de estado + mensaje (flex-grow) + acción a la derecha. ✅ - **Estados:** Las 4 variantes (info/success/warning/danger) presentes con sus roles de color correctos. ✅ | | #### Tooltip [Section titled “Tooltip”](#tooltip) **Código:** `src/ui/atoms/Tooltip/index.tsx` · **Figma:** [`102:150`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=102-150) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Tooltip](/design/parity/figma/atoms/figma-tooltip.png)](/design/parity/figma/atoms/figma-tooltip.png "figma Tooltip — abrir a tamaño completo") | [![app Tooltip](/design/parity/history/2026-08-07/screenshots/playground/tooltip.png)](/design/parity/history/2026-08-07/screenshots/playground/tooltip.png "app Tooltip — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Fondo del tooltip **#1F2933** medido en ambos paneles (Figma 66% del pixel-count; app muestreado en la píldora “Arriba” → #1F2933). Opaco, sin transparencia. Texto blanco. Fix de tooltip opaco confirmado. - **Iconos:** Caret/flecha direccional presente y correctamente orientado en las cuatro variantes (top/bottom/left/right). - **Layout:** El showcase de /playground demuestra las 4 posiciones alrededor de un botón “Objetivo” (outlined, borde primary) — decisión del showcase, no defecto. El Figma solo muestra un tooltip individual con texto de ejemplo “Editar”. - **Tipografía/Espaciado/Bordes:** Píldora redondeada con padding compacto, sin borde, coherente con el diseño. Sin residuo real detectado. | | ### Moléculas [Section titled “Moléculas”](#moléculas) Código en `src/ui/molecules/**`. #### Balance Card [Section titled “Balance Card”](#balance-card) **Código:** `src/ui/molecules/BalanceCard/index.tsx` | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Balance Card](/design/parity/figma/molecules/figma-balance-card-context.png)](/design/parity/figma/molecules/figma-balance-card-context.png "figma Balance Card — abrir a tamaño completo") | [![app Balance Card](/design/parity/history/2026-08-07/screenshots/playground/balance-card.png)](/design/parity/history/2026-08-07/screenshots/playground/balance-card.png "app Balance Card — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Variante primary medida en #0052CC (fondo) con botón tonal #E6EEFA + texto azul — igual que la card azul de Figma. Cards planas blancas para el resto de variantes. - **Bordes:** Hairline de tarjeta plana en #E5E7EB, radios redondeados; sin sombras. Correcto. - **Iconos:** Botón “actualizar” con icono de refresco en brand #0052CC; adorno discreto arriba-derecha, coherente con Figma. - **Tipografía:** “Saldo disponible” en gris secundario, monto en jerarquía fuerte (#1F2933 en cards blancas / blanco sobre primary), sufijo MXN atenuado (#9CA3AF). Sin desvíos. - **Estados:** Skeleton “cargando” con barra pálida \~#F6F9FB (≈#F0F4F8); estado primary destacado. Todos presentes. - **Layout:** El panel APP muestra variantes extra de ejemplo (con moneda, crédito $8,000, cargando) que no aparecen en el contexto de Figma — decisión del showcase, no defecto. La estructura label→monto→acción replica la card de Figma. | | #### Cart Bar [Section titled “Cart Bar”](#cart-bar) **Código:** `src/ui/molecules/CartBar/index.tsx` · **Figma:** [`44:80`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=44-80) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Cart Bar](/design/parity/figma/molecules/figma-cart-bar.png)](/design/parity/figma/molecules/figma-cart-bar.png "figma Cart Bar — abrir a tamaño completo") | [![app Cart Bar](/design/parity/history/2026-08-07/screenshots/playground/cart-bar.png)](/design/parity/history/2026-08-07/screenshots/playground/cart-bar.png "app Cart Bar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Barra medida `#0052CC` en ambos paneles (Figma 52.5% de píxeles, APP 5.5% por ser captura de página completa); azul de marca canónico correcto. Fondo del showcase `#F5F7FA`. - **Tipografía:** Bloque apilado “N artículos” (secundario, más claro) sobre “$monto” (blanco, peso fuerte) reproduce la jerarquía de Figma. - **Bordes:** Esquinas redondeadas (\~8px) en ambos; barra plana sin sombra. Coincide. - **Iconos:** Carrito relleno blanco a la izquierda en ambos paneles. Coincide. - **Layout:** Icono + texto a la izquierda, CTA a la derecha. Estructura idéntica a Figma. - **Estados:** El CTA difiere por texto de ejemplo (“Ver carrito” en Figma vs “Cobrar” en la app) — decisión del showcase, no defecto. La app añade una variante “vacío · deshabilitado” (sin referencia en Figma) que atenúa el texto del CTA a gris pero mantiene la barra en azul pleno; comportamiento razonable para el estado, sin residuo del componente. | | #### Customer Row [Section titled “Customer Row”](#customer-row) **Código:** `src/ui/molecules/CustomerRow/index.tsx` | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Customer Row](/design/parity/figma/molecules/figma-customer-row.png)](/design/parity/figma/molecules/figma-customer-row.png "figma Customer Row — abrir a tamaño completo") | [![app Customer Row](/design/parity/history/2026-08-07/screenshots/playground/customer-row.png)](/design/parity/history/2026-08-07/screenshots/playground/customer-row.png "app Customer Row — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Avatar con tinte `#E6EEFA` medido en ambos paneles (canónico `palette.primary.light`) e iniciales en azul de marca. Nombre `#1F2933` (medido `#454E56` en bordes por antialias), teléfono gris secundario, chevron `#6B7280`. Todo alineado. - **Iconos:** Chevron de acción en gris, coincide con Figma. - **Layout:** Avatar + bloque nombre/teléfono + chevron a la derecha; misma estructura y proporciones que el diseño. - **Estados:** El panel APP añade una variante `SELECTED` con fondo tintado (`#E1E9F6`, familia primary.light) y esquinas redondeadas — es un estado del showcase que sigue el patrón de selección con tinte, no un defecto. Figma solo muestra el estado default. - Sin residuo real del componente. | | #### Denomination Row [Section titled “Denomination Row”](#denomination-row) **Código:** `src/ui/molecules/DenominationRow/index.tsx` | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Denomination Row](/design/parity/figma/molecules/figma-denomination-row.png)](/design/parity/figma/molecules/figma-denomination-row.png "figma Denomination Row — abrir a tamaño completo") | [![app Denomination Row](/design/parity/history/2026-08-07/screenshots/playground/denomination-row.png)](/design/parity/history/2026-08-07/screenshots/playground/denomination-row.png "app Denomination Row — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** *(re-auditado tras remediación)*- **Color:** El subtotal difiere. En Figma el `$0.00` se renderiza en gris atenuado (medido \~#62696F/#6B7280, tipo `text.secondary`), claramente más claro que el label `$500`. En la app el subtotal es oscuro `#1F2933` (`src/ui/molecules/DenominationRow/index.tsx:85` usa `color="text.primary"`). Diferencia real pero menor/ambigua (la única muestra en Figma es el estado cero). Se deja como residuo aceptado. - **Iconos/Layout:** Residuo de operadores **cerrado** — los separadores `×` (entre denom e input) y `=` (entre input y subtotal) ahora se renderizan como glifos `text.secondary` grises (`index.tsx:51-58,74-81`), igual que Figma. - **Color:** Resto coincide — label denominación `text.primary` oscuro, `×` y `=` en `text.secondary` gris, en ambos paneles. - **Bordes:** Input blanco con hairline `#E5E7EB` medido (canónico) y esquinas redondeadas — coincide. - **Layout:** Fila flex `label · × · input · = · subtotal`, subtotal alineado a la derecha (`ml:auto`), ancho de label e input \~96px en ambos. - **Tipografía:** `subtitle2` para label/subtotal, `body2` para separadores; coincide con Figma. La diferencia de formato `$500` (Figma) vs `$500.00` (app) es dato de ejemplo del showcase, no defecto. | | #### Empty State [Section titled “Empty State”](#empty-state) **Código:** `src/ui/molecules/EmptyState/index.tsx` · **Figma:** [`99:151`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=99-151) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Empty State](/design/parity/figma/molecules/figma-empty-state.png)](/design/parity/figma/molecules/figma-empty-state.png "figma Empty State — abrir a tamaño completo") | [![app Empty State](/design/parity/history/2026-08-07/screenshots/playground/empty-state.png)](/design/parity/history/2026-08-07/screenshots/playground/empty-state.png "app Empty State — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Tinte del contenedor de icono medido `#F0F4F8` en Figma y app; glifo de adorno `#9CA3AF` en ambos. Título `#1F2933`, descripción gris medio (`#6B7280`, lectura clareada por antialias del texto fino). Botón relleno `#0052CC` idéntico en los dos paneles; botón outlined con borde y texto azul de marca. - **Bordes:** Contenedor de icono con esquinas redondeadas + botón outlined con borde sólido 1px; botón relleno sin borde. Coincide. - **Iconos:** Contenedor tintado con icono de adorno centrado, patrón correcto. El icono de ejemplo difiere (regalo en Figma / etiqueta en la variante “con acciones” del showcase) — decisión del showcase, no defecto. - **Layout:** Estructura vertical centrada contenedor-de-icono → título → descripción → fila de acciones (outlined + relleno), reproducida fielmente. Las variantes adicionales del showcase (búsqueda vacía, sin descripción) son ejemplos del playground. - **Estados:** Botones primario/outlined con el patrón cromático correcto; sin residuo detectable. | | #### Held-order Card [Section titled “Held-order Card”](#held-order-card) **Código:** `src/ui/molecules/HeldOrderCard/index.tsx` | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Held-order Card](/design/parity/figma/molecules/figma-held-order-card.png)](/design/parity/figma/molecules/figma-held-order-card.png "figma Held-order Card — abrir a tamaño completo") | [![app Held-order Card](/design/parity/history/2026-08-07/screenshots/playground/held-order-card.png)](/design/parity/history/2026-08-07/screenshots/playground/held-order-card.png "app Held-order Card — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación)*- **Color:** Botón `Reanudar venta` tonal medido #E6EEFA con texto azul de marca #0052CC. Avatar `SR` fill #E6EEFA + iniciales #0052CC. Folio `#H-2271` en azul de marca. Nombre/meta en grises canónicos, precio en negrita oscuro. Coincide. - **Bordes:** Tarjetas planas con hairline #E5E7EB y radio redondeado; chips (`Hace 4 min`, `En espera por…`) con borde neutro tipo StatusBadge. Sin sombras pesadas. - **Estados:** Variante `Reanudar deshabilitado` con fill/texto disabled gris (#E5E7EB/#9CA3AF) correctamente aplicada; variantes Default y Con artículos consistentes. - **Layout:** El showcase presenta tarjetas compactas (avatar arriba-derecha, chips en fila, meta como badges) frente a la tarjeta expandida de Figma; es la decisión de composición del showcase, no residuo del componente. Estructura, roles de color y patrón de botón tonal coinciden. - **Iconos/Tipografía:** Textos de ejemplo (`Carlos Méndez`, `6 art.`, `LR`) distintos por ser datos del showcase; no computa como defecto.Sin residuo real del componente. | | #### KPI Stat Card [Section titled “KPI Stat Card”](#kpi-stat-card) **Código:** `src/ui/molecules/KpiStatCard/index.tsx` · **Figma:** [`99:63`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=99-63) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma KPI Stat Card](/design/parity/figma/molecules/figma-kpi-stat-card.png)](/design/parity/figma/molecules/figma-kpi-stat-card.png "figma KPI Stat Card — abrir a tamaño completo") | [![app KPI Stat Card](/design/parity/history/2026-08-07/screenshots/playground/kpi-stat-card.png)](/design/parity/history/2026-08-07/screenshots/playground/kpi-stat-card.png "app KPI Stat Card — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Coincide. Contenedor de icono tintado medido en **#E6EEFA** en ambos paneles (primary.light). Valor en **#1F2933**, label gris de adorno (\~#9499A4 ≈ #9CA3AF), divisor/borde hairline correcto. - **Iconos:** Único residuo real — el indicador de delta usa un **chevron fino** (`^`/`v`) en la app, mientras Figma dibuja un **triángulo relleno ▲/▼**. Diferencia de glifo, no de color. Culpable probable: el icono elegido en `src/ui/**` para el componente KPI (revisar el render del delta/trend). - **Estados:** success verde ≈ **#17BB84** (app) vs **#10B981** (Figma) — mismo rol, tono casi idéntico; danger **#EF4444** con tinte \*-soft en el contenedor de icono coincide. - **Layout:** Estructura correcta — label + icono tintado arriba, valor grande tabular, delta con flecha+% abajo. Tarjeta plana con hairline. - **Salvedad:** las etiquetas/valores de ejemplo distintos por variante (Órdenes hoy / Ingresos del mes / Devoluciones) son decisión del showcase, no defecto. | | #### List Row [Section titled “List Row”](#list-row) **Código:** `src/ui/molecules/ListRow/index.tsx` · **Figma:** [`41:60`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=41-60) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma List Row](/design/parity/figma/molecules/figma-list-row.png)](/design/parity/figma/molecules/figma-list-row.png "figma List Row — abrir a tamaño completo") | [![app List Row](/design/parity/history/2026-08-07/screenshots/playground/list-row.png)](/design/parity/history/2026-08-07/screenshots/playground/list-row.png "app List Row — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación)*- **Color:** Contenedor de icono con tinte `#E6EEFA` y glifo `#0052CC` medidos idénticos en ambos paneles. Badge Info con fill `#EFF6FF` (rol info) + texto azul, igual que Figma. - **Iconos:** Residuo del chevron **cerrado** — ahora se pinta en azul de marca (`primary.main`, `src/ui/molecules/ListRow/index.tsx:145`). En el showcase el chevron es de trazo algo más fino que el de Figma (grosor de icono), diferencia visual menor. - **Estados:** Pressed = tinte `#E6EEFA`; Selected = `#E1E9F6` (tinte info algo más profundo para diferenciar del pressed) — patrón de selección con tinte redondeado correcto. Los estados `pressed`/`selected` extra y las etiquetas de sección son decisión del showcase. - **Layout:** Estructura icono-tintado + título/subtítulo + slot de trailing (chevron / valor / badge) consistente con el diseño. Las filas del showcase van sobre el fondo `#F5F7FA` en vez de tarjetas blancas con hairline; es el encuadre del playground, no del componente. - **Tipografía:** Título en oscuro, secundario en gris y valor `$1,234.00` en oscuro — coinciden. | | #### Product Card [Section titled “Product Card”](#product-card) **Código:** `src/ui/molecules/ProductCard/index.tsx` · **Figma:** [`44:50`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=44-50) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Product Card](/design/parity/figma/molecules/figma-product-card.png)](/design/parity/figma/molecules/figma-product-card.png "figma Product Card — abrir a tamaño completo") | [![app Product Card](/design/parity/history/2026-08-07/screenshots/playground/product-card.png)](/design/parity/history/2026-08-07/screenshots/playground/product-card.png "app Product Card — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** *(re-auditado tras remediación — commit `d515d29`)*- **Color:** Precio azul de marca correcto — medido **#0052CC** en ambos paneles. Título ≈#1F2933 y hairline #E5E7EB confirmados. Residuo de precio **cerrado**. - **Estados (stock disponible):** Residuo **cerrado**. “24 en stock” ahora se pinta como **StatusBadge success** (pill verde soft), en vez de caption gris plano (`src/ui/molecules/ProductCard/index.tsx:106-118`, ``), reproduciendo la pill verde de Figma. Stock bajo cae a la variante `warning` (ámbar). - **Estados (Agotado):** Residuos remanentes (menores, no bloqueantes): la app resuelve “sin stock” con StatusBadge danger en la **esquina superior-izquierda sobre la imagen** (`index.tsx:85-89`) más **card a opacity 0.6** (`index.tsx:64`), mientras Figma pone la pill roja **debajo del precio** a opacidad plena. La atenuación global lava el precio azul. Diferencia de tratamiento/posición, no de token; se deja como residuo aceptado. - **Bordes/Layout:** Tarjeta plana + hairline, placeholder 1:1 y título coinciden. El borde azul del estado “Seleccionado” es un estado extra del showcase (no en Figma), correcto. - **Iconos:** Botón “+” relleno sólido azul de marca `#0052CC` correcto (feature POS `onAdd`; Figma no lo dibuja); glifo placeholder `#9CA3AF` es decisión del showcase. - **Residuo remanente (aceptado):** posición de la pill Agotado (sobre imagen vs bajo precio) y opacity 0.6 de la tarjeta agotada. | | #### Profile Card [Section titled “Profile Card”](#profile-card) **Código:** `src/ui/molecules/ProfileCard/index.tsx` | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Profile Card](/design/parity/figma/molecules/figma-profile-card-context.png)](/design/parity/figma/molecules/figma-profile-card-context.png "figma Profile Card — abrir a tamaño completo") | [![app Profile Card](/design/parity/history/2026-08-07/screenshots/playground/profile-card.png)](/design/parity/history/2026-08-07/screenshots/playground/profile-card.png "app Profile Card — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Avatar con tinte `#E6EEFA` e iniciales `#0052CC` (medido) — patrón avatar tinte+iniciales canónico. Nombre `#1F2933`, rol gris \~`#6B7280` (medido). Tarjeta default blanca `#FFFFFF`. - **Bordes:** Estado `selected` con borde azul de marca `#0052CC` 1px (medido), sin relleno; default plano con hairline claro. Correcto. - **Iconos:** N/A — el avatar usa iniciales “AV”, no icono. - **Layout:** Avatar circular a la izquierda + bloque nombre/rol; dos estados (default / selected) en el showcase. Estructura coincide con el patrón de selección con borde. - **Estados:** default y selected verificados; el borde marca la selección (no tinte de fondo), consistente con list-row/tarjeta seleccionable.*Salvedad: la referencia Figma del par es la pantalla “Selecciona tu tienda” (tarjetas de tienda); el panel APP es el showcase del molecule ProfileCard con datos de ejemplo “Ana Vega / Gerente de tienda” — el contenido de ejemplo es decisión del showcase, no defecto. Sin residuo real del componente.* | | #### Provider Tile [Section titled “Provider Tile”](#provider-tile) **Código:** `src/ui/molecules/ProviderTile/index.tsx` · **Figma:** [`48:63`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=48-63) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Provider Tile](/design/parity/figma/molecules/figma-provider-tile.png)](/design/parity/figma/molecules/figma-provider-tile.png "figma Provider Tile — abrir a tamaño completo") | [![app Provider Tile](/design/parity/history/2026-08-07/screenshots/playground/provider-tile.png)](/design/parity/history/2026-08-07/screenshots/playground/provider-tile.png "app Provider Tile — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Estado Activo medido en app — borde `#004FCB ≈ #0052CC` (primary.main, 2px sólido) y fondo `#E6EEFA` (palette.primary.light); Figma activo mide borde `#0043C8` / fondo `#E6EEFA`. Glifo y label recoloreados a primary.main. Default = superficie blanca + hairline `divider`. Todo cableado por rol en `src/ui/molecules/ProviderTile/index.tsx`. - **Layout:** Estructura idéntica a Figma — contenedor de icono redondeado tintado (56×56, radius.md) apilado sobre label centrado; tile 112px min, radius.lg. No es una fila-badge horizontal. - **Estados:** Default (tinte neutral `surface.variant` en el contenedor, glifo `text.secondary`) y Activo (tinte primary.light + anillo 2px + recoloreado a primary) reproducen las variantes Figma “Default”/“Activo”. - **Iconos:** El showcase usa un glifo tipo tarjeta mientras Figma muestra un teléfono — decisión de icono de ejemplo del showcase, no defecto (`icon` es prop). - **Bordes:** Figma dibuja el tile Default con una sombra suave elevada; la app lo aplana a blanco + hairline, coherente con la remediación “tarjetas planas+hairline” — intención de DS, no residuo. | | #### Quantity Stepper [Section titled “Quantity Stepper”](#quantity-stepper) **Código:** `src/ui/molecules/QuantityStepper/index.tsx` · **Figma:** [`44:69`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=44-69) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Quantity Stepper](/design/parity/figma/molecules/figma-quantity-stepper.png)](/design/parity/figma/molecules/figma-quantity-stepper.png "figma Quantity Stepper — abrir a tamaño completo") | [![app Quantity Stepper](/design/parity/history/2026-08-07/screenshots/playground/quantity-stepper.png)](/design/parity/history/2026-08-07/screenshots/playground/quantity-stepper.png "app Quantity Stepper — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Fondo de botones tintado `#E6EEFA` (palette.primary.light) e iconos −/+ en azul de marca `#0052CC` (primary.main), medidos en el panel APP; el dígito central va en tinta oscura. Coincide con el diseño. El origen `src/ui/molecules/QuantityStepper/index.tsx` cablea `primary.light` / `primary.main` correctamente. - **Estados:** Fila “editable” con ambos botones tintados y “en mínimo” con el − deshabilitado en gris (`action.disabledBackground` `#E5E7EB` + `text.disabled` `#9CA3AF`) y el + activo azul; replica exactamente las dos filas de Figma (habilitado / − deshabilitado en gris). - **Bordes:** Botones cuadrado-redondeado usando `theme.shape.borderRadius` (canónico). Coincide. - **Iconos:** −/+ con trazo azul de marca; sin defecto. - **Layout:** Estructura \[−] \[valor] \[+] con separación uniforme; igual que Figma. - **Nota de medición:** La fuente Figma (176 px) diluye el azul al reescalar (lecturas \~`#145FD0`/`#F0F4F8`), pero es artefacto de resolución, no discrepancia; el APP usa los tokens canónicos correctos. | | #### Summary Row [Section titled “Summary Row”](#summary-row) **Código:** `src/ui/molecules/SummaryRow/index.tsx` · **Figma:** [`43:66`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=43-66) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Summary Row](/design/parity/figma/molecules/figma-summary-row.png)](/design/parity/figma/molecules/figma-summary-row.png "figma Summary Row — abrir a tamaño completo") | [![app Summary Row](/design/parity/history/2026-08-07/screenshots/playground/summary-row.png)](/design/parity/history/2026-08-07/screenshots/playground/summary-row.png "app Summary Row — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación — commit `d515d29`)*- **Color:** El TOTAL standalone mide exactamente **#0052CC** en ambos paneles (azul de marca canónico). El acento se aplica vía `tone="primary"` en el showcase — **no** vía `emphasis`. Decisión de API deliberada: `emphasis` controla solo peso/tamaño; el color lo decide `tone` (`default` = #1F2933, `success` = #10B981, `primary` = #0052CC). Así el mismo componente pinta el TOTAL en azul aquí pero **oscuro** en el POS Cash Tender (cuyo frame lo quiere en tinta oscura), sin regresión cruzada. - **Tipografía:** Coincide — TOTAL en negrita/mayor (h5) y label en subtitle1; Subtotal en body2/subtitle2. Jerarquía correcta. - **Bordes:** Coincide — divisor hairline #E5E7EB sobre la fila del total. - **Layout:** Coincide — label a la izquierda, valor a la derecha, alineación baseline, `space-between`. - **Estados (showcase):** La fila “Descuento” en verde #10B981 (`tone="success"`) y el ejemplo negativo −$50.00 son ejemplos del showcase (no en el panel Figma); no son defecto. | | ### Organismos [Section titled “Organismos”](#organismos) Código en `src/ui/organisms/**`. #### Breadcrumbs [Section titled “Breadcrumbs”](#breadcrumbs) **Código:** `src/ui/organisms/Breadcrumbs/Breadcrumbs.tsx` · **Figma:** [`94:21`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=94-21) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Breadcrumbs](/design/parity/figma/organisms/figma-breadcrumbs.png)](/design/parity/figma/organisms/figma-breadcrumbs.png "figma Breadcrumbs — abrir a tamaño completo") | [![app Breadcrumbs](/design/parity/history/2026-08-07/screenshots/playground/breadcrumbs.png)](/design/parity/history/2026-08-07/screenshots/playground/breadcrumbs.png "app Breadcrumbs — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación)*- **Color:** Enlaces intermedios `#6B7280` (medido) = Figma `#6B7280` ✓. Último nivel resaltado `#1F2933` = Figma ✓. Fondo showcase `#F5F7FA` es la superficie del /playground, no defecto. - **Iconos:** Residuo del chevron **cerrado** — el separador ahora fija `color: 'text.disabled'` (#9CA3AF, gris de adorno) en `src/ui/organisms/Breadcrumbs/Breadcrumbs.tsx:37`, igual que Figma. - **Tipografía:** Último nivel en peso mayor (bold) tal como Figma; enlaces navegables en regular. Coincide. - **Layout/Estados:** Patrón trail con separador chevron, último nivel resaltado y `aria-current="page"` correcto. El showcase muestra dos ejemplos (ruta corta + ruta profunda), decisión del showcase. | | #### Bulk Action Bar [Section titled “Bulk Action Bar”](#bulk-action-bar) **Código:** `src/ui/organisms/DataTable/BulkActionBar.tsx` · **Figma:** [`99:122`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=99-122) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Bulk Action Bar](/design/parity/figma/organisms/figma-bulk-action-bar.png)](/design/parity/figma/organisms/figma-bulk-action-bar.png "figma Bulk Action Bar — abrir a tamaño completo") | [![app Bulk Action Bar](/design/parity/history/2026-08-07/screenshots/playground/bulk-action-bar.png)](/design/parity/history/2026-08-07/screenshots/playground/bulk-action-bar.png "app Bulk Action Bar — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación — commit `d515d29`)*- **Color:** Tint de fondo idéntico — Figma #E6EEFA vs App #E6EEFA (medido; `theme.palette.action.selected` en `BulkActionBar.tsx:54`). Editar/Exportar en primary #0052CC, Eliminar en rojo. Coincide. - **Tipografía:** El contador “3 seleccionados” ya se pinta en **azul de marca + bold** (`primary.main`, `BulkActionBar.tsx:72`), igual que Figma. Residuo cerrado. - **Bordes:** Residuo cerrado — se añadió `border: 1px solid primary.main` (#0052CC) alrededor de la barra tonal (`BulkActionBar.tsx`), reproduciendo el contorno azul de Figma en los cuatro cantos. - **Iconos:** El elemento líder difiere por diseño: Figma muestra un **checkbox relleno azul con check** (indicador de selección); la App muestra un **icono X** (afordancia `onClear`/Xmark, decisión de interacción del componente, no defecto de estilo). - **Layout:** Estructura coincide — líder + contador (flex:1) + acciones a la derecha, radio redondeado. La segunda fila (“selección amplia”, 12 seleccionados) es variante de demostración del showcase.Neto: paridad de color, borde y estructura; el único delta es la afordancia líder (X de limpiar vs checkbox), decisión de interacción deliberada. | | #### Chart — Bar [Section titled “Chart — Bar”](#chart--bar) **Código:** `src/ui/organisms/charts/*` · **Figma:** [`101:101`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=101-101) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Chart — Bar](/design/parity/figma/organisms/figma-chart-bar.png)](/design/parity/figma/organisms/figma-chart-bar.png "figma Chart — Bar — abrir a tamaño completo") | [![app Chart — Bar](/design/parity/history/2026-08-07/screenshots/playground/chart-bar.png)](/design/parity/history/2026-08-07/screenshots/playground/chart-bar.png "app Chart — Bar — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Barras medidas en `#0052CC` (azul de marca) en Figma y app; el swatch de la leyenda también `#0052CC`. Fondo de card blanco. Serie primaria correcta — sin residuo de color. - **Bordes:** Único residuo real del componente: las barras de la app salen con esquinas superiores **rectas**, mientras Figma las dibuja con **radio superior redondeado** (\~8-10px). `BarChart/index.tsx` usa `@mui/x-charts` sin pasar `borderRadius` a la serie; agregar `borderRadius` al `` cerraría la brecha. - **Layout:** El panel Figma es un recorte mínimo (solo título + 5 barras). La app muestra ejes X/Y, gridlines horizontales, ticks y leyenda “Ventas” — pero el propio frame del showcase se rotula “BARRAS CON EJES Y LEYENDA”, así que esa estructura extra es decisión del showcase, no defecto. - **Tipografía:** Título “Por método de pago” en gris oscuro `#1F2933`, labels de eje en gris tenue — consistente con los tokens de texto. - **Estados:** No aplican estados interactivos en esta captura. | | #### Chart — Donut [Section titled “Chart — Donut”](#chart--donut) **Código:** `src/ui/organisms/charts/*` · **Figma:** [`101:114`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=101-114) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Chart — Donut](/design/parity/figma/organisms/figma-chart-donut.png)](/design/parity/figma/organisms/figma-chart-donut.png "figma Chart — Donut — abrir a tamaño completo") | [![app Chart — Donut](/design/parity/history/2026-08-07/screenshots/playground/chart-donut.png)](/design/parity/history/2026-08-07/screenshots/playground/chart-donut.png "app Chart — Donut — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Serie medida idéntica en ambos paneles — Bebidas `#0052CC` (primary de marca), Abarrotes `#3B82F6` (info/serie2), Botanas `#10B981`, Otros `#F59E0B`. Paridad exacta en las 4 cuñas y en el mismo orden. - **Tipografía:** Título “Categorías” en gris tinta oscuro (#1F2933) y labels de leyenda en gris; total central “9,500” en bold + “Total” en gris secundario. Consistente con la escala tipográfica. - **Layout:** Dona (donut) con leyenda vertical a la derecha, marcador circular + etiqueta por serie; misma relación de grosor de anillo. Estructura equivalente. - **Iconos:** Marcadores de leyenda como puntos rellenos del color de serie — coincide. - **Estados:** N/A (chart estático). - **Salvedad (no defecto):** El **total central “9,500”** es la variante del showcase (su título es “Dona con leyenda y total central”); Figma muestra la dona sin total central. Decisión del showcase. - **Residuo menor (cosmético):** La app renderiza `padAngle` (pequeño gap entre arcos) mientras que en Figma los segmentos se tocan sin separación. Diferencia mínima de estilo del donut; no altera color, serie ni jerarquía. | | #### Chart — Line [Section titled “Chart — Line”](#chart--line) **Código:** `src/ui/organisms/charts/*` · **Figma:** [`101:91`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=101-91) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Chart — Line](/design/parity/figma/organisms/figma-chart-line.png)](/design/parity/figma/organisms/figma-chart-line.png "figma Chart — Line — abrir a tamaño completo") | [![app Chart — Line](/design/parity/history/2026-08-07/screenshots/playground/chart-line.png)](/design/parity/history/2026-08-07/screenshots/playground/chart-line.png "app Chart — Line — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Trazo de la serie medido = **#0052CC** en ambos paneles (marca), coincide. Relleno de área en Figma medido = **#E6EEFA** (primary.light) ✅ como tinte, pero **ausente en la app**. - **Layout:** Único residuo real — Figma muestra la línea sobre un **área rellena** (#E6EEFA bajo la curva); la app renderiza **línea pura sin relleno**. `src/ui/organisms/charts/LineChart/index.tsx` no pasa `area: true` a la serie de `@mui/x-charts`. Fix: activar el relleno con el tinte de marca (alpha de primary.light). - **Iconos/Ejes:** Ejes (8–16), gridlines horizontales (0–80) y leyenda “Ventas” son de la **variante del showcase** (“línea con ejes, gridlines y leyenda”), no defecto vs Figma (que es una miniatura sin ejes). - **Estados:** Variantes Vacío (“Sin datos para mostrar”) y Cargando (skeleton #F0F4F8) presentes y correctas, sin equivalente en la miniatura de Figma. - **Tipografía/Bordes:** Título “Ventas por hora” #1F2933 y tarjeta plana + hairline coinciden; eje/grid usan text.secondary/#E5E7EB (tokens), OK. | | #### Command Palette [Section titled “Command Palette”](#command-palette) **Código:** `src/ui/organisms/CommandPalette/index.tsx` · **Figma:** [`113:651`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=113-651) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Command Palette](/design/parity/figma/organisms/figma-command-palette-context.png)](/design/parity/figma/organisms/figma-command-palette-context.png "figma Command Palette — abrir a tamaño completo") | [![app Command Palette](/design/parity/history/2026-08-07/screenshots/playground/command-palette.png)](/design/parity/history/2026-08-07/screenshots/playground/command-palette.png "app Command Palette — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** El tint de selección coincide — app **#E5EDFA** ≈ `palette.primary.light` **#E6EEFA** (Figma **#E6EEFA**). Scrim, tarjeta blanca plana + hairline `divider` y campo de búsqueda blanco con foco azul: OK. - **Iconos:** Residuo real — en Figma cada icono de fila va dentro de un contenedor redondeado tintado **#F0F4F8**; en la app el icono queda desnudo sobre blanco (**#FFFFFF** medido alrededor). Falta el chip de icono. Culpable: `src/ui/organisms/CommandPalette/index.tsx` L231–235 (`ListItemIcon` + `Icon` sin wrapper tintado). El adorno gris #9CA3AF sí está, pero sin el contenedor. - **Tipografía:** Los encabezados de sección difieren — Figma usa estilo *overline* (MAYÚSCULAS + tracking, \~11px, gris **#8C9097**: “PRODUCTOS”); la app los muestra en Title Case y más grandes (gris **#7A7B7D**: “Productos”). Causa: `ListSubheader` sin override (no hay `MuiListSubheader` en `theme.ts`; usa el default MUI \~16px sin `text-transform`). L217. - **Layout:** Filas de una sola línea en la app vs. dos líneas en Figma (título + metadata: “SKU 750105530 · $18.00”, “24 Oct · $406.00 · Pagado”). El tipo `CommandItem` no tiene campo secundario y `ListItemText` sólo pinta `primary` (L236) — limitación del componente, no sólo dato del showcase. - **Estados:** Falta el chip **ESC** dentro del campo de búsqueda (Figma lo muestra a la derecha); el `SearchField` no lleva ese `endAdornment` (L196–203). Afordancia menor. - **Salvedad:** El icono de producto (etiqueta en app vs. caja de regalo en Figma) y la consulta escrita “coca” vs. placeholder son decisiones del showcase, no defectos. | | #### Confirm Dialog [Section titled “Confirm Dialog”](#confirm-dialog) **Código:** `src/ui/organisms/ConfirmDialog/index.tsx` · **Figma:** [`102:122`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=102-122) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Confirm Dialog](/design/parity/figma/organisms/figma-confirm-dialog.png)](/design/parity/figma/organisms/figma-confirm-dialog.png "figma Confirm Dialog — abrir a tamaño completo") | [![app Confirm Dialog](/design/parity/history/2026-08-07/screenshots/playground/confirm-dialog.png)](/design/parity/history/2026-08-07/screenshots/playground/confirm-dialog.png "app Confirm Dialog — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Icono de peligro con contenedor tintado red-soft `#FEF2F2` y triángulo `#EF4444` medidos idénticos en ambos paneles. Botón Eliminar en relleno rojo sólido `#EF4444` (app y Figma iguales). Botón Cancelar outlined con azul de marca (`#0556CD` app / `#0153CC` Figma ≈ `#0052CC`). Título oscuro y cuerpo gris coinciden. - **Iconos:** Patrón de contenedor tintado + glifo relleno (triángulo de advertencia) correcto y consistente con el resto de organismos remediados. - **Bordes:** Tarjeta plana blanca con esquinas redondeadas; Cancelar con borde 1px sólido azul, Eliminar sin borde. Coincide. - **Layout:** Dos botones a ancho completo lado a lado (\~50/50), Cancelar izquierda / acción destructiva derecha. Estructura idéntica. - **Estados:** Botón destructivo en rol danger, secundario outlined — patrón correcto. - **Salvedad:** Los dos paneles están a escalas muy distintas (Figma 448×308 recorte; app dialog pequeño centrado sobre overlay). La sombra sutil del modal en la app es propia del componente overlay y no es defecto. Sin residuo real que reportar. | | #### Data Table [Section titled “Data Table”](#data-table) **Código:** `src/ui/organisms/DataTable/DataTable.tsx` · **Figma:** [`95:59`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=95-59) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Data Table](/design/parity/figma/organisms/figma-data-table.png)](/design/parity/figma/organisms/figma-data-table.png "figma Data Table — abrir a tamaño completo") | [![app Data Table](/design/parity/history/2026-08-07/screenshots/playground/data-table.png)](/design/parity/history/2026-08-07/screenshots/playground/data-table.png "app Data Table — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Fila seleccionada con tinte `#E6EEFA` medido en Figma vs `#E5EDFA` en app (palette.primary.light) — coincide. Divisores `#E5E7EB`, textos muted grises — OK. - **Estados:** StatusBadges renderizados como píldoras con tinte-soft: Activo `#ECFDF5` (Figma `#ECFCF5`), Agotado `#FEF2F2`, Bajo amarillo-soft — coinciden en patrón y tono. Checkbox seleccionado = relleno sólido `#0052CC` con check blanco y esquinas redondeadas. - **Layout:** Estructura de columnas (avatar+iniciales / Producto / SKU / Categoría / Precio / Stock / Estado / acción), header hairline y avatar con tinte+iniciales azules coinciden. Estados extra del panel app (Cargando/skeleton `#F0F4F8`, Vacío con contenedor de icono tintado, Filas seleccionadas + BulkActionBar) son composiciones del showcase, no en el frame de Figma. - **Iconos:** Salvedad de showcase — el icono de editar se pinta como `IconButton variant="outlined"` (caja redondeada con borde `#E5E7EB`) en cada fila, mientras Figma muestra el glyph sin contenedor (la caja sólo aparece en la fila seleccionada/hover). Es decisión del showcase (`DataTableShowcase.tsx:150`), no defecto del componente DataTable. Sin residuo real del organismo. | | #### Dialog / Modal [Section titled “Dialog / Modal”](#dialog--modal) **Código:** `src/ui/organisms/Dialog/index.tsx` · **Figma:** [`102:91`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=102-91) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Dialog / Modal](/design/parity/figma/organisms/figma-dialog.png)](/design/parity/figma/organisms/figma-dialog.png "figma Dialog / Modal — abrir a tamaño completo") | [![app Dialog / Modal](/design/parity/history/2026-08-07/screenshots/playground/dialog.png)](/design/parity/history/2026-08-07/screenshots/playground/dialog.png "app Dialog / Modal — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Confirmado tras remediación — Guardar cambios `#0052CC` sólido + texto blanco; Cancelar outlined con borde y texto azul de marca sobre blanco; título `#1F2933`; ✕ gris (`text.secondary`); scrim gris. Coincide. - **Bordes:** Campos con relleno blanco + hairline `#E5E7EB` redondeado, alto \~40 con label estático arriba. Coincide. - **Layout:** Residuo real — Figma dibuja un **divisor hairline bajo el header** y otro **sobre el footer**; la app los omite (header/body/footer fluyen sin líneas). Culpable: `src/ui/organisms/Dialog/index.tsx` usa `DialogTitle`/`DialogActions` sin `borderBottom`/`borderTop` (`dividers`). - **Estados:** En Figma el 2º campo aparece **enfocado** (aro azul); en la app está sin foco con valor “Texto activo”. Es decisión de estado del showcase (`DialogShowcase.tsx`), **no defecto**. - **Iconos:** ✕ de cierre con `size={18}`, color gris, alineado a la derecha del título. Coincide. - **Tipografía:** Título bold, labels bold, helper y placeholder en gris. Coincide.Estructura, tokens y botones ya conformes; único pendiente accionable = añadir los divisores de header/footer. | | #### Filter Bar [Section titled “Filter Bar”](#filter-bar) **Código:** `src/ui/organisms/DataTable/FilterBar.tsx` · **Figma:** [`99:73`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=99-73) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Filter Bar](/design/parity/figma/organisms/figma-filter-bar.png)](/design/parity/figma/organisms/figma-filter-bar.png "figma Filter Bar — abrir a tamaño completo") | [![app Filter Bar](/design/parity/history/2026-08-07/screenshots/playground/filter-bar.png)](/design/parity/history/2026-08-07/screenshots/playground/filter-bar.png "app Filter Bar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Trigger “Filtros” medido en #0052CC (borde + texto + icono) en ambos paneles; enlace “Quitar todos” #0052CC. Coincide con azul de marca. - **Bordes:** Campo de búsqueda blanco #FFFFFF con hairline y botón outlined 1px sólido; dropdown “Selecciona una opción” con hairline. Alineado con canon. - **Estados:** Badge contador = círculo relleno SÓLIDO #0052CC + “2” blanco (excepción correcta de relleno para contadores). Chips de “Filtros aplicados” con outline hairline y close en gris adorno (\~#9CA3AF). - **Iconos:** El showcase añade icono de hamburguesa antes de “Filtros” (Figma sin icono) — decisión del showcase, no defecto. - **Layout:** Figma muestra segmented (Todos/Activos/Bajo stock); el showcase lo sustituye por dos filas de búsqueda + fila de chips “Filtros aplicados” — contenido de showcase, no residuo del componente. - **Color (nota, no defecto):** El campo de búsqueda en Figma trae relleno tintado #F0F4F8; la app lo pinta blanco #FFFFFF, consistente con la decisión canónica de campos blancos+hairline (fix verificado). | | #### Page Header [Section titled “Page Header”](#page-header) **Código:** `src/ui/organisms/PageHeader/PageHeader.tsx` · **Figma:** [`94:31`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=94-31) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Page Header](/design/parity/figma/organisms/figma-page-header.png)](/design/parity/figma/organisms/figma-page-header.png "figma Page Header — abrir a tamaño completo") | [![app Page Header](/design/parity/history/2026-08-07/screenshots/playground/page-header.png)](/design/parity/history/2026-08-07/screenshots/playground/page-header.png "app Page Header — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Botón primario medido **#0052CC** en Figma y app; fondo de sección #F5F7FA / #F5F5F5 (escalas distintas, equivalente); subtítulo gris \~#868C98 acorde a #6B7280. Azul de marca ya remediado y verificado. - **Tipografía:** Título en peso alto y color oscuro (navy \~#1F2933), subtítulo secundario en gris, eyebrow en gris tenue mayúsculas — jerarquía idéntica. - **Layout:** Patrón título + subtítulo + acción primaria alineada a la derecha coincide. El panel APP muestra además las variantes *solo título* y *con breadcrumbs* — decisión del showcase de /playground, no defecto del componente. - **Bordes:** Botón con esquinas redondeadas y texto blanco `+ Nuevo producto` igual en ambos. - **Iconos:** Icono `+` de adorno dentro del botón presente en ambos paneles. - **Estados:** Sin residuo medible. Coincide. | | #### Pagination [Section titled “Pagination”](#pagination) **Código:** `src/ui/organisms/DataTable/Pagination.tsx` · **Figma:** [`94:43`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=94-43) | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Pagination](/design/parity/figma/organisms/figma-pagination.png)](/design/parity/figma/organisms/figma-pagination.png "figma Pagination — abrir a tamaño completo") | [![app Pagination](/design/parity/history/2026-08-07/screenshots/playground/pagination.png)](/design/parity/history/2026-08-07/screenshots/playground/pagination.png "app Pagination — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Página activa con relleno SÓLIDO primary — medido **#0052CC** tanto en app (`205,134`) como en Figma (`65,17`), con texto blanco. Es la excepción al patrón de tinte y está bien aplicada. - **Estados:** Tiles inactivos planos blancos con hairline (#E5E7EB) y numeral oscuro; activo destaca por el relleno azul. Correcto. - **Bordes:** Esquinas redondeadas en los tiles coinciden; chevrons ‹ › como botones ghost sin relleno. - **Layout:** El showcase muestra 3 variantes (página actual / primera / última) — decisión del showcase, no defecto. Figma solo muestra el patrón base. - **Tipografía:** Numerales centrados, peso consistente entre ambos. - Sin residuo real del componente. | | #### POS — Approval Modal [Section titled “POS — Approval Modal”](#pos--approval-modal) **Código:** `src/ui/organisms/pos/ApprovalModal/index.tsx` · **Figma:** [`104:161`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=104-161) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Approval Modal](/design/parity/figma/organisms/figma-pos-approval-modal.png)](/design/parity/figma/organisms/figma-pos-approval-modal.png "figma POS — Approval Modal — abrir a tamaño completo") | [![app POS — Approval Modal](/design/parity/history/2026-08-07/screenshots/playground/pos-approval-modal.png)](/design/parity/history/2026-08-07/screenshots/playground/pos-approval-modal.png "app POS — Approval Modal — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores**- **Color:** Marca #0052CC verificado (PIN dots en Figma, glifo de backspace en ambos). Lock tile warning-soft `#FFFBEB` + glifo `#F59E0B` coinciden exacto. Error `#EF4444` correcto. - **Color (residuo):** El tile de la tecla borrar usa `theme.palette.action.hover` → renderiza `#F5F5F5`, cuando el diseño pide surface-variant/skeleton `#F0F4F8` (ligero tinte azulado). Diferencia casi imperceptible pero real. Culpable: `src/ui/organisms/pos/ApprovalModal/index.tsx` L253 (`backgroundColor: theme.palette.action.hover`) — el propio comentario dice `--color-surface-variant`; conviene cablearlo al token skeleton/surface-variant. - **Layout:** Figma muestra el par Cancelar (outlined) + Autorizar (filled); la app renderiza solo el link de texto Cancelar. **No es defecto**: el organismo soporta ese par vía `requireConfirm`; el showcase elige a propósito la variante auto-submit shipped (`ApprovalModalShowcase.tsx` L43-49, sin `requireConfirm`). - **Iconos:** Lock relleno naranja y glifo backspace primary coinciden. - **Estados:** Default (dots vacíos, sin PIN tecleado) y PIN incorrecto (dots rojos + mensaje) correctos; el default de Figma con 2 dots llenos es solo un estado de entrada distinto del showcase. - **Tipografía/Bordes:** Título, subtítulo y hairline de campos sin discrepancias medibles. | | #### POS — Cash Tender [Section titled “POS — Cash Tender”](#pos--cash-tender) **Código:** `src/ui/organisms/pos/CashTender/index.tsx` · **Figma:** [`105:200`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=105-200) | Diseño Figma | Captura de la app | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Cash Tender](/design/parity/figma/organisms/figma-pos-cash-tender.png)](/design/parity/figma/organisms/figma-pos-cash-tender.png "figma POS — Cash Tender — abrir a tamaño completo") | [![app POS — Cash Tender](/design/parity/history/2026-08-07/screenshots/playground/pos-cash-tender.png)](/design/parity/history/2026-08-07/screenshots/playground/pos-cash-tender.png "app POS — Cash Tender — abrir a tamaño completo") | | Notas | | | **✅ Coincide** *(re-auditado tras remediación — commit `d515d29`)*- **Color:** Residuo del **Total** cerrado. El monto “Total $406.00” ahora se pinta en **tinta oscura bold (#1F2933)**, no azul — se desacopló el color de `emphasis` en `SummaryRow` (color lo decide `tone`, no el énfasis), así el POS conserva el total oscuro que muestra su frame mientras el SummaryRow standalone sigue en azul vía `tone="primary"`. - **Color:** Callout **Cambio** con tinte de éxito correcto — app `#ECFDF5` vs Figma `#ECFCF5` (`success.light`), label + monto en `success.main` (`CashTender/index.tsx`). Coincide. - **Color:** Fondo de los chips de monto `#F5F5F5` coincide con Figma. - **Estados:** El botón `Confirmar` disabled = `#E5E7EB` + texto `#9CA3AF` (input vacío en el showcase); al habilitarse usa `variant="filled"` (azul sólido, como Figma). Diferencia de estado, no defecto. Ningún chip aparece seleccionado (input vacío) — no evaluable en esta captura. - **Bordes/Iconos:** Input blanco + hairline + label estático arriba; ✕ de cierre gris en ambos. Coincide. - **Datos del showcase (no defectos):** título “Pago en efectivo”/label “Recibido” vs. “Efectivo”/“Monto recibido” del Figma, y chips `$406/$500/$1000` — copy y props por defecto del showcase. | | #### POS — Discount Sheet [Section titled “POS — Discount Sheet”](#pos--discount-sheet) **Código:** `src/ui/organisms/pos/DiscountSheet/index.tsx` · **Figma:** [`104:214`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=104-214) | Diseño Figma | Captura de la app | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Discount Sheet](/design/parity/figma/organisms/figma-pos-discount-sheet.png)](/design/parity/figma/organisms/figma-pos-discount-sheet.png "figma POS — Discount Sheet — abrir a tamaño completo") | [![app POS — Discount Sheet](/design/parity/history/2026-08-07/screenshots/playground/pos-discount-sheet.png)](/design/parity/history/2026-08-07/screenshots/playground/pos-discount-sheet.png "app POS — Discount Sheet — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** *(re-auditado tras remediación)*- **Color:** Verificado y coincide. Thumb del segmented blanco `#FFFFFF` sobre track `#F5F5F5`; banner de total con tinte verde `#ECFDF5` + cifra esmeralda (residuo del highlight success **cerrado**); botón disabled `#E5E7EB` + texto `#9CA3AF`; texto principal `#2D363F`. Todos los fixes de la fase 2 se sostienen. - **Layout (feature-gap):** Falta el caption de autorización que Figma muestra bajo el campo (`Máx. 20% sin autorización`). El showcase no lo renderiza — verificar si el organism expone esa prop de límite/ayuda. - **Layout (feature-gap):** Falta el selector de **Motivo** (Figma: dropdown `Motivo: Cliente frecuente`). La app omite el campo de razón del descuento; es un elemento funcional del sheet, no solo texto de ejemplo. - **Estados:** El botón `Aplicar descuento` aparece en estado **disabled** (campo vacío en el showcase), por lo que no se compara directo el azul sólido del CTA de Figma; la geometría y el tono disabled sí coinciden. - **Bordes/Iconos:** Coinciden — card plana con hairline, campo blanco con borde suave, icono de cierre `×` en gris de adorno. - **Tipografía:** Coincide. Diferencias de etiqueta (`%/Monto/Cupón` vs `Porcentaje/Monto fijo/Cupón`, `Nuevo total` vs `Total con descuento`) son decisión del showcase, no defecto.Residuos remanentes = **feature-gaps** (selector de Motivo + caption de autorización), no defectos de token/estilo; se difieren al flujo de checkout. | | #### POS — Result Modal [Section titled “POS — Result Modal”](#pos--result-modal) **Código:** `src/ui/organisms/pos/ResultModal/index.tsx` | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Result Modal](/design/parity/figma/organisms/figma-pos-result-modal-context.png)](/design/parity/figma/organisms/figma-pos-result-modal-context.png "figma POS — Result Modal — abrir a tamaño completo") | [![app POS — Result Modal](/design/parity/history/2026-08-07/screenshots/playground/pos-result-modal.png)](/design/parity/history/2026-08-07/screenshots/playground/pos-result-modal.png "app POS — Result Modal — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Azul de marca del CTA “Nueva venta” medido **#0052CC** en Figma y app (idéntico). Check de éxito **#10B981** en ambos. Halo/icono de error usan rol error **#EF4444 + #FDE8E8** (tinte \*-soft), correcto. - **Tipografía:** Título “¡Venta completada!” #1F2933 en los dos; subtítulo folio en gris secundario; pesos y jerarquía coinciden. - **Espaciado:** Estructura vertical (check → título → folio → bloque resumen → fila de acciones → CTA) idéntica en proporción. - **Bordes:** Tarjeta modal plana con esquinas redondeadas; botones outlined con borde 1px sólido azul; bloque-resumen tintado (#F7F9FC Figma / #F5F7FA app, equivalentes). - **Iconos:** Check relleno blanco sobre círculo verde igual; en la variante error, `!` blanco sobre círculo rojo con halo tenue. - **Layout:** Tres botones outlined (Imprimir/Compartir/Email) en fila + CTA sólido a ancho completo debajo, igual que Figma. - **Estados:** El panel APP añade la variante **ERROR — Reintentar** que el contexto Figma no incluye (solo muestra éxito). Es decisión del showcase para exhibir el estado de error, no un defecto del componente. | | #### POS — Split Payment [Section titled “POS — Split Payment”](#pos--split-payment) **Código:** `src/ui/organisms/pos/SplitPayment/index.tsx` · **Figma:** [`105:253`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=105-253) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Split Payment](/design/parity/figma/organisms/figma-pos-split-payment.png)](/design/parity/figma/organisms/figma-pos-split-payment.png "figma POS — Split Payment — abrir a tamaño completo") | [![app POS — Split Payment](/design/parity/history/2026-08-07/screenshots/playground/pos-split-payment.png)](/design/parity/history/2026-08-07/screenshots/playground/pos-split-payment.png "app POS — Split Payment — abrir a tamaño completo") | | Notas | | | **⚠️ Diferencias menores** *(re-auditado tras remediación)*Todos los tokens remediados verifican; el residuo es de **estado/composición del showcase**, no de color.- **Color:** Sin defecto. Medido en ambos paneles: azul de marca **#0052CC** (Total / borde “Agregar pago”), banner Restante **#FFFBEB** + texto **#F59E0B** (idéntico Figma↔app), segmented thumb **#FFFFFF** sobre contenedor **#F5F5F5**, CTA deshabilitado gris. Coincide (el tinte *warning* del Restante ya está aplicado). - **Layout:** Los paneles renderizan **estados distintos** del organism: Figma muestra el estado lleno (lista de pagos Efectivo/Tarjeta con filas tintadas #F7F9FC, íconos y X por fila, botón outlined “Agregar pago”, CTA “Finalizar venta”); la app muestra el estado de **entrada** (Total + selector segmented + input Monto + botón “Agregar” + Restante). No es defecto de remediación: es otra pantalla del mismo flujo. - **Estados:** El botón “Confirmar”/“Agregar” aparece deshabilitado (Monto vacío) con relleno tonal gris, consistente con el “Finalizar venta” deshabilitado del Figma. - **Tipografía:** Título “Pago dividido” vs “Pago mixto” — texto de ejemplo del showcase, no defecto. - **Pendiente de verificar:** con el showcase en estado vacío no se confirma en esta captura el estilo de las **filas de pago** (fondo #F7F9FC, ícono, X de eliminar) ni el botón **outlined “Agregar pago”** (borde #0052CC 1px). Se recomienda una variante del showcase con pagos ya agregados para cerrar la paridad estructural. | | #### POS — Tender Picker [Section titled “POS — Tender Picker”](#pos--tender-picker) **Código:** `src/ui/organisms/pos/TenderPicker/index.tsx` · **Figma:** [`105:151`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=105-151) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma POS — Tender Picker](/design/parity/figma/organisms/figma-pos-tender-picker.png)](/design/parity/figma/organisms/figma-pos-tender-picker.png "figma POS — Tender Picker — abrir a tamaño completo") | [![app POS — Tender Picker](/design/parity/history/2026-08-07/screenshots/playground/pos-tender-picker.png)](/design/parity/history/2026-08-07/screenshots/playground/pos-tender-picker.png "app POS — Tender Picker — abrir a tamaño completo") | | Notas | | | **❌ Diferencias importantes** — el patrón del componente diverge del diseño. Figma es un *selector de método* de una sola pantalla (chip Total, radios de selección con estado activo, subtítulos, iconos en contenedor tintado, CTA Continuar); la app es una *lista de navegación con chevron* que solo emite intención y hace drill-in a subflujos. Los tokens que sí están presentes coinciden con el canon; el residuo es estructural.- **Color:** Lo medido coincide — Figma: chip Total y fila seleccionada `#E6EEFA` (primary.light), Continuar `#0052CC`, contenedor de icono `#F0F4F8`. App: card blanca, borde `#E7E9EC`≈`#E5E7EB`, texto oscuro. No hay defecto de color en lo renderizado. - **Layout:** Falta el **chip “Total a cobrar”** superior (tinte `#E6EEFA` + monto en primary bold). No existe en `TenderPicker/index.tsx`. - **Layout:** Falta el **botón “Continuar”** (relleno sólido `#0052CC`, texto blanco) al pie del diálogo. - **Estados:** No hay **modelo de selección** — Figma usa radios/checkbox con fila activa (borde primary + tinte `#E6EEFA` + check relleno); la app usa `ChevronRight` de drill-in y `onSelect` sin estado seleccionado (líneas 96–98). - **Tipografía:** Faltan los **subtítulos** por opción (“Pago en efectivo + cambio”, etc.); la app solo muestra `label` (línea 93–95). - **Iconos:** Iconos **sin contenedor tintado** — heredan `text.primary` (#1F2933) bare (línea 92), mientras Figma los envuelve en cuadro redondeado `#F0F4F8` (y verde para Efectivo). El fix “contenedor tintado de iconos en ListRow” no está aplicado aquí. - **Salvedad:** el chevron vs radio no es “texto de ejemplo del showcase” sino un modelo de interacción distinto → residuo real del componente, no decisión del showcase. - **Culpable:** `src/ui/organisms/pos/TenderPicker/index.tsx` (no contempla total, subtítulo, selección ni CTA). | | #### Scrim / Overlay [Section titled “Scrim / Overlay”](#scrim--overlay) **Código:** `src/ui/organisms/Scrim/index.tsx` | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — sin render exportado en `design/web/organisms/` | [![app Scrim / Overlay](/design/parity/history/2026-08-07/screenshots/playground/scrim.png)](/design/parity/history/2026-08-07/screenshots/playground/scrim.png "app Scrim / Overlay — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Impersonation Banner [Section titled “Impersonation Banner”](#impersonation-banner) **Código:** `src/ui/organisms/ImpersonationBanner/index.tsx` | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ⚠️ **Falta diseño** — no hay render de Figma exportado — sin render exportado en `design/web/organisms/` | [![app Impersonation Banner](/design/parity/history/2026-08-07/screenshots/playground/impersonation-banner.png)](/design/parity/history/2026-08-07/screenshots/playground/impersonation-banner.png "app Impersonation Banner — abrir a tamaño completo") | | Notas | | | **⛔ Comparación no posible — falta el render de Figma.** La app está capturada pero no hay diseño exportado contra el cual compararla; el motivo está en la celda izquierda (nodo sin exportar, o pantalla sin frame diseñado). **Siguiente paso:** exportar el nodo a `terminales-solution/docs/design/web/` y volver a correr `pnpm docs:parity`; si no existe frame, es una decisión de diseño pendiente, no una desviación de la implementación. | | #### Side Nav [Section titled “Side Nav”](#side-nav) **Código:** `src/ui/organisms/SideNav/SideNav.tsx` · **Figma:** [`97:35`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=97-35) | Diseño Figma | Captura de la app | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Side Nav](/design/parity/figma/organisms/figma-side-nav.png)](/design/parity/figma/organisms/figma-side-nav.png "figma Side Nav — abrir a tamaño completo") | [![app Side Nav](/design/parity/history/2026-08-07/screenshots/playground/side-nav.png)](/design/parity/history/2026-08-07/screenshots/playground/side-nav.png "app Side Nav — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Ítem activo con tinte de marca **#E6EEFA** e ícono/texto **#0052CC** (medido en los 3 estados del showcase). Riel colapsado reproduce el mismo par tinte+azul en el ítem activo. Encabezados de sección en gris neutro (\~#9CA3AF). Ítems inactivos en neutro oscuro sobre blanco. - **Tipografía:** Encabezados de sección en mayúsculas/tracking; label de ítem activo en peso semibold azul; inactivos en neutro. Jerarquía equivalente al diseño. - **Espaciado:** Agrupación por secciones (OPERACIÓN, CATÁLOGO, EQUIPO, FINANZAS, ADMINISTRACIÓN) con el mismo orden y densidad de ítems que Figma. - **Bordes:** Píldora activa con esquinas redondeadas, sin borde; consistente con el patrón de selección tintado. - **Iconos:** Íconos de ítems inactivos en neutro oscuro (estilo relleno). Algunos glifos difieren respecto a Figma (p. ej. Punto de venta = caja registradora vs carrito; Productos = etiqueta vs regalo; Servicios = factura vs flechas de intercambio; Reportes = línea vs barras) — **decisión del showcase (íconos de ejemplo), no defecto**. - **Layout:** El showcase de /playground muestra 3 estados (expandido activo, activo por módulo, colapsado) y omite el logo “Terminales”, el selector de tienda y el footer de usuario que sí aparecen en Figma — **fuera del alcance del showcase, no residuo del componente**. - **Estados:** Activo, inactivo y colapsado-activo verificados con color medido; todos siguen el patrón canónico tinte #E6EEFA + #0052CC. | | #### Sticky Save Bar [Section titled “Sticky Save Bar”](#sticky-save-bar) **Código:** `src/ui/organisms/StickySaveBar/StickySaveBar.tsx` · **Figma:** [`103:134`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=103-134) | Diseño Figma | Captura de la app | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Sticky Save Bar](/design/parity/figma/organisms/figma-sticky-save-bar.png)](/design/parity/figma/organisms/figma-sticky-save-bar.png "figma Sticky Save Bar — abrir a tamaño completo") | [![app Sticky Save Bar](/design/parity/history/2026-08-07/screenshots/playground/sticky-save-bar.png)](/design/parity/history/2026-08-07/screenshots/playground/sticky-save-bar.png "app Sticky Save Bar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Botón *Guardar* con relleno sólido #0052CC medido idéntico en Figma y app. Botón de texto *Descartar* en azul de marca (trazos medidos #4681DA/#82AAE6, antialiasing de texto primary) en ambos. - **Iconos:** El icono de información es un círculo relleno neutro #6B7280 en ambos paneles (medido exacto), con la “i” blanca. - **Tipografía:** Mensaje “Tienes cambios sin guardar” en gris neutro; jerarquía y peso equivalentes. - **Layout:** Barra plana con hairline; a la izquierda icono + mensaje, a la derecha *Descartar* (botón de texto) + *Guardar* (botón relleno primary). Orden y alineación coinciden. - **Estados:** El patrón de botón relleno primary (excepción de relleno sólido) se respeta en *Guardar*; *Descartar* como acción secundaria de texto. - **Salvedad showcase:** El contenedor con borde y el área de vista previa tenue por encima de la barra son marco del showcase de /playground, no defecto del componente. | | #### Tabs [Section titled “Tabs”](#tabs) **Código:** `src/ui/organisms/Tabs/Tabs.tsx` · **Figma:** [`94:6`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=94-6) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Tabs](/design/parity/figma/organisms/figma-tabs.png)](/design/parity/figma/organisms/figma-tabs.png "figma Tabs — abrir a tamaño completo") | [![app Tabs](/design/parity/history/2026-08-07/screenshots/playground/tabs.png)](/design/parity/history/2026-08-07/screenshots/playground/tabs.png "app Tabs — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Pestaña activa medida en **#0052CC** tanto en Figma (texto `30,18`) como en la app (texto `58,97`); el indicador inferior también mide **#0052CC** en la app (`80,118`). Azul de marca correcto. - **Bordes:** Divisor/hairline bajo la tira de pestañas medido en **#E5E7EB** en ambos (`70,120` app). Coincide. - **Tipografía:** Pestaña activa en semibold azul + inactivas en gris neutro; jerarquía idéntica a Figma. - **Estados:** El patrón de selección (texto primary + subrayado sólido primary) está correctamente aplicado; MUI Tabs con `indicator` en primary. - **Layout:** El panel APP es un showcase de /playground con tres filas extra (activa+indicador / hover / disabled) que Figma no muestra — decisión del showcase, no defecto. El estado hover destaca “Historial” y el disabled atenúa las pestañas, coherentes con los tokens. - **Espaciado:** Fondo del contenedor difiere (Figma #F5F5F5 neutro vs playground #F5F7FA) pero es el lienzo del showcase, no el componente.Sin residuo real del componente. | | #### Top Bar [Section titled “Top Bar”](#top-bar) **Código:** `src/ui/organisms/TopBar/TopBar.tsx` · **Figma:** [`98:51`](https://www.figma.com/design/ndebIq7cqexBCf9eKVU0Fm/?node-id=98-51) | Diseño Figma | Captura de la app | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![figma Top Bar](/design/parity/figma/organisms/figma-top-bar.png)](/design/parity/figma/organisms/figma-top-bar.png "figma Top Bar — abrir a tamaño completo") | [![app Top Bar](/design/parity/history/2026-08-07/screenshots/playground/top-bar.png)](/design/parity/history/2026-08-07/screenshots/playground/top-bar.png "app Top Bar — abrir a tamaño completo") | | Notas | | | **✅ Coincide**- **Color:** Marca #0052CC medido en el logo del row *Punto de venta* y en el ícono del avatar; tinte de avatar #E6EEFA (palette.primary.light) confirmado. Badge de notificación #EF4444 (error). Todo canónico. - **Tipografía:** Título #1F2933, secundario “Bienvenido” en gris, jerarquía correcta. - **Iconos:** Campana de adorno #6B7280/#9CA3AF (no azul), correcta; ícono de avatar relleno sobre contenedor tintado, patrón esperado. - **Bordes:** Tarjetas planas con hairline claro; divisor vertical sutil entre acciones y menú de usuario. - **Layout:** El panel APP es un showcase con 3 variantes (base sin badge / con logo+badge+menú / con navegación atrás), mientras Figma muestra una única barra con buscador + chips de estado (Turno abierto, Sincronizado) + grupo de avatares. Esa diferencia de contenido de ejemplo es decisión del showcase, no defecto — los tokens y estados subyacentes coinciden. - **Estados:** Menú de usuario con avatar tintado + iniciales/ícono y badge numérico correctos; sin residuo de paridad detectable. | | *** ## Apéndice — otras vistas de `/playground` [Section titled “Apéndice — otras vistas de /playground”](#apéndice--otras-vistas-de-playground) El registro de `/playground` también incluye **composiciones de pantalla** hechas con el design system y algunos tableros de flujo completo. Cuando la pantalla ya tiene ruta real, arriba se muestra la captura de la app; estas son las que quedan. Sirven como *referencia de implementación* — cómo se vería la pantalla armada solo con componentes del DS — y son un buen punto de partida cuando toque construir la pantalla de verdad. | Vista | Captura | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `cuenta-flow` | [![playground cuenta-flow](/design/parity/history/2026-08-07/screenshots/playground/cuenta-flow.png)](/design/parity/history/2026-08-07/screenshots/playground/cuenta-flow.png) | | `reportes-flow` | [![playground reportes-flow](/design/parity/history/2026-08-07/screenshots/playground/reportes-flow.png)](/design/parity/history/2026-08-07/screenshots/playground/reportes-flow.png) | | `ventas-flow` | [![playground ventas-flow](/design/parity/history/2026-08-07/screenshots/playground/ventas-flow.png)](/design/parity/history/2026-08-07/screenshots/playground/ventas-flow.png) | | `w25-stock` | [![playground w25-stock](/design/parity/history/2026-08-07/screenshots/playground/w25-stock.png)](/design/parity/history/2026-08-07/screenshots/playground/w25-stock.png) | | `w26-ajuste` | [![playground w26-ajuste](/design/parity/history/2026-08-07/screenshots/playground/w26-ajuste.png)](/design/parity/history/2026-08-07/screenshots/playground/w26-ajuste.png) | | `w27-movimientos` | [![playground w27-movimientos](/design/parity/history/2026-08-07/screenshots/playground/w27-movimientos.png)](/design/parity/history/2026-08-07/screenshots/playground/w27-movimientos.png) | | `w28-transferencia` | [![playground w28-transferencia](/design/parity/history/2026-08-07/screenshots/playground/w28-transferencia.png)](/design/parity/history/2026-08-07/screenshots/playground/w28-transferencia.png) | | `w29-ventas` | [![playground w29-ventas](/design/parity/history/2026-08-07/screenshots/playground/w29-ventas.png)](/design/parity/history/2026-08-07/screenshots/playground/w29-ventas.png) | | `w30-detalle-venta` | [![playground w30-detalle-venta](/design/parity/history/2026-08-07/screenshots/playground/w30-detalle-venta.png)](/design/parity/history/2026-08-07/screenshots/playground/w30-detalle-venta.png) | | `w31-rechazo-pin` | [![playground w31-rechazo-pin](/design/parity/history/2026-08-07/screenshots/playground/w31-rechazo-pin.png)](/design/parity/history/2026-08-07/screenshots/playground/w31-rechazo-pin.png) | | `w31-reembolso-parcial` | [![playground w31-reembolso-parcial](/design/parity/history/2026-08-07/screenshots/playground/w31-reembolso-parcial.png)](/design/parity/history/2026-08-07/screenshots/playground/w31-reembolso-parcial.png) | | `w32-turnos` | [![playground w32-turnos](/design/parity/history/2026-08-07/screenshots/playground/w32-turnos.png)](/design/parity/history/2026-08-07/screenshots/playground/w32-turnos.png) | | `w33-detalle-z` | [![playground w33-detalle-z](/design/parity/history/2026-08-07/screenshots/playground/w33-detalle-z.png)](/design/parity/history/2026-08-07/screenshots/playground/w33-detalle-z.png) | | `w34-abrir-turno` | [![playground w34-abrir-turno](/design/parity/history/2026-08-07/screenshots/playground/w34-abrir-turno.png)](/design/parity/history/2026-08-07/screenshots/playground/w34-abrir-turno.png) | | `w35-cerrar-turno` | [![playground w35-cerrar-turno](/design/parity/history/2026-08-07/screenshots/playground/w35-cerrar-turno.png)](/design/parity/history/2026-08-07/screenshots/playground/w35-cerrar-turno.png) | | `w36-movimientos-caja` | [![playground w36-movimientos-caja](/design/parity/history/2026-08-07/screenshots/playground/w36-movimientos-caja.png)](/design/parity/history/2026-08-07/screenshots/playground/w36-movimientos-caja.png) | | `w37-empleados` | [![playground w37-empleados](/design/parity/history/2026-08-07/screenshots/playground/w37-empleados.png)](/design/parity/history/2026-08-07/screenshots/playground/w37-empleados.png) | | `w38-empleado-edit` | [![playground w38-empleado-edit](/design/parity/history/2026-08-07/screenshots/playground/w38-empleado-edit.png)](/design/parity/history/2026-08-07/screenshots/playground/w38-empleado-edit.png) | | `w39-invitar` | [![playground w39-invitar](/design/parity/history/2026-08-07/screenshots/playground/w39-invitar.png)](/design/parity/history/2026-08-07/screenshots/playground/w39-invitar.png) | | `w40-roles` | [![playground w40-roles](/design/parity/history/2026-08-07/screenshots/playground/w40-roles.png)](/design/parity/history/2026-08-07/screenshots/playground/w40-roles.png) | | `w55-reportes-overview` | [![playground w55-reportes-overview](/design/parity/history/2026-08-07/screenshots/playground/w55-reportes-overview.png)](/design/parity/history/2026-08-07/screenshots/playground/w55-reportes-overview.png) | | `w56-reporte-ventas` | [![playground w56-reporte-ventas](/design/parity/history/2026-08-07/screenshots/playground/w56-reporte-ventas.png)](/design/parity/history/2026-08-07/screenshots/playground/w56-reporte-ventas.png) | | `w58-cortes-z` | [![playground w58-cortes-z](/design/parity/history/2026-08-07/screenshots/playground/w58-cortes-z.png)](/design/parity/history/2026-08-07/screenshots/playground/w58-cortes-z.png) | | `w59-mi-cuenta` | [![playground w59-mi-cuenta](/design/parity/history/2026-08-07/screenshots/playground/w59-mi-cuenta.png)](/design/parity/history/2026-08-07/screenshots/playground/w59-mi-cuenta.png) | | `w61-ajustes` | [![playground w61-ajustes](/design/parity/history/2026-08-07/screenshots/playground/w61-ajustes.png)](/design/parity/history/2026-08-07/screenshots/playground/w61-ajustes.png) | | `w62-bitacora` | [![playground w62-bitacora](/design/parity/history/2026-08-07/screenshots/playground/w62-bitacora.png)](/design/parity/history/2026-08-07/screenshots/playground/w62-bitacora.png) | | `w65-cerrar-denominacion` | [![playground w65-cerrar-denominacion](/design/parity/history/2026-08-07/screenshots/playground/w65-cerrar-denominacion.png)](/design/parity/history/2026-08-07/screenshots/playground/w65-cerrar-denominacion.png) | # Desviaciones intencionales — Web (App mejor que Figma) > **Propósito.** El objetivo de la paridad de diseño es que la app coincida con el diseño en Figma. Pero no toda diferencia es un defecto: a veces la implementación actual es **deliberadamente mejor** que el frame de Figma — más usable, más eficiente, o más consistente con el design system / la plataforma web. Cuando ese es el caso, **conservamos la implementación** y la registramos aquí con su razón, en lugar de degradarla para igualar el píxel del diseño. > > Esto complementa la galería [`README.md`](/design/parity/): cada entrada de aquí corresponde a la fila **Desviación (propuesta)** de una tabla de la galería. Una desviación **aceptada** convierte un ❌/⚠️ de la columna *Notas* en un **✅ (desviación aceptada)**. > > **Regla operativa (por qué existe este archivo):** *antes de crear un draft de UI polish* para una diferencia, revisá si ya está registrada como desviación aquí (o en la fila **Desviación** de la galería). Si lo está, **no se abre issue** — se acepta la implementación y, si corresponde, se actualiza el frame de Figma. ## Cómo se decide una desviación [Section titled “Cómo se decide una desviación”](#cómo-se-decide-una-desviación) Una diferencia se registra como desviación intencional (no como bug) solo si cumple **al menos una** de estas y **ninguna** regla de negocio/accesibilidad la contradice: 1. **Más usuario-amigable** — mejora la usabilidad real (afordancia más clara, menos pasos, feedback de estado, área de click adecuada). 2. **Más eficiente / consistente con el design system** — usa un token, un componente MUI o un patrón ya establecido en la app, evitando un valor único del frame. 3. **Más consistente con la plataforma web** — respeta convenciones de la web / MUI (drawer sobre lista, focus ring, inputs nativos, densidad de tabla) que el frame —pensado de forma genérica— no refleja. Requisitos de la entrada: **evidencia** (medida u observada, no “se ve mejor”), el archivo de código, y —si aplica— una nota para que **el diseño en Figma se actualice** a la implementación (para que el frame deje de ser la “verdad” divergente). ## Estado [Section titled “Estado”](#estado) * **Abierto 2026-08-07** (a partir de la re-auditoría de esa fecha — ver galería). Veredicto de la capa de componentes: **45 ✅ · 14 ⚠️ · 0 ❌** (59 componentes) — verde. En pantallas gran parte de la remediación de 2026-07 ya cascadeó (muchas ❌→⚠️/✅); los ❌ restantes son de composición, no de token. * **9 candidatas** de desviación surgidas de la comparación Figma↔App (la última, el matiz del tinte de varianza W32, agregada por #613). Pendientes de visto bueno del diseño/PO. Se listan con la regla que cumplen, la evidencia y una recomendación. ## Candidatas propuestas (pendientes de aceptación) [Section titled “Candidatas propuestas (pendientes de aceptación)”](#candidatas-propuestas-pendientes-de-aceptación) > Cada una es un caso donde la app difiere del frame de forma potencialmente mejor. **Decisión pendiente**: aceptar (conservar la app + actualizar Figma) o rechazar (igualar el frame). ### AdminShell — nav lateral reorganizado (Comercios / Tiendas / Apariencia / Terminales / Ajustes) [Section titled “AdminShell — nav lateral reorganizado (Comercios / Tiendas / Apariencia / Terminales / Ajustes)”](#adminshell--nav-lateral-reorganizado-comercios--tiendas--apariencia--terminales--ajustes) * **Capa / nodo Figma:** shell · `97:35` (Side Nav) * **Código:** `src/components/AdminShell/adminNav.ts` * **Diseño dice:** el grupo ADMINISTRACIÓN lista «Comercio y tiendas / Terminales / Ajustes». * **App hace:** tras el overhaul (#538) el nivel comercio↔tienda se separó — «**Comercios / Tiendas**» — y se agregó un ítem de white-label «**Apariencia**»; queda «Terminales / Ajustes». * **Por qué se conserva:** regla **2/3** — la IA nueva refleja el modelo de datos real (comercio y tienda son entidades distintas) y expone el editor de marca ya implementado. * **Acción en Figma:** actualizar la navegación del frame `97:35` a la IA nueva; deprecar «Comercio y tiendas» como un solo ítem. * **Recomendación:** **aceptar** (Figma desactualizado). · **Aceptada por:** *(pendiente)* · **Fecha:** — ### Altas en drawer lateral sobre la lista (en vez de página completa) [Section titled “Altas en drawer lateral sobre la lista (en vez de página completa)”](#altas-en-drawer-lateral-sobre-la-lista-en-vez-de-página-completa) * **Capa / nodo Figma:** patrón · varios (W21/W23/W24/W38/W46b) * **Código:** `src/app/admin/**` (formularios de alta montados como `Dialog`/drawer del DS) * **Diseño dice:** varios frames dibujan el alta/edición como **página completa** con sticky save bar. * **App hace:** resuelve el alta como **drawer lateral derecho** sobre el listado (Categoría, Impuesto, Empleado, Depósito…), preservando el contexto de la tabla. * **Por qué se conserva:** regla **1/3** — patrón web común; el usuario no pierde el listado ni su scroll/filtros, y el retorno es inmediato. * **Acción en Figma:** documentar el patrón drawer-sobre-lista para altas, o confirmar la página completa como canónica caso por caso. * **Recomendación:** **aceptar el patrón** (revisar caso a caso). · **Aceptada por:** *(pendiente)* · **Fecha:** — ### Search Field — relleno outlined blanco + hairline (en vez del soft-fill del frame) [Section titled “Search Field — relleno outlined blanco + hairline (en vez del soft-fill del frame)”](#search-field--relleno-outlined-blanco--hairline-en-vez-del-soft-fill-del-frame) * **Capa / nodo Figma:** atom · `37:35` * **Código:** `src/ui/atoms/SearchField/index.tsx` * **Diseño dice:** campo de búsqueda con relleno suave (fill gris `#F0F4F8`). * **App hace:** relleno blanco + borde hairline `#E5E7EB` (variante outlined), coherente con el resto de los inputs del DS (`FormField`, `Select`, `Textarea`). * **Por qué se conserva:** regla **2** — un solo tratamiento de input en toda la app; mezclar filled y outlined rompería la consistencia. * **Acción en Figma:** unificar el search field al tratamiento outlined del resto de campos, o marcar el soft-fill como excepción deliberada. * **Recomendación:** **aceptar.** · **Aceptada por:** *(pendiente)* · **Fecha:** — ### Date-range Picker — dos campos Desde / Hasta (en vez del pill combinado con chevron) [Section titled “Date-range Picker — dos campos Desde / Hasta (en vez del pill combinado con chevron)”](#date-range-picker--dos-campos-desde--hasta-en-vez-del-pill-combinado-con-chevron) * **Capa / nodo Figma:** atom · `103:116` * **Código:** `src/ui/atoms/DateRangePicker/index.tsx` * **Diseño dice:** un solo pill combinado con chevron que abre el calendario. * **App hace:** dos inputs explícitos (Desde / Hasta). * **Por qué se conserva:** regla **1** — los dos campos comunican el rango y permiten teclear cada extremo sin abrir el popover. * **Acción en Figma:** decidir: adoptar los dos campos en el frame, o mantener el pill y ajustar la app. * **Recomendación:** *a discutir.* · **Aceptada por:** *(pendiente)* · **Fecha:** — ### W01 · Iniciar sesión — toggle de visibilidad de contraseña (ojo) [Section titled “W01 · Iniciar sesión — toggle de visibilidad de contraseña (ojo)”](#w01--iniciar-sesión--toggle-de-visibilidad-de-contraseña-ojo) * **Capa / nodo Figma:** screen · `107:358` * **Código:** `src/ui/Inputs/TextInput.tsx` (campo password) * **Diseño dice:** el campo de contraseña no muestra affordance de mostrar/ocultar. * **App hace:** agrega un icono de ojo para revelar la contraseña. * **Por qué se conserva:** regla **1** — affordance estándar que reduce errores de tecleo. * **Acción en Figma:** agregar el icono trailing de mostrar/ocultar al campo password del frame `107:358`. * **Recomendación:** **aceptar.** · **Aceptada por:** *(pendiente)* · **Fecha:** — * **Nota:** el tamaño de campos/botón de W01 (más altos que Figma) **NO** es desviación — es un bug de overrides `sx` (`TextInput.tsx`/`PrimaryButton.tsx`); va a la galería como ❌ a corregir. ### W30 · Detalle de venta — tabla de Artículos más completa que el frame [Section titled “W30 · Detalle de venta — tabla de Artículos más completa que el frame”](#w30--detalle-de-venta--tabla-de-artículos-más-completa-que-el-frame) * **Capa / nodo Figma:** screen · W30 * **Código:** `src/app/admin/pos/sales-orders/[id]/**` * **Diseño dice:** desglose de artículos más escueto. * **App hace:** tabla de líneas más rica (columnas y totales adicionales) manteniendo el layout de dos tarjetas y los tokens del DS. * **Por qué se conserva:** regla **1/2** — más información útil en la misma composición. * **Acción en Figma:** enriquecer la tabla de artículos del frame W30 para reflejar las columnas reales. * **Recomendación:** **aceptar.** · **Aceptada por:** *(pendiente)* · **Fecha:** — ### W54 · Apariencia (white-label) — acciones extra en el encabezado (Restablecer / Cancelar) [Section titled “W54 · Apariencia (white-label) — acciones extra en el encabezado (Restablecer / Cancelar)”](#w54--apariencia-white-label--acciones-extra-en-el-encabezado-restablecer--cancelar) * **Capa / nodo Figma:** screen · W54 * **Código:** `src/components/WhiteLabel/ThemeEditorForm.tsx` * **Diseño dice:** el encabezado del editor solo muestra «+ Guardar». * **App hace:** agrega «**Restablecer estilos**» (outlined) y «**Cancelar**» junto a Guardar. * **Por qué se conserva:** regla **1** — un editor de tema necesita descartar y revertir cambios. * **Acción en Figma:** agregar las acciones Restablecer/Cancelar al encabezado del frame W54. * **Recomendación:** **aceptar.** · **Aceptada por:** *(pendiente)* · **Fecha:** — ### Product Card — estado Agotado atenúa la tarjeta (alpha 0.6) [Section titled “Product Card — estado Agotado atenúa la tarjeta (alpha 0.6)”](#product-card--estado-agotado-atenúa-la-tarjeta-alpha-06) * **Capa / nodo Figma:** molecule · `44:50` * **Código:** `src/ui/molecules/ProductCard/index.tsx` * **Diseño dice:** la tarjeta agotada mantiene opacidad plena; pill roja bajo el precio. * **App hace:** baja toda la tarjeta a **alpha 0.6** en Agotado (y la vuelve no-interactiva). * **Por qué se conserva:** regla **1** — comunica “no disponible” de un vistazo. (Espejo de la desviación ya aceptada en móvil.) * **Acción en Figma:** añadir un estado OutOfStock a 60% de opacidad a la variante del componente. * **Recomendación:** **aceptar.** · **Aceptada por:** *(pendiente)* · **Fecha:** — ### VAS — navegación por sidebar (sin barra de pestañas en la página) — W41 [Section titled “VAS — navegación por sidebar (sin barra de pestañas en la página) — W41”](#vas--navegación-por-sidebar-sin-barra-de-pestañas-en-la-página--w41) * **Capa / nodo Figma:** screen · `127:6816` (Pago de servicios) * **Código:** `src/components/Vas/VasSaleScreen.tsx` + rutas `src/app/admin/{service-payments,airtime,giftcards,transfers,deposits}` * **Diseño dice:** cada pantalla VAS lleva una **barra de pestañas** en la página (Servicios / Tiempo aire / Pines / Transferir / Depósitos / Wallet) para saltar entre servicios. * **App hace:** los servicios VAS son **rutas propias** navegables desde el **sidebar de admin** (grupo Finanzas); no se duplica una barra de pestañas dentro de la página. La CTA está unificada en **“Continuar”** en todo VAS (ver #548), en vez del “Agregar a la venta” del frame. * **Por qué se conserva:** regla **3** — la IA del admin web usa el sidebar como navegación primaria; una barra de pestañas in-page duplicaría esa navegación. La copia “Continuar” es consistente en todos los flujos VAS. * **Acción en Figma:** decidir — adoptar la navegación por sidebar en los frames VAS (quitar la barra de pestañas y alinear la CTA a “Continuar”), **o** confirmar la barra in-page como canónica y re-alinear la app. * **Recomendación:** *a discutir (probable Figma desactualizado).* · **Aceptada por:** *(pendiente)* · **Fecha:** — ### VAS TAE — flujo por pasos en vez de página compuesta — W42 [Section titled “VAS TAE — flujo por pasos en vez de página compuesta — W42”](#vas-tae--flujo-por-pasos-en-vez-de-página-compuesta--w42) * **Capa / nodo Figma:** screen · `127:7043` (Tiempo Aire) * **Código:** `src/components/Vas/VasSaleScreen.tsx` (`kind="AIRTIME"`), ruta `src/app/admin/airtime` * **Diseño dice:** una **página compuesta** — Compañía (chips) + grilla de Monto + panel de Captura (teléfono/total) en una sola vista. * **App hace:** un **flujo por pasos** (paso 1: elegir proveedor → paso 2: monto/captura), en línea con el resto de los flujos VAS de la app. * **Por qué se conserva:** regla **1** — el flujo por pasos reduce la carga visual en pantalla táctil y reutiliza el patrón VAS compartido; el frame compuesto es un layout genérico. * **Acción en Figma:** documentar el flujo por pasos en el frame `127:7043`, o confirmar la página compuesta como objetivo (implicaría rediseñar el `VasSaleScreen`). * **Recomendación:** *a discutir.* · **Aceptada por:** *(pendiente)* · **Fecha:** — ### Turnos — matiz del tinte de varianza (faltante en rojo vs ámbar del frame) — W32 [Section titled “Turnos — matiz del tinte de varianza (faltante en rojo vs ámbar del frame) — W32”](#turnos--matiz-del-tinte-de-varianza-faltante-en-rojo-vs-ámbar-del-frame--w32) * **Capa / nodo Figma:** screen · `119:2117` (Turnos, columna “Dif.”) * **Código:** `src/components/PosShifts/ShiftList.tsx` (columna `variance`) + `ShiftDetail.tsx` (`varianceStatusVariant`) * **Diseño dice:** el frame dibuja la varianza negativa (faltante, `−$20`) como **píldora ámbar**; el resto de la columna es texto plano. * **App hace:** #613 restauró el `StatusBadge` en la lista para tintar sólo la excepción (faltante/discrepancia), pero mapea **negativo → `danger` (rojo)** reutilizando `varianceStatusVariant`, la **fuente única** de la semántica de varianza que el detalle W33 ya renderiza (rojo = faltante, issue #51/#464). * **Por qué se conserva:** regla **2** — un solo helper gobierna el color de varianza en lista y detalle; forzar ámbar sólo en la lista rompería esa consistencia y duplicaría la regla. El realce estructural (píldora en la excepción, texto plano en lo normal) ya coincide con el frame; sólo difiere el matiz. * **Acción en Figma:** alinear el frame `119:2117` al rojo semántico de faltante (consistente con W33), **o** confirmar el ámbar como canónico y ajustar el helper compartido en un solo lugar. * **Recomendación:** *a discutir (probable matiz de frame).* · **Aceptada por:** *(pendiente)* · **Fecha:** — *** ## Desviaciones aceptadas [Section titled “Desviaciones aceptadas”](#desviaciones-aceptadas) ### FAB de navegación en `/terminal/home` (speed-dial reubicado fuera del header) — ✅ ACEPTADA [Section titled “FAB de navegación en /terminal/home (speed-dial reubicado fuera del header) — ✅ ACEPTADA”](#fab-de-navegación-en-terminalhome-speed-dial-reubicado-fuera-del-header---aceptada) * **Capa / nodo Figma:** screen · W10 (`255:3399`) * **Código:** `src/app/terminal/home/**` (DS `Fab` speed-dial; ver `src/ui/atoms/Fab/index.tsx`) * **Diseño dice:** el frame W10 no incluye un FAB; las acciones de turno viven en otra parte del layout. * **App hace:** un **FAB speed-dial** abajo-izquierda que hospeda **Movimiento de caja / Cerrar turno / Anular venta**, visible solo con turno abierto — reubicado deliberadamente fuera del top bar del kiosco. * **Por qué se conserva:** regla **1/3** — acceso rápido a las acciones de turno sin recargar el header del POS; patrón táctil idiomático. **Decisión de producto explícita: el FAB se mantiene.** * **Acción en Figma:** documentar el FAB de navegación en el frame W10 (`255:3399`). * **Aceptada por:** usuario (PO). · **Fecha:** 2026-08-08. ### Consola de liquidaciones (ops) — pantalla sin frame de Figma — ✅ ACEPTADA [Section titled “Consola de liquidaciones (ops) — pantalla sin frame de Figma — ✅ ACEPTADA”](#consola-de-liquidaciones-ops--pantalla-sin-frame-de-figma---aceptada) * **Capa / nodo Figma:** *ninguno* — pantalla construida **sin frame** (herramienta interna). * **Código:** `src/components/Settlements/SettlementsOpsConsole.tsx` · ruta `src/app/admin/settlements/ops/page.tsx` (gap #55, issue #575). * **Diseño dice:** no existe frame — la consola de operaciones nunca se diseñó en Figma. * **App hace:** arma la pantalla **a partir de patrones del DS** (el mismo compuesto lista-con-acciones que `DepositList`: `PageHeader` + `Tabs` + `DataTable` + `Dialog`/`ConfirmDialog` + toasts), con dos colas — **Liquidaciones pendientes** (Marcar liquidada capturando la clave de rastreo SPEI por destino / Rechazar con motivo) y **Cuentas bancarias por verificar** (ver carátula, Verificar / Rechazar). Es una superficie **platform-admin-only** (404 para el resto). * **Por qué se conserva:** regla **2** — reutiliza componentes y tokens ya establecidos, sin inventar primitivos ni un layout único; es una herramienta interna de operaciones, no una pantalla de cliente, por lo que **no** entra a la galería de paridad ni lleva frame de Figma. * **Acción en Figma:** *ninguna* — no se crea frame (decisión de producto). Si a futuro se diseña, se parte de esta implementación. * **Aceptada por:** usuario (PO), decisión de gap #55. · **Fecha:** 2026-08-09. ### W35 «Cerrar turno» — el desglose por denominación (W65) sustituye el resumen de dos tarjetas — ✅ ACEPTADA (gap #27) [Section titled “W35 «Cerrar turno» — el desglose por denominación (W65) sustituye el resumen de dos tarjetas — ✅ ACEPTADA (gap #27)”](#w35-cerrar-turno--el-desglose-por-denominación-w65-sustituye-el-resumen-de-dos-tarjetas---aceptada-gap-27) * **Capa / nodo Figma:** screen · W35 `132:9670` → **sucesor W65 `574:14732`** * **Código:** `src/components/ShiftClose/ShiftCloseView.tsx` (W65, grid de denominación) + `ShiftCloseSummary.tsx` / `ShiftCloseSummaryStep.tsx` (W35, paso de resumen). * **Diseño dice:** el frame **W35** (`132:9670`) dibuja el cierre como **dos tarjetas de resumen** (“Conteo de efectivo” + “Ventas por método”) más “Imprimir corte (Z)” / “Cerrar turno”. * **App hace:** la superficie de conteo enviada (`/admin/pos/shifts/close` → `ShiftCloseView`) es el **desglose por denominación** del frame **W65** (`574:14732`): 3 `KpiStatCard` + una `DenominationRow` por denominación + total contado / esperado / diferencia. El resumen W35 (`ShiftCloseSummary`) queda como el **paso posterior** al conteo (#587), no como la pantalla de conteo. * **Por qué se conserva:** **gap #27** — decisión de producto registrada (2026-07-06): «W65 extiende/sustituye W35». Contar billete a billete es más preciso para el arqueo de caja que capturar un total, así que **W65 es el sucesor canónico de W35**, no un defecto de composición. Regla **1/2**. * **Acción en Figma:** marcar `132:9670` (W35) como **superseded-by** `574:14732` (W65); conservar el resumen de dos tarjetas como el paso de confirmación posterior al conteo. * **Evidencia de paridad:** `docs/ui-evidence/671/` — el slot `w35-cerrar-turno` se recapturó contra `ShiftCloseSummary` (coincide con `132:9670`) y `w65-cerrar-turno-desglose` contra `ShiftCloseView` (coincide con `574:14732`), reconciliando el veredicto **best-effort** de #669/#670, que había capturado el grid W65 bajo el nombre de W35. **Por gap #27 el “mismatch” W35↔grid no se re-abre como issue.** * **Aceptada por:** decisión de producto, gap #27. · **Fecha:** 2026-07-06. ### W65 en la terminal · Cerrar turno (Corte Z) — bandejas Billetes/Monedas + resumen fijo al pie [Section titled “W65 en la terminal · Cerrar turno (Corte Z) — bandejas Billetes/Monedas + resumen fijo al pie”](#w65-en-la-terminal--cerrar-turno-corte-z--bandejas-billetesmonedas--resumen-fijo-al-pie) * **Capa / nodo Figma:** screen · W65 `574:14732` (terminal: diálogo del FAB de turno en `/terminal/home`) * **Código:** `src/app/terminal/shift/close/ShiftCloseView.tsx` + `CloseReconciliationBar.tsx` (el W65 de admin, `src/components/ShiftClose/ShiftCloseView.tsx`, no cambia de layout). * **Diseño dice:** título «Cerrar turno · Corte Z», 3 KPI (Ventas brutas / Efectivo esperado / Pagos con tarjeta), una rejilla de 2 columnas con las 11 denominaciones mezcladas y los totales + botón «Cerrar turno (Z-Report)» al final de la tarjeta. * **App hace:** (1) un solo título — la barra del diálogo ya dice «Cerrar turno», la vista sólo agrega la línea «Corte Z · Turno abierto desde …»; (2) KPI sólo con cifras reales: el snapshot de cierre del cajero no trae ventas (api `getCloseSnapshot`), así que se muestran «Fondo inicial» + «Efectivo esperado» y las tarjetas de ventas aparecen sólo si el api las envía; (3) el conteo en dos bandejas **Billetes** ($1,000–$20) y **Monedas** ($10–$0.50) con subtotal por bandeja; (4) los totales, la Diferencia con su estado (Sin contar / Cuadra / Faltante / Sobrante), la línea de autorización de supervisor y el botón «Cerrar turno» viven en una barra fija al pie (geometría de `StickySaveBar`, colores de `StatusBadge`), siempre visible mientras se cuenta. * **Por qué se conserva:** regla **1** — en la terminal el botón y la diferencia quedaban bajo el pliegue y las tarjetas de ventas sólo podían mostrar «—». Es la variante «B — Panel fijo» del canvas `pos-shift-actions`, llevada al pie para que funcione igual a 768 px. * **Acción en Figma:** derivar un frame terminal de W65 con las bandejas y el resumen fijo; quitar «(Z-Report)» del botón. * **Recomendación:** **aceptar.** · **Aceptada por:** *(pendiente)* · **Fecha:** — ### `/invitacion` — página pública de aceptación de invitación sin frame de Figma — ✅ ACEPTADA (gap #55) [Section titled “/invitacion — página pública de aceptación de invitación sin frame de Figma — ✅ ACEPTADA (gap #55)”](#invitacion--página-pública-de-aceptación-de-invitación-sin-frame-de-figma---aceptada-gap-55) * **Capa / nodo Figma:** screen · **no existe frame** — la landing de aceptación nunca se diseñó en Figma (MerchantInvitations E5, spec `docs/integrations/verification/merchant-invitations-completion-design.md`). * **Código:** `src/app/invitacion/` (`page.tsx`, `AcceptView.tsx`, `invitacion.hooks.ts`, `invitacion.i18n.ts`). * **Diseño dice:** no existe frame — la página pública de aceptación no tiene artboard. * **App hace:** arma la pantalla **a partir de patrones del DS** (`Paper`/tarjeta centrada + `Banner` de éxito/peligro + `Button`), con la máquina de estados de la spec §6.2: sin tuple válido → estado “invitación venció o no es válida” (falla cerrada); sin sesión → CTAs “Iniciar sesión” / “Crear mi cuenta” que preservan la tuple `{teamId, membershipId, userId, secret}` al volver a `/invitacion`; con sesión → `POST /pos/invites/accept` (mock-first) → pantalla de éxito con CTA persistente “Crea tu PIN para vender” (deep-link a la terminal, E6). es-MX, sin nombres de proveedor. * **Por qué se conserva:** regla **2** — reutiliza componentes y tokens del DS sin inventar primitivos ni un layout único; es una landing de flujo completada pre-prod (misma disciplina no-flag / es-MX / sin marca de la ola MerchantVerification). gap #55 (ruta canvas/patrón-DS, no Figma). * **Acción en Figma:** *ninguna* — no se crea frame por ahora; coordinar con terminales-design si se quiere un artboard W-invitacion dedicado. Si a futuro se diseña, se parte de esta implementación. * **Aceptada por:** tl-verification, decisión de gap #55. · **Fecha:** 2026-09-03. * **Seguimiento (batch `8ac7050bee`, #712):** el ida-y-vuelta post-auth quedó cableado de punta a punta — `/login` y `/signup` ahora **consumen** el `returnTo` (mismo concepto que el `redirect` de los guards de sesión) validado a ruta interna del mismo origen (anti open-redirect, OWASP A01) vía `src/core/auth/postAuthRedirect.ts`, y regresan a `/invitacion` con la tuple intacta tras iniciar sesión / crear cuenta (incluye las rutas MFA y magic-link). Sin cambio visual: reutiliza los frames de Figma existentes de `/login` y `/signup`. ### W39 · Invitar usuario — estados de resultado en-diálogo (éxito / «correo ya invitado o registrado») sin frame — ✅ ACEPTADA (gap #19) [Section titled “W39 · Invitar usuario — estados de resultado en-diálogo (éxito / «correo ya invitado o registrado») sin frame — ✅ ACEPTADA (gap #19)”](#w39--invitar-usuario--estados-de-resultado-en-diálogo-éxito--correo-ya-invitado-o-registrado-sin-frame---aceptada-gap-19) * **Capa / nodo Figma:** dialog · W39 `131:8930` (existe el frame por defecto del formulario; **faltan** los frames de éxito y de error, gap #19 / draft `web-parity-staff-roles-invite.md` FD-7). * **Código:** `src/components/Users/InviteUserDialog.tsx` (panel de resultado en-diálogo); showcase `src/app/playground/showcases/PersonalInvitarScreenShowcase.tsx` (tres frames lado a lado). * **Diseño dice:** el frame W39 sólo dibuja el formulario; el resultado de enviar la invitación no tiene artboard (antes era sólo un toast). * **App hace:** al enviar, el diálogo reemplaza el formulario por un **panel de resultado** armado con patrones del DS (`Icon` `CircleCheck`/`TriangleExclamation` + título + cuerpo): éxito («Invitación enviada» + acción “Invitar a otra persona” / “Listo”) y error («Ese correo ya tiene acceso» cuando el backend responde 409 / ya-miembro, con acción “Volver” para corregir y reintentar). Se conserva el toast por compatibilidad. es-MX, sin nombres de proveedor. * **Por qué se conserva:** regla **2** — completa un flujo pre-prod reutilizando componentes/tokens del DS sin inventar primitivos; misma disciplina no-flag / es-MX / sin marca de la ola MerchantInvitations. * **Acción en Figma:** coordinar con terminales-design el frame de éxito/error de W39 (gap #19 / #55). Si a futuro se diseña, se parte de esta implementación. * **Aceptada por:** tl-verification, decisión de gap #19. · **Fecha:** 2026-09-03. *** *Para aceptar una candidata: movela desde «Candidatas propuestas» a esta sección con quién la aceptó y la fecha, poné su celda **Desviación** de la galería como `🔶 Aceptada`, y actualizá el frame de Figma.* # Design-parity remediation plan > the order of operations to close the 153 audited gaps between Figma and the app > **Método reutilizable:** las lecciones de este esfuerzo (para el repo móvil y proyectos futuros) están destiladas en `terminales-solution/docs/design/design-parity-playbook.md`. > **Purpose:** the order of operations to close the 153 audited gaps between Figma and the app with the least total work. Companion to [README.md](/design/parity/). The core idea: **almost every per-screen difference is a symptom of a handful of systemic defects**, so we fix layers, not screens — tokens → theme → atoms → patterns — and re-run the audit after each layer to watch notes flip to ✅ instead of hand-fixing them. > > **Progress (2026-07-30): Phases 0–2 done and re-audited; Phases 3–4 largely delivered.** Since the 2026-07-23 note below, the molecule/organism residuals and the screen patterns were worked as real tickets — the admin list/screen build-outs (#460–#474), the full-page detail views (#466–#471), the tender-picker rework (#472) and the filled-glyph residuals (#474) all merged 2026-07-25, followed by a **round-2 polish pass** across login, dashboard, terminal and shifts on 2026-07-28 and a fresh gallery re-capture. What remains of Phase 4 is the **PO decision list** (dialog-vs-page and similar), now carried as web issues #498–#501. Re-run `pnpm screenshots` + `pnpm docs:parity` before trusting any per-screen verdict below. > > **Progress (2026-07-23): Phases 0, 1, 2 are DONE and re-audited.** DESIGN.md is wired as the token source, the MUI theme carries the \~15 systemic overrides, the atoms are fixed, and the showcase `sx` hacks are promoted to real props. The re-audit against the post-fix captures moved the per-screen verdict from **96 ❌ / 7 ⚠️ / 0 ✅** to **37 ❌ / 58 ⚠️ / 6 ✅** — most screens dropped from major to minor. What remains is captured as the **residual backlog** in the audit’s Hallazgos sistémicos section (11 items, measured, with culprit files) — that list supersedes the original 18 findings below, which are kept for the record. Phases 3–5 below are the remaining work. ## The discovery that shapes everything [Section titled “The discovery that shapes everything”](#the-discovery-that-shapes-everything) The audit’s hardest open question — *which* values are the truth (`#0052CC` vs `#0057B8`, the Tailwind-ish grey ramp vs the published tokens) — turns out to be already answered: **`terminales-solution/docs/design/DESIGN.md` is the canonical spec, and the Figma frames were painted from it.** Every value the audit measured in the Figma renders appears in DESIGN.md verbatim: | Role | DESIGN.md | Measured in Figma renders | App today (`tokens.local.json`) | | ------------------------------------- | ----------------------------------------------------- | -------------------------------- | ------------------------------- | | `primary` | `#0052CC` | `#0052CC` | `#0057B8` ❌ | | `primary-strong` (pressed) | `#003D99` | `#003D99` | `#003A8F` ❌ | | `primary-soft` | `#E6EEFA` | `#E6EEFA` | *(role missing)* ❌ | | `text-primary / secondary / tertiary` | `#1F2933 / #6B7280 / #9CA3AF` | same | `#242B35 / #384252 / #8292AA` ❌ | | `border` | `#E5E7EB` | `#E5E7EB` | `#C8CED9` ❌ | | `success / warning / danger` + softs | `#10B981/#ECFDF5` `#F59E0B/#FFFBEB` `#EF4444/#FEF2F2` | same | mains ✓, softs unwired | | `info` / `info-soft` | `#3B82F6 / #EFF6FF` | same | *(role missing)* ❌ | | Radius scale | `sm 8 / md 12 / lg 16 / xl 24` | matches (dialog 16, buttons 12…) | flat `borderRadius: 8` ❌ | This resolves systemic findings **#3, #6, #8, #12, #13** as *one decision* instead of five component-by-component debates. It also reframes finding #13: the “legacy Tailwind palette” is actually the **canonical** palette; the “published tokens” are the drift. Meanwhile the app’s token pipeline is broken: `pnpm tokens:build` calls `scripts/generate-tokens.js`, **which no longer exists** — `src/styles/tokens.local.ts` is a frozen artifact of a deleted generator, and `src/styles/tokens.ts` (the “Figma Token Engine” export) is stale (its CTA primary is grey `#384252`). Three competing sources, none of them DESIGN.md. > ⚠️ **One confirmation needed from the PO/design before Phase 0 lands:** is `#0057B8` anywhere a deliberate brand decision (client brand book, AngelPay requirement)? All repo evidence says DESIGN.md/`#0052CC` is the intent, but this is the one call that must not be silently wrong, because everything downstream inherits it. ## Guiding principles [Section titled “Guiding principles”](#guiding-principles) 1. **Fix at the highest layer that causes the defect.** Never patch a screen for what a token causes; never patch a component for what a theme override causes. The audit’s leverage map (below) assigns every systemic finding to its layer. 2. **Measure, don’t eyeball.** Every fix’s definition of done is a re-captured screenshot whose sampled hex/geometry matches the token — the same `imgtool` sampler the audit used. 3. **Re-audit instead of re-annotating.** After each phase: `pnpm screenshots` → re-run the comparison for affected entries → notes flip to ✅ mechanically. The gallery is the progress dashboard. 4. **Dimensions: web frames win over DESIGN.md.** DESIGN.md is brand-neutral and mobile-leaning (input height 56, button 52); the web frames use denser desktop metrics (\~40–44 fields). Colors, type scale, radius and spacing come from DESIGN.md; **web component dimensions come from the 1440px web frames** (`design/web/`). 5. **Don’t restyle what has a pending structural decision.** 13 screens have a dialog-vs-full-page mismatch (see Phase 4) — styling them before the PO decides the container is wasted work. ## Phase 0 — Source of truth + pipeline repair *(small, unblocks everything)* [Section titled “Phase 0 — Source of truth + pipeline repair (small, unblocks everything)”](#phase-0--source-of-truth--pipeline-repair-small-unblocks-everything) 1. Confirm the `#0052CC` decision (above). 2. **One generator, one source.** Rewrite `scripts/generate-tokens.js` (or a new `scripts/generate-tokens.mjs`) to read **DESIGN.md** (it’s YAML frontmatter + sections — already machine-readable, and already the format of the [design.md spec](https://stitch.withgoogle.com/docs/design-md/specification) the frontend should stay compatible with) and emit `src/styles/tokens.generated.ts`: colors (light + a derived/authored dark scheme), type scale, radius scale, spacing scale. 3. Extend DESIGN.md with the roles the audit proved missing — it’s the canonical file, so gaps get fixed *there*: `action/disabled` colors (finding #10), data-viz series (#18), shadows/ elevation (it has none today), dark scheme (today `tokens.local.json` just duplicates light). 4. Retire the drift: `tokens.local.json`, `src/styles/tokens.local.ts` and the stale parts of `src/styles/tokens.ts` stop being imported; `src/ui/theme/tokens.ts` reads only the generated file. `pnpm tokens:build` works again and CI can verify freshness (same pattern as `verify:api-types`). *Kills systemic findings #3, #6, #8, #12, #13, and the token halves of #10/#18.* ## Phase 1 — MUI theme as the enforcement point *(the big lever)* [Section titled “Phase 1 — MUI theme as the enforcement point (the big lever)”](#phase-1--mui-theme-as-the-enforcement-point-the-big-lever) This is where the audit’s findings concentrate: **11 of 18 systemic defects are missing or wrong theme overrides**, not component bugs. Work through `src/ui/theme/theme.ts` override by override, verifying each against its playground capture before moving on: | Override | Fixes finding | What | | ---------------------------------------------------------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `palette` (primary, info, action, divider, text) | #3 #8 #10 #12 | wire generated tokens; add `info`, `action.disabled*` | | Typography `color` codemod | #1 | `color="text.secondary"` → `textSecondary` in the 18 files (MUI v9 no-op) + an ESLint `no-restricted-syntax` rule so it can’t come back | | `MuiTextField` / `MuiOutlinedInput` / `MuiInputBase` / `MuiInputLabel` | #2 | white fill, `#E5E7EB` border, web field height (40–44 per web frames), radius `md` | | `MuiButton` | #4 #5 | remove the `contained` boxShadow (it silently defeats `disableElevation`), add `size="small"`, wire the hover token | | `MuiCheckbox` / `MuiRadio` / `MuiSwitch` | #11 | designed geometry (rounded 20×20 checkbox, 44×24 track + white 18px thumb), `#D1D5DB` unchecked stroke | | `MuiCard` | #7 | flat + hairline `1px #E5E7EB`, radius `lg` | | `MuiAlert` | #9 | explicit soft backgrounds from tokens; stop MUI’s `lighten()/darken()` recomputation | | `MuiChip` / `MuiTooltip` / `MuiSkeleton` | #16 | designed height/font, opaque `#1F2933` tooltip, `#F0F4F8` skeleton | | `MuiTableCell` / `MuiTableHead` | (Data Table note) | header fill `#F7F9FC`, `#E5E7EB` row borders, designed row heights | | `MuiDialog` / `MuiDialogTitle/Content/Actions` | (Confirm Dialog note) | radius `lg`, 24px action padding, designed gaps | | `shape` → radius scale usage | #14 | components pick `sm/md/lg` from the scale instead of one flat 8 | *Kills #1, #2, #4, #5, #7, #9, #10, #11, #14, #16 and most of every screen-level color/spacing note. This phase alone should flip the majority of the 96 ❌.* ## Phase 2 — Atom repairs + API promotion *(small; most “atom” work died in Phase 1)* [Section titled “Phase 2 — Atom repairs + API promotion (small; most “atom” work died in Phase 1)”](#phase-2--atom-repairs--api-promotion-small-most-atom-work-died-in-phase-1) Real component defects the theme can’t fix: * **Avatar**: soft variant (`primary-soft` fill + primary initials — today inverted), L-size font step. * **Icon**: default `size` + expose the 16/20/24/32/40 scale in the API, not just the showcase (#15). * **Button**: `loading` state must keep the active fill + white spinner + visible label (today it collapses to disabled-grey). * **Banner**: solid status glyphs, radius `sm`, 14px/700 title. * **StatusBadge**: use the `*-soft` tokens that already exist instead of `alpha(main, 0.12)`. * **Icon registry — no `gift` glyph** (#369): the EmptyState reference illustration is a gift glyph; the registry’s nearest product glyph (`Tag`) stands in (`EmptyStateShowcase.tsx`). Add a `gift` glyph or accept `Tag`. *(Gap confirmed + traced by the #617 showcase sweep.)* * **Button — no `:active`/pressed treatment (gap DS-2)** (#359): the DS Button defines no distinct pressed fill, so `ButtonShowcase` pins Pressed to the same fill as Hover. Add a pressed token or accept Pressed≡Hover. *(#617.)* * **KeypadKey — Pressed is fill-only** (#371): the pressed capture uses the atom’s real `selected` prop (brand-tint fill), which matches the Figma frame — no shadow/scale/border delta. Confirmed fill-only against Figma; no change needed unless the design adds depth cues. *(#617.)* * **Promote showcase `sx` hacks into real props** (#17): BalanceCard `tone`, IconButton variants, ProviderTile active, ListRow trailing, SummaryRow tone, Toast dark. Until these are API, parity can never be captured honestly. ## Phase 3 — Molecule/organism residuals [Section titled “Phase 3 — Molecule/organism residuals”](#phase-3--moleculeorganism-residuals) Work down the remaining ❌ notes that survive Phases 0–2 (re-audit first — don’t fix from stale notes): ConfirmDialog layout (centered column, equal-width actions, tinted icon container), PageHeader type hierarchy (2.0× title/subtitle ratio), chart series colors from the new data-viz tokens, DataTable row-hover/selected treatment. ## Phase 4 — Screen patterns + the PO decision list [Section titled “Phase 4 — Screen patterns + the PO decision list”](#phase-4--screen-patterns--the-po-decision-list) The consolidation the plan assumed is **mostly already true**: the DS `DataTable` backs 34 screens vs 2 legacy `GenericTable` usages, and `GenericForm` backs 20 forms. So “fix once, apply everywhere” is the architecture already — Phase 1’s table/dialog overrides restyle nearly all screens at once. Remaining screen work: 1. Migrate the 2 `GenericTable` stragglers; retire `src/ui/Generics` (the legacy-component audit already owns this). 2. **The 13 structural mismatches need a product decision each** — Figma designs a full page, the app implements a dialog over the list (W21 producto, W30 detalle de venta, W33 corte Z, W38/39 empleado/invitar, W56 tabs, W58, W46b, Categoría/Impuesto alta…). Per screen: keep the dialog (cheaper, revise Figma) or build the page (parity with design). Until decided, these screens’ style notes stay frozen. 3. Forms without a Figma frame follow the house standard (`useDialog` + `GenericForm` + DS atoms); forms *with* a frame follow the frame. ## Phase 5 — Collection hygiene *(parallel to everything)* [Section titled “Phase 5 — Collection hygiene (parallel to everything)”](#phase-5--collection-hygiene-parallel-to-everything) The 37 ⛔ entries: fix the `/reset-password` capture (add `?userId&secret` to its target), add targets for reachable-but-uncaptured states (W02 MFA), export the missing Figma nodes (W12, the four CLI frames), and re-run `pnpm screenshots` + `pnpm docs:parity`. ## Verification loop (the efficiency engine) [Section titled “Verification loop (the efficiency engine)”](#verification-loop-the-efficiency-engine) ```bash # per fix pnpm screenshots --grep # recapture just the affected surface node /imgtool.mjs sample … # sampled hex == token? pnpm docs:parity # notes preserved, gallery rebuilt # per phase pnpm screenshots && re-run the audit workflow for affected entries → notes flip to ✅ ``` Anti-regression: the ESLint rule from Phase 1, `verify:tokens` freshness check in CI, and the byte-stable screenshot harness (`git diff` on `docs/design-parity/screenshots/` as a visual- regression signal). ## Compatibility: design.md spec + white-label [Section titled “Compatibility: design.md spec + white-label”](#compatibility-designmd-spec--white-label) * **design.md spec:** DESIGN.md already follows it. Phase 0 makes the app actually *consume* it, which is the spec’s whole point — one machine-readable file drives the product. Future themes = another DESIGN.md-shaped document per brand. * **White-label:** `mergeMerchantTheme` (`src/ui/theme/WhiteLabelThemeProvider.tsx`) layers merchant overrides (`primary`, `accent`, `fontFamily`, `mode`) over the base theme via `createTheme(base, overrides)` — untouched by this plan. The base theme becomes *derived from DESIGN.md*; merchant overrides keep applying on top at runtime. One caution for Phase 1: derive tint roles (`primary-soft`) from `primary` at theme-build time, or merchants overriding `primary` will keep the default blue tint. ## Leverage map [Section titled “Leverage map”](#leverage-map) | Systemic finding | Killed in | | --------------------------------- | ------------------------------------------ | | #3 #6 #8 #12 #13 | Phase 0 (one source of truth) | | #1 #2 #4 #5 #7 #9 #10 #11 #14 #16 | Phase 1 (theme overrides) | | #15 #17 | Phase 2 (atom APIs) | | #18 | Phase 0 (tokens) + Phase 3 (charts wiring) | Order of execution: **0 → 1 → 2 → (re-audit) → 3 → 4**, with 5 running in parallel whenever. # Data model — POS collections > an entity-relationship view of the POS Appwrite collections, grouped by domain > **Purpose:** an entity-relationship view of the POS Appwrite collections, grouped by domain. **Status:** as of 2026-06-19. Authoritative definitions: [../business-rules/pos-domain-model.md](/business-rules/pos-domain-model/). Exact attributes are fixed in the AppwriteData tickets. ## Mermaid ER (key relationships) [Section titled “Mermaid ER (key relationships)”](#mermaid-er-key-relationships) ``` erDiagram MERCHANT ||--o{ STORE : "groups (merchantId)" STORE ||--|| STORE_TEAM : "is an Appwrite Team" STORE ||--o{ TERMINAL : has STORE ||--o{ EMPLOYEE : staffs AUTH_USER ||--|| EMPLOYEE : "profile (userId)" STORE_TEAM ||--o{ EMPLOYEE : "membership role = POS role" TERMINAL ||--o{ SHIFT : runs EMPLOYEE ||--o{ SHIFT : opens SHIFT ||--o{ CASH_MOVEMENT : records CATEGORY ||--o{ PRODUCT : groups PRODUCT ||--o{ VARIANT : has VARIANT ||--o{ BARCODE : "scanned by" PRODUCT }o--o{ TAX_RATE : "taxed by" VARIANT ||--o{ STOCK_LEVEL : "stocked per store" STORE ||--o{ STOCK_LEVEL : holds STOCK_LEVEL ||--o{ STOCK_MOVEMENT : "ledger of" CART ||--o{ LINE_ITEM : contains CART ||--o{ DISCOUNT : applies LINE_ITEM }o--|| VARIANT : "of" CART ||--o| SALES_ORDER : "finalized as" SALES_ORDER ||--o{ ORDER_PAYMENT : "paid by" SALES_ORDER ||--o{ TRANSACTION : "ledgered as" ORDER_PAYMENT }o--o| PAYMENT_INTENT : "via" SALES_ORDER ||--o| RECEIPT : "issues" STORE_TEAM ||--o{ AUDIT_LOG : "scopes (RLS)" SHIFT ||--o| SHIFT_REPORT : "frozen Z-report" STORE ||--o{ DAILY_SALES_ROLLUP : "aggregated per business date" ``` > `STORE_TEAM` is the store’s Appwrite Team; `AUTH_USER` is the Appwrite Auth user. POS role = the user’s `STORE_TEAM` membership role (no `pos_roles`/`pos_permissions` collections). ## Domain grouping [Section titled “Domain grouping”](#domain-grouping) | Domain | Collections | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **catalog** | `categories`, `products`, `variants`, `barcodes`, `tax_rates` | | **foundations** | `merchants`, `merchant_settings`, `merchant_themes`, `stores` (+`storeTeamId`,`merchantId`), `terminals`, `audit_log` | | **inventory** | `stock_levels`, `stock_movements` | | **sales** | `carts`, `line_items`, `discounts`, `sales_orders`, `customers`, `order_payments`, `transactions`, `payment_intents`, `receipts` | | **staff** | `employees` (profile), `shifts`, `cash_movements`, `approvals` — *roles via store-team membership; no `pos_roles`/`pos_permissions`* | | **reporting** | `shift_reports` (frozen Z-report snapshot), `daily_sales_rollups` (incremental per-store/day aggregate) — *derived & reconcilable, not source of truth* | | **wallet** (Track K) | `merchant_balances`, `balance_movements`, `deposits`, `charges`, `payments` — *merchant wallet ledger; see [../business-rules/pos-domain-model.md](/business-rules/pos-domain-model/) §wallet* | | **notifications** | `notifications`, `notification_reads` | | **config/platform** | `meta`, `feature_flags` | ### value\_added\_services *(see [../business-rules/value-added-services.md](/business-rules/value-added-services/))* [Section titled “value\_added\_services (see ../business-rules/value-added-services.md)”](#value_added_services-see-business-rulesvalue-added-servicesmd) > **Partly built (2026-07-12).** `providers`, `provider_products`, `serialized_units` are **live** in the config. `service_fulfillments` + `pin_vouchers` are **still to build** (documented but not yet in the schema), and the VAS field additions below are **not yet applied** to `line_items`/`transactions`. | Domain | Collections | | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **value\_added\_services** | `providers` ✅ (carriers/billers/PIN brands, synced mirror), `provider_products` ✅ (denomination/price mirror), `serialized_units` ✅ (chips/SIM inventory units), `service_fulfillments` ⬜ (external-call record + reconciliation), `pin_vouchers` ⬜ (PIN custody/delivery) | Plus **field additions** to existing collections: `products` (`kind`, `providerId`, `isVirtual`, `amountMode`, `referenceType`, `isSerialized`), `line_items` (`kind`, `providerId`, `providerProductId`, `recipientRef`, `denomination`, `serviceReference`; `variantId` optional), `transactions` (`type` += `REFUND`/`REVERSAL`, `status` += `PENDING_EXTERNAL`, `externalProviderId`, `externalFolio`, `externalStatusCode`). ## Notes [Section titled “Notes”](#notes) * **Tenant scoping:** every tenant-owned document is permission-scoped to its **store team** (`Role.team(storeTeamId)`); rows also carry `storeTeamId` + `merchantId`. Merchant is grouping data, not a permission boundary. *(Updated 2026-06-19: store=team.)* * **Append-only ledgers:** `stock_movements` and `transactions` are append-only; current state (`stock_levels`) is reconciled from movements via a durable workflow. * **Idempotency:** sales documents carry **`clientId`** for offline-safe sync. See [../business-rules/offline-and-idempotency.md](/business-rules/offline-and-idempotency/). * **New vs legacy:** `products`, `transactions`, `payment_intents`, `audit_log` are **new** Appwrite collections distinct from the legacy loan/financial entities, which stay in TypeORM until their own migration. * **Reporting persistence (hybrid):** `shift_reports` is a **frozen snapshot** written by the shift-close workflow; `daily_sales_rollups` is an **incremental aggregate** maintained on order finalize/void. Both key on the **store-local business date** (`stores.timezone`) and are **reconcilable** by replaying the `transactions` ledger — they are never the source of truth. Voids/refunds must decrement the affected rollup. See [../business-rules/reporting.md](/business-rules/reporting/). # Migration epics — dependency graph > show the order and dependencies of the Appwrite migration epics > **Purpose:** show the order and dependencies of the Appwrite migration epics. **Status:** as of 2026-06-19. Epic detail in [../progress/roadmap.md](/progress/roadmap/). ## Mermaid [Section titled “Mermaid”](#mermaid) ``` flowchart LR A["A · Foundation
CLI, ADR, SDK modules,
type-gen, CI"] B["B · Data
schema, repo layer,
catalog/foundations/
inventory/sales/staff, tests"] C["C · Auth
guard, teams,
login/2FA/forgot,
PIN, presence"] D["D · Storage
buckets, file service,
web/mobile upload"] E["E · Functions
scaffold + CI,
hello-world"] F["F · Client integration
RQ↔Appwrite adapter,
web re-point,
mobile offline-first"] G["G · Cutover
prod flags →
decommission TypeORM/
BetterAuth"] A --> B A --> C A --> D A --> E B --> F C --> F D --> G E --> G F --> G ``` ## ASCII (fallback) [Section titled “ASCII (fallback)”](#ascii-fallback) ```text ┌───────────────► B Data ───────┐ │ │ A Foundation ────┼───────────────► C Auth ───────┼──► F Client integration ──► G Cutover & │ │ decommission ├───────────────► D Storage ─────────────────────────────────► (G) │ │ └───────────────► E Functions ───────────────────────────────► (G) ``` ## Reading notes [Section titled “Reading notes”](#reading-notes) * **A (Foundation) is the only root** — everything depends on the SDK modules, schema skeleton, type-gen, and CI being in place. * **B (Data) and C (Auth)** feed **F (Client integration)** — clients can’t go direct until collections + auth exist. * **D (Storage) and E (Functions)** are parallel tracks that also gate **G**. * **G (Cutover)** depends on the user-facing Appwrite paths being live; it introduces production flags, then decommissions the legacy stack. It is effectively the sink of the DAG. * Within each epic, tickets follow the **Contracts → Data → Endpoints → UI → cleanup** ladder and are wired by `blocked_by`. See `ticket-conventions`. # System context diagram > show how clients, the API, and Appwrite relate at runtime > **Purpose:** show how clients, the API, and Appwrite relate at runtime. **Status:** as of 2026-06-19. ## Mermaid [Section titled “Mermaid”](#mermaid) ``` flowchart TB subgraph Clients WEB["Web
Next.js 16 / React 19"] AND["Android terminal
Kotlin (offline-first)"] end subgraph Backend API["NestJS API
+ DBOS durable workflows"] PSP["PSP / external services"] PG[("Postgres
legacy non-POS via TypeORM")] end subgraph Appwrite DB[("Databases
POS collections")] ACC["Account
sessions / JWT / MFA / custom token"] TEAMS["Teams
1 per merchant"] STO["Storage
buckets + file tokens"] FN["Functions"] RT["Realtime"] end WEB -- "simple reads/writes (SDK)" --> DB AND -- "simple reads/writes (SDK)" --> DB AND -- "cache refresh" --> RT WEB -- "cookie session" --> ACC AND -- "JWT" --> ACC WEB -- "complex commands (REST)" --> API AND -- "complex commands + PIN login (REST)" --> API API -- "validate Appwrite token" --> ACC API -- "orchestrate / write" --> DB API -- "mint custom token (PIN)" --> ACC API -- "capture/refund" --> PSP API -- "legacy domains" --> PG DB -. "permissions scoped by" .- TEAMS ``` ## ASCII (fallback) [Section titled “ASCII (fallback)”](#ascii-fallback) ```text Web (Next.js) Android terminal (Kotlin, offline-first) | \ / | cookie | \ simple reads/writes (SDK) / | JWT session | \------------> Appwrite <-------/ | | Databases / Account / Teams / | | Storage / Functions / Realtime | | ^ | | complex commands | validate token / | complex commands | (REST) | mint PIN custom token | + PIN login (REST) +-------------> NestJS API (+ DBOS) <-----------+ | \ PSP / external Postgres (legacy non-POS via TypeORM) ``` ## Reading notes [Section titled “Reading notes”](#reading-notes) * **Direct path:** clients use the Appwrite **SDK** for simple reads/writes and sessions. * **Complex path:** state-changing/durable commands go to **NestJS**, which validates the Appwrite token and may orchestrate Appwrite writes (DBOS for must-succeed flows). * **PIN login:** NestJS verifies the PIN and mints an Appwrite **custom token** the client exchanges for a session. * **Tenant isolation** is enforced by Appwrite **document permissions** tied to **Teams** (one per merchant). * **Postgres/TypeORM** remains only for **legacy non-POS** domains until their later migration. See `appwrite` and [../business-rules/auth-and-multitenancy.md](/business-rules/auth-and-multitenancy/). # Glossary > shared vocabulary for the POS domain and the Appwrite platform, so agents and humans use terms consistently > **Purpose:** shared vocabulary for the POS domain and the Appwrite platform, so agents and humans use terms consistently. **Status:** as of 2026-07-30. ## POS domain [Section titled “POS domain”](#pos-domain) | Term | Meaning | | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Merchant** | The organization that owns stores. An **org record in the `merchants` collection** (not a team). Stores reference it via `merchantId`. | | **Store** | An operational location belonging to a merchant. **Each store is an Appwrite Team** (the tenant boundary) and also a `stores` collection doc carrying `storeTeamId`. | | **Terminal** | A physical POS device (Android), paired to a store. Collection `terminals` (storeId, kind, pairingCode, lastSeenAt, currentShiftId). | | **Employee** | A staff member. Identity is an **Appwrite Auth user**; `employees` is a POS **profile** (pinHash, storeTeamId) linked 1:1 by `userId`. | | **POS role** | Per-store operational role: cashier / supervisor / manager. **= the store-team membership role.** Role→permission mapping is a code matrix (no `pos_roles`/`pos_permissions` collections). | | **Shift** | A cashier work session (open → close) tied to a terminal. Collection `shifts`. | | **Cash movement** | Cash in/out during a shift (drops, payouts, float). Collection `cash_movements`. | | **Product** | A POS sellable item (SKU). **New** Appwrite collection `products` (distinct from the legacy loan `Product`). | | **Variant** | A single-axis variation of a product (size/color). Collection `variants`. | | **Barcode** | A scannable code mapped to a variant. Collection `barcodes`. | | **Tax rate** | A configurable tax applied to line items. Collection `tax_rates`. | | **Stock level** | On-hand quantity for a (variant, store) pair. Collection `stock_levels`. | | **Stock movement** | Append-only inventory ledger entry. Collection `stock_movements`. | | **Cart** | An in-progress sale. Collection `carts` with `line_items`, `discounts`. | | **Sales order** | A finalized sale. Collection `sales_orders` with `order_payments`. | | **Tender** | A method of payment (cash, card, …) applied to an order. | | **Transaction** | POS financial ledger entry (`SALE`/`VOID`). New collection `transactions` (distinct from legacy financial Transaction). | | **Payment intent** | A PSP-agnostic intent to capture payment. Collection `payment_intents`; PSP adapter stays in NestJS. | | **Receipt** | A thin artifact representing a printed/issued receipt. Collection `receipts`. | | **Audit log** | Record of sensitive operations (action/entity/before/after). Collection `audit_log`. | | **clientId** | A client-generated UUID stamped on money-path writes so a **retry or replay is idempotent** (server dedups). Note: this is for *online* retries — writes are never queued offline (PO, 2026-07-06). | | **Z-report** | End-of-day summary report per terminal/store. | ## Appwrite platform [Section titled “Appwrite platform”](#appwrite-platform) | Term | Meaning | | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Project** | The Appwrite project (per environment). | | **TablesDB / Table / Row / Column** | Appwrite’s data primitives, **renamed from Database/Collection/Document/Attribute**. Use the new names in new writing; older docs and some code identifiers still say “collection” — read it as “table”. We use a **single shared database** with one table per POS domain. | | **Column / Index / Relationship** | Table schema elements, declared in `appwrite.config.json`. **Row-size limits are real:** a single string column is capped at 16 381 chars and a whole row at roughly 65 KB, so a big JSON blob cannot be one column — model the overflow as child rows (the `carts` pattern). | | **Query** | Appwrite’s query API (`Query.equal`, pagination, ordering) — our primary read path. | | **Account** | Per-user auth: sessions, JWT, MFA, recovery, custom tokens. | | **Session** | A logged-in user session (cookie on Web, token on Android). | | **JWT** | Short-lived token a client mints from its session; the API validates it. | | **Custom token** | Server-minted token used for PIN login (NestJS validates PIN → mints token). | | **MFA / 2FA** | Multi-factor auth via **email/SMS factors** (no TOTP): verify email/phone → enable → email/SMS challenge. | | **Team** | A group of users = **one per store**; membership roles are the POS roles. The tenant boundary for permissions. | | **Label** | Server-set user category; reserved for cross-merchant **platform** admins (`Role.label(...)`), not POS roles. | | **Presence** | Appwrite Presences API (GA 2026-05-25): per-user online/status records broadcast on `Channel.presences()`, auto-expiring. | | **Membership** | A user’s join to a team, with role(s) (owner/admin/member). | | **Permissions** | Per-document read/write rules (by team, role, user) enforcing tenant + store scoping. | | **Storage / Bucket / File token** | File storage; buckets declared as code; file tokens grant scoped access. | | **Function** | Server-side function deployed to Appwrite; we deploy via CI/CD. | | **Realtime** | Subscription channel pushing document changes to clients (cache refresh). | | **`appwrite.config.json`** | Schema-as-code: tables, columns, indexes, relationships, buckets, functions. Lives at `terminales-api/infra/appwrite/`. **Since M6 it is the only DDL path for Appwrite** — no console edits. | | **`appwrite push`** | Applies `appwrite.config.json` to an environment. Gated through `appwrite-deploy.yml` and preceded by `pnpm appwrite:validate`. | | **`appwrite types`** | Generates typed models from the schema (feeds API + Web). | ## Project / tooling [Section titled “Project / tooling”](#project--tooling) | Term | Meaning | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Project 11** | The GitHub Project board for this solution. | | **Epic A–G** | The Appwrite migration work, grouped (Foundation → Data → Auth → Storage → Functions → Client integration → Cutover/Decommission). **Complete** — see [../progress/roadmap.md](/progress/roadmap/). | | **Wave 1 / 2 / 3** | The successive planning-and-execution passes after the migration. **Wave 3** (from 2026-07-11) = verification & completion of the MVP. | | **M0–M11** | The milestone ladder in [../progress/milestones.md](/progress/milestones/). **M6** is the production-cutover gate (crossed 2026-07-24); **M11** is go-live. | | **DBOS** | Durable-execution framework used in NestJS for must-succeed workflows (finalize/settlement, VAS fulfillment, wallet serialization). | | **Spec-writer** | The convention/flow for authoring draft tickets. See `ticket-conventions`. | | **`agentic`** | The CLI that drives the whole pipeline — plan (draft/promote/sprint/assign) and dev (start/auto/review/pr/finish). Runs 24/7 in per-user loops on the ionos server. | | **ionos** | The remote development + hosting server (formerly “terminales-solution”). Runs the agent loops, the stage stack, and the observability stack. | | **Mock E2E / Live E2E** | The two Playwright suites. **Mock** = hermetic, `NEXT_PUBLIC_USE_MOCKS=true`. **Live** = `--project=live`, mocks OFF, against a real API + cloud Appwrite with the `e2e_store` seed. Tracked per story in [../product/user-stories.md](/product/user-stories/). | | **Design parity** | The Figma ↔ app comparison discipline: regenerate screenshots, diff against the frame, fix by layer. Galleries live in `terminales-web/docs/design-parity/` and `terminales/docs/design-parity/`. | | **W-xx / M-xx** | Canonical screen ids — web / mobile — from `flows-screens-master-map`. Every parity and story doc cites them. | | **Mantarys** | The VAS aggregator (the “VB webservice”): TAE, gift-card PINs, service/bill payment. See `mantarys`. | | **AngelPay** | The card PSP; card-present tenders route to a paired handheld terminal. | # Repositories > the repos that make up the solution, their roles, stacks, and key commands > **Purpose:** the repos that make up the solution, their roles, stacks, and key commands. **Status:** as of 2026-07-30. All repos are owned by `inspiraCode` and default to the `main` branch. Roles come from `terminales-solution.code-workspace`. **Branching:** the three **code** repos go through PRs. **`terminales-solution` commits straight to `main`** — no PRs for board, manifests, or docs. ## The four repos [Section titled “The four repos”](#the-four-repos) | Repo | Role | Purpose | | ----------------------------------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------- | | [`terminales-api`](https://github.com/inspiraCode/terminales-api) | `api` | NestJS backend: complex/durable business logic, token validation, DBOS workflows, PSP + VAS orchestration. | | [`terminales-web`](https://github.com/inspiraCode/terminales-web) | `web` | Next.js admin + browser terminal. | | [`terminales`](https://github.com/inspiraCode/terminales) | `mobile` | Android/Kotlin in-store terminal (offline-first **reads**; operational writes are online-only). | | `terminales-solution` | `multi` / infra | Planner board, manifests, conventions, this knowledge base, and the two published sites. | ## Stacks & commands [Section titled “Stacks & commands”](#stacks--commands) ### `terminales-api` (NestJS) [Section titled “terminales-api (NestJS)”](#terminales-api-nestjs) * **Stack:** NestJS + **Appwrite server SDK** (`node-appwrite`) + **DBOS**. TypeORM and BetterAuth are decommissioned; `typeorm` remains **only** to run migrations against the legacy Postgres remainder. * **Package manager:** pnpm. **Tests:** Jest. * **Commands:** `pnpm install` · `pnpm lint` · `pnpm test` · `pnpm test:e2e` · `pnpm start:dev` * **Schema & data:** `pnpm appwrite:validate` (offline schema check) · `pnpm appwrite:types` · `pnpm appwrite:backup` / `appwrite:restore` · `pnpm seed:e2e` + `pnpm verify:e2e-schema` (the E2E tenancy-graph seed) · `pnpm migration:{generate,create,run,revert,show}`. * **Key paths:** `src/auth/appwrite-auth.guard.ts` (+ `roles/`, `pin-login/`, `teams/`, `user-management/`, `platform-admin/`), `src/pos/**` (`customers`, `held-orders`, `inventory`, `notifications`, `foundations`, `audit`), `src/vas/**` (aggregator port + DBOS fulfillment), `src/wallet/**`, `src/reports/**`, `src/dbos/**`, `src/database/migrations/**` (+ `MIGRATIONS.md`, the DDL policy). * **Domain docs:** `docs/pos/4.APPWRITE_REUSE_ANALYSIS.md`; runbooks in `docs/runbooks/` (backup-restore, mantarys-connectivity). ### `terminales-web` (Next.js) [Section titled “terminales-web (Next.js)”](#terminales-web-nextjs) * **Stack:** Next.js 16, React 19, **MUI v9** (+ `x-charts`, `x-date-pickers`), TanStack React Query, Appwrite Web SDK, Axios, **Sentry**, Playwright. * **Package manager:** **pnpm**. **Tests:** Jest + Playwright. * **Commands:** `pnpm install` · `pnpm lint` · `pnpm typecheck` · `pnpm test` · `pnpm test:e2e` · `pnpm dev` * **Design parity:** `pnpm screenshots` (Playwright captures on its own :4000 prod server, mocks ON + all flags ON) · `pnpm docs:parity` (rebuild the gallery README, preserving hand-written notes) · `pnpm docs:parity:snapshot` (freeze a dated copy). * **Types:** `pnpm api-types` / `verify:api-types` (openapi-typescript) and `pnpm appwrite-types` / `verify:appwrite-types` — both coexist by design. `pnpm tokens:build` / `verify:tokens` for design tokens. * **Structure:** atomic design system under `src/ui/{atoms,molecules,organisms,templates,theme}` alongside the older named folders (`AppBar/`, `Buttons/`, `Generics/`, …) that it is progressively replacing — see `docs/design/legacy-deprecation-map.md`. Entity folders were relocated **out of the route tree** (2026-07-28); routes live in `src/app`, components in `src/components`. `USE_MOCKS` from `src/env.ts`. See `web-data-layer`. ### `terminales` (Android / Kotlin) [Section titled “terminales (Android / Kotlin)”](#terminales-android--kotlin) * **Stack:** Android, Kotlin, Jetpack **Compose**, Hilt, Room, `io.appwrite:sdk-for-android`. Package `com.axios.terminales`. * **Build:** Gradle. **Tests:** JUnit; **Maestro** for the screenshot/parity harness. * **Commands:** `./gradlew dependencies` · `./gradlew lintDebug` · `./gradlew testDebugUnitTest` · `./gradlew installDebug` * **Structure:** feature packages under `app/src/main/java/com/axios/terminales/` (`auth`, `shifts`, `payment`, `vas`, `wallet`, `customers`, `reporting`, `notifications`, `terminal`, `account`) with the design system in `ui/components/terminales/{atoms,molecules,organisms}`; `harness/` backs the Playground captures. * **Parity:** flows in `tools/*.yaml` (Maestro) + `tools/capture-screenshot.sh`; gallery built by `tools/build-gallery.mjs` into `docs/design-parity/`. ### `terminales-solution` (planner / infra / sites) [Section titled “terminales-solution (planner / infra / sites)”](#terminales-solution-planner--infra--sites) * **Holds:** `board/` (status mirror), `manifests/`, `backlog/{ideation,drafts}`, `conventions/`, `review/` (the reviewer rubric), `runbooks/`, `solution.yml` (Project 11 metadata, sprints, agent roster), and `docs/` (this knowledge base). * **Also ships three sites:** `docs-site/` (Astro **Starlight** → [docs.io.inspiracode.com](https://docs.io.inspiracode.com); publishes only the allowlist in `publish.config.mjs`) and `presentacion/` (password-gated Spanish owner-facing progress site, SSR, at presentacion.io.inspiracode.com) and `manuales-site/` (password-gated manuals, at manuales.io.inspiracode.com) — all three served from ionos, see `ci/docs-sites/README.md`. * **Auto-commit:** the `agentic` CLI auto-commits only `board/`, `manifests/`, `backlog/`. `docs/` is **not** auto-committed — commit it deliberately. ## Repo selection for tickets [Section titled “Repo selection for tickets”](#repo-selection-for-tickets) Each ticket targets exactly one repo (`agent_meta.repos` = single element) — **except E2E tickets** (`agent_meta.e2e: true`), which may list several because a flow is verified across the live stack. The title’s first token (`API` / `Web` / `Mobile` / `Infra` / `Multi`) and `repos` disambiguate which repo `dev-start` uses. See `ticket-conventions`. # System overview > explain what terminales is, who it serves, and where it's headed > **Purpose:** explain what `terminales` is, who it serves, and where it’s headed. **Status:** as of 2026-07-30. ## What it is [Section titled “What it is”](#what-it-is) `terminales` is a **multi-tenant Point-of-Sale (POS)** platform. Multiple merchants operate independently on shared infrastructure; each merchant runs one or more **stores**, each store has one or more **terminals** (Android devices), staffed by **employees** who open/close **shifts** and process **sales**. It is delivered as: * a **web admin + web terminal** (Next.js) for back-office and browser-based selling, * an **Android terminal app** (Kotlin) for in-store selling — offline-capable for **reads**, but never for taking a sale, * an **API** (NestJS) for complex/durable business logic, * a **backend platform** (Appwrite) providing data, auth, storage, and functions. ## Goals [Section titled “Goals”](#goals) 1. **Sell reliably — and never sell what cannot be settled.** *(Revised 2026-07-06.)* The Android terminal keeps **reading** from its local cache without connectivity (catalog, cached reports, notifications, theming), but every money-path write requires the network: the UI blocks with a clear “sin conexión” state and **nothing is queued**. Reliability is provided server-side by **DBOS** durable workflows plus `clientId` idempotency for online retries — not by device-side replay. 2. **Isolate merchants safely.** One merchant must never see another’s data; isolation is enforced at the data layer, not just the UI. 3. **Reduce backend maintenance surface.** Move CRUD/data/auth/storage onto **Appwrite** (managed primitives) and reserve hand-written server code for logic that genuinely needs it (durability, PSP orchestration, fiscal rules). 4. **Keep clients fast on low-resource devices.** Clients talk to Appwrite **directly** for simple reads/writes; only complex commands round-trip through the API. 5. **Reproducible environments.** Schema, buckets, and functions are **code** (`infra/appwrite/appwrite.config.json`), applied per environment via CLI/CI. Since the M6 gate this is the *only* DDL path for Appwrite — no console edits — with reviewed TypeORM migrations the only one for the legacy Postgres remainder. ## Current phase: verifying a feature-complete MVP on stage [Section titled “Current phase: verifying a feature-complete MVP on stage”](#current-phase-verifying-a-feature-complete-mvp-on-stage) The system was first built on **NestJS + TypeORM + BetterAuth**. That backend has been **fully migrated to Appwrite** and the legacy stack removed: * **Data** → Appwrite **TablesDB** (the POS domain). * **Auth** → Appwrite Account + Teams (BetterAuth removed entirely, 2026-07-04). * **Storage** → Appwrite Storage buckets with scoped file-tokens. * **Functions** → Appwrite Functions, deployed by CI. * **NestJS/DBOS stays** for complex/durable logic — settlement, VAS fulfillment, wallet integrity, PSP orchestration. Three gates have been passed since: the **migration** (Epics A–G, complete 2026-07-03), the **stage environment** (`api-stage.io.inspiracode.com` on the ionos server, 2026-07-08), and the **M6 production-cutover gate** (2026-07-11..24) — which ended the pre-production regimes. Feature flags now exist and, per the PO’s 2026-07-14 directive, are **all ON**; database migrations are back as the only DDL path; CI is re-enabled and runs on self-hosted hardware. What is left is **verification, not construction**. Every MVP user story has an implementation; each is being driven to green on three independent axes — Built, Mock E2E, Live E2E — in [../product/user-stories.md](/product/user-stories/) (web) and [../product/mobile-user-stories.md](/product/mobile-user-stories/) (mobile), alongside Figma ↔ app parity galleries for both clients. The one structural gap remaining is the **VAS aggregator transport** (`Mantarys`). There are still **no live users and no production release**. ## How work is organized [Section titled “How work is organized”](#how-work-is-organized) * **Backlog & board:** GitHub **Project 11** (`inspiraCode`). The solution root (`terminales-solution`) holds the planner mirror (`board/`, `manifests/`, `backlog/`) and conventions. * **Tickets** are authored as drafts in `backlog/drafts/` following the spec-writer flow, then uploaded via `agentic plan draft`. See `ticket-conventions`. * **Implementation is agent-driven.** Per-user autonomous loops run tickets end-to-end (implement → commit → self-review → PR) on the ionos server via the `agentic` CLI; loops are `--assigned-only`, so unassigned work sits idle. See `agentic-workflow`. * **Knowledge** (this directory) is the durable big-picture layer; granular tasks stay on Project 11. * **Stakeholders read two published sites:** the knowledge-base site (`docs-site/` → [docs.io.inspiracode.com](https://docs.io.inspiracode.com), allowlisted subset only) and a password-gated Spanish progress site (`presentacion/`). ## Related reading [Section titled “Related reading”](#related-reading) * [repositories.md](/overview/repositories/) — the four repos in detail. * [../product/user-stories.md](/product/user-stories/) — what works today, story by story. * `appwrite` — the backend platform. * [../progress/milestones.md](/progress/milestones/) — the milestone ladder M0–M11 (current state). * [../progress/roadmap.md](/progress/roadmap/) — the migration plan (Epics A–G; complete, historical). # PDR 0001 — MVP go-live scope > freeze what is IN and OUT of the production MVP so the Wave 3 backlog contains > **Purpose:** freeze what is IN and OUT of the production MVP so the Wave 3 backlog contains no speculative work and the go-live date has a bounded scope. This closes the §4.6 “Decisiones de PO pendientes” table of the Wave 2 analysis. * Status: Accepted * Date: 2026-07-11 * Deciders: Product Owner (Wave 3 planning session) * Repos: Multi * Related: `wave2-mvp-analysis` §4.6, [../adr/0002-full-appwrite-migration-decommission.md](/adr/0002-full-appwrite-migration-decommission/), `0003-vas-fulfillment-dbos`, [../business-rules/value-added-services.md](/business-rules/value-added-services/), `flows-screens-master-map` ## Context [Section titled “Context”](#context) Wave 2 closed with an open PO-decision table (§4.6): eleven product calls, each with draft-ready candidates, that had to land before MVP scope could be declared final. Two were resolved into Wave 2 (held orders → W64, denominación → W65). The rest were “deferred to a potential wave 3.” Wave 3 is that wave, and the PO has now made the scope call. ## Decision — VAS + Clientes are IN; the rest of §4.6 is deferred [Section titled “Decision — VAS + Clientes are IN; the rest of §4.6 is deferred”](#decision--vas--clientes-are-in-the-rest-of-46-is-deferred) **In scope for the production MVP (build in Wave 3):** * **VAS / servicios financieros** (W-09 + M-5) — the special-categories vertical: airtime/TAE, gift-card PINs, service/bill payment, and SIM chips, plus the legacy financial domains (deposits, transfers, wallet/merchant-balance) that back W44–W47. Migrated to the Appwrite POS model per [value-added-services.md](/business-rules/value-added-services/) and `TDR-0003`. This also drives the Bucket-2 domains of the [full migration](/adr/0002-full-appwrite-migration-decommission/). *Phasing amendment (2026-07-11, [PDR-0003](/pdr/0003-vas-connectivity-phasing-and-wallet-visibility/)):* MVP ships **Phase 1** connectivity only (single shared VB webService user); **Phase 2** (per-merchant webService users) and **Phase 3** (direct provider integration + certification) are post-MVP, as are bank-statement-import deposit validation and the wallet withdrawal/payout flow. * **Clientes / Customers** (BG-1; web W-13 `231:3`–`233:383`, mobile M30–M33, gaps #25/#42/#43/#45) — customer directory + sale linkage (`sales_orders.customerId`). Wallet/fiado stays parked; the `customer` role stays reserved (no self-service portal in MVP). **Deferred (post-MVP; kept in the §5 post-MVP catalog and the Wave 3 §5):** * **QR tenders CoDi/SPEI + CFDI receipt QR** (BG-9; gaps #29/#39/#40) — the 5th tender and CFDI QR. Keep the payment-method enum and receipt schema additive/extensible so it lands later without a schema break. * **Cobro rápido + UX v2 absorptions** (gaps #28/#30/#31/#32/#44) — quick-charge modal, quick-actions FAB, PIN profile selector, low-stock KPI quick-filters. * **POS interaction modes** — tap-to-add / long-press details, fullscreen POS mode. * **Dashboard KPI drill-down** (W07; gap #8) — per-KPI destination wiring. **Rationale:** the client needs the special-categories/financial vertical to operate their business day one (it is a core revenue surface, not a nice-to-have), and customer capture is a low-cost additive that several flows already anticipate. QR tenders, cobro-rápido, POS-interaction, and KPI drill-down are speed/UX enhancements or depend on external/CFDI tracks — deferring them does not stop the client operating live, and each has an additive seam so it lands cleanly later. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected) * **Verify-and-harden only (defer VAS + Clientes too)** → rejected; VAS is a primary revenue surface the client needs at launch, so shipping without it is not a viable MVP for them. * **All deferred modules in (also QR/cobro-rápido/POS-interaction/KPI)** → rejected; that scope pushes the launch date out for enhancements the client can operate without. ## Consequences [Section titled “Consequences”](#consequences) * Track C (VAS) and Track D (Clientes) become MVP-gating work in the Wave 3 backlog. * The VAS vertical discriminator is **`products.kind`** (enum `[PHYSICAL, TAE_AIRTIME, GIFT_PIN, SERVICE_PAYMENT]`), per [value-added-services.md](/business-rules/value-added-services/) — this **supersedes** the tentative `products.vertical` name floated in Wave 2 §4.6. * The deferred items keep their §5 additive-readiness requirements (enum-extensible tender/receipt schemas, `sales_orders.customerId` optional, etc.). * MVP scope is now **final** — the Wave 3 backlog draws no work from outside this decision. ## References [Section titled “References”](#references) * [../business-rules/value-added-services.md](/business-rules/value-added-services/) — VAS domain model. * [../adr/0002-full-appwrite-migration-decommission.md](/adr/0002-full-appwrite-migration-decommission/) — VAS domains are Bucket 2. * `wave2-mvp-analysis` §4.6 + §5. # PDR 0002 — MVP reports catalog > record the product decision on which reports the MVP must ship, so Wave 3 > **Purpose:** record the product decision on which reports the MVP must ship, so Wave 3 verifies against a fixed list and the deferred reports are explicit. The domain detail (data, filters, sources, fields) lives in [reporting.md](/business-rules/reporting/); this PDR records the *scope decision* and the verification obligation. * Status: Accepted * Date: 2026-07-11 * Deciders: Product Owner (confirmed in [reporting.md](/business-rules/reporting/), 2026-07-09) + Wave 3 planning * Repos: `terminales-api`, `terminales-web`, `terminales` * Related: [../business-rules/reporting.md](/business-rules/reporting/), `wave3-mvp-analysis` §3.6 + Track G ## Context [Section titled “Context”](#context) The Wave 2 analysis drafted “remaining MVP report endpoints” (rows 40/41) as build work. As of 2026-07-09 the API MVP report suite is **implemented** (`src/reports/pos/mvp-reports.controller.ts`, #178) with CSV export (`report-csv.util.ts`) and rollup-backed long ranges (`daily_sales_rollups` via `src/pos/sales/finalize-workflow.ts`). So in Wave 3 reports become a **verification** obligation (do the eight work correctly, are the web views present, are the margin/rollup fields deployed) rather than net-new build — this PDR pins the list they verify against. ## Decision — eight must-have MVP reports; CSV-only export for v1 [Section titled “Decision — eight must-have MVP reports; CSV-only export for v1”](#decision--eight-must-have-mvp-reports-csv-only-export-for-v1) **The MVP report set (must exist and be correct at go-live):** 1. **Daily Sales Summary (Corte Diario)** — gross/net/tax/discounts/refunds, txn count, avg ticket, units, sales-by-tender, sales-by-hour. 2. **Shift Z-Report & Cortes de caja history** — frozen `shift_reports` snapshot; expected vs counted cash + variance; reprintable. 3. **Sales by Product / Category** — units, gross, net, **margin** (needs `line_items.unitCost` from `products.costPrice`), top-N. 4. **Sales by Payment Method (tender mix)** — amount/count/%, commissions, net, effective rate. 5. **Sales by Employee** — per-cashier sales, txn count, avg ticket, discounts, voids, cash variance. 6. **Commissions Report** (W57) — by provider/method/concept; depends on `transactions.commission` / `netAmount` (AngelPay Q5 for card commission data). 7. **Cash Movements Report** — non-sale cash in/out from `cash_movements`. 8. **Voids & Discounts (exceptions)** — voided orders + applied discounts with who authorized. **Export:** **CSV only for v1.** PDF, share-sheet, and email delivery are explicitly future formats. **Scope rules carried from [reporting.md](/business-rules/reporting/):** reports are **online-only** (the offline terminal computes no aggregates); rollups/snapshots are **derived & reconcilable, never authoritative** (rebuildable from the `transactions` / `stock_movements` ledgers; voids **decrement**, never drop); everything keys on the **store-local business date** (`stores.timezone`), not UTC; RLS scopes every read by role (cashier = own shift, supervisor = store, manager/owner = portfolio). **Deferred (post-MVP “later phases”):** Inventory/Stock valuation, Sales by Terminal/Device, Multi-store Comparison, Tax/Fiscal CFDI, Merchant Balance/Settlement, Refunds, Sales Trends/ABC, and Customer reports (the last blocked until the Clientes module lands per [PDR-0001](/pdr/0001-mvp-golive-scope/)). **Rationale:** these eight cover the operational, cash-reconciliation, and loss-prevention needs a merchant has on day one; CSV is a universal, low-effort export that unblocks accounting without a rendering pipeline; the deferred set is analytics/fiscal depth the client can operate without. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected) * **Ship reports with PDF/email export in v1** → deferred; CSV meets the day-one need, richer formats are a renderer swap later (§5 print-from-web readiness). * **Include Customer/Refund/Multi-store reports in MVP** → deferred; they depend on post-MVP entities (customers, explicit refund model) or are portfolio analytics beyond the launch need. ## Consequences [Section titled “Consequences”](#consequences) * Track G includes a **reports verification** ticket per platform: prove the eight endpoints return correct figures (incl. void-decrement and business-date correctness), confirm the web report views (W55–W58) render them with the documented filters + CSV export, and verify the reporting field additions (`line_items.unitCost`, `sales_orders.businessDate`, `transactions.commission/netAmount`, etc.) are deployed. * The Commissions report (6) stays gated on the AngelPay Q5 commission-data answer; it is verified as far as its data source allows and flagged where it can’t be. ## References [Section titled “References”](#references) * [../business-rules/reporting.md](/business-rules/reporting/) — full report catalog, sources, fields, indexes. * [./0001-mvp-golive-scope.md](/pdr/0001-mvp-golive-scope/) — Customer reports blocked on Clientes. # PDR 0003 — VAS provider connectivity phasing & wallet visibility > record the Product Owner's 2026-07-11 decisions on (a) how terminales connects to > **Purpose:** record the Product Owner’s 2026-07-11 decisions on (a) how terminales connects to the external VAS providers across three phases, (b) which balances exist, who sees each, and what credits/debits them, and (c) the deposit channels that fund the merchant wallet. These decisions revise the Wave-2-era “two-wallets display” rule and drive the MerchantBalance track and the VAS Phase-1 integration. * Status: Accepted * Date: 2026-07-11 * Deciders: Product Owner (Wave 3 refinement session) * Repos: Multi — `terminales-api`, `terminales-web`, `terminales` * Related: `0004-merchant-wallet-dbos-integrity` (the wallet engine), `0003-vas-fulfillment-dbos` (amended by this PDR), [./0001-mvp-golive-scope.md](/pdr/0001-mvp-golive-scope/), [../business-rules/value-added-services.md](/business-rules/value-added-services/), `apitae-webservice` > ## Amendment 2026-08-03 — external platform balances ARE shown and CAN fund VAS > > [Section titled “Amendment 2026-08-03 — external platform balances ARE shown and CAN fund VAS”](#amendment-2026-08-03--external-platform-balances-are-shown-and-can-fund-vas) > > **Deciders:** Product Owner (client meeting, 2026-08-03). **Status:** Accepted; amends Decision 1 (Phase 2) and Decision 2 of this PDR. Drives the **Wave-3 “Plataformas externas de saldo”** design module (Figma web flow **W-14** / mobile **M-9** — see `figma-wave3-external-platforms-prompts`). > > The client decided terminales will **support external balance platforms** as a first-class product surface. This changes two things previously fixed here: > > 1. **External platform balances ARE displayed** (reversing Decision 2’s “the VB webService balance is NEVER displayed — in no phase”). A merchant **connects one or more external platforms** (Mantarys and its instances **VentaMóvil / Sureste**, which are the same aggregator behind different backend URLs, plus a generic “Otra plataforma”), each with its **own per-merchant username/password credential** (the Phase-2 seam `resolveVbCredentials(merchantId)`, now brought forward as a product feature). The connected platforms and **their balances are shown, owner-only** (same `merchants.ownerId` guard as the merchant wallet). Auth is **username + password**, not Keycloak. > 2. **A connected platform’s balance is a funding source for VAS** (extending Decision 3’s tender list). At checkout the operator may pick a connected platform whose balance funds the **VAS portion** of the sale — a money path **distinct from and additional to** the merchant-wallet debit (which remains the default). Insufficient platform balance → that platform is disabled at selection and, if none suffice, the VAS sale is blocked (no negatives). Online-only (no queue), idempotent by `clientId`. > > **Unchanged:** the merchant wallet (`merchant_balances`) and store cash keep their audiences and rules; the two ledgers stay independent (platform-balance debits are a **separate ledger per connection**, not mirrored into `balance_movements`). Still-open (gaps #46–#49 in the design master map): the per-merchant **connection data model** + credential encryption + per-platform balance ledger; reversal/ timeout handling for platform-funded VAS; the “recargar saldo” destination; and mixed-cart (products + VAS) split behavior. > ## Amendment 2026-08-04 — adding balance to a platform (wave 3.1) > > [Section titled “Amendment 2026-08-04 — adding balance to a platform (wave 3.1)”](#amendment-2026-08-04--adding-balance-to-a-platform-wave-31) > > **Deciders:** Product Owner. **Status:** Accepted; extends Decision 3 (deposit channels / crediting matrix). Drives the **wave-3.1** “Agregar saldo + Depósitos” design (`§Q` in `figma-wave3-external-platforms-prompts`). > > Merchants can **top up** a connected platform balance (or terminales’ own wallet) from the handheld. This makes each **per-platform ledger bidirectional** (credits from top-ups/deposits + debits from VAS). Funding sources: (1) **card on the handheld** (channel-E style, immediate — reuses the AngelPay bridge); (2) **transfer from the merchant wallet** (immediate); (3) a **deposit** with a new **`target`** (merchant wallet **or** a connected platform) — bank channels A–C stay **PENDING → owner-validated**, cash D immediate. This resolves the “recargar saldo” destination (design gap #48). > > **Vendor dependency (not a design blocker):** the Mantarys webservice has **no inbound-credit method** (`Check_Balance` only reads; `Request_Transaction` only debits). So the card-charge top-up is designed forward-looking: terminales records the credit against the per-platform ledger and reconciles the shown balance against `Check_Balance`; the real provider-side crediting requires a **new Mantarys integration** (design gap #50). The `deposits` model gains a `target` (design gap #51). Ledgers stay independent — a platform top-up credit is **not** mirrored into `balance_movements`. ## Context [Section titled “Context”](#context) [PDR-0001](/pdr/0001-mvp-golive-scope/) put VAS + the financial domains in MVP scope, and `TDR-0003` fixed the fulfillment model (aggregator adapter + DBOS durable workflows over the VB webService). Two questions stayed open: **how credentials/ connectivity to the external system evolve**, and **which balance the product actually shows and spends**. The Wave-2-era docs assumed a per-tenant VB credential model and allowed displaying the VB provider float (“two-wallets rule … display/low-float warning”). The PO has now decided both. ## Decision 1 — provider connectivity in three phases [Section titled “Decision 1 — provider connectivity in three phases”](#decision-1--provider-connectivity-in-three-phases) * **Phase 1 (MVP):** terminales uses the client’s **existing system** (the VB webService + SQL Server + provider connections that also back `apitae-sales`) through a **single shared webService user managed in the terminales backend**. terminales makes purchases, deposits and recharges **on behalf of all merchants** with this one credential. Entry point is the **VB webService directly** (the aggregator adapter of TDR-0003) — terminales does **not** call the `apitae-sales` REST API. This is explicitly a **temporary MVP solution**. * **Phase 2 (post-MVP):** each merchant connects **their own webService user**, so merchants transact on their own behalf. The Phase-1 credential resolution must therefore be a **seam** (resolve credentials per call from config; MVP config returns the shared user). * **Phase 3 (post-MVP, future):** direct integration with the providers themselves (TAE, services, …), requiring **per-provider certification** of the terminales solution. **Rationale:** Phase 1 reuses infrastructure the client already operates and trusts, with zero provider-side certification effort — the fastest path to a working MVP. The per-call credential seam makes Phase 2 a configuration change, not a rewrite. Phase 3 is deferred because certification is long-lead and not needed to operate. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected) * **Through `apitae-sales` (REST)** → rejected; adds a hop and couples the MVP to the legacy API surface. The aggregator calls the VB methods directly (TDR-0003’s design stands). * **Per-merchant webService users at MVP** → deferred to Phase 2; onboarding every merchant with the upstream provider before launch would delay go-live for no day-one benefit. ## Decision 2 — two balances, two audiences; the VB balance is never displayed [Section titled “Decision 2 — two balances, two audiences; the VB balance is never displayed”](#decision-2--two-balances-two-audiences-the-vb-balance-is-never-displayed) * **Merchant wallet (`merchant_balances`)** — lives in and is fully managed by terminales. It is the balance the **merchant owner** sees in the web/mobile apps, the balance that **funds VAS operations** (purchases, deposits, recharges — terminales pays the provider on the merchant’s behalf in Phase 1), and the balance that is **withdrawable to the merchant’s bank account** (withdrawal flow itself is post-MVP — seam only, see `TDR-0004`). **Visibility: merchant owners only** — cashiers/supervisors have no wallet read access. *Ownership mechanism (decided 2026-07-12):* owner = the additive **`merchants.ownerId`** column (Appwrite Auth user id — the field [roles-permissions-and-labels.md](/business-rules/roles-permissions-and-labels/) already assumed); the wallet guard (`assertMerchantOwner`) resolves against it and fails closed when unset. A store-team `manager` who is not the owner is denied. * **Store cash balance** — the existing cash-control domain (shift open float, cash movements, Z-reports). This is what **employees** see and reconcile. It is not a new entity; it stays on the shipped shifts/cash\_movements/shift\_reports collections. * **The external VB webService balance (`Check_Balance`) is NEVER displayed — in no phase.** It may be read internally (ops metric / low-float alert for the platform) but never surfaces in any UI or in any API response consumed by a UI. — **⚠ AMENDED 2026-08-03 (see Amendment at top):** connected **external platform balances ARE now displayed, owner-only**, in the Wave-3 “Plataformas externas de saldo” module. The internal-only rule no longer holds for platforms a merchant has explicitly connected with their own credential. **Rationale:** the merchant wallet is the product’s own money surface; showing a supplier float merchants cannot act on adds confusion and support load. The owners-only rule keeps wallet data (revenue-shaped) away from store staff, matching the roles model. ### Alternatives rejected [Section titled “Alternatives rejected”](#alternatives-rejected-1) * **Displaying the VB float read-only with a low-float warning** (the Wave-2 rule in TDR-0003 Decision 4 / value-added-services.md) → **superseded by this PDR**; internal-only now. * **One balance for everything (wallet = store cash)** → rejected; cash physically in the drawer and platform-held funds are different money with different audiences and controls. ## Decision 3 — deposit channels and the crediting matrix [Section titled “Decision 3 — deposit channels and the crediting matrix”](#decision-3--deposit-channels-and-the-crediting-matrix) A **Deposit** is a “payment” made by a customer to the merchant (or a top-up by the merchant), with statuses `pending → declined | available → applied → canceling | canceled | refunded`. A deposit **credits nothing until it is `available`**. | Channel | How | Initial status | Credits | | ------- | --------------------------------------------------------------------------------------------- | ----------------------- | ----------------------------------------------------------------------- | | **A** | Customer deposits at a bank branch to the merchant’s account; receipt optionally uploaded | `pending` | **Wallet**, after manual validation → `available` | | **B** | Customer bank-transfers from their own bank app; receipt optionally uploaded | `pending` | **Wallet**, after manual validation → `available` | | **C** | Customer deposits cash at a convenience store; receipt optionally uploaded | `pending` | **Wallet**, after manual validation → `available` | | **D** | Customer pays the merchant **in cash, in person**; merchant registers it and prints a receipt | `available` immediately | **Store cash** (never the wallet — the merchant already holds the cash) | | **E** | Customer pays by **card on the handheld terminal**; receipt printed | `available` immediately | **Wallet** | * **Autopay top-up:** the merchant or an employee can top up the **merchant wallet by card on the handheld** — mechanically a channel-E deposit without a cart; needs its own handheld menu/screen. Credits the wallet immediately. * **Validation:** in MVP, `pending` deposits (A/B/C) are validated **manually by the merchant** (owner/manager) to flip them to `available`. **Bank-statement import** for automatic validation is **post-MVP** (seam noted in the deposit-channels ticket). * **Balance as a tender:** customers can pay with cash, card, or **balance**; attempting to pay with balance when there is none → **the sale is blocked** (no negative balances, no credit line in MVP). — **⚠ EXTENDED 2026-08-03 (see Amendment at top):** a connected **external platform’s balance** is an additional funding source for the **VAS portion** of a sale (distinct from the merchant-wallet debit); same “blocked when insufficient” rule applies. **Rationale:** the matrix mirrors where the money physically lands — cash in the drawer belongs to store cash control; everything that reaches the merchant’s account through the platform rail becomes platform-held wallet balance once verified. Manual validation is the cheapest safe MVP control; statement import automates it later without changing the state machine. ## Consequences [Section titled “Consequences”](#consequences) * The **Wave-2 “two-wallets display” rule is superseded**: `TDR-0003` Decision 4 is amended (VB float internal-only; **VAS fulfillment debits the merchant wallet** via the `TDR-0004` engine). The ledgers remain independent — VB movements are still never mirrored into `balance_movements`. * The W44–W47 wallet screens display **only** the merchant wallet (owners-only) — the VB-float panel is removed from the web financial-services draft. * New MerchantBalance track (Track K / `manifests/wave3-10-merchant-balance.tsv`): wallet schema * DBOS engine + deposit channels/validation + VAS debit wiring + handheld autopay top-up. * Post-MVP catalog gains: Phase 2 (per-merchant webService user), Phase 3 (direct provider certification), bank-statement import validation, withdrawal/payout flow. * The per-tenant credential text in `apitae-webservice` is annotated as the Phase-2 model; Phase 1 uses the single backend-managed shared user. ## References [Section titled “References”](#references) * `0004-merchant-wallet-dbos-integrity` — how the wallet stays correct under concurrency. * `0003-vas-fulfillment-dbos` — fulfillment model this PDR amends. * [../business-rules/value-added-services.md](/business-rules/value-added-services/) · `apitae-webservice`. * [../product/user-stories.md](/product/user-stories/) — the BAL story area implements this PDR. # Visual plans > Interactive visual plans for the terminales POS flows (web + mobile), in the agentic-plans app Interactive visual documentation for the terminales POS flows — wireframes, screen boards, implementation maps, and decisions. Each link opens the live, pannable canvas in the visual planner; **no account is required**. ## Web [Section titled “Web”](#web) * [W-01 · Acceso y Onboarding](https://plans.io.inspiracode.com/plans/plan-68c53d9e9f054bab) * [W-02 · Inicio y Navegación](https://plans.io.inspiracode.com/plans/plan-cdff4ab95a804069) * [W-03 · POS Web — Venta](https://plans.io.inspiracode.com/plans/plan-4ac96253bf0a4cb4) * [W-04 · Catálogo](https://plans.io.inspiracode.com/plans/plan-b8fb13e53bcc4ea6) * [W-05 · Inventario](https://plans.io.inspiracode.com/plans/plan-d1e57acfa3744702) * [W-06 · Ventas y Tickets](https://plans.io.inspiracode.com/plans/plan-faf9e90daa344dea) * [W-07 · Turnos y Caja](https://plans.io.inspiracode.com/plans/plan-6d229f1a7e184e2e) * [W-08 · Personal](https://plans.io.inspiracode.com/plans/plan-a0eefafd022c4e0a) * [W-09 · Servicios financieros](https://plans.io.inspiracode.com/plans/plan-cb7b02708df64daa) * [W-10 · Comercio, Tiendas y Terminales](https://plans.io.inspiracode.com/plans/plan-d0751d42ab864b60) * [W-11 · Reportes](https://plans.io.inspiracode.com/plans/plan-2a890d73b4374b84) * [W-12 · Cuenta y Ajustes](https://plans.io.inspiracode.com/plans/plan-ae6c1e90d3fb4602) * [W-13 · v2 — nuevas funciones](https://plans.io.inspiracode.com/plans/plan-ad5f1ab218ab4bc3) ## Móvil [Section titled “Móvil”](#móvil) * [M-1 · Acceso y Onboarding](https://plans.io.inspiracode.com/plans/plan-9a30d3fdce894c1a) * [M-2 · Inicio y Turno](https://plans.io.inspiracode.com/plans/plan-7d6af91925f84078) * [M-3 · Venta POS](https://plans.io.inspiracode.com/plans/plan-2394e2d950c94963) * [M-4 · Cobro y Recibos](https://plans.io.inspiracode.com/plans/plan-0edebc4873e44591) * [M-5 · Categorías especiales](https://plans.io.inspiracode.com/plans/plan-753a2040d8f4455e) * [M-6 · Cuenta y Reportes](https://plans.io.inspiracode.com/plans/plan-632f8c49aca7448f) * [M-7 · Acciones compartidas de venta (backfill)](https://plans.io.inspiracode.com/plans/plan-261d45ad8c864b28) * [M-8 · Clientes y v2](https://plans.io.inspiracode.com/plans/plan-20b5d20fb1f0446c) ## Índices [Section titled “Índices”](#índices) * [Índice web](https://plans.io.inspiracode.com/plans/plan-8e971bcd67084e7f) * [Índice móvil](https://plans.io.inspiracode.com/plans/plan-bc9e3153754c4e03) # Web built-but-unreachable surfaces — findings + cleanup plan * Status: Draft for PO/Alfredo ruling * Date: 2026-09-12 * Author: web verification lane (fell out of the #164 e2e fixme audit) * Method: static analysis of `terminales-web@main` — route inventory (79 `src/app/**/page.tsx`), inbound-link greps, and a scan of all 110 playground-registry components for a shipped-route mount. > **The pattern.** A component or page can be *built*, pass its own tests, and have a playground entry — yet be reachable by no user, because nothing mounts it or links to it. Every measure reads “done” except the only one that counts. This cycle surfaced it repeatedly; this doc gives the honest list and a fix scoped as **three separate workstreams**, not one “wire everything” list — most of a flat list would be wrong work. ## The honest list — 6 genuine instances [Section titled “The honest list — 6 genuine instances”](#the-honest-list--6-genuine-instances) Corrected from the “five” figure previously circulated: **VAS web is NOT one** — it turned out reachable and merely mis-documented (the kind of wrong entry a flat count produces). The real six, in two sub-types: **Route-level (a built page with no way in):** 1. **Clientes** — `/admin/customers` (+ `/[id]`) built, but no drawer entry; the list is reachable only circularly. PDR-0001 puts Clientes IN MVP. (See [mvp-completeness-web.md](/product/mvp-completeness-web/) T2.) 2. **Merchant wallet** — `/admin/wallet` built and live-verified (US-BAL-1), but no owner-facing entry point. PDR-0003 names wallet visibility. (T3.) 3. **Stock-movements reception** — `/admin/pos/stock-movements/receive` built, but its parent list never links it; receive/transfer/adjust run as dialogs. **Component-level (a built component mounted in no route):** 4. **StockTransferDialog** — built + tested, but not mounted in `/admin/pos/stock-movements` (only the playground). Found in the #164 audit; kept as an honest `test.fixme` (web PR #914). W28 was retired from the registry, so it is not among the 110. 5. **CommandPalette** (⌘K global search) — built organism, rendered nowhere (not in AdminShell/AppShell). 6. **ImpersonationBanner** (platform-admin impersonation, US-PLAT-2) — built organism, rendered nowhere. The registry scan also found **3 superseded twins** and **13 parity scaffolds** (below) — those are not “features a user needs” but they are the *noise* that hides instances like #4–#6, which is why the process fix (Workstream C) matters most. *** ## Workstream A — wire-or-cut the 2 unreachable features (Alfredo’s call) [Section titled “Workstream A — wire-or-cut the 2 unreachable features (Alfredo’s call)”](#workstream-a--wire-or-cut-the-2-unreachable-features-alfredos-call) Both are **built**, so the question is wire-or-cut, not build-or-defer — decide against the real (small) cost, not an imagined one. | Feature | What it is | My read on cost-to-wire | | ----------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **CommandPalette** (⌘K) | Global search palette (US-SEARCH-1). | **UI wiring is small** — mount the palette in `AdminShell` behind a global ⌘K handler. BUT making it *return results* needs the search backend endpoint, which is **unticketed** (draft `web-parity-global-search-backend.md`). So: cheap to surface, not cheap to make useful. Recommend cut from MVP unless search is a launch requirement. | | **ImpersonationBanner** | Banner shown while a platform-admin impersonates a merchant (US-PLAT-2). | **Small** if the impersonation session state exists to read (US-PLAT-2 is PARTIAL/live-tested, so the plumbing is likely present) — mount the banner in the shell keyed on that state. Verify the state is actually populated before committing. Recommend wire if impersonation ships in MVP; else cut. | ☐ **Ruling — CommandPalette:** wire (shell only / shell+backend) or cut? ☐ **Ruling — ImpersonationBanner:** wire or cut? (Note: the two are estimates, not certainties. Each wiring PR would ship with the test that proves reachability — the exact assertion these components currently lack.) *** ## Workstream B — delete the 3 superseded twins (done, at PO gate) [Section titled “Workstream B — delete the 3 superseded twins (done, at PO gate)”](#workstream-b--delete-the-3-superseded-twins-done-at-po-gate) `TenderPicker`, `CashTender`, `SplitPayment` (`src/ui/organisms/pos/`) are DS organisms superseded by the terminal’s bespoke `TenderMethodDialog` / `CashTenderDialog` / `SplitTenderDialog`. Mounted in no route; only consumers were the registry, the barrel, and their own tests. Absence proven mechanically (grep imports + JSX; the terminal’s `TenderMethod` type is local; `suggestQuickAmounts` has zero consumers). Removed with their showcases + registry entries so nothing points at a deleted component. `tsc`/`eslint` clean. → **web PR #916** (to PO gate; not merged). This is dead-code removal, not a product decision. *** ## Workstream C — the process fix (the root cause, most valuable) [Section titled “Workstream C — the process fix (the root cause, most valuable)”](#workstream-c--the-process-fix-the-root-cause-most-valuable) The registry scan found **13 parity scaffolds** — Cuenta (w59–62 + CuentaFlow), Reportes (w55–58 + ReportesFlow), Ventas (w30 + VentasFlow), Dashboard (w07) — each a mock design-parity twin of a shipped screen, kept in the playground **after** the live route shipped. Thirteen scaffolds = the parity pipeline creates a mock twin per screen and **never retires it**, leaving two trees per screen permanently. That “which one is real?” ambiguity is exactly what made the VAS specs point at a dead route and a `cuenta` test assert against a showcase instead of the shipped component (both fixed in the #164 audit, PRs #913/#914). **Proposed rule (concrete, with owner and trigger):** > **A parity scaffold — its playground showcase, its parity screen component, and its registry entry — is retired in the same PR that ships (or marks live-green) its real route.** > > * **Owner:** the developer who ships the live route (they alone know the mock twin is now redundant). > * **Trigger:** the PR that mounts the live route or turns its live e2e spec green. Retiring the twin is a line in that PR’s Definition of Done. > * **Backstop:** the registry scan in this doc, run each verification sweep, catches stragglers — the cheapest way to find the next instance before a customer does. (Not a substitute for the DoD trigger; a wish with no owner is not a rule.) ✅ **ADOPTED (PO, 2026-09-12).** Canonical statement (with the why) in design-parity-playbook.md §10; one-line DoD reference in `ticket-content-template` so it reaches the doer. Backstop: this registry scan, re-run each verification sweep. *** ## Not findings (for completeness, so they aren’t re-litigated) [Section titled “Not findings (for completeness, so they aren’t re-litigated)”](#not-findings-for-completeness-so-they-arent-re-litigated) * 3 UNSURE registry entries (w31 refund/void showcases) paint the unmounted parity backdrop but demo components that ARE mounted (`ApprovalModal`, `RefundCaptureDialog`). * 8 inline parity-composition showcases (w25/w26/w37/w38/w39/w48/w33/w34) have no extractable product component; their live route siblings exist and are mounted. * DS primitives (Button, Chip, Avatar, DataTable, …) are mounted transitively across the app. # Liquidaciones — the payout model (owner requests, operator settles) > the one shared description of how a merchant withdraws money from the platform — who does what, how the withdrawable amount is derived, and when it refreshes. This is the product-level truth that owners, operators, and the AI agents building the surfaces all read from the same page > **Purpose:** the one shared description of how a merchant withdraws money from the platform — who does what, how the withdrawable amount is derived, and when it refreshes. This is the product-level truth that owners, operators, and the AI agents building the surfaces all read from the same page. **Audience:** product, platform operators/admins, and the AI agents building the liquidaciones surfaces. **Status:** as of 2026-09-21 — the payout write plane is enabled in production (Liquidaciones enablement wave, PO Alfredo 2026-09-21). Source decision log: `../../backlog/ideation/archive/api-liquidaciones-operator-rollup-authz.md`. **Related:** Card-settlement reconciliation runbook · operator-queues runbook — Liquidaciones · [merchant verification — money-out-only](/business-rules/merchant-verification-policy/) · [card-settlement model](/business-rules/card-settlement-model/). ## The model in one line [Section titled “The model in one line”](#the-model-in-one-line) A merchant **owner requests** a withdrawal of their own comercio’s confirmed balance; a **platform operator validates it and moves the money**, then records the payout or rejects it. The owner never moves money and never triggers a payout — they ask, the operator disburses. ## Roles — who does what (Option C, source-narrowed) [Section titled “Roles — who does what (Option C, source-narrowed)”](#roles--who-does-what-option-c-source-narrowed) The payout path is **owner-scoped self-serve for the request only**. The two sides are deliberately split so no single actor both asks for and releases money: * **The merchant owner submits** a liquidación request for **their own** comercio, and views the request and its status. That is the whole of the owner’s power on this path. An owner cannot request against another merchant, cannot approve, and cannot disburse. * **The platform operator validates and moves the money.** The operator works the liquidaciones queue: they confirm the request, actually send the funds to the merchant’s bank out-of-band, and then either mark the request settled (recording the payout reference per destination) or reject it. Settle and reject are the two operator actions that resolve a request into a withdrawal or a reversal. * **Admin ⊇ operator.** A platform admin can do everything an operator can, plus the structural/dangerous escalations (verification overrides, credential management, merchant lifecycle). Any admin capability is a superset of the operator’s, never a separate parallel path. * **The owner never triggers a payout.** Submitting a request is a *request*, not a disbursement. The money only leaves the platform when an operator acts. This separation is the core control of the model. Roles and the labels behind them: [roles-permissions-and-labels.md](/business-rules/roles-permissions-and-labels/). The operator’s day-to-day queue steps for liquidaciones: operator-queues runbook § Liquidaciones. ## The withdrawable amount — confirmed net, not gross balance [Section titled “The withdrawable amount — confirmed net, not gross balance”](#the-withdrawable-amount--confirmed-net-not-gross-balance) There are two different “balances,” and conflating them is the classic payout bug. The payout model withdraws **only** the confirmed one: * **Gross wallet `availableBalance`** — the snapshot a card sale credits at the moment of sale. It is immediately usable for VAS, merchant→merchant transfers, and wallet top-ups. This figure is **not** what a withdrawal draws against, because at sale time that card money is still virtual (promised by the payment processor, not yet confirmed as deposited to the platform). * **Withdrawable amount = `settlement_reports.availableAmount`** — the **confirmed net** from card settlement: the gross of **confirmed** sales, **T+1**, **net of the platform operator’s effective-dated commission**. This is the figure a liquidación may draw down. A withdrawal is gated on this confirmed number, never on the gross snapshot. So “how much can I withdraw” and “how much can I spend on VAS/transfers/top-ups” are answered by two distinct figures on purpose: settlement gates money-**out** to a bank; it does not touch the immediately-usable in-platform balance. The full derivation of the confirmed net (the three separate money figures, the effective-dated commission, and how confirmation is imported) lives in the CardSettlement decision log — `../../backlog/ideation/api-card-settlement-angelpay-reconciliation.md` — and its domain page, [card-settlement model](/business-rules/card-settlement-model/). ## Refresh semantics — the source updates the report, not a trigger [Section titled “Refresh semantics — the source updates the report, not a trigger”](#refresh-semantics--the-source-updates-the-report-not-a-trigger) The withdrawable report refreshes when its **source** produces new confirmed data, not when anyone presses a button. The source is a realm setting, **`settlement.source`**, whose default is **`angelpay_import`** (the payment processor’s settlement import): * **The report updates when the operator imports the processor’s export.** Confirmed settlement enters the system only through that import; that is what advances `settlement_reports.availableAmount`. There is no owner-side action that produces new confirmed money. * **The owner’s “actualizar” re-reads the latest report.** When an owner refreshes, they re-fetch the most recent settlement report and see its **`asOf`** timestamp — they are looking at the freshest confirmed figure, not causing a recomputation. There is **no owner-triggered rollup.** * **The on-demand rollup endpoint is an operator/support tool for the fallback source only.** The `POST /pos/settlements/ops/rollup` trigger stays operator/admin-only and applies **only** when `settlement.source = computed_rollup` — the fallback that computes reports from the platform’s own records for a future processor that cannot provide a settlement export. It is not part of the normal owner path. This **supersedes** the earlier A/B/C/D rollup-trigger question in the ideation log (`../../backlog/ideation/archive/api-liquidaciones-operator-rollup-authz.md`): the resolved model is that the **source** (an import), not a **trigger**, drives the report. The owner “requests” and “views”; nobody but the source advances the confirmed number. The kill-switch semantics for `settlement.source` and the on-demand fallback are in the card-settlement reconciliation runbook; realm-setting resolution is in [runtime-settings.md](/business-rules/runtime-settings/). ## The money-out gate — verified merchant + verified bank account [Section titled “The money-out gate — verified merchant + verified bank account”](#the-money-out-gate--verified-merchant--verified-bank-account) A withdrawal is money leaving the platform, so it sits behind the money-out gate: * A liquidación requires a **verified merchant** (owner KYC approved) **and** a **verified bank account** (a verified CLABE is the payout destination, so its verification is intrinsic to the gate). If either is missing, the request is default-deny and the owner is directed to complete verification. * **Money-OUT always needs verification.** Liquidaciones, merchant→merchant transfers and wallet→platform top-ups require a verified merchant in every policy. Under the default **policy B** an unverified merchant also cannot sell, open shifts, move cash or invite staff (it can set up: catalog, appearance, 1 store, 1 terminal); a platform admin can flip the realm switch `merchant.unverifiedCanOperate` to policy A, where selling is open from day one and only money-out waits. * **`allowUnverifiedSales` is retired** — it is neither a column to read nor an override to set. It is superseded by attestation (a platform admin can verify checks on a merchant’s behalf, including confirming its registered bank account) plus suspension as the single audited kill-switch. The authoritative rule and its capability table: [merchant verification policy](/business-rules/merchant-verification-policy/). ## The path, end to end [Section titled “The path, end to end”](#the-path-end-to-end) 1. **Owner requests.** From the merchant surface the owner submits a liquidación request against their own comercio’s withdrawable amount, choosing a verified bank-account destination. 2. **The money is reserved.** Submitting the request reserves the amount — it is debited from the withdrawable figure and the request lands in progress. The reservation is what keeps the money from being requested twice. 3. **Operator validates and disburses.** The operator works the request in the liquidaciones queue, sends the funds to the destination out-of-band, and marks it settled — recording the payout reference for each destination as the merchant’s proof. 4. **Or the operator rejects.** If the request cannot be paid, the operator rejects it with a curated reason; a compensating credit restores the reserved amount so the money is never stranded, and the owner sees the reason. The operator-side mechanics (curated rejection reasons, capturing the reference per destination, auditing) are in the operator-queues runbook § Liquidaciones. ## What this page deliberately does not promise [Section titled “What this page deliberately does not promise”](#what-this-page-deliberately-does-not-promise) * **No timeframe.** This page states no turnaround or duration for a payout. The disbursement is completed by a person on the platform team; until there is a committed queue-watch cadence that makes an interval true, stating one would be a promise the operation does not guarantee. The mechanism — reserve on request, operator disburses, notify on completion — is what is true; a duration is not. * **No vendor or brand names.** This page names mechanisms only — “the payment processor” / “procesador de pagos”, “the platform”, “the platform operator”. Owner- and operator-facing copy never carries the processor’s brand; brand names live in code, column mappings, and internal decision logs, never on this shared model page or in examples. # Mobile standalone — the web/mobile split & the standalone model > the one shared description of the standalone-N86 direction — what the N86 Android app is (and is not) relative to the Next.js web UI, which merchant operations live on the device versus on the web, and the surfaces this MobileStandalone wave adds (device request, personnel, on-device redemption, bank-account entry, verification read-only view). This is the product-level truth the mobile TL, the web team, product, and the AI agents building these surfaces all read from the same page > **Purpose:** the one shared description of the **standalone-N86 direction** — what the N86 Android app is (and is not) relative to the Next.js web UI, which merchant operations live on the device versus on the web, and the surfaces this `MobileStandalone` wave adds (device request, personnel, on-device redemption, bank-account entry, verification read-only view). This is the product-level truth the mobile TL, the web team, product, and the AI agents building these surfaces all read from the same page. **Audience:** product, the mobile (N86 Android) and web teams, and the AI agents implementing the wave tickets. **Status:** as of 2026-09-23. `MobileStandalone` feature, PO-approved 2026-09-22 (PO brainstorm, Alfredo). Design log — the source of every decision below: `../../backlog/ideation/archive/mobile-standalone-expansion.md`. **Related:** Device requests runbook · [Merchant verification policy](/business-rules/merchant-verification-policy/) · [Liquidaciones — the payout model](/product/liquidaciones-payout-model/) · Card-settlement reconciliation runbook · Bank-transfer reconciliation runbook · Web ↔ Mobile backend-parity audit · [Roles, permissions & labels](/business-rules/roles-permissions-and-labels/). This page is domain truth, not an implementation guide. Where a mechanism already ships it is **linked, not re-explained** — see the module/screen references inline. It states *which client owns each operation* and *why*; it does not restate controller or screen internals. ## The principle — independent from the web *frontend*, not from the API [Section titled “The principle — independent from the web frontend, not from the API”](#the-principle--independent-from-the-web-frontend-not-from-the-api) The N86 app is being built into a **complete standalone merchant-operations client**: handing a merchant a provisioned N86 is enough to run their store. “Standalone” is a statement about the **web frontend**, not about the backend. The app is **independent from the Next.js web UI but a CLIENT of the shared `terminales-api`** — there is **no forked business logic**. Every rule that the server owns stays on the server, and both clients call the same surface. Concretely, the money path and every durable, audited, or authorization-bearing operation stay **server-side**: the **DBOS** durable workflows (settlement, device-request fulfillment, wallet queue), PIN scrypt hashing, provisioning that creates Teams, server-side approvals, idempotency, void-vs-refund rules, CSV generation, and HMAC webhook verification are the API’s job and are never re-implemented in the app. The app reaches them through **hand-rolled OkHttp per-endpoint clients** with a `JwtAuthInterceptor` (Appwrite JWT) for the `/pos/...` routes, and the **Appwrite SDK** for reads and realtime. This wave adds **no new backend** beyond what already exists or ships in the DeviceRequests wave. This is the same architectural model the Web ↔ Mobile backend-parity audit describes: `terminales-api` is a **business-logic-only surface** (a controller exists only where the server key or an unenforceable-by-client rule is needed); routine catalog/merchant/store reads and writes are **direct-Appwrite-by-design** under row-level security. “Same backend” therefore has two valid shapes — **API-mediated** (money, approvals, PIN) and **direct-Appwrite** (catalog/theme reads, the payment\_intents bridge) — and the standalone goal is met by the app adopting the *same* shape the web uses for each domain, not by cloning the web’s logic. ## The split — what lives on the device vs the web [Section titled “The split — what lives on the device vs the web”](#the-split--what-lives-on-the-device-vs-the-web) The table is the split as of this wave. “Device” means the operation is performed on the N86; “Web” means it is completed in the Next.js UI. Both talk to the same `terminales-api` / Appwrite surface. | Domain | Client | Notes | | ------------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | Catalog | **Device** (must-have) | Read/browse on device; full catalog-*edit* parity is deferred (see below). | | Inventory | **Device** (must-have) | Read on device; full inventory-*edit* parity is deferred. | | Reports | **Device** (must-have) | Reports surfaced on device; full reports parity is deferred. | | Shifts | **Device** | Open/close shift on the N86. | | POS — all sales | **Device** | Every sale runs on the device; the money path stays server-side. | | Notifications | **Device** | Delivered via **Appwrite Realtime** (no push — see Conventions). | | Withdrawals | **Device** | Owner requests; operator settles server-side — see [Liquidaciones](/product/liquidaciones-payout-model/). | | **Device request** | **Device** *(new this wave)* | “Solicitar terminal” + a Solicitudes tracker under the existing **Terminales** area; calls the DeviceRequests API. See Device requests runbook. | | **Personnel** | **Device** *(new this wave)* | Role-gated **Personal** section — reverses the prior web-only IA (see below). | | **Bank-account entry** | **Device** *(new this wave)* | CLABE + titular + carátula capture → account PENDING (see below). | | Connections / platforms | **Web** | External-platform connections stay web-only in this wave. | | Verification *completion* | **Web / KYC** | Mobile shows a **read-only status view** plus **inline bank** entry; the remaining checks complete in web/KYC (see below). | The three areas this wave brings to the device — **device request, personnel, and bank-account entry** — are the previously web-only pieces the standalone goal needs. Verification is not a new mobile build: the read-only view already shipped (see below). ## Personnel — the IA reversal (documented explicitly) [Section titled “Personnel — the IA reversal (documented explicitly)”](#personnel--the-ia-reversal-documented-explicitly) Personnel was **documented web-only**: the mobile drawer README, `app/.../navigation/HomeDrawerDestinations.kt` (lines 12–14), and `res/values/strings.xml` (line 867, “management areas stay web-only”) all stated that personnel management lived on the web. **This wave deliberately reverses that information architecture.** Personnel comes to mobile at **full parity** — list / create / edit / deactivate, assign a role within the caller’s role ceiling, and **issue an activation code** — under a **new owner/manager-gated “Personal” section** in the mobile nav. Because this reverses a previously documented decision, the reversal is called out in **one place** and the affected IA docs/strings are updated to match, so there is no lingering “web-only” claim contradicting the shipped app. Role gating is **display/UX defence-in-depth only** — the server authorizes every write (`auth/PosRole.kt` on the client; server-side role checks own the truth). See [Roles, permissions & labels](/business-rules/roles-permissions-and-labels/) for the store-role model. ## Staff onboarding — the loop closes on the device [Section titled “Staff onboarding — the loop closes on the device”](#staff-onboarding--the-loop-closes-on-the-device) Staff onboarding is a two-step loop that now completes **entirely on the N86**: 1. A **manager issues an activation code** from the new Personal section (the client calls the existing employee/activation API — `activation.controller.ts` issue; `EmployeeService` server-side). 2. The **new cashier redeems that code on the N86** to activate their account and **set a PIN** — via the `@Public` `POST /pos/employees/activate` redeem. No redemption screen exists in the app yet (it has PIN login and create-PIN only), so this on-device **activation-code redemption** screen is net-new. The result: a manager can onboard a cashier without ever touching the web UI — the staff-onboarding loop **closes on the device**. ## Bank & verification — bank is always editable on mobile [Section titled “Bank & verification — bank is always editable on mobile”](#bank--verification--bank-is-always-editable-on-mobile) Verification’s **read-only status view already exists** on the device (shipped in #418): `ui/screens/verification/MerchantVerificationScreen.kt` shows the per-check checklist, a KYC **Custom Tab** CTA, and a staff read-only variant. So there is **no new verification-view work** — the remaining verification checks **complete in web/KYC**, and the mobile screen deep-links out for them. The net-new piece is **bank-account entry on the device**: CLABE + titular + **carátula capture** (camera/file → Appwrite Storage upload, reusing `storage/UploadScreen.kt` + `AppwriteFileUploader`), wired into the verification screen’s **bank row** so the owner can complete bank on-device while other checks deep-link to web. On save the account goes **PENDING → an operator verifies the carátula** through the bank-verification queue. **Bank is always a must-have and always editable on mobile, regardless of how the verification phases evolve.** Verification as a whole gates money-*out* always and, under the default policy B, selling and operating too (see [Merchant verification policy](/business-rules/merchant-verification-policy/)); the merchant’s bank account is the payout destination, so it must be enterable and correctable on the device independent of the rest of the checklist. The operator side of confirming and reconciling that account is covered in the Bank-transfer reconciliation runbook and, for card proceeds, the Card-settlement reconciliation runbook. ## Conventions (mobile must follow) [Section titled “Conventions (mobile must follow)”](#conventions-mobile-must-follow) * **Money = integer minor units, scale 10000** (`core/money/PosMoney.kt`). No floating-point currency anywhere. * **es-MX single locale** (`res/values/strings.xml`) — one locale, no runtime language switching. * **Realtime = Appwrite Realtime, no push (no FCM)** — notifications and live updates arrive over Appwrite Realtime subscriptions. * **KYC via a Custom Tab, never a WebView** — reuse `core/common/CustomTabLauncher.kt` for any hosted KYC deep-link. * **No vendor/brand names** on any merchant-facing surface — the PSP is an internal `provider` attribute, never shown (consistent with the PSP-generic model in the Device requests runbook). * **Roles are display/UX defence-in-depth only; the server authorizes** every mutation. ## Deferred (explicit — stated so they are not assumed in-scope) [Section titled “Deferred (explicit — stated so they are not assumed in-scope)”](#deferred-explicit--stated-so-they-are-not-assumed-in-scope) * **Full mobile parity scoping for POS / catalog-edit / inventory-edit / reports** beyond what already ships — a separate standalone-parity effort, not this wave. * **Offline policy** — the app **still blocks all offline operation** (POS-9, block-all-offline); an offline model is a separate cross-cutting decision. * **App distribution & updates** — how the N86 build is delivered and updated is out of scope here. * **Multi-PSP beyond the abstraction** — build the provider abstraction now, but **only AngelPay is wired**; adding a second PSP is deferred (the model absorbs it with no schema change). # Mobile user stories — the derived subset > the explicit, reviewable list of which MVP user stories the mobile app (terminales, Android / Kotlin / Jetpack Compose) must satisfy, and the mobile authority for their screens + status. Each row is a story shared with the web list (user-stories.md) — the story text and roles are common — but the mobile screens, owning flow, Pass C ticket, and mobile status are tracked here (the web file is web-only since the 2026-07-30 split). Two stories are mobile-only (US-BAL-4 card-on-handheld credit, US-BAL-5 handheld autopay top-up) and exist only here > **Purpose:** the explicit, reviewable list of which MVP user stories the **mobile app** (`terminales`, Android / Kotlin / Jetpack Compose) must satisfy, and the **mobile authority** for their screens + status. Each row is a story **shared** with the web list ([user-stories.md](/product/user-stories/)) — the **story text and roles are common** — but the **mobile screens, owning flow, Pass C ticket, and mobile status** are tracked **here** (the web file is web-only since the 2026-07-30 split). Two stories are **mobile-only** (US-BAL-4 card-on-handheld credit, US-BAL-5 handheld autopay top-up) and exist only here. **Status:** 2026-09-10 (late) — **`terminales` PR #452 open (pending gate): PB-1 + PB-2.** PB-1 was filed as “the drawer opens from Inicio but not again” and that framing was **wrong**: an isolation repro showed the **area-root hamburger was dead on every non-Inicio root**, so with the bottom nav bar retired and the drawer the only way to switch areas, **a user who entered M25 Reportes or M24 Mi cuenta on current main could not leave them for another area**, with nothing in logcat when the tap died. Android system back still escaped to Inicio (Alfredo confirmed that 09-09), so it was not a hard trap — but every in-app route out was gone. **Inicio masked it by coincidence of layout, not design:** the M06 dashboard happens to place its hamburger clear of the 48px strip; nothing was special-cased. That is why the 53-flow sweep walked past it and why a position-dependent bug looked ordering-dependent. Cause: the drawer’s left-edge swipe zones were overlay boxes carrying `pointerInput` drawn over the content, and Compose hands a tap to the topmost hit sibling and stops, so a full-height 48px strip swallowed every tap in the left margin; production leaves the Scaffold `topBar` slot empty so each screen’s own bar sat underneath. The **sub-screen back arrow** had the identical defect and is fixed in the same commit. PB-2 (owner “Cerrar sesión” below the N86 fold) is fixed structurally — the cashier margin was only \~13dp and flaked pre-fix, so the fold was marginal for **every** role, not just the owner. Gate run on the merge commit: 1466 tests / 0 failures, lint unchanged. **Neither fix is confirmed on the physical N86** (not attached). Prior: 2026-09-10 — **T-11 rehearsal on the `manual` realm (Alfredo driving the N86, terminales-n86 watching live):** pairing, PIN, shift open, catalog, cash sale + **printed** receipt, and a **REAL approved card charge** ($1.00, chip, auth 895897) all passed on current main; evidence in `terminales/docs/ui-evidence/n86-t11-rehearsal/`. Two defects came out of it — **receipt printing fails on the CARD path** (3 successful prints in the session, every one after CASH; 4 card payments, none printed; root cause NOT yet established) and the pre-shell pairing gate’s dead roster link. The web→handheld bridge leg was not reached. Prior: 2026-09-09 (late) — **fixes from the N86 session shipped to main in `terminales` PR #446 (dc8a265c)**: BUG-1 Home shift card (S11), BUG-5/6/7 Historial folio · store-timezone times · reload after void (S12), BUG-11 magic-link error mapping (S16), UX-9 Reintentar feedback + reachability probe (S15), UX-14 coin rows, UX-15 “Abierto desde” label. PR #447 (S9: drawer hijack from the Sell FAB, “Más opciones” drawer entry, “Terminal lista (pago remoto)” Home tile, drawer close) merged 2026-09-10 (575d81a3). **PR #448 (pending gate)**: S10 direct singular VAS sale, no cart (US-VAS-1/2/3 → Built ✅ on the new path; Live stays gated on the aggregator); S14 catalog empty-grid (BUG-4); S17 session tenant guard; S13 N86 design fit (UX-2/3/4/6/7/8/10/12/13). Earlier: **N86 device session (Alfredo + terminales-n86, qa-design realm, 14 legs)** results folded in as `N86 09-09` notes; full evidence `terminales/docs/ui-evidence/n86-s4-2026-09-09/`, bug list in `team/workers/terminales-n86.md`. Earlier the same day: **delta pass by `terminales-n86` (T-8)** against `terminales` origin/main `7053943e`: rows below carry a `Δ 09-09` note where the 07-31 status drifted (VAS rebuilt on the api path, BAL-4 live inside the unified tail, BAL-5 dormant, CLI flags retired, POS-2 disabled, POS-4 unified + device-verified, POS-8 restore-only on the handheld). Full ground truth: `team/workers/terminales-n86.md`. Prior status (2026-07-31) — restructured to mirror the web file: the single ambiguous “Coverage (mobile)” symbol is replaced by three explicit status columns (**Built · Mock E2E · Live E2E**), and the **Live E2E** column now carries the results of the **first mobile Pass D live-backend run** (this session). Prior status note (2026-07-30): the PO’s 2026-07-22 review admitted **`US-VAS-5` Transferir saldo** into mobile MVP and deferred eight items with TODOs (see [§ Deferred TODOs](#deferred-todos--po-mobile-scope-review-2026-07-22)). **Companions:** [user-stories.md](/product/user-stories/) (the web stories — shared story text) · `flows-screens-master-map` (M-1…M-8 flows + navigation edges) · `README` (per-screen Figma nodes + code cross-refs) · `design-parity-matrix` · [roles-permissions-and-labels.md](/business-rules/roles-permissions-and-labels/) ## The three status columns [Section titled “The three status columns”](#the-three-status-columns) Mirrors [user-stories.md](/product/user-stories/). The columns are independent — a story can be built but unverified, or design-aligned but blocked live: * **Built** — is the **mobile** feature implemented and design-aligned? ✅ shipped · 🟡 partial / behind a flag / not design-aligned · ⚠ missing / legacy / not built * **Mock E2E** — covered by a **Pass C** parity + navigation Maestro flow (`.maestro/flow-*.yaml`, mock-first / screenshot harness): ✅ screen parity + nav pass · 🟡 partial · ⚠ failing · — no flow * **Live E2E** — verified against the **real backend** this session (local `terminales-api` :3020 + cloud Appwrite + the app on an emulator, driven by Maestro): ✅ money-path/nav passes live · 🟡 partial (core proven, a leg blocked or not driven) · ⚠ blocked / product finding · 🔌 hardware-bound (card-present, not automatable on an emulator) · ⏭️ gated (needs a flag) · — not run (VAS/BAL deferred this session) * **Notes** — one line: the real gap, finding, or the fix that unblocked it. > **Live E2E run — 2026-07-31 (groups ACC · POS · VOID · SHIFT · CLI · ACCT · NOTIF; VAS + BAL deferred).** Stack lifted from `verify/user-stories-functionality` worktrees of both repos. Seed: `scripts/seed-e2e.ts` (cashier `e2e_cashier@terminales.dev` PIN 1234, supervisor 4321, store “Sucursal E2E”, product “Coca 600 E2E”). Bugs found + fixed and flow drift repaired are recorded in the Notes and in [§ Live E2E run — findings](#live-e2e-run--findings-2026-07-31). ## Why mobile is a subset [Section titled “Why mobile is a subset”](#why-mobile-is-a-subset) The mobile app is the **operator device**: a cashier (with supervisor approvals) standing at a counter or walking a floor with a handheld. The web app is the **operator device plus the back office** — it additionally carries catalog authoring, inventory, staff administration, reporting, fleet/terminal configuration, white-label, and the whole platform-administration surface. That split is not accidental; it follows the role index in [roles-permissions-and-labels.md](/business-rules/roles-permissions-and-labels/): `cashier` and `supervisor` journeys are almost entirely present on mobile, `manager`-only journeys are mostly web, and the platform roles (`platform_admin` / `platform_support` / `platform_developer`) have **no mobile presence at all**. | | Stories | | ------------------------------------------------------------- | ------- | | Total MVP stories ([user-stories.md](/product/user-stories/)) | **66** | | **In mobile scope** (this document) | **31** | | Web-only | **35** | Of the 31 mobile stories, 28 name at least one canonical `M##` Figma node; the remaining 3 (`US-POS-9`, `US-VAS-4`, `US-BAL-5`) are mobile-scoped in the source but have no canonical mobile frame yet — they are flagged **(no M frame)** below and are also design gaps. ## How to read this [Section titled “How to read this”](#how-to-read-this) * **ID** — the story id from [user-stories.md](/product/user-stories/). E2E specs and cases reference this id; the mobile Pass D drafts will do the same. * **Mobile screens** — the canonical `M##` frame(s) with their Figma node ids, exactly as the source cites them. `M27a`–`M27e` are the shared POS overlays backfilled as flow **M-7** (they are the mobile side of the same `135:*` nodes the web uses). * **Flow** — the master-map mobile flow (`M-1`…`M-8`) the screens belong to. * **Pass C ticket** — the mobile UI parity + navigation issue that owns those screens today (the UI-only / mock phase). These are the seven `Mobile UI … Flow Screens Parity And Navigation` issues on `inspiraCode/terminales`. * **Built · Mock E2E · Live E2E · Notes** — the three status columns defined [above](#the-three-status-columns). **Live E2E** carries this session’s Pass D result. ## Mobile flow → Pass C ticket index [Section titled “Mobile flow → Pass C ticket index”](#mobile-flow--pass-c-ticket-index) | Flow | Screens | Pass C ticket | | ---------------------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | **M-1** · Acceso y Onboarding | 01–05 | [#221](https://github.com/inspiraCode/terminales/issues/221) Mobile UI MobileAuth Access Flow Screens Parity And Navigation | | **M-2** · Inicio y Turno | 06, 07, 28, 08, 09, 10, 29 | [#222](https://github.com/inspiraCode/terminales/issues/222) Mobile UI ShiftManagement Shift And Cash Flow Screens Parity And Navigation | | **M-3** · Venta POS | 11–14 | [#223](https://github.com/inspiraCode/terminales/issues/223) Mobile UI PosCheckout Sale Flow Screens Parity And Navigation | | **M-4** · Cobro y Recibos (+ **M-7** overlays) | 15–19, 27 | [#224](https://github.com/inspiraCode/terminales/issues/224) Mobile UI PosPayments Charge And Receipt Flow Screens Parity And Navigation | | **M-5** · Categorías especiales (VAS) | 20–23 | [#225](https://github.com/inspiraCode/terminales/issues/225) Mobile UI Vas Special Categories Flow Screens Parity And Navigation | | **M-6** · Cuenta y Reportes | 24–26 | [#226](https://github.com/inspiraCode/terminales/issues/226) Mobile UI AccountReports Account And Reports Flow Screens Parity And Navigation | | **M-8** · Clientes y v2 | 30–35 | [#227](https://github.com/inspiraCode/terminales/issues/227) Mobile UI Customers Clients Flow Screens Parity And Navigation | All seven are `blocked_by` the Maestro screenshot harness ([#215](https://github.com/inspiraCode/terminales/issues/215)) and the Pass B design-system component families (#216–#220, #247–#252). *** ## ACC — Access & onboarding (M-1) — 5 stories [Section titled “ACC — Access & onboarding (M-1) — 5 stories”](#acc--access--onboarding-m-1--5-stories) | ID | Story | Roles | Mobile screens | Flow | Built | Mock E2E | Live E2E | Notes | | -------- | -------------------------------------------------------------- | ------- | ------------------- | ---- | ----- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | US-ACC-1 | Sign in with email + password on the Figma split-screen login. | all | M01 `52:27` | M-1 | ✅ | ✅ | 🟡 | **N86 09-09:** email/password ✅ on the device; **magic link ✗ on qa-design** (“Correo o contraseña incorrectos” — BUG-11: wrong error mapping on mobile + the App-Link host is not a registered web platform on the self-hosted realm, Q-n86-6). Prior: `flow-auth.yaml` — invalid creds → mapped error, no session; valid → real Appwrite session → shell. **Magic-link (AC3) also driven live (2026-08-01):** `createMagicURLToken` rejected the `terminales://` scheme (same Appwrite-Cloud limitation as recovery), so it too was broken in prod; migrated `MAGIC_LINK_REDIRECT_URL` to the verified **https App Link** (`https://links.io.inspiracode.com/magic-link`). `flow-auth-magic-send`+`-complete` request the link, read `userId`/`secret` from Mailpit, open the App Link and land on M05 store select | | US-ACC-2 | Second-factor (email/SMS) challenge when enabled. | all | M03 `55:59` | M-1 | ✅ | ✅ | ✅ | **valid-code leg now driven live** — cloud Appwrite’s custom SMTP points at the IONOS Mailpit, so `flow-auth-mfa-send`+`flow-auth-mfa-enter` read the OTP from Mailpit’s API and complete the challenge (session elevates to M05). Wrong-code rejection also covered | | US-ACC-3 | Recover a forgotten password via a link. | all | M04/M04b `539:1215` | M-1 | ✅ | ✅ | ✅ | **valid-link leg now driven live (2026-08-01).** Root cause of the send-400 was **not** a missing platform: Appwrite Cloud rejects the `terminales://` custom scheme in `createRecovery` (accepts only http(s) / `appwrite-callback-…`), so recovery was broken in prod too. **Fixed** by migrating `RECOVERY_REDIRECT_URL` to a verified **https App Link** (`https://links.io.inspiracode.com/recovery`; `assetlinks.json` hosted on the IONOS box, host `verified` on-device). `flow-auth-recovery-send`+`-reset` request the mail → read `userId`/`secret` from Mailpit → open the App Link → set the new password (“Tu contraseña se actualizó”). Invalid/expired token still rejected. gap #33 | | US-ACC-4 | Fast PIN access with a profile selector. | cashier | M02 `300:4256` | M-1 | ✅ | ✅ | ✅ | **N86 09-09:** PIN unlock + sign-out → “Acceso rápido con PIN” re-entry both green on the device. Prior: wrong PIN refused server-side; correct PIN (1234) unlocks through `POST /pos/pin-login`. Profile selector deferred v2 (gap #31) | | US-ACC-5 | Pick which store/terminal I operate. | all | M05/M05b `546:1229` | M-1 | ✅ | ✅ | ✅ | live store list; pick scopes the session tenant; unpaired terminal blocks the sale surface, pairing lifts it. **gap #36 closed this session**: a new “Seleccionar terminal” roster screen (`TerminalSelectionScreen` → `GET /pos/terminals`) lists the store’s terminals with online status, reachable from “Registro de terminal” → “Ver terminales de la tienda” (`flow-terminal-select.yaml` verifies it renders the seeded terminals) | **Not on mobile:** `US-ACC-6` (merchant owner onboards a commerce, W06) — a web onboarding journey, and post-MVP. **Node-id discrepancy to resolve — deferred, TODO `T-8`:** for `US-ACC-3` and `US-ACC-5` the source cites the `M04b`/`M05b` variant nodes (`539:1215`, `546:1229`) while the Pass C ticket (#221) lists the canonical frames `56:88` (04 Recuperar contraseña) and `57:85` (05 Seleccionar tienda/terminal). Both are recorded here as the source has them; the parity ticket is the one that will settle which frame is canonical. ## VER — Merchant verification (KYC) UX (M-1) — 4 stories [Section titled “VER — Merchant verification (KYC) UX (M-1) — 4 stories”](#ver--merchant-verification-kyc-ux-m-1--4-stories) > Sub-project D (approved 2026-09-02). Merged mobile PRs: #416 (module + gate), #418→#421 (banner / blocked dialog / progress), #419→PR 423 (notification category + deep link). Mirrors the web `merchant-verification-ux` canvas. **Maestro / live coverage is not yet recorded here** — the marks below reflect merge status only. **Δ 09-09 (terminales-n86):** confirmed — no `.maestro/flow-*` covers VER, no live run recorded; the Custom-Tab deep-link return is deferred by design (spec §15: the progress screen refreshes on foreground + “Actualizar estado”). Scheduled in the T-8 emulator sweep + N86 device plan. (Web mirror = the `VER` section in `user-stories.md`.) | ID | Story | Roles | Mobile screens | Flow | Built | Mock E2E | Live E2E | Notes | | -------- | ----------------------------------------------------------- | -------------------- | -------------------------- | ---- | ----- | -------- | -------- | ------------------------------------------------------------------------------ | | US-VER-1 | Verification **status banner** in the shell, per state. | all (merchant scope) | `merchant-verification-ux` | M-1 | ✅ | — | — | shipped #418→#421; banner mirror of web US-VER-1; e2e/live coverage unrecorded | | US-VER-2 | Merchant actions **gated until verified** (blocked dialog). | manager (owner) | `merchant-verification-ux` | M-1 | ✅ | — | — | shipped #416 (gate) + #418→#421 (dialog); e2e/live coverage unrecorded | | US-VER-3 | **Verification progress** screen (checklist + reasons). | manager (owner) | `merchant-verification-ux` | M-1 | ✅ | — | — | shipped #418→#421; e2e/live coverage unrecorded | | US-VER-4 | **Verification notification** deep-links into progress. | manager (owner) | `merchant-verification-ux` | M-1 | ✅ | — | — | shipped #419→PR 423 (notification category + deep link) | ## POS — Point-of-sale sale (M-3, M-4, M-7) — 8 stories [Section titled “POS — Point-of-sale sale (M-3, M-4, M-7) — 8 stories”](#pos--point-of-sale-sale-m-3-m-4-m-7--8-stories) | ID | Story | Roles | Mobile screens | Flow | Built | Mock E2E | Live E2E | Notes | | -------- | --------------------------------------------------------------------------- | ------------------- | ------------------------------- | ---- | ----- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | US-POS-1 | Browse/search the catalog and add items (incl. barcode scan) to a cart. | cashier | M11 `67:303` · M13 `300:4196` | M-3 | ✅ | ✅ | ✅ | `flow-pos-venta`/`flow-cobro-recibos` — catalog renders live, product → M12 detail → add to cart. (Seed had 0 stock → “Agotado”/not addable; fixed by seeding a `stock_levels` row) | | US-POS-2 | Apply a line/order discount, requesting supervisor approval over threshold. | cashier, supervisor | M27a `135:738` · M27b `135:929` | M-7 | ⏭️ | ✅ | 🟡 | **Δ 09-09: discounts DISABLED for MVP** (Alfredo 2026-09-01) — UI hidden (#411→#412, `FEATURE_FLAG_DISCOUNTS=false`); story parked, not a gap. Prior: the shared **M27b** supervisor-approval sheet is **verified live** (proven end-to-end by US-VOID-1); the discount-entry leg (cart → “Aplicar”) not driven standalone. over-threshold reject state gap #12 | | US-POS-3 | Charge **cash** and record change. | cashier | M27d `135:1047` | M-7 | ✅ | ✅ | ✅ | **N86 09-09:** two cash sales green on the device (folios 6aa1e592, 6aa20a2d); design asks UX-2 (cash sheet does not fit 360×640) + UX-3 (success CTAs below the fold). Prior: `flow-cobro-recibos` — “Exacto” → finalize; server `pos.sales.finalized total=1800 tenders=1`, stock decremented. **Fixed a crash**: finalize threw `IllegalAccessError` (a `private companion` on the `@Serializable` response DTO) | | US-POS-4 | Charge a **card** via the paired terminal. | cashier | M15/M16 `71:403`/`72:407` | M-4 | ✅ | ✅ | ✅🔌 | **N86 09-10 (realm `manual`, owner session): real card charge APPROVED end-to-end** — `SALE.charge ok code=00 auth=895897 ref=260910162721 APRO`, chip, $1.00, serial N860W175796; server shows one CARD payment + one COMPLETED SALE transaction, no duplication. **Gotcha:** the credential’s `environment` is asserted against the build’s `ANGELPAY_ENV` (defaults QA), so a PROD credential on a QA build renders “Terminal no habilitada” — re-provision the credential, do not rebuild. **Δ 09-09: built on the unified settlement pipeline** (`NativeCardTender` + `CardTenderViewModel` + `SettlementJobObserver`, `CardChargeMode` REAL/EMULATE\_\* — #397) and **live-verified with a real $1 charge on the Nexgo N86 2026-08-24** (single card; web→handheld bridge live 2026-08-19, #379/#393). Still card-present (🔌) for evidence; the $1 cash+card **exactly-once split drive** is liquidaciones’ activation bundle (device-gated) | | US-POS-5 | **Mixed/split** tender across methods. | cashier | M27c `135:978` | M-7 | ✅ | ✅ | ✅ | **Multi-tender settlement proven live** — server `pos.settlement.tender_applied` twice against a running balance (leg 1 $1 → remaining $1799; leg 2 → remaining \~$0). Took clearing a 4-layer stack: `sales_orders.amountPaid`/`amountRemaining` + `status` enum `OPEN` + `order_payments.clientId` (cloud schema) **and 2 real app bugs** — the tender DTO omitted `storeTeamId`/sent non-whitelisted `changeGiven` (→403), and the shared `TenderResponseDto` read `remaining`/`status` where the server returns `amountRemaining`/`finalized` (balance always read 0, swallowing tenders). Both fixed in `PosOrderGateway`/`CardChargeCoordinator`. Residual: the 8-nines saturation leg leaves a **1-cent** remainder so it doesn’t auto-finalize — a coerce-rounding follow-up, not a settlement failure. **Δ 09-09:** the split CARD leg on the *unified* pipeline is shipped dormant on web (#754) and referenced by `SplitPaymentViewModel` on mobile — unverified on the handheld until activation | | US-POS-7 | Print/render an 80mm receipt. | cashier | M17 `73:439` | M-4 | ✅ | ✅ | ✅ | **N86 09-10: prints after a CASH sale, FAILS after a CARD sale** (“Error de impresión”). 3 successful prints in the session, every one after cash; 4 card payments, not one print after any. Root cause OPEN — the failure emitted no telemetry in a 924k-line capture, so either logcat dropped it or the printer layer is never reached; deliberate repro (cash control → card) is the next step, hardware-blocked. Prior: M17 “¡Venta exitosa!” renders folio/total/Cambio/Imprimir from the finalized order. print/email error state gap #14 **RESOLVED + ACCEPTED ON PAPER 09-12.** Root cause was NOT a printer fault: `PosCheckoutFlowContent.kt:203` was `onPrint = onPrint ?: { localPrintFailed = true }` — documented placeholder scaffolding. **Only the cash path was ever wired** (#275); card, manual-charge and split passed no print action, so the error was **fabricated in the UI and the printer was never called**. That is why a 924k-line capture held no telemetry: nothing to log. Fixed in **#459** (all four paths wired; the placeholder DELETED and `onPrint` made a required parameter, so a future tender cannot acquire a fake failure by omission). **Accepted on hardware 09-12:** card sale folio `#6aa5db6c`, auth `361329`, full printer success sequence, **paper out and renders correctly — both confirmed by Alfredo**, which is the only evidence that can settle 80mm rendering. **Still unprinted anywhere:** the manual-charge and split receipt paths (newly wired, never exercised on paper). | | US-POS-8 | **Hold** a sale and resume it later (mine/all + author). | cashier | M34 `300:4185` | M-8 | 🟡 | 🟡 | ✅ | **Δ 09-09: RESTORE-ONLY on the handheld** — “Guardar en espera” (create-a-hold) was removed from the handheld by PO decision 2026-09-05 (#438); holds are created on the web POS and resumed on the handheld. `HeldOrdersApiClient` wire contract pinned to live OpenAPI (#385). `flow-pos-hold-resume.yaml` still scripts the removed create step → drift to repair. Prior: `flow-pos-hold-resume` — “Guardar en espera” parks the cart, “Reanudar venta” restores it. (Web flags this as a candidate bug; **mobile works**) | | US-POS-9 | Sale surface **blocked with a clear banner when offline**. | cashier | **(no M frame)** — M-3, gap #35 | M-3 | ✅ | ✅ | ✅ | **N86 09-09:** airplane mode → banner + Cobrar disabled ✅; reconnect recovers only after \~30 s and “Reintentar” gives no feedback (UX-9); two stacked banners (UX-8). Offline cash queue = POST-MVP (Alfredo 09-09). Prior: `flow-pos-offline.yaml` (new) — airplane-mode → M13 `CartOfflineBanner` (InlineAlert “Sin conexión — no es posible cobrar esta venta.”) renders and “Cobrar” is withheld (`canCheckout = lines && !isOffline`); “Reintentar” restores it. The blocking state was **already built** (no figma frame); this run drove it live | **Not on mobile:** `US-POS-6` (durable finalize across a network blip, W19) — a DBOS/backend durability story with a web-only screen citation; it protects the mobile sale path too but has no mobile surface of its own. ## VOID — Same-day void (M-7) — 1 story [Section titled “VOID — Same-day void (M-7) — 1 story”](#void--same-day-void-m-7--1-story) | ID | Story | Roles | Mobile screens | Flow | Built | Mock E2E | Live E2E | Notes | | --------- | ------------------------------------------------------------------------------------ | ------------------- | --------------- | ---- | ----- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | US-VOID-1 | Void a finalized ticket **within the current shift** with a supervisor PIN + reason. | cashier, supervisor | M27e `135:1078` | M-7 | ✅ | ✅ | ✅ | **N86 09-09:** two voids approved on the device (`SALE_VOID GRANTED`), reached via Reportes → Ventas del día because the Sell-FAB “Anular venta” opens the nav drawer over the ticket search (BUG-2, S9); approval sheet opens half-height (UX-6); Historial does not refresh after the void (BUG-7). Prior: `flow-void.yaml` (new) — M19 → Cancelar → reason → M27b supervisor PIN 4321 → M19 “Cancelado”. Server `pos.approval.granted action=SALE_VOID` + `pos.sales.voided`. reject state gap #17 | **Not on mobile:** `US-VOID-2` (cancel the linked card payment-intent — backend behaviour of the same void) and `US-VOID-3` (refunds beyond same-day void — post-MVP). ## SHIFT — Shifts & cash (M-2) — 4 stories [Section titled “SHIFT — Shifts & cash (M-2) — 4 stories”](#shift--shifts--cash-m-2--4-stories) | ID | Story | Roles | Mobile screens | Flow | Built | Mock E2E | Live E2E | Notes | | ---------- | ---------------------------------------------------------------------- | ---------- | -------------------------- | ---- | ----- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | US-SHIFT-1 | Open a shift with an opening float. | cashier | M08 `61:212` | M-2 | ✅ | ✅ | 🟡 | **N86 09-09:** $500 open accepted by the server but the Home card keeps “Sin turno” until the next PIN login (BUG-1, S11). Prior: `flow-inicio-turno` — $500 float opens a real shift; a duplicate open → server 409 “Ya hay un turno abierto” | | US-SHIFT-2 | Record cash movements (pay-in/pay-out) with a reason. | cashier | M10/M29 `63:258`/`291:951` | M-2 | ✅ | ✅ | ✅ | **N86 09-09:** −$125 / +$200 with reasons green on the device; reached via the Sell FAB only through the drawer hijack (BUG-2). Prior: pay-out −$125 + pay-in +$200, each with a required reason; reason-required rejection enforced; movements persist in M10. concept picker undesigned (gap #38) | | US-SHIFT-3 | Close my shift with a **denomination breakdown** and see the variance. | cashier | M09 `300:4229` | M-2 | 🟡 | 🟡 | 🟡 | **N86 09-09: driven live** — count $610 vs expected $575 → “Descuadre detectado” → supervisor approval → `mobile_shift_close_total{result=success}`. But **Ventas brutas is hardcoded $0 on mobile and the server’s expected cash excluded the shift’s $3 cash sale (BUG-10, Q-n86-4 = gap #18)**; no coin rows in the count grid (UX-14); Home still shows the shift open afterwards (BUG-1) | | US-SHIFT-4 | Approve an over-tolerance cash variance at close. | supervisor | M27b `135:929` | M-7 | ✅ | 🟡 | ✅ | **N86 09-09:** `mobile_approval action=SHIFT_VARIANCE result=GRANTED` on the device, shift closed. Prior: the shared **M27b** approval sheet is verified live (US-VOID-1); the shift-close variance leg not driven (depends on SHIFT-3) | `US-SHIFT-4` is a story whose screens and flow ticket disagree (sequencing note, TODO `T-7`): the journey is shift-close (M-2 / #222) but the approval overlay it renders is the shared `M27b`, owned by #224. Both tickets must land before it can be exercised end-to-end. `US-VAS-5` has the same shape across #225/#224. **Not on mobile:** `US-SHIFT-5` (immutable Z-report per closed shift, W33/W58) — the *report artifact* is a web/back-office surface, even though mobile M09 performs the close that produces it. ## VAS — Value-added services (M-5, M-4) — 5 stories [Section titled “VAS — Value-added services (M-5, M-4) — 5 stories”](#vas--value-added-services-m-5-m-4--5-stories) > **Deferred this session** — VAS was not part of the 2026-07-31 Pass D run (`Built`/`Mock E2E` carried forward from the prior review; `Live E2E` = —). It requires `FEATURE_FLAG_VAS_MOBILE_CATEGORIES` (app, committed **on**) + a VAS-configured API (aggregator). | ID | Story | Roles | Mobile screens | Flow | Built | Mock E2E | Live E2E | Notes | | -------- | ---------------------------------------------------------------------------------------- | ---------------- | --------------------------- | --------- | ----- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | US-VAS-1 | Sell **airtime/TAE** to a phone number. | cashier | M21 `80:545` | M-5 | ✅ | 🟡 | ⏭️ | **Δ 09-09: rebuilt on the api path** (`vas/VasApiClient.kt`, TAE tab in-place in the M11 picker — #356, robustness #375→#383). Live = gated on the aggregator (fail-closed 503 stub until Mantarys lands); **TAE tab never device-checked** | | US-VAS-2 | Sell **gift-card PINs** with secure delivery/reprint. | cashier | M22 `81:589` | M-5 | ✅ | 🟡 | ⏭️ | **Δ 09-09: rebuilt on the api path** (Pines tab, same client). Live gated on the aggregator; **Pines tab never device-checked** | | US-VAS-3 | Pay a **service/bill** (with adeudo lookup). | cashier | M20 `76:503` | M-5 | ✅ | 🟡 | 🟡 | **Δ 09-09: rebuilt on the api path**; **Servicios tab driven live on the N86 2026-08-12** (form → aggregator stub); adeudo robustness #375→#383 | | US-VAS-4 | Sell a **SIM chip** from serialized inventory (ICCID/NIP). | cashier | **(no M frame)** — M-5 | M-5 | ⚠ | — | — | Track C; `serialized_units` (deferred) | | US-VAS-5 | Register a customer **transfer** (financial services) and confirm it with a comprobante. | cashier, manager | M23 `82:631` · M18 `74:456` | M-5 → M-4 | ⚠ | 🟡 | — | **Δ 09-09: still UNBUILT** — no transfer capture/confirm screens on main (`connections/AddBalance*` is Conexiones top-up, a different flow) | **`US-VAS-5` is in mobile MVP — PO decision 2026-07-22.** Mobile carries the **transfer leg only**: `M23` Transferir saldo → `M18` Confirmar transferencia / Comprobante. Registering **deposit channels A–C** and manually validating them (`pending → available`) stays web-only — that is `US-BAL-2`. The wallet **balance** itself is never shown on mobile (owner-only, [PDR-0003](/pdr/0003-vas-connectivity-phasing-and-wallet-visibility/)). Note the story spans two flow tickets: `M23` sits in M-5 (#225) and its confirmation screen `M18` in M-4 (#224) — both must land before the journey is exercisable end-to-end. **Not on mobile:** `US-VAS-6` (VAS reporting by kind/provider/commission, W57) — back-office reporting. ## BAL — Merchant wallet (M-4, M-5) — 4 stories [Section titled “BAL — Merchant wallet (M-4, M-5) — 4 stories”](#bal--merchant-wallet-m-4-m-5--4-stories) > **Deferred this session** — BAL was not part of the 2026-07-31 Pass D run. Requires `FEATURE_FLAG_MERCHANT_WALLET*` (backend) + the Track-K wallet wiring. | ID | Story | Roles | Mobile screens | Flow | Built | Mock E2E | Live E2E | Notes | | -------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------- | ----------------------------------------------- | ---- | ----- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | US-BAL-3 | Customer’s **direct cash payment (channel D)** credits **store cash**, never the wallet, with a printable receipt. | cashier | M27d `135:1047` | M-7 | 🟡 | 🟡 | — | rides existing cash-movements; channel semantics Track K (deferred) | | US-BAL-4 | **Card payment on the handheld (channel E)** credits the merchant wallet immediately. | cashier | M15/M16 `71:403`/`72:407` | M-4 | ✅ | 🟡 | ✅🔌 | **Δ 09-09: the channel-E deposit is inside the unified settlement tail** (order\_payment → finalize → deposit, exactly-once via the deposits UNIQUE) **proven live on the N86 2026-08-24** with real $1 charges. Card-present for evidence | | US-BAL-5 | **Top up the wallet by card on the handheld (autopay)** — confirms success without revealing the balance to non-owners. | manager, cashier | **(no M frame)** — new handheld screen, Track K | — | 🟡 | 🟡 | 🔌 | **Δ 09-09: built but DORMANT** — `TopupViewModel` + `wallet-topup.yaml` shipped ([#259](https://github.com/inspiraCode/terminales/issues/259)); gated by `FEATURE_FLAG_MERCHANT_WALLET_TOPUP_MOBILE=false` (BuildConfig). Flag-minimalism: PO decides keep-and-flip vs drop before launch | | US-BAL-6 | VAS sale **blocked when the merchant wallet lacks funds**. | cashier | M20–M23 | M-5 | 🟡 | ⚠ | — | **Δ 09-09:** Conexiones balance sheet + POS platform-balance tender selector shipped (#357→#362, #359→#361, #373→#381); the insufficient-funds **block** itself is unverified | `US-BAL-5` is the only mobile story with **no Pass C parity ticket** (deferred, TODO `T-6`) — it has no canonical Figma frame, so it never entered the flow audit. It is already covered by the pre-existing E2E draft `backlog/drafts/mobile-merchant-balance-autopay-topup-e2e.md` → issue [#259](https://github.com/inspiraCode/terminales/issues/259). A frame must be designed before it can be parity-audited. **Not on mobile:** `US-BAL-1` (wallet balance + movements — **owners only**, W47), `US-BAL-2` (register and manually validate deposit channels A–C, W46), `US-BAL-7` (serialize wallet operations — system/backend), `US-BAL-8` (withdraw to bank — post-MVP). Per [PDR-0003](/pdr/0003-vas-connectivity-phasing-and-wallet-visibility/) the wallet **balance** is deliberately owner-only and therefore deliberately absent from the cashier-facing mobile app. ## CLI — Clientes / customers (M-8) — 2 stories [Section titled “CLI — Clientes / customers (M-8) — 2 stories”](#cli--clientes--customers-m-8--2-stories) | ID | Story | Roles | Mobile screens | Flow | Built | Mock E2E | Live E2E | Notes | | -------- | ------------------------------------------------------------ | ------- | ----------------------------- | ---- | ----- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | US-CLI-1 | Look up / quick-create a customer and assign them to a sale. | cashier | M32/M33 `300:4086`/`300:4129` | M-8 | ✅ | ✅ | 🟡 | **Δ 09-09: mobile flag RETIRED (#387)**. Prior: **wired live this session**: the cart FAB “Asignar cliente” now loads the real directory (`CartCustomerPickerViewModel` → `CustomersApiClient`, not mock recents); picking stores the customer on `CartStateHolder` and it threads through finalize onto `sales_orders.customerId` (`flow-clientes-assign.yaml` picks the seeded “Ana López” + a cash sale persists `order.customerId=e2e_customer_ana`). Gated on the two CLI flags | | US-CLI-2 | Customer directory with detail. | manager | M30/M31 `300:4041`/`300:4052` | M-8 | ✅ | ✅ | 🟡 | **Δ 09-09: mobile flag RETIRED (#387) → no longer gated on the app side**; follow-ups #369 open (detail page-scan, 20-row truncation, unassign analytics). Prior: `flow-clientes` — M30 renders the live seeded directory (Ana/Beto/Celia) + server-side search. **Gated on TWO flags**: `FEATURE_FLAG_CUSTOMERS_MOBILE` (app, default OFF → no nav row, gap #45) **and** backend `FEATURE_FLAG_CUSTOMERS_MODULE` (default OFF → `403 El módulo de clientes no está habilitado`). With both on, the directory works live | Mobile is **ahead of web** here: the master map records that the v2 Clientes module (`230:2`) was absorbed into the mobile flow M-8 but not yet into web. ## ACCT — Account & settings (M-6) — 1 story [Section titled “ACCT — Account & settings (M-6) — 1 story”](#acct--account--settings-m-6--1-story) | ID | Story | Roles | Mobile screens | Flow | Built | Mock E2E | Live E2E | Notes | | --------- | -------------------------------------------- | ----- | -------------- | ---- | ----- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | US-ACCT-1 | View/edit my account and security (2FA/PIN). | all | M24 `83:647` | M-6 | 🟡 | ✅ | ✅ | `flow-cuenta-reportes` — M24 “Mi cuenta” renders Datos personales / Soporte / Cerrar sesión live. **N86 09-10 (PB-2, fix in #452):** “Cerrar sesión” sat **below the 640dp fold for an OWNER** — the owner-only “Plataformas de saldo” row (#358) costs 71dp and pushed it to top 586dp in a 568dp body. Measured, not estimated. The cashier margin was only \~13dp and flaked to a failure once in six pre-fix runs, so read this as *the fold was marginal for every role and the owner crossed it first*. Now top bar → `weight(1f)` scrolling band → pinned footer (the S13 cash-sheet/receipt shape); sign-out also raised from 40dp to 52dp, having been below the 48dp a11y floor independently. **PB-1 also applied to this screen** — its hamburger was dead, so Mi cuenta could not be left via the drawer. Edit + 2FA/PIN legs not driven (security via 2FA #124) | **Not on mobile:** `US-ACCT-2` (general store settings, W61) and `US-ACCT-3` (audit log / bitácora, W62) — manager back-office. ## NOTIF — Notifications (M-2) — 1 story [Section titled “NOTIF — Notifications (M-2) — 1 story”](#notif--notifications-m-2--1-story) | ID | Story | Roles | Mobile screens | Flow | Built | Mock E2E | Live E2E | Notes | | ---------- | ------------------------------------------------ | ----- | -------------------------- | ---- | ----- | -------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | US-NOTIF-1 | Notifications center + detail with unread badge. | all | M07/M28 `60:177`/`164:937` | M-2 | ✅ | 🟡 | ✅ | **N86 09-09:** list (4 unread, day groups), detail, “Ver producto” deep link → M12, mark-all → badge 0 (read state via api) all green; UX-12 raw payload keys in Detalles. Prior: `flow-notificaciones.yaml` (new) — the top-bar bell opens M07, which renders live seeded notifications day-grouped (unread + read). detail/deep-link gap #34 | *** ## Gaps follow-up — “missing designs” are mostly built-but-unframed (2026-07-31) [Section titled “Gaps follow-up — “missing designs” are mostly built-but-unframed (2026-07-31)”](#gaps-follow-up--missing-designs-are-mostly-built-but-unframed-2026-07-31) A pass over the design/frame gaps flagged in this file found that, **for the in-scope groups, the “missing figma design” screens are already implemented in code** (mock-first, from the design system + string resources) — what they lacked was a canonical figma frame and a live driving. Confirmed by building nothing and simply driving them: * **US-POS-9 (offline-blocked sale surface, “no M frame”)** — the M13 block already exists: `CartScreen.CartOfflineBanner` (an `InlineAlert` over `pos_cart_offline_*`) renders when `CartViewModel.isOffline`, and `canCheckout = lines && !isOffline` withholds “Cobrar”. Authored `flow-pos-offline.yaml` (airplane-mode → banner + block → “Reintentar” restores) — **now ✅ Live**. * **US-CLI-1 assign-on-sale — wired live this session** (was mock): the cart FAB now uses the real directory and threads `customerId` through finalize onto the order (see the US-CLI-1 row). `AssignCustomerFlow` (the order-PATCH variant) still exists for assign-after-finalize. * **Terminal-selection screen (gap #36) — built this session.** Rather than a “become another terminal” picker (which conflicts with the claim-one-terminal handheld model), the new `TerminalSelectionScreen` is a **roster/visibility** screen: it lists the store’s terminals via `GET /pos/terminals` (store-scoped server-side) with each one’s online status and marks this device’s own, reachable from “Registro de terminal”. Binding still happens by pairing code. See the US-ACC-5 row. * Still built-but-unframed and drivable (not yet driven this pass): **US-SHIFT-3** close-shift M09 (`ShiftCloseScreen`), the **gap #12** discount over-threshold state (verified indirectly via US-VOID-1’s M27b approval), and **gap #33** new-password step (driven in `flow-auth`). * **Email stories (US-ACC-2 MFA, US-ACC-3 recovery):** **Mailpit** added to `terminales-api/docker-compose.yaml` (SMTP `:54325` matching `.env`, inbox `http://localhost:8025`). It catches mail the **api** sends; it does **not** catch the MFA/recovery OTP+reset emails, which **cloud Appwrite** sends. Runbook to close those legs via the **IONOS Mailpit** + Appwrite project SMTP + `terminales://` platform registration: `terminales-api/docs/runbooks/appwrite-mailpit-email-e2e.md` (the console-SMTP + platform steps need Appwrite console access + the CI-secret Mailpit host — a hand-off). ## Live E2E run — findings (2026-07-31) [Section titled “Live E2E run — findings (2026-07-31)”](#live-e2e-run--findings-2026-07-31) First mobile Pass D run (groups ACC · POS · VOID · SHIFT · CLI · ACCT · NOTIF; VAS + BAL deferred). The app talks to **cloud Appwrite** for domain data + auth and to a **local `terminales-api` :3020** for the POS API (the app reaches it via `adb reverse tcp:3020`); Postgres holds only DBOS/cache, so an empty `public` schema is expected. Work landed on the `verify/user-stories-functionality` branch of each repo. **App bug found + fixed (real defect):** * **Cash finalize crashed the app** — `java.lang.IllegalAccessError: Field 'FinalizeOrderResponseDto.Companion' is inaccessible` (`SaleFinalizeApiClient`). The `@Serializable` response DTO carried a **`private companion object`** (for its JSON key constants), so the reified `decodeFromString` — inlined into a synthetic lambda — could not reach the generated `serializer()`. Every cash sale crashed at the finalize response. Mobile Pass D had never run, so it was never caught. **Fix:** make that companion non-private. Unblocks US-POS-3/POS-7 (and any decode of that DTO). **Backend / cloud-schema drift found (needs a fix to unblock):** * **US-POS-5 split tender — a chain of settlement schema drift + one app bug.** Peeling it back layer by layer: 1. `sales_orders` missing `amountPaid` + `amountRemaining` (`double`) — **added** (`createFloatAttribute`). 2. `sales_orders.status` enum allowed only `(FINALIZED, VOID)`; settlement opens as `OPEN` — **`OPEN` added** to the enum. 3. With settlement now **opening** live, the **tender** leg 403’d — a **real app bug**: `CashTenderRequestDto` omitted `storeTeamId` (which the server’s role check + `AddTenderDto` require) and sent `changeGiven` (rejected by `forbidNonWhitelisted`). **Fixed** in `PosOrderGateway`/`CardChargeCoordinator` (thread `storeTeamId` from the resolved sale context, drop `changeGiven`) — a manual `POST …/tender` with `storeTeamId` confirmed the **403 is cleared**. 4. The tender then 500’d on **`order_payments` missing `clientId`** (the per-tender idempotency key it queries + writes) — added via `scratchpad/fix-order-payments.js` (it was the only missing column). 5. With the tender reaching the server, it silently no-op’d: the shared **`TenderResponseDto` read `remaining`/`status`/`orderPaymentId`** but the endpoints return **`amountRemaining`/`finalized`** (open returns the row’s top-level `$id`; tender returns a nested `order`). The balance therefore always decoded to **0**, so the split never advanced. **Fixed** the response model + accessors to decode both shapes (`amountRemaining`, `finalized`, top-level `$id`). * **Result:** split settlement now works live — two `pos.settlement.tender_applied` events against a server-recomputed running balance. Residual: the flow’s 8-nines saturation leg leaves a **1-cent** remainder (`amountRemaining=1`) so it doesn’t auto-finalize — a coerce-rounding follow-up worth a small ticket, not a settlement failure. * The shared-cloud-schema writes (steps 2 + 4) were **run by the user** (the auto-mode classifier blocked the agent from writing to the shared cloud project); the float attributes (step 1) went through directly. * **Non-fatal rollup error on finalize** — `pos.rollup.update_failed … Unknown attribute: "salesByKind"` — the reporting rollup writes a `salesByKind` attribute the cloud collection lacks. The sale still commits (rollup is best-effort), but the report aggregate silently misses it. **Environment gating discovered (not bugs — needed to exercise the stories):** * **CLI needs two flags** — `FEATURE_FLAG_CUSTOMERS_MOBILE` (app, default OFF → the “Clientes” nav row is absent, gap #45) **and** backend `FEATURE_FLAG_CUSTOMERS_MODULE` (default OFF → the API 403s “El módulo de clientes no está habilitado”). Both were flipped on to verify US-CLI-2 live; **the app flag must be reverted before merge** (CI nav-gate tests enforce OFF). * **Seed gap — FIXED** — `scripts/seed-e2e.ts` seeded the product but no variant/`stock_levels` row, so the default variant read **quantity 0 → “Agotado” → not addable** and the whole POS money-path was blocked. The seed now upserts a **default variant + a `stock_levels` row (quantity 100)** so the catalog is sellable on a fresh seed (verified: `seed:e2e` runs clean). **Maestro flow drift repaired (the flows had never run against the live app):** product taps by coordinate → by name; the cash keypad `id:numpad_key_*` → text taps (its `NumericKeypad` testTag is a non-merged semantics node `id` can’t see) and cash tendered via **“Exacto”** (the seeded price is high); a `contentDescription:` selector (invalid in Maestro 2.5.1) → text; the action-hub rows made scroll-robust for the taller VAS-on layout (`centerElement`); the M27b approval keypad uses **bare digits** (not M02’s `"Tecla "`), its first tap needs `waitForAnimationToEnd`, and on 1080×2400 the keypad pushes the **`[Cancelar|Aprobar]` row below the fold → must `scrollUntilVisible "Aprobar"`** (a real M27b layout finding affecting every approval: void / discount / variance). New helper `subflows/claim-terminal.yaml` (idempotent pair + PIN after `clearState`, which wipes the local pairing) and the runner’s in-app login is the **cashier** (whose PIN matches) while the pairing code is minted with the **manager** (issuer-only). **New flows authored:** `flow-void.yaml` (US-VOID-1, emulator-runnable — the combined `flow-cobro-recibos` reaches void only through a card-present leg) and `flow-notificaciones.yaml` (US-NOTIF-1). ### US-ACC-3 password recovery — driven green via https App Links (2026-08-01) [Section titled “US-ACC-3 password recovery — driven green via https App Links (2026-08-01)”](#us-acc-3-password-recovery--driven-green-via-https-app-links-2026-08-01) **Real product defect found + fixed.** US-ACC-3’s send half had been logged as “infra — register the `terminales://` platform”. That premise was **wrong**, proven live by probing `createRecovery` with a non-existent email (URL validation runs before user lookup, so `404 user_not_found` = URL accepted, `400 general_argument_invalid` = URL rejected): | `url` | result | | ---------------------------------- | --------------------------------------------------------- | | `terminales://auth/recovery` | **400 rejected** — arbitrary custom scheme never accepted | | `https:///recovery` | 404 accepted (a `*` Web platform covers any https host) | | `appwrite-callback-terminales://…` | 404 accepted (Appwrite’s standard mobile scheme) | So **no platform registration can fix it** — Appwrite Cloud’s redirect-`url` validator accepts only `http(s)://` or `appwrite-callback-://`. Recovery was therefore broken in **production**, not just the E2E. **Fix (best-practice, mobile):** migrate the recovery callback to a **verified Android App Link** (also the right security posture — a custom scheme carrying a single-use reset secret is hijackable; an App Link is bound to the app’s signing cert): * `RECOVERY_REDIRECT_URL` → `https://${BuildConfig.MAGIC_LINK_HOST}/recovery`; `magic.link.host=links.io.inspiracode.com`. * `/recovery` added to the `autoVerify` https ``; `AuthDeepLinks` routes verified `https:///recovery` → `Recovery`. Contracts re-pinned in `AppwriteRecoveryRepositoryTest` + `AuthDeepLinksTest` (full unit suite + spotless/detekt/lint green). * **Infra:** `assetlinks.json` (package + **debug** cert SHA-256) served at `https://links.io.inspiracode.com/.well-known/assetlinks.json` via a small nginx behind the shared Traefik on the IONOS box (`/opt/terminales-assetlinks/`, TLS auto-issued). The host reports `verified` on the emulator (`pm get-app-links`). * **Verified end-to-end:** M04 *Enviar enlace* → `createRecovery` 201 → mail in Mailpit → App Link opens the reset screen → password set. `run-recovery.sh` orchestrates send → Mailpit read → reset. **Magic-link + verify-email — same fix applied (2026-08-01).** Both shared the root cause and were migrated in the same way: * **Magic-link** (US-ACC-1 AC3): `MAGIC_LINK_REDIRECT_URL` → `https://${MAGIC_LINK_HOST}/magic-link`. **Driven green end-to-end** (M01 → `createMagicURLToken` → Mailpit → App Link → session → M05): `flow-auth-magic-send`+`-complete`. * **Verify-email** (#93 AC1): `EMAIL_VERIFY_REDIRECT_URL` → `https://${MAGIC_LINK_HOST}/verify-email`, `/verify-email` added to the `autoVerify` filter, routed in `AuthDeepLinks`. **URL acceptance proven live** (a signed-in `createVerification` returns `409 user_email_already_verified` for the https url on a verified account vs `400 general_argument_invalid` for the custom scheme) and the routing is unit-tested. Full UI run **staged** (`flow-auth-verify-email.yaml`): its send screen is **playground-only** (`Routes.Playground.APPWRITE_MFA_SETTINGS`) and needs an **unverified** account, so it isn’t a production journey yet. `AuthDeepLinks` now routes all three callbacks on both transports (verified https + the `terminales://` custom-scheme fallback for in-flight emails). US-ACC-2 MFA is unaffected (it uses the email OTP challenge, not this deep link). **Before shipping:** add the **release** signing-cert fingerprint to `assetlinks.json`, and give verify-email a production entry point. *** ## Explicitly out of mobile scope [Section titled “Explicitly out of mobile scope”](#explicitly-out-of-mobile-scope) These areas have **zero** mobile stories. They are the back-office and platform surfaces that make web the larger app. | Area | Stories | Why web-only | | -------------------------------------------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **CAT** — Catalog | US-CAT-1…3 (3) | Product/variant/barcode/price authoring, categories, tax rates — manager/supervisor authoring, not counter work. Mobile *consumes* the catalog (US-POS-1). | | **INV** — Inventory | US-INV-1…3 (3) | Stock levels, adjustments with reason, inter-store transfers — manager/supervisor. (Note gap #34: a notification deep-link points at a mobile inventory flow that does not exist.) | | **STAFF** — Staff & access | US-STAFF-1…4 (4) | Employees + PINs, invites, role→permission matrix, server-side cross-store deny — administration. | | **REP** — Reports | US-REP-1…6 (6) | Daily sales, margin, tender/employee mix, exception reports, commissions, role-scoped CSV export. Mobile has a **Reportes** screen (M25) but no story claims it — **PO deferred 2026-07-22**, TODO `T-1`. | | **TERM / WL** — Commerce, stores, terminals, white-label | US-TERM-1, US-TERM-2, US-WL-1 (3) | Fleet configuration, terminal pairing, brand/receipt theming — manager. | | **PLAT** — Platform administration | US-PLAT-1…3 (3) | Merchant/user management, audited impersonation, white-label assist — platform roles only; **no mobile surface by design**. | | **SEARCH** — Global search | US-SEARCH-1 (1) | ⌘K command palette — a desktop interaction pattern. | | Remaining web-only rows in mobile-touching areas | US-ACC-6, US-POS-6, US-VOID-2/3, US-SHIFT-5, US-VAS-6, US-BAL-1/2/7/8, US-ACCT-2/3 (12) | Listed under their areas above. | ## Coverage inversion — mobile screens with no user story [Section titled “Coverage inversion — mobile screens with no user story”](#coverage-inversion--mobile-screens-with-no-user-story) **Resolved 2026-07-22:** `M23` Transferir saldo and `M18` Confirmar transferencia / Comprobante are now claimed by `US-VAS-5` (PO: in mobile MVP) — see [VAS](#vas--value-added-services-m-5-m-4--5-stories). Seven canonical mobile frames remain owned by a Pass C parity ticket but **not claimed by any story**. All are **deferred with a TODO** — tracked as `T-1`…`T-5` in [§ Deferred TODOs](#deferred-todos--po-mobile-scope-review-2026-07-22). None blocks the UI parity tickets (#221–#227); each must be settled before the mobile E2E (Pass D) batch is finalized. | Screen | Node | Flow | Pass C ticket | Status | Assessment | | ----------------------------- | ---------- | ---- | ------------- | ------------ | -------------------------------------------------------------------------------------------------------------------- | | M06 Inicio (Dashboard) | `300:4317` | M-2 | #222 | **TODO T-2** | Hero-flow entry point; no story. Bottom-nav IA still undecided (gap #37, 4 vs 5 items). | | M12 Detalle de producto | `68:344` | M-3 | #223 | **TODO T-3** | Detail step implied by US-POS-1, named by no story. | | M14 Cobrar / Método de pago | `70:389` | M-3 | #223 | **TODO T-3** | The tender picker every US-POS-3/4/5 passes through; implied, never named. | | M19 Detalle de ticket | `75:475` | M-4 | #224 | **TODO T-4** | Adjacent to US-VOID-1 and US-POS-7. Missing Cancelado/Reembolsado states (gap #35). | | M25 Reportes | `85:699` | M-6 | #226 | **TODO T-1** | REP is entirely web-only, yet a mobile Reportes screen exists. **PO deferred** which reports and at what role scope. | | M26 Historial / Buscar ticket | `86:740` | M-6 | #226 | **TODO T-1** | Ticket lookup feeding M19; adjacent to US-VOID-1. Deferred with M25. | | M35 FAB · Acceso rápido | `300:4289` | M-8 | #227 | **TODO T-5** | Quick-action FAB; “Abrir menú” destination undesigned, active-cart rule undecided (gap #44). | **Do not author mobile E2E tickets for these seven screens** — there is no story to give them acceptance semantics. #221–#227 already cover them as UI parity + navigation only, which is sufficient for the current phase. ## Role shape on mobile [Section titled “Role shape on mobile”](#role-shape-on-mobile) | Role | Mobile stories | | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **cashier** | 23 named explicitly (+ the 6 `all` stories = 29 reachable) — the bulk of the app: sale, tenders, receipt, shift, cash movements, VAS, transfers, customer assign, hold/resume, PIN access | | **supervisor** | 3 named explicitly — approval overlays only (US-POS-2 discount, US-VOID-1 void, US-SHIFT-4 variance) — plus everything `cashier` and `all` reach | | **manager / owner** | 3 — US-CLI-2 (customer directory), US-BAL-5 (handheld wallet top-up), US-VAS-5 (transfers, shared with cashier); everything else managerial is web | | **platform\_admin / support / developer** | 0 — by design | | **customer / guest** | 0 — reserved roles, no MVP stories on either platform | This is the concrete evidence for the “mobile = operator, web = operator + back office” framing: **three** manager-touching stories out of thirty-one, and **none** of them manager-exclusive back-office work. ## What this file is for next [Section titled “What this file is for next”](#what-this-file-is-for-next) The mobile track completed Pass A (Maestro harness, #215), Pass B (component families, #216–#220 / #247–#252), and Pass C (the seven flow parity + navigation tickets, #221–#227) — all **UI-only / mock**. **Pass D** — the mobile E2E batch: backend + frontend lifted together against the live stack — **began 2026-07-31**: the seven in-scope groups (ACC · POS · VOID · SHIFT · CLI · ACCT · NOTIF) were driven live and their **Live E2E** column filled above; VAS + BAL remain to run. See [§ Live E2E run — findings](#live-e2e-run--findings-2026-07-31) for the bug/schema/flow fixes that unblocked it. The 31 stories above are that batch’s scope list — **one E2E ticket per story**, or fewer if stories sharing a screen and a money path are collapsed (the M27 overlay cluster, US-POS-2/3/5 + US-VOID-1 + US-SHIFT-4, is the obvious candidate). The scope list is **settled enough to author against**: the PO’s 2026-07-22 review admitted `US-VAS-5` and deferred everything else with TODOs `T-1`…`T-8`. Those deferrals are all *unclaimed screens* (T-1…T-5), a *missing frame* (T-6), a *sequencing note* (T-7), and a *node-id cleanup* (T-8) — none of them removes a story, and only T-1 (mobile reports) could **add** one. So Pass D can be authored for the 31 now, with M25/M26 explicitly excluded until T-1 lands. ## Deferred TODOs — PO mobile scope review (2026-07-22) [Section titled “Deferred TODOs — PO mobile scope review (2026-07-22)”](#deferred-todos--po-mobile-scope-review-2026-07-22) Raised while deriving this file (relocated here in the 2026-07-30 web ⇄ mobile split — all are mobile-scope). All **deferred, not dismissed**. None blocks the mobile UI/parity tickets (#221–#227); all must be settled before the **mobile E2E (Pass D)** batch is finalized, because an E2E ticket for a screen with no story has no acceptance semantics to test against. | # | Item | Deferred decision | | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | | T-1 | **`M25` Reportes + `M26` Historial / Buscar ticket** have mobile frames but every REP story is web-only. | Which reports does mobile get, at which role scope? | | T-2 | **`M06` Inicio (Dashboard)** (`300:4317`) — the hero-flow entry point — is claimed by no story. Bottom-nav IA is also still undecided (master-map gap #37, 4 vs 5 items). | Does the mobile dashboard need its own story, or is it purely navigational chrome? Settle alongside gap #37. | | T-3 | **`M12` Detalle de producto** (`68:344`) and **`M14` Cobrar / Método de pago** (`70:389`) are implied by `US-POS-1` and `US-POS-3/4/5` but named by neither. | Extend the existing stories’ Screens columns, or accept them as implied steps. Low risk either way. | | T-4 | **`M19` Detalle de ticket** (`75:475`) is adjacent to `US-VOID-1` / `US-POS-7` but unclaimed, and is missing its Cancelado/Reembolsado states (gap #35). | Attach to a story once the void/refund state package is designed. | | T-5 | **`M35` FAB · Acceso rápido** (`300:4289`) is unclaimed; its “Abrir menú” destination is undesigned and the active-cart rule is undecided (gap #44). | Rides on gap #44 — PO decides the menu destination and the discard-vs-block rule for an active cart. | | T-6 | **`US-BAL-5`** (handheld wallet top-up / autopay) is mobile-scoped but has **no Figma frame**, so it never entered the Pass C parity audit. It is covered by E2E draft `backlog/drafts/mobile-merchant-balance-autopay-topup-e2e.md` → [#259](https://github.com/inspiraCode/terminales/issues/259). | Design the handheld top-up frame so the story can be parity-audited, or accept it as E2E-verified only. | | T-7 | **`US-SHIFT-4`** straddles two tickets: the journey is shift-close (M-2 → [#222](https://github.com/inspiraCode/terminales/issues/222)) but the approval overlay it renders is the shared `M27b`, owned by [#224](https://github.com/inspiraCode/terminales/issues/224). | No decision needed — just sequencing: both tickets must land before the story is exercisable end-to-end. | | T-8 | **Node-id discrepancy**: `US-ACC-3` / `US-ACC-5` cite the `M04b`/`M05b` variants (`539:1215`, `546:1229`) while [#221](https://github.com/inspiraCode/terminales/issues/221) lists canonical `56:88` / `57:85`. | Settle which frame is canonical during #221; then correct the Screens column here. | ## Maintenance [Section titled “Maintenance”](#maintenance) Mobile screens + **mobile** status are tracked **here**; the shared **story text and roles** live in [user-stories.md](/product/user-stories/) (web). When either changes: * a story gains or loses an `M##` screen → add or remove its row here, and update the counts in [Why mobile is a subset](#why-mobile-is-a-subset); * a story’s **mobile** implementation/coverage changes → update its **Built · Mock E2E · Live E2E** columns here directly (no longer mirrored from the web file); * a **shared** story’s text or roles change → mirror the wording here (the web file owns the story text); * a new mobile frame is designed → check it against the [coverage inversion](#coverage-inversion--mobile-screens-with-no-user-story) table and either attach it to a story or record it as an open question; * a new **mobile-only** story is added → add it here directly (US-BAL-4 / US-BAL-5 are the current mobile-only stories). # MVP completeness — cross-lane inventory, 2026-09-12 > answer "what is left before MVP" with evidence rather than impression, so the remaining > **Purpose:** answer “what is left before MVP” with evidence rather than impression, so the remaining work can be delegated. Compiled from seven lane inventories in one night; every lane read code or a running system rather than recalling. Scope is frozen by [PDR-0001](/pdr/0001-mvp-golive-scope/) (VAS + Clientes IN; QR tenders, cobro rápido, POS interaction modes, KPI drill-down OUT) and [PDR-0003](/pdr/0003-vas-connectivity-phasing-and-wallet-visibility/) (VAS Phase 1 only). > > Status: awaiting PO rulings (D1–D9 below). Not a plan until those are answered. ## The headline [Section titled “The headline”](#the-headline) **Almost nothing is unbuilt. Most of what remains is not running where it matters.** Four lanes arrived at that independently. The verification reconcile was live on the api and two commits behind on the web the human clicked. The VAS aggregator is M0–M12 merged and fails closed on every realm. The category migration has run on zero realms. A fresh merchant could not open a shift because the realm lagged main’s permissive default. A code-complete inventory is not an answer to “can we ship” — hence the per-realm deploy matrix, commissioned separately. **Three real build gaps survive that framing**, and two of them are the things PDR-0001 explicitly put IN. ## The three genuine gaps [Section titled “The three genuine gaps”](#the-three-genuine-gaps) ### G1 — VAS web (W-09) is not built [Section titled “G1 — VAS web (W-09) is not built”](#g1--vas-web-w-09-is-not-built) PDR-0001 names VAS as a core revenue surface the client needs day one. The designs exist (W41–W46); the web rebuild never landed — the legacy TypeORM surface is all there is. US-VAS-4 has no e2e and no Figma. **This is the largest hole in the frozen scope**, and it has no lane. Mobile VAS shipped; web did not. ### G2 — Clientes exists as routes nobody can reach [Section titled “G2 — Clientes exists as routes nobody can reach”](#g2--clientes-exists-as-routes-nobody-can-reach) `/admin/customers` and `/admin/customers/[id]` are built and have **no UI entry point**. The board marks US-CLI “not absorbed”; the web `CUSTOMERS_WEB` flag is dead (not referenced in `src/`) while the api’s `CUSTOMERS_MODULE` is true. Code, board and both flag planes disagree. PDR-0001 puts Clientes IN. ### G3 — An invited cashier cannot sign in [Section titled “G3 — An invited cashier cannot sign in”](#g3--an-invited-cashier-cannot-sign-in) Found independently by the auth and onboarding lanes from opposite ends, which is why it is the most certain item here. Accept creates a membership and a PIN-less employee row but **no password and no first session**; `setOwnPin` needs a session that needs a password the invite never creates, so pin-login 401s. No endpoint, no ticket. The solo owner is unaffected — they are created as an employee at onboarding. **If MVP includes non-owner staff, this is required.** ## Verification — CORRECTED 2026-09-12 late: the images do not decode [Section titled “Verification — CORRECTED 2026-09-12 late: the images do not decode”](#verification--corrected-2026-09-12-late-the-images-do-not-decode) > **Supersedes the section below.** The “document front never reaches the engine” diagnosis was WRONG and was caught by insisting on a seam check before the fix. Reading the one real capture at rest (`vs_76441da68c9f5a026c3404185610`, the only prod session with stored `capture_segments`) shows `document_front`, `document_back`, `selfie_still` and `selfie_video` all present — and the checks **RAN**: `document` = CONSIDER with `undecodable_image:front`, `undecodable_image:back`, `mrz_absent`, `portrait_crop_failed`; `face_match` = CONSIDER with `face_recapture_required`. > > **The engine works and is reporting a real problem: the stored images do not decode.** The earlier `not_implemented` / `missing_id_portrait` symptoms came only from SYNTHETIC step-only drives that stored no evidence at all, and were carried onto a real capture nobody had inspected. > > Two of the three months-risks retire. What remains is one discriminator: open the stored front image. A readable image means the OCR cannot read a real Mexican INE (the long tail). A corrupt, truncated or re-encoded one means it is capture-side on the astro half (days, not months). **Both front and back failing identically points at the pipeline, not the photography.** ## Verification — the superseded diagnosis, kept for the record [Section titled “Verification — the superseded diagnosis, kept for the record”](#verification--the-superseded-diagnosis-kept-for-the-record) Every real capture lands `needs_review`: document check returns `not_implemented`, face match returns `missing_id_portrait`. The cause is pinned to the **document front image never reaching the engine** — an evidence-assembly gap in the verification pipeline, not a vendor gap and not thresholds. The engine checks are code-complete. Owner: the verification solution. Read: weeks, not months. An isolation repro at the engine boundary was requested before the fix is built. **CORRECTED — selling IS blocked.** An earlier version of this document said a fresh merchant sells by default. That was wrong: the PO read `allowUnverifiedSales` from a stale local branch. On `origin/main`, `merchant-provisioning.service.ts:229` stamps **`allowUnverifiedSales: false`** on every create path (`createMerchant` and the self-serve `createOwnMerchant`), locked by two specs. Since `overrideOpen` is `allowUnverifiedSales !== false`, a merchant is **born gate-closed** and cannot sell, open a shift, register a cash movement, invite staff or add a second store. Combined with B1 (emails disabled everywhere), that is the real MVP position: **a fresh merchant cannot trade and cannot self-verify to become able to** — the contact leg needs an email that cannot be sent. The only path today is an out-of-band override. Corroborating evidence from `manual`: of four merchants, the only one that has ever traded (13 finalize-sale audits) carries `allowUnverifiedSales=true`; the born-false one has a store and zero sales. And the audit log holds **zero** `verification.override.opened` rows across all 43 entries, so the three `true` values were written **out-of-band** — console, seed, or direct REST — not through the audited admin endpoint. ## What each lane found, in one line [Section titled “What each lane found, in one line”](#what-each-lane-found-in-one-line) | Lane | State | | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **merchants / verification** | Seams proven on `manual`; #718/#906 close a visible wrong percentage; declined-path proof needs one Alfredo window | | **conexiones / VAS aggregator** | M0–M12 MERGED and finished; dormant because `VAS_AGGREGATOR_MODE` is unset → fail-closed 503 on every realm. A deploy-and-activate problem | | **liquidaciones** | sale → settled → “sees balance owed” WORKS (proven with a real card charge). Withdraw does not exist for the merchant: `DISPOSE_ENABLED=false` per the A5 ruling, pending #53 + #54. UI \~80% built | | **onboarding** | Self-serve signup → POS works on current main, including silent terminal self-provision. Blockers are per-realm infra: SMTP, the onboarding flag, and running current code. Phone verify unwired (no SMS vendor) | | **auth** | Role model coherent in shape; two inconsistencies — gate↔data signal-less refusal, and membership-without-employee-row. Employee pickers code-closed, flag-gated only | | **n86 / mobile** | Proposes handheld MVP = cash+card sale with printed receipt, shift open/close, onboarding gate. Everything else after | | **verify (recon)** | 79 routes: 12 built-and-unreachable. \~11 flows specified-not-built. Flag baseline captured | ## Rulings needed (D1–D9) [Section titled “Rulings needed (D1–D9)”](#rulings-needed-d1d9) | # | Decision | Consequence | | ------ | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | **D1** | Does MVP include non-owner staff (cashiers, supervisors)? | If yes, G3 is required work at the auth↔onboarding↔merchants seam | | **D2** | Self-serve payout (W76/W77) in MVP? | UI \~2–4 days; real cost is deps #53 (bank verification) + #54 (platform inbound-credit). Today a merchant watches money accrue and cannot withdraw it | | **D3** | Handheld depth — n86’s reduced MVP, or web-parity? | Decides the mobile queue entirely | | **D4** | VAS web (G1) — confirm IN per PDR-0001, or re-scope? | It is the largest unbuilt item and currently has no owner | | **D5** | Clientes (G2) — confirm IN? | Cheap if yes: routes exist, they need an entry point and a flag decision | | **D6** | Mobile discounts — re-enable or cut? | Finished and deliberately hidden; a product call, not work | | **D7** | Ship with escalate-always verification + permissive selling, or hold for the idFront fix? | Decides whether launch waits weeks on another team | | **D8** | `CURRENT_TERMS_VERSION` bump policy — who bumps, does it force re-acceptance? | Undefined in code (`TODO(human)`) | | **D9** | Gift-cards admin list — finish or cut? | Shipped dark behind a flag, mock-only, no user story. Contradicts feature-flag minimalism | ## Delegation blockers, independent of the rulings [Section titled “Delegation blockers, independent of the rulings”](#delegation-blockers-independent-of-the-rulings) * **26 drafted tickets sit unuploaded in `backlog/drafts/`** with manifests. The fleet cannot see files. **And they need a STALENESS CHECK before any of them is uploaded.** Two are already known to predate a shipped decision: the web and mobile SIM-chip drafts propose a `POST /vas/chips/sell` route and an ICCID point-of-sale UI, but api #512 resolved chip sale through the ordinary cart line — a loop picking either up would build a phantom vertical nobody could reach. A draft describes the world on the day it was written, and this world has moved four times in one cycle. * **13 open issues are off the board**, so no loop will pick them up. * **Settlements is built with no user-story rows at all** — the completeness board undercounts what exists. ## Known-partial, accepted [Section titled “Known-partial, accepted”](#known-partial-accepted) Top-up is ledger-only (gap #50) — accepted for MVP by PDR-0003; real vendor crediting is post-MVP. Offline cash queue is post-MVP by PO ruling. Wallet withdrawal is a deliberate unbuilt seam. ## Addendum — merchants sub-lane audit (same night, after the table above) [Section titled “Addendum — merchants sub-lane audit (same night, after the table above)”](#addendum--merchants-sub-lane-audit-same-night-after-the-table-above) **G4 — a merchant cannot edit or delete a store.** Only CREATE and a merchant-scoped LIST exist; there is no edit-store and no delete-store endpoint. A merchant who mistypes their store name at signup cannot correct it. If that is MVP, it is unbuilt work with no ticket. → **D10** **Resolved, no work needed:** `STORAGE_CANONICAL_BUCKETS` is a no-op guard — no migration ever ran because there was nothing to migrate; `manual` and `qa-design` each hold exactly the five canonical buckets with no legacy ones (the only unknown is the realm mobile points at). `WRITE_GATEWAY` is default-ON everywhere and its coverage spans every tenant-write resource; OFF is the legacy client-direct path that caused the `create("users")` regression. White-label theming is built AND wired live. Deposits/wallet is substantially built with no MVP gap. **Platform-admin console** is read + impersonate + the `allowUnverifiedSales` override. No merchant suspend/edit/delete. Demo-adequate, not an admin console — acceptable for MVP unless operations needs more. ### Two more rulings [Section titled “Two more rulings”](#two-more-rulings) | # | Decision | Consequence | | ------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **D10** | Is editing (or deleting) a store in MVP? | Unbuilt: no endpoint either way. A merchant cannot fix a mistyped store name today | | **D11** | Does `allowUnverifiedSales` need a TTL? | It has NO expiry, and since nothing auto-approves it is **the only way any merchant trades today**. Indefinite means unverified trading is permanent by default, not temporary | ### D8 blast radius, now measured [Section titled “D8 blast radius, now measured”](#d8-blast-radius-now-measured) Bumping `CURRENT_TERMS_VERSION` **lapses every merchant on an older version at once** — each must re-accept. Three shapes to choose between: retroactive lapse-all, grandfather-new-only, or a grace deadline. How much it hurts depends on whether re-acceptance hard-blocks selling, which is part of the same ruling. ## Addendum 2 — web recon (see [`mvp-completeness-web.md`](/product/mvp-completeness-web/)) [Section titled “Addendum 2 — web recon (see mvp-completeness-web.md)”](#addendum-2--web-recon-see-mvp-completeness-webmd) The web lane’s route/flow/flag recon is a companion document with a ruling checkbox per item. Its triangulations — findings three independent inventories agree on — are the part worth reading first. **G5 — the merchant wallet is built, live-green, and has no door.** `/admin/wallet` works and US-BAL-1 shipped; nothing in the UI links to it. PDR-0003 puts wallet VISIBILITY in MVP. So this joins VAS web and Clientes: **the third surface a PDR names as IN that a merchant cannot reach.** That is the pattern worth naming. Three of the gaps in the frozen scope are not unbuilt — they are built and unreachable. A completeness view that counts merged code would score all three as done, which is exactly why the route inventory (79 routes, 12 with no UI entry) found what seven lane inventories did not. **Environment note, now confirmed:** `qa-design`’s api digest is byte-identical to `stage-f4d8e98` and to stage’s — the deploy took, the tag merely reads `:stage-latest` locally. There was no discrepancy, and the flag baseline is trustworthy. Read digests, never tags. ## Addendum 3 — the per-realm deploy matrix (the answer to “can we ship”) [Section titled “Addendum 3 — the per-realm deploy matrix (the answer to “can we ship”)”](#addendum-3--the-per-realm-deploy-matrix-the-answer-to-can-we-ship) Read from running containers by digest, with capability presence established by git ancestry of the introducing commit against the running SHA. Full matrix in `team/workers/terminales-conexiones.md`. **Running api images:** `manual` = stage-12af9ac (5 behind tip) · `qa-design` = stage-f4d8e98 · `stage` = same digest · `dev` and `qa-sh` = stage-96f47a2, **120 commits behind**, predating everything · `prod` = none. ### B1 — emails are DISABLED on all five realms (`DISABLE_EMAILS=1`) [Section titled “B1 — emails are DISABLED on all five realms (DISABLE\_EMAILS=1)”](#b1--emails-are-disabled-on-all-five-realms-disable_emails1) **The single biggest blocker, and it is configuration, not code.** Verification’s contact/email gate, employee invites, password reset and magic links cannot complete on ANY realm. This reframes G3: the invitee first-password gap is real, but even a perfect invite could not have been delivered. Fix is SMTP plus `DISABLE_EMAILS=0` per realm, or routing to the existing mailpit catcher. Human-gated config. ### No single realm is end-to-end MVP-shippable [Section titled “No single realm is end-to-end MVP-shippable”](#no-single-realm-is-end-to-end-mvp-shippable) * **`manual` is closest** — near-tip code, `FEATURE_FLAG_MERCHANT_ONBOARDING` ON (the only realm), deny-gate and self-provision and verification-reconcile present, and a real merchant already trading cash and card. Between it and full MVP trade: emails disabled; VAS deliberately fail-closed (money safety); 5 commits behind tip; and its Appwrite schema/backfill state UNKNOWN pending a realm read (commissioned). * **`qa-design`** is the only realm with `VAS_AGGREGATOR_MODE=mantarys` and sandbox credentials — VAS is being certified there — but onboarding is OFF, so store creation 404s, and it has no merchant. * **`stage`** has credentials staged and fail-closed; **`dev`/`qa-sh`** are 120 behind and predate everything. So `manual` does core POS but not VAS or email; `qa-design` does VAS-sandbox but not onboarding or email. **Neither is a merchant-trading MVP candidate today**, and the gap in both cases is configuration and deployment rather than unbuilt code — the same conclusion the lane inventories reached from the other side. ## Addendum 4 — the sell gate, the override provenance, and the verification cause (final) [Section titled “Addendum 4 — the sell gate, the override provenance, and the verification cause (final)”](#addendum-4--the-sell-gate-the-override-provenance-and-the-verification-cause-final) ### The sell gate has no server-side hole [Section titled “The sell gate has no server-side hole”](#the-sell-gate-has-no-server-side-hole) `sales.controller.ts:71` gates `@Post('finalize')` before any write, `finalizeOrder` awaits `assertCanSell` internally, and **both** clients finalize through that endpoint. The cart is client-direct under RLS by design, but a cart is not a sale. So “the POS shell renders” and “the merchant cannot complete a sale” are both true — the UI opens and finalize 403s, which is the open-then-403 UX defect already drafted, not a security gap. **Code is STRICTER than the design doc, and code wins.** Shipped `overrideOpen` is `allowUnverifiedSales === true` (`merchant-capabilities.service.ts:266`); `ux-design.md:86` specifies `!== false`. A null merchant is DENIED by the running code and permitted by the design. Correct the design doc to match the behaviour, which is the safer of the two. ### GOVERNANCE — the three open overrides on `manual` are untraceable [Section titled “GOVERNANCE — the three open overrides on manual are untraceable”](#governance--the-three-open-overrides-on-manual-are-untraceable) `manual` runs `seed.profile: none`; all four merchants were created by real onboarding and born `false`; no repo script writes the override to `true`; the only api path that does is the audited platform-admin endpoint — and the audit log holds **zero** `verification.override.opened` rows across all 43 entries. So the trading gate on the realm holding our only real merchant, card sale and settlement was opened **by a direct REST or console write**, bypassing the audited endpoint the bootstrap runbook documents. It cannot be pinned to a commit or a person. That needs a policy answer, not just a note. ### Verification — FINAL cause, after four diagnoses [Section titled “Verification — FINAL cause, after four diagnoses”](#verification--final-cause-after-four-diagnoses) The documents are **captured as video**. `context.idFront` and `context.idBack` point at 125KB WebM chunks; the engine’s document check calls `Image.open()` on a video and reports `undecodable_image`. Frame extraction runs for the SELFIE video (producing a clean 480×640 JPEG) but **not** for the document videos — the asymmetry is the bug. **No readable document still has ever existed**, so the fix is not a repoint: extend frame extraction to the document videos, or capture documents as photos on the astro side. Real work, still days rather than months, and the OCR-accuracy long tail remains untested because the engine has never been handed a document it could open. Three earlier diagnoses — missing evidence, bad image quality, a dangling storage ref — were each disproven by opening the next layer. **None was built.** The cost was a night; the alternative was shipping a fix for a cause that did not exist. **The error label is itself a defect:** `undecodable_image` here means “handed a video, tried to open it as a still”. It must distinguish wrong-media-type from a corrupt still, or the next instance costs another night. ## Addendum 5 — B1 RETRACTED: emails already work (to a catcher) [Section titled “Addendum 5 — B1 RETRACTED: emails already work (to a catcher)”](#addendum-5--b1-retracted-emails-already-work-to-a-catcher) **The “single biggest blocker” was not one.** `DISABLE_EMAILS` gates nothing: `src/env.ts:124` compares `=== 'true'` and the value is `'1'`, so the flag is inert and no send-branch reads it. Appwrite’s SMTP on every realm already points at the self-hosted `appwrite-realms-mailpit`. **Proven, not inferred:** mailpit already holds three real “Account Verification for Terminales manual” emails sent 2026-09-10. Caught before acting: we were one step from redeploying five realms — including `manual`, which holds the only real merchant, card sale and settlement — to flip a variable that does nothing, and then reporting email as fixed. **The honest, narrower finding that replaces it:** mailpit is a **catcher, not delivery**. Verification, invitations and password reset work end to end for our own addresses; **no external customer receives any email until a real SMTP provider replaces mailpit.** That is a decision for the PO, not a defect. **What this does and does not change:** * The binding sequence stands, but not for the reason given: the enforcement backfill must still not run before merchants can actually complete verification. * **Trading is still blocked**, and the cause is now unambiguous: a merchant is born gate-closed and cannot become verified because the KYC documents are captured as video (addendum 4). Email was never the gate. * The staff-invite chain is no longer email-blocked at all — and the auth design’s **activation code** (an owner reads a code to the cashier in front of them) removes the dependency entirely. ## VAS manual testing — recommendation: `qa-design`, not `manual` [Section titled “VAS manual testing — recommendation: qa-design, not manual”](#vas-manual-testing--recommendation-qa-design-not-manual) Pointing `manual` at any aggregator, even the sandbox, abandons the fail-closed money-safety posture chosen deliberately for it and mixes test VAS data into the only realm with real trading history. `qa-design` is already bound to the sandbox aggregator with credentials set. Shortest path to driving VAS there: enable `FEATURE_FLAG_MERCHANT_ONBOARDING` (unset today, so store-create 404s) or seed a merchant, enable and run the catalog sync (the catalog is empty), then drive a sandbox sale. Emails already work there. ## Addendum 6 — G1 RETRACTED: VAS web is built and reachable [Section titled “Addendum 6 — G1 RETRACTED: VAS web is built and reachable”](#addendum-6--g1-retracted-vas-web-is-built-and-reachable) **The largest “gap” in this document was not one.** The VAS sale surface for the three aggregator verticals — TAE, Pines, Servicios — is **built, wired, reachable and live-backed on web**: `/terminal/home` → ProductPicker → `VasSaleScreen` (1,370 lines), calling `vas/catalog`, `vas/sale`, `vas/adeudo`, `vas/pin/reprint` and `vas/sale/:id`. There is **zero TypeORM in `terminales-web`**; the legacy surface the earlier claim referred to lives in the api. It was reported missing because the docs, the board stories and the e2e specs all still point at `/admin/airtime` and `/admin/giftcards` — routes that **#847 deliberately deleted** when it moved VAS into the POS picker. The feature moved; its address book did not. **This is the fourth built-but-mislocated surface found in one cycle**, after Clientes, the merchant wallet and the settlements reverse-orphan. The pattern is the finding: **our documentation describes where things used to be.** A completeness view assembled from documents will keep reporting phantom gaps until someone walks the routes. It is also why the route inventory found what seven lane inventories could not. ### The real, narrower VAS gaps [Section titled “The real, narrower VAS gaps”](#the-real-narrower-vas-gaps) 1. **The e2e suite is BROKEN, not absent.** Three specs (`US-VAS-1` ×2, `US-VAS-2`) navigate to the deleted admin routes and fail at navigation; only `US-VAS-3` was migrated to the picker. Rule 13’s shape exactly — #732 wrote the specs, #847 removed the routes, nobody built the combination — and green component tests masked the red e2e. **First build item, approved.** 2. **SIM chips (US-VAS-4) are genuinely absent** — no `CHIP` kind, no screen, no Figma frame. It is physical serialized inventory via `/vas/chips/import|activate`, not `/vas/sale`. Ruled IN (PDR-0001 names it); building from the backend contract plus mobile parity rather than waiting for design. 3. **Design state-frames** for aggregator-error, timeout, no-stock and reverso are missing on web; the shipped mobile surface already encodes that taxonomy, so web derives from it. 4. **Everything live is dormant** because `VAS_AGGREGATOR_MODE` is unset → 503. Deploy/config, not a build item. **Net effect on MVP:** the largest named gap collapses to a broken test suite, one genuinely missing vertical, and some error states. That is a materially better position than this document reported. ## Addendum 7 — G3 RESOLVED: the invitee credential chain is built and merged [Section titled “Addendum 7 — G3 RESOLVED: the invitee credential chain is built and merged”](#addendum-7--g3-resolved-the-invitee-credential-chain-is-built-and-merged) **An invited cashier can now sign in.** api #722 + web #908 (both merged 2026-09-12) close the gap that had no endpoint and no ticket: invite → **activation code → short-lived session (no password) → PIN → PIN-login**. The credential is the invite secret; a session is minted with the same primitive PIN-login already uses; no password is ever created for a cashier. **The design’s best property is that it does not need email.** An owner reads an 8-digit code aloud to the person in front of them, who enters it on the terminal. So the emailed invite became **convenience for remote staff, not a prerequisite** — “better once SMTP is decided”, never “blocked on it”. That matters because every other estimate in this document had been sequenced behind an email decision. Four security properties were fail-verified (each deliberately broken, the test watched to fail, then restored): single-use, timing-uniform failure, code bound to its employee, and a per-employee lockout. Two defects were caught in review that green suites could not see — a timing oracle, and a `storeId` / `storeTeamId` collapse in fixtures (see rule 4’s corollary). ### But “can sign in” is NOT “can sell” — and the boundary is earlier than expected [Section titled “But “can sign in” is NOT “can sell” — and the boundary is earlier than expected”](#but-can-sign-in-is-not-can-sell--and-the-boundary-is-earlier-than-expected) On a **fresh** merchant the honest end of the chain is: *the cashier signs in, reaches the shift screen, and **opening a shift 403s***. Both `assertCanOpenShift` (`shift.service:356`) and `assertCanSell` (`sales.service:199`) key on the merchant’s verification state, and a merchant is born `allowUnverifiedSales: false`. So the block comes **before** the sale, not at finalize. That is the merchant verification gate doing its job, not an auth defect — but the chain must not be reported as complete while meaning “can sell”. On an overridden merchant (La Esperanza) it sells today. ### Still open on staff [Section titled “Still open on staff”](#still-open-on-staff) * **Employee-row provisioning** (`Api SEC Stores Membership Without Employee Row`) — assigned to the auth lane, next. Does not block the invite path; blocks a manager minted by `POST /pos/stores`. * **The supervisor path is designed but never walked.** Activation is identical for a supervisor, and their distinct capability is same-day void approval — but no supervisor has been activated *via a code* and then approved a void. Flagged as a gap, not claimed. Needs one guided walk. # Web MVP completeness — reconnaissance for the go-live scan * Status: Draft for PO ruling * Date: 2026-09-12 * Author: web verification lane (pre-sweep reconnaissance) * Scope lens: [PDR-0001 MVP go-live scope](/pdr/0001-mvp-golive-scope/) + [PDR-0003 VAS phasing & wallet visibility](/pdr/0003-vas-connectivity-phasing-and-wallet-visibility/) * Sources: `docs/product/user-stories.md`, `docs/design/flows-screens-master-map.md`, `backlog/`, `manifests/`, 365 terminales-web issues, and a route-level read of all 79 `src/app/**/page.tsx` at `terminales-web@main` (`4361cddc`). > **Why this exists.** The go-live sweep was deferred until MVP work lands, so that a sweep finds *defects* rather than *absences*. This document is the completeness scan that tells us how much MVP work there is. It is built from the planning artefacts and the code — **not** from the running UI, because a screen-derived list can only rediscover what exists; a flow specified and never built has no screen to find it by, and that absence is the point. > > **How to use it.** Each rule-able item carries a `☐ Ruling:` line. The most valuable section is **§1 (triangulation)** — a gap that three independent lists agree on is worth more than three separate entries. Nothing here has been built, changed, filed as a ticket, or flipped; this is reconnaissance. *** ## Headline [Section titled “Headline”](#headline) **The two things PDR-0001 singles out as IN for the MVP are the two least-finished surfaces on the web.** PDR-0001 froze MVP scope to *“VAS + Clientes are IN; the rest of §4.6 is deferred.”* Both are unbuilt or unreachable on the web today: * **VAS / servicios financieros (W-09, Track C)** — the special-categories revenue vertical the client *“needs to operate their business day one … a core revenue surface, not a nice-to-have”* — exists on the web only as legacy TypeORM airtime. The Appwrite POS rebuild (US-VAS-1..6) never landed. This is the single largest hole. * **Clientes / Customers (W-13, Track D)** — the other explicit inclusion — exists as routes (`/admin/customers`, `/admin/customers/[id]`) that **no UI affordance reaches**, and the status board still marks the flow ⚠ “not absorbed.” No lane reported this, because each lane sees its own initiative and neither VAS nor Clientes has a standing lane. It only surfaces by triangulating the flow inventory against the route inventory against PDR-0001. *** ## Baseline correction — the flag baseline is trustworthy [Section titled “Baseline correction — the flag baseline is trustworthy”](#baseline-correction--the-flag-baseline-is-trustworthy) The pre-flight noted qa-design’s api container carries the tag `:stage-latest` rather than `stage-f4d8e98`. **Reconciled by digest, not tag:** the running `terminales-qa-design-api` image digest is `sha256:2d24d092de7e9b191f17fc1d14e9c067328acd78304095b4c3ea3ebe89567222`, which is byte-identical to the merchants-lane record for f4d8e98 **and** to the `terminales-stage-api` (stage-f4d8e98) container. The deploy took; the tag is simply named `latest` locally (the deploy recipe retags `stage-` → `stage-latest`). **No discrepancy** — the api plane is current main. *** ## §1 — Triangulated findings (the crown jewels) [Section titled “§1 — Triangulated findings (the crown jewels)”](#1--triangulated-findings-the-crown-jewels) Each of these is asserted by two or three independent inventories (Flow / Route / Flag). Ranked by MVP weight. ### T1 — VAS web vertical: IN scope, essentially unbuilt on web [Section titled “T1 — VAS web vertical: IN scope, essentially unbuilt on web”](#t1--vas-web-vertical-in-scope-essentially-unbuilt-on-web) * **Flow:** US-VAS-1..6 all ⚠ “Built”; screens W41–W46 fully designed; drafts still unbuilt in `backlog/drafts/`; US-VAS-4 has no e2e and no Figma frame. * **PDR-0001:** explicitly IN (Track C), MVP = **Phase-1 connectivity only** per PDR-0003 (single shared VB webService user; Phases 2/3, bank-statement deposit validation, and wallet payout are post-MVP). * **Consequence:** the primary MVP revenue surface has no web build. Every downstream story that rides it (US-BAL-6 wallet-funds gate on VAS, US-VAS-6 reporting) is blocked behind it. * ☐ **Ruling:** confirm VAS-web (Phase-1) is the top MVP build item and assign a lane. ### T2 — Clientes: routes exist, no one can reach them, board says “not absorbed” [Section titled “T2 — Clientes: routes exist, no one can reach them, board says “not absorbed””](#t2--clientes-routes-exist-no-one-can-reach-them-board-says-not-absorbed) * **Route:** `/admin/customers` has **0 external inbound links** (grep of `href=`/`router.push`/`redirect`/`