Dodato go:embed - disk-first logika za statičke fajlove i šablone

This commit is contained in:
2026-06-04 20:14:03 +02:00
parent 4f54ca3470
commit ccc08aee08
15 changed files with 443 additions and 64 deletions
+17 -6
View File
@@ -38,6 +38,8 @@
<div class="poruka-greska">Proverite unete podatke.</div>
{{else if eq .Greska "2"}}
<div class="poruka-greska">Greška pri čuvanju. Pokušajte ponovo.</div>
{{else if eq .Greska "3"}}
<div class="poruka-greska">Ova radnja nije dozvoljena.</div>
{{end}}
<!-- lista korisnika -->
@@ -62,18 +64,19 @@
<tr class="animiraj" style="border-bottom:0.5px solid var(--ivica);">
<td style="padding:10px 20px;font-size:14px;font-weight:500;color:var(--tekst-glavni);">{{.KorisnickoIme}}</td>
<td style="padding:10px 20px;">
{{if eq $.KorisnikUloga "superadmin"}}
{{if eq .Uloga "superadmin"}}
<span style="font-size:13px;color:var(--tekst-sporedni);">Superadmin</span>
{{else if eq $.KorisnikUloga "superadmin"}}
<form method="POST" action="/admin/korisnici/{{.ID}}/uloga" style="display:inline;">
<select name="uloga" onchange="this.form.submit()"
style="padding:4px 8px;border:0.5px solid var(--ivica);border-radius:6px;background:var(--pozadina);color:var(--tekst-glavni);font-size:12px;">
<option value="superadmin" {{if eq .Uloga "superadmin"}}selected{{end}}>Superadmin</option>
<option value="admin" {{if eq .Uloga "admin"}}selected{{end}}>Admin</option>
<option value="radnik" {{if eq .Uloga "radnik"}}selected{{end}}>Radnik</option>
</select>
</form>
{{else}}
<span style="font-size:13px;color:var(--tekst-sporedni);">
{{if eq .Uloga "superadmin"}}Superadmin{{else if eq .Uloga "admin"}}Admin{{else}}Radnik{{end}}
{{if eq .Uloga "admin"}}Admin{{else}}Radnik{{end}}
</span>
{{end}}
</td>
@@ -106,6 +109,15 @@
{{if .Aktivan}}Deaktiviraj{{else}}Aktiviraj{{end}}
</button>
</form>
{{if eq .Uloga "radnik"}}
<form method="POST" action="/admin/korisnici/{{.ID}}/obrisi" style="display:inline;"
onsubmit="return confirm('Obrisati korisnika {{.KorisnickoIme}}? Ova radnja je trajna.')">
<button type="submit"
style="padding:5px 12px;border:0.5px solid #fca5a5;border-radius:6px;background:transparent;color:#dc2626;font-size:12px;cursor:pointer;">
Obriši
</button>
</form>
{{end}}
{{end}}
</td>
</tr>
@@ -124,12 +136,12 @@
<div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(180px, 1fr));gap:14px;margin-bottom:16px;">
<div>
<label style="font-size:13px;color:var(--tekst-sporedni);display:block;margin-bottom:6px;">Korisničko ime</label>
<input type="text" name="korisnicko_ime" required minlength="3"
<input type="text" name="korisnicko_ime" required minlength="3" autocomplete="off"
style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;">
</div>
<div>
<label style="font-size:13px;color:var(--tekst-sporedni);display:block;margin-bottom:6px;">Lozinka</label>
<input type="password" name="lozinka" required minlength="8"
<input type="password" name="lozinka" required minlength="8" autocomplete="new-password"
style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;">
</div>
<div>
@@ -138,7 +150,6 @@
style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;">
<option value="radnik">Radnik</option>
<option value="admin">Admin</option>
{{if eq .KorisnikUloga "superadmin"}}<option value="superadmin">Superadmin</option>{{end}}
</select>
</div>
</div>
@@ -27,7 +27,7 @@
<div class="kartica animiraj" style="padding:0;overflow:hidden;">
<div style="padding:16px 20px;border-bottom:0.5px solid var(--ivica);display:flex;align-items:center;justify-content:space-between;">
<span style="font-size:15px;font-weight:500;color:var(--tekst-glavni);">Poslednjih 50 pokušaja</span>
<span style="font-size:12px;color:var(--tekst-sporedni);">Vreme prikazano u UTC</span>
<span style="font-size:12px;color:var(--tekst-sporedni);">Vreme prikazano po lokalnom vremenu servera</span>
</div>
{{if .Istorija}}
+122 -14
View File
@@ -8,6 +8,44 @@
.animiraj:nth-child(2) { animation-delay: 0.16s; }
.animiraj:nth-child(3) { animation-delay: 0.22s; }
.animiraj:nth-child(4) { animation-delay: 0.28s; }
.toast {
position: fixed;
bottom: 24px;
right: 24px;
z-index: 9999;
display: flex;
align-items: center;
gap: 10px;
padding: 12px 18px;
border-radius: 10px;
font-size: 13px;
font-weight: 500;
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
animation: toastIn 0.3s ease forwards;
max-width: 340px;
}
.toast-greska {
background: #fef2f2;
color: #dc2626;
border: 0.5px solid #fca5a5;
}
.toast-uspeh {
background: #f0fdf4;
color: #16a34a;
border: 0.5px solid #86efac;
}
@keyframes toastIn {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes toastOut {
from { opacity: 1; transform: translateY(0); }
to { opacity: 0; transform: translateY(12px); }
}
.toast.nestaje {
animation: toastOut 0.3s ease forwards;
}
</style>
{{end}}
@@ -15,9 +53,10 @@
<div style="width:100%;max-width:100%;">
{{if .Sacuvano}}
<div class="poruka-uspeh">
Podešavanja su uspešno sačuvana.
</div>
<div class="poruka-uspeh">Podešavanja su uspešno sačuvana.</div>
{{end}}
{{if .BackupVracen}}
<div class="poruka-uspeh">Baza je uspešno obnovljena iz rezervne kopije.</div>
{{end}}
<!-- upload loga — posebna forma jer je multipart, mora biti van glavne forme -->
@@ -27,11 +66,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="var(--sb-akcent)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
<span style="font-size:15px;font-weight:500;color:var(--tekst-glavni);">Logo firme</span>
</div>
{{if .LogoGreska}}
<div style="background:rgba(207,87,87,0.1);border:0.5px solid var(--greska);border-radius:8px;padding:8px 12px;margin-bottom:10px;font-size:13px;color:var(--greska);">
{{.LogoGreska}}
</div>
{{end}}
{{if .LogoPutanja}}
<div style="margin-bottom:12px;">
<img src="{{.LogoPutanja}}" alt="Trenutni logo"
@@ -150,12 +184,62 @@
</div>
<div style="display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px;">
<div style="font-size:13px;color:var(--tekst-sporedni);">Verzija programa: <span style="color:var(--tekst-glavni);font-weight:500;">{{.Verzija}}</span></div>
<a href="/podesavanja/backup"
style="display:inline-flex;align-items:center;gap:6px;padding:8px 16px;background:var(--kartica);border:0.5px solid var(--ivica);border-radius:8px;font-size:13px;color:var(--tekst-sporedni);text-decoration:none;transition:background 0.2s;"
onmouseover="this.style.background='var(--pozadina)'" onmouseout="this.style.background='var(--kartica)'">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
Preuzmi backup baze
</a>
<div style="display:flex;gap:8px;flex-wrap:wrap;">
<a href="/podesavanja/backup"
style="display:inline-flex;align-items:center;gap:6px;padding:8px 16px;background:var(--kartica);border:0.5px solid var(--ivica);border-radius:8px;font-size:13px;color:var(--tekst-sporedni);text-decoration:none;transition:background 0.2s;"
onmouseover="this.style.background='var(--pozadina)'" onmouseout="this.style.background='var(--kartica)'">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
Preuzmi backup baze
</a>
<button type="button" onclick="toggleBackupPanel()"
style="display:inline-flex;align-items:center;gap:6px;padding:8px 16px;background:var(--kartica);border:0.5px solid var(--ivica);border-radius:8px;font-size:13px;color:var(--tekst-sporedni);cursor:pointer;transition:background 0.2s;"
onmouseover="this.style.background='var(--pozadina)'" onmouseout="this.style.background='var(--kartica)'">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><polyline points="7 5 12 10 17 5"/></svg>
Vrati backup baze
</button>
</div>
</div>
<!-- panel sa listom backupa -->
<div id="backup-panel" style="display:none;margin-top:16px;border-top:0.5px solid var(--ivica);padding-top:16px;">
<div style="font-size:13px;font-weight:500;color:var(--tekst-glavni);margin-bottom:10px;">Dostupne rezervne kopije</div>
{{if .Backupi}}
<div style="overflow-x:auto;">
<table style="width:100%;border-collapse:collapse;font-size:13px;">
<thead>
<tr style="border-bottom:0.5px solid var(--ivica);">
<th style="padding:8px 12px;text-align:left;font-weight:500;color:var(--tekst-sporedni);">Naziv fajla</th>
<th style="padding:8px 12px;text-align:left;font-weight:500;color:var(--tekst-sporedni);">Datum</th>
<th style="padding:8px 12px;text-align:right;font-weight:500;color:var(--tekst-sporedni);">Veličina</th>
<th style="padding:8px 12px;text-align:right;font-weight:500;color:var(--tekst-sporedni);"></th>
</tr>
</thead>
<tbody>
{{range .Backupi}}
<tr style="border-bottom:0.5px solid var(--ivica);">
<td style="padding:8px 12px;color:var(--tekst-glavni);font-family:monospace;font-size:12px;">{{.Ime}}</td>
<td style="padding:8px 12px;color:var(--tekst-sporedni);">{{.Datum}}</td>
<td style="padding:8px 12px;text-align:right;color:var(--tekst-sporedni);">{{.Velicina}}</td>
<td style="padding:8px 12px;text-align:right;">
<form method="POST" action="/podesavanja/backup/vrati"
onsubmit="return confirm('Vratiti bazu na stanje od: {{.Datum}}?\n\nTrenutna baza će biti automatski sačuvana pre obnove.')">
<input type="hidden" name="ime" value="{{.Ime}}">
<button type="submit"
style="padding:4px 12px;background:transparent;border:0.5px solid #fca5a5;border-radius:6px;color:#dc2626;font-size:12px;cursor:pointer;">
Vrati
</button>
</form>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
{{else}}
<div style="padding:20px;text-align:center;color:var(--tekst-sporedni);font-size:13px;">
Nema dostupnih rezervnih kopija.
</div>
{{end}}
</div>
</div>
@@ -170,4 +254,28 @@
</form>
</div>
<script>
function toggleBackupPanel() {
var p = document.getElementById('backup-panel');
p.style.display = p.style.display === 'none' ? 'block' : 'none';
}
</script>
{{if .LogoGreska}}
<div id="toast-logo" class="toast toast-greska">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0;"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
{{.LogoGreska}}
</div>
<script>
(function() {
var t = document.getElementById('toast-logo');
if (!t) return;
setTimeout(function() {
t.classList.add('nestaje');
setTimeout(function() { t.remove(); }, 300);
}, 4000);
})();
</script>
{{end}}
{{end}}