Adição dos novos modelos e do tema escuro
No files matched your search
@@ -4,380 +4,389 @@ import { useState, useEffect } from 'react';
|
||||
import Header from "@/components/Header";
|
||||
import Footer from "@/components/Footer";
|
||||
import BoardCard from "@/components/BoardCard";
|
||||
import {
|
||||
Search,
|
||||
Filter,
|
||||
ShoppingCart,
|
||||
CheckCircle,
|
||||
AlertTriangle,
|
||||
RefreshCw,
|
||||
Layers,
|
||||
ChevronDown
|
||||
} from 'lucide-react';
|
||||
|
||||
export default function PlacasPage() {
|
||||
const [boards, setBoards] = useState([]);
|
||||
const [filteredBoards, setFilteredBoards] = useState([]);
|
||||
const [headers, setHeaders] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [activeFilters, setActiveFilters] = useState({});
|
||||
const [filterOptions, setFilterOptions] = useState({});
|
||||
const [boards, setBoards] = useState([]);
|
||||
const [filteredBoards, setFilteredBoards] = useState([]);
|
||||
const [headers, setHeaders] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [activeFilters, setActiveFilters] = useState({});
|
||||
const [filterOptions, setFilterOptions] = useState({});
|
||||
|
||||
const excludedColumns = ['Nome', 'Link', 'Imagem'];
|
||||
const excludedColumns = ['Nome', 'Link', 'Imagem', 'Loja Oficial'];
|
||||
|
||||
useEffect(() => {
|
||||
fetchBoards();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
fetchBoards();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
applyFilters();
|
||||
}, [boards, searchTerm, activeFilters]);
|
||||
useEffect(() => {
|
||||
applyFilters();
|
||||
}, [boards, searchTerm, activeFilters]);
|
||||
|
||||
const fetchBoards = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await fetch('/api/placas');
|
||||
const fetchBoards = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await fetch('/api/placas');
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Erro ao carregar dados das placas');
|
||||
}
|
||||
if (!response.ok) {
|
||||
throw new Error('Erro ao carregar dados das placas');
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
setBoards(result.data);
|
||||
setHeaders(result.headers);
|
||||
setFilteredBoards(result.data);
|
||||
const result = await response.json();
|
||||
setBoards(result.data);
|
||||
setHeaders(result.headers);
|
||||
setFilteredBoards(result.data);
|
||||
|
||||
generateFilterOptions(result.data, result.headers);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
generateFilterOptions(result.data, result.headers);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const generateFilterOptions = (data, headers) => {
|
||||
const options = {};
|
||||
const filterableColumns = headers.filter(h => !excludedColumns.includes(h));
|
||||
const generateFilterOptions = (data, headers) => {
|
||||
const options = {};
|
||||
const filterableColumns = headers.filter(h => !excludedColumns.includes(h));
|
||||
|
||||
filterableColumns.forEach(column => {
|
||||
const uniqueValues = new Set();
|
||||
data.forEach(item => {
|
||||
const value = item[column];
|
||||
if (value && value.toString().trim()) {
|
||||
uniqueValues.add(value.toString().trim());
|
||||
}
|
||||
});
|
||||
filterableColumns.forEach(column => {
|
||||
const uniqueValues = new Set();
|
||||
data.forEach(item => {
|
||||
const value = item[column];
|
||||
if (value && value.toString().trim()) {
|
||||
uniqueValues.add(value.toString().trim());
|
||||
}
|
||||
});
|
||||
|
||||
const values = Array.from(uniqueValues).sort();
|
||||
const values = Array.from(uniqueValues).sort();
|
||||
|
||||
// Verificar se é um filtro binário (apenas Sim/Não/Opcional)
|
||||
const binaryValues = ['Sim', 'Não', 'Opcional'];
|
||||
const isBinaryFilter = values.every(v => binaryValues.includes(v));
|
||||
// Verificar se é um filtro binário (apenas Sim/Não/Opcional)
|
||||
const binaryValues = ['Sim', 'Não', 'Opcional'];
|
||||
const isBinaryFilter = values.every(v => binaryValues.includes(v));
|
||||
|
||||
options[column] = {
|
||||
values: values,
|
||||
isBinary: isBinaryFilter
|
||||
};
|
||||
});
|
||||
options[column] = {
|
||||
values: values,
|
||||
isBinary: isBinaryFilter
|
||||
};
|
||||
});
|
||||
|
||||
setFilterOptions(options);
|
||||
};
|
||||
setFilterOptions(options);
|
||||
};
|
||||
|
||||
const applyFilters = () => {
|
||||
let filtered = [...boards];
|
||||
const applyFilters = () => {
|
||||
let filtered = [...boards];
|
||||
|
||||
if (searchTerm) {
|
||||
filtered = filtered.filter(board => {
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
return (
|
||||
board.Nome?.toLowerCase().includes(searchLower) ||
|
||||
board.Descrição?.toLowerCase().includes(searchLower)
|
||||
);
|
||||
});
|
||||
}
|
||||
if (searchTerm) {
|
||||
filtered = filtered.filter(board => {
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
return (
|
||||
board.Nome?.toLowerCase().includes(searchLower) ||
|
||||
board.Descrição?.toLowerCase().includes(searchLower)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Object.keys(activeFilters).forEach(column => {
|
||||
if (activeFilters[column] && activeFilters[column].length > 0) {
|
||||
filtered = filtered.filter(board => {
|
||||
const boardValue = board[column]?.toString().trim();
|
||||
Object.keys(activeFilters).forEach(column => {
|
||||
if (activeFilters[column] && activeFilters[column].length > 0) {
|
||||
filtered = filtered.filter(board => {
|
||||
const boardValue = board[column]?.toString().trim();
|
||||
|
||||
// Para filtros binários, o valor armazenado é 'binary-enabled'
|
||||
if (activeFilters[column].includes('binary-enabled')) {
|
||||
return boardValue === 'Sim' || boardValue === 'Opcional';
|
||||
}
|
||||
// Para filtros binários, o valor armazenado é 'binary-enabled'
|
||||
if (activeFilters[column].includes('binary-enabled')) {
|
||||
return boardValue === 'Sim' || boardValue === 'Opcional';
|
||||
}
|
||||
|
||||
return activeFilters[column].includes(boardValue);
|
||||
});
|
||||
}
|
||||
});
|
||||
return activeFilters[column].includes(boardValue);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
setFilteredBoards(filtered);
|
||||
};
|
||||
setFilteredBoards(filtered);
|
||||
};
|
||||
|
||||
const handleFilterToggle = (column, value) => {
|
||||
setActiveFilters(prev => {
|
||||
const newFilters = { ...prev };
|
||||
const handleFilterToggle = (column, value) => {
|
||||
setActiveFilters(prev => {
|
||||
const newFilters = { ...prev };
|
||||
|
||||
if (!newFilters[column]) {
|
||||
newFilters[column] = [value];
|
||||
} else {
|
||||
const index = newFilters[column].indexOf(value);
|
||||
if (index > -1) {
|
||||
// Remove o valor (criar novo array sem o valor)
|
||||
newFilters[column] = newFilters[column].filter(v => v !== value);
|
||||
if (newFilters[column].length === 0) {
|
||||
delete newFilters[column];
|
||||
}
|
||||
} else {
|
||||
// Adiciona o valor (criar novo array com o valor)
|
||||
newFilters[column] = [...newFilters[column], value];
|
||||
}
|
||||
}
|
||||
if (!newFilters[column]) {
|
||||
newFilters[column] = [value];
|
||||
} else {
|
||||
const index = newFilters[column].indexOf(value);
|
||||
if (index > -1) {
|
||||
// Remove o valor
|
||||
newFilters[column] = newFilters[column].filter(v => v !== value);
|
||||
if (newFilters[column].length === 0) {
|
||||
delete newFilters[column];
|
||||
}
|
||||
} else {
|
||||
// Adiciona o valor
|
||||
newFilters[column] = [...newFilters[column], value];
|
||||
}
|
||||
}
|
||||
|
||||
return newFilters;
|
||||
});
|
||||
};
|
||||
return newFilters;
|
||||
});
|
||||
};
|
||||
|
||||
const clearFilters = () => {
|
||||
setActiveFilters({});
|
||||
setSearchTerm('');
|
||||
};
|
||||
const clearFilters = () => {
|
||||
setActiveFilters({});
|
||||
setSearchTerm('');
|
||||
};
|
||||
|
||||
const activeFilterCount = Object.keys(activeFilters).length + (searchTerm ? 1 : 0);
|
||||
const activeFilterCount = Object.keys(activeFilters).length + (searchTerm ? 1 : 0);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="bg-linear-to-br from-blue-50 via-white to-purple-50 min-h-screen">
|
||||
<Header />
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="bg-gradient-to-br from-slate-50 via-white to-purple-50/40 dark:from-slate-950 dark:via-slate-950 dark:to-slate-900 min-h-screen text-slate-900 dark:text-slate-100 flex flex-col justify-between">
|
||||
<Header />
|
||||
|
||||
<main className="max-w-7xl mx-auto px-4 py-16">
|
||||
<div className="text-center">
|
||||
<div className="text-6xl mb-4">⏳</div>
|
||||
<p className="text-xl text-gray-600">Carregando placas...</p>
|
||||
</div>
|
||||
</main>
|
||||
<main className="max-w-7xl mx-auto px-6 py-24 flex-1 flex items-center justify-center">
|
||||
<div className="text-center select-none animate-pulse">
|
||||
<RefreshCw className="w-12 h-12 text-purple-500 animate-spin mx-auto mb-4" />
|
||||
<p className="text-xs font-bold text-slate-400 dark:text-slate-500 uppercase tracking-widest">Consultando banco de placas...</p>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="bg-linear-to-br from-blue-50 via-white to-purple-50 min-h-screen">
|
||||
<Header />
|
||||
if (error) {
|
||||
return (
|
||||
<div className="bg-gradient-to-br from-slate-50 via-white to-purple-50/40 dark:from-slate-950 dark:via-slate-950 dark:to-slate-900 min-h-screen text-slate-900 dark:text-slate-100 flex flex-col justify-between">
|
||||
<Header />
|
||||
|
||||
<main className="max-w-7xl mx-auto px-4 py-16">
|
||||
<div className="text-center">
|
||||
<div className="text-6xl mb-4">❌</div>
|
||||
<p className="text-xl text-red-600 mb-4">Erro ao carregar dados</p>
|
||||
<p className="text-gray-600 mb-6">{error}</p>
|
||||
<main className="max-w-7xl mx-auto px-6 py-24 flex-1 flex items-center justify-center">
|
||||
<div className="text-center bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl border border-slate-200/60 dark:border-slate-800/80 rounded-3xl p-8 max-w-md shadow-2xl select-none">
|
||||
<div className="w-12 h-12 bg-red-500/10 border border-red-500/20 rounded-2xl flex items-center justify-center text-red-500 mx-auto mb-4">
|
||||
<AlertTriangle className="w-6 h-6" />
|
||||
</div>
|
||||
<p className="text-sm font-bold text-red-600 dark:text-red-400 mb-2 uppercase tracking-widest">Falha ao sincronizar dados</p>
|
||||
<p className="text-xs text-slate-450 dark:text-slate-550 mb-6 leading-relaxed">{error}</p>
|
||||
|
||||
<button
|
||||
onClick={fetchBoards}
|
||||
className="bg-linear-to-r from-blue-600 to-purple-600 text-white px-6 py-3 rounded-xl font-semibold hover:shadow-lg transition-all"
|
||||
>
|
||||
Tentar Novamente
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
<button
|
||||
onClick={fetchBoards}
|
||||
className="inline-flex items-center gap-2 bg-slate-900 dark:bg-slate-100 text-white dark:text-slate-950 px-5 py-3 rounded-xl font-bold text-xs uppercase tracking-wider hover:opacity-90 active:scale-95 transition-all shadow-md cursor-pointer"
|
||||
>
|
||||
<RefreshCw className="w-4 h-4" />
|
||||
<span>Tentar Novamente</span>
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const filterableColumns = headers.filter(h => !excludedColumns.includes(h));
|
||||
const filterableColumns = headers.filter(h => !excludedColumns.includes(h));
|
||||
|
||||
return (
|
||||
<div className="bg-linear-to-br from-blue-50 via-white to-purple-50 min-h-screen">
|
||||
<Header />
|
||||
return (
|
||||
<div className="bg-gradient-to-br from-slate-50 via-white to-purple-50/40 dark:from-slate-950 dark:via-slate-950 dark:to-slate-900 min-h-screen text-slate-900 dark:text-slate-100 transition-colors duration-300">
|
||||
<Header />
|
||||
|
||||
<main id="conteudo" className="max-w-7xl mx-auto px-4 py-16">
|
||||
<section className="text-center mb-16">
|
||||
<div className="inline-flex items-center gap-2 bg-white/70 backdrop-blur-sm px-4 py-2 rounded-full mb-6 border border-purple-200 shadow-sm">
|
||||
<span className="text-xl">🛒</span>
|
||||
<span className="text-sm font-semibold text-gray-700">Encontre sua placa ideal</span>
|
||||
</div>
|
||||
<main id="conteudo" className="max-w-7xl mx-auto px-6 pt-16 pb-24">
|
||||
{/* Top Hero Heading Block */}
|
||||
<section className="text-center mb-16 select-none">
|
||||
<div className="inline-flex items-center gap-2 bg-white/70 dark:bg-slate-900/50 backdrop-blur-md px-4 py-2 rounded-full mb-6 border border-slate-200/60 dark:border-slate-800/60 shadow-xs">
|
||||
<ShoppingCart className="w-4 h-4 text-purple-500" />
|
||||
<span className="text-xs font-bold text-slate-700 dark:text-slate-350 uppercase tracking-wider">Encontre sua placa ideal</span>
|
||||
</div>
|
||||
|
||||
<h1 className="text-5xl md:text-7xl font-black bg-linear-to-r from-blue-600 via-purple-600 to-pink-600 bg-clip-text text-transparent leading-tight mb-6 tracking-tight">
|
||||
Catálogo de Placas ESP32
|
||||
</h1>
|
||||
<h1 className="text-4xl md:text-6xl font-display leading-tight font-extrabold bg-gradient-to-r from-blue-600 via-purple-600 to-pink-600 dark:from-blue-400 dark:via-purple-400 dark:to-pink-400 bg-clip-text text-transparent mb-6 tracking-tight">
|
||||
Catálogo de Placas ESP32
|
||||
</h1>
|
||||
|
||||
<p className="text-xl md:text-2xl text-gray-700 mb-4 font-medium">
|
||||
Encontre a placa perfeita para o seu projeto
|
||||
</p>
|
||||
<div className="inline-flex items-center gap-2 bg-green-50 px-6 py-3 rounded-full border-2 border-green-200">
|
||||
<svg className="w-5 h-5 text-green-600" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
|
||||
</svg>
|
||||
<p className="text-sm text-green-700 font-bold">
|
||||
Todos os links são verificados e de fontes confiáveis
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<p className="text-sm md:text-base text-slate-500 dark:text-slate-400 max-w-2xl mx-auto leading-relaxed mb-6 font-semibold">
|
||||
Compare e selecione placas oficiais e alternativas de desenvolvimento do microcontrolador ESP32 para seus projetos.
|
||||
</p>
|
||||
|
||||
<div className="grid lg:grid-cols-4 gap-8">
|
||||
<div className="lg:col-span-1">
|
||||
<div className="bg-white rounded-2xl shadow-lg overflow-hidden sticky top-4">
|
||||
{/* Header dos Filtros */}
|
||||
<div className="bg-linear-to-r from-blue-600 to-purple-600 p-6 text-white">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold flex items-center gap-2">
|
||||
🔍 Filtros
|
||||
</h2>
|
||||
{activeFilterCount > 0 && (
|
||||
<p className="text-sm text-blue-100 mt-1">
|
||||
{activeFilterCount} filtro(s) ativo(s)
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{activeFilterCount > 0 && (
|
||||
<button
|
||||
onClick={clearFilters}
|
||||
className="bg-white/20 hover:bg-white/30 text-white px-3 py-1.5 rounded-lg font-semibold text-xs transition-all hover:scale-105"
|
||||
>
|
||||
Limpar
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="inline-flex items-center gap-2 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 px-4 py-2 rounded-full border border-emerald-500/20">
|
||||
<CheckCircle className="w-4 h-4 shrink-0" />
|
||||
<p className="text-[10px] font-bold uppercase tracking-wider">
|
||||
Links de lojas integradas de fontes verificadas e confiáveis
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Conteúdo dos Filtros */}
|
||||
<div className="p-6">
|
||||
{/* Barra de Busca */}
|
||||
<div className="mb-6">
|
||||
<label className="block text-xs font-semibold text-gray-600 mb-2 uppercase tracking-wide">
|
||||
Buscar por nome
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Digite para buscar..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full px-4 py-3 pl-10 border-2 border-gray-200 rounded-xl focus:border-purple-500 focus:outline-none transition-colors text-sm bg-gray-50 focus:bg-white"
|
||||
/>
|
||||
<svg className="absolute left-3 top-3.5 w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid lg:grid-cols-4 gap-8">
|
||||
{/* Left Filters Sidebar */}
|
||||
<div className="lg:col-span-1">
|
||||
<div className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl border border-slate-200/60 dark:border-slate-800/80 rounded-3xl shadow-xl overflow-hidden sticky top-4">
|
||||
|
||||
{/* Separador */}
|
||||
<div className="border-t border-gray-200 my-2"></div>
|
||||
{/* Sidebar Banner Header */}
|
||||
<div className="bg-gradient-to-r from-blue-600 to-purple-600 p-6 text-white select-none">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<h2 className="text-base font-display font-extrabold flex items-center gap-2">
|
||||
<Filter className="w-4.5 h-4.5" />
|
||||
<span>Filtros de Busca</span>
|
||||
</h2>
|
||||
{activeFilterCount > 0 && (
|
||||
<p className="text-[10px] text-blue-150 font-bold uppercase tracking-wider mt-1.5 animate-pulse">
|
||||
{activeFilterCount} selecionado(s)
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Filtros por Categoria */}
|
||||
<div className="space-y-2 max-h-[calc(100vh-450px)] overflow-y-auto pr-2 custom-scrollbar">
|
||||
{filterableColumns.map((column) => {
|
||||
const filterData = filterOptions[column];
|
||||
if (!filterData) return null;
|
||||
{activeFilterCount > 0 && (
|
||||
<button
|
||||
onClick={clearFilters}
|
||||
className="bg-white/20 hover:bg-white/30 text-white px-3 py-1.5 rounded-xl font-bold text-[10px] uppercase tracking-wider transition-all hover:scale-105 active:scale-95 cursor-pointer"
|
||||
>
|
||||
Limpar
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
const isActive = activeFilters[column]?.length > 0;
|
||||
{/* Sidebar Filters Area */}
|
||||
<div className="p-6 space-y-6">
|
||||
{/* Text search by name */}
|
||||
<div>
|
||||
<label className="block text-[9px] font-bold text-slate-400 dark:text-slate-500 mb-2.5 uppercase tracking-widest select-none">
|
||||
Buscar Placa
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Digite o nome da placa..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full px-4 py-3 pl-10 border border-slate-200 dark:border-slate-800 rounded-2xl focus:ring-2 focus:ring-purple-500 dark:focus:ring-purple-500 focus:outline-none transition-colors text-xs bg-slate-50/50 dark:bg-slate-950/20 text-slate-800 dark:text-slate-100"
|
||||
/>
|
||||
<Search className="absolute left-3.5 top-3.5 w-4 h-4 text-slate-400 dark:text-slate-500" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
return (
|
||||
<div key={column} className={`pb-2 border-b border-gray-100 last:border-b-0 ${isActive ? 'bg-purple-50/50 rounded-lg' : ''}`}>
|
||||
{filterData.isBinary ? (
|
||||
// Filtro binário: checkbox integrado ao título
|
||||
<label className="flex justify-between cursor-pointer hover:bg-purple-50 p-3 rounded-lg transition-all group">
|
||||
<span className="font-semibold text-gray-700 group-hover:text-purple-700 transition-colors">
|
||||
{column}
|
||||
</span>
|
||||
<div className="border-t border-slate-100 dark:border-slate-850/45 my-2"></div>
|
||||
|
||||
<div className="mt-0.5">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={activeFilters[column]?.includes('binary-enabled') || false}
|
||||
onChange={() => handleFilterToggle(column, 'binary-enabled')}
|
||||
className="w-5 h-5 rounded-md border-2 border-gray-300 text-purple-600 focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 transition-all cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
) : (
|
||||
// Filtro normal: título separado + múltiplas opções
|
||||
<>
|
||||
<h3 className="font-bold text-gray-800 mb-3 text-sm uppercase tracking-wide flex items-center gap-2">
|
||||
{column}
|
||||
{isActive && (
|
||||
<span className="bg-purple-600 text-white text-xs px-2 py-0.5 rounded-full">
|
||||
{activeFilters[column].length}
|
||||
</span>
|
||||
)}
|
||||
</h3>
|
||||
<div className="space-y-2 max-h-44 overflow-y-auto pr-2">
|
||||
{filterData.values.map((option) => (
|
||||
<label
|
||||
key={option}
|
||||
className="flex items-center gap-3 cursor-pointer hover:bg-purple-50 p-2 rounded-lg transition-all group"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={activeFilters[column]?.includes(option) || false}
|
||||
onChange={() => handleFilterToggle(column, option)}
|
||||
className="w-4 h-4 rounded border-2 border-gray-300 text-purple-600 focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 transition-all cursor-pointer"
|
||||
/>
|
||||
<span className="text-sm text-gray-700 group-hover:text-purple-700 transition-colors">
|
||||
{option}
|
||||
</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Dinamic filters container with customized scrollbar */}
|
||||
<div className="space-y-4 max-h-[calc(100vh-450px)] overflow-y-auto pr-2 custom-scrollbar">
|
||||
{filterableColumns.map((column) => {
|
||||
const filterData = filterOptions[column];
|
||||
if (!filterData) return null;
|
||||
|
||||
{/* Grid de Placas */}
|
||||
<div className="lg:col-span-3">
|
||||
<div className="mb-6 flex justify-between items-center">
|
||||
<p className="text-gray-600">
|
||||
<span className="font-bold text-purple-600">{filteredBoards.length}</span> placa(s) encontrada(s)
|
||||
</p>
|
||||
</div>
|
||||
const isActive = activeFilters[column]?.length > 0;
|
||||
|
||||
{filteredBoards.length === 0 ? (
|
||||
<div className="text-center py-16">
|
||||
<div className="text-6xl mb-4">🔍</div>
|
||||
<p className="text-xl text-gray-600 mb-2">Nenhuma placa encontrada</p>
|
||||
<p className="text-gray-500">Tente ajustar os filtros</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid md:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||
{filteredBoards.map((board, index) => (
|
||||
<BoardCard key={index} board={board} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
return (
|
||||
<div key={column} className={`pb-3 border-b border-slate-100 dark:border-slate-850/30 last:border-b-0 ${isActive ? 'bg-purple-500/5 dark:bg-purple-400/5 p-3 rounded-2xl border border-purple-500/10 dark:border-purple-400/10' : ''}`}>
|
||||
{filterData.isBinary ? (
|
||||
// Binary filter (checkbox inside title wrapper)
|
||||
<label className="flex justify-between items-center cursor-pointer p-1 rounded-lg transition-all group select-none">
|
||||
<span className="text-xs font-bold text-slate-700 dark:text-slate-350 group-hover:text-purple-650 dark:group-hover:text-purple-400 transition-colors">
|
||||
{column}
|
||||
</span>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Estilos customizados para scrollbar
|
||||
const styles = `
|
||||
.custom-scrollbar::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||
background: linear-gradient(to bottom, #3b82f6, #9333ea);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background: linear-gradient(to bottom, #2563eb, #7c3aed);
|
||||
}
|
||||
`;
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
const styleSheet = document.createElement('style');
|
||||
styleSheet.textContent = styles;
|
||||
document.head.appendChild(styleSheet);
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={activeFilters[column]?.includes('binary-enabled') || false}
|
||||
onChange={() => handleFilterToggle(column, 'binary-enabled')}
|
||||
className="w-4.5 h-4.5 rounded-md border border-slate-300 dark:border-slate-750 text-purple-650 focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 transition-all cursor-pointer bg-white/50 dark:bg-slate-950/30"
|
||||
/>
|
||||
</label>
|
||||
) : (
|
||||
// Standard options filter with dropdown listing
|
||||
<>
|
||||
<h3 className="font-bold text-slate-700 dark:text-slate-300 mb-3.5 text-xs uppercase tracking-widest flex items-center gap-2 select-none">
|
||||
<span>{column}</span>
|
||||
{isActive && (
|
||||
<span className="bg-purple-650 text-white text-[9px] px-2 py-0.5 rounded-full font-bold select-none">
|
||||
{activeFilters[column].length}
|
||||
</span>
|
||||
)}
|
||||
</h3>
|
||||
|
||||
<div className="space-y-2.5 max-h-44 overflow-y-auto pr-2 custom-scrollbar">
|
||||
{filterData.values.map((option) => (
|
||||
<label
|
||||
key={option}
|
||||
className="flex items-center gap-3 cursor-pointer hover:bg-purple-500/5 dark:hover:bg-purple-400/5 p-1.5 rounded-lg transition-all group select-none"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={activeFilters[column]?.includes(option) || false}
|
||||
onChange={() => handleFilterToggle(column, option)}
|
||||
className="w-4 h-4 rounded border border-slate-350 dark:border-slate-750 text-purple-650 focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 transition-all cursor-pointer bg-white/50 dark:bg-slate-950/30"
|
||||
/>
|
||||
<span className="text-xs font-semibold text-slate-600 dark:text-slate-400 group-hover:text-purple-650 dark:group-hover:text-purple-400 transition-colors">
|
||||
{option}
|
||||
</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Catalog Boards Grid */}
|
||||
<div className="lg:col-span-3">
|
||||
<div className="mb-6 flex justify-between items-center select-none">
|
||||
<p className="text-xs font-bold text-slate-400 dark:text-slate-500 uppercase tracking-widest">
|
||||
Total Encontrado: <span className="text-purple-650 dark:text-purple-400">{filteredBoards.length}</span> Placa(s)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{filteredBoards.length === 0 ? (
|
||||
<div className="text-center py-20 bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl border border-slate-200/60 dark:border-slate-800/80 rounded-3xl p-8 shadow-md select-none">
|
||||
<Search className="w-12 h-12 text-slate-300 dark:text-slate-700 mx-auto mb-4" />
|
||||
<p className="text-base font-display font-extrabold text-slate-800 dark:text-slate-200 mb-1">Nenhuma Placa Correspondente</p>
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400">Tente ajustar ou redefinir seus filtros laterais para expandir a busca.</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid md:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||
{filteredBoards.map((board, index) => (
|
||||
<BoardCard key={index} board={board} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
|
||||
<style dangerouslySetInnerHTML={{ __html: `
|
||||
.custom-scrollbar::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||
background: #cbd5e1;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.dark .custom-scrollbar::-webkit-scrollbar-thumb {
|
||||
background: #334155;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background: #94a3b8;
|
||||
}
|
||||
`}} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,274 +2,360 @@
|
||||
|
||||
import Header from "@/components/Header";
|
||||
import Footer from "@/components/Footer";
|
||||
import { useState } from "react";
|
||||
import { useState, Fragment } from "react";
|
||||
import Link from "next/link";
|
||||
import seriesData from "@/public/series.json";
|
||||
import {
|
||||
Scale,
|
||||
GitCompare,
|
||||
Check,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
ArrowRight,
|
||||
FileText,
|
||||
ExternalLink,
|
||||
ShoppingBag,
|
||||
Cpu,
|
||||
Network,
|
||||
Database,
|
||||
Sliders,
|
||||
Activity,
|
||||
HelpCircle,
|
||||
Thermometer
|
||||
} from "lucide-react";
|
||||
|
||||
// Category to Icon mapping
|
||||
const categoryIcons = {
|
||||
"Geral": <HelpCircle className="w-4 h-4 text-purple-500" />,
|
||||
"Processador": <Cpu className="w-4 h-4 text-blue-500" />,
|
||||
"Conectividade": <Network className="w-4 h-4 text-indigo-500" />,
|
||||
"Memória": <Database className="w-4 h-4 text-pink-500" />,
|
||||
"Periféricos Digitais": <Sliders className="w-4 h-4 text-sky-500" />,
|
||||
"Periféricos Analógicos": <Activity className="w-4 h-4 text-emerald-500" />,
|
||||
"Interfaces Especiais": <Activity className="w-4 h-4 text-cyan-500" />,
|
||||
"Especificações Ambientais": <Thermometer className="w-4 h-4 text-red-500" />,
|
||||
};
|
||||
|
||||
export default function Comparacao() {
|
||||
const [selectedSeries, setSelectedSeries] = useState(["ESP32", "ESP32-S3"]);
|
||||
const series = Object.entries(seriesData);
|
||||
const [selectedSeries, setSelectedSeries] = useState(["ESP32", "ESP32-S3"]);
|
||||
const [collapsedCategories, setCollapsedCategories] = useState({
|
||||
"Interfaces Especiais": true,
|
||||
"Especificações Ambientais": true,
|
||||
});
|
||||
|
||||
const toggleSeries = (seriesKey) => {
|
||||
if (selectedSeries.includes(seriesKey)) {
|
||||
if (selectedSeries.length > 2) {
|
||||
setSelectedSeries(selectedSeries.filter(s => s !== seriesKey));
|
||||
}
|
||||
} else {
|
||||
if (selectedSeries.length < 4) {
|
||||
setSelectedSeries([...selectedSeries, seriesKey]);
|
||||
}
|
||||
}
|
||||
};
|
||||
const series = Object.entries(seriesData);
|
||||
|
||||
const compareFields = [
|
||||
{ category: "Geral", fields: [
|
||||
{ key: "nome_completo", label: "Nome Completo" },
|
||||
{ key: "descricao", label: "Descrição" },
|
||||
]},
|
||||
{ category: "Processador", fields: [
|
||||
{ key: "arquitetura", label: "Arquitetura" },
|
||||
{ key: "nucleos", label: "Núcleos" },
|
||||
{ key: "frequencia", label: "Frequência" },
|
||||
{ key: "coprocessador_ulp", label: "Coprocessador ULP" },
|
||||
{ key: "aceleradores_ia", label: "Aceleradores de IA" },
|
||||
]},
|
||||
{ category: "Conectividade", fields: [
|
||||
{ key: "wifi", label: "Wi-Fi" },
|
||||
{ key: "bluetooth", label: "Bluetooth" },
|
||||
{ key: "zigbee_thread", label: "Zigbee/Thread" },
|
||||
{ key: "matter", label: "Matter" },
|
||||
{ key: "ethernet", label: "Ethernet" },
|
||||
]},
|
||||
{ category: "Memória", fields: [
|
||||
{ key: "memoria_sram", label: "SRAM" },
|
||||
{ key: "memoria_sram_rtc", label: "SRAM RTC" },
|
||||
{ key: "memoria_rom", label: "ROM" },
|
||||
{ key: "flash_externa", label: "Flash Externa" },
|
||||
{ key: "psram_externa", label: "PSRAM Externa" },
|
||||
]},
|
||||
{ category: "Periféricos Digitais", fields: [
|
||||
{ key: "gpio", label: "GPIO" },
|
||||
{ key: "uart", label: "UART" },
|
||||
{ key: "spi", label: "SPI" },
|
||||
{ key: "i2c", label: "I²C" },
|
||||
{ key: "i2s", label: "I²S" },
|
||||
{ key: "pwm", label: "PWM" },
|
||||
{ key: "can", label: "CAN" },
|
||||
{ key: "usb", label: "USB" },
|
||||
{ key: "sdio", label: "SDIO" },
|
||||
]},
|
||||
{ category: "Periféricos Analógicos", fields: [
|
||||
{ key: "adc", label: "ADC" },
|
||||
{ key: "dac", label: "DAC" },
|
||||
{ key: "touch", label: "Touch Capacitivo" },
|
||||
{ key: "sensor_hall", label: "Sensor Hall" },
|
||||
{ key: "sensor_temperatura", label: "Sensor de Temperatura" },
|
||||
]},
|
||||
{ category: "Interfaces Especiais", fields: [
|
||||
{ key: "lcd", label: "LCD" },
|
||||
{ key: "camera", label: "Câmera" },
|
||||
{ key: "mipi_csi", label: "MIPI CSI" },
|
||||
{ key: "mipi_dsi", label: "MIPI DSI" },
|
||||
{ key: "ethernet_mac", label: "Ethernet MAC" },
|
||||
]},
|
||||
{ category: "Especificações Ambientais", fields: [
|
||||
{ key: "temperatura_operacao", label: "Temp. Operação" },
|
||||
{ key: "temperatura_armazenamento", label: "Temp. Armazenamento" },
|
||||
]},
|
||||
];
|
||||
const toggleSeries = (seriesKey) => {
|
||||
if (selectedSeries.includes(seriesKey)) {
|
||||
if (selectedSeries.length > 2) {
|
||||
setSelectedSeries(selectedSeries.filter(s => s !== seriesKey));
|
||||
}
|
||||
} else {
|
||||
if (selectedSeries.length < 4) {
|
||||
setSelectedSeries([...selectedSeries, seriesKey]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const renderValue = (value) => {
|
||||
if (!value || value === "Não") {
|
||||
return <span className="text-slate-400 font-medium">—</span>;
|
||||
}
|
||||
if (value === "Sim") {
|
||||
return <span className="text-slate-700 font-semibold">✓</span>;
|
||||
}
|
||||
return <span className="text-slate-800 font-medium">{value}</span>;
|
||||
};
|
||||
const toggleCategory = (category) => {
|
||||
setCollapsedCategories({
|
||||
...collapsedCategories,
|
||||
[category]: !collapsedCategories[category]
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-gray-50 min-h-screen">
|
||||
<Header />
|
||||
const compareFields = [
|
||||
{ category: "Geral", fields: [
|
||||
{ key: "nome_completo", label: "Nome Completo" },
|
||||
{ key: "descricao", label: "Descrição" },
|
||||
]},
|
||||
{ category: "Processador", fields: [
|
||||
{ key: "arquitetura", label: "Arquitetura" },
|
||||
{ key: "nucleos", label: "Núcleos" },
|
||||
{ key: "frequencia", label: "Frequência" },
|
||||
{ key: "coprocessador_ulp", label: "Coprocessador ULP" },
|
||||
{ key: "aceleradores_ia", label: "Aceleradores de IA" },
|
||||
]},
|
||||
{ category: "Conectividade", fields: [
|
||||
{ key: "wifi", label: "Wi-Fi" },
|
||||
{ key: "bluetooth", label: "Bluetooth" },
|
||||
{ key: "zigbee_thread", label: "Zigbee/Thread" },
|
||||
{ key: "matter", label: "Matter" },
|
||||
{ key: "ethernet", label: "Ethernet" },
|
||||
]},
|
||||
{ category: "Memória", fields: [
|
||||
{ key: "memoria_sram", label: "SRAM" },
|
||||
{ key: "memoria_sram_rtc", label: "SRAM RTC" },
|
||||
{ key: "memoria_rom", label: "ROM" },
|
||||
{ key: "flash_externa", label: "Flash Externa" },
|
||||
{ key: "psram_externa", label: "PSRAM Externa" },
|
||||
]},
|
||||
{ category: "Periféricos Digitais", fields: [
|
||||
{ key: "gpio", label: "GPIO" },
|
||||
{ key: "uart", label: "UART" },
|
||||
{ key: "spi", label: "SPI" },
|
||||
{ key: "i2c", label: "I²C" },
|
||||
{ key: "i2s", label: "I²S" },
|
||||
{ key: "pwm", label: "PWM" },
|
||||
{ key: "can", label: "CAN" },
|
||||
{ key: "usb", label: "USB" },
|
||||
{ key: "sdio", label: "SDIO" },
|
||||
]},
|
||||
{ category: "Periféricos Analógicos", fields: [
|
||||
{ key: "adc", label: "ADC" },
|
||||
{ key: "dac", label: "DAC" },
|
||||
{ key: "touch", label: "Touch Capacitivo" },
|
||||
{ key: "sensor_hall", label: "Sensor Hall" },
|
||||
{ key: "sensor_temperatura", label: "Sensor de Temperatura" },
|
||||
]},
|
||||
{ category: "Interfaces Especiais", fields: [
|
||||
{ key: "lcd", label: "LCD" },
|
||||
{ key: "camera", label: "Câmera" },
|
||||
{ key: "mipi_csi", label: "MIPI CSI" },
|
||||
{ key: "mipi_dsi", label: "MIPI DSI" },
|
||||
{ key: "ethernet_mac", label: "Ethernet MAC" },
|
||||
]},
|
||||
{ category: "Especificações Ambientais", fields: [
|
||||
{ key: "temperatura_operacao", label: "Temp. Operação" },
|
||||
{ key: "temperatura_armazenamento", label: "Temp. Armazenamento" },
|
||||
]},
|
||||
];
|
||||
|
||||
<main id="conteudo" className="px-4 pt-16 pb-20 max-w-7xl mx-auto">
|
||||
<section className="mb-16">
|
||||
<div className="border-l-4 border-slate-700 pl-6 mb-8">
|
||||
<p className="text-sm font-semibold text-slate-600 uppercase tracking-wider mb-2">
|
||||
Análise Técnica Comparativa
|
||||
</p>
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-slate-900 mb-4">
|
||||
Comparação de Séries ESP32
|
||||
</h1>
|
||||
<p className="text-base text-slate-600 max-w-3xl leading-relaxed">
|
||||
Análise detalhada de especificações técnicas para seleção de microcontroladores ESP32
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
const renderValue = (value) => {
|
||||
if (!value || value === "Não") {
|
||||
return <span className="text-slate-400 dark:text-slate-600 font-medium select-none">—</span>;
|
||||
}
|
||||
if (value === "Sim") {
|
||||
return (
|
||||
<span className="inline-flex items-center justify-center w-6 h-6 rounded-full bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border border-emerald-500/20 shadow-xs">
|
||||
<Check className="w-3.5 h-3.5" />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return <span className="text-slate-800 dark:text-slate-200 font-semibold">{value}</span>;
|
||||
};
|
||||
|
||||
<section className="mb-12">
|
||||
<div className="bg-white border border-slate-200 rounded-lg p-6 mb-6">
|
||||
<h2 className="text-xl font-semibold text-slate-900 mb-2">
|
||||
Seleção de Modelos
|
||||
</h2>
|
||||
<p className="text-sm text-slate-600 mb-6">
|
||||
Selecione de 2 a 4 séries para análise comparativa
|
||||
</p>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-8 gap-3">
|
||||
{series.map(([key, seriesItem]) => (
|
||||
<button
|
||||
key={key}
|
||||
onClick={() => toggleSeries(key)}
|
||||
className={`p-4 border rounded-lg transition-all duration-200 ${
|
||||
selectedSeries.includes(key)
|
||||
? 'border-slate-700 bg-slate-50 shadow-sm'
|
||||
: 'border-slate-200 bg-white hover:border-slate-300 hover:bg-slate-50'
|
||||
}`}
|
||||
>
|
||||
<div className="text-3xl mb-2">{seriesItem.icone}</div>
|
||||
<div className="text-xs font-semibold text-slate-800">{key}</div>
|
||||
{selectedSeries.includes(key) && (
|
||||
<div className="mt-2 flex justify-center">
|
||||
<div className="w-2 h-2 rounded-full bg-slate-700"></div>
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<div className="inline-flex items-center gap-2 bg-slate-100 px-4 py-2 rounded border border-slate-200">
|
||||
<span className="text-slate-700 font-medium text-sm">
|
||||
{selectedSeries.length} / 4 modelos selecionados
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
return (
|
||||
<div className="bg-gradient-to-br from-slate-50 via-white to-purple-50/40 dark:from-slate-950 dark:via-slate-950 dark:to-slate-900 min-h-screen text-slate-900 dark:text-slate-100 transition-colors duration-300">
|
||||
<Header />
|
||||
|
||||
<div className="bg-white border border-slate-200 shadow-sm overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr className="bg-slate-800 border-b border-slate-700">
|
||||
<th className="px-6 py-4 text-left text-white font-semibold text-sm uppercase tracking-wider sticky left-0 bg-slate-800 z-10">
|
||||
Especificação
|
||||
</th>
|
||||
{selectedSeries.map(seriesKey => {
|
||||
const seriesItem = seriesData[seriesKey];
|
||||
return (
|
||||
<th key={seriesKey} className="px-6 py-4 text-center text-white font-semibold text-sm uppercase tracking-wider min-w-48">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<span className="text-2xl">{seriesItem.icone}</span>
|
||||
<span>{seriesKey}</span>
|
||||
</div>
|
||||
</th>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{compareFields.map(({ category, fields }) => (
|
||||
<>
|
||||
<tr key={category} className="bg-slate-100 border-y border-slate-200">
|
||||
<td colSpan={selectedSeries.length + 1} className="px-6 py-3 font-semibold text-slate-800 text-sm uppercase tracking-wide">
|
||||
{category}
|
||||
</td>
|
||||
</tr>
|
||||
{fields.map(({ key, label }) => (
|
||||
<tr key={key} className="border-b border-slate-100 hover:bg-slate-50 transition-colors">
|
||||
<td className="px-6 py-3 font-medium text-slate-700 text-sm bg-white sticky left-0 border-r border-slate-100">
|
||||
{label}
|
||||
</td>
|
||||
{selectedSeries.map(seriesKey => {
|
||||
const seriesItem = seriesData[seriesKey];
|
||||
const value = seriesItem[key];
|
||||
return (
|
||||
<td key={seriesKey} className="px-6 py-3 text-center text-sm">
|
||||
{renderValue(value)}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<main id="conteudo" className="px-6 pt-16 pb-24 max-w-7xl mx-auto">
|
||||
|
||||
{/* Call-to-Action para Catálogo de Placas */}
|
||||
<div className="my-16 bg-slate-800 border border-slate-700 p-10">
|
||||
<div className="flex flex-col md:flex-row items-center justify-between gap-8">
|
||||
<div className="flex-1">
|
||||
<p className="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">
|
||||
Recursos Adicionais
|
||||
</p>
|
||||
<h3 className="text-2xl md:text-3xl font-bold text-white mb-4">
|
||||
Catálogo de Placas de Desenvolvimento
|
||||
</h3>
|
||||
<p className="text-base text-slate-300 leading-relaxed">
|
||||
Acesse nossa base de placas de desenvolvimento verificadas com referências diretas para fornecedores oficiais.
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/catalogo"
|
||||
className="inline-flex items-center justify-center gap-3 bg-white text-slate-800 px-6 py-3 font-semibold text-sm hover:bg-slate-100 transition-colors duration-200 whitespace-nowrap border border-slate-200"
|
||||
>
|
||||
<span>Acessar Catálogo</span>
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
{/* Top Section */}
|
||||
<section className="mb-16">
|
||||
<div className="border-l-4 border-purple-500 pl-6 max-w-3xl">
|
||||
<p className="text-xs font-bold text-purple-600 dark:text-purple-400 uppercase tracking-widest mb-2 select-none">
|
||||
Análise Técnica Comparativa
|
||||
</p>
|
||||
<h1 className="text-4xl md:text-5xl font-display font-extrabold text-slate-800 dark:text-slate-100 mb-4 tracking-tight leading-tight">
|
||||
Comparador de Séries ESP32
|
||||
</h1>
|
||||
<p className="text-base text-slate-500 dark:text-slate-400 leading-relaxed">
|
||||
Coloque lado a lado as especificações completas de clock, memória, interfaces de rádio e periféricos analógicos para validar a engenharia e o custo-benefício de cada microcontrolador.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="mt-12">
|
||||
<h3 className="text-lg font-semibold text-slate-900 mb-6 border-l-4 border-slate-700 pl-4">
|
||||
Documentação Técnica
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{selectedSeries.map(seriesKey => {
|
||||
const seriesItem = seriesData[seriesKey];
|
||||
return (
|
||||
<div
|
||||
key={seriesKey}
|
||||
className="bg-white border border-slate-200 p-5 hover:border-slate-300 hover:shadow-sm transition-all duration-200"
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="text-3xl">{seriesItem.icone}</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<Link href={`/series/${seriesKey}`} className="no-underline">
|
||||
<h4 className="text-base font-semibold text-slate-900 mb-1">{seriesKey}</h4>
|
||||
</Link>
|
||||
<p className="text-xs text-slate-600 mb-3">Documentação oficial</p>
|
||||
<a
|
||||
href={seriesItem.datasheet}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 text-xs font-medium text-slate-700 hover:text-slate-900"
|
||||
>
|
||||
<span>Datasheet</span>
|
||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{/* Model Selector Card */}
|
||||
<section className="mb-12">
|
||||
<div className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl border border-slate-200/60 dark:border-slate-800/80 rounded-3xl p-6 md:p-8 shadow-2xl relative overflow-hidden">
|
||||
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-6">
|
||||
<div>
|
||||
<h2 className="text-lg font-bold text-slate-800 dark:text-slate-100 mb-1">
|
||||
Escolha as Séries
|
||||
</h2>
|
||||
<p className="text-xs text-slate-400 dark:text-slate-500">
|
||||
Selecione de 2 a até 4 modelos simultâneos para montar sua planilha de comparação.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
<div className="inline-flex items-center gap-2 bg-purple-500/10 text-purple-600 dark:text-purple-400 px-3.5 py-1.5 rounded-full border border-purple-500/20 text-xs font-bold shadow-xs select-none">
|
||||
<GitCompare className="w-3.5 h-3.5" />
|
||||
<span>{selectedSeries.length} de 4 selecionados</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Brand-colored dynamic button selector grid */}
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-8 gap-3">
|
||||
{series.map(([key, seriesItem]) => {
|
||||
const isSelected = selectedSeries.includes(key);
|
||||
return (
|
||||
<button
|
||||
key={key}
|
||||
onClick={() => toggleSeries(key)}
|
||||
style={{
|
||||
borderColor: isSelected ? seriesItem.cor : 'transparent'
|
||||
}}
|
||||
className={`p-4 border rounded-2xl transition-all duration-300 cursor-pointer active:scale-95 text-center flex flex-col items-center justify-center shadow-xs ${
|
||||
isSelected
|
||||
? 'bg-slate-100 dark:bg-slate-950 shadow-md scale-[1.02] border-2'
|
||||
: 'border-slate-200/60 dark:border-slate-800/80 bg-white/70 dark:bg-slate-900/30 hover:border-purple-400/50 hover:bg-purple-500/5'
|
||||
}`}
|
||||
>
|
||||
<div className="text-3xl mb-2 select-none transform hover:scale-110 transition-transform">{seriesItem.icone}</div>
|
||||
<div className="text-xs font-bold text-slate-800 dark:text-slate-200">{key}</div>
|
||||
|
||||
{isSelected && (
|
||||
<div
|
||||
className="mt-2.5 w-2 h-2 rounded-full shrink-0 animate-pulse"
|
||||
style={{ backgroundColor: seriesItem.cor }}
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Side by side interactive Table */}
|
||||
<div className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl border border-slate-200/60 dark:border-slate-800/80 rounded-3xl overflow-hidden shadow-2xl">
|
||||
<div className="overflow-x-auto scrollbar-thin">
|
||||
<table className="w-full border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-slate-850 dark:bg-slate-950 border-b border-slate-750 dark:border-slate-900">
|
||||
<th className="px-6 py-5 text-left text-white font-bold text-xs uppercase tracking-widest sticky left-0 bg-slate-850 dark:bg-slate-950 z-20 shadow-xs border-r border-slate-700/30">
|
||||
Especificação
|
||||
</th>
|
||||
{selectedSeries.map(seriesKey => {
|
||||
const seriesItem = seriesData[seriesKey];
|
||||
return (
|
||||
<th key={seriesKey} className="px-6 py-5 text-center text-white font-bold text-xs uppercase tracking-widest min-w-48">
|
||||
<div className="flex flex-col items-center gap-2 select-none">
|
||||
<span className="text-3xl transform hover:scale-105 transition-transform">{seriesItem.icone}</span>
|
||||
<span className="text-sm font-extrabold tracking-wide" style={{ color: seriesItem.cor }}>{seriesKey}</span>
|
||||
</div>
|
||||
</th>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{compareFields.map(({ category, fields }) => {
|
||||
const isCollapsed = collapsedCategories[category];
|
||||
return (
|
||||
<Fragment key={category}>
|
||||
{/* Collapsible Category Accordion Header */}
|
||||
<tr
|
||||
onClick={() => toggleCategory(category)}
|
||||
className="bg-slate-100/80 dark:bg-slate-950/60 border-y border-slate-250/60 dark:border-slate-800/80 cursor-pointer select-none hover:bg-slate-200/60 dark:hover:bg-slate-900/80 transition-colors"
|
||||
>
|
||||
<td
|
||||
colSpan={selectedSeries.length + 1}
|
||||
className="px-6 py-3.5 font-bold text-slate-800 dark:text-slate-200 text-xs uppercase tracking-wider"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
{categoryIcons[category] || <HelpCircle className="w-4 h-4 text-purple-500" />}
|
||||
<span>{category}</span>
|
||||
</div>
|
||||
{isCollapsed ? (
|
||||
<ChevronDown className="w-4 h-4 opacity-60 text-purple-600 dark:text-purple-400" />
|
||||
) : (
|
||||
<ChevronUp className="w-4 h-4 opacity-60 text-purple-600 dark:text-purple-400" />
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{/* Fields list within category (if expanded) */}
|
||||
{!isCollapsed && fields.map(({ key, label }) => (
|
||||
<tr
|
||||
key={key}
|
||||
className="border-b border-slate-100 dark:border-slate-800/40 hover:bg-purple-500/5 dark:hover:bg-purple-400/5 transition-colors group"
|
||||
>
|
||||
<td className="px-6 py-4 font-bold text-slate-600 dark:text-slate-400 text-xs bg-white dark:bg-slate-900/10 sticky left-0 border-r border-slate-150/60 dark:border-slate-800/60 shrink-0 z-10">
|
||||
{label}
|
||||
</td>
|
||||
{selectedSeries.map(seriesKey => {
|
||||
const seriesItem = seriesData[seriesKey];
|
||||
const value = seriesItem[key];
|
||||
return (
|
||||
<td key={seriesKey} className="px-6 py-4 text-center text-xs">
|
||||
{renderValue(value)}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Professional CTA Banner for the Catalog */}
|
||||
<div className="my-20 bg-gradient-to-br from-slate-900 to-indigo-950 dark:from-slate-950 dark:to-purple-950/20 border border-slate-800/80 rounded-3xl p-8 md:p-12 shadow-2xl relative overflow-hidden">
|
||||
<div className="absolute right-0 bottom-0 w-96 h-96 bg-purple-500/5 rounded-full blur-3xl pointer-events-none" />
|
||||
|
||||
<div className="flex flex-col md:flex-row items-center justify-between gap-8 relative z-10">
|
||||
<div className="flex-1 max-w-2xl text-center md:text-left">
|
||||
<div className="inline-flex items-center gap-2 bg-white/10 text-slate-350 px-3 py-1 rounded-full mb-4 text-xs font-semibold select-none">
|
||||
<ShoppingBag className="w-3.5 h-3.5" />
|
||||
<span>Hardware Adicional</span>
|
||||
</div>
|
||||
<h3 className="text-2xl md:text-3xl font-display font-extrabold text-white mb-4">
|
||||
Catálogo de Placas de Desenvolvimento
|
||||
</h3>
|
||||
<p className="text-sm text-slate-400 leading-relaxed">
|
||||
Quer testar o chip selecionado na bancada? Compare placas de desenvolvimento com layouts de pinos mapeados, conexões de periféricos e links oficiais dos melhores fornecedores.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href="/catalogo"
|
||||
className="inline-flex items-center justify-center gap-2 bg-white text-slate-900 px-6 py-3.5 rounded-xl font-bold text-sm hover:bg-slate-100 transition-all duration-300 shrink-0 shadow-md active:scale-95"
|
||||
>
|
||||
<span>Acessar Catálogo</span>
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Detailed technical datasheets section */}
|
||||
<div className="mt-12">
|
||||
<h3 className="text-xl font-display font-extrabold text-slate-800 dark:text-slate-100 mb-6 border-l-4 border-purple-500 pl-4 select-none">
|
||||
Documentação Técnica Relacionada
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{selectedSeries.map(seriesKey => {
|
||||
const seriesItem = seriesData[seriesKey];
|
||||
return (
|
||||
<div
|
||||
key={seriesKey}
|
||||
className="bg-white/70 dark:bg-slate-900/40 backdrop-blur-xl border border-slate-200/80 dark:border-slate-800/80 p-5 rounded-2xl hover:border-purple-500/50 dark:hover:border-purple-400/50 hover:shadow-lg transition-all duration-300 flex flex-col justify-between"
|
||||
>
|
||||
<div className="flex gap-4 items-start mb-4">
|
||||
<div className="text-4xl select-none">{seriesItem.icone}</div>
|
||||
<div className="min-w-0">
|
||||
<Link href={`/series/${seriesKey}`} className="no-underline">
|
||||
<h4 className="text-sm font-extrabold text-slate-850 dark:text-slate-100 hover:text-purple-600 dark:hover:text-purple-400 transition-colors">
|
||||
{seriesKey}
|
||||
</h4>
|
||||
</Link>
|
||||
<p className="text-[10px] text-slate-400 dark:text-slate-500 font-bold uppercase tracking-wider mt-1">Guia de Hardware</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a
|
||||
href={seriesItem.datasheet}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-1.5 w-full text-center py-2 bg-slate-100 dark:bg-slate-950 hover:bg-purple-500/10 dark:hover:bg-purple-400/10 border border-slate-200 dark:border-slate-800 rounded-xl text-xs font-bold text-slate-650 dark:text-slate-350 hover:text-purple-600 dark:hover:text-purple-400 transition-colors"
|
||||
>
|
||||
<span>Datasheet Oficial</span>
|
||||
<ExternalLink className="w-3.5 h-3.5" />
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,179 +4,274 @@ import Footer from "@/components/Footer";
|
||||
import { notFound } from "next/navigation";
|
||||
import CodeExample from "@/components/CodeExample";
|
||||
import frameworksData from "@/public/frameworks.json";
|
||||
import {
|
||||
ArrowLeft,
|
||||
BookOpen,
|
||||
Code,
|
||||
Layers,
|
||||
Zap,
|
||||
Compass,
|
||||
CheckCircle,
|
||||
Terminal,
|
||||
FileText,
|
||||
Sliders,
|
||||
Award,
|
||||
Target
|
||||
} from "lucide-react";
|
||||
|
||||
export default async function FrameworkDetail({ params }) {
|
||||
const { key } = await params;
|
||||
const framework = frameworksData[key];
|
||||
const { key } = await params;
|
||||
const framework = frameworksData[key];
|
||||
|
||||
if (!framework) {
|
||||
notFound();
|
||||
}
|
||||
if (!framework) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const features = framework.caracteristicas || [];
|
||||
const useCases = framework.casos_uso || [];
|
||||
const features = framework.caracteristicas || [];
|
||||
const useCases = framework.casos_uso || [];
|
||||
|
||||
return (
|
||||
<div className="bg-linear-to-br from-blue-50 via-white to-purple-50 min-h-screen">
|
||||
<Header />
|
||||
return (
|
||||
<div className="bg-gradient-to-br from-slate-50 via-white to-purple-50/40 dark:from-slate-950 dark:via-slate-950 dark:to-slate-900 min-h-screen text-slate-900 dark:text-slate-100 transition-colors duration-300">
|
||||
<Header />
|
||||
|
||||
<main className="px-4 pt-16 pb-8 max-w-7xl mx-auto">
|
||||
<div className="mb-8">
|
||||
<Link href="/frameworks" className="inline-flex items-center text-gray-600 hover:text-purple-600 transition-colors mb-4">
|
||||
<span className="mr-2">←</span> Voltar para Frameworks
|
||||
</Link>
|
||||
<main className="px-6 pt-16 pb-24 max-w-7xl mx-auto">
|
||||
{/* Back navigation link */}
|
||||
<div className="mb-8">
|
||||
<Link
|
||||
href="/frameworks"
|
||||
className="inline-flex items-center gap-2 text-xs font-bold text-slate-500 dark:text-slate-400 hover:text-purple-600 dark:hover:text-purple-400 transition-colors uppercase tracking-wider mb-6"
|
||||
>
|
||||
<ArrowLeft className="w-3.5 h-3.5" />
|
||||
<span>Voltar para Frameworks</span>
|
||||
</Link>
|
||||
|
||||
<div className="bg-white rounded-3xl shadow-xl p-8 border-2 border-gray-100"
|
||||
style={{ borderColor: framework.cor + '30' }}>
|
||||
<div className="flex items-start justify-between flex-wrap gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-6xl">{framework.icone}</span>
|
||||
<div>
|
||||
<h1 className="text-4xl md:text-5xl font-extrabold text-gray-800 mb-2">
|
||||
{framework.nome}
|
||||
</h1>
|
||||
<p className="text-xl text-gray-600 mb-2">{framework.nome_completo}</p>
|
||||
<span className="inline-block px-4 py-2 rounded-full text-sm font-semibold text-white"
|
||||
style={{ backgroundColor: framework.cor }}>
|
||||
{framework.tipo}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* Top Info Banner Panel */}
|
||||
<div
|
||||
className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-3xl p-6 md:p-8 shadow-2xl border relative overflow-hidden"
|
||||
style={{ borderColor: `${framework.cor}40` }}
|
||||
>
|
||||
<div className="flex flex-col lg:flex-row items-start lg:items-center justify-between gap-8">
|
||||
<div className="flex items-center gap-5">
|
||||
<span className="text-6xl select-none filter drop-shadow-md">{framework.icone}</span>
|
||||
<div>
|
||||
<h1 className="text-4xl font-display font-extrabold text-slate-850 dark:text-slate-100 tracking-tight leading-none mb-3">
|
||||
{framework.nome}
|
||||
</h1>
|
||||
<p className="text-sm font-semibold text-slate-400 dark:text-slate-500 mb-4">{framework.nome_completo}</p>
|
||||
<span
|
||||
className="inline-block px-3 py-1 rounded-full text-[10px] font-bold uppercase tracking-widest select-none"
|
||||
style={{ backgroundColor: `${framework.cor}18`, color: framework.cor, border: `1px solid ${framework.cor}30` }}
|
||||
>
|
||||
{framework.tipo}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mx-auto sm:mx-0 grid items-center gap-3">
|
||||
{framework.documentacao && (
|
||||
<a
|
||||
href={framework.documentacao}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 bg-linear-to-r from-blue-600 to-purple-600 text-white px-5 py-2 rounded-xl font-semibold shadow hover:shadow-lg transition"
|
||||
>
|
||||
📘 Documentação
|
||||
</a>
|
||||
)}
|
||||
{/* Call to Actions CTA Grid */}
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-1 gap-3 w-full lg:w-auto">
|
||||
{framework.documentacao && (
|
||||
<a
|
||||
href={framework.documentacao}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-2 bg-slate-900 dark:bg-slate-100 text-white dark:text-slate-950 px-5 py-3 rounded-xl font-bold text-xs uppercase tracking-wider hover:opacity-90 transition-opacity shadow-lg"
|
||||
>
|
||||
<BookOpen className="w-4 h-4" />
|
||||
<span>Documentação Oficial</span>
|
||||
</a>
|
||||
)}
|
||||
|
||||
{framework.repositorio && (
|
||||
<a
|
||||
href={framework.repositorio}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 bg-white text-gray-800 px-5 py-2 rounded-xl font-semibold shadow border-2 border-gray-100 hover:border-purple-300 transition"
|
||||
>
|
||||
🔗 Repositório
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{framework.repositorio && (
|
||||
<a
|
||||
href={framework.repositorio}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-2 bg-purple-500/10 text-purple-600 dark:text-purple-400 border border-purple-500/20 px-5 py-3 rounded-xl font-bold text-xs uppercase tracking-wider hover:bg-purple-500/20 transition-all shadow-xs"
|
||||
>
|
||||
<Code className="w-4 h-4" />
|
||||
<span>Repositório no GitHub</span>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-lg text-gray-700 mt-6 leading-relaxed">
|
||||
{framework.descricao}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 dark:text-slate-200 mt-6 leading-relaxed max-w-4xl font-semibold">
|
||||
{framework.descricao}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-6 mb-8">
|
||||
<div className="bg-white rounded-2xl shadow-lg p-6 border-l-4" style={{ borderColor: framework.cor }}>
|
||||
<h3 className="text-sm font-semibold text-gray-600 mb-2">LINGUAGEM</h3>
|
||||
<p className="text-3xl font-bold text-gray-800">{framework.linguagem}</p>
|
||||
<p className="text-gray-600 mt-1">Principal</p>
|
||||
</div>
|
||||
{/* Framework Core Specifications Grid */}
|
||||
<div className="grid md:grid-cols-3 gap-6 mb-8 select-none animate-fadeIn">
|
||||
<div
|
||||
className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-2xl shadow-lg p-6 border-l-4"
|
||||
style={{ borderColor: framework.cor }}
|
||||
>
|
||||
<h3 className="text-[10px] font-bold text-slate-400 dark:text-slate-500 uppercase tracking-widest mb-1.5 flex items-center gap-1.5">
|
||||
<Code className="w-3.5 h-3.5 text-blue-500" />
|
||||
<span>LINGUAGEM DE DESENVOLVIMENTO</span>
|
||||
</h3>
|
||||
<p className="text-2xl font-extrabold text-slate-800 dark:text-slate-100">{framework.linguagem}</p>
|
||||
<p className="text-xs text-slate-400 mt-1">Sintaxe principal do ecossistema</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl shadow-lg p-6 border-l-4" style={{ borderColor: framework.cor }}>
|
||||
<h3 className="text-sm font-semibold text-gray-600 mb-2">TIPO</h3>
|
||||
<p className="text-3xl font-bold text-gray-800">{framework.tipo}</p>
|
||||
<p className="text-gray-600 mt-1">Categoria</p>
|
||||
</div>
|
||||
<div
|
||||
className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-2xl shadow-lg p-6 border-l-4"
|
||||
style={{ borderColor: framework.cor }}
|
||||
>
|
||||
<h3 className="text-[10px] font-bold text-slate-400 dark:text-slate-500 uppercase tracking-widest mb-1.5 flex items-center gap-1.5">
|
||||
<Layers className="w-3.5 h-3.5 text-purple-500" />
|
||||
<span>CATEGORIA DO FRAMEWORK</span>
|
||||
</h3>
|
||||
<p className="text-2xl font-extrabold text-slate-800 dark:text-slate-100">{framework.tipo}</p>
|
||||
<p className="text-xs text-slate-400 mt-1">Classificação de arquitetura</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl shadow-lg p-6 border-l-4" style={{ borderColor: framework.cor }}>
|
||||
<h3 className="text-sm font-semibold text-gray-600 mb-2">FUNÇÃO PRINCIPAL</h3>
|
||||
<p className="text-xl font-bold text-gray-800">{framework.funcao_principal}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-2xl shadow-lg p-6 border-l-4"
|
||||
style={{ borderColor: framework.cor }}
|
||||
>
|
||||
<h3 className="text-[10px] font-bold text-slate-400 dark:text-slate-450 mb-1.5 flex items-center gap-1.5">
|
||||
<Target className="w-3.5 h-3.5 text-emerald-500" />
|
||||
<span>OBJETIVO PRINCIPAL</span>
|
||||
</h3>
|
||||
<p className="text-sm font-bold text-slate-700 dark:text-slate-200 leading-relaxed mt-1.5">{framework.funcao_principal}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid lg:grid-cols-3 gap-6 mb-8">
|
||||
<div className="lg:col-span-2 bg-white rounded-2xl shadow-lg p-6 border-2 border-gray-100">
|
||||
<h2 className="text-2xl font-bold text-gray-800 mb-4 pb-3 border-b-2" style={{ borderColor: framework.cor }}>
|
||||
✨ Principais Características
|
||||
</h2>
|
||||
<ul className="list-disc list-inside space-y-2 text-gray-700">
|
||||
{features.map((f, i) => (
|
||||
<li key={i} className="pl-2">{f}</li>
|
||||
))}
|
||||
</ul>
|
||||
{/* side-by-side details block */}
|
||||
<div className="grid lg:grid-cols-3 gap-6 mb-8 items-start">
|
||||
{/* Left specs card */}
|
||||
<div className="lg:col-span-2 bg-white/80 dark:bg-slate-900/45 backdrop-blur-xl rounded-3xl p-8 border border-slate-200/60 dark:border-slate-800/80 shadow-2xl space-y-8">
|
||||
<div>
|
||||
<h2
|
||||
className="text-lg font-display font-extrabold text-slate-850 dark:text-slate-100 mb-6 pb-3 border-b flex items-center gap-2.5 select-none"
|
||||
style={{ borderBottomColor: `${framework.cor}30` }}
|
||||
>
|
||||
<Zap className="w-5 h-5" style={{ color: framework.cor }} />
|
||||
<span>Características de Destaque</span>
|
||||
</h2>
|
||||
|
||||
<h2 className="text-2xl font-bold text-gray-800 mt-8 mb-4 pb-3 border-b-2" style={{ borderColor: framework.cor }}>
|
||||
📦 Casos de Uso
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-2 gap-3">
|
||||
{useCases.map((u, i) => (
|
||||
<div key={i} className="bg-gray-50 p-3 rounded-lg text-gray-700">{u}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<ul className="space-y-3">
|
||||
{features.map((feature, i) => (
|
||||
<li key={i} className="flex items-start gap-2.5 text-xs font-semibold text-slate-650 dark:text-slate-200">
|
||||
<CheckCircle className="w-4 h-4 text-emerald-500 dark:text-emerald-450 mt-0.5 shrink-0" />
|
||||
<span>{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl shadow-lg p-6 border-2 border-gray-100">
|
||||
<h2 className="text-2xl font-bold text-gray-800 mb-4 pb-3 border-b-2" style={{ borderColor: framework.cor }}>
|
||||
📋 Detalhes Rápidos
|
||||
</h2>
|
||||
<div className="space-y-3 text-gray-700">
|
||||
<div><span className="font-semibold">Nome completo:</span> {framework.nome_completo}</div>
|
||||
<div><span className="font-semibold">Linguagem:</span> {framework.linguagem}</div>
|
||||
<div><span className="font-semibold">Tipo:</span> {framework.tipo}</div>
|
||||
<div><span className="font-semibold">Cor (tema):</span> <span className="inline-block w-4 h-4 rounded ml-2" style={{ backgroundColor: framework.cor }} /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2
|
||||
className="text-lg font-display font-extrabold text-slate-850 dark:text-slate-100 mb-6 pb-3 border-b flex items-center gap-2.5 select-none"
|
||||
style={{ borderBottomColor: `${framework.cor}30` }}
|
||||
>
|
||||
<Compass className="w-5 h-5" style={{ color: framework.cor }} />
|
||||
<span>Casos de Uso Recomendados</span>
|
||||
</h2>
|
||||
|
||||
{framework.exemplos && (
|
||||
<CodeExample framework={framework} />
|
||||
)}
|
||||
<div className="grid md:grid-cols-2 gap-3.5">
|
||||
{useCases.map((useCase, i) => (
|
||||
<div key={i} className="bg-slate-50/50 dark:bg-slate-900/60 p-4 rounded-2xl border border-slate-150/40 dark:border-slate-800/80 text-xs font-bold text-slate-700 dark:text-slate-200 flex items-center gap-2 hover:bg-purple-500/5 dark:hover:bg-purple-400/5 transition-colors select-none">
|
||||
<span className="w-2.5 h-2.5 rounded-full shrink-0" style={{ backgroundColor: framework.cor }} />
|
||||
<span>{useCase}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center md:flex gap-4 mt-6">
|
||||
{framework.documentacao && (
|
||||
<a
|
||||
href={framework.documentacao}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center mb-4 gap-2 bg-linear-to-r from-blue-600 to-purple-600 text-white px-6 py-3 rounded-xl font-semibold shadow hover:shadow-xl transition"
|
||||
>
|
||||
📚 Documentação Oficial
|
||||
</a>
|
||||
)}
|
||||
{/* Right summary metadata card */}
|
||||
<div className="bg-white/80 dark:bg-slate-900/45 backdrop-blur-xl rounded-3xl p-8 border border-slate-200/60 dark:border-slate-800/80 shadow-2xl select-none">
|
||||
<h2
|
||||
className="text-lg font-display font-extrabold text-slate-850 dark:text-slate-100 mb-6 pb-3 border-b flex items-center gap-2.5"
|
||||
style={{ borderBottomColor: `${framework.cor}30` }}
|
||||
>
|
||||
<FileText className="w-5 h-5" style={{ color: framework.cor }} />
|
||||
<span>Resumo Técnico</span>
|
||||
</h2>
|
||||
|
||||
{framework.repositorio && (
|
||||
<a
|
||||
href={framework.repositorio}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center mb-4 gap-2 bg-white text-gray-800 px-6 py-3 rounded-xl font-semibold shadow border-2 border-gray-100 hover:border-purple-300 transition"
|
||||
>
|
||||
💻 Repositório no GitHub
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
<div className="space-y-4 text-xs font-bold text-slate-650 dark:text-slate-200">
|
||||
<div className="flex justify-between items-center py-2 border-b border-slate-100 dark:border-slate-850/45">
|
||||
<span className="text-slate-400 dark:text-slate-500 uppercase tracking-wider text-[9px]">Nome Oficial</span>
|
||||
<span className="text-slate-800 dark:text-slate-200 font-extrabold">{framework.nome}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b border-slate-100 dark:border-slate-850/45">
|
||||
<span className="text-slate-400 dark:text-slate-500 uppercase tracking-wider text-[9px]">Linguagem</span>
|
||||
<span className="text-slate-800 dark:text-slate-200 font-extrabold">{framework.linguagem}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b border-slate-100 dark:border-slate-850/45">
|
||||
<span className="text-slate-400 dark:text-slate-500 uppercase tracking-wider text-[9px]">Tipo / Suite</span>
|
||||
<span className="text-slate-800 dark:text-slate-200 font-extrabold">{framework.tipo}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-slate-400 dark:text-slate-500 uppercase tracking-wider text-[9px]">Cor Temática</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<code className="text-[10px] text-slate-500 uppercase">{framework.cor}</code>
|
||||
<span className="inline-block w-4 h-4 rounded-md shadow-xs border border-white/20" style={{ backgroundColor: framework.cor }} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
{/* Coding examples container (renders only if framework has examples defined) */}
|
||||
{framework.exemplos && (
|
||||
<div className="mt-12">
|
||||
<CodeExample framework={framework} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Technical documentation double CTAs */}
|
||||
<div className="flex flex-wrap gap-4 mt-8 select-none">
|
||||
{framework.documentacao && (
|
||||
<a
|
||||
href={framework.documentacao}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-2 bg-slate-900 dark:bg-slate-100 text-white dark:text-slate-950 px-6 py-3.5 rounded-xl font-bold text-xs uppercase tracking-wider hover:opacity-90 transition-opacity shadow-lg"
|
||||
>
|
||||
<BookOpen className="w-4 h-4" />
|
||||
<span>Documentação do Desenvolvedor</span>
|
||||
</a>
|
||||
)}
|
||||
|
||||
{framework.repositorio && (
|
||||
<a
|
||||
href={framework.repositorio}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-2 bg-purple-500/10 text-purple-600 dark:text-purple-400 border border-purple-500/20 px-6 py-3.5 rounded-xl font-bold text-xs uppercase tracking-wider hover:bg-purple-500/20 transition-all shadow-xs"
|
||||
>
|
||||
<Code className="w-4 h-4" />
|
||||
<span>Código Fonte no GitHub</span>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }) {
|
||||
const { key } = await params;
|
||||
const framework = frameworksData[key];
|
||||
const { key } = await params;
|
||||
const framework = frameworksData[key];
|
||||
|
||||
if (!framework) {
|
||||
return { title: "Framework não encontrado" };
|
||||
}
|
||||
if (!framework) {
|
||||
return { title: "Framework não encontrado" };
|
||||
}
|
||||
|
||||
return {
|
||||
title: `ESPDocs - ${framework.nome_completo}`,
|
||||
description: framework.descricao,
|
||||
};
|
||||
return {
|
||||
title: `ESPDocs - ${framework.nome_completo}`,
|
||||
description: framework.descricao,
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
return Object.keys(frameworksData).map((key) => ({
|
||||
key: key,
|
||||
}));
|
||||
return Object.keys(frameworksData).map((key) => ({
|
||||
key: key,
|
||||
}));
|
||||
}
|
||||
@@ -2,200 +2,237 @@ import Header from "@/components/Header";
|
||||
import Footer from "@/components/Footer";
|
||||
import Link from "next/link";
|
||||
import frameworksData from "@/public/frameworks.json";
|
||||
import {
|
||||
Cpu,
|
||||
Target,
|
||||
Zap,
|
||||
Compass,
|
||||
ExternalLink,
|
||||
GraduationCap,
|
||||
ArrowRight,
|
||||
Code,
|
||||
Layers,
|
||||
Sparkles,
|
||||
Music,
|
||||
Camera,
|
||||
Brain,
|
||||
Home
|
||||
} from "lucide-react";
|
||||
|
||||
export default function Frameworks() {
|
||||
const frameworks = Object.entries(frameworksData);
|
||||
const frameworks = Object.entries(frameworksData);
|
||||
|
||||
return (
|
||||
<div className="bg-linear-to-br from-blue-50 via-white to-purple-50 min-h-screen">
|
||||
<Header />
|
||||
return (
|
||||
<div className="bg-gradient-to-br from-slate-50 via-white to-purple-50/40 dark:from-slate-950 dark:via-slate-950 dark:to-slate-900 min-h-screen text-slate-900 dark:text-slate-100 transition-colors duration-300">
|
||||
<Header />
|
||||
|
||||
<main id="conteudo" className="px-4 pt-16 pb-20 max-w-7xl mx-auto">
|
||||
<section className="text-center mb-16">
|
||||
<div className="inline-flex items-center gap-2 bg-white/70 backdrop-blur-sm px-4 py-2 rounded-full mb-6 border border-purple-200 shadow-sm">
|
||||
<span className="text-xl">🛠️</span>
|
||||
<span className="text-sm font-semibold text-gray-700">Ferramentas de Desenvolvimento</span>
|
||||
</div>
|
||||
<main id="conteudo" className="px-6 pt-16 pb-24 max-w-7xl mx-auto">
|
||||
{/* Top Hero Section */}
|
||||
<section className="text-center mb-16 select-none">
|
||||
<div className="inline-flex items-center gap-2 bg-white/70 dark:bg-slate-900/50 backdrop-blur-md px-4 py-2 rounded-full mb-6 border border-slate-200/60 dark:border-slate-800/60 shadow-xs">
|
||||
<Layers className="w-4 h-4 text-purple-500" />
|
||||
<span className="text-xs font-bold text-slate-700 dark:text-slate-350 uppercase tracking-wider">Ferramentas de Desenvolvimento</span>
|
||||
</div>
|
||||
|
||||
<h1 className="text-5xl md:text-7xl font-black bg-linear-to-r from-blue-600 via-purple-600 to-pink-600 bg-clip-text text-transparent mb-6 leading-tight tracking-tight">
|
||||
Frameworks ESP32
|
||||
</h1>
|
||||
<h1 className="text-4xl md:text-6xl font-display font-extrabold bg-gradient-to-r from-blue-600 via-purple-600 to-pink-600 dark:from-blue-400 dark:via-purple-400 dark:to-pink-400 bg-clip-text text-transparent mb-6 leading-none tracking-tight">
|
||||
Frameworks ESP32
|
||||
</h1>
|
||||
|
||||
<p className="text-lg md:text-xl text-gray-600 max-w-3xl mx-auto leading-relaxed">
|
||||
Descubra os frameworks e bibliotecas disponíveis para ESP32. De desenvolvimento geral a
|
||||
aplicações especializadas em áudio, visão computacional, IA e IoT.
|
||||
</p>
|
||||
</section>
|
||||
<p className="text-sm md:text-base text-slate-500 dark:text-slate-400 max-w-2xl mx-auto leading-relaxed font-semibold">
|
||||
Descubra o ecossistema de software do ESP32. De desenvolvimento geral C/C++ a
|
||||
soluções dedicadas a áudio de alto desempenho, visão computacional na borda e IoT inteligente.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{frameworks.map(([key, framework]) => (
|
||||
<Link
|
||||
key={key}
|
||||
href={`/frameworks/${key}`}
|
||||
className="group"
|
||||
>
|
||||
<div className="bg-white rounded-2xl shadow-lg hover:shadow-2xl transform hover:-translate-y-2 transition-all duration-300 border-2 border-gray-100 hover:border-opacity-50 h-full flex flex-col">
|
||||
{/* Cards Grid */}
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{frameworks.map(([key, framework]) => (
|
||||
<Link
|
||||
key={key}
|
||||
href={`/frameworks/${key}`}
|
||||
className="group flex"
|
||||
>
|
||||
<div
|
||||
className="bg-white/70 dark:bg-slate-900/40 backdrop-blur-xl border border-slate-200/60 dark:border-slate-800/80 rounded-3xl shadow-lg hover:shadow-2xl transition-all duration-500 hover:-translate-y-1.5 flex flex-col justify-between overflow-hidden w-full"
|
||||
style={{ borderTop: `4px solid ${framework.cor}` }}
|
||||
>
|
||||
|
||||
<div className="p-6 border-b border-gray-100">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-5xl">{framework.icone}</span>
|
||||
<div className="flex flex-col gap-2 items-end">
|
||||
<span className="px-3 py-1 rounded-full text-xs font-semibold text-white"
|
||||
style={{ backgroundColor: framework.cor }}>
|
||||
{framework.linguagem}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-6 pb-4 border-b border-slate-100 dark:border-slate-850/40 flex-1">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<span className="text-5xl select-none filter drop-shadow-md">{framework.icone}</span>
|
||||
<div className="flex flex-col gap-1 items-end select-none">
|
||||
<span
|
||||
className="px-2.5 py-1 rounded-full text-[10px] font-bold uppercase tracking-wider"
|
||||
style={{
|
||||
backgroundColor: `${framework.cor}18`,
|
||||
color: framework.cor,
|
||||
border: `1px solid ${framework.cor}25`
|
||||
}}
|
||||
>
|
||||
{framework.linguagem}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold text-gray-800 mb-1 group-hover:text-purple-600 transition-colors">
|
||||
{framework.nome}
|
||||
</h2>
|
||||
<h2 className="text-xl font-display font-extrabold text-slate-850 dark:text-slate-100 mb-1 group-hover:text-purple-650 dark:group-hover:text-purple-400 transition-colors">
|
||||
{framework.nome}
|
||||
</h2>
|
||||
|
||||
<p className="text-xs text-gray-500 mb-3 font-medium">
|
||||
{framework.nome_completo}
|
||||
</p>
|
||||
<p className="text-[10px] text-slate-400 dark:text-slate-500 font-bold uppercase tracking-wider mb-4 leading-none select-none">
|
||||
{framework.nome_completo}
|
||||
</p>
|
||||
|
||||
<p className="text-sm text-gray-600 line-clamp-3">
|
||||
{framework.descricao}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 dark:text-slate-300 leading-relaxed line-clamp-3">
|
||||
{framework.descricao}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="p-6 space-y-4 grow">
|
||||
<div>
|
||||
<span className="inline-block px-3 py-1 rounded-lg text-xs font-semibold"
|
||||
style={{
|
||||
backgroundColor: `${framework.cor}20`,
|
||||
color: framework.cor
|
||||
}}>
|
||||
{framework.tipo}
|
||||
</span>
|
||||
</div>
|
||||
<div className="p-6 py-5 space-y-4 flex-1 flex flex-col justify-between select-none">
|
||||
<div className="space-y-4">
|
||||
{/* Main Feature */}
|
||||
<div>
|
||||
<h4 className="text-[9px] font-bold text-slate-400 dark:text-slate-450 mb-1.5 uppercase tracking-widest flex items-center gap-1.5">
|
||||
<Target className="w-3.5 h-3.5 text-blue-500" />
|
||||
<span>Objetivo Principal</span>
|
||||
</h4>
|
||||
<p className="text-xs text-slate-650 dark:text-slate-200 font-medium leading-relaxed">
|
||||
{framework.funcao_principal}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 className="text-xs font-bold text-gray-700 mb-2 uppercase tracking-wide">
|
||||
🎯 Função Principal
|
||||
</h4>
|
||||
<p className="text-sm text-gray-600 line-clamp-2">
|
||||
{framework.funcao_principal}
|
||||
</p>
|
||||
</div>
|
||||
{/* Key Specs */}
|
||||
<div>
|
||||
<h4 className="text-[9px] font-bold text-slate-400 dark:text-slate-450 mb-2 uppercase tracking-widest flex items-center gap-1.5">
|
||||
<Zap className="w-3.5 h-3.5 text-amber-500" />
|
||||
<span>Recursos Chave</span>
|
||||
</h4>
|
||||
<ul className="space-y-1">
|
||||
{framework.caracteristicas.slice(0, 3).map((carac, idx) => (
|
||||
<li key={idx} className="text-xs text-slate-600 dark:text-slate-300 flex items-start gap-1.5">
|
||||
<span className="text-[8px] mt-1 shrink-0" style={{ color: framework.cor }}>■</span>
|
||||
<span className="line-clamp-1">{carac}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 className="text-xs font-bold text-gray-700 mb-2 uppercase tracking-wide">
|
||||
⚡ Características
|
||||
</h4>
|
||||
<ul className="space-y-1">
|
||||
{framework.caracteristicas.slice(0, 3).map((carac, idx) => (
|
||||
<li key={idx} className="text-xs text-gray-600 flex items-start">
|
||||
<span className="mr-2 mt-0.5" style={{ color: framework.cor }}>▪</span>
|
||||
<span className="line-clamp-1">{carac}</span>
|
||||
</li>
|
||||
))}
|
||||
{framework.caracteristicas.length > 3 && (
|
||||
<li className="text-xs text-gray-400 italic">
|
||||
+{framework.caracteristicas.length - 3} mais...
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
{/* Use Cases tags */}
|
||||
<div>
|
||||
<h4 className="text-[9px] font-bold text-slate-400 dark:text-slate-450 mb-2 uppercase tracking-widest flex items-center gap-1.5">
|
||||
<Compass className="w-3.5 h-3.5 text-emerald-500" />
|
||||
<span>Aplicações Ideais</span>
|
||||
</h4>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{framework.casos_uso.slice(0, 3).map((caso, idx) => (
|
||||
<span key={idx} className="text-[9px] font-bold bg-slate-100 dark:bg-slate-900/60 text-slate-650 dark:text-slate-200 px-2.5 py-1 rounded-md border border-slate-200/40 dark:border-slate-800/80">
|
||||
{caso}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 className="text-xs font-bold text-gray-700 mb-2 uppercase tracking-wide">
|
||||
💼 Casos de Uso
|
||||
</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{framework.casos_uso.slice(0, 3).map((caso, idx) => (
|
||||
<span key={idx} className="text-xs bg-gray-100 text-gray-700 px-2 py-1 rounded">
|
||||
{caso}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* CTA Link */}
|
||||
<div className="pt-4 border-t border-slate-100 dark:border-slate-850/40 mt-auto flex items-center justify-between text-xs font-bold text-slate-700 dark:text-slate-300 group-hover:text-purple-650 dark:group-hover:text-purple-400 transition-colors">
|
||||
<span>Acessar Detalhes e Exemplos</span>
|
||||
<ArrowRight className="w-4 h-4 transform group-hover:translate-x-1.5 transition-transform" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="px-6 pb-6">
|
||||
<div className="flex items-center justify-between text-sm font-semibold group-hover:text-purple-600 transition-colors">
|
||||
<span>Ver Detalhes e Exemplos</span>
|
||||
<span className="transform group-hover:translate-x-2 transition-transform">→</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
{/* Bottom Informative Cards Section */}
|
||||
<div className="mt-16 grid md:grid-cols-2 gap-8 select-none">
|
||||
{/* Card 1: Beginner Guide */}
|
||||
<div className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-3xl shadow-xl p-8 border border-slate-200/60 dark:border-slate-800/80 flex flex-col justify-between relative overflow-hidden">
|
||||
<div>
|
||||
<div className="flex items-center gap-3.5 mb-5">
|
||||
<div className="w-12 h-12 bg-blue-500/10 border border-blue-500/20 rounded-2xl flex items-center justify-center text-blue-500">
|
||||
<GraduationCap className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-display font-extrabold text-slate-850 dark:text-slate-100">
|
||||
Iniciante no ESP32?
|
||||
</h3>
|
||||
<p className="text-[10px] font-bold text-blue-500 uppercase tracking-wider leading-none">Guia de Aprendizado</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-16 grid md:grid-cols-2 gap-8">
|
||||
<div className="bg-linear-to-br from-white to-blue-50 rounded-3xl shadow-2xl p-8 border-2 border-blue-100">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-12 h-12 bg-linear-to-br from-blue-500 to-blue-600 rounded-xl flex items-center justify-center">
|
||||
<span className="text-2xl">🎓</span>
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold text-gray-900">
|
||||
Iniciante no ESP32?
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-gray-700 mb-6 leading-relaxed text-base">
|
||||
Se você está começando, recomendo iniciar com
|
||||
<strong>Arduino</strong> ou
|
||||
<strong>MicroPython</strong>
|
||||
para prototipagem rápida, ou
|
||||
<strong>ESP-IDF</strong> para projetos profissionais com controle total.
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Link href="/frameworks/Arduino" className="inline-flex items-center gap-2 px-5 py-3 bg-blue-100 text-blue-700 rounded-xl text-sm font-bold hover:bg-blue-200 transition-all shadow-sm hover:shadow-md transform hover:-translate-y-0.5">
|
||||
<span className="text-lg">📘</span>
|
||||
<span>Arduino</span>
|
||||
</Link>
|
||||
<Link href="/frameworks/MicroPython" className="inline-flex items-center gap-2 px-5 py-3 bg-purple-100 text-purple-700 rounded-xl text-sm font-bold hover:bg-purple-200 transition-all shadow-sm hover:shadow-md transform hover:-translate-y-0.5">
|
||||
<span className="text-lg">🐍</span>
|
||||
<span>MicroPython</span>
|
||||
</Link>
|
||||
<Link href="/frameworks/ESP-IDF" className="inline-flex items-center gap-2 px-5 py-3 bg-red-100 text-red-700 rounded-xl text-sm font-bold hover:bg-red-200 transition-all shadow-sm hover:shadow-md transform hover:-translate-y-0.5">
|
||||
<span className="text-lg">⚙️</span>
|
||||
<span>ESP-IDF</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400 leading-relaxed mb-6 font-semibold">
|
||||
Se você está iniciando a sua jornada de hardware, recomendamos adotar o ecossistema
|
||||
<strong className="text-slate-750 dark:text-slate-300">Arduino</strong> ou
|
||||
<strong className="text-slate-750 dark:text-slate-300">MicroPython</strong>
|
||||
para testar sensores e APIs. Para projetos profissionais industriais de alta performance e eficiência energética, o framework nativo
|
||||
<strong className="text-slate-750 dark:text-slate-300">ESP-IDF</strong> é o padrão exigido no mercado.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-linear-to-br from-white to-purple-50 rounded-3xl shadow-2xl p-8 border-2 border-purple-100">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-12 h-12 bg-linear-to-br from-purple-500 to-pink-600 rounded-xl flex items-center justify-center">
|
||||
<span className="text-2xl">🚀</span>
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold text-gray-900">
|
||||
Aplicações Especializadas?
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-gray-700 mb-6 leading-relaxed text-base">
|
||||
Para projetos específicos, explore frameworks especializados como
|
||||
<strong>ESP-ADF</strong> (áudio),
|
||||
<strong>ESP-WHO</strong> (visão),
|
||||
<strong>ESP-NN</strong> (IA) ou
|
||||
<strong>ESP-Matter</strong> (smart home).
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Link href="/frameworks/ESP-ADF" className="inline-flex items-center gap-2 px-5 py-3 bg-blue-100 text-blue-700 rounded-xl text-sm font-bold hover:bg-blue-200 transition-all shadow-sm hover:shadow-md transform hover:-translate-y-0.5">
|
||||
<span className="text-lg">🎵</span>
|
||||
<span>Áudio</span>
|
||||
</Link>
|
||||
<Link href="/frameworks/ESP-WHO" className="inline-flex items-center gap-2 px-5 py-3 bg-orange-100 text-orange-700 rounded-xl text-sm font-bold hover:bg-orange-200 transition-all shadow-sm hover:shadow-md transform hover:-translate-y-0.5">
|
||||
<span className="text-lg">📷</span>
|
||||
<span>Visão</span>
|
||||
</Link>
|
||||
<Link href="/frameworks/ESP-NN" className="inline-flex items-center gap-2 px-5 py-3 bg-teal-100 text-teal-700 rounded-xl text-sm font-bold hover:bg-teal-200 transition-all shadow-sm hover:shadow-md transform hover:-translate-y-0.5">
|
||||
<span className="text-lg">🧠</span>
|
||||
<span>IA</span>
|
||||
</Link>
|
||||
<Link href="/frameworks/ESP-Matter" className="inline-flex items-center gap-2 px-5 py-3 bg-yellow-100 text-yellow-700 rounded-xl text-sm font-bold hover:bg-yellow-200 transition-all shadow-sm hover:shadow-md transform hover:-translate-y-0.5">
|
||||
<span className="text-lg">🏠</span>
|
||||
<span>Smart Home</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<div className="flex flex-wrap gap-2.5 pt-4 border-t border-slate-100 dark:border-slate-850/40 mt-auto">
|
||||
<Link href="/frameworks/Arduino" className="inline-flex items-center gap-1.5 px-4 py-2.5 bg-blue-500/10 text-blue-600 dark:text-blue-400 border border-blue-500/20 rounded-xl text-xs font-bold hover:bg-blue-500/20 transition-all">
|
||||
<span>Arduino</span>
|
||||
<ArrowRight className="w-3.5 h-3.5" />
|
||||
</Link>
|
||||
<Link href="/frameworks/MicroPython" className="inline-flex items-center gap-1.5 px-4 py-2.5 bg-purple-500/10 text-purple-600 dark:text-purple-400 border border-purple-500/20 rounded-xl text-xs font-bold hover:bg-purple-500/20 transition-all">
|
||||
<span>MicroPython</span>
|
||||
<ArrowRight className="w-3.5 h-3.5" />
|
||||
</Link>
|
||||
<Link href="/frameworks/ESP-IDF" className="inline-flex items-center gap-1.5 px-4 py-2.5 bg-red-500/10 text-red-600 dark:text-red-450 border border-red-500/20 rounded-xl text-xs font-bold hover:bg-red-500/20 transition-all">
|
||||
<span>ESP-IDF</span>
|
||||
<ArrowRight className="w-3.5 h-3.5" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
{/* Card 2: Specialized Solutions */}
|
||||
<div className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-3xl shadow-xl p-8 border border-slate-200/60 dark:border-slate-800/80 flex flex-col justify-between relative overflow-hidden">
|
||||
<div>
|
||||
<div className="flex items-center gap-3.5 mb-5">
|
||||
<div className="w-12 h-12 bg-purple-500/10 border border-purple-500/20 rounded-2xl flex items-center justify-center text-purple-500">
|
||||
<Sparkles className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-display font-extrabold text-slate-850 dark:text-slate-100">
|
||||
Soluções Especializadas
|
||||
</h3>
|
||||
<p className="text-[10px] font-bold text-purple-500 uppercase tracking-wider leading-none">Domínios Avançados</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400 leading-relaxed mb-6 font-semibold">
|
||||
Projetos complexos de IoT demandam bibliotecas altamente otimizadas na arquitetura.
|
||||
A Espressif fornece extensões prontas como o
|
||||
<strong className="text-slate-750 dark:text-slate-300">ESP-ADF</strong> para processamento de áudio digital,
|
||||
<strong className="text-slate-750 dark:text-slate-300">ESP-WHO</strong> para processamento de imagem por câmera,
|
||||
<strong className="text-slate-750 dark:text-slate-300">ESP-NN</strong> para redes neurais locais e
|
||||
<strong className="text-slate-750 dark:text-slate-300">ESP-Matter</strong> para automação residencial.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-2.5 pt-4 border-t border-slate-100 dark:border-slate-850/40 mt-auto">
|
||||
<Link href="/frameworks/ESP-ADF" className="inline-flex items-center gap-1.5 px-3.5 py-2 bg-slate-150/45 dark:bg-slate-850 text-slate-700 dark:text-slate-300 border border-slate-200/60 dark:border-slate-850/60 rounded-xl text-xs font-bold hover:bg-slate-200 dark:hover:bg-slate-800 transition-all">
|
||||
<Music className="w-3.5 h-3.5 text-blue-500" />
|
||||
<span>Áudio</span>
|
||||
</Link>
|
||||
<Link href="/frameworks/ESP-WHO" className="inline-flex items-center gap-1.5 px-3.5 py-2 bg-slate-150/45 dark:bg-slate-850 text-slate-700 dark:text-slate-300 border border-slate-200/60 dark:border-slate-850/60 rounded-xl text-xs font-bold hover:bg-slate-200 dark:hover:bg-slate-800 transition-all">
|
||||
<Camera className="w-3.5 h-3.5 text-orange-500" />
|
||||
<span>Visão</span>
|
||||
</Link>
|
||||
<Link href="/frameworks/ESP-NN" className="inline-flex items-center gap-1.5 px-3.5 py-2 bg-slate-150/45 dark:bg-slate-850 text-slate-700 dark:text-slate-300 border border-slate-200/60 dark:border-slate-850/60 rounded-xl text-xs font-bold hover:bg-slate-200 dark:hover:bg-slate-800 transition-all">
|
||||
<Brain className="w-3.5 h-3.5 text-teal-500" />
|
||||
<span>Inteligência Artificial</span>
|
||||
</Link>
|
||||
<Link href="/frameworks/ESP-Matter" className="inline-flex items-center gap-1.5 px-3.5 py-2 bg-slate-150/45 dark:bg-slate-850 text-slate-700 dark:text-slate-300 border border-slate-200/60 dark:border-slate-850/60 rounded-xl text-xs font-bold hover:bg-slate-200 dark:hover:bg-slate-800 transition-all">
|
||||
<Home className="w-3.5 h-3.5 text-yellow-500" />
|
||||
<span>Matter</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1 +1,47 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
--font-sans: var(--font-inter), ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||
--font-display: var(--font-outfit), ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||
|
||||
/* Premium dark mode palette accents */
|
||||
--color-brand-purple: #8b5cf6;
|
||||
--color-brand-blue: #3b82f6;
|
||||
--color-brand-pink: #ec4899;
|
||||
}
|
||||
|
||||
/* Enable class-based dark mode selector in Tailwind v4 */
|
||||
@variant dark (&:where(.dark, .dark *));
|
||||
|
||||
@layer base {
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-slate-50 text-slate-900 transition-colors duration-300;
|
||||
}
|
||||
|
||||
body.dark {
|
||||
@apply bg-slate-950 text-slate-50;
|
||||
}
|
||||
|
||||
/* Sleek selection highlighting */
|
||||
::selection {
|
||||
@apply bg-purple-500/30 text-purple-200;
|
||||
}
|
||||
|
||||
/* Scrollbar customization for a premium, non-default OS look */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
@apply bg-transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
@apply bg-slate-300 dark:bg-slate-800 rounded-full hover:bg-slate-400 dark:hover:bg-slate-700 transition-colors;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,18 @@
|
||||
import { Roboto } from "next/font/google";
|
||||
import { Outfit, Inter } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import Providers from "@/components/Providers";
|
||||
|
||||
const roboto = Roboto({ subsets: ["latin"] });
|
||||
const outfit = Outfit({
|
||||
subsets: ["latin"],
|
||||
variable: "--font-outfit",
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
variable: "--font-inter",
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
export const metadata = {
|
||||
title: "ESPDocs",
|
||||
@@ -11,9 +22,11 @@ export const metadata = {
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="pt-BR">
|
||||
<body className={`${roboto.className} font-sans`}>
|
||||
{children}
|
||||
<html lang="pt-BR" className={`${outfit.variable} ${inter.variable}`} suppressHydrationWarning>
|
||||
<body className="font-sans antialiased bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-slate-50 transition-colors duration-300">
|
||||
<Providers>
|
||||
{children}
|
||||
</Providers>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -1,176 +1,252 @@
|
||||
"use client";
|
||||
|
||||
import Header from "@/components/Header";
|
||||
import Footer from "@/components/Footer";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
SlidersHorizontal,
|
||||
ShoppingBag,
|
||||
Table2,
|
||||
Cpu,
|
||||
Layers,
|
||||
Scale,
|
||||
FileText,
|
||||
Code,
|
||||
RefreshCw,
|
||||
Globe,
|
||||
ArrowRight,
|
||||
Terminal,
|
||||
Compass,
|
||||
Info
|
||||
} from "lucide-react";
|
||||
|
||||
export default function Home() {
|
||||
const resources = [
|
||||
{name: "Seletor Inteligente", description: "Responda perguntas sobre seu projeto e descubra qual série ESP32 é ideal para você", icon: "🎯"},
|
||||
{name: "Catálogo de Placas", description: "Encontre placas de desenvolvimento ESP32 verificadas com filtros avançados e links seguros para compra", icon: "🛒"},
|
||||
{name: "Especificações Organizadas", description: "Especificações técnicas de cada série ESP32 organizadas em tabelas comparativas em português", icon: "🔧"},
|
||||
{name: "Diagramas de Conexões", description: "Visualize diagramas interativos dos componentes e pinos de cada série ESP32", icon: "📊"},
|
||||
{name: "Informações sobre Frameworks", description: "Conheça frameworks como ESP-IDF, Arduino e MicroPython com descrições, casos de uso e exemplos", icon: "⚡"},
|
||||
{name: "Comparação de Séries", description: "Compare lado a lado as especificações de diferentes séries ESP32 em uma única visualização", icon: "📈"},
|
||||
{name: "Links para Documentação", description: "Acesso direto aos datasheets, manuais técnicos e guias oficiais da Espressif (em inglês)", icon: "📄"},
|
||||
{name: "Exemplos de Código", description: "Snippets de código básico para diferentes frameworks com links para repositórios oficiais", icon: "💻"},
|
||||
{name: "Informações Atualizadas", description: "Dados atualizados sobre as séries e frameworks mais recentes do ecossistema ESP32", icon: "🔄"},
|
||||
{name: "Interface em Português", description: "Navegação e organização de informações totalmente em português para facilitar a consulta", icon: "📚"},
|
||||
]
|
||||
{
|
||||
name: "Seletor Inteligente",
|
||||
description: "Responda perguntas rápidas sobre seu projeto e descubra a série ESP32 ideal em segundos.",
|
||||
icon: <SlidersHorizontal className="w-6 h-6" />,
|
||||
colorClass: "text-purple-600 bg-purple-50 dark:bg-purple-950/40 dark:text-purple-400"
|
||||
},
|
||||
{
|
||||
name: "Catálogo de Placas",
|
||||
description: "Encontre placas de desenvolvimento ESP32 verificadas com filtros avançados e links seguros.",
|
||||
icon: <ShoppingBag className="w-6 h-6" />,
|
||||
colorClass: "text-blue-600 bg-blue-50 dark:bg-blue-950/40 dark:text-blue-400"
|
||||
},
|
||||
{
|
||||
name: "Especificações Organizadas",
|
||||
description: "Especificações técnicas completas de cada microcontrolador organizadas para fácil leitura.",
|
||||
icon: <Table2 className="w-6 h-6" />,
|
||||
colorClass: "text-pink-600 bg-pink-50 dark:bg-pink-950/40 dark:text-pink-400"
|
||||
},
|
||||
{
|
||||
name: "Diagramas de Conexões",
|
||||
description: "Visualize diagramas de pinos interativos e mapeamentos de periféricos de forma dinâmica.",
|
||||
icon: <Cpu className="w-6 h-6" />,
|
||||
colorClass: "text-emerald-600 bg-emerald-50 dark:bg-emerald-950/40 dark:text-emerald-400"
|
||||
},
|
||||
{
|
||||
name: "Suporte a Frameworks",
|
||||
description: "Encontre casos de uso, documentações e snippets para ESP-IDF, Arduino e MicroPython.",
|
||||
icon: <Layers className="w-6 h-6" />,
|
||||
colorClass: "text-amber-600 bg-amber-50 dark:bg-amber-950/40 dark:text-amber-400"
|
||||
},
|
||||
{
|
||||
name: "Comparação de Modelos",
|
||||
description: "Compare lado a lado as especificações de múltiplos chips em uma visualização única.",
|
||||
icon: <Scale className="w-6 h-6" />,
|
||||
colorClass: "text-cyan-600 bg-cyan-50 dark:bg-cyan-950/40 dark:text-cyan-400"
|
||||
},
|
||||
{
|
||||
name: "Links para Documentação",
|
||||
description: "Acesso direto aos datasheets oficiais, manuais de referência e guias da Espressif.",
|
||||
icon: <FileText className="w-6 h-6" />,
|
||||
colorClass: "text-red-600 bg-red-50 dark:bg-red-950/40 dark:text-red-400"
|
||||
},
|
||||
{
|
||||
name: "Exemplos de Código",
|
||||
description: "Gabaritos e snippets funcionais de código para iniciar seus projetos rapidamente.",
|
||||
icon: <Code className="w-6 h-6" />,
|
||||
colorClass: "text-indigo-600 bg-indigo-50 dark:bg-indigo-950/40 dark:text-indigo-400"
|
||||
},
|
||||
{
|
||||
name: "Informações Atualizadas",
|
||||
description: "Dados sempre revisados e sincronizados com os lançamentos mais recentes de silício.",
|
||||
icon: <RefreshCw className="w-6 h-6" />,
|
||||
colorClass: "text-teal-600 bg-teal-50 dark:bg-teal-950/40 dark:text-teal-400"
|
||||
},
|
||||
{
|
||||
name: "Interface em Português",
|
||||
description: "Navegação, termos técnicos e explicações adaptadas ao nosso idioma nativo.",
|
||||
icon: <Globe className="w-6 h-6" />,
|
||||
colorClass: "text-sky-600 bg-sky-50 dark:bg-sky-950/40 dark:text-sky-400"
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="bg-linear-to-br from-blue-50 via-white to-purple-50 min-h-screen">
|
||||
return (
|
||||
<div className="bg-gradient-to-br from-slate-50 via-white to-purple-50/40 dark:from-slate-950 dark:via-slate-950 dark:to-slate-900 min-h-screen text-slate-900 dark:text-slate-100 transition-colors duration-300">
|
||||
<Header />
|
||||
|
||||
<main className="px-4 pt-12 pb-16 max-w-7xl mx-auto">
|
||||
{/* Hero Section */}
|
||||
<section className="text-center mb-20">
|
||||
<h1 className="text-5xl md:text-7xl lg:text-8xl font-black bg-linear-to-r from-blue-600 via-purple-600 to-pink-600 bg-clip-text text-transparent mb-6 tracking-tight">
|
||||
ESPDocs
|
||||
</h1>
|
||||
<main className="px-6 pt-16 pb-24 max-w-7xl mx-auto">
|
||||
{/* Hero Section */}
|
||||
<section className="text-center mb-24 max-w-5xl mx-auto">
|
||||
<h1 className="text-5xl md:text-7xl lg:text-8xl font-display font-extrabold bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500 bg-clip-text text-transparent mb-6 tracking-tight leading-tight select-none">
|
||||
ESPDocs
|
||||
</h1>
|
||||
|
||||
<h2 className="text-xl md:text-2xl lg:text-3xl text-gray-800 font-bold mb-6 max-w-4xl mx-auto leading-tight">
|
||||
Organização e acesso facilitado ao ecossistema ESP32
|
||||
</h2>
|
||||
<h2 className="text-xl md:text-3xl text-slate-800 dark:text-slate-200 font-bold mb-6 max-w-3xl mx-auto leading-snug">
|
||||
Organização e acesso facilitado ao ecossistema ESP32
|
||||
</h2>
|
||||
|
||||
<p className="text-base md:text-lg text-gray-600 max-w-2xl mx-auto leading-relaxed mb-10">
|
||||
Plataforma não oficial com interface em português que organiza e centraliza
|
||||
informações sobre ESP32. Compare especificações, visualize diagramas e acesse
|
||||
a documentação oficial da Espressif.
|
||||
</p>
|
||||
<p className="text-base md:text-lg text-slate-500 dark:text-slate-400 max-w-2xl mx-auto leading-relaxed mb-12">
|
||||
Plataforma independente criada para desenvolvedores que buscam comparar especificações, consultar pinouts, selecionar chips ideais e acessar referências em português de forma rápida e intuitiva.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
|
||||
<Link
|
||||
className="group inline-flex items-center gap-3 bg-linear-to-r from-green-600 to-emerald-600 text-white px-8 py-4 rounded-xl font-bold text-base shadow-lg hover:shadow-2xl transform hover:-translate-y-1 transition-all duration-300"
|
||||
href="/seletor"
|
||||
>
|
||||
<span className="text-xl">🎯</span>
|
||||
<span>Encontrar ESP32 Ideal</span>
|
||||
<svg className="w-5 h-5 transform group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</Link>
|
||||
{/* Botões de Ação */}
|
||||
<div className="flex flex-wrap gap-4 justify-center items-center">
|
||||
<Link
|
||||
className="group inline-flex items-center gap-2.5 bg-gradient-to-r from-purple-600 to-indigo-600 text-white px-7 py-3.5 rounded-xl font-bold text-sm shadow-lg hover:shadow-purple-500/20 hover:-translate-y-0.5 transition-all duration-300 cursor-pointer active:scale-95"
|
||||
href="/seletor"
|
||||
>
|
||||
<Compass className="w-4 h-4" />
|
||||
<span>Encontrar ESP32 Ideal</span>
|
||||
<ArrowRight className="w-4 h-4 transform group-hover:translate-x-1 transition-transform" />
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
className="group inline-flex items-center gap-3 bg-linear-to-r from-blue-600 to-purple-600 text-white px-8 py-4 rounded-xl font-bold text-base shadow-lg hover:shadow-2xl transform hover:-translate-y-1 transition-all duration-300"
|
||||
href="/series"
|
||||
>
|
||||
<span className="text-xl">📚</span>
|
||||
<span>Explorar Séries ESP32</span>
|
||||
</Link>
|
||||
<Link
|
||||
className="group inline-flex items-center gap-2.5 bg-slate-900 hover:bg-slate-800 dark:bg-slate-800 dark:hover:bg-slate-700 text-white px-7 py-3.5 rounded-xl font-bold text-sm shadow-md hover:-translate-y-0.5 transition-all duration-300 cursor-pointer active:scale-95"
|
||||
href="/series"
|
||||
>
|
||||
<Cpu className="w-4 h-4" />
|
||||
<span>Explorar Séries</span>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
className="group inline-flex items-center gap-3 bg-white text-gray-800 px-8 py-4 rounded-xl font-bold text-base shadow-lg hover:shadow-xl border-2 border-gray-200 hover:border-purple-500 transform hover:-translate-y-1 transition-all duration-300"
|
||||
href="/frameworks"
|
||||
>
|
||||
<span className="text-xl">⚡</span>
|
||||
<span>Frameworks</span>
|
||||
</Link>
|
||||
<Link
|
||||
className="group inline-flex items-center gap-2.5 bg-white dark:bg-slate-900 text-slate-700 dark:text-slate-300 px-7 py-3.5 rounded-xl font-bold text-sm shadow-xs border border-slate-200 dark:border-slate-800 hover:border-purple-500 dark:hover:border-purple-400 hover:text-purple-600 dark:hover:text-purple-400 hover:-translate-y-0.5 transition-all duration-300 cursor-pointer active:scale-95"
|
||||
href="/frameworks"
|
||||
>
|
||||
<Terminal className="w-4 h-4" />
|
||||
<span>Frameworks</span>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
className="group inline-flex items-center gap-3 bg-white text-gray-800 px-8 py-4 rounded-xl font-bold text-base shadow-lg hover:shadow-xl border-2 border-gray-200 hover:border-purple-500 transform hover:-translate-y-1 transition-all duration-300"
|
||||
href="/catalogo"
|
||||
>
|
||||
<span className="text-xl">🛒</span>
|
||||
<span>Catálogo</span>
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
<Link
|
||||
className="group inline-flex items-center gap-2.5 bg-white dark:bg-slate-900 text-slate-700 dark:text-slate-300 px-7 py-3.5 rounded-xl font-bold text-sm shadow-xs border border-slate-200 dark:border-slate-800 hover:border-purple-500 dark:hover:border-purple-400 hover:text-purple-600 dark:hover:text-purple-400 hover:-translate-y-0.5 transition-all duration-300 cursor-pointer active:scale-95"
|
||||
href="/catalogo"
|
||||
>
|
||||
<ShoppingBag className="w-4 h-4" />
|
||||
<span>Placas</span>
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Recursos Section */}
|
||||
<section className="mb-20">
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-gray-900 mb-4">Recursos da Plataforma</h2>
|
||||
<p className="text-lg text-gray-600 max-w-2xl mx-auto">Ferramentas e informações organizadas para facilitar seu trabalho com ESP32</p>
|
||||
</div>
|
||||
{/* Recursos Section */}
|
||||
<section className="mb-24">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-3xl md:text-4xl font-display font-extrabold text-slate-800 dark:text-slate-100 mb-4">
|
||||
Recursos da Plataforma
|
||||
</h2>
|
||||
<p className="text-base text-slate-500 dark:text-slate-400 max-w-xl mx-auto leading-relaxed">
|
||||
Conjunto completo de utilitários projetados para maximizar a produtividade e eliminar atritos no desenvolvimento de firmware.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{resources.map((resource, index) => (
|
||||
<div
|
||||
key={resource.name}
|
||||
className="group bg-white p-6 rounded-2xl shadow-md hover:shadow-xl transition-all duration-300 border-2 border-gray-100 hover:border-purple-200"
|
||||
style={{ animationDelay: `${index * 50}ms` }}
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="text-4xl shrink-0 transform group-hover:scale-110 transition-transform">
|
||||
{resource.icon}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-lg font-bold text-gray-900 mb-2 group-hover:text-purple-600 transition-colors">
|
||||
{resource.name}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 leading-relaxed">
|
||||
{resource.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{resources.map((resource) => (
|
||||
<div
|
||||
key={resource.name}
|
||||
className="group bg-white/70 dark:bg-slate-900/40 backdrop-blur-md p-6 rounded-2xl border border-slate-200/60 dark:border-slate-800/80 hover:border-purple-500/40 dark:hover:border-purple-400/40 shadow-xs hover:shadow-xl hover:shadow-purple-500/5 hover:-translate-y-1 transition-all duration-300"
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className={`w-12 h-12 rounded-xl flex items-center justify-center shrink-0 transform group-hover:scale-105 transition-transform duration-300 shadow-xs ${resource.colorClass}`}>
|
||||
{resource.icon}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-base font-bold text-slate-800 dark:text-slate-100 mb-2 group-hover:text-purple-600 dark:group-hover:text-purple-400 transition-colors">
|
||||
{resource.name}
|
||||
</h3>
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400 leading-relaxed">
|
||||
{resource.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Ciência Embarcada Section */}
|
||||
<section className="mb-12">
|
||||
<div className="bg-linear-to-br from-white to-purple-50 rounded-3xl shadow-xl p-8 md:p-12 border-2 border-purple-100">
|
||||
{/* Logo e Link do Ciência Embarcada */}
|
||||
<div className="flex flex-col items-center mb-6">
|
||||
{/* Ciência Embarcada Section */}
|
||||
<section className="mb-8">
|
||||
<div className="bg-gradient-to-br from-white to-purple-50/50 dark:from-slate-900/40 dark:to-purple-950/10 rounded-3xl shadow-xs border border-slate-200/80 dark:border-slate-800/80 p-8 md:p-12 relative overflow-hidden">
|
||||
{/* Background soft glow decoration */}
|
||||
<div className="absolute right-0 bottom-0 w-80 h-80 bg-purple-500/5 rounded-full blur-3xl pointer-events-none" />
|
||||
|
||||
<div className="flex flex-col items-center text-center relative z-10 max-w-3xl mx-auto">
|
||||
<a
|
||||
href="https://cienciaembarcada.com.br"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="group"
|
||||
className="group mb-6"
|
||||
>
|
||||
<img
|
||||
src="/logo.png"
|
||||
alt="Ciência Embarcada"
|
||||
className="h-16 md:h-20 mb-4 transition-transform duration-300 group-hover:scale-110"
|
||||
alt="Ciência Embarcada Logo"
|
||||
className="h-16 md:h-20 mb-2 transition-transform duration-300 group-hover:scale-105"
|
||||
/>
|
||||
</a>
|
||||
<p className="text-lg font-semibold text-gray-800 mb-2">
|
||||
Conteúdo por <a
|
||||
|
||||
<p className="text-lg font-bold text-slate-800 dark:text-slate-200 mb-3">
|
||||
Conteúdo e Curadoria por{' '}
|
||||
<a
|
||||
href="https://cienciaembarcada.com.br"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-purple-600 hover:text-purple-800 transition-colors"
|
||||
className="text-purple-600 dark:text-purple-400 hover:underline transition-colors font-extrabold"
|
||||
>
|
||||
Ciência Embarcada
|
||||
</a>
|
||||
</p>
|
||||
<p className="text-sm text-gray-600 max-w-xl text-center mb-4">
|
||||
Artigos sobre ESP32, Eletrônica, IoT, Cibersegurança e muito mais.
|
||||
Visite o blog para aprender com tutoriais práticos e projetos reais!
|
||||
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400 leading-relaxed mb-8 max-w-xl">
|
||||
Acesse artigos profundos sobre programação de microcontroladores, design de hardware, Internet das Coisas (IoT), laboratórios práticos e projetos reais em nosso blog parceiro oficial.
|
||||
</p>
|
||||
|
||||
<a
|
||||
href="https://cienciaembarcada.com.br"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 bg-linear-to-r from-blue-600 to-purple-600 text-white px-6 py-3 rounded-xl font-semibold hover:shadow-lg transform hover:-translate-y-1 transition-all duration-300"
|
||||
className="inline-flex items-center gap-2 bg-gradient-to-r from-purple-600 to-indigo-600 text-white px-6 py-3 rounded-xl font-bold text-sm shadow-md hover:shadow-purple-500/10 hover:-translate-y-0.5 transition-all duration-300 cursor-pointer active:scale-95"
|
||||
>
|
||||
🚀 Visitar Blog
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
||||
</svg>
|
||||
<span>Visitar Blog Ciência Embarcada</span>
|
||||
<ExternalLinkIcon />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Disclaimer */}
|
||||
<div className="border-t-2 border-purple-200 pt-6 mt-6">
|
||||
<div className="bg-blue-50 border-l-4 border-blue-500 p-4 rounded-r-lg">
|
||||
<div className="flex items-start gap-3">
|
||||
<svg className="w-5 h-5 text-blue-600 mt-0.5 shrink-0" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" />
|
||||
</svg>
|
||||
<p className="text-sm text-gray-700 leading-relaxed">
|
||||
<strong className="font-semibold text-gray-900">Plataforma não oficial:</strong> O ESPDocs é independente e sem afiliação com a Espressif Systems.
|
||||
Organiza informações públicas e links para documentação oficial. Para dados técnicos críticos,
|
||||
consulte sempre os datasheets e manuais oficiais da Espressif.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
{/* Disclaimer */}
|
||||
<div className="border-t border-slate-200/80 dark:border-slate-800/80 pt-8 mt-10">
|
||||
<div className="bg-slate-100/60 dark:bg-slate-900/60 border-l-4 border-purple-500 p-5 rounded-r-2xl">
|
||||
<div className="flex gap-4">
|
||||
<Info className="w-5 h-5 text-purple-600 dark:text-purple-400 mt-0.5 shrink-0" />
|
||||
<p className="text-xs leading-relaxed text-slate-500 dark:text-slate-400">
|
||||
<strong className="font-bold text-slate-700 dark:text-slate-200">Plataforma independente e não oficial:</strong> O ESPDocs é um projeto educativo que compila, filtra e expõe dados públicos e links de documentações oficiais fornecidas pela fabricante Espressif Systems. Para projetos industriais ou decisões de engenharia críticas, sempre consulte a documentação técnica nativa do fabricante.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* Local mini-icon component for external link */
|
||||
function ExternalLinkIcon() {
|
||||
return (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -6,420 +6,495 @@ import { notFound } from "next/navigation";
|
||||
import seriesData from "@/public/series.json";
|
||||
import ConnectionsDiagram from "@/components/ConnectionsDiagram";
|
||||
import SeriesTabMenu from "@/components/SeriesTabMenu";
|
||||
import {
|
||||
Cpu,
|
||||
Activity,
|
||||
Database,
|
||||
Wifi,
|
||||
Bluetooth,
|
||||
Battery,
|
||||
Sparkles,
|
||||
Home,
|
||||
ArrowRight,
|
||||
ArrowLeft,
|
||||
ExternalLink,
|
||||
Scale,
|
||||
FileText,
|
||||
Code,
|
||||
BookOpen,
|
||||
AlertTriangle,
|
||||
Moon,
|
||||
Plug,
|
||||
Compass,
|
||||
Sliders,
|
||||
MapPin,
|
||||
Volume2,
|
||||
Fingerprint,
|
||||
MessageSquare,
|
||||
Repeat,
|
||||
Share2,
|
||||
Music,
|
||||
Clock,
|
||||
HardDrive,
|
||||
Folder,
|
||||
Thermometer,
|
||||
ChevronRight,
|
||||
Lightbulb
|
||||
} from "lucide-react";
|
||||
|
||||
export default async function SerieDetail({ params }) {
|
||||
const { key } = await params;
|
||||
const serie = seriesData[key];
|
||||
const { key } = await params;
|
||||
const serie = seriesData[key];
|
||||
|
||||
if (!serie) {
|
||||
notFound();
|
||||
}
|
||||
if (!serie) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
let conexoes = [];
|
||||
try {
|
||||
conexoes = require(`@/public/conexoes/${key}.json`);
|
||||
} catch (error) {
|
||||
console.log(`Arquivo de conexões não encontrado para ${key}`);
|
||||
}
|
||||
let conexoes = [];
|
||||
try {
|
||||
conexoes = require(`@/public/conexoes/${key}.json`);
|
||||
} catch (error) {
|
||||
console.log(`Arquivo de conexões não encontrado para ${key}`);
|
||||
}
|
||||
|
||||
const seriesKeys = Object.keys(seriesData);
|
||||
const currentIndex = seriesKeys.indexOf(key);
|
||||
const previousKey = currentIndex > 0 ? seriesKeys[currentIndex - 1] : null;
|
||||
const nextKey = currentIndex < seriesKeys.length - 1 ? seriesKeys[currentIndex + 1] : null;
|
||||
const seriesKeys = Object.keys(seriesData);
|
||||
const currentIndex = seriesKeys.indexOf(key);
|
||||
const previousKey = currentIndex > 0 ? seriesKeys[currentIndex - 1] : null;
|
||||
const nextKey = currentIndex < seriesKeys.length - 1 ? seriesKeys[currentIndex + 1] : null;
|
||||
|
||||
const processadorSpecs = [
|
||||
{ label: "Arquitetura", value: serie.arquitetura, icon: "🔧" },
|
||||
{ label: "Núcleos", value: serie.nucleos, icon: "⚙️" },
|
||||
{ label: "Frequência", value: serie.frequencia, icon: "🚀" },
|
||||
{ label: "Coprocessador ULP", value: serie.coprocessador_ulp || "Não", icon: "💤" },
|
||||
{ label: "Aceleradores IA", value: serie.aceleradores_ia || "Não", icon: "🤖" },
|
||||
];
|
||||
const processadorSpecs = [
|
||||
{ label: "Arquitetura", value: serie.arquitetura, icon: <Cpu className="w-5 h-5 text-blue-500" /> },
|
||||
{ label: "Núcleos", value: serie.nucleos, icon: <Sliders className="w-5 h-5 text-blue-500" /> },
|
||||
{ label: "Frequência", value: serie.frequencia, icon: <Activity className="w-5 h-5 text-blue-500" /> },
|
||||
{ label: "Coprocessador ULP", value: serie.coprocessador_ulp || "Não", icon: <Moon className="w-5 h-5 text-blue-500" /> },
|
||||
{ label: "Aceleradores IA", value: serie.aceleradores_ia || "Não", icon: <Sparkles className="w-5 h-5 text-purple-500" /> },
|
||||
];
|
||||
|
||||
const conectividadeSpecs = [
|
||||
{ label: "Wi-Fi", value: serie.wifi || "Não", icon: "📶" },
|
||||
{ label: "Bluetooth", value: serie.bluetooth || "Não", icon: "📱" },
|
||||
{ label: "Zigbee/Thread", value: serie.zigbee_thread || "Não", icon: "🌐" },
|
||||
{ label: "Matter", value: serie.matter || "Não", icon: "🏠" },
|
||||
{ label: "Ethernet", value: serie.ethernet || serie.ethernet_mac || "Não", icon: "🔌" },
|
||||
{ label: "CAN", value: serie.can || "Não", icon: "🚗" },
|
||||
];
|
||||
const conectividadeSpecs = [
|
||||
{ label: "Wi-Fi", value: serie.wifi || "Não", icon: <Wifi className="w-5 h-5 text-indigo-500" /> },
|
||||
{ label: "Bluetooth", value: serie.bluetooth || "Não", icon: <Bluetooth className="w-5 h-5 text-indigo-500" /> },
|
||||
{ label: "Zigbee/Thread", value: serie.zigbee_thread || "Não", icon: <Compass className="w-5 h-5 text-indigo-500" /> },
|
||||
{ label: "Matter", value: serie.matter || "Não", icon: <Home className="w-5 h-5 text-indigo-500" /> },
|
||||
{ label: "Ethernet", value: serie.ethernet || serie.ethernet_mac || "Não", icon: <Plug className="w-5 h-5 text-indigo-500" /> },
|
||||
{ label: "CAN", value: serie.can || "Não", icon: <Activity className="w-5 h-5 text-indigo-500" /> },
|
||||
];
|
||||
|
||||
const memoriaSpecs = [
|
||||
{ label: "SRAM", value: serie.memoria_sram, icon: "💾" },
|
||||
{ label: "SRAM RTC", value: serie.memoria_sram_rtc, icon: "⏰" },
|
||||
{ label: "ROM", value: serie.memoria_rom, icon: "📀" },
|
||||
{ label: "Flash Externa", value: serie.flash_externa, icon: "💿" },
|
||||
{ label: "PSRAM Externa", value: serie.psram_externa || "Não", icon: "🎯" },
|
||||
];
|
||||
const memoriaSpecs = [
|
||||
{ label: "SRAM", value: serie.memoria_sram, icon: <Database className="w-5 h-5 text-pink-500" /> },
|
||||
{ label: "SRAM RTC", value: serie.memoria_sram_rtc, icon: <Clock className="w-5 h-5 text-pink-500" /> },
|
||||
{ label: "ROM", value: serie.memoria_rom, icon: <Folder className="w-5 h-5 text-pink-500" /> },
|
||||
{ label: "Flash Externa", value: serie.flash_externa, icon: <HardDrive className="w-5 h-5 text-pink-500" /> },
|
||||
{ label: "PSRAM Externa", value: serie.psram_externa || "Não", icon: <Sliders className="w-5 h-5 text-pink-500" /> },
|
||||
];
|
||||
|
||||
const perifericos = [
|
||||
{ label: "GPIO", value: serie.gpio, icon: "📍" },
|
||||
{ label: "ADC", value: serie.adc, icon: "📊" },
|
||||
{ label: "DAC", value: serie.dac || "Não", icon: "🔊" },
|
||||
{ label: "Touch", value: serie.touch || "Não", icon: "👆" },
|
||||
{ label: "UART", value: serie.uart, icon: "💬" },
|
||||
{ label: "SPI", value: serie.spi, icon: "🔄" },
|
||||
{ label: "I2C", value: serie.i2c, icon: "🔗" },
|
||||
{ label: "I2S", value: serie.i2s, icon: "🎵" },
|
||||
{ label: "PWM", value: serie.pwm, icon: "〰️" },
|
||||
{ label: "USB", value: serie.usb || "Não", icon: "🔌" },
|
||||
];
|
||||
const perifericos = [
|
||||
{ label: "GPIO", value: serie.gpio, icon: <MapPin className="w-5 h-5 text-emerald-500" /> },
|
||||
{ label: "ADC", value: serie.adc, icon: <Activity className="w-5 h-5 text-emerald-500" /> },
|
||||
{ label: "DAC", value: serie.dac || "Não", icon: <Volume2 className="w-5 h-5 text-emerald-500" /> },
|
||||
{ label: "Touch", value: serie.touch || "Não", icon: <Fingerprint className="w-5 h-5 text-emerald-500" /> },
|
||||
{ label: "UART", value: serie.uart, icon: <MessageSquare className="w-5 h-5 text-emerald-500" /> },
|
||||
{ label: "SPI", value: serie.spi, icon: <Repeat className="w-5 h-5 text-emerald-500" /> },
|
||||
{ label: "I2C", value: serie.i2c, icon: <Share2 className="w-5 h-5 text-emerald-500" /> },
|
||||
{ label: "I2S", value: serie.i2s, icon: <Music className="w-5 h-5 text-emerald-500" /> },
|
||||
{ label: "PWM", value: serie.pwm, icon: <Sliders className="w-5 h-5 text-emerald-500" /> },
|
||||
{ label: "USB", value: serie.usb || "Não", icon: <Plug className="w-5 h-5 text-emerald-500" /> },
|
||||
];
|
||||
|
||||
const interfacesEspeciais = [];
|
||||
if (serie.lcd) interfacesEspeciais.push({ label: "LCD", value: serie.lcd, icon: "🖥️" });
|
||||
if (serie.camera) interfacesEspeciais.push({ label: "Camera", value: serie.camera, icon: "📷" });
|
||||
if (serie.mipi_csi) interfacesEspeciais.push({ label: "MIPI CSI", value: serie.mipi_csi, icon: "📹" });
|
||||
if (serie.mipi_dsi) interfacesEspeciais.push({ label: "MIPI DSI", value: serie.mipi_dsi, icon: "🖼️" });
|
||||
if (serie.sdio) interfacesEspeciais.push({ label: "SDIO", value: serie.sdio, icon: "💳" });
|
||||
if (serie.sensor_hall) interfacesEspeciais.push({ label: "Sensor Hall", value: serie.sensor_hall, icon: "🧲" });
|
||||
if (serie.sensor_temperatura) interfacesEspeciais.push({ label: "Sensor Temperatura", value: serie.sensor_temperatura, icon: "🌡️" });
|
||||
const interfacesEspeciais = [];
|
||||
if (serie.lcd) interfacesEspeciais.push({ label: "LCD", value: serie.lcd, icon: <Sliders className="w-5 h-5 text-cyan-500" /> });
|
||||
if (serie.camera) interfacesEspeciais.push({ label: "Câmera", value: serie.camera, icon: <Sliders className="w-5 h-5 text-cyan-500" /> });
|
||||
if (serie.mipi_csi) interfacesEspeciais.push({ label: "MIPI CSI", value: serie.mipi_csi, icon: <Sliders className="w-5 h-5 text-cyan-500" /> });
|
||||
if (serie.mipi_dsi) interfacesEspeciais.push({ label: "MIPI DSI", value: serie.mipi_dsi, icon: <Sliders className="w-5 h-5 text-cyan-500" /> });
|
||||
if (serie.sdio) interfacesEspeciais.push({ label: "SDIO", value: serie.sdio, icon: <Sliders className="w-5 h-5 text-cyan-500" /> });
|
||||
if (serie.sensor_hall) interfacesEspeciais.push({ label: "Sensor Hall", value: serie.sensor_hall, icon: <Sliders className="w-5 h-5 text-cyan-500" /> });
|
||||
if (serie.sensor_temperatura) interfacesEspeciais.push({ label: "Sensor Temperatura", value: serie.sensor_temperatura, icon: <Thermometer className="w-5 h-5 text-cyan-500" /> });
|
||||
|
||||
// Consumo de Energia
|
||||
const consumoEnergia = [];
|
||||
if (serie.consumo_energia) {
|
||||
Object.entries(serie.consumo_energia).forEach(([modo, consumo]) => {
|
||||
const modoLabels = {
|
||||
'active': 'Modo Ativo',
|
||||
'active_cpu_240mhz': 'Ativo (CPU 240MHz)',
|
||||
'active_cpu_160mhz': 'Ativo (CPU 160MHz)',
|
||||
'active_wifi_tx': 'Ativo (Wi-Fi TX)',
|
||||
'active_wifi_tx_peak': 'Ativo (Wi-Fi TX Pico)',
|
||||
'active_wifi': 'Ativo (Wi-Fi)',
|
||||
'active_wifi6': 'Ativo (Wi-Fi 6)',
|
||||
'modem_sleep': 'Modem Sleep',
|
||||
'modem_sleep_cpu_160mhz': 'Modem Sleep (CPU 160MHz)',
|
||||
'light_sleep': 'Light Sleep',
|
||||
'deep_sleep': 'Deep Sleep',
|
||||
'hibernation': 'Hibernação'
|
||||
};
|
||||
// Consumo de Energia
|
||||
const consumoEnergia = [];
|
||||
if (serie.consumo_energia) {
|
||||
Object.entries(serie.consumo_energia).forEach(([modo, consumo]) => {
|
||||
const modoLabels = {
|
||||
'active': 'Modo Ativo',
|
||||
'active_cpu_240mhz': 'Ativo (CPU 240MHz)',
|
||||
'active_cpu_160mhz': 'Ativo (CPU 160MHz)',
|
||||
'active_wifi_tx': 'Ativo (Wi-Fi TX)',
|
||||
'active_wifi_tx_peak': 'Ativo (Wi-Fi TX Pico)',
|
||||
'active_wifi': 'Ativo (Wi-Fi)',
|
||||
'active_wifi6': 'Ativo (Wi-Fi 6)',
|
||||
'modem_sleep': 'Modem Sleep',
|
||||
'modem_sleep_cpu_160mhz': 'Modem Sleep (CPU 160MHz)',
|
||||
'light_sleep': 'Light Sleep',
|
||||
'deep_sleep': 'Deep Sleep',
|
||||
'hibernation': 'Hibernação'
|
||||
};
|
||||
|
||||
consumoEnergia.push({
|
||||
label: modoLabels[modo] || modo.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()),
|
||||
value: consumo,
|
||||
icon: "🔋"
|
||||
});
|
||||
});
|
||||
}
|
||||
consumoEnergia.push({
|
||||
label: modoLabels[modo] || modo.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()),
|
||||
value: consumo,
|
||||
icon: <Battery className="w-5 h-5 text-teal-500" />
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
id: 'devboard',
|
||||
label: 'Placa de Desenvolvimento',
|
||||
available: serie.placa,
|
||||
content: (
|
||||
<div className="bg-white rounded-3xl shadow-xl p-8 border-2 border-gray-100">
|
||||
<h2 className="text-2xl font-bold text-gray-800 mb-6 pb-3 border-b-2 flex items-center gap-3" style={{ borderColor: serie.cor }}>
|
||||
<span>🔧</span>
|
||||
Placa de Desenvolvimento Oficial
|
||||
</h2>
|
||||
<div className="flex justify-center">
|
||||
<div className="relative w-full max-w-4xl">
|
||||
<Image
|
||||
src={serie.placa}
|
||||
alt={`${key} DevKit - Placa de Desenvolvimento`}
|
||||
width={1200}
|
||||
height={800}
|
||||
className="rounded-xl shadow-lg w-full h-auto"
|
||||
priority
|
||||
/>
|
||||
<p className="text-center text-sm text-gray-600 mt-4">
|
||||
{key} - Placa de desenvolvimento oficial da Espressif
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 'connections',
|
||||
label: 'Diagrama de Conexões',
|
||||
available: conexoes.length > 0,
|
||||
content: (
|
||||
<div className="bg-white rounded-3xl shadow-xl p-8 border-2 border-gray-100">
|
||||
<div className="bg-amber-50 border-l-4 border-amber-500 p-4 mb-6 rounded-lg">
|
||||
<div className="flex items-start gap-3">
|
||||
<span className="text-2xl">⚠️</span>
|
||||
<div>
|
||||
<h4 className="font-bold text-amber-900 mb-1">Diagrama do SoC (System on Chip)</h4>
|
||||
<p className="text-amber-800 text-sm leading-relaxed">
|
||||
Este diagrama mostra as conexões e periféricos do chip <strong>{key}</strong> (SoC),
|
||||
não da placa de desenvolvimento completa. As placas de desenvolvimento podem ter
|
||||
configurações diferentes de pinos expostos e componentes adicionais.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
const tabs = [
|
||||
{
|
||||
id: 'devboard',
|
||||
label: 'Placa de Desenvolvimento',
|
||||
available: serie.placa,
|
||||
content: (
|
||||
<div className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-3xl p-6 md:p-8 border border-slate-200/60 dark:border-slate-800/80 shadow-2xl">
|
||||
<h2 className="text-xl font-bold text-slate-800 dark:text-slate-100 mb-6 pb-3 border-b border-slate-200/65 dark:border-slate-800/65 flex items-center gap-3">
|
||||
<Sliders className="w-5 h-5" style={{ color: serie.cor }} />
|
||||
<span>Kit de Desenvolvimento Oficial</span>
|
||||
</h2>
|
||||
<div className="flex justify-center">
|
||||
<div className="relative w-full max-w-3xl bg-white dark:bg-white overflow-hidden rounded-2xl border border-slate-200/50 dark:border-slate-850/50 shadow-lg p-4">
|
||||
<Image
|
||||
src={serie.placa}
|
||||
alt={`${key} DevKit - Placa de Desenvolvimento`}
|
||||
width={1200}
|
||||
height={800}
|
||||
className="rounded-xl object-contain w-full h-auto max-h-[450px]"
|
||||
priority
|
||||
/>
|
||||
<p className="text-center text-xs text-slate-500 dark:text-slate-400 mt-4 select-none">
|
||||
Placa de desenvolvimento padrão da Espressif Systems para a série {key}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 'connections',
|
||||
label: 'Diagrama de Conexões',
|
||||
available: conexoes.length > 0,
|
||||
content: (
|
||||
<div className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-3xl p-6 md:p-8 border border-slate-200/60 dark:border-slate-800/80 shadow-2xl">
|
||||
<div className="bg-amber-500/10 text-amber-600 dark:text-amber-400 border border-amber-500/20 p-5 mb-8 rounded-2xl">
|
||||
<div className="flex items-start gap-3">
|
||||
<AlertTriangle className="w-5 h-5 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<h4 className="font-bold text-sm text-amber-850 dark:text-amber-300 mb-1 select-none">Diagrama do SoC (System on Chip)</h4>
|
||||
<p className="text-xs text-amber-800 dark:text-amber-400 leading-relaxed">
|
||||
Este mapa refere-se exclusivamente às portas e conexões do silício bruto do microcontrolador <strong>{key}</strong>. As placas de desenvolvimento podem expor apenas uma parcela destas conexões, ou roteá-las de formas diferentes dependendo do circuito impresso.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ConnectionsDiagram connections={conexoes} serie={serie} />
|
||||
<ConnectionsDiagram connections={conexoes} serie={serie} />
|
||||
|
||||
<div className="bg-white rounded-2xl shadow-lg p-6 border-2 border-gray-100 mt-6">
|
||||
<h3 className="text-lg font-bold text-gray-800 mb-3 flex items-center gap-2">
|
||||
<svg className="w-5 h-5 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" />
|
||||
</svg>
|
||||
Sobre a Matriz de Conexões Flexível
|
||||
</h3>
|
||||
<p className="text-gray-700 leading-relaxed">
|
||||
O ESP32 possui uma matriz de conexões flexível que permite mapear diversas funções para múltiplos pinos.
|
||||
Isso significa que muitas conexões podem ser configuradas para desempenhar diferentes papéis, dependendo das necessidades do seu projeto.
|
||||
Consulte a documentação oficial para entender como aproveitar ao máximo as GPIOs
|
||||
<a href={`https://docs.espressif.com/projects/esp-idf/en/latest/${String(key).toLowerCase().replace("-", "")}/api-reference/peripherals/gpio.html`} target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:underline">
|
||||
nesse link.
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
];
|
||||
<div className="bg-slate-100/50 dark:bg-slate-950/40 rounded-2xl p-6 border border-slate-200/60 dark:border-slate-800/80 mt-8">
|
||||
<h3 className="text-sm font-bold text-slate-800 dark:text-slate-100 mb-3 flex items-center gap-2 select-none">
|
||||
<Lightbulb className="w-4 h-4 text-purple-500" />
|
||||
<span>Flexibilidade das Portas GPIO</span>
|
||||
</h3>
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400 leading-relaxed">
|
||||
O {key} integra uma matriz IO multiplexada e de baixa latência (IO MUX) que permite reconfigurar a grande maioria dos pinos digitais para assumir funções de SPI, I2C, UART, LEDC PWM ou barramentos de câmera e display.
|
||||
Para mais detalhes sobre as restrições físicas de GPIOs strapping ou analógicas, consulte o
|
||||
<a
|
||||
href={`https://docs.espressif.com/projects/esp-idf/en/latest/${String(key).toLowerCase().replace("-", "")}/api-reference/peripherals/gpio.html`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-purple-600 dark:text-purple-400 hover:underline inline-flex items-center gap-0.5"
|
||||
>
|
||||
<span>Guia Oficial de GPIO</span>
|
||||
<ExternalLink className="w-2.5 h-2.5" />
|
||||
</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="bg-linear-to-br from-blue-50 via-white to-purple-50 min-h-screen">
|
||||
<Header />
|
||||
return (
|
||||
<div className="bg-gradient-to-br from-slate-50 via-white to-purple-50/40 dark:from-slate-950 dark:via-slate-950 dark:to-slate-900 min-h-screen text-slate-900 dark:text-slate-100 transition-colors duration-300">
|
||||
<Header />
|
||||
|
||||
<main className="px-4 pt-16 pb-8 max-w-7xl mx-auto">
|
||||
<div className="mb-8">
|
||||
<Link href="/series" className="inline-flex items-center text-gray-600 hover:text-purple-600 transition-colors mb-4">
|
||||
<span className="mr-2">←</span> Voltar para Séries
|
||||
</Link>
|
||||
<main className="px-6 pt-16 pb-24 max-w-7xl mx-auto">
|
||||
|
||||
<div className="bg-white rounded-3xl shadow-xl p-8 border-2 border-gray-100"
|
||||
style={{ borderColor: serie.cor + '30' }}>
|
||||
<div className="flex items-start justify-between flex-wrap gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-6xl">{serie.icone}</span>
|
||||
<div>
|
||||
<h1 className="text-4xl md:text-5xl font-extrabold text-gray-800 mb-2">
|
||||
{key}
|
||||
</h1>
|
||||
<p className="text-xl text-gray-600 mb-2">{serie.nome_completo}</p>
|
||||
<span className="inline-block px-4 py-2 rounded-full text-sm font-semibold text-white"
|
||||
style={{ backgroundColor: serie.cor }}>
|
||||
{serie.arquitetura}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* Breadcrumb Navigation */}
|
||||
<div className="mb-8">
|
||||
<Link
|
||||
href="/series"
|
||||
className="inline-flex items-center gap-2 text-xs font-bold text-slate-500 dark:text-slate-400 hover:text-purple-600 dark:hover:text-purple-400 transition-colors uppercase tracking-wider mb-6"
|
||||
>
|
||||
<ArrowLeft className="w-3.5 h-3.5" />
|
||||
<span>Voltar para Séries</span>
|
||||
</Link>
|
||||
|
||||
<div className="grid gap-3 mx-auto md:mx-0">
|
||||
<a
|
||||
href={serie.datasheet}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 bg-blue-600 text-white px-6 py-3 rounded-xl font-semibold shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-300"
|
||||
>
|
||||
📄 Datasheet Oficial
|
||||
</a>
|
||||
{/* Top Info Banner Card */}
|
||||
<div
|
||||
className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-3xl p-6 md:p-8 shadow-2xl border relative overflow-hidden"
|
||||
style={{ borderColor: `${serie.cor}40` }}
|
||||
>
|
||||
<div className="flex flex-col lg:flex-row items-start lg:items-center justify-between gap-8">
|
||||
<div className="flex items-center gap-5">
|
||||
<span className="text-6xl select-none filter drop-shadow-md">{serie.icone}</span>
|
||||
<div>
|
||||
<h1 className="text-4xl md:text-5xl font-display font-extrabold text-slate-850 dark:text-slate-100 tracking-tight leading-none mb-3">
|
||||
{key}
|
||||
</h1>
|
||||
<p className="text-sm font-semibold text-slate-400 dark:text-slate-500 mb-4">{serie.nome_completo}</p>
|
||||
<span
|
||||
className="inline-block px-3 py-1 rounded-full text-[10px] font-bold uppercase tracking-widest"
|
||||
style={{ backgroundColor: `${serie.cor}18`, color: serie.cor, border: `1px solid ${serie.cor}30` }}
|
||||
>
|
||||
{serie.arquitetura}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a
|
||||
href={serie.guia_de_programacao}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 bg-green-600 text-white px-6 py-3 rounded-xl font-semibold shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-300"
|
||||
>
|
||||
💻 Guia de Programação
|
||||
</a>
|
||||
{/* Links Block */}
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-1 gap-3 w-full lg:w-auto">
|
||||
<a
|
||||
href={serie.datasheet}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-2 bg-slate-900 dark:bg-slate-100 text-white dark:text-slate-950 px-5 py-3 rounded-xl font-bold text-xs uppercase tracking-wider hover:opacity-90 transition-opacity shadow-lg"
|
||||
>
|
||||
<FileText className="w-4 h-4" />
|
||||
<span>Datasheet Oficial</span>
|
||||
</a>
|
||||
|
||||
{serie.manual_tecnico && <a
|
||||
href={serie.manual_tecnico}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 bg-purple-600 text-white px-6 py-3 rounded-xl font-semibold shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-300"
|
||||
>
|
||||
📖 Manual Técnico
|
||||
</a>}
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
href={serie.guia_de_programacao}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-2 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border border-emerald-500/20 px-5 py-3 rounded-xl font-bold text-xs uppercase tracking-wider hover:bg-emerald-500/20 transition-all shadow-xs"
|
||||
>
|
||||
<Code className="w-4 h-4" />
|
||||
<span>Guia de Programação</span>
|
||||
</a>
|
||||
|
||||
<p className="text-lg text-gray-700 mt-6 leading-relaxed">
|
||||
{serie.descricao}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{serie.manual_tecnico && (
|
||||
<a
|
||||
href={serie.manual_tecnico}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-2 bg-purple-500/10 text-purple-600 dark:text-purple-400 border border-purple-500/20 px-5 py-3 rounded-xl font-bold text-xs uppercase tracking-wider hover:bg-purple-500/20 transition-all shadow-xs"
|
||||
>
|
||||
<BookOpen className="w-4 h-4" />
|
||||
<span>Manual Técnico</span>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-6 mb-8">
|
||||
<div className="bg-white rounded-2xl shadow-lg p-6 border-l-4" style={{ borderColor: serie.cor }}>
|
||||
<h3 className="text-sm font-semibold text-gray-600 mb-2">PROCESSADOR</h3>
|
||||
<p className="text-3xl font-bold text-gray-800">{serie.nucleos.includes("2") ? "Dual-Core" : "Single-Core"}</p>
|
||||
<p className="text-gray-600 mt-1">{serie.frequencia}</p>
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400 mt-6 leading-relaxed max-w-4xl">
|
||||
{serie.descricao}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl shadow-lg p-6 border-l-4" style={{ borderColor: serie.cor }}>
|
||||
<h3 className="text-sm font-semibold text-gray-600 mb-2">MEMÓRIA SRAM</h3>
|
||||
<p className="text-3xl font-bold text-gray-800">{serie.memoria_sram}</p>
|
||||
<p className="text-gray-600 mt-1">+ {serie.memoria_sram_rtc} RTC</p>
|
||||
</div>
|
||||
{/* Primary Features Highlight Grid */}
|
||||
<div className="grid md:grid-cols-3 gap-6 mb-8">
|
||||
<div
|
||||
className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-2xl shadow-lg p-6 border-l-4"
|
||||
style={{ borderColor: serie.cor }}
|
||||
>
|
||||
<h3 className="text-[10px] font-bold text-slate-400 dark:text-slate-500 uppercase tracking-widest mb-1.5 select-none">PROCESSADOR</h3>
|
||||
<p className="text-2xl font-extrabold text-slate-800 dark:text-slate-100">{serie.nucleos.includes("2") ? "Dual-Core" : "Single-Core"}</p>
|
||||
<p className="text-xs text-slate-400 mt-1">{serie.frequencia}</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl shadow-lg p-6 border-l-4" style={{ borderColor: serie.cor }}>
|
||||
<h3 className="text-sm font-semibold text-gray-600 mb-2">GPIO</h3>
|
||||
<p className="text-3xl font-bold text-gray-800">{serie.gpio}</p>
|
||||
<p className="text-gray-600 mt-1">Pinos disponíveis</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-2xl shadow-lg p-6 border-l-4"
|
||||
style={{ borderColor: serie.cor }}
|
||||
>
|
||||
<h3 className="text-[10px] font-bold text-slate-400 dark:text-slate-500 uppercase tracking-widest mb-1.5 select-none">MEMÓRIA SRAM</h3>
|
||||
<p className="text-2xl font-extrabold text-slate-800 dark:text-slate-100">{serie.memoria_sram}</p>
|
||||
<p className="text-xs text-slate-400 mt-1">+ {serie.memoria_sram_rtc} RTC SRAM</p>
|
||||
</div>
|
||||
|
||||
<SeriesTabMenu tabs={tabs} color={serie.cor} />
|
||||
<div
|
||||
className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-2xl shadow-lg p-6 border-l-4"
|
||||
style={{ borderColor: serie.cor }}
|
||||
>
|
||||
<h3 className="text-[10px] font-bold text-slate-400 dark:text-slate-500 uppercase tracking-widest mb-1.5 select-none">GPIO DISPONÍVEIS</h3>
|
||||
<p className="text-2xl font-extrabold text-slate-800 dark:text-slate-100">{serie.gpio}</p>
|
||||
<p className="text-xs text-slate-400 mt-1">Pinos I/O programáveis</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<SpecSection title="⚙️ Processador" specs={processadorSpecs} cor={serie.cor} />
|
||||
<SpecSection title="📡 Conectividade" specs={conectividadeSpecs} cor={serie.cor} />
|
||||
<SpecSection title="💾 Memória" specs={memoriaSpecs} cor={serie.cor} />
|
||||
{consumoEnergia.length > 0 && (
|
||||
<div className="bg-white rounded-2xl shadow-lg p-6 border-2 border-gray-100">
|
||||
<h2 className="text-2xl font-bold text-gray-800 mb-4 pb-3 border-b-2 flex items-center gap-3" style={{ borderColor: serie.cor }}>
|
||||
<span>🔋</span>
|
||||
Consumo de Energia
|
||||
</h2>
|
||||
<div className="bg-blue-50 border-l-4 border-blue-500 p-4 mb-6 rounded-lg">
|
||||
<div className="flex items-start gap-3">
|
||||
<span className="text-2xl">💡</span>
|
||||
<div>
|
||||
<p className="text-blue-900 text-sm leading-relaxed">
|
||||
<strong>Valores típicos @3.3V, 25°C.</strong> O consumo real varia com clock, estado de RF (Wi-Fi/Bluetooth ligado/desligado) e configurações.
|
||||
Para precisão, consulte o datasheet oficial.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{consumoEnergia.map((spec, index) => (
|
||||
<div key={index} className="flex items-start gap-3 p-3 rounded-lg hover:bg-gray-50 transition-colors">
|
||||
<span className="text-2xl">{spec.icon}</span>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-semibold text-gray-600">{spec.label}</p>
|
||||
<p className="text-base font-bold text-gray-800 mt-1">{spec.value}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<SpecSection title="🔌 Periféricos" specs={perifericos} cor={serie.cor} />
|
||||
{interfacesEspeciais.length > 0 && (
|
||||
<SpecSection title="✨ Interfaces Especiais" specs={interfacesEspeciais} cor={serie.cor} />
|
||||
)}
|
||||
<SpecSection
|
||||
title="🌡️ Temperatura"
|
||||
specs={[
|
||||
{ label: "Operação", value: serie.temperatura_operacao, icon: "🔧" },
|
||||
{ label: "Armazenamento", value: serie.temperatura_armazenamento, icon: "📦" },
|
||||
]}
|
||||
cor={serie.cor}
|
||||
/>
|
||||
</div>
|
||||
{/* DevBoard and Connections Tabs */}
|
||||
<SeriesTabMenu tabs={tabs} color={serie.cor} />
|
||||
|
||||
{/* Call-to-Action para Catálogo de Placas */}
|
||||
<div className="my-16 bg-linear-to-br from-blue-600 via-purple-600 to-pink-600 rounded-3xl shadow-2xl p-10 text-white border-2 border-purple-400">
|
||||
<div className="flex flex-col md:flex-row items-center justify-between gap-8">
|
||||
<div className="flex-1">
|
||||
<div className="inline-flex items-center gap-2 bg-white/20 backdrop-blur-sm px-4 py-2 rounded-full mb-4 border border-white/30">
|
||||
<span className="text-xl">🎯</span>
|
||||
<span className="text-sm font-semibold">Próximo Passo</span>
|
||||
</div>
|
||||
<h3 className="text-3xl md:text-4xl font-black mb-4">
|
||||
Pronto para começar?
|
||||
</h3>
|
||||
<p className="text-lg text-white/90 leading-relaxed">
|
||||
Encontre placas de desenvolvimento {key} verificadas no nosso catálogo.
|
||||
Links diretos para lojas oficiais e confiáveis com os melhores preços!
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/catalogo"
|
||||
className="inline-flex items-center justify-center gap-2 bg-white text-purple-700 px-8 py-4 rounded-xl font-bold text-lg shadow-xl hover:shadow-2xl transform hover:-translate-y-1 transition-all duration-300 whitespace-nowrap group"
|
||||
>
|
||||
<span className="text-xl">🛒</span>
|
||||
<span>Ver Placas Disponíveis</span>
|
||||
<svg
|
||||
className="w-5 h-5 transition-transform group-hover:translate-x-1"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
{/* Specs List Cards */}
|
||||
<div className="space-y-6 mt-12">
|
||||
<SpecSection title="Processador" specs={processadorSpecs} cor={serie.cor} icon={<Cpu className="w-5 h-5" style={{ color: serie.cor }} />} />
|
||||
<SpecSection title="Conectividade RF" specs={conectividadeSpecs} cor={serie.cor} icon={<Wifi className="w-5 h-5" style={{ color: serie.cor }} />} />
|
||||
<SpecSection title="Estrutura de Memória" specs={memoriaSpecs} cor={serie.cor} icon={<Database className="w-5 h-5" style={{ color: serie.cor }} />} />
|
||||
|
||||
<div className="grid sm:flex sm:justify-between items-center gap-4 mt-12 md:mx-8">
|
||||
{previousKey ? (
|
||||
<Link
|
||||
href={`/series/${previousKey}`}
|
||||
className="flex items-center gap-2 bg-white px-6 py-3 rounded-xl shadow-lg hover:shadow-xl transform hover:-translate-x-1 transition-all duration-300 border-2 border-gray-100 hover:border-purple-300"
|
||||
>
|
||||
<span>←</span>
|
||||
<div className="text-left">
|
||||
<p className="text-xs text-gray-600">Anterior</p>
|
||||
<p className="font-semibold text-gray-800">{previousKey}</p>
|
||||
</div>
|
||||
</Link>
|
||||
) : null}
|
||||
{/* Power Consumption section */}
|
||||
{consumoEnergia.length > 0 && (
|
||||
<div className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-2xl p-6 md:p-8 border border-slate-200/60 dark:border-slate-800/80 shadow-2xl">
|
||||
<h2 className="text-xl font-bold text-slate-800 dark:text-slate-100 mb-6 pb-3 border-b border-slate-200/65 dark:border-slate-800/65 flex items-center gap-3 select-none">
|
||||
<Battery className="w-5 h-5" style={{ color: serie.cor }} />
|
||||
<span>Consumo de Energia</span>
|
||||
</h2>
|
||||
|
||||
<Link
|
||||
href="/comparacao"
|
||||
className="bg-linear-to-r from-blue-600 to-purple-600 text-white px-6 py-3 rounded-xl font-semibold shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-300"
|
||||
>
|
||||
📊 Comparar Séries
|
||||
</Link>
|
||||
<div className="bg-blue-500/10 border border-blue-500/20 p-4 mb-6 rounded-xl">
|
||||
<div className="flex items-start gap-3 text-blue-650 dark:text-blue-400">
|
||||
<Lightbulb className="w-5 h-5 shrink-0 mt-0.5" />
|
||||
<p className="text-xs leading-relaxed">
|
||||
<strong>Especificações típicas medidas sob 3.3V com 25°C de temperatura ambiente.</strong> O consumo absoluto depende ativamente do ciclo de trabalho (duty cycle) das interfaces Wi-Fi/Bluetooth e da frequência de clock configurada via software.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{nextKey ? (
|
||||
<Link
|
||||
href={`/series/${nextKey}`}
|
||||
className="flex items-center gap-2 bg-white px-6 py-3 rounded-xl shadow-lg hover:shadow-xl transform hover:translate-x-1 transition-all duration-300 border-2 border-gray-100 hover:border-purple-300"
|
||||
>
|
||||
<div className="text-right">
|
||||
<p className="text-xs text-gray-600">Próximo</p>
|
||||
<p className="font-semibold text-gray-800">{nextKey}</p>
|
||||
</div>
|
||||
<span>→</span>
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
</main>
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{consumoEnergia.map((spec, index) => (
|
||||
<div key={index} className="flex items-center gap-3.5 p-3.5 bg-slate-50/50 dark:bg-slate-950/20 rounded-xl hover:bg-purple-500/5 dark:hover:bg-purple-400/5 border border-slate-150/40 dark:border-slate-850/40 transition-colors">
|
||||
<div className="p-2 bg-white dark:bg-slate-900 rounded-lg shadow-xs border border-slate-200/30 dark:border-slate-850/30 shrink-0">
|
||||
{spec.icon}
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="text-[10px] text-slate-400 dark:text-slate-500 font-bold uppercase tracking-wider">{spec.label}</p>
|
||||
<p className="text-sm font-extrabold text-slate-800 dark:text-slate-200 mt-0.5">{spec.value}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
<SpecSection title="Periféricos Disponíveis" specs={perifericos} cor={serie.cor} icon={<Sliders className="w-5 h-5" style={{ color: serie.cor }} />} />
|
||||
|
||||
{interfacesEspeciais.length > 0 && (
|
||||
<SpecSection title="Interfaces de Hardware Especiais" specs={interfacesEspeciais} cor={serie.cor} icon={<Sparkles className="w-5 h-5" style={{ color: serie.cor }} />} />
|
||||
)}
|
||||
|
||||
<SpecSection
|
||||
title="Limites Operacionais de Temperatura"
|
||||
icon={<Thermometer className="w-5 h-5" style={{ color: serie.cor }} />}
|
||||
specs={[
|
||||
{ label: "Operação", value: serie.temperatura_operacao, icon: <Thermometer className="w-5 h-5 text-red-500" /> },
|
||||
{ label: "Armazenamento", value: serie.temperatura_armazenamento, icon: <Folder className="w-5 h-5 text-red-500" /> },
|
||||
]}
|
||||
cor={serie.cor}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* DevBoard CTA Panel */}
|
||||
<div className="my-20 bg-gradient-to-br from-slate-900 to-indigo-950 dark:from-slate-950 dark:to-purple-950/20 border border-slate-800/80 rounded-3xl p-8 md:p-12 shadow-2xl relative overflow-hidden">
|
||||
<div className="absolute right-0 bottom-0 w-96 h-96 bg-purple-500/5 rounded-full blur-3xl pointer-events-none" />
|
||||
|
||||
<div className="flex flex-col md:flex-row items-center justify-between gap-8 relative z-10">
|
||||
<div className="flex-1 max-w-2xl text-center md:text-left">
|
||||
<div className="inline-flex items-center gap-2 bg-white/10 text-slate-350 px-3 py-1 rounded-full mb-4 text-xs font-semibold select-none">
|
||||
<Home className="w-3.5 h-3.5" />
|
||||
<span>Hardware Fisico</span>
|
||||
</div>
|
||||
<h3 className="text-2xl md:text-3xl font-display font-extrabold text-white mb-4">
|
||||
Pronto para começar a prototipar?
|
||||
</h3>
|
||||
<p className="text-sm text-slate-400 leading-relaxed">
|
||||
Explore toda a nossa grade de placas de desenvolvimento de parceiros oficiais para a série {key}. Compare dimensões, conversores USB-serial e pinagens prontas para protoboards.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href="/catalogo"
|
||||
className="inline-flex items-center justify-center gap-2 bg-white text-slate-900 px-6 py-3.5 rounded-xl font-bold text-sm hover:bg-slate-100 transition-all duration-300 shrink-0 shadow-md active:scale-95"
|
||||
>
|
||||
<span>Ver Placas Disponíveis</span>
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Previous / Next Nav bars */}
|
||||
<div className="grid sm:flex sm:justify-between items-center gap-4 mt-12 px-2 select-none">
|
||||
{previousKey ? (
|
||||
<Link
|
||||
href={`/series/${previousKey}`}
|
||||
className="flex items-center gap-3 bg-white/70 dark:bg-slate-900/40 backdrop-blur-xl px-5 py-3.5 rounded-2xl shadow-lg border border-slate-200/60 dark:border-slate-800/80 hover:border-purple-500/55 dark:hover:border-purple-400/55 transition-all group shrink-0"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4 text-slate-400 group-hover:-translate-x-1.5 transition-transform" />
|
||||
<div className="text-left">
|
||||
<p className="text-[10px] text-slate-400 dark:text-slate-500 font-bold uppercase tracking-wider">Anterior</p>
|
||||
<p className="text-sm font-extrabold text-slate-800 dark:text-slate-200">{previousKey}</p>
|
||||
</div>
|
||||
</Link>
|
||||
) : (
|
||||
<div className="hidden sm:block w-36" />
|
||||
)}
|
||||
|
||||
<Link
|
||||
href="/comparacao"
|
||||
className="inline-flex items-center justify-center gap-2 bg-slate-900 dark:bg-slate-100 text-white dark:text-slate-950 px-6 py-3.5 rounded-xl font-bold text-xs uppercase tracking-wider hover:opacity-90 transition-opacity shadow-md"
|
||||
>
|
||||
<Scale className="w-4 h-4" />
|
||||
<span>Comparar Séries</span>
|
||||
</Link>
|
||||
|
||||
{nextKey ? (
|
||||
<Link
|
||||
href={`/series/${nextKey}`}
|
||||
className="flex items-center gap-3 bg-white/70 dark:bg-slate-900/40 backdrop-blur-xl px-5 py-3.5 rounded-2xl shadow-lg border border-slate-200/60 dark:border-slate-800/80 hover:border-purple-500/55 dark:hover:border-purple-400/55 transition-all group shrink-0"
|
||||
>
|
||||
<div className="text-right">
|
||||
<p className="text-[10px] text-slate-400 dark:text-slate-500 font-bold uppercase tracking-wider">Próximo</p>
|
||||
<p className="text-sm font-extrabold text-slate-800 dark:text-slate-200">{nextKey}</p>
|
||||
</div>
|
||||
<ArrowRight className="w-4 h-4 text-slate-400 group-hover:translate-x-1.5 transition-transform" />
|
||||
</Link>
|
||||
) : (
|
||||
<div className="hidden sm:block w-36" />
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SpecSection({ title, specs, cor }) {
|
||||
return (
|
||||
<div className="bg-white rounded-2xl shadow-lg p-6 border-2 border-gray-100">
|
||||
<h2 className="text-2xl font-bold text-gray-800 mb-4 pb-3 border-b-2" style={{ borderColor: cor }}>
|
||||
{title}
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{specs.map((spec, index) => (
|
||||
<div key={index} className="flex items-start gap-3 p-3 rounded-lg hover:bg-gray-50 transition-colors">
|
||||
<span className="text-2xl">{spec.icon}</span>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-semibold text-gray-600">{spec.label}</p>
|
||||
<p className="text-base font-bold text-gray-800 mt-1">{spec.value}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
function SpecSection({ title, specs, cor, icon }) {
|
||||
return (
|
||||
<div className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-2xl p-6 md:p-8 border border-slate-200/60 dark:border-slate-800/80 shadow-2xl">
|
||||
<h2 className="text-xl font-bold text-slate-800 dark:text-slate-100 mb-6 pb-3 border-b border-slate-200/65 dark:border-slate-800/65 flex items-center gap-3 select-none">
|
||||
{icon}
|
||||
<span>{title}</span>
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{specs.map((spec, index) => (
|
||||
<div key={index} className="flex items-center gap-3.5 p-3.5 bg-slate-50/50 dark:bg-slate-950/20 rounded-xl hover:bg-purple-500/5 dark:hover:bg-purple-400/5 border border-slate-150/40 dark:border-slate-850/40 transition-colors">
|
||||
<div className="p-2 bg-white dark:bg-slate-900 rounded-lg shadow-xs border border-slate-200/30 dark:border-slate-850/30 shrink-0">
|
||||
{spec.icon}
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="text-[10px] text-slate-400 dark:text-slate-500 font-bold uppercase tracking-wider">{spec.label}</p>
|
||||
<p className="text-sm font-extrabold text-slate-800 dark:text-slate-200 mt-0.5">{spec.value}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }) {
|
||||
const { key } = await params;
|
||||
const serie = seriesData[key];
|
||||
const { key } = await params;
|
||||
const serie = seriesData[key];
|
||||
|
||||
if (!serie) {
|
||||
return {
|
||||
title: "Série não encontrada",
|
||||
};
|
||||
}
|
||||
if (!serie) {
|
||||
return {
|
||||
title: "Série não encontrada",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
title: `ESPDocs - ${serie.nome_completo}`,
|
||||
description: serie.descricao,
|
||||
};
|
||||
return {
|
||||
title: `ESPDocs - ${serie.nome_completo}`,
|
||||
description: serie.descricao,
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
return Object.keys(seriesData).map((key) => ({
|
||||
key: key,
|
||||
}));
|
||||
return Object.keys(seriesData).map((key) => ({
|
||||
key: key,
|
||||
}));
|
||||
}
|
||||
@@ -1,159 +1,222 @@
|
||||
"use client";
|
||||
|
||||
import Header from "@/components/Header";
|
||||
import Footer from "@/components/Footer";
|
||||
import Link from "next/link";
|
||||
import seriesData from "@/public/series.json";
|
||||
import {
|
||||
Cpu,
|
||||
Activity,
|
||||
Database,
|
||||
Wifi,
|
||||
Bluetooth,
|
||||
Battery,
|
||||
Sparkles,
|
||||
Home,
|
||||
ArrowRight,
|
||||
Scale,
|
||||
Compass
|
||||
} from "lucide-react";
|
||||
|
||||
export default function Series() {
|
||||
const series = Object.entries(seriesData);
|
||||
const series = Object.entries(seriesData);
|
||||
|
||||
return (
|
||||
<div className="bg-linear-to-br from-blue-50 via-white to-purple-50 min-h-screen">
|
||||
<Header />
|
||||
return (
|
||||
<div className="bg-gradient-to-br from-slate-50 via-white to-purple-50/40 dark:from-slate-950 dark:via-slate-950 dark:to-slate-900 min-h-screen text-slate-900 dark:text-slate-100 transition-colors duration-300">
|
||||
<Header />
|
||||
|
||||
<main id="conteudo" className="px-4 pt-16 pb-20 max-w-7xl mx-auto">
|
||||
<section className="text-center mb-16">
|
||||
<div className="inline-flex items-center gap-2 bg-white/70 backdrop-blur-sm px-4 py-2 rounded-full mb-6 border border-purple-200 shadow-sm">
|
||||
<span className="text-xl">⚡</span>
|
||||
<span className="text-sm font-semibold text-gray-700">Explore o Ecossistema ESP32</span>
|
||||
</div>
|
||||
<main id="conteudo" className="px-6 pt-16 pb-24 max-w-7xl mx-auto">
|
||||
|
||||
<h1 className="text-5xl md:text-7xl font-black bg-linear-to-r from-blue-600 via-purple-600 to-pink-600 bg-clip-text text-transparent mb-6 leading-tight tracking-tight">
|
||||
Séries ESP32
|
||||
</h1>
|
||||
{/* Top Section */}
|
||||
<section className="text-center mb-20">
|
||||
<div className="inline-flex items-center gap-2 bg-purple-500/10 dark:bg-purple-400/5 px-4 py-2 rounded-full mb-6 border border-purple-500/20 shadow-xs select-none">
|
||||
<Compass className="w-4 h-4 text-purple-600 dark:text-purple-400" />
|
||||
<span className="text-xs font-bold text-purple-600 dark:text-purple-400 uppercase tracking-widest">Explore o Ecossistema ESP32</span>
|
||||
</div>
|
||||
|
||||
<p className="text-lg md:text-xl text-gray-600 max-w-3xl mx-auto leading-relaxed">
|
||||
Explore toda a família ESP32. Cada série foi projetada para atender necessidades específicas,
|
||||
desde projetos simples até aplicações de IA e IoT avançadas.
|
||||
</p>
|
||||
</section>
|
||||
<h1 className="text-5xl md:text-7xl font-display font-extrabold bg-gradient-to-r from-blue-600 via-purple-600 to-pink-600 dark:from-blue-400 dark:via-purple-400 dark:to-pink-400 bg-clip-text text-transparent mb-6 tracking-tight leading-tight">
|
||||
Séries ESP32
|
||||
</h1>
|
||||
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{series.map(([key, serie]) => (
|
||||
<Link
|
||||
key={key}
|
||||
href={`/series/${key}`}
|
||||
className="group"
|
||||
>
|
||||
<div className="bg-white rounded-2xl shadow-lg hover:shadow-2xl transform hover:-translate-y-2 transition-all duration-300 border-2 border-gray-100 hover:border-opacity-50 h-full"
|
||||
style={{ '--hover-color': serie.cor }}>
|
||||
<p className="text-base md:text-lg text-slate-500 dark:text-slate-400 max-w-3xl mx-auto leading-relaxed">
|
||||
Conheça toda a família de microcontroladores da Espressif. Cada série foi projetada com foco em engenharia especializada, desde nós de sensores autônomos de baixo consumo até poderosos aceleradores de inteligência artificial.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div className="p-6 border-b border-gray-100">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-4xl">{serie.icone}</span>
|
||||
<span className="px-3 py-1 rounded-full text-xs font-semibold text-white"
|
||||
style={{ backgroundColor: serie.cor }}>
|
||||
{serie.arquitetura.includes("RISC-V") ? "RISC-V" : "Xtensa"}
|
||||
</span>
|
||||
</div>
|
||||
{/* Series Grid */}
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{series.map(([key, serie]) => {
|
||||
const isRiscV = serie.arquitetura.includes("RISC-V");
|
||||
return (
|
||||
<Link
|
||||
key={key}
|
||||
href={`/series/${key}`}
|
||||
className="group block"
|
||||
>
|
||||
{/* Glassmorphic card container with dynamic color borders and shadows */}
|
||||
<div
|
||||
className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-3xl p-6 md:p-8 border border-slate-200/60 dark:border-slate-800/80 hover:shadow-2xl transition-all duration-500 h-full flex flex-col justify-between"
|
||||
style={{
|
||||
borderColor: 'rgba(148, 163, 184, 0.1)',
|
||||
transition: 'border-color 0.3s, box-shadow 0.3s, transform 0.3s'
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.borderColor = serie.cor;
|
||||
e.currentTarget.style.boxShadow = `0 15px 35px -12px ${serie.cor}35`;
|
||||
e.currentTarget.style.transform = 'translateY(-6px)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.borderColor = '';
|
||||
e.currentTarget.style.boxShadow = '';
|
||||
e.currentTarget.style.transform = '';
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
{/* Header block with Icon & Architecture */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<span className="text-4xl select-none filter drop-shadow-md transform group-hover:scale-110 transition-transform duration-300">{serie.icone}</span>
|
||||
<span
|
||||
className={`px-3 py-1 rounded-full text-[10px] font-bold tracking-widest uppercase select-none`}
|
||||
style={{ backgroundColor: `${serie.cor}18`, color: serie.cor, border: `1px solid ${serie.cor}30` }}
|
||||
>
|
||||
{isRiscV ? "RISC-V" : "Xtensa"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold text-gray-800 mb-2 group-hover:text-purple-600 transition-colors">
|
||||
{key}
|
||||
</h2>
|
||||
{/* Title and description */}
|
||||
<h2 className="text-2xl font-display font-extrabold text-slate-850 dark:text-slate-100 mb-3 group-hover:text-purple-600 dark:group-hover:text-purple-400 transition-colors">
|
||||
{key}
|
||||
</h2>
|
||||
|
||||
<p className="text-sm text-gray-600 line-clamp-2">
|
||||
{serie.descricao}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-xs text-slate-450 dark:text-slate-500 line-clamp-2 leading-relaxed mb-6">
|
||||
{serie.descricao}
|
||||
</p>
|
||||
|
||||
<div className="p-6 space-y-3">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-600">⚙️ Núcleos:</span>
|
||||
<span className="font-semibold text-gray-800">{serie.nucleos}</span>
|
||||
</div>
|
||||
{/* Detailed specs micro-grid */}
|
||||
<div className="space-y-3.5 border-t border-slate-100 dark:border-slate-800/60 pt-6">
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<div className="flex items-center gap-2 text-slate-400 dark:text-slate-500 font-semibold select-none">
|
||||
<Cpu className="w-3.5 h-3.5 text-blue-500/80" />
|
||||
<span>Núcleos:</span>
|
||||
</div>
|
||||
<span className="font-bold text-slate-800 dark:text-slate-200">{serie.nucleos}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-600">🚀 Frequência:</span>
|
||||
<span className="font-semibold text-gray-800">{serie.frequencia}</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<div className="flex items-center gap-2 text-slate-400 dark:text-slate-500 font-semibold select-none">
|
||||
<Activity className="w-3.5 h-3.5 text-amber-500/80" />
|
||||
<span>Frequência:</span>
|
||||
</div>
|
||||
<span className="font-bold text-slate-800 dark:text-slate-200">{serie.frequencia}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-600">💾 SRAM:</span>
|
||||
<span className="font-semibold text-gray-800">{serie.memoria_sram}</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<div className="flex items-center gap-2 text-slate-400 dark:text-slate-500 font-semibold select-none">
|
||||
<Database className="w-3.5 h-3.5 text-pink-500/80" />
|
||||
<span>SRAM:</span>
|
||||
</div>
|
||||
<span className="font-bold text-slate-800 dark:text-slate-200">{serie.memoria_sram}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-600">📶 Wi-Fi:</span>
|
||||
<span className="font-semibold text-gray-800">
|
||||
{String(serie.wifi).includes("Não") ? "❌" : "✅"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<div className="flex items-center gap-2 text-slate-400 dark:text-slate-500 font-semibold select-none">
|
||||
<Wifi className="w-3.5 h-3.5 text-emerald-500/80" />
|
||||
<span>Wi-Fi:</span>
|
||||
</div>
|
||||
<span className="font-bold text-slate-800 dark:text-slate-200">
|
||||
{String(serie.wifi).includes("Não") ? (
|
||||
<span className="text-slate-350 dark:text-slate-650">—</span>
|
||||
) : (
|
||||
<span className="text-emerald-500 dark:text-emerald-400 font-extrabold">Sim</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-600">📱 Bluetooth:</span>
|
||||
<span className="font-semibold text-gray-800">
|
||||
{serie.bluetooth === "Não" ? "❌" : "✅"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<div className="flex items-center gap-2 text-slate-400 dark:text-slate-500 font-semibold select-none">
|
||||
<Bluetooth className="w-3.5 h-3.5 text-indigo-500/80" />
|
||||
<span>Bluetooth:</span>
|
||||
</div>
|
||||
<span className="font-bold text-slate-800 dark:text-slate-200">
|
||||
{serie.bluetooth === "Não" ? (
|
||||
<span className="text-slate-350 dark:text-slate-650">—</span>
|
||||
) : (
|
||||
<span className="text-indigo-500 dark:text-indigo-400 font-extrabold">Sim</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{serie.consumo_energia?.deep_sleep && (
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-600">🔋 Deep Sleep:</span>
|
||||
<span className="font-semibold text-gray-800">{serie.consumo_energia.deep_sleep}</span>
|
||||
</div>
|
||||
)}
|
||||
{serie.consumo_energia?.deep_sleep && (
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<div className="flex items-center gap-2 text-slate-400 dark:text-slate-500 font-semibold select-none">
|
||||
<Battery className="w-3.5 h-3.5 text-green-500/80" />
|
||||
<span>Deep Sleep:</span>
|
||||
</div>
|
||||
<span className="font-bold text-slate-800 dark:text-slate-200">{serie.consumo_energia.deep_sleep}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{serie.aceleradores_ia && (
|
||||
<div className="pt-2 border-t border-gray-100">
|
||||
<span className="inline-block px-2 py-1 bg-purple-100 text-purple-700 text-xs font-semibold rounded">
|
||||
🤖 Aceleradores IA
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{/* IA / Matter Badges & Action Footer */}
|
||||
<div className="mt-8">
|
||||
<div className="flex flex-wrap gap-2 mb-6">
|
||||
{serie.aceleradores_ia && (
|
||||
<span className="inline-flex items-center gap-1 px-2.5 py-1 bg-purple-500/10 text-purple-600 dark:text-purple-400 text-[10px] font-bold uppercase rounded-lg border border-purple-500/20 shadow-xs select-none">
|
||||
<Sparkles className="w-3 h-3" />
|
||||
<span>Aceleradores IA</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{serie.matter === "Sim" && (
|
||||
<div className="pt-2 border-t border-gray-100">
|
||||
<span className="inline-block px-2 py-1 bg-green-100 text-green-700 text-xs font-semibold rounded">
|
||||
🏠 Suporte Matter
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{serie.matter === "Sim" && (
|
||||
<span className="inline-flex items-center gap-1 px-2.5 py-1 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 text-[10px] font-bold uppercase rounded-lg border border-emerald-500/20 shadow-xs select-none">
|
||||
<Home className="w-3 h-3" />
|
||||
<span>Suporte Matter</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="px-6 pb-6">
|
||||
<div className="flex items-center justify-between text-sm font-semibold group-hover:text-purple-600 transition-colors">
|
||||
<span>Ver Detalhes</span>
|
||||
<span className="transform group-hover:translate-x-2 transition-transform">→</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-xs font-bold text-slate-500 group-hover:text-purple-600 dark:group-hover:text-purple-400 transition-colors uppercase tracking-wider select-none border-t border-slate-100 dark:border-slate-800/60 pt-4">
|
||||
<span>Ver Detalhes</span>
|
||||
<ArrowRight className="w-4 h-4 transform group-hover:translate-x-2 transition-transform duration-300" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="mt-16 bg-linear-to-br from-white to-purple-50 rounded-3xl shadow-2xl p-10 border-2 border-purple-100">
|
||||
<div className="text-center mb-8">
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 bg-linear-to-br from-blue-500 to-purple-600 rounded-2xl mb-4">
|
||||
<span className="text-3xl">💡</span>
|
||||
</div>
|
||||
<h3 className="text-3xl font-bold text-gray-900 mb-3">
|
||||
Não sabe qual escolher?
|
||||
</h3>
|
||||
<p className="text-gray-600 max-w-2xl mx-auto leading-relaxed text-base">
|
||||
Compare as especificações de cada série para encontrar a mais adequada ao seu projeto.
|
||||
Considere requisitos de conectividade, processamento e recursos específicos.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex justify-center">
|
||||
<Link
|
||||
href="/comparacao"
|
||||
className="inline-flex items-center justify-center gap-2 bg-linear-to-r from-blue-600 via-purple-600 to-pink-600 text-white px-8 py-4 rounded-xl font-bold shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-300 group"
|
||||
>
|
||||
<span className="text-xl">📊</span>
|
||||
<span>Comparar Séries</span>
|
||||
<svg
|
||||
className="w-5 h-5 transition-transform group-hover:translate-x-1"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{/* Quick comparison CTA Box */}
|
||||
<div className="mt-20 bg-gradient-to-br from-slate-900 to-indigo-950 dark:from-slate-950 dark:to-purple-950/20 rounded-3xl p-8 md:p-12 shadow-2xl relative overflow-hidden border border-slate-800/80">
|
||||
<div className="absolute right-0 bottom-0 w-96 h-96 bg-purple-500/5 rounded-full blur-3xl pointer-events-none" />
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
<div className="flex flex-col md:flex-row items-center justify-between gap-8 relative z-10">
|
||||
<div className="flex-1 max-w-2xl text-center md:text-left">
|
||||
<div className="inline-flex items-center gap-2 bg-white/10 text-slate-350 px-3 py-1 rounded-full mb-4 text-xs font-semibold select-none animate-pulse">
|
||||
<Scale className="w-3.5 h-3.5" />
|
||||
<span>Decisão Técnica</span>
|
||||
</div>
|
||||
<h3 className="text-2xl md:text-3xl font-display font-extrabold text-white mb-4">
|
||||
Ficou em dúvida de qual escolher?
|
||||
</h3>
|
||||
<p className="text-sm text-slate-400 leading-relaxed">
|
||||
Use nosso comparador técnico dinâmico para posicionar as especificações de conectividade RF, potência de rádio e suporte a barramentos de periféricos lado a lado.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href="/comparacao"
|
||||
className="inline-flex items-center justify-center gap-2 bg-white text-slate-900 px-6 py-3.5 rounded-xl font-bold text-sm hover:bg-slate-100 transition-all duration-300 shrink-0 shadow-md active:scale-95"
|
||||
>
|
||||
<span>Comparar Séries</span>
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,227 +1,262 @@
|
||||
import Header from "@/components/Header";
|
||||
import Footer from "@/components/Footer";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
Languages,
|
||||
GitCompare,
|
||||
Globe,
|
||||
Code,
|
||||
Target,
|
||||
HelpCircle,
|
||||
Users,
|
||||
Accessibility,
|
||||
ArrowLeft,
|
||||
Award,
|
||||
BookOpen,
|
||||
Compass,
|
||||
Terminal,
|
||||
Layers
|
||||
} from "lucide-react";
|
||||
|
||||
export default function Sobre() {
|
||||
const features = [
|
||||
{ icon: "📚", title: "Interface em Português", desc: "Organização e apresentação de dados técnicos totalmente em português." },
|
||||
{ icon: "🔧", title: "Comparações Visuais", desc: "Tabelas comparativas e diagramas para facilitar a escolha da série ideal." },
|
||||
{ icon: "🌐", title: "Acesso Centralizado", desc: "Links diretos para documentação oficial, repositórios e exemplos." },
|
||||
{ icon: "♿", title: "Código Aberto", desc: "Projeto open-source com contribuições bem-vindas no GitHub." },
|
||||
];
|
||||
const features = [
|
||||
{ icon: <Languages className="w-6 h-6 text-purple-500" />, title: "Interface em Português", desc: "Organização e apresentação de dados técnicos totalmente em português." },
|
||||
{ icon: <GitCompare className="w-6 h-6 text-blue-500" />, title: "Comparações Visuais", desc: "Tabelas comparativas e diagramas para facilitar a escolha da série ideal." },
|
||||
{ icon: <Globe className="w-6 h-6 text-emerald-500" />, title: "Acesso Centralizado", desc: "Links diretos para documentação oficial, repositórios e exemplos." },
|
||||
{ icon: <Code className="w-6 h-6 text-amber-500" />, title: "Código Aberto", desc: "Projeto open-source com contribuições bem-vindas no GitHub." },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="bg-linear-to-br from-blue-50 via-white to-purple-50 min-h-screen text-gray-800">
|
||||
<a href="#conteudo" className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:bg-white focus:px-4 focus:py-2 focus:rounded-md focus:shadow focus:ring-2 focus:ring-purple-500">
|
||||
Ir para o conteúdo
|
||||
</a>
|
||||
return (
|
||||
<div className="bg-gradient-to-br from-slate-50 via-white to-purple-50/40 dark:from-slate-950 dark:via-slate-950 dark:to-slate-900 min-h-screen text-slate-900 dark:text-slate-100 transition-colors duration-300">
|
||||
<a href="#conteudo" className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:bg-white focus:px-4 focus:py-2 focus:rounded-md focus:shadow focus:ring-2 focus:ring-purple-500">
|
||||
Ir para o conteúdo
|
||||
</a>
|
||||
|
||||
<Header />
|
||||
<Header />
|
||||
|
||||
<main id="conteudo" className="px-4 pt-12 pb-20 max-w-7xl mx-auto">
|
||||
<header className="relative overflow-hidden rounded-3xl p-10 md:p-16 bg-linear-to-br from-blue-600 via-purple-600 to-pink-600 text-white shadow-2xl mb-12">
|
||||
<div className="absolute inset-0 bg-black/10"></div>
|
||||
<div className="relative z-10 max-w-4xl mx-auto">
|
||||
<h1 className="text-4xl md:text-6xl font-black leading-tight mb-6">
|
||||
Sobre o ESPDocs
|
||||
</h1>
|
||||
<main id="conteudo" className="px-6 pt-16 pb-24 max-w-7xl mx-auto">
|
||||
{/* Top Hero Heading Block */}
|
||||
<header className="relative overflow-hidden rounded-3xl p-10 md:p-16 bg-gradient-to-br from-blue-600 via-purple-600 to-pink-600 text-white shadow-2xl mb-12 select-none">
|
||||
<div className="absolute inset-0 bg-black/10"></div>
|
||||
<div className="relative z-10 max-w-4xl mx-auto">
|
||||
<h1 className="text-4xl md:text-6xl font-display font-extrabold leading-tight mb-6">
|
||||
Sobre o ESPDocs
|
||||
</h1>
|
||||
|
||||
<p className="text-lg md:text-xl opacity-95 max-w-3xl mb-8 leading-relaxed">
|
||||
Uma plataforma não oficial com interface em português que organiza
|
||||
informações técnicas do ESP32 e facilita o acesso à documentação oficial
|
||||
da Espressif. Feita por um entusiasta para a comunidade.
|
||||
</p>
|
||||
<p className="text-sm md:text-base opacity-95 max-w-3xl mb-8 leading-relaxed font-semibold">
|
||||
Uma plataforma não oficial com interface em português que organiza
|
||||
informações técnicas do ESP32 e facilita o acesso à documentação oficial
|
||||
da Espressif. Desenvolvida para a comunidade de hardware livre.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 items-start sm:items-center">
|
||||
<Link href="/" className="inline-flex items-center justify-center gap-2 bg-white text-blue-700 px-6 py-3 rounded-xl font-bold shadow-lg hover:shadow-xl transition-all transform hover:-translate-y-1" aria-label="Voltar para a página inicial">
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
||||
</svg>
|
||||
<span>Voltar</span>
|
||||
</Link>
|
||||
<div className="flex flex-col sm:flex-row gap-4 items-start sm:items-center">
|
||||
<Link href="/" className="inline-flex items-center justify-center gap-2 bg-white text-blue-700 px-6 py-3 rounded-xl font-bold shadow-lg hover:shadow-xl active:scale-98 transition-all" aria-label="Voltar para a página inicial">
|
||||
<ArrowLeft className="w-5 h-5" />
|
||||
<span>Início</span>
|
||||
</Link>
|
||||
|
||||
<a
|
||||
href="https://github.com/lucasrguerra/ESPDocs"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-2 bg-white/10 backdrop-blur-sm border-2 border-white/30 text-white px-6 py-3 rounded-xl font-bold hover:bg-white/20 transition-all"
|
||||
aria-label="Abrir repositório do projeto no GitHub (abre em nova aba)"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 .5a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2.1c-3.3.7-4-1.6-4-1.6-.5-1.2-1.2-1.5-1.2-1.5-1-.7.1-.7.1-.7 1.1.1 1.7 1.1 1.7 1.1 1 .1 1.6-.7 1.9-1 .1-.8.4-1.3.8-1.6-2.6-.3-5.3-1.3-5.3-5.8 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.6.1-3.3 0 0 1-.3 3.3 1.2a11.4 11.4 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.6 1.7.2 3 .1 3.3.8.8 1.2 1.9 1.2 3.1 0 4.5-2.7 5.5-5.3 5.8.5.4.9 1.1.9 2.2v3.2c0 .3.2.7.8.6A12 12 0 0012 .5z" fill="currentColor"/>
|
||||
</svg>
|
||||
<span>GitHub</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<a
|
||||
href="https://github.com/lucasrguerra/ESPDocs"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-2 bg-white/10 backdrop-blur-xs border border-white/30 text-white px-6 py-3 rounded-xl font-bold hover:bg-white/20 active:scale-98 transition-all"
|
||||
aria-label="Abrir repositório do projeto no GitHub (abre em nova aba)"
|
||||
>
|
||||
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||
<path d="M12 .5a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2.1c-3.3.7-4-1.6-4-1.6-.5-1.2-1.2-1.5-1.2-1.5-1-.7.1-.7.1-.7 1.1.1 1.7 1.1 1.7 1.1 1 .1 1.6-.7 1.9-1 .1-.8.4-1.3.8-1.6-2.6-.3-5.3-1.3-5.3-5.8 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.6.1-3.3 0 0 1-.3 3.3 1.2a11.4 11.4 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.6 1.7.2 3 .1 3.3.8.8 1.2 1.9 1.2 3.1 0 4.5-2.7 5.5-5.3 5.8.5.4.9 1.1.9 2.2v3.2c0 .3.2.7.8.6A12 12 0 0012 .5z" />
|
||||
</svg>
|
||||
<span>GitHub</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="mt-12 grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<section className="lg:col-span-2 space-y-8">
|
||||
<article className="bg-white rounded-3xl p-8 shadow-lg border-2 border-gray-100 hover:border-purple-200 transition-colors">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-12 h-12 bg-linear-to-br from-blue-500 to-purple-600 rounded-xl flex items-center justify-center">
|
||||
<span className="text-2xl">🎯</span>
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold text-gray-900">Missão</h2>
|
||||
</div>
|
||||
<p className="text-gray-700 leading-relaxed text-base">
|
||||
Facilitar o acesso e a compreensão do ecossistema ESP32 através de uma
|
||||
interface em português que organiza especificações técnicas, compara séries,
|
||||
apresenta frameworks e centraliza links para a documentação oficial da
|
||||
Espressif. O objetivo é economizar tempo na busca por informações técnicas.
|
||||
</p>
|
||||
</article>
|
||||
{/* Two columns details grid */}
|
||||
<div className="mt-12 grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<section className="lg:col-span-2 space-y-8">
|
||||
{/* Missao article */}
|
||||
<article className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-3xl p-8 shadow-xl border border-slate-200/60 dark:border-slate-800/80 hover:border-purple-500/20 dark:hover:border-purple-400/20 transition-all">
|
||||
<div className="flex items-center gap-3.5 mb-5 select-none">
|
||||
<div className="w-12 h-12 bg-purple-500/10 border border-purple-500/20 rounded-2xl flex items-center justify-center text-purple-500">
|
||||
<Target className="w-6 h-6" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-display font-extrabold text-slate-850 dark:text-slate-100">Missão</h2>
|
||||
</div>
|
||||
<p className="text-slate-650 dark:text-slate-350 leading-relaxed font-semibold text-sm">
|
||||
Facilitar o acesso e a compreensão do ecossistema ESP32 através de uma
|
||||
interface em português que organiza especificações técnicas, compara séries,
|
||||
apresenta frameworks e centraliza links para a documentação oficial da
|
||||
Espressif. O objetivo principal é otimizar o tempo no desenvolvimento de firmware e IoT.
|
||||
</p>
|
||||
</article>
|
||||
|
||||
<article className="bg-white rounded-3xl p-8 shadow-lg border-2 border-gray-100">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-12 h-12 bg-linear-to-br from-purple-500 to-pink-600 rounded-xl flex items-center justify-center">
|
||||
<span className="text-2xl">❓</span>
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold text-gray-900">Por que o ESPDocs?</h2>
|
||||
</div>
|
||||
<p className="text-gray-700 leading-relaxed mb-6 text-base">
|
||||
As informações sobre ESP32 estão dispersas em datasheets (PDF), repositórios
|
||||
GitHub e documentação oficial — tudo em inglês. O ESPDocs organiza essas
|
||||
informações em uma interface em português, facilitando comparações e consultas
|
||||
rápidas antes de mergulhar na documentação técnica oficial.
|
||||
</p>
|
||||
{/* Por que o ESPDocs article */}
|
||||
<article className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-3xl p-8 shadow-xl border border-slate-200/60 dark:border-slate-800/80">
|
||||
<div className="flex items-center gap-3.5 mb-5 select-none">
|
||||
<div className="w-12 h-12 bg-blue-500/10 border border-blue-500/20 rounded-2xl flex items-center justify-center text-blue-500">
|
||||
<HelpCircle className="w-6 h-6" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-display font-extrabold text-slate-850 dark:text-slate-100">Por que o ESPDocs?</h2>
|
||||
</div>
|
||||
<p className="text-slate-650 dark:text-slate-350 leading-relaxed mb-6 font-semibold text-sm">
|
||||
As informações sobre os microcontroladores ESP32 muitas vezes estão dispersas em datasheets densos,
|
||||
manuais de referência (TRM), repositórios secundários e documentação técnica — tudo inteiramente em inglês.
|
||||
O ESPDocs condensa e estrutura esses dados, auxiliando na triagem rápida antes de aprofundar na engenharia oficial.
|
||||
</p>
|
||||
|
||||
<div className="grid sm:grid-cols-2 gap-4">
|
||||
{features.map((f) => (
|
||||
<div key={f.title} className="flex gap-4 items-start bg-linear-to-br from-gray-50 to-white p-5 rounded-xl border-2 border-gray-100 hover:border-purple-200 transition-colors">
|
||||
<div className="text-3xl shrink-0" aria-hidden="true">{f.icon}</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="font-bold text-gray-900 mb-1 text-base">{f.title}</h3>
|
||||
<p className="text-sm text-gray-600 leading-relaxed">{f.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</article>
|
||||
<div className="grid sm:grid-cols-2 gap-4">
|
||||
{features.map((f) => (
|
||||
<div key={f.title} className="flex gap-4 items-start bg-slate-50/50 dark:bg-slate-950/20 p-5 rounded-2xl border border-slate-150/40 dark:border-slate-850/40 hover:border-purple-500/10 dark:hover:border-purple-400/10 transition-colors">
|
||||
<div className="shrink-0 p-1 bg-white dark:bg-slate-900 rounded-xl shadow-xs" aria-hidden="true">
|
||||
{f.icon}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="font-bold text-slate-800 dark:text-slate-200 mb-1 text-sm">{f.title}</h3>
|
||||
<p className="text-xs text-slate-500 dark:text-slate-450 leading-relaxed font-semibold">{f.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article className="bg-white rounded-3xl p-8 shadow-lg border-2 border-gray-100">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-12 h-12 bg-linear-to-br from-green-500 to-teal-600 rounded-xl flex items-center justify-center">
|
||||
<span className="text-2xl">🤝</span>
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold text-gray-900">Como contribuir</h2>
|
||||
</div>
|
||||
<p className="text-gray-700 leading-relaxed mb-4 text-base">
|
||||
Correções, traduções e novos exemplos são bem-vindos. Abra uma
|
||||
issue ou um pull request no repositório. Busco por clareza nas
|
||||
descrições e exemplos testáveis.
|
||||
</p>
|
||||
<div className="bg-linear-to-r from-blue-50 to-purple-50 rounded-xl p-5 border-2 border-blue-100">
|
||||
<ol className="list-decimal list-inside text-gray-700 space-y-2 text-sm">
|
||||
<li className="font-medium">Fork do repositório e crie uma branch clara.</li>
|
||||
<li className="font-medium">Adicione exemplos ou corrija o conteúdo com referências quando possível.</li>
|
||||
<li className="font-medium">Abra um PR e descreva o que foi alterado.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</article>
|
||||
{/* Contribuir article */}
|
||||
<article className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-3xl p-8 shadow-xl border border-slate-200/60 dark:border-slate-800/80">
|
||||
<div className="flex items-center gap-3.5 mb-5 select-none">
|
||||
<div className="w-12 h-12 bg-emerald-500/10 border border-emerald-500/20 rounded-2xl flex items-center justify-center text-emerald-500">
|
||||
<Users className="w-6 h-6" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-display font-extrabold text-slate-850 dark:text-slate-100">Como Contribuir</h2>
|
||||
</div>
|
||||
|
||||
<article className="bg-linear-to-br from-purple-50 to-pink-50 rounded-3xl p-8 shadow-lg border-2 border-purple-100">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-12 h-12 bg-linear-to-br from-purple-600 to-pink-600 rounded-xl flex items-center justify-center">
|
||||
<span className="text-2xl">♿</span>
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold text-gray-900">Acessibilidade</h2>
|
||||
</div>
|
||||
<p className="text-gray-700 leading-relaxed text-base">
|
||||
O site prioriza leitura, contraste legível e navegação por teclado.
|
||||
Se encontrar barreiras, por favor relate para que eu possa melhorar.
|
||||
</p>
|
||||
</article>
|
||||
</section>
|
||||
<p className="text-slate-650 dark:text-slate-350 leading-relaxed mb-6 font-semibold text-sm">
|
||||
Correções de tradução, refinamento de tabelas e novos trechos funcionais de exemplos são muito bem-vindos!
|
||||
A plataforma cresce por meio da comunidade.
|
||||
</p>
|
||||
|
||||
<aside className="space-y-6">
|
||||
<div className="bg-white rounded-3xl p-8 shadow-lg border-2 border-gray-100 hover:border-purple-200 transition-colors">
|
||||
<div className="flex flex-col items-center mb-6">
|
||||
<div className="w-20 h-20 bg-linear-to-br from-purple-100 to-pink-100 rounded-2xl flex items-center justify-center mb-4">
|
||||
<img
|
||||
src="/logo.png"
|
||||
alt="Ciência Embarcada"
|
||||
className="h-16 w-16 object-contain"
|
||||
/>
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-gray-900 text-center">Sobre o Autor</h3>
|
||||
</div>
|
||||
<div className="bg-slate-50/50 dark:bg-slate-950/20 rounded-2xl p-5 border border-slate-150/40 dark:border-slate-850/40">
|
||||
<ol className="list-decimal list-inside text-slate-700 dark:text-slate-300 space-y-2 text-xs font-semibold">
|
||||
<li>Faça o fork do repositório no GitHub.</li>
|
||||
<li>Implemente melhorias e revise referências técnicas oficiais.</li>
|
||||
<li>Abra o seu Pull Request (PR) detalhando os ajustes efetuados.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<p className="text-gray-700 text-sm leading-relaxed mb-4 text-justify">
|
||||
<strong>Lucas Rayan Guerra</strong>, criador do <a
|
||||
href="https://cienciaembarcada.com.br"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-purple-600 hover:text-purple-800 font-semibold"
|
||||
>
|
||||
Ciência Embarcada
|
||||
</a>, é Técnico em Desenvolvimento de Sistemas, estudante de Ciência da Computação na UFRPE e de Blockchain e Criptografia Digital na FMU.
|
||||
</p>
|
||||
{/* Accessibility note */}
|
||||
<article className="bg-gradient-to-br from-purple-500/5 to-pink-500/5 dark:from-purple-500/10 dark:to-pink-500/10 rounded-3xl p-8 shadow-lg border border-purple-500/20 dark:border-pink-500/20">
|
||||
<div className="flex items-center gap-3.5 mb-5 select-none">
|
||||
<div className="w-12 h-12 bg-pink-500/10 border border-pink-500/20 rounded-2xl flex items-center justify-center text-pink-500">
|
||||
<Accessibility className="w-6 h-6" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-display font-extrabold text-slate-850 dark:text-slate-100">Acessibilidade</h2>
|
||||
</div>
|
||||
<p className="text-slate-650 dark:text-slate-350 leading-relaxed font-semibold text-sm">
|
||||
O ESPDocs preza pela legibilidade, alto contraste cromático e compatibilidade de navegação por teclado.
|
||||
Caso encontre alguma barreira técnica de leitura ou navegação, sinta-se encorajado a relatar via Issue.
|
||||
</p>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<p className="text-gray-700 text-sm leading-relaxed mb-4 text-justify">
|
||||
Recifense apaixonado por Pernambuco, Diretor de Software da Semine AgriTech e palestrante em eventos como o REC'n'Play, Lucas compartilha conhecimento sobre ESP32, Eletrônica, IoT, Cibersegurança e muito mais.
|
||||
</p>
|
||||
{/* Sidebar area */}
|
||||
<aside className="space-y-6">
|
||||
{/* Author Card */}
|
||||
<div className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl border border-slate-200/60 dark:border-slate-800/80 rounded-3xl p-8 shadow-xl">
|
||||
<div className="flex flex-col items-center mb-6 select-none">
|
||||
<div className="w-20 h-20 bg-gradient-to-br from-purple-500/10 to-pink-500/10 rounded-2xl flex items-center justify-center mb-4 border border-purple-500/20 shadow-inner">
|
||||
<img
|
||||
src="/logo.png"
|
||||
alt="Ciência Embarcada Logo"
|
||||
className="h-16 w-16 object-contain"
|
||||
/>
|
||||
</div>
|
||||
<h3 className="text-lg font-display font-extrabold text-slate-850 dark:text-slate-100 text-center">Sobre o Autor</h3>
|
||||
</div>
|
||||
|
||||
<div className="bg-purple-50 border border-purple-100 rounded-lg p-4 mb-4 text-justify">
|
||||
<p className="text-sm text-gray-700 italic">
|
||||
"O Ciência Embarcada nasceu do desejo de criar um espaço livre e organizado para compartilhar conhecimentos e experiências sobre tecnologia e ciência."
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-4 text-xs font-semibold text-slate-650 dark:text-slate-350 text-justify leading-relaxed">
|
||||
<p>
|
||||
<strong>Lucas Rayan Guerra</strong>, criador da iniciativa <a
|
||||
href="https://cienciaembarcada.com.br"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-purple-600 hover:text-purple-800 dark:text-purple-400 dark:hover:text-purple-300 font-bold"
|
||||
>
|
||||
Ciência Embarcada
|
||||
</a>, é Técnico em Desenvolvimento de Sistemas, estudante de Ciência da Computação na UFRPE e estuda Blockchain e Criptografia.
|
||||
</p>
|
||||
|
||||
<a
|
||||
href="https://cienciaembarcada.com.br"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center w-full px-4 py-2 rounded-lg bg-linear-to-r from-blue-600 to-purple-600 text-white font-semibold shadow-sm hover:shadow-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500 transition-all"
|
||||
>
|
||||
🚀 Visitar Ciência Embarcada
|
||||
</a>
|
||||
</div>
|
||||
<p>
|
||||
Diretor de Software na Semine AgriTech e palestrante em feiras e conferências (como o REC'n'Play), compartilha ativamente material sobre hardware, internet das coisas e cibersegurança.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl p-6 shadow-md border border-gray-100">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-lg font-semibold">Contribua</h3>
|
||||
<span className="text-xs bg-green-100 text-green-800 px-2 py-1 rounded-full">Open Source</span>
|
||||
</div>
|
||||
<div className="bg-purple-500/5 dark:bg-purple-400/5 border border-purple-500/10 dark:border-purple-400/10 rounded-2xl p-4 my-5 text-justify">
|
||||
<p className="text-[10px] text-slate-650 dark:text-slate-400 italic font-semibold leading-relaxed">
|
||||
"O Ciência Embarcada nasceu para criar um espaço de curadoria técnica livre, promovendo a popularização científica e do hardware embarcado."
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p className="text-gray-700 mb-4">
|
||||
Ajude a melhorar o ESPDocs com exemplos, correções e traduções.
|
||||
</p>
|
||||
<a
|
||||
href="https://cienciaembarcada.com.br"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-1.5 w-full px-4 py-3 rounded-xl bg-gradient-to-r from-blue-600 to-purple-600 text-white font-bold text-xs uppercase tracking-wider shadow-md hover:shadow-lg active:scale-98 transition-all"
|
||||
>
|
||||
<span>Visitar Ciência Embarcada</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
<a
|
||||
href="https://github.com/lucasrguerra/ESPDocs"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center px-4 py-2 rounded-lg bg-linear-to-r from-blue-600 to-purple-600 text-white font-semibold shadow-sm hover:shadow-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500"
|
||||
aria-label="Abrir repositório no GitHub"
|
||||
>
|
||||
Ver repositório
|
||||
</a>
|
||||
</div>
|
||||
{/* Repository / Contributions card */}
|
||||
<div className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl border border-slate-200/60 dark:border-slate-800/80 rounded-3xl p-6 shadow-xl">
|
||||
<div className="flex items-center justify-between mb-4 select-none">
|
||||
<h3 className="text-sm font-display font-extrabold text-slate-800 dark:text-slate-200">Contribua</h3>
|
||||
<span className="text-[9px] bg-emerald-500/10 text-emerald-600 dark:text-emerald-450 px-2.5 py-0.5 rounded-full font-bold uppercase tracking-wider border border-emerald-500/20">Open Source</span>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 text-sm text-gray-500">
|
||||
<strong>Importante:</strong> Esta é uma plataforma de organização de informações, não uma
|
||||
tradução da documentação oficial. A interface está em português, mas datasheets, manuais
|
||||
técnicos e repositórios linkados são da Espressif e estão em inglês. Para decisões
|
||||
técnicas críticas, sempre consulte a documentação oficial.
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 dark:text-slate-450 leading-relaxed mb-4 font-semibold">
|
||||
Ajude a expandir a cobertura do ESPDocs submetendo exemplos de códigos ou aprimoramentos técnicos.
|
||||
</p>
|
||||
|
||||
<div className="bg-white/80 rounded-2xl p-4 shadow-sm border border-gray-100">
|
||||
<h4 className="font-semibold mb-2">Resumo</h4>
|
||||
<ul className="text-sm text-gray-700 space-y-2">
|
||||
<li>🔎 Conteúdo técnico explicado.</li>
|
||||
<li>🧪 Exemplos e projetos.</li>
|
||||
<li>🤝 Comunidade e contribuições.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</main>
|
||||
<a
|
||||
href="https://github.com/lucasrguerra/ESPDocs"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-2 w-full px-4 py-3 rounded-xl bg-slate-900 dark:bg-slate-100 text-white dark:text-slate-950 font-bold text-xs uppercase tracking-wider shadow-md hover:opacity-90 active:scale-98 transition-all"
|
||||
aria-label="Abrir repositório no GitHub"
|
||||
>
|
||||
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||
<path d="M12 .5a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2.1c-3.3.7-4-1.6-4-1.6-.5-1.2-1.2-1.5-1.2-1.5-1-.7.1-.7.1-.7 1.1.1 1.7 1.1 1.7 1.1 1 .1 1.6-.7 1.9-1 .1-.8.4-1.3.8-1.6-2.6-.3-5.3-1.3-5.3-5.8 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.6.1-3.3 0 0 1-.3 3.3 1.2a11.4 11.4 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.6 1.7.2 3 .1 3.3.8.8 1.2 1.9 1.2 3.1 0 4.5-2.7 5.5-5.3 5.8.5.4.9 1.1.9 2.2v3.2c0 .3.2.7.8.6A12 12 0 0012 .5z" />
|
||||
</svg>
|
||||
<span>Ver Repositório</span>
|
||||
</a>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
<div className="mt-4 text-[10px] text-slate-450 dark:text-slate-500 leading-relaxed font-semibold border-t border-slate-100 dark:border-slate-850/45 pt-4">
|
||||
<strong>Aviso Legal:</strong> O ESPDocs é um agregador e organizador. Manuais técnicos detalhados e repositórios oficiais referenciados são de propriedade exclusiva da Espressif Systems. Para projetos críticos comerciais, consulte sempre a documentação nativa oficial.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Summary sidebar card */}
|
||||
<div className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl border border-slate-200/60 dark:border-slate-800/80 rounded-3xl p-6 shadow-xl select-none">
|
||||
<h4 className="text-xs font-display font-extrabold text-slate-800 dark:text-slate-200 mb-3 uppercase tracking-wider">Conteúdo Técnico</h4>
|
||||
<ul className="text-xs text-slate-600 dark:text-slate-400 space-y-2.5 font-bold">
|
||||
<li className="flex items-center gap-2">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-purple-500" />
|
||||
<span>Focado em clareza técnica</span>
|
||||
</li>
|
||||
<li className="flex items-center gap-2">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-blue-500" />
|
||||
<span>Exemplos práticos de códigos</span>
|
||||
</li>
|
||||
<li className="flex items-center gap-2">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500" />
|
||||
<span>Links oficiais mapeados</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,101 +1,99 @@
|
||||
"use client";
|
||||
'use client';
|
||||
|
||||
import { Award, CheckCircle, HelpCircle, ExternalLink } from 'lucide-react';
|
||||
|
||||
export default function BoardCard({ board }) {
|
||||
const mainFields = ['Nome', 'Link', 'Imagem', 'Loja Oficial'];
|
||||
const mainFields = ['Nome', 'Link', 'Imagem', 'Loja Oficial'];
|
||||
|
||||
const technicalFields = Object.keys(board).filter(
|
||||
key => !mainFields.includes(key) && board[key]
|
||||
);
|
||||
const technicalFields = Object.keys(board).filter(
|
||||
key => !mainFields.includes(key) && board[key]
|
||||
);
|
||||
|
||||
const hasFeature = (value) => {
|
||||
const yesValues = ['sim', 'opcional'];
|
||||
return yesValues.includes(value?.toString().toLowerCase().trim());
|
||||
};
|
||||
const hasFeature = (value) => {
|
||||
const yesValues = ['sim', 'opcional'];
|
||||
return yesValues.includes(value?.toString().toLowerCase().trim());
|
||||
};
|
||||
|
||||
// Filtrar apenas características que têm "Sim" ou "Opcional"
|
||||
const availableFeatures = technicalFields.filter(field => hasFeature(board[field]));
|
||||
const optionalFeatures = technicalFields.filter(field =>
|
||||
board[field]?.toString().toLowerCase().trim() === 'opcional'
|
||||
);
|
||||
// Filtrar apenas características que têm "Sim" ou "Opcional"
|
||||
const availableFeatures = technicalFields.filter(field => hasFeature(board[field]));
|
||||
const optionalFeatures = technicalFields.filter(field =>
|
||||
board[field]?.toString().toLowerCase().trim() === 'opcional'
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-2xl shadow-lg hover:shadow-2xl transition-all duration-300 overflow-hidden group flex flex-col h-full">
|
||||
{/* Imagem */}
|
||||
{board.Imagem && (
|
||||
<div
|
||||
className="mx-auto w-fit"
|
||||
dangerouslySetInnerHTML={{ __html: board.Imagem }}
|
||||
/>
|
||||
)}
|
||||
return (
|
||||
<div className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl border border-slate-200/60 dark:border-slate-800/80 rounded-3xl shadow-lg hover:shadow-2xl hover:-translate-y-1.5 transition-all duration-300 overflow-hidden group flex flex-col h-full animate-fadeIn select-none">
|
||||
{/* Image Container with premium glass background */}
|
||||
{board.Imagem && (
|
||||
<div
|
||||
className="mx-auto w-full p-4 flex justify-center bg-white dark:bg-white border-b border-slate-100 dark:border-slate-850/40 shrink-0"
|
||||
dangerouslySetInnerHTML={{ __html: board.Imagem }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="p-6 flex flex-col grow">
|
||||
{/* Nome */}
|
||||
<h3 className="text-xl font-bold text-gray-800 mb-2 line-clamp-2">
|
||||
{board.Nome || 'Placa sem nome'}
|
||||
</h3>
|
||||
<div className="p-6 flex flex-col grow justify-between">
|
||||
<div>
|
||||
{/* Loja Oficial Badge */}
|
||||
{board['Loja Oficial'] && board['Loja Oficial'].toLowerCase() === 'sim' && (
|
||||
<div className="mb-2">
|
||||
<span className="inline-flex items-center gap-1 bg-blue-500/10 text-blue-600 dark:text-blue-400 border border-blue-500/20 rounded-full px-2.5 py-0.5 text-[9px] font-bold uppercase tracking-wider">
|
||||
<Award className="w-3.5 h-3.5 shrink-0" />
|
||||
<span>Loja Oficial</span>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Loja Oficial */}
|
||||
{board['Loja Oficial'] && board['Loja Oficial'].toLowerCase() === 'sim' && (
|
||||
<div className="mb-4">
|
||||
<p className="text-sm text-blue-600 flex items-center gap-1.5">
|
||||
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M6.267 3.455a3.066 3.066 0 001.745-.723 3.066 3.066 0 013.976 0 3.066 3.066 0 001.745.723 3.066 3.066 0 012.812 2.812c.051.643.304 1.254.723 1.745a3.066 3.066 0 010 3.976 3.066 3.066 0 00-.723 1.745 3.066 3.066 0 01-2.812 2.812 3.066 3.066 0 00-1.745.723 3.066 3.066 0 01-3.976 0 3.066 3.066 0 00-1.745-.723 3.066 3.066 0 01-2.812-2.812 3.066 3.066 0 00-.723-1.745 3.066 3.066 0 010-3.976 3.066 3.066 0 00.723-1.745 3.066 3.066 0 012.812-2.812zm7.44 5.252a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
|
||||
</svg>
|
||||
<span>Link da loja oficial do fabricante</span>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{/* Nome */}
|
||||
<h3 className="text-base font-display font-extrabold text-slate-850 dark:text-slate-100 mb-4 line-clamp-2 leading-snug group-hover:text-purple-650 dark:group-hover:text-purple-400 transition-colors">
|
||||
{board.Nome || 'Placa Genérica'}
|
||||
</h3>
|
||||
|
||||
{/* Características Técnicas */}
|
||||
{availableFeatures.length > 0 && (
|
||||
<div className="mb-4 grow">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<svg className="w-4 h-4 text-green-600" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
|
||||
</svg>
|
||||
<h4 className="text-sm font-semibold text-gray-700">Recursos Disponíveis</h4>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{availableFeatures.map((field) => {
|
||||
const isOptional = optionalFeatures.includes(field);
|
||||
return (
|
||||
<div
|
||||
key={field}
|
||||
className={`text-xs px-3 py-1.5 rounded-lg font-medium flex items-center gap-1 ${
|
||||
isOptional
|
||||
? 'bg-yellow-100 text-yellow-700 border border-yellow-200'
|
||||
: 'bg-green-100 text-green-700 border border-green-200'
|
||||
}`}
|
||||
>
|
||||
{isOptional ? (
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
)}
|
||||
<span>{field}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* Características Técnicas */}
|
||||
{availableFeatures.length > 0 && (
|
||||
<div className="mb-6">
|
||||
<h4 className="text-[9px] font-bold text-slate-400 dark:text-slate-500 uppercase tracking-widest flex items-center gap-1.5 mb-2.5">
|
||||
<CheckCircle className="w-3.5 h-3.5 text-emerald-500" />
|
||||
<span>Recursos Embarcados</span>
|
||||
</h4>
|
||||
|
||||
{/* Link para mais informações - sempre no final */}
|
||||
{board.Link && (
|
||||
<a
|
||||
href={board.Link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="block w-full bg-linear-to-r from-blue-600 to-purple-600 text-white text-center px-4 py-3 rounded-xl font-semibold hover:shadow-lg transform hover:-translate-y-1 transition-all duration-300 mt-auto"
|
||||
>
|
||||
Abrir no AliExpress 🔗
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{availableFeatures.map((field) => {
|
||||
const isOptional = optionalFeatures.includes(field);
|
||||
return (
|
||||
<div
|
||||
key={field}
|
||||
className={`text-[9px] px-2 py-0.5 rounded-lg font-bold flex items-center gap-1 ${
|
||||
isOptional
|
||||
? 'bg-amber-500/10 dark:bg-amber-500/15 border border-amber-500/20 text-amber-600 dark:text-amber-400'
|
||||
: 'bg-emerald-500/10 dark:bg-emerald-500/15 border border-emerald-500/20 text-emerald-600 dark:text-emerald-400'
|
||||
}`}
|
||||
>
|
||||
{isOptional ? (
|
||||
<HelpCircle className="w-2.5 h-2.5 shrink-0" />
|
||||
) : (
|
||||
<CheckCircle className="w-2.5 h-2.5 shrink-0" />
|
||||
)}
|
||||
<span>{field}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Link de Compra */}
|
||||
{board.Link && (
|
||||
<a
|
||||
href={board.Link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="block w-full bg-slate-900 dark:bg-slate-100 text-white dark:text-slate-950 text-center px-4 py-3 rounded-xl font-bold text-xs uppercase tracking-wider hover:opacity-90 active:scale-98 transition-all shadow-md mt-auto flex items-center justify-center gap-1.5"
|
||||
>
|
||||
<span>Adquirir no AliExpress</span>
|
||||
<ExternalLink className="w-3.5 h-3.5" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,141 +1,178 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Code, Copy, CheckCircle, ExternalLink, HelpCircle } from 'lucide-react'
|
||||
|
||||
export default function CodeExample({ framework }) {
|
||||
const examplesUrl = framework?.exemplos;
|
||||
const color = framework?.cor || '#6B7280';
|
||||
const examplesUrl = framework?.exemplos;
|
||||
const color = framework?.cor || '#6B7280';
|
||||
|
||||
const [sample, setSample] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [sample, setSample] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setSample('');
|
||||
setError(null);
|
||||
setLoading(false);
|
||||
useEffect(() => {
|
||||
setSample('');
|
||||
setError(null);
|
||||
setLoading(false);
|
||||
|
||||
if (!framework) return;
|
||||
if (!framework) return;
|
||||
|
||||
const exemploPath = framework.exemplo;
|
||||
if (!exemploPath) {
|
||||
const msg = `// Exemplos completos: ${examplesUrl || 'Ver repositório'}\n// Abra o link acima para ver exemplos e código completo.\n`;
|
||||
setSample(msg);
|
||||
return;
|
||||
}
|
||||
const exemploPath = framework.exemplo;
|
||||
if (!exemploPath) {
|
||||
const msg = `// Exemplos completos: ${examplesUrl || 'Ver repositório'}\n// Abra o link acima para ver exemplos e código completo.\n`;
|
||||
setSample(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
const url = exemploPath.startsWith('/') ? exemploPath : `/${exemploPath}`;
|
||||
const url = exemploPath.startsWith('/') ? exemploPath : `/${exemploPath}`;
|
||||
|
||||
let cancelled = false;
|
||||
setLoading(true);
|
||||
let cancelled = false;
|
||||
setLoading(true);
|
||||
|
||||
fetch(url)
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
return res.text();
|
||||
})
|
||||
.then((text) => {
|
||||
if (cancelled) return;
|
||||
if (text && text.trim().length > 0) {
|
||||
setSample(text);
|
||||
} else {
|
||||
setSample(`// Arquivo de exemplo está vazio. Consulte: ${examplesUrl || 'repositório'}\n`);
|
||||
setError('Arquivo vazio');
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
if (cancelled) return;
|
||||
setSample(`// Não foi possível carregar o exemplo local. Consulte: ${examplesUrl || 'repositório'}\n`);
|
||||
setError(err.message || 'Falha ao carregar exemplo');
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) setLoading(false);
|
||||
});
|
||||
fetch(url)
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
return res.text();
|
||||
})
|
||||
.then((text) => {
|
||||
if (cancelled) return;
|
||||
if (text && text.trim().length > 0) {
|
||||
setSample(text);
|
||||
} else {
|
||||
setSample(`// Arquivo de exemplo está vazio. Consulte o repositório oficial.\n`);
|
||||
setError('Arquivo vazio');
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
if (cancelled) return;
|
||||
setSample(`// Não foi possível carregar o exemplo localmente.\n// Consulte o link do GitHub acima para exemplos funcionais.\n`);
|
||||
setError(err.message || 'Falha ao carregar exemplo');
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) setLoading(false);
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [framework?.exemplo, framework?.exemplos]);
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [framework?.exemplo, framework?.exemplos]);
|
||||
|
||||
const lines = (sample || '').split('\n');
|
||||
const lines = (sample || '').split('\n');
|
||||
|
||||
async function handleCopy() {
|
||||
try {
|
||||
await navigator.clipboard.writeText(sample);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch {}
|
||||
}
|
||||
async function handleCopy() {
|
||||
try {
|
||||
await navigator.clipboard.writeText(sample);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-2xl shadow-lg p-6 border-2 border-gray-100 mb-8">
|
||||
<div className="md:flex items-start justify-between gap-4 mb-4">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-800 mb-1">Exemplo de Código</h3>
|
||||
<p className="text-sm text-gray-600">Trecho rápido para começar — personalize conforme necessário.</p>
|
||||
</div>
|
||||
return (
|
||||
<div className="bg-white/80 dark:bg-slate-900/40 backdrop-blur-xl rounded-3xl p-6 md:p-8 border border-slate-200/60 dark:border-slate-800/80 shadow-2xl mb-8 animate-fadeIn">
|
||||
<div className="md:flex items-center justify-between gap-6 mb-6">
|
||||
<div className="select-none">
|
||||
<h3 className="text-xl font-display font-extrabold text-slate-850 dark:text-slate-100 mb-1 flex items-center gap-2">
|
||||
<Code className="w-5 h-5" style={{ color }} />
|
||||
<span>Demonstração de Código</span>
|
||||
</h3>
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400 font-semibold">Trecho de inicialização rápida — copie e personalize em seu projeto.</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 md:flex items-center gap-2">
|
||||
<span
|
||||
className="inline-flex items-center text-sm font-semibold px-3 py-1 rounded-full text-white"
|
||||
style={{ backgroundColor: color }}
|
||||
>
|
||||
{framework?.linguagem || 'Código'}
|
||||
</span>
|
||||
<div className="flex flex-wrap items-center gap-2.5 mt-4 md:mt-0 select-none">
|
||||
<span
|
||||
className="inline-flex items-center text-[10px] font-bold px-2.5 py-1 rounded-full uppercase tracking-wider"
|
||||
style={{
|
||||
backgroundColor: `${color}18`,
|
||||
color,
|
||||
border: `1px solid ${color}25`
|
||||
}}
|
||||
>
|
||||
{framework?.linguagem || 'Código'}
|
||||
</span>
|
||||
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="inline-flex items-center gap-2 bg-white text-gray-800 px-3 py-2 rounded-lg font-medium shadow border-2 border-gray-100 cursor-pointer hover:shadow-lg transition"
|
||||
title="Copiar código"
|
||||
>
|
||||
{copied ? '✅ Copiado' : '📋 Copiar'}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="inline-flex items-center gap-1.5 bg-slate-50 dark:bg-slate-850 hover:bg-slate-100 dark:hover:bg-slate-800 text-slate-700 dark:text-slate-250 px-3.5 py-2 rounded-xl font-bold text-xs shadow-xs border border-slate-200/60 dark:border-slate-800/80 cursor-pointer transition active:scale-95"
|
||||
title="Copiar código para a área de transferência"
|
||||
>
|
||||
{copied ? (
|
||||
<>
|
||||
<CheckCircle className="w-3.5 h-3.5 text-emerald-500" />
|
||||
<span className="text-emerald-600 dark:text-emerald-450">Copiado!</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Copy className="w-3.5 h-3.5" />
|
||||
<span>Copiar</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{examplesUrl && (
|
||||
<a
|
||||
href={examplesUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-3 py-2 rounded-lg font-semibold text-white col-span-2"
|
||||
style={{
|
||||
background: `linear-gradient(90deg, ${color} 0%, rgba(124,58,237,1) 100%)`
|
||||
}}
|
||||
title="Abrir exemplos no GitHub"
|
||||
>
|
||||
🔗 Ver Outros no GitHub
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{examplesUrl && (
|
||||
<a
|
||||
href={examplesUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1.5 px-4 py-2 rounded-xl text-xs font-bold text-white shadow-lg active:scale-95 transition hover:opacity-95"
|
||||
style={{
|
||||
background: `linear-gradient(90deg, ${color} 0%, rgba(147,51,234,1) 100%)`,
|
||||
boxShadow: `0 8px 20px -8px ${color}`
|
||||
}}
|
||||
title="Abrir exemplos no GitHub"
|
||||
>
|
||||
<span>Mais Exemplos</span>
|
||||
<ExternalLink className="w-3.5 h-3.5" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg overflow-hidden border">
|
||||
<div className="p-0">
|
||||
<div className="flex items-center justify-between bg-linear-to-r from-gray-900 to-gray-800 p-3">
|
||||
<div className="text-xs text-gray-300 font-medium">{framework?.nome}</div>
|
||||
<div className="text-xs text-gray-400">
|
||||
{loading ? 'Carregando exemplo...' : (framework?.exemplo ? 'Exemplo do framework' : 'Abrir no GitHub')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-2xl overflow-hidden border border-slate-200/60 dark:border-slate-800/80 shadow-md">
|
||||
<div className="select-none">
|
||||
<div className="flex items-center justify-between bg-slate-100/80 dark:bg-slate-950/70 border-b border-slate-200/60 dark:border-slate-850/60 px-4 py-3.5">
|
||||
<div className="text-xs font-bold text-slate-750 dark:text-slate-250 flex items-center gap-1.5">
|
||||
<span className="w-2.5 h-2.5 rounded-full bg-red-500/80" />
|
||||
<span className="w-2.5 h-2.5 rounded-full bg-yellow-500/80" />
|
||||
<span className="w-2.5 h-2.5 rounded-full bg-green-500/80" />
|
||||
<span className="ml-2 font-mono text-[10px] text-slate-500 dark:text-slate-400">{framework?.nome} example</span>
|
||||
</div>
|
||||
<div className="text-[9px] font-bold text-slate-400 dark:text-slate-500 uppercase tracking-widest">
|
||||
{loading ? 'Buscando exemplo...' : (framework?.exemplo ? 'Local Source' : 'External GitHub')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex">
|
||||
<pre
|
||||
className="bg-gray-900 text-gray-100 p-4 w-full overflow-auto"
|
||||
style={{ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Roboto Mono", "Courier New", monospace' }}
|
||||
>
|
||||
{lines.map((line, idx) => (
|
||||
<div key={idx} className="flex select-text">
|
||||
<span className="text-gray-500 pr-4 text-right w-8 select-none">{idx + 1}</span>
|
||||
<code className="whitespace-pre-wrap flex-1">{line === '' ? '\u00A0' : line}</code>
|
||||
</div>
|
||||
))}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex bg-slate-950">
|
||||
<pre
|
||||
className="text-slate-200 p-5 w-full overflow-auto text-xs leading-relaxed scrollbar-thin select-text"
|
||||
style={{ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Roboto Mono", "Courier New", monospace' }}
|
||||
>
|
||||
{lines.map((line, idx) => (
|
||||
<div key={idx} className="flex">
|
||||
<span className="text-slate-600 dark:text-slate-650 pr-4 text-right w-8 select-none border-r border-slate-900/60 mr-4 font-semibold">{idx + 1}</span>
|
||||
<code className="whitespace-pre flex-1 text-left">{line === '' ? '\u00A0' : line}</code>
|
||||
</div>
|
||||
))}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <p className="text-xs text-red-500 mt-2">Não foi possível carregar o arquivo de exemplo: {error}. Usando instrução para o repositório.</p>}
|
||||
<p className="text-xs text-gray-500 mt-3">Dica: personalize este trecho conforme o seu projeto. Os exemplos completos estão no repositório.</p>
|
||||
</div>
|
||||
);
|
||||
{error && (
|
||||
<div className="bg-red-500/10 border border-red-500/20 rounded-xl p-3.5 mt-3 select-none">
|
||||
<p className="text-[10px] font-bold text-red-600 dark:text-red-400">
|
||||
Nota: Não foi possível carregar o arquivo de exemplo local ({error}). Use o botão "Mais Exemplos" para ver os códigos oficiais.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-4 flex items-start gap-2 bg-slate-50/50 dark:bg-slate-950/20 border border-slate-150/40 dark:border-slate-850/40 p-4 rounded-2xl select-none">
|
||||
<HelpCircle className="w-4.5 h-4.5 text-purple-500 shrink-0 mt-0.5" />
|
||||
<p className="text-[10px] font-semibold text-slate-500 dark:text-slate-400 leading-relaxed">
|
||||
<strong>Dica de Desenvolvimento:</strong> Personalize o trecho de inicialização com as pinagens e configurações corretas correspondentes à série do seu microcontrolador (ex: ESP32-S3, ESP32-C6). A documentação detalhada da API pode ser acessada na barra superior do framework.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,123 +1,147 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { ExternalLink } from "lucide-react";
|
||||
|
||||
export default function Footer() {
|
||||
const year = new Date().getFullYear();
|
||||
const [year, setYear] = useState(2026);
|
||||
|
||||
return (
|
||||
<footer role="contentinfo" aria-label="Rodapé do site ESPDocs" className="bg-linear-to-r from-blue-600 to-purple-600 text-white mt-12">
|
||||
<div className="max-w-6xl mx-auto px-6 pt-8 pb-4 rounded-t-2xl shadow-inner border-t border-white/10">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-2xl font-extrabold tracking-tight">ESPDocs</h3>
|
||||
<p className="text-sm text-white/90 max-w-sm">
|
||||
Documentação não oficial em português para ESP32 — focada em clareza, exemplos práticos e comunidade.
|
||||
</p>
|
||||
useEffect(() => {
|
||||
setYear(new Date().getFullYear());
|
||||
}, []);
|
||||
|
||||
<div className="">
|
||||
<a
|
||||
href="https://github.com/lucasrguerra/ESPDocs"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Ver repositório do ESPDocs no GitHub (abre em nova aba)"
|
||||
className="inline-flex items-center gap-2 bg-white/10 hover:bg-white/20 px-3 py-2 rounded-md text-sm font-semibold focus:outline-none focus:ring-2 focus:ring-white/30 transition"
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true" className="opacity-90">
|
||||
<path d="M12 .5a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2.1c-3.3.7-4-1.6-4-1.6-.5-1.2-1.2-1.5-1.2-1.5-1-.7.1-.7.1-.7 1.1.1 1.7 1.1 1.7 1.1 1 .1 1.6-.7 1.9-1 .1-.8.4-1.3.8-1.6-2.6-.3-5.3-1.3-5.3-5.8 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.6.1-3.3 0 0 1-.3 3.3 1.2a11.4 11.4 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.6 1.7.2 3 .1 3.3.8.8 1.2 1.9 1.2 3.1 0 4.5-2.7 5.5-5.3 5.8.5.4.9 1.1.9 2.2v3.2c0 .3.2.7.8.6A12 12 0 0012 .5z" fill="currentColor"/>
|
||||
</svg>
|
||||
Ver no GitHub
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<footer role="contentinfo" aria-label="Rodapé do site ESPDocs" className="bg-slate-50 dark:bg-slate-950 text-slate-600 dark:text-slate-400 mt-24 border-t border-slate-250/30 dark:border-slate-800/80 transition-all duration-300">
|
||||
<div className="max-w-7xl mx-auto px-6 py-12 md:py-16">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-10 md:gap-16">
|
||||
|
||||
<nav aria-label="Links úteis" className="text-sm">
|
||||
<h4 className="text-sm font-semibold mb-2">Links úteis</h4>
|
||||
<ul className="space-y-2">
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/lucasrguerra/ESPDocs"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:underline text-white/95 focus:outline-none focus:ring-2 focus:ring-white/20 rounded"
|
||||
aria-label="Abrir repositório no GitHub"
|
||||
>
|
||||
Repositório no GitHub
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/lucasrguerra/ESPDocs/issues"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:underline text-white/90 focus:outline-none focus:ring-2 focus:ring-white/20 rounded"
|
||||
aria-label="Abrir issues do repositório"
|
||||
>
|
||||
Reportar bug / Abrir issue
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://cienciaembarcada.com.br"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:underline text-white/90 focus:outline-none focus:ring-2 focus:ring-white/20 rounded"
|
||||
aria-label="Visitar Ciência Embarcada (abre em nova aba)"
|
||||
>
|
||||
Ciência Embarcada
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{/* Coluna 1: Logo & Info */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-2xl font-display font-bold text-slate-800 dark:text-slate-100">
|
||||
ESPDocs
|
||||
</h3>
|
||||
<p className="text-sm leading-relaxed text-slate-500 dark:text-slate-400">
|
||||
Plataforma brasileira e independente que centraliza e organiza informações técnicas sobre o ecossistema ESP32. Focada em clareza, usabilidade e agilidade no desenvolvimento de hardware.
|
||||
</p>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-semibold">Créditos</h4>
|
||||
<p className="text-sm text-white/90">
|
||||
Desenvolvido por{' '}
|
||||
<a
|
||||
href="https://linkedin.com/in/lucasrguerra"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline hover:text-white"
|
||||
aria-label="Perfil LinkedIn de Lucas Rayan Guerra (abre em nova aba)"
|
||||
>
|
||||
Lucas Rayan Guerra
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<div className="pt-2">
|
||||
<a
|
||||
href="https://github.com/lucasrguerra/ESPDocs"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Ver repositório do ESPDocs no GitHub (abre em nova aba)"
|
||||
className="inline-flex items-center gap-2 bg-slate-200/60 dark:bg-slate-900 hover:bg-slate-200 dark:hover:bg-slate-800 text-slate-700 dark:text-slate-200 px-4 py-2.5 rounded-xl text-sm font-semibold transition-all cursor-pointer hover:shadow-xs active:scale-[0.98]"
|
||||
>
|
||||
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M12 .5a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2.1c-3.3.7-4-1.6-4-1.6-.5-1.2-1.2-1.5-1.2-1.5-1-.7.1-.7.1-.7 1.1.1 1.7 1.1 1.7 1.1 1 .1 1.6-.7 1.9-1 .1-.8.4-1.3.8-1.6-2.6-.3-5.3-1.3-5.3-5.8 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.6.1-3.3 0 0 1-.3 3.3 1.2a11.4 11.4 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.6 1.7.2 3 .1 3.3.8.8 1.2 1.9 1.2 3.1 0 4.5-2.7 5.5-5.3 5.8.5.4.9 1.1.9 2.2v3.2c0 .3.2.7.8.6A12 12 0 0012 .5z" fill="currentColor"/>
|
||||
</svg>
|
||||
<span>Ver no GitHub</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3 mt-2">
|
||||
<a
|
||||
href="https://github.com/lucasrguerra/ESPDocs"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center w-9 h-9 rounded-md bg-white/10 hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/20"
|
||||
aria-label="GitHub do projeto"
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M12 .5a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2.1c-3.3.7-4-1.6-4-1.6-.5-1.2-1.2-1.5-1.2-1.5-1-.7.1-.7.1-.7 1.1.1 1.7 1.1 1.7 1.1 1 .1 1.6-.7 1.9-1 .1-.8.4-1.3.8-1.6-2.6-.3-5.3-1.3-5.3-5.8 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.6.1-3.3 0 0 1-.3 3.3 1.2a11.4 11.4 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.6 1.7.2 3 .1 3.3.8.8 1.2 1.9 1.2 3.1 0 4.5-2.7 5.5-5.3 5.8.5.4.9 1.1.9 2.2v3.2c0 .3.2.7.8.6A12 12 0 0012 .5z" fill="currentColor"/>
|
||||
</svg>
|
||||
</a>
|
||||
{/* Coluna 2: Links Úteis */}
|
||||
<nav aria-label="Links úteis" className="space-y-4">
|
||||
<h4 className="text-sm font-bold uppercase tracking-wider text-slate-800 dark:text-slate-200">
|
||||
Recursos
|
||||
</h4>
|
||||
<ul className="space-y-3 text-sm">
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/lucasrguerra/ESPDocs"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-purple-600 dark:hover:text-purple-400 transition-colors flex items-center gap-1.5"
|
||||
aria-label="Abrir repositório no GitHub"
|
||||
>
|
||||
<span>Repositório Principal</span>
|
||||
<ExternalLink className="w-3.5 h-3.5 opacity-60" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/lucasrguerra/ESPDocs/issues"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-purple-600 dark:hover:text-purple-400 transition-colors flex items-center gap-1.5"
|
||||
aria-label="Abrir issues do repositório"
|
||||
>
|
||||
<span>Reportar um Bug / Feedback</span>
|
||||
<ExternalLink className="w-3.5 h-3.5 opacity-60" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://cienciaembarcada.com.br"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-purple-600 dark:hover:text-purple-400 transition-colors flex items-center gap-1.5"
|
||||
aria-label="Visitar Ciência Embarcada (abre em nova aba)"
|
||||
>
|
||||
<span className="font-semibold text-purple-600 dark:text-purple-400">Ciência Embarcada</span>
|
||||
<ExternalLink className="w-3.5 h-3.5 opacity-60 text-purple-600 dark:text-purple-400" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<a
|
||||
href="https://linkedin.com/in/lucasrguerra"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center w-9 h-9 rounded-md bg-white/10 hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/20"
|
||||
aria-label="LinkedIn do autor"
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M4.98 3.5a2.5 2.5 0 11.001 5.001A2.5 2.5 0 014.98 3.5zM3 8.98h4v12H3v-12zM9.5 8.98h3.8v1.6h.1c.5-.9 1.8-1.9 3.6-1.9 3.8 0 4.5 2.5 4.5 5.8v6.5h-4v-5.8c0-1.4-.1-3.2-2-3.2-2 0-2.3 1.6-2.3 3.1v5.9h-4v-12z" fill="currentColor"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Coluna 3: Autor & Social */}
|
||||
<div className="space-y-4">
|
||||
<h4 className="text-sm font-bold uppercase tracking-wider text-slate-800 dark:text-slate-200">
|
||||
Sobre o Autor
|
||||
</h4>
|
||||
<p className="text-sm leading-relaxed text-slate-500 dark:text-slate-400">
|
||||
Desenvolvido com dedicação por{' '}
|
||||
<a
|
||||
href="https://linkedin.com/in/lucasrguerra"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="font-semibold underline text-slate-700 dark:text-slate-200 hover:text-purple-600 dark:hover:text-purple-400 transition-colors"
|
||||
aria-label="Perfil LinkedIn de Lucas Rayan Guerra (abre em nova aba)"
|
||||
>
|
||||
Lucas Rayan Guerra
|
||||
</a>
|
||||
, compartilhando eletrônica, IoT e computação no Brasil.
|
||||
</p>
|
||||
|
||||
<div className="mt-6 pt-4 border-t border-white/10 text-sm text-white/80 text-center">
|
||||
<div>
|
||||
© {year} ESPDocs — Conteúdo não oficial. Consulte sempre datasheets e documentos oficiais para decisões críticas.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
<div className="flex items-center gap-3 pt-2">
|
||||
<a
|
||||
href="https://github.com/lucasrguerra/ESPDocs"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center w-10 h-10 rounded-xl bg-slate-200/60 dark:bg-slate-900 hover:bg-slate-200 dark:hover:bg-slate-800 text-slate-700 dark:text-slate-200 transition-all cursor-pointer shadow-xs"
|
||||
aria-label="GitHub do projeto"
|
||||
>
|
||||
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M12 .5a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2.1c-3.3.7-4-1.6-4-1.6-.5-1.2-1.2-1.5-1.2-1.5-1-.7.1-.7.1-.7 1.1.1 1.7 1.1 1.7 1.1 1 .1 1.6-.7 1.9-1 .1-.8.4-1.3.8-1.6-2.6-.3-5.3-1.3-5.3-5.8 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.6.1-3.3 0 0 1-.3 3.3 1.2a11.4 11.4 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.6 1.7.2 3 .1 3.3.8.8 1.2 1.9 1.2 3.1 0 4.5-2.7 5.5-5.3 5.8.5.4.9 1.1.9 2.2v3.2c0 .3.2.7.8.6A12 12 0 0012 .5z" fill="currentColor"/>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://linkedin.com/in/lucasrguerra"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center w-10 h-10 rounded-xl bg-slate-200/60 dark:bg-slate-900 hover:bg-slate-200 dark:hover:bg-slate-800 text-slate-700 dark:text-slate-200 transition-all cursor-pointer shadow-xs"
|
||||
aria-label="LinkedIn do autor"
|
||||
>
|
||||
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M4.98 3.5a2.5 2.5 0 11.001 5.001A2.5 2.5 0 014.98 3.5zM3 8.98h4v12H3v-12zM9.5 8.98h3.8v1.6h.1c.5-.9 1.8-1.9 3.6-1.9 3.8 0 4.5 2.5 4.5 5.8v6.5h-4v-5.8c0-1.4-.1-3.2-2-3.2-2 0-2.3 1.6-2.3 3.1v5.9h-4v-12z" fill="currentColor"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Divisor & Isenção */}
|
||||
<div className="mt-12 pt-8 border-t border-slate-205/60 dark:border-slate-800/80 flex flex-col md:flex-row justify-between items-center gap-4 text-xs text-slate-400 dark:text-slate-500">
|
||||
<p className="text-center md:text-left leading-relaxed">
|
||||
© {year} ESPDocs. Esta plataforma independente de código aberto não possui afiliação com a Espressif Systems.
|
||||
</p>
|
||||
<p className="text-center md:text-right shrink-0">
|
||||
Feito para a comunidade maker 🇧🇷
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
@@ -1,181 +1,223 @@
|
||||
"use client";
|
||||
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import Link from "next/link";
|
||||
|
||||
import Button from "@mui/material/Button";
|
||||
import Drawer from "@mui/material/Drawer";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
import HomeIcon from "@mui/icons-material/Home";
|
||||
import AboutIcon from "@mui/icons-material/Info";
|
||||
import ModelsIcon from "@mui/icons-material/Memory";
|
||||
import FrameworksIcon from "@mui/icons-material/Apps";
|
||||
import CompareIcon from "@mui/icons-material/CompareArrows";
|
||||
import ShoppingCartIcon from "@mui/icons-material/ShoppingCart";
|
||||
import MenuBookIcon from "@mui/icons-material/MenuBook";
|
||||
import FindInPageIcon from "@mui/icons-material/FindInPage";
|
||||
import { useTheme } from "next-themes";
|
||||
import { createPortal } from "react-dom";
|
||||
import {
|
||||
Home,
|
||||
Info,
|
||||
BookOpen,
|
||||
Cpu,
|
||||
SlidersHorizontal,
|
||||
Terminal,
|
||||
Scale,
|
||||
ShoppingBag,
|
||||
Menu,
|
||||
X,
|
||||
Sun,
|
||||
Moon
|
||||
} from "lucide-react";
|
||||
|
||||
export default function Header() {
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const pathname = usePathname();
|
||||
|
||||
const pathname = usePathname();
|
||||
const menuItems = [
|
||||
{ text: "Início", href: "/", icon: <HomeIcon />, featured: true },
|
||||
{ text: "Sobre o Projeto", brief: "Sobre", href: "/sobre", icon: <AboutIcon /> },
|
||||
{ text: "Glossário", href: "/glossario", icon: <MenuBookIcon /> },
|
||||
{ text: "Séries", href: "/series", icon: <ModelsIcon />, featured: true },
|
||||
{ text: "Seletor de ESP32", brief: "Seletor", href: "/seletor", icon: <FindInPageIcon />, featured: true },
|
||||
{ text: "Frameworks", href: "/frameworks", icon: <FrameworksIcon />, featured: true },
|
||||
{ text: "Comparar Séries", brief: "Comparar", href: "/comparacao", icon: <CompareIcon />, featured: true },
|
||||
{ text: "Catálogo de Placas", brief: "Catálogo", href: "/catalogo", icon: <ShoppingCartIcon /> },
|
||||
];
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
const featuredItems = menuItems.filter(item => item.featured);
|
||||
const menuItems = [
|
||||
{ text: "Início", href: "/", icon: <Home className="w-4 h-4" />, featured: true },
|
||||
{ text: "Sobre o Projeto", brief: "Sobre", href: "/sobre", icon: <Info className="w-4 h-4" /> },
|
||||
{ text: "Glossário", href: "/glossario", icon: <BookOpen className="w-4 h-4" /> },
|
||||
{ text: "Séries", href: "/series", icon: <Cpu className="w-4 h-4" />, featured: true },
|
||||
{ text: "Seletor de ESP32", brief: "Seletor", href: "/seletor", icon: <SlidersHorizontal className="w-4 h-4" />, featured: true },
|
||||
{ text: "Frameworks", href: "/frameworks", icon: <Terminal className="w-4 h-4" />, featured: true },
|
||||
{ text: "Comparar Séries", brief: "Comparar", href: "/comparacao", icon: <Scale className="w-4 h-4" />, featured: true },
|
||||
{ text: "Catálogo de Placas", brief: "Catálogo", href: "/catalogo", icon: <ShoppingBag className="w-4 h-4" /> },
|
||||
];
|
||||
|
||||
function isActive(href) {
|
||||
if (href === "/") {
|
||||
return pathname === "/";
|
||||
}
|
||||
const featuredItems = menuItems.filter(item => item.featured);
|
||||
|
||||
return String(pathname).includes(href) && href !== "/";
|
||||
}
|
||||
function isActive(href) {
|
||||
if (href === "/") {
|
||||
return pathname === "/";
|
||||
}
|
||||
return String(pathname).includes(href) && href !== "/";
|
||||
}
|
||||
|
||||
return (
|
||||
<header className="bg-white/80 backdrop-blur-md py-4 px-4 md:px-8 text-black shadow-lg border-b border-gray-100 sticky top-0 z-50">
|
||||
<div className="flex justify-between max-w-7xl mx-auto items-center gap-8">
|
||||
<Link href="/" className="group shrink-0">
|
||||
<h1 className="text-2xl md:text-3xl font-bold bg-linear-to-r from-blue-600 via-purple-600 to-pink-600 bg-clip-text text-transparent transition-all duration-300 group-hover:scale-105">
|
||||
ESPDocs
|
||||
</h1>
|
||||
</Link>
|
||||
// Lock scroll when mobile menu is open
|
||||
useEffect(() => {
|
||||
if (drawerOpen) {
|
||||
document.body.style.overflow = "hidden";
|
||||
} else {
|
||||
document.body.style.overflow = "";
|
||||
}
|
||||
return () => {
|
||||
document.body.style.overflow = "";
|
||||
};
|
||||
}, [drawerOpen]);
|
||||
|
||||
{/* Menu Desktop - visível em telas médias e grandes */}
|
||||
<nav className="hidden lg:flex items-center gap-2 flex-1 justify-end" aria-label="Menu principal">
|
||||
{featuredItems.map((item) => (
|
||||
<Link
|
||||
key={item.text}
|
||||
href={item.href}
|
||||
className={`group flex items-center gap-2 px-4 py-2.5 rounded-xl font-medium transition-all duration-300 ${
|
||||
isActive(item.href)
|
||||
? "bg-linear-to-r from-blue-600 to-purple-600 text-white shadow-lg shadow-purple-200"
|
||||
: "text-gray-700 hover:bg-linear-to-r hover:from-blue-50 hover:to-purple-50 hover:shadow-md hover:-translate-y-0.5"
|
||||
}`}
|
||||
aria-label={item.brief || item.text}
|
||||
aria-current={isActive(item.href) ? "page" : undefined}
|
||||
>
|
||||
<span className={`transition-transform duration-300 ${
|
||||
isActive(item.href) ? "scale-110" : "group-hover:scale-110"
|
||||
}`}>
|
||||
{item.icon}
|
||||
</span>
|
||||
<span className="text-sm whitespace-nowrap">{item.brief || item.text}</span>
|
||||
</Link>
|
||||
))}
|
||||
return (
|
||||
<header className="bg-white/80 dark:bg-slate-950/80 backdrop-blur-md py-4 px-4 md:px-8 shadow-xs border-b border-slate-100 dark:border-slate-800/60 sticky top-0 z-50 transition-colors duration-300">
|
||||
<div className="flex justify-between max-w-7xl mx-auto items-center gap-8">
|
||||
{/* LOGO */}
|
||||
<Link href="/" className="group shrink-0">
|
||||
<h1 className="text-2xl md:text-3xl font-display font-extrabold bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500 bg-clip-text text-transparent transition-all duration-300 group-hover:scale-[1.02]">
|
||||
ESPDocs
|
||||
</h1>
|
||||
</Link>
|
||||
|
||||
{/* Botão Menu Completo Desktop */}
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={() => setDrawerOpen(true)}
|
||||
className="border-2 border-gray-300 text-gray-700 hover:border-purple-500 hover:text-purple-600 transition-all duration-300 hover:shadow-lg rounded-xl! px-4"
|
||||
aria-label="Abrir menu completo"
|
||||
aria-expanded={drawerOpen}
|
||||
aria-controls="mobile-menu-drawer"
|
||||
>
|
||||
<MenuIcon />
|
||||
</Button>
|
||||
</nav>
|
||||
{/* Menu Desktop */}
|
||||
<nav className="hidden lg:flex items-center gap-2 flex-1 justify-end" aria-label="Menu principal">
|
||||
{featuredItems.map((item) => (
|
||||
<Link
|
||||
key={item.text}
|
||||
href={item.href}
|
||||
className={`group flex items-center gap-2 px-4 py-2 rounded-xl font-medium transition-all duration-300 text-sm ${
|
||||
isActive(item.href)
|
||||
? "bg-gradient-to-r from-blue-600 to-purple-600 text-white shadow-md shadow-purple-500/10"
|
||||
: "text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-900/60 hover:text-purple-600 dark:hover:text-purple-400"
|
||||
}`}
|
||||
aria-label={item.brief || item.text}
|
||||
aria-current={isActive(item.href) ? "page" : undefined}
|
||||
>
|
||||
<span className={`transition-transform duration-300 ${
|
||||
isActive(item.href) ? "scale-110" : "group-hover:scale-110"
|
||||
}`}>
|
||||
{item.icon}
|
||||
</span>
|
||||
<span className="whitespace-nowrap">{item.brief || item.text}</span>
|
||||
</Link>
|
||||
))}
|
||||
|
||||
{/* Botão Menu Mobile - visível apenas em telas pequenas */}
|
||||
<div className="lg:hidden">
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={() => setDrawerOpen(true)}
|
||||
className="border-2 border-gray-300 text-gray-700 hover:border-purple-500 hover:text-purple-600 transition-all duration-300 hover:shadow-lg rounded-xl! px-4"
|
||||
aria-label="Abrir menu de navegação"
|
||||
aria-expanded={drawerOpen}
|
||||
aria-controls="mobile-menu-drawer"
|
||||
>
|
||||
<MenuIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/* Divider */}
|
||||
<div className="h-5 w-px bg-slate-200 dark:bg-slate-800 mx-2" />
|
||||
|
||||
{/* Drawer Mobile */}
|
||||
<Drawer
|
||||
anchor="right"
|
||||
open={drawerOpen}
|
||||
onClose={() => setDrawerOpen(false)}
|
||||
id="mobile-menu-drawer"
|
||||
>
|
||||
<div className="p-6 w-72">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-2xl font-bold bg-linear-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
|
||||
Menu
|
||||
</h2>
|
||||
<div className="h-1 w-16 bg-linear-to-r from-blue-600 to-purple-600 rounded-full mt-2"></div>
|
||||
</div>
|
||||
{/* Theme Switcher Button */}
|
||||
<ThemeToggle />
|
||||
|
||||
<List component="nav" aria-label="Menu de navegação">
|
||||
{menuItems.map((item, index) => (
|
||||
<ListItem
|
||||
key={item.text}
|
||||
disablePadding
|
||||
className="mb-3"
|
||||
style={{
|
||||
animation: `slideIn 0.3s ease-out ${index * 0.1}s both`
|
||||
}}
|
||||
>
|
||||
<div className="w-full">
|
||||
<Link
|
||||
className={`flex items-center w-full p-4 text-left rounded-xl transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 ${
|
||||
isActive(item.href)
|
||||
? "bg-linear-to-r from-blue-600 to-purple-600 text-white shadow-lg transform scale-105"
|
||||
: "hover:bg-white hover:shadow-md hover:transform hover:scale-102 text-gray-700"
|
||||
}`}
|
||||
onClick={() => { setDrawerOpen(false); }}
|
||||
href={item.href}
|
||||
aria-label={item.text}
|
||||
aria-current={isActive(item.href) ? "page" : undefined}
|
||||
>
|
||||
<span className={`transition-transform duration-300 ${isActive(item.href) ? "scale-110" : "group-hover:scale-110"}`}>
|
||||
{item.icon}
|
||||
</span>
|
||||
<span className="ml-3 font-medium">{item.text}</span>
|
||||
{isActive(item.href) && (
|
||||
<span className="ml-auto">
|
||||
<span className="inline-block w-2 h-2 bg-white rounded-full animate-pulse"></span>
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
</div>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
{/* Full Menu Burger Trigger */}
|
||||
<button
|
||||
onClick={() => setDrawerOpen(true)}
|
||||
className="p-2.5 rounded-xl border border-slate-200 dark:border-slate-800 text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-900/80 hover:border-purple-500 dark:hover:border-purple-400 hover:text-purple-600 dark:hover:text-purple-400 transition-all cursor-pointer"
|
||||
aria-label="Abrir menu completo"
|
||||
aria-expanded={drawerOpen}
|
||||
>
|
||||
<Menu className="w-5 h-5" />
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div className="mt-8 p-4 bg-white/50 backdrop-blur-sm rounded-xl border border-gray-200">
|
||||
<p className="text-xs text-gray-600 text-center">
|
||||
Documentação não oficial
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Drawer>
|
||||
{/* Botões Mobile (Theme + Burger) */}
|
||||
<div className="flex lg:hidden items-center gap-2">
|
||||
<ThemeToggle />
|
||||
<button
|
||||
onClick={() => setDrawerOpen(true)}
|
||||
className="p-2.5 rounded-xl border border-slate-200 dark:border-slate-800 text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-900/80 hover:border-purple-500 dark:hover:border-purple-400 transition-all cursor-pointer"
|
||||
aria-label="Abrir menu de navegação"
|
||||
aria-expanded={drawerOpen}
|
||||
>
|
||||
<Menu className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style jsx global>{`
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</header>
|
||||
);
|
||||
{/* Render side-panel and overlay at body level via React Portal to prevent layout leakage */}
|
||||
{mounted && createPortal(
|
||||
<>
|
||||
{drawerOpen && (
|
||||
<div
|
||||
className="fixed inset-0 bg-slate-950/50 dark:bg-black/75 backdrop-blur-xs z-[100] transition-opacity duration-300"
|
||||
onClick={() => setDrawerOpen(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={`fixed top-0 right-0 h-screen w-80 max-w-[85vw] bg-white dark:bg-slate-900 shadow-2xl z-[101] transform transition-transform duration-300 ease-in-out flex flex-col border-l border-slate-200 dark:border-slate-800 ${
|
||||
drawerOpen ? "translate-x-0" : "translate-x-full"
|
||||
}`}
|
||||
>
|
||||
{/* Drawer Header */}
|
||||
<div className="flex items-center justify-between p-6 border-b border-slate-100 dark:border-slate-800/80">
|
||||
<h2 className="text-xl font-display font-bold bg-gradient-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent">
|
||||
Menu Navegação
|
||||
</h2>
|
||||
<button
|
||||
onClick={() => setDrawerOpen(false)}
|
||||
className="p-2 text-slate-500 hover:text-slate-800 dark:text-slate-400 dark:hover:text-white rounded-xl hover:bg-slate-50 dark:hover:bg-slate-800/60 transition-all cursor-pointer"
|
||||
aria-label="Fechar menu"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Drawer Menu Items */}
|
||||
<nav className="flex-1 overflow-y-auto p-6 animate-fadeIn" aria-label="Navegação móvel">
|
||||
<ul className="space-y-2">
|
||||
{menuItems.map((item) => (
|
||||
<li key={item.text}>
|
||||
<Link
|
||||
href={item.href}
|
||||
className={`flex items-center gap-3 w-full p-3.5 rounded-xl font-medium transition-all duration-205 text-sm ${
|
||||
isActive(item.href)
|
||||
? "bg-gradient-to-r from-blue-600 to-purple-600 text-white shadow-md shadow-purple-500/10"
|
||||
: "text-slate-650 dark:text-slate-350 hover:bg-slate-50 dark:hover:bg-slate-800/40 hover:text-purple-600 dark:hover:text-purple-400"
|
||||
}`}
|
||||
onClick={() => setDrawerOpen(false)}
|
||||
aria-current={isActive(item.href) ? "page" : undefined}
|
||||
>
|
||||
<span className="shrink-0">{item.icon}</span>
|
||||
<span className="grow text-left">{item.text}</span>
|
||||
{isActive(item.href) && (
|
||||
<span className="w-1.5 h-1.5 bg-white rounded-full animate-ping" />
|
||||
)}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{/* Drawer Footer Info */}
|
||||
<div className="p-6 border-t border-slate-100 dark:border-slate-800/80 bg-slate-50/50 dark:bg-slate-900/50">
|
||||
<p className="text-center text-xs text-slate-400 dark:text-slate-500 font-medium">
|
||||
ESPDocs • Plataforma Não Oficial
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
, document.body
|
||||
)}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
/* Internal ThemeToggle Component */
|
||||
function ThemeToggle() {
|
||||
const { resolvedTheme, setTheme } = useTheme();
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
useEffect(() => setMounted(true), []);
|
||||
|
||||
if (!mounted) {
|
||||
return (
|
||||
<div className="w-10 h-10 rounded-xl bg-slate-100 dark:bg-slate-900 animate-pulse border border-transparent" />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
|
||||
className="w-10 h-10 rounded-xl bg-slate-100 dark:bg-slate-900 text-slate-600 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-800/80 hover:text-purple-600 dark:hover:text-purple-400 border border-slate-250/20 dark:border-slate-800/60 shadow-xs flex items-center justify-center transition-all duration-300 cursor-pointer active:scale-95 shrink-0"
|
||||
aria-label="Alternar tema"
|
||||
>
|
||||
{resolvedTheme === "dark" ? (
|
||||
<Sun className="w-5 h-5 text-yellow-500" />
|
||||
) : (
|
||||
<Moon className="w-5 h-5 text-indigo-600" />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider } from "next-themes";
|
||||
|
||||
export default function Providers({ children }) {
|
||||
return (
|
||||
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem={true}>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,80 +1,82 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
|
||||
export default function SeriesTabMenu({ tabs, color }) {
|
||||
const availableTabs = tabs.filter(tab => tab.available);
|
||||
const [activeTab, setActiveTab] = useState(availableTabs[0]?.id || 'specs');
|
||||
const availableTabs = tabs.filter(tab => tab.available);
|
||||
const [activeTab, setActiveTab] = useState(availableTabs[0]?.id || 'specs');
|
||||
|
||||
const activeTabContent = availableTabs.find(tab => tab.id === activeTab)?.content;
|
||||
const activeTabContent = availableTabs.find(tab => tab.id === activeTab)?.content;
|
||||
|
||||
return (
|
||||
<div className="mb-8">
|
||||
<div className="hidden md:flex bg-white rounded-2xl shadow-lg py-2 px-4 mb-6 border-2 border-gray-100 gap-4">
|
||||
{availableTabs.map((tab) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`flex-1 flex items-center justify-center gap-2 py-4 px-6 rounded-xl font-semibold transition-all duration-300 cursor-pointer ${
|
||||
activeTab === tab.id
|
||||
? 'text-white shadow-lg transform scale-105'
|
||||
: 'text-gray-600 hover:bg-gray-50'
|
||||
}`}
|
||||
style={
|
||||
activeTab === tab.id
|
||||
? { backgroundColor: color }
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<span className="hidden lg:inline">{tab.label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
return (
|
||||
<div className="mb-12">
|
||||
{/* Desktop Tabs */}
|
||||
<div className="hidden md:flex bg-white/70 dark:bg-slate-900/40 backdrop-blur-xl rounded-2xl shadow-xl py-2 px-3 mb-6 border border-slate-200/60 dark:border-slate-800/80 gap-3">
|
||||
{availableTabs.map((tab) => {
|
||||
const isActive = activeTab === tab.id;
|
||||
return (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`flex-1 flex items-center justify-center gap-2 py-3 px-5 rounded-xl font-bold text-xs uppercase tracking-wider transition-all duration-300 cursor-pointer active:scale-98 ${
|
||||
isActive
|
||||
? 'text-white shadow-lg scale-[1.02]'
|
||||
: 'text-slate-650 dark:text-slate-350 hover:bg-slate-100/50 dark:hover:bg-slate-800/40 hover:text-slate-800 dark:hover:text-slate-100'
|
||||
}`}
|
||||
style={
|
||||
isActive
|
||||
? { backgroundColor: color, boxShadow: `0 10px 20px -10px ${color}` }
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<span>{tab.label}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="md:hidden mb-6">
|
||||
<div className="bg-white rounded-2xl shadow-lg border-2 border-gray-100 overflow-hidden">
|
||||
<button
|
||||
onClick={() => {
|
||||
const dropdown = document.getElementById('mobile-tab-dropdown');
|
||||
dropdown.classList.toggle('hidden');
|
||||
}}
|
||||
className="w-full flex items-center justify-between py-4 px-6 font-semibold text-white"
|
||||
style={{ backgroundColor: color }}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">
|
||||
{availableTabs.find(tab => tab.id === activeTab)?.icon}
|
||||
</span>
|
||||
<span>
|
||||
{availableTabs.find(tab => tab.id === activeTab)?.label}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-xl">▼</span>
|
||||
</button>
|
||||
{/* Mobile Dropdown Tab Selector */}
|
||||
<div className="md:hidden mb-6">
|
||||
<div className="bg-white/70 dark:bg-slate-900/40 backdrop-blur-xl rounded-2xl shadow-lg border border-slate-200/60 dark:border-slate-800/80 overflow-hidden">
|
||||
<button
|
||||
onClick={() => {
|
||||
const dropdown = document.getElementById('mobile-tab-dropdown');
|
||||
dropdown.classList.toggle('hidden');
|
||||
}}
|
||||
className="w-full flex items-center justify-between py-4 px-6 font-bold text-sm uppercase tracking-wider text-white transition-all duration-300"
|
||||
style={{ backgroundColor: color }}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span>
|
||||
{availableTabs.find(tab => tab.id === activeTab)?.label}
|
||||
</span>
|
||||
</div>
|
||||
<ChevronDown className="w-4 h-4 shrink-0 transition-transform duration-300" />
|
||||
</button>
|
||||
|
||||
<div id="mobile-tab-dropdown" className="hidden">
|
||||
{availableTabs.map((tab) => (
|
||||
tab.id !== activeTab && (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => {
|
||||
setActiveTab(tab.id);
|
||||
document.getElementById('mobile-tab-dropdown').classList.add('hidden');
|
||||
}}
|
||||
className="w-full flex items-center gap-3 py-4 px-6 text-gray-700 hover:bg-gray-50 transition-colors border-t border-gray-100"
|
||||
>
|
||||
<span className="text-xl">{tab.icon}</span>
|
||||
<span className="font-medium">{tab.label}</span>
|
||||
</button>
|
||||
)
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mobile-tab-dropdown" className="hidden">
|
||||
{availableTabs.map((tab) => (
|
||||
tab.id !== activeTab && (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => {
|
||||
setActiveTab(tab.id);
|
||||
document.getElementById('mobile-tab-dropdown').classList.add('hidden');
|
||||
}}
|
||||
className="w-full flex items-center gap-3 py-4 px-6 text-slate-700 dark:text-slate-300 hover:bg-slate-150/40 dark:hover:bg-slate-800/30 transition-colors border-t border-slate-200/60 dark:border-slate-800/60 font-semibold text-xs uppercase tracking-wider text-left"
|
||||
>
|
||||
<span>{tab.label}</span>
|
||||
</button>
|
||||
)
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="animate-fadeIn">
|
||||
{activeTabContent}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
<div className="animate-fadeIn">
|
||||
{activeTabContent}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -12,8 +12,11 @@
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@mui/icons-material": "^7.3.4",
|
||||
"@mui/material": "^7.3.4",
|
||||
"framer-motion": "^12.40.0",
|
||||
"googleapis": "^165.0.0",
|
||||
"lucide-react": "^1.16.0",
|
||||
"next": "^15.5.9",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
},
|
||||
@@ -1877,16 +1880,6 @@
|
||||
"integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.8.tgz",
|
||||
"integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-transition-group": {
|
||||
"version": "4.4.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
|
||||
@@ -4019,6 +4012,33 @@
|
||||
"node": ">=12.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "12.40.0",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.40.0.tgz",
|
||||
"integrity": "sha512-uaBd3qC1v3KQqBEjwTUd183K6PbS+j0yR9w9VmEOLWA/tnUcSn8Xa3uck7t4dgpDoUss8xQTcj8W2L07lrnLFg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-dom": "^12.40.0",
|
||||
"motion-utils": "^12.39.0",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
@@ -5453,6 +5473,15 @@
|
||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/lucide-react": {
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.16.0.tgz",
|
||||
"integrity": "sha512-dYwyPzb4MEKpGUmNYk3WKWPnMrHs3FKM+q94kAnJrcDIqqn1hq2xY8scaS2ovsOCM5D51ey2gaRG3PBb1vgoYQ==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
@@ -5528,6 +5557,21 @@
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "12.40.0",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.40.0.tgz",
|
||||
"integrity": "sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-utils": "^12.39.0"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-utils": {
|
||||
"version": "12.39.0",
|
||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz",
|
||||
"integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
@@ -5627,6 +5671,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/next-themes": {
|
||||
"version": "0.4.6",
|
||||
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz",
|
||||
"integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/next/node_modules/postcss": {
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
@@ -7098,21 +7152,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/unbox-primitive": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
|
||||
|
||||
@@ -13,8 +13,11 @@
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@mui/icons-material": "^7.3.4",
|
||||
"@mui/material": "^7.3.4",
|
||||
"framer-motion": "^12.40.0",
|
||||
"googleapis": "^165.0.0",
|
||||
"lucide-react": "^1.16.0",
|
||||
"next": "^15.5.9",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
[
|
||||
{
|
||||
"numero": "1",
|
||||
"nome": "ANT",
|
||||
"tipo": "analog",
|
||||
"funcoes": ["Conexão de antena de RF (Wi-Fi e Bluetooth)"],
|
||||
"categorias": ["Antena", "RF"],
|
||||
"avisos": ["Pino de RF dedicado. Evite traçar pistas longas ou sem casamento de impedância de 50 ohms."]
|
||||
},
|
||||
{
|
||||
"numero": "2",
|
||||
"nome": "VDDA3P3",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação analógica de 3.3V"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": [
|
||||
"Tensão recomendada: 3.3V (3.0V a 3.6V)",
|
||||
"Fornece energia para os circuitos analógicos e de RF do chip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "3",
|
||||
"nome": "VDDA3P3",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação analógica de 3.3V"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": [
|
||||
"Tensão recomendada: 3.3V (3.0V a 3.6V)",
|
||||
"Fornece energia para os circuitos analógicos e de RF do chip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "4",
|
||||
"nome": "GPIO0",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD3P3_RTC"],
|
||||
"funcoes": [
|
||||
"GPIO0",
|
||||
"ADC1_CH0"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC1 Canal 0"]
|
||||
},
|
||||
{
|
||||
"numero": "5",
|
||||
"nome": "GPIO1",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD3P3_RTC"],
|
||||
"funcoes": [
|
||||
"GPIO1",
|
||||
"ADC1_CH1"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC1 Canal 1"]
|
||||
},
|
||||
{
|
||||
"numero": "6",
|
||||
"nome": "GPIO2",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD3P3_RTC"],
|
||||
"funcoes": [
|
||||
"GPIO2",
|
||||
"ADC1_CH2",
|
||||
"FSPIQ"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Mapeado para a função SPI MISO (FSPIQ) por padrão na inicialização do flash"]
|
||||
},
|
||||
{
|
||||
"numero": "7",
|
||||
"nome": "CHIP_EN",
|
||||
"tipo": "analog",
|
||||
"funcoes": ["Habilitar o chip (Power-up / Reset)"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": [
|
||||
"Ativo Alto. Puxe para nível alto para inicializar o chip.",
|
||||
"Não deixe este pino flutuando. Use um circuito RC de atraso externo para inicialização estável."
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "8",
|
||||
"nome": "GPIO3",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD3P3_RTC"],
|
||||
"funcoes": [
|
||||
"GPIO3",
|
||||
"ADC1_CH3"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC1 Canal 3"]
|
||||
},
|
||||
{
|
||||
"numero": "9",
|
||||
"nome": "GPIO4",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD3P3_RTC"],
|
||||
"funcoes": [
|
||||
"GPIO4",
|
||||
"MTMS",
|
||||
"FSPIQ",
|
||||
"ADC1_CH4"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"JTAG",
|
||||
"SPI",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": [
|
||||
"Pino multifuncional: JTAG MTMS, SPI2 MISO e ADC1 Canal 4.",
|
||||
"Pode ser usado para depuração JTAG em conjunto com outros pinos JTAG."
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "10",
|
||||
"nome": "GPIO5",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD3P3_RTC"],
|
||||
"funcoes": [
|
||||
"GPIO5",
|
||||
"MTDI",
|
||||
"FSPIWP"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"JTAG",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino multifuncional: JTAG MTDI e SPI2 Write Protect (FSPIWP)."]
|
||||
},
|
||||
{
|
||||
"numero": "11",
|
||||
"nome": "VDD3P3_RTC",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação do domínio de RTC e IOs de baixo consumo"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": ["Tensão recomendada: 3.3V"]
|
||||
},
|
||||
{
|
||||
"numero": "12",
|
||||
"nome": "GPIO6",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD3P3_RTC"],
|
||||
"funcoes": [
|
||||
"GPIO6",
|
||||
"MTCK",
|
||||
"FSPICLK"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"JTAG",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino multifuncional: JTAG MTCK e SPI2 Clock (FSPICLK)."]
|
||||
},
|
||||
{
|
||||
"numero": "13",
|
||||
"nome": "GPIO7",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD3P3_RTC"],
|
||||
"funcoes": [
|
||||
"GPIO7",
|
||||
"MTDO",
|
||||
"FSPID"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"JTAG",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino multifuncional: JTAG MTDO e SPI2 MOSI (FSPID)."]
|
||||
},
|
||||
{
|
||||
"numero": "14",
|
||||
"nome": "GPIO8",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD3P3_CPU"],
|
||||
"funcoes": ["GPIO8"],
|
||||
"categorias": ["GPIO", "Strapping"],
|
||||
"avisos": [
|
||||
"Pino de Strapping para controle de boot.",
|
||||
"Em conjunto com o GPIO9, seleciona o modo de inicialização do SoC (deve estar em 1 durante o reset para Joint Download Boot se GPIO9=0)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "15",
|
||||
"nome": "GPIO9",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD3P3_CPU"],
|
||||
"funcoes": ["GPIO9"],
|
||||
"categorias": ["GPIO", "Strapping"],
|
||||
"avisos": [
|
||||
"Pino de Strapping principal. Nível alto (1) no reset activa SPI Boot (padrão via pull-up fraco).",
|
||||
"Nível baixo (0) no reset coloca o chip no modo de download UART/SPI."
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "16",
|
||||
"nome": "GPIO10",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD3P3_CPU"],
|
||||
"funcoes": [
|
||||
"GPIO10",
|
||||
"FSPICS0"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino multifuncional: SPI2 Chip Select 0 (FSPICS0)."]
|
||||
},
|
||||
{
|
||||
"numero": "17",
|
||||
"nome": "VDD3P3_CPU",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação digital da CPU e IOs correspondentes (3.3V)"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": ["Tensão recomendada: 3.3V"]
|
||||
},
|
||||
{
|
||||
"numero": "18",
|
||||
"nome": "GPIO18",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD3P3_CPU"],
|
||||
"funcoes": ["GPIO18"],
|
||||
"categorias": ["GPIO"]
|
||||
},
|
||||
{
|
||||
"numero": "19",
|
||||
"nome": "GPIO19",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD3P3_CPU"],
|
||||
"funcoes": [
|
||||
"GPIO19",
|
||||
"U0RXD"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"UART"
|
||||
],
|
||||
"avisos": ["Pino de recepção UART0 (Serial RX por padrão para gravação e monitoramento)."]
|
||||
},
|
||||
{
|
||||
"numero": "20",
|
||||
"nome": "GPIO20",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD3P3_CPU"],
|
||||
"funcoes": [
|
||||
"GPIO20",
|
||||
"U0TXD"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"UART"
|
||||
],
|
||||
"avisos": ["Pino de transmissão UART0 (Serial TX por padrão para gravação e monitoramento)."]
|
||||
},
|
||||
{
|
||||
"numero": "21",
|
||||
"nome": "VDDA",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação de entrada analógica de 3.3V"],
|
||||
"categorias": ["Power"]
|
||||
},
|
||||
{
|
||||
"numero": "22",
|
||||
"nome": "XTAL_N",
|
||||
"tipo": "analog",
|
||||
"funcoes": ["Conexão externa do cristal oscilador (Saída)"],
|
||||
"categorias": ["Clock"]
|
||||
},
|
||||
{
|
||||
"numero": "23",
|
||||
"nome": "XTAL_P",
|
||||
"tipo": "analog",
|
||||
"funcoes": ["Conexão externa do cristal oscilador (Entrada)"],
|
||||
"categorias": ["Clock"]
|
||||
},
|
||||
{
|
||||
"numero": "24",
|
||||
"nome": "VDDA",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação de entrada analógica de 3.3V"],
|
||||
"categorias": ["Power"]
|
||||
},
|
||||
{
|
||||
"numero": "25",
|
||||
"nome": "GND",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Pino de aterramento central (Exposed Pad)"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": ["Deve ser devidamente soldado ao plano de terra (GND) da PCB para desempenho estável e dissipação térmica."]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,536 @@
|
||||
[
|
||||
{
|
||||
"numero": "1",
|
||||
"nome": "ANT_2G",
|
||||
"tipo": "analog",
|
||||
"funcoes": ["Conexão de antena de RF (Wi-Fi e Bluetooth)"],
|
||||
"categorias": ["Antena", "RF"],
|
||||
"avisos": ["Pino de RF dedicado a 2.4 GHz. Requer casamento de impedância de 50 ohms."]
|
||||
},
|
||||
{
|
||||
"numero": "2",
|
||||
"nome": "VDDA3",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação do domínio analógico de 3.3V"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": ["Tensão recomendada: 3.3V (3.0V a 3.6V). Fornece energia para circuitos de RF e clock."]
|
||||
},
|
||||
{
|
||||
"numero": "3",
|
||||
"nome": "VDDA4",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação do domínio analógico de 3.3V"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": ["Tensão recomendada: 3.3V (3.0V a 3.6V). Fornece energia para circuitos de RF e clock."]
|
||||
},
|
||||
{
|
||||
"numero": "4",
|
||||
"nome": "CHIP_PU",
|
||||
"tipo": "analog",
|
||||
"funcoes": ["Habilitar o chip (Power-up / Reset)"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": [
|
||||
"Ativo Alto. Puxe para nível alto para inicializar o chip.",
|
||||
"Não deixe este pino flutuando. Recomenda-se usar um circuito RC externo para reset limpo."
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "5",
|
||||
"nome": "VDDPST1",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação digital dos domínios HP (High Performance) e LP (Low Power)"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": ["Tensão recomendada: 3.3V. Alimenta a parte digital de baixo consumo e pinos associados."]
|
||||
},
|
||||
{
|
||||
"numero": "6",
|
||||
"nome": "GPIO0",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST1"],
|
||||
"funcoes": [
|
||||
"GPIO0",
|
||||
"XTAL_32K_P"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"Clock"
|
||||
],
|
||||
"avisos": ["Pino multifuncional de baixo consumo. Pode ser conectado ao cristal oscilador de 32 kHz externo (Entrada P)."]
|
||||
},
|
||||
{
|
||||
"numero": "7",
|
||||
"nome": "GPIO1",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST1"],
|
||||
"funcoes": [
|
||||
"GPIO1",
|
||||
"XTAL_32K_N",
|
||||
"ADC1_CH0"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"Clock",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino multifuncional. Conexão do cristal de 32 kHz externo (Saída N) e entrada analógica ADC1 Canal 0."]
|
||||
},
|
||||
{
|
||||
"numero": "8",
|
||||
"nome": "GPIO2",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST1"],
|
||||
"funcoes": ["GPIO2"],
|
||||
"categorias": ["GPIO"]
|
||||
},
|
||||
{
|
||||
"numero": "9",
|
||||
"nome": "GPIO3",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST1"],
|
||||
"funcoes": [
|
||||
"GPIO3",
|
||||
"MTMS",
|
||||
"ADC1_CH1"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"JTAG",
|
||||
"ADC",
|
||||
"Strapping"
|
||||
],
|
||||
"avisos": [
|
||||
"Pino multifuncional: JTAG MTMS, ADC1 Canal 1 e pino de Strapping para controle de borda de clock do SDIO.",
|
||||
"Flutuante por padrão durante o reset."
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "10",
|
||||
"nome": "GPIO4",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST1"],
|
||||
"funcoes": [
|
||||
"GPIO4",
|
||||
"MTDI",
|
||||
"ADC1_CH2"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"JTAG",
|
||||
"ADC",
|
||||
"Strapping"
|
||||
],
|
||||
"avisos": [
|
||||
"Pino multifuncional: JTAG MTDI, ADC1 Canal 2 e pino de Strapping para controle de borda de clock do SDIO.",
|
||||
"Flutuante por padrão durante o reset."
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "11",
|
||||
"nome": "GPIO5",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST1"],
|
||||
"funcoes": [
|
||||
"GPIO5",
|
||||
"MTCK",
|
||||
"ADC1_CH3"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"JTAG",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino multifuncional: JTAG MTCK e entrada analógica ADC1 Canal 3."]
|
||||
},
|
||||
{
|
||||
"numero": "12",
|
||||
"nome": "GPIO6",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST1"],
|
||||
"funcoes": [
|
||||
"GPIO6",
|
||||
"MTDO"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"JTAG"
|
||||
],
|
||||
"avisos": ["Pino multifuncional JTAG MTDO."]
|
||||
},
|
||||
{
|
||||
"numero": "13",
|
||||
"nome": "GPIO25",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO25",
|
||||
"SDIO_CMD",
|
||||
"FSPICS0"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"SDIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino multifuncional de alta velocidade: SDIO Comando ou SPI2 Chip Select 0."]
|
||||
},
|
||||
{
|
||||
"numero": "14",
|
||||
"nome": "GPIO26",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO26",
|
||||
"SDIO_CLK",
|
||||
"FSPICLK"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"SDIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino multifuncional de alta velocidade: SDIO Clock ou SPI2 Clock."]
|
||||
},
|
||||
{
|
||||
"numero": "15",
|
||||
"nome": "GPIO27",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO27",
|
||||
"SDIO_DATA0",
|
||||
"FSPID"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"SDIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino multifuncional de alta velocidade: SDIO Dados 0 ou SPI2 MOSI (FSPID)."]
|
||||
},
|
||||
{
|
||||
"numero": "16",
|
||||
"nome": "GPIO28",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO28",
|
||||
"SDIO_DATA1",
|
||||
"FSPIQ"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"SDIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino de alta velocidade: SDIO Dados 1 ou SPI2 MISO (FSPIQ)."]
|
||||
},
|
||||
{
|
||||
"numero": "17",
|
||||
"nome": "GPIO22",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO22",
|
||||
"SDIO_DATA2",
|
||||
"FSPIWP"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"SDIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino de alta velocidade: SDIO Dados 2 ou SPI2 Write Protect."]
|
||||
},
|
||||
{
|
||||
"numero": "18",
|
||||
"nome": "GPIO23",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO23",
|
||||
"SDIO_DATA3",
|
||||
"FSPIHD"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"SDIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino de alta velocidade: SDIO Dados 3 ou SPI2 Hold."]
|
||||
},
|
||||
{
|
||||
"numero": "19",
|
||||
"nome": "GPIO14",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD_SPI", "VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO14",
|
||||
"SPICS1"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino dedicado para Chip Select 1 da interface de memória SPI Flash/PSRAM."]
|
||||
},
|
||||
{
|
||||
"numero": "20",
|
||||
"nome": "GPIO15",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD_SPI", "VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO15",
|
||||
"SPICS0"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino dedicado para Chip Select 0 da interface de memória SPI Flash."]
|
||||
},
|
||||
{
|
||||
"numero": "21",
|
||||
"nome": "VDDPST2",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação do domínio digital HP (High Performance)"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": ["Tensão recomendada: 3.3V (3.0V a 3.6V). Alimenta as interfaces digitais rápidas."]
|
||||
},
|
||||
{
|
||||
"numero": "22",
|
||||
"nome": "GPIO16",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD_SPI", "VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO16",
|
||||
"SPIQ"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino de dados Q (MISO) da interface de memória flash SPI."]
|
||||
},
|
||||
{
|
||||
"numero": "23",
|
||||
"nome": "GPIO17",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD_SPI", "VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO17",
|
||||
"SPIWP"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino de proteção contra escrita (Write Protect) da interface de memória flash SPI."]
|
||||
},
|
||||
{
|
||||
"numero": "24",
|
||||
"nome": "VDD_SPI",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Tensão de saída LDO gerada a partir de VDDPST2 para alimentar a flash e PSRAM"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": [
|
||||
"Tensão de saída configurável via software de acordo com as especificações da flash interna/externa (normalmente 3.3V ou 1.8V).",
|
||||
"Não deve ser usado como fonte geral de energia de alta corrente."
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "25",
|
||||
"nome": "GPIO19",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD_SPI", "VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO19",
|
||||
"SPIHD"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino de controle de Hold da interface de memória flash SPI."]
|
||||
},
|
||||
{
|
||||
"numero": "26",
|
||||
"nome": "GPIO20",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD_SPI", "VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO20",
|
||||
"SPICLK"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino de sinal de clock da interface de memória flash SPI."]
|
||||
},
|
||||
{
|
||||
"numero": "27",
|
||||
"nome": "GPIO21",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD_SPI", "VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO21",
|
||||
"SPID"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino de dados D (MOSI) da interface de memória flash SPI."]
|
||||
},
|
||||
{
|
||||
"numero": "28",
|
||||
"nome": "GPIO12",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO12",
|
||||
"USB_D-"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"USB"
|
||||
],
|
||||
"avisos": ["Pino de sinal negativo do transceptor USB Serial/JTAG integrado."]
|
||||
},
|
||||
{
|
||||
"numero": "29",
|
||||
"nome": "GPIO13",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO13",
|
||||
"USB_D+"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"USB"
|
||||
],
|
||||
"avisos": [
|
||||
"Pino de sinal positivo do transceptor USB Serial/JTAG integrado.",
|
||||
"Possui resistor de pull-up integrado ativado por padrão na inicialização do USB."
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "30",
|
||||
"nome": "GPIO24",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST2"],
|
||||
"funcoes": ["GPIO24"],
|
||||
"categorias": ["GPIO"]
|
||||
},
|
||||
{
|
||||
"numero": "31",
|
||||
"nome": "GPIO8",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST2"],
|
||||
"funcoes": ["GPIO8"],
|
||||
"categorias": ["GPIO", "Strapping"],
|
||||
"avisos": [
|
||||
"Pino de Strapping para seleção do modo de boot e controle de impressão das mensagens da ROM.",
|
||||
"Flutuante por padrão durante o reset. Nível alto (1) ativa a impressão do log de inicialização."
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "32",
|
||||
"nome": "GPIO9",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST2"],
|
||||
"funcoes": ["GPIO9"],
|
||||
"categorias": ["GPIO", "Strapping"],
|
||||
"avisos": [
|
||||
"Pino de Strapping principal para seleção do modo de boot.",
|
||||
"Equipado com pull-up fraco integrado. Nível alto (1) no reset ativa SPI Boot (carrega do flash). Nível baixo (0) força o modo de Download."
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "33",
|
||||
"nome": "GPIO10",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO10",
|
||||
"U0RXD"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"UART"
|
||||
],
|
||||
"avisos": ["Recepção serial UART0 (Serial RX para gravação do firmware externa e depuração)."]
|
||||
},
|
||||
{
|
||||
"numero": "34",
|
||||
"nome": "GPIO11",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST2"],
|
||||
"funcoes": [
|
||||
"GPIO11",
|
||||
"U0TXD"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"UART"
|
||||
],
|
||||
"avisos": ["Transmissão serial UART0 (Serial TX para gravação do firmware externa e depuração)."]
|
||||
},
|
||||
{
|
||||
"numero": "35",
|
||||
"nome": "GPIO29",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST2"],
|
||||
"funcoes": ["GPIO29"],
|
||||
"categorias": ["GPIO"]
|
||||
},
|
||||
{
|
||||
"numero": "36",
|
||||
"nome": "GPIO7",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST2"],
|
||||
"funcoes": ["GPIO7"],
|
||||
"categorias": ["GPIO", "Strapping"],
|
||||
"avisos": [
|
||||
"Pino de Strapping para seleção da fonte dos sinais JTAG (inicialização precoce).",
|
||||
"Sem resistores de pull internos no reset. Deve ser mantido estável por circuito externo se usado."
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "37",
|
||||
"nome": "VDDA1",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação do domínio analógico de 3.3V"],
|
||||
"categorias": ["Power"]
|
||||
},
|
||||
{
|
||||
"numero": "38",
|
||||
"nome": "XTAL_N",
|
||||
"tipo": "analog",
|
||||
"funcoes": ["Conexão externa do cristal oscilador de 40 MHz (Saída N)"],
|
||||
"categorias": ["Clock"]
|
||||
},
|
||||
{
|
||||
"numero": "39",
|
||||
"nome": "XTAL_P",
|
||||
"tipo": "analog",
|
||||
"funcoes": ["Conexão externa do cristal oscilador de 40 MHz (Entrada P)"],
|
||||
"categorias": ["Clock"]
|
||||
},
|
||||
{
|
||||
"numero": "40",
|
||||
"nome": "VDDA2",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação do domínio analógico de 3.3V"],
|
||||
"categorias": ["Power"]
|
||||
},
|
||||
{
|
||||
"numero": "41",
|
||||
"nome": "GND",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Pino de aterramento central (Exposed Pad)"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": ["Deve ser adequadamente soldado ao plano de terra (GND) da PCB para controle de RF e térmica estáveis."]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,956 @@
|
||||
[
|
||||
{
|
||||
"numero": "1",
|
||||
"nome": "ANT",
|
||||
"tipo": "analog",
|
||||
"funcoes": ["Conexão de antena de RF (Wi-Fi e Bluetooth)"],
|
||||
"categorias": ["Antena", "RF"],
|
||||
"avisos": ["Pino de RF dedicado para 2.4 GHz. Requer casamento de impedância de 50 ohms."]
|
||||
},
|
||||
{
|
||||
"numero": "2",
|
||||
"nome": "VDDA3",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação do domínio analógico (3.3V)"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": ["Tensão recomendada: 3.3V (3.0V a 3.6V). Alimenta a seção de RF e clock."]
|
||||
},
|
||||
{
|
||||
"numero": "3",
|
||||
"nome": "VDDA4",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação do domínio analógico (3.3V)"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": ["Tensão recomendada: 3.3V (3.0V a 3.6V). Alimenta a seção de RF e clock."]
|
||||
},
|
||||
{
|
||||
"numero": "4",
|
||||
"nome": "CHIP_PU",
|
||||
"tipo": "analog",
|
||||
"funcoes": ["Habilitar o chip (Power-up / Reset)"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": [
|
||||
"Ativo Alto. Puxe para nível alto para inicializar o chip.",
|
||||
"Não deixe este pino flutuando. Recomenda-se usar um circuito RC externo de atraso para inicialização estável."
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "5",
|
||||
"nome": "GPIO0",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO0",
|
||||
"XTAL_32K_N"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"Clock"
|
||||
],
|
||||
"avisos": ["Pino de clock de baixo consumo. Pode ser conectado ao cristal de 32.768 kHz externo (Pino N)."]
|
||||
},
|
||||
{
|
||||
"numero": "6",
|
||||
"nome": "GPIO1",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO1",
|
||||
"XTAL_32K_P",
|
||||
"ADC1_CH0"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"Clock",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino multifuncional. Cristal de 32.768 kHz externo (Pino P) e entrada analógica ADC1 Canal 0."]
|
||||
},
|
||||
{
|
||||
"numero": "7",
|
||||
"nome": "GPIO2",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO2",
|
||||
"ADC1_CH1"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC1 Canal 1."]
|
||||
},
|
||||
{
|
||||
"numero": "8",
|
||||
"nome": "GPIO3",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO3",
|
||||
"ADC1_CH2"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC1 Canal 2."]
|
||||
},
|
||||
{
|
||||
"numero": "9",
|
||||
"nome": "GPIO4",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO4",
|
||||
"ADC1_CH3"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC1 Canal 3."]
|
||||
},
|
||||
{
|
||||
"numero": "10",
|
||||
"nome": "GPIO5",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO5",
|
||||
"ADC1_CH4"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC1 Canal 4."]
|
||||
},
|
||||
{
|
||||
"numero": "11",
|
||||
"nome": "VDDPST_1",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação de I/O do domínio 1 (3.3V)"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": ["Tensão recomendada: 3.3V."]
|
||||
},
|
||||
{
|
||||
"numero": "12",
|
||||
"nome": "GPIO6",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO6",
|
||||
"ADC1_CH5"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC1 Canal 5."]
|
||||
},
|
||||
{
|
||||
"numero": "13",
|
||||
"nome": "GPIO7",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO7",
|
||||
"ADC1_CH6"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC1 Canal 6."]
|
||||
},
|
||||
{
|
||||
"numero": "14",
|
||||
"nome": "GPIO8",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO8",
|
||||
"ADC1_CH7"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC1 Canal 7."]
|
||||
},
|
||||
{
|
||||
"numero": "15",
|
||||
"nome": "GPIO9",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO9",
|
||||
"ADC1_CH8"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC1 Canal 8."]
|
||||
},
|
||||
{
|
||||
"numero": "16",
|
||||
"nome": "GPIO10",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO10",
|
||||
"ADC1_CH9"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC1 Canal 9."]
|
||||
},
|
||||
{
|
||||
"numero": "17",
|
||||
"nome": "GPIO11",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO11",
|
||||
"ADC1_CH10"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC1 Canal 10."]
|
||||
},
|
||||
{
|
||||
"numero": "18",
|
||||
"nome": "VREF_TOUCH",
|
||||
"tipo": "analog",
|
||||
"funcoes": ["Tensão de referência para os pinos Touch capacitivos"],
|
||||
"categorias": ["Touch"],
|
||||
"avisos": ["Pino analógico dedicado para estabilização de leitura capacitiva."]
|
||||
},
|
||||
{
|
||||
"numero": "19",
|
||||
"nome": "GPIO12",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO12",
|
||||
"ADC2_CH0"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC2 Canal 0."]
|
||||
},
|
||||
{
|
||||
"numero": "20",
|
||||
"nome": "GPIO13",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO13",
|
||||
"ADC2_CH1"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC2 Canal 1."]
|
||||
},
|
||||
{
|
||||
"numero": "21",
|
||||
"nome": "GPIO14",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO14",
|
||||
"ADC2_CH2"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC2 Canal 2."]
|
||||
},
|
||||
{
|
||||
"numero": "22",
|
||||
"nome": "GPIO15",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO15",
|
||||
"ADC2_CH3"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC2 Canal 3."]
|
||||
},
|
||||
{
|
||||
"numero": "23",
|
||||
"nome": "GPIO16",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO16",
|
||||
"ADC2_CH4"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC2 Canal 4."]
|
||||
},
|
||||
{
|
||||
"numero": "24",
|
||||
"nome": "GPIO17",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO17",
|
||||
"ADC2_CH5"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC2 Canal 5."]
|
||||
},
|
||||
{
|
||||
"numero": "25",
|
||||
"nome": "GPIO18",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO18",
|
||||
"ADC2_CH6"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC2 Canal 6."]
|
||||
},
|
||||
{
|
||||
"numero": "26",
|
||||
"nome": "GPIO19",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_1"],
|
||||
"funcoes": [
|
||||
"GPIO19",
|
||||
"ADC2_CH7"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino de entrada analógica ADC2 Canal 7."]
|
||||
},
|
||||
{
|
||||
"numero": "27",
|
||||
"nome": "SDIO_DATA0",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_SD"],
|
||||
"funcoes": [
|
||||
"SDIO_DATA0",
|
||||
"FSPID"
|
||||
],
|
||||
"categorias": [
|
||||
"SDIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino da interface SDIO de alta velocidade (Dados 0) ou SPI2 MOSI."]
|
||||
},
|
||||
{
|
||||
"numero": "28",
|
||||
"nome": "SDIO_DATA1",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_SD"],
|
||||
"funcoes": [
|
||||
"SDIO_DATA1",
|
||||
"FSPIQ"
|
||||
],
|
||||
"categorias": [
|
||||
"SDIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino da interface SDIO de alta velocidade (Dados 1) ou SPI2 MISO."]
|
||||
},
|
||||
{
|
||||
"numero": "29",
|
||||
"nome": "SDIO_DATA2",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_SD"],
|
||||
"funcoes": [
|
||||
"SDIO_DATA2",
|
||||
"FSPIWP"
|
||||
],
|
||||
"categorias": [
|
||||
"SDIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino da interface SDIO de alta velocidade (Dados 2) ou SPI2 Write Protect."]
|
||||
},
|
||||
{
|
||||
"numero": "30",
|
||||
"nome": "VDD_PSRAM_1P8_1",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação de 1.8V para PSRAM integrada no encapsulamento"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": ["Gerado pelo LDO interno para alimentar a PSRAM integrada. Não conectar a circuitos de potência externa."]
|
||||
},
|
||||
{
|
||||
"numero": "31",
|
||||
"nome": "SDIO_DATA3",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_SD"],
|
||||
"funcoes": [
|
||||
"SDIO_DATA3",
|
||||
"FSPIHD"
|
||||
],
|
||||
"categorias": [
|
||||
"SDIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Pino da interface SDIO de alta velocidade (Dados 3) ou SPI2 Hold."]
|
||||
},
|
||||
{
|
||||
"numero": "32",
|
||||
"nome": "SDIO_CLK",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_SD"],
|
||||
"funcoes": [
|
||||
"SDIO_CLK",
|
||||
"FSPICLK"
|
||||
],
|
||||
"categorias": [
|
||||
"SDIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Clock da interface SDIO de alta velocidade ou SPI2 Clock."]
|
||||
},
|
||||
{
|
||||
"numero": "33",
|
||||
"nome": "SDIO_CMD",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_SD"],
|
||||
"funcoes": [
|
||||
"SDIO_CMD",
|
||||
"FSPICS0"
|
||||
],
|
||||
"categorias": [
|
||||
"SDIO",
|
||||
"SPI"
|
||||
],
|
||||
"avisos": ["Comando da interface SDIO de alta velocidade ou SPI2 Chip Select 0."]
|
||||
},
|
||||
{
|
||||
"numero": "34",
|
||||
"nome": "VDD_PSRAM_1P8_2",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação de 1.8V para PSRAM integrada no encapsulamento"],
|
||||
"categorias": ["Power"]
|
||||
},
|
||||
{
|
||||
"numero": "35",
|
||||
"nome": "VDD_LDO_1P8",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Pino de entrada/saída LDO de 1.8V"],
|
||||
"categorias": ["Power"]
|
||||
},
|
||||
{
|
||||
"numero": "36",
|
||||
"nome": "SPICS",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD_SPI"],
|
||||
"funcoes": ["SPICS"],
|
||||
"categorias": ["SPI"],
|
||||
"avisos": ["Chip Select dedicado para a Flash SPI externa ou interna."]
|
||||
},
|
||||
{
|
||||
"numero": "37",
|
||||
"nome": "SPIQ",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD_SPI"],
|
||||
"funcoes": ["SPIQ"],
|
||||
"categorias": ["SPI"],
|
||||
"avisos": ["Dado Q (MISO) dedicado para a Flash SPI externa ou interna."]
|
||||
},
|
||||
{
|
||||
"numero": "38",
|
||||
"nome": "SPIWP",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD_SPI"],
|
||||
"funcoes": ["SPIWP"],
|
||||
"categorias": ["SPI"],
|
||||
"avisos": ["Write Protect dedicado para a Flash SPI externa ou interna."]
|
||||
},
|
||||
{
|
||||
"numero": "39",
|
||||
"nome": "VDD_SPI",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Tensão de alimentação dedicada para a interface Flash SPI"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": ["Tensão típica: 3.3V ou 1.8V gerada internamente."]
|
||||
},
|
||||
{
|
||||
"numero": "40",
|
||||
"nome": "SPIHD",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD_SPI"],
|
||||
"funcoes": ["SPIHD"],
|
||||
"categorias": ["SPI"],
|
||||
"avisos": ["Hold dedicado para a Flash SPI externa ou interna."]
|
||||
},
|
||||
{
|
||||
"numero": "41",
|
||||
"nome": "SPICLK",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD_SPI"],
|
||||
"funcoes": ["SPICLK"],
|
||||
"categorias": ["SPI"],
|
||||
"avisos": ["Clock dedicado para a Flash SPI externa ou interna."]
|
||||
},
|
||||
{
|
||||
"numero": "42",
|
||||
"nome": "SPID",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDD_SPI"],
|
||||
"funcoes": ["SPID"],
|
||||
"categorias": ["SPI"],
|
||||
"avisos": ["Dado D (MOSI) dedicado para a Flash SPI externa ou interna."]
|
||||
},
|
||||
{
|
||||
"numero": "43",
|
||||
"nome": "VCCA/VDDPST_2",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação analógica e digital do domínio 2 (3.3V)"],
|
||||
"categorias": ["Power"]
|
||||
},
|
||||
{
|
||||
"numero": "44",
|
||||
"nome": "USB_DP",
|
||||
"tipo": "analog",
|
||||
"funcoes": ["Pino positivo do transceptor USB Serial/JTAG integrado"],
|
||||
"categorias": ["USB"],
|
||||
"avisos": ["USB D+. Possui pull-up de hardware interno."]
|
||||
},
|
||||
{
|
||||
"numero": "45",
|
||||
"nome": "USB_DM",
|
||||
"tipo": "analog",
|
||||
"funcoes": ["Pino negativo do transceptor USB Serial/JTAG integrado"],
|
||||
"categorias": ["USB"],
|
||||
"avisos": ["USB D-."]
|
||||
},
|
||||
{
|
||||
"numero": "46",
|
||||
"nome": "GPIO33",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_3"],
|
||||
"funcoes": [
|
||||
"GPIO33",
|
||||
"ADC2_CH8"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino multifuncional: GPIO33 e ADC2 Canal 8."]
|
||||
},
|
||||
{
|
||||
"numero": "47",
|
||||
"nome": "GPIO34",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_3"],
|
||||
"funcoes": [
|
||||
"GPIO34",
|
||||
"ADC2_CH9"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
],
|
||||
"avisos": ["Pino multifuncional: GPIO34 e ADC2 Canal 9."]
|
||||
},
|
||||
{
|
||||
"numero": "48",
|
||||
"nome": "GPIO35",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_3"],
|
||||
"funcoes": [
|
||||
"GPIO35",
|
||||
"ADC2_CH10"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "49",
|
||||
"nome": "GPIO36",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_3"],
|
||||
"funcoes": [
|
||||
"GPIO36",
|
||||
"ADC2_CH11"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "50",
|
||||
"nome": "GPIO37",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_3"],
|
||||
"funcoes": [
|
||||
"GPIO37",
|
||||
"ADC2_CH12"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "51",
|
||||
"nome": "GPIO38",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_3"],
|
||||
"funcoes": [
|
||||
"GPIO38",
|
||||
"ADC2_CH13"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "52",
|
||||
"nome": "GPIO39",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_3"],
|
||||
"funcoes": [
|
||||
"GPIO39",
|
||||
"ADC2_CH14"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "53",
|
||||
"nome": "GPIO40",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_3"],
|
||||
"funcoes": [
|
||||
"GPIO40",
|
||||
"ADC2_CH15"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "54",
|
||||
"nome": "VDDPST_3",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação de I/O do domínio 3 (3.3V)"],
|
||||
"categorias": ["Power"]
|
||||
},
|
||||
{
|
||||
"numero": "55",
|
||||
"nome": "GPIO42",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_3"],
|
||||
"funcoes": [
|
||||
"GPIO42",
|
||||
"ADC2_CH16"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "56",
|
||||
"nome": "GPIO43",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_3"],
|
||||
"funcoes": [
|
||||
"GPIO43",
|
||||
"ADC2_CH17"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "57",
|
||||
"nome": "GPIO44",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_3"],
|
||||
"funcoes": [
|
||||
"GPIO44",
|
||||
"ADC2_CH18"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "58",
|
||||
"nome": "GPIO45",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_3"],
|
||||
"funcoes": [
|
||||
"GPIO45",
|
||||
"ADC2_CH19"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "59",
|
||||
"nome": "GPIO46",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_3"],
|
||||
"funcoes": [
|
||||
"GPIO46",
|
||||
"ADC2_CH20"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "60",
|
||||
"nome": "GPIO47",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_3"],
|
||||
"funcoes": [
|
||||
"GPIO47",
|
||||
"ADC2_CH21"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"ADC"
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "61",
|
||||
"nome": "GPIO48",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_3"],
|
||||
"funcoes": ["GPIO48"],
|
||||
"categorias": ["GPIO"]
|
||||
},
|
||||
{
|
||||
"numero": "62",
|
||||
"nome": "GPIO49",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_3"],
|
||||
"funcoes": ["GPIO49"],
|
||||
"categorias": ["GPIO"]
|
||||
},
|
||||
{
|
||||
"numero": "63",
|
||||
"nome": "VREF_ADC",
|
||||
"tipo": "analog",
|
||||
"funcoes": ["Tensão de referência estável externa para calibração do ADC"],
|
||||
"categorias": ["Power", "ADC"],
|
||||
"avisos": ["Pino analógico dedicado para calibração precisa do conversor analógico-digital."]
|
||||
},
|
||||
{
|
||||
"numero": "64",
|
||||
"nome": "VDDPST_4",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação de I/O do domínio 4 (3.3V)"],
|
||||
"categorias": ["Power"]
|
||||
},
|
||||
{
|
||||
"numero": "65",
|
||||
"nome": "GPIO50",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_4"],
|
||||
"funcoes": ["GPIO50"],
|
||||
"categorias": ["GPIO"]
|
||||
},
|
||||
{
|
||||
"numero": "66",
|
||||
"nome": "GPIO51",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_4"],
|
||||
"funcoes": ["GPIO51"],
|
||||
"categorias": ["GPIO"]
|
||||
},
|
||||
{
|
||||
"numero": "67",
|
||||
"nome": "GPIO52",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_4"],
|
||||
"funcoes": ["GPIO52"],
|
||||
"categorias": ["GPIO"]
|
||||
},
|
||||
{
|
||||
"numero": "68",
|
||||
"nome": "GPIO53",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_4"],
|
||||
"funcoes": ["GPIO53"],
|
||||
"categorias": ["GPIO"]
|
||||
},
|
||||
{
|
||||
"numero": "69",
|
||||
"nome": "GPIO54",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_4"],
|
||||
"funcoes": [
|
||||
"GPIO54",
|
||||
"MTDO"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"JTAG"
|
||||
],
|
||||
"avisos": ["Pino multifuncional: JTAG MTDO."]
|
||||
},
|
||||
{
|
||||
"numero": "70",
|
||||
"nome": "GPIO55",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_4"],
|
||||
"funcoes": [
|
||||
"GPIO55",
|
||||
"MTCK"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"JTAG"
|
||||
],
|
||||
"avisos": ["Pino multifuncional: JTAG MTCK."]
|
||||
},
|
||||
{
|
||||
"numero": "71",
|
||||
"nome": "GPIO56",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_4"],
|
||||
"funcoes": [
|
||||
"GPIO56",
|
||||
"MTDI"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"JTAG"
|
||||
],
|
||||
"avisos": ["Pino multifuncional: JTAG MTDI."]
|
||||
},
|
||||
{
|
||||
"numero": "72",
|
||||
"nome": "GPIO57",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_4"],
|
||||
"funcoes": [
|
||||
"GPIO57",
|
||||
"MTMS"
|
||||
],
|
||||
"categorias": [
|
||||
"GPIO",
|
||||
"JTAG"
|
||||
],
|
||||
"avisos": ["Pino multifuncional: JTAG MTMS."]
|
||||
},
|
||||
{
|
||||
"numero": "73",
|
||||
"nome": "GPIO58",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_4"],
|
||||
"funcoes": ["GPIO58"],
|
||||
"categorias": ["GPIO"]
|
||||
},
|
||||
{
|
||||
"numero": "74",
|
||||
"nome": "GPIO59",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_4"],
|
||||
"funcoes": ["GPIO59"],
|
||||
"categorias": ["GPIO"]
|
||||
},
|
||||
{
|
||||
"numero": "75",
|
||||
"nome": "GPIO60",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_4"],
|
||||
"funcoes": ["GPIO60"],
|
||||
"categorias": ["GPIO", "Strapping"],
|
||||
"avisos": [
|
||||
"Pino de Strapping para boot (junto com GPIO61).",
|
||||
"Equipado com pull-up interno ativo por padrão."
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "76",
|
||||
"nome": "GPIO61",
|
||||
"tipo": "io",
|
||||
"alimentacao": ["VDDPST_4"],
|
||||
"funcoes": ["GPIO61"],
|
||||
"categorias": ["GPIO", "Strapping"],
|
||||
"avisos": [
|
||||
"Pino de Strapping principal para seleção de boot.",
|
||||
"Equipado com pull-up interno ativo por padrão. Nível alto (1) no reset ativa SPI Boot. Nível baixo (0) força o modo Download."
|
||||
]
|
||||
},
|
||||
{
|
||||
"numero": "77",
|
||||
"nome": "VDDA1",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação do domínio analógico de 3.3V"],
|
||||
"categorias": ["Power"]
|
||||
},
|
||||
{
|
||||
"numero": "78",
|
||||
"nome": "XTAL_N",
|
||||
"tipo": "analog",
|
||||
"funcoes": ["Conexão externa do cristal oscilador de alta velocidade (Saída N)"],
|
||||
"categorias": ["Clock"]
|
||||
},
|
||||
{
|
||||
"numero": "79",
|
||||
"nome": "XTAL_P",
|
||||
"tipo": "analog",
|
||||
"funcoes": ["Conexão externa do cristal oscilador de alta velocidade (Entrada P)"],
|
||||
"categorias": ["Clock"]
|
||||
},
|
||||
{
|
||||
"numero": "80",
|
||||
"nome": "VDDA2",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Alimentação do domínio analógico de 3.3V"],
|
||||
"categorias": ["Power"]
|
||||
},
|
||||
{
|
||||
"numero": "81",
|
||||
"nome": "GND",
|
||||
"tipo": "power",
|
||||
"funcoes": ["Pino de aterramento central (Exposed Pad)"],
|
||||
"categorias": ["Power"],
|
||||
"avisos": ["Deve ser adequadamente soldado ao plano de terra (GND) da PCB para controle de RF e dissipação térmica estáveis."]
|
||||
}
|
||||
]
|
||||
|
Before Width: | Height: | Size: 337 KiB After Width: | Height: | Size: 372 KiB |
|
Before Width: | Height: | Size: 365 KiB After Width: | Height: | Size: 399 KiB |
|
Before Width: | Height: | Size: 301 KiB After Width: | Height: | Size: 336 KiB |
|
Before Width: | Height: | Size: 321 KiB After Width: | Height: | Size: 350 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 6.6 MiB |
|
Before Width: | Height: | Size: 472 KiB After Width: | Height: | Size: 542 KiB |
@@ -131,6 +131,86 @@
|
||||
"placa": "/placas/esp32-s3.png",
|
||||
"cor": "#fd7e14"
|
||||
},
|
||||
"ESP32-S31": {
|
||||
"nome_completo": "ESP32-S31",
|
||||
"icone": "🌀",
|
||||
"descricao": "Processador dual-core RISC-V de alta performance a 320MHz com Wi-Fi 6, Bluetooth 5.4 e Classic, Zigbee/Thread, Matter e aceleração IA",
|
||||
"arquitetura": "RISC-V dual-core de 32 bits (HP) + RISC-V single-core (LP)",
|
||||
"nucleos": "2 (Dual-core HP) + 1 (Single-core LP)",
|
||||
"frequencia": "Até 320 MHz",
|
||||
"coprocessador_ulp": "Sim (RISC-V, LP CPU de 40 MHz)",
|
||||
"aceleradores_ia": "Sim (Custom AI and DSP extension XespV)",
|
||||
"wifi": "Wi-Fi 6 (802.11ax) 2.4 GHz",
|
||||
"bluetooth": "Bluetooth 5.4 LE e Bluetooth Classic",
|
||||
"zigbee_thread": "IEEE 802.15.4 (Zigbee 3.0, Thread 1.4)",
|
||||
"matter": "Sim",
|
||||
"memoria_sram": "512 KB (HP SRAM)",
|
||||
"memoria_sram_rtc": "32 KB (LP SRAM)",
|
||||
"memoria_rom": "320 KB",
|
||||
"flash_externa": "Até 256 MB via QSPI",
|
||||
"psram_externa": "Suporta até 64 MB (16 MB ou 32 MB DDR OPI integrados no encapsulamento)",
|
||||
"gpio": "60",
|
||||
"adc": "2x SAR ADC de 12 bits, até 16 canais",
|
||||
"dac": "Sim (2x DAC de 10 bits e 2x DAC de 12 bits)",
|
||||
"touch": "14 canais capacitivos",
|
||||
"uart": "5 (4 UART + 1 LP UART)",
|
||||
"spi": "7 (6 SPI + 1 LP SPI)",
|
||||
"i2c": "3 (2 I2C + 1 LP I2C)",
|
||||
"i2s": "2",
|
||||
"pwm": "LED PWM (até 8 canais), Motor PWM (MCPWM)",
|
||||
"usb": "1 (USB 2.0 High-Speed OTG e USB Serial/JTAG)",
|
||||
"can": "Sim (CAN FD Controller)",
|
||||
"sensor_temperatura": "1 (interno)",
|
||||
"consumo_energia": {
|
||||
"active": "Consumo otimizado de alta performance configurável",
|
||||
"light_sleep": "0.1-0.8 mA",
|
||||
"deep_sleep": "Baixo consumo com LP CPU ativa"
|
||||
},
|
||||
"temperatura_operacao": "-40°C a 85°C",
|
||||
"temperatura_armazenamento": "-40°C a 150°C",
|
||||
"datasheet": "https://documentation.espressif.com/esp32-s31_datasheet_en.html",
|
||||
"manual_tecnico": "https://documentation.espressif.com/esp32-s31_technical_reference_manual_en.html",
|
||||
"guia_de_programacao": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s31/get-started/index.html",
|
||||
"placa": "/placas/esp32-s31.png",
|
||||
"cor": "#ff2a5f"
|
||||
},
|
||||
"ESP32-C2": {
|
||||
"nome_completo": "ESP32-C2 (ESP8684)",
|
||||
"icone": "🌱",
|
||||
"descricao": "Microcontrolador RISC-V ultra econômico e de baixo consumo com Wi-Fi 4 e Bluetooth 5.0 LE",
|
||||
"arquitetura": "RISC-V single-core de 32 bits",
|
||||
"nucleos": "1 (Single-core)",
|
||||
"frequencia": "Até 120 MHz",
|
||||
"wifi": "IEEE 802.11 b/g/n (2.4 GHz, até 72.2 Mbps)",
|
||||
"bluetooth": "Bluetooth 5.0 LE",
|
||||
"memoria_sram": "272 KB (16 KB para cache)",
|
||||
"memoria_sram_rtc": "Não",
|
||||
"memoria_rom": "576 KB",
|
||||
"flash_externa": "Não suportada diretamente (2 MB ou 4 MB de Flash integrados no encapsulamento)",
|
||||
"psram_externa": "Não",
|
||||
"gpio": "14",
|
||||
"adc": "1x SAR ADC de 12 bits, até 5 canais",
|
||||
"dac": "Não",
|
||||
"touch": "Não",
|
||||
"uart": "2",
|
||||
"spi": "3 (SPI0/1 para Flash interna, SPI2 para uso geral)",
|
||||
"i2c": "1 (Master-only)",
|
||||
"i2s": "Não",
|
||||
"pwm": "LED PWM (até 6 canais)",
|
||||
"sensor_temperatura": "1 (interno)",
|
||||
"consumo_energia": {
|
||||
"active_cpu_120mhz": "13 mA",
|
||||
"light_sleep": "130 µA",
|
||||
"deep_sleep": "5 µA"
|
||||
},
|
||||
"temperatura_operacao": "-40°C a 105°C",
|
||||
"temperatura_armazenamento": "-40°C a 150°C",
|
||||
"datasheet": "https://documentation.espressif.com/esp32-c2_datasheet_en.html",
|
||||
"manual_tecnico": "https://documentation.espressif.com/esp32-c2_technical_reference_manual_en.html",
|
||||
"guia_de_programacao": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32c2/get-started/index.html",
|
||||
"placa": "/placas/esp32-c2.png",
|
||||
"cor": "#8c52ff"
|
||||
},
|
||||
"ESP32-C3": {
|
||||
"nome_completo": "ESP32-C3",
|
||||
"icone": "💎",
|
||||
@@ -174,20 +254,22 @@
|
||||
"ESP32-C5": {
|
||||
"nome_completo": "ESP32-C5",
|
||||
"icone": "📶",
|
||||
"descricao": "O primeiro com suporte a Wi-Fi 6 dual-band e Bluetooth 5.3, equipado com um processador RISC-V",
|
||||
"arquitetura": "RISC-V single-core de 32 bits",
|
||||
"nucleos": "1 (Single-core)",
|
||||
"descricao": "O primeiro com suporte a Wi-Fi 6 dual-band e Bluetooth 6.0, equipado com CPU RISC-V e coprocessador ULP",
|
||||
"arquitetura": "RISC-V single-core de 32 bits (HP) + RISC-V single-core (LP)",
|
||||
"nucleos": "1 (Single-core HP) + 1 (Single-core LP)",
|
||||
"frequencia": "Até 240 MHz",
|
||||
"coprocessador_ulp": "Sim (RISC-V, LP CPU de 48 MHz)",
|
||||
"wifi": "Wi-Fi 6 (802.11ax) dual-band 2.4 GHz e 5 GHz",
|
||||
"bluetooth": "Bluetooth 5.3 LE",
|
||||
"zigbee_thread": "IEEE 802.15.4",
|
||||
"memoria_sram": "560 KB",
|
||||
"bluetooth": "Bluetooth 6.0 LE",
|
||||
"zigbee_thread": "IEEE 802.15.4 (Zigbee 3.0, Thread 1.4)",
|
||||
"matter": "Sim",
|
||||
"memoria_sram": "384 KB",
|
||||
"memoria_sram_rtc": "16 KB",
|
||||
"memoria_rom": "320 KB",
|
||||
"flash_externa": "Até 16 MB via QSPI",
|
||||
"psram_externa": "Não",
|
||||
"gpio": "29",
|
||||
"adc": "1x SAR ADC de 12 bits, até 7 canais",
|
||||
"adc": "1x SAR ADC de 12 bits, até 6 canais",
|
||||
"dac": "Não",
|
||||
"touch": "Não",
|
||||
"uart": "3",
|
||||
@@ -213,20 +295,21 @@
|
||||
"ESP32-C6": {
|
||||
"nome_completo": "ESP32-C6",
|
||||
"icone": "🌐",
|
||||
"descricao": "Recursos de segurança avançada, Wi-Fi 6, Bluetooth, Zigbee e Thread com suporte a Matter",
|
||||
"arquitetura": "RISC-V single-core de 32 bits",
|
||||
"nucleos": "1 (Single-core)",
|
||||
"descricao": "Recursos de segurança avançada, Wi-Fi 6, Bluetooth 5.3, Zigbee e Thread com suporte a Matter",
|
||||
"arquitetura": "RISC-V single-core de 32 bits (HP) + RISC-V single-core (LP)",
|
||||
"nucleos": "1 (Single-core HP) + 1 (Single-core LP)",
|
||||
"frequencia": "Até 160 MHz",
|
||||
"coprocessador_ulp": "Sim (RISC-V, LP CPU de 20 MHz)",
|
||||
"wifi": "Wi-Fi 6 (802.11ax) 2.4 GHz",
|
||||
"bluetooth": "Bluetooth 5.3 LE",
|
||||
"zigbee_thread": "IEEE 802.15.4 (Zigbee, Thread)",
|
||||
"zigbee_thread": "IEEE 802.15.4 (Zigbee 3.0, Thread 1.3)",
|
||||
"matter": "Sim",
|
||||
"memoria_sram": "512 KB",
|
||||
"memoria_sram_rtc": "16 KB",
|
||||
"memoria_rom": "320 KB",
|
||||
"flash_externa": "Até 16 MB via QSPI",
|
||||
"psram_externa": "Não",
|
||||
"gpio": "30 ou 22",
|
||||
"gpio": "30",
|
||||
"adc": "1x SAR ADC de 12 bits, até 7 canais",
|
||||
"dac": "Não",
|
||||
"touch": "Não",
|
||||
@@ -236,7 +319,7 @@
|
||||
"i2s": "1",
|
||||
"pwm": "LED PWM (até 6 canais), Motor PWM",
|
||||
"usb": "1 (USB Serial/JTAG)",
|
||||
"sdio": "1 (SD/SDIO/MMC Host)",
|
||||
"sdio": "1 (SDIO 2.0 Slave)",
|
||||
"sensor_temperatura": "1 (interno)",
|
||||
"consumo_energia": {
|
||||
"active_wifi6": "Varia conforme perfil TX/RX",
|
||||
@@ -251,11 +334,52 @@
|
||||
"placa": "/placas/esp32-c6.png",
|
||||
"cor": "#17a2b8"
|
||||
},
|
||||
"ESP32-C61": {
|
||||
"nome_completo": "ESP32-C61",
|
||||
"icone": "🛰️",
|
||||
"descricao": "Microcontrolador RISC-V econômico de 160MHz com Wi-Fi 6, Bluetooth 5.0 LE e suporte a Flash/PSRAM no encapsulamento",
|
||||
"arquitetura": "RISC-V single-core de 32 bits",
|
||||
"nucleos": "1 (Single-core)",
|
||||
"frequencia": "Até 160 MHz",
|
||||
"wifi": "Wi-Fi 6 (802.11ax) 2.4 GHz",
|
||||
"bluetooth": "Bluetooth 5.0 LE",
|
||||
"zigbee_thread": "Não",
|
||||
"matter": "Não",
|
||||
"memoria_sram": "320 KB",
|
||||
"memoria_sram_rtc": "Não",
|
||||
"memoria_rom": "256 KB",
|
||||
"flash_externa": "Até 16 MB via QSPI (modelos com 4 MB ou 8 MB integrados no encapsulamento)",
|
||||
"psram_externa": "Até 16 MB via QSPI (modelos com 2 MB ou 8 MB integrados no encapsulamento)",
|
||||
"gpio": "30",
|
||||
"adc": "1x SAR ADC de 12 bits, até 4 canais",
|
||||
"dac": "Não",
|
||||
"touch": "Não",
|
||||
"uart": "3",
|
||||
"spi": "3 (2 para Flash/PSRAM, 1 para uso geral)",
|
||||
"i2c": "1",
|
||||
"i2s": "1",
|
||||
"pwm": "LED PWM (até 6 canais)",
|
||||
"usb": "1 (USB Serial/JTAG)",
|
||||
"sdio": "1 (SDIO 2.0 Slave)",
|
||||
"sensor_temperatura": "1 (interno)",
|
||||
"consumo_energia": {
|
||||
"active": "Baixo consumo com clock dinâmico",
|
||||
"light_sleep": "0.1-0.8 mA",
|
||||
"deep_sleep": "7 µA"
|
||||
},
|
||||
"temperatura_operacao": "-40°C a 105°C",
|
||||
"temperatura_armazenamento": "-40°C a 150°C",
|
||||
"datasheet": "https://documentation.espressif.com/esp32-c61_datasheet_en.html",
|
||||
"manual_tecnico": "https://documentation.espressif.com/esp32-c61_technical_reference_manual_en.html",
|
||||
"guia_de_programacao": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32c61/get-started/index.html",
|
||||
"placa": "/placas/esp32-c61.png",
|
||||
"cor": "#0dcaf0"
|
||||
},
|
||||
"ESP32-P4": {
|
||||
"nome_completo": "ESP32-P4",
|
||||
"icone": "⚡",
|
||||
"descricao": "Alto desempenho com processador RISC-V dual-core a 400MHz e aceleradores de IA",
|
||||
"arquitetura": "RISC-V de 32 bits dual-core de alto desempenho + single-core de baixo consumo",
|
||||
"descricao": "Alto desempenho com processador RISC-V dual-core a 400MHz, aceleradores de IA e ricas interfaces de mídia",
|
||||
"arquitetura": "RISC-V de 32 bits dual-core de alto desempenho (HP) + single-core de baixo consumo (LP)",
|
||||
"nucleos": "2 (Dual-core HP) + 1 (Single-core LP)",
|
||||
"frequencia": "Até 400 MHz",
|
||||
"aceleradores_ia": "Sim (Vector Instructions, aceleradores de IA)",
|
||||
@@ -263,9 +387,10 @@
|
||||
"bluetooth": "Não",
|
||||
"ethernet": "Sim (Ethernet MAC integrado)",
|
||||
"memoria_sram": "768 KB",
|
||||
"memoria_rom": "768 KB",
|
||||
"memoria_sram_rtc": "32 KB",
|
||||
"memoria_rom": "128 KB",
|
||||
"flash_externa": "Até 128 MB via QSPI/Octal SPI",
|
||||
"psram_externa": "Até 32 MB via QSPI/Octal SPI",
|
||||
"psram_externa": "Suporta até 64 MB (16 MB ou 32 MB integrados no encapsulamento via OPI/HPI)",
|
||||
"gpio": "55",
|
||||
"adc": "1x SAR ADC de 12 bits, até 8 canais",
|
||||
"dac": "Não",
|
||||
@@ -298,13 +423,13 @@
|
||||
"ESP32-H2": {
|
||||
"nome_completo": "ESP32-H2",
|
||||
"icone": "🔋",
|
||||
"descricao": "Focado em ultra-baixo consumo com Bluetooth, Zigbee e Thread. Mas sem suporte a Wi-Fi",
|
||||
"descricao": "Focado em ultra-baixo consumo com Bluetooth 5.3, Zigbee e Thread. Mas sem suporte a Wi-Fi",
|
||||
"arquitetura": "RISC-V single-core de 32 bits",
|
||||
"nucleos": "1 (Single-core)",
|
||||
"frequencia": "Até 96 MHz",
|
||||
"wifi": "Não",
|
||||
"bluetooth": "Bluetooth 5.2 LE",
|
||||
"zigbee_thread": "IEEE 802.15.4 (Zigbee, Thread)",
|
||||
"bluetooth": "Bluetooth 5.3 LE",
|
||||
"zigbee_thread": "IEEE 802.15.4 (Zigbee 3.0, Thread 1.3)",
|
||||
"matter": "Sim",
|
||||
"memoria_sram": "320 KB",
|
||||
"memoria_sram_rtc": "16 KB",
|
||||
|
||||