Servis detalji: glatko osvežavanje bez treperenja i skoka
Sve forme na detaljima (dodavanje/brisanje rada i dela, promena statusa) sada osvežavaju ceo #glavni-sadrzaj preko data-refresh umesto punog reloda: status badge se ažurira odmah, nema skoka na vrh ni treptaja pozadine. AJAX grana dodaje bez-anim reflow (CSS #glavni-sadrzaj.bez-anim) i re-izvršava inline skripte da se combobox pretrage ponovo povežu nakon zamene.
This commit is contained in:
@@ -1100,6 +1100,14 @@ select {
|
||||
#dobavljaci-rezultati.bez-anim .tabela tbody tr,
|
||||
#dobavljaci-rezultati.bez-anim .animiraj { animation: none; }
|
||||
|
||||
/* osvežavanje dela stranice preko data-refresh (npr. detalji naloga) ne sme da
|
||||
re-animira kartice — sprečava treptaj pri dodavanju/brisanju i promeni statusa */
|
||||
#glavni-sadrzaj.bez-anim .tabela tbody tr,
|
||||
#glavni-sadrzaj.bez-anim .animiraj,
|
||||
.bez-anim.kartica,
|
||||
.bez-anim .tabela tbody tr,
|
||||
.bez-anim .animiraj { animation: none; }
|
||||
|
||||
/* korisnikova preferencija animacije: body[data-animacija] nadjačava podrazumevano.
|
||||
Kad korisnik odabere stil, animiraju se i redovi tabela i mobilne kartice. */
|
||||
[data-animacija="bez"] .animiraj,
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
</span>
|
||||
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
|
||||
{{template "status-badge-detalji" .Nalog.Status}}
|
||||
<form method="post" action="/servis/{{.Nalog.ID}}/status" style="display:flex;align-items:center;gap:6px;" data-full-reload>
|
||||
<form method="post" action="/servis/{{.Nalog.ID}}/status" style="display:flex;align-items:center;gap:6px;" data-refresh="#glavni-sadrzaj">
|
||||
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||
<select name="status" style="font-size:13px;padding:4px 8px;border-radius:6px;border:0.5px solid var(--ivica);background:var(--kartica-pozadina);color:var(--tekst-glavni);cursor:pointer;">
|
||||
{{range .SviStatusi}}
|
||||
@@ -168,7 +168,7 @@
|
||||
|
||||
<!-- forma za dodavanje rada iz cenovnika usluga -->
|
||||
{{if index .Dozvole "servis.izmeni"}}
|
||||
<form method="POST" action="/servis/{{.Nalog.ID}}/radovi" style="margin-bottom:16px;" data-refresh="#radovi-spisak">
|
||||
<form method="POST" action="/servis/{{.Nalog.ID}}/radovi" style="margin-bottom:16px;" data-refresh="#glavni-sadrzaj">
|
||||
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||
<div style="display:grid;grid-template-columns:1fr 80px 120px auto;gap:10px;align-items:flex-end;">
|
||||
<div>
|
||||
@@ -217,7 +217,7 @@
|
||||
<td style="padding:9px 10px;text-align:right;font-size:14px;font-weight:500;color:var(--tekst-glavni);">{{dinari .Ukupno}} din</td>
|
||||
<td style="padding:9px 10px;text-align:center;">
|
||||
{{if index $.Dozvole "servis.izmeni"}}
|
||||
<form method="POST" action="/servis/{{$.Nalog.ID}}/radovi/{{.ID}}/obrisi" style="display:inline;" data-refresh="#radovi-spisak">
|
||||
<form method="POST" action="/servis/{{$.Nalog.ID}}/radovi/{{.ID}}/obrisi" style="display:inline;" data-refresh="#glavni-sadrzaj">
|
||||
<input type="hidden" name="_csrf" value="{{$.CsrfToken}}">
|
||||
<button type="submit"
|
||||
data-potvrda="Ukloniti rad '{{.Naziv}}'?"
|
||||
@@ -247,21 +247,27 @@
|
||||
|
||||
<!-- forma za dodavanje dela -->
|
||||
{{if index .Dozvole "servis.izmeni"}}
|
||||
<form method="POST" action="/servis/{{.Nalog.ID}}/delovi" style="margin-bottom:16px;" data-refresh="#delovi-spisak">
|
||||
<form method="POST" action="/servis/{{.Nalog.ID}}/delovi" style="margin-bottom:16px;" data-refresh="#glavni-sadrzaj">
|
||||
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||
<div style="display:grid;grid-template-columns:1fr 80px 120px auto;gap:10px;align-items:flex-end;">
|
||||
<div style="margin-bottom:10px;">
|
||||
<label style="font-size:12px;color:var(--tekst-sporedni);display:block;margin-bottom:4px;">Kategorija</label>
|
||||
<select id="deo-kategorija" style="width:100%;"></select>
|
||||
</div>
|
||||
<div style="display:grid;grid-template-columns:1fr 70px 80px 120px auto;gap:10px;align-items:flex-end;">
|
||||
<div>
|
||||
<label style="font-size:12px;color:var(--tekst-sporedni);display:block;margin-bottom:4px;">Artikal</label>
|
||||
<input type="text" id="deo-artikal-pretraga" list="artikli-datalist" autocomplete="off"
|
||||
placeholder="Pretraži artikal..." style="width:100%;">
|
||||
<datalist id="artikli-datalist">
|
||||
{{range .Artikli}}<option value="{{.Naziv}} ({{.Kolicina}} kom)" data-id="{{.ID}}" data-cena="{{.ProdajnaCena}}">{{end}}
|
||||
</datalist>
|
||||
<datalist id="artikli-datalist"></datalist>
|
||||
<input type="hidden" name="artikal_id" id="deo-artikal">
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size:12px;color:var(--tekst-sporedni);display:block;margin-bottom:4px;">U magacinu</label>
|
||||
<div id="deo-umagacinu" style="font-size:14px;font-weight:500;color:var(--tekst-glavni);text-align:center;padding:8px 0;">—</div>
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size:12px;color:var(--tekst-sporedni);display:block;margin-bottom:4px;">Kol.</label>
|
||||
<input type="number" name="kolicina" min="1" value="1" style="width:100%;" required>
|
||||
<input type="number" name="kolicina" id="deo-kol" min="1" value="1" style="width:100%;" required>
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size:12px;color:var(--tekst-sporedni);display:block;margin-bottom:4px;">Cena/kom (din)</label>
|
||||
@@ -296,7 +302,7 @@
|
||||
<td style="padding:9px 10px;text-align:right;font-size:14px;font-weight:500;color:var(--tekst-glavni);">{{dinari .Ukupno}} din</td>
|
||||
<td style="padding:9px 10px;text-align:center;">
|
||||
{{if index $.Dozvole "servis.izmeni"}}
|
||||
<form method="POST" action="/servis/{{$.Nalog.ID}}/delovi/{{.ID}}/obrisi" style="display:inline;" data-refresh="#delovi-spisak">
|
||||
<form method="POST" action="/servis/{{$.Nalog.ID}}/delovi/{{.ID}}/obrisi" style="display:inline;" data-refresh="#glavni-sadrzaj">
|
||||
<input type="hidden" name="_csrf" value="{{$.CsrfToken}}">
|
||||
<button type="submit"
|
||||
data-potvrda="Ukloniti deo '{{.ArtikalNaziv}}'?"
|
||||
@@ -368,6 +374,70 @@ function vežiCombobox(pretragaId, datalistId, hiddenId, cenaId) {
|
||||
pretraga.addEventListener('change', uskladi);
|
||||
}
|
||||
vežiCombobox('rad-usluga-pretraga', 'usluge-datalist', 'rad-usluga', 'rad-cena');
|
||||
vežiCombobox('deo-artikal-pretraga', 'artikli-datalist', 'deo-artikal', 'deo-cena');
|
||||
|
||||
// Delovi: kategorija filter + ograničenje količine na magacinsko stanje.
|
||||
// Datalist se gradi iz JSON-a po izabranoj kategoriji; pri izboru artikla
|
||||
// postavljamo cenu, max količine = stanje, i prikaz dostupne količine.
|
||||
(function () {
|
||||
var ARTIKLI = {{json .Artikli}} || [];
|
||||
var kategSel = document.getElementById('deo-kategorija');
|
||||
var pretraga = document.getElementById('deo-artikal-pretraga');
|
||||
var datalist = document.getElementById('artikli-datalist');
|
||||
var hidden = document.getElementById('deo-artikal');
|
||||
var cena = document.getElementById('deo-cena');
|
||||
var umagacinu = document.getElementById('deo-umagacinu');
|
||||
if (!pretraga || !datalist) return;
|
||||
|
||||
function labela(a) { return a.Naziv + ' (' + a.Kolicina + ' kom)'; }
|
||||
|
||||
if (kategSel) {
|
||||
var kategs = [];
|
||||
ARTIKLI.forEach(function (a) {
|
||||
var k = a.KategorijaNaziv || '';
|
||||
if (k && kategs.indexOf(k) < 0) kategs.push(k);
|
||||
});
|
||||
kategs.sort();
|
||||
kategSel.innerHTML = '<option value="">Sve kategorije</option>' +
|
||||
kategs.map(function (k) { return '<option value="' + k + '">' + k + '</option>'; }).join('');
|
||||
}
|
||||
|
||||
function popuniDatalist() {
|
||||
var izabrana = kategSel ? kategSel.value : '';
|
||||
datalist.innerHTML = ARTIKLI
|
||||
.filter(function (a) { return !izabrana || (a.KategorijaNaziv || '') === izabrana; })
|
||||
.map(function (a) {
|
||||
return '<option value="' + labela(a) + '" data-id="' + a.ID +
|
||||
'" data-cena="' + a.ProdajnaCena + '" data-stanje="' + a.Kolicina + '">';
|
||||
}).join('');
|
||||
}
|
||||
popuniDatalist();
|
||||
|
||||
if (kategSel) kategSel.addEventListener('change', function () {
|
||||
popuniDatalist();
|
||||
pretraga.value = '';
|
||||
hidden.value = '';
|
||||
if (umagacinu) umagacinu.textContent = '—';
|
||||
});
|
||||
|
||||
function uskladi() {
|
||||
var opt = null, opts = datalist.options;
|
||||
for (var i = 0; i < opts.length; i++) {
|
||||
if (opts[i].value === pretraga.value) { opt = opts[i]; break; }
|
||||
}
|
||||
if (opt) {
|
||||
hidden.value = opt.dataset.id || '';
|
||||
if (cena) {
|
||||
var c = parseFloat(opt.dataset.cena);
|
||||
cena.value = (!isNaN(c) && c > 0) ? c.toFixed(2) : '0';
|
||||
}
|
||||
if (umagacinu) umagacinu.textContent = (parseInt(opt.dataset.stanje, 10) || 0);
|
||||
} else {
|
||||
hidden.value = '';
|
||||
if (umagacinu) umagacinu.textContent = '—';
|
||||
}
|
||||
}
|
||||
pretraga.addEventListener('input', uskladi);
|
||||
pretraga.addEventListener('change', uskladi);
|
||||
})();
|
||||
</script>
|
||||
{{end}}
|
||||
|
||||
@@ -393,8 +393,20 @@
|
||||
var doc = new DOMParser().parseFromString(html, 'text/html');
|
||||
var novi = doc.querySelector(ciljSel);
|
||||
var stari = document.querySelector(ciljSel);
|
||||
if (novi && stari) stari.replaceWith(novi);
|
||||
f.reset();
|
||||
if (novi && stari) {
|
||||
// bez-anim spречava treptaj re-animacije kartica pri zameni
|
||||
novi.classList.add('bez-anim');
|
||||
stari.replaceWith(novi);
|
||||
void novi.offsetHeight;
|
||||
novi.classList.remove('bez-anim');
|
||||
// replaceWith ne pokreće <script> iz fragmenta — re-izvrši ih
|
||||
// ručno (npr. da se combobox/pretrage ponovo povežu)
|
||||
novi.querySelectorAll('script').forEach(function (old) {
|
||||
var s = document.createElement('script');
|
||||
if (old.src) s.src = old.src; else s.textContent = old.textContent;
|
||||
old.parentNode.replaceChild(s, old);
|
||||
});
|
||||
}
|
||||
if (btn) btn.disabled = false;
|
||||
})
|
||||
.catch(function () { location.href = res.url; });
|
||||
|
||||
Reference in New Issue
Block a user