Files
ESPDocs/app/[locale]/page.jsx
T
Lucas RayanandAntigravity dcd4ef7e1b
CI / Lint e auditorias (push) Successful in 44s
CI / Trivy (dependências e segredos) (push) Successful in 9s
CI / Imagem Docker (linux/arm64) (push) Successful in 3m56s
fix(i18n): extract navigation links module and restore verbatim pt footer
- Move getLocalizedHref, LOCALES, and CANONICAL_ROUTES to i18n/links.js to resolve SSR 500 error on home
- Import from @/i18n/links in page.jsx, Header.jsx, Footer.jsx, and LanguageSelector.jsx
- Restore exact production footer texts, author links, and SVG icons in pt
- Verify 100% visible text parity on all pt pages against production with cmp-pt.py

Co-Authored-By: Antigravity <noreply@google.com>
2026-11-01 10:19:22 -03:00

353 lines
15 KiB
React
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import BookCover from "@/components/BookCover";
import Link from "next/link";
import seriesData from "@/public/series.json";
import frameworksData from "@/public/frameworks.json";
import { SITE } from "@/lib/seo";
import { getLocalizedHref } from "@/i18n/links";
import {
AlertTriangle,
ArrowRight,
ArrowUpRight,
BookOpen,
Box,
Cpu,
Scale,
SlidersHorizontal,
Terminal,
} from "lucide-react";
const FEATURED = ["ESP32", "ESP32-S3", "ESP32-C6", "ESP32-C3", "ESP32-P4", "ESP32-H2"];
const TOOLS = [
{
href: "/seletor",
title: "Seletor inteligente",
desc: "Responda oito perguntas sobre o projeto e receba os chips mais compatíveis, com justificativa técnica.",
icon: SlidersHorizontal,
action: "Encontrar meu chip",
},
{
href: "/comparacao",
title: "Comparador de séries",
desc: "Coloque até quatro modelos lado a lado e compare núcleos, rádio, memória, periféricos e segurança.",
icon: Scale,
action: "Comparar agora",
},
{
href: "/diagnostico",
title: "Diagnóstico de falhas",
desc: "Causas e correções para brownout, bootloop, Guru Meditation, watchdog e falhas de flash.",
icon: AlertTriangle,
action: "Abrir diagnóstico",
},
{
href: "/componentes",
title: "Component Registry",
desc: "Drivers, bibliotecas e BSPs do registro oficial da Espressif, com manifesto pronto para o projeto.",
icon: Box,
action: "Explorar componentes",
},
{
href: "/frameworks",
title: "Frameworks e SDKs",
desc: "ESP-IDF, Arduino, Rust, Matter, MicroPython e as ferramentas que ficam em volta do chip.",
icon: Terminal,
action: "Ver frameworks",
},
{
href: "/catalogo",
title: "Catálogo de placas",
desc: "DevKits e módulos reais, com interface USB, sensores integrados e link para a loja oficial.",
icon: Cpu,
action: "Ver placas",
},
];
const ERRORS = [
{
code: "Brownout detector was triggered",
cause: "Queda de tensão na inicialização do rádio.",
fix: "Capacitor de 10–100 µF junto ao chip e fonte de 5 V com corrente suficiente.",
},
{
code: "Guru Meditation Error",
cause: "Ponteiro inválido, stack overflow ou uso incorreto de interrupção.",
fix: "Leia o backtrace com addr2line e dimensione a stack da task no FreeRTOS.",
},
{
code: "ADC2 indisponível com Wi-Fi",
cause: "O controlador de rádio reserva o ADC2 enquanto está ativo.",
fix: "Use os pinos do ADC1 para sensores analógicos que convivem com o Wi-Fi.",
},
{
code: "Flash Read err / bootloader",
cause: "Tensão ou modo de flash incorretos, ou pino de strapping preso no boot.",
fix: "Confira 3,3 V, pull-ups de boot e a configuração de flash no esptool.",
},
];
export default async function Home({ params }) {
const { locale } = await params;
const isPt = locale === "pt";
const getLinkHref = (href) => getLocalizedHref(`/${locale}${href}`, locale);
const seriesCount = Object.keys(seriesData).length;
const frameworkCount = Object.keys(frameworksData).length;
const featured = FEATURED.map((key) => [key, seriesData[key]]).filter(([, serie]) => Boolean(serie));
return (
<div className="site-canvas min-h-screen">
<Header />
<main id="conteudo">
<section className="shell pb-10 pt-14 sm:pt-20 lg:pb-14 lg:pt-24">
<div className="mx-auto max-w-4xl text-center">
<span className="chip chip-accent">
<Cpu className="h-3.5 w-3.5" aria-hidden="true" />
Documentação aberta do ESP32
</span>
<h1 className="display mt-6 text-[clamp(2.9rem,7.4vw,5.4rem)]">
O ESP32,
<br className="sm:hidden" /> sem ruído.
</h1>
<p className="lede mx-auto mt-6 max-w-2xl">
Especificações, pinagem, comparações e diagnóstico em uma leitura só.
Menos tradução de datasheet, mais tempo com o protótipo na bancada.
</p>
<div className="mt-8 flex flex-col items-center justify-center gap-3 sm:flex-row">
<Link href={getLinkHref("/seletor")} className="btn btn-primary w-full sm:w-auto">
<SlidersHorizontal className="h-4 w-4" aria-hidden="true" />
Escolher meu chip
</Link>
<Link href={getLinkHref("/series")} className="btn btn-ghost w-full sm:w-auto">
Explorar as {seriesCount} séries
<ArrowRight className="h-4 w-4" aria-hidden="true" />
</Link>
</div>
<p className="mt-7 flex flex-wrap items-center justify-center gap-x-5 gap-y-2 text-sm text-muted">
<span><strong className="font-semibold text-ink">{seriesCount}</strong> séries</span>
<span aria-hidden="true" className="text-line-strong">·</span>
<span><strong className="font-semibold text-ink">{frameworkCount}</strong> frameworks</span>
{isPt && (
<>
<span aria-hidden="true" className="text-line-strong">·</span>
<span><strong className="font-semibold text-ink">78</strong> capítulos no livro</span>
<span aria-hidden="true" className="text-line-strong">·</span>
<span>100% em português</span>
</>
)}
</p>
</div>
<div className="panel mt-12 overflow-hidden sm:mt-16">
<div className="flex flex-wrap items-center justify-between gap-3 px-5 py-4 sm:px-7">
<div>
<h2 className="text-sm font-semibold text-ink">Séries em destaque</h2>
<p className="mt-0.5 text-xs text-muted">O essencial de cada família, antes de abrir o datasheet.</p>
</div>
<Link href="/series" className="inline-flex min-h-11 items-center gap-1.5 text-sm font-medium text-accent transition-colors hover:text-accent-strong">
Índice completo
<ArrowRight className="h-4 w-4" aria-hidden="true" />
</Link>
</div>
<div className="grid gap-px border-t border-line bg-line sm:grid-cols-2 lg:grid-cols-3">
{featured.map(([key, serie]) => (
<Link
key={key}
href={`/series/${key}`}
className="group flex min-w-0 flex-col bg-surface p-5 transition-colors duration-300 hover:bg-surface-2 sm:p-6"
>
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<h3 className="font-display text-xl font-semibold tracking-[-0.035em] text-ink">{key}</h3>
<p className="mt-1 truncate text-xs text-muted">{serie.arquitetura}</p>
</div>
<span className="grid h-9 w-9 shrink-0 place-items-center rounded-full bg-accent-soft text-accent transition-transform duration-300 group-hover:scale-105">
<Cpu className="h-4 w-4" aria-hidden="true" />
</span>
</div>
<p className="mt-5 line-clamp-2 text-sm leading-relaxed text-muted">{serie.descricao}</p>
<div className="mt-5 flex items-center justify-between gap-3 border-t border-line pt-3.5">
<span className="truncate font-mono text-[0.6875rem] text-ink-soft">{serie.frequencia}</span>
<span className="inline-flex shrink-0 items-center gap-1 text-xs font-medium text-accent">
Ficha
<ArrowUpRight className="h-3.5 w-3.5 transition-transform duration-300 group-hover:-translate-y-0.5 group-hover:translate-x-0.5" aria-hidden="true" />
</span>
</div>
</Link>
))}
</div>
</div>
</section>
{isPt && (
<section className="shell section pt-8 sm:pt-12" aria-labelledby="livro-titulo">
<div className="relative isolate overflow-hidden rounded-[2rem] bg-[#0b0d10] ring-1 ring-white/10 dark:bg-[#05070a] dark:ring-white/15 sm:rounded-[2.5rem]">
<div
className="pointer-events-none absolute -left-24 top-1/2 -z-10 h-80 w-80 -translate-y-1/2 rounded-full bg-[#2dd4bf]/15 blur-3xl"
aria-hidden="true"
/>
<div className="grid lg:grid-cols-[0.82fr_1.18fr]">
<div className="order-1 flex flex-col justify-center p-7 sm:p-10 lg:order-2 lg:p-14">
<span className="inline-flex w-fit items-center gap-2 rounded-full border border-white/15 bg-white/5 px-3 py-1 text-xs font-semibold text-[#75cdc1]">
<BookOpen className="h-3.5 w-3.5" aria-hidden="true" />
Livro novo
</span>
<h2 id="livro-titulo" className="mt-5 font-display text-[clamp(2rem,4.2vw,3.2rem)] font-semibold leading-[1.05] tracking-[-0.04em] text-[#f4f6f8]">
{SITE.livroTitulo}
</h2>
<p className="mt-2 text-base text-[#a6afbd]">{SITE.livroSubtitulo}</p>
<p className="mt-5 max-w-xl text-sm leading-relaxed text-[#c4cbd5]">
Um percurso completo pelo hardware e pelo firmware do ESP32: da arquitetura do chip
à implantação de produtos conectados. São 12 partes e 78 capítulos sobre Arduino,
ESP-IDF, FreeRTOS, Wi-Fi, BLE, Thread e Matter, periféricos, depuração, baixo consumo
e segurança.
</p>
<p className="mt-6 flex flex-wrap items-center gap-x-3 gap-y-1.5 text-xs text-[#a6afbd]">
<span>12 partes</span>
<span aria-hidden="true" className="text-white/20">·</span>
<span>78 capítulos</span>
<span aria-hidden="true" className="text-white/20">·</span>
<span>ISBN {SITE.livroIsbn}</span>
</p>
<div className="mt-7 flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center">
<a
href={SITE.livroAmazon}
target="_blank"
rel="noopener noreferrer"
className="inline-flex min-h-11 items-center justify-center gap-2 rounded-full bg-[#2dd4bf] px-5 text-sm font-semibold text-[#06201d] transition-colors duration-300 hover:bg-[#5eead4]"
>
Comprar digital
<ArrowUpRight className="h-4 w-4" aria-hidden="true" />
</a>
<a
href={SITE.livroUiclap}
target="_blank"
rel="noopener noreferrer"
className="inline-flex min-h-11 items-center justify-center gap-2 rounded-full border border-white/20 px-5 text-sm font-semibold text-[#f4f6f8] transition-colors duration-300 hover:border-white/40 hover:bg-white/5"
>
Comprar impresso
<ArrowUpRight className="h-4 w-4" aria-hidden="true" />
</a>
<Link href="/pt/livro" className="inline-flex min-h-11 items-center justify-center gap-1.5 rounded-full px-3 text-sm font-medium text-[#a6afbd] transition-colors duration-300 hover:text-[#f4f6f8]">
Ficha completa
<ArrowRight className="h-4 w-4" aria-hidden="true" />
</Link>
</div>
</div>
<div className="order-2 flex items-center justify-center border-t border-white/10 p-8 sm:p-12 lg:order-1 lg:border-r lg:border-t-0 lg:p-14">
<div className="relative w-[min(15.5rem,68%)] lg:w-[min(17rem,100%)]">
<div className="absolute -inset-10 -z-10 rounded-full bg-[#2dd4bf]/10 blur-3xl" aria-hidden="true" />
<BookCover />
</div>
</div>
</div>
</div>
</section>
)}
<section className="shell section" aria-labelledby="ferramentas-titulo">
<div className="mx-auto max-w-2xl text-center">
<p className="eyebrow">Ferramentas</p>
<h2 id="ferramentas-titulo" className="display mt-4 text-[clamp(2rem,4.8vw,3.4rem)]">
Comece pelo problema.
</h2>
<p className="lede mt-4">
Cada ferramenta resolve uma etapa concreta do projeto, sem cadastro e sem rodeio.
</p>
</div>
<div className="mt-10 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{TOOLS.map((tool) => {
const Icon = tool.icon;
return (
<Link key={tool.href} href={tool.href} className="card card-interactive group flex flex-col p-5 sm:p-6">
<span className="grid h-11 w-11 place-items-center rounded-2xl bg-accent-soft text-accent">
<Icon className="h-5 w-5" aria-hidden="true" />
</span>
<h3 className="mt-5 font-display text-lg font-semibold tracking-[-0.025em] text-ink">{tool.title}</h3>
<p className="mt-2 flex-1 text-sm leading-relaxed text-muted">{tool.desc}</p>
<span className="mt-5 inline-flex items-center gap-1.5 text-sm font-medium text-accent">
{tool.action}
<ArrowRight className="h-4 w-4 transition-transform duration-300 group-hover:translate-x-0.5" aria-hidden="true" />
</span>
</Link>
);
})}
</div>
</section>
<section className="shell section pt-0" aria-labelledby="diagnostico-titulo">
<div className="grid items-start gap-10 lg:grid-cols-[0.78fr_1.22fr] lg:gap-16">
<div className="lg:sticky lg:top-28">
<p className="eyebrow">Diagnóstico</p>
<h2 id="diagnostico-titulo" className="display mt-4 text-[clamp(2rem,4.8vw,3.4rem)]">
O primeiro boot não precisa virar noite.
</h2>
<p className="lede mt-4">
Os erros mais comuns têm causa conhecida. O guia parte da mensagem no terminal e chega ao ajuste de hardware ou firmware.
</p>
<Link href="/diagnostico" className="btn btn-accent mt-7">
<AlertTriangle className="h-4 w-4" aria-hidden="true" />
Abrir os 30+ diagnósticos
</Link>
</div>
<div className="panel-dark min-w-0 overflow-hidden rounded-[1.75rem]">
<div className="flex flex-wrap items-center justify-between gap-3 border-b border-white/10 px-5 py-4 sm:px-6">
<span className="inline-flex items-center gap-2 font-mono text-xs text-[#75cdc1]">
<Terminal className="h-4 w-4" aria-hidden="true" />
boot.log
</span>
<span className="text-xs text-[#a6afbd]">4 exemplos de 30+</span>
</div>
<div className="divide-y divide-white/10">
{ERRORS.map((item) => (
<Link key={item.code} href="/diagnostico" className="group grid gap-2 px-5 py-5 transition-colors duration-300 hover:bg-white/[0.035] sm:px-6">
<span className="break-words font-mono text-[0.75rem] font-medium leading-relaxed text-[#ffb39b]">{item.code}</span>
<span className="text-sm font-medium text-[#f4f6f8]">{item.cause}</span>
<span className="text-xs leading-relaxed text-[#a6afbd]">{item.fix}</span>
</Link>
))}
</div>
</div>
</div>
</section>
<section className="shell pb-20 pt-4 md:pb-28">
<div className="mx-auto max-w-3xl text-center">
<p className="eyebrow">Curadoria independente</p>
<h2 className="display mt-4 text-[clamp(2rem,4.5vw,3.2rem)]">
Feito para consultar no meio do projeto.
</h2>
<p className="lede mx-auto mt-4 max-w-2xl">
O ESPDocs organiza dados públicos da Espressif e aponta sempre para a fonte oficial.
Para decisões críticas, o datasheet e o Technical Reference Manual continuam sendo a palavra final.
</p>
<div className="mt-8 flex flex-col items-center justify-center gap-3 sm:flex-row">
<Link href="/glossario" className="btn btn-ghost w-full sm:w-auto">
<BookOpen className="h-4 w-4" aria-hidden="true" />
Glossário
</Link>
<Link href="/sobre" className="btn btn-primary w-full sm:w-auto">
Sobre o projeto
<ArrowRight className="h-4 w-4" aria-hidden="true" />
</Link>
</div>
</div>
</section>
</main>
<Footer />
</div>
);
}