From efa4c0693f5a2f5ca6355b18d3f26cdbbf8ed9ad Mon Sep 17 00:00:00 2001 From: Angie Date: Sat, 26 Jul 2025 01:17:32 +0200 Subject: [PATCH] feat: added store name for the report and filtering by shop update --- frontend/api.js | 141 +++++++++++++++ frontend/script.js | 442 ++++++++++++++++++++++----------------------- 2 files changed, 358 insertions(+), 225 deletions(-) diff --git a/frontend/api.js b/frontend/api.js index 4a241b8..ceb89d3 100644 --- a/frontend/api.js +++ b/frontend/api.js @@ -337,3 +337,144 @@ function deleteUser(userId) { apiDeleteUser(userId) ); } + +//Reports +//GET all reports +async function getReports() { + const token = localStorage.getItem("token"); + try { + const response = await fetch(`${API_BASE_URL}/reports`, { + method: "GET", + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + }); + const data = await response.json(); + if (response.ok) { + return { success: true, reports: data.reports }; + } else { + return { + success: false, + error: data.error || data.message || "Ошибка получения отчетов", + }; + } + } catch (err) { + return { success: false, error: "Нет соединения с сервером" }; + } +} + +async function loadReports() { + const tbody = document.getElementById("reportsTableBody"); + const filterStore = document.getElementById("filterStore"); + + const result = await getReports(); + console.log("getReports() result:", result); + + window.reportsList = result.success ? result.reports : []; + + if (!result.success) { + showNotification(result.error || "Ошибка загрузки отчетов", "error"); + return; + } + const reports = result.reports; + + // Build a map storeId => storeName from all reports + const storeMap = {}; + reports.forEach((r) => { + if (r.storeId && r.storeName) { + storeMap[r.storeId] = r.storeName; + } + }); + + // Get unique [storeId, storeName] pairs sorted alphabetically + const storesWithReports = Object.entries(storeMap) + .map(([id, name]) => ({ id, name })) + .sort((a, b) => a.name.localeCompare(b.name)); + + filterStore.innerHTML = ` + + ${storesWithReports + .map((store) => ``) + .join("")} +`; + + tbody.innerHTML = ""; + reports.forEach((report) => { + const store = database.stores.find((s) => s.id === report.storeId); + // Fallback for username if user might be missing from local array: + const user = database.users.find((u) => u.id === report.userId); + const profit = + (Number(report.totalIncome) || 0) - (Number(report.totalExpenses) || 0); + + const row = document.createElement("tr"); + row.className = "hover:bg-gray-50"; + row.innerHTML = ` + ${ + report.reportDate || report.date || "" + } + ${ + report.storeName || report.storeId + } + €${Number( + report.totalIncome + ).toFixed(2)} + €${Number( + report.totalExpenses + ).toFixed(2)} + €${profit.toFixed(2)} + ${ + user ? user.username : report.userId + } + + + ${report.isVerified ? "Проверен" : "Не проверен"} + + + + + + + `; + tbody.appendChild(row); + }); + + setupReportsFilters(); +} + +// Use global array for backend reports +function viewReport(reportId) { + if (!window.reportsList) { + showNotification("Reports not loaded yet!", "error"); + return; + } + const report = window.reportsList.find((r) => r.id === reportId); + if (report) { + showReportModal(report, true); + } else { + showNotification("Report not found!", "error"); + } +} + +//create report +// createReport(data) +//edit report +// updateReport(id, data) +//accept report (admin only) +// verifyReport(id) +//delete report +// deleteReport(id) diff --git a/frontend/script.js b/frontend/script.js index 64a37cc..c3c081d 100644 --- a/frontend/script.js +++ b/frontend/script.js @@ -499,6 +499,10 @@ document.getElementById("todayReportBtn").addEventListener("click", () => { } }); +function safeToFixed(value, digits = 2) { + return (Number(value) || 0).toFixed(digits); +} + // Показ модального окна отчета с исправленной прокруткой function showReportModal(report, isAdmin = false) { const modal = document.getElementById("reportViewModal"); @@ -509,157 +513,146 @@ function showReportModal(report, isAdmin = false) { const store = database.stores.find((s) => s.id === report.storeId); const user = database.users.find((u) => u.id === report.userId); - title.textContent = `Отчет от ${report.date} - ${ + title.textContent = `Отчет от ${report.date || report.reportDate} - ${ store ? store.name : "Неизвестный магазин" }`; - // Формирование содержимого с красивыми стилями content.innerHTML = ` -
- -
-

