import React, { useState
} from 'react';
import { Plus, Edit2, Save, X,
Upload, Download, Trash2, User, GraduationCap, FileText, Briefcase, Award, Globe, Presentation
} from 'lucide-react';
const PortfolioWebsite
= () => {
const
[editMode, setEditMode] = useState(false);
const
[sections, setSections] = useState({
profile: {
name: 'Your
Name',
title:
'Professional Title',
bio: 'Your
professional bio goes here...',
image: null,
icon: User
},
education: {
title:
'Education',
content: [],
icon: GraduationCap
},
resume: {
title: 'Resume',
files: [],
icon: FileText
},
projects: {
title: 'Projects
Managed',
content: [],
icon: Briefcase
},
experience: {
title:
'Experience & Skills',
content: [],
icon: Award
},
presentations: {
title:
'Presentations',
files: [],
icon:
Presentation
},
memberships: {
title:
'Engineering Memberships',
content: [],
icon: Award
},
countries: {
title:
'Countries of Operation',
content: [],
icon: Globe
}
});
const
[customSections, setCustomSections]
= useState([]);
const
[newSectionName, setNewSectionName]
= useState('');
const
[showAddSection, setShowAddSection]
= useState(false);
const
handleProfileUpdate = (field, value) => {
setSections(prev => ({
...prev,
profile: { ...prev.profile, [field]: value }
}));
};
const
handleImageUpload = (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onloadend = () => {
handleProfileUpdate('image', reader.result);
};
reader.readAsDataURL(file);
}
};
const
addContentItem = (sectionKey)
=> {
setSections(prev => ({
...prev,
[sectionKey]: {
...prev[sectionKey],
content: [...prev[sectionKey].content,
'']
}
}));
};
const
updateContentItem = (sectionKey,
index, value) => {
setSections(prev => ({
...prev,
[sectionKey]: {
...prev[sectionKey],
content: prev[sectionKey].content.map((item, i)
=> i === index ? value : item)
}
}));
};
const
removeContentItem = (sectionKey,
index) => {
setSections(prev => ({
...prev,
[sectionKey]: {
...prev[sectionKey],
content: prev[sectionKey].content.filter((_, i) => i !== index)
}
}));
};
const
addCustomSection = () => {
if (newSectionName.trim()) {
setCustomSections(prev => [...prev, {
id: Date.now(),
title: newSectionName,
content: [],
files: []
}]);
setNewSectionName('');
setShowAddSection(false);
}
};
const
exportData = () => {
const data = {
sections,
customSections,
exportDate: new Date().toISOString()
};
const blob = new Blob([JSON.stringify(data, null, 2)], { type:
'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'portfolio-data.json';
a.click();
};
const
importData = (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (event) => {
try {
const data = JSON.parse(event.target.result);
if (data.sections) setSections(data.sections);
if (data.customSections) setCustomSections(data.customSections);
} catch
(error) {
alert('Error importing data. Please check the file format.');
}
};
reader.readAsText(file);
}
};
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100">
{/* Header */}
<header className="bg-white shadow-sm border-b border-slate-200">
<div className="max-w-6xl mx-auto px-4 py-4 flex
justify-between items-center">
<h1 className="text-2xl font-bold
text-slate-800">Professional Portfolio</h1>
<div className="flex gap-2">
<label className="cursor-pointer px-4 py-2 bg-slate-600
text-white rounded-lg hover:bg-slate-700 transition
flex items-center gap-2">
<Upload size={18} />
Import
<input type="file" accept=".json" onChange={importData} className="hidden" />
</label>
<button
onClick={exportData}
className="px-4 py-2 bg-slate-600 text-white rounded-lg hover:bg-slate-700 transition flex items-center
gap-2"
>
<Download size={18} />
Export
</button>
<button
onClick={() => setEditMode(!editMode)}
className={`px-4 py-2 rounded-lg transition flex items-center gap-2 ${
editMode
? 'bg-green-600 text-white hover:bg-green-700'
:
'bg-blue-600 text-white hover:bg-blue-700'
}`}
>
{editMode ?
<Save size={18} /> :
<Edit2 size={18} />}
{editMode ?
'Save' : 'Edit'}
</button>
</div>
</div>
</header>
<main className="max-w-6xl mx-auto px-4 py-8">
{/* Profile
Section */}
<div className="bg-white
rounded-xl shadow-md p-8 mb-6">
<div className="flex flex-col md:flex-row gap-8 items-start">
<div className="flex flex-col items-center">
<div className="w-40 h-40 rounded-full bg-slate-200 flex
items-center justify-center overflow-hidden">
{sections.profile.image ? (
<img src={sections.profile.image} alt="Profile" className="w-full h-full object-cover" />
) : (
<User size={64} className="text-slate-400"
/>
)}
</div>
{editMode && (
<label className="mt-4 px-4 py-2
bg-blue-600 text-white rounded-lg cursor-pointer
hover:bg-blue-700 transition">
Upload Photo
<input type="file" accept="image/*"
onChange={handleImageUpload}
className="hidden" />
</label>
)}
</div>
<div className="flex-1">
{editMode ?
(
<div className="space-y-4">
<input
type="text"
value={sections.profile.name}
onChange={(e) => handleProfileUpdate('name', e.target.value)}
className="w-full text-3xl font-bold border-b-2
border-blue-500 focus:outline-none"
placeholder="Your Name"
/>
<input
type="text"
value={sections.profile.title}
onChange={(e) => handleProfileUpdate('title', e.target.value)}
className="w-full text-xl text-slate-600 border-b
border-slate-300 focus:outline-none
focus:border-blue-500"
placeholder="Professional Title"
/>
<textarea
value={sections.profile.bio}
onChange={(e) => handleProfileUpdate('bio', e.target.value)}
className="w-full text-slate-700 border
border-slate-300 rounded-lg p-3 focus:outline-none focus:border-blue-500"
rows="4"
placeholder="Professional bio..."
/>
</div>
) : (
<div>
<h2 className="text-3xl font-bold
text-slate-800 mb-2">{sections.profile.name}</h2>
<p className="text-xl text-slate-600
mb-4">{sections.profile.title}</p>
<p className="text-slate-700
leading-relaxed">{sections.profile.bio}</p>
</div>
)}
</div>
</div>
</div>
{/* Content
Sections */}
<div className="grid md:grid-cols-2
gap-6">
{Object.entries(sections).filter(([key])
=> key !== 'profile').map(([key,
section]) => {
const Icon = section.icon;
return (
<div
key={key} className="bg-white
rounded-xl shadow-md p-6">
<div className="flex items-center
gap-3 mb-4">
<Icon size={24} className="text-blue-600"
/>
<h3 className="text-xl font-semibold
text-slate-800">{section.title}</h3>
</div>
{section.content ? (
<div className="space-y-3">
{section.content.map((item, index) => (
<div key={index} className="flex
gap-2">
{editMode ? (
<>
<textarea
value={item}
onChange={(e) => updateContentItem(key,
index, e.target.value)}
className="flex-1 border border-slate-300
rounded-lg p-2 focus:outline-none
focus:border-blue-500"
rows="2"
placeholder="Enter content..."
/>
<button
onClick={() => removeContentItem(key, index)}
className="px-3 py-2 bg-red-500
text-white rounded-lg hover:bg-red-600"
>
<Trash2 size={16} />
</button>
</>
) : (
<p className="text-slate-700">{item}</p>
)}
</div>
))}
{editMode && (
<button
onClick={() => addContentItem(key)}
className="w-full px-4 py-2 border-2
border-dashed border-slate-300 text-slate-600 rounded-lg
hover:border-blue-500 hover:text-blue-600
transition"
>
+ Add Item
</button>
)}
{!editMode && section.content.length === 0
&& (
<p className="text-slate-400
italic">No content added yet</p>
)}
</div>
) : null}
</div>
);
})}
{/* Custom
Sections */}
{customSections.map((section) => (
<div
key={section.id} className="bg-white
rounded-xl shadow-md p-6">
<h3 className="text-xl font-semibold text-slate-800
mb-4">{section.title}</h3>
<div className="space-y-3">
{section.content.map((item, index) => (
<div key={index} className="flex
gap-2">
{editMode ?
(
<>
<textarea
value={item}
onChange={(e) => {
const updated = [...customSections];
const sectionIndex =
updated.findIndex(s =>
s.id === section.id);
updated[sectionIndex].content[index]
= e.target.value;
setCustomSections(updated);
}}
className="flex-1 border border-slate-300
rounded-lg p-2 focus:outline-none
focus:border-blue-500"
rows="2"
placeholder="Enter content..."
/>
<button
onClick={() => {
const updated = [...customSections];
const sectionIndex =
updated.findIndex(s =>
s.id === section.id);
updated[sectionIndex].content.splice(index, 1);
setCustomSections(updated);
}}
className="px-3 py-2 bg-red-500
text-white rounded-lg hover:bg-red-600"
>
<Trash2 size={16} />
</button>
</>
)
: (
<p className="text-slate-700">{item}</p>
)}
</div>
))}
{editMode && (
<button
onClick={() => {
const updated = [...customSections];
const index = updated.findIndex(s
=> s.id === section.id);
updated[index].content.push('');
setCustomSections(updated);
}}
className="w-full px-4 py-2 border-2 border-dashed
border-slate-300 text-slate-600 rounded-lg hover:border-blue-500 hover:text-blue-600 transition"
>
+
Add Item
</button>
)}
{!editMode && section.content.length === 0
&& (
<p className="text-slate-400
italic">No content added yet</p>
)}
</div>
</div>
))}
</div>
{/* Add New
Section */}
{editMode && (
<div className="mt-6">
{showAddSection ?
(
<div className="bg-white
rounded-xl shadow-md p-6">
<div className="flex gap-4">
<input
type="text"
value={newSectionName}
onChange={(e) => setNewSectionName(e.target.value)}
placeholder="New section name..."
className="flex-1 border border-slate-300 rounded-lg px-4 py-2 focus:outline-none
focus:border-blue-500"
onKeyPress={(e) => e.key ===
'Enter' && addCustomSection()}
/>
<button
onClick={addCustomSection}
className="px-6 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition"
>
Add
</button>
<button
onClick={() => {
setShowAddSection(false);
setNewSectionName('');
}}
className="px-6 py-2 bg-slate-400 text-white rounded-lg hover:bg-slate-500 transition"
>
<X size={20} />
</button>
</div>
</div>
) : (
<button
onClick={() => setShowAddSection(true)}
className="w-full px-6 py-4 border-2 border-dashed
border-slate-300 text-slate-600 rounded-xl hover:border-blue-500
hover:text-blue-600 transition flex items-center justify-center gap-2"
>
<Plus size={24} />
Add
New Section
</button>
)}
</div>
)}
</main>
</div>
);
};
export default PortfolioWebsite;