feat: restyle experience section

This commit is contained in:
Alvalens 2025-05-26 23:09:29 +07:00
parent 661482c405
commit bf31fda249

View file

@ -1,5 +1,70 @@
"use client";
import Hr from "@/components/Hr"; import Hr from "@/components/Hr";
import { motion } from "framer-motion"; import { motion, AnimatePresence } from "framer-motion";
import { useState } from "react";
const experiences = [
{
id: 1,
period: "2023 - Present",
startDate: "Jan 2023",
endDate: "Present",
company: "PT Hafdzamedia Teknologi Aplikasi",
position: "Fullstack Web Developer",
type: "Part-time",
description:
"Responsible for building and maintaining scalable web applications using the latest technologies. Collaborated with cross-functional teams to deliver high-quality solutions that meet business requirements.",
skills: ["React", "Next.js", "Laravel", "MySql"],
},
{
id: 2,
period: "2024 - Present",
startDate: "Mar 2024",
endDate: "Present",
company: "Outlier",
position: "AI Trainer",
type: "Freelance",
description:
"Crafted and refined chatbot prompts using prompt engineering and RLHF techniques to improve the AI responses in English and Indonesian, leading to more accurate and contextually relevant interactions.",
skills: ["Generative AI", "Prompt Engineering", "Problem Solving"],
},
{
id: 3,
period: "2023 - Present",
startDate: "Jun 2023",
endDate: "Present",
company: "Self-Employed",
position: "Web Developer",
type: "Freelance",
description:
"Specialized in crafting innovative solutions using Laravel and Next.js, enhancing client satisfaction and project efficiency. Contributed expertise in both frontend and backend development during collaborative team projects.",
skills: ["React", "Next.js", "Laravel", "MySql"],
},
{
id: 4,
period: "2022 - 2023",
startDate: "Sep 2022",
endDate: "Aug 2023",
company: "TechStart Solutions",
position: "Frontend Developer",
type: "Internship",
description:
"Developed responsive user interfaces using React and TypeScript. Worked closely with UX designers to implement pixel-perfect designs and improved website performance by 40%.",
skills: ["React", "TypeScript", "CSS3", "Figma"],
},
{
id: 5,
period: "2021 - 2022",
startDate: "Oct 2021",
endDate: "Sep 2022",
company: "Digital Innovation Labs",
position: "Junior Developer",
type: "Contract",
description:
"Built REST APIs using Node.js and Express. Implemented database schemas and optimized queries for better performance. Participated in code reviews and agile development processes.",
skills: ["Node.js", "Express", "MongoDB", "Git"],
},
];
function Title() { function Title() {
return ( return (
@ -18,7 +83,6 @@ function Title() {
}} }}
transition={{ transition={{
delay: 0.7, delay: 0.7,
type: "spring", type: "spring",
}}> }}>
Profesional Experience Profesional Experience
@ -28,135 +92,194 @@ function Title() {
); );
} }
function TimelineCard({ experience, index, isEven }) {
return (
<motion.div
initial={{ opacity: 0, y: -20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.15, duration: 0.5 }}
className={`flex ps-5 md:ps-0 ${
isEven
? "md:justify-center md:translate-x-60"
: "md:justify-center md:-translate-x-60"
} justify-center mb-4`}>
<div className="bg-gradient-to-r from-black to-gray-800 text-white px-12 py-3 rounded-xl shadow-lg border border-gray-600 min-w-max">
<div className="flex items-center justify-center gap-6">
<div className="text-center">
<div className="text-sm font-bold">{experience.startDate}</div>
<div className="text-xs text-gray-300">Start</div>
</div>
<div className="w-px h-8 bg-gray-500"></div>
<div className="text-center">
<div className="text-sm font-bold">{experience.endDate}</div>
<div className="text-xs text-gray-300">End</div>
</div>
<div className="w-px h-8 bg-gray-500"></div>
<div className="text-center">
<div className="text-sm font-medium text-gray-400">
{experience.type}
</div>
<div className="text-xs text-gray-300">Type</div>
</div>
</div>
</div>
</motion.div>
);
}
function ExperienceCard({ experience, index, isEven }) {
return (
<motion.div
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.2, duration: 0.6 }}
className={`relative group ${
isEven ? "md:ml-auto md:pl-12" : "md:mr-auto md:pr-12"
} md:w-1/2`}>
{" "}
{/* Card */}
<div
className={`bg-white/20 backdrop-blur-sm border border-gray-300/30 rounded-2xl p-6 shadow-lg
hover:shadow-xl hover:bg-white/30 transition-all duration-300 ml-12 md:ml-0`}>
{/* Company & Position */}
<div className="mb-4">
<h3 className="font-bold text-xl text-black mb-1">
{experience.company}
</h3>
<h4 className="font-medium text-lg text-gray-700">
{experience.position}
<span className="text-sm font-normal text-gray-500 ml-2">
• {experience.type}
</span>
</h4>
</div>
{/* Description */}
<p className="text-gray-600 text-justify leading-relaxed mb-4">
{experience.description}
</p>
{/* Skills */}
<div className="flex flex-wrap gap-2">
{experience.skills.map((skill, idx) => (
<span
key={idx}
className="bg-gray-200/60 hover:bg-gray-300/60 border border-gray-400/40 text-black px-3 py-1 rounded-full text-sm font-medium transition-all duration-300 backdrop-blur-sm hover:scale-105">
{skill}
</span>
))}
</div>
</div>
</motion.div>
);
}
function Wrapper({ children }) { function Wrapper({ children }) {
return ( return (
<div className="mx-auto container gap-10 p-10 grid grid-cols-1 my-10 "> <div className="mx-auto container px-6 py-10">
<motion.div <div
className="flex justify-center items-start flex-col mb-5 " className="flex justify-center items-center flex-col">
initial={{
opacity: 0,
x: -200,
}}
whileInView={{
opacity: 1,
x: 0,
}}
transition={{
delay: 0.5,
type: "spring",
}}>
<section className="grid gap-8 md:gap-12">
<div className="relative after:absolute after:inset-y-2 after:w-1 after:bg-gray-700 after:left-[-4px] md:after:left-1 lg:after:left-2 xl:after:left-3 grid gap-8 md:gap-12 pl-6 md:pl-8">
{children} {children}
</div> </div>
</section>
</motion.div>
</div> </div>
); );
} }
export default function Experience() { export default function Experience() {
const [showAll, setShowAll] = useState(false);
const displayedExperiences = showAll ? experiences : experiences.slice(0, 3);
return ( return (
<> <>
<Title/> <Title />
<Wrapper> <Wrapper>
<div className="grid gap-4 relative"> {" "}
<div className="aspect-square w-5 bg-black rounded-full absolute left-0 translate-x-[-29.5px] z-10" /> <div className="relative w-full max-w-6xl mx-auto">
<div className="font-medium text-lg">2023 - Present</div> {" "}
<div> {/* Timeline line - hidden on mobile, visible on md+ */}
<h3 className="font-semibold text-xl text-black"> <div className="hidden md:block absolute left-1/2 transform -translate-x-1/2 w-1 bg-gradient-to-b from-black via-gray-400 to-transparent h-full"></div>
PT Hafdzamedia Teknologi Aplikasi {/* Mobile timeline line */}
</h3> <div className="md:hidden absolute left-0 w-1 bg-gradient-to-b from-black via-gray-400 to-transparent h-full"></div>{" "}
<h4 className=" font-light text-md mb-4"> {/* Experience cards */}
Fullstack Web Developer | Part-time <div className="space-y-12 md:space-y-16 relative">
</h4> <AnimatePresence>
<p className="text-justify"> {displayedExperiences.map((experience, index) => (
Responsible for building and maintaining scalable <div key={experience.id} className="relative">
web applications using the latest technologies. {/* Timeline period card - flows naturally above content */}
Collaborated with cross-functional teams to deliver <TimelineCard
high-quality solutions that meet business experience={experience}
requirements. index={index}
</p> isEven={index % 2 === 1}
<div className="flex flex-wrap gap-2 mt-4 text-sm"> />
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
React
</div>
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
Next.js
</div>
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
Laravel
</div>
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
MySql
</div>
</div>
</div>
</div>
<div className="grid gap-4 relative">
<div className="aspect-square w-5 bg-black rounded-full absolute left-0 translate-x-[-29.5px] z-10" />
<div className="font-medium text-lg">2024 - Present</div>
<div>
<h3 className="font-semibold text-xl text-black">
Outlier
</h3>
<h4 className=" font-light text-md mb-4">
AI Trainer | Freelance
</h4>
<p className="text-justify">
Crafted and refined chatbot prompts using prompt
engineering and RLHF techniques to improve the AI
responses in English and Indonesian, leading to more
accurate and contextually relevant interactions.
</p>
<div className="flex flex-wrap gap-2 mt-4 text-sm"> {/* Timeline dot - positioned at the start of the experience card */}
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl"> <div
Generative AI className={`absolute w-6 h-6 bg-black rounded-full border-4 border-white shadow-lg z-30
</div> md:left-1/2 md:-translate-x-1/2 md:top-4
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl"> left-0 -translate-x-1/2 top-2`}
Prompt Engineering />
</div>
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl"> {/* Experience content card */}
Problem Solving <ExperienceCard
</div> experience={experience}
</div> index={index}
</div> isEven={index % 2 === 1}
</div> />
<div className="grid gap-4 relative">
<div className="aspect-square w-5 bg-black rounded-full absolute left-0 translate-x-[-29.5px] z-10" />
<div className="font-medium text-lg">2023 - Present</div>
<div>
<h3 className="font-semibold text-xl text-black">
Self-Employed
</h3>
<h4 className=" font-light text-md mb-4">
Web Developer | Freelance
</h4>
<p className="text-justify">
Specialized in crafting innovative solutions using
Laravel and Next.js, enhancing client satisfaction
and project efficiency. Contributed expertise in
both frontend and backend development during
collaborative team projects, leading to successful
project completions
</p>
<div className="flex flex-wrap gap-2 mt-4 text-sm">
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
React
</div>
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
Next.js
</div>
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
Laravel
</div>
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
MySql
</div>
</div> </div>
))}
</AnimatePresence>
</div> </div>
{/* Expand/Collapse button */}
{experiences.length > 3 && (
<motion.div
className="flex justify-center mt-12"
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
transition={{ delay: 0.5 }}>
<button
onClick={() => setShowAll(!showAll)}
className="bg-black hover:bg-gray-800 text-white px-8 py-3 rounded-full font-medium
transition-all duration-300 hover:scale-105 shadow-lg flex items-center gap-2">
{showAll ? (
<>
Show Less
<svg
className="w-4 h-4 transform rotate-180"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 9l-7 7-7-7"
/>
</svg>
</>
) : (
<>
View More Experience
<svg
className="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 9l-7 7-7-7"
/>
</svg>
</>
)}
</button>
</motion.div>
)}{" "}
{/* Gradient fade effect at bottom */}
{!showAll && (
<div className="absolute bottom-0 left-0 right-0 h-32 bg-gradient-to-t from-stale-300 to-transparent pointer-events-none"></div>
)}
</div> </div>
</Wrapper> </Wrapper>
</> </>