← all branches

content/auto-draft

needs attentionviewing older commit
5a57a8f · incrementalPR #325reviewed 2026-07-20 17:14 UTC0H · 5M · 8L
The branch
Purpose
Autonomous content draft for the GDMTO vs GDMTH informational blog post — a key SEO cluster target for the keyword 'tarifa GDMTH' and 'GDMTO vs GDMTH'
Goal
Publish a complete, technically accurate blog post explaining when GDMTH beats GDMTO for Mexican large-demand electricity customers
Sub-goals
  • SG-1: Author GDMTO vs GDMTH explainer with real CFE tariff data
  • SG-2: Add interactive SVG charts grounding the cost comparison in real VdM Centro jun-2026 rates
  • SG-3: Add load-profile visualization showing WHY the winning tariff flips with consumption shape
The changes (whole branch)
What
Rewrote GdmtoVsGdmthCostChart to use real CFE tariffs (GDMTH/GDMTO, Valle de México Centro, junio 2026) with stacked bars showing energía/capacidad/distribución/cargo fijo breakdown. Added new LoadProfilesChart showing dual 24h load curves over ToU bands with 100 kW GDMTH threshold reference line. Inserted LoadProfilesChart into the MDX before the cost chart.
Why
Previous chart used illustrative 10,000 kWh / made-up prices. New version grounds the educational content in real tariff numbers and realistic C&I load profiles (3,000 kWh/day = 90,000 kWh/month), making the article credible for readers who know CFE tariffs.
Areas
apps/web/content/blog+1831apps/web/src/components/blog+3512apps/web/src/app/(marketing)/blog+22apps/web/content/calendar.yml+11
Blast
4 files, +537/−6 lines. Scope: apps/web only — no backend, no infra, no domain changes.
content-only apps/web blog seo-cluster
ci· No CI checks found for PR 325coderabbit· No .coderabbit.yaml in repo

Findings · 13

correctness2

medium

ToU band [22,24] extends ~12 px past plot edge due to /23 x-scale

apps/web/src/components/blog/charts.tsx

x() maps hr=0..23 to the plot width, so x(24)=px0+(24/23)×plotW overflows by plotW/23≈12 px into the right padding. The intermedia band background visually leaks past the polyline's endpoint. Fix by switching the scale to /24 (mapping hr to an interval boundary) or clamping band rects with a clipPath.

low

x-axis 24:00 label placed at x(23) — one slot early

apps/web/src/components/blog/charts.tsx

x(Math.min(24,23))=x(23) places the end-of-day label at the last data point rather than the plot right boundary. With a /23 scale this coincides with the polyline's last point. Switching to a /24 scale resolves both this and the band overflow at once.

conventions2

low

let cum = 0 mutated inside SEG.map() callback

apps/web/src/components/blog/charts.tsx

The stacked-bar offset accumulator mutates a closed-over variable inside a .map() callback. This works in SSR but breaks the side-effect-free convention for chart map callbacks. Use reduce to accumulate both the offset and the rendered elements in one pass.

low

panel() key prop on <g> has no reconciliation effect when called as a function

apps/web/src/components/blog/charts.tsx

The returned <g key={p.name}> element's key prop is ignored by React when the element is produced by a plain function call rather than JSX element instantiation. Harmless in SSR but misleading; resolves by converting to a proper component.

tests2

medium

Pure tariff calculation functions have no unit tests

apps/web/src/components/blog/charts.tsx

blockKWh, dfCap, billGdmth, billGdmto are pure financial functions with real peso amounts published in the article. A silent arithmetic error would mislead readers making tariff decisions. A test file at apps/web/src/components/blog/__tests__/charts.calculations.test.ts should verify: correct block boundary slicing (0-5 base, 18-21 punta), dfCap formula, and round-trip bill values against the RATES constants.

low

dfCap implicitly depends on RATES.days — not testable in isolation

apps/web/src/components/blog/charts.tsx

dfCap(energyTotal, fc) captures RATES.days from the module scope. Changing the signature to dfCap(energyTotal, fc, days) would allow testing edge cases (28/31-day months) without module mocking.

