8cec26a03f
- Uklonjena globalna tema i pozadinska slika aplikacije (ostala samo lična pozadina po korisniku) - Uklonjena animacija treperenja pozadine pri navigaciji; dodat sessionStorage za instant prikaz - Dozvole premeštene iz sidebar-a u Podešavanja → Sistem; vidljive i adminu (samo Radnik kolona) - Admin može menjati samo dozvole uloge Radnik, superadmin menja i Radnik i Admin - Zatamnjivanje kartice NTech na stranici prijave — novi slider u Podešavanja → Izgled - Upozorenje na dashboard-u (kritične zalihe) — popravljen kontrast boje
260 lines
8.7 KiB
HTML
260 lines
8.7 KiB
HTML
{{define "base"}}
|
||
<!doctype html>
|
||
<html lang="sr">
|
||
<head>
|
||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>{{block "naslov" .}}NTech{{end}}</title>
|
||
<meta name="csrf-token" content="{{.CsrfToken}}">
|
||
|
||
<script>
|
||
if (window.innerWidth > 768 && localStorage.getItem('sidebar-skupljen') === 'true') {
|
||
document.documentElement.classList.add('sidebar-init-skupljen');
|
||
}
|
||
// pozadinska slika iz prethodne stranice — sprečava treperenje pri navigaciji
|
||
(function() {
|
||
var bg = sessionStorage.getItem('ntech-bg');
|
||
if (bg) {
|
||
var s = document.documentElement.style;
|
||
s.backgroundImage = 'url(' + bg + ')';
|
||
s.backgroundSize = 'cover';
|
||
s.backgroundPosition = 'center';
|
||
s.backgroundAttachment = 'fixed';
|
||
}
|
||
})();
|
||
</script>
|
||
|
||
<!-- tema — učitava se prva -->
|
||
<link rel="stylesheet" href="/static/css/teme/{{.Tema}}.css" />
|
||
|
||
<!-- glavni stilovi -->
|
||
<link rel="stylesheet" href="/static/css/main.css" />
|
||
|
||
<!-- tailwind -->
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
|
||
{{block "dodatni-css" .}}{{end}}
|
||
|
||
{{if .AppPozadina}}
|
||
<style>
|
||
.app-bg {
|
||
position: fixed;
|
||
inset: {{if ne .AppPozadinaBlurPozadine "0"}}-20px{{else}}0{{end}};
|
||
background-image: url('{{.AppPozadina}}');
|
||
background-size: cover;
|
||
background-position: center;
|
||
filter: blur({{.AppPozadinaBlurPozadine}}px);
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
}
|
||
.app-overlay {
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(0,0,0,{{.AppPozadinaOpacity}}%);
|
||
pointer-events: none;
|
||
z-index: 1;
|
||
}
|
||
.raspored {
|
||
position: relative;
|
||
z-index: 2;
|
||
}
|
||
.sidebar {
|
||
background: rgba(0,0,0,{{if .AppPozadinaGlassOpacity}}{{.AppPozadinaGlassOpacity}}%{{else}}0.3{{end}}) !important;
|
||
backdrop-filter: blur({{.AppPozadinaBlur}}px);
|
||
-webkit-backdrop-filter: blur({{.AppPozadinaBlur}}px);
|
||
border-right: 1px solid rgba(255,255,255,0.12) !important;
|
||
}
|
||
.sidebar .nav-stavka,
|
||
.sidebar .logo-naziv,
|
||
.sidebar .logo-podnazlov {
|
||
text-shadow: 0 1px 3px rgba(0,0,0,0.8);
|
||
color: rgba(255,255,255,0.95) !important;
|
||
}
|
||
.sidebar .nav-stavka svg {
|
||
color: rgba(255,255,255,0.95) !important;
|
||
stroke: rgba(255,255,255,0.95) !important;
|
||
}
|
||
.sidebar .nav-oznaka {
|
||
text-shadow: 0 1px 3px rgba(0,0,0,0.8);
|
||
color: rgba(255,255,255,0.7) !important;
|
||
}
|
||
.topbar {
|
||
background: rgba(0,0,0,{{if .AppPozadinaGlassOpacity}}{{.AppPozadinaGlassOpacity}}%{{else}}0.08{{end}}) !important;
|
||
backdrop-filter: blur({{.AppPozadinaBlur}}px);
|
||
-webkit-backdrop-filter: blur({{.AppPozadinaBlur}}px);
|
||
border-bottom: 1px solid rgba(255,255,255,0.12) !important;
|
||
}
|
||
.kartica {
|
||
background: rgba(0,0,0,{{if .AppPozadinaGlassOpacity}}{{.AppPozadinaGlassOpacity}}%{{else}}0.08{{end}}) !important;
|
||
backdrop-filter: blur({{.AppPozadinaBlur}}px);
|
||
-webkit-backdrop-filter: blur({{.AppPozadinaBlur}}px);
|
||
border: 1px solid rgba(255,255,255,0.12) !important;
|
||
}
|
||
.kartica p,
|
||
.kartica span,
|
||
.kartica h1,
|
||
.kartica h2,
|
||
.kartica h3,
|
||
.kartica h4,
|
||
.kartica label,
|
||
.kartica td,
|
||
.kartica th,
|
||
.kartica li,
|
||
.kartica a {
|
||
color: rgba(255,255,255,0.95) !important;
|
||
text-shadow: 0 1px 3px rgba(0,0,0,0.7);
|
||
}
|
||
table, th, td {
|
||
color: rgba(255,255,255,0.95) !important;
|
||
text-shadow: 0 1px 3px rgba(0,0,0,0.8);
|
||
}
|
||
tr {
|
||
background: rgba(0,0,0,0.2);
|
||
}
|
||
tr:hover {
|
||
background: rgba(0,0,0,0.35);
|
||
}
|
||
thead th {
|
||
background: rgba(0,0,0,0.4) !important;
|
||
}
|
||
div:has(> canvas) {
|
||
background: rgba(0,0,0,0.3);
|
||
border-radius: 8px;
|
||
padding: 8px;
|
||
}
|
||
</style>
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
var meni = document.getElementById('avatar-meni');
|
||
if (!meni) return;
|
||
meni.style.backdropFilter = 'none';
|
||
meni.style.webkitBackdropFilter = 'none';
|
||
meni.style.background = 'var(--kartica)';
|
||
});
|
||
</script>
|
||
{{end}}
|
||
</head>
|
||
<body>
|
||
{{if .AppPozadina}}<div class="app-bg"></div><div class="app-overlay"></div>{{end}}
|
||
<div class="raspored">
|
||
<div class="sidebar-overlay" id="sidebar-overlay"></div>
|
||
{{template "sidebar" .}}
|
||
|
||
<div class="glavni-sadrzaj">
|
||
{{template "topbar" .}}
|
||
|
||
<main class="sadrzaj">{{block "sadrzaj" .}}{{end}}</main>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- flash poruka — prikazuje se kao toast u gornjem desnom uglu -->
|
||
{{if .Flash}}
|
||
<div id="flash-toast" class="flash-toast flash-{{.Flash.Tip}}" role="alert">
|
||
<span class="flash-ikona">{{if eq .Flash.Tip "uspeh"}}✓{{else}}!{{end}}</span>
|
||
<span class="flash-tekst">{{.Flash.Poruka}}</span>
|
||
<button class="flash-zatvori" onclick="this.parentElement.remove()" aria-label="Zatvori">×</button>
|
||
</div>
|
||
<script>
|
||
(function() {
|
||
var t = document.getElementById('flash-toast');
|
||
if (!t) return;
|
||
setTimeout(function() {
|
||
t.classList.add('flash-izlaz');
|
||
setTimeout(function() { if (t.parentElement) t.remove(); }, 350);
|
||
}, 4000);
|
||
})();
|
||
</script>
|
||
{{end}}
|
||
|
||
<!-- alpine.js za interaktivnost -->
|
||
<script
|
||
src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"
|
||
defer
|
||
></script>
|
||
|
||
<!-- htmx za komunikaciju sa serverom -->
|
||
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.x.x/dist/htmx.min.js"></script>
|
||
|
||
<!-- sidebar logika -->
|
||
<!-- sidebar logika -->
|
||
<script>
|
||
const sidebar = document.getElementById("sidebar");
|
||
const hamburger = document.getElementById("hamburger");
|
||
const overlay = document.getElementById("sidebar-overlay");
|
||
const mobilni = () => window.innerWidth <= 768;
|
||
|
||
// učitaj stanje iz localStorage samo za desktop
|
||
if (!mobilni() && localStorage.getItem("sidebar-skupljen") === "true") {
|
||
sidebar.classList.add("skupljen");
|
||
}
|
||
document.documentElement.classList.remove('sidebar-init-skupljen');
|
||
|
||
hamburger.addEventListener("click", () => {
|
||
if (mobilni()) {
|
||
// mobilno — drawer ponašanje
|
||
sidebar.classList.toggle("otvoren");
|
||
overlay.classList.toggle("aktivan");
|
||
} else {
|
||
// desktop — skupljanje
|
||
sidebar.classList.toggle("skupljen");
|
||
localStorage.setItem(
|
||
"sidebar-skupljen",
|
||
sidebar.classList.contains("skupljen"),
|
||
);
|
||
}
|
||
});
|
||
|
||
// zatvori sidebar klikom na overlay
|
||
overlay.addEventListener("click", () => {
|
||
sidebar.classList.remove("otvoren");
|
||
overlay.classList.remove("aktivan");
|
||
});
|
||
|
||
// zatvori sidebar kada se promeni veličina prozora
|
||
window.addEventListener("resize", () => {
|
||
if (!mobilni()) {
|
||
sidebar.classList.remove("otvoren");
|
||
overlay.classList.remove("aktivan");
|
||
}
|
||
});
|
||
</script>
|
||
|
||
{{block "dodatni-js" .}}{{end}}
|
||
|
||
<!-- čuva URL pozadine za sledeću navigaciju (bez treperenja) -->
|
||
<script>
|
||
{{if .AppPozadina}}
|
||
sessionStorage.setItem('ntech-bg', '{{.AppPozadina}}');
|
||
{{else}}
|
||
sessionStorage.removeItem('ntech-bg');
|
||
document.documentElement.style.backgroundImage = '';
|
||
{{end}}
|
||
</script>
|
||
|
||
<!-- CSRF: automatski dodaje skriveno polje u sve POST forme -->
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
var m = document.querySelector('meta[name="csrf-token"]');
|
||
if (!m || !m.content) return;
|
||
document.querySelectorAll('form[method="POST"],form[method="post"]').forEach(function(f) {
|
||
if (f.querySelector('input[name="_csrf"]')) return;
|
||
var i = document.createElement('input');
|
||
i.type = 'hidden'; i.name = '_csrf'; i.value = m.content;
|
||
f.appendChild(i);
|
||
});
|
||
|
||
// data-potvrda: sigurna alternativa za onclick=confirm() na dugmadima i linkovima
|
||
// Vrednost atributa je poruka koja se prikazuje korisniku. Go template je HTML-escape-uje,
|
||
// JS čita originalnu vrednost — nema problema sa specijalnim karakterima u imenima.
|
||
document.querySelectorAll('[data-potvrda]').forEach(function(el) {
|
||
el.addEventListener('click', function(e) {
|
||
if (!confirm(el.getAttribute('data-potvrda'))) e.preventDefault();
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|
||
{{end}}
|