Основная информация

-
-
Дата: ${report.date}
-
Магазин: ${ - store ? store.name : "Неизвестно" - }
-
Пользователь: ${ - user ? user.username : "Неизвестно" - }
-
Статус: - - ${ - report.verified - ? "Проверен" - : "Не проверен" - } - -
-
-
- - -
-

Доходы (Ingresos)

-
-
Income: €${report.income.toFixed( - 2 - )}
-
Caja inicial: €${report.cajaInicial.toFixed( - 2 - )}
-
Total income: €${report.totalIncome.toFixed( - 2 - )}
-
-
- - -
-

Зарплаты (Wages)

- ${ - report.wages && report.wages.length > 0 - ? ` -
- ${report.wages - .map( - (w) => ` -
- ${w.name} - €${w.amount.toFixed(2)} -
- ` - ) - .join("")} -
-
- Total wages: - €${report.totalWages.toFixed( - 2 - )} -
-
-
- ` - : '

Нет данных о зарплатах

' - } -
- - -
-

Расходы (Expenses)

- ${ - report.expenses && report.expenses.length > 0 - ? ` -
- ${report.expenses - .map( - (e) => ` -
- ${e.name} - €${e.amount.toFixed(2)} -
- ` - ) - .join("")} -
-
- Total expenses internal: - €${report.totalExpensesInternal.toFixed( - 2 - )} -
-
-
- ` - : '

Нет данных о расходах

' - } -
- - -
-

Итоговые расчеты

-
-
Total income: €${report.totalIncome.toFixed( - 2 - )}
-
Total expenses: €${report.totalExpenses.toFixed( - 2 - )}
-
Envelope: €${report.envelope.toFixed( - 2 - )}
-
- Caja final: €${report.cajaFinal.toFixed( - 2 - )} -
-
-
+
+ +
+

Основная информация

+
+
Дата: ${ + report.date || report.reportDate || "" + }
+
Магазин: ${ + report.storeName || report.storeId + }
+
Пользователь: ${ + user ? user.username : report.userId + }
+
Статус: + + ${ + report.isVerified || report.verified + ? "Проверен" + : "Не проверен" + } + +
+
+
+ +
+

Доходы (Ingresos)

+
+
Income: €${safeToFixed(report.income)}
+
Caja inicial: €${safeToFixed( + report.cajaInicial || report.initialCash + )}
+
Total income: €${safeToFixed( + report.totalIncome + )}
+
+
+ +
+

Зарплаты (Wages)

+ ${ + Array.isArray(report.wages) && report.wages.length > 0 + ? ` +
+ ${report.wages + .map( + (w) => ` +
+ ${w.name} + €${safeToFixed(w.amount)} +
+ ` + ) + .join("")} +
+
+ Total wages: + €${safeToFixed(report.totalWages)}
- `; +
+
+ ` + : '

Нет данных о зарплатах

' + } +
+ +
+

Расходы (Expenses)

+ ${ + Array.isArray(report.expenses) && report.expenses.length > 0 + ? ` +
+ ${report.expenses + .map( + (e) => ` +
+ ${e.name} + €${safeToFixed(e.amount)} +
+ ` + ) + .join("")} +
+
+ Total expenses internal: + €${safeToFixed(report.totalExpensesInternal)} +
+
+
+ ` + : '

Нет данных о расходах

' + } +
+ +
+

Итоговые расчеты

+
+
Total income: €${safeToFixed( + report.totalIncome + )}
+
Total expenses: €${safeToFixed( + report.totalExpenses + )}
+
Envelope: €${safeToFixed( + report.envelope + )}
+
+ Caja final: €${safeToFixed( + report.cajaFinal || report.finalCash + )} +
+
+
+
+ `; - // Настройка кнопок в зависимости от роли и статуса buttons.innerHTML = ""; if (isAdmin) { - // Кнопки для администратора buttons.innerHTML = ` - - - - `; + + + + `; document.getElementById("editReportBtn").addEventListener("click", () => { editReport(report); @@ -669,16 +662,15 @@ function showReportModal(report, isAdmin = false) { verifyReport(report.id); }); } else { - // Кнопки для пользователя - if (!report.verified) { + if (!report.isVerified && !report.verified) { buttons.innerHTML = ` - - - `; + + + `; document .getElementById("editReportUserBtn") @@ -688,10 +680,10 @@ function showReportModal(report, isAdmin = false) { }); } else { buttons.innerHTML = ` - - `; + + `; } } @@ -1161,83 +1153,83 @@ function createCharts() { } // Загрузка отчетов в админке -function loadReports() { - const tbody = document.getElementById("reportsTableBody"); - const filterStore = document.getElementById("filterStore"); +// function loadReports() { +// const tbody = document.getElementById("reportsTableBody"); +// const filterStore = document.getElementById("filterStore"); - // Заполнение фильтра магазинов - filterStore.innerHTML = ''; - database.stores.forEach((store) => { - const option = document.createElement("option"); - option.value = store.id; - option.textContent = store.name; - filterStore.appendChild(option); - }); +// // Заполнение фильтра магазинов +// filterStore.innerHTML = ''; +// database.stores.forEach((store) => { +// const option = document.createElement("option"); +// option.value = store.id; +// option.textContent = store.name; +// filterStore.appendChild(option); +// }); - // Отображение отчетов - tbody.innerHTML = ""; - database.reports.forEach((report) => { - const store = database.stores.find((s) => s.id === report.storeId); - const user = database.users.find((u) => u.id === report.userId); - const profit = report.totalIncome - report.totalExpenses; +// // Отображение отчетов +// tbody.innerHTML = ""; +// database.reports.forEach((report) => { +// const store = database.stores.find((s) => s.id === report.storeId); +// const user = database.users.find((u) => u.id === report.userId); +// const profit = report.totalIncome - report.totalExpenses; - const row = document.createElement("tr"); - row.className = "hover:bg-gray-50"; - row.innerHTML = ` - ${ - report.date - } - ${ - store ? store.name : "Неизвестно" - } - €${report.totalIncome.toFixed( - 2 - )} - €${report.totalExpenses.toFixed( - 2 - )} - €${profit.toFixed(2)} - ${ - user ? user.username : "Неизвестно" - } - - - ${report.verified ? "Проверен" : "Не проверен"} - - - - - - - `; - tbody.appendChild(row); - }); +// const row = document.createElement("tr"); +// row.className = "hover:bg-gray-50"; +// row.innerHTML = ` +// ${ +// report.date +// } +// ${ +// store ? store.name : "Неизвестно" +// } +// €${report.totalIncome.toFixed( +// 2 +// )} +// €${report.totalExpenses.toFixed( +// 2 +// )} +// €${profit.toFixed(2)} +// ${ +// user ? user.username : "Неизвестно" +// } +// +// +// ${report.verified ? "Проверен" : "Не проверен"} +// +// +// +// +// +// +// `; +// tbody.appendChild(row); +// }); - // Настройка фильтров и экспорта - setupReportsFilters(); -} +// // Настройка фильтров и экспорта +// setupReportsFilters(); +// } // Просмотр отчета -function viewReport(reportId) { - const report = database.reports.find((r) => r.id === reportId); - if (report) { - showReportModal(report, true); // true = админ режим - } -} +// function viewReport(reportId) { +// const report = database.reports.find((r) => r.id === reportId); +// if (report) { +// showReportModal(report, true); // true = админ режим +// } +// } // Удаление отчета function deleteReport(reportId) {