115 lines
3.6 KiB
TypeScript
115 lines
3.6 KiB
TypeScript
"use client";
|
|
|
|
import { motion, type Variants } from "motion/react";
|
|
import { useReducedMotion } from "@/app/hooks/useReducedMotion";
|
|
import type { PortfolioExperience } from "@/lib/portfolio-data";
|
|
import { DrawLine } from "./animations/DrawLine";
|
|
import { Section } from "./Section";
|
|
|
|
export default function Experience({
|
|
experiences = [],
|
|
}: {
|
|
experiences?: PortfolioExperience[];
|
|
}) {
|
|
const shouldReduceMotion = useReducedMotion();
|
|
const sortedExperiences = [...experiences]
|
|
.filter((experience) => experience.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;
|
|
});
|
|
|
|
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="Experiência em engenharia de software e sistemas críticos"
|
|
description="Projetos e contextos em que combinei arquitetura de software, integrações 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" data-reel="left">
|
|
{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" data-reel="right">
|
|
{exp.description}
|
|
</p>
|
|
|
|
<ul className="ds-list">
|
|
{exp.achievements?.map((achievement) => (
|
|
<li key={achievement}>{achievement}</li>
|
|
))}
|
|
</ul>
|
|
|
|
<ul className="ds-tags mt-sp-6" data-reel-stagger="fade">
|
|
{exp.technologies?.slice(0, 6).map((tech) => (
|
|
<li key={tech} className="ds-tag">
|
|
{tech}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</motion.article>
|
|
))}
|
|
</motion.div>
|
|
</Section>
|
|
);
|
|
}
|