/**
* =========================================================================
* PLATEFORME ECO-PANNEAU.FR - VERSION 1.0.0
* Interface Administrateur - Onglets Clients et Facturation
* =========================================================================
*/
const { useState } = React;
const {
Users, KeyRound, ShieldAlert, Shield, Zap, MessageSquare,
Trash2, Loader, CheckCircle, Mail, AlertTriangle, FileText,
Download, RefreshCw
} = window;
// =========================================================================
// 1. ONGLET : BASE CLIENTS
// =========================================================================
window.AdminClientsTab = ({ data, refreshData, showToast, setActiveTab }) => {
const [isSaving, setIsSaving] = useState(false);
const [accessRequestModal, setAccessRequestModal] = useState(null);
const [creditModal, setCreditModal] = useState(null);
const [pwdRequest, setPwdRequest] = useState(null);
const [confirmDialog, setConfirmDialog] = useState(null);
const clients = data.clients || [];
const sendAccessRequest = async (uid, mode) => {
setIsSaving(true);
try {
const res = await fetch(window.ECO_CONFIG.apiBaseUrl + 'clients/request_access', {
method: 'POST',
body: JSON.stringify({ uid, mode })
});
if ((await res.json()).status === 'success') {
showToast("Demande d'accès envoyée avec succès.", "success");
setAccessRequestModal(null);
refreshData();
} else {
showToast("Erreur lors de la demande", "error");
}
} catch (e) {
showToast("Erreur réseau", "error");
} finally { setIsSaving(false); }
};
const revokeAccess = async (uid) => {
setConfirmDialog({
title: "Révocation d'accès",
message: "Êtes-vous sûr de vouloir révoquer l'autorisation d'accès technique pour ce client ?",
confirmText: "Oui, révoquer",
isDestructive: true,
onConfirm: async () => {
setIsSaving(true);
try {
const res = await fetch(window.ECO_CONFIG.apiBaseUrl + 'clients/revoke_access', {
method: 'POST',
body: JSON.stringify({ uid })
});
if ((await res.json()).status === 'success') {
showToast("Accès révoqué.", "success");
refreshData();
} else {
showToast("Erreur lors de la révocation", "error");
}
} finally { setIsSaving(false); }
}
});
};
const impersonateClient = async (uid) => {
setIsSaving(true);
try {
const res = await fetch(window.ECO_CONFIG.apiBaseUrl + 'auth/impersonate', {
method: 'POST',
body: JSON.stringify({ uid })
});
if ((await res.json()).status === 'success') {
window.location.href = '?';
} else {
showToast("Erreur d'accès", "error");
}
} finally { setIsSaving(false); }
};
const deleteClient = (uid, clientName) => {
setPwdRequest({
title: `Suppression du compte : ${clientName}`,
desc: (ATTENTION : Saisissez le mot de passe administrateur pour détruire définitivement le compte de {clientName} et toutes ses données associées. Cette action est irréversible.),
onConfirm: async (pwd) => {
setIsSaving(true);
try {
const res = await fetch(window.ECO_CONFIG.apiBaseUrl + 'clients/admin_delete', {
method: 'POST',
body: JSON.stringify({ uid, pwd })
});
if ((await res.json()).status === 'success') {
showToast("Client supprimé avec succès.", "success");
refreshData();
} else {
showToast("Erreur de suppression (Mot de passe incorrect ?)", 'error');
}
} finally { setIsSaving(false); }
}
});
};
const handleAddCredit = async (e) => {
e.preventDefault();
const amount = parseFloat(e.target.amount.value);
const reason = e.target.reason.value;
if (amount <= 0) return showToast("Montant invalide", "error");
setIsSaving(true);
try {
const res = await fetch(window.ECO_CONFIG.apiBaseUrl + 'invoices/credit', {
method: 'POST',
body: JSON.stringify({ client_uid: creditModal.client_uid, amount, reason })
});
const d = await res.json();
if (d.status === 'success') {
showToast("Crédit ajouté avec succès !", "success");
setCreditModal(null);
refreshData();
} else {
showToast(d.message, 'error');
}
} catch (err) {
showToast("Erreur réseau", "error");
}
setIsSaving(false);
};
return (
Base clients
Gestion des comptes et prise de contrôle.
{clients.map(c => {
const isImpersonable = c.admin_access_until && new Date(String(c.admin_access_until || '').replace(' ', 'T')) > new Date();
const isSuspended = c.paymentStatus === 'suspended';
return (
{c.name}
{c.email}
ID : {c.id.substring(0,8)}
{c.wallet_balance !== 0 && (
0 ? 'bg-emerald-100 text-emerald-700' : 'bg-red-100 text-red-700'}`}>
{c.wallet_balance > 0 ? `Solde: +${c.wallet_balance.toFixed(2)}€` : `Dette: ${c.wallet_balance.toFixed(2)}€`}
)}
{isSuspended ? 'Suspendu' : 'Actif'}
{c.discount > 0 && Remise {c.discount}%}
{isImpersonable ? (
) : (
)}
);
})}
{clients.length === 0 && (
)}
{/* MODALES LOCALES */}
{accessRequestModal && (
setAccessRequestModal(null)} zIndex="z-[250]">
Comment souhaitez-vous envoyer la demande d'autorisation d'accès temporaire (24h) à ce client ?
)}
{creditModal && (
setCreditModal(null)} preventClose={isSaving}>
)}
{pwdRequest && (
setPwdRequest(null)} zIndex="z-[250]">
{pwdRequest.desc}
)}
{confirmDialog &&
setConfirmDialog(null)} />}
);
};
// =========================================================================
// 2. ONGLET : FACTURATION GLOBALE
// =========================================================================
window.AdminInvoicesTab = ({ data, refreshData, showToast, setActiveTab }) => {
const [isSaving, setIsSaving] = useState(false);
const [refundModal, setRefundModal] = useState(null);
const clients = data.clients || [];
const handleRefund = async (e) => {
e.preventDefault();
const amount = parseFloat(e.target.amount.value);
const reason = e.target.reason.value;
const mode = e.target.mode.value;
if (amount <= 0 || amount > refundModal.invoice.amount) return showToast("Montant invalide", "error");
setIsSaving(true);
try {
const res = await fetch(window.ECO_CONFIG.apiBaseUrl + 'invoices/refund', {
method: 'POST',
body: JSON.stringify({ invoice_ref: refundModal.invoice.invoiceNumber, amount, mode, reason })
});
const d = await res.json();
if (d.status === 'success') {
showToast("Remboursement ou avoir traité avec succès !", "success");
setRefundModal(null);
refreshData();
} else {
showToast(d.message, 'error');
}
} catch (err) {
showToast("Erreur réseau", "error");
}
setIsSaving(false);
};
return (
Facturation globale
Historique des paiements de tous les clients.
Export CSV
| Date |
Client / Panneau |
Détail |
Montant |
Actions |
{data.invoices?.map((inv, i) => {
const client = clients.find(c => c.id === inv.clientName);
return (
|
{new Date(String(inv.created_at || '').replace(' ', 'T')).toLocaleDateString('fr-FR')}
|
{client?.name || 'Client Inconnu'}
{inv.panneauName}
|
{inv.type} |
{inv.amount} € |
{!inv.type.includes('Avoir') && (
)}
|
);
})}
{(!data.invoices || data.invoices.length === 0) && (
)}
{/* MODALE DE REMBOURSEMENT */}
{refundModal && (
setRefundModal(null)} preventClose={isSaving} zIndex="z-[250]">
)}
);
};
/*
EOF ========== [_www/_react/_admin_clients_factures.jsx]
*/