document.addEventListener("DOMContentLoaded", () => { // Éléments du DOM const yearFilter = document.getElementById("yearFilter"); const categoryFilter = document.getElementById("categoryFilter"); const typeFilter = document.getElementById("typeFilter"); const applyFilters = document.getElementById("applyFilters"); const transactionsList = document.getElementById("transactionsList"); const totalRevenue = document.getElementById("totalRevenue"); const totalExpenses = document.getElementById("totalExpenses"); const balanceAmount = document.getElementById("balanceAmount"); const revenuesByCategoryList = document.getElementById( "revenuesByCategoryList" ); const expensesByCategoryList = document.getElementById( "expensesByCategoryList" ); // Boutons d'onglets const tabButtons = document.querySelectorAll(".tab-button"); const tabContents = document.querySelectorAll(".tab-content"); // Modal d'importation const importButton = document.getElementById("importButton"); const importModal = document.getElementById("importModal"); const closeImportModal = document.getElementById("closeImportModal"); const importRevenues = document.getElementById("importRevenues"); const importExpenses = document.getElementById("importExpenses"); // Variables pour les graphiques let revenuesChart = null; let expensesChart = null; // Initialiser la page initPage(); // Gestionnaire d'événements pour les filtres applyFilters.addEventListener("click", () => { loadData({ year: yearFilter.value, category: categoryFilter.value, type: typeFilter.value, }); }); // Gestionnaires d'événements pour les onglets tabButtons.forEach((button) => { button.addEventListener("click", () => { const tabId = button.id.replace("tab", "").toLowerCase(); switchTab(tabId); }); }); // Gestionnaires d'événements pour le modal d'importation importButton.addEventListener("click", () => { importModal.classList.remove("hidden"); }); closeImportModal.addEventListener("click", () => { importModal.classList.add("hidden"); }); importRevenues.addEventListener("click", () => { importData("revenue"); }); importExpenses.addEventListener("click", () => { importData("expense"); }); // Fonction d'initialisation function initPage() { // Années pour le filtre (année courante et les deux précédentes) const currentYear = new Date().getFullYear(); yearFilter.innerHTML = ``; for (let year = currentYear; year >= currentYear - 2; year--) { yearFilter.innerHTML += ``; } // Charger les données initiales loadData(); // Générer les couleurs pour les graphiques generateChartColors(); } // Fonction pour charger les données async function loadData(filters = {}) { try { const response = await fetch( "/api/accounting?" + new URLSearchParams(filters) ); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); // Mettre à jour les statistiques updateStatistics(data.statistics); // Mettre à jour les transactions displayTransactions(data.transactions); // Mettre à jour les catégories de filtres updateCategoryFilters(data.categories); // Mettre à jour les graphiques updateCharts(data.statistics); } catch (error) { console.error("Erreur lors du chargement des données:", error); showMessage("Erreur lors du chargement des données", "error"); } } // Fonction pour importer des données async function importData(type) { try { const response = await fetch(`/api/accounting/import/${type}`, { method: "POST", }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); showMessage(result.message || "Importation réussie"); importModal.classList.add("hidden"); // Recharger les données loadData(); } catch (error) { console.error("Erreur lors de l'importation:", error); showMessage("Erreur lors de l'importation", "error"); } } // Fonction pour mettre à jour les statistiques function updateStatistics(stats) { totalRevenue.textContent = `${stats.total_revenue.toFixed(2)} €`; totalExpenses.textContent = `${stats.total_expenses.toFixed(2)} €`; const balance = stats.balance; balanceAmount.textContent = `${Math.abs(balance).toFixed(2)} €`; if (balance >= 0) { balanceAmount.classList.remove("text-red-500"); balanceAmount.classList.add("text-green-500"); } else { balanceAmount.classList.remove("text-green-500"); balanceAmount.classList.add("text-red-500"); } } // Fonction pour mettre à jour les catégories du filtre function updateCategoryFilters(categories) { categoryFilter.innerHTML = ''; categories.forEach((category) => { categoryFilter.innerHTML += ``; }); } // Fonction pour afficher les transactions function displayTransactions(transactions) { transactionsList.innerHTML = ""; transactions.forEach((transaction) => { const row = document.createElement("tr"); const date = new Date(transaction.date); const formattedDate = date.toLocaleDateString(); const isRevenue = transaction.type === "revenue"; const amount = `${isRevenue ? "+" : "-"} ${Math.abs( transaction.amount ).toFixed(2)} €`; const amountClass = isRevenue ? "text-green-500" : "text-red-500"; row.innerHTML = ` ${formattedDate} ${transaction.description} ${amount} ${transaction.category || "Non catégorisé"} ${isRevenue ? "Revenu" : "Dépense"} `; transactionsList.appendChild(row); }); if (transactions.length === 0) { transactionsList.innerHTML = ` Aucune transaction trouvée `; } } // Fonction pour changer d'onglet function switchTab(tabId) { // Mettre à jour les classes des boutons tabButtons.forEach((button) => { const buttonTabId = button.id.replace("tab", "").toLowerCase(); if (buttonTabId === tabId) { button.classList.add("active", "border-blue-500", "text-blue-600"); button.classList.remove("border-transparent", "text-gray-500"); } else { button.classList.remove("active", "border-blue-500", "text-blue-600"); button.classList.add("border-transparent", "text-gray-500"); } }); // Afficher le contenu de l'onglet sélectionné tabContents.forEach((content) => { const contentId = content.id; if (contentId === `${tabId}Content`) { content.classList.remove("hidden"); } else { content.classList.add("hidden"); } }); } // Génération de couleurs pour les graphiques function generateChartColors(count = 10) { const colors = [ "#4F46E5", "#10B981", "#F59E0B", "#EF4444", "#EC4899", "#8B5CF6", "#06B6D4", "#84CC16", "#F97316", "#6366F1", ]; // Si on a besoin de plus de couleurs, on les génère aléatoirement if (count > colors.length) { for (let i = colors.length; i < count; i++) { const r = Math.floor(Math.random() * 200); const g = Math.floor(Math.random() * 200); const b = Math.floor(Math.random() * 200); colors.push(`rgb(${r}, ${g}, ${b})`); } } return colors; } // Mise à jour des graphiques function updateCharts(statistics) { updateRevenuesChart(statistics.revenue_by_category); updateExpensesChart(statistics.expenses_by_category); } // Mise à jour du graphique des revenus function updateRevenuesChart(revenuesByCategory) { const ctx = document.getElementById("revenuesByCategoryChart"); const colors = generateChartColors(Object.keys(revenuesByCategory).length); // Détruire le graphique précédent s'il existe if (revenuesChart) { revenuesChart.destroy(); } // Créer le nouveau graphique revenuesChart = new Chart(ctx, { type: "pie", data: { labels: Object.keys(revenuesByCategory), datasets: [ { data: Object.values(revenuesByCategory), backgroundColor: colors, borderWidth: 1, }, ], }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: "right", }, tooltip: { callbacks: { label: function (context) { const value = context.raw; const total = context.dataset.data.reduce((a, b) => a + b, 0); const percentage = ((value / total) * 100).toFixed(1); return `${value.toFixed(2)} € (${percentage}%)`; }, }, }, }, }, }); // Mettre à jour la liste des revenus par catégorie updateCategoryList(revenuesByCategoryList, revenuesByCategory); } // Mise à jour du graphique des dépenses function updateExpensesChart(expensesByCategory) { const ctx = document.getElementById("expensesByCategoryChart"); const colors = generateChartColors(Object.keys(expensesByCategory).length); // Détruire le graphique précédent s'il existe if (expensesChart) { expensesChart.destroy(); } // Créer le nouveau graphique expensesChart = new Chart(ctx, { type: "pie", data: { labels: Object.keys(expensesByCategory), datasets: [ { data: Object.values(expensesByCategory), backgroundColor: colors, borderWidth: 1, }, ], }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: "right", }, tooltip: { callbacks: { label: function (context) { const value = context.raw; const total = context.dataset.data.reduce((a, b) => a + b, 0); const percentage = ((value / total) * 100).toFixed(1); return `${value.toFixed(2)} € (${percentage}%)`; }, }, }, }, }, }); // Mettre à jour la liste des dépenses par catégorie updateCategoryList(expensesByCategoryList, expensesByCategory); } // Mise à jour des listes de catégories function updateCategoryList(listElement, categoryData) { listElement.innerHTML = ""; const total = Object.values(categoryData).reduce((a, b) => a + b, 0); Object.entries(categoryData) .sort((a, b) => b[1] - a[1]) // Trier par montant décroissant .forEach(([category, amount]) => { const percentage = ((amount / total) * 100).toFixed(1); const row = document.createElement("tr"); row.innerHTML = ` ${category} ${amount.toFixed(2)} € ${percentage}% `; listElement.appendChild(row); }); } // Fonctions globales pour l'édition et la suppression window.editTransaction = async (transactionId) => { alert("Fonctionnalité d'édition à implémenter"); // TODO: Implémenter l'édition des transactions }; window.deleteTransaction = async (transactionId) => { if (confirm("Êtes-vous sûr de vouloir supprimer cette transaction ?")) { try { const response = await fetch( `/api/accounting/transaction/${transactionId}`, { method: "DELETE", } ); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } showMessage("Transaction supprimée avec succès"); loadData(); } catch (error) { console.error("Erreur lors de la suppression:", error); showMessage("Erreur lors de la suppression", "error"); } } }; // Fonction pour afficher des messages à l'utilisateur function showMessage(message, type = "success") { // On pourrait implémenter un système de notification plus élaboré ici alert(message); } });