diff --git a/frontend/api.js b/frontend/api.js
index 20d9b45..a1da71e 100644
--- a/frontend/api.js
+++ b/frontend/api.js
@@ -1,7 +1,60 @@
//API
const API_BASE_URL = "http://195.209.214.159/api";
-//Login
+//API local
+// const API_BASE_URL = "http://localhost:3001/api";
+
+//SHARED
+
+//REACTIVITY (experimental)
+
+function createReactiveState(stateObj, onChange) {
+ return new Proxy(stateObj, {
+ set(target, prop, value) {
+ target[prop] = value;
+ onChange(prop, value);
+ return true;
+ },
+ });
+}
+
+//universal global state
+window.appState = createReactiveState(
+ window.appState || {
+ currentUser: null,
+ usersList: [],
+ reportsList: [],
+ storesList: [],
+ editingReportId: null,
+ editingUserId: null,
+ editingStoreId: null,
+ editingTodoId: null,
+ revenueChartInstance: null,
+ expensesChartInstance: null,
+ storesChartInstance: null,
+ trendsChartInstance: null,
+ adminTabsInitialized: false,
+ },
+ function (prop, value) {
+ // React to changes in critical state
+ if (prop === "storesList") {
+ // if (typeof loadStores === "function") loadStores();
+ if (typeof loadUserStores === "function") loadUserStores();
+ if (typeof updateDashboard === "function") updateDashboard();
+ }
+ if (prop === "usersList") {
+ // if (typeof loadUsers === "function") loadUsers();
+ if (typeof updateDashboard === "function") updateDashboard();
+ }
+ if (prop === "reportsList") {
+ // if (typeof loadReports === "function") loadReports();
+ if (typeof updateDashboard === "function") updateDashboard();
+ }
+ }
+);
+
+//AUTH
+//Login & Logout
async function loginUser(username, password) {
try {
const response = await fetch(`${API_BASE_URL}/auth/login`, {
@@ -28,35 +81,19 @@ async function loginUser(username, password) {
}
}
-document.getElementById("loginForm").addEventListener("submit", async (e) => {
- e.preventDefault();
+function logout() {
+ appState.currentUser = null;
+ localStorage.removeItem("token");
- const username = document.getElementById("username").value;
- const password = document.getElementById("password").value;
+ // Show login screen, hide other interfaces
+ document.getElementById("loginScreen").classList.remove("hidden");
+ document.getElementById("userInterface").classList.add("hidden");
+ document.getElementById("adminInterface").classList.add("hidden");
+ document.getElementById("loginForm").reset();
+ document.getElementById("loginError").classList.add("hidden");
- // Call backend login
- const result = await loginUser(username, password);
-
- if (result.success) {
- // Save user/token in JS (or localStorage)
- currentUser = result.user;
- localStorage.setItem("token", result.token);
-
- document.getElementById("loginScreen").classList.add("hidden");
-
- if (result.user.role === "admin") {
- showAdminInterface();
- } else {
- showUserInterface();
- }
-
- showNotification("Успешная авторизация!");
- } else {
- const errorDiv = document.getElementById("loginError");
- errorDiv.textContent = result.error;
- errorDiv.classList.remove("hidden");
- }
-});
+ showNotification("Вы вышли из системы", "info");
+}
document.addEventListener("DOMContentLoaded", async () => {
const token = localStorage.getItem("token");
@@ -70,9 +107,9 @@ document.addEventListener("DOMContentLoaded", async () => {
});
if (response.ok) {
const data = await response.json();
- currentUser = data.user;
+ appState.currentUser = data.user;
document.getElementById("loginScreen").classList.add("hidden");
- if (currentUser.role === "admin") {
+ if (appState.currentUser.role === "admin") {
showAdminInterface();
} else {
showUserInterface();
@@ -91,24 +128,10 @@ document.addEventListener("DOMContentLoaded", async () => {
}
});
-function logout() {
- currentUser = null;
- localStorage.removeItem("token");
-
- // Show login screen, hide other interfaces
- document.getElementById("loginScreen").classList.remove("hidden");
- document.getElementById("userInterface").classList.add("hidden");
- document.getElementById("adminInterface").classList.add("hidden");
- document.getElementById("loginForm").reset();
- document.getElementById("loginError").classList.add("hidden");
-
- showNotification("Вы вышли из системы", "info");
-}
-
document.getElementById("logoutBtn").addEventListener("click", logout);
document.getElementById("adminLogoutBtn").addEventListener("click", logout);
-//Users
+//USERS
//GET all users (admin only)
async function getAllUsers() {
@@ -135,68 +158,7 @@ async function getAllUsers() {
}
}
-let usersList = [];
-
-async function loadUsers() {
- const tbody = document.getElementById("usersTableBody");
- tbody.innerHTML = "";
-
- const result = await getAllUsers();
-
- console.log("getAllUsers result:", result);
-
- if (!result.success) {
- showNotification(result.error, "error");
- return;
- }
-
- const users = result.users;
- window.usersList = users;
-
- users.forEach((user) => {
- const userStores =
- user.stores
- .map((storeId) => {
- const store = (window.storesList || []).find((s) => s.id === storeId);
- return store ? store.name : "Нет доступа";
- })
- .join(", ") || "Нет доступа";
-
- const row = document.createElement("tr");
- row.className = "hover:bg-gray-50";
- row.innerHTML = `
-
${user.id} |
- ${user.username} |
-
-
- ${user.role === "admin" ? "Администратор" : "Сотрудник"}
-
- |
- ${userStores} |
-
-
-
- |
- `;
- tbody.appendChild(row);
- });
-
- if (users.length === 0) {
- showNotification("Нет пользователей для отображения", "info");
- }
-}
+appState.usersList = [];
//add user
async function createUser(userData) {
@@ -331,14 +293,7 @@ async function apiDeleteUser(userId) {
}
}
-// 4. UI trigger: delete user with modal
-function deleteUser(userId) {
- showConfirmModal("Вы уверены, что хотите удалить этого пользователя?", () =>
- apiDeleteUser(userId)
- );
-}
-
-//Reports
+//REPORTS ADMIN
//GET all reports
async function getReports() {
const token = localStorage.getItem("token");
@@ -365,109 +320,6 @@ async function getReports() {
}
}
-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 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)} |
- ${
- report.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");
- }
-}
-
//edit report
async function updateReport(reportId, data) {
const token = localStorage.getItem("token");
@@ -500,136 +352,6 @@ async function updateReport(reportId, data) {
}
}
-function editReport(report) {
- console.log("editReport() called with:", report);
- const content = document.getElementById("reportViewContent");
- const buttons = document.getElementById("reportModalButtons");
-
- const shopName = (report.storeName || report.storeId || "").replace(
- /"/g,
- """
- );
-
- content.innerHTML = `
-
- `;
-
- buttons.innerHTML = `
-
-
- `;
-
- document
- .getElementById("saveReportBtn")
- .addEventListener("click", async (e) => {
- e.preventDefault();
- await saveEditedReport(report.id);
- });
-
- document.getElementById("cancelEditBtn").addEventListener("click", (e) => {
- e.preventDefault();
- showReportModal(report, true);
- });
-}
-
-async function saveEditedReport(reportId) {
- // Get values from the form
- const storeId = document.getElementById("editStoreSelect").value;
- const reportDate = document.getElementById("editDate").value;
- const incomeVal = document.getElementById("editIncome").value;
- const initialCashVal = document.getElementById("editCajaInicial").value;
- const envelopeVal = document.getElementById("editEnvelope").value;
- const isVerified = document.getElementById("editVerified").checked ? 1 : 0;
-
- // Build the payload dynamically
- const data = {};
-
- if (storeId) data.storeId = parseInt(storeId, 10);
- if (reportDate) data.reportDate = reportDate;
- if (incomeVal !== "") data.income = parseFloat(incomeVal);
- if (initialCashVal !== "") data.initialCash = parseFloat(initialCashVal);
- if (envelopeVal !== "") data.envelope = parseFloat(envelopeVal);
-
- if (data.income !== undefined && data.initialCash !== undefined) {
- data.totalIncome = data.income + data.initialCash;
- }
- data.isVerified = isVerified;
-
- // REMOVE any NaN values
- Object.keys(data).forEach((k) => {
- if (typeof data[k] === "number" && isNaN(data[k])) delete data[k];
- });
-
- await updateReport(reportId, data);
-}
-
//accept report (admin only)
async function verifyReport(reportId) {
const token = localStorage.getItem("token");
@@ -684,7 +406,7 @@ async function apiDeleteReport(reportId) {
}
}
-//worker
+//REPORTS WORKER
//create report
// api.js
async function createReport(data) {
@@ -715,8 +437,7 @@ async function createReport(data) {
}
}
-//stores (for admin)
-
+//STORES (for admin)
// 1. Get all stores
async function getStores() {
const token = localStorage.getItem("token");
diff --git a/frontend/script.js b/frontend/script.js
index 1b74e7d..95efe03 100644
--- a/frontend/script.js
+++ b/frontend/script.js
@@ -1,147 +1,10 @@
-// Глобальные переменные
-let currentUser = null;
-let editingReportId = null;
-let editingUserId = null;
-let editingStoreId = null;
-let editingTodoId = null;
-
-//default state
-let revenueChartInstance = null;
-let expensesChartInstance = null;
-let storesChartInstance = null;
-let trendsChartInstance = null;
+//SHARED
//helper to destroyChart on change
function destroyChart(instance) {
if (instance) instance.destroy();
}
-// База данных (симуляция)
-let database = {
- users: [
- {
- id: 1,
- username: "admin",
- password: "admin123",
- role: "admin",
- stores: [],
- },
- {
- id: 2,
- username: "employee",
- password: "password123",
- role: "employee",
- stores: [1, 2],
- },
- {
- id: 3,
- username: "cashier1",
- password: "password123",
- role: "employee",
- stores: [1, 2],
- },
- {
- id: 4,
- username: "cashier2",
- password: "password123",
- role: "employee",
- stores: [3],
- },
- ],
- stores: [
- { id: 1, name: "Магазин 1" },
- { id: 2, name: "Магазин 2" },
- { id: 3, name: "Магазин 3" },
- { id: 4, name: "Магазин 4" },
- ],
- reports: [],
- todos: [
- {
- id: 1,
- title: "Исправить модальные окна",
- description: "Исправить прокрутку и видимость кнопок в модальных окнах",
- completed: true,
- priority: "high",
- createdAt: "2024-01-15",
- },
- {
- id: 2,
- title: "Добавить экспорт в PDF",
- description: "Реализовать функцию экспорта отчетов в PDF формат",
- completed: false,
- priority: "medium",
- createdAt: "2024-01-16",
- },
- {
- id: 3,
- title: "Улучшить графики",
- description: "Добавить больше интерактивности в графики Dashboard",
- completed: false,
- priority: "low",
- createdAt: "2024-01-17",
- },
- ],
-};
-
-// Генерация тестовых данных для отчетов
-function generateTestData() {
- const reports = [];
- const today = new Date();
-
- for (let i = 0; i < 30; i++) {
- const date = new Date(today);
- date.setDate(date.getDate() - i);
-
- const storeId = Math.floor(Math.random() * 4) + 1;
- const userId = Math.floor(Math.random() * 3) + 2; // employee пользователи
-
- const income = Math.floor(Math.random() * 2000) + 500;
- const cajaInicial = Math.floor(Math.random() * 300) + 100;
- const totalIncome = income + cajaInicial;
-
- const wages = Math.floor(Math.random() * 400) + 100;
- const expenses = Math.floor(Math.random() * 200) + 50;
- const totalExpenses = wages + expenses;
-
- const envelope = Math.floor(Math.random() * 200) + 100;
- const cajaFinal = totalIncome - totalExpenses - envelope;
-
- reports.push({
- id: i + 1,
- date: date.toISOString().split("T")[0],
- storeId: storeId,
- userId: userId,
- income: income,
- cajaInicial: cajaInicial,
- totalIncome: totalIncome,
- wages: [
- {
- name: "Сотрудник " + (Math.floor(Math.random() * 3) + 1),
- amount: wages,
- },
- ],
- expenses: [
- {
- name: "Расходы " + (Math.floor(Math.random() * 3) + 1),
- amount: expenses,
- },
- ],
- totalWages: wages,
- totalExpensesInternal: expenses,
- totalExpenses: totalExpenses,
- envelope: envelope,
- cajaFinal: cajaFinal,
- verified: Math.random() > 0.3,
- createdAt: date.toISOString(),
- });
- }
-
- database.reports = reports;
-}
-
-// Инициализация тестовых данных
-generateTestData();
-
// Система уведомлений
function showNotification(message, type = "success") {
const notification = document.getElementById("notification");
@@ -196,6 +59,57 @@ function hideModal(modalId) {
document.body.classList.remove("modal-open");
}
}
+// Настройка вкладок администратора
+function setupAdminTabs() {
+ if (appState.adminTabsInitialized) return;
+ appState.adminTabsInitialized = true;
+
+ const tabButtons = document.querySelectorAll(".admin-tab-btn");
+ const tabContents = document.querySelectorAll(".admin-tab-content");
+
+ tabButtons.forEach((button) => {
+ button.addEventListener("click", () => {
+ const tabId = button.dataset.tab;
+
+ // Переключение активной вкладки
+ tabButtons.forEach((btn) => {
+ btn.className =
+ "admin-tab-btn px-6 py-3 font-medium text-gray-600 hover:text-blue-600 transition-colors border-b-2 border-transparent hover:border-blue-500";
+ });
+ button.className =
+ "admin-tab-btn px-6 py-3 font-medium transition-colors border-b-2 border-blue-500 text-blue-600";
+
+ // Показ/скрытие содержимого
+ tabContents.forEach((content) => {
+ content.classList.add("hidden");
+ });
+ document.getElementById(tabId + "Tab").classList.remove("hidden");
+
+ if (tabId === "dashboard") updateDashboard();
+ // Optionally, reload todos for TODO tab
+ if (tabId === "todo") loadTodos();
+
+ // Загрузка данных при переключении
+ // switch (tabId) {
+ // case "dashboard":
+ // updateDashboard();
+ // break;
+ // case "reports":
+ // loadReports();
+ // break;
+ // case "users":
+ // loadUsers();
+ // break;
+ // case "stores":
+ // loadStores();
+ // break;
+ // case "todo":
+ // loadTodos();
+ // break;
+ // }
+ });
+ });
+}
// Закрытие модального окна при клике вне его
document.addEventListener("click", (e) => {
@@ -204,41 +118,57 @@ document.addEventListener("click", (e) => {
}
});
+// Глобальные функции для onclick обработчиков
+window.viewReport = viewReport;
+window.deleteReport = deleteReport;
+window.editUser = editUser;
+window.deleteUser = deleteUser;
+window.editStore = editStore;
+window.deleteStore = deleteStore;
+window.editTodo = editTodo;
+window.deleteTodo = deleteTodo;
+window.toggleTodo = toggleTodo;
+
+//AUTH
+
// Обработчики авторизации
-// document.getElementById("loginForm").addEventListener("submit", (e) => {
-// e.preventDefault();
+document.getElementById("loginForm").addEventListener("submit", async (e) => {
+ e.preventDefault();
-// const username = document.getElementById("username").value;
-// const password = document.getElementById("password").value;
+ const username = document.getElementById("username").value;
+ const password = document.getElementById("password").value;
-// const user = database.users.find(
-// (u) => u.username === username && u.password === password
-// );
+ // Call backend login
+ const result = await loginUser(username, password);
-// if (user) {
-// currentUser = user;
-// document.getElementById("loginScreen").classList.add("hidden");
+ if (result.success) {
+ // Save user/token in JS (or localStorage)
+ appState.currentUser = result.user;
+ localStorage.setItem("token", result.token);
-// if (user.role === "admin") {
-// showAdminInterface();
-// } else {
-// showUserInterface();
-// }
+ document.getElementById("loginScreen").classList.add("hidden");
-// showNotification("Успешная авторизация!");
-// } else {
-// const errorDiv = document.getElementById("loginError");
-// errorDiv.textContent = "Неверный логин или пароль";
-// errorDiv.classList.remove("hidden");
-// }
-// });
+ if (result.user.role === "admin") {
+ showAdminInterface();
+ } else {
+ showUserInterface();
+ }
+
+ showNotification("Успешная авторизация!");
+ } else {
+ const errorDiv = document.getElementById("loginError");
+ // errorDiv.textContent = result.error;
+ errorDiv.textContent = "Неверный логин или пароль";
+ errorDiv.classList.remove("hidden");
+ }
+});
// Показать интерфейс пользователя
function showUserInterface() {
document.getElementById("userInterface").classList.remove("hidden");
document.getElementById(
"userWelcome"
- ).textContent = `Добро пожаловать, ${currentUser.username}!`;
+ ).textContent = `Добро пожаловать, ${appState.currentUser.username}!`;
loadUserStores();
setupFormCalculations();
@@ -249,328 +179,514 @@ async function showAdminInterface() {
document.getElementById("adminInterface").classList.remove("hidden");
document.getElementById(
"adminWelcome"
- ).textContent = `Добро пожаловать, ${currentUser.username}!`;
+ ).textContent = `Добро пожаловать, ${appState.currentUser.username}!`;
- await Promise.all([loadUsers(), loadReports(), loadStores()]);
+ await Promise.all([loadUsers(), loadStores(), loadReports()]);
updateDashboard();
-
loadTodos();
setupAdminTabs();
+
+ // Activate first tab (dashboard)
+ document.querySelector('.admin-tab-btn[data-tab="dashboard"]').click();
}
-// Загрузка магазинов для пользователя
-function loadUserStores() {
- const select = document.getElementById("storeSelect");
- if (!select) return;
- select.innerHTML =
- '';
+// Обработчики выхода
+document.getElementById("logoutBtn").addEventListener("click", logout);
+document.getElementById("adminLogoutBtn").addEventListener("click", logout);
- // For admin: show all
- if (currentUser.role === "admin") {
- (window.storesList || []).forEach((store) => {
- const option = document.createElement("option");
- option.value = store.id;
- option.textContent = store.name;
- select.appendChild(option);
+function logout() {
+ appState.currentUser = null;
+ appState.editingReportId = null;
+
+ appState.usersList = [];
+ appState.storesList = [];
+ appState.reportsList = [];
+
+ database.users = [];
+ database.reports = [];
+ database.stores = [];
+
+ // Remove all admin tab event listeners by replacing each node
+ document.querySelectorAll(".admin-tab-btn").forEach((btn) => {
+ btn.replaceWith(btn.cloneNode(true));
+ });
+
+ appState.adminTabsInitialized = false;
+
+ document.getElementById("loginScreen").classList.remove("hidden");
+ document.getElementById("userInterface").classList.add("hidden");
+ document.getElementById("adminInterface").classList.add("hidden");
+
+ document.getElementById("loginForm").reset();
+ document.getElementById("loginError").classList.add("hidden");
+
+ showNotification("Вы вышли из системы");
+}
+
+//DASHBOARD
+
+// Обновление дашборда
+function updateDashboard() {
+ const reports = appState.reportsList || [];
+ const users = appState.usersList || [];
+
+ // Расчет статистики
+ const totalRevenue = reports.reduce((sum, r) => sum + r.totalIncome, 0);
+ const totalExpenses = reports.reduce((sum, r) => sum + r.totalExpenses, 0);
+ const totalReports = reports.length;
+ const totalUsers = users.length;
+
+ // Обновление карточек
+ document.getElementById(
+ "totalRevenueCard"
+ ).textContent = `€${totalRevenue.toFixed(2)}`;
+ document.getElementById(
+ "totalExpensesCard"
+ ).textContent = `€${totalExpenses.toFixed(2)}`;
+ document.getElementById("totalReportsCard").textContent = totalReports;
+ document.getElementById("totalUsersCard").textContent = totalUsers;
+
+ // Создание графиков
+ createCharts();
+}
+
+// Создание графиков
+function createCharts() {
+ // График доходов по дням
+
+ const reports = appState.reportsList || [];
+ const stores = appState.storesList || [];
+
+ const revenueCtx = document.getElementById("revenueChart");
+ if (revenueCtx) {
+ destroyChart(appState.revenueChartInstance);
+ const last7Days = [];
+ const revenueData = [];
+
+ for (let i = 6; i >= 0; i--) {
+ const date = new Date();
+ date.setDate(date.getDate() - i);
+ const dateStr = date.toISOString().split("T")[0];
+ last7Days.push(dateStr);
+
+ const dayReports = reports.filter(
+ (r) => (r.reportDate || r.date) === dateStr
+ );
+
+ const dayRevenue = dayReports.reduce(
+ (sum, r) => sum + (Number(r.totalIncome) || 0),
+ 0
+ );
+
+ revenueData.push(dayRevenue);
+ }
+
+ appState.revenueChartInstance = new Chart(revenueCtx, {
+ type: "line",
+ data: {
+ labels: last7Days,
+ datasets: [
+ {
+ label: "Доходы",
+ data: revenueData,
+ borderColor: "rgb(59, 130, 246)",
+ backgroundColor: "rgba(59, 130, 246, 0.1)",
+ tension: 0.4,
+ },
+ ],
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ plugins: {
+ legend: {
+ display: false,
+ },
+ },
+ scales: {
+ y: {
+ beginAtZero: true,
+ ticks: {
+ callback: function (value) {
+ return "€" + value.toFixed(0);
+ },
+ },
+ },
+ },
+ },
});
- } else {
- // For employee: only their own stores
- (currentUser.stores || []).forEach((storeObj) => {
- // storeObj could be an object ({id, name, ...}) or just an ID; check backend API
- let store = storeObj;
- if (typeof storeObj === "number") {
- // If backend sends just IDs, look up in window.storesList
- store = (window.storesList || []).find((s) => s.id === storeObj);
- }
- if (store) {
- const option = document.createElement("option");
- option.value = store.id;
- option.textContent = store.name;
- select.appendChild(option);
- }
+ }
+
+ // Круговая диаграмма расходов
+ const expensesCtx = document.getElementById("expensesChart");
+ if (expensesCtx) {
+ destroyChart(appState.expensesChartInstance);
+ const totalWages = reports.reduce(
+ (sum, r) => sum + (Number(r.totalWages) || 0),
+ 0
+ );
+ const totalInternal = reports.reduce(
+ (sum, r) => sum + (Number(r.totalExpenses) || 0),
+ 0
+ );
+
+ appState.expensesChartInstance = new Chart(expensesCtx, {
+ type: "doughnut",
+ data: {
+ labels: ["Зарплаты", "Прочие расходы"],
+ datasets: [
+ {
+ data: [totalWages, totalInternal],
+ backgroundColor: ["#F59E0B", "#EF4444"],
+ },
+ ],
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ plugins: {
+ legend: {
+ position: "bottom",
+ },
+ },
+ },
+ });
+ }
+
+ // График по магазинам
+ const storesCtx = document.getElementById("storesChart");
+ if (storesCtx) {
+ destroyChart(appState.storesChartInstance);
+ const storeData = stores.map((store) => {
+ const storeReports = reports.filter((r) => r.storeId === store.id);
+ const revenue = storeReports.reduce((sum, r) => sum + r.totalIncome, 0);
+ return { name: store.name, revenue };
+ });
+ console.log("storeData for bar chart:", storeData);
+
+ appState.storesChartInstance = new Chart(storesCtx, {
+ type: "bar",
+ data: {
+ labels: storeData.map((s) => s.name),
+ datasets: [
+ {
+ label: "Доходы по магазинам",
+ data: storeData.map((s) => s.revenue),
+ backgroundColor: "#10B981",
+ },
+ ],
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ plugins: {
+ legend: {
+ display: false,
+ },
+ },
+ scales: {
+ y: {
+ beginAtZero: true,
+ ticks: {
+ callback: function (value) {
+ return "€" + value.toFixed(0);
+ },
+ },
+ },
+ },
+ },
+ });
+ }
+
+ // Тренды продаж
+ const trendsCtx = document.getElementById("trendsChart");
+ if (trendsCtx) {
+ destroyChart(appState.trendsChartInstance);
+ const last30Days = [];
+ const profitData = [];
+
+ for (let i = 29; i >= 0; i--) {
+ const date = new Date();
+ date.setDate(date.getDate() - i);
+ const dateStr = date.toISOString().split("T")[0];
+ last30Days.push(dateStr);
+
+ const dayReports = reports.filter(
+ (r) => (r.reportDate || r.date) === dateStr
+ );
+ const dayProfit = dayReports.reduce(
+ (sum, r) =>
+ sum + ((Number(r.totalIncome) || 0) - (Number(r.totalExpenses) || 0)),
+ 0
+ );
+ profitData.push(dayProfit);
+ }
+
+ appState.trendsChartInstance = new Chart(trendsCtx, {
+ type: "line",
+ data: {
+ labels: last30Days,
+ datasets: [
+ {
+ label: "Прибыль",
+ data: profitData,
+ borderColor: "#8B5CF6",
+ backgroundColor: "rgba(139, 92, 246, 0.1)",
+ tension: 0.4,
+ fill: true,
+ },
+ ],
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ plugins: {
+ legend: {
+ display: false,
+ },
+ },
+ scales: {
+ y: {
+ beginAtZero: true,
+ ticks: {
+ callback: function (value) {
+ return "€" + value.toFixed(0);
+ },
+ },
+ },
+ },
+ },
});
}
}
-// Настройка автоматических расчетов в форме
-function setupFormCalculations() {
- const incomeInput = document.getElementById("income");
- const cajaInicialInput = document.getElementById("cajaInicial");
- const envelopeInput = document.getElementById("envelope");
+//REPORTS
- function updateCalculations() {
- const income = parseFloat(incomeInput.value) || 0;
- const cajaInicial = parseFloat(cajaInicialInput.value) || 0;
- const envelope = parseFloat(envelopeInput.value) || 0;
-
- const totalIncome = income + cajaInicial;
- document.getElementById("totalIncome").value = totalIncome.toFixed(2);
- document.getElementById("displayTotalIncome").value =
- totalIncome.toFixed(2);
-
- const totalWages = calculateTotalWages();
- const totalExpensesInternal = calculateTotalExpenses();
- const totalExpenses = totalWages + totalExpensesInternal;
-
- document.getElementById("totalExpenses").value = totalExpenses.toFixed(2);
-
- const cajaFinal = totalIncome - totalExpenses - envelope;
- document.getElementById("cajaFinal").textContent = cajaFinal.toFixed(2);
+async function loadReports() {
+ if (!appState.storesList || appState.storesList.length === 0) {
+ await loadStores();
}
- incomeInput.addEventListener("input", updateCalculations);
- cajaInicialInput.addEventListener("input", updateCalculations);
- envelopeInput.addEventListener("input", updateCalculations);
+ const tbody = document.getElementById("reportsTableBody");
+ const filterStore = document.getElementById("filterStore");
- setupDynamicRows();
-}
+ const result = await getReports();
+ console.log("getReports() result:", result);
-// Настройка динамических строк
-function setupDynamicRows() {
- document.getElementById("addWage").addEventListener("click", () => {
- addWageRow();
+ appState.reportsList = result.success ? result.reports : [];
+
+ if (!result.success) {
+ showNotification(result.error || "Ошибка загрузки отчетов", "error");
+ return;
+ }
+ const reports = result.reports;
+ const stores = appState.storesList || [];
+
+ filterStore.innerHTML = `
+
+ ${stores
+ .map((store) => ``)
+ .join("")}
+ `;
+
+ tbody.innerHTML = "";
+ reports.forEach((report) => {
+ const profit =
+ (Number(report.totalIncome) || 0) - (Number(report.totalExpenses) || 0);
+
+ const store = stores.find((s) => Number(s.id) === Number(report.storeId));
+ const storeName = store ? store.name : report.storeName || report.storeId;
+
+ const row = document.createElement("tr");
+ row.className = "hover:bg-gray-50";
+ row.innerHTML = `
+ ${
+ report.reportDate || report.date || ""
+ } |
+ ${storeName} |
+ €${Number(
+ report.totalIncome
+ ).toFixed(2)} |
+ €${Number(
+ report.totalExpenses
+ ).toFixed(2)} |
+ €${profit.toFixed(2)} |
+ ${
+ report.username || report.userId
+ } |
+
+
+ ${report.isVerified ? "Проверен" : "Не проверен"}
+
+ |
+
+
+
+ |
+ `;
+ tbody.appendChild(row);
});
- document.getElementById("addExpense").addEventListener("click", () => {
- addExpenseRow();
- });
-
- // Обновление при изменении значений
- document.addEventListener("input", (e) => {
- if (
- e.target.classList.contains("wage-amount") ||
- e.target.classList.contains("expense-amount")
- ) {
- updateTotals();
- }
- });
-
- // Удаление строк
- document.addEventListener("click", (e) => {
- if (e.target.classList.contains("remove-wage")) {
- e.target.closest(".wage-row").remove();
- updateTotals();
- } else if (e.target.classList.contains("remove-expense")) {
- e.target.closest(".expense-row").remove();
- updateTotals();
- }
- });
+ setupReportsFilters();
}
-function addWageRow() {
- const container = document.getElementById("wagesContainer");
- const row = document.createElement("div");
- row.className = "wage-row grid grid-cols-1 md:grid-cols-3 gap-4 mb-3";
- row.innerHTML = `
-
-
-
- `;
- container.appendChild(row);
-}
-
-function addExpenseRow() {
- const container = document.getElementById("expensesContainer");
- const row = document.createElement("div");
- row.className = "expense-row grid grid-cols-1 md:grid-cols-3 gap-4 mb-3";
- row.innerHTML = `
-
-
-
- `;
- container.appendChild(row);
-}
-
-function calculateTotalWages() {
- const amounts = document.querySelectorAll(".wage-amount");
- let total = 0;
- amounts.forEach((amount) => {
- total += parseFloat(amount.value) || 0;
- });
- return total;
-}
-
-function calculateTotalExpenses() {
- const amounts = document.querySelectorAll(".expense-amount");
- let total = 0;
- amounts.forEach((amount) => {
- total += parseFloat(amount.value) || 0;
- });
- return total;
-}
-
-function updateTotals() {
- const totalWages = calculateTotalWages();
- const totalExpensesInternal = calculateTotalExpenses();
-
- document.getElementById("totalWages").textContent = totalWages.toFixed(2);
- document.getElementById("totalExpensesInternal").textContent =
- totalExpensesInternal.toFixed(2);
-
- const totalExpenses = totalWages + totalExpensesInternal;
- document.getElementById("totalExpenses").value = totalExpenses.toFixed(2);
-
- // Обновить Caja Final
- const income = parseFloat(document.getElementById("income").value) || 0;
- const cajaInicial =
- parseFloat(document.getElementById("cajaInicial").value) || 0;
- const envelope = parseFloat(document.getElementById("envelope").value) || 0;
- const totalIncome = income + cajaInicial;
- const cajaFinal = totalIncome - totalExpenses - envelope;
-
- document.getElementById("cajaFinal").textContent = cajaFinal.toFixed(2);
-}
-
-// Отправка формы отчета
-// document.getElementById("reportForm").addEventListener("submit", (e) => {
-// e.preventDefault();
-
-// const formData = {
-// id: editingReportId || Date.now(),
-// date: new Date().toISOString().split("T")[0],
-// storeId: parseInt(document.getElementById("storeSelect").value),
-// userId: currentUser.id,
-// income: parseFloat(document.getElementById("income").value),
-// cajaInicial: parseFloat(document.getElementById("cajaInicial").value),
-// totalIncome: parseFloat(document.getElementById("totalIncome").value),
-// envelope: parseFloat(document.getElementById("envelope").value),
-// verified: false,
-// createdAt: new Date().toISOString(),
-// };
-
-// // Сбор зарплат
-// const wages = [];
-// const wageRows = document.querySelectorAll(".wage-row");
-// wageRows.forEach((row) => {
-// const name = row.querySelector(".wage-name").value;
-// const amount = parseFloat(row.querySelector(".wage-amount").value) || 0;
-// if (name && amount > 0) {
-// wages.push({ name, amount });
-// }
-// });
-
-// // Сбор расходов
-// const expenses = [];
-// const expenseRows = document.querySelectorAll(".expense-row");
-// expenseRows.forEach((row) => {
-// const name = row.querySelector(".expense-name").value;
-// const amount = parseFloat(row.querySelector(".expense-amount").value) || 0;
-// if (name && amount > 0) {
-// expenses.push({ name, amount });
-// }
-// });
-
-// formData.wages = wages;
-// formData.expenses = expenses;
-// formData.totalWages = calculateTotalWages();
-// formData.totalExpensesInternal = calculateTotalExpenses();
-// formData.totalExpenses = formData.totalWages + formData.totalExpensesInternal;
-// formData.cajaFinal =
-// formData.totalIncome - formData.totalExpenses - formData.envelope;
-
-// if (editingReportId) {
-// const index = database.reports.findIndex((r) => r.id === editingReportId);
-// database.reports[index] = formData;
-// editingReportId = null;
-// showNotification("Отчет успешно обновлен!");
-// } else {
-// database.reports.push(formData);
-// showNotification("Отчет успешно сохранен!");
-// }
-
-// // Очистка формы
-// document.getElementById("reportForm").reset();
-// document.getElementById("wagesContainer").innerHTML = `
-//
-//
-//
-//
-//
-// `;
-// document.getElementById("expensesContainer").innerHTML = `
-//
-//
-//
-//
-//
-// `;
-
-// updateTotals();
-// });
-
-document.getElementById("reportForm").addEventListener("submit", async (e) => {
- e.preventDefault();
-
- const formData = {
- storeId: parseInt(document.getElementById("storeSelect").value),
- reportDate: new Date().toISOString().split("T")[0], // Or get from input if user chooses date
- income: parseFloat(document.getElementById("income").value),
- initialCash: parseFloat(document.getElementById("cajaInicial").value),
- totalIncome: parseFloat(document.getElementById("totalIncome").value),
- wages: JSON.stringify(collectWages()),
- expenses: JSON.stringify(collectExpenses()),
- totalWages: calculateTotalWages(),
- totalExpenses: calculateTotalExpenses() + calculateTotalWages(),
- envelope: parseFloat(document.getElementById("envelope").value),
- finalCash:
- parseFloat(document.getElementById("totalIncome").value) -
- (calculateTotalWages() + calculateTotalExpenses()) -
- parseFloat(document.getElementById("envelope").value),
- };
- console.log("Sending report:", formData);
- const result = await createReport(formData);
- if (result.success) {
- showNotification("Отчет успешно создан!");
- document.getElementById("reportForm").reset();
+// Use global array for backend reports
+function viewReport(reportId) {
+ if (!appState.reportsList) {
+ showNotification("Reports not loaded yet!", "error");
+ return;
+ }
+ const report = appState.reportsList.find((r) => r.id === reportId);
+ if (report) {
+ showReportModal(report, true);
} else {
- showNotification(result.error || "Ошибка создания отчета", "error");
+ showNotification("Report not found!", "error");
}
-});
-
-function collectWages() {
- const wages = [];
- document.querySelectorAll(".wage-row").forEach((row) => {
- const name = row.querySelector(".wage-name").value;
- const amount = parseFloat(row.querySelector(".wage-amount").value) || 0;
- if (name && amount > 0) wages.push({ name, amount });
- });
- return wages;
-}
-function collectExpenses() {
- const expenses = [];
- document.querySelectorAll(".expense-row").forEach((row) => {
- const name = row.querySelector(".expense-name").value;
- const amount = parseFloat(row.querySelector(".expense-amount").value) || 0;
- if (name && amount > 0) expenses.push({ name, amount });
- });
- return expenses;
}
-// Отчет за сегодня для пользователя
-document.getElementById("todayReportBtn").addEventListener("click", () => {
- const today = new Date().toISOString().split("T")[0];
- const todayReport = database.reports.find(
- (r) => r.date === today && r.userId === currentUser.id
+function editReport(report) {
+ console.log("editReport() called with:", report);
+ const content = document.getElementById("reportViewContent");
+ const buttons = document.getElementById("reportModalButtons");
+
+ const shopName = (report.storeName || report.storeId || "").replace(
+ /"/g,
+ """
);
- if (todayReport) {
- showReportModal(todayReport, false); // false = не админ режим
- } else {
- showNotification("Отчет за сегодня не найден", "error");
- }
-});
+ content.innerHTML = `
+
+ `;
-function safeToFixed(value, digits = 2) {
- return (Number(value) || 0).toFixed(digits);
+ buttons.innerHTML = `
+
+
+ `;
+
+ document
+ .getElementById("saveReportBtn")
+ .addEventListener("click", async (e) => {
+ e.preventDefault();
+ await saveEditedReport(report.id);
+ });
+
+ document.getElementById("cancelEditBtn").addEventListener("click", (e) => {
+ e.preventDefault();
+ showReportModal(report, true);
+ });
+}
+
+async function saveEditedReport(reportId) {
+ // Get values from the form
+ const storeId = document.getElementById("editStoreSelect").value;
+ const reportDate = document.getElementById("editDate").value;
+ const incomeVal = document.getElementById("editIncome").value;
+ const initialCashVal = document.getElementById("editCajaInicial").value;
+ const envelopeVal = document.getElementById("editEnvelope").value;
+ const isVerified = document.getElementById("editVerified").checked ? 1 : 0;
+
+ // Build the payload dynamically
+ const data = {};
+
+ if (storeId) data.storeId = parseInt(storeId, 10);
+ if (reportDate) data.reportDate = reportDate;
+ if (incomeVal !== "") data.income = parseFloat(incomeVal);
+ if (initialCashVal !== "") data.initialCash = parseFloat(initialCashVal);
+ if (envelopeVal !== "") data.envelope = parseFloat(envelopeVal);
+
+ if (data.income !== undefined && data.initialCash !== undefined) {
+ data.totalIncome = data.income + data.initialCash;
+ }
+ data.isVerified = isVerified;
+
+ // REMOVE any NaN values
+ Object.keys(data).forEach((k) => {
+ if (typeof data[k] === "number" && isNaN(data[k])) delete data[k];
+ });
+
+ await updateReport(reportId, data);
}
// Показ модального окна отчета с исправленной прокруткой
@@ -763,579 +879,6 @@ function showReportModal(report, isAdmin = false) {
showModal("reportViewModal");
}
-// Редактирование отчета администратором
-// function editReport(report) {
-// const content = document.getElementById("reportViewContent");
-// const buttons = document.getElementById("reportModalButtons");
-
-// // Преобразуем просмотр в форму редактирования
-// content.innerHTML = `
-//
-// `;
-
-// // Изменяем кнопки на режим редактирования
-// buttons.innerHTML = `
-//
-//
-// `;
-
-// document.getElementById("saveReportBtn").addEventListener("click", () => {
-// saveEditedReport(report.id);
-// });
-
-// document.getElementById("cancelEditBtn").addEventListener("click", () => {
-// showReportModal(report, true); // Вернуться к просмотру
-// });
-// }
-
-// Сохранение отредактированного отчета
-// function saveEditedReport(reportId) {
-// const reportIndex = database.reports.findIndex((r) => r.id === reportId);
-// if (reportIndex === -1) return;
-
-// const report = database.reports[reportIndex];
-
-// // Обновляем данные
-// report.storeId = parseInt(document.getElementById("editStoreSelect").value);
-// report.date = document.getElementById("editDate").value;
-// report.income = parseFloat(document.getElementById("editIncome").value);
-// report.cajaInicial = parseFloat(
-// document.getElementById("editCajaInicial").value
-// );
-// report.envelope = parseFloat(document.getElementById("editEnvelope").value);
-// report.verified = document.getElementById("editVerified").checked;
-
-// // Пересчитываем итоги
-// report.totalIncome = report.income + report.cajaInicial;
-// report.cajaFinal =
-// report.totalIncome - report.totalExpenses - report.envelope;
-
-// database.reports[reportIndex] = report;
-
-// showNotification("Отчет успешно обновлен!");
-// hideModal("reportViewModal");
-
-// // Обновляем таблицу отчетов если мы в админке
-// if (currentUser.role === "admin") {
-// loadReports();
-// updateDashboard();
-// }
-// }
-
-// Подтверждение отчета
-// function verifyReport(reportId) {
-// const reportIndex = database.reports.findIndex((r) => r.id === reportId);
-// if (reportIndex !== -1) {
-// database.reports[reportIndex].verified = true;
-// showNotification("Отчет подтвержден!");
-// hideModal("reportViewModal");
-// loadReports();
-// updateDashboard();
-// }
-// }
-
-// Заполнение формы данными отчета (для пользователя)
-function fillFormWithReport(report) {
- editingReportId = report.id;
-
- document.getElementById("storeSelect").value = report.storeId;
- document.getElementById("income").value = report.income;
- document.getElementById("cajaInicial").value = report.cajaInicial;
- document.getElementById("envelope").value = report.envelope;
-
- // Заполнение зарплат
- const wagesContainer = document.getElementById("wagesContainer");
- wagesContainer.innerHTML = "";
- if (report.wages && report.wages.length > 0) {
- report.wages.forEach((wage) => {
- const row = document.createElement("div");
- row.className = "wage-row grid grid-cols-1 md:grid-cols-3 gap-4 mb-3";
- row.innerHTML = `
-
-
-
- `;
- wagesContainer.appendChild(row);
- });
- } else {
- addWageRow();
- }
-
- // Заполнение расходов
- const expensesContainer = document.getElementById("expensesContainer");
- expensesContainer.innerHTML = "";
- if (report.expenses && report.expenses.length > 0) {
- report.expenses.forEach((expense) => {
- const row = document.createElement("div");
- row.className = "expense-row grid grid-cols-1 md:grid-cols-3 gap-4 mb-3";
- row.innerHTML = `
-
-
-
- `;
- expensesContainer.appendChild(row);
- });
- } else {
- addExpenseRow();
- }
-
- updateTotals();
-}
-
-// === АДМИН ПАНЕЛЬ ===
-
-// Настройка вкладок администратора
-function setupAdminTabs() {
- const tabButtons = document.querySelectorAll(".admin-tab-btn");
- const tabContents = document.querySelectorAll(".admin-tab-content");
-
- tabButtons.forEach((button) => {
- button.addEventListener("click", () => {
- const tabId = button.dataset.tab;
-
- // Переключение активной вкладки
- tabButtons.forEach((btn) => {
- btn.className =
- "admin-tab-btn px-6 py-3 font-medium text-gray-600 hover:text-blue-600 transition-colors border-b-2 border-transparent hover:border-blue-500";
- });
- button.className =
- "admin-tab-btn px-6 py-3 font-medium transition-colors border-b-2 border-blue-500 text-blue-600";
-
- // Показ/скрытие содержимого
- tabContents.forEach((content) => {
- content.classList.add("hidden");
- });
- document.getElementById(tabId + "Tab").classList.remove("hidden");
-
- // Загрузка данных при переключении
- switch (tabId) {
- case "dashboard":
- updateDashboard();
- break;
- case "reports":
- loadReports();
- break;
- case "users":
- loadUsers();
- break;
- case "stores":
- loadStores();
- break;
- case "todo":
- loadTodos();
- break;
- }
- });
- });
-}
-
-// Обновление дашборда
-function updateDashboard() {
- // const reports = database.reports;
- const reports = window.reportsList || [];
- const users = window.usersList || [];
-
- // Расчет статистики
- const totalRevenue = reports.reduce((sum, r) => sum + r.totalIncome, 0);
- const totalExpenses = reports.reduce((sum, r) => sum + r.totalExpenses, 0);
- const totalReports = reports.length;
- const totalUsers = users.length;
-
- // Обновление карточек
- document.getElementById(
- "totalRevenueCard"
- ).textContent = `€${totalRevenue.toFixed(2)}`;
- document.getElementById(
- "totalExpensesCard"
- ).textContent = `€${totalExpenses.toFixed(2)}`;
- document.getElementById("totalReportsCard").textContent = totalReports;
- document.getElementById("totalUsersCard").textContent = totalUsers;
-
- // Создание графиков
- createCharts();
-}
-
-// Создание графиков
-function createCharts() {
- // График доходов по дням
-
- const reports = window.reportsList || [];
- const stores = window.storesList || [];
-
- const revenueCtx = document.getElementById("revenueChart");
- if (revenueCtx) {
- destroyChart(revenueChartInstance);
- const last7Days = [];
- const revenueData = [];
-
- for (let i = 6; i >= 0; i--) {
- const date = new Date();
- date.setDate(date.getDate() - i);
- const dateStr = date.toISOString().split("T")[0];
- last7Days.push(dateStr);
-
- const dayReports = reports.filter(
- (r) => (r.reportDate || r.date) === dateStr
- );
-
- const dayRevenue = dayReports.reduce(
- (sum, r) => sum + (Number(r.totalIncome) || 0),
- 0
- );
-
- revenueData.push(dayRevenue);
- }
-
- revenueChartInstance = new Chart(revenueCtx, {
- type: "line",
- data: {
- labels: last7Days,
- datasets: [
- {
- label: "Доходы",
- data: revenueData,
- borderColor: "rgb(59, 130, 246)",
- backgroundColor: "rgba(59, 130, 246, 0.1)",
- tension: 0.4,
- },
- ],
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: {
- display: false,
- },
- },
- scales: {
- y: {
- beginAtZero: true,
- ticks: {
- callback: function (value) {
- return "€" + value.toFixed(0);
- },
- },
- },
- },
- },
- });
- }
-
- // Круговая диаграмма расходов
- const expensesCtx = document.getElementById("expensesChart");
- if (expensesCtx) {
- destroyChart(expensesChartInstance);
- const totalWages = reports.reduce(
- (sum, r) => sum + (Number(r.totalWages) || 0),
- 0
- );
- const totalInternal = reports.reduce(
- (sum, r) => sum + (Number(r.totalExpenses) || 0),
- 0
- );
-
- expensesChartInstance = new Chart(expensesCtx, {
- type: "doughnut",
- data: {
- labels: ["Зарплаты", "Прочие расходы"],
- datasets: [
- {
- data: [totalWages, totalInternal],
- backgroundColor: ["#F59E0B", "#EF4444"],
- },
- ],
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: {
- position: "bottom",
- },
- },
- },
- });
- }
-
- // График по магазинам
- const storesCtx = document.getElementById("storesChart");
- if (storesCtx) {
- destroyChart(storesChartInstance);
- const storeData = stores.map((store) => {
- const storeReports = reports.filter((r) => r.storeId === store.id);
- const revenue = storeReports.reduce((sum, r) => sum + r.totalIncome, 0);
- return { name: store.name, revenue };
- });
- console.log("storeData for bar chart:", storeData);
-
- storesChartInstance = new Chart(storesCtx, {
- type: "bar",
- data: {
- labels: storeData.map((s) => s.name),
- datasets: [
- {
- label: "Доходы по магазинам",
- data: storeData.map((s) => s.revenue),
- backgroundColor: "#10B981",
- },
- ],
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: {
- display: false,
- },
- },
- scales: {
- y: {
- beginAtZero: true,
- ticks: {
- callback: function (value) {
- return "€" + value.toFixed(0);
- },
- },
- },
- },
- },
- });
- }
-
- // Тренды продаж
- const trendsCtx = document.getElementById("trendsChart");
- if (trendsCtx) {
- destroyChart(trendsChartInstance);
- const last30Days = [];
- const profitData = [];
-
- for (let i = 29; i >= 0; i--) {
- const date = new Date();
- date.setDate(date.getDate() - i);
- const dateStr = date.toISOString().split("T")[0];
- last30Days.push(dateStr);
-
- const dayReports = reports.filter(
- (r) => (r.reportDate || r.date) === dateStr
- );
- const dayProfit = dayReports.reduce(
- (sum, r) =>
- sum + ((Number(r.totalIncome) || 0) - (Number(r.totalExpenses) || 0)),
- 0
- );
- profitData.push(dayProfit);
- }
-
- trendsChartInstance = new Chart(trendsCtx, {
- type: "line",
- data: {
- labels: last30Days,
- datasets: [
- {
- label: "Прибыль",
- data: profitData,
- borderColor: "#8B5CF6",
- backgroundColor: "rgba(139, 92, 246, 0.1)",
- tension: 0.4,
- fill: true,
- },
- ],
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: {
- display: false,
- },
- },
- scales: {
- y: {
- beginAtZero: true,
- ticks: {
- callback: function (value) {
- return "€" + value.toFixed(0);
- },
- },
- },
- },
- },
- });
- }
-}
-
-// Загрузка отчетов в админке
-// 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);
-// });
-
-// // Отображение отчетов
-// 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);
-// });
-
-// // Настройка фильтров и экспорта
-// setupReportsFilters();
-// }
-
-// Просмотр отчета
-// function viewReport(reportId) {
-// const report = database.reports.find((r) => r.id === reportId);
-// if (report) {
-// showReportModal(report, true); // true = админ режим
-// }
-// }
-
-// Удаление отчета
-// function deleteReport(reportId) {
-// if (confirm("Вы уверены, что хотите удалить этот отчет?")) {
-// database.reports = database.reports.filter((r) => r.id !== reportId);
-// loadReports();
-// updateDashboard();
-// showNotification("Отчет удален!");
-// }
-// }
-
-function deleteReport(reportId) {
- showConfirmModal("Вы уверены, что хотите удалить этот отчет?", () => {
- apiDeleteReport(reportId);
- loadReports();
- updateDashboard();
- showNotification("Отчет удален!");
- });
-}
-
// Настройка фильтров отчетов
function setupReportsFilters() {
document
@@ -1351,7 +894,7 @@ function applyReportsFilters() {
const dateFrom = document.getElementById("filterDateFrom").value;
const dateTo = document.getElementById("filterDateTo").value;
- let filteredReports = window.reportsList || [];
+ let filteredReports = appState.reportsList || [];
if (storeFilter) {
filteredReports = filteredReports.filter(
@@ -1422,55 +965,73 @@ function applyReportsFilters() {
showNotification(`Найдено ${filteredReports.length} отчетов`);
}
+function deleteReport(reportId) {
+ showConfirmModal("Вы уверены, что хотите удалить этот отчет?", () => {
+ apiDeleteReport(reportId);
+ loadReports();
+ updateDashboard();
+ showNotification("Отчет удален!");
+ });
+}
+
+// Заполнение формы данными отчета (для пользователя)
+function fillFormWithReport(report) {
+ appState.editingReportId = report.id;
+
+ document.getElementById("storeSelect").value = report.storeId;
+ document.getElementById("income").value = report.income;
+ document.getElementById("cajaInicial").value = report.cajaInicial;
+ document.getElementById("envelope").value = report.envelope;
+
+ // Заполнение зарплат
+ const wagesContainer = document.getElementById("wagesContainer");
+ wagesContainer.innerHTML = "";
+ if (report.wages && report.wages.length > 0) {
+ report.wages.forEach((wage) => {
+ const row = document.createElement("div");
+ row.className = "wage-row grid grid-cols-1 md:grid-cols-3 gap-4 mb-3";
+ row.innerHTML = `
+
+
+
+ `;
+ wagesContainer.appendChild(row);
+ });
+ } else {
+ addWageRow();
+ }
+
+ // Заполнение расходов
+ const expensesContainer = document.getElementById("expensesContainer");
+ expensesContainer.innerHTML = "";
+ if (report.expenses && report.expenses.length > 0) {
+ report.expenses.forEach((expense) => {
+ const row = document.createElement("div");
+ row.className = "expense-row grid grid-cols-1 md:grid-cols-3 gap-4 mb-3";
+ row.innerHTML = `
+
+
+
+ `;
+ expensesContainer.appendChild(row);
+ });
+ } else {
+ addExpenseRow();
+ }
+
+ updateTotals();
+}
+
// Экспорт в Excel
-// function exportToExcel() {
-// const data = database.reports.map((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;
-
-// return {
-// Дата: report.date,
-// Магазин: store ? store.name : "Неизвестно",
-// Доход: `€${report.totalIncome.toFixed(2)}`,
-// Расходы: `€${report.totalExpenses.toFixed(2)}`,
-// Прибыль: `€${profit.toFixed(2)}`,
-// Пользователь: user ? user.username : "Неизвестно",
-// Статус: report.verified ? "Проверен" : "Не проверен",
-// };
-// });
-
-// // Создание CSV
-// const headers = Object.keys(data[0]);
-// const csvContent = [
-// headers.join(","),
-// ...data.map((row) => headers.map((header) => `"${row[header]}"`).join(",")),
-// ].join("\n");
-
-// // Скачивание файла
-// const blob = new Blob([csvContent], {
-// type: "text/csv;charset=utf-8;",
-// });
-// const link = document.createElement("a");
-// const url = URL.createObjectURL(blob);
-// link.setAttribute("href", url);
-// link.setAttribute(
-// "download",
-// `cash_reports_${new Date().toISOString().split("T")[0]}.csv`
-// );
-// link.style.visibility = "hidden";
-// document.body.appendChild(link);
-// link.click();
-// document.body.removeChild(link);
-
-// showNotification("Отчет экспортирован!");
-// }
-
function exportToExcel() {
// Use backend data
- const reports = window.reportsList || [];
- const stores = window.storesList || [];
- const users = window.usersList || [];
+ const reports = appState.reportsList || [];
+ const stores = appState.storesList || [];
+ const users = appState.usersList || [];
if (!reports.length) {
showNotification("Нет отчетов для экспорта", "info");
@@ -1533,73 +1094,312 @@ function exportToExcel() {
showNotification("Отчет экспортирован!");
}
-// === УПРАВЛЕНИЕ ПОЛЬЗОВАТЕЛЯМИ ===
+//REPORTS FORM LOGIC
-// Загрузка пользователей
-// function loadUsers() {
-// const tbody = document.getElementById("usersTableBody");
-// tbody.innerHTML = "";
+// Загрузка магазинов для пользователя
+function loadUserStores() {
+ const select = document.getElementById("storeSelect");
+ if (!select) return;
+ select.innerHTML =
+ '';
-// database.users.forEach((user) => {
-// const userStores = database.stores.filter((s) =>
-// user.stores.includes(s.id)
-// );
-// const storeNames =
-// userStores.map((s) => s.name).join(", ") || "Нет доступа";
-
-// const row = document.createElement("tr");
-// row.className = "hover:bg-gray-50";
-// row.innerHTML = `
-// ${user.id} |
-// ${
-// user.username
-// } |
-//
-//
-// ${
-// user.role === "admin"
-// ? "Администратор"
-// : "Сотрудник"
-// }
-//
-// |
-// ${storeNames} |
-//
-//
-//
-// |
-// `;
-// tbody.appendChild(row);
-// });
-// }
-
-// Добавление пользователя
-document.addEventListener("DOMContentLoaded", function () {
- const addUserBtn = document.getElementById("addUserBtn");
- if (addUserBtn) {
- addUserBtn.addEventListener("click", () => {
- editingUserId = null;
- showUserEditModal();
+ // For admin: show all
+ if (appState.currentUser.role === "admin") {
+ (appState.storesList || []).forEach((store) => {
+ const option = document.createElement("option");
+ option.value = store.id;
+ option.textContent = store.name;
+ select.appendChild(option);
});
+ } else {
+ // For employee: only their own stores
+ (appState.currentUser.stores || []).forEach((storeObj) => {
+ // storeObj could be an object ({id, name, ...}) or just an ID; check backend API
+ let store = storeObj;
+ if (typeof storeObj === "number") {
+ // If backend sends just IDs, look up in appState.storesList
+ store = (appState.storesList || []).find((s) => s.id === storeObj);
+ }
+ if (store) {
+ const option = document.createElement("option");
+ option.value = store.id;
+ option.textContent = store.name;
+ select.appendChild(option);
+ }
+ });
+ }
+}
+
+// Настройка автоматических расчетов в форме
+function setupFormCalculations() {
+ const incomeInput = document.getElementById("income");
+ const cajaInicialInput = document.getElementById("cajaInicial");
+ const envelopeInput = document.getElementById("envelope");
+
+ function updateCalculations() {
+ const income = parseFloat(incomeInput.value) || 0;
+ const cajaInicial = parseFloat(cajaInicialInput.value) || 0;
+ const envelope = parseFloat(envelopeInput.value) || 0;
+
+ const totalIncome = income + cajaInicial;
+ document.getElementById("totalIncome").value = totalIncome.toFixed(2);
+ document.getElementById("displayTotalIncome").value =
+ totalIncome.toFixed(2);
+
+ const totalWages = calculateTotalWages();
+ const totalExpensesInternal = calculateTotalExpenses();
+ const totalExpenses = totalWages + totalExpensesInternal;
+
+ document.getElementById("totalExpenses").value = totalExpenses.toFixed(2);
+
+ const cajaFinal = totalIncome - totalExpenses - envelope;
+ document.getElementById("cajaFinal").textContent = cajaFinal.toFixed(2);
+ }
+
+ incomeInput.addEventListener("input", updateCalculations);
+ cajaInicialInput.addEventListener("input", updateCalculations);
+ envelopeInput.addEventListener("input", updateCalculations);
+
+ setupDynamicRows();
+}
+
+// Настройка динамических строк
+function setupDynamicRows() {
+ document.getElementById("addWage").addEventListener("click", () => {
+ addWageRow();
+ });
+
+ document.getElementById("addExpense").addEventListener("click", () => {
+ addExpenseRow();
+ });
+
+ // Обновление при изменении значений
+ document.addEventListener("input", (e) => {
+ if (
+ e.target.classList.contains("wage-amount") ||
+ e.target.classList.contains("expense-amount")
+ ) {
+ updateTotals();
+ }
+ });
+
+ // Удаление строк
+ document.addEventListener("click", (e) => {
+ if (e.target.classList.contains("remove-wage")) {
+ e.target.closest(".wage-row").remove();
+ updateTotals();
+ } else if (e.target.classList.contains("remove-expense")) {
+ e.target.closest(".expense-row").remove();
+ updateTotals();
+ }
+ });
+}
+
+function addWageRow() {
+ const container = document.getElementById("wagesContainer");
+ const row = document.createElement("div");
+ row.className = "wage-row grid grid-cols-1 md:grid-cols-3 gap-4 mb-3";
+ row.innerHTML = `
+
+
+
+ `;
+ container.appendChild(row);
+}
+
+function addExpenseRow() {
+ const container = document.getElementById("expensesContainer");
+ const row = document.createElement("div");
+ row.className = "expense-row grid grid-cols-1 md:grid-cols-3 gap-4 mb-3";
+ row.innerHTML = `
+
+
+
+ `;
+ container.appendChild(row);
+}
+
+function calculateTotalWages() {
+ const amounts = document.querySelectorAll(".wage-amount");
+ let total = 0;
+ amounts.forEach((amount) => {
+ total += parseFloat(amount.value) || 0;
+ });
+ return total;
+}
+
+function calculateTotalExpenses() {
+ const amounts = document.querySelectorAll(".expense-amount");
+ let total = 0;
+ amounts.forEach((amount) => {
+ total += parseFloat(amount.value) || 0;
+ });
+ return total;
+}
+
+function updateTotals() {
+ const totalWages = calculateTotalWages();
+ const totalExpensesInternal = calculateTotalExpenses();
+
+ document.getElementById("totalWages").textContent = totalWages.toFixed(2);
+ document.getElementById("totalExpensesInternal").textContent =
+ totalExpensesInternal.toFixed(2);
+
+ const totalExpenses = totalWages + totalExpensesInternal;
+ document.getElementById("totalExpenses").value = totalExpenses.toFixed(2);
+
+ // Обновить Caja Final
+ const income = parseFloat(document.getElementById("income").value) || 0;
+ const cajaInicial =
+ parseFloat(document.getElementById("cajaInicial").value) || 0;
+ const envelope = parseFloat(document.getElementById("envelope").value) || 0;
+ const totalIncome = income + cajaInicial;
+ const cajaFinal = totalIncome - totalExpenses - envelope;
+
+ document.getElementById("cajaFinal").textContent = cajaFinal.toFixed(2);
+}
+
+function collectWages() {
+ const wages = [];
+ document.querySelectorAll(".wage-row").forEach((row) => {
+ const name = row.querySelector(".wage-name").value;
+ const amount = parseFloat(row.querySelector(".wage-amount").value) || 0;
+ if (name && amount > 0) wages.push({ name, amount });
+ });
+ return wages;
+}
+function collectExpenses() {
+ const expenses = [];
+ document.querySelectorAll(".expense-row").forEach((row) => {
+ const name = row.querySelector(".expense-name").value;
+ const amount = parseFloat(row.querySelector(".expense-amount").value) || 0;
+ if (name && amount > 0) expenses.push({ name, amount });
+ });
+ return expenses;
+}
+
+function safeToFixed(value, digits = 2) {
+ return (Number(value) || 0).toFixed(digits);
+}
+
+document.getElementById("reportForm").addEventListener("submit", async (e) => {
+ e.preventDefault();
+
+ const formData = {
+ storeId: parseInt(document.getElementById("storeSelect").value),
+ reportDate: new Date().toISOString().split("T")[0], // Or get from input if user chooses date
+ income: parseFloat(document.getElementById("income").value),
+ initialCash: parseFloat(document.getElementById("cajaInicial").value),
+ totalIncome: parseFloat(document.getElementById("totalIncome").value),
+ wages: JSON.stringify(collectWages()),
+ expenses: JSON.stringify(collectExpenses()),
+ totalWages: calculateTotalWages(),
+ totalExpenses: calculateTotalExpenses() + calculateTotalWages(),
+ envelope: parseFloat(document.getElementById("envelope").value),
+ finalCash:
+ parseFloat(document.getElementById("totalIncome").value) -
+ (calculateTotalWages() + calculateTotalExpenses()) -
+ parseFloat(document.getElementById("envelope").value),
+ };
+ console.log("Sending report:", formData);
+ const result = await createReport(formData);
+ if (result.success) {
+ showNotification("Отчет успешно создан!");
+ await loadReports();
+ document.getElementById("reportForm").reset();
+ } else {
+ showNotification(result.error || "Ошибка создания отчета", "error");
}
});
+// Отчет за сегодня для пользователя
+document.getElementById("todayReportBtn").addEventListener("click", () => {
+ const today = new Date().toISOString().split("T")[0];
+ const todayReport = database.reports.find(
+ (r) => r.date === today && r.userId === appState.currentUser.id
+ );
+
+ if (todayReport) {
+ showReportModal(todayReport, false); // false = не админ режим
+ } else {
+ showNotification("Отчет за сегодня не найден", "error");
+ }
+});
+
+//USERS
+
+async function loadUsers() {
+ console.log("Loading users...");
+ const tbody = document.getElementById("usersTableBody");
+ tbody.innerHTML = "";
+
+ const result = await getAllUsers();
+
+ // console.log("getAllUsers result:", result);
+
+ if (!result.success) {
+ showNotification(result.error, "error");
+ return;
+ }
+
+ const users = result.users;
+ appState.usersList = users;
+
+ users.forEach((user) => {
+ const userStores =
+ user.stores
+ .map((storeId) => {
+ const store = (appState.storesList || []).find(
+ (s) => s.id === storeId
+ );
+ return store ? store.name : "Нет доступа";
+ })
+ .join(", ") || "Нет доступа";
+
+ const row = document.createElement("tr");
+ row.className = "hover:bg-gray-50";
+ row.innerHTML = `
+ ${user.id} |
+ ${user.username} |
+
+
+ ${user.role === "admin" ? "Администратор" : "Сотрудник"}
+
+ |
+ ${userStores} |
+
+
+
+ |
+ `;
+ tbody.appendChild(row);
+ });
+
+ if (users.length === 0) {
+ showNotification("Нет пользователей для отображения", "info");
+ }
+}
+
// Редактирование пользователя
function editUser(userId) {
- editingUserId = userId;
- const user = usersList.find((u) => u.id === userId);
+ appState.editingUserId = userId;
+ const user = appState.usersList.find((u) => u.id === userId);
showUserEditModal(user);
}
@@ -1609,12 +1409,20 @@ function showUserEditModal(user) {
const title = document.getElementById("userModalTitle");
const form = document.getElementById("userEditForm");
- // If editingUserId, it's an edit; else, it's add
- title.textContent = editingUserId
+ // If appState.editingUserId, it's an edit; else, it's add
+ title.textContent = appState.editingUserId
? "Редактирование пользователя"
: "Добавление пользователя";
document.getElementById("userLogin").value = (user && user.username) || "";
+ const loginInput = document.getElementById("userLogin");
+ loginInput.value = (user && user.username) || "";
+ if (appState.editingUserId) {
+ loginInput.disabled = true;
+ } else {
+ loginInput.disabled = false;
+ }
+
document.getElementById("userPassword").value = "";
document.getElementById("userRole").value = (user && user.role) || "employee";
@@ -1622,7 +1430,7 @@ function showUserEditModal(user) {
const storesContainer = document.getElementById("userStoresAccess");
storesContainer.innerHTML = "";
- (window.storesList || []).forEach((store) => {
+ (appState.storesList || []).forEach((store) => {
const isChecked = user && user.stores && user.stores.includes(store.id);
const checkbox = document.createElement("label");
checkbox.className = "flex items-center";
@@ -1638,22 +1446,6 @@ function showUserEditModal(user) {
showModal("userEditModal");
}
-// Сохранение пользователя
-document.addEventListener("DOMContentLoaded", function () {
- const saveUserBtn = document.getElementById("saveUserBtn");
- const cancelUserBtn = document.getElementById("cancelUserBtn");
-
- if (saveUserBtn) {
- saveUserBtn.addEventListener("click", saveUser);
- }
-
- if (cancelUserBtn) {
- cancelUserBtn.addEventListener("click", () => {
- hideModal("userEditModal");
- });
- }
-});
-
//save user for create and update
async function saveUser() {
const login = document.getElementById("userLogin").value.trim();
@@ -1680,7 +1472,7 @@ async function saveUser() {
// Determine CREATE or EDIT
let result;
- if (!editingUserId) {
+ if (!appState.editingUserId) {
if (!password) {
showNotification("Укажите пароль для нового пользователя!", "error");
return;
@@ -1692,11 +1484,11 @@ async function saveUser() {
}
} else {
// EDIT
- result = await updateUser(editingUserId, userData);
+ result = await updateUser(appState.editingUserId, userData);
if (result.success) {
showNotification("Пользователь обновлен!");
}
- editingUserId = null;
+ appState.editingUserId = null;
}
// After save: UI update or error
@@ -1709,27 +1501,41 @@ async function saveUser() {
}
}
-// Удаление пользователя
-// function deleteUser(userId) {
-// const user = database.users.find((u) => u.id === userId);
-// const userReports = database.reports.filter((r) => r.userId === userId);
+//UI trigger: delete user with modal
+function deleteUser(userId) {
+ showConfirmModal("Вы уверены, что хотите удалить этого пользователя?", () =>
+ apiDeleteUser(userId)
+ );
+}
-// let message = `Вы уверены, что хотите удалить пользователя "${user.username}"?`;
-// if (userReports.length > 0) {
-// message += `\n\nВнимание! У этого пользователя есть ${userReports.length} связанных отчетов. Они также будут удалены.`;
-// }
+// Добавление пользователя
+document.addEventListener("DOMContentLoaded", function () {
+ const addUserBtn = document.getElementById("addUserBtn");
+ if (addUserBtn) {
+ addUserBtn.addEventListener("click", () => {
+ appState.editingUserId = null;
+ showUserEditModal();
+ });
+ }
+});
-// if (confirm(message)) {
-// database.users = database.users.filter((u) => u.id !== userId);
-// database.reports = database.reports.filter((r) => r.userId !== userId);
-// loadUsers();
-// loadReports();
-// updateDashboard();
-// showNotification("Пользователь и связанные отчеты удалены!");
-// }
-// }
+// Сохранение пользователя
+document.addEventListener("DOMContentLoaded", function () {
+ const saveUserBtn = document.getElementById("saveUserBtn");
+ const cancelUserBtn = document.getElementById("cancelUserBtn");
-// === УПРАВЛЕНИЕ МАГАЗИНАМИ ===
+ if (saveUserBtn) {
+ saveUserBtn.addEventListener("click", saveUser);
+ }
+
+ if (cancelUserBtn) {
+ cancelUserBtn.addEventListener("click", () => {
+ hideModal("userEditModal");
+ });
+ }
+});
+
+//SHOPS
// Загрузка магазинов
async function loadStores() {
@@ -1750,7 +1556,8 @@ async function loadStores() {
return;
}
const stores = data.stores;
- window.storesList = stores;
+ // appState.usersList = users;
+ appState.storesList = stores;
stores.forEach((store) => {
const row = document.createElement("tr");
@@ -1785,18 +1592,9 @@ async function loadStores() {
}
}
-// Добавление магазина
-document.getElementById("addStoreBtn").onclick = () => {
- window.editingStoreId = null;
- document.getElementById("storeModalTitle").textContent =
- "Добавление магазина";
- document.getElementById("storeName").value = "";
- showModal("storeEditModal");
-};
-
// Редактирование магазина
function editStore(storeId) {
- window.editingStoreId = storeId;
+ appState.editingStoreId = storeId;
showStoreEditModal();
}
@@ -1806,7 +1604,9 @@ function showStoreEditModal() {
const title = document.getElementById("storeModalTitle");
const form = document.getElementById("storeEditForm");
- const store = window.storesList.find((s) => s.id === window.editingStoreId);
+ const store = appState.storesList.find(
+ (s) => s.id === appState.editingStoreId
+ );
if (!store) {
showNotification("Магазин не найден!", "error");
@@ -1826,7 +1626,7 @@ async function saveStore() {
}
let result;
- if (window.editingStoreId == null) {
+ if (appState.editingStoreId == null) {
// Add
result = await createStore({ name });
if (result.success) {
@@ -1839,11 +1639,12 @@ async function saveStore() {
}
} else {
// Edit
- result = await updateStore(window.editingStoreId, { name });
+ result = await updateStore(appState.editingStoreId, { name });
if (result.success) {
showNotification("Магазин обновлен!");
hideModal("storeEditModal");
await loadStores();
+ await loadReports();
if (typeof loadUsers === "function") loadUsers();
if (typeof loadUserStores === "function") loadUserStores();
if (typeof updateDashboard === "function") updateDashboard();
@@ -1863,68 +1664,14 @@ async function saveStore() {
}
hideModal("storeEditModal");
- window.editingStoreId = null;
+ appState.editingStoreId = null;
await loadStores();
if (typeof updateDashboard === "function") updateDashboard();
}
-// Сохранение магазина
-// document.addEventListener("DOMContentLoaded", function () {
-// const saveStoreBtn = document.getElementById("saveStoreBtn");
-// const cancelStoreBtn = document.getElementById("cancelStoreBtn");
-
-// if (saveStoreBtn) {
-// saveStoreBtn.addEventListener("click", saveStore);
-// }
-
-// if (cancelStoreBtn) {
-// cancelStoreBtn.addEventListener("click", () => {
-// hideModal("storeEditModal");
-// });
-// }
-// });
-//2nd version
-document.addEventListener("DOMContentLoaded", function () {
- const saveStoreBtn = document.getElementById("saveStoreBtn");
- const cancelStoreBtn = document.getElementById("cancelStoreBtn");
- if (saveStoreBtn) saveStoreBtn.onclick = saveStore;
- if (cancelStoreBtn)
- cancelStoreBtn.onclick = () => {
- hideModal("storeEditModal");
- window.editingStoreId = null;
- document.getElementById("storeEditForm").reset();
- };
-});
-
// Удаление магазина
-// function deleteStore(storeId) {
-// const store = database.stores.find((s) => s.id === storeId);
-// const storeReports = database.reports.filter((r) => r.storeId === storeId);
-
-// let message = `Вы уверены, что хотите удалить магазин "${store.name}"?`;
-// if (storeReports.length > 0) {
-// message += `\n\nВнимание! У этого магазина есть ${storeReports.length} связанных отчетов. Они также будут удалены.`;
-// }
-
-// if (confirm(message)) {
-// database.stores = database.stores.filter((s) => s.id !== storeId);
-// database.reports = database.reports.filter((r) => r.storeId !== storeId);
-
-// // Удаление магазина из доступов пользователей
-// database.users.forEach((user) => {
-// user.stores = user.stores.filter((sid) => sid !== storeId);
-// });
-
-// loadStores();
-// loadUsers();
-// loadReports();
-// updateDashboard();
-// showNotification("Магазин и связанные отчеты удалены!");
-// }
-// }
-
function deleteStore(storeId) {
- const store = window.storesList.find((s) => s.id === storeId);
+ const store = appState.storesList.find((s) => s.id === storeId);
let message = `Вы уверены, что хотите удалить этот магазин "${store.name}"?`;
if (store.reportsCount && store.reportsCount > 0) {
@@ -1947,7 +1694,29 @@ async function handleDeleteStore(storeId) {
}
}
-// === TODO СИСТЕМА ===
+// Добавление магазина
+document.getElementById("addStoreBtn").onclick = () => {
+ appState.editingStoreId = null;
+ document.getElementById("storeModalTitle").textContent =
+ "Добавление магазина";
+ document.getElementById("storeName").value = "";
+ showModal("storeEditModal");
+};
+
+// Сохранение магазина
+document.addEventListener("DOMContentLoaded", function () {
+ const saveStoreBtn = document.getElementById("saveStoreBtn");
+ const cancelStoreBtn = document.getElementById("cancelStoreBtn");
+ if (saveStoreBtn) saveStoreBtn.onclick = saveStore;
+ if (cancelStoreBtn)
+ cancelStoreBtn.onclick = () => {
+ hideModal("storeEditModal");
+ appState.editingStoreId = null;
+ document.getElementById("storeEditForm").reset();
+ };
+});
+
+//TODO
// Загрузка TODO
function loadTodos() {
@@ -2029,20 +1798,9 @@ function toggleTodo(todoId) {
showNotification("Статус задачи обновлен!");
}
-// Добавление TODO
-document.addEventListener("DOMContentLoaded", function () {
- const addTodoBtn = document.getElementById("addTodoBtn");
- if (addTodoBtn) {
- addTodoBtn.addEventListener("click", () => {
- editingTodoId = null;
- showTodoEditModal();
- });
- }
-});
-
// Редактирование TODO
function editTodo(todoId) {
- editingTodoId = todoId;
+ appState.editingTodoId = todoId;
showTodoEditModal();
}
@@ -2052,8 +1810,8 @@ function showTodoEditModal() {
const title = document.getElementById("todoModalTitle");
const form = document.getElementById("todoEditForm");
- if (editingTodoId) {
- const todo = database.todos.find((t) => t.id === editingTodoId);
+ if (appState.editingTodoId) {
+ const todo = database.todos.find((t) => t.id === appState.editingTodoId);
title.textContent = "Редактирование задачи";
document.getElementById("todoTitle").value = todo.title;
document.getElementById("todoDescription").value = todo.description;
@@ -2066,22 +1824,6 @@ function showTodoEditModal() {
showModal("todoEditModal");
}
-// Сохранение TODO
-document.addEventListener("DOMContentLoaded", function () {
- const saveTodoBtn = document.getElementById("saveTodoBtn");
- const cancelTodoBtn = document.getElementById("cancelTodoBtn");
-
- if (saveTodoBtn) {
- saveTodoBtn.addEventListener("click", saveTodo);
- }
-
- if (cancelTodoBtn) {
- cancelTodoBtn.addEventListener("click", () => {
- hideModal("todoEditModal");
- });
- }
-});
-
function saveTodo() {
const title = document.getElementById("todoTitle").value;
const description = document.getElementById("todoDescription").value;
@@ -2092,9 +1834,11 @@ function saveTodo() {
return;
}
- if (editingTodoId) {
+ if (appState.editingTodoId) {
// Редактирование
- const todoIndex = database.todos.findIndex((t) => t.id === editingTodoId);
+ const todoIndex = database.todos.findIndex(
+ (t) => t.id === appState.editingTodoId
+ );
database.todos[todoIndex].title = title;
database.todos[todoIndex].description = description;
database.todos[todoIndex].priority = priority;
@@ -2126,31 +1870,158 @@ function deleteTodo(todoId) {
}
}
-// Обработчики выхода
-document.getElementById("logoutBtn").addEventListener("click", logout);
-document.getElementById("adminLogoutBtn").addEventListener("click", logout);
+// Добавление TODO
+document.addEventListener("DOMContentLoaded", function () {
+ const addTodoBtn = document.getElementById("addTodoBtn");
+ if (addTodoBtn) {
+ addTodoBtn.addEventListener("click", () => {
+ appState.editingTodoId = null;
+ showTodoEditModal();
+ });
+ }
+});
-function logout() {
- currentUser = null;
- editingReportId = null;
+// Сохранение TODO
+document.addEventListener("DOMContentLoaded", function () {
+ const saveTodoBtn = document.getElementById("saveTodoBtn");
+ const cancelTodoBtn = document.getElementById("cancelTodoBtn");
- document.getElementById("loginScreen").classList.remove("hidden");
- document.getElementById("userInterface").classList.add("hidden");
- document.getElementById("adminInterface").classList.add("hidden");
+ if (saveTodoBtn) {
+ saveTodoBtn.addEventListener("click", saveTodo);
+ }
- document.getElementById("loginForm").reset();
- document.getElementById("loginError").classList.add("hidden");
+ if (cancelTodoBtn) {
+ cancelTodoBtn.addEventListener("click", () => {
+ hideModal("todoEditModal");
+ });
+ }
+});
- showNotification("Вы вышли из системы");
+// ####################MOCK###########
+// База данных (симуляция)
+let database = {
+ users: [
+ {
+ id: 1,
+ username: "admin",
+ password: "admin123",
+ role: "admin",
+ stores: [],
+ },
+ {
+ id: 2,
+ username: "employee",
+ password: "password123",
+ role: "employee",
+ stores: [1, 2],
+ },
+ {
+ id: 3,
+ username: "cashier1",
+ password: "password123",
+ role: "employee",
+ stores: [1, 2],
+ },
+ {
+ id: 4,
+ username: "cashier2",
+ password: "password123",
+ role: "employee",
+ stores: [3],
+ },
+ ],
+ stores: [
+ { id: 1, name: "Магазин 1" },
+ { id: 2, name: "Магазин 2" },
+ { id: 3, name: "Магазин 3" },
+ { id: 4, name: "Магазин 4" },
+ ],
+ reports: [],
+ todos: [
+ {
+ id: 1,
+ title: "Исправить модальные окна",
+ description: "Исправить прокрутку и видимость кнопок в модальных окнах",
+ completed: true,
+ priority: "high",
+ createdAt: "2024-01-15",
+ },
+ {
+ id: 2,
+ title: "Добавить экспорт в PDF",
+ description: "Реализовать функцию экспорта отчетов в PDF формат",
+ completed: false,
+ priority: "medium",
+ createdAt: "2024-01-16",
+ },
+ {
+ id: 3,
+ title: "Улучшить графики",
+ description: "Добавить больше интерактивности в графики Dashboard",
+ completed: false,
+ priority: "low",
+ createdAt: "2024-01-17",
+ },
+ ],
+};
+
+// Генерация тестовых данных для отчетов
+function generateTestData() {
+ const reports = [];
+ const today = new Date();
+
+ for (let i = 0; i < 30; i++) {
+ const date = new Date(today);
+ date.setDate(date.getDate() - i);
+
+ const storeId = Math.floor(Math.random() * 4) + 1;
+ const userId = Math.floor(Math.random() * 3) + 2; // employee пользователи
+
+ const income = Math.floor(Math.random() * 2000) + 500;
+ const cajaInicial = Math.floor(Math.random() * 300) + 100;
+ const totalIncome = income + cajaInicial;
+
+ const wages = Math.floor(Math.random() * 400) + 100;
+ const expenses = Math.floor(Math.random() * 200) + 50;
+ const totalExpenses = wages + expenses;
+
+ const envelope = Math.floor(Math.random() * 200) + 100;
+ const cajaFinal = totalIncome - totalExpenses - envelope;
+
+ reports.push({
+ id: i + 1,
+ date: date.toISOString().split("T")[0],
+ storeId: storeId,
+ userId: userId,
+ income: income,
+ cajaInicial: cajaInicial,
+ totalIncome: totalIncome,
+ wages: [
+ {
+ name: "Сотрудник " + (Math.floor(Math.random() * 3) + 1),
+ amount: wages,
+ },
+ ],
+ expenses: [
+ {
+ name: "Расходы " + (Math.floor(Math.random() * 3) + 1),
+ amount: expenses,
+ },
+ ],
+ totalWages: wages,
+ totalExpensesInternal: expenses,
+ totalExpenses: totalExpenses,
+ envelope: envelope,
+ cajaFinal: cajaFinal,
+ verified: Math.random() > 0.3,
+ createdAt: date.toISOString(),
+ });
+ }
+
+ database.reports = reports;
}
-// Глобальные функции для onclick обработчиков
-window.viewReport = viewReport;
-window.deleteReport = deleteReport;
-window.editUser = editUser;
-window.deleteUser = deleteUser;
-window.editStore = editStore;
-window.deleteStore = deleteStore;
-window.editTodo = editTodo;
-window.deleteTodo = deleteTodo;
-window.toggleTodo = toggleTodo;
+// Инициализация тестовых данных
+generateTestData();
+
+// ####################################