Files
cash-report-system/frontend/api.js
2025-07-31 01:46:26 +02:00

536 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//API
// const API_BASE_URL = "http://195.209.214.159/api";
//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 loadUserStores === "function") loadUserStores();
if (typeof updateDashboard === "function") updateDashboard();
}
if (prop === "usersList") {
if (typeof updateDashboard === "function") updateDashboard();
}
if (prop === "reportsList") {
if (typeof updateDashboard === "function") updateDashboard();
}
}
);
//AUTH
//Login & Logout
async function loginUser(username, password) {
try {
const response = await fetch(`${API_BASE_URL}/auth/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ username, password }),
});
const data = await response.json();
if (response.ok) {
return { success: true, ...data };
} else {
return {
success: false,
error:
data.error ||
(data.errors && data.errors[0]?.msg) ||
"Ошибка авторизации",
};
}
} catch (err) {
return { success: false, error: "Нет соединения с сервером" };
}
}
function logout() {
appState.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.addEventListener("DOMContentLoaded", async () => {
const token = localStorage.getItem("token");
if (token) {
// Try to get user info from backend
try {
const response = await fetch(`${API_BASE_URL}/auth/me`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (response.ok) {
const data = await response.json();
appState.currentUser = data.user;
document.getElementById("loginScreen").classList.add("hidden");
if (appState.currentUser.role === "admin") {
showAdminInterface();
} else {
showUserInterface();
}
} else {
// Token invalid/expired
localStorage.removeItem("token");
document.getElementById("loginScreen").classList.remove("hidden");
}
} catch (err) {
showNotification("Нет соединения с сервером", "error");
}
} else {
// No token, show login screen
document.getElementById("loginScreen").classList.remove("hidden");
}
});
document.getElementById("logoutBtn").addEventListener("click", logout);
document.getElementById("adminLogoutBtn").addEventListener("click", logout);
//USERS
//GET all users (admin only)
async function getAllUsers() {
const token = localStorage.getItem("token");
try {
const response = await fetch(`${API_BASE_URL}/users`, {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
});
const data = await response.json();
if (response.ok) {
return { success: true, users: data.users };
} else {
return {
success: false,
error: data.error || data.message || "Ошибка получения пользователей",
};
}
} catch (err) {
return { success: false, error: "Нет соединения с сервером" };
}
}
appState.usersList = [];
//add user
async function createUser(userData) {
const token = localStorage.getItem("token");
try {
const response = await fetch(`${API_BASE_URL}/users`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(userData),
});
const data = await response.json();
if (response.ok) {
return { success: true, ...data };
} else {
return {
success: false,
error:
data.error ||
(data.errors && data.errors[0]?.msg) ||
"Ошибка создания пользователя",
};
}
} catch {
return { success: false, error: "Нет соединения с сервером" };
}
}
//edit user
async function updateUser(userId, userData) {
const token = localStorage.getItem("token");
try {
const response = await fetch(`${API_BASE_URL}/users/${userId}`, {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(userData),
});
const data = await response.json();
if (response.ok) {
return { success: true };
} else {
return {
success: false,
error:
data.error ||
(data.errors && data.errors[0]?.msg) ||
"Ошибка обновления пользователя",
};
}
} catch {
return { success: false, error: "Нет соединения с сервером" };
}
}
// 1. Create modal on-demand if missing
function ensureConfirmModal() {
if (document.getElementById("confirmModal")) return;
const modal = document.createElement("div");
modal.id = "confirmModal";
modal.className =
"hidden fixed inset-0 z-50 bg-black bg-opacity-30 flex items-center justify-center";
modal.innerHTML = `
<div class="bg-white rounded-lg shadow-lg p-8 max-w-sm w-full relative">
<div id="confirmModalText" class="text-lg text-gray-700 mb-6"></div>
<div class="flex justify-end space-x-4">
<button id="confirmModalOk" class="px-5 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700">Удалить</button>
<button id="confirmModalCancel" class="px-5 py-2 bg-gray-300 text-gray-700 rounded-lg hover:bg-gray-400">Отмена</button>
</div>
</div>
`;
document.body.appendChild(modal);
}
// 2. Show confirm modal
function showConfirmModal(message, onConfirm) {
ensureConfirmModal();
const modal = document.getElementById("confirmModal");
const text = document.getElementById("confirmModalText");
const okBtn = document.getElementById("confirmModalOk");
const cancelBtn = document.getElementById("confirmModalCancel");
text.textContent = message;
modal.classList.remove("hidden");
function cleanup() {
modal.classList.add("hidden");
okBtn.removeEventListener("click", okHandler);
cancelBtn.removeEventListener("click", cancelHandler);
}
function okHandler() {
cleanup();
onConfirm();
}
function cancelHandler() {
cleanup();
}
okBtn.addEventListener("click", okHandler);
cancelBtn.addEventListener("click", cancelHandler);
}
// 3. API call to delete user
async function apiDeleteUser(userId) {
const token = localStorage.getItem("token");
try {
const response = await fetch(`${API_BASE_URL}/users/${userId}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
});
const data = await response.json();
if (response.ok) {
showNotification("Пользователь удален!");
loadUsers();
updateDashboard();
} else {
showNotification(data.error || "Ошибка удаления пользователя", "error");
}
} catch {
showNotification("Нет соединения с сервером", "error");
}
}
//REPORTS ADMIN
//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();
console.log(data);
if (response.ok) {
return { success: true, reports: data.reports };
} else {
return {
success: false,
error: data.error || data.message || "Ошибка получения отчетов",
};
}
} catch (err) {
return { success: false, error: "Нет соединения с сервером" };
}
}
//edit report
async function updateReport(reportId, data) {
const token = localStorage.getItem("token");
try {
const res = await fetch(`${API_BASE_URL}/reports/${reportId}`, {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
const result = await res.json();
if (res.ok && result.updated) {
showNotification("Отчет обновлен!", "success");
hideModal("reportViewModal");
await loadReports();
console.log(data);
if (typeof updateDashboard === "function") updateDashboard();
return { success: true };
} else {
showNotification(result.error || "Ошибка обновления отчета", "error");
return { success: false, error: result.error };
}
} catch (err) {
showNotification("Ошибка сети. Попробуйте еще раз.", "error");
return { success: false, error: err.message };
}
}
//accept report (admin only)
async function verifyReport(reportId) {
const token = localStorage.getItem("token");
try {
const response = await fetch(`${API_BASE_URL}/reports/${reportId}/verify`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
});
const data = await response.json();
if (response.ok) {
showNotification("Отчет успешно подтвержден!", "success");
hideModal("reportViewModal");
await loadReports();
if (typeof updateDashboard === "function") {
updateDashboard(); // Refresh dashboard if it exists
}
return { success: true, ...data };
} else {
showNotification(data.error || "Ошибка подтверждения отчета", "error");
return { success: false, error: data.error || "Ошибка" };
}
} catch (err) {
showNotification("Нет соединения с сервером", "error");
return { success: false, error: "Нет соединения с сервером" };
}
}
//delete report (admin only)
async function apiDeleteReport(reportId) {
const token = localStorage.getItem("token");
try {
const response = await fetch(`${API_BASE_URL}/reports/${reportId}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
});
const data = await response.json();
if (response.ok && data.deleted) {
showNotification("Отчет удален!");
await loadReports();
if (typeof updateDashboard === "function") updateDashboard();
} else {
showNotification(data.error || "Ошибка удаления отчета", "error");
}
} catch {
showNotification("Нет соединения с сервером", "error");
}
}
//REPORTS WORKER
//create report
// api.js
async function createReport(data) {
const token = localStorage.getItem("token");
try {
const response = await fetch(`${API_BASE_URL}/reports`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
const result = await response.json();
if (response.ok && result.id) {
return { success: true, id: result.id };
} else {
return {
success: false,
error:
result.error ||
(result.errors && result.errors[0]?.msg) ||
"Ошибка создания отчета",
};
}
} catch (err) {
return { success: false, error: "Нет соединения с сервером" };
}
}
//STORES (for admin)
// 1. Get all stores
async function getStores() {
const token = localStorage.getItem("token");
try {
const response = await fetch(`${API_BASE_URL}/stores`, {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
});
const data = await response.json();
if (response.ok) {
return { success: true, stores: data.stores };
} else {
return {
success: false,
error: data.error || "Ошибка получения магазинов",
};
}
} catch {
return { success: false, error: "Нет соединения с сервером" };
}
}
// 2. Create new store (admin only)
async function createStore(storeData) {
const token = localStorage.getItem("token");
try {
const response = await fetch(`${API_BASE_URL}/stores`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(storeData),
});
const data = await response.json();
if (response.ok && data.id) {
return { success: true, id: data.id };
} else {
return {
success: false,
error: data.error || "Ошибка создания магазина",
};
}
} catch {
return { success: false, error: "Нет соединения с сервером" };
}
}
// 3. Edit store (admin only)
async function updateStore(storeId, storeData) {
const token = localStorage.getItem("token");
try {
const response = await fetch(`${API_BASE_URL}/stores/${storeId}`, {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(storeData),
});
const data = await response.json();
return {
success: response.ok && data.updated,
error: data.error || "",
status: response.status,
};
} catch {
return { success: false, error: "Нет соединения с сервером" };
}
}
// 4. Delete store (admin only)
async function deleteStoreApi(storeId) {
const token = localStorage.getItem("token");
try {
const response = await fetch(`${API_BASE_URL}/stores/${storeId}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
});
const data = await response.json();
if (response.ok && data.deleted) {
return { success: true };
} else {
return {
success: false,
error: data.error || "Ошибка удаления магазина",
};
}
} catch {
return { success: false, error: "Нет соединения с сервером" };
}
}