374 lines
13 KiB
HTML
374 lines
13 KiB
HTML
{% extends "base.html" %}
|
|
{% block body_class %}page-bg bg-orders{% endblock %}
|
|
{% block content %}
|
|
<style>
|
|
body.bg-orders {
|
|
background-color: #B3B3DA;
|
|
background-image:
|
|
linear-gradient(rgba(179, 179, 218, 0.75), rgba(179, 179, 218, 0.75)),
|
|
url('{{ url_for("static", filename="bg/bg_zakaz.png", v=1) }}');
|
|
background-size: auto 40vh;
|
|
}
|
|
.orders-page .card {
|
|
background-color: #B3B3DA;
|
|
border: 1px solid #B3B3DA;
|
|
box-shadow: none;
|
|
}
|
|
.orders-page .card-body {
|
|
background-color: transparent;
|
|
}
|
|
.orders-page .form-control,
|
|
.orders-page .form-select {
|
|
background-color: #ffffff;
|
|
border: 1px solid #B3B3DA;
|
|
}
|
|
.orders-page .form-control:focus,
|
|
.orders-page .form-select:focus {
|
|
box-shadow: 0 0 0 0.1rem rgba(0, 0, 0, 0.15);
|
|
border-color: #B3B3DA;
|
|
}
|
|
.orders-form .form-control,
|
|
.orders-form .form-select {
|
|
min-width: 160px;
|
|
}
|
|
.orders-form .btn {
|
|
white-space: nowrap;
|
|
}
|
|
.row-actions {
|
|
background: #f8f9fa;
|
|
}
|
|
.row-actions__inner {
|
|
max-height: 0;
|
|
opacity: 0;
|
|
transform: translateY(-6px);
|
|
transition: max-height 0.2s ease, opacity 0.2s ease, transform 0.2s ease;
|
|
overflow: hidden;
|
|
padding: 0 0.75rem;
|
|
}
|
|
.row-actions.show .row-actions__inner {
|
|
max-height: 80px;
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
.edit-modal {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(15, 23, 32, 0.55);
|
|
display: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 1050;
|
|
padding: 1rem;
|
|
}
|
|
.edit-modal.show {
|
|
display: flex;
|
|
}
|
|
.edit-modal__dialog {
|
|
background: #fff;
|
|
border-radius: 0.75rem;
|
|
width: min(840px, 100%);
|
|
max-height: 85vh;
|
|
overflow: auto;
|
|
}
|
|
.edit-modal__header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0.75rem 1rem;
|
|
border-bottom: 1px solid #e2e2e2;
|
|
}
|
|
.edit-modal__title {
|
|
margin: 0;
|
|
font-size: 1.05rem;
|
|
}
|
|
.edit-modal__footer {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 0.5rem;
|
|
padding: 0.75rem 1rem;
|
|
border-top: 1px solid #e2e2e2;
|
|
}
|
|
</style>
|
|
<div class="orders-page">
|
|
<div class="d-flex align-items-center justify-content-between mb-3">
|
|
<h3 class="mb-0">Разовые заказы</h3>
|
|
<div class="d-flex gap-2">
|
|
<a class="btn btn-success" href="/report/single_orders.xlsx">Экспорт в Excel</a>
|
|
</div>
|
|
</div>
|
|
|
|
{% if session.get('role') in ('admin','storekeeper') %}
|
|
<div class="card mb-3">
|
|
<div class="card-body">
|
|
<form method="post" action="/orders/single/add" class="row g-2 orders-form">
|
|
<div class="col-md-6">
|
|
<input name="name" class="form-control" placeholder="Наименование" required>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<input name="model" class="form-control" placeholder="Модель">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<input name="quantity" class="form-control" placeholder="Кол-во" value="">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<select name="device_type" id="singleDeviceType" class="form-select">
|
|
<option value="">Тип устройства...</option>
|
|
{% for value, label in device_types %}
|
|
<option value="{{ value }}">{{ label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-7">
|
|
<select name="device_id" id="singleDevice" class="form-select">
|
|
<option value="">Устройство...</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select name="cabinet_id" class="form-select">
|
|
<option value="">Кабинет...</option>
|
|
{% for cid, cname in cabinets %}
|
|
<option value="{{ cid }}">{{ cname }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<input name="link" class="form-control" placeholder="Ссылка на модель">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<input name="note" class="form-control" placeholder="Примечание">
|
|
</div>
|
|
<div class="col-md-2 d-grid">
|
|
<button class="btn btn-primary">Добавить</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<table class="table table-striped table-sm">
|
|
<tr>
|
|
<th>Наименование</th>
|
|
<th>Модель</th>
|
|
<th>Количество</th>
|
|
<th>Тип устройства</th>
|
|
<th>Устройство</th>
|
|
<th>Кабинет</th>
|
|
<th>Ссылка</th>
|
|
<th>Примечание</th>
|
|
</tr>
|
|
{% if rows %}
|
|
{% for rid, name, model, qty, status, dtype, dtable, did, dlabel, cab_id, cabinet, link, note in rows %}
|
|
<tr class="js-single-order-row"
|
|
data-id="{{ rid }}"
|
|
data-name="{{ name }}"
|
|
data-model="{{ model }}"
|
|
data-qty="{{ qty }}"
|
|
data-dtype="{{ dtype }}"
|
|
data-device="{{ dtable }}:{{ did }}"
|
|
data-cabinet="{{ cab_id }}"
|
|
data-link="{{ link }}"
|
|
data-note="{{ note }}">
|
|
<td>{{ name }}</td>
|
|
<td>{{ model }}</td>
|
|
<td>{{ qty }}</td>
|
|
<td>{{ device_type_labels.get(dtype, dtype) }}</td>
|
|
<td>{{ dlabel }}</td>
|
|
<td>{{ cabinet }}</td>
|
|
<td>
|
|
{% if link %}
|
|
<a href="{{ link }}" target="_blank" rel="noopener">Открыть</a>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ note }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
{% else %}
|
|
<tr>
|
|
<td colspan="8" class="text-muted">Нет данных</td>
|
|
</tr>
|
|
{% endif %}
|
|
</table>
|
|
</div>
|
|
|
|
{% if session.get('role') in ('admin','storekeeper') %}
|
|
<div class="edit-modal" id="singleOrderEditModal" aria-hidden="true">
|
|
<div class="edit-modal__dialog">
|
|
<div class="edit-modal__header">
|
|
<h4 class="edit-modal__title">Редактирование заказа</h4>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" data-modal-close>Закрыть</button>
|
|
</div>
|
|
<form method="post" action="/orders/single/edit" id="singleOrderEditForm">
|
|
<input type="hidden" name="id" id="singleOrderModalId">
|
|
<div class="row g-2 px-3 py-3">
|
|
<div class="col-md-4">
|
|
<input name="name" id="singleOrderModalName" class="form-control" placeholder="Наименование" required>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<input name="model" id="singleOrderModalModel" class="form-control" placeholder="Модель">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<input name="quantity" id="singleOrderModalQty" class="form-control" placeholder="Количество">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<select name="device_type" id="singleOrderModalType" class="form-select">
|
|
<option value="">Тип устройства...</option>
|
|
{% for value, label in device_types %}
|
|
<option value="{{ value }}">{{ label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<select name="device_id" id="singleOrderModalDevice" class="form-select">
|
|
<option value="">Устройство...</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<select name="cabinet_id" id="singleOrderModalCabinet" class="form-select">
|
|
<option value="">Кабинет...</option>
|
|
{% for cid, cname in cabinets %}
|
|
<option value="{{ cid }}">{{ cname }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<input name="link" id="singleOrderModalLink" class="form-control" placeholder="Ссылка на модель">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<input name="note" id="singleOrderModalNote" class="form-control" placeholder="Примечание">
|
|
</div>
|
|
</div>
|
|
<div class="edit-modal__footer">
|
|
<button type="button" class="btn btn-outline-secondary" data-modal-close>Отмена</button>
|
|
<button class="btn btn-primary">Сохранить</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<script>
|
|
(function() {
|
|
const devicesByType = {{ devices_by_type | tojson }};
|
|
const typeSelect = document.getElementById('singleDeviceType');
|
|
const deviceSelect = document.getElementById('singleDevice');
|
|
if (!typeSelect || !deviceSelect) return;
|
|
|
|
function fillDevices(type) {
|
|
deviceSelect.innerHTML = '<option value="">Устройство...</option>';
|
|
if (!type || !devicesByType[type]) return;
|
|
devicesByType[type].forEach((item) => {
|
|
const opt = document.createElement('option');
|
|
opt.value = item.value;
|
|
opt.textContent = item.label;
|
|
deviceSelect.appendChild(opt);
|
|
});
|
|
}
|
|
|
|
typeSelect.addEventListener('change', () => fillDevices(typeSelect.value));
|
|
})();
|
|
</script>
|
|
|
|
{% if session.get('role') in ('admin','storekeeper') %}
|
|
<script>
|
|
(function() {
|
|
const devicesByType = {{ devices_by_type | tojson }};
|
|
const rows = document.querySelectorAll('.js-single-order-row');
|
|
if (!rows.length) return;
|
|
const modal = document.getElementById('singleOrderEditModal');
|
|
if (!modal) return;
|
|
|
|
const fields = {
|
|
id: document.getElementById('singleOrderModalId'),
|
|
name: document.getElementById('singleOrderModalName'),
|
|
model: document.getElementById('singleOrderModalModel'),
|
|
qty: document.getElementById('singleOrderModalQty'),
|
|
type: document.getElementById('singleOrderModalType'),
|
|
device: document.getElementById('singleOrderModalDevice'),
|
|
cabinet: document.getElementById('singleOrderModalCabinet'),
|
|
link: document.getElementById('singleOrderModalLink'),
|
|
note: document.getElementById('singleOrderModalNote'),
|
|
};
|
|
|
|
function closeModal() {
|
|
modal.classList.remove('show');
|
|
modal.setAttribute('aria-hidden', 'true');
|
|
}
|
|
function openModal() {
|
|
modal.classList.add('show');
|
|
modal.setAttribute('aria-hidden', 'false');
|
|
}
|
|
modal.addEventListener('click', (e) => {
|
|
if (e.target === modal || e.target.hasAttribute('data-modal-close')) {
|
|
closeModal();
|
|
}
|
|
});
|
|
|
|
function fillDevices(type, selected) {
|
|
fields.device.innerHTML = '<option value="">Устройство...</option>';
|
|
if (!type || !devicesByType[type]) return;
|
|
devicesByType[type].forEach((item) => {
|
|
const opt = document.createElement('option');
|
|
opt.value = item.value;
|
|
opt.textContent = item.label;
|
|
if (selected && selected === item.value) opt.selected = true;
|
|
fields.device.appendChild(opt);
|
|
});
|
|
}
|
|
|
|
function fillModalFromRow(row) {
|
|
fields.id.value = row.getAttribute('data-id') || '';
|
|
fields.name.value = row.getAttribute('data-name') || '';
|
|
fields.model.value = row.getAttribute('data-model') || '';
|
|
fields.qty.value = row.getAttribute('data-qty') || '';
|
|
const dtype = row.getAttribute('data-dtype') || '';
|
|
const device = row.getAttribute('data-device') || '';
|
|
fields.type.value = dtype;
|
|
fillDevices(dtype, device);
|
|
fields.cabinet.value = row.getAttribute('data-cabinet') || '';
|
|
fields.link.value = row.getAttribute('data-link') || '';
|
|
fields.note.value = row.getAttribute('data-note') || '';
|
|
return true;
|
|
}
|
|
|
|
fields.type.addEventListener('change', () => {
|
|
fillDevices(fields.type.value);
|
|
});
|
|
|
|
function showActionRow(row) {
|
|
const existing = row.parentElement.querySelector('.row-actions');
|
|
if (existing) existing.remove();
|
|
const rowId = row.getAttribute('data-id') || '';
|
|
const actionRow = document.createElement('tr');
|
|
actionRow.className = 'row-actions';
|
|
actionRow.innerHTML = `<td colspan="8">
|
|
<div class="row-actions__inner">
|
|
<button type="button" class="btn btn-sm btn-outline-primary js-open-single-order-modal">Редактировать</button>
|
|
<form method="post" action="/orders/single/delete" class="d-inline ms-2" onsubmit="return confirm('Удалить заказ?')">
|
|
<input type="hidden" name="id" value="${rowId}">
|
|
<button class="btn btn-sm btn-outline-danger" type="submit">Удалить</button>
|
|
</form>
|
|
</div>
|
|
</td>`;
|
|
row.insertAdjacentElement('afterend', actionRow);
|
|
const btn = actionRow.querySelector('.js-open-single-order-modal');
|
|
btn.addEventListener('click', () => {
|
|
if (fillModalFromRow(row)) openModal();
|
|
});
|
|
requestAnimationFrame(() => actionRow.classList.add('show'));
|
|
}
|
|
|
|
rows.forEach((row) => {
|
|
row.addEventListener('click', (e) => {
|
|
if (e.target.closest('button, input, select, textarea, a')) return;
|
|
showActionRow(row);
|
|
});
|
|
row.addEventListener('dblclick', (e) => {
|
|
if (e.target.closest('button, input, select, textarea, a')) return;
|
|
if (fillModalFromRow(row)) openModal();
|
|
});
|
|
});
|
|
})();
|
|
</script>
|
|
{% endif %}
|
|
{% endblock %}
|