Files
Inv_web/source/templates/base.html
2026-02-23 20:59:05 +03:00

408 lines
12 KiB
HTML
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.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Инвентаризация картриджей</title>
<link rel="icon" type="image/png" href="{{ url_for('static', filename='icon.png') }}">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
@font-face {
font-family: 'Chicago Regular';
src: url('{{ url_for("static", filename="chicago-regular.ttf") }}') format('truetype');
font-weight: 400;
font-style: normal;
font-display: swap;
}
html, body {
height: 100%;
}
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
body.page-bg {
background-repeat: no-repeat;
background-position: right bottom;
background-size: auto 100vh;
background-attachment: fixed;
}
body.bg-cabinets { background-image: url('{{ url_for("static", filename="bg_cab.png", v=2) }}'); }
body.bg-cartridges { background-image: url('{{ url_for("static", filename="bg_cart.png", v=2) }}'); }
body.bg-devices { background-image: url('{{ url_for("static", filename="bg_dev.png", v=2) }}'); }
body.bg-history { background-image: url('{{ url_for("static", filename="bg_hist.png", v=2) }}'); }
body.bg-orders { background-image: url('{{ url_for("static", filename="bg_order.png", v=2) }}'); }
.nav-wrap {
position: sticky;
top: 0;
z-index: 1000;
position: relative;
}
.nav-legacy,
.nav-legacy .navbar-brand,
.nav-legacy .nav-main-btn,
.nav-legacy .nav-link,
.nav-legacy .btn,
.nav-legacy .btn-outline-light,
.subnav,
.subnav-link,
.subnav-sublink,
.subnav-nested > summary {
font-family: 'Chicago Regular', sans-serif;
}
.nav-legacy {
background: #d9d9d9;
border-bottom: 1px solid #808080;
box-shadow: inset 0 1px 0 #f7f7f7, inset 0 -1px 0 #b5b5b5;
padding-top: 0;
padding-bottom: 0;
min-height: 28px;
align-items: stretch;
}
.nav-legacy::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: -1px;
height: 1px;
background: #000;
}
.nav-legacy .navbar-brand {
color: #111;
font-weight: 700;
letter-spacing: 0.2px;
font-size: 0.95rem;
padding: 0 0.4rem;
margin: 0;
display: flex;
align-items: center;
}
.nav-legacy .navbar-brand img {
height: 18px;
width: auto;
display: block;
}
.nav-main-btn {
color: #111;
background: transparent;
border: 0;
padding: 0 0.5rem;
font-weight: 600;
border-radius: 2px;
line-height: 1.1;
font-size: 0.95rem;
align-self: stretch;
display: flex;
align-items: center;
height: 100%;
text-decoration: none;
}
.nav-main-btn.nav-link {
color: #111;
}
.nav-main-btn.nav-link:hover,
.nav-main-btn.nav-link:focus {
color: #fff;
text-decoration: none;
}
.nav-main-btn:hover,
.nav-main-btn:focus {
color: #fff;
background: #372ba6;
box-shadow: none;
outline: none;
}
.nav-main-btn.active {
color: #fff;
background: #372ba6;
box-shadow: none;
}
.nav-legacy .btn,
.nav-legacy .btn:focus,
.nav-legacy .btn:hover {
color: #111 !important;
border-color: #9a9a9a;
background: #bdbdbd;
}
.nav-legacy .btn-outline-light,
.nav-legacy .btn-outline-light:hover,
.nav-legacy .btn-outline-light:focus {
color: #111 !important;
border-color: #9a9a9a;
background: #bdbdbd;
}
.subnav {
position: absolute;
left: 0;
width: max-content;
min-width: 120px;
top: 100%;
background: #d7d7d7;
border-top: 1px solid #7d7d7d;
border-bottom: 1px solid #000;
overflow: hidden;
opacity: 1;
max-height: 0;
transition: max-height 0.18s steps(6, end);
}
.subnav-inner {
width: max-content;
}
.subnav.open {
max-height: 240px;
}
.subnav-inner {
overflow: hidden;
}
.subnav-panel {
display: none;
padding: 0.35rem 0.75rem;
}
.subnav-panel.active {
display: flex;
flex-direction: column;
gap: 0.35rem;
align-items: stretch;
}
.subnav-link {
color: #111;
text-decoration: none;
padding: 0.12rem 0.4rem;
border-radius: 2px;
background: transparent;
font-size: 0.92rem;
display: block;
width: 100%;
box-sizing: border-box;
}
.subnav-link:hover {
background: #372ba6;
color: #fff;
box-shadow: none;
}
.subnav-group {
display: flex;
flex-direction: column;
gap: 0.35rem;
padding: 0.25rem 0.5rem;
background: transparent;
border-radius: 0.6rem;
}
.subnav-group-title {
color: #444;
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.subnav-sublink {
color: #111;
text-decoration: none;
padding: 0.12rem 0.4rem;
border-radius: 2px;
background: transparent;
margin-left: 0.4rem;
font-size: 0.9rem;
display: block;
width: 100%;
box-sizing: border-box;
}
.subnav-sublink:hover {
background: #372ba6;
color: #fff;
box-shadow: none;
}
.subnav-nested {
border: 0;
padding: 0;
margin: 0;
}
.subnav-nested > summary {
list-style: none;
cursor: pointer;
color: #111;
padding: 0.12rem 0.4rem;
border-radius: 2px;
background: transparent;
width: 100%;
display: block;
box-sizing: border-box;
}
.subnav-nested > summary::-webkit-details-marker {
display: none;
}
.subnav-nested[open] > summary {
background: #bdbdbd;
color: #111;
box-shadow: inset 0 0 0 1px #9a9a9a;
}
.subnav-nested-menu {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 0.5rem;
padding: 0.4rem 0.5rem 0.2rem 0.75rem;
max-height: 0;
opacity: 0;
transform: translateY(-4px);
overflow: hidden;
transition: max-height 0.18s steps(4, end), opacity 0.12s ease, transform 0.12s ease;
}
.subnav-nested[open] .subnav-nested-menu {
max-height: 120px;
opacity: 1;
transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
.subnav-nested-menu {
transition: none;
}
}
</style>
</head>
{% set body_class_value = (session.get('role') == 'admin') and (self.body_class() | trim) or '' %}
<body class="{{ body_class_value }}">
<div class="nav-wrap">
<nav class="navbar nav-legacy px-3">
<a class="navbar-brand" href="/">
<img src="{{ url_for('static', filename='main_menu_logo.png') }}" alt="Главное меню">
</a>
{% if session.get('user') %}
<div class="d-flex align-items-center gap-3">
<button class="nav-main-btn" type="button" data-subnav="printers" onclick="window.__toggleSubnav && window.__toggleSubnav('printers')">МФУ/Принтеры</button>
<button class="nav-main-btn" type="button" data-subnav="devices" onclick="window.__toggleSubnav && window.__toggleSubnav('devices')">Устройства</button>
<button class="nav-main-btn" type="button" data-subnav="building" onclick="window.__toggleSubnav && window.__toggleSubnav('building')">Здание</button>
<button class="nav-main-btn" type="button" data-subnav="history" onclick="window.__toggleSubnav && window.__toggleSubnav('history')">История</button>
<a class="nav-main-btn nav-link" href="/orders">Заказы</a>
</div>
{% endif %}
<div class="ms-auto d-flex align-items-center gap-2">
{% if session.get('user') %}
<span class="text-white small">Пользователь: {{ session.get('user') }} ({{ session.get('role') }})</span>
<a class="btn btn-info btn-sm" href="/report/all.zip">Полный отчёт (ZIP)</a>
<a class="btn btn-outline-light btn-sm" href="/logout">Выйти</a>
{% else %}
<a class="btn btn-outline-light btn-sm" href="/login">Войти</a>
{% endif %}
</div>
</nav>
{% if session.get('user') %}
<div class="subnav" id="subnav">
<div class="subnav-inner">
<div class="subnav-panel" data-panel="printers">
<a class="subnav-link" href="/cartridges">Картриджы</a>
<a class="subnav-link" href="/consumables">Расходные материалы</a>
<a class="subnav-link" href="/catalog">Каталог</a>
</div>
<div class="subnav-panel" data-panel="devices">
<a class="subnav-link" href="/devices">МФУ/Принтеры</a>
<a class="subnav-link" href="/computers">Компьютеры/Ноутбуки</a>
<a class="subnav-link" href="/projectors">Проекторы</a>
</div>
<div class="subnav-panel" data-panel="building">
<a class="subnav-link" href="/cabinets">Кабинеты</a>
</div>
<div class="subnav-panel" data-panel="history">
<details class="subnav-nested">
<summary>Картриджи</summary>
<div class="subnav-nested-menu">
</div>
</details>
<details class="subnav-nested">
<summary>Расходные материалы</summary>
<div class="subnav-nested-menu">
</div>
</details>
<details class="subnav-nested">
<summary>Устройства</summary>
<div class="subnav-nested-menu">
<a class="subnav-sublink" href="/equipment/history/additions">История добавления</a>
<a class="subnav-sublink" href="/equipment/history/movements">Движение техники</a>
</div>
</details>
</div>
</div>
</div>
{% endif %}
</div>
<div class="container mt-4 flex-grow-1">
{% with messages = get_flashed_messages() %}
{% if messages %}<div class="alert alert-info">{{ messages[0] }}</div>{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
<footer class="text-center text-muted py-3">
Система инвентаризации © {{ current_year }} г. Alexey Sergeevich
</footer>
<script>
(function() {
const subnav = document.getElementById('subnav');
if (!subnav) return;
const panels = Array.from(subnav.querySelectorAll('.subnav-panel'));
const triggers = Array.from(document.querySelectorAll('[data-subnav]'));
let active = null;
function getTrigger(target) {
let el = target;
if (el && el.nodeType === 3) el = el.parentNode;
while (el && el.nodeType === 1) {
if (el.getAttribute && el.getAttribute('data-subnav')) return el;
el = el.parentNode;
}
return null;
}
function closeAll() {
panels.forEach(p => p.classList.remove('active'));
triggers.forEach(t => t.classList.remove('active'));
subnav.classList.remove('open');
active = null;
}
function openPanel(name, triggerEl) {
const panel = subnav.querySelector(`[data-panel="${name}"]`);
if (!panel) return;
panels.forEach(p => p.classList.toggle('active', p === panel));
triggers.forEach(t => t.classList.toggle('active', t.dataset.subnav === name));
subnav.classList.add('open');
const navWrap = document.querySelector('.nav-wrap');
if (navWrap && triggerEl) {
const navRect = navWrap.getBoundingClientRect();
const trigRect = triggerEl.getBoundingClientRect();
const left = Math.max(0, trigRect.left - navRect.left);
subnav.style.left = `${left}px`;
subnav.style.minWidth = `${Math.max(120, trigRect.width)}px`;
}
active = name;
}
window.__toggleSubnav = function(name) {
if (active === name) {
closeAll();
return;
}
const triggerEl = document.querySelector(`[data-subnav="${name}"]`);
openPanel(name, triggerEl);
};
triggers.forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
});
});
document.addEventListener('click', (e) => {
let target = e.target;
if (target && target.nodeType === 3) target = target.parentNode;
const isTrigger = !!getTrigger(target);
const isSubnav = target && subnav.contains(target);
if (!isTrigger && !isSubnav) closeAll();
});
subnav.addEventListener('click', (e) => e.stopPropagation());
})();
</script>
</body>
</html>