Servis: pregled troškova, auto-cena delova, modalni prozor za potvrdu
- Detalji naloga prikazuju cenu usluge, ugrađene delove, ukupno i za naplatu kao zasebne stavke - Otpremnica uključuje stavku ugrađenih delova u obračun - Biranje artikla u formi za delove automatski popunjava cenu po komadu - Zamenjen confirm() sa prilagođenim modalnim prozorom za sve potvrde
This commit is contained in:
@@ -161,22 +161,38 @@
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Konačna cena</div>
|
||||
<div style="font-size:20px;font-weight:600;color:var(--sb-akcent);">
|
||||
<div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Cena usluge</div>
|
||||
<div style="font-size:16px;font-weight:500;color:var(--tekst-glavni);">
|
||||
{{if .Nalog.CenaKonacna}}{{.Nalog.CenaKonacnaStr}} din{{else}}—{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{if gt .UkupnoDelovi 0.0}}
|
||||
<div>
|
||||
<div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Ugrađeni delovi</div>
|
||||
<div style="font-size:16px;font-weight:500;color:var(--tekst-glavni);">
|
||||
{{printf "%.2f" .UkupnoDelovi}} din
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Nalog.CenaKonacna}}
|
||||
<div>
|
||||
<div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Ukupno</div>
|
||||
<div style="font-size:20px;font-weight:600;color:var(--sb-akcent);">
|
||||
{{printf "%.2f" .UkupnoSve}} din
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div>
|
||||
<div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Avans</div>
|
||||
<div style="font-size:16px;font-weight:500;color:var(--tekst-glavni);">
|
||||
{{if .Nalog.Avans}}{{.Nalog.AvansStr}} din{{else}}—{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{if .Nalog.PreostaloZaNaplatu}}
|
||||
{{if .Nalog.CenaKonacna}}
|
||||
<div>
|
||||
<div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Preostalo za naplatu</div>
|
||||
<div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Za naplatu</div>
|
||||
<div style="font-size:20px;font-weight:600;color:#16a34a;">
|
||||
{{.Nalog.PreostaloZaNaplatuStr}} din
|
||||
{{printf "%.2f" .PreostaloSve}} din
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -199,7 +215,7 @@
|
||||
<select name="artikal_id" style="width:100%;" required>
|
||||
<option value="">— odaberi —</option>
|
||||
{{range .Artikli}}
|
||||
<option value="{{.ID}}">{{.Naziv}} ({{.Kolicina}} kom)</option>
|
||||
<option value="{{.ID}}" data-cena="{{.ProdajnaCena}}">{{.Naziv}} ({{.Kolicina}} kom)</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
@@ -279,4 +295,21 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var sel = document.querySelector('select[name="artikal_id"]');
|
||||
var cenaInput = document.querySelector('input[name="cena_komada"]');
|
||||
if (!sel || !cenaInput) return;
|
||||
sel.addEventListener('change', function() {
|
||||
var opt = sel.options[sel.selectedIndex];
|
||||
var cena = parseFloat(opt.dataset.cena);
|
||||
if (!isNaN(cena) && cena > 0) {
|
||||
cenaInput.value = cena.toFixed(2);
|
||||
} else {
|
||||
cenaInput.value = '0';
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{{end}}
|
||||
|
||||
@@ -199,6 +199,12 @@
|
||||
<td>Cena usluge</td>
|
||||
<td class="desno" style="width:160px;">{{.Nalog.CenaKonacnaStr}} din</td>
|
||||
</tr>
|
||||
{{if gt .UkupnoDelovi 0.0}}
|
||||
<tr>
|
||||
<td>Ugrađeni delovi i materijal</td>
|
||||
<td class="desno">{{printf "%.2f" .UkupnoDelovi}} din</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{if .Nalog.Avans}}
|
||||
<tr>
|
||||
<td>Avans (plaćeno)</td>
|
||||
@@ -207,12 +213,10 @@
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{if .Nalog.PreostaloZaNaplatu}}
|
||||
<div class="naplata-blok">
|
||||
<div class="naplata-labela">Za naplatu pri preuzimanju:</div>
|
||||
<div class="naplata-iznos">{{.Nalog.PreostaloZaNaplatuStr}} din</div>
|
||||
<div class="naplata-iznos">{{printf "%.2f" .PreostaloSve}} din</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -226,8 +226,51 @@
|
||||
|
||||
{{block "dodatni-js" .}}{{end}}
|
||||
|
||||
<!-- modal za potvrdu akcije -->
|
||||
<div id="potvrda-modal" style="display:none;position:fixed;inset:0;z-index:9999;align-items:center;justify-content:center;">
|
||||
<div id="potvrda-pozadina" style="position:absolute;inset:0;background:rgba(0,0,0,0.55);backdrop-filter:blur(3px);"></div>
|
||||
<div style="position:relative;background:var(--kartica-pozadina);border:0.5px solid var(--ivica);border-radius:12px;padding:28px 28px 22px;max-width:380px;width:90%;box-shadow:0 20px 60px rgba(0,0,0,0.35);">
|
||||
<div style="font-size:15px;font-weight:500;color:var(--tekst-glavni);margin-bottom:20px;line-height:1.5;" id="potvrda-poruka"></div>
|
||||
<div style="display:flex;gap:10px;justify-content:flex-end;">
|
||||
<button id="potvrda-odustani" class="btn-sekundarno">Odustani</button>
|
||||
<button id="potvrda-potvrdi" class="btn-opasno">Potvrdi</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CSRF i potvrda: inicijalizacija na učitavanju i posle htmx swap-a -->
|
||||
<script>
|
||||
(function() {
|
||||
var modal = document.getElementById('potvrda-modal');
|
||||
var poruka = document.getElementById('potvrda-poruka');
|
||||
var btnPotvrdi = document.getElementById('potvrda-potvrdi');
|
||||
var btnOdustani = document.getElementById('potvrda-odustani');
|
||||
var pozadina = document.getElementById('potvrda-pozadina');
|
||||
var _resolveFn = null;
|
||||
|
||||
function prikaziModal(tekst) {
|
||||
return new Promise(function(resolve) {
|
||||
poruka.textContent = tekst;
|
||||
modal.style.display = 'flex';
|
||||
_resolveFn = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
function zatvoriModal(rezultat) {
|
||||
modal.style.display = 'none';
|
||||
if (_resolveFn) { _resolveFn(rezultat); _resolveFn = null; }
|
||||
}
|
||||
|
||||
btnPotvrdi.addEventListener('click', function() { zatvoriModal(true); });
|
||||
btnOdustani.addEventListener('click', function() { zatvoriModal(false); });
|
||||
pozadina.addEventListener('click', function() { zatvoriModal(false); });
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape' && modal.style.display === 'flex') zatvoriModal(false);
|
||||
});
|
||||
|
||||
window._ntechPotvrdi = prikaziModal;
|
||||
})();
|
||||
|
||||
function ntechInicijalizuj() {
|
||||
var m = document.querySelector('meta[name="csrf-token"]');
|
||||
if (m && m.content) {
|
||||
@@ -242,11 +285,18 @@
|
||||
if (el._potvrda) return;
|
||||
el._potvrda = true;
|
||||
el.addEventListener('click', function(e) {
|
||||
if (!confirm(el.getAttribute('data-potvrda'))) e.preventDefault();
|
||||
e.preventDefault();
|
||||
window._ntechPotvrdi(el.getAttribute('data-potvrda')).then(function(ok) {
|
||||
if (!ok) return;
|
||||
// dugme unutar forme — submit forme
|
||||
var forma = el.closest('form');
|
||||
if (forma) { forma.submit(); return; }
|
||||
// link
|
||||
if (el.href) { window.location.href = el.href; }
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
// htmx:afterSettle listener se dodaje samo jednom — ne sme da se gomila po swap-ovima
|
||||
if (!window._ntechCsrfDodato) {
|
||||
window._ntechCsrfDodato = true;
|
||||
document.addEventListener('htmx:afterSettle', ntechInicijalizuj);
|
||||
|
||||
Reference in New Issue
Block a user