- Added section IDs to About, Contact, and Hero components for improved navigation and accessibility. - Refactored Header component to utilize a structured navigation array, enhancing maintainability and responsiveness. - Updated Skills component to improve type handling and visual consistency, including a loading state and error handling. - Adjusted styling and animations across components for a smoother user experience and better visual appeal.
141 lines
5.4 KiB
TypeScript
141 lines
5.4 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
import { motion } from "framer-motion"; // Ajustado para o padrão estável
|
|
import { MagicCard } from "@/components/magicui/magic-card";
|
|
import {
|
|
Code, Database, Cloud, Cpu, Smartphone,
|
|
Zap, GitBranch, TestTube, Terminal, Layers
|
|
} from "lucide-react";
|
|
import { usePortfolioData } from "@/lib/usePortfolioData";
|
|
|
|
// --- RESOLUÇÃO DE TIPAGEM (Wrapper) ---
|
|
// Estendemos a interface original para aceitar as props de gradiente que a lib pode ter omitido nos tipos
|
|
interface FixedMagicCardProps extends React.ComponentProps<typeof MagicCard> {
|
|
gradientColor?: string;
|
|
gradientSize?: number;
|
|
gradientOpacity?: number;
|
|
}
|
|
|
|
const FixedMagicCard = (props: FixedMagicCardProps) => {
|
|
// @ts-ignore - Forçamos o cast para o componente original aceitar as props em runtime
|
|
return <MagicCard {...props} />;
|
|
};
|
|
|
|
const ICON_MAP: Record<string, React.ElementType> = {
|
|
Code, Database, Cloud, Cpu, Smartphone, Zap, GitBranch, TestTube, Terminal, Layers
|
|
};
|
|
|
|
const getCategoryStyles = (color: string) => {
|
|
const base = color.replace("text-", "");
|
|
return {
|
|
icon: color,
|
|
bg: `bg-${base}/10`,
|
|
border: `border-slate-800/50`,
|
|
};
|
|
};
|
|
|
|
export default function Skills() {
|
|
const { data, loading, error } = usePortfolioData();
|
|
|
|
if (loading) return <LoadingState />;
|
|
if (error || !data) return <ErrorState />;
|
|
|
|
return (
|
|
<section id="skills" className="relative py-32 px-6 bg-[#030712] overflow-hidden">
|
|
<div className="max-w-7xl mx-auto relative z-10">
|
|
|
|
{/* Header Seção */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true, margin: "-100px" }}
|
|
className="flex flex-col md:flex-row justify-between items-end mb-20 gap-8"
|
|
>
|
|
<div className="text-left">
|
|
<h2 className="text-sm font-mono text-blue-500 uppercase tracking-[0.3em] mb-4 flex items-center gap-2 font-bold">
|
|
<Layers className="w-4 h-4" /> Stack de Engenharia
|
|
</h2>
|
|
<h3 className="text-4xl md:text-6xl font-black text-white">
|
|
Arsenal <span className="text-slate-700">Tecnológico</span>
|
|
</h3>
|
|
</div>
|
|
<p className="text-slate-400 max-w-md text-lg font-light leading-relaxed border-l border-slate-800 pl-6">
|
|
Sistemas resilientes construídos com precisão matemática e rigor arquitetural.
|
|
</p>
|
|
</motion.div>
|
|
|
|
{/* Grid de Habilidades */}
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
{data.skills.technical.map((category, index) => {
|
|
const Icon = ICON_MAP[category.icon] || Code;
|
|
const styles = getCategoryStyles(category.color);
|
|
|
|
return (
|
|
<motion.div
|
|
key={category.category}
|
|
initial={{ opacity: 0, y: 30 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true, amount: 0.2 }}
|
|
transition={{
|
|
duration: 0.5,
|
|
delay: index * 0.05,
|
|
ease: "easeOut"
|
|
}}
|
|
>
|
|
<FixedMagicCard
|
|
className="group p-8 flex flex-col h-full bg-slate-900/50 border-slate-800 transition-colors duration-300 cursor-default"
|
|
gradientColor="#262626"
|
|
gradientSize={250}
|
|
gradientOpacity={0.4}
|
|
>
|
|
<div className="flex items-start justify-between mb-8">
|
|
<div className={`p-4 rounded-2xl ${styles.bg} border border-white/5 transition-all duration-500 group-hover:scale-110 group-hover:rotate-3`}>
|
|
<Icon className={`w-8 h-8 ${styles.icon}`} />
|
|
</div>
|
|
<span className="text-[10px] font-mono text-slate-700 uppercase tracking-widest font-bold">
|
|
ID_0{index}
|
|
</span>
|
|
</div>
|
|
|
|
<h3 className="text-xl font-bold text-white mb-6">
|
|
{category.category}
|
|
</h3>
|
|
|
|
<div className="flex flex-wrap gap-2 mt-auto">
|
|
{category.skills.map((skill, sIdx) => (
|
|
<span
|
|
key={sIdx}
|
|
className="px-3 py-1 text-[11px] font-mono bg-slate-950/50 text-slate-400 rounded border border-slate-800 group-hover:border-blue-500/30 transition-all duration-300"
|
|
>
|
|
{skill}
|
|
</span>
|
|
))}
|
|
</div>
|
|
</FixedMagicCard>
|
|
</motion.div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
// --- Componentes de Suporte (Manter no mesmo arquivo para contexto ou mover para /components) ---
|
|
function LoadingState() {
|
|
return (
|
|
<div className="py-32 flex flex-col items-center justify-center space-y-4 bg-[#030712]">
|
|
<div className="w-12 h-12 border-4 border-blue-500/20 border-t-blue-500 rounded-full animate-spin" />
|
|
<span className="text-slate-500 font-mono text-sm animate-pulse uppercase tracking-tighter">Initializing Systems...</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ErrorState() {
|
|
return (
|
|
<div className="py-32 text-center text-red-400 font-mono bg-[#030712]">
|
|
[FATAL_ERROR]: Failed to synchronize portfolio data.
|
|
</div>
|
|
);
|
|
} |