Files
wortis_tpe/lib/index.html

1340 lines
50 KiB
HTML

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard Admin TPE - Wortis</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary: #2563eb;
--primary-dark: #1e40af;
--success: #10b981;
--warning: #f59e0b;
--danger: #ef4444;
--dark: #1e293b;
--light: #f8fafc;
--border: #e2e8f0;
--text: #334155;
--text-light: #64748b;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: var(--light);
color: var(--text);
line-height: 1.6;
}
.container {
display: flex;
min-height: 100vh;
}
/* Sidebar */
.sidebar {
width: 260px;
background: var(--dark);
color: white;
padding: 20px 0;
position: fixed;
height: 100vh;
overflow-y: auto;
transition: transform 0.3s;
}
.sidebar.hidden {
transform: translateX(-100%);
}
.logo {
padding: 0 20px 20px;
border-bottom: 1px solid rgba(255,255,255,0.1);
margin-bottom: 20px;
}
.logo h2 {
font-size: 24px;
color: white;
}
.logo p {
font-size: 12px;
color: rgba(255,255,255,0.6);
}
.nav-menu {
list-style: none;
}
.nav-item {
padding: 12px 20px;
cursor: pointer;
transition: all 0.3s;
display: flex;
align-items: center;
gap: 12px;
border-left: 3px solid transparent;
}
.nav-item:hover {
background: rgba(255,255,255,0.1);
border-left-color: var(--primary);
}
.nav-item.active {
background: rgba(37,99,235,0.2);
border-left-color: var(--primary);
}
.nav-item span {
font-size: 20px;
}
/* Main Content */
.main-content {
flex: 1;
margin-left: 260px;
padding: 20px;
transition: margin-left 0.3s;
}
.main-content.expanded {
margin-left: 0;
}
/* Header */
.header {
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
font-size: 28px;
color: var(--dark);
}
.header-actions {
display: flex;
gap: 10px;
align-items: center;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.3s;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: var(--primary);
color: white;
}
.btn-primary:hover {
background: var(--primary-dark);
}
.btn-success {
background: var(--success);
color: white;
}
.btn-danger {
background: var(--danger);
color: white;
}
.btn-secondary {
background: var(--border);
color: var(--text);
}
/* Stats Cards */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.stat-card {
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
display: flex;
justify-content: space-between;
align-items: center;
}
.stat-info h3 {
font-size: 14px;
color: var(--text-light);
margin-bottom: 5px;
}
.stat-info p {
font-size: 28px;
font-weight: bold;
color: var(--dark);
}
.stat-icon {
width: 60px;
height: 60px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 30px;
}
.stat-icon.blue { background: rgba(37,99,235,0.1); color: var(--primary); }
.stat-icon.green { background: rgba(16,185,129,0.1); color: var(--success); }
.stat-icon.orange { background: rgba(245,158,11,0.1); color: var(--warning); }
.stat-icon.red { background: rgba(239,68,68,0.1); color: var(--danger); }
/* Content Sections */
.content-section {
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
display: none;
}
.content-section.active {
display: block;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 2px solid var(--border);
}
.section-header h2 {
font-size: 22px;
color: var(--dark);
}
/* Search and Filters */
.filters {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.search-box {
flex: 1;
min-width: 250px;
padding: 10px 15px;
border: 1px solid var(--border);
border-radius: 6px;
font-size: 14px;
}
.filter-select {
padding: 10px 15px;
border: 1px solid var(--border);
border-radius: 6px;
font-size: 14px;
cursor: pointer;
}
/* Table */
.table-container {
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
}
th {
background: var(--light);
padding: 12px;
text-align: left;
font-weight: 600;
color: var(--dark);
border-bottom: 2px solid var(--border);
}
td {
padding: 12px;
border-bottom: 1px solid var(--border);
}
tr:hover {
background: var(--light);
}
.badge {
padding: 4px 10px;
border-radius: 20px;
font-size: 12px;
font-weight: 500;
}
.badge.active { background: rgba(16,185,129,0.1); color: var(--success); }
.badge.inactive { background: rgba(239,68,68,0.1); color: var(--danger); }
.badge.pending { background: rgba(245,158,11,0.1); color: var(--warning); }
.badge.completed { background: rgba(16,185,129,0.1); color: var(--success); }
.badge.agent { background: rgba(37,99,235,0.1); color: var(--primary); }
.badge.enterprise { background: rgba(139,92,246,0.1); color: #8b5cf6; }
/* Modal */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 1000;
align-items: center;
justify-content: center;
}
.modal.active {
display: flex;
}
.modal-content {
background: white;
padding: 30px;
border-radius: 10px;
max-width: 500px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
}
.modal-header {
margin-bottom: 20px;
}
.modal-header h3 {
font-size: 22px;
color: var(--dark);
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: var(--dark);
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 10px;
border: 1px solid var(--border);
border-radius: 6px;
font-size: 14px;
}
.modal-actions {
display: flex;
gap: 10px;
margin-top: 20px;
}
/* Loading */
.loading {
text-align: center;
padding: 40px;
color: var(--text-light);
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid var(--border);
border-top-color: var(--primary);
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Responsive */
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
}
.sidebar.visible {
transform: translateX(0);
}
.main-content {
margin-left: 0;
}
.stats-grid {
grid-template-columns: 1fr;
}
.filters {
flex-direction: column;
}
}
/* Action buttons in table */
.action-btns {
display: flex;
gap: 5px;
}
.btn-sm {
padding: 5px 10px;
font-size: 12px;
}
/* Pagination */
.pagination {
display: flex;
justify-content: center;
gap: 5px;
margin-top: 20px;
}
.page-btn {
padding: 8px 12px;
border: 1px solid var(--border);
background: white;
cursor: pointer;
border-radius: 4px;
}
.page-btn.active {
background: var(--primary);
color: white;
border-color: var(--primary);
}
.toggle-sidebar {
display: none;
background: var(--primary);
color: white;
border: none;
padding: 10px;
cursor: pointer;
border-radius: 6px;
}
@media (max-width: 768px) {
.toggle-sidebar {
display: block;
}
}
</style>
</head>
<body>
<div class="container">
<!-- Sidebar -->
<aside class="sidebar" id="sidebar">
<div class="logo">
<h2>🏦 TPE Admin</h2>
<p>Tableau de bord</p>
</div>
<ul class="nav-menu">
<li class="nav-item active" data-section="dashboard">
<span>📊</span> Dashboard
</li>
<li class="nav-item" data-section="agents">
<span>👥</span> Agents
</li>
<li class="nav-item" data-section="enterprises">
<span>🏢</span> Entreprises
</li>
<li class="nav-item" data-section="transactions">
<span>💳</span> Transactions
</li>
<li class="nav-item" data-section="recharges">
<span>💰</span> Recharges
</li>
<li class="nav-item" data-section="services">
<span>⚙️</span> Services
</li>
</ul>
</aside>
<!-- Main Content -->
<main class="main-content" id="mainContent">
<!-- Header -->
<div class="header">
<div>
<button class="toggle-sidebar" onclick="toggleSidebar()"></button>
<h1 id="pageTitle">Dashboard</h1>
</div>
<div class="header-actions">
<button class="btn btn-primary" onclick="refreshData()">🔄 Actualiser</button>
</div>
</div>
<!-- Dashboard Section -->
<section class="content-section active" id="dashboard">
<div class="stats-grid" id="statsGrid">
<div class="loading">
<div class="spinner"></div>
<p>Chargement des statistiques...</p>
</div>
</div>
</section>
<!-- Agents Section -->
<section class="content-section" id="agents">
<div class="section-header">
<h2>Gestion des Agents</h2>
<button class="btn btn-primary" onclick="openModal('createAgent')">+ Nouvel Agent</button>
</div>
<div class="filters">
<input type="text" class="search-box" placeholder="🔍 Rechercher un agent..." onkeyup="searchAgents(this.value)">
<select class="filter-select" onchange="filterAgents(this.value)">
<option value="">Tous les statuts</option>
<option value="active">Actif</option>
<option value="inactive">Inactif</option>
<option value="blocked">Bloqué</option>
</select>
<select class="filter-select" onchange="filterAgentType(this.value)">
<option value="">Tous les types</option>
<option value="agent">Agent individuel</option>
<option value="enterprise_member">Membre entreprise</option>
</select>
</div>
<div class="table-container">
<table id="agentsTable">
<thead>
<tr>
<th>ID Agent</th>
<th>Nom</th>
<th>Type</th>
<th>Solde</th>
<th>Statut</th>
<th>Date création</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="agentsTableBody">
<tr><td colspan="7" class="loading">Chargement...</td></tr>
</tbody>
</table>
</div>
<div class="pagination" id="agentsPagination"></div>
</section>
<!-- Enterprises Section -->
<section class="content-section" id="enterprises">
<div class="section-header">
<h2>Gestion des Entreprises</h2>
<button class="btn btn-primary" onclick="openModal('createEnterprise')">+ Nouvelle Entreprise</button>
</div>
<div class="filters">
<input type="text" class="search-box" placeholder="🔍 Rechercher une entreprise...">
<select class="filter-select">
<option value="">Tous les statuts</option>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th>ID</th>
<th>Nom</th>
<th>Email</th>
<th>Solde</th>
<th>Membres</th>
<th>Transactions</th>
<th>Statut</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="enterprisesTableBody">
<tr><td colspan="8" class="loading">Chargement...</td></tr>
</tbody>
</table>
</div>
</section>
<!-- Transactions Section -->
<section class="content-section" id="transactions">
<div class="section-header">
<h2>Historique des Transactions</h2>
<button class="btn btn-secondary" onclick="exportTransactions()">📥 Exporter</button>
</div>
<div class="filters">
<input type="text" class="search-box" placeholder="🔍 Rechercher...">
<input type="date" class="filter-select" onchange="filterByDate()">
<select class="filter-select">
<option value="">Tous les services</option>
<option value="Internet">Internet</option>
<option value="Pelisa">Pelisa</option>
<option value="E2C">E2C</option>
</select>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th>ID Transaction</th>
<th>Agent</th>
<th>Service</th>
<th>Montant</th>
<th>Commission</th>
<th>Statut</th>
<th>Date</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="transactionsTableBody">
<tr><td colspan="8" class="loading">Chargement...</td></tr>
</tbody>
</table>
</div>
</section>
<!-- Recharges Section -->
<section class="content-section" id="recharges">
<div class="section-header">
<h2>Demandes de Recharge</h2>
</div>
<div class="filters">
<select class="filter-select" onchange="filterRecharges(this.value)">
<option value="">Tous</option>
<option value="pending">En attente</option>
<option value="approved">Approuvée</option>
<option value="rejected">Rejetée</option>
</select>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th>ID</th>
<th>Agent/Entreprise</th>
<th>Type</th>
<th>Montant</th>
<th>Statut</th>
<th>Date demande</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="rechargesTableBody">
<tr><td colspan="7" class="loading">Chargement...</td></tr>
</tbody>
</table>
</div>
</section>
<!-- Services Section -->
<section class="content-section" id="services">
<div class="section-header">
<h2>Gestion des Services</h2>
<button class="btn btn-primary" onclick="openModal('createService')">+ Nouveau Service</button>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th>Service</th>
<th>Description</th>
<th>Rang</th>
<th>Statut</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="servicesTableBody">
<tr><td colspan="5" class="loading">Chargement...</td></tr>
</tbody>
</table>
</div>
</section>
</main>
</div>
<!-- Modal: Create Agent -->
<div class="modal" id="createAgentModal">
<div class="modal-content">
<div class="modal-header">
<h3>Créer un Nouvel Agent</h3>
</div>
<form onsubmit="createAgent(event)">
<div class="form-group">
<label>Nom complet *</label>
<input type="text" name="nom" required>
</div>
<div class="form-group">
<label>Téléphone</label>
<input type="tel" name="telephone">
</div>
<div class="form-group">
<label>Email</label>
<input type="email" name="email">
</div>
<div class="form-group">
<label>PIN (6 chiffres minimum) *</label>
<input type="password" name="pin" minlength="6" required>
</div>
<div class="form-group">
<label>Rôle</label>
<select name="role">
<option value="agent">Agent</option>
<option value="admin">Admin</option>
</select>
</div>
<div class="modal-actions">
<button type="button" class="btn btn-secondary" onclick="closeModal('createAgent')">Annuler</button>
<button type="submit" class="btn btn-primary">Créer l'agent</button>
</div>
</form>
</div>
</div>
<!-- Modal: Create Enterprise -->
<div class="modal" id="createEnterpriseModal">
<div class="modal-content">
<div class="modal-header">
<h3>Créer une Nouvelle Entreprise</h3>
</div>
<form onsubmit="createEnterprise(event)">
<div class="form-group">
<label>Nom de l'entreprise *</label>
<input type="text" name="nom_entreprise" required>
</div>
<div class="form-group">
<label>Email *</label>
<input type="email" name="email_entreprise" required>
</div>
<div class="form-group">
<label>Téléphone *</label>
<input type="tel" name="telephone_entreprise" required>
</div>
<div class="form-group">
<label>Adresse</label>
<input type="text" name="adresse">
</div>
<div class="form-group">
<label>Secteur d'activité</label>
<input type="text" name="secteur_activite">
</div>
<div class="modal-actions">
<button type="button" class="btn btn-secondary" onclick="closeModal('createEnterprise')">Annuler</button>
<button type="submit" class="btn btn-primary">Créer l'entreprise</button>
</div>
</form>
</div>
</div>
<!-- Modal: Recharge Agent -->
<div class="modal" id="rechargeModal">
<div class="modal-content">
<div class="modal-header">
<h3>Recharger le Solde</h3>
</div>
<form onsubmit="rechargeAgent(event)">
<input type="hidden" name="agent_id" id="rechargeAgentId">
<div class="form-group">
<label>Agent</label>
<input type="text" id="rechargeAgentName" readonly>
</div>
<div class="form-group">
<label>Montant (FCFA) *</label>
<input type="number" name="montant" min="100" required>
</div>
<div class="modal-actions">
<button type="button" class="btn btn-secondary" onclick="closeModal('recharge')">Annuler</button>
<button type="submit" class="btn btn-success">Recharger</button>
</div>
</form>
</div>
</div>
<script>
const API_URL = 'https://api.live.wortis.cg/tpe';
let currentPage = 1;
let currentFilter = {};
// Toggle Sidebar
function toggleSidebar() {
document.getElementById('sidebar').classList.toggle('visible');
}
// Navigation
document.querySelectorAll('.nav-item').forEach(item => {
item.addEventListener('click', () => {
const section = item.dataset.section;
// Update active nav
document.querySelectorAll('.nav-item').forEach(i => i.classList.remove('active'));
item.classList.add('active');
// Update active section
document.querySelectorAll('.content-section').forEach(s => s.classList.remove('active'));
document.getElementById(section).classList.add('active');
// Update title
const titles = {
'dashboard': 'Dashboard',
'agents': 'Gestion des Agents',
'enterprises': 'Gestion des Entreprises',
'transactions': 'Historique des Transactions',
'recharges': 'Demandes de Recharge',
'services': 'Gestion des Services'
};
document.getElementById('pageTitle').textContent = titles[section];
// Load data
loadSectionData(section);
});
});
// Load Section Data
async function loadSectionData(section) {
switch(section) {
case 'dashboard':
await loadDashboard();
break;
case 'agents':
await loadAgents();
break;
case 'enterprises':
await loadEnterprises();
break;
case 'transactions':
await loadTransactions();
break;
case 'recharges':
await loadRecharges();
break;
case 'services':
await loadServices();
break;
}
}
// Load Dashboard
async function loadDashboard() {
try {
const response = await fetch(`${API_URL}/admin/system/stats`);
const data = await response.json();
if (data.success) {
const stats = data.stats;
document.getElementById('statsGrid').innerHTML = `
<div class="stat-card">
<div class="stat-info">
<h3>Agents Actifs</h3>
<p>${stats.active_agents || 0}</p>
</div>
<div class="stat-icon blue">👥</div>
</div>
<div class="stat-card">
<div class="stat-info">
<h3>Entreprises</h3>
<p>${stats.total_enterprises || 0}</p>
</div>
<div class="stat-icon green">🏢</div>
</div>
<div class="stat-card">
<div class="stat-info">
<h3>Transactions (Aujourd'hui)</h3>
<p>${stats.daily_transactions || 0}</p>
</div>
<div class="stat-icon orange">💳</div>
</div>
<div class="stat-card">
<div class="stat-info">
<h3>Volume Total</h3>
<p>${formatMoney(stats.total_volume || 0)}</p>
</div>
<div class="stat-icon blue">💰</div>
</div>
<div class="stat-card">
<div class="stat-info">
<h3>Commissions Totales</h3>
<p>${formatMoney(stats.total_commissions || 0)}</p>
</div>
<div class="stat-icon green">📈</div>
</div>
<div class="stat-card">
<div class="stat-info">
<h3>Taux de Succès</h3>
<p>${stats.success_rate || 0}%</p>
</div>
<div class="stat-icon green">✅</div>
</div>
<div class="stat-card">
<div class="stat-info">
<h3>Recharges en Attente</h3>
<p>${stats.pending_recharges || 0}</p>
</div>
<div class="stat-icon orange">⏳</div>
</div>
<div class="stat-card">
<div class="stat-info">
<h3>Membres Entreprise</h3>
<p>${stats.total_enterprise_members || 0}</p>
</div>
<div class="stat-icon blue">👔</div>
</div>
`;
}
} catch (error) {
console.error('Erreur chargement dashboard:', error);
showNotification('Erreur de chargement des statistiques', 'error');
}
}
// Load Agents
async function loadAgents(page = 1) {
try {
const response = await fetch(`${API_URL}/admin/agents?page=${page}&limit=50`);
const data = await response.json();
if (data.success) {
const tbody = document.getElementById('agentsTableBody');
tbody.innerHTML = data.agents.map(agent => `
<tr>
<td><strong>${agent.agent_id}</strong></td>
<td>${agent.nom}</td>
<td><span class="badge ${agent.type === 'enterprise_member' ? 'enterprise' : 'agent'}">${agent.type === 'enterprise_member' ? 'Entreprise' : 'Individuel'}</span></td>
<td><strong>${formatMoney(agent.solde || 0)}</strong></td>
<td><span class="badge ${agent.status || 'active'}">${getStatusLabel(agent.status || 'active')}</span></td>
<td>${formatDate(agent.created_at)}</td>
<td>
<div class="action-btns">
<button class="btn btn-sm btn-primary" onclick="viewAgent('${agent.agent_id}')">👁️</button>
<button class="btn btn-sm btn-success" onclick="openRechargeModal('${agent.agent_id}', '${agent.nom}')">💰</button>
<button class="btn btn-sm btn-danger" onclick="toggleAgentStatus('${agent.agent_id}', '${agent.status}')">🔒</button>
</div>
</td>
</tr>
`).join('');
// Update pagination
updatePagination('agents', data.pagination);
}
} catch (error) {
console.error('Erreur chargement agents:', error);
showNotification('Erreur de chargement des agents', 'error');
}
}
// Load Enterprises
async function loadEnterprises() {
try {
const response = await fetch(`${API_URL}/enterprises`);
const data = await response.json();
if (data.success) {
const tbody = document.getElementById('enterprisesTableBody');
tbody.innerHTML = data.enterprises.map(ent => `
<tr>
<td><strong>${ent.enterprise_id}</strong></td>
<td>${ent.nom_entreprise}</td>
<td>${ent.email_entreprise}</td>
<td><strong>${formatMoney(ent.solde_entreprise || 0)}</strong></td>
<td>${ent.total_members || 0}</td>
<td>${ent.total_transactions || 0}</td>
<td><span class="badge ${ent.status}">${getStatusLabel(ent.status)}</span></td>
<td>
<div class="action-btns">
<button class="btn btn-sm btn-primary" onclick="viewEnterprise('${ent.enterprise_id}')">👁️</button>
<button class="btn btn-sm btn-success" onclick="rechargeEnterprise('${ent.enterprise_id}')">💰</button>
<button class="btn btn-sm btn-secondary" onclick="viewMembers('${ent.enterprise_id}')">👥</button>
</div>
</td>
</tr>
`).join('');
}
} catch (error) {
console.error('Erreur chargement entreprises:', error);
showNotification('Erreur de chargement des entreprises', 'error');
}
}
// Load Transactions
async function loadTransactions() {
try {
// Pour l'instant, on affiche un message
const tbody = document.getElementById('transactionsTableBody');
tbody.innerHTML = `
<tr>
<td colspan="8" style="text-align: center; padding: 40px;">
<p style="font-size: 18px; color: var(--text-light);">
📊 Pour voir les transactions d'un agent spécifique,<br>
utilisez l'endpoint: /tpe/agent/{agent_id}/transactions
</p>
</td>
</tr>
`;
} catch (error) {
console.error('Erreur chargement transactions:', error);
}
}
// Load Recharges
async function loadRecharges() {
try {
const response = await fetch(`${API_URL}/recharge/get_recharges`);
const data = await response.json();
if (data.success) {
const tbody = document.getElementById('rechargesTableBody');
tbody.innerHTML = data.all_recharges.map(recharge => `
<tr>
<td>${recharge._id.$oid.substring(0, 8)}...</td>
<td><strong>${recharge.agent_id || recharge.enterprise_id}</strong></td>
<td><span class="badge ${recharge.type === 'enterprise_recharge' ? 'enterprise' : 'agent'}">${recharge.type === 'enterprise_recharge' ? 'Entreprise' : 'Agent'}</span></td>
<td><strong>${formatMoney(recharge.montant)}</strong></td>
<td><span class="badge ${recharge.status}">${getStatusLabel(recharge.status)}</span></td>
<td>${formatDate(recharge.created_at)}</td>
<td>
${recharge.status === 'pending' ? `
<div class="action-btns">
<button class="btn btn-sm btn-success" onclick="approveRecharge('${recharge._id.$oid}', '${recharge.agent_id}')">✅ Approuver</button>
<button class="btn btn-sm btn-danger" onclick="rejectRecharge('${recharge._id.$oid}')">❌ Rejeter</button>
</div>
` : `<span class="badge ${recharge.status}">${getStatusLabel(recharge.status)}</span>`}
</td>
</tr>
`).join('');
}
} catch (error) {
console.error('Erreur chargement recharges:', error);
showNotification('Erreur de chargement des recharges', 'error');
}
}
// Load Services
async function loadServices() {
try {
const response = await fetch(`${API_URL}/get_services_tpe`);
const data = await response.json();
if (data.all_services) {
const tbody = document.getElementById('servicesTableBody');
tbody.innerHTML = data.all_services.map(service => `
<tr>
<td><strong>${service.service_name || service.name}</strong></td>
<td>${service.description || 'N/A'}</td>
<td>${service.rang || 'N/A'}</td>
<td><span class="badge active">Actif</span></td>
<td>
<div class="action-btns">
<button class="btn btn-sm btn-primary">✏️ Modifier</button>
</div>
</td>
</tr>
`).join('');
}
} catch (error) {
console.error('Erreur chargement services:', error);
showNotification('Erreur de chargement des services', 'error');
}
}
// Create Agent
async function createAgent(event) {
event.preventDefault();
const formData = new FormData(event.target);
const data = Object.fromEntries(formData);
try {
const response = await fetch(`${API_URL}/users/register`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
showNotification(`Agent créé avec succès! ID: ${result.generated_agent_id}`, 'success');
closeModal('createAgent');
loadAgents();
} else {
showNotification(result.message, 'error');
}
} catch (error) {
showNotification('Erreur lors de la création de l\'agent', 'error');
}
}
// Create Enterprise
async function createEnterprise(event) {
event.preventDefault();
const formData = new FormData(event.target);
const data = Object.fromEntries(formData);
try {
const response = await fetch(`${API_URL}/enterprise/create`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
showNotification(`Entreprise créée avec succès! ID: ${result.enterprise.enterprise_id}`, 'success');
closeModal('createEnterprise');
loadEnterprises();
} else {
showNotification(result.message, 'error');
}
} catch (error) {
showNotification('Erreur lors de la création de l\'entreprise', 'error');
}
}
// Recharge Agent
function openRechargeModal(agentId, agentName) {
document.getElementById('rechargeAgentId').value = agentId;
document.getElementById('rechargeAgentName').value = `${agentId} - ${agentName}`;
document.getElementById('rechargeModal').classList.add('active');
}
async function rechargeAgent(event) {
event.preventDefault();
const formData = new FormData(event.target);
const agentId = formData.get('agent_id');
const montant = formData.get('montant');
try {
const response = await fetch(`${API_URL}/agent/${agentId}/recharge`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ montant: parseFloat(montant) })
});
const result = await response.json();
if (result.success) {
showNotification(`Recharge de ${formatMoney(montant)} effectuée avec succès!`, 'success');
closeModal('recharge');
loadAgents();
} else {
showNotification(result.message, 'error');
}
} catch (error) {
showNotification('Erreur lors de la recharge', 'error');
}
}
// Approve Recharge
async function approveRecharge(rechargeId, agentId) {
if (!confirm('Confirmer l\'approbation de cette recharge?')) return;
const pin = prompt('Entrez votre PIN pour approuver:');
if (!pin) return;
try {
const response = await fetch(`${API_URL}/recharge/approve/${rechargeId}/agent/${agentId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
pin: pin,
approved_by: 'admin'
})
});
const result = await response.json();
if (result.success) {
showNotification('Recharge approuvée avec succès!', 'success');
loadRecharges();
} else {
showNotification(result.message, 'error');
}
} catch (error) {
showNotification('Erreur lors de l\'approbation', 'error');
}
}
// View Agent Details
async function viewAgent(agentId) {
try {
const response = await fetch(`${API_URL}/agent/${agentId}/balance`);
const data = await response.json();
if (data.success) {
alert(`
Détails de l'Agent
━━━━━━━━━━━━━━━━━━━━
ID: ${data.agent_id}
Nom: ${data.nom}
Type: ${data.type === 'enterprise_member' ? 'Membre Entreprise' : 'Agent Individuel'}
Solde: ${formatMoney(data.solde)} FCFA
Transactions: ${data.total_transactions}
Commissions: ${formatMoney(data.total_commission)} FCFA
`);
}
} catch (error) {
showNotification('Erreur lors de la récupération des détails', 'error');
}
}
// View Enterprise Details
async function viewEnterprise(enterpriseId) {
try {
const response = await fetch(`${API_URL}/enterprise/${enterpriseId}/balance`);
const data = await response.json();
if (data.success) {
alert(`
Détails de l'Entreprise
━━━━━━━━━━━━━━━━━━━━━━━
ID: ${data.enterprise_id}
Nom: ${data.nom_entreprise}
Solde: ${formatMoney(data.solde_entreprise)} FCFA
Membres: ${data.total_members}
Transactions: ${data.statistics.total_transactions}
Volume Total: ${formatMoney(data.statistics.total_volume)} FCFA
Commissions: ${formatMoney(data.statistics.total_commissions)} FCFA
`);
}
} catch (error) {
showNotification('Erreur lors de la récupération des détails', 'error');
}
}
// Modal Functions
function openModal(modalName) {
document.getElementById(`${modalName}Modal`).classList.add('active');
}
function closeModal(modalName) {
document.getElementById(`${modalName}Modal`).classList.remove('active');
}
// Utility Functions
function formatMoney(amount) {
return new Intl.NumberFormat('fr-FR').format(amount) + ' FCFA';
}
function formatDate(dateString) {
if (!dateString) return 'N/A';
const date = new Date(dateString);
return date.toLocaleDateString('fr-FR', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
function getStatusLabel(status) {
const labels = {
'active': 'Actif',
'inactive': 'Inactif',
'blocked': 'Bloqué',
'pending': 'En attente',
'approved': 'Approuvée',
'rejected': 'Rejetée',
'completed': 'Complétée'
};
return labels[status] || status;
}
function showNotification(message, type = 'info') {
alert(message); // Simplification - vous pouvez implémenter un meilleur système de notification
}
function updatePagination(section, paginationData) {
const container = document.getElementById(`${section}Pagination`);
if (!container || !paginationData) return;
let html = '';
for (let i = 1; i <= paginationData.pages; i++) {
html += `<button class="page-btn ${i === paginationData.page ? 'active' : ''}" onclick="loadAgents(${i})">${i}</button>`;
}
container.innerHTML = html;
}
function refreshData() {
const activeSection = document.querySelector('.content-section.active').id;
loadSectionData(activeSection);
showNotification('Données actualisées', 'success');
}
function searchAgents(query) {
// Implementation de la recherche
console.log('Recherche:', query);
}
function filterAgents(status) {
currentFilter.status = status;
loadAgents();
}
function filterAgentType(type) {
currentFilter.type = type;
loadAgents();
}
function filterRecharges(status) {
// Implémenter le filtre
loadRecharges();
}
function exportTransactions() {
showNotification('Fonctionnalité d\'export en cours de développement', 'info');
}
// Close modal on outside click
document.querySelectorAll('.modal').forEach(modal => {
modal.addEventListener('click', (e) => {
if (e.target === modal) {
modal.classList.remove('active');
}
});
});
// Initialize
document.addEventListener('DOMContentLoaded', () => {
loadDashboard();
});
</script>
</body>
</html>