improvement5

medium

panel() is a plain function returning JSX — should be a capitalized component

apps/web/src/components/blog/charts.tsx

panel() is called as {panel(0, PROFILES[0], true)} instead of <Panel .../>, bypassing React's reconciler. The key prop on the returned <g> has no effect when called as a function. For SSR this is harmless, but it's a latent hazard if hooks or error boundaries are ever added. Rename to Panel and render as JSX.

medium

billGdmth and billGdmto duplicate their billing skeleton

apps/web/src/components/blog/charts.tsx

Both functions share the same structure: compute demand charges, energy charges, fixed charges, total, perKwh. The differences are which demand peak drives capacidad/distribución and which energy rate applies. A generic computeBill(curve, rates, config) abstraction would make adding future tariffs a config change rather than a new function.

low

SEG key strings are untyped — should be keyof Bill

apps/web/src/components/blog/charts.tsx

b.bill[s.key] has no compile-time guard that s.key is a valid Bill field. If a Bill field is renamed, the lookup silently returns undefined and the chart renders empty segments. Type the key field as keyof Omit<Bill, 'total' | 'perKwh'> (the stackable fields) to catch this at compile time.

low

RATES.days must be kept in sync with RATES.period manually

apps/web/src/components/blog/charts.tsx

days:30 is correct for junio 2026 but is a separate field from period:'junio 2026'. If the article is updated to a different month without updating days, demand/fixed charges will be silently wrong. Add a co-located comment or derive days from the period string.

low

Legend x-spacing (i × 128) is a magic number tied to SEG length

apps/web/src/components/blog/charts.tsx

At 4 items the last item lands at pad.left+384 px, within the 720 px viewBox. Adding a fifth segment would overflow silently. Derive the step from (plotW / SEG.length) to make it self-adjusting.

seo2

medium

Back-to-back SVG charts (LoadProfilesChart + GdmtoVsGdmthCostChart) risk CLS

apps/web/content/blog/gdmto-vs-gdmth.mdx

Both charts use viewBox + width:100%/height:auto. If the blog-content CSS does not pre-reserve height, the browser reflows twice as each SVG's intrinsic ratio resolves, causing CLS. Verify the figure elements have aspect-ratio or min-height applied in CSS, or add a wrapper with explicit height reservation.

low

SVG <text> chart titles are lower-confidence for Googlebot than HTML text

apps/web/src/components/blog/charts.tsx

The LoadProfilesChart title 'Cómo se ve cada perfil de carga en un día' lives in a SVG <text> node. Googlebot indexes SVG text but at lower confidence. The <figcaption> HTML covers the key concepts reliably. No action required unless keyword targeting for 'perfil de carga' is a priority.

History · 14 commits

  1. c27ea57needs attentionincremental3H · 5M · 4L2026-08-04 17:46
  2. ff69645needs attentionincremental0H · 6M · 8L2026-07-28 18:21
  3. 0257d74safeincremental0H · 0M · 0L2026-07-20 23:00
  4. 0795a9dsafeincremental0H · 0M · 2L2026-07-20 22:42
  5. 5c57414needs attentionincremental2H · 7M · 10L2026-07-20 22:29
  6. 5a57a8fneeds attentionincremental0H · 5M · 8L2026-07-20 17:14current
  7. cc9056eneeds attentionfull1H · 3M · 9L2026-07-20 16:40
  8. 8b79d33needs attentionincremental5H · 7M · 11L2026-07-14 18:10
  9. f0c337cneeds attentionincremental1H · 1M · 5L2026-07-14 17:15
  10. 5c9de97needs attentionincremental0H · 5M · 7L2026-07-14 17:02
  11. 92cce08needs attentionincremental1H · 4M · 3L2026-07-14 02:00
  12. b7dd610needs attentionincremental0H · 6M · 7L2026-07-14 01:43
  13. 8c43554needs attentionfull0H · 2M · 1L2026-07-14 01:15
  14. 2149391blockedfull8H · 6M · 7L2026-07-10 17:30