content/auto-draft
needs attentionviewing older commit5a57a8f · incrementalPR #325reviewed 2026-07-20 17:14 UTC0H · 5M · 8L- 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
- 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+183−1apps/web/src/components/blog+351−2apps/web/src/app/(marketing)/blog+2−2apps/web/content/calendar.yml+1−1
- Blast
- 4 files, +537/−6 lines. Scope: apps/web only — no backend, no infra, no domain changes.
Findings · 13
correctness2
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.
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
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.
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
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.
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
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.
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.
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.
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.
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
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.
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
- c27ea57needs attentionincremental3H · 5M · 4L2026-08-04 17:46
- ff69645needs attentionincremental0H · 6M · 8L2026-07-28 18:21
- 0257d74safeincremental0H · 0M · 0L2026-07-20 23:00
- 0795a9dsafeincremental0H · 0M · 2L2026-07-20 22:42
- 5c57414needs attentionincremental2H · 7M · 10L2026-07-20 22:29
- 5a57a8fneeds attentionincremental0H · 5M · 8L2026-07-20 17:14current
- cc9056eneeds attentionfull1H · 3M · 9L2026-07-20 16:40
- 8b79d33needs attentionincremental5H · 7M · 11L2026-07-14 18:10
- f0c337cneeds attentionincremental1H · 1M · 5L2026-07-14 17:15
- 5c9de97needs attentionincremental0H · 5M · 7L2026-07-14 17:02
- 92cce08needs attentionincremental1H · 4M · 3L2026-07-14 02:00
- b7dd610needs attentionincremental0H · 6M · 7L2026-07-14 01:43
- 8c43554needs attentionfull0H · 2M · 1L2026-07-14 01:15
- 2149391blockedfull8H · 6M · 7L2026-07-10 17:30