feat: Implement initial portfolio website structure with core components and a new post creator tool.
This commit is contained in:
1 parent
036363b521
commit
aa07d6faeb
15 files changed
+1337
-1855
No files matched your search
+29
-121
@@ -1,134 +1,42 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import {
|
||||
Code, Database, Cloud, Cpu, Smartphone,
|
||||
Zap, GitBranch, TestTube, Terminal, Layers
|
||||
} from "lucide-react";
|
||||
import { usePortfolioData } from "@/lib/usePortfolioData";
|
||||
|
||||
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`,
|
||||
};
|
||||
};
|
||||
import { Section } from "./Section";
|
||||
|
||||
export default function Skills() {
|
||||
const { data, loading, error } = usePortfolioData();
|
||||
|
||||
if (loading) return <LoadingState />;
|
||||
if (error || !data) return <ErrorState />;
|
||||
if (loading) return <div className="ds-loading">Loading skill registry</div>;
|
||||
if (error || !data) {
|
||||
return <div className="ds-error">Error loading skill registry</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<section id="skills" className="relative py-32 px-6">
|
||||
{/* Background Decoration */}
|
||||
<div className="absolute top-0 left-0 w-full h-full pointer-events-none">
|
||||
<div className="absolute top-[40%] left-[50%] -translate-x-1/2 w-[800px] h-[800px] bg-blue-900/5 rounded-full blur-[100px]" />
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-blue-950/30 border border-blue-500/20 text-blue-400 font-mono text-xs tracking-wider mb-4">
|
||||
<Layers className="w-3 h-3" />
|
||||
<span>MODULE_REGISTRY.lib</span>
|
||||
<Section
|
||||
id="skills"
|
||||
eyebrow="MODULE_REGISTRY.lib"
|
||||
title="Stack modular para produto, operação e escala"
|
||||
description="Ferramentas escolhidas por previsibilidade operacional, velocidade de entrega e facilidade de manutenção."
|
||||
dark
|
||||
>
|
||||
<div className="ds-grid-2">
|
||||
{data.skills.technical.map((category, index) => (
|
||||
<article key={category.category} className="ds-card">
|
||||
<div className="flex items-center justify-between gap-sp-4">
|
||||
<div className="ds-kicker">ID_0{index + 1}X</div>
|
||||
<div className="ds-metric">{category.skills.length} items</div>
|
||||
</div>
|
||||
<h3 className="text-4xl md:text-6xl font-black text-white tracking-tighter">
|
||||
Arsenal <span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-500 to-indigo-500">Tecnológico</span>
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-slate-400 max-w-md text-sm md:text-base font-light leading-relaxed border-l-2 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-6">
|
||||
{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: 50 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, amount: 0.2 }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: index * 0.05,
|
||||
ease: "easeOut"
|
||||
}}
|
||||
className="h-full"
|
||||
>
|
||||
<div
|
||||
className="group relative h-full p-8 flex flex-col bg-zinc-900/40 border border-white/5 rounded-2xl backdrop-blur-sm hover:border-blue-500/20 hover:bg-zinc-900/60 transition-all duration-300 overflow-hidden"
|
||||
>
|
||||
{/* Scanning Line Effect */}
|
||||
<div className="absolute top-0 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-blue-500/50 to-transparent -translate-x-full group-hover:animate-scan" />
|
||||
|
||||
<div className="flex items-start justify-between mb-8">
|
||||
<div className={`p-4 rounded-xl bg-slate-800/50 border border-white/5 group-hover:bg-blue-500/10 group-hover:border-blue-500/20 transition-all duration-300`}>
|
||||
<Icon className={`w-8 h-8 text-slate-400 group-hover:text-blue-400 transition-colors`} />
|
||||
</div>
|
||||
<span className="text-[10px] font-mono text-slate-600 uppercase tracking-widest font-bold group-hover:text-blue-500/50 transition-colors">
|
||||
ID_0{index}X
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h3 className="text-xl font-bold text-white mb-6 group-hover:text-blue-200 transition-colors">
|
||||
{category.category}
|
||||
</h3>
|
||||
|
||||
<div className="flex flex-wrap gap-2 mt-auto">
|
||||
{category.skills.map((skill, sIdx) => (
|
||||
<span
|
||||
key={sIdx}
|
||||
className="px-2.5 py-1 text-[10px] font-mono bg-black/40 text-slate-400 rounded border border-white/5 group-hover:border-blue-500/20 group-hover:text-blue-300 transition-all duration-300 cursor-default"
|
||||
>
|
||||
{skill}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<h3 className="ds-card-title">{category.category}</h3>
|
||||
<ul className="ds-tags mt-sp-6">
|
||||
{category.skills.map((skill) => (
|
||||
<li key={skill} className="ds-tag">
|
||||
{skill}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user