Complete implementation of Spanish (es-419) language support: - Canonical catalogs and data: messages/es.json, data/es/**, lib/data/es/**, public/exemplos/es/** - Canonical slugs: /es/series, /es/catalogo, /es/comparacion, /es/componentes, /es/diagnostico, /es/frameworks, /es/glosario, /es/selector, /es/acerca - Dedicated search index at /api/busca/es with Spanish accent normalization - Strict exclusion of book references in Spanish routes and catalogs - Language selector and root negotiation with es active (ENABLED_LOCALES = ['pt', 'en', 'es']) - Extended verification suite: check-i18n, smoke-imagem and verifica-vazamentos Co-Authored-By: Antigravity <noreply@google.com>
1934 lines
95 KiB
React
1934 lines
95 KiB
React
"use client";
|
|
|
|
import { useState, useMemo } from "react";
|
|
import { usePathname } from "next/navigation";
|
|
import Header from "@/components/Header";
|
|
import Footer from "@/components/Footer";
|
|
import Link from "next/link";
|
|
import { getLocalizedHref } from "@/i18n/links";
|
|
import seriesDataPt from "@/public/series.json";
|
|
import seriesDataEn from "@/data/en/series.json";
|
|
import seriesDataEs from "@/data/es/series.json";
|
|
import PageHero from "@/components/PageHero";
|
|
import {
|
|
Activity,
|
|
AlertTriangle,
|
|
ArrowRight,
|
|
ArrowUpRight,
|
|
Award,
|
|
Ban,
|
|
Battery,
|
|
BookOpen,
|
|
Bot,
|
|
Cable,
|
|
Check,
|
|
CheckCircle,
|
|
ChevronLeft,
|
|
ChevronRight,
|
|
Cpu,
|
|
Edit2,
|
|
Filter,
|
|
HardDrive,
|
|
HelpCircle,
|
|
Home as HomeIcon,
|
|
KeyRound,
|
|
Layers,
|
|
Lock,
|
|
Monitor,
|
|
Network,
|
|
Radio,
|
|
RotateCcw,
|
|
ShieldCheck,
|
|
ShoppingBag,
|
|
Sliders,
|
|
SlidersHorizontal,
|
|
Sparkles,
|
|
Volume2,
|
|
Wifi,
|
|
Zap,
|
|
} from "lucide-react";
|
|
|
|
// Dynamic icon mapper for options and presets
|
|
const iconMap = {
|
|
// Categorias
|
|
"category_smarthome": <HomeIcon className="h-5 w-5" aria-hidden="true" />,
|
|
"category_multimedia": <Monitor className="h-5 w-5" aria-hidden="true" />,
|
|
"category_ai_edge": <Bot className="h-5 w-5" aria-hidden="true" />,
|
|
"category_wearable": <Battery className="h-5 w-5" aria-hidden="true" />,
|
|
"category_industrial": <Cpu className="h-5 w-5" aria-hidden="true" />,
|
|
"category_basic_iot": <Sparkles className="h-5 w-5" aria-hidden="true" />,
|
|
|
|
// Conectividade
|
|
"connectivity_wifi6_dualband": <Wifi className="h-5 w-5" aria-hidden="true" />,
|
|
"connectivity_wifi6_mesh": <Network className="h-5 w-5" aria-hidden="true" />,
|
|
"connectivity_mesh_only": <Radio className="h-5 w-5" aria-hidden="true" />,
|
|
"connectivity_wifi_ble": <Wifi className="h-5 w-5" aria-hidden="true" />,
|
|
"connectivity_bt_classic_audio": <Volume2 className="h-5 w-5" aria-hidden="true" />,
|
|
"connectivity_ethernet": <Cable className="h-5 w-5" aria-hidden="true" />,
|
|
"connectivity_none_local": <Ban className="h-5 w-5" aria-hidden="true" />,
|
|
|
|
// Alimentação
|
|
"power_battery_critical": <Battery className="h-5 w-5" aria-hidden="true" />,
|
|
"power_ulp_coprocessor": <Activity className="h-5 w-5" aria-hidden="true" />,
|
|
"power_always_on": <Zap className="h-5 w-5" aria-hidden="true" />,
|
|
|
|
// Periféricos / Hardware
|
|
"hardware_mipi_multimedia": <Monitor className="h-5 w-5" aria-hidden="true" />,
|
|
"hardware_usb_otg": <Cable className="h-5 w-5" aria-hidden="true" />,
|
|
"hardware_can_twai": <Sliders className="h-5 w-5" aria-hidden="true" />,
|
|
"hardware_dac_audio": <Volume2 className="h-5 w-5" aria-hidden="true" />,
|
|
"hardware_many_gpios": <SlidersHorizontal className="h-5 w-5" aria-hidden="true" />,
|
|
"hardware_standard_sensors": <Cpu className="h-5 w-5" aria-hidden="true" />,
|
|
|
|
// Memória
|
|
"memory_heavy_psram": <HardDrive className="h-5 w-5" aria-hidden="true" />,
|
|
"memory_moderate_sram": <Layers className="h-5 w-5" aria-hidden="true" />,
|
|
"memory_minimal_cost": <Sparkles className="h-5 w-5" aria-hidden="true" />,
|
|
|
|
// IA
|
|
"ai_ai_vector": <Bot className="h-5 w-5" aria-hidden="true" />,
|
|
"ai_standard": <Ban className="h-5 w-5" aria-hidden="true" />,
|
|
|
|
// Segurança
|
|
"security_key_manager_dpa": <KeyRound className="h-5 w-5" aria-hidden="true" />,
|
|
"security_hardware_ecc_ecdsa": <ShieldCheck className="h-5 w-5" aria-hidden="true" />,
|
|
"security_matter_ecdsa": <ShieldCheck className="h-5 w-5" aria-hidden="true" />,
|
|
"security_secure_boot": <Lock className="h-5 w-5" aria-hidden="true" />,
|
|
"security_standard_tls": <HelpCircle className="h-5 w-5" aria-hidden="true" />,
|
|
|
|
// Ecossistema
|
|
"ecosystem_arduino_ready": <CheckCircle className="h-5 w-5" aria-hidden="true" />,
|
|
"ecosystem_advanced_idf": <Cpu className="h-5 w-5" aria-hidden="true" />,
|
|
};
|
|
|
|
const getOptionIcon = (questionId, value) => {
|
|
const key = `${questionId}_${value}`;
|
|
return iconMap[key] || <HelpCircle className="h-5 w-5" aria-hidden="true" />;
|
|
};
|
|
|
|
// 7 Preset Configurations for 1-click recommendations
|
|
const PRESETS_PT = [
|
|
{
|
|
id: "preset_smarthome",
|
|
title: "Smart Home & Matter",
|
|
badge: "Automação 802.15.4",
|
|
description: "Wi-Fi 6, Thread/Zigbee e baixo consumo",
|
|
answers: {
|
|
category: "smarthome",
|
|
connectivity: "wifi6_mesh",
|
|
power: "ulp_coprocessor",
|
|
hardware: "standard_sensors",
|
|
memory: "moderate_sram",
|
|
ai: "standard",
|
|
security: "hardware_ecc_ecdsa",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_hmi",
|
|
title: "Painel Touch & Telas",
|
|
badge: "Display / Câmera",
|
|
description: "Display LCD/MIPI, PSRAM e alta taxa de quadros",
|
|
answers: {
|
|
category: "multimedia",
|
|
connectivity: "wifi_ble",
|
|
power: "always_on",
|
|
hardware: "mipi_multimedia",
|
|
memory: "heavy_psram",
|
|
ai: "standard",
|
|
security: "secure_boot",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_ai",
|
|
title: "Edge AI & Visão",
|
|
badge: "Aceleração Vetorial",
|
|
description: "Acelerador neural, câmera e alta memória",
|
|
answers: {
|
|
category: "ai_edge",
|
|
connectivity: "wifi_ble",
|
|
power: "always_on",
|
|
hardware: "mipi_multimedia",
|
|
memory: "heavy_psram",
|
|
ai: "ai_vector",
|
|
security: "secure_boot",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_wearable",
|
|
title: "Wearable a Bateria",
|
|
badge: "Deep Sleep <10µA",
|
|
description: "Eficiência extrema e rádio Bluetooth LE",
|
|
answers: {
|
|
category: "wearable",
|
|
connectivity: "wifi_ble",
|
|
power: "battery_critical",
|
|
hardware: "standard_sensors",
|
|
memory: "minimal_cost",
|
|
ai: "standard",
|
|
security: "standard_tls",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_wifi6_5ghz",
|
|
title: "Wi-Fi 6 Dual-Band (5GHz)",
|
|
badge: "2.4 & 5 GHz",
|
|
description: "Banda de 5 GHz com menor interferência de RF",
|
|
answers: {
|
|
category: "smarthome",
|
|
connectivity: "wifi6_dualband",
|
|
power: "always_on",
|
|
hardware: "standard_sensors",
|
|
memory: "moderate_sram",
|
|
ai: "standard",
|
|
security: "key_manager_dpa",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_industrial",
|
|
title: "Industrial / CAN & Rede",
|
|
badge: "Ethernet & TWAI",
|
|
description: "Barramento CAN/TWAI, Ethernet e alta robustez",
|
|
answers: {
|
|
category: "industrial",
|
|
connectivity: "ethernet",
|
|
power: "always_on",
|
|
hardware: "can_twai",
|
|
memory: "moderate_sram",
|
|
ai: "standard",
|
|
security: "secure_boot",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_budget",
|
|
title: "IoT Básico de Baixo Custo",
|
|
badge: "Ultra-Econômico",
|
|
description: "Menor custo por unidade para sensores simples",
|
|
answers: {
|
|
category: "basic_iot",
|
|
connectivity: "wifi_ble",
|
|
power: "battery_critical",
|
|
hardware: "standard_sensors",
|
|
memory: "minimal_cost",
|
|
ai: "standard",
|
|
security: "standard_tls",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
}
|
|
];
|
|
|
|
const PRESETS_EN = [
|
|
{
|
|
id: "preset_smarthome",
|
|
title: "Smart Home & Matter",
|
|
badge: "802.15.4 Automation",
|
|
description: "Wi-Fi 6, Thread/Zigbee, and low power",
|
|
answers: {
|
|
category: "smarthome",
|
|
connectivity: "wifi6_mesh",
|
|
power: "ulp_coprocessor",
|
|
hardware: "standard_sensors",
|
|
memory: "moderate_sram",
|
|
ai: "standard",
|
|
security: "hardware_ecc_ecdsa",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_hmi",
|
|
title: "Touch Panel & Displays",
|
|
badge: "Display / Camera",
|
|
description: "LCD/MIPI display, PSRAM, and high frame rates",
|
|
answers: {
|
|
category: "multimedia",
|
|
connectivity: "wifi_ble",
|
|
power: "always_on",
|
|
hardware: "mipi_multimedia",
|
|
memory: "heavy_psram",
|
|
ai: "standard",
|
|
security: "secure_boot",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_ai",
|
|
title: "Edge AI & Vision",
|
|
badge: "Vector Acceleration",
|
|
description: "Neural accelerator, camera, and large memory",
|
|
answers: {
|
|
category: "ai_edge",
|
|
connectivity: "wifi_ble",
|
|
power: "always_on",
|
|
hardware: "mipi_multimedia",
|
|
memory: "heavy_psram",
|
|
ai: "ai_vector",
|
|
security: "secure_boot",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_wearable",
|
|
title: "Battery-Powered Wearable",
|
|
badge: "Deep Sleep <10µA",
|
|
description: "Extreme efficiency and Bluetooth LE radio",
|
|
answers: {
|
|
category: "wearable",
|
|
connectivity: "wifi_ble",
|
|
power: "battery_critical",
|
|
hardware: "standard_sensors",
|
|
memory: "minimal_cost",
|
|
ai: "standard",
|
|
security: "standard_tls",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_wifi6_5ghz",
|
|
title: "Dual-Band Wi-Fi 6 (5GHz)",
|
|
badge: "2.4 & 5 GHz",
|
|
description: "5 GHz band with reduced RF interference",
|
|
answers: {
|
|
category: "smarthome",
|
|
connectivity: "wifi6_dualband",
|
|
power: "always_on",
|
|
hardware: "standard_sensors",
|
|
memory: "moderate_sram",
|
|
ai: "standard",
|
|
security: "key_manager_dpa",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_industrial",
|
|
title: "Industrial / CAN & Wired Net",
|
|
badge: "Ethernet & TWAI",
|
|
description: "CAN/TWAI bus, Ethernet, and high robustness",
|
|
answers: {
|
|
category: "industrial",
|
|
connectivity: "ethernet",
|
|
power: "always_on",
|
|
hardware: "can_twai",
|
|
memory: "moderate_sram",
|
|
ai: "standard",
|
|
security: "secure_boot",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_budget",
|
|
title: "Entry-Level Low-Cost IoT",
|
|
badge: "Ultra-Budget",
|
|
description: "Lowest unit cost for simple connected sensors",
|
|
answers: {
|
|
category: "basic_iot",
|
|
connectivity: "wifi_ble",
|
|
power: "battery_critical",
|
|
hardware: "standard_sensors",
|
|
memory: "minimal_cost",
|
|
ai: "standard",
|
|
security: "standard_tls",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
}
|
|
];
|
|
|
|
const PRESETS_ES = [
|
|
{
|
|
id: "preset_smarthome",
|
|
title: "Smart Home y Matter",
|
|
badge: "Automatización 802.15.4",
|
|
description: "Wi-Fi 6, Thread/Zigbee y bajo consumo",
|
|
answers: {
|
|
category: "smarthome",
|
|
connectivity: "wifi6_mesh",
|
|
power: "ulp_coprocessor",
|
|
hardware: "standard_sensors",
|
|
memory: "moderate_sram",
|
|
ai: "standard",
|
|
security: "hardware_ecc_ecdsa",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_hmi",
|
|
title: "Panel táctil y pantallas",
|
|
badge: "Pantalla / Cámara",
|
|
description: "Pantalla LCD/MIPI, PSRAM y alta tasa de cuadros",
|
|
answers: {
|
|
category: "multimedia",
|
|
connectivity: "wifi_ble",
|
|
power: "always_on",
|
|
hardware: "mipi_multimedia",
|
|
memory: "heavy_psram",
|
|
ai: "standard",
|
|
security: "secure_boot",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_ai",
|
|
title: "Edge AI y visión",
|
|
badge: "Aceleración vectorial",
|
|
description: "Acelerador neuronal, cámara y gran memoria",
|
|
answers: {
|
|
category: "ai_edge",
|
|
connectivity: "wifi_ble",
|
|
power: "always_on",
|
|
hardware: "mipi_multimedia",
|
|
memory: "heavy_psram",
|
|
ai: "ai_vector",
|
|
security: "secure_boot",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_wearable",
|
|
title: "Dispositivo portátil a batería",
|
|
badge: "Deep Sleep <10µA",
|
|
description: "Extrema eficiencia y radio Bluetooth LE",
|
|
answers: {
|
|
category: "wearable",
|
|
connectivity: "wifi_ble",
|
|
power: "battery_critical",
|
|
hardware: "standard_sensors",
|
|
memory: "minimal_cost",
|
|
ai: "standard",
|
|
security: "standard_tls",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_wifi6_5ghz",
|
|
title: "Wi-Fi 6 de doble banda (5GHz)",
|
|
badge: "2.4 y 5 GHz",
|
|
description: "Banda de 5 GHz con menor interferencia de RF",
|
|
answers: {
|
|
category: "smarthome",
|
|
connectivity: "wifi6_dualband",
|
|
power: "always_on",
|
|
hardware: "standard_sensors",
|
|
memory: "moderate_sram",
|
|
ai: "standard",
|
|
security: "key_manager_dpa",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_industrial",
|
|
title: "Industrial / CAN y red cableada",
|
|
badge: "Ethernet y TWAI",
|
|
description: "Bus CAN/TWAI, Ethernet y alta robustez",
|
|
answers: {
|
|
category: "industrial",
|
|
connectivity: "ethernet",
|
|
power: "always_on",
|
|
hardware: "can_twai",
|
|
memory: "moderate_sram",
|
|
ai: "standard",
|
|
security: "secure_boot",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
},
|
|
{
|
|
id: "preset_budget",
|
|
title: "IoT básico de bajo costo",
|
|
badge: "Económico",
|
|
description: "Menor costo unitario para sensores conectados simples",
|
|
answers: {
|
|
category: "basic_iot",
|
|
connectivity: "wifi_ble",
|
|
power: "battery_critical",
|
|
hardware: "standard_sensors",
|
|
memory: "minimal_cost",
|
|
ai: "standard",
|
|
security: "standard_tls",
|
|
ecosystem: "arduino_ready",
|
|
}
|
|
}
|
|
];
|
|
|
|
const QUESTIONS_PT = [
|
|
{
|
|
id: "category",
|
|
question: "1. Qual é o foco principal ou aplicação do seu projeto?",
|
|
description: "Categorizar sua aplicação ajuda a equilibrar formato físico, custo por unidade, barramentos de mídia e poder computacional.",
|
|
options: [
|
|
{ value: "smarthome", label: "Automação / Smart Home / Dispositivos Conectados", icon: "category_smarthome" },
|
|
{ value: "multimedia", label: "Interfaces Gráficas (Telas LCD/MIPI / Câmeras / IHM)", icon: "category_multimedia" },
|
|
{ value: "ai_edge", label: "Inteligência Artificial na Borda (Visão / Áudio / TinyML)", icon: "category_ai_edge" },
|
|
{ value: "wearable", label: "Dispositivo Portátil / Vestível (Alimentado a Bateria)", icon: "category_wearable" },
|
|
{ value: "industrial", label: "Industrial / Robótica / Redes de Campo e Automação", icon: "category_industrial" },
|
|
{ value: "basic_iot", label: "IoT Básico / Sensor Conectado de Ultra-Baixo Custo", icon: "category_basic_iot" },
|
|
]
|
|
},
|
|
{
|
|
id: "connectivity",
|
|
question: "2. Qual tecnologia de rede sem fio ou cabeada você precisa?",
|
|
description: "O ecossistema ESP32 oferece desde rádios Wi-Fi 6 Dual-Band (2.4/5GHz), Bluetooth 6 e Mesh 802.15.4 até Ethernet cabeada e Bluetooth Clássico.",
|
|
options: [
|
|
{ value: "wifi6_dualband", label: "Wi-Fi 6 Dual-Band (2.4 GHz e 5 GHz) + Bluetooth 6.0", icon: "connectivity_wifi6_dualband" },
|
|
{ value: "wifi6_mesh", label: "Wi-Fi 6 (2.4 GHz) + Matter / Zigbee 3.0 / Thread (802.15.4)", icon: "connectivity_wifi6_mesh" },
|
|
{ value: "mesh_only", label: "Apenas Rádio Mesh (Zigbee / Thread / Matter) + BLE (Sem Wi-Fi)", icon: "connectivity_mesh_only" },
|
|
{ value: "wifi_ble", label: "Wi-Fi 4 Tradicional (2.4 GHz) + Bluetooth LE", icon: "connectivity_wifi_ble" },
|
|
{ value: "bt_classic_audio", label: "Bluetooth Clássico BR/EDR (Áudio A2DP / SPP Serial)", icon: "connectivity_bt_classic_audio" },
|
|
{ value: "ethernet", label: "Rede Cabeada Industrial (Ethernet MAC integrado)", icon: "connectivity_ethernet" },
|
|
{ value: "none_local", label: "Apenas Processamento Local / Sem Conexão Sem Fio", icon: "connectivity_none_local" },
|
|
]
|
|
},
|
|
{
|
|
id: "power",
|
|
question: "3. Como o dispositivo será alimentado eletricamente?",
|
|
description: "A escolha da fonte define a importância de modos de Deep Sleep ultra-baixos (<10µA) e coprocessadores ULP para monitoramento contínuo.",
|
|
options: [
|
|
{ value: "battery_critical", label: "Bateria / Solar (Consumo Ultra-Baixo em Deep Sleep <10µA)", icon: "power_battery_critical" },
|
|
{ value: "ulp_coprocessor", label: "Misto / Monitorar sensores em background via Coprocessador ULP", icon: "power_ulp_coprocessor" },
|
|
{ value: "always_on", label: "Tomada, Fonte Externa ou USB (Sempre Ativo / Alto Desempenho)", icon: "power_always_on" },
|
|
]
|
|
},
|
|
{
|
|
id: "hardware",
|
|
question: "4. Quais periféricos ou barramentos físicos especiais você vai usar?",
|
|
description: "Algumas funcionalidades exigem blocos de silício dedicados, como interfaces de vídeo MIPI/RGB, barramento CAN industrial ou USB nativo.",
|
|
options: [
|
|
{ value: "mipi_multimedia", label: "Display Gráfico LCD (RGB/MIPI DSI) ou Câmeras (DVP/MIPI CSI)", icon: "hardware_mipi_multimedia" },
|
|
{ value: "usb_otg", label: "USB Nativo OTG / Host (Emular teclado/mouse HID, CDC ou modo Host)", icon: "hardware_usb_otg" },
|
|
{ value: "can_twai", label: "Barramento Automotivo / Industrial CAN (TWAI ou CAN-FD)", icon: "hardware_can_twai" },
|
|
{ value: "dac_audio", label: "Áudio Analógico com DAC integrado (saída direta de som)", icon: "hardware_dac_audio" },
|
|
{ value: "many_gpios", label: "Muitos pinos livres de expansão (Mais de 35 GPIOs físicos)", icon: "hardware_many_gpios" },
|
|
{ value: "standard_sensors", label: "Sensores comuns com barramentos tradicionais (I2C, SPI, UART, PWM)", icon: "hardware_standard_sensors" },
|
|
]
|
|
},
|
|
{
|
|
id: "memory",
|
|
question: "5. Qual o volume de memória RAM / PSRAM necessário?",
|
|
description: "Projetos gráficos (LVGL), visão computacional, áudio ou pilhas pesadas exigem suporte a PSRAM externa ou integrada de alta densidade.",
|
|
options: [
|
|
{ value: "heavy_psram", label: "Alta Densidade de PSRAM (>4 MB a 64 MB para buffers de vídeo, IA ou LVGL)", icon: "memory_heavy_psram" },
|
|
{ value: "moderate_sram", label: "SRAM interna equilibrada (320 KB a 512 KB) com ou sem PSRAM leve", icon: "memory_moderate_sram" },
|
|
{ value: "minimal_cost", label: "Memória compacta e econômica (firmware enxuto de baixo custo)", icon: "memory_minimal_cost" },
|
|
]
|
|
},
|
|
{
|
|
id: "ai",
|
|
question: "6. Seu projeto usará Inteligência Artificial local (Edge AI / TinyML)?",
|
|
description: "Instruções vetoriais e aceleradores neurais dedicados em hardware reduzem o tempo de inferência de redes neurais de segundos para milissegundos.",
|
|
options: [
|
|
{ value: "ai_vector", label: "Sim, preciso de acelerador vetorial de IA em hardware (TinyML / Visão)", icon: "ai_ai_vector" },
|
|
{ value: "standard", label: "Não, processamento lógico e matemático comum é suficiente", icon: "ai_standard" },
|
|
]
|
|
},
|
|
{
|
|
id: "security",
|
|
question: "7. Qual nível de segurança e criptografia por hardware o produto exige?",
|
|
description: "As séries diferem em suporte a Key Manager, proteção contra ataques de canal lateral (DPA), curvas elípticas (ECC/ECDSA) e aceleradores AES.",
|
|
options: [
|
|
{ value: "key_manager_dpa", label: "Proteção Máxima: Key Manager em hardware (chave isolada da CPU) e Proteção DPA", icon: "security_key_manager_dpa" },
|
|
{ value: "hardware_ecc_ecdsa", label: "Acelerador de Curvas Elípticas (ECC) e Assinatura Digital ECDSA por hardware", icon: "security_hardware_ecc_ecdsa" },
|
|
{ value: "secure_boot", label: "Boot Seguro V2 e Criptografia de Flash (XTS-AES) contra cópia do firmware", icon: "security_secure_boot" },
|
|
{ value: "standard_tls", label: "Comunicação TLS/HTTPS comum (Aceleradores AES e SHA por hardware)", icon: "security_standard_tls" },
|
|
]
|
|
},
|
|
{
|
|
id: "ecosystem",
|
|
question: "8. Qual é a sua preferência de ambiente de desenvolvimento?",
|
|
description: "Chips mais maduros têm amplo suporte oficial no Arduino Core com centenas de bibliotecas prontas, enquanto chips preliminares utilizam ESP-IDF.",
|
|
options: [
|
|
{ value: "arduino_ready", label: "Pronto para Arduino IDE (suporte maduro com vasta coleção de bibliotecas)", icon: "ecosystem_arduino_ready" },
|
|
{ value: "advanced_idf", label: "ESP-IDF profissional ou aberto a silícios de última geração (S31, H4, etc.)", icon: "ecosystem_advanced_idf" },
|
|
]
|
|
}
|
|
];
|
|
|
|
const QUESTIONS_EN = [
|
|
{
|
|
id: "category",
|
|
question: "1. What is the main focus or application of your project?",
|
|
description: "Categorizing your application helps balance physical form factor, unit cost, media buses, and compute performance.",
|
|
options: [
|
|
{ value: "smarthome", label: "Automation / Smart Home / Connected Devices", icon: "category_smarthome" },
|
|
{ value: "multimedia", label: "Graphical Interfaces (LCD/MIPI Displays / Cameras / HMI)", icon: "category_multimedia" },
|
|
{ value: "ai_edge", label: "Edge Artificial Intelligence (Vision / Audio / TinyML)", icon: "category_ai_edge" },
|
|
{ value: "wearable", label: "Portable / Wearable Device (Battery-Powered)", icon: "category_wearable" },
|
|
{ value: "industrial", label: "Industrial / Robotics / Fieldbuses and Automation", icon: "category_industrial" },
|
|
{ value: "basic_iot", label: "Basic IoT / Ultra-Low-Cost Connected Sensor", icon: "category_basic_iot" },
|
|
]
|
|
},
|
|
{
|
|
id: "connectivity",
|
|
question: "2. Which wireless or wired networking technology do you need?",
|
|
description: "The ESP32 ecosystem offers everything from Dual-Band Wi-Fi 6 (2.4/5GHz), Bluetooth 6, and 802.15.4 Mesh to wired Ethernet and Bluetooth Classic.",
|
|
options: [
|
|
{ value: "wifi6_dualband", label: "Dual-Band Wi-Fi 6 (2.4 GHz & 5 GHz) + Bluetooth 6.0", icon: "connectivity_wifi6_dualband" },
|
|
{ value: "wifi6_mesh", label: "Wi-Fi 6 (2.4 GHz) + Matter / Zigbee 3.0 / Thread (802.15.4)", icon: "connectivity_wifi6_mesh" },
|
|
{ value: "mesh_only", label: "Mesh Radio Only (Zigbee / Thread / Matter) + BLE (No Wi-Fi)", icon: "connectivity_mesh_only" },
|
|
{ value: "wifi_ble", label: "Traditional Wi-Fi 4 (2.4 GHz) + Bluetooth LE", icon: "connectivity_wifi_ble" },
|
|
{ value: "bt_classic_audio", label: "Bluetooth Classic BR/EDR (A2DP Audio / SPP Serial)", icon: "connectivity_bt_classic_audio" },
|
|
{ value: "ethernet", label: "Industrial Wired Network (Integrated Ethernet MAC)", icon: "connectivity_ethernet" },
|
|
{ value: "none_local", label: "Local Processing Only / No Wireless Connection", icon: "connectivity_none_local" },
|
|
]
|
|
},
|
|
{
|
|
id: "power",
|
|
question: "3. How will the device be electrically powered?",
|
|
description: "Power source choices determine the need for ultra-low Deep Sleep modes (<10µA) and ULP coprocessors for continuous monitoring.",
|
|
options: [
|
|
{ value: "battery_critical", label: "Battery / Solar (Ultra-Low Deep Sleep Current <10µA)", icon: "power_battery_critical" },
|
|
{ value: "ulp_coprocessor", label: "Mixed / Background Sensor Monitoring via ULP Coprocessor", icon: "power_ulp_coprocessor" },
|
|
{ value: "always_on", label: "Mains, External Power Supply, or USB (Always-On / High Performance)", icon: "power_always_on" },
|
|
]
|
|
},
|
|
{
|
|
id: "hardware",
|
|
question: "4. Which specialized peripherals or physical buses will you use?",
|
|
description: "Certain features require dedicated silicon blocks, such as MIPI/RGB video interfaces, industrial CAN bus, or native USB.",
|
|
options: [
|
|
{ value: "mipi_multimedia", label: "Graphic LCD Display (RGB/MIPI DSI) or Cameras (DVP/MIPI CSI)", icon: "hardware_mipi_multimedia" },
|
|
{ value: "usb_otg", label: "Native USB OTG / Host (Emulate HID keyboard/mouse, CDC, or Host mode)", icon: "hardware_usb_otg" },
|
|
{ value: "can_twai", label: "Automotive / Industrial CAN Bus (TWAI or CAN-FD)", icon: "hardware_can_twai" },
|
|
{ value: "dac_audio", label: "Analog Audio with Integrated DAC (Direct Sound Output)", icon: "hardware_dac_audio" },
|
|
{ value: "many_gpios", label: "Many Expansion Pins (More than 35 Physical GPIOs)", icon: "hardware_many_gpios" },
|
|
{ value: "standard_sensors", label: "Standard Sensors with Common Buses (I2C, SPI, UART, PWM)", icon: "hardware_standard_sensors" },
|
|
]
|
|
},
|
|
{
|
|
id: "memory",
|
|
question: "5. What volume of RAM / PSRAM is required?",
|
|
description: "Graphic UI projects (LVGL), computer vision, audio, or heavy stacks require high-density external or integrated PSRAM.",
|
|
options: [
|
|
{ value: "heavy_psram", label: "High PSRAM Density (>4 MB to 64 MB for video buffers, AI, or LVGL)", icon: "memory_heavy_psram" },
|
|
{ value: "moderate_sram", label: "Balanced Internal SRAM (320 KB to 512 KB) with or without light PSRAM", icon: "memory_moderate_sram" },
|
|
{ value: "minimal_cost", label: "Compact and Economical Memory (Lean, low-cost firmware)", icon: "memory_minimal_cost" },
|
|
]
|
|
},
|
|
{
|
|
id: "ai",
|
|
question: "6. Will your project use local Artificial Intelligence (Edge AI / TinyML)?",
|
|
description: "Hardware vector instructions and dedicated neural accelerators cut neural network inference times from seconds to milliseconds.",
|
|
options: [
|
|
{ value: "ai_vector", label: "Yes, I need hardware AI vector acceleration (TinyML / Vision)", icon: "ai_ai_vector" },
|
|
{ value: "standard", label: "No, standard logic and mathematical processing is sufficient", icon: "ai_standard" },
|
|
]
|
|
},
|
|
{
|
|
id: "security",
|
|
question: "7. What level of hardware security and cryptography does the product require?",
|
|
description: "Series differ in Key Manager support, side-channel attack protection (DPA), elliptic curves (ECC/ECDSA), and AES accelerators.",
|
|
options: [
|
|
{ value: "key_manager_dpa", label: "Maximum Protection: Hardware Key Manager (isolated from CPU) and DPA Protection", icon: "security_key_manager_dpa" },
|
|
{ value: "hardware_ecc_ecdsa", label: "Hardware Elliptic Curve Accelerator (ECC) and ECDSA Digital Signature", icon: "security_hardware_ecc_ecdsa" },
|
|
{ value: "secure_boot", label: "Secure Boot V2 and Flash Encryption (XTS-AES) against firmware cloning", icon: "security_secure_boot" },
|
|
{ value: "standard_tls", label: "Standard TLS/HTTPS Communication (Hardware AES and SHA accelerators)", icon: "security_standard_tls" },
|
|
]
|
|
},
|
|
{
|
|
id: "ecosystem",
|
|
question: "8. What is your development environment preference?",
|
|
description: "Mature chips offer broad official support in Arduino Core with hundreds of ready libraries, while newer silicon starts in ESP-IDF.",
|
|
options: [
|
|
{ value: "arduino_ready", label: "Arduino IDE Ready (mature support with vast library ecosystem)", icon: "ecosystem_arduino_ready" },
|
|
{ value: "advanced_idf", label: "Professional ESP-IDF or open to next-gen silicon (S31, H4, etc.)", icon: "ecosystem_advanced_idf" },
|
|
]
|
|
}
|
|
];
|
|
|
|
const QUESTIONS_ES = [
|
|
{
|
|
id: "category",
|
|
question: "1. ¿Cuál es el enfoque principal o aplicación de su proyecto?",
|
|
description: "Categorizar su aplicación ayuda a equilibrar formato físico, costo por unidad, buses de medios y potencia de cómputo.",
|
|
options: [
|
|
{ value: "smarthome", label: "Automatización / Hogar inteligente / Dispositivos conectados", icon: "category_smarthome" },
|
|
{ value: "multimedia", label: "Interfaces gráficas (Pantallas LCD/MIPI / Cámaras / HMI)", icon: "category_multimedia" },
|
|
{ value: "ai_edge", label: "Inteligencia artificial en el borde (Visión / Audio / TinyML)", icon: "category_ai_edge" },
|
|
{ value: "wearable", label: "Dispositivo portátil / Vestible (Alimentado por batería)", icon: "category_wearable" },
|
|
{ value: "industrial", label: "Industrial / Robótica / Redes de campo y automatización", icon: "category_industrial" },
|
|
{ value: "basic_iot", label: "IoT básico / Sensor conectado de ultra bajo costo", icon: "category_basic_iot" },
|
|
]
|
|
},
|
|
{
|
|
id: "connectivity",
|
|
question: "2. ¿Qué tecnología de red inalámbrica o cableada necesita?",
|
|
description: "El ecosistema ESP32 ofrece desde radios Wi-Fi 6 de doble banda (2.4/5GHz), Bluetooth 6 y redes Mesh 802.15.4 hasta Ethernet cableado y Bluetooth clásico.",
|
|
options: [
|
|
{ value: "wifi6_dualband", label: "Wi-Fi 6 de doble banda (2.4 GHz y 5 GHz) + Bluetooth 6.0", icon: "connectivity_wifi6_dualband" },
|
|
{ value: "wifi6_mesh", label: "Wi-Fi 6 (2.4 GHz) + Matter / Zigbee 3.0 / Thread (802.15.4)", icon: "connectivity_wifi6_mesh" },
|
|
{ value: "mesh_only", label: "Solo radio Mesh (Zigbee / Thread / Matter) + BLE (sin Wi-Fi)", icon: "connectivity_mesh_only" },
|
|
{ value: "wifi_ble", label: "Wi-Fi 4 tradicional (2.4 GHz) + Bluetooth LE", icon: "connectivity_wifi_ble" },
|
|
{ value: "bt_classic_audio", label: "Bluetooth clásico BR/EDR (Audio A2DP / SPP serie)", icon: "connectivity_bt_classic_audio" },
|
|
{ value: "ethernet", label: "Red cableada industrial (controlador Ethernet MAC integrado)", icon: "connectivity_ethernet" },
|
|
{ value: "none_local", label: "Solo procesamiento local / Sin conexión inalámbrica", icon: "connectivity_none_local" },
|
|
]
|
|
},
|
|
{
|
|
id: "power",
|
|
question: "3. ¿Cómo se alimentará eléctricamente el dispositivo?",
|
|
description: "La elección de la fuente define la importancia de modos Deep Sleep ultra bajos (<10µA) y coprocesadores ULP para monitoreo continuo.",
|
|
options: [
|
|
{ value: "battery_critical", label: "Batería / Solar (Consumo ultra bajo en Deep Sleep <10µA)", icon: "power_battery_critical" },
|
|
{ value: "ulp_coprocessor", label: "Mixto / Monitorear sensores en segundo plano mediante coprocesador ULP", icon: "power_ulp_coprocessor" },
|
|
{ value: "always_on", label: "Enchufe, fuente externa o USB (Siempre activo / Alto rendimiento)", icon: "power_always_on" },
|
|
]
|
|
},
|
|
{
|
|
id: "hardware",
|
|
question: "4. ¿Qué periféricos o buses físicos especiales va a utilizar?",
|
|
description: "Algunas funciones requieren bloques de silicio dedicados, como interfaces de video MIPI/RGB, bus CAN industrial o USB nativo.",
|
|
options: [
|
|
{ value: "mipi_multimedia", label: "Pantalla gráfica LCD (RGB/MIPI DSI) o Cámaras (DVP/MIPI CSI)", icon: "hardware_mipi_multimedia" },
|
|
{ value: "usb_otg", label: "USB nativo OTG / Host (Emular teclado/ratón HID, CDC o modo Host)", icon: "hardware_usb_otg" },
|
|
{ value: "can_twai", label: "Bus automotriz / industrial CAN (TWAI o CAN-FD)", icon: "hardware_can_twai" },
|
|
{ value: "dac_audio", label: "Audio analógico con DAC integrado (salida de sonido directa)", icon: "hardware_dac_audio" },
|
|
{ value: "many_gpios", label: "Muchos pines libres de expansión (Más de 35 GPIOs físicos)", icon: "hardware_many_gpios" },
|
|
{ value: "standard_sensors", label: "Sensores estándar con buses tradicionales (I2C, SPI, UART, PWM)", icon: "hardware_standard_sensors" },
|
|
]
|
|
},
|
|
{
|
|
id: "memory",
|
|
question: "5. ¿Qué volumen de memoria RAM / PSRAM es necesario?",
|
|
description: "Los proyectos gráficos (LVGL), visión por computadora, audio o pilas pesadas requieren soporte para PSRAM externa o integrada de alta densidad.",
|
|
options: [
|
|
{ value: "heavy_psram", label: "Alta densidad de PSRAM (>4 MB a 64 MB para buffers de video, IA o LVGL)", icon: "memory_heavy_psram" },
|
|
{ value: "moderate_sram", label: "SRAM interna equilibrada (320 KB a 512 KB) con o sin PSRAM ligera", icon: "memory_moderate_sram" },
|
|
{ value: "minimal_cost", label: "Memoria compacta y económica (firmware ligero de bajo costo)", icon: "memory_minimal_cost" },
|
|
]
|
|
},
|
|
{
|
|
id: "ai",
|
|
question: "6. ¿Su proyecto utilizará Inteligencia Artificial local (Edge AI / TinyML)?",
|
|
description: "Las instrucciones vectoriales y los aceleradores neuronales dedicados en hardware reducen el tiempo de inferencia de redes neuronales de segundos a milisegundos.",
|
|
options: [
|
|
{ value: "ai_vector", label: "Sí, necesito acelerador vectorial de IA en hardware (TinyML / Visión)", icon: "ai_ai_vector" },
|
|
{ value: "standard", label: "No, el procesamiento lógico y matemático estándar es suficiente", icon: "ai_standard" },
|
|
]
|
|
},
|
|
{
|
|
id: "security",
|
|
question: "7. ¿Qué nivel de seguridad y criptografía por hardware exige el producto?",
|
|
description: "Las series difieren en soporte para Key Manager, protección contra ataques de canal lateral (DPA), curvas elípticas (ECC/ECDSA) y aceleradores AES.",
|
|
options: [
|
|
{ value: "key_manager_dpa", label: "Protección máxima: Key Manager en hardware (clave aislada de la CPU) y Protección DPA", icon: "security_key_manager_dpa" },
|
|
{ value: "hardware_ecc_ecdsa", label: "Acelerador de curvas elípticas (ECC) y firma digital ECDSA por hardware", icon: "security_hardware_ecc_ecdsa" },
|
|
{ value: "secure_boot", label: "Secure Boot V2 y cifrado de flash (XTS-AES) contra copia de firmware", icon: "security_secure_boot" },
|
|
{ value: "standard_tls", label: "Comunicación TLS/HTTPS estándar (Aceleradores AES y SHA por hardware)", icon: "security_standard_tls" },
|
|
]
|
|
},
|
|
{
|
|
id: "ecosystem",
|
|
question: "8. ¿Cuál es su preferencia de entorno de desarrollo?",
|
|
description: "Los chips más maduros tienen amplio soporte oficial en Arduino Core con cientos de bibliotecas disponibles, mientras que chips preliminares utilizan ESP-IDF.",
|
|
options: [
|
|
{ value: "arduino_ready", label: "Listo para Arduino IDE (soporte maduro con amplia colección de bibliotecas)", icon: "ecosystem_arduino_ready" },
|
|
{ value: "advanced_idf", label: "ESP-IDF profesional o abierto a silicio de última generación (S31, H4, etc.)", icon: "ecosystem_advanced_idf" },
|
|
]
|
|
}
|
|
];
|
|
|
|
const FILTROS_PT = [
|
|
{ key: "wifi6", label: "Wi-Fi 6 (802.11ax)" },
|
|
{ key: "wifi5ghz", label: "Wi-Fi dual-band (5 GHz)" },
|
|
{ key: "bluetoothClassic", label: "Bluetooth Clássico (A2DP)" },
|
|
{ key: "leAudio", label: "Bluetooth LE Audio" },
|
|
{ key: "matter", label: "Matter / 802.15.4" },
|
|
{ key: "ethernet", label: "Ethernet MAC" },
|
|
{ key: "aiAccel", label: "Aceleração vetorial de IA" },
|
|
{ key: "psram", label: "Suporte a PSRAM" },
|
|
{ key: "mipiDisplayCamera", label: "Telas e câmeras (MIPI/RGB)" },
|
|
{ key: "usbOTG", label: "USB nativo OTG" },
|
|
{ key: "canBus", label: "Barramento CAN / TWAI" },
|
|
{ key: "dac", label: "Conversor DAC" },
|
|
{ key: "touch", label: "Touch capacitivo" },
|
|
{ key: "arduinoReady", label: "Suporte ao Arduino Core" },
|
|
{ key: "eccHardware", label: "Curvas elípticas (ECC)" },
|
|
{ key: "ecdsaHardware", label: "Assinatura ECDSA em hardware" },
|
|
{ key: "keyManager", label: "Key Manager em hardware" },
|
|
{ key: "secureBootV2", label: "Secure Boot V2" },
|
|
];
|
|
|
|
const FILTROS_EN = [
|
|
{ key: "wifi6", label: "Wi-Fi 6 (802.11ax)" },
|
|
{ key: "wifi5ghz", label: "Dual-band Wi-Fi (5 GHz)" },
|
|
{ key: "bluetoothClassic", label: "Bluetooth Classic (A2DP)" },
|
|
{ key: "leAudio", label: "Bluetooth LE Audio" },
|
|
{ key: "matter", label: "Matter / 802.15.4" },
|
|
{ key: "ethernet", label: "Ethernet MAC" },
|
|
{ key: "aiAccel", label: "AI vector acceleration" },
|
|
{ key: "psram", label: "PSRAM support" },
|
|
{ key: "mipiDisplayCamera", label: "Displays and cameras (MIPI/RGB)" },
|
|
{ key: "usbOTG", label: "Native USB OTG" },
|
|
{ key: "canBus", label: "CAN / TWAI bus" },
|
|
{ key: "dac", label: "DAC converter" },
|
|
{ key: "touch", label: "Capacitive touch" },
|
|
{ key: "arduinoReady", label: "Arduino Core support" },
|
|
{ key: "eccHardware", label: "Elliptic curves (ECC)" },
|
|
{ key: "ecdsaHardware", label: "Hardware ECDSA signature" },
|
|
{ key: "keyManager", label: "Hardware Key Manager" },
|
|
{ key: "secureBootV2", label: "Secure Boot V2" },
|
|
];
|
|
|
|
const FILTROS_ES = [
|
|
{ key: "wifi6", label: "Wi-Fi 6 (802.11ax)" },
|
|
{ key: "wifi5ghz", label: "Wi-Fi de doble banda (5 GHz)" },
|
|
{ key: "bluetoothClassic", label: "Bluetooth clásico (A2DP)" },
|
|
{ key: "leAudio", label: "Bluetooth LE Audio" },
|
|
{ key: "matter", label: "Matter / 802.15.4" },
|
|
{ key: "ethernet", label: "Ethernet MAC" },
|
|
{ key: "aiAccel", label: "Aceleración vectorial de IA" },
|
|
{ key: "psram", label: "Soporte para PSRAM" },
|
|
{ key: "mipiDisplayCamera", label: "Pantallas y cámaras (MIPI/RGB)" },
|
|
{ key: "usbOTG", label: "USB nativo OTG" },
|
|
{ key: "canBus", label: "Bus CAN / TWAI" },
|
|
{ key: "dac", label: "Conversor DAC" },
|
|
{ key: "touch", label: "Touch capacitivo" },
|
|
{ key: "arduinoReady", label: "Soporte para Arduino Core" },
|
|
{ key: "eccHardware", label: "Curvas elípticas (ECC)" },
|
|
{ key: "ecdsaHardware", label: "Firma ECDSA en hardware" },
|
|
{ key: "keyManager", label: "Key Manager en hardware" },
|
|
{ key: "secureBootV2", label: "Secure Boot V2" },
|
|
];
|
|
|
|
const ROTULOS_ETAPA_PT = {
|
|
category: "Aplicação",
|
|
connectivity: "Conectividade",
|
|
power: "Alimentação",
|
|
hardware: "Periféricos",
|
|
memory: "Memória",
|
|
ai: "Inteligência artificial",
|
|
security: "Segurança",
|
|
ecosystem: "Ambiente de desenvolvimento",
|
|
};
|
|
|
|
const ROTULOS_ETAPA_EN = {
|
|
category: "Application",
|
|
connectivity: "Connectivity",
|
|
power: "Power Supply",
|
|
hardware: "Peripherals",
|
|
memory: "Memory",
|
|
ai: "Artificial Intelligence",
|
|
security: "Security",
|
|
ecosystem: "Development Environment",
|
|
};
|
|
|
|
const ROTULOS_ETAPA_ES = {
|
|
category: "Aplicación",
|
|
connectivity: "Conectividad",
|
|
power: "Alimentación",
|
|
hardware: "Periféricos",
|
|
memory: "Memoria",
|
|
ai: "Inteligencia artificial",
|
|
security: "Seguridad",
|
|
ecosystem: "Entorno de desarrollo",
|
|
};
|
|
|
|
export default function Seletor() {
|
|
const pathname = usePathname();
|
|
const isEn = pathname?.startsWith("/en");
|
|
const isEs = pathname?.startsWith("/es");
|
|
const currentLocale = isEs ? "es" : isEn ? "en" : "pt";
|
|
|
|
const seriesData = isEs ? seriesDataEs : isEn ? seriesDataEn : seriesDataPt;
|
|
const PRESETS = isEs ? PRESETS_ES : isEn ? PRESETS_EN : PRESETS_PT;
|
|
const questions = isEs ? QUESTIONS_ES : isEn ? QUESTIONS_EN : QUESTIONS_PT;
|
|
const FILTROS = isEs ? FILTROS_ES : isEn ? FILTROS_EN : FILTROS_PT;
|
|
const ROTULOS_ETAPA = isEs ? ROTULOS_ETAPA_ES : isEn ? ROTULOS_ETAPA_EN : ROTULOS_ETAPA_PT;
|
|
|
|
const [mode, setMode] = useState("wizard"); // "wizard" | "filter"
|
|
const [currentStep, setCurrentStep] = useState(0);
|
|
const [currentAnswer, setCurrentAnswer] = useState(null);
|
|
const [activePreset, setActivePreset] = useState(null);
|
|
|
|
const [answers, setAnswers] = useState({
|
|
category: null,
|
|
connectivity: null,
|
|
power: null,
|
|
hardware: null,
|
|
memory: null,
|
|
ai: null,
|
|
security: null,
|
|
ecosystem: null,
|
|
});
|
|
|
|
const [showResults, setShowResults] = useState(false);
|
|
const [showSummary, setShowSummary] = useState(false);
|
|
|
|
// Direct Parametric Filter State (for "filter" mode)
|
|
const [filters, setFilters] = useState({
|
|
wifi6: false,
|
|
wifi5ghz: false,
|
|
bluetoothClassic: false,
|
|
leAudio: false,
|
|
matter: false,
|
|
ethernet: false,
|
|
aiAccel: false,
|
|
psram: false,
|
|
mipiDisplayCamera: false,
|
|
usbOTG: false,
|
|
canBus: false,
|
|
dac: false,
|
|
touch: false,
|
|
arduinoReady: false,
|
|
keyManager: false,
|
|
eccHardware: false,
|
|
ecdsaHardware: false,
|
|
secureBootV2: false,
|
|
});
|
|
|
|
const handleAnswer = (questionId, value) => {
|
|
setCurrentAnswer(value);
|
|
setAnswers(prev => ({ ...prev, [questionId]: value }));
|
|
setActivePreset(null);
|
|
};
|
|
|
|
const handleNext = () => {
|
|
if (currentAnswer !== null) {
|
|
if (currentStep < questions.length - 1) {
|
|
const nextStep = currentStep + 1;
|
|
setCurrentStep(nextStep);
|
|
setCurrentAnswer(answers[questions[nextStep].id] || null);
|
|
} else {
|
|
setShowSummary(true);
|
|
}
|
|
}
|
|
};
|
|
|
|
const goBack = () => {
|
|
if (currentStep > 0) {
|
|
const prevStep = currentStep - 1;
|
|
setCurrentStep(prevStep);
|
|
setCurrentAnswer(answers[questions[prevStep].id] || null);
|
|
}
|
|
};
|
|
|
|
const resetQuiz = () => {
|
|
setCurrentStep(0);
|
|
setCurrentAnswer(null);
|
|
setShowResults(false);
|
|
setShowSummary(false);
|
|
setActivePreset(null);
|
|
setAnswers({
|
|
category: null,
|
|
connectivity: null,
|
|
power: null,
|
|
hardware: null,
|
|
memory: null,
|
|
ai: null,
|
|
security: null,
|
|
ecosystem: null,
|
|
});
|
|
};
|
|
|
|
const applyPreset = (preset) => {
|
|
setAnswers(preset.answers);
|
|
setActivePreset(preset.id);
|
|
setCurrentStep(questions.length - 1);
|
|
setCurrentAnswer(preset.answers.ecosystem);
|
|
setShowSummary(false);
|
|
setShowResults(true);
|
|
};
|
|
|
|
// Comprehensive Multi-Criteria Scoring Engine based on series data
|
|
const calculateRecommendations = () => {
|
|
const scores = {};
|
|
const reasons = {};
|
|
const alerts = {};
|
|
const r = (pt, en, es) => isEs ? es : isEn ? en : pt;
|
|
|
|
Object.keys(seriesData).forEach(key => {
|
|
scores[key] = 0;
|
|
reasons[key] = [];
|
|
alerts[key] = [];
|
|
const serie = seriesData[key];
|
|
const seg = serie.seguranca || {};
|
|
|
|
const hasMatter = serie.matter === "Sim" || serie.matter === "Yes";
|
|
const hasNoWifi = !serie.wifi || serie.wifi === "Não" || serie.wifi === "No";
|
|
const hasNoBluetooth = !serie.bluetooth || serie.bluetooth === "Não" || serie.bluetooth === "No";
|
|
|
|
// 1. CATEGORIA (category)
|
|
if (answers.category === "smarthome") {
|
|
if (hasMatter) {
|
|
scores[key] += 25;
|
|
reasons[key].push(r("Suporte nativo a Matter para interoperabilidade residencial inteligente", "Native Matter support for smart home interoperability", "Soporte nativo para Matter para interoperabilidad en el hogar inteligente"));
|
|
}
|
|
if (serie.zigbee_thread && serie.zigbee_thread !== "Não" && serie.zigbee_thread !== "No") {
|
|
scores[key] += 20;
|
|
reasons[key].push(r("Rádio 802.15.4 integrado compatível com Zigbee 3.0 e Thread", "Integrated 802.15.4 radio compatible with Zigbee 3.0 and Thread", "Radio 802.15.4 integrada compatible con Zigbee 3.0 y Thread"));
|
|
}
|
|
if (!hasNoWifi) {
|
|
scores[key] += 10;
|
|
reasons[key].push(r("Rádio Wi-Fi integrado para conexão direta ao roteador", "Integrated Wi-Fi radio for direct router connection", "Radio Wi-Fi integrada para conexión directa al enrutador"));
|
|
}
|
|
} else if (answers.category === "multimedia") {
|
|
if (serie.mipi_dsi || serie.mipi_csi) {
|
|
scores[key] += 35;
|
|
reasons[key].push(r("Barramentos avançados de alta velocidade MIPI DSI (telas) e MIPI CSI (câmeras)", "High-speed MIPI DSI (displays) and MIPI CSI (cameras) buses", "Buses avanzados de alta velocidad MIPI DSI (pantallas) y MIPI CSI (cámaras)"));
|
|
} else if (serie.lcd || serie.camera) {
|
|
scores[key] += 25;
|
|
reasons[key].push(r("Interfaces dedicadas em hardware para telas LCD RGB e câmeras DVP", "Dedicated hardware interfaces for RGB LCD displays and DVP cameras", "Interfaces de hardware dedicadas para pantallas LCD RGB y cámaras DVP"));
|
|
} else {
|
|
scores[key] -= 40;
|
|
alerts[key].push(r("Sem barramentos dedicados para telas coloridas ou câmeras (apenas SPI/I2C simples)", "No dedicated buses for color displays or cameras (SPI/I2C only)", "Sin buses dedicados para pantallas a color o cámaras (solo SPI/I2C)"));
|
|
}
|
|
|
|
if (serie.psram_externa && serie.psram_externa !== "Não" && serie.psram_externa !== "No") {
|
|
scores[key] += 20;
|
|
reasons[key].push(r("Suporte a PSRAM de alta capacidade para framebuffers gráficos e processamento de imagem", "High-capacity PSRAM support for graphics framebuffers and image processing", "Soporte para PSRAM de alta capacidad para búferes de gráficos y procesamiento de imágenes"));
|
|
}
|
|
} else if (answers.category === "ai_edge") {
|
|
if (serie.aceleradores_ia) {
|
|
scores[key] += 40;
|
|
reasons[key].push(r(`Aceleração neural e instruções vetoriais dedicadas (${serie.aceleradores_ia})`, `Dedicated neural acceleration and vector instructions (${serie.aceleradores_ia})`, `Aceleración neuronal e instrucciones vectoriales dedicadas (${serie.aceleradores_ia})`));
|
|
} else {
|
|
scores[key] -= 50;
|
|
alerts[key].push(r("Sem aceleradores neurais dedicados em hardware", "No dedicated hardware neural accelerators", "Sin aceleradores neuronales dedicados en hardware"));
|
|
}
|
|
if (serie.nucleos && serie.nucleos.includes("2")) {
|
|
scores[key] += 10;
|
|
reasons[key].push(r("Processador Dual-Core para pipeline paralelo de captura e inferência", "Dual-core processor for parallel capture and inference pipeline", "Procesador dual-core para pipeline paralelo de captura e inferencia"));
|
|
}
|
|
} else if (answers.category === "wearable") {
|
|
const sleep = serie.consumo_energia?.deep_sleep;
|
|
if (sleep) {
|
|
const uaMatch = sleep.match(/(\d+)\s*µA/);
|
|
if (uaMatch && parseInt(uaMatch[1]) <= 10) {
|
|
scores[key] += 25;
|
|
reasons[key].push(r(`Consumo em Deep Sleep ultrabaixo de apenas ${sleep} preserva baterias compactas`, `Ultra-low Deep Sleep current of just ${sleep} preserves compact batteries`, `Consumo en Deep Sleep ultrabajo de solo ${sleep} preserva baterías compactas`));
|
|
}
|
|
}
|
|
if (!hasNoBluetooth) {
|
|
scores[key] += 15;
|
|
reasons[key].push(r("Rádio Bluetooth de alta eficiência energética para pareamento contínuo", "Energy-efficient Bluetooth radio for continuous pairing", "Radio Bluetooth de alta eficiencia energética para emparejamiento continuo"));
|
|
}
|
|
if (key === "ESP32-C2" || key === "ESP32-C3" || key === "ESP32-H2") {
|
|
scores[key] += 10;
|
|
reasons[key].push(r("Encapsulamento compacto QFN reduz o footprint na placa", "Compact QFN package reduces board footprint", "Encapsulado compacto QFN reduce el espacio en la placa"));
|
|
}
|
|
} else if (answers.category === "industrial") {
|
|
if (serie.ethernet || serie.ethernet_mac) {
|
|
scores[key] += 30;
|
|
reasons[key].push(r("Controlador MAC Ethernet integrado para conexões cabeadas industriais imunes a ruídos de RF", "Integrated Ethernet MAC controller for industrial wired connections immune to RF noise", "Controlador Ethernet MAC integrado para conexiones cableadas industriales inmunes al ruido de RF"));
|
|
}
|
|
if (serie.can) {
|
|
scores[key] += 25;
|
|
reasons[key].push(r(`Interface de barramento industrial ${serie.can}`, `Industrial bus interface ${serie.can}`, `Interfaz de bus industrial ${serie.can}`));
|
|
}
|
|
if (parseInt(serie.gpio) >= 30) {
|
|
scores[key] += 10;
|
|
reasons[key].push(r(`Ampla contagem de portas digitais (${serie.gpio} GPIOs) para relés e atuadores`, `Large count of digital ports (${serie.gpio} GPIOs) for relays and actuators`, `Gran cantidad de puertos digitales (${serie.gpio} GPIOs) para relés y actuadores`));
|
|
}
|
|
} else if (answers.category === "basic_iot") {
|
|
if (key === "ESP32-C2" || key === "ESP32-C3" || key === "ESP32-C61") {
|
|
scores[key] += 30;
|
|
reasons[key].push(r("Excelente relação custo-benefício para sensores e atuadores conectados em escala", "Cost-effective for connected sensors and actuators at scale", "Excelente relación costo-beneficio para sensores y actuadores conectados a escala"));
|
|
}
|
|
if (!hasNoWifi && !hasNoBluetooth) {
|
|
scores[key] += 15;
|
|
reasons[key].push(r("Wi-Fi + BLE integrados em módulo econômico", "Integrated Wi-Fi + BLE in an economical module", "Wi-Fi + BLE integrados en un módulo económico"));
|
|
}
|
|
}
|
|
|
|
// 2. CONECTIVIDADE (connectivity)
|
|
if (answers.connectivity === "wifi6_dualband") {
|
|
if (String(serie.wifi).includes("5 GHz") || String(serie.wifi).includes("dual-band")) {
|
|
scores[key] += 50;
|
|
reasons[key].push(r("Exclusivo Wi-Fi 6 Dual-Band (2.4 GHz e 5 GHz) para ambientes com alto ruído de RF", "Exclusive Dual-Band Wi-Fi 6 (2.4 GHz and 5 GHz) for high-noise RF environments", "Exclusivo Wi-Fi 6 de doble banda (2.4 GHz y 5 GHz) para entornos con alto ruido de RF"));
|
|
} else {
|
|
scores[key] -= 250;
|
|
alerts[key].push(r("Incompatível: Não possui rádio Wi-Fi na faixa de 5 GHz", "Incompatible: No 5 GHz Wi-Fi radio", "Incompatible: No cuenta con radio Wi-Fi en la banda de 5 GHz"));
|
|
}
|
|
} else if (answers.connectivity === "wifi6_mesh") {
|
|
if (String(serie.wifi).includes("Wi-Fi 6") && hasMatter) {
|
|
scores[key] += 40;
|
|
reasons[key].push(r("Wi-Fi 6 (802.11ax) somado a rádio 802.15.4 para Thread/Zigbee/Matter", "Wi-Fi 6 (802.11ax) plus 802.15.4 radio for Thread/Zigbee/Matter", "Wi-Fi 6 (802.11ax) sumado a radio 802.15.4 para Thread/Zigbee/Matter"));
|
|
} else if (hasMatter) {
|
|
scores[key] += 15;
|
|
reasons[key].push(r("Suporte a Matter/Thread, porém sem Wi-Fi 6", "Matter/Thread support, but without Wi-Fi 6", "Soporte para Matter/Thread, pero sin Wi-Fi 6"));
|
|
} else {
|
|
scores[key] -= 150;
|
|
alerts[key].push(r("Incompatível: Sem rádio 802.15.4 integrado para Thread/Zigbee", "Incompatible: No integrated 802.15.4 radio for Thread/Zigbee", "Incompatible: Sin radio 802.15.4 integrada para Thread/Zigbee"));
|
|
}
|
|
} else if (answers.connectivity === "mesh_only") {
|
|
if (hasNoWifi && hasMatter) {
|
|
scores[key] += 45;
|
|
reasons[key].push(r("Foco 100% em 802.15.4 (Zigbee/Thread/Matter) com baixo consumo e sem rádio Wi-Fi", "100% focus on 802.15.4 (Zigbee/Thread/Matter) with low power and no Wi-Fi radio", "Enfoque 100% en 802.15.4 (Zigbee/Thread/Matter) con bajo consumo y sin radio Wi-Fi"));
|
|
} else if (hasMatter) {
|
|
scores[key] += 20;
|
|
reasons[key].push(r("Suporta redes Mesh (Zigbee/Thread), mas inclui rádio Wi-Fi adicional", "Supports Mesh networks (Zigbee/Thread), but includes additional Wi-Fi radio", "Soporta redes Mesh (Zigbee/Thread), pero incluye radio Wi-Fi adicional"));
|
|
} else {
|
|
scores[key] -= 180;
|
|
alerts[key].push(r("Incompatível: Sem rádio 802.15.4 para protocolo Thread ou Zigbee", "Incompatible: No 802.15.4 radio for Thread or Zigbee", "Incompatible: Sin radio 802.15.4 para protocolo Thread o Zigbee"));
|
|
}
|
|
} else if (answers.connectivity === "wifi_ble") {
|
|
if (!hasNoWifi && !hasNoBluetooth) {
|
|
scores[key] += 30;
|
|
reasons[key].push(r(`Pilha completa com Wi-Fi (${serie.wifi}) e Bluetooth (${serie.bluetooth})`, `Full stack with Wi-Fi (${serie.wifi}) and Bluetooth (${serie.bluetooth})`, `Pila completa con Wi-Fi (${serie.wifi}) y Bluetooth (${serie.bluetooth})`));
|
|
} else if (!hasNoWifi) {
|
|
scores[key] += 10;
|
|
alerts[key].push(r("Possui Wi-Fi, mas não possui rádio Bluetooth integrado", "Has Wi-Fi, but lacks integrated Bluetooth", "Cuenta con Wi-Fi, pero carece de Bluetooth integrado"));
|
|
} else {
|
|
scores[key] -= 200;
|
|
alerts[key].push(r("Incompatível: Não possui rádio Wi-Fi integrado", "Incompatible: No integrated Wi-Fi radio", "Incompatible: No cuenta con radio Wi-Fi integrada"));
|
|
}
|
|
} else if (answers.connectivity === "bt_classic_audio") {
|
|
if (String(serie.bluetooth).includes("Classic") || String(serie.bluetooth).includes("Clássico")) {
|
|
scores[key] += 50;
|
|
reasons[key].push(r("Suporte a Bluetooth Clássico (BR/EDR) com perfis A2DP de áudio e SPP serial", "Bluetooth Classic (BR/EDR) support with A2DP audio and serial SPP profiles", "Soporte para Bluetooth clásico (BR/EDR) con perfiles de audio A2DP y SPP serie"));
|
|
} else {
|
|
scores[key] -= 250;
|
|
alerts[key].push(r("Incompatível: Não possui Bluetooth Clássico (apenas BLE, incompatível com áudio A2DP legado)", "Incompatible: No Bluetooth Classic (BLE only, incompatible with legacy A2DP audio)", "Incompatible: No cuenta con Bluetooth clásico (solo BLE, incompatible con audio A2DP heredado)"));
|
|
}
|
|
} else if (answers.connectivity === "ethernet") {
|
|
if (serie.ethernet || serie.ethernet_mac) {
|
|
scores[key] += 45;
|
|
reasons[key].push(r("Controlador MAC Ethernet nativo integrado (requer apenas transceiver PHY)", "Native integrated Ethernet MAC controller (requires only PHY transceiver)", "Controlador Ethernet MAC nativo integrado (requiere solo transceptor PHY)"));
|
|
} else {
|
|
scores[key] -= 180;
|
|
alerts[key].push(r("Sem MAC Ethernet interno (necessita de módulo SPI Ethernet externo como W5500)", "No internal Ethernet MAC (requires external SPI Ethernet module like W5500)", "Sin Ethernet MAC interno (requiere módulo SPI Ethernet externo como W5500)"));
|
|
}
|
|
} else if (answers.connectivity === "none_local") {
|
|
if (hasNoWifi || hasNoBluetooth || key === "ESP32-P4") {
|
|
scores[key] += 35;
|
|
reasons[key].push(r("Arquitetura orientada a processamento local dedicado sem custo de transceivers de rádio", "Architecture geared towards dedicated local processing without radio transceiver cost", "Arquitectura orientada a procesamiento local dedicado sin costo de transceptores de radio"));
|
|
} else {
|
|
scores[key] += 10;
|
|
}
|
|
}
|
|
|
|
// 3. ALIMENTAÇÃO (power)
|
|
if (answers.power === "battery_critical") {
|
|
const sleep = serie.consumo_energia?.deep_sleep;
|
|
if (sleep) {
|
|
const uaMatch = sleep.match(/(\d+)\s*µA/);
|
|
if (uaMatch && parseInt(uaMatch[1]) <= 10) {
|
|
scores[key] += 25;
|
|
reasons[key].push(r(`Consumo de suspensão de ${sleep} permite operação prolongada por bateria`, `Sleep current of ${sleep} enables extended battery operation`, `Corriente de reposo de ${sleep} permite operación prolongada con batería`));
|
|
} else {
|
|
scores[key] += 10;
|
|
reasons[key].push(r(`Modo de baixo consumo de ${sleep}`, `Low power mode of ${sleep}`, `Modo de bajo consumo de ${sleep}`));
|
|
}
|
|
}
|
|
if (serie.coprocessador_ulp && serie.coprocessador_ulp !== "Não" && serie.coprocessador_ulp !== "No") {
|
|
scores[key] += 10;
|
|
reasons[key].push(r("Coprocessador ULP permite checar pinos sem despertar a CPU principal", "ULP coprocessor monitors pins without waking the main CPU", "Coprocesador ULP monitorea pines sin despertar la CPU principal"));
|
|
}
|
|
} else if (answers.power === "ulp_coprocessor") {
|
|
if (serie.coprocessador_ulp && serie.coprocessador_ulp !== "Não" && serie.coprocessador_ulp !== "No") {
|
|
scores[key] += 35;
|
|
reasons[key].push(r(`Coprocessador ULP / CPU de Baixo Consumo (${serie.coprocessador_ulp})`, `ULP coprocessor / Low-power core (${serie.coprocessador_ulp})`, `Coprocesador ULP / Núcleo de bajo consumo (${serie.coprocessador_ulp})`));
|
|
} else {
|
|
scores[key] -= 30;
|
|
alerts[key].push(r("Sem coprocessador ULP dedicado", "No dedicated ULP coprocessor", "Sin coprocesador ULP dedicado"));
|
|
}
|
|
} else if (answers.power === "always_on") {
|
|
if (serie.nucleos && serie.nucleos.includes("2")) {
|
|
scores[key] += 15;
|
|
reasons[key].push(r("Processador Dual-Core para multitarefa paralela contínua", "Dual-Core processor for continuous parallel multitasking", "Procesador dual-core para multitarea paralela continua"));
|
|
}
|
|
const freqMatch = serie.frequencia?.match(/(\d+)\s*MHz/);
|
|
if (freqMatch && parseInt(freqMatch[1]) >= 240) {
|
|
scores[key] += 15;
|
|
reasons[key].push(r(`Frequência de clock de alto desempenho (${serie.frequencia})`, `High-performance clock frequency (${serie.frequencia})`, `Frecuencia de reloj de alto rendimiento (${serie.frequencia})`));
|
|
}
|
|
}
|
|
|
|
// 4. HARDWARE & PERIFÉRICOS (hardware)
|
|
if (answers.hardware === "mipi_multimedia") {
|
|
if (serie.mipi_dsi || serie.mipi_csi) {
|
|
scores[key] += 40;
|
|
reasons[key].push(r("Interface MIPI DSI (telas) e MIPI CSI (câmeras) de altíssima taxa de transferência", "High-throughput MIPI DSI (displays) and MIPI CSI (cameras) interface", "Interfaz de muy alta velocidad MIPI DSI (pantallas) y MIPI CSI (cámaras)"));
|
|
} else if (serie.lcd || serie.camera) {
|
|
scores[key] += 25;
|
|
reasons[key].push(r("Interfaces paralelas para LCD RGB e câmeras DVP de 8/16 bits", "Parallel interfaces for RGB LCD and 8/16-bit DVP cameras", "Interfaces paralelas para LCD RGB y cámaras DVP de 8/16 bits"));
|
|
} else {
|
|
scores[key] -= 180;
|
|
alerts[key].push(r("Incompatível: Sem interfaces dedicadas de tela/câmera (limitado a barramento SPI/I2C)", "Incompatible: No dedicated display/camera interfaces (limited to SPI/I2C bus)", "Incompatible: Sin interfaces dedicadas de pantalla/cámara (limitado a bus SPI/I2C)"));
|
|
}
|
|
} else if (answers.hardware === "usb_otg") {
|
|
if (serie.usb && String(serie.usb).includes("OTG 2.0")) {
|
|
scores[key] += 40;
|
|
reasons[key].push(r(`Porta USB 2.0 High-Speed OTG (${serie.usb}) para emulação HID, CDC e modo Host`, `USB 2.0 High-Speed OTG port (${serie.usb}) for HID emulation, CDC and Host mode`, `Puerto USB 2.0 High-Speed OTG (${serie.usb}) para emulación HID, CDC y modo Host`));
|
|
} else if (serie.usb && String(serie.usb).includes("OTG")) {
|
|
scores[key] += 30;
|
|
reasons[key].push(r(`Interface USB OTG 1.1 integrada (${serie.usb}) para emulação HID e Host`, `Integrated USB OTG 1.1 interface (${serie.usb}) for HID emulation and Host mode`, `Interfaz USB OTG 1.1 integrada (${serie.usb}) para emulación HID y Host`));
|
|
} else {
|
|
scores[key] -= 160;
|
|
alerts[key].push(r("Incompatível: Sem USB OTG nativo (USB Serial/JTAG não suporta modo Host ou HID customizado)", "Incompatible: No native USB OTG (USB Serial/JTAG does not support Host mode or custom HID)", "Incompatible: Sin USB OTG nativo (USB Serial/JTAG no admite modo Host ni HID personalizado)"));
|
|
}
|
|
} else if (answers.hardware === "can_twai") {
|
|
if (serie.can && String(serie.can).includes("CAN FD")) {
|
|
scores[key] += 40;
|
|
reasons[key].push(r("Controlador avançado CAN-FD (Flexible Data-Rate) para alta largura de banda automotiva", "Advanced CAN-FD (Flexible Data-Rate) controller for automotive high bandwidth", "Controlador avanzado CAN-FD (Flexible Data-Rate) para gran ancho de banda automotriz"));
|
|
} else if (serie.can) {
|
|
scores[key] += 30;
|
|
reasons[key].push(r(`Controlador de barramento industrial ${serie.can}`, `Industrial bus controller ${serie.can}`, `Controlador de bus industrial ${serie.can}`));
|
|
} else {
|
|
scores[key] -= 150;
|
|
alerts[key].push(r("Incompatível: Sem controlador de barramento CAN / TWAI nativo", "Incompatible: No native CAN / TWAI bus controller", "Incompatible: Sin controlador de bus CAN / TWAI nativo"));
|
|
}
|
|
} else if (answers.hardware === "dac_audio") {
|
|
if (serie.dac && serie.dac !== "Não" && serie.dac !== "No") {
|
|
scores[key] += 45;
|
|
reasons[key].push(r(`Conversores Digital-Analógico embutidos (${serie.dac}) para saída de áudio direta sem DAC externo`, `Built-in Digital-to-Analog converters (${serie.dac}) for direct audio output without external DAC`, `Conversores digital-analógico integrados (${serie.dac}) para salida de audio directa sin DAC externo`));
|
|
} else {
|
|
scores[key] -= 150;
|
|
alerts[key].push(r("Incompatível: Não possui conversor DAC interno de áudio (requer codec I2S externo)", "Incompatible: No internal audio DAC converter (requires external I2S codec)", "Incompatible: No cuenta con conversor DAC de audio interno (requiere códec I2S externo)"));
|
|
}
|
|
} else if (answers.hardware === "many_gpios") {
|
|
const gpios = parseInt(serie.gpio) || 0;
|
|
if (gpios >= 40) {
|
|
scores[key] += 35;
|
|
reasons[key].push(r(`Excepcional número de pinos disponíveis (${gpios} GPIOs)`, `Exceptional number of available pins (${gpios} GPIOs)`, `Excepcional cantidad de pines disponibles (${gpios} GPIOs)`));
|
|
} else if (gpios >= 28) {
|
|
scores[key] += 15;
|
|
reasons[key].push(r(`Quantidade moderada de portas (${gpios} GPIOs)`, `Moderate pin count (${gpios} GPIOs)`, `Cantidad moderada de puertos (${gpios} GPIOs)`));
|
|
} else {
|
|
scores[key] -= 60;
|
|
alerts[key].push(r(`Contagem restrita de pinos (${gpios} GPIOs)`, `Restricted pin count (${gpios} GPIOs)`, `Cantidad reducida de pines (${gpios} GPIOs)`));
|
|
}
|
|
} else if (answers.hardware === "standard_sensors") {
|
|
scores[key] += 10;
|
|
}
|
|
|
|
// 5. MEMÓRIA & PSRAM (memory)
|
|
if (answers.memory === "heavy_psram") {
|
|
if (serie.psram_externa && (String(serie.psram_externa).includes("64 MB") || String(serie.psram_externa).includes("1 GB"))) {
|
|
scores[key] += 40;
|
|
reasons[key].push(r(`Expansão massiva de memória PSRAM (${serie.psram_externa})`, `Massive PSRAM memory expansion (${serie.psram_externa})`, `Expansión masiva de memoria PSRAM (${serie.psram_externa})`));
|
|
} else if (serie.psram_externa && serie.psram_externa !== "Não" && serie.psram_externa !== "No") {
|
|
scores[key] += 25;
|
|
reasons[key].push(r(`Suporte a memória PSRAM externa (${serie.psram_externa})`, `Support for external PSRAM memory (${serie.psram_externa})`, `Soporte para memoria PSRAM externa (${serie.psram_externa})`));
|
|
} else {
|
|
scores[key] -= 180;
|
|
alerts[key].push(r("Incompatível: Sem suporte a memória PSRAM externa ou integrada", "Incompatible: No support for external or integrated PSRAM", "Incompatible: Sin soporte para memoria PSRAM externa o integrada"));
|
|
}
|
|
} else if (answers.memory === "moderate_sram") {
|
|
const sramMatch = serie.memoria_sram?.match(/(\d+)\s*KB/);
|
|
if (sramMatch && parseInt(sramMatch[1]) >= 380) {
|
|
scores[key] += 15;
|
|
reasons[key].push(r(`SRAM interna confortável (${serie.memoria_sram})`, `Comfortable internal SRAM (${serie.memoria_sram})`, `SRAM interna amplia (${serie.memoria_sram})`));
|
|
} else {
|
|
scores[key] += 8;
|
|
}
|
|
} else if (answers.memory === "minimal_cost") {
|
|
if (key === "ESP32-C2" || key === "ESP32-C3" || key === "ESP32-H2") {
|
|
scores[key] += 25;
|
|
reasons[key].push(r("Memória enxuta e silício compacto para menor custo unitário", "Lean memory and compact silicon for lower unit cost", "Memoria reducida y silicio compacto para menor costo unitario"));
|
|
} else {
|
|
scores[key] += 5;
|
|
}
|
|
}
|
|
|
|
// 6. INTELIGÊNCIA ARTIFICIAL (ai)
|
|
if (answers.ai === "ai_vector") {
|
|
if (serie.aceleradores_ia) {
|
|
scores[key] += 45;
|
|
reasons[key].push(r("Acelerador neural de silício e instruções vetoriais para inferência veloz", "Silicon neural accelerator and vector instructions for fast inference", "Acelerador neuronal de silicio e instrucciones vectoriales para inferencia rápida"));
|
|
} else {
|
|
scores[key] -= 180;
|
|
alerts[key].push(r("Incompatível: Sem aceleração vetorial por hardware para IA na borda", "Incompatible: No hardware vector acceleration for edge AI", "Incompatible: Sin aceleración vectorial por hardware para IA en el borde"));
|
|
}
|
|
} else if (answers.ai === "standard") {
|
|
scores[key] += 5;
|
|
}
|
|
|
|
// 7. SEGURANÇA (security)
|
|
const temECDSA = String(seg.ecdsa).startsWith("Sim") || String(seg.ecdsa).startsWith("Yes");
|
|
const temECC = String(seg.ecc).startsWith("Sim") || String(seg.ecc).startsWith("Yes");
|
|
const temAES = String(seg.aes).startsWith("AES");
|
|
const temKeyManager = seg.key_manager === "Sim" || seg.key_manager === "Yes";
|
|
const temDPA = seg.protecao_dpa === "Sim" || seg.protecao_dpa === "Yes";
|
|
|
|
if (answers.security === "key_manager_dpa") {
|
|
if (temKeyManager && temDPA) {
|
|
scores[key] += 45;
|
|
reasons[key].push(r("Key Manager em silício (chaves isoladas da CPU) e proteção contra análise de consumo DPA", "Silicon Key Manager (keys isolated from CPU) and DPA side-channel protection", "Key Manager en silicio (claves aisladas de la CPU) y protección contra análisis de consumo DPA"));
|
|
} else if (temKeyManager) {
|
|
scores[key] += 35;
|
|
reasons[key].push(r("Periférico Key Manager integrado para isolamento total de chaves privadas", "Integrated Key Manager peripheral for complete private key isolation", "Periférico Key Manager integrado para aislamiento total de claves privadas"));
|
|
} else if (temDPA) {
|
|
scores[key] += 20;
|
|
reasons[key].push(r("Proteção física DPA em hardware contra ataques de canal lateral", "Hardware DPA physical protection against side-channel attacks", "Protección física DPA en hardware contra ataques de canal lateral"));
|
|
} else {
|
|
scores[key] -= 160;
|
|
alerts[key].push(r("Incompatível: Sem Key Manager nem proteção física contra DPA", "Incompatible: No Key Manager or physical DPA protection", "Incompatible: Sin Key Manager ni protección física contra DPA"));
|
|
}
|
|
} else if (answers.security === "hardware_ecc_ecdsa") {
|
|
if (temECDSA) {
|
|
scores[key] += 40;
|
|
reasons[key].push(r(`Acelerador de assinatura digital ECDSA determinístico em hardware (${seg.ecc})`, `Deterministic hardware ECDSA digital signature accelerator (${seg.ecc})`, `Acelerador de firma digital ECDSA determinista en hardware (${seg.ecc})`));
|
|
} else if (temECC) {
|
|
scores[key] += 30;
|
|
reasons[key].push(r(`Coprocessador de curvas elípticas ECC em hardware (${seg.ecc})`, `Hardware ECC elliptic curve coprocessor (${seg.ecc})`, `Coprocesador de curvas elípticas ECC en hardware (${seg.ecc})`));
|
|
} else {
|
|
scores[key] -= 220;
|
|
alerts[key].push(r("Incompatível: Não possui acelerador de hardware para curvas elípticas (ECC/ECDSA)", "Incompatible: No hardware accelerator for elliptic curves (ECC/ECDSA)", "Incompatible: No cuenta con acelerador de hardware para curvas elípticas (ECC/ECDSA)"));
|
|
}
|
|
} else if (answers.security === "secure_boot") {
|
|
if (String(seg.secure_boot).includes("V2")) {
|
|
scores[key] += 25;
|
|
reasons[key].push(r(`Secure Boot ${seg.secure_boot} com criptografia de flash ${seg.criptografia_flash}`, `Secure Boot ${seg.secure_boot} with flash encryption ${seg.criptografia_flash}`, `Secure Boot ${seg.secure_boot} con cifrado de flash ${seg.criptografia_flash}`));
|
|
} else if (String(seg.secure_boot).includes("V1")) {
|
|
scores[key] += 5;
|
|
alerts[key].push(r("Secure Boot em esquema V1 (legado com limitações de segurança)", "Secure Boot V1 scheme (legacy with security limitations)", "Secure Boot en esquema V1 (heredado con limitaciones de seguridad)"));
|
|
} else {
|
|
scores[key] -= 80;
|
|
alerts[key].push(r("Sem Secure Boot V2 estável", "No stable Secure Boot V2", "Sin Secure Boot V2 estable"));
|
|
}
|
|
} else if (answers.security === "standard_tls") {
|
|
if (temAES) {
|
|
scores[key] += 15;
|
|
reasons[key].push(r(`Criptografia de tráfego TLS acelerada por hardware (${seg.aes})`, `Hardware-accelerated TLS traffic encryption (${seg.aes})`, `Cifrado de tráfico TLS acelerado por hardware (${seg.aes})`));
|
|
} else {
|
|
scores[key] += 5;
|
|
}
|
|
}
|
|
|
|
// 8. ECOSSISTEMA (ecosystem)
|
|
if (answers.ecosystem === "arduino_ready") {
|
|
if (String(serie.arduino_core).startsWith("Sim") || String(serie.arduino_core).startsWith("Yes")) {
|
|
const countMatch = serie.arduino_core.match(/(\d+)\s*(definições|board|definitions)/);
|
|
const count = countMatch ? parseInt(countMatch[1]) : 1;
|
|
if (count >= 20) {
|
|
scores[key] += 25;
|
|
reasons[key].push(r(`Excelente maturidade no core oficial Arduino (${count} variantes de placas)`, `Excellent maturity in the official Arduino core (${count} board variants)`, `Excelente madurez en el core oficial de Arduino (${count} variantes de placas)`));
|
|
} else {
|
|
scores[key] += 15;
|
|
reasons[key].push(r(`Suporte no core oficial Arduino (${count} placa registrada)`, `Support in the official Arduino core (${count} registered board)`, `Soporte en el core oficial de Arduino (${count} placa registrada)`));
|
|
}
|
|
} else {
|
|
scores[key] -= 200;
|
|
alerts[key].push(r("Incompatível: Ainda não suportado no core oficial do Arduino (desenvolvimento restrito ao ESP-IDF)", "Incompatible: Not yet supported in the official Arduino core (development restricted to ESP-IDF)", "Incompatible: Aún no soportado en el core oficial de Arduino (desarrollo restringido a ESP-IDF)"));
|
|
}
|
|
} else if (answers.ecosystem === "advanced_idf") {
|
|
scores[key] += 15;
|
|
}
|
|
});
|
|
|
|
return { scores, reasons, alerts };
|
|
};
|
|
|
|
// Parametric Direct Filter Matcher
|
|
const filteredChips = useMemo(() => {
|
|
if (mode !== "filter") return [];
|
|
|
|
return Object.entries(seriesData).filter(([_, serie]) => {
|
|
const seg = serie.seguranca || {};
|
|
const hasMatter = serie.matter === "Sim" || serie.matter === "Yes";
|
|
|
|
if (filters.wifi6 && !String(serie.wifi).includes("Wi-Fi 6") && !String(serie.wifi).includes("802.11ax")) return false;
|
|
if (filters.wifi5ghz && (!serie.wifi || !String(serie.wifi).includes("5 GHz"))) return false;
|
|
if (filters.bluetoothClassic && (!serie.bluetooth || (!String(serie.bluetooth).includes("Classic") && !String(serie.bluetooth).includes("Clássico")))) return false;
|
|
if (filters.leAudio && (!serie.bluetooth || !String(serie.bluetooth).includes("LE Audio"))) return false;
|
|
if (filters.matter && !hasMatter) return false;
|
|
if (filters.ethernet && !serie.ethernet && !serie.ethernet_mac) return false;
|
|
if (filters.aiAccel && !serie.aceleradores_ia) return false;
|
|
if (filters.psram && (!serie.psram_externa || serie.psram_externa === "Não" || serie.psram_externa === "No")) return false;
|
|
if (filters.mipiDisplayCamera && !serie.mipi_dsi && !serie.mipi_csi && !serie.lcd && !serie.camera) return false;
|
|
if (filters.usbOTG && (!serie.usb || !String(serie.usb).includes("OTG"))) return false;
|
|
if (filters.canBus && !serie.can) return false;
|
|
if (filters.dac && (!serie.dac || serie.dac === "Não" || serie.dac === "No")) return false;
|
|
if (filters.touch && (!serie.touch || serie.touch === "Não" || serie.touch === "No")) return false;
|
|
if (filters.arduinoReady && (!serie.arduino_core || (!String(serie.arduino_core).startsWith("Sim") && !String(serie.arduino_core).startsWith("Yes")))) return false;
|
|
if (filters.keyManager && seg.key_manager !== "Sim" && seg.key_manager !== "Yes") return false;
|
|
if (filters.eccHardware && (!seg.ecc || seg.ecc === "Não" || seg.ecc === "No")) return false;
|
|
if (filters.ecdsaHardware && (!seg.ecdsa || (!String(seg.ecdsa).startsWith("Sim") && !String(seg.ecdsa).startsWith("Yes")))) return false;
|
|
if (filters.secureBootV2 && (!seg.secure_boot || !String(seg.secure_boot).includes("V2"))) return false;
|
|
|
|
return true;
|
|
});
|
|
}, [mode, filters, seriesData]);
|
|
|
|
const { scores: allScores, reasons: allReasons, alerts: allAlerts } = useMemo(() => {
|
|
if (showResults && Object.values(answers).every(a => a !== null)) {
|
|
return calculateRecommendations();
|
|
}
|
|
return { scores: {}, reasons: {}, alerts: {} };
|
|
}, [showResults, answers, seriesData]);
|
|
|
|
const recommendations = useMemo(() => {
|
|
const entries = Object.entries(allScores)
|
|
.filter(([_, score]) => score > 0)
|
|
.sort(([, a], [, b]) => b - a);
|
|
|
|
const maxScore = entries.length > 0 ? entries[0][1] : 1;
|
|
|
|
return entries.map(([key, score]) => ({
|
|
key,
|
|
score,
|
|
percentage: Math.min(100, Math.round((score / maxScore) * 100)),
|
|
}));
|
|
}, [allScores]);
|
|
|
|
const topThree = recommendations.slice(0, 3);
|
|
const otherRecommendations = recommendations.slice(3);
|
|
|
|
const progress = ((currentStep + 1) / questions.length) * 100;
|
|
|
|
const filtrosAtivos = FILTROS.filter(({ key }) => filters[key]).length;
|
|
const limparFiltros = () => setFilters(Object.fromEntries(FILTROS.map(({ key }) => [key, false])));
|
|
|
|
// O texto das perguntas vem numerado ("1. Qual é..."); a interface já mostra a etapa.
|
|
const semNumero = (texto) => texto.replace(/^\d+\.\s*/, "");
|
|
|
|
const pergunta = questions[currentStep];
|
|
|
|
return (
|
|
<div className="site-canvas min-h-screen">
|
|
<Header />
|
|
|
|
<main id="conteudo">
|
|
<PageHero
|
|
isEn={isEn}
|
|
isEs={isEs}
|
|
trail={isEs ? [{ label: "Selector de chips" }] : isEn ? [{ label: "Chip selector" }] : [{ label: "Seletor de chip" }]}
|
|
icon={Sparkles}
|
|
label={isEs ? "Selector inteligente" : isEn ? "Smart selector" : "Seletor inteligente"}
|
|
title={isEs ? "¿Qué ESP32 elegir?" : isEn ? "Which ESP32 to choose?" : "Qual ESP32 escolher?"}
|
|
lede={isEs
|
|
? "Responda a ocho preguntas sobre su proyecto, o marque las funciones obligatorias, y vea los modelos más compatibles con su justificación técnica."
|
|
: isEn
|
|
? "Answer eight questions about your project, or select required features, to find the best matching models with technical rationale."
|
|
: "Responda a oito perguntas sobre o projeto, ou marque os recursos obrigatórios, e veja os modelos mais compatíveis com a justificativa técnica."}
|
|
>
|
|
<div role="group" aria-label={isEs ? "Modo de selección" : isEn ? "Selection mode" : "Modo de seleção"} className="flex flex-wrap gap-2">
|
|
<button type="button" onClick={() => setMode("wizard")} aria-pressed={mode === "wizard"} className="chip-btn">
|
|
<Sparkles className="h-4 w-4" aria-hidden="true" />
|
|
{isEs ? "Asistente guiado" : isEn ? "Guided wizard" : "Assistente guiado"}
|
|
</button>
|
|
<button type="button" onClick={() => setMode("filter")} aria-pressed={mode === "filter"} className="chip-btn">
|
|
<Filter className="h-4 w-4" aria-hidden="true" />
|
|
{isEs ? "Filtro por recursos" : isEn ? "Feature filter" : "Filtro por recursos"}
|
|
</button>
|
|
</div>
|
|
</PageHero>
|
|
|
|
{/* Assistente: pergunta */}
|
|
{mode === "wizard" && !showResults && !showSummary && (
|
|
<section className="shell pb-6 pt-10" aria-labelledby="pergunta-titulo">
|
|
<div className="panel mx-auto max-w-3xl p-5 sm:p-9">
|
|
<div className="flex items-center justify-between gap-4 text-sm">
|
|
<p className="font-medium text-ink">
|
|
{isEs ? (
|
|
<>Paso {currentStep + 1} de {questions.length}</>
|
|
) : isEn ? (
|
|
<>Step {currentStep + 1} of {questions.length}</>
|
|
) : (
|
|
<>Etapa {currentStep + 1} de {questions.length}</>
|
|
)}
|
|
<span className="text-muted"> · {ROTULOS_ETAPA[pergunta.id]}</span>
|
|
</p>
|
|
<span className="tabular-nums text-muted">{Math.round(progress)}%</span>
|
|
</div>
|
|
<div
|
|
role="progressbar"
|
|
aria-valuemin={0}
|
|
aria-valuemax={100}
|
|
aria-valuenow={Math.round(progress)}
|
|
aria-label={isEs ? "Progreso del cuestionario" : isEn ? "Quiz progress" : "Progresso do questionário"}
|
|
className="mt-3 h-1.5 overflow-hidden rounded-full bg-line"
|
|
>
|
|
<div className="h-full rounded-full bg-accent transition-[width] duration-500 ease-out" style={{ width: `${progress}%` }} />
|
|
</div>
|
|
|
|
<h2 id="pergunta-titulo" className="section-title mt-8">{semNumero(pergunta.question)}</h2>
|
|
<p className="mt-3 text-sm leading-relaxed text-muted sm:text-base">{pergunta.description}</p>
|
|
|
|
<div role="radiogroup" aria-labelledby="pergunta-titulo" className="mt-7 grid gap-2.5">
|
|
{pergunta.options.map((option) => {
|
|
const isSelected = currentAnswer === option.value;
|
|
return (
|
|
<button
|
|
key={option.value}
|
|
type="button"
|
|
role="radio"
|
|
aria-checked={isSelected}
|
|
onClick={() => handleAnswer(pergunta.id, option.value)}
|
|
className={`flex min-h-14 items-center gap-4 rounded-xl border p-3.5 text-left transition-colors sm:p-4 ${
|
|
isSelected ? "border-accent bg-accent-soft" : "border-line-strong bg-surface hover:bg-surface-2"
|
|
}`}
|
|
>
|
|
<span className={`grid h-10 w-10 shrink-0 place-items-center rounded-full ${isSelected ? "bg-accent text-accent-contrast" : "bg-surface-2 text-accent"}`}>
|
|
{getOptionIcon(pergunta.id, option.value)}
|
|
</span>
|
|
<span className="grow text-sm font-medium leading-snug text-ink sm:text-base">{option.label}</span>
|
|
{isSelected && <Check className="h-5 w-5 shrink-0 text-accent" aria-hidden="true" />}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
<div className="mt-8 flex flex-col-reverse items-stretch gap-3 border-t border-line pt-6 sm:flex-row sm:items-center sm:justify-between">
|
|
<button type="button" onClick={goBack} disabled={currentStep === 0} className="btn btn-ghost disabled:cursor-not-allowed disabled:opacity-40">
|
|
<ChevronLeft className="h-4 w-4" aria-hidden="true" />
|
|
{isEs ? "Volver" : isEn ? "Back" : "Voltar"}
|
|
</button>
|
|
<button type="button" onClick={resetQuiz} className="inline-flex min-h-11 items-center justify-center gap-1.5 text-sm font-medium text-muted hover:text-ink">
|
|
<RotateCcw className="h-4 w-4" aria-hidden="true" />
|
|
{isEs ? "Reiniciar" : isEn ? "Restart" : "Recomeçar"}
|
|
</button>
|
|
<button type="button" onClick={handleNext} disabled={currentAnswer === null} className="btn btn-primary disabled:cursor-not-allowed disabled:opacity-40">
|
|
{currentStep === questions.length - 1
|
|
? (isEs ? "Revisar respuestas" : isEn ? "Review answers" : "Revisar respostas")
|
|
: (isEs ? "Avanzar" : isEn ? "Next" : "Avançar")}
|
|
<ChevronRight className="h-4 w-4" aria-hidden="true" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
{/* Atalhos por aplicação */}
|
|
{mode === "wizard" && !showResults && !showSummary && currentStep === 0 && (
|
|
<section className="shell pb-6 pt-10" aria-labelledby="atalhos-titulo">
|
|
<h2 id="atalhos-titulo" className="text-base font-semibold text-ink">
|
|
{isEs ? "O comience con un perfil predefinido" : isEn ? "Or start with a ready-made profile" : "Ou comece por um perfil pronto"}
|
|
</h2>
|
|
<p className="mt-1 text-sm text-muted">
|
|
{isEs
|
|
? "Aplica las ocho respuestas de una sola vez y muestra el resultado. Puede ajustarlas después."
|
|
: isEn
|
|
? "Applies all eight answers at once and displays recommendations. You can adjust them anytime."
|
|
: "Aplica as oito respostas de uma vez e mostra o resultado. Você pode ajustar depois."}
|
|
</p>
|
|
<div className="mt-4 grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
|
{PRESETS.map((preset) => (
|
|
<button
|
|
key={preset.id}
|
|
type="button"
|
|
onClick={() => applyPreset(preset)}
|
|
className="card card-interactive group flex items-start gap-4 p-4 text-left sm:p-5"
|
|
>
|
|
<span className="grid h-10 w-10 shrink-0 place-items-center rounded-full bg-accent-soft text-accent">
|
|
{iconMap[`category_${preset.answers.category}`] || <Sparkles className="h-5 w-5" aria-hidden="true" />}
|
|
</span>
|
|
<span className="min-w-0">
|
|
<span className="block font-semibold text-ink">{preset.title}</span>
|
|
<span className="mt-0.5 block text-sm text-muted">{preset.description}</span>
|
|
<span className="mt-2 inline-flex items-center gap-1 text-sm font-medium text-accent">
|
|
{isEs ? "Ver recomendación" : isEn ? "View recommendation" : "Ver recomendação"}
|
|
<ArrowRight className="h-3.5 w-3.5 transition-transform group-hover:translate-x-0.5" aria-hidden="true" />
|
|
</span>
|
|
</span>
|
|
</button>
|
|
))}
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
{/* Assistente: revisão */}
|
|
{mode === "wizard" && showSummary && !showResults && (
|
|
<section className="shell pb-6 pt-10" aria-labelledby="resumo-titulo">
|
|
<div className="panel mx-auto max-w-4xl p-5 sm:p-9">
|
|
<h2 id="resumo-titulo" className="section-title">
|
|
{isEs ? "Revise sus respuestas" : isEn ? "Review your answers" : "Revise suas respostas"}
|
|
</h2>
|
|
<p className="mt-3 text-sm text-muted sm:text-base">
|
|
{isEn
|
|
? "Review your choices before computing compatibility for each series."
|
|
: "Confira as escolhas antes de calcular a compatibilidade com cada série."}
|
|
</p>
|
|
|
|
<ul className="mt-7 grid gap-3 md:grid-cols-2">
|
|
{questions.map((question) => {
|
|
const answer = answers[question.id];
|
|
const selectedOption = question.options.find((opt) => opt.value === answer);
|
|
return (
|
|
<li key={question.id} className="tile flex items-start justify-between gap-3">
|
|
<div className="min-w-0">
|
|
<p className="text-xs font-medium text-muted">{ROTULOS_ETAPA[question.id]}</p>
|
|
{selectedOption && <p className="mt-1 text-sm font-medium leading-snug text-ink">{selectedOption.label}</p>}
|
|
</div>
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
setCurrentStep(questions.findIndex((q) => q.id === question.id));
|
|
setCurrentAnswer(answers[question.id]);
|
|
setShowSummary(false);
|
|
}}
|
|
className="grid h-11 w-11 shrink-0 place-items-center rounded-full text-muted transition-colors hover:bg-surface hover:text-accent"
|
|
aria-label={isEs ? `Editar respuesta: ${ROTULOS_ETAPA[question.id]}` : isEn ? `Edit answer: ${ROTULOS_ETAPA[question.id]}` : `Editar resposta: ${ROTULOS_ETAPA[question.id]}`}
|
|
>
|
|
<Edit2 className="h-4 w-4" aria-hidden="true" />
|
|
</button>
|
|
</li>
|
|
);
|
|
})}
|
|
</ul>
|
|
|
|
<div className="mt-8 flex flex-col-reverse gap-3 border-t border-line pt-6 sm:flex-row sm:justify-between">
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
setCurrentStep(questions.length - 1);
|
|
setCurrentAnswer(answers[questions[questions.length - 1].id]);
|
|
setShowSummary(false);
|
|
}}
|
|
className="btn btn-ghost"
|
|
>
|
|
<ChevronLeft className="h-4 w-4" aria-hidden="true" />
|
|
{isEs ? "Volver" : isEn ? "Back" : "Voltar"}
|
|
</button>
|
|
<button type="button" onClick={() => setShowResults(true)} className="btn btn-primary">
|
|
<Sparkles className="h-4 w-4" aria-hidden="true" />
|
|
{isEs ? "Ver recomendaciones" : isEn ? "View recommendations" : "Ver recomendações"}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
{/* Assistente: resultado */}
|
|
{mode === "wizard" && showResults && recommendations.length > 0 && (
|
|
<section className="shell pb-6 pt-10" aria-labelledby="resultado-titulo">
|
|
<div className="flex flex-col justify-between gap-4 sm:flex-row sm:items-end">
|
|
<div>
|
|
<h2 id="resultado-titulo" className="section-title">
|
|
{isEs ? "Sus recomendaciones" : isEn ? "Your recommendations" : "Suas recomendações"}
|
|
</h2>
|
|
<p role="status" className="mt-2 text-sm text-muted">
|
|
{isEn
|
|
? `${recommendations.length} matching models, ranked from most to least recommended.`
|
|
: `${recommendations.length} modelos compatíveis, do mais ao menos indicado.`}
|
|
</p>
|
|
</div>
|
|
<div className="flex flex-wrap gap-2">
|
|
<button type="button" onClick={() => { setShowResults(false); setShowSummary(true); }} className="btn btn-ghost btn-sm">
|
|
<Edit2 className="h-4 w-4" aria-hidden="true" />
|
|
{isEs ? "Ajustar respuestas" : isEn ? "Adjust answers" : "Ajustar respostas"}
|
|
</button>
|
|
<button type="button" onClick={resetQuiz} className="btn btn-ghost btn-sm">
|
|
<RotateCcw className="h-4 w-4" aria-hidden="true" />
|
|
{isEs ? "Reiniciar" : isEn ? "Restart" : "Recomeçar"}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<ol className="mt-8 grid gap-4 lg:grid-cols-3">
|
|
{topThree.map((rec, index) => {
|
|
const seriesKey = rec.key;
|
|
const serie = seriesData[seriesKey];
|
|
const matchReasons = allReasons[seriesKey] || [];
|
|
const chipAlerts = allAlerts[seriesKey] || [];
|
|
const primeiro = index === 0;
|
|
const rankLabel = isEn
|
|
? `${index + 1}${index === 0 ? "st" : index === 1 ? "nd" : index === 2 ? "rd" : "th"} recommendation`
|
|
: `${index + 1}ª recomendação`;
|
|
return (
|
|
<li key={seriesKey} className={`card flex flex-col p-5 sm:p-6 ${primeiro ? "border-accent ring-2 ring-accent/20" : ""}`}>
|
|
<div className="flex items-center justify-between gap-3">
|
|
<span className={`chip ${primeiro ? "chip-accent" : ""}`}>
|
|
{primeiro && <Award className="h-3.5 w-3.5" aria-hidden="true" />}
|
|
{rankLabel}
|
|
</span>
|
|
<span className="text-sm text-muted">
|
|
{isEs ? "Afinidad" : isEn ? "Match" : "Afinidade"} <strong className="font-semibold text-ink">{rec.percentage}%</strong>
|
|
</span>
|
|
</div>
|
|
|
|
<h3 className="mt-4 font-display text-2xl font-semibold tracking-[-0.035em] text-ink">{seriesKey}</h3>
|
|
<p className="mt-1 line-clamp-2 text-sm text-muted">{serie.descricao}</p>
|
|
|
|
<div className="mt-5 h-1.5 overflow-hidden rounded-full bg-line" aria-hidden="true">
|
|
<div className="h-full rounded-full bg-accent" style={{ width: `${rec.percentage}%` }} />
|
|
</div>
|
|
|
|
<h4 className="mt-6 text-sm font-semibold text-ink">
|
|
{isEs ? "Por qué encaja" : isEn ? "Why it fits" : "Por que combina"}
|
|
</h4>
|
|
<ul className="mt-2 space-y-2">
|
|
{matchReasons.slice(0, 3).map((reason) => (
|
|
<li key={reason} className="flex items-start gap-2 text-sm leading-relaxed text-ink-soft">
|
|
<Check className="mt-1 h-3.5 w-3.5 shrink-0 text-ok" aria-hidden="true" />
|
|
{reason}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
{chipAlerts.length > 0 && (
|
|
<div className="callout callout-warn mt-5">
|
|
<p className="flex items-center gap-1.5 font-semibold text-ink">
|
|
<AlertTriangle className="h-4 w-4 text-gold" aria-hidden="true" />
|
|
{isEs ? "Puntos de atención" : isEn ? "Attention points" : "Pontos de atenção"}
|
|
</p>
|
|
<ul className="mt-1.5 list-disc space-y-1 pl-5">
|
|
{chipAlerts.slice(0, 2).map((alert) => (
|
|
<li key={alert}>{alert}</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
)}
|
|
|
|
<dl className="mt-5 grid grid-cols-2 gap-2">
|
|
{[
|
|
[isEs ? "Arquitectura" : isEn ? "Architecture" : "Arquitetura", serie.arquitetura.split(" ")[0]],
|
|
["Clock", serie.frequencia],
|
|
["GPIO", isEs ? `${serie.gpio} pines` : isEn ? `${serie.gpio} pins` : `${serie.gpio} pinos`],
|
|
["SRAM", serie.memoria_sram],
|
|
].map(([rotulo, valor]) => (
|
|
<div key={rotulo} className="tile !p-3">
|
|
<dt className="text-xs text-muted">{rotulo}</dt>
|
|
<dd className="mt-0.5 truncate text-sm font-medium text-ink">{valor}</dd>
|
|
</div>
|
|
))}
|
|
</dl>
|
|
|
|
<Link href={getLocalizedHref(`/series/${seriesKey}`, currentLocale)} className={`btn mt-6 w-full ${primeiro ? "btn-primary" : "btn-ghost"}`}>
|
|
{isEs ? `Abrir la ficha de ${seriesKey}` : isEn ? `Open ${seriesKey} datasheet` : `Abrir a ficha do ${seriesKey}`}
|
|
<ArrowUpRight className="h-4 w-4" aria-hidden="true" />
|
|
</Link>
|
|
</li>
|
|
);
|
|
})}
|
|
</ol>
|
|
|
|
<div className="panel mt-10 p-5 sm:p-8">
|
|
<div className="flex flex-col justify-between gap-3 sm:flex-row sm:items-center">
|
|
<div>
|
|
<h3 className="font-display text-xl font-semibold tracking-[-0.03em] text-ink">
|
|
{isEs ? "Los finalistas lado a lado" : isEn ? "The finalists side by side" : "Os finalistas lado a lado"}
|
|
</h3>
|
|
<p className="mt-1 text-sm text-muted">
|
|
{isEn
|
|
? "Key specifications of the top three recommendations."
|
|
: "As principais especificações das três primeiras recomendações."}
|
|
</p>
|
|
</div>
|
|
<Link href={getLocalizedHref(`/comparacao?chips=${topThree.map((t) => t.key).join(",")}`, currentLocale)} className="inline-flex min-h-11 items-center gap-1.5 text-sm font-medium text-accent hover:text-accent-strong">
|
|
{isEs ? "Abrir en el comparador completo" : isEn ? "Open in full comparison" : "Abrir no comparador completo"}
|
|
<ArrowRight className="h-4 w-4" aria-hidden="true" />
|
|
</Link>
|
|
</div>
|
|
|
|
<div className="mt-6 overflow-x-auto">
|
|
<table className="w-full min-w-[32rem] border-collapse text-left text-sm">
|
|
<caption className="sr-only">
|
|
{isEs ? "Comparación de las tres series recomendadas" : isEn ? "Comparison of the three recommended series" : "Comparação das três séries recomendadas"}
|
|
</caption>
|
|
<thead>
|
|
<tr className="border-b border-line">
|
|
<th scope="col" className="px-3 py-3 font-medium text-muted">
|
|
{isEs ? "Parámetro" : isEn ? "Parameter" : "Parâmetro"}
|
|
</th>
|
|
{topThree.map((rec) => (
|
|
<th key={rec.key} scope="col" className="px-3 py-3 font-display text-base font-semibold text-ink">{rec.key}</th>
|
|
))}
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-line text-ink-soft">
|
|
{[
|
|
[isEs ? "Arquitectura" : isEn ? "Architecture" : "Arquitetura", (c) => c.arquitetura],
|
|
["Clock", (c) => c.frequencia],
|
|
["Wi-Fi", (c) => c.wifi],
|
|
["Bluetooth", (c) => c.bluetooth],
|
|
["Matter / 802.15.4", (c) => ((c.matter === "Sim" || c.matter === "Yes" || c.matter === "Sí") ? (isEs ? "Sí" : isEn ? "Yes" : "Sim") : (isEs || isEn ? "No" : "Não"))],
|
|
[isEs ? "Aceleración de IA" : isEn ? "AI Acceleration" : "Aceleração de IA", (c) => (c.aceleradores_ia ? (isEs ? "Vectorial" : isEn ? "Vector" : "Vetorial") : (isEs || isEn ? "No" : "Não"))],
|
|
[isEs ? "PSRAM externa" : isEn ? "External PSRAM" : "PSRAM externa", (c) => (c.psram_externa && c.psram_externa !== "Não" && c.psram_externa !== "No" ? c.psram_externa : (isEs || isEn ? "No" : "Não"))],
|
|
["Arduino Core", (c) => ((String(c.arduino_core).startsWith("Sim") || String(c.arduino_core).startsWith("Yes") || String(c.arduino_core).startsWith("Sí")) ? (isEs ? "Soportado" : isEn ? "Supported" : "Suportado") : (isEs ? "Solo ESP-IDF" : isEn ? "ESP-IDF only" : "Só ESP-IDF"))],
|
|
].map(([rotulo, obter]) => (
|
|
<tr key={rotulo}>
|
|
<th scope="row" className="px-3 py-3 align-top font-medium text-muted">{rotulo}</th>
|
|
{topThree.map((rec) => (
|
|
<td key={rec.key} className="px-3 py-3 align-top">{obter(seriesData[rec.key])}</td>
|
|
))}
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
{otherRecommendations.length > 0 && (
|
|
<div className="mt-10">
|
|
<h3 className="font-display text-xl font-semibold tracking-[-0.03em] text-ink">
|
|
{isEs ? "Otras alternativas evaluadas" : isEn ? "Other evaluated alternatives" : "Outras alternativas avaliadas"}
|
|
</h3>
|
|
<ul className="mt-4 grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
|
|
{otherRecommendations.map((rec) => (
|
|
<li key={rec.key}>
|
|
<Link href={getLocalizedHref(`/series/${rec.key}`, currentLocale)} className="card card-interactive flex items-center justify-between gap-3 px-4 py-3.5">
|
|
<span className="font-display text-lg font-semibold tracking-[-0.03em] text-ink">{rec.key}</span>
|
|
<span className="text-sm text-muted">{rec.percentage}%</span>
|
|
</Link>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
)}
|
|
</section>
|
|
)}
|
|
|
|
{mode === "wizard" && showResults && recommendations.length === 0 && (
|
|
<section className="shell pb-6 pt-10">
|
|
<div className="card mx-auto max-w-2xl px-6 py-14 text-center">
|
|
<HelpCircle className="mx-auto h-10 w-10 text-gold" aria-hidden="true" />
|
|
<h2 className="mt-4 font-display text-2xl font-semibold text-ink">
|
|
{isEs ? "Ningún chip cumple con todos los requisitos" : isEn ? "No chip meets all requirements" : "Nenhum chip atende a todos os requisitos"}
|
|
</h2>
|
|
<p className="mx-auto mt-2 max-w-md text-sm text-muted">
|
|
{isEn
|
|
? "This combination has criteria that no series satisfies simultaneously. Try relaxing one of the answers."
|
|
: "Esta combinação tem critérios que nenhuma série cumpre ao mesmo tempo. Flexibilize uma das respostas e tente de novo."}
|
|
</p>
|
|
<div className="mt-6 flex flex-col justify-center gap-3 sm:flex-row">
|
|
<button type="button" onClick={() => { setShowResults(false); setShowSummary(true); }} className="btn btn-primary">
|
|
<Edit2 className="h-4 w-4" aria-hidden="true" />
|
|
{isEs ? "Ajustar respuestas" : isEn ? "Adjust answers" : "Ajustar respostas"}
|
|
</button>
|
|
<button type="button" onClick={resetQuiz} className="btn btn-ghost">
|
|
<RotateCcw className="h-4 w-4" aria-hidden="true" />
|
|
{isEs ? "Reiniciar" : isEn ? "Restart" : "Recomeçar"}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
{/* Filtro por recursos */}
|
|
{mode === "filter" && (
|
|
<section className="shell pb-6 pt-10" aria-labelledby="filtro-titulo">
|
|
<div className="panel p-5 sm:p-8">
|
|
<div className="flex flex-col justify-between gap-3 sm:flex-row sm:items-end">
|
|
<div className="max-w-xl">
|
|
<h2 id="filtro-titulo" className="section-title">
|
|
{isEs ? "Marque lo que es obligatorio" : isEn ? "Check required features" : "Marque o que é obrigatório"}
|
|
</h2>
|
|
<p className="mt-2 text-sm text-muted">
|
|
{isEn
|
|
? "Only series that have all selected features are kept."
|
|
: "Só ficam as séries que têm todos os recursos marcados."}
|
|
</p>
|
|
</div>
|
|
{filtrosAtivos > 0 && (
|
|
<button type="button" onClick={limparFiltros} className="inline-flex min-h-11 items-center gap-1.5 text-sm font-medium text-accent hover:text-accent-strong">
|
|
<RotateCcw className="h-4 w-4" aria-hidden="true" />
|
|
{isEn
|
|
? `Clear ${filtrosAtivos} ${filtrosAtivos === 1 ? "filter" : "filters"}`
|
|
: `Limpar ${filtrosAtivos} ${filtrosAtivos === 1 ? "filtro" : "filtros"}`}
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
<div className="mt-6 flex flex-wrap gap-2" role="group" aria-label={isEs ? "Recursos" : isEn ? "Features" : "Recursos"}>
|
|
{FILTROS.map(({ key, label }) => (
|
|
<button key={key} type="button" onClick={() => setFilters((prev) => ({ ...prev, [key]: !prev[key] }))} aria-pressed={filters[key]} className="chip-btn">
|
|
{filters[key] && <Check className="h-4 w-4" aria-hidden="true" />}
|
|
{label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<h3 role="status" aria-live="polite" className="mt-8 text-base font-semibold text-ink">
|
|
{isEn
|
|
? `${filteredChips.length} of ${Object.keys(seriesData).length} series match`
|
|
: `${filteredChips.length} de ${Object.keys(seriesData).length} séries compatíveis`}
|
|
</h3>
|
|
|
|
{filteredChips.length > 0 ? (
|
|
<div className="mt-4 grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
{filteredChips.map(([seriesKey, serie]) => (
|
|
<Link key={seriesKey} href={getLocalizedHref(`/series/${seriesKey}`, currentLocale)} className="card card-interactive group flex flex-col p-5 sm:p-6">
|
|
<h4 className="font-display text-2xl font-semibold tracking-[-0.035em] text-ink">{seriesKey}</h4>
|
|
<p className="mt-1 line-clamp-2 min-h-[2.9rem] text-sm text-muted">{serie.descricao}</p>
|
|
<dl className="mt-4 divide-y divide-line border-y border-line text-sm">
|
|
{[
|
|
["Wi-Fi", serie.wifi],
|
|
["Matter / Thread", (serie.matter === "Sim" || serie.matter === "Yes" || serie.matter === "Sí") ? (isEs ? "Sí" : isEn ? "Yes" : "Sim") : (isEs || isEn ? "No" : "Não")],
|
|
["Clock", serie.frequencia],
|
|
["GPIO", isEs ? `${serie.gpio} pines` : isEn ? `${serie.gpio} pins` : `${serie.gpio} pinos`],
|
|
["PSRAM", (serie.psram_externa && serie.psram_externa !== "Não" && serie.psram_externa !== "No") ? serie.psram_externa : (isEs || isEn ? "No" : "Não")],
|
|
].map(([rotulo, valor]) => (
|
|
<div key={rotulo} className="flex items-baseline justify-between gap-4 py-2">
|
|
<dt className="text-muted">{rotulo}</dt>
|
|
<dd className="truncate text-right font-medium text-ink">{valor}</dd>
|
|
</div>
|
|
))}
|
|
</dl>
|
|
<span className="mt-4 inline-flex items-center gap-1 text-sm font-medium text-accent">
|
|
{isEs ? "Abrir ficha" : isEn ? "Open datasheet" : "Abrir ficha"}
|
|
<ArrowUpRight className="h-4 w-4 transition-transform group-hover:-translate-y-0.5 group-hover:translate-x-0.5" aria-hidden="true" />
|
|
</span>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="card mt-4 px-6 py-14 text-center">
|
|
<HelpCircle className="mx-auto h-10 w-10 text-gold" aria-hidden="true" />
|
|
<h4 className="mt-4 font-display text-xl font-semibold text-ink">
|
|
{isEs ? "Ninguna serie cumple con todos los filtros" : isEn ? "No series matches all filters" : "Nenhuma série atende a todos os filtros"}
|
|
</h4>
|
|
<p className="mx-auto mt-2 max-w-md text-sm text-muted">
|
|
{isEn
|
|
? "Uncheck more restrictive features, like 5 GHz Wi-Fi together with Ethernet MAC."
|
|
: "Desmarque os recursos mais restritivos, como Wi-Fi 5 GHz junto com Ethernet MAC."}
|
|
</p>
|
|
<button type="button" onClick={limparFiltros} className="btn btn-primary mt-6">
|
|
{isEs ? "Limpiar filtros" : isEn ? "Clear filters" : "Limpar filtros"}
|
|
</button>
|
|
</div>
|
|
)}
|
|
</section>
|
|
)}
|
|
|
|
<section className="shell section" aria-labelledby="proximos-titulo">
|
|
<h2 id="proximos-titulo" className="section-title">
|
|
{isEs ? "Próximos pasos" : isEn ? "Next steps" : "Próximos passos"}
|
|
</h2>
|
|
<div className="mt-8 grid gap-4 md:grid-cols-3">
|
|
{[
|
|
{
|
|
href: "/catalogo",
|
|
icon: ShoppingBag,
|
|
titulo: isEs ? "Catálogo de placas" : isEn ? "Board catalog" : "Catálogo de placas",
|
|
texto: isEs
|
|
? "DevKits y módulos reales, con patillaje y enlace a la tienda oficial."
|
|
: isEn
|
|
? "Real DevKits and modules, with pinout and official store links."
|
|
: "DevKits e módulos reais, com pinagem e link para a loja."
|
|
},
|
|
{
|
|
href: "/comparacao",
|
|
icon: SlidersHorizontal,
|
|
titulo: isEs ? "Comparador lado a lado" : isEn ? "Side-by-side comparison" : "Comparador lado a lado",
|
|
texto: isEs
|
|
? "Frecuencia, GPIOs, ADC/DAC, consumo y seguridad en una tabla."
|
|
: isEn
|
|
? "Frequency, GPIOs, ADC/DAC, power consumption, and security in one table."
|
|
: "Frequência, GPIOs, ADC/DAC, consumo e segurança em uma tabela."
|
|
},
|
|
{
|
|
href: "/glossario",
|
|
icon: BookOpen,
|
|
titulo: isEs ? "Glosario técnico" : isEn ? "Technical glossary" : "Glossário técnico",
|
|
texto: isEs
|
|
? "¿Dudas sobre ULP, SRAM RTC, JTAG o Matter? Consulte las definiciones."
|
|
: isEn
|
|
? "Questions about ULP, RTC SRAM, JTAG or Matter? Check definitions."
|
|
: "Dúvida sobre ULP, SRAM RTC, JTAG ou Matter? Veja as definições."
|
|
},
|
|
].map(({ href, icon: Icon, titulo, texto }) => (
|
|
<Link key={href} href={getLocalizedHref(href, currentLocale)} className="card card-interactive group flex flex-col p-5 sm:p-6">
|
|
<span className="grid h-10 w-10 place-items-center rounded-full bg-accent-soft text-accent">
|
|
<Icon className="h-5 w-5" aria-hidden="true" />
|
|
</span>
|
|
<h3 className="mt-4 font-display text-xl font-semibold tracking-[-0.03em] text-ink">{titulo}</h3>
|
|
<p className="mt-2 flex-1 text-sm leading-relaxed text-muted">{texto}</p>
|
|
<span className="mt-4 inline-flex items-center gap-1 text-sm font-medium text-accent">
|
|
{isEs ? "Abrir" : isEn ? "Open" : "Abrir"}
|
|
<ArrowRight className="h-4 w-4 transition-transform group-hover:translate-x-0.5" aria-hidden="true" />
|
|
</span>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<Footer />
|
|
</div>
|
|
);
|
|
}
|