Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions resume-generator/template.pug
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@ html(lang="en")
.a4-page
.action-bar.no-print
button(onclick="window.print()")
svg(width='25' height='22' viewbox='0 0 25 22' fill='none' xmlns='http://www.w3.org/2000/svg')
path(d='M19.6113 0.25C19.7975 0.250059 19.9749 0.325939 20.1045 0.458984C20.2338 0.591855 20.3055 0.770971 20.3057 0.956055V4.81543H22.1299C23.5658 4.81543 24.7499 5.94567 24.75 7.34766V16.4785C24.7499 16.6637 24.6782 16.8427 24.5488 16.9756C24.4192 17.1087 24.2419 17.1845 24.0557 17.1846H20.3057V21.0439C20.3055 21.229 20.2338 21.4081 20.1045 21.541C19.9749 21.6741 19.7975 21.7499 19.6113 21.75H5.38867C5.20246 21.7499 5.02513 21.6741 4.89551 21.541C4.76615 21.4081 4.69446 21.229 4.69434 21.0439V17.1846H0.944336C0.758061 17.1845 0.580822 17.1087 0.451172 16.9756C0.321767 16.8427 0.250065 16.6637 0.25 16.4785V7.34766C0.250096 5.94567 1.43415 4.81543 2.87012 4.81543H4.69434V0.956055C4.69446 0.77097 4.76615 0.591854 4.89551 0.458984C5.02513 0.325939 5.20246 0.250059 5.38867 0.25H19.6113ZM6.08301 20.3369H18.917V14.4453H6.08301V20.3369ZM2.87012 6.22852C2.17472 6.22852 1.63877 6.7486 1.63867 7.34766V15.7715H4.69434V13.7393C4.69434 13.5541 4.76612 13.3752 4.89551 13.2422C5.02514 13.109 5.20238 13.0323 5.38867 13.0322H19.6113C19.7976 13.0323 19.9749 13.109 20.1045 13.2422C20.2339 13.3752 20.3057 13.5541 20.3057 13.7393V15.7715H23.3613V7.34766C23.3612 6.7486 22.8253 6.22852 22.1299 6.22852H2.87012ZM19.167 8.46777C19.4711 8.46786 19.7617 8.5918 19.9746 8.81055C20.1873 9.02912 20.3057 9.32462 20.3057 9.63086C20.3056 9.85927 20.2394 10.0835 20.1152 10.2744C19.9909 10.4654 19.8132 10.6153 19.6045 10.7041C19.3956 10.7929 19.1655 10.8159 18.9434 10.7705C18.7212 10.7251 18.5182 10.6133 18.3594 10.4502C18.2007 10.2872 18.0933 10.0806 18.0498 9.85645C18.0064 9.63217 18.0279 9.39917 18.1133 9.1875C18.1987 8.97577 18.3439 8.79363 18.5312 8.66504C18.7187 8.53639 18.9402 8.46777 19.167 8.46777ZM6.08301 4.81543H18.917V1.66309H6.08301V4.81543Z' stroke-width='0.5')
svg(xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='#000000' viewbox='0 0 256 256')
path(d='M214.67,72H200V40a8,8,0,0,0-8-8H64a8,8,0,0,0-8,8V72H41.33C27.36,72,16,82.77,16,96v80a8,8,0,0,0,8,8H56v32a8,8,0,0,0,8,8H192a8,8,0,0,0,8-8V184h32a8,8,0,0,0,8-8V96C240,82.77,228.64,72,214.67,72ZM72,48H184V72H72ZM184,208H72V160H184Zm40-40H200V152a8,8,0,0,0-8-8H64a8,8,0,0,0-8,8v16H32V96c0-4.41,4.19-8,9.33-8H214.67c5.14,0,9.33,3.59,9.33,8Zm-24-52a12,12,0,1,1-12-12A12,12,0,0,1,200,116Z')
a(download="" href="/Resume-CraigWayneGovender.pdf" target="_blank")
svg(xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='#000000' viewbox='0 0 256 256')
path(d='M224,144v64a8,8,0,0,1-8,8H40a8,8,0,0,1-8-8V144a8,8,0,0,1,16,0v56H208V144a8,8,0,0,1,16,0Zm-101.66,5.66a8,8,0,0,0,11.32,0l40-40a8,8,0,0,0-11.32-11.32L136,124.69V32a8,8,0,0,0-16,0v92.69L93.66,98.34a8,8,0,0,0-11.32,11.32Z')
header
h1 #{first_names} #{last_name}
h2 #{latest_role}
Expand All @@ -61,20 +64,21 @@ html(lang="en")
| #{contact.phone}
span
i.fab.fa-linkedin
a(target="_blank" href=`https://www.linkedin.com/in/${contact.linkedin_username}`)
| in/#{contact.linkedin_username}
a(target="_blank" href=`https://www.linkedin.com/in/${social.linkedin}`)
| in/#{social.linkedin}
p.summary #{summary}

section.experience
.title EMPLOYMENT
each experience in work_experience
// last 4 experience
each experience in (work_experience.slice(0,4))
.item
header
h4.title #{experience.position}
.date
| #{experience.period.start} - #{experience.period.end}
.subtitle
span.organization #{experience.organization}
span.organization #{experience.organization.name}
span.location #{experience.location.country}
ul
each responsibility in experience.responsibilities
Expand All @@ -101,7 +105,7 @@ html(lang="en")
if index < sorted_skills.length - 1
| ,
.supported-brands.muted.no-print
.title Supported Products
.title ATS Friendly
svg.active(viewbox='0 0 51.4 107.7' xmlns='http://www.w3.org/2000/svg')
path(fill="#23A47F" d='M44.9 32c0 5.2-2.2 9.8-5.8 13.4-4 4-9.8 5-9.8 8.4 0 4.6 7.4 3.2 14.5 10.3 4.7 4.7 7.6 10.9 7.6 18.1 0 14.2-11.4 25.5-25.7 25.5S0 96.4 0 82.2C0 75 2.9 68.8 7.6 64.1c7.1-7.1 14.5-5.7 14.5-10.3 0-3.4-5.8-4.4-9.8-8.4-3.6-3.6-5.8-8.2-5.8-13.6C6.5 21.4 15 13 25.4 13c2 0 3.8.3 5.3.3 2.7 0 4.1-1.2 4.1-3.1 0-1.1-.5-2.5-.5-4 0-3.4 2.9-6.2 6.4-6.2S47 2.9 47 6.4c0 3.7-2.9 5.4-5.1 6.2-1.8.6-3.2 1.4-3.2 3.2C38.7 19.2 44.9 22.5 44.9 32zM42.9 82.2c0-9.9-7.3-17.9-17.2-17.9s-17.2 8-17.2 17.9c0 9.8 7.3 17.9 17.2 17.9S42.9 92 42.9 82.2zM37 31.8c0-6.3-5.1-11.5-11.3-11.5s-11.3 5.2-11.3 11.5S5.1 38.1 11.3 31.8z')

Expand Down
72 changes: 24 additions & 48 deletions src/components/Experience.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import {Badge} from "./ui/badge";
import {motion} from "motion/react";
import {ImageWithFallback} from "./figma/ImageWithFallback";

interface Position {
company: string;
company_url: string;
companyLogo: string;
organization: {
name: string,
url: string,
logo: string
},
position: string;
date_start: string;
date_end?: string;
location: string;
period: {
start: string,
end?: string
}
location: {
city: string
country: string
}
description: string;
achievements: string[];
responsibilities: string[];
technologies: string[];
}

Expand All @@ -23,10 +29,8 @@ interface ExperienceProps {
}
}


// Function to extract and format start date from duration string
function formatTimelineDate(position: Position): { year: number; month: string } {
const start_date_obj = new Date(position.date_start);
const start_date_obj = new Date(position.period.start);
const month_index = start_date_obj.getMonth();

const month_lists = [
Expand Down Expand Up @@ -157,16 +161,13 @@ export function Experience({data}: ExperienceProps) {
viewport={{once: true}}
transition={{duration: 0.6}}
>
<Badge variant="outline" className="mb-4">Experience</Badge>
<h2 className="mb-6">{data.subtitle}</h2>
<p className="text-lg text-muted-foreground max-w-3xl mx-auto">
{data.description}
</p>
</motion.div>

{/* Timeline Container */}
<div className="relative">
{/* Vertical Timeline Line - Hidden on mobile */}
<div className="absolute left-8 top-0 bottom-0 w-0.5 bg-border overflow-hidden hidden md:block">
<motion.div
className="w-full bg-gradient-to-b from-primary via-primary to-primary/60"
Expand All @@ -177,7 +178,6 @@ export function Experience({data}: ExperienceProps) {
/>
</div>

{/* Timeline Items */}
<motion.div
className="space-y-8"
variants={containerVariants}
Expand All @@ -194,7 +194,6 @@ export function Experience({data}: ExperienceProps) {
variants={cardVariants}
className="relative pl-0 md:pl-24"
>
{/* Timeline Date - Hidden on mobile */}
<motion.div
className="absolute left-0 top-6 flex flex-col items-center text-center min-w-16 hidden md:flex"
variants={timelineDotVariants}
Expand All @@ -207,19 +206,13 @@ export function Experience({data}: ExperienceProps) {
className="text-sm font-semibold text-foreground bg-background px-2 py-1 rounded">
{timelineDate.month}
</div>
{/* Connection dot */}
<div
className="w-3 h-3 bg-primary rounded-full border-2 border-background shadow-sm mt-2"/>
</motion.div>

{/* Experience Card */}
<div
className="bg-white border border-gray-200 rounded-xl p-6 shadow-sm hover:shadow-md transition-all duration-300 relative">
{/* Card Connector Line */}
<div
className="absolute left-0 top-9 w-6 h-0.5 bg-border -translate-x-full hidden md:block"></div>

{/* Location in top right */}
<motion.div
className="absolute top-4 right-4"
initial={{opacity: 0, x: 20}}
Expand All @@ -228,12 +221,11 @@ export function Experience({data}: ExperienceProps) {
transition={{duration: 0.4, delay: 0.3}}
>
<p className="text-xs text-gray-500 bg-gray-50 px-2 py-1 rounded-md">
{exp.location}
{exp.location.city}, {exp.location.country}
</p>
</motion.div>

{/* Header with company logo and title */}
<div className="flex items-start gap-4 mb-4 pr-20">
<div className="flex items-start gap-4 pr-20">
<motion.div
className="w-14 h-14 rounded-xl flex items-center justify-center flex-shrink-0 border-2 border-gray-200 overflow-hidden bg-white"
initial={{opacity: 0, scale: 0.5, rotate: -10}}
Expand All @@ -242,8 +234,8 @@ export function Experience({data}: ExperienceProps) {
transition={{duration: 0.4, delay: 0.2}}
>
<ImageWithFallback
src={exp.companyLogo}
alt={`${exp.company} logo`}
src={exp.organization.logo}
alt={`${exp.organization.name} logo`}
className="w-full h-full object-cover"
/>
</motion.div>
Expand All @@ -253,46 +245,39 @@ export function Experience({data}: ExperienceProps) {
{exp.position}
</h3>
<p className="text-lg text-primary font-medium mb-2">
<a href={exp.company_url} target="_blank">
{exp.company}
<a href={exp.organization.url} target="_blank">
{exp.organization.name}
</a>
</p>
</div>
</div>

{/* Description and achievements as bullet points */}
<motion.div
className="space-y-3 mb-6"
variants={bulletsContainerVariants}
initial="hidden"
whileInView="visible"
viewport={{once: true}}
>
{/* Main description as first bullet */}
<motion.div
className="flex items-start gap-3"
variants={bulletVariants}
>
<div
className="w-2 h-2 bg-primary rounded-full mt-2 flex-shrink-0"></div>
<p className="text-gray-700 leading-relaxed">{exp.description}</p>
</motion.div>

{/* Achievements as additional bullets */}
{exp.achievements.map((achievement, achIndex) => (
{exp.responsibilities.map((responsibility, responsibility_index) => (
<motion.div
key={achIndex}
key={responsibility_index}
className="flex items-start gap-3"
variants={bulletVariants}
>
<div
className="w-2 h-2 bg-primary/70 rounded-full mt-2 flex-shrink-0"></div>
<p className="text-gray-700 leading-relaxed">{achievement}</p>
<p className="text-gray-700 leading-relaxed">{responsibility}</p>
</motion.div>
))}
</motion.div>

{/* Technology badges */}
<motion.div
className="flex flex-wrap gap-2"
variants={badgeContainerVariants}
Expand All @@ -315,15 +300,6 @@ export function Experience({data}: ExperienceProps) {
);
})}
</motion.div>

{/* Timeline End Marker - Hidden on mobile */}
<motion.div
className="absolute left-6 -bottom-2 w-4 h-4 bg-gradient-to-br from-primary/60 to-primary/30 rounded-full border-4 border-background shadow-sm hidden md:block"
initial={{scale: 0, opacity: 0}}
whileInView={{scale: 1, opacity: 1}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 1.5}}
/>
</div>
</div>
</section>
Expand Down
10 changes: 3 additions & 7 deletions src/components/Hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import HeroProfileImage from './HeroProfileImage/HeroProfileImage';
interface HeroProps {
data: {
title: string;
availabilityStatus: string;
description: string;
summary: string;
profileImage: string;
socialLinks: {
github: string;
Expand Down Expand Up @@ -59,10 +58,7 @@ export function Hero({ data }: HeroProps) {
animate="visible"
>
<motion.div className="space-y-2" variants={itemVariants}>
<Badge variant="secondary" className="mb-4 mt-8 md:mt-0">
{data.availabilityStatus}
</Badge>
<h1 className="text-4xl lg:text-5xl xl:text-6xl tracking-tight">
<h1 className="text-4xl lg:text-5xl xl:text-6xl tracking-tight mb-4 mt-8 md:mt-0">
{data.title.split(' ').slice(0, 2).join(' ')}
<span className="block text-primary">{data.title.split(' ').slice(2).join(' ')}</span>
</h1>
Expand All @@ -72,7 +68,7 @@ export function Hero({ data }: HeroProps) {
className="text-lg text-muted-foreground max-w-lg"
variants={itemVariants}
>
{data.description}
{data.summary}
</motion.p>

<motion.div
Expand Down
Loading