<!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">
<!-- DASHBOARD (Visible Immediately) -->
<div id="dashboard-screen" class="flex h-screen overflow-hidden">
<!-- SIDEBAR -->
<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 text-xs text-gray-500 text-center">
Mubarik Admin v1.0
</div>
</aside>
<!-- MAIN CONTENT -->
<main class="flex-1 overflow-y-auto bg-gray-100 p-8 relative">
<!-- SECTION: PROJECTS -->
<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>
<!-- Add Project Form -->
<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>
<!-- Projects List -->
<div id="list-projects" class="grid grid-cols-1 md:grid-cols-2 gap-6"></div>
</div>
<!-- SECTION: SKILLS -->
<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>
<!-- SECTION: SERVICES -->
<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>
<!-- Colors optional default in JS -->
<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>
<!-- SECTION: JOURNEY -->
<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>
<!-- SECTION: MESSAGES (Read Only) -->
<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>
<!-- SECTION: STATS -->
<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>
<!-- SECTION: CERTIFICATES -->
<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>
<!-- SECTION: SOCIALS -->
<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>
<!-- UTILITY STYLES (Tailwind classes abstracted) -->
<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>
<!-- FIREBASE LOGIC -->
<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-app.js";
import { getFirestore, collection, addDoc, getDocs, deleteDoc, doc, query, orderBy } from "https://www.gstatic.com/firebasejs/11.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"
};
let app, db;
try {
app = initializeApp(firebaseConfig);
db = getFirestore(app);
} catch(e) {
console.error("Firebase Init Error:", e);
}
// --- GLOBAL FUNCTION DEFINITIONS (Attached to window) ---
// UI Navigation
window.switchTab = (tabName) => {
// Hide all tabs
document.querySelectorAll('.tab-content').forEach(el => el.classList.add('hidden'));
// Show selected
const tab = document.getElementById(`tab-${tabName}`);
if(tab) tab.classList.remove('hidden');
// Highlight sidebar (Simple reset and set)
document.querySelectorAll('.nav-btn').forEach(btn => btn.classList.remove('bg-gray-800', 'border-l-4', 'border-brand-accent'));
// Note: In a real app we'd target the clicked button specifically, but for now we just load data.
// Fetch data for this tab
loadCollectionData(tabName);
}
// Generic Create
window.handleCreate = async (e, collectionName) => {
e.preventDefault();
if(!db) return alert("Database not connected");
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!");
// Manual reload for simplicity in admin:
loadCollectionData(collectionName);
} catch (error) {
alert("Error: " + error.message);
console.error(error);
} finally {
btn.innerText = originalText;
btn.disabled = false;
}
};
// Generic Delete
window.deleteItem = async (collectionName, id) => {
if(!db) return;
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);
}
}
}
// --- 4. RENDER LOGIC PER COLLECTION ---
async function loadCollectionData(colName) {
const container = document.getElementById(`list-${colName}`);
if(!container) return;
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>';
if(!db) {
container.innerHTML = '<p class="text-red-500">Firebase not initialized.</p>';
return;
}
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>`;
console.error(e);
}
}
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}</span>
<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 '';
}
// Initialize with Projects
switchTab('projects');
</script>
</body>
</html>
form admin
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Survey Admin | Somali Books</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;700;900&display=swap" rel="stylesheet">
<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: {
fontFamily: { sans: ['Outfit', 'sans-serif'] },
colors: { brand: { dark: '#050505', accent: '#4f46e5' } }
}
}
}
</script>
<style>
.glass { background: rgba(255, 255, 255, 0.03); backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.05); }
</style>
</head>
<body class="bg-brand-dark text-slate-200 font-sans min-h-screen p-6">
<div class="max-w-7xl mx-auto">
<!-- Header -->
<div class="flex justify-between items-center mb-8">
<h1 class="text-2xl font-bold text-white"><i class="fa-solid fa-chart-pie mr-2 text-brand-accent"></i> Survey Dashboard</h1>
<div class="flex gap-3">
<button onclick="exportCSV()" class="bg-green-600 hover:bg-green-500 text-white px-4 py-2 rounded-lg text-sm font-bold transition-all">
<i class="fa-solid fa-file-csv mr-2"></i> Export CSV
</button>
<a href="index.html" class="glass px-4 py-2 rounded-lg text-sm hover:bg-white/10 transition-all">Exit</a>
</div>
</div>
<!-- Stats -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
<div class="glass p-6 rounded-2xl">
<p class="text-slate-400 text-xs uppercase tracking-widest font-bold">Total Responses</p>
<h2 class="text-4xl font-black text-white mt-2" id="totalCount">0</h2>
</div>
<div class="glass p-6 rounded-2xl">
<p class="text-slate-400 text-xs uppercase tracking-widest font-bold">Most Wanted Feature</p>
<h2 class="text-xl font-bold text-brand-accent mt-2" id="topFeature">...</h2>
</div>
<div class="glass p-6 rounded-2xl">
<p class="text-slate-400 text-xs uppercase tracking-widest font-bold">Pricing Preference</p>
<h2 class="text-xl font-bold text-green-400 mt-2" id="topPrice">...</h2>
</div>
</div>
<!-- Data Table -->
<div class="glass rounded-2xl overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full text-left text-sm">
<thead class="bg-white/5 text-slate-300 uppercase text-xs font-bold">
<tr>
<th class="p-4">Time</th>
<th class="p-4">Name</th>
<th class="p-4">Age/Gender</th>
<th class="p-4">Phone</th>
<th class="p-4">Book Prefs</th>
<th class="p-4">Payment</th>
<th class="p-4">Contact</th>
</tr>
</thead>
<tbody class="divide-y divide-white/5" id="tableBody">
<tr><td colspan="7" class="p-4 text-center text-slate-500">Loading data...</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-app.js";
import { getFirestore, collection, getDocs, query, orderBy } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-firestore.js";
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"
};
let db;
let surveyData = [];
try {
const app = initializeApp(firebaseConfig);
db = getFirestore(app);
fetchData();
} catch(e) { console.error(e); }
async function fetchData() {
if(!db) return;
try {
// Fetch surveys ordered by time descending
const q = query(collection(db, "app_surveys"), orderBy("timestamp", "desc"));
const snap = await getDocs(q);
surveyData = snap.docs.map(doc => {
const d = doc.data();
// Convert Firestore timestamp to JS Date
d.jsDate = d.timestamp ? d.timestamp.toDate() : new Date();
return d;
});
updateStats(surveyData);
renderTable(surveyData);
} catch(e) {
console.error("Error fetching docs:", e);
document.getElementById('tableBody').innerHTML = `<tr><td colspan="7" class="p-4 text-center text-red-400">Error loading data. Check console.</td></tr>`;
}
}
function updateStats(data) {
document.getElementById('totalCount').innerText = data.length;
// Simple logic to find top Feature
const features = {};
const prices = {};
data.forEach(d => {
if(Array.isArray(d.features)) d.features.forEach(f => features[f] = (features[f]||0)+1);
if(d.price) prices[d.price] = (prices[d.price]||0)+1;
});
const topF = Object.keys(features).reduce((a, b) => features[a] > features[b] ? a : b, "N/A");
const topP = Object.keys(prices).reduce((a, b) => prices[a] > prices[b] ? a : b, "N/A");
document.getElementById('topFeature').innerText = topF;
document.getElementById('topPrice').innerText = topP;
}
function renderTable(data) {
const tbody = document.getElementById('tableBody');
if(data.length === 0) {
tbody.innerHTML = `<tr><td colspan="7" class="p-4 text-center text-slate-500">No surveys found yet.</td></tr>`;
return;
}
tbody.innerHTML = data.map(d => `
<tr class="hover:bg-white/5 transition-colors">
<td class="p-4 text-slate-400 text-xs">${d.jsDate.toLocaleDateString()}</td>
<td class="p-4 font-bold text-white">${d.name || "Anonymous"}</td>
<td class="p-4">${d.age || "-"} / ${d.gender || "-"}</td>
<td class="p-4"><span class="bg-brand-accent/20 text-brand-accent px-2 py-1 rounded text-xs">${d.device || "?"}</span></td>
<td class="p-4 text-xs max-w-[150px] truncate">${Array.isArray(d.content_pref) ? d.content_pref.join(", ") : "-"}</td>
<td class="p-4 text-green-400 font-bold">${d.price || "-"}</td>
<td class="p-4 text-xs text-slate-400">${d.contact_info || "-"}</td>
</tr>
`).join('');
}
// Export CSV Function attached to window for button access
window.exportCSV = function() {
if(surveyData.length === 0) return alert("No data to export");
const headers = ["Timestamp", "Name", "Age", "Gender", "Location", "Phone Type", "Internet", "Preferences", "Format", "Features", "Price", "Payment", "Contact"];
const rows = surveyData.map(d => [
d.jsDate.toLocaleString(),
d.name || "",
d.age || "",
d.gender || "",
d.location || "",
d.device || "",
d.internet || "",
Array.isArray(d.content_pref) ? d.content_pref.join(";") : "",
d.format || "",
Array.isArray(d.features) ? d.features.join(";") : "",
d.price || "",
d.payment_method || "",
d.contact_info || ""
]);
let csvContent = "data:text/csv;charset=utf-8,"
+ headers.join(",") + "\n"
+ rows.map(e => e.map(i => `"${i}"`).join(",")).join("\n");
const encodedUri = encodeURI(csvContent);
const link = document.createElement("a");
link.setAttribute("href", encodedUri);
link.setAttribute("download", "survey_data.csv");
document.body.appendChild(link);
link.click();
}
</script>
</body>
</html>
frotn
<!DOCTYPE html>
<html lang="so" class="scroll-smooth">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sahanka App-ka | Somali Books</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;700;900&display=swap" rel="stylesheet">
<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: {
fontFamily: { sans: ['Outfit', 'sans-serif'] },
colors: {
brand: {
dark: '#050505',
accent: '#4f46e5',
glow: '#8b5cf6',
teal: '#14b8a6',
}
},
animation: { 'fade-in': 'fadeIn 0.6s ease-out forwards' },
keyframes: { fadeIn: { '0%': { opacity: '0', transform: 'translateY(10px)' }, '100%': { opacity: '1', transform: 'translateY(0)' } } }
}
}
}
</script>
<style>
.glass {
background: rgba(255, 255, 255, 0.03);
backdrop-filter: blur(16px);
border: 1px solid rgba(255, 255, 255, 0.05);
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
}
input[type="checkbox"], input[type="radio"] { accent-color: #4f46e5; width: 1.2rem; height: 1.2rem; cursor: pointer; }
.section-header { border-left: 4px solid #4f46e5; padding-left: 1rem; margin-bottom: 1.5rem; }
</style>
</head>
<body class="bg-brand-dark text-slate-200 min-h-screen relative overflow-x-hidden selection:bg-brand-accent selection:text-white font-sans">
<!-- Background -->
<div class="fixed inset-0 z-0 pointer-events-none">
<div class="absolute top-[-10%] left-[-10%] w-96 h-96 bg-brand-accent/20 rounded-full blur-[120px]"></div>
<div class="absolute bottom-[-10%] right-[-10%] w-96 h-96 bg-brand-teal/10 rounded-full blur-[120px]"></div>
</div>
<!-- Header -->
<nav class="fixed w-full z-50 glass border-b border-white/5 top-0">
<div class="max-w-4xl mx-auto px-4 py-4 flex justify-between items-center">
<a href="index.html" class="text-slate-400 hover:text-white transition-colors flex items-center gap-2">
<i class="fa-solid fa-arrow-left"></i> <span class="hidden sm:inline">Back Home</span>
</a>
<h1 class="text-lg font-bold text-white">Somali Books App Survey</h1>
<button onclick="toggleShareModal()" class="w-10 h-10 rounded-full bg-brand-accent/10 text-brand-accent hover:bg-brand-accent hover:text-white transition-all flex items-center justify-center">
<i class="fa-solid fa-share-nodes"></i>
</button>
</div>
</nav>
<!-- Main Form -->
<main class="relative z-10 pt-28 pb-20 px-4">
<div class="max-w-3xl mx-auto">
<form id="surveyForm" class="glass rounded-3xl p-6 md:p-12 shadow-2xl space-y-12">
<div class="text-center border-b border-white/5 pb-8">
<h2 class="text-3xl font-bold text-white mb-2">Sahanka App-ka Cusub</h2>
<p class="text-slate-400 text-sm">Fadlan nala wadaag fikradaada si aan u dhisno app-ka aad u baahan tahay.</p>
</div>
<!-- SECTION A -->
<div class="animate-fade-in">
<div class="section-header">
<h3 class="text-xl font-bold text-white">A. Personal Information</h3>
</div>
<div class="space-y-5">
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
<input type="text" name="name" class="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 focus:border-brand-accent focus:outline-none transition-all text-white" placeholder="Magaca (Optional)">
<select name="age" class="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 focus:border-brand-accent focus:outline-none transition-all text-white [&>option]:bg-black">
<option value="" disabled selected>Da’da</option>
<option value="<18">< 18</option>
<option value="18-24">18 – 24</option>
<option value="25-34">25 – 34</option>
<option value="35-44">35 – 44</option>
<option value="45-54">45 – 54</option>
<option value="55+">55+</option>
</select>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
<select name="gender" class="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 focus:border-brand-accent focus:outline-none transition-all text-white [&>option]:bg-black">
<option value="" disabled selected>Jinsiga</option>
<option value="Male">Lab</option>
<option value="Female">Dhedig</option>
</select>
<input type="text" name="location" class="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 focus:border-brand-accent focus:outline-none transition-all text-white" placeholder="Goobta (Magaalo & Dal)">
</div>
<div>
<label class="block text-sm text-slate-400 mb-2">Luqaadaha aad ku hadasho:</label>
<div class="flex flex-wrap gap-4">
<label class="flex items-center gap-2"><input type="checkbox" name="languages" value="Somali"> Somali</label>
<label class="flex items-center gap-2"><input type="checkbox" name="languages" value="English"> English</label>
<label class="flex items-center gap-2"><input type="checkbox" name="languages" value="Arabic"> Arabic</label>
</div>
</div>
</div>
</div>
<!-- SECTION B -->
<div class="animate-fade-in" style="animation-delay: 0.1s">
<div class="section-header border-l-brand-teal">
<h3 class="text-xl font-bold text-white">B. Device & Usage</h3>
</div>
<div class="space-y-5">
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
<div>
<label class="block text-sm text-slate-400 mb-2">Nooca Telefoonka:</label>
<div class="flex gap-4">
<label class="flex items-center gap-2"><input type="radio" name="device" value="Android"> Android</label>
<label class="flex items-center gap-2"><input type="radio" name="device" value="iOS"> iPhone</label>
</div>
</div>
<div>
<label class="block text-sm text-slate-400 mb-2">Inta jeer aad isticmaasho apps:</label>
<select name="usage_freq" class="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 focus:border-brand-teal focus:outline-none text-white [&>option]:bg-black">
<option value="Daily">Maalin kasta</option>
<option value="Weekly">Usbuuci mar</option>
<option value="Rarely">Marar dhif ah</option>
</select>
</div>
</div>
</div>
</div>
<!-- SECTION C -->
<div class="animate-fade-in" style="animation-delay: 0.2s">
<div class="section-header border-l-brand-glow">
<h3 class="text-xl font-bold text-white">C. Content Preferences</h3>
</div>
<div class="space-y-5">
<label class="block text-sm text-slate-400">Nooca buugaagta (Dooro inta badan):</label>
<div class="grid grid-cols-2 md:grid-cols-3 gap-3">
<label class="bg-white/5 p-3 rounded-lg border border-white/5 flex items-center gap-2"><input type="checkbox" name="content_pref" value="Diini"> Diini</label>
<label class="bg-white/5 p-3 rounded-lg border border-white/5 flex items-center gap-2"><input type="checkbox" name="content_pref" value="Taariikh"> Taariikh</label>
<label class="bg-white/5 p-3 rounded-lg border border-white/5 flex items-center gap-2"><input type="checkbox" name="content_pref" value="Horumar"> Horumar</label>
<label class="bg-white/5 p-3 rounded-lg border border-white/5 flex items-center gap-2"><input type="checkbox" name="content_pref" value="Sheekooyin"> Sheekooyin</label>
<label class="bg-white/5 p-3 rounded-lg border border-white/5 flex items-center gap-2"><input type="checkbox" name="content_pref" value="Carruur"> Carruur</label>
<label class="bg-white/5 p-3 rounded-lg border border-white/5 flex items-center gap-2"><input type="checkbox" name="content_pref" value="Waxbarasho"> Waxbarasho</label>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
<div>
<label class="block text-sm text-slate-400 mb-2">Qaabka:</label>
<div class="flex gap-4">
<label class="flex items-center gap-2"><input type="radio" name="format" value="Reading"> Akhris</label>
<label class="flex items-center gap-2"><input type="radio" name="format" value="Audio"> Dhageysi</label>
<label class="flex items-center gap-2"><input type="radio" name="format" value="Both"> Labadaba</label>
</div>
</div>
<div>
<label class="block text-sm text-slate-400 mb-2">Nooca Codka:</label>
<div class="flex gap-4">
<label class="flex items-center gap-2"><input type="radio" name="voice" value="Male"> Rag</label>
<label class="flex items-center gap-2"><input type="radio" name="voice" value="Female"> Dumar</label>
<label class="flex items-center gap-2"><input type="radio" name="voice" value="Any"> Kasta</label>
</div>
</div>
</div>
</div>
</div>
<!-- SECTION D -->
<div class="animate-fade-in" style="animation-delay: 0.3s">
<div class="section-header border-l-pink-500">
<h3 class="text-xl font-bold text-white">D. App Features</h3>
</div>
<div class="space-y-5">
<label class="block text-sm text-slate-400">Dooro Feature-yada muhiimka ah:</label>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
<label class="flex items-center gap-2"><input type="checkbox" name="features" value="Offline"> Download Offline</label>
<label class="flex items-center gap-2"><input type="checkbox" name="features" value="Save Progress"> Save Progress</label>
<label class="flex items-center gap-2"><input type="checkbox" name="features" value="Dark Mode"> Dark Mode</label>
<label class="flex items-center gap-2"><input type="checkbox" name="features" value="Speed Control"> Speed Control</label>
<label class="flex items-center gap-2"><input type="checkbox" name="features" value="Kids Mode"> Carruur Mode</label>
<label class="flex items-center gap-2"><input type="checkbox" name="features" value="Translation"> Translation</label>
</div>
</div>
</div>
<!-- SECTION F & G -->
<div class="animate-fade-in" style="animation-delay: 0.4s">
<div class="section-header border-l-green-500">
<h3 class="text-xl font-bold text-white">F. Payment & Feedback</h3>
</div>
<div class="space-y-5">
<div>
<label class="block text-sm text-slate-400 mb-2">Qiimaha ku habboon bishii?</label>
<div class="flex flex-wrap gap-4">
<label class="flex items-center gap-2"><input type="radio" name="price" value="Free"> Bilaash</label>
<label class="flex items-center gap-2"><input type="radio" name="price" value="$1-3"> $1 - $3</label>
<label class="flex items-center gap-2"><input type="radio" name="price" value="$3-5"> $3 - $5</label>
<label class="flex items-center gap-2"><input type="radio" name="price" value="$5+"> $5+</label>
</div>
</div>
<div>
<label class="block text-sm text-slate-400 mb-2">Habka Lacag bixinta:</label>
<select name="payment_method" class="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 focus:border-green-500 focus:outline-none text-white [&>option]:bg-black">
<option value="Zaad">Zaad Service</option>
<option value="EVC">EVC Plus</option>
<option value="Sahal">Sahal</option>
<option value="PayPal">PayPal</option>
</select>
</div>
<textarea name="feedback" class="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 focus:border-green-500 focus:outline-none text-white h-24 resize-none" placeholder="Talo soo jeedin guud (General Feedback)..."></textarea>
<div class="bg-white/5 p-4 rounded-xl">
<label class="flex items-center gap-3 cursor-pointer">
<input type="checkbox" name="notify" value="Yes" checked>
<span class="text-sm">Ila soo socodsii marka App-ka la furo (Notify me on launch)</span>
</label>
<input type="text" name="contact_info" class="w-full mt-3 bg-black/40 border border-white/10 rounded-lg px-4 py-2 text-white focus:border-brand-accent focus:outline-none" placeholder="Email ama Number...">
</div>
</div>
</div>
<div class="pt-6 text-center">
<button type="submit" id="submitBtn" class="w-full md:w-auto bg-gradient-to-r from-brand-accent to-brand-glow text-white font-bold text-lg px-12 py-4 rounded-full hover:scale-105 hover:shadow-lg hover:shadow-brand-accent/50 transition-all">
Dir Jawaabaha (Submit)
</button>
</div>
</form>
</div>
</main>
<!-- Share Modal -->
<div id="shareModal" class="fixed inset-0 z-[100] bg-black/80 backdrop-blur-sm hidden flex-col items-center justify-center opacity-0 transition-opacity duration-300">
<div class="bg-brand-dark border border-white/10 rounded-2xl p-6 w-full max-w-sm mx-4 transform scale-95 transition-transform duration-300" id="shareContent">
<div class="flex justify-between items-center mb-4">
<h3 class="font-bold text-white">Share Survey</h3>
<button onclick="toggleShareModal()" class="text-slate-400 hover:text-white"><i class="fa-solid fa-xmark"></i></button>
</div>
<div class="grid grid-cols-2 gap-3">
<button onclick="shareTo('whatsapp')" class="bg-white/5 p-3 rounded-lg hover:bg-[#25D366]/20 text-white"><i class="fa-brands fa-whatsapp text-2xl mb-1 block"></i> WhatsApp</button>
<button onclick="copyLink()" class="bg-white/5 p-3 rounded-lg hover:bg-white/10 text-white"><i class="fa-solid fa-link text-2xl mb-1 block"></i> Copy Link</button>
</div>
</div>
</div>
<!-- Logic -->
<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-app.js";
import { getFirestore, collection, addDoc } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-firestore.js";
// CONFIG
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"
};
let db;
try {
const app = initializeApp(firebaseConfig);
db = getFirestore(app);
} catch(e) { console.error("Firebase Error", e); }
const form = document.getElementById('surveyForm');
const STORAGE_KEY = 'somali_survey_draft';
// Auto-Save
function loadDraft() {
const saved = localStorage.getItem(STORAGE_KEY);
if(saved) {
const data = JSON.parse(saved);
Object.keys(data).forEach(k => {
const el = form.elements[k];
if(el) {
if(el.type === 'checkbox' || el.type === 'radio') {
if(el instanceof RadioNodeList) {
el.forEach(r => { if(data[k].includes(r.value)) r.checked = true; });
}
} else {
el.value = data[k];
}
}
});
}
}
form.addEventListener('input', () => {
const fd = new FormData(form);
const obj = {};
fd.forEach((v, k) => {
if(!obj[k]) obj[k] = v;
else {
if(!Array.isArray(obj[k])) obj[k] = [obj[k]];
obj[k].push(v);
}
});
localStorage.setItem(STORAGE_KEY, JSON.stringify(obj));
});
// Submit
form.addEventListener('submit', async (e) => {
e.preventDefault();
const btn = document.getElementById('submitBtn');
const originalText = btn.innerHTML;
btn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> Diraya...';
btn.disabled = true;
const fd = new FormData(form);
const data = { timestamp: new Date() };
// Capture multi-selects
const multiKeys = ['languages', 'content_pref', 'features'];
fd.forEach((v, k) => {
if (multiKeys.includes(k)) {
if (!data[k]) data[k] = [];
data[k].push(v);
} else {
data[k] = v;
}
});
try {
if(db) {
await addDoc(collection(db, "app_surveys"), data);
} else {
console.warn("DB not connected, simulating.");
await new Promise(r => setTimeout(r, 1000));
}
localStorage.removeItem(STORAGE_KEY);
alert("Mahadsanid! Xogtaada waa la helay.");
window.location.href = "index.html";
} catch(err) {
alert("Khalad: " + err.message);
btn.innerHTML = originalText;
btn.disabled = false;
}
});
// Helpers
window.toggleShareModal = () => {
const m = document.getElementById('shareModal');
const c = document.getElementById('shareContent');
if(m.classList.contains('hidden')) {
m.classList.remove('hidden'); m.classList.add('flex');
setTimeout(() => { m.classList.remove('opacity-0'); c.classList.remove('scale-95'); c.classList.add('scale-100'); }, 10);
} else {
m.classList.add('opacity-0'); c.classList.remove('scale-100'); c.classList.add('scale-95');
setTimeout(() => { m.classList.add('hidden'); m.classList.remove('flex'); }, 300);
}
}
window.shareTo = (p) => {
const url = encodeURIComponent(window.location.href);
const txt = encodeURIComponent("Ka qayb qaado sahanka App-ka cusub!");
if(p === 'whatsapp') window.open(`https://wa.me/?text=${txt}%20${url}`);
}
window.copyLink = () => { navigator.clipboard.writeText(window.location.href); alert("Link Copied!"); }
loadDraft();
</script>
</body>
</html>