File: /home/niyknzcu/nexlancedigital.com/claude - Copy/admin.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Panel โ Nexlance</title>
<link rel="stylesheet" href="dashboard.css">
<link rel="stylesheet" href="app.css">
<!-- Firebase SDK -->
<script src="https://www.gstatic.com/firebasejs/10.14.1/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.14.1/firebase-auth-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.14.1/firebase-firestore-compat.js"></script>
<style>
.admin-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:16px; margin-bottom:28px; }
.admin-stat { background:#fff; border-radius:14px; padding:20px; border:1px solid #f0ecff; }
.admin-stat h4 { font-size:0.78rem; color:#aaa; text-transform:uppercase; letter-spacing:.5px; margin-bottom:8px; }
.admin-stat h2 { font-size:1.6rem; font-weight:800; color:#333; }
.admin-stat .sub { font-size:0.78rem; color:#aaa; margin-top:4px; }
.admin-section { background:#fff; border-radius:14px; border:1px solid #f0ecff; overflow:hidden; margin-bottom:24px; }
.admin-section-header { padding:16px 20px; border-bottom:1px solid #f0ecff; display:flex; justify-content:space-between; align-items:center; }
.admin-section-header h3 { font-size:0.95rem; color:#4b3fbf; font-weight:700; }
.admin-section-body { padding:20px; }
.user-row { display:grid; grid-template-columns:1fr 1fr auto auto; gap:12px; align-items:center; padding:10px 0; border-bottom:1px solid #f8f5ff; font-size:0.875rem; }
.user-row:last-child { border-bottom:none; }
.user-avatar { width:32px; height:32px; background:linear-gradient(135deg,#6c5ce7,#a29bfe); border-radius:50%; display:flex; align-items:center; justify-content:center; color:#fff; font-weight:700; font-size:0.75rem; }
.info-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px; }
.info-item { background:#f8f5ff; border-radius:10px; padding:12px 16px; }
.info-item .label { font-size:0.75rem; color:#aaa; text-transform:uppercase; letter-spacing:.4px; margin-bottom:4px; }
.info-item .value { font-size:0.9rem; font-weight:600; color:#333; }
.danger-zone { border:1.5px solid #ffe0e0; border-radius:14px; padding:20px; }
.danger-zone h3 { color:#d63031; margin-bottom:14px; font-size:0.95rem; }
.danger-row { display:flex; justify-content:space-between; align-items:center; padding:10px 0; border-bottom:1px solid #fff0f0; font-size:0.875rem; color:#555; }
.danger-row:last-child { border-bottom:none; }
.btn-danger { background:#d63031; color:#fff; border:none; border-radius:8px; padding:7px 16px; font-size:0.8rem; font-weight:600; cursor:pointer; transition:background .2s; }
.btn-danger:hover { background:#c0392b; }
.activity-row { padding:10px 0; border-bottom:1px solid #f8f5ff; font-size:0.875rem; color:#555; display:flex; gap:10px; align-items:flex-start; }
.activity-row:last-child { border-bottom:none; }
.activity-time { font-size:0.75rem; color:#aaa; white-space:nowrap; }
</style>
</head>
<body>
<div class="container">
<aside class="sidebar">
<h2 class="logo">Nexlance</h2>
<ul class="nav">
<li><a href="dashboard.html">๐ Dashboard</a></li>
<li><a href="clients.html">๐ฅ Clients</a></li>
<li><a href="team.html">๐งโ๐ผ Team</a></li>
<li><a href="projects.html">๐ Projects</a></li>
<li><a href="invoices.html">๐งพ Invoices</a></li>
<li><a href="services.html">๐ Services</a></li>
<li><a href="access-roles.html">๐ Access / Roles</a></li>
<li class="active"><a href="admin.html">โ๏ธ Admin</a></li>
<hr style="border:none;border-top:1px solid #c9bfff;margin:12px 0 8px;">
<li><a href="developer-info.html">๐จโ๐ป Support Info</a></li>
</ul>
</aside>
<main class="main">
<div class="topbar">
<input type="text" placeholder="Search...">
<div class="profile"><a href="admin.html" style="color:inherit;text-decoration:none;">โ๏ธ Admin</a></div>
</div>
<div class="page-header">
<div class="page-header-left">
<h1>Admin Panel</h1>
<p>System settings, user management & platform control</p>
</div>
</div>
<!-- System Stats -->
<div class="admin-grid" id="adminStats">
<div class="admin-stat">
<h4>Registered Users</h4>
<h2 id="statUsers">โ</h2>
<div class="sub">Local accounts</div>
</div>
<div class="admin-stat">
<h4>Team Members</h4>
<h2 id="statTeam">โ</h2>
<div class="sub">Active in dashboard</div>
</div>
<div class="admin-stat">
<h4>Pending Invites</h4>
<h2 id="statInvites">โ</h2>
<div class="sub">Awaiting confirmation</div>
</div>
<div class="admin-stat">
<h4>Platform Version</h4>
<h2>v1.0</h2>
<div class="sub">Nexlance Agency Suite</div>
</div>
</div>
<!-- System Information -->
<div class="admin-section">
<div class="admin-section-header">
<h3>โ๏ธ System Information</h3>
</div>
<div class="admin-section-body">
<div class="info-grid">
<div class="info-item">
<div class="label">Platform</div>
<div class="value">Nexlance Agency Suite</div>
</div>
<div class="info-item">
<div class="label">Version</div>
<div class="value">v1.0.0</div>
</div>
<div class="info-item">
<div class="label">Database</div>
<div class="value" id="dbStatus">Checking...</div>
</div>
<div class="info-item">
<div class="label">Storage</div>
<div class="value" id="storageInfo">โ</div>
</div>
<div class="info-item">
<div class="label">Current Admin</div>
<div class="value" id="currentAdmin">โ</div>
</div>
<div class="info-item">
<div class="label">Last Login</div>
<div class="value" id="lastLogin">โ</div>
</div>
<div class="info-item">
<div class="label">Browser</div>
<div class="value" id="browserInfo">โ</div>
</div>
<div class="info-item">
<div class="label">Page Loaded</div>
<div class="value" id="pageLoaded">โ</div>
</div>
</div>
</div>
</div>
<!-- Registered Users -->
<div class="admin-section">
<div class="admin-section-header">
<h3>๐ค Registered Users</h3>
<span id="userCount" style="font-size:0.82rem;color:#aaa;"></span>
</div>
<div class="admin-section-body" style="padding:0;">
<div id="usersList" style="padding:8px 20px 4px;"></div>
</div>
</div>
<!-- Pending Invitations -->
<div class="admin-section" id="invitesSection">
<div class="admin-section-header">
<h3>โณ Pending Invitations</h3>
<button class="btn btn-sm btn-secondary" onclick="window.location.href='access-roles.html'">Manage โ</button>
</div>
<div class="admin-section-body" style="padding:0;">
<div id="invitesList" style="padding:8px 20px 4px;"></div>
</div>
</div>
<!-- Recent Activity -->
<div class="admin-section">
<div class="admin-section-header">
<h3>๐ Recent Activity</h3>
<button class="btn btn-sm btn-secondary" onclick="refreshActivity()">Refresh</button>
</div>
<div class="admin-section-body" style="padding:8px 20px;">
<div id="activityLog"></div>
</div>
</div>
<!-- Danger Zone -->
<div class="danger-zone">
<h3>โ ๏ธ Danger Zone</h3>
<div class="danger-row">
<div>
<strong>Clear All Invitations</strong>
<div style="font-size:0.8rem;color:#aaa;margin-top:2px;">Remove all pending and accepted invite records</div>
</div>
<button class="btn-danger" onclick="clearInvites()">Clear Invites</button>
</div>
<div class="danger-row">
<div>
<strong>Reset Registered Users</strong>
<div style="font-size:0.8rem;color:#aaa;margin-top:2px;">Delete all login accounts from local storage</div>
</div>
<button class="btn-danger" onclick="resetUsers()">Reset Users</button>
</div>
<div class="danger-row">
<div>
<strong>Reset Invoice Data</strong>
<div style="font-size:0.8rem;color:#aaa;margin-top:2px;">Restore invoices to sample data</div>
</div>
<button class="btn-danger" onclick="resetInvoices()">Reset Invoices</button>
</div>
<div class="danger-row">
<div>
<strong>Sign Out</strong>
<div style="font-size:0.8rem;color:#aaa;margin-top:2px;">Log out of the current admin session</div>
</div>
<button class="btn-danger" onclick="signOut()">Sign Out</button>
</div>
</div>
</main>
</div>
<script src="supabase-config.js"></script>
<script>
function getUsers() { try { return JSON.parse(localStorage.getItem('nexlance_users') || '[]'); } catch(e){ return []; } }
function getInvites() { try { return JSON.parse(localStorage.getItem('nexlance_invites') || '[]'); } catch(e){ return []; } }
async function initAdmin() {
const users = getUsers();
const invites = getInvites().filter(i => i.status === 'pending');
const members = await fetchTeamMembers();
// Stats
document.getElementById('statUsers').textContent = users.length;
document.getElementById('statTeam').textContent = members.length;
document.getElementById('statInvites').textContent = invites.length;
// System info
const user = (() => { try { return JSON.parse(localStorage.getItem('nexlance_user')||'null'); } catch(e){ return null; } })();
document.getElementById('currentAdmin').textContent = user ? user.name : 'Guest';
document.getElementById('lastLogin').textContent = user ? 'This session' : 'โ';
document.getElementById('dbStatus').textContent = isSupabaseConfigured ? '๐ข Supabase Connected' : '๐ก Local / Sample Mode';
document.getElementById('browserInfo').textContent = navigator.userAgent.split(' ').slice(-1)[0] || 'โ';
document.getElementById('pageLoaded').textContent = new Date().toLocaleTimeString('en-IN');
// localStorage usage estimate
let total = 0;
for (const k in localStorage) { if (localStorage.hasOwnProperty(k)) total += (localStorage[k].length + k.length) * 2; }
document.getElementById('storageInfo').textContent = (total / 1024).toFixed(1) + ' KB used';
// Render users
renderUsers(users);
renderInvites(invites);
renderActivity(members);
}
function renderUsers(users) {
const el = document.getElementById('usersList');
document.getElementById('userCount').textContent = users.length + ' account' + (users.length !== 1 ? 's' : '');
if (!users.length) {
el.innerHTML = '<p style="color:#aaa;font-size:0.875rem;padding:10px 0;">No registered users yet.</p>';
return;
}
el.innerHTML = users.map(u => `
<div class="user-row">
<div style="display:flex;align-items:center;gap:10px;">
<div class="user-avatar">${getInitials(u.name)}</div>
<div>
<div style="font-weight:600;color:#333;">${u.name}</div>
<div style="font-size:0.78rem;color:#aaa;">${u.mobile || ''}</div>
</div>
</div>
<div style="color:#888;font-size:0.85rem;">${u.email}</div>
<div style="font-size:0.75rem;color:#aaa;">${u.createdAt ? new Date(u.createdAt).toLocaleDateString('en-IN') : 'โ'}</div>
<button class="btn-danger" style="font-size:0.75rem;padding:5px 10px;" onclick="deleteUser('${u.email}')">Remove</button>
</div>`).join('');
}
function renderInvites(invites) {
const el = document.getElementById('invitesList');
if (!invites.length) {
el.innerHTML = '<p style="color:#aaa;font-size:0.875rem;padding:10px 0;">No pending invitations.</p>';
return;
}
const roleColors = {Admin:'badge-red','Project Manager':'badge-blue',Developer:'badge-purple',Designer:'badge-teal',Client:'badge-gray'};
el.innerHTML = invites.map(inv => `
<div class="user-row">
<div><strong>${inv.name}</strong></div>
<div style="color:#888;font-size:0.85rem;">${inv.email}</div>
<span class="badge ${roleColors[inv.role]||'badge-gray'}" style="font-size:0.75rem;">${inv.role}</span>
<span style="font-family:monospace;background:#f0ecff;color:#6c5ce7;padding:3px 10px;border-radius:6px;font-size:0.82rem;">${inv.code}</span>
</div>`).join('');
}
function renderActivity(members) {
const el = document.getElementById('activityLog');
const events = [
{ icon:'๐', text:'Admin panel accessed', time: new Date().toLocaleTimeString('en-IN') },
{ icon:'๐ฅ', text:`${members.length} team members loaded`, time: new Date().toLocaleTimeString('en-IN') },
{ icon:'๐พ', text:'Local data synced', time: new Date().toLocaleTimeString('en-IN') },
];
el.innerHTML = events.map(e => `
<div class="activity-row">
<span>${e.icon}</span>
<span style="flex:1;">${e.text}</span>
<span class="activity-time">${e.time}</span>
</div>`).join('');
}
function refreshActivity() {
showToast('Activity refreshed', 'info');
const el = document.getElementById('activityLog');
el.innerHTML = `<div class="activity-row"><span>๐</span><span style="flex:1;">Activity log refreshed</span><span class="activity-time">${new Date().toLocaleTimeString('en-IN')}</span></div>` + el.innerHTML;
}
/* ---- Danger Zone Actions ---- */
function deleteUser(email) {
if (!confirm('Remove user ' + email + '?')) return;
const users = getUsers().filter(u => u.email !== email);
localStorage.setItem('nexlance_users', JSON.stringify(users));
renderUsers(users);
document.getElementById('statUsers').textContent = users.length;
showToast('User removed.', 'info');
}
function clearInvites() {
if (!confirm('Clear all invite records? This cannot be undone.')) return;
localStorage.removeItem('nexlance_invites');
document.getElementById('invitesList').innerHTML = '<p style="color:#aaa;font-size:0.875rem;padding:10px 0;">No pending invitations.</p>';
document.getElementById('statInvites').textContent = '0';
showToast('All invites cleared.', 'info');
}
function resetUsers() {
if (!confirm('Delete ALL registered user accounts? They will need to re-register.')) return;
localStorage.removeItem('nexlance_users');
renderUsers([]);
document.getElementById('statUsers').textContent = '0';
showToast('All users reset.', 'info');
}
function resetInvoices() {
if (!confirm('Reset invoice data to sample data?')) return;
localStorage.removeItem('nexlance_invoices');
showToast('Invoices reset to sample data.', 'success');
}
function signOut() {
if (!confirm('Sign out of admin session?')) return;
localStorage.removeItem('nexlance_auth');
localStorage.removeItem('nexlance_user');
window.location.href = 'login.html';
}
initAdmin();
</script>
</body>
</html>