588 lines
23 KiB
HTML
588 lines
23 KiB
HTML
{% extends "base.html" %}
|
|
{% block body_class %}page-bg bg-devices{% endblock %}
|
|
{% block content %}
|
|
<style>
|
|
body.bg-devices {
|
|
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_projects.png", v=1) }}');
|
|
background-size: auto 40vh;
|
|
}
|
|
.projectors-page .card {
|
|
background-color: #B3B3DA;
|
|
border: 1px solid #B3B3DA;
|
|
box-shadow: none;
|
|
}
|
|
.projectors-page .card-body {
|
|
background-color: transparent;
|
|
}
|
|
.projectors-page .form-control,
|
|
.projectors-page .form-select {
|
|
background-color: #ffffff;
|
|
border: 1px solid #B3B3DA;
|
|
}
|
|
.projectors-page .form-control:focus,
|
|
.projectors-page .form-select:focus {
|
|
box-shadow: 0 0 0 0.1rem rgba(0, 0, 0, 0.15);
|
|
border-color: #B3B3DA;
|
|
}
|
|
.table td,
|
|
.table th {
|
|
white-space: normal;
|
|
word-break: break-word;
|
|
}
|
|
.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: 60px;
|
|
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(1000px, 100%);
|
|
max-height: 85vh;
|
|
overflow: auto;
|
|
padding: 1.25rem 1.5rem;
|
|
box-shadow: 0 24px 48px rgba(15, 23, 32, 0.25);
|
|
}
|
|
.edit-modal__header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 1rem;
|
|
}
|
|
.edit-modal__title {
|
|
font-size: 1.1rem;
|
|
font-weight: 600;
|
|
margin: 0;
|
|
}
|
|
.edit-modal__footer {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 0.5rem;
|
|
margin-top: 1rem;
|
|
}
|
|
.field-hidden {
|
|
display: none;
|
|
}
|
|
.projector-add-card {
|
|
position: relative;
|
|
}
|
|
.projector-search-btn {
|
|
position: absolute;
|
|
right: 12px;
|
|
bottom: 12px;
|
|
}
|
|
</style>
|
|
|
|
<div class="projectors-page">
|
|
<div class="d-flex align-items-center justify-content-between mb-2">
|
|
<h3 class="mb-0">Презентационное оборудование</h3>
|
|
</div>
|
|
|
|
{% if session.get('role') not in ('admin','storekeeper') %}
|
|
<div class="alert alert-secondary">Режим просмотра: изменения доступны только администратору.</div>
|
|
{% endif %}
|
|
|
|
{% if session.get('role') in ('admin','storekeeper') %}
|
|
<div class="card mb-3 projector-add-card">
|
|
<div class="card-body">
|
|
<h5 class="card-title mb-2">Добавить устройство</h5>
|
|
<button class="btn btn-outline-primary projector-search-btn" type="button" id="toggleProjectorSearch" aria-label="Поиск">🔍</button>
|
|
<form method="post" action="/projectors/add" class="row g-2" id="projectorAddForm">
|
|
<div class="col-md-2">
|
|
<input name="inventory_number" class="form-control" placeholder="Инв. №">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select name="kit_type" class="form-select" id="kitTypeSelect">
|
|
<option value="">Тип...</option>
|
|
{% for value, label in kit_types %}
|
|
<option value="{{ value }}">{{ label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div> <div class="col-md-2 js-field" data-types="kit,projector,board,display,tv">
|
|
<select name="brand_select" class="form-select" id="projectorBrandSelect">
|
|
<option value="">Бренд...</option>
|
|
{% for b in projector_brands %}
|
|
<option value="{{ b }}">{{ b }}</option>
|
|
{% endfor %}
|
|
<option value="__custom__">+ Добавить бренд</option>
|
|
</select>
|
|
<input name="brand_custom" id="projectorBrandCustom" class="form-control mt-2 d-none" placeholder="Новый бренд">
|
|
<input type="hidden" name="brand" id="projectorBrandValue">
|
|
</div>
|
|
<div class="col-md-2 js-field" data-types="kit,projector,display,tv">
|
|
<input name="projector_model" class="form-control" placeholder="Модель проектора">
|
|
</div>
|
|
<div class="col-md-2 js-field" data-types="kit,projector,display,tv">
|
|
<input name="projector_serial" class="form-control" placeholder="Серийный № проектора">
|
|
</div>
|
|
<div class="col-md-2 js-field" data-types="kit">
|
|
<input name="projector_inventory_number" class="form-control" placeholder="Инв. № проектора">
|
|
</div>
|
|
<div class="col-md-2 js-field" data-types="kit,board">
|
|
<input name="board_model" class="form-control" placeholder="Модель доски">
|
|
</div>
|
|
<div class="col-md-2 js-field" data-types="kit,board">
|
|
<input name="board_serial" class="form-control" placeholder="Серийный № доски">
|
|
</div>
|
|
<div class="col-md-2 js-field" data-types="kit">
|
|
<input name="board_inventory_number" class="form-control" placeholder="Инв. № доски">
|
|
</div>
|
|
<div class="col-md-3 js-field" data-types="kit,display">
|
|
<select name="computer_id" class="form-select" id="projectorComputerSelect">
|
|
<option value="">Ноутбук / ПК (модель)...</option>
|
|
{% for cid, cinv, cbrand, cmodel, ctype in computers %}
|
|
{% set comp_text = [cbrand, cmodel]|select|join(' ') %}
|
|
<option value="{{ cid }}" data-inv="{{ cinv }}">{{ comp_text }}{% if cinv %} ({{ cinv }}){% endif %}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3 js-field" data-types="kit">
|
|
<input name="computer_inventory_number" id="projectorComputerInv" class="form-control" placeholder="Инв. № ноутбука/ПК" readonly>
|
|
</div>
|
|
<div class="col-md-2 js-field" data-types="kit,projector,board,display,tv">
|
|
<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-2 js-field" data-types="kit,projector,board,display">
|
|
<input name="date_in_operation" type="date" class="form-control" placeholder="Дата ввода">
|
|
</div>
|
|
<div class="col-md-3 js-field" data-types="kit,projector,board,display">
|
|
<input name="note" class="form-control" placeholder="Примечание">
|
|
</div>
|
|
<div class="col-md-2 d-grid">
|
|
<button class="btn btn-success">Добавить</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="card mb-3 d-none" id="projectorSearchCard">
|
|
<div class="card-body">
|
|
<form class="row g-2 align-items-center" id="projectorSearchForm">
|
|
<div class="col-md-6">
|
|
<input id="projectorSearchInput" class="form-control" placeholder="Модель проектора/комплекта">
|
|
</div>
|
|
<div class="col-md-2 d-grid">
|
|
<button class="btn btn-primary" type="submit">Найти</button>
|
|
</div>
|
|
<div class="col-md-2 d-grid">
|
|
<button class="btn btn-outline-secondary" type="button" id="projectorSearchClear">Очистить</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<table class="table table-striped align-middle">
|
|
<tr class="projector-row">
|
|
<th>Инв. №</th>
|
|
<th>Тип</th>
|
|
<th>Бренд</th>
|
|
<th>Модель проектора</th>
|
|
<th>Серийный №</th>
|
|
<th>Модель доски</th>
|
|
<th>Серийный № доски</th>
|
|
<th>Ноутбук / ПК</th>
|
|
<th>Кабинет</th>
|
|
<th>Дата ввода</th>
|
|
<th>Примечание</th>
|
|
<th>Добавлено</th>
|
|
</tr>
|
|
{% for id, inv, kit_type, brand, proj_model, proj_serial, board_model, board_serial, proj_inv, board_inv, comp_inv_num, comp_id, comp_inv, comp_brand, comp_model, comp_type, cab_id, cab_name, date_in_operation, note, date_added in rows %}
|
|
{% set comp_text = [comp_brand, comp_model]|select|join(' ') %}
|
|
<tr class="projector-row js-projector-row"
|
|
data-id="{{ id }}"
|
|
data-inventory-number="{{ inv }}"
|
|
data-kit-type="{{ kit_type }}"
|
|
data-brand="{{ brand or '' }}"
|
|
data-projector-model="{{ proj_model or '' }}"
|
|
data-projector-serial="{{ proj_serial or '' }}"
|
|
data-board-model="{{ board_model or '' }}"
|
|
data-board-serial="{{ board_serial or '' }}"
|
|
data-projector-inventory-number="{{ proj_inv or '' }}"
|
|
data-board-inventory-number="{{ board_inv or '' }}"
|
|
data-computer-inventory-number="{{ comp_inv_num or '' }}"
|
|
data-computer-id="{{ comp_id or '' }}"
|
|
data-cabinet-id="{{ cab_id or '' }}"
|
|
data-date-in-operation="{{ date_in_operation.strftime('%Y-%m-%d') if date_in_operation else '' }}"
|
|
data-note="{{ note or '' }}">
|
|
<td>{{ inv }}</td>
|
|
<td>{{ kit_type_labels.get(kit_type, kit_type) }}</td>
|
|
<td>{{ brand or '' }}</td>
|
|
<td>{{ proj_model or '' }}</td>
|
|
<td>{{ proj_serial or '' }}</td>
|
|
<td>{{ board_model or proj_model or '' }}</td>
|
|
<td>{{ board_serial or proj_serial or '' }}</td>
|
|
<td>
|
|
{% if comp_text %}
|
|
{{ comp_text }}{% if comp_inv %} ({{ comp_inv }}){% endif %}
|
|
{% else %}
|
|
{{ comp_inv or '' }}
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ cab_name or '' }}</td>
|
|
<td>{{ date_in_operation or '' }}</td>
|
|
<td>{{ note or '' }}</td>
|
|
<td>{{ date_added }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</table>
|
|
|
|
{% if session.get('role') in ('admin','storekeeper') %}
|
|
<div class="edit-modal" id="projectorEditModal" 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="/projectors/edit" id="projectorEditForm">
|
|
<input type="hidden" name="id" id="projectorModalId">
|
|
<div class="row g-2">
|
|
<div class="col-md-3">
|
|
<input name="inventory_number" id="projectorModalInv" class="form-control" placeholder="Инв. №">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<select name="kit_type" id="projectorModalKitType" class="form-select">
|
|
<option value="">Тип...</option>
|
|
{% for value, label in kit_types %}
|
|
<option value="{{ value }}">{{ label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div> <div class="col-md-3 js-modal-field" data-types="kit,projector,board,display,tv">
|
|
<select name="brand_select" id="projectorModalBrandSelect" class="form-select">
|
|
<option value="">Бренд...</option>
|
|
{% for b in projector_brands %}
|
|
<option value="{{ b }}">{{ b }}</option>
|
|
{% endfor %}
|
|
<option value="__custom__">+ Добавить бренд</option>
|
|
</select>
|
|
<input name="brand_custom" id="projectorModalBrandCustom" class="form-control mt-2 d-none" placeholder="Новый бренд">
|
|
<input type="hidden" name="brand" id="projectorModalBrand">
|
|
</div>
|
|
<div class="col-md-3 js-modal-field" data-types="kit,projector,display,tv">
|
|
<input name="projector_model" id="projectorModalModel" class="form-control" placeholder="Модель">
|
|
</div>
|
|
<div class="col-md-3 js-modal-field" data-types="kit,projector,display,tv">
|
|
<input name="projector_serial" id="projectorModalSerial" class="form-control" placeholder="Серийный №">
|
|
</div>
|
|
<div class="col-md-3 js-modal-field" data-types="kit">
|
|
<input name="projector_inventory_number" id="projectorModalProjectorInv" class="form-control" placeholder="Инв. № проектора">
|
|
</div>
|
|
<div class="col-md-3 js-modal-field" data-types="kit,board">
|
|
<input name="board_model" id="projectorModalBoard" class="form-control" placeholder="Модель доски">
|
|
<input name="board_serial" id="projectorModalBoardSerial" class="form-control mt-2" placeholder="Серийный № доски">
|
|
</div>
|
|
<div class="col-md-3 js-modal-field" data-types="kit">
|
|
<input name="board_inventory_number" id="projectorModalBoardInv" class="form-control" placeholder="Инв. № доски">
|
|
</div>
|
|
<div class="col-md-4 js-modal-field" data-types="kit,display">
|
|
<select name="computer_id" id="projectorModalComputer" class="form-select">
|
|
<option value="">Ноутбук / ПК (модель)...</option>
|
|
{% for cid, cinv, cbrand, cmodel, ctype in computers %}
|
|
{% set comp_text = [cbrand, cmodel]|select|join(' ') %}
|
|
<option value="{{ cid }}" data-inv="{{ cinv }}">{{ comp_text }}{% if cinv %} ({{ cinv }}){% endif %}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4 js-modal-field" data-types="kit">
|
|
<input name="computer_inventory_number" id="projectorModalComputerInv" class="form-control" placeholder="Инв. № ноутбука/ПК" readonly>
|
|
</div>
|
|
<div class="col-md-3 js-modal-field" data-types="kit,projector,board,display,tv">
|
|
<select name="cabinet_id" id="projectorModalCabinet" class="form-select">
|
|
<option value="">Кабинет...</option>
|
|
{% for cid, cname in cabinets %}
|
|
<option value="{{ cid }}">{{ cname }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3 js-modal-field" data-types="kit,projector,board,display">
|
|
<input name="date_in_operation" id="projectorModalDateIn" type="date" class="form-control">
|
|
</div>
|
|
<div class="col-md-6 js-modal-field" data-types="kit,projector,board,display">
|
|
<input name="note" id="projectorModalNote" 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 select = document.getElementById('projectorBrandSelect');
|
|
const customInput = document.getElementById('projectorBrandCustom');
|
|
const valueInput = document.getElementById('projectorBrandValue');
|
|
if (!select || !customInput || !valueInput) return;
|
|
function syncBrand() {
|
|
if (select.value === '__custom__') {
|
|
customInput.classList.remove('d-none');
|
|
valueInput.value = customInput.value.trim();
|
|
} else {
|
|
customInput.classList.add('d-none');
|
|
customInput.value = '';
|
|
valueInput.value = select.value;
|
|
}
|
|
}
|
|
select.addEventListener('change', syncBrand);
|
|
customInput.addEventListener('input', syncBrand);
|
|
syncBrand();
|
|
})();
|
|
(function() {
|
|
const select = document.getElementById('projectorModalBrandSelect');
|
|
const customInput = document.getElementById('projectorModalBrandCustom');
|
|
const valueInput = document.getElementById('projectorModalBrand');
|
|
if (!select || !customInput || !valueInput) return;
|
|
function syncBrand() {
|
|
if (select.value === '__custom__') {
|
|
customInput.classList.remove('d-none');
|
|
valueInput.value = customInput.value.trim();
|
|
} else {
|
|
customInput.classList.add('d-none');
|
|
customInput.value = '';
|
|
valueInput.value = select.value;
|
|
}
|
|
}
|
|
select.addEventListener('change', syncBrand);
|
|
customInput.addEventListener('input', syncBrand);
|
|
syncBrand();
|
|
})();
|
|
(function() {
|
|
const kitSelect = document.getElementById('kitTypeSelect');
|
|
const addForm = document.getElementById('projectorAddForm');
|
|
const compSelect = document.getElementById('projectorComputerSelect');
|
|
const compInv = document.getElementById('projectorComputerInv');
|
|
function applyType(container, type, selector) {
|
|
const fields = Array.from(container.querySelectorAll(selector));
|
|
fields.forEach((field) => {
|
|
const types = (field.dataset.types || '').split(',').map(t => t.trim()).filter(Boolean);
|
|
const show = !types.length || types.includes(type);
|
|
field.classList.toggle('field-hidden', !show);
|
|
});
|
|
}
|
|
if (kitSelect && addForm) {
|
|
kitSelect.addEventListener('change', () => {
|
|
applyType(addForm, kitSelect.value, '.js-field');
|
|
});
|
|
applyType(addForm, kitSelect.value, '.js-field');
|
|
}
|
|
if (compSelect && compInv) {
|
|
compSelect.addEventListener('change', () => {
|
|
const opt = compSelect.options[compSelect.selectedIndex];
|
|
const inv = opt ? (opt.getAttribute('data-inv') || '') : '';
|
|
compInv.value = inv;
|
|
});
|
|
}
|
|
})();
|
|
|
|
(function() {
|
|
const rows = document.querySelectorAll('.js-projector-row');
|
|
if (!rows.length) return;
|
|
const modal = document.getElementById('projectorEditModal');
|
|
const form = document.getElementById('projectorEditForm');
|
|
if (!modal || !form) return;
|
|
const modalFields = {
|
|
id: document.getElementById('projectorModalId'),
|
|
inventory_number: document.getElementById('projectorModalInv'),
|
|
kit_type: document.getElementById('projectorModalKitType'),
|
|
brand: document.getElementById('projectorModalBrand'),
|
|
brand_select: document.getElementById('projectorModalBrandSelect'),
|
|
brand_custom: document.getElementById('projectorModalBrandCustom'),
|
|
projector_model: document.getElementById('projectorModalModel'),
|
|
projector_serial: document.getElementById('projectorModalSerial'),
|
|
projector_inventory_number: document.getElementById('projectorModalProjectorInv'),
|
|
board_model: document.getElementById('projectorModalBoard'),
|
|
board_serial: document.getElementById('projectorModalBoardSerial'),
|
|
board_inventory_number: document.getElementById('projectorModalBoardInv'),
|
|
computer_inventory_number: document.getElementById('projectorModalComputerInv'),
|
|
computer_id: document.getElementById('projectorModalComputer'),
|
|
cabinet_id: document.getElementById('projectorModalCabinet'),
|
|
date_in_operation: document.getElementById('projectorModalDateIn'),
|
|
note: document.getElementById('projectorModalNote'),
|
|
};
|
|
|
|
function closeModal() {
|
|
modal.classList.remove('show');
|
|
modal.setAttribute('aria-hidden', 'true');
|
|
}
|
|
|
|
function openModal() {
|
|
modal.classList.add('show');
|
|
modal.setAttribute('aria-hidden', 'false');
|
|
}
|
|
|
|
function applyModalType(type) {
|
|
const fields = Array.from(modal.querySelectorAll('.js-modal-field'));
|
|
fields.forEach((field) => {
|
|
const types = (field.dataset.types || '').split(',').map(t => t.trim()).filter(Boolean);
|
|
const show = !types.length || types.includes(type);
|
|
field.classList.toggle('field-hidden', !show);
|
|
});
|
|
}
|
|
|
|
modal.addEventListener('click', (e) => {
|
|
if (e.target === modal || e.target.hasAttribute('data-modal-close')) {
|
|
closeModal();
|
|
}
|
|
});
|
|
|
|
const kitTypeSelect = document.getElementById('projectorModalKitType');
|
|
const modalComputerSelect = document.getElementById('projectorModalComputer');
|
|
const modalComputerInv = document.getElementById('projectorModalComputerInv');
|
|
if (kitTypeSelect) {
|
|
kitTypeSelect.addEventListener('change', () => {
|
|
applyModalType(kitTypeSelect.value);
|
|
});
|
|
}
|
|
if (modalComputerSelect && modalComputerInv) {
|
|
modalComputerSelect.addEventListener('change', () => {
|
|
const opt = modalComputerSelect.options[modalComputerSelect.selectedIndex];
|
|
const inv = opt ? (opt.getAttribute('data-inv') || '') : '';
|
|
modalComputerInv.value = inv;
|
|
});
|
|
}
|
|
|
|
function fillModalFromRow(row) {
|
|
const map = [
|
|
"id",
|
|
"inventory_number",
|
|
"kit_type",
|
|
"brand",
|
|
"projector_model",
|
|
"projector_serial",
|
|
"projector_inventory_number",
|
|
"board_model",
|
|
"board_serial",
|
|
"board_inventory_number",
|
|
"computer_inventory_number",
|
|
"computer_id",
|
|
"cabinet_id",
|
|
"date_in_operation",
|
|
"note",
|
|
];
|
|
let filled = false;
|
|
map.forEach((name) => {
|
|
if (!modalFields[name]) return;
|
|
const dataKey = name.replace(/_/g, '-');
|
|
const attrValue = row.getAttribute(`data-${dataKey}`);
|
|
if (attrValue !== null) {
|
|
modalFields[name].value = attrValue || '';
|
|
filled = true;
|
|
}
|
|
});
|
|
if (kitTypeSelect) {
|
|
applyModalType(kitTypeSelect.value);
|
|
}
|
|
return filled;
|
|
}
|
|
|
|
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="11">
|
|
<div class="row-actions__inner">
|
|
<button type="button" class="btn btn-sm btn-outline-primary js-open-projector-modal">Редактировать</button>
|
|
<form method="post" action="/projectors/delete" class="d-inline ms-2">
|
|
<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-projector-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>
|
|
<script>
|
|
window.addEventListener('DOMContentLoaded', () => {
|
|
const toggleBtn = document.getElementById('toggleProjectorSearch');
|
|
const card = document.getElementById('projectorSearchCard');
|
|
const form = document.getElementById('projectorSearchForm');
|
|
const input = document.getElementById('projectorSearchInput');
|
|
const clearBtn = document.getElementById('projectorSearchClear');
|
|
if (!toggleBtn || !card || !form || !input) return;
|
|
|
|
const rows = () => document.querySelectorAll('.js-projector-row');
|
|
|
|
const applyFilter = () => {
|
|
const q = (input.value || '').trim().toLowerCase();
|
|
rows().forEach((row) => {
|
|
const model = (row.getAttribute('data-projector-model') || '').toLowerCase();
|
|
const match = !q || model.includes(q);
|
|
row.classList.toggle('d-none', !match);
|
|
});
|
|
};
|
|
|
|
toggleBtn.addEventListener('click', () => {
|
|
card.classList.toggle('d-none');
|
|
if (!card.classList.contains('d-none')) input.focus();
|
|
});
|
|
|
|
form.addEventListener('submit', (e) => {
|
|
e.preventDefault();
|
|
applyFilter();
|
|
});
|
|
|
|
if (clearBtn) {
|
|
clearBtn.addEventListener('click', () => {
|
|
input.value = '';
|
|
applyFilter();
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
</div>
|
|
{% endblock %}
|