"use client";
import { useState, useMemo } from "react";
import { usePathname } from "next/navigation";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import PageHero from "@/components/PageHero";
import SectionHead from "@/components/SectionHead";
import * as componentesPt from "@/lib/componentRegistryData";
import * as componentesEn from "@/lib/data/en/componentes";
import * as componentesEs from "@/lib/data/es/componentes";
import * as componentesFr from "@/lib/data/fr/componentes";
import * as componentesDe from "@/lib/data/de/componentes";
import {
ArrowUpRight,
Box,
Brain,
Check,
CircuitBoard,
Cloud,
Copy,
Cpu,
Download,
HelpCircle,
Home,
Layers,
Monitor,
Plus,
Search,
ShieldCheck,
Terminal,
Trash2,
Wrench,
X,
} from "lucide-react";
const ICONES_CATEGORIA = {
ui_display: Monitor,
cloud_conectividade: Cloud,
audio_voz_ia: Brain,
smart_home_mesh: Home,
drivers_perifericos: Cpu,
bsp_placas: CircuitBoard,
sistema_utilitarios: Wrench,
};
export default function ComponentRegistryPage() {
const pathname = usePathname();
const isEn = pathname?.startsWith("/en");
const isEs = pathname?.startsWith("/es");
const isFr = pathname?.startsWith("/fr");
const isDe = pathname?.startsWith("/de");
const { CATEGORIAS_COMPONENTES, COMPONENTES_REGISTRY, GUIA_COMPONENT_MANAGER } = isEs ? componentesEs : isEn ? componentesEn : isFr ? componentesFr : isDe ? componentesDe : componentesPt;
const [busca, setBusca] = useState("");
const [categoriaAtiva, setCategoriaAtiva] = useState("todos");
const [chipFiltro, setChipFiltro] = useState("todos");
const [copiadoId, setCopiadoId] = useState(null);
const [manifestoCopiado, setManifestoCopiado] = useState(false);
// Componentes selecionados no construtor de idf_component.yml
const [componentesSelecionados, setComponentesSelecionados] = useState([
"espressif-esp_lvgl_port",
"espressif-led_indicator"
]);
// Lista de chips únicos para filtro
const chipsDisponiveis = ["todos", "ESP32", "ESP32-S3", "ESP32-C6", "ESP32-P4", "ESP32-H2", "ESP32-C3"];
// Filtragem dos componentes
const componentesFiltrados = useMemo(() => {
return COMPONENTES_REGISTRY.filter((comp) => {
const matchCategoria = categoriaAtiva === "todos" || comp.categoria === categoriaAtiva;
const matchChip = chipFiltro === "todos" || (comp.chips && comp.chips.includes(chipFiltro));
const matchBusca =
busca.trim() === "" ||
comp.name.toLowerCase().includes(busca.toLowerCase()) ||
comp.namespace.toLowerCase().includes(busca.toLowerCase()) ||
comp.titulo.toLowerCase().includes(busca.toLowerCase()) ||
comp.descricao.toLowerCase().includes(busca.toLowerCase()) ||
comp.tags.some((t) => t.toLowerCase().includes(busca.toLowerCase()));
return matchCategoria && matchChip && matchBusca;
});
}, [busca, categoriaAtiva, chipFiltro, COMPONENTES_REGISTRY]);
// Manipuladores de cópia
const copiarComando = (id, comando) => {
navigator.clipboard.writeText(comando);
setCopiadoId(id);
setTimeout(() => setCopiadoId(null), 2000);
};
const toggleSelecaoManifesto = (id) => {
setComponentesSelecionados((prev) =>
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
);
};
// Geração dinâmica do manifesto YAML
const manifestoYaml = useMemo(() => {
const selecionados = COMPONENTES_REGISTRY.filter((c) =>
componentesSelecionados.includes(c.id)
);
if (selecionados.length === 0) {
return isEs
? `# No hay componentes seleccionados.\n# Haga clic en "+ Agregar al manifiesto" en las tarjetas de abajo.`
: isEn
? `# No components selected.\n# Click "+ Add to Manifest" on the cards below.`
: isFr
? `# Aucun composant sélectionné.\n# Cliquez sur "+ Ajouter au manifeste" sur les cartes ci-dessous.`
: isDe
? `# Keine Komponenten ausgewählt.\n# Klicken Sie auf den Karten unten auf "+ Zum Manifest hinzufügen".`
: `# Nenhum componente selecionado.\n# Clique em "+ Adicionar ao Manifesto" nos cards abaixo.`;
}
let yaml = isEs
? `# main/idf_component.yml\n# Generado mediante ESPDocs Component Hub (https://espdocs.cienciaembarcada.com.br/es/componentes)\n\ndependencies:\n`
: isEn
? `# main/idf_component.yml\n# Generated via ESPDocs Component Hub (https://espdocs.cienciaembarcada.com.br/en/components)\n\ndependencies:\n`
: isFr
? `# main/idf_component.yml\n# Généré via ESPDocs Component Hub (https://espdocs.cienciaembarcada.com.br/fr/composants)\n\ndependencies:\n`
: isDe
? `# main/idf_component.yml\n# Erstellt über den ESPDocs Component Hub (https://espdocs.cienciaembarcada.com.br/de/komponenten)\n\ndependencies:\n`
: `# main/idf_component.yml\n# Gerado via ESPDocs Component Hub (https://espdocs.cienciaembarcada.com.br/componentes)\n\ndependencies:\n`;
selecionados.forEach((c) => {
yaml += ` ${c.namespace}/${c.name}: "${c.versao}"\n`;
});
return yaml;
}, [componentesSelecionados, COMPONENTES_REGISTRY, isEn, isEs, isFr, isDe]);
const copiarManifesto = () => {
navigator.clipboard.writeText(manifestoYaml);
setManifestoCopiado(true);
setTimeout(() => setManifestoCopiado(false), 2000);
};
const baixarManifesto = () => {
const blob = new Blob([manifestoYaml], { type: "text/yaml;charset=utf-8" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "idf_component.yml";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
const limparFiltros = () => {
setBusca("");
setCategoriaAtiva("todos");
setChipFiltro("todos");
};
const totalFiltros = (busca.trim() ? 1 : 0) + (categoriaAtiva !== "todos" ? 1 : 0) + (chipFiltro !== "todos" ? 1 : 0);
return (
{isEs ? "Abrir el registro oficial" : isEn ? "Open official registry" : isFr ? "Ouvrir le registre officiel" : isDe ? "Offizielles Repository öffnen" : "Abrir o registro oficial"}
{isEs ? "(abre en una nueva pestaña)" : isEn ? "(opens in a new tab)" : isFr ? "(ouvre un nouvel onglet)" : isDe ? "(öffnet in neuem Tab)" : "(abre em nova aba)"}
{isEs ? "Generar idf_component.yml" : isEn ? "Generate idf_component.yml" : isFr ? "Générer idf_component.yml" : isDe ? "idf_component.yml erstellen" : "Gerar idf_component.yml"}
{isEs ? "Buscar por nombre, namespace o etiqueta" : isEn ? "Search by name, namespace, or tag" : isFr ? "Rechercher par nom, namespace ou tag" : isDe ? "Nach Name, Namespace oder Tag suchen" : "Buscar por nome, namespace ou tag"}
setBusca(e.target.value)}
placeholder={isEs ? "Buscar por nombre, namespace o etiqueta" : isEn ? "Search by name, namespace, or tag" : isFr ? "Rechercher par nom, namespace ou tag" : isDe ? "Nach Name, Namespace oder Tag suchen" : "Buscar por nome, namespace ou tag"}
autoComplete="off"
className="field pl-12 pr-4 text-base"
/>
{isEs ? "Categoría" : isEn ? "Category" : isFr ? "Catégorie" : isDe ? "Kategorie" : "Categoria"}
{CATEGORIAS_COMPONENTES.map((cat) => {
const Icone = ICONES_CATEGORIA[cat.id] ?? Layers;
const contagem = cat.id === "todos" ? COMPONENTES_REGISTRY.length : COMPONENTES_REGISTRY.filter((c) => c.categoria === cat.id).length;
return (
setCategoriaAtiva(cat.id)} aria-pressed={categoriaAtiva === cat.id} className="chip-btn">
{cat.id === "todos" ? (isEs ? "Todos" : isEn ? "All" : isFr ? "Tous" : isDe ? "Alle" : "Todos") : cat.nome.replace(/\s*\(.*\)/, "").split(",")[0].split(" &")[0]}
{contagem}
);
})}
{isEs ? "Chip compatible" : isEn ? "Compatible chip" : isFr ? "Puce compatible" : isDe ? "Kompatibler Chip" : "Chip compatível"}
{chipsDisponiveis.map((chip) => (
setChipFiltro(chip)} aria-pressed={chipFiltro === chip} className="chip-btn">
{chip === "todos" ? (isEs ? "Todos" : isEn ? "All" : isFr ? "Tous" : isDe ? "Alle" : "Todos") : chip}
))}
{componentesFiltrados.length} {componentesFiltrados.length === 1 ? (isEs ? "componente" : isEn ? "component" : isFr ? "composant" : isDe ? "Komponente" : "componente") : (isEs ? "componentes" : isEn ? "components" : isFr ? "composants" : isDe ? "Komponenten" : "componentes")}
{componentesFiltrados.length === 0 ? (
{isEs ? "No se encontraron componentes" : isEn ? "No components found" : isFr ? "Aucun composant trouvé" : isDe ? "Keine Komponenten gefunden" : "Nenhum componente encontrado"}
{isEs
? "Busque por un término más general o regrese a todas las categorías."
: isEn
? "Search for a more general term or return to all categories."
: isFr
? "Recherchez un terme plus général ou revenez à toutes les catégories."
: isDe
? "Suchen Sie nach einem allgemeineren Begriff oder kehren Sie zu allen Kategorien zurück."
: "Busque por um termo mais geral ou volte para todas as categorias."}
{isEs ? "Limpiar filtros" : isEn ? "Clear filters" : isFr ? "Effacer les filtres" : isDe ? "Filter zurücksetzen" : "Limpar filtros"}
) : (
{componentesFiltrados.map((comp) => {
const selecionado = componentesSelecionados.includes(comp.id);
const cliCmd = `idf.py add-dependency "${comp.namespace}/${comp.name}${comp.versao}"`;
const foiCopiado = copiadoId === comp.id;
return (
{comp.namespace}/
{comp.oficial && (
{isEs ? "Oficial" : isEn ? "Official" : isFr ? "Officiel" : isDe ? "Offiziell" : "Oficial"}
)}
{comp.name}
{comp.titulo}
{comp.versao}
{comp.descricao}
{comp.tags.map((tag) => (
{tag}
))}
copiarComando(comp.id, cliCmd)}
className="code-panel flex w-full min-h-11 items-center justify-between gap-3 rounded-lg px-3 py-2 text-left font-mono text-xs transition-colors hover:border-white/30"
aria-label={isEs ? `Copiar comando para agregar ${comp.name}` : isEn ? `Copy command to add ${comp.name}` : isFr ? `Copier la commande pour ajouter ${comp.name}` : isDe ? `Befehl zum Hinzufügen von ${comp.name} kopieren` : `Copiar comando para adicionar ${comp.name}`}
>
{cliCmd}
{foiCopiado ? : }
{foiCopiado ? (isEs ? "Comando copiado" : isEn ? "Command copied" : isFr ? "Commande copiée" : isDe ? "Befehl kopiert" : "Comando copiado") : ""}
Chips:
{comp.chips.join(", ")}
toggleSelecaoManifesto(comp.id)}
aria-pressed={selecionado}
className={`btn btn-sm ${selecionado ? "btn-primary" : "btn-ghost"}`}
>
{selecionado ? : }
{selecionado ? (isEs ? "En manifiesto" : isEn ? "In manifest" : isFr ? "Dans le manifeste" : isDe ? "Im Manifest" : "No manifesto") : (isEs ? "Agregar al manifiesto" : isEn ? "Add to manifest" : isFr ? "Ajouter au manifeste" : isDe ? "Zum Manifest hinzufügen" : "Adicionar ao manifesto")}
);
})}
)}
{isEs ? "Generador de manifiesto" : isEn ? "Manifest generator" : isFr ? "Générateur de manifeste" : isDe ? "Manifest-Generator" : "Gerador de manifesto"}
{isEs ? "Su idf_component.yml" : isEn ? "Your idf_component.yml" : isFr ? "Votre idf_component.yml" : isDe ? "Ihre idf_component.yml" : "Seu idf_component.yml"}
{isEs ? (
<>Guarde el archivo en la carpeta main/ del proyecto y ESP-IDF descargará todas las bibliotecas durante la compilación.>
) : isEn ? (
<>Save this file in your project's main/ folder and ESP-IDF will download all dependencies during build.>
) : isFr ? (
<>Enregistrez le fichier dans le dossier main/ du projet et ESP-IDF téléchargera toutes les bibliothèques lors de la compilation.>
) : isDe ? (
<>Speichern Sie die Datei im Ordner main/ Ihres Projekts, und ESP-IDF lädt beim Build alle Abhängigkeiten herunter.>
) : (
<>Salve o arquivo na pasta main/ do projeto e o ESP-IDF baixa todas as bibliotecas na compilação.>
)}
{manifestoCopiado ? : }
{manifestoCopiado ? (isEs ? "Copiado" : isEn ? "Copied" : isFr ? "Copié" : isDe ? "Kopiert" : "Copiado") : (isEs ? "Copiar YAML" : isEn ? "Copy YAML" : isFr ? "Copier le YAML" : isDe ? "YAML kopieren" : "Copiar YAML")}
{isEs ? "Descargar .yml" : isEn ? "Download .yml" : isFr ? "Télécharger .yml" : isDe ? ".yml herunterladen" : "Baixar .yml"}
{componentesSelecionados.length > 0 && (
setComponentesSelecionados([])} className="btn btn-ghost btn-sm">
{isEs ? "Limpiar" : isEn ? "Clear" : isFr ? "Effacer" : isDe ? "Leeren" : "Limpar"}
)}
{componentesSelecionados.length > 0 && (
{componentesSelecionados.map((id) => {
const c = COMPONENTES_REGISTRY.find((item) => item.id === id);
if (!c) return null;
return (
{c.name}
toggleSelecaoManifesto(id)}
className="-my-2 grid h-11 w-11 place-items-center rounded-full hover:bg-accent/15"
aria-label={isEs ? `Eliminar ${c.name} del manifiesto` : isEn ? `Remove ${c.name} from manifest` : isFr ? `Retirer ${c.name} du manifeste` : isDe ? `${c.name} aus dem Manifest entfernen` : `Remover ${c.name} do manifesto`}
>
);
})}
)}
{GUIA_COMPONENT_MANAGER.vantagens.map((v) => (
{v.titulo}
{v.descricao}
))}
{isEs ? "Comandos esenciales" : isEn ? "Essential commands" : isFr ? "Commandes essentielles" : isDe ? "Wichtige Befehle" : "Comandos essenciais"}
{GUIA_COMPONENT_MANAGER.comandosCli.map((cmd, i) => (
$ {cmd.exemplo}
{cmd.explicacao}
copiarComando(`cmd-${i}`, cmd.exemplo)}
className="inline-flex min-h-11 shrink-0 items-center gap-1.5 self-start rounded-full border border-white/15 px-4 text-sm font-medium text-[#e9eef4] transition-colors hover:bg-white/10 md:self-auto"
>
{copiadoId === `cmd-${i}` ? : }
{copiadoId === `cmd-${i}` ? (isEs ? "Copiado" : isEn ? "Copied" : isFr ? "Copié" : isDe ? "Kopiert" : "Copiado") : (isEs ? "Copiar" : isEn ? "Copy" : isFr ? "Copier" : isDe ? "Kopieren" : "Copiar")}
))}
{isEs ? "Versionamiento semántico en ESP-IDF" : isEn ? "Semantic versioning in ESP-IDF" : isFr ? "Versionnement sémantique dans ESP-IDF" : isDe ? "Semantische Versionierung in ESP-IDF" : "Versionamento semântico no ESP-IDF"}
{isEs ? (
<>
^1.2.0: acepta actualizaciones menores y correcciones (1.2.1, 1.3.0), pero bloquea cambios importantes de API (2.0.0). Recomendado.
~1.2.0: acepta solo correcciones de errores (1.2.1, 1.2.5).
1.2.0: bloquea en esta versión exacta.
*: acepta cualquier versión. Evite en producción.
>
) : isEn ? (
<>
^1.2.0: accepts minor updates and bug fixes (1.2.1, 1.3.0), but blocks breaking API changes (2.0.0). Recommended.
~1.2.0: accepts patch releases only (1.2.1, 1.2.5).
1.2.0: locks to this exact version.
*: accepts any version. Avoid in production.
>
) : isFr ? (
<>
^1.2.0: accepte les mises à jour mineures et les correctifs (1.2.1, 1.3.0), mais bloque les changements majeurs d'API (2.0.0). Recommandé.
~1.2.0: accepte uniquement les correctifs de bugs (1.2.1, 1.2.5).
1.2.0: verrouille sur cette version exacte.
*: accepte n'importe quelle version. À éviter en production.
>
) : isDe ? (
<>
^1.2.0: akzeptiert Minor-Updates und Bugfixes (1.2.1, 1.3.0), blockiert aber Breaking Changes (2.0.0). Empfohlen.
~1.2.0: akzeptiert nur Patch-Releases (1.2.1, 1.2.5).
1.2.0: bindet fest an diese Version.
*: akzeptiert jede Version. In Produktion vermeiden.
>
) : (
<>
^1.2.0: aceita atualizações menores e correções (1.2.1, 1.3.0), mas bloqueia mudanças grandes de API (2.0.0). Recomendado.
~1.2.0: aceita só correções de bugs (1.2.1, 1.2.5).
1.2.0: trava na versão exata.
*: aceita qualquer versão. Evite em produção.
>
)}
);
}