Files
orion/static/admin/dashboard.html

604 lines
23 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Dashboard - Multi-Tenant Ecommerce Platform</title>
<link rel="stylesheet" href="/static/css/shared/base.css">
<link rel="stylesheet" href="/static/css/admin/admin.css">
</head>
<body>
<!-- Header -->
<header class="admin-header">
<div class="header-left">
<h1>🔐 Admin Dashboard</h1>
</div>
<div class="header-right">
<span class="user-info">Welcome, <strong id="adminUsername">Admin</strong></span>
<button class="btn-logout" onclick="handleLogout()">Logout</button>
</div>
</header>
<!-- Main Container -->
<div class="admin-container">
<!-- Sidebar -->
<aside class="admin-sidebar">
<nav>
<ul class="nav-menu">
<li class="nav-item">
<a href="#" class="nav-link active" onclick="showSection('dashboard')">
📊 Dashboard
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link" onclick="showSection('vendors')">
🏪 Vendors
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link" onclick="showSection('users')">
👥 Users
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link" onclick="showSection('imports')">
📦 Import Jobs
</a>
</li>
</ul>
</nav>
</aside>
<!-- Main Content -->
<main class="admin-content">
<!-- Dashboard View -->
<div id="dashboardView">
<!-- Stats Grid -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-header">
<div>
<div class="stat-title">Total Vendors</div>
</div>
<div class="stat-icon">🏪</div>
</div>
<div class="stat-value" id="totalVendors">-</div>
<div class="stat-subtitle">
<span id="activeVendors">-</span> active
</div>
</div>
<div class="stat-card">
<div class="stat-header">
<div>
<div class="stat-title">Total Users</div>
</div>
<div class="stat-icon">👥</div>
</div>
<div class="stat-value" id="totalUsers">-</div>
<div class="stat-subtitle">
<span id="activeUsers">-</span> active
</div>
</div>
<div class="stat-card">
<div class="stat-header">
<div>
<div class="stat-title">Verified Vendors</div>
</div>
<div class="stat-icon"></div>
</div>
<div class="stat-value" id="verifiedVendors">-</div>
<div class="stat-subtitle">
<span id="verificationRate">-</span>% verification rate
</div>
</div>
<div class="stat-card">
<div class="stat-header">
<div>
<div class="stat-title">Import Jobs</div>
</div>
<div class="stat-icon">📦</div>
</div>
<div class="stat-value" id="totalImports">-</div>
<div class="stat-subtitle">
<span id="completedImports">-</span> completed
</div>
</div>
</div>
<!-- Recent Vendors -->
<div class="content-section">
<div class="section-header">
<h2 class="section-title">Recent Vendors</h2>
<button class="btn-primary" onclick="showSection('vendors')">View All</button>
</div>
<div id="recentVendorsList">
<div class="loading">Loading recent vendors...</div>
</div>
</div>
<!-- Recent Import Jobs -->
<div class="content-section">
<div class="section-header">
<h2 class="section-title">Recent Import Jobs</h2>
<button class="btn-primary" onclick="showSection('imports')">View All</button>
</div>
<div id="recentImportsList">
<div class="loading">Loading recent imports...</div>
</div>
</div>
</div>
<!-- Vendors View -->
<div id="vendorsView" style="display: none;">
<div class="content-section">
<div class="section-header">
<h2 class="section-title">Vendor Management</h2>
<button class="btn-primary" onclick="window.location.href='/static/admin/vendors.html'">
Create New Vendor
</button>
</div>
<div id="vendorsList">
<div class="loading">Loading vendors...</div>
</div>
</div>
</div>
<!-- Users View -->
<div id="usersView" style="display: none;">
<div class="content-section">
<div class="section-header">
<h2 class="section-title">User Management</h2>
</div>
<div id="usersList">
<div class="loading">Loading users...</div>
</div>
</div>
</div>
<!-- Imports View -->
<div id="importsView" style="display: none;">
<div class="content-section">
<div class="section-header">
<h2 class="section-title">Import Jobs</h2>
</div>
<div id="importsList">
<div class="loading">Loading import jobs...</div>
</div>
</div>
</div>
</main>
</div>
<script>
const API_BASE_URL = '/api/v1';
let currentSection = 'dashboard';
// Check authentication
function checkAuth() {
const token = localStorage.getItem('admin_token');
const user = localStorage.getItem('admin_user');
if (!token || !user) {
window.location.href = '/static/admin/login.html';
return false;
}
try {
const userData = JSON.parse(user);
if (userData.role !== 'admin') {
alert('Access denied. Admin privileges required.');
localStorage.removeItem('admin_token');
localStorage.removeItem('admin_user');
window.location.href = '/static/admin/login.html';
return false;
}
document.getElementById('adminUsername').textContent = userData.username;
return true;
} catch (e) {
window.location.href = '/static/admin/login.html';
return false;
}
}
// Logout
function handleLogout() {
if (confirm('Are you sure you want to logout?')) {
localStorage.removeItem('admin_token');
localStorage.removeItem('admin_user');
window.location.href = '/static/admin/login.html';
}
}
// API Call with auth
async function apiCall(endpoint, options = {}) {
const token = localStorage.getItem('admin_token');
const defaultOptions = {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
}
};
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
...defaultOptions,
...options,
headers: {
...defaultOptions.headers,
...options.headers
}
});
if (response.status === 401) {
localStorage.removeItem('admin_token');
localStorage.removeItem('admin_user');
window.location.href = '/static/admin/login.html';
throw new Error('Unauthorized');
}
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'API request failed');
}
return response.json();
}
// Load dashboard data
async function loadDashboard() {
try {
const data = await apiCall('/admin/dashboard');
// Update stats
document.getElementById('totalVendors').textContent = data.vendors.total_vendors || 0;
document.getElementById('activeVendors').textContent = data.vendors.active_vendors || 0;
document.getElementById('verifiedVendors').textContent = data.vendors.verified_vendors || 0;
document.getElementById('verificationRate').textContent = Math.round(data.vendors.verification_rate || 0);
document.getElementById('totalUsers').textContent = data.users.total_users || 0;
document.getElementById('activeUsers').textContent = data.users.active_users || 0;
// Display recent vendors
displayRecentVendors(data.recent_vendors || []);
// Display recent imports
displayRecentImports(data.recent_imports || []);
} catch (error) {
console.error('Failed to load dashboard:', error);
}
}
// Display recent vendors
function displayRecentVendors(vendors) {
const container = document.getElementById('recentVendorsList');
if (vendors.length === 0) {
container.innerHTML = `
<div class="empty-state">
<div class="empty-state-icon">🏪</div>
<p>No vendors yet</p>
</div>
`;
return;
}
const tableHTML = `
<table class="data-table">
<thead>
<tr>
<th>Vendor Code</th>
<th>Name</th>
<th>Subdomain</th>
<th>Status</th>
<th>Created</th>
</tr>
</thead>
<tbody>
${vendors.map(v => `
<tr>
<td><strong>${v.vendor_code}</strong></td>
<td>${v.name}</td>
<td>${v.subdomain}</td>
<td>
${v.is_verified ? '<span class="badge badge-success">Verified</span>' : '<span class="badge badge-warning">Pending</span>'}
${v.is_active ? '<span class="badge badge-success">Active</span>' : '<span class="badge badge-danger">Inactive</span>'}
</td>
<td>${new Date(v.created_at).toLocaleDateString()}</td>
</tr>
`).join('')}
</tbody>
</table>
`;
container.innerHTML = tableHTML;
}
// Display recent imports
function displayRecentImports(imports) {
const container = document.getElementById('recentImportsList');
if (imports.length === 0) {
container.innerHTML = `
<div class="empty-state">
<div class="empty-state-icon">📦</div>
<p>No import jobs yet</p>
</div>
`;
return;
}
const tableHTML = `
<table class="data-table">
<thead>
<tr>
<th>ID</th>
<th>Marketplace</th>
<th>Vendor</th>
<th>Status</th>
<th>Processed</th>
<th>Created</th>
</tr>
</thead>
<tbody>
${imports.map(j => `
<tr>
<td>#${j.id}</td>
<td>${j.marketplace}</td>
<td>${j.vendor_name || '-'}</td>
<td>
${j.status === 'completed' ? '<span class="badge badge-success">Completed</span>' :
j.status === 'failed' ? '<span class="badge badge-danger">Failed</span>' :
'<span class="badge badge-warning">Processing</span>'}
</td>
<td>${j.total_processed || 0}</td>
<td>${new Date(j.created_at).toLocaleDateString()}</td>
</tr>
`).join('')}
</tbody>
</table>
`;
container.innerHTML = tableHTML;
}
// Show section
function showSection(section) {
// Update nav
document.querySelectorAll('.nav-link').forEach(link => {
link.classList.remove('active');
});
event.target.classList.add('active');
// Hide all views
document.getElementById('dashboardView').style.display = 'none';
document.getElementById('vendorsView').style.display = 'none';
document.getElementById('usersView').style.display = 'none';
document.getElementById('importsView').style.display = 'none';
// Show selected view
currentSection = section;
switch(section) {
case 'dashboard':
document.getElementById('dashboardView').style.display = 'block';
loadDashboard();
break;
case 'vendors':
document.getElementById('vendorsView').style.display = 'block';
loadVendors();
break;
case 'users':
document.getElementById('usersView').style.display = 'block';
loadUsers();
break;
case 'imports':
document.getElementById('importsView').style.display = 'block';
loadImports();
break;
}
}
// Load vendors
async function loadVendors() {
try {
const data = await apiCall('/admin/vendors?limit=100');
displayVendorsList(data.vendors);
} catch (error) {
console.error('Failed to load vendors:', error);
document.getElementById('vendorsList').innerHTML = `
<div class="empty-state">
<p>Failed to load vendors: ${error.message}</p>
</div>
`;
}
}
// Display vendors list
function displayVendorsList(vendors) {
const container = document.getElementById('vendorsList');
if (vendors.length === 0) {
container.innerHTML = `
<div class="empty-state">
<div class="empty-state-icon">🏪</div>
<p>No vendors found</p>
</div>
`;
return;
}
const tableHTML = `
<table class="data-table">
<thead>
<tr>
<th>ID</th>
<th>Vendor Code</th>
<th>Name</th>
<th>Subdomain</th>
<th>Email</th>
<th>Status</th>
<th>Created</th>
</tr>
</thead>
<tbody>
${vendors.map(v => `
<tr>
<td>${v.id}</td>
<td><strong>${v.vendor_code}</strong></td>
<td>${v.name}</td>
<td>${v.subdomain}</td>
<td>${v.contact_email || '-'}</td>
<td>
${v.is_verified ? '<span class="badge badge-success">Verified</span>' : '<span class="badge badge-warning">Pending</span>'}
${v.is_active ? '<span class="badge badge-success">Active</span>' : '<span class="badge badge-danger">Inactive</span>'}
</td>
<td>${new Date(v.created_at).toLocaleDateString()}</td>
</tr>
`).join('')}
</tbody>
</table>
`;
container.innerHTML = tableHTML;
}
// Load users
async function loadUsers() {
try {
const users = await apiCall('/admin/users?limit=100');
displayUsersList(users);
} catch (error) {
console.error('Failed to load users:', error);
document.getElementById('usersList').innerHTML = `
<div class="empty-state">
<p>Failed to load users: ${error.message}</p>
</div>
`;
}
}
// Display users list
function displayUsersList(users) {
const container = document.getElementById('usersList');
if (users.length === 0) {
container.innerHTML = `
<div class="empty-state">
<div class="empty-state-icon">👥</div>
<p>No users found</p>
</div>
`;
return;
}
const tableHTML = `
<table class="data-table">
<thead>
<tr>
<th>ID</th>
<th>Username</th>
<th>Email</th>
<th>Role</th>
<th>Status</th>
<th>Created</th>
</tr>
</thead>
<tbody>
${users.map(u => `
<tr>
<td>${u.id}</td>
<td><strong>${u.username}</strong></td>
<td>${u.email}</td>
<td>${u.role}</td>
<td>
${u.is_active ? '<span class="badge badge-success">Active</span>' : '<span class="badge badge-danger">Inactive</span>'}
</td>
<td>${new Date(u.created_at).toLocaleDateString()}</td>
</tr>
`).join('')}
</tbody>
</table>
`;
container.innerHTML = tableHTML;
}
// Load imports
async function loadImports() {
try {
const imports = await apiCall('/admin/marketplace-import-jobs?limit=100');
displayImportsList(imports);
} catch (error) {
console.error('Failed to load imports:', error);
document.getElementById('importsList').innerHTML = `
<div class="empty-state">
<p>Failed to load import jobs: ${error.message}</p>
</div>
`;
}
}
// Display imports list
function displayImportsList(imports) {
const container = document.getElementById('importsList');
if (imports.length === 0) {
container.innerHTML = `
<div class="empty-state">
<div class="empty-state-icon">📦</div>
<p>No import jobs found</p>
</div>
`;
return;
}
const tableHTML = `
<table class="data-table">
<thead>
<tr>
<th>Job ID</th>
<th>Marketplace</th>
<th>Vendor</th>
<th>Status</th>
<th>Processed</th>
<th>Errors</th>
<th>Created</th>
</tr>
</thead>
<tbody>
${imports.map(j => `
<tr>
<td>#${j.job_id}</td>
<td>${j.marketplace}</td>
<td>${j.vendor_name || '-'}</td>
<td>
${j.status === 'completed' ? '<span class="badge badge-success">Completed</span>' :
j.status === 'failed' ? '<span class="badge badge-danger">Failed</span>' :
'<span class="badge badge-warning">Processing</span>'}
</td>
<td>${j.total_processed || 0}</td>
<td>${j.error_count || 0}</td>
<td>${new Date(j.created_at).toLocaleDateString()}</td>
</tr>
`).join('')}
</tbody>
</table>
`;
container.innerHTML = tableHTML;
}
// Initialize
window.addEventListener('DOMContentLoaded', () => {
if (checkAuth()) {
loadDashboard();
}
});
</script>
</body>
</html>