91 lines
3.1 KiB
TypeScript
91 lines
3.1 KiB
TypeScript
"use client";
|
|
|
|
import { useMemo } from "react";
|
|
import { usePortfolioData } from "@/lib/usePortfolioData";
|
|
import { Section } from "./Section";
|
|
import { Reveal } from "./Reveal";
|
|
|
|
export default function Projects() {
|
|
const { data, loading, error } = usePortfolioData();
|
|
|
|
const projects = useMemo(
|
|
() => data?.projects.filter((p) => p.featured) || [],
|
|
[data],
|
|
);
|
|
|
|
if (loading)
|
|
return <div className="ds-loading">Loading featured projects</div>;
|
|
if (error)
|
|
return <div className="ds-error">Error loading featured projects</div>;
|
|
|
|
return (
|
|
<Section
|
|
id="projects"
|
|
eyebrow="PRODUCAO_INTELECTUAL"
|
|
title="Projetos com recorte técnico claro e impacto observável"
|
|
description="Seleção de produtos e ferramentas onde performance, confiabilidade e clareza de arquitetura foram requisitos reais."
|
|
dark
|
|
disableChildrenAnimation
|
|
>
|
|
<div className="ds-grid-2">
|
|
{projects.map((project, index) => (
|
|
<Reveal key={project.id} y={50} duration={0.8} delay={0.1 * index}>
|
|
<article className="ds-card h-full">
|
|
<div className="flex items-start justify-between gap-sp-6">
|
|
<div>
|
|
<div className="ds-kicker">{project.category}</div>
|
|
<h3 className="ds-card-title">{project.title}</h3>
|
|
<p className="mt-sp-2 text-[11px] uppercase tracking-[0.1em] text-[rgba(255,255,255,.36)]">
|
|
{project.subtitle}
|
|
</p>
|
|
</div>
|
|
<span className="ds-tag">{project.status}</span>
|
|
</div>
|
|
<p className="ds-copy">{project.description}</p>
|
|
<div className="mt-sp-6 flex flex-wrap gap-sp-3">
|
|
{project.githubUrl && (
|
|
<a
|
|
href={project.githubUrl}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="ds-link ds-link-muted"
|
|
>
|
|
GitHub
|
|
</a>
|
|
)}
|
|
{project.demoUrl && (
|
|
<a
|
|
href={project.demoUrl}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="ds-link ds-link-accent"
|
|
>
|
|
Live demo
|
|
</a>
|
|
)}
|
|
</div>
|
|
<ul className="ds-tags mt-sp-6">
|
|
{project.technologies.slice(0, 6).map((tech: string) => (
|
|
<li key={tech} className="ds-tag">
|
|
{tech}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
<div className="mt-sp-6 flex flex-wrap gap-sp-4">
|
|
<span className="ds-metric">
|
|
Complexity {project.metrics?.complexity || 3}/5
|
|
</span>
|
|
{project.metrics?.teamSize && (
|
|
<span className="ds-metric">
|
|
Team {project.metrics.teamSize}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</article>
|
|
</Reveal>
|
|
))}
|
|
</div>
|
|
</Section>
|
|
);
|
|
}
|