abbd6d2940
- Circle photo frame with placeholder - Name in sidebar and main header - Contact with icons in gray circles - About Me section instead of Profile - Experience with border-left dots - Location field in experience - Education with location - Skills and Languages as bullet lists - Version manager with save/load - Print to PDF button
672 lines
26 KiB
TypeScript
672 lines
26 KiB
TypeScript
"use client";
|
||
|
||
import { useState } from "react";
|
||
import BackToMC from "@/components/mission-control/BackToMC";
|
||
|
||
interface Experience {
|
||
id: string;
|
||
title: string;
|
||
company: string;
|
||
period: string;
|
||
location: string;
|
||
description: string;
|
||
}
|
||
|
||
interface Education {
|
||
id: string;
|
||
degree: string;
|
||
school: string;
|
||
year: string;
|
||
location: string;
|
||
}
|
||
|
||
interface Skill {
|
||
id: string;
|
||
name: string;
|
||
}
|
||
|
||
export default function ResumeBuilderPage() {
|
||
const [activeTab, setActiveTab] = useState<"edit" | "preview">("edit");
|
||
|
||
// Personal Info
|
||
const [personalInfo, setPersonalInfo] = useState({
|
||
name: "HAITHAM KHALIFA",
|
||
title: "Senior Full-Stack Developer & Entrepreneur",
|
||
email: "Haitham@Khalifa.se",
|
||
phone: "+34 614 821 331",
|
||
location: "Málaga, Spain",
|
||
linkedin: "linkedin.com/in/haithamekhalifa/",
|
||
website: "haithamkhalifa.com",
|
||
summary: "A results-oriented leader with 15+ years of experience in driving e-commerce growth, optimizing online platforms, and implementing successful digital marketing strategies. Proven ability to increase online sales, improve customer engagement, and manage cross-functional teams to achieve business objectives."
|
||
});
|
||
|
||
// Experience
|
||
const [experiences, setExperiences] = useState<Experience[]>([
|
||
{
|
||
id: "1",
|
||
title: "Product Owner & IT Consultant",
|
||
company: "Protein.com",
|
||
period: "2020-2025",
|
||
location: "Sweden, Malmö",
|
||
description: "• Managed product backlog to drive e-commerce sales.\n• Developed/executed SEO strategies (+20% organic traffic).\n• Implemented/managed MarTech for e-commerce performance.\n• Drove Agile practices.\n• Utilized data analytics to optimize e-commerce growth.\n• Managed product data feeds."
|
||
}
|
||
]);
|
||
|
||
// Education
|
||
const [education, setEducation] = useState<Education[]>([
|
||
{
|
||
id: "1",
|
||
degree: "Intensive Software Development Academy",
|
||
school: "Lund University",
|
||
year: "2019",
|
||
location: "Sweden, Lund"
|
||
},
|
||
{
|
||
id: "2",
|
||
degree: "Bachelor of Computer Science",
|
||
school: "Modern Academy Maadi",
|
||
year: "2001 - 2006",
|
||
location: "Egypt, Cairo"
|
||
}
|
||
]);
|
||
|
||
// Skills
|
||
const [skills, setSkills] = useState<Skill[]>([
|
||
{ id: "1", name: "E-commerce Strategy" },
|
||
{ id: "2", name: "Digital Marketing" },
|
||
{ id: "3", name: "SEO/SEM" },
|
||
{ id: "4", name: "Product Ownership" },
|
||
{ id: "5", name: "Team Leadership" }
|
||
]);
|
||
|
||
// Languages
|
||
const [languages, setLanguages] = useState([
|
||
{ id: "1", name: "Arabic" },
|
||
{ id: "2", name: "English" },
|
||
{ id: "3", name: "Swedish" }
|
||
]);
|
||
|
||
// Versions
|
||
const [versions, setVersions] = useState<string[]>(["Default"]);
|
||
const [currentVersion, setCurrentVersion] = useState("Default");
|
||
|
||
// CRUD operations
|
||
const addExperience = () => {
|
||
setExperiences([...experiences, {
|
||
id: Date.now().toString(),
|
||
title: "New Position",
|
||
company: "Company",
|
||
period: "2023-Present",
|
||
location: "City, Country",
|
||
description: "• Key responsibility.\n• Achievement."
|
||
}]);
|
||
};
|
||
|
||
const removeExperience = (id: string) => {
|
||
setExperiences(experiences.filter(e => e.id !== id));
|
||
};
|
||
|
||
const updateExperience = (id: string, field: keyof Experience, value: string) => {
|
||
setExperiences(experiences.map(e => e.id === id ? { ...e, [field]: value } : e));
|
||
};
|
||
|
||
const addEducation = () => {
|
||
setEducation([...education, {
|
||
id: Date.now().toString(),
|
||
degree: "New Degree",
|
||
school: "University",
|
||
year: "2020 - 2024",
|
||
location: "City, Country"
|
||
}]);
|
||
};
|
||
|
||
const removeEducation = (id: string) => {
|
||
setEducation(education.filter(e => e.id !== id));
|
||
};
|
||
|
||
const updateEducation = (id: string, field: keyof Education, value: string) => {
|
||
setEducation(education.map(e => e.id === id ? { ...e, [field]: value } : e));
|
||
};
|
||
|
||
const addSkill = () => {
|
||
setSkills([...skills, { id: Date.now().toString(), name: "New Skill" }]);
|
||
};
|
||
|
||
const removeSkill = (id: string) => {
|
||
setSkills(skills.filter(s => s.id !== id));
|
||
};
|
||
|
||
const updateSkill = (id: string, name: string) => {
|
||
setSkills(skills.map(s => s.id === id ? { ...s, name } : s));
|
||
};
|
||
|
||
const addLanguage = () => {
|
||
setLanguages([...languages, { id: Date.now().toString(), name: "New Language" }]);
|
||
};
|
||
|
||
const removeLanguage = (id: string) => {
|
||
setLanguages(languages.filter(l => l.id !== id));
|
||
};
|
||
|
||
const updateLanguage = (id: string, name: string) => {
|
||
setLanguages(languages.map(l => l.id === id ? { ...l, name } : l));
|
||
};
|
||
|
||
const saveVersion = () => {
|
||
const name = prompt("Enter version name:", `Resume - ${currentVersion}`);
|
||
if (name && !versions.includes(name)) {
|
||
setVersions([...versions, name]);
|
||
setCurrentVersion(name);
|
||
}
|
||
};
|
||
|
||
const loadVersion = (version: string) => {
|
||
setCurrentVersion(version);
|
||
};
|
||
|
||
const handlePrint = () => {
|
||
window.print();
|
||
};
|
||
|
||
return (
|
||
<div className="min-h-screen bg-slate-950 text-white">
|
||
<BackToMC />
|
||
|
||
{/* Toolbar */}
|
||
<div className="sticky top-0 z-50 bg-slate-800 border-b border-slate-700 px-6 py-3 flex items-center justify-between">
|
||
<div className="flex items-center gap-4">
|
||
<h1 className="text-xl font-bold text-blue-400">Haitham's Resume Manager</h1>
|
||
<div className="flex gap-2">
|
||
<button
|
||
onClick={() => setActiveTab("edit")}
|
||
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||
activeTab === "edit" ? "bg-blue-600 text-white" : "bg-slate-700 text-slate-300"
|
||
}`}
|
||
>
|
||
Edit
|
||
</button>
|
||
<button
|
||
onClick={() => setActiveTab("preview")}
|
||
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||
activeTab === "preview" ? "bg-blue-600 text-white" : "bg-slate-700 text-slate-300"
|
||
}`}
|
||
>
|
||
Preview
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-3">
|
||
<select
|
||
value={currentVersion}
|
||
onChange={(e) => loadVersion(e.target.value)}
|
||
className="bg-slate-700 text-white px-3 py-2 rounded-lg text-sm"
|
||
>
|
||
{versions.map(v => (
|
||
<option key={v} value={v}>{v}</option>
|
||
))}
|
||
</select>
|
||
<button
|
||
onClick={saveVersion}
|
||
className="bg-green-600 hover:bg-green-500 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors"
|
||
>
|
||
💾 Save Version
|
||
</button>
|
||
<button
|
||
onClick={handlePrint}
|
||
className="bg-slate-600 hover:bg-slate-500 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors"
|
||
>
|
||
🖨️ Print to PDF
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{activeTab === "edit" ? (
|
||
<div className="p-6 max-w-4xl mx-auto space-y-6">
|
||
{/* Personal Info */}
|
||
<div className="bg-slate-800 rounded-xl p-6 border border-slate-700">
|
||
<h2 className="text-lg font-bold mb-4 text-blue-400">👤 Personal Information</h2>
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm text-slate-400 mb-1">Full Name</label>
|
||
<input
|
||
type="text"
|
||
value={personalInfo.name}
|
||
onChange={(e) => setPersonalInfo({...personalInfo, name: e.target.value})}
|
||
className="w-full bg-slate-700 border border-slate-600 rounded-lg px-3 py-2 text-white"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm text-slate-400 mb-1">Professional Title</label>
|
||
<input
|
||
type="text"
|
||
value={personalInfo.title}
|
||
onChange={(e) => setPersonalInfo({...personalInfo, title: e.target.value})}
|
||
className="w-full bg-slate-700 border border-slate-600 rounded-lg px-3 py-2 text-white"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm text-slate-400 mb-1">Email</label>
|
||
<input
|
||
type="email"
|
||
value={personalInfo.email}
|
||
onChange={(e) => setPersonalInfo({...personalInfo, email: e.target.value})}
|
||
className="w-full bg-slate-700 border border-slate-600 rounded-lg px-3 py-2 text-white"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm text-slate-400 mb-1">Phone</label>
|
||
<input
|
||
type="tel"
|
||
value={personalInfo.phone}
|
||
onChange={(e) => setPersonalInfo({...personalInfo, phone: e.target.value})}
|
||
className="w-full bg-slate-700 border border-slate-600 rounded-lg px-3 py-2 text-white"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm text-slate-400 mb-1">Location</label>
|
||
<input
|
||
type="text"
|
||
value={personalInfo.location}
|
||
onChange={(e) => setPersonalInfo({...personalInfo, location: e.target.value})}
|
||
className="w-full bg-slate-700 border border-slate-600 rounded-lg px-3 py-2 text-white"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm text-slate-400 mb-1">LinkedIn</label>
|
||
<input
|
||
type="text"
|
||
value={personalInfo.linkedin}
|
||
onChange={(e) => setPersonalInfo({...personalInfo, linkedin: e.target.value})}
|
||
className="w-full bg-slate-700 border border-slate-600 rounded-lg px-3 py-2 text-white"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<div className="mt-4">
|
||
<label className="block text-sm text-slate-400 mb-1">About Me</label>
|
||
<textarea
|
||
value={personalInfo.summary}
|
||
onChange={(e) => setPersonalInfo({...personalInfo, summary: e.target.value})}
|
||
rows={4}
|
||
className="w-full bg-slate-700 border border-slate-600 rounded-lg px-3 py-2 text-white"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Experience */}
|
||
<div className="bg-slate-800 rounded-xl p-6 border border-slate-700">
|
||
<div className="flex items-center justify-between mb-4">
|
||
<h2 className="text-lg font-bold text-blue-400">💼 Experience</h2>
|
||
<button
|
||
onClick={addExperience}
|
||
className="bg-blue-600 hover:bg-blue-500 text-white px-3 py-1 rounded-lg text-sm"
|
||
>
|
||
+ Add
|
||
</button>
|
||
</div>
|
||
<div className="space-y-4">
|
||
{experiences.map((exp) => (
|
||
<div key={exp.id} className="bg-slate-700/50 rounded-lg p-4 border border-slate-600">
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm text-slate-400 mb-1">Job Title</label>
|
||
<input
|
||
type="text"
|
||
value={exp.title}
|
||
onChange={(e) => updateExperience(exp.id, "title", e.target.value)}
|
||
className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm text-slate-400 mb-1">Company</label>
|
||
<input
|
||
type="text"
|
||
value={exp.company}
|
||
onChange={(e) => updateExperience(exp.id, "company", e.target.value)}
|
||
className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm text-slate-400 mb-1">Period</label>
|
||
<input
|
||
type="text"
|
||
value={exp.period}
|
||
onChange={(e) => updateExperience(exp.id, "period", e.target.value)}
|
||
className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm text-slate-400 mb-1">Location</label>
|
||
<input
|
||
type="text"
|
||
value={exp.location}
|
||
onChange={(e) => updateExperience(exp.id, "location", e.target.value)}
|
||
className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm"
|
||
/>
|
||
</div>
|
||
<div className="col-span-2">
|
||
<label className="block text-sm text-slate-400 mb-1">Description (one bullet per line, start with •)</label>
|
||
<textarea
|
||
value={exp.description}
|
||
onChange={(e) => updateExperience(exp.id, "description", e.target.value)}
|
||
rows={5}
|
||
className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<button
|
||
onClick={() => removeExperience(exp.id)}
|
||
className="mt-2 text-red-400 text-sm hover:text-red-300"
|
||
>
|
||
Remove
|
||
</button>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Education */}
|
||
<div className="bg-slate-800 rounded-xl p-6 border border-slate-700">
|
||
<div className="flex items-center justify-between mb-4">
|
||
<h2 className="text-lg font-bold text-blue-400">🎓 Education</h2>
|
||
<button
|
||
onClick={addEducation}
|
||
className="bg-blue-600 hover:bg-blue-500 text-white px-3 py-1 rounded-lg text-sm"
|
||
>
|
||
+ Add
|
||
</button>
|
||
</div>
|
||
<div className="space-y-4">
|
||
{education.map((edu) => (
|
||
<div key={edu.id} className="bg-slate-700/50 rounded-lg p-4 border border-slate-600">
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm text-slate-400 mb-1">Degree</label>
|
||
<input
|
||
type="text"
|
||
value={edu.degree}
|
||
onChange={(e) => updateEducation(edu.id, "degree", e.target.value)}
|
||
className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm text-slate-400 mb-1">School</label>
|
||
<input
|
||
type="text"
|
||
value={edu.school}
|
||
onChange={(e) => updateEducation(edu.id, "school", e.target.value)}
|
||
className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm text-slate-400 mb-1">Year</label>
|
||
<input
|
||
type="text"
|
||
value={edu.year}
|
||
onChange={(e) => updateEducation(edu.id, "year", e.target.value)}
|
||
className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm text-slate-400 mb-1">Location</label>
|
||
<input
|
||
type="text"
|
||
value={edu.location}
|
||
onChange={(e) => updateEducation(edu.id, "location", e.target.value)}
|
||
className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<button
|
||
onClick={() => removeEducation(edu.id)}
|
||
className="mt-2 text-red-400 text-sm hover:text-red-300"
|
||
>
|
||
Remove
|
||
</button>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Skills */}
|
||
<div className="bg-slate-800 rounded-xl p-6 border border-slate-700">
|
||
<div className="flex items-center justify-between mb-4">
|
||
<h2 className="text-lg font-bold text-blue-400">🛠️ Skills</h2>
|
||
<button
|
||
onClick={addSkill}
|
||
className="bg-blue-600 hover:bg-blue-500 text-white px-3 py-1 rounded-lg text-sm"
|
||
>
|
||
+ Add
|
||
</button>
|
||
</div>
|
||
<div className="flex flex-wrap gap-2">
|
||
{skills.map((skill) => (
|
||
<div key={skill.id} className="flex items-center gap-2 bg-slate-700 px-3 py-1 rounded-lg">
|
||
<input
|
||
type="text"
|
||
value={skill.name}
|
||
onChange={(e) => updateSkill(skill.id, e.target.value)}
|
||
className="bg-transparent border-none text-white text-sm w-32"
|
||
/>
|
||
<button
|
||
onClick={() => removeSkill(skill.id)}
|
||
className="text-slate-400 hover:text-red-400 text-lg"
|
||
>
|
||
×
|
||
</button>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Languages */}
|
||
<div className="bg-slate-800 rounded-xl p-6 border border-slate-700">
|
||
<div className="flex items-center justify-between mb-4">
|
||
<h2 className="text-lg font-bold text-blue-400">🌍 Languages</h2>
|
||
<button
|
||
onClick={addLanguage}
|
||
className="bg-blue-600 hover:bg-blue-500 text-white px-3 py-1 rounded-lg text-sm"
|
||
>
|
||
+ Add
|
||
</button>
|
||
</div>
|
||
<div className="flex flex-wrap gap-2">
|
||
{languages.map((lang) => (
|
||
<div key={lang.id} className="flex items-center gap-2 bg-slate-700 px-3 py-1 rounded-lg">
|
||
<input
|
||
type="text"
|
||
value={lang.name}
|
||
onChange={(e) => updateLanguage(lang.id, e.target.value)}
|
||
className="bg-transparent border-none text-white text-sm w-24"
|
||
/>
|
||
<button
|
||
onClick={() => removeLanguage(lang.id)}
|
||
className="text-slate-400 hover:text-red-400 text-lg"
|
||
>
|
||
×
|
||
</button>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
) : (
|
||
/* Resume Preview - Haitham's Design */
|
||
<div className="p-8">
|
||
<style>{`
|
||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
|
||
.resume-preview {
|
||
width: 850px;
|
||
min-height: 1100px;
|
||
background: white;
|
||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||
margin: 0 auto;
|
||
display: flex;
|
||
font-family: 'Inter', sans-serif;
|
||
}
|
||
.sidebar {
|
||
width: 280px;
|
||
background-color: #f3f3f3;
|
||
padding: 2.5rem 1.5rem;
|
||
}
|
||
.main-content {
|
||
flex: 1;
|
||
padding: 3.5rem 3rem;
|
||
background: white;
|
||
}
|
||
.circle-frame {
|
||
width: 176px;
|
||
height: 176px;
|
||
border-radius: 9999px;
|
||
border: 10px solid #e2e2e2;
|
||
overflow: hidden;
|
||
margin: 0 auto 2rem;
|
||
background: #d1d1d1;
|
||
}
|
||
.circle-frame img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
.section-header {
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
color: #1a1a1a;
|
||
border-bottom: 1px solid #ccc;
|
||
padding-bottom: 4px;
|
||
margin-bottom: 12px;
|
||
text-transform: capitalize;
|
||
}
|
||
.main-header {
|
||
font-size: 24px;
|
||
font-weight: 800;
|
||
letter-spacing: 0.05em;
|
||
color: #1a1a1a;
|
||
border-bottom: 2px solid #1a1a1a;
|
||
padding-bottom: 8px;
|
||
margin-bottom: 24px;
|
||
text-transform: uppercase;
|
||
}
|
||
.experience-item {
|
||
position: relative;
|
||
padding-left: 32px;
|
||
border-left: 2px solid #e5e5e5;
|
||
padding-bottom: 24px;
|
||
}
|
||
.experience-dot {
|
||
position: absolute;
|
||
left: -9px;
|
||
top: 4px;
|
||
width: 16px;
|
||
height: 16px;
|
||
background-color: #333;
|
||
border: 3px solid white;
|
||
border-radius: 9999px;
|
||
z-index: 10;
|
||
}
|
||
@media print {
|
||
body { background: white; }
|
||
.resume-preview { box-shadow: none; margin: 0; }
|
||
}
|
||
`}</style>
|
||
|
||
<div className="resume-preview">
|
||
{/* Sidebar */}
|
||
<div className="sidebar">
|
||
<div className="circle-frame">
|
||
<img src="Resume.jpg" alt={personalInfo.name} onError="this.src='https://via.placeholder.com/176?text=Photo'" />
|
||
</div>
|
||
|
||
<h1 className="text-2xl font-black text-center text-gray-900 mb-8 tracking-tighter uppercase leading-none">
|
||
{personalInfo.name}
|
||
</h1>
|
||
|
||
{/* Contact Section */}
|
||
<div className="space-y-3 text-[11px] text-gray-700 mb-10">
|
||
<div className="flex items-center gap-3">
|
||
<span className="w-5 h-5 flex items-center justify-center bg-gray-800 text-white rounded-full text-[10px]">📞</span>
|
||
<span className="font-medium">{personalInfo.phone}</span>
|
||
</div>
|
||
<div className="flex items-center gap-3">
|
||
<span className="w-5 h-5 flex items-center justify-center bg-gray-800 text-white rounded-full text-[10px]">✉️</span>
|
||
<span className="font-medium">{personalInfo.email}</span>
|
||
</div>
|
||
<div className="flex items-center gap-3">
|
||
<span className="w-5 h-5 flex items-center justify-center bg-gray-800 text-white rounded-full text-[10px] font-bold">in</span>
|
||
<span className="font-medium">{personalInfo.linkedin}</span>
|
||
</div>
|
||
<div className="flex items-center gap-3">
|
||
<span className="w-5 h-5 flex items-center justify-center bg-gray-800 text-white rounded-full text-[10px]">📍</span>
|
||
<span className="font-medium">{personalInfo.location}</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* About Me */}
|
||
<div className="mb-10">
|
||
<h2 className="section-header">About Me</h2>
|
||
<div className="text-[10px] leading-relaxed text-gray-600 font-medium text-justify">
|
||
{personalInfo.summary}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Skills Section */}
|
||
<div className="mb-10">
|
||
<h2 className="section-header">Skills</h2>
|
||
<div className="text-[11px] text-gray-700 space-y-1 font-medium">
|
||
{skills.map((skill) => (
|
||
<p key={skill.id}>• {skill.name}</p>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Languages Section */}
|
||
<div>
|
||
<h2 className="section-header">Language</h2>
|
||
<div className="text-[11px] text-gray-700 space-y-1 font-medium">
|
||
{languages.map((lang) => (
|
||
<p key={lang.id}>• {lang.name}</p>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Main Content */}
|
||
<div className="main-content">
|
||
<h1 className="main-header">{personalInfo.name}</h1>
|
||
|
||
{/* Experience */}
|
||
<div className="mb-10">
|
||
<h2 className="section-header">Experience</h2>
|
||
{experiences.map((exp) => (
|
||
<div key={exp.id} className="experience-item">
|
||
<div className="experience-dot" />
|
||
<div className="flex justify-between items-baseline mb-1">
|
||
<h3 className="text-lg font-bold text-gray-800 leading-none">{exp.title}</h3>
|
||
<span className="text-xs font-bold text-gray-400">{exp.period}</span>
|
||
</div>
|
||
<p className="text-sm font-bold text-blue-600 mb-3">{exp.company} | {exp.location}</p>
|
||
<div className="text-[11px] text-gray-600 space-y-1.5 leading-snug whitespace-pre-line">
|
||
{exp.description}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Education */}
|
||
<div>
|
||
<h2 className="section-header">Education</h2>
|
||
{education.map((edu) => (
|
||
<div key={edu.id} className="mb-4">
|
||
<p className="text-[11px] font-bold text-gray-800 leading-tight">{edu.degree}</p>
|
||
<p className="text-[10px] text-gray-600">{edu.school}</p>
|
||
<p className="text-[9px] text-gray-400 font-bold">{edu.location} - {edu.year}</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|