content/auto-draft-fix
needs attention024e25c · fullpre-PRreviewed 2026-08-04 17:37 UTC0H · 1M · 3L · 13I- Purpose
- Draft + fix a new pillar-cluster blog post on 'demanda contratada CFE' with accompanying SVG charts, following the content pipeline auto-draft workflow.
- Goal
- Publish a complete, SEO-ready blog post targeting the 'demanda contratada CFE' informational keyword, with code-generated charts that distinguish the four demand concepts readers confuse.
- Sub-goals
- SG-1: New MDX post demanda-contratada-cfe.mdx with full content (FAQ, Fuentes, cross-links)
- SG-2: Hero chart ContractedDemandLadder distinguishing contratada/medida/derivada/facturable
- SG-3: Three supporting charts: DemandUtilizationChart, PeakTimeShareHistogram, LoadFactorComparisonChart, PuntaDemandHeatmap
- SG-4: Register new charts in page.tsx mdxComponents
- SG-5: Advance calendar.yml status idea→review
- What
- Added 225-line MDX blog post, 457 lines of new SVG chart components (5 components), registered them in blog page mdxComponents, and flipped calendar status.
- Why
- Content pipeline scheduled this keyword as next priority in calendar.yml; the branch fixes a prior draft and adds the missing demand-visualization charts.
- Areas
- apps/web/content/blog/demanda-contratada-cfe.mdx+225−0apps/web/src/components/blog/charts.tsx+457−0apps/web/src/app/(marketing)/blog/[slug]/page.tsx+2−2apps/web/content/calendar.yml+1−1
- Blast
- 4 files, +685/−3 lines; confined to apps/web content and components — no backend, no schema, no infra.
Findings · 14
correctness2
LoadFactorComparisonChart — 'same energy' claim is inaccurate (3030 vs 2930 kWh)
apps/web/src/components/blog/charts.tsx:1571
The two demand profiles have different hourly totals: peaky sums to 3030, flat to 2930 — a 3.4% gap. The subtitle 'Mismo consumo mensual, dos formas de consumirlo' is therefore inaccurate. Adjust one array so they match, or soften the subtitle to 'consumo similar'.
PeakTimeShareHistogram — toLocaleString('es-MX') may fall back on minimal-ICU Node builds
apps/web/src/components/blog/charts.tsx:1493
On a Node.js build without full ICU, 'es-MX' silently falls back to 'en-US', rendering 13,523 vs 13.523. Use `Intl.NumberFormat('es-MX').format(saving)` for explicitness, or verify full ICU is present in the build target.
security1
Security: clean
No security findings. All chart components are pure SVG with hardcoded data; no dangerouslySetInnerHTML, no user input, no credentials.
conventions4
CTAs use plain markdown links, not the required Button component
apps/web/content/blog/demanda-contratada-cfe.mdx:217
The blog authoring standard requires CTAs to use `<Button variant="cta">` / `<Button variant="secondary">` from `@batu/ui`. Lines 217-219 use plain `→ [text](/path)` markdown links instead. Either add explicit JSX Button elements in the MDX or map a custom CTA component in mdxComponents.
Multi-line comment blocks on new chart components violate the no-docstring rule
apps/web/src/components/blog/charts.tsx:1175
Five new charts open with multi-paragraph `/** ... */` JSDoc blocks. The project rule says 'never write multi-paragraph docstrings or multi-line comment blocks'. Collapse to a single-line comment or remove entirely.
app.cfe.mx in Fuentes is plain text, not a hyperlink
apps/web/content/blog/demanda-contratada-cfe.mdx:227
Citations in Fuentes should be linkable where a canonical URL exists. Change to `[app.cfe.mx](https://app.cfe.mx/Aplicaciones/CCFE/Tarifas/TarifasCRENegocio/Tarifas/GranDemandaMTH.aspx)` for reader UX and GEO extractability.
TODO screenshot comment present — track in Linear before traffic scales
apps/web/content/blog/demanda-contratada-cfe.mdx:211
One `{/* TODO: screenshot real de la tabla de recibos... */}` placeholder exists. Per convention this is acceptable for a pending product screenshot, but it should be tracked in Linear so it's added before the post accumulates significant organic traffic.
tests1
No render smoke tests for mdxComponents registrations
apps/web/src/app/(marketing)/blog/[slug]/page.tsx
New chart components are registered in mdxComponents with no runtime assertion they render without throwing. TypeScript catches wiring errors at compile time; a Playwright smoke test on /blog/demanda-contratada-cfe would catch runtime errors. Consistent with existing pattern — no action required unless the team adds coverage for all blog routes.
improvement4
PuntaDemandHeatmap legend label field declared but never rendered
apps/web/src/components/blog/charts.tsx:1405
The `legend` array carries `label` strings ('bajo', '', '', '', 'pico') but the `.map()` only renders the color swatch — the label field is never used. Either drop the `label` key or render the labels alongside the swatches.
CAP_RATE 386.37 is function-scoped but also cited in MDX prose — sync risk on rate change
apps/web/src/components/blog/charts.tsx:1493
When CFE publishes a new resolution, this rate must be updated in both `PeakTimeShareHistogram` (local const) and `demanda-contratada-cfe.mdx` (prose on lines ~45 and 228). Extracting to a named export in a shared tariff module would prevent drift.
19 figcaptions repeat identical style object — candidate for a shared constant
apps/web/src/components/blog/charts.tsx
Every chart repeats `style={{ fontSize: '0.8rem', color: MUTED, marginTop: '0.5rem' }}` on its figcaption. A module-level `CAPTION_STYLE` constant or a `.chart-caption` CSS class would eliminate the repetition.
Import line in page.tsx is ~460 chars — expanding to multi-line would improve diff readability
apps/web/src/app/(marketing)/blog/[slug]/page.tsx:10
Adding one chart currently produces a massive single-line diff. Multi-line named imports would make each new chart a clean one-line addition.
seo2
Sitemap priority is flat 0.7 for all blog posts regardless of pillar vs cluster
apps/web/src/app/sitemap.ts
Pillar posts (which aggregate more authority) could reasonably get 0.8. Sitemap priority is a weak signal but aligning it to content hierarchy is good hygiene. Not blocking for this PR.
Article JSON-LD has no image field — limits Top Stories eligibility
apps/web/src/app/(marketing)/blog/[slug]/page.tsx
Google's Article rich result guidelines recommend an `image` field. Adding `image: { '@type': 'ImageObject', url: 'https://batuenergy.com/og-image.png', width: 1200, height: 630 }` to the articleSchema would apply to all blog posts and improve rich result eligibility.