vBot-2.1 / app /static /admin.html
Ajit Panday
Fix customer list display and make database fields optional
7bff0d0
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vBot Admin Interface</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style>
:root {
--primary-color: #2c3e50;
--secondary-color: #3498db;
--accent-color: #e74c3c;
--background-color: #f8f9fa;
--card-background: #ffffff;
}
body {
background-color: var(--background-color);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.container {
max-width: 1200px;
margin-top: 2rem;
padding: 0 1rem;
}
.page-header {
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
color: white;
padding: 2rem 0;
margin-bottom: 2rem;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.page-header h1 {
margin: 0;
font-size: 2.5rem;
font-weight: 600;
}
.card {
border: none;
border-radius: 15px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
margin-bottom: 2rem;
background: var(--card-background);
transition: transform 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
}
.card-header {
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
color: white;
border-radius: 15px 15px 0 0 !important;
padding: 1.5rem;
}
.card-header h5 {
margin: 0;
font-size: 1.5rem;
font-weight: 600;
}
.card-body {
padding: 2rem;
}
.form-control {
border-radius: 8px;
border: 1px solid #dee2e6;
padding: 0.75rem;
transition: all 0.3s ease;
}
.form-control:focus {
border-color: var(--secondary-color);
box-shadow: 0 0 0 0.2rem rgba(52, 152, 219, 0.25);
}
.btn {
border-radius: 8px;
padding: 0.75rem 1.5rem;
font-weight: 600;
transition: all 0.3s ease;
}
.btn-primary {
background: var(--secondary-color);
border: none;
}
.btn-primary:hover {
background: #2980b9;
transform: translateY(-2px);
}
.btn-success {
background: #2ecc71;
border: none;
}
.btn-success:hover {
background: #27ae60;
transform: translateY(-2px);
}
.table {
border-radius: 10px;
overflow: hidden;
}
.table thead th {
background: var(--primary-color);
color: white;
border: none;
padding: 1rem;
}
.table tbody td {
padding: 1rem;
vertical-align: middle;
}
.badge {
padding: 0.5rem 1rem;
border-radius: 20px;
font-weight: 500;
}
.alert {
border-radius: 10px;
border: none;
padding: 1rem;
}
.token-section {
max-width: 500px;
margin: 0 auto;
}
.hidden {
display: none;
}
.form-label {
font-weight: 500;
color: var(--primary-color);
}
.customer-list {
margin-top: 2rem;
}
.api-key {
background: #f8f9fa;
padding: 0.5rem;
border-radius: 5px;
font-family: monospace;
font-size: 0.9rem;
}
.status-badge {
padding: 0.5rem 1rem;
border-radius: 20px;
font-weight: 500;
}
.status-active {
background: #2ecc71;
color: white;
}
.status-inactive {
background: #e74c3c;
color: white;
}
.loading {
display: none;
text-align: center;
padding: 2rem;
}
.loading i {
font-size: 2rem;
color: var(--secondary-color);
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="container">
<div class="page-header text-center">
<h1><i class="fas fa-robot me-2"></i>vBot Admin Interface</h1>
</div>
<!-- Login Section -->
<div class="card token-section" id="loginSection">
<div class="card-header">
<h5><i class="fas fa-lock me-2"></i>Admin Login</h5>
</div>
<div class="card-body">
<form id="loginForm">
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-user"></i></span>
<input type="text" class="form-control" id="username" required>
</div>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-key"></i></span>
<input type="password" class="form-control" id="password" required>
</div>
</div>
<button type="submit" class="btn btn-primary w-100">
<i class="fas fa-sign-in-alt me-2"></i>Login
</button>
</form>
<div id="tokenStatus" class="mt-3"></div>
</div>
</div>
<!-- Main Content (Hidden until login) -->
<div id="mainContent" class="hidden">
<!-- Add Customer Section -->
<div class="card">
<div class="card-header">
<h5><i class="fas fa-user-plus me-2"></i>Add New Customer</h5>
</div>
<div class="card-body">
<form id="customerForm">
<div class="row">
<div class="col-md-6 mb-3">
<label for="name" class="form-label">Name</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-user"></i></span>
<input type="text" class="form-control" id="name" required>
</div>
</div>
<div class="col-md-6 mb-3">
<label for="company_name" class="form-label">Company Name</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-building"></i></span>
<input type="text" class="form-control" id="company_name" required>
</div>
</div>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-envelope"></i></span>
<input type="email" class="form-control" id="email" required>
</div>
</div>
<button type="submit" class="btn btn-success w-100">
<i class="fas fa-plus-circle me-2"></i>Add Customer
</button>
</form>
<div id="customerStatus" class="mt-3"></div>
</div>
</div>
<!-- Customer List Section -->
<div class="card customer-list">
<div class="card-header">
<h5><i class="fas fa-users me-2"></i>Customer List</h5>
</div>
<div class="card-body">
<div class="loading">
<i class="fas fa-spinner"></i>
</div>
<div id="customerList" class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Company</th>
<th>Email</th>
<th>API Key</th>
<th>Status</th>
</tr>
</thead>
<tbody id="customerTableBody"></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script>
let accessToken = null;
// Login Form Handler
document.getElementById('loginForm').addEventListener('submit', async (e) => {
e.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
try {
const response = await fetch('/api/v1/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`
});
if (response.ok) {
const data = await response.json();
accessToken = data.access_token;
document.getElementById('tokenStatus').innerHTML =
'<div class="alert alert-success"><i class="fas fa-check-circle me-2"></i>Login successful!</div>';
// Show main content and hide login section
document.getElementById('loginSection').classList.add('hidden');
document.getElementById('mainContent').classList.remove('hidden');
loadCustomers();
} else {
document.getElementById('tokenStatus').innerHTML =
'<div class="alert alert-danger"><i class="fas fa-exclamation-circle me-2"></i>Login failed. Please check your credentials.</div>';
}
} catch (error) {
document.getElementById('tokenStatus').innerHTML =
'<div class="alert alert-danger"><i class="fas fa-exclamation-circle me-2"></i>Error connecting to server.</div>';
}
});
// Customer Form Handler
document.getElementById('customerForm').addEventListener('submit', async (e) => {
e.preventDefault();
if (!accessToken) {
document.getElementById('customerStatus').innerHTML =
'<div class="alert alert-danger"><i class="fas fa-exclamation-circle me-2"></i>Please login first.</div>';
return;
}
const customerData = {
name: document.getElementById('name').value,
company_name: document.getElementById('company_name').value,
email: document.getElementById('email').value
};
try {
console.log('Sending customer data:', { ...customerData, db_password: '***' });
const response = await fetch('/api/v1/customers/', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(customerData)
});
const data = await response.json();
console.log('Server response:', data);
if (response.ok) {
document.getElementById('customerStatus').innerHTML =
'<div class="alert alert-success"><i class="fas fa-check-circle me-2"></i>Customer added successfully!</div>';
document.getElementById('customerForm').reset();
loadCustomers();
} else {
document.getElementById('customerStatus').innerHTML =
`<div class="alert alert-danger"><i class="fas fa-exclamation-circle me-2"></i>Error: ${data.detail || 'Unknown error occurred'}</div>`;
}
} catch (error) {
console.error('Error creating customer:', error);
document.getElementById('customerStatus').innerHTML =
`<div class="alert alert-danger"><i class="fas fa-exclamation-circle me-2"></i>Error connecting to server: ${error.message}</div>`;
}
});
// Load Customers
async function loadCustomers() {
if (!accessToken) return;
const loading = document.querySelector('.loading');
loading.style.display = 'block';
try {
const response = await fetch('/api/v1/customers/', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
if (response.ok) {
const customers = await response.json();
const tbody = document.getElementById('customerTableBody');
tbody.innerHTML = '';
if (customers.length === 0) {
tbody.innerHTML = `
<tr>
<td colspan="5" class="text-center">No customers found</td>
</tr>
`;
return;
}
customers.forEach(customer => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${customer.name}</td>
<td>${customer.company_name}</td>
<td>${customer.email}</td>
<td><code class="api-key">${customer.api_key}</code></td>
<td>
<span class="status-badge ${customer.is_active ? 'status-active' : 'status-inactive'}">
${customer.is_active ? '<i class="fas fa-check-circle me-1"></i>Active' : '<i class="fas fa-times-circle me-1"></i>Inactive'}
</span>
</td>
`;
tbody.appendChild(tr);
});
} else {
const error = await response.json();
console.error('Error loading customers:', error);
document.getElementById('customerList').innerHTML = `
<div class="alert alert-danger">
<i class="fas fa-exclamation-circle me-2"></i>
Error loading customers: ${error.detail || 'Unknown error'}
</div>
`;
}
} catch (error) {
console.error('Error loading customers:', error);
document.getElementById('customerList').innerHTML = `
<div class="alert alert-danger">
<i class="fas fa-exclamation-circle me-2"></i>
Error connecting to server
</div>
`;
} finally {
loading.style.display = 'none';
}
}
</script>
</body>
</html>