Files
sitemente/app/mission-control/resume/page.tsx
T
horus abbd6d2940 feat(resume): full redesign matching Haitham's design
- 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
2026-03-24 00:27:13 +01:00

672 lines
26 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"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>
);
}