361 lines
15 KiB
React
361 lines
15 KiB
React
"use client";
|
|
|
|
import Header from "@/components/Header";
|
|
import Footer from "@/components/Footer";
|
|
import { useState, Fragment } from "react";
|
|
import Link from "next/link";
|
|
import seriesData from "@/public/series.json";
|
|
import {
|
|
Scale,
|
|
GitCompare,
|
|
Check,
|
|
ChevronDown,
|
|
ChevronUp,
|
|
ArrowRight,
|
|
FileText,
|
|
ExternalLink,
|
|
ShoppingBag,
|
|
Cpu,
|
|
Network,
|
|
Database,
|
|
Sliders,
|
|
Activity,
|
|
HelpCircle,
|
|
Thermometer
|
|
} from "lucide-react";
|
|
|
|
// Category to Icon mapping
|
|
const categoryIcons = {
|
|
"Geral": <HelpCircle className="w-4 h-4 text-purple-500" />,
|
|
"Processador": <Cpu className="w-4 h-4 text-blue-500" />,
|
|
"Conectividade": <Network className="w-4 h-4 text-indigo-500" />,
|
|
"Memória": <Database className="w-4 h-4 text-pink-500" />,
|
|
"Periféricos Digitais": <Sliders className="w-4 h-4 text-sky-500" />,
|
|
"Periféricos Analógicos": <Activity className="w-4 h-4 text-emerald-500" />,
|
|
"Interfaces Especiais": <Activity className="w-4 h-4 text-cyan-500" />,
|
|
"Especificações Ambientais": <Thermometer className="w-4 h-4 text-red-500" />,
|
|
};
|
|
|
|
export default function Comparacao() {
|
|
const [selectedSeries, setSelectedSeries] = useState(["ESP32", "ESP32-S3"]);
|
|
const [collapsedCategories, setCollapsedCategories] = useState({
|
|
"Interfaces Especiais": true,
|
|
"Especificações Ambientais": true,
|
|
});
|
|
|
|
const series = Object.entries(seriesData);
|
|
|
|
const toggleSeries = (seriesKey) => {
|
|
if (selectedSeries.includes(seriesKey)) {
|
|
if (selectedSeries.length > 2) {
|
|
setSelectedSeries(selectedSeries.filter(s => s !== seriesKey));
|
|
}
|
|
} else {
|
|
if (selectedSeries.length < 4) {
|
|
setSelectedSeries([...selectedSeries, seriesKey]);
|
|
}
|
|
}
|
|
};
|
|
|
|
const toggleCategory = (category) => {
|
|
setCollapsedCategories({
|
|
...collapsedCategories,
|
|
[category]: !collapsedCategories[category]
|
|
});
|
|
};
|
|
|
|
const compareFields = [
|
|
{ category: "Geral", fields: [
|
|
{ key: "nome_completo", label: "Nome Completo" },
|
|
{ key: "descricao", label: "Descrição" },
|
|
]},
|
|
{ category: "Processador", fields: [
|
|
{ key: "arquitetura", label: "Arquitetura" },
|
|
{ key: "nucleos", label: "Núcleos" },
|
|
{ key: "frequencia", label: "Frequência" },
|
|
{ key: "coprocessador_ulp", label: "Coprocessador ULP" },
|
|
{ key: "aceleradores_ia", label: "Aceleradores de IA" },
|
|
]},
|
|
{ category: "Conectividade", fields: [
|
|
{ key: "wifi", label: "Wi-Fi" },
|
|
{ key: "bluetooth", label: "Bluetooth" },
|
|
{ key: "zigbee_thread", label: "Zigbee/Thread" },
|
|
{ key: "matter", label: "Matter" },
|
|
{ key: "ethernet", label: "Ethernet" },
|
|
]},
|
|
{ category: "Memória", fields: [
|
|
{ key: "memoria_sram", label: "SRAM" },
|
|
{ key: "memoria_sram_rtc", label: "SRAM RTC" },
|
|
{ key: "memoria_rom", label: "ROM" },
|
|
{ key: "flash_externa", label: "Flash Externa" },
|
|
{ key: "psram_externa", label: "PSRAM Externa" },
|
|
]},
|
|
{ category: "Periféricos Digitais", fields: [
|
|
{ key: "gpio", label: "GPIO" },
|
|
{ key: "uart", label: "UART" },
|
|
{ key: "spi", label: "SPI" },
|
|
{ key: "i2c", label: "I²C" },
|
|
{ key: "i2s", label: "I²S" },
|
|
{ key: "pwm", label: "PWM" },
|
|
{ key: "can", label: "CAN" },
|
|
{ key: "usb", label: "USB" },
|
|
{ key: "sdio", label: "SDIO" },
|
|
]},
|
|
{ category: "Periféricos Analógicos", fields: [
|
|
{ key: "adc", label: "ADC" },
|
|
{ key: "dac", label: "DAC" },
|
|
{ key: "touch", label: "Touch Capacitivo" },
|
|
{ key: "sensor_hall", label: "Sensor Hall" },
|
|
{ key: "sensor_temperatura", label: "Sensor de Temperatura" },
|
|
]},
|
|
{ category: "Interfaces Especiais", fields: [
|
|
{ key: "lcd", label: "LCD" },
|
|
{ key: "camera", label: "Câmera" },
|
|
{ key: "mipi_csi", label: "MIPI CSI" },
|
|
{ key: "mipi_dsi", label: "MIPI DSI" },
|
|
{ key: "ethernet_mac", label: "Ethernet MAC" },
|
|
]},
|
|
{ category: "Especificações Ambientais", fields: [
|
|
{ key: "temperatura_operacao", label: "Temp. Operação" },
|
|
{ key: "temperatura_armazenamento", label: "Temp. Armazenamento" },
|
|
]},
|
|
];
|
|
|
|
const renderValue = (value) => {
|
|
if (!value || value === "Não") {
|
|
return <span className="text-slate-400 dark:text-slate-600 font-medium select-none">—</span>;
|
|
}
|
|
if (value === "Sim") {
|
|
return (
|
|
<span className="inline-flex items-center justify-center w-6 h-6 rounded-full bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border border-emerald-500/20 shadow-xs">
|
|
<Check className="w-3.5 h-3.5" />
|
|
</span>
|
|
);
|
|
}
|
|
return <span className="text-slate-800 dark:text-slate-200 font-semibold">{value}</span>;
|
|
};
|
|
|
|
return (
|
|
<div className="bg-gradient-to-br from-slate-50 via-white to-purple-50/40 dark:from-slate-950 dark:via-slate-950 dark:to-slate-900 min-h-screen text-slate-900 dark:text-slate-100 transition-colors duration-300">
|
|
<Header />
|
|
|
|
<main id="conteudo" className="px-6 pt-16 pb-24 max-w-7xl mx-auto">
|
|
|
|
{/* Top Section */}
|
|
<section className="mb-16">
|
|
<div className="border-l-4 border-purple-500 pl-6 max-w-3xl">
|
|
<p className="text-xs font-bold text-purple-600 dark:text-purple-400 uppercase tracking-widest mb-2 select-none">
|
|
Análise Técnica Comparativa
|
|
</p>
|
|
<h1 className="text-4xl md:text-5xl font-display font-extrabold text-slate-800 dark:text-slate-100 mb-4 tracking-tight leading-tight">
|
|
Comparador de Séries ESP32
|
|
</h1>
|
|
<p className="text-base text-slate-500 dark:text-slate-400 leading-relaxed">
|
|
Coloque lado a lado as especificações completas de clock, memória, interfaces de rádio e periféricos analógicos para validar a engenharia e o custo-benefício de cada microcontrolador.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Model Selector Card */}
|
|
<section className="mb-12">
|
|
<div className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl border border-slate-200/60 dark:border-slate-800/80 rounded-3xl p-6 md:p-8 shadow-2xl relative overflow-hidden">
|
|
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-6">
|
|
<div>
|
|
<h2 className="text-lg font-bold text-slate-800 dark:text-slate-100 mb-1">
|
|
Escolha as Séries
|
|
</h2>
|
|
<p className="text-xs text-slate-400 dark:text-slate-500">
|
|
Selecione de 2 a até 4 modelos simultâneos para montar sua planilha de comparação.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="inline-flex items-center gap-2 bg-purple-500/10 text-purple-600 dark:text-purple-400 px-3.5 py-1.5 rounded-full border border-purple-500/20 text-xs font-bold shadow-xs select-none">
|
|
<GitCompare className="w-3.5 h-3.5" />
|
|
<span>{selectedSeries.length} de 4 selecionados</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Brand-colored dynamic button selector grid */}
|
|
<div className="grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-8 gap-3">
|
|
{series.map(([key, seriesItem]) => {
|
|
const isSelected = selectedSeries.includes(key);
|
|
return (
|
|
<button
|
|
key={key}
|
|
onClick={() => toggleSeries(key)}
|
|
style={{
|
|
borderColor: isSelected ? seriesItem.cor : 'transparent'
|
|
}}
|
|
className={`p-4 border rounded-2xl transition-all duration-300 cursor-pointer active:scale-95 text-center flex flex-col items-center justify-center shadow-xs ${
|
|
isSelected
|
|
? 'bg-slate-100 dark:bg-slate-950 shadow-md scale-[1.02] border-2'
|
|
: 'border-slate-200/60 dark:border-slate-800/80 bg-white/70 dark:bg-slate-900/30 hover:border-purple-400/50 hover:bg-purple-500/5'
|
|
}`}
|
|
>
|
|
<div className="text-3xl mb-2 select-none transform hover:scale-110 transition-transform">{seriesItem.icone}</div>
|
|
<div className="text-xs font-bold text-slate-800 dark:text-slate-200">{key}</div>
|
|
|
|
{isSelected && (
|
|
<div
|
|
className="mt-2.5 w-2 h-2 rounded-full shrink-0 animate-pulse"
|
|
style={{ backgroundColor: seriesItem.cor }}
|
|
/>
|
|
)}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Side by side interactive Table */}
|
|
<div className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl border border-slate-200/60 dark:border-slate-800/80 rounded-3xl overflow-hidden shadow-2xl">
|
|
<div className="overflow-x-auto scrollbar-thin">
|
|
<table className="w-full border-collapse">
|
|
<thead>
|
|
<tr className="bg-slate-850 dark:bg-slate-950 border-b border-slate-750 dark:border-slate-900">
|
|
<th className="px-6 py-5 text-left text-white font-bold text-xs uppercase tracking-widest sticky left-0 bg-slate-850 dark:bg-slate-950 z-20 shadow-xs border-r border-slate-700/30">
|
|
Especificação
|
|
</th>
|
|
{selectedSeries.map(seriesKey => {
|
|
const seriesItem = seriesData[seriesKey];
|
|
return (
|
|
<th key={seriesKey} className="px-6 py-5 text-center text-white font-bold text-xs uppercase tracking-widest min-w-48">
|
|
<div className="flex flex-col items-center gap-2 select-none">
|
|
<span className="text-3xl transform hover:scale-105 transition-transform">{seriesItem.icone}</span>
|
|
<span className="text-sm font-extrabold tracking-wide" style={{ color: seriesItem.cor }}>{seriesKey}</span>
|
|
</div>
|
|
</th>
|
|
);
|
|
})}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{compareFields.map(({ category, fields }) => {
|
|
const isCollapsed = collapsedCategories[category];
|
|
return (
|
|
<Fragment key={category}>
|
|
{/* Collapsible Category Accordion Header */}
|
|
<tr
|
|
onClick={() => toggleCategory(category)}
|
|
className="bg-slate-100/80 dark:bg-slate-950/60 border-y border-slate-250/60 dark:border-slate-800/80 cursor-pointer select-none hover:bg-slate-200/60 dark:hover:bg-slate-900/80 transition-colors"
|
|
>
|
|
<td
|
|
colSpan={selectedSeries.length + 1}
|
|
className="px-6 py-3.5 font-bold text-slate-800 dark:text-slate-200 text-xs uppercase tracking-wider"
|
|
>
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-2">
|
|
{categoryIcons[category] || <HelpCircle className="w-4 h-4 text-purple-500" />}
|
|
<span>{category}</span>
|
|
</div>
|
|
{isCollapsed ? (
|
|
<ChevronDown className="w-4 h-4 opacity-60 text-purple-600 dark:text-purple-400" />
|
|
) : (
|
|
<ChevronUp className="w-4 h-4 opacity-60 text-purple-600 dark:text-purple-400" />
|
|
)}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
|
|
{/* Fields list within category (if expanded) */}
|
|
{!isCollapsed && fields.map(({ key, label }) => (
|
|
<tr
|
|
key={key}
|
|
className="border-b border-slate-100 dark:border-slate-800/40 hover:bg-purple-500/5 dark:hover:bg-purple-400/5 transition-colors group"
|
|
>
|
|
<td className="px-6 py-4 font-bold text-slate-600 dark:text-slate-400 text-xs bg-white dark:bg-slate-900/10 sticky left-0 border-r border-slate-150/60 dark:border-slate-800/60 shrink-0 z-10">
|
|
{label}
|
|
</td>
|
|
{selectedSeries.map(seriesKey => {
|
|
const seriesItem = seriesData[seriesKey];
|
|
const value = seriesItem[key];
|
|
return (
|
|
<td key={seriesKey} className="px-6 py-4 text-center text-xs">
|
|
{renderValue(value)}
|
|
</td>
|
|
);
|
|
})}
|
|
</tr>
|
|
))}
|
|
</Fragment>
|
|
);
|
|
})}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Professional CTA Banner for the Catalog */}
|
|
<div className="my-20 bg-gradient-to-br from-slate-900 to-indigo-950 dark:from-slate-950 dark:to-purple-950/20 border border-slate-800/80 rounded-3xl p-8 md:p-12 shadow-2xl relative overflow-hidden">
|
|
<div className="absolute right-0 bottom-0 w-96 h-96 bg-purple-500/5 rounded-full blur-3xl pointer-events-none" />
|
|
|
|
<div className="flex flex-col md:flex-row items-center justify-between gap-8 relative z-10">
|
|
<div className="flex-1 max-w-2xl text-center md:text-left">
|
|
<div className="inline-flex items-center gap-2 bg-white/10 text-slate-350 px-3 py-1 rounded-full mb-4 text-xs font-semibold select-none">
|
|
<ShoppingBag className="w-3.5 h-3.5" />
|
|
<span>Hardware Adicional</span>
|
|
</div>
|
|
<h3 className="text-2xl md:text-3xl font-display font-extrabold text-white mb-4">
|
|
Catálogo de Placas de Desenvolvimento
|
|
</h3>
|
|
<p className="text-sm text-slate-400 leading-relaxed">
|
|
Quer testar o chip selecionado na bancada? Compare placas de desenvolvimento com layouts de pinos mapeados, conexões de periféricos e links oficiais dos melhores fornecedores.
|
|
</p>
|
|
</div>
|
|
|
|
<Link
|
|
href="/catalogo"
|
|
className="inline-flex items-center justify-center gap-2 bg-white text-slate-900 px-6 py-3.5 rounded-xl font-bold text-sm hover:bg-slate-100 transition-all duration-300 shrink-0 shadow-md active:scale-95"
|
|
>
|
|
<span>Acessar Catálogo</span>
|
|
<ArrowRight className="w-4 h-4" />
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Detailed technical datasheets section */}
|
|
<div className="mt-12">
|
|
<h3 className="text-xl font-display font-extrabold text-slate-800 dark:text-slate-100 mb-6 border-l-4 border-purple-500 pl-4 select-none">
|
|
Documentação Técnica Relacionada
|
|
</h3>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
{selectedSeries.map(seriesKey => {
|
|
const seriesItem = seriesData[seriesKey];
|
|
return (
|
|
<div
|
|
key={seriesKey}
|
|
className="bg-white/70 dark:bg-slate-900/40 backdrop-blur-xl border border-slate-200/80 dark:border-slate-800/80 p-5 rounded-2xl hover:border-purple-500/50 dark:hover:border-purple-400/50 hover:shadow-lg transition-all duration-300 flex flex-col justify-between"
|
|
>
|
|
<div className="flex gap-4 items-start mb-4">
|
|
<div className="text-4xl select-none">{seriesItem.icone}</div>
|
|
<div className="min-w-0">
|
|
<Link href={`/series/${seriesKey}`} className="no-underline">
|
|
<h4 className="text-sm font-extrabold text-slate-850 dark:text-slate-100 hover:text-purple-600 dark:hover:text-purple-400 transition-colors">
|
|
{seriesKey}
|
|
</h4>
|
|
</Link>
|
|
<p className="text-[10px] text-slate-400 dark:text-slate-500 font-bold uppercase tracking-wider mt-1">Guia de Hardware</p>
|
|
</div>
|
|
</div>
|
|
|
|
<a
|
|
href={seriesItem.datasheet}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="inline-flex items-center justify-center gap-1.5 w-full text-center py-2 bg-slate-100 dark:bg-slate-950 hover:bg-purple-500/10 dark:hover:bg-purple-400/10 border border-slate-200 dark:border-slate-800 rounded-xl text-xs font-bold text-slate-650 dark:text-slate-350 hover:text-purple-600 dark:hover:text-purple-400 transition-colors"
|
|
>
|
|
<span>Datasheet Oficial</span>
|
|
<ExternalLink className="w-3.5 h-3.5" />
|
|
</a>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<Footer />
|
|
</div>
|
|
);
|
|
} |