diff --git a/public/app.js b/public/app.js
index 74b98f5..c0ca81b 100644
--- a/public/app.js
+++ b/public/app.js
@@ -8,92 +8,92 @@ const inventoryList = document.getElementById('inventory');
const advanceButton = document.getElementById('advance-date');
const API_BASE =
- window.location.protocol === 'file:' ? 'http://localhost:3000' : window.location.origin;
+ window.location.protocol === 'file:' ? 'http://localhost:3000' : window.location.origin;
let products = [];
let orders = [];
let currentDate = '';
function getLocalISODate() {
- const now = new Date();
- const offsetMs = now.getTimezoneOffset() * 60 * 1000;
- return new Date(now.getTime() - offsetMs).toISOString().slice(0, 10);
+ const now = new Date();
+ const offsetMs = now.getTimezoneOffset() * 60 * 1000;
+ return new Date(now.getTime() - offsetMs).toISOString().slice(0, 10);
}
class OrderCard {
- constructor(order, allOrders, allProducts) {
- this.order = order;
- this.orders = allOrders;
- this.products = allProducts;
- }
+ constructor(order, allOrders, allProducts) {
+ this.order = order;
+ this.orders = allOrders;
+ this.products = allProducts;
+ }
- build() {
- const card = document.createElement('div');
- card.className = 'card shadow-sm order-card';
+ build() {
+ const card = document.createElement('div');
+ card.className = 'card shadow-sm order-card';
- const body = document.createElement('div');
- body.className = 'card-body';
+ const body = document.createElement('div');
+ body.className = 'card-body';
- body.appendChild(this.buildHeader());
- body.appendChild(this.buildItemsSection());
+ body.appendChild(this.buildHeader());
+ body.appendChild(this.buildItemsSection());
- card.appendChild(body);
- return card;
- }
+ card.appendChild(body);
+ return card;
+ }
- buildHeader() {
- const header = document.createElement('div');
- header.className = 'd-flex justify-content-between flex-wrap gap-2';
+ buildHeader() {
+ const header = document.createElement('div');
+ header.className = 'd-flex justify-content-between flex-wrap gap-2';
- const info = document.createElement('div');
- const name = document.createElement('h3');
- name.className = 'h5 mb-1';
- name.textContent = this.order.customer_name;
+ const info = document.createElement('div');
+ const name = document.createElement('h3');
+ name.className = 'h5 mb-1';
+ name.textContent = this.order.customer_name;
- const idMeta = document.createElement('div');
- idMeta.className = 'text-muted order-meta';
- idMeta.textContent = `ID: ${this.order.id}`;
+ const idMeta = document.createElement('div');
+ idMeta.className = 'text-muted order-meta';
+ idMeta.textContent = `ID: ${this.order.id}`;
- const dateMeta = document.createElement('div');
- dateMeta.className = 'text-muted order-meta';
- dateMeta.textContent = `Дата: ${this.order.order_date}`;
+ const dateMeta = document.createElement('div');
+ dateMeta.className = 'text-muted order-meta';
+ dateMeta.textContent = `Дата: ${this.order.order_date}`;
- info.appendChild(name);
- info.appendChild(idMeta);
- info.appendChild(dateMeta);
+ info.appendChild(name);
+ info.appendChild(idMeta);
+ info.appendChild(dateMeta);
- const actions = document.createElement('div');
- actions.className = 'd-flex gap-2 align-items-start';
- actions.appendChild(this.buildButton('Изменить', 'btn-outline-secondary', {
- action: 'edit-order',
- id: this.order.id
- }));
- actions.appendChild(this.buildButton('Удалить', 'btn-outline-danger', {
- action: 'delete-order',
- id: this.order.id
- }));
+ const actions = document.createElement('div');
+ actions.className = 'd-flex gap-2 align-items-start';
+ actions.appendChild(this.buildButton('Изменить', 'btn-outline-secondary', {
+ action: 'edit-order',
+ id: this.order.id
+ }));
+ actions.appendChild(this.buildButton('Удалить', 'btn-outline-danger', {
+ action: 'delete-order',
+ id: this.order.id
+ }));
- header.appendChild(info);
- header.appendChild(actions);
+ header.appendChild(info);
+ header.appendChild(actions);
- return header;
- }
+ return header;
+ }
- buildItemsSection() {
- const wrapper = document.createElement('div');
- wrapper.className = 'mt-3';
+ buildItemsSection() {
+ const wrapper = document.createElement('div');
+ wrapper.className = 'mt-3';
- wrapper.appendChild(this.buildItemsTable());
- wrapper.appendChild(this.buildAddItemForm());
+ wrapper.appendChild(this.buildItemsTable());
+ wrapper.appendChild(this.buildAddItemForm());
- return wrapper;
- }
+ return wrapper;
+ }
- buildItemsTable() {
- const table = document.createElement('table');
- table.className = 'table table-sm align-middle';
+ buildItemsTable() {
+ const table = document.createElement('table');
+ table.className = 'table table-sm align-middle';
- table.innerHTML = `
+ table.innerHTML = `
`;
- const body = document.createElement('tbody');
- this.order.items.forEach((item) => {
- body.appendChild(this.buildItemRow(item));
- });
+ const body = document.createElement('tbody');
+ this.order.items.forEach((item) => {
+ body.appendChild(this.buildItemRow(item));
+ });
- table.appendChild(body);
- return table;
- }
+ table.appendChild(body);
+ return table;
+ }
- buildItemRow(item) {
- const row = document.createElement('tr');
+ buildItemRow(item) {
+ const row = document.createElement('tr');
- const idCell = document.createElement('td');
- idCell.className = 'text-muted small';
- idCell.textContent = item.product_id ?? item.id;
+ const idCell = document.createElement('td');
+ idCell.className = 'text-muted small';
+ idCell.textContent = item.product_id ?? item.id;
- const productCell = document.createElement('td');
- const productSelect = this.buildProductSelect(item);
- productSelect.classList.add('form-select-sm');
- productSelect.dataset.action = 'item-product';
- productSelect.dataset.itemId = item.id;
- productSelect.setAttribute('aria-label', 'Товар');
- productCell.appendChild(productSelect);
+ const productCell = document.createElement('td');
+ const productSelect = this.buildProductSelect(item);
+ productSelect.classList.add('form-select-sm');
+ productSelect.dataset.action = 'item-product';
+ productSelect.dataset.itemId = item.id;
+ productSelect.setAttribute('aria-label', 'Товар');
+ productCell.appendChild(productSelect);
- const quantityCell = document.createElement('td');
- const quantityInput = document.createElement('input');
- quantityInput.type = 'number';
- quantityInput.min = '1';
- quantityInput.className = 'form-control form-control-sm';
- quantityInput.value = item.quantity;
- quantityInput.dataset.action = 'item-quantity';
- quantityInput.dataset.itemId = item.id;
- quantityInput.setAttribute('aria-label', 'Количество');
- quantityCell.appendChild(quantityInput);
+ const quantityCell = document.createElement('td');
+ const quantityInput = document.createElement('input');
+ quantityInput.type = 'number';
+ quantityInput.min = '1';
+ quantityInput.className = 'form-control form-control-sm';
+ quantityInput.value = item.quantity;
+ quantityInput.dataset.action = 'item-quantity';
+ quantityInput.dataset.itemId = item.id;
+ quantityInput.setAttribute('aria-label', 'Количество');
+ quantityCell.appendChild(quantityInput);
- const actionsCell = document.createElement('td');
- actionsCell.className = 'text-end item-actions';
- actionsCell.appendChild(this.buildItemActionButtons(item));
- actionsCell.appendChild(this.buildMoveControls(item));
+ const actionsCell = document.createElement('td');
+ actionsCell.className = 'text-end item-actions';
+ actionsCell.appendChild(this.buildItemActionButtons(item));
+ actionsCell.appendChild(this.buildMoveControls(item));
- row.appendChild(idCell);
- row.appendChild(productCell);
- row.appendChild(quantityCell);
- row.appendChild(actionsCell);
+ row.appendChild(idCell);
+ row.appendChild(productCell);
+ row.appendChild(quantityCell);
+ row.appendChild(actionsCell);
- return row;
- }
+ return row;
+ }
- buildItemActionButtons(item) {
- const wrapper = document.createElement('div');
- wrapper.className = 'd-flex gap-2 justify-content-end';
+ buildItemActionButtons(item) {
+ const wrapper = document.createElement('div');
+ wrapper.className = 'd-flex gap-2 justify-content-end';
- wrapper.appendChild(
- this.buildButton('Сохранить', 'btn-outline-primary', {
- action: 'save-item',
- orderId: this.order.id,
- itemId: item.id
- })
- );
- wrapper.appendChild(
- this.buildButton('Удалить', 'btn-outline-danger', {
- action: 'delete-item',
- orderId: this.order.id,
- itemId: item.id
- })
- );
+ wrapper.appendChild(
+ this.buildButton('Сохранить', 'btn-outline-primary', {
+ action: 'save-item',
+ orderId: this.order.id,
+ itemId: item.id
+ })
+ );
+ wrapper.appendChild(
+ this.buildButton('Удалить', 'btn-outline-danger', {
+ action: 'delete-item',
+ orderId: this.order.id,
+ itemId: item.id
+ })
+ );
- return wrapper;
- }
+ return wrapper;
+ }
- buildMoveControls(item) {
- const wrapper = document.createElement('div');
- wrapper.className = 'd-flex gap-2 justify-content-end mt-2';
+ buildMoveControls(item) {
+ const wrapper = document.createElement('div');
+ wrapper.className = 'd-flex gap-2 justify-content-end mt-2';
- const select = document.createElement('select');
- select.className = 'form-select form-select-sm';
- select.dataset.action = 'move-target';
- select.dataset.itemId = item.id;
- select.setAttribute('aria-label', 'Выбор заказа для перемещения');
+ const select = document.createElement('select');
+ select.className = 'form-select form-select-sm';
+ select.dataset.action = 'move-target';
+ select.dataset.itemId = item.id;
+ select.setAttribute('aria-label', 'Выбор заказа для перемещения');
- const placeholder = document.createElement('option');
- placeholder.value = '';
- placeholder.textContent = 'Переместить в...';
- select.appendChild(placeholder);
+ const placeholder = document.createElement('option');
+ placeholder.value = '';
+ placeholder.textContent = 'Переместить в...';
+ select.appendChild(placeholder);
- this.orders
- .filter((otherOrder) => otherOrder.id !== this.order.id)
- .forEach((otherOrder) => {
- const option = document.createElement('option');
- option.value = otherOrder.id;
- option.textContent = otherOrder.customer_name;
- select.appendChild(option);
- });
+ this.orders
+ .filter((otherOrder) => otherOrder.id !== this.order.id)
+ .forEach((otherOrder) => {
+ const option = document.createElement('option');
+ option.value = otherOrder.id;
+ option.textContent = otherOrder.customer_name;
+ select.appendChild(option);
+ });
- wrapper.appendChild(select);
- wrapper.appendChild(
- this.buildButton('Перенести', 'btn-outline-secondary', {
- action: 'move-item',
- itemId: item.id
- })
- );
+ wrapper.appendChild(select);
+ wrapper.appendChild(
+ this.buildButton('Перенести', 'btn-outline-secondary', {
+ action: 'move-item',
+ itemId: item.id
+ })
+ );
- return wrapper;
- }
+ return wrapper;
+ }
- buildAddItemForm() {
- const form = document.createElement('form');
- form.className = 'row g-2 align-items-end';
- form.dataset.action = 'add-item';
- form.dataset.orderId = this.order.id;
+ buildAddItemForm() {
+ const form = document.createElement('form');
+ form.className = 'row g-2 align-items-end';
+ form.dataset.action = 'add-item';
+ form.dataset.orderId = this.order.id;
- const productCol = document.createElement('div');
- productCol.className = 'col-md-6';
- const productLabel = document.createElement('label');
- productLabel.className = 'form-label';
- productLabel.textContent = 'Товар';
- const productSelect = this.buildProductSelect();
- const productSelectId = `add-item-product-${this.order.id}`;
- productSelect.name = 'productId';
- productSelect.id = productSelectId;
- productLabel.htmlFor = productSelectId;
- productCol.appendChild(productLabel);
- productCol.appendChild(productSelect);
+ const productCol = document.createElement('div');
+ productCol.className = 'col-md-6';
+ const productLabel = document.createElement('label');
+ productLabel.className = 'form-label';
+ productLabel.textContent = 'Товар';
+ const productSelect = this.buildProductSelect();
+ const productSelectId = `add-item-product-${this.order.id}`;
+ productSelect.name = 'productId';
+ productSelect.id = productSelectId;
+ productLabel.htmlFor = productSelectId;
+ productCol.appendChild(productLabel);
+ productCol.appendChild(productSelect);
- const quantityCol = document.createElement('div');
- quantityCol.className = 'col-md-3';
- const quantityLabel = document.createElement('label');
- quantityLabel.className = 'form-label';
- quantityLabel.textContent = 'Кол-во';
- const quantityInput = document.createElement('input');
- const quantityInputId = `add-item-quantity-${this.order.id}`;
- quantityInput.type = 'number';
- quantityInput.min = '1';
- quantityInput.className = 'form-control';
- quantityInput.name = 'quantity';
- quantityInput.id = quantityInputId;
- quantityLabel.htmlFor = quantityInputId;
- quantityInput.required = true;
- quantityCol.appendChild(quantityLabel);
- quantityCol.appendChild(quantityInput);
+ const quantityCol = document.createElement('div');
+ quantityCol.className = 'col-md-3';
+ const quantityLabel = document.createElement('label');
+ quantityLabel.className = 'form-label';
+ quantityLabel.textContent = 'Кол-во';
+ const quantityInput = document.createElement('input');
+ const quantityInputId = `add-item-quantity-${this.order.id}`;
+ quantityInput.type = 'number';
+ quantityInput.min = '1';
+ quantityInput.className = 'form-control';
+ quantityInput.name = 'quantity';
+ quantityInput.id = quantityInputId;
+ quantityLabel.htmlFor = quantityInputId;
+ quantityInput.required = true;
+ quantityCol.appendChild(quantityLabel);
+ quantityCol.appendChild(quantityInput);
- const actionCol = document.createElement('div');
- actionCol.className = 'col-md-3';
- const addButton = document.createElement('button');
- addButton.type = 'submit';
- addButton.className = 'btn btn-success w-100';
- addButton.textContent = 'Добавить позицию';
- actionCol.appendChild(addButton);
+ const actionCol = document.createElement('div');
+ actionCol.className = 'col-md-3';
+ const addButton = document.createElement('button');
+ addButton.type = 'submit';
+ addButton.className = 'btn btn-success w-100';
+ addButton.textContent = 'Добавить позицию';
+ actionCol.appendChild(addButton);
- form.appendChild(productCol);
- form.appendChild(quantityCol);
- form.appendChild(actionCol);
+ form.appendChild(productCol);
+ form.appendChild(quantityCol);
+ form.appendChild(actionCol);
- return form;
- }
+ return form;
+ }
- buildProductSelect(selectedId) {
- const select = document.createElement('select');
- select.className = 'form-select';
- this.products.forEach((product) => {
- const option = document.createElement('option');
- option.value = product.id;
- option.textContent = product.name;
- if (selectedId && product.id === selectedId) {
- option.selected = true;
- }
- select.appendChild(option);
- });
- return select;
- }
+ buildProductSelect(selectedId) {
+ const select = document.createElement('select');
+ select.className = 'form-select';
+ this.products.forEach((product) => {
+ const option = document.createElement('option');
+ option.value = product.id;
+ option.textContent = product.name;
+ if (selectedId && product.id === selectedId) {
+ option.selected = true;
+ }
+ select.appendChild(option);
+ });
+ return select;
+ }
- buildButton(label, style, data = {}) {
- const button = document.createElement('button');
- button.type = 'button';
- button.className = `btn ${style} btn-sm`;
- button.textContent = label;
- Object.entries(data).forEach(([key, value]) => {
- button.dataset[key] = value;
- });
- return button;
- }
+ buildButton(label, style, data = {}) {
+ const button = document.createElement('button');
+ button.type = 'button';
+ button.className = `btn ${style} btn-sm`;
+ button.textContent = label;
+ Object.entries(data).forEach(([key, value]) => {
+ button.dataset[key] = value;
+ });
+ return button;
+ }
}
function showNotification(message, type = 'warning') {
- notification.textContent = message;
- notification.className = `alert alert-${type}`;
- notification.classList.remove('d-none');
+ notification.textContent = message;
+ notification.className = `alert alert-${type}`;
+ notification.classList.remove('d-none');
- setTimeout(() => {
- notification.classList.add('d-none');
- }, 4000);
+ setTimeout(() => {
+ notification.classList.add('d-none');
+ }, 4000);
}
function resolveApiUrl(url) {
- if (url.startsWith('http://') || url.startsWith('https://')) {
- return url;
- }
- if (url.startsWith('/')) {
- return `${API_BASE}${url}`;
- }
- return `${API_BASE}/${url}`;
+ if (url.startsWith('http://') || url.startsWith('https://')) {
+ return url;
+ }
+ if (url.startsWith('/')) {
+ return `${API_BASE}${url}`;
+ }
+ return `${API_BASE}/${url}`;
}
async function fetchJson(url, options) {
- const response = await fetch(resolveApiUrl(url), options);
- if (!response.ok) {
- const payload = await response.json().catch(() => ({ message: 'Ошибка запроса.' }));
- throw new Error(payload.message || 'Ошибка запроса.');
- }
- if (response.status === 204) {
- return null;
- }
- return response.json();
+ const response = await fetch(resolveApiUrl(url), options);
+ if (!response.ok) {
+ const payload = await response.json().catch(() => ({
+ message: 'Ошибка запроса.'
+ }));
+ throw new Error(payload.message || 'Ошибка запроса.');
+ }
+ if (response.status === 204) {
+ return null;
+ }
+ return response.json();
}
function renderInventory() {
- inventoryList.innerHTML = '';
- products.forEach((product) => {
- const item = document.createElement('li');
- item.className = 'list-group-item d-flex justify-content-between align-items-center';
- item.innerHTML = `
+ inventoryList.innerHTML = '';
+ products.forEach((product) => {
+ const item = document.createElement('li');
+ item.className = 'list-group-item d-flex justify-content-between align-items-center';
+ item.innerHTML = `
${product.name}
${product.quantity}
`;
- inventoryList.appendChild(item);
- });
+ inventoryList.appendChild(item);
+ });
}
function renderOrders() {
- ordersContainer.innerHTML = '';
- if (orders.length === 0) {
- ordersContainer.innerHTML = 'ID
@@ -104,473 +104,502 @@ class OrderCard {
Заказы отсутствуют.
'; - return; - } + ordersContainer.innerHTML = ''; + if (orders.length === 0) { + ordersContainer.innerHTML = 'Заказы отсутствуют.
'; + return; + } - orders.forEach((order) => { - const card = new OrderCard(order, orders, products).build(); - ordersContainer.appendChild(card); - }); + orders.forEach((order) => { + const card = new OrderCard(order, orders, products).build(); + ordersContainer.appendChild(card); + }); } function normalizeProducts(data) { - if (!Array.isArray(data)) { - return []; - } - return data.map((product) => ({ - id: product.id ?? product.ID, - name: product.name ?? product.pr_name, - quantity: product.quantity ?? product.count - })); + if (!Array.isArray(data)) { + return []; + } + return data.map((product) => ({ + id: product.id ?? product.ID, + name: product.name ?? product.pr_name, + quantity: product.quantity ?? product.count + })); } function normalizeOrderItems(items) { - if (!Array.isArray(items)) { - return []; - } - return items.map((item, index) => ({ - id: item.id ?? item.item_id ?? `${item.product_id ?? item.productId ?? 'item'}-${index}`, - product_id: item.product_id ?? item.productId ?? item.product, - product_name: item.product_name ?? item.name, - quantity: item.quantity, - price: item.price - })); + if (!Array.isArray(items)) { + return []; + } + return items.map((item, index) => ({ + id: item.id ?? item.item_id ?? `${item.product_id ?? item.productId ?? 'item'}-${index}`, + product_id: item.product_id ?? item.productId ?? item.product, + product_name: item.product_name ?? item.name, + quantity: item.quantity, + price: item.price + })); } function normalizeOrders(data) { - if (!Array.isArray(data)) { - return []; - } - return data.map((order) => ({ - id: order.id ?? order.ID, - customer_name: order.customer_name ?? order.customer, - order_date: order.order_date ?? order.date, - status: order.status, - total_amount: order.total_amount ?? order.total, - items: normalizeOrderItems(order.items) - })); + if (!Array.isArray(data)) { + return []; + } + return data.map((order) => ({ + id: order.id ?? order.ID, + customer_name: order.customer_name ?? order.customer, + order_date: order.order_date ?? order.date, + status: order.status, + total_amount: order.total_amount ?? order.total, + items: normalizeOrderItems(order.items) + })); } async function refreshData() { - const [currentDateData, productsData, ordersData] = await Promise.all([ - fetchJson('/api/current-date'), - fetchJson('/api/products'), - fetchJson('/api/orders') - ]); - currentDate = currentDateData.currentDate; - products = normalizeProducts(productsData); - orders = normalizeOrders(ordersData); + const [currentDateData, productsData, ordersData] = await Promise.all([ + fetchJson('/api/current-date'), + fetchJson('/api/products'), + fetchJson('/api/orders') + ]); + currentDate = currentDateData.currentDate; + products = normalizeProducts(productsData); + orders = normalizeOrders(ordersData); - currentDateEl.textContent = currentDate; - orderDateInput.min = currentDate; - if (!orderDateInput.value) { - orderDateInput.value = currentDate; - } + currentDateEl.textContent = currentDate; + orderDateInput.min = currentDate; + if (!orderDateInput.value) { + orderDateInput.value = currentDate; + } - renderInventory(); - renderOrders(); + renderInventory(); + renderOrders(); } function initializeApp() { - const requiredElements = [ - notification, - currentDateEl, - orderDateInput, - customerNameInput, - orderForm, - ordersContainer, - inventoryList, - advanceButton - ]; + const requiredElements = [ + notification, + currentDateEl, + orderDateInput, + customerNameInput, + orderForm, + ordersContainer, + inventoryList, + advanceButton + ]; - if (requiredElements.some((element) => !element)) { - console.error('Missing required elements. Проверьте разметку страницы.'); - return; - } - - const initialDate = getLocalISODate(); - currentDate = initialDate; - currentDateEl.textContent = initialDate; - orderDateInput.min = initialDate; - if (!orderDateInput.value) { - orderDateInput.value = initialDate; - } - - orderForm.addEventListener('submit', async (event) => { - event.preventDefault(); - try { - await fetchJson('/api/orders', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - customerName: customerNameInput.value.trim(), - orderDate: orderDateInput.value - }) - }); - customerNameInput.value = ''; - orderDateInput.value = currentDate; - await refreshData(); - } catch (error) { - showNotification(error.message, 'warning'); + if (requiredElements.some((element) => !element)) { + console.error('Missing required elements. Проверьте разметку страницы.'); + return; } - }); - advanceButton.addEventListener('click', async () => { - try { - await fetchJson('/api/current-date/advance', { method: 'POST' }); - await refreshData(); - showNotification('Дата переведена. Заказы за сегодня отправлены.', 'success'); - } catch (error) { - showNotification(error.message, 'warning'); + const initialDate = getLocalISODate(); + currentDate = initialDate; + currentDateEl.textContent = initialDate; + orderDateInput.min = initialDate; + if (!orderDateInput.value) { + orderDateInput.value = initialDate; } - }); - ordersContainer.addEventListener('submit', async (event) => { - if (event.target.matches('form[data-action="add-item"]')) { - event.preventDefault(); - const orderId = event.target.dataset.orderId; - const productId = event.target.productId.value; - const quantity = Number.parseInt(event.target.quantity.value, 10); + orderForm.addEventListener('submit', async (event) => { + event.preventDefault(); + try { + await fetchJson('/api/orders', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + customerName: customerNameInput.value.trim(), + orderDate: orderDateInput.value + }) + }); + customerNameInput.value = ''; + orderDateInput.value = currentDate; + await refreshData(); + } catch (error) { + showNotification(error.message, 'warning'); + } + }); - try { - const response = await fetchJson(`/api/orders/${orderId}/items`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ productId, quantity }) - }); - const order = orders.find((entry) => entry.id === orderId); - const product = products.find((entry) => entry.id === productId); - if (order) { - const newItem = normalizeOrderItems([ - { - id: response?.itemId, - product_id: productId, - product_name: product?.name, - quantity + advanceButton.addEventListener('click', async () => { + try { + await fetchJson('/api/current-date/advance', { + method: 'POST' + }); + await refreshData(); + showNotification('Дата переведена. Заказы за сегодня отправлены.', 'success'); + } catch (error) { + showNotification(error.message, 'warning'); + } + }); + + ordersContainer.addEventListener('submit', async (event) => { + if (event.target.matches('form[data-action="add-item"]')) { + event.preventDefault(); + const orderId = event.target.dataset.orderId; + const productId = event.target.productId.value; + const quantity = Number.parseInt(event.target.quantity.value, 10); + + try { + const response = await fetchJson(`/api/orders/${orderId}/items`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + productId, + quantity + }) + }); + const order = orders.find((entry) => entry.id === orderId); + const product = products.find((entry) => entry.id === productId); + if (order) { + const newItem = normalizeOrderItems([{ + id: response?.itemId, + product_id: productId, + product_name: product?.name, + quantity + }])[0]; + order.items = [...order.items, newItem]; + renderOrders(); + } + event.target.reset(); + await refreshData(); + } catch (error) { + showNotification(error.message, 'warning'); } - ])[0]; - order.items = [...order.items, newItem]; - renderOrders(); } - event.target.reset(); - await refreshData(); - } catch (error) { + }); + + ordersContainer.addEventListener('click', async (event) => { + const { + action, + id, + itemId, + orderId + } = event.target.dataset; + + try { + if (action === 'delete-order') { + await fetchJson(`/api/orders/${id}`, { + method: 'DELETE' + }); + await refreshData(); + return; + } + + if (action === 'edit-order') { + const newName = prompt('Введите новое ФИО заказчика', ''); + if (newName === null) { + return; + } + const newDate = prompt('Введите дату заказа (YYYY-MM-DD)', currentDate); + if (!newDate) { + return; + } + Date + await fetchJson(`/api/orders/${id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + customerName: newName, + orderDate: newDate + }) + }); + await refreshData(); + return; + } + + if (action === 'save-item') { + const quantityInput = ordersContainer.querySelector( + `[data-action="item-quantity"][data-item-id="${itemId}"]` + ); + const productSelect = ordersContainer.querySelector( + `[data-action="item-product"][data-item-id="${itemId}"]` + ); + + await fetchJson(`/api/orders/${orderId}/items/${itemId}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + productId: productSelect.value, + quantity: Number.parseInt(quantityInput.value, 10) + }) + }); + await refreshData(); + return; + } + + if (action === 'delete-item') { + await fetchJson(`/api/orders/${orderId}/items/${itemId}`, { + method: 'DELETE' + }); + await refreshData(); + return; + } + + if (action === 'move-item') { + const targetSelect = ordersContainer.querySelector( + `[data-action="move-target"][data-item-id="${itemId}"]` + ); + if (!targetSelect.value) { + showNotification('Выберите заказ для перемещения.', 'warning'); + return; + } + await fetchJson(`/api/order-items/${itemId}/move`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + targetOrderId: targetSelect.value + }) + }); + await refreshData(); + return; + } + } catch (error) { + showNotification(error.message, 'warning'); + } + }); + + refreshData().catch((error) => { showNotification(error.message, 'warning'); - } - } - }); - - ordersContainer.addEventListener('click', async (event) => { - const { action, id, itemId, orderId } = event.target.dataset; - - try { - if (action === 'delete-order') { - await fetchJson(`/api/orders/${id}`, { method: 'DELETE' }); - await refreshData(); - return; - } - - if (action === 'edit-order') { - const newName = prompt('Введите новое ФИО заказчика', ''); - if (newName === null) { - return; - } - const newDate = prompt('Введите дату заказа (YYYY-MM-DD)', currentDate); - if (!newDate) { - return; - } - Date - await fetchJson(`/api/orders/${id}`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ customerName: newName, orderDate: newDate }) - }); - await refreshData(); - return; - } - - if (action === 'save-item') { - const quantityInput = ordersContainer.querySelector( - `[data-action="item-quantity"][data-item-id="${itemId}"]` - ); - const productSelect = ordersContainer.querySelector( - `[data-action="item-product"][data-item-id="${itemId}"]` - ); - - await fetchJson(`/api/orders/${orderId}/items/${itemId}`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - productId: productSelect.value, - quantity: Number.parseInt(quantityInput.value, 10) - }) - }); - await refreshData(); - return; - } - - if (action === 'delete-item') { - await fetchJson(`/api/orders/${orderId}/items/${itemId}`, { method: 'DELETE' }); - await refreshData(); - return; - } - - if (action === 'move-item') { - const targetSelect = ordersContainer.querySelector( - `[data-action="move-target"][data-item-id="${itemId}"]` - ); - if (!targetSelect.value) { - showNotification('Выберите заказ для перемещения.', 'warning'); - return; - } - await fetchJson(`/api/order-items/${itemId}/move`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ targetOrderId: targetSelect.value }) - }); - await refreshData(); - return; - } - } catch (error) { - showNotification(error.message, 'warning'); - } - }); - - refreshData().catch((error) => { - showNotification(error.message, 'warning'); - }); + }); } initializeApp(); \ No newline at end of file