fix(resume): simplified React code

This commit is contained in:
2026-03-24 00:36:39 +01:00
parent 21c4f321f3
commit 1052dac49c
+81 -107
View File
@@ -1,31 +1,23 @@
"use client"; "use client";
import { useEffect, useState } from "react"; import { useEffect } from "react";
import BackToMC from "@/components/mission-control/BackToMC"; import BackToMC from "@/components/mission-control/BackToMC";
export default function ResumeBuilderPage() { export default function ResumeBuilderPage() {
const [isClient, setIsClient] = useState(false);
useEffect(() => { useEffect(() => {
setIsClient(true); // Load the resume script after component mounts
const script = document.createElement("script");
script.src = "/resume-builder.js";
script.async = true;
document.body.appendChild(script);
}, []); }, []);
if (!isClient) {
return ( return (
<div className="min-h-screen bg-slate-950 text-white"> <div className="min-h-screen bg-slate-200">
<BackToMC />
<div className="p-8 text-center">
<p className="text-slate-400">Loading Resume Builder...</p>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-slate-200 text-white">
<BackToMC /> <BackToMC />
<style jsx global>{` {/* Include the styles inline */}
<style>{`
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap');
:root { :root {
@@ -34,14 +26,6 @@ export default function ResumeBuilderPage() {
--accent-blue: #2563eb; --accent-blue: #2563eb;
} }
body {
font-family: 'Roboto', sans-serif;
background-color: #e5e7eb;
-webkit-print-color-adjust: exact;
margin: 0;
padding: 0;
}
.resume-page { .resume-page {
width: 850px; width: 850px;
min-height: 1100px; min-height: 1100px;
@@ -171,17 +155,17 @@ export default function ResumeBuilderPage() {
`}</style> `}</style>
{/* Toolbar */} {/* 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="sticky top-0 z-50 bg-gray-800 px-6 py-3 flex items-center justify-between">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<h1 className="font-bold text-lg text-blue-400">Haitham's Resume Manager</h1> <h1 className="font-bold text-lg text-blue-400">Haitham&apos;s Resume Manager</h1>
<select id="resumeSelect" className="bg-gray-700 text-white px-3 py-1 rounded border border-gray-600 outline-none"> <select id="resumeSelect" className="bg-gray-700 text-white px-3 py-1 rounded border border-gray-600 outline-none">
</select> </select>
</div> </div>
<div className="flex gap-2"> <div className="flex gap-2">
<button onClick={createNewProfile} className="bg-blue-600 hover:bg-blue-700 px-4 py-1 rounded text-sm font-medium transition">New Version</button> <button id="btnNew" className="bg-blue-600 hover:bg-blue-700 px-4 py-1 rounded text-sm font-medium transition">New Version</button>
<button onClick={saveCurrentProfile} className="bg-green-600 hover:bg-green-700 px-4 py-1 rounded text-sm font-medium transition">Save Changes</button> <button id="btnSave" className="bg-green-600 hover:bg-green-700 px-4 py-1 rounded text-sm font-medium transition">Save Changes</button>
<button onClick={() => window.print()} className="bg-gray-600 hover:bg-gray-700 px-4 py-1 rounded text-sm font-medium transition">Print to PDF</button> <button id="btnPrint" className="bg-gray-600 hover:bg-gray-700 px-4 py-1 rounded text-sm font-medium transition">Print to PDF</button>
<button onClick={deleteProfile} className="bg-red-600 hover:bg-red-700 px-4 py-1 rounded text-sm font-medium transition">Delete</button> <button id="btnDelete" className="bg-red-600 hover:bg-red-700 px-4 py-1 rounded text-sm font-medium transition">Delete</button>
</div> </div>
</div> </div>
@@ -189,10 +173,10 @@ export default function ResumeBuilderPage() {
<div className="resume-page mt-20"> <div className="resume-page mt-20">
{/* Sidebar */} {/* Sidebar */}
<div className="sidebar"> <div className="sidebar">
<div className="circle-frame" onClick={() => document.getElementById('photoInput')?.click()}> <div className="circle-frame" id="photoFrame">
<img id="userPhoto" src="Resume.jpg" alt="Haitham Khalifa" /> <img id="userPhoto" src="Resume.jpg" alt="Haitham Khalifa" />
<div className="photo-edit-overlay no-print">EDIT PHOTO</div> <div className="photo-edit-overlay no-print">EDIT PHOTO</div>
<input type="file" id="photoInput" className="hidden" accept="image/*" onChange={handlePhotoUpload} /> <input type="file" id="photoInput" className="hidden" accept="image/*" />
</div> </div>
<h1 id="userName" contentEditable suppressContentEditableWarning className="text-2xl font-black text-center text-gray-900 mb-8 tracking-tighter uppercase leading-none">HAITHAM KHALIFA</h1> <h1 id="userName" contentEditable suppressContentEditableWarning className="text-2xl font-black text-center text-gray-900 mb-8 tracking-tighter uppercase leading-none">HAITHAM KHALIFA</h1>
@@ -337,49 +321,53 @@ export default function ResumeBuilderPage() {
</div> </div>
</div> </div>
{/* JavaScript */}
<script dangerouslySetInnerHTML={{ __html: ` <script dangerouslySetInnerHTML={{ __html: `
// Data Structure
let resumes = JSON.parse(localStorage.getItem('haitham_resumes_v2') || '{}'); let resumes = JSON.parse(localStorage.getItem('haitham_resumes_v2') || '{}');
let currentProfileName = localStorage.getItem('haitham_current_profile_v2') || 'Main Resume'; let currentProfileName = localStorage.getItem('haitham_current_profile_v2') || 'Main Resume';
const initialContent = { const getEl = (id) => document.getElementById(id);
name: "HAITHAM KHALIFA",
photo: "Resume.jpg",
phone: "+34 614 821 331",
email: "Haitham@Khalifa.se",
linkedin: "linkedin.com/in/haithamekhalifa/",
location: "Málaga, Spain",
about: document.getElementById('aboutMe')?.innerText || '',
education: document.getElementById('educationList')?.innerHTML || '',
skills: document.getElementById('skillsList')?.innerHTML || '',
language: document.getElementById('languageList')?.innerHTML || '',
experience: document.getElementById('experienceContainer')?.innerHTML || ''
};
function init() { const getInitialContent = () => ({
name: getEl('userName')?.innerText || '',
photo: getEl('userPhoto')?.src || '',
phone: getEl('userPhone')?.innerText || '',
email: getEl('userEmail')?.innerText || '',
linkedin: getEl('userLinkedin')?.innerText || '',
location: getEl('userLocation')?.innerText || '',
about: getEl('aboutMe')?.innerText || '',
education: getEl('educationList')?.innerHTML || '',
skills: getEl('skillsList')?.innerHTML || '',
language: getEl('languageList')?.innerHTML || '',
experience: getEl('experienceContainer')?.innerHTML || ''
});
const init = () => {
if (Object.keys(resumes).length === 0) { if (Object.keys(resumes).length === 0) {
resumes['Main Resume'] = { ...initialContent }; resumes['Main Resume'] = getInitialContent();
saveToDisk(); saveToDisk();
} }
updateSelect(); updateSelect();
loadProfile(currentProfileName); loadProfile(currentProfileName);
} };
function handlePhotoUpload(input) { const handlePhotoUpload = (input) => {
const file = input.files[0]; const file = input.files[0];
if (file) { if (file) {
const reader = new FileReader(); const reader = new FileReader();
reader.onload = function(e) { reader.onload = (e) => {
const base64Image = e.target.result; getEl('userPhoto').src = e.target.result;
document.getElementById('userPhoto').src = base64Image;
saveCurrentProfile(); saveCurrentProfile();
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
} }
} };
function updateSelect() { getEl('photoInput')?.addEventListener('change', (e) => handlePhotoUpload(e.target));
const select = document.getElementById('resumeSelect'); getEl('photoFrame')?.addEventListener('click', () => getEl('photoInput')?.click());
const updateSelect = () => {
const select = getEl('resumeSelect');
if (!select) return; if (!select) return;
select.innerHTML = ''; select.innerHTML = '';
Object.keys(resumes).forEach(name => { Object.keys(resumes).forEach(name => {
@@ -389,47 +377,35 @@ export default function ResumeBuilderPage() {
if (name === currentProfileName) opt.selected = true; if (name === currentProfileName) opt.selected = true;
select.appendChild(opt); select.appendChild(opt);
}); });
} };
function loadProfile(name) { const loadProfile = (name) => {
const p = resumes[name] || resumes[Object.keys(resumes)[0]]; const p = resumes[name] || resumes[Object.keys(resumes)[0]];
if (!p) return; if (!p) return;
currentProfileName = name; currentProfileName = name;
localStorage.setItem('haitham_current_profile_v2', name); localStorage.setItem('haitham_current_profile_v2', name);
document.getElementById('userName').innerText = p.name || ''; getEl('userName').innerText = p.name || '';
document.getElementById('userPhoto').src = p.photo || "Resume.jpg"; getEl('userPhoto').src = p.photo || 'Resume.jpg';
document.getElementById('userPhone').innerText = p.phone || ''; getEl('userPhone').innerText = p.phone || '';
document.getElementById('userEmail').innerText = p.email || ''; getEl('userEmail').innerText = p.email || '';
document.getElementById('userLinkedin').innerText = p.linkedin || ''; getEl('userLinkedin').innerText = p.linkedin || '';
document.getElementById('userLocation').innerText = p.location || ''; getEl('userLocation').innerText = p.location || '';
document.getElementById('aboutMe').innerText = p.about || ''; getEl('aboutMe').innerText = p.about || '';
document.getElementById('educationList').innerHTML = p.education || ''; getEl('educationList').innerHTML = p.education || '';
document.getElementById('skillsList').innerHTML = p.skills || ''; getEl('skillsList').innerHTML = p.skills || '';
document.getElementById('languageList').innerHTML = p.language || ''; getEl('languageList').innerHTML = p.language || '';
document.getElementById('experienceContainer').innerHTML = p.experience || ''; getEl('experienceContainer').innerHTML = p.experience || '';
}
function saveCurrentProfile() {
resumes[currentProfileName] = {
name: document.getElementById('userName')?.innerText || '',
photo: document.getElementById('userPhoto')?.src || '',
phone: document.getElementById('userPhone')?.innerText || '',
email: document.getElementById('userEmail')?.innerText || '',
linkedin: document.getElementById('userLinkedin')?.innerText || '',
location: document.getElementById('userLocation')?.innerText || '',
about: document.getElementById('aboutMe')?.innerText || '',
education: document.getElementById('educationList')?.innerHTML || '',
skills: document.getElementById('skillsList')?.innerHTML || '',
language: document.getElementById('languageList')?.innerHTML || '',
experience: document.getElementById('experienceContainer')?.innerHTML || ''
}; };
const saveCurrentProfile = () => {
resumes[currentProfileName] = getInitialContent();
saveToDisk(); saveToDisk();
showStatus('Changes Saved'); showStatus('Changes Saved');
} };
function createNewProfile() { const createNewProfile = () => {
const name = prompt("Name this version (e.g., Geely IT Manager):"); const name = prompt('Name this version (e.g., Geely IT Manager):');
if (name && !resumes[name]) { if (name && !resumes[name]) {
resumes[name] = { ...resumes[currentProfileName] }; resumes[name] = { ...resumes[currentProfileName] };
currentProfileName = name; currentProfileName = name;
@@ -437,12 +413,12 @@ export default function ResumeBuilderPage() {
updateSelect(); updateSelect();
loadProfile(name); loadProfile(name);
} else if (resumes[name]) { } else if (resumes[name]) {
alert("This name already exists."); alert('This name already exists.');
}
} }
};
function deleteProfile() { const deleteProfile = () => {
if (Object.keys(resumes).length <= 1) return alert("You need at least one profile."); if (Object.keys(resumes).length <= 1) return alert('You need at least one profile.');
if (confirm('Delete "' + currentProfileName + '"?')) { if (confirm('Delete "' + currentProfileName + '"?')) {
delete resumes[currentProfileName]; delete resumes[currentProfileName];
currentProfileName = Object.keys(resumes)[0]; currentProfileName = Object.keys(resumes)[0];
@@ -450,14 +426,14 @@ export default function ResumeBuilderPage() {
updateSelect(); updateSelect();
loadProfile(currentProfileName); loadProfile(currentProfileName);
} }
} };
function saveToDisk() { const saveToDisk = () => {
localStorage.setItem('haitham_resumes_v2', JSON.stringify(resumes)); localStorage.setItem('haitham_resumes_v2', JSON.stringify(resumes));
} };
function showStatus(msg) { const showStatus = (msg) => {
const btn = document.querySelector('button[onclick="saveCurrentProfile()"]'); const btn = getEl('btnSave');
if (!btn) return; if (!btn) return;
const original = btn.textContent; const original = btn.textContent;
btn.textContent = msg; btn.textContent = msg;
@@ -466,17 +442,15 @@ export default function ResumeBuilderPage() {
btn.textContent = original; btn.textContent = original;
btn.style.backgroundColor = ''; btn.style.backgroundColor = '';
}, 2000); }, 2000);
} };
document.addEventListener('DOMContentLoaded', function() { getEl('btnNew')?.addEventListener('click', createNewProfile);
const select = document.getElementById('resumeSelect'); getEl('btnSave')?.addEventListener('click', saveCurrentProfile);
if (select) { getEl('btnDelete')?.addEventListener('click', deleteProfile);
select.addEventListener('change', (e) => { getEl('btnPrint')?.addEventListener('click', () => window.print());
loadProfile(e.target.value); getEl('resumeSelect')?.addEventListener('change', (e) => loadProfile(e.target.value));
});
} document.addEventListener('DOMContentLoaded', init);
init();
});
`}} /> `}} />
</div> </div>
); );