feat/job-energy
needs attentionviewing older commit8e15033 · fullpre-PRreviewed 2026-08-03 16:40 UTC1H · 3M · 4L · 2I- Purpose
- Publish the Energy Strategist job posting as a fully SEO-optimised public page on batuenergy.com/carreras/energy-strategist
- Goal
- Add a server-rendered job posting page with JobPosting JSON-LD schema, update the /carreras index with a job card, and register the route in the sitemap
- Sub-goals
- SG-1: New energy-strategist/page.tsx with metadata, JSON-LD, and full job content
- SG-2: Job card added to carreras/page.tsx before the full-stack card
- SG-3: Route added to sitemap.ts at priority 0.6
- What
- Added 661-line static server component for the Energy Strategist role (job header, apply CTAs, 8 content sections, JobPosting schema). Updated the careers index to show the new role card. Added the route to the sitemap.
- Why
- First commercial hire at Batu — the page needed to exist as a crawlable, schema-rich URL before outreach begins.
- Areas
- apps/web/src/app/(marketing)/carreras/energy-strategist+661−0apps/web/src/app/(marketing)/carreras/page.tsx+21−0apps/web/src/app/sitemap.ts+1−0
- Blast
- 3 files, +683/−0 lines. apps/web only. No backend, no database, no infra. Pure marketing page addition.
Findings · 10
correctness1
canonical relative path, mailto encoding, map keys, schema.org types all correct
apps/web/src/app/(marketing)/carreras/energy-strategist/page.tsx
alternates.canonical '/carreras/energy-strategist' resolves correctly via root-layout metadataBase. mailto subject URL-encoding is valid. All .map() calls have unique stable keys. jobPostingSchema fields (datePosted, validThrough, monthsOfExperience: 0, employmentType: FULL_TIME) are schema.org valid. No correctness bugs.
security1
No security findings — pure static page
apps/web/src/app/(marketing)/carreras/energy-strategist/page.tsx
JsonLd component correctly escapes <, >, & before dangerouslySetInnerHTML injection. External link has rel=noopener noreferrer. No user input, no dynamic data, no API calls, no open redirects.
conventions1
`const process` shadows Node.js global
apps/web/src/app/(marketing)/carreras/energy-strategist/page.tsx:131
Module-level `const process = [...]` shadows the Node.js `process` global. Harmless today (the global is never accessed in this file), but any future `process.env` read would silently get undefined. Rename to `hiringProcess` or `applicationSteps`.
tests1
/carreras/energy-strategist absent from e2e smoke suite
e2e/web/marketing.spec.ts:17
The marketing smoke spec covers only 4 routes. /carreras and /carreras/energy-strategist are live, built routes but not in ROUTES[]. A broken import or routing regression would not be caught in CI. Adding them is a one-liner; the sibling full-stack page has the same gap.
improvement3
Job card JSX is identically duplicated in carreras/page.tsx
apps/web/src/app/(marketing)/carreras/page.tsx:136
The energy-strategist card block (lines 136–155) is structurally identical to the full-stack card (lines 157–176) — same icon, h3, subtitle, arrow. A `JobCard` component accepting href/title/meta would eliminate the duplication. Both `{/* Job card */}` comments are redundant. With a third job posting, this becomes mandatory.
Structural duplication between job page files — shared layout candidate
apps/web/src/app/(marketing)/carreras/energy-strategist/page.tsx:148
Back-link, job header, top/bottom apply CTA blocks, and footer legal note are identical between energy-strategist/page.tsx and full-stack-engineer-ai-native/page.tsx (~80 lines of duplicate markup). A shared `JobPostingLayout` server component taking title, badges, applyEmail, and applySubject would centralise these.
`pt-20 py-20` top-padding is redundant
apps/web/src/app/(marketing)/carreras/energy-strategist/page.tsx:153
`className="min-h-screen pt-20 py-20 md:py-28"` — pt-20 and py-20 both set top padding to 5rem. Drop pt-20; `py-20 md:py-28` achieves the same result. Same pattern in full-stack-engineer-ai-native/page.tsx:84.
seo3
meta description too long — 179 chars (limit 160)
apps/web/src/app/(marketing)/carreras/energy-strategist/page.tsx:9
The description is 179 characters; search engines truncate at ~160, cutting off the 'Híbrido CDMX.' closer which is a key differentiator. Trim ~19 chars — e.g. change 'Perfil de ingeniería, sin experiencia en ventas requerida.' to 'Sin experiencia en ventas requerida.' to save ~22 chars.
metadata.title suffix should be '| Batu', not '| Carreras Batu Energy'
apps/web/src/app/(marketing)/carreras/energy-strategist/page.tsx:8
CLAUDE.md convention: 'keyword first, | Batu suffix'. The page uses '| Carreras Batu Energy'. The twitter.title on line 33 already uses the correct '| Batu Energy' form. The sibling full-stack page has the same mismatch — both should align to 'Energy Strategist | Batu' (24 chars).
JSON-LD applicationContact.contactType: 'hiring' is not in schema.org controlled vocabulary
apps/web/src/app/(marketing)/carreras/energy-strategist/page.tsx:69
ContactPoint.contactType controlled values (customer support, sales, billing support, etc.) do not include 'hiring'. Google's Rich Results validator may flag this. The JobPosting's `url` (line 62) and `directApply: true` (line 65) already surface the apply path; removing `applicationContact` entirely is the cleanest fix.