// нахожу экран входа и главное окно
const authScreen = document.getElementById("authScreen");
const app = document.getElementById("app");
// нахожу формы входа и регистрации
const loginFormContainer = document.getElementById("loginFormContainer");
const registerFormContainer = document.getElementById("registerFormContainer");
const loginForm = document.getElementById("loginForm");
const registerForm = document.getElementById("registerForm");
// сюда пишу ошибки
const loginError = document.getElementById("loginError");
const registerError = document.getElementById("registerError");
// кнопки переключения вход / регистрация
const showRegisterButton = document.getElementById("showRegister");
const showLoginButton = document.getElementById("showLogin");
// кнопка выйти
const logoutButton = document.getElementById("logoutButton");
// где показываю кто вошел
const currentUser = document.getElementById("currentUser");
const currentRole = document.getElementById("currentRole");
const userAvatar = document.getElementById("userAvatar");
// заголовок страницы и место для таблицы
const pageTitle = document.getElementById("pageTitle");
const pageDescription = document.getElementById("pageDescription");
const pageContent = document.getElementById("pageContent");
const addButton = document.getElementById("addButton");
// всплывающее окно
const modal = document.getElementById("modal");
const modalTitle = document.getElementById("modalTitle");
const modalForm = document.getElementById("modalForm");
const closeModal = document.getElementById("closeModal");
// маленькое сообщение снизу
const toast = document.getElementById("toast");
// список всех страниц и откуда брать данные
const pages = {
products: { title: "Товары", description: "Все товары магазина", endpoint: "/api/products" },
categories: { title: "Категории", description: "Категории товаров", endpoint: "/api/categories" },
warehouses: { title: "Склады", description: "Все склады", endpoint: "/api/warehouses" },
stock: { title: "Остатки", description: "Что лежит на складах", endpoint: "/api/stocks" },
users: { title: "Пользователи", description: "Все люди в системе", endpoint: "/api/users" },
orders: { title: "Заказы", description: "Все заказы", endpoint: "/api/orders" }
};
// какая страница открыта сейчас
let currentPage = "products";
// кого изменяю (если null значит добавляю нового)
let editingId = null;
// кто вошел в систему
let currentAuthUser = null;
// показываю сообщение снизу на 2 секунды
function showToast(message) {
toast.textContent = message; // пишу текст
toast.classList.add("show"); // показываю
setTimeout(() => {
toast.classList.remove("show"); // прячу обратно
}, 2500);
}
// защита от плохих символов чтоб не ломали страницу
function escapeHtml(value) {
if (value === null || value === undefined) {
return "";
}
return String(value)
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "'");
}
// делаю цену красивой: 1000 -> 1 000,00 ₽
function formatMoney(value) {
return Number(value).toLocaleString("ru-RU", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}) + " ₽";
}
// делаю дату понятной
function formatDate(value) {
if (!value) {
return "";
}
return new Date(value).toLocaleString("ru-RU");
}
// одна функция для всех запросов на сервер
async function api(url, options = {}) {
// отправляю запрос
const response = await fetch(url, {
headers: { "Content-Type": "application/json" },
...options
});
// если ошибка то показываю что случилось
if (!response.ok) {
let message = "Ошибка.";
const text = await response.text();
if (text) {
try {
const data = JSON.parse(text);
message = data.title || data.message || text;
} catch {
message = text;
}
}
throw new Error(message);
}
// если пусто то возвращаю null
if (response.status === 204) {
return null;
}
const text = await response.text();
if (!text) {
return null;
}
return JSON.parse(text); // превращаю ответ в объект
}
// показываю форму входа
function showLoginForm() {
loginFormContainer.classList.remove("hidden");
registerFormContainer.classList.add("hidden");
loginError.textContent = "";
registerError.textContent = "";
}
// показываю форму регистрации
function showRegisterForm() {
loginFormContainer.classList.add("hidden");
registerFormContainer.classList.remove("hidden");
loginError.textContent = "";
registerError.textContent = "";
}
// захожу в программу после входа
function enterApplication(user) {
currentAuthUser = user; // запоминаю кто вошел
sessionStorage.setItem("demoUser", JSON.stringify(user)); // сохраняю чтоб не выкидывало
currentUser.textContent = user.login; // пишу логин
currentRole.textContent = user.role || "Client"; // пишу роль
userAvatar.textContent = user.login ? user.login.charAt(0).toUpperCase() : "U"; // первая буква в кружок
authScreen.classList.add("hidden"); // прячу вход
app.classList.remove("hidden"); // показываю программу
showPage("products"); // открываю товары
}
// выхожу из аккаунта
function logout() {
sessionStorage.removeItem("demoUser"); // забываю пользователя
currentAuthUser = null;
app.classList.add("hidden"); // прячу программу
authScreen.classList.remove("hidden"); // показываю вход
loginForm.reset(); // чищу поля
registerForm.reset();
showLoginForm();
}
// когда жму войти
loginForm.addEventListener("submit", async (event) => {
event.preventDefault(); // чтоб страница не перезагружалась
loginError.textContent = "";
const login = document.getElementById("login").value.trim();
const password = document.getElementById("password").value;
if (!login || !password) {
loginError.textContent = "Напишите логин и пароль.";
return;
}
try {
const user = await api("/api/auth/login", {
method: "POST",
body: JSON.stringify({ login, password })
});
enterApplication(user); // захожу
loginForm.reset();
} catch (error) {
loginError.textContent = error.message || "Не получилось войти.";
}
});
// когда жму зарегистрироваться
registerForm.addEventListener("submit", async (event) => {
event.preventDefault();
registerError.textContent = "";
const login = document.getElementById("registerLogin").value.trim();
const password = document.getElementById("registerPassword").value;
const passwordRepeat = document.getElementById("registerPasswordRepeat").value;
// простые проверки
if (!login || !password || !passwordRepeat) {
registerError.textContent = "Заполните все поля.";
return;
}
if (login.length < 2) {
registerError.textContent = "Логин слишком короткий.";
return;
}
if (password.length < 4) {
registerError.textContent = "Пароль слишком короткий.";
return;
}
if (password !== passwordRepeat) {
registerError.textContent = "Пароли не совпадают.";
return;
}
try {
await api("/api/auth/register", {
method: "POST",
body: JSON.stringify({ login, password, roleId: 2 })
});
registerForm.reset();
showLoginForm(); // иду на вход
document.getElementById("login").value = login;
loginError.textContent = "Готово. Теперь войдите.";
} catch (error) {
registerError.textContent = error.message || "Не получилось.";
}
});
// кнопки переключения
showRegisterButton.addEventListener("click", showRegisterForm);
showLoginButton.addEventListener("click", showLoginForm);
logoutButton.addEventListener("click", logout);
// кнопки меню слева
document.querySelectorAll(".nav-button").forEach((button) => {
button.addEventListener("click", () => {
// убираю синий цвет у всех
document.querySelectorAll(".nav-button").forEach((b) => b.classList.remove("active"));
button.classList.add("active"); // крашу нажатую
showPage(button.dataset.page); // открываю страницу
});
});
// открываю страницу (товары, заказы и тд)
async function showPage(page) {
currentPage = page; // запоминаю где я
const config = pages[page];
if (!config) {
return;
}
pageTitle.textContent = config.title; // пишу название
pageDescription.textContent = config.description;
pageContent.innerHTML = "
Загрузка...
"; // пока грузится
try {
const data = await api(config.endpoint); // беру данные с сервера
renderPage(page, data); // рисую
} catch (error) {
pageContent.innerHTML = "
Не получилось: " + escapeHtml(error.message) + "
";
}
}
// выбираю что рисовать
function renderPage(page, data) {
// если пусто то так и пишу
if (!Array.isArray(data) || data.length === 0) {
pageContent.innerHTML = "
Пока пустоНажмите Добавить
";
return;
}
if (page === "products") {
renderProducts(data);
}
if (page === "categories") {
renderCategories(data);
}
if (page === "warehouses") {
renderWarehouses(data);
}
if (page === "stock") {
renderStock(data);
}
if (page === "users") {
renderUsers(data);
}
if (page === "orders") {
renderOrders(data);
}
}
// рисую товары карточками
function renderProducts(products) {
let html = "
"; // начало сетки
for (const p of products) {
html += "