Files
portifolio-evandrodevbr/app/components/Projects.tsx
T
Evandro Fonseca JuniorandClaude Sonnet 5 d76bbf5974 feat: revamp portfolio with case studies, curriculo page, and local SEO
Adds dynamic case study and curriculo routes, project health monitoring
for live demo status, and strengthens local SEO (structured data,
service-area targeting for Garuva/Joinville/Curitiba, a dedicated
"criação de sites" service page) so search results better match the
professional title (Engenheiro da Computação) and experience (5 anos).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-07-15 01:31:46 -03:00

149 lines
4.4 KiB
TypeScript

"use client";
import type { PortfolioProject } from "@/lib/portfolio-data";
import { motion, type Variants, useReducedMotion } from "framer-motion";
import { Section } from "./Section";
import MagneticButton from "./cinematic/MagneticButton";
import { DrawLine } from "./animations/DrawLine";
export default function Projects({
projects,
}: {
projects: PortfolioProject[];
}) {
const shouldReduceMotion = useReducedMotion();
const gridVariants: Variants = {
hidden: {},
visible: {
transition: {
delayChildren: 0.08,
staggerChildren: 0.08,
},
},
};
const cardVariants: Variants = {
hidden: shouldReduceMotion
? { opacity: 0 }
: { opacity: 0, y: 24, clipPath: "inset(100% 0 0 0)" },
visible: {
opacity: 1,
y: 0,
clipPath: "inset(0% 0 0 0)",
transition: shouldReduceMotion
? { duration: 0.25 }
: { duration: 0.8, ease: [0.16, 1, 0.3, 1] },
},
};
return (
<Section
id="projects"
eyebrow="Selected work"
title="Projetos em Next.js, Tauri, Rust e integracao de sistemas"
description="Cases e produtos que mostram minha atuacao em engenharia de software, consultoria tecnica e construcao de sistemas de alta exigencia."
dark
disableChildrenAnimation
>
<motion.div
className="ds-grid-2"
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.2 }}
variants={gridVariants}
>
{projects.map((project) => (
<motion.article
key={project.id}
className="ds-card h-full"
variants={cardVariants}
whileHover={
shouldReduceMotion
? undefined
: {
y: -4,
boxShadow: "0 20px 44px rgba(0, 0, 0, 0.42)",
}
}
whileTap={shouldReduceMotion ? undefined : { scale: 0.99 }}
transition={{ type: "spring", stiffness: 170, damping: 18 }}
>
<div className="flex items-start justify-between gap-sp-6">
<div className="max-w-[560px]">
<div className="ds-kicker">{project.category}</div>
<DrawLine delay={0.05} />
<h3 className="ds-card-title">{project.title}</h3>
{project.subtitle && (
<p className="mt-sp-3 text-[13px] leading-6 text-[rgba(255,255,255,.56)]">
{project.subtitle}
</p>
)}
</div>
<MagneticButton as="span" className="ds-tag" strength={0.15}>
{project.status}
</MagneticButton>
</div>
<p className="ds-copy">{project.description}</p>
<ul className="ds-tags mt-sp-6">
{project.technologies.slice(0, 5).map((tech) => (
<li key={tech} className="ds-tag">
{tech}
</li>
))}
</ul>
<div className="mt-sp-6 flex flex-wrap gap-sp-3">
{project.githubUrl && (
<MagneticButton
as="a"
href={project.githubUrl}
target="_blank"
rel="noreferrer"
className="ds-link ds-link-muted"
strength={0.25}
>
Ver codigo
</MagneticButton>
)}
{project.demoUrl && (
<MagneticButton
as="a"
href={project.demoUrl}
target="_blank"
rel="noreferrer"
className="ds-link ds-link-accent"
strength={0.25}
>
Abrir projeto
</MagneticButton>
)}
</div>
</motion.article>
))}
</motion.div>
<div className="mt-sp-8 flex flex-wrap gap-sp-3">
<MagneticButton
as="a"
href="/cases"
className="ds-link ds-link-accent"
strength={0.2}
>
Explorar todos os cases
</MagneticButton>
<MagneticButton
as="a"
href="/servicos"
className="ds-link ds-link-muted"
strength={0.2}
>
Ver servicos relacionados
</MagneticButton>
</div>
</Section>
);
}