feat: restyle experience section
This commit is contained in:
parent
661482c405
commit
bf31fda249
1 changed files with 242 additions and 119 deletions
|
|
@ -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>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue