Files

124 lines
3.9 KiB
TypeScript

"use client";
import { useMemo } from "react";
import { motion, type Variants, useReducedMotion } from "framer-motion";
import { usePortfolioData } from "@/lib/usePortfolioData";
import { Section } from "./Section";
import { DrawLine } from "./animations/DrawLine";
import MagneticButton from "./cinematic/MagneticButton";
import { SectionLoading } from "./SectionLoading";
export default function Experience() {
const { data, loading, error } = usePortfolioData();
const shouldReduceMotion = useReducedMotion();
const sortedExperiences = useMemo(() => {
if (!data?.experiences) return [];
return [...data.experiences]
.filter((exp) => exp.featured)
.sort((a, b) => {
const yearA = Number.parseInt(a.period.match(/\d{4}/)?.[0] || "0", 10);
const yearB = Number.parseInt(b.period.match(/\d{4}/)?.[0] || "0", 10);
return yearB - yearA;
});
}, [data]);
if (loading) {
return <SectionLoading message="Loading selected experience" />;
}
if (error || !data) {
return <div className="ds-error">Error loading selected experience</div>;
}
const listVariants: Variants = {
hidden: {},
visible: {
transition: {
delayChildren: 0.08,
staggerChildren: 0.08,
},
},
};
const itemVariants: Variants = {
hidden: shouldReduceMotion
? { opacity: 0 }
: { opacity: 0, y: 20, 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="experience"
eyebrow="Selected experience"
title="Experiencia em engenharia de software e sistemas criticos"
description="Projetos e contextos em que combinei arquitetura de software, integracoes complexas e entrega com responsabilidade operacional."
disableChildrenAnimation
>
<motion.div
className="space-y-sp-6"
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.18 }}
variants={listVariants}
>
{sortedExperiences.map((exp) => (
<motion.article
key={exp.id}
className="ds-card h-full"
variants={itemVariants}
whileHover={
shouldReduceMotion
? undefined
: {
y: -4,
boxShadow: "0 16px 36px rgba(10,10,10,0.15)",
}
}
whileTap={shouldReduceMotion ? undefined : { scale: 0.995 }}
transition={{ type: "spring", stiffness: 170, damping: 20 }}
>
<div className="flex flex-col gap-sp-4 lg:flex-row lg:items-start lg:justify-between">
<div className="max-w-[720px]">
<div className="ds-kicker">{exp.period}</div>
<DrawLine delay={0.04} />
<h3 className="ds-card-title">{exp.title}</h3>
<p className="mt-sp-3 text-[13px] leading-6 text-[rgba(10,10,10,.64)] dark:text-[rgba(255,255,255,.6)]">
{exp.company} • {exp.type}
</p>
</div>
<div className="ds-metric">{exp.location}</div>
</div>
<p className="ds-copy">{exp.description}</p>
<ul className="ds-list">
{exp.achievements?.map((achievement) => (
<li key={achievement}>{achievement}</li>
))}
</ul>
<ul className="ds-tags mt-sp-6">
{exp.technologies?.slice(0, 6).map((tech) => (
<MagneticButton key={tech} as="li" className="ds-tag" strength={0.15}>
{tech}
</MagneticButton>
))}
</ul>
</motion.article>
))}
</motion.div>
</Section>
);
}