feat/energia-ui
needs attentionadcbf45 · incrementalPR #303reviewed 2026-07-13 19:48 UTC1H · 1M · 4L · 2I- Purpose
- Enable preview of the energía feature — 7 energy-management screens as real Next.js routes in the app, using illustrative Axo/Neto mock data, so the module can be reviewed on a Vercel preview without a fully-wired backend.
- Goal
- Ship a non-mergeable preview of the energía UI module with real routes, layout, recharts charts, and org-switcher; soft-launch gate forced open on this branch (TEMP edit) for reachability.
- Sub-goals
- SG-1: Port 7-screen energía module into /energia/* routes (resumen, hallazgos, demanda, pagos, pagos/conciliación, esg, medición, sitios)
- SG-2: Ground to minimal v2 design + real Axo/Neto illustrative data with org switcher
- SG-3: Remove misleading 'mark paid' affordance — payment status is read-only from CFE
- What
- Removed the local client-side 'paid' state toggle (useState + 'Marcar pagado' button) from BillsTableContent. Badge now reflects only r.estatus from CFE data. Download button renamed 'PDF' → 'Recibo'. Footer text updated to explain CFE is the authoritative source of payment status.
- Why
- The manual 'mark paid' affordance was client-only with no persistence, misleading demo UX, and contrary to the correct product model where CFE is authoritative for payment status. The fix makes the demo accurately represent how production should work.
- Areas
- apps/platform/src/app/[locale]/(dashboard)/energia+3476−0apps/platform/src/lib/demo+651−0apps/platform/src/lib/soft-launch.ts+2−1apps/platform/src/components/DashboardSidebar.tsx+10−0apps/platform/src/messages+28−0domains/core + packages/api+2−2
- Blast
- 28 files, +4,169/−3 across the energia module, demo lib, and platform infra. Mostly net-new additions for the new module. Two existing files changed: soft-launch gate (+2/−1 to force-enable energia) and org type/schema (+1/−1 to add OrgModuleKey).
Findings · 8
security2
Axo/Neto billing figures embedded in client JS bundle via realData.ts import (pre-existing)
apps/platform/src/app/[locale]/(dashboard)/energia/_lib/realData.ts
realData.ts carries a comment '// INTERNAL: real customer data' but is imported by a 'use client' component, shipping Grupo Axo / Tiendas Neto billing figures in the browser JS bundle. Not introduced by this diff. Mitigation: ensure the energia route stays strictly gated via soft-launch module entitlement. Consider moving to server-only data (RSC / route handler) before production.
Security improvement: client-side payment status override removed
apps/platform/src/app/[locale]/(dashboard)/energia/_components/sheetContent.tsx
The removed 'paid: Record<string, boolean>' useState allowed local override of displayed payment status without server persistence. Though cosmetic in a demo, it enabled misleading state (a user could mark all bills 'paid' and screenshot). The new read-only display from r.estatus correctly enforces CFE as the sole source of truth.
conventions4
Em dash used as punctuation separator in user-facing component copy
apps/platform/src/app/[locale]/(dashboard)/energia/_components/sheetContent.tsx:240
The footer text 'CFE — es la fuente de verdad' uses an em dash as a punctuation connector. The copy style rule (ui-patterns.md § Copy Style) bans em dashes in user-facing copy including component JSX (exceptions apply only to the honest-absence glyph rendering null/unknown values). Replace with a period or comma: 'El estado de pago se sincroniza automáticamente desde CFE. Es la fuente de verdad, no se marca a mano.'
JSDoc for BillsTableContent still references removed 'paid toggle'
apps/platform/src/app/[locale]/(dashboard)/energia/_components/sheetContent.tsx:157
The JSDoc comment reads '...with per-row PDF stub + paid toggle.' The paid toggle was the exact affordance removed by this diff. Update to reflect the current shape, e.g. '...with per-row receipt download stub (status read-only from CFE).'
'Acciones' column header is plural but action set is now a single download
apps/platform/src/app/[locale]/(dashboard)/energia/_components/sheetContent.tsx:205
After removing 'Marcar pagado', the 'Acciones' column contains only one action (download). 'Acciones' implies multiple. Consider renaming to 'Recibo' or 'Descarga' to match the single download button.
Hardcoded Spanish copy throughout — expected for demo branch, extract before production
apps/platform/src/app/[locale]/(dashboard)/energia/_components/sheetContent.tsx
All user-facing strings are hardcoded JSX rather than next-intl messages keys. Consistent with the demo branch intent; needs extraction to messages/es.json + messages/en.json at production wiring time.
improvement2
'sin_confirmar' bills appear in CSV export but are silently absent from the bills table and summary totals
apps/platform/src/app/[locale]/(dashboard)/energia/_components/sheetContent.tsx:153
pagosCsvRows() emits rows with estatus='sin_confirmar' sourced from p.unknown/p.unknownMxn (line 153), which in demo data can represent 282 bills worth 6.47M MXN. BillsTableContent only builds 'vencido' and 'pendiente' rows (lines 177-178). The summary badges (line 192-196) also exclude unknownMxn from the displayed total — so the UI total diverges from the CSV export total. In production, an entire bill cohort would be invisible in the table but present in the CSV. Either add a third BillRow status variant + build() call, or add an explicit comment explaining why sin_confirmar is intentionally excluded. Pre-existing issue, not introduced by this diff.
BillRow.estatus type gap: 'sin_confirmar' used in pagosCsvRows but absent from BillRow union
apps/platform/src/app/[locale]/(dashboard)/energia/_components/sheetContent.tsx:129
BillRow.estatus is typed as 'vencido' | 'pendiente' (line 129) while pagosCsvRows uses the string 'sin_confirmar' (line 137 build signature: estatus: string). Both derive from the same EnergiaOrgData.payment source but diverge silently. When wired to production CFE data, the status vocabulary should come from a shared PaymentStatus union type.