← all branches

feat/energia-ui

needs attentionviewing older commit
30ee8dc · fullPR #303reviewed 2026-07-13 03:43 UTC5H · 9M · 10L · 7I
The branch
Purpose
Port the axo-rfp demo pages into the energia route so the 7-screen energía module runs as real Next.js pages in a Vercel preview — enabling an RFP demo for Grupo Axo without needing to seed an entitled org.
Goal
7-screen energía module (resumen, hallazgos, demanda, pagos, conciliación, esg, medición, sitios) mounted as real Next.js pages under /energia/* with a shared sub-nav, using illustrative mock data from the axoMockData dataset.
Sub-goals
  • SG-1: Wire energia as a real OrgModuleKey (domain type + API schema + admin panel toggle)
  • SG-2: New route tree — layout with sub-nav + 7 page files with recharts + @batu/ui + mock data
  • SG-3: TEMP soft-launch bypass — energia removed from GATED_PATH_MODULES so any authenticated user can reach the preview (marked for revert before merge)
  • SG-4: i18n nav keys in en.json + es.json; sidebar entry wired
The changes (whole branch)
What
Added 'energia' to ORG_MODULES + OrgModuleKeySchema (domain + API schema lockstep). Built a 7-page route tree under /energia/* with layout, sub-nav, and mock data pages. Added DashboardSidebar wiring, admin panel toggle, and i18n nav keys. TEMP: two soft-launch.ts edits force energia live for all authenticated users on this branch.
Why
RFP demo for Grupo Axo — needed the feature running live in-app on a Vercel preview without seeding a prod org. Stacked on feat/mod-gates (#299) which established the per-org module entitlement system.
Areas
apps/platform/energia route tree (8 files)+44140apps/platform/lib/demo (3 files)+6510apps/platform/lib/soft-launch + tests+172apps/platform/components + messages (4 files)+380domains/core + packages/api (2 files)+22
Blast
23 files, +5358/-3. Concentrated in apps/platform energia route tree (new) and demo lib (new). Only 4 files modified: soft-launch.ts (+2/-2), soft-launch.test.ts (+15/-1), organization.type.ts (+1/-1), organization.schemas.ts (+1/-1).
PREVIEW/DEMO BRANCH — DO NOT MERGE as-is TEMP soft-launch bypass must be reverted before merge Stacked on feat/mod-gates (PR #299) — merge parent first
typecheck· not run in this reviewci· CI check API not accessible via personal access tokencoderabbit· no .coderabbit.yaml in repo

Findings · 23

correctness4

high

TEMP bypass: isGated('/energia') returns false but tests assert true

apps/platform/src/lib/soft-launch.ts:50

The TEMP edit comments out the `['/energia', 'energia']` entry from GATED_PATH_MODULES, so isGated('/energia') returns false for all callers including SoftLaunchGuard. The test at soft-launch.test.ts:35 asserts `isGated('/energia').toBe(true)` — it will FAIL against the current code. Intentional for preview, but CI is running a broken test suite.

high

TEMP bypass: navState('energia') returns 'live' but tests assert 'greyed'

apps/platform/src/lib/soft-launch.ts:58

'energia' is absent from GREYED, so navState('energia') falls through to the default 'live'. Tests at soft-launch.test.ts:100 and the nav↔route invariant block assert 'greyed' — both fail against the current code.

medium

Unentitled orgs can reach /energia via direct URL (soft-launch gate bypassed)

apps/platform/src/lib/soft-launch.ts:44

With energia absent from GATED_PATH_MODULES, SoftLaunchGuard does not redirect unentitled orgs. Any authenticated user can access /energia directly. This is intentional for the preview demo (mock data only, no real data exposed), but must be reverted before merge.

low

navState('energia')='live' makes energia invisible in sidebar for unentitled orgs

apps/platform/src/components/DashboardSidebar.tsx:129

Because navState returns 'live', requiredModule='energia' is set, and the sidebar filter drops the item for orgs without the entitlement — so unentitled orgs see NO energia entry (not even a greyed teaser). The route is still reachable via URL.

security2

medium

All /energia/* routes have no server-side auth guard

apps/platform/src/app/[locale]/(dashboard)/energia/layout.tsx:1

Every energia page is 'use client' and relies entirely on client-side SoftLaunchGuard + dashboard layout redirect for auth. This matches the existing (dashboard) pattern and only serves mock data today. Flag so it is not forgotten when real data is wired — server-side auth guard will be required then.

low

PapaParse error.message interpolated into UI state

apps/platform/src/app/[locale]/(dashboard)/energia/pagos/conciliacion/page.tsx:106

Error message is set into a React text state and rendered as text node (not innerHTML) — no XSS risk. PapaParse error messages are generic strings in practice. Low severity.

conventions6

medium

4 pages use next/link + manual /${locale} prefix instead of @/i18n/navigation

apps/platform/src/app/[locale]/(dashboard)/energia/resumen/page.tsx:4

resumen, pagos, sitios, and pagos/conciliacion import Link from 'next/link' and manually prefix hrefs with `/${locale}/…`. Convention is `import { Link } from '@/i18n/navigation'` with locale-free paths (as correctly done in energia/layout.tsx). These 4 pages bypass next-intl routing and are fragile if locales are added.

medium

All energia page body strings are hardcoded Spanish — not i18n-ready

apps/platform/src/app/[locale]/(dashboard)/energia/resumen/page.tsx:205

Every page body renders inline Spanish string literals. Only energia/layout.tsx uses useTranslations. Acceptable for a preview branch, but the entire surface needs extraction before production.

medium

lib/demo/ is a shared location for route-scoped demo utilities

apps/platform/src/lib/demo/TimeFrameControl.tsx:1

axoMockData.ts, TimeFrameControl.tsx, and timeFrame.ts are consumed exclusively by the energia route tree. Convention puts route-scoped utilities in route/_lib/. Acceptable here as a quarantine zone for deletion-bound demo assets.

low

JSX structural comments explain the obvious in layout.tsx

apps/platform/src/app/[locale]/(dashboard)/energia/layout.tsx:43

{/* Navigation Tabs */} and {/* Page Content */} are what-comments — convention allows only why-comments.

low

Multiple what-comments across energia page files

apps/platform/src/app/[locale]/(dashboard)/energia/resumen/page.tsx:88

Lines 88, 95, 114, 132+ are what-comments restating what the code below obviously does. Similar patterns in hallazgos and demanda. Low severity on demo code.

low

layout.tsx has a multi-paragraph JSDoc

apps/platform/src/app/[locale]/(dashboard)/energia/layout.tsx:20

The EnergiaLayout JSDoc (lines 20–26) is multi-paragraph explaining implementation detail. Convention says no multi-paragraph docstrings.

tests4

high

soft-launch tests are broken by the TEMP bypass

apps/platform/src/lib/__tests__/soft-launch.test.ts:35

Tests at lines 35, 89–93, 100, and the full nav↔route invariant block (lines 124–156) assert energia IS gated/greyed. With the TEMP bypass active, these fail. CI is running a broken suite on this branch.

medium

reconcile.ts has no unit tests

apps/platform/src/app/[locale]/(dashboard)/energia/pagos/conciliacion/_lib/reconcile.ts:53

reconcile() is a pure function with non-trivial logic: three-way status classification, ±$1 float tolerance boundary, sort ordering, and summary aggregation. Neither reconcile() nor exportDiscrepanciesCsv() has a test file. The ±$1 tolerance is a product decision with real edge cases worth pinning.

low

timeFrame.ts pure utilities untested

apps/platform/src/lib/demo/timeFrame.ts:32

currentWindow(), compareWindow(), sumInWindow(), pctChange(), describeWindow() are pure with deterministic output and edge cases worth pinning (pctChange(x, 0) → null, ytd slice, startIdx clamped). Low severity — demo code.

low

axoMockData.ts deterministic generators untested

apps/platform/src/lib/demo/axoMockData.ts:89

Seeded RNG guarantees stable output — snapshot tests would catch regressions cheaply. Acceptable to skip given the PR's deletion intent.

improvement7

high

reconcile.ts types are entangled with demo types — not a clean swap for real data

apps/platform/src/app/[locale]/(dashboard)/energia/pagos/conciliacion/_lib/reconcile.ts:1

reconcile() accepts AxoBill/AxoSite as parameter types. When wiring real data the function needs a type-signature rewrite, not just an import swap. The algorithm is worth keeping; generalize the type signature or move to domains/utility for the next iteration.

high

soft-launch.ts TEMP comments are the only merge guard — easy to miss

apps/platform/src/lib/soft-launch.ts:50

Two TEMP comments mark what must be reverted before any merge. If merged as-is, all Batu orgs get energia fully live, bypassing the per-org rollout. The PR title says DO NOT MERGE but TEMP comments are the only in-code safeguard.

medium

Format helpers duplicated in resumen/page.tsx instead of shared _lib/formatters.ts

apps/platform/src/app/[locale]/(dashboard)/energia/resumen/page.tsx:48

formatKwhShort, formatSignedMxn, formatSignedKwh, formatSignedTons are defined inline but not exported. portfolioHealth already has formatMxn/formatKwh. Move to energia/_lib/formatters.ts for sharing across the module in the next iteration.

medium

All energia pages are full client components — complicates real-data wiring

apps/platform/src/app/[locale]/(dashboard)/energia/resumen/page.tsx:1

Every page starts with 'use client' and reads mock data at module scope. Standard pattern is a server component page passing prefetched data to interactive client components. Extracting computation into _hooks/useEnergiaData.ts per page will make the mock→real swap cleaner.

medium

Large pages (630–769 lines) lack _components extraction

apps/platform/src/app/[locale]/(dashboard)/energia/hallazgos/page.tsx:1

hallazgos (769 lines), conciliacion (667 lines), resumen (630 lines) each contain 3–6 distinct sections inline. Extracting natural boundaries (KPI bar, chart, table/drill panel) into _components/ will make per-section data wiring easier in the next iteration.

medium

reconcile.ts business logic belongs in a domain lib, not a route _lib/

apps/platform/src/app/[locale]/(dashboard)/energia/pagos/conciliacion/_lib/reconcile.ts:53

reconcile() implements substantive logic (CSV-vs-bills diff). Canonical form puts pure business logic in domains/utility/src/bills/. Route _lib/ placement pressures callers to run it client-side; at 2,000+ RPUs real scale this becomes a performance issue.

low

Artificial setTimeout in conciliacion demo should not be mistaken for real debounce

apps/platform/src/app/[locale]/(dashboard)/energia/pagos/conciliacion/page.tsx:72

setTimeout(resolve, 1400) simulates processing latency. Remove when wiring to a real endpoint — don't mistake for a debounce or rate-limiting mechanism.

History · 5 commits

  1. adcbf45needs attentionincremental1H · 1M · 4L2026-07-13 19:48
  2. 3da3216needs attentionincremental5H · 14M · 14L2026-07-13 17:15
  3. 6859d29safeincremental0H · 1M · 1L2026-07-13 13:18
  4. d563614needs attentionincremental3H · 5M · 7L2026-07-13 04:06
  5. 30ee8dcneeds attentionfull5H · 9M · 10L2026-07-13 03:43current