<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Panel | Mubarik Osman</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: { colors: { brand: { accent: '#4f46e5', dark: '#0f172a' } } }
}
}
</script>
</head>
<body class="bg-gray-100 text-slate-800 font-sans antialiased">
<div id="login-screen" class="fixed inset-0 z-50 flex items-center justify-center bg-gray-900 transition-opacity duration-500">
<div class="bg-white p-8 rounded-2xl shadow-2xl w-full max-w-md">
<div class="text-center mb-8">
<h1 class="text-3xl font-black text-gray-800">MUBARIK<span class="text-brand-accent">.</span></h1>
<p class="text-gray-500 text-sm mt-2">Admin Dashboard Login</p>
</div>
<form id="login-form" class="space-y-4">
<div>
<label class="block text-sm font-bold mb-2">Email</label>
<input type="email" id="login-email" class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-brand-accent focus:outline-none" placeholder="admin@example.com" required>
</div>
<div>
<label class="block text-sm font-bold mb-2">Password</label>
<input type="password" id="login-password" class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-brand-accent focus:outline-none" placeholder="••••••••" required>
</div>
<button type="submit" class="w-full bg-brand-accent text-white py-3 rounded-lg font-bold hover:bg-indigo-700 transition-colors shadow-lg">
<i class="fa-solid fa-lock mr-2"></i> Access Dashboard
</button>
</form>
<p id="login-error" class="text-red-500 text-sm text-center mt-4 hidden"></p>
</div>
</div>
<div id="dashboard-screen" class="hidden flex h-screen overflow-hidden">
<aside class="w-64 bg-brand-dark text-white flex flex-col shadow-xl z-20">
<div class="h-20 flex items-center justify-center border-b border-gray-800">
<h2 class="text-2xl font-bold tracking-tighter">ADMIN<span class="text-brand-accent">.</span></h2>
</div>
<nav class="flex-1 overflow-y-auto py-4">
<ul class="space-y-1 px-2">
<li><button onclick="switchTab('projects')" class="nav-btn w-full text-left px-4 py-3 rounded-lg hover:bg-gray-800 transition-colors flex items-center gap-3"><i class="fa-solid fa-briefcase w-5"></i> Projects</button></li>
<li><button onclick="switchTab('services')" class="nav-btn w-full text-left px-4 py-3 rounded-lg hover:bg-gray-800 transition-colors flex items-center gap-3"><i class="fa-solid fa-layer-group w-5"></i> Services</button></li>
<li><button onclick="switchTab('skills')" class="nav-btn w-full text-left px-4 py-3 rounded-lg hover:bg-gray-800 transition-colors flex items-center gap-3"><i class="fa-solid fa-bolt w-5"></i> Skills</button></li>
<li><button onclick="switchTab('experience')" class="nav-btn w-full text-left px-4 py-3 rounded-lg hover:bg-gray-800 transition-colors flex items-center gap-3"><i class="fa-solid fa-road w-5"></i> Journey</button></li>
<li><button onclick="switchTab('certificates')" class="nav-btn w-full text-left px-4 py-3 rounded-lg hover:bg-gray-800 transition-colors flex items-center gap-3"><i class="fa-solid fa-certificate w-5"></i> Certificates</button></li>
<li><button onclick="switchTab('stats')" class="nav-btn w-full text-left px-4 py-3 rounded-lg hover:bg-gray-800 transition-colors flex items-center gap-3"><i class="fa-solid fa-chart-simple w-5"></i> Stats</button></li>
<li><button onclick="switchTab('socials')" class="nav-btn w-full text-left px-4 py-3 rounded-lg hover:bg-gray-800 transition-colors flex items-center gap-3"><i class="fa-solid fa-share-nodes w-5"></i> Socials</button></li>
<li class="pt-4 mt-4 border-t border-gray-800">
<button onclick="switchTab('messages')" class="nav-btn w-full text-left px-4 py-3 rounded-lg hover:bg-gray-800 text-yellow-400 transition-colors flex items-center gap-3"><i class="fa-solid fa-envelope w-5"></i> Messages</button>
</li>
</ul>
</nav>
<div class="p-4 border-t border-gray-800">
<button id="logout-btn" class="w-full bg-red-600 hover:bg-red-700 text-white py-2 rounded-lg font-bold text-sm transition-colors">Logout</button>
</div>
</aside>
<main class="flex-1 overflow-y-auto bg-gray-100 p-8 relative">
<div id="tab-projects" class="tab-content hidden">
<div class="flex justify-between items-center mb-8">
<h2 class="text-3xl font-bold text-gray-800">Manage Projects</h2>
</div>
<div class="bg-white p-6 rounded-xl shadow-sm mb-8 border border-gray-200">
<h3 class="font-bold text-lg mb-4 text-brand-accent">Add New Project</h3>
<form onsubmit="handleCreate(event, 'projects')" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<input type="text" name="title" placeholder="Project Title (e.g., HantiKaab)" class="input-std" required>
<input type="text" name="type" placeholder="Type (e.g., SaaS, Mobile App)" class="input-std" required>
<input type="text" name="img" placeholder="Image URL (https://...)" class="input-std" required>
<input type="text" name="desc" placeholder="Short Description" class="input-std md:col-span-2" required>
<button type="submit" class="btn-primary md:col-span-2">Add Project</button>
</form>
</div>
<div id="list-projects" class="grid grid-cols-1 md:grid-cols-2 gap-6"></div>
</div>
<div id="tab-skills" class="tab-content hidden">
<h2 class="text-3xl font-bold text-gray-800 mb-8">Manage Skills</h2>
<div class="bg-white p-6 rounded-xl shadow-sm mb-8 border border-gray-200">
<h3 class="font-bold text-lg mb-4 text-brand-accent">Add New Skill</h3>
<form onsubmit="handleCreate(event, 'skills')" class="grid grid-cols-1 md:grid-cols-3 gap-4">
<input type="text" name="name" placeholder="Skill Name (e.g. React)" class="input-std" required>
<select name="cat" class="input-std" required>
<option value="frontend">Frontend</option>
<option value="backend">Backend</option>
<option value="design">Design</option>
<option value="soft">Soft Skill</option>
</select>
<input type="text" name="icon" placeholder="FontAwesome Class (e.g. fa-brands fa-react)" class="input-std" required>
<button type="submit" class="btn-primary md:col-span-3">Add Skill</button>
</form>
</div>
<div id="list-skills" class="flex flex-wrap gap-3"></div>
</div>
<div id="tab-services" class="tab-content hidden">
<h2 class="text-3xl font-bold text-gray-800 mb-8">Manage Services</h2>
<div class="bg-white p-6 rounded-xl shadow-sm mb-8 border border-gray-200">
<h3 class="font-bold text-lg mb-4 text-brand-accent">Add Service</h3>
<form onsubmit="handleCreate(event, 'services')" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<input type="text" name="title" placeholder="Service Title" class="input-std" required>
<input type="text" name="icon" placeholder="Icon (fa-solid fa-code)" class="input-std" required>
<input type="text" name="desc" placeholder="Description" class="input-std md:col-span-2" required>
<button type="submit" class="btn-primary md:col-span-2">Add Service</button>
</form>
</div>
<div id="list-services" class="grid grid-cols-1 md:grid-cols-2 gap-6"></div>
</div>
<div id="tab-experience" class="tab-content hidden">
<h2 class="text-3xl font-bold text-gray-800 mb-8">Manage Journey</h2>
<div class="bg-white p-6 rounded-xl shadow-sm mb-8 border border-gray-200">
<h3 class="font-bold text-lg mb-4 text-brand-accent">Add Experience</h3>
<form onsubmit="handleCreate(event, 'experience')" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<input type="text" name="year" placeholder="Year Range (e.g. 2023 - Present)" class="input-std" required>
<input type="text" name="title" placeholder="Job Title" class="input-std" required>
<input type="text" name="company" placeholder="Company Name" class="input-std" required>
<input type="text" name="desc" placeholder="Description" class="input-std md:col-span-2" required>
<button type="submit" class="btn-primary md:col-span-2">Add Entry</button>
</form>
</div>
<div id="list-experience" class="space-y-4"></div>
</div>
<div id="tab-messages" class="tab-content hidden">
<h2 class="text-3xl font-bold text-gray-800 mb-8">Client Messages</h2>
<div id="list-messages" class="space-y-4">
<p class="text-gray-500">Loading messages...</p>
</div>
</div>
<div id="tab-stats" class="tab-content hidden">
<h2 class="text-3xl font-bold text-gray-800 mb-8">Manage Stats</h2>
<div class="bg-white p-6 rounded-xl shadow-sm mb-8 border border-gray-200">
<h3 class="font-bold text-lg mb-4 text-brand-accent">Add Stat</h3>
<form onsubmit="handleCreate(event, 'stats')" class="grid grid-cols-1 md:grid-cols-3 gap-4">
<input type="number" name="count" placeholder="Number (e.g. 100)" class="input-std" required>
<input type="text" name="label" placeholder="Label (e.g. Projects)" class="input-std" required>
<input type="text" name="suffix" placeholder="Suffix (e.g. + or %)" class="input-std">
<button type="submit" class="btn-primary md:col-span-3">Add Stat</button>
</form>
</div>
<div id="list-stats" class="grid grid-cols-2 md:grid-cols-4 gap-4"></div>
</div>
<div id="tab-certificates" class="tab-content hidden">
<h2 class="text-3xl font-bold text-gray-800 mb-8">Manage Certificates</h2>
<div class="bg-white p-6 rounded-xl shadow-sm mb-8 border border-gray-200">
<h3 class="font-bold text-lg mb-4 text-brand-accent">Add Certificate</h3>
<form onsubmit="handleCreate(event, 'certificates')" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<input type="text" name="title" placeholder="Cert Title" class="input-std" required>
<input type="text" name="img" placeholder="Image URL" class="input-std" required>
<input type="text" name="desc" placeholder="Description" class="input-std md:col-span-2" required>
<button type="submit" class="btn-primary md:col-span-2">Add Certificate</button>
</form>
</div>
<div id="list-certificates" class="grid grid-cols-1 md:grid-cols-2 gap-6"></div>
</div>
<div id="tab-socials" class="tab-content hidden">
<h2 class="text-3xl font-bold text-gray-800 mb-8">Manage Socials</h2>
<div class="bg-white p-6 rounded-xl shadow-sm mb-8 border border-gray-200">
<h3 class="font-bold text-lg mb-4 text-brand-accent">Add Social Link</h3>
<form onsubmit="handleCreate(event, 'socials')" class="grid grid-cols-1 md:grid-cols-3 gap-4">
<input type="text" name="name" placeholder="Name (e.g. GitHub)" class="input-std" required>
<input type="text" name="url" placeholder="URL link" class="input-std" required>
<input type="text" name="icon" placeholder="FontAwesome Icon" class="input-std" required>
<button type="submit" class="btn-primary md:col-span-3">Add Link</button>
</form>
</div>
<div id="list-socials" class="grid grid-cols-2 md:grid-cols-4 gap-4"></div>
</div>
</main>
</div>
<style>
.input-std { @apply w-full px-4 py-3 rounded-lg border border-gray-200 focus:border-brand-accent focus:outline-none focus:ring-1 focus:ring-brand-accent text-sm; }
.btn-primary { @apply w-full bg-brand-accent text-white py-3 rounded-lg font-bold hover:bg-indigo-700 transition-colors shadow-md; }
.btn-delete { @apply text-red-500 hover:text-red-700 text-sm font-bold ml-auto; }
</style>
<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.6.1/firebase-app.js";
import { getAuth, signInWithEmailAndPassword, onAuthStateChanged, signOut } from "https://www.gstatic.com/firebasejs/9.6.1/firebase-auth.js";
import { getFirestore, collection, addDoc, getDocs, deleteDoc, doc, query, orderBy, onSnapshot } from "https://www.gstatic.com/firebasejs/9.6.1/firebase-firestore.js";
// --- 1. CONFIG (Same as Index) ---
const firebaseConfig = {
apiKey: "AIzaSyC7dQr5Ub5LP-UUq30N6Sn7X1JB_AZFLcQ",
authDomain: "portfalio1.firebaseapp.com",
projectId: "portfalio1",
storageBucket: "portfalio1.firebasestorage.app",
messagingSenderId: "806539469974",
appId: "1:806539469974:web:368f1711bdd2d09a5f8eb7",
measurementId: "G-CGFDMTH1N8"
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
// --- 2. AUTHENTICATION LOGIC ---
const loginScreen = document.getElementById('login-screen');
const dashboardScreen = document.getElementById('dashboard-screen');
const loginForm = document.getElementById('login-form');
const logoutBtn = document.getElementById('logout-btn');
onAuthStateChanged(auth, (user) => {
if (user) {
loginScreen.classList.add('hidden');
dashboardScreen.classList.remove('hidden');
// Load default tab
switchTab('projects');
} else {
loginScreen.classList.remove('hidden');
dashboardScreen.classList.add('hidden');
}
});
loginForm.addEventListener('submit', (e) => {
e.preventDefault();
const email = document.getElementById('login-email').value;
const password = document.getElementById('login-password').value;
const errorMsg = document.getElementById('login-error');
signInWithEmailAndPassword(auth, email, password)
.catch((error) => {
errorMsg.textContent = "Login Failed: " + error.message;
errorMsg.classList.remove('hidden');
});
});
logoutBtn.addEventListener('click', () => signOut(auth));
// --- 3. UI NAVIGATION ---
window.switchTab = (tabName) => {
// Hide all tabs
document.querySelectorAll('.tab-content').forEach(el => el.classList.add('hidden'));
// Show selected
document.getElementById(`tab-${tabName}`).classList.remove('hidden');
// Highlight sidebar
document.querySelectorAll('.nav-btn').forEach(btn => btn.classList.remove('bg-gray-800', 'border-l-4', 'border-brand-accent'));
// (Simple highlighting logic omitted for brevity, functionality works)
// Fetch data for this tab
loadCollectionData(tabName);
}
// --- 4. DATA HANDLING (CRUD) ---
// Generic Create
window.handleCreate = async (e, collectionName) => {
e.preventDefault();
const btn = e.target.querySelector('button');
const originalText = btn.innerText;
btn.innerText = "Saving...";
btn.disabled = true;
const formData = new FormData(e.target);
const data = Object.fromEntries(formData.entries());
// Add default color for services if missing
if(collectionName === 'services') {
data.from = '#4f46e5';
data.to = '#8b5cf6';
}
// Add Date for messages sorting
if(collectionName === 'messages') data.timestamp = new Date();
try {
await addDoc(collection(db, collectionName), data);
e.target.reset();
alert("Item added successfully!");
// No need to reload, real-time listener will handle it?
// Or just manual reload for simplicity in admin:
loadCollectionData(collectionName);
} catch (error) {
alert("Error: " + error.message);
} finally {
btn.innerText = originalText;
btn.disabled = false;
}
};
// Generic Delete
window.deleteItem = async (collectionName, id) => {
if(confirm("Are you sure you want to delete this item? This updates the live site.")) {
try {
await deleteDoc(doc(db, collectionName, id));
loadCollectionData(collectionName);
} catch(e) {
alert("Delete failed: " + e.message);
}
}
}
// --- 5. RENDER LOGIC PER COLLECTION ---
async function loadCollectionData(colName) {
const container = document.getElementById(`list-${colName}`);
container.innerHTML = '<div class="w-full text-center py-10"><i class="fa-solid fa-spinner fa-spin text-2xl text-brand-accent"></i></div>';
let q = collection(db, colName);
// Sort messages by date
if(colName === 'messages') q = query(collection(db, colName), orderBy('timestamp', 'desc'));
try {
const querySnapshot = await getDocs(q);
container.innerHTML = ''; // Clear loader
if (querySnapshot.empty) {
container.innerHTML = '<p class="text-gray-400 italic col-span-full">No items found in database.</p>';
return;
}
querySnapshot.forEach((doc) => {
const data = doc.data();
const id = doc.id;
const html = generateCardHTML(colName, data, id);
container.insertAdjacentHTML('beforeend', html);
});
} catch (e) {
container.innerHTML = `<p class="text-red-500">Error loading data: ${e.message}</p>`;
}
}
function generateCardHTML(type, data, id) {
// Helper for Delete Button
const delBtn = `<button onclick="deleteItem('${type}', '${id}')" class="absolute top-2 right-2 bg-red-100 text-red-600 p-2 rounded-full hover:bg-red-200 transition-colors shadow-sm"><i class="fa-solid fa-trash"></i></button>`;
if (type === 'projects') {
return `
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden relative group">
${delBtn}
<div class="h-32 bg-gray-100 overflow-hidden">
<img src="${data.img || data.image}" class="w-full h-full object-cover">
</div>
<div class="p-4">
<span class="text-xs font-bold text-brand-accent uppercase">${data.type}</span>
<h4 class="font-bold text-lg">${data.title}</h4>
<p class="text-xs text-gray-500 mt-1 line-clamp-2">${data.desc}</p>
</div>
</div>`;
}
if (type === 'skills') {
return `
<div class="bg-white px-4 py-2 rounded-lg border border-gray-200 shadow-sm flex items-center gap-3 relative pr-10">
<i class="${data.icon} text-brand-accent"></i>
<div>
<p class="font-bold text-sm">${data.name}</p>
<p class="text-[10px] text-gray-400 uppercase">${data.cat}</p>
</div>
<button onclick="deleteItem('${type}', '${id}')" class="text-red-400 hover:text-red-600 absolute right-3"><i class="fa-solid fa-times"></i></button>
</div>`;
}
if (type === 'services') {
return `
<div class="bg-white p-4 rounded-xl border border-gray-200 relative">
${delBtn}
<div class="flex items-center gap-3 mb-2">
<div class="w-8 h-8 rounded bg-indigo-50 flex items-center justify-center text-brand-accent"><i class="${data.icon}"></i></div>
<h4 class="font-bold">${data.title}</h4>
</div>
<p class="text-xs text-gray-500">${data.desc}</p>
</div>`;
}
if (type === 'experience') {
return `
<div class="bg-white p-4 rounded-xl border border-gray-200 relative flex gap-4 items-start">
<div class="flex-1">
<span class="text-xs font-bold bg-gray-100 px-2 py-1 rounded text-gray-600">${data.year}</span>
<h4 class="font-bold mt-1">${data.title}</h4>
<p class="text-sm text-brand-accent">${data.company}</p>
</div>
<button onclick="deleteItem('${type}', '${id}')" class="text-red-400 hover:text-red-600"><i class="fa-solid fa-trash"></i></button>
</div>`;
}
if (type === 'messages') {
const date = data.timestamp ? new Date(data.timestamp.seconds * 1000).toLocaleDateString() : 'N/A';
return `
<div class="bg-white p-4 rounded-xl border border-l-4 border-l-brand-accent border-gray-200 relative">
<div class="flex justify-between items-start mb-2">
<div>
<h4 class="font-bold text-gray-800">${data.name}</h4>
<a href="mailto:${data.email}" class="text-sm text-brand-accent hover:underline">${data.email}</a>
</div>
<span class="text-xs text-gray-400">${date}</span>
</div>
<p class="text-sm text-gray-600 bg-gray-50 p-3 rounded italic">"${data.message}"</p>
<div class="mt-2 text-right">
<button onclick="deleteItem('${type}', '${id}')" class="text-xs text-red-400 hover:text-red-600">Delete Message</button>
</div>
</div>`;
}
if (type === 'stats') {
return `
<div class="bg-white p-4 rounded-xl border border-gray-200 text-center relative group">
${delBtn}
<h3 class="text-2xl font-black text-brand-accent">${data.count}${data.suffix || ''}</h3>
<p class="text-xs font-bold uppercase text-gray-500">${data.label}</p>
</div>`;
}
if (type === 'certificates') {
return `
<div class="bg-white p-4 rounded-xl border border-gray-200 relative group flex gap-3 items-center">
<img src="${data.img}" class="w-12 h-12 rounded object-cover">
<div class="flex-1">
<h4 class="font-bold text-sm">${data.title}</h4>
<p class="text-xs text-gray-500 truncate w-32">${data.desc}</p>
</div>
<button onclick="deleteItem('${type}', '${id}')" class="text-red-400 hover:text-red-600"><i class="fa-solid fa-trash"></i></button>
</div>`;
}
if (type === 'socials') {
return `
<div class="bg-white p-3 rounded-lg border border-gray-200 flex items-center justify-between">
<div class="flex items-center gap-2">
<i class="${data.icon} text-gray-600"></i>
<span class="font-bold text-sm">${data.name}</span>
</div>
<button onclick="deleteItem('${type}', '${id}')" class="text-red-400 hover:text-red-600"><i class="fa-solid fa-times"></i></button>
</div>`;
}
return '';
}
</script>
</body>
</html>