Servis: custom dropdown za usluge i artikle (naziv+cena), live preračun na javnoj stranici
This commit is contained in:
@@ -11,6 +11,35 @@
|
||||
.detalji-kartica:nth-child(5) { animation-delay: 0.36s; }
|
||||
/* bez animacija na detaljima — smetaju pri dodavanju/brisanju stavki */
|
||||
#sadrzaj .kartica.animiraj { animation: none; }
|
||||
/* custom dropdown za usluge i artikle */
|
||||
.cust-sel { position: relative; }
|
||||
.cust-sel-btn {
|
||||
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
||||
height: 38px; padding: 0 10px; box-sizing: border-box; cursor: pointer;
|
||||
border: 0.5px solid var(--ivica) !important; border-radius: 8px; font-size: 14px;
|
||||
background: var(--kartica) !important; color: var(--tekst-glavni) !important;
|
||||
user-select: none;
|
||||
}
|
||||
.cust-sel-btn:focus { border-color: #3b82f6 !important; outline: none; }
|
||||
.cust-sel-tekst { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.cust-sel-chevron { font-size: 10px; color: var(--tekst-sporedni); flex-shrink: 0; transition: transform 0.15s; }
|
||||
.cust-sel.otvoren .cust-sel-chevron { transform: rotate(180deg); }
|
||||
.cust-sel-lista {
|
||||
display: none; position: absolute; top: calc(100% + 3px); left: 0; right: 0; z-index: 500;
|
||||
background: var(--kartica); border: 0.5px solid var(--ivica); border-radius: 10px;
|
||||
max-height: 260px; overflow-y: auto; box-shadow: 0 8px 30px rgba(0,0,0,0.4);
|
||||
}
|
||||
.cust-sel.otvoren .cust-sel-lista { display: block; }
|
||||
.cso {
|
||||
display: flex; align-items: center; justify-content: space-between; gap: 12px;
|
||||
padding: 8px 12px; cursor: pointer; border-bottom: 0.5px solid var(--ivica); font-size: 13px;
|
||||
}
|
||||
.cso:last-child { border-bottom: none; }
|
||||
.cso:hover, .cso.izabran { background: rgba(255,255,255,0.06); }
|
||||
.cso-naziv { color: var(--tekst-glavni); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.cso-cena { color: #f59e0b; font-weight: 600; white-space: nowrap; flex-shrink: 0; }
|
||||
.cso-prazno { color: var(--tekst-sporedni); }
|
||||
.kartica.cust-aktivan { position: relative; z-index: 100; }
|
||||
</style>
|
||||
{{end}}
|
||||
|
||||
@@ -467,10 +496,17 @@
|
||||
<div class="detalji-forma-rad">
|
||||
<div>
|
||||
<label style="font-size:12px;color:var(--tekst-sporedni);display:block;margin-bottom:4px;">Usluga</label>
|
||||
<select name="usluga_id" id="rad-usluga" style="width:100%;" required>
|
||||
<option value="">— odaberi uslugu —</option>
|
||||
{{range .Usluge}}{{if not (index $.KorisceneUsluge .ID)}}<option value="{{.ID}}" data-cena="{{.Cena}}">{{.Naziv}} — {{dinari .Cena}} din</option>{{end}}{{end}}
|
||||
</select>
|
||||
<div class="cust-sel" data-sel="rad-usluga">
|
||||
<div class="cust-sel-btn" tabindex="0">
|
||||
<span class="cust-sel-tekst cso-prazno">— odaberi uslugu —</span>
|
||||
<span class="cust-sel-chevron">▾</span>
|
||||
</div>
|
||||
<div class="cust-sel-lista">
|
||||
<div class="cso" data-val="" data-cena="0"><span class="cso-prazno">— odaberi uslugu —</span></div>
|
||||
{{range .Usluge}}{{if not (index $.KorisceneUsluge .ID)}}<div class="cso" data-val="{{.ID}}" data-cena="{{.Cena}}"><span class="cso-naziv">{{.Naziv}}</span><span class="cso-cena">{{dinari .Cena}} din</span></div>{{end}}{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="usluga_id" id="rad-usluga">
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size:12px;color:var(--tekst-sporedni);display:block;margin-bottom:4px;">Kol.</label>
|
||||
@@ -581,9 +617,14 @@
|
||||
<div class="detalji-forma-deo">
|
||||
<div>
|
||||
<label style="font-size:12px;color:var(--tekst-sporedni);display:block;margin-bottom:4px;">Artikal</label>
|
||||
<select name="artikal_id" id="deo-artikal" style="width:100%;" required>
|
||||
<option value="">— odaberi artikal —</option>
|
||||
</select>
|
||||
<div class="cust-sel" id="deo-artikal-cust" data-sel="deo-artikal">
|
||||
<div class="cust-sel-btn" tabindex="0">
|
||||
<span class="cust-sel-tekst cso-prazno">— odaberi artikal —</span>
|
||||
<span class="cust-sel-chevron">▾</span>
|
||||
</div>
|
||||
<div class="cust-sel-lista"></div>
|
||||
</div>
|
||||
<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>
|
||||
@@ -692,27 +733,30 @@
|
||||
</div>
|
||||
</div>
|
||||
<script>(function(){
|
||||
window._dinFmt=function(v){var s=Math.round((v||0)*100);var dec=String(s%100).padStart(2,'0');var cel=String(Math.floor(s/100));var r='';for(var i=0;i<cel.length;i++){if(i>0&&(cel.length-i)%3===0)r+='.';r+=cel[i];}return r+','+dec;}
|
||||
var k=document.getElementById('deo-kategorija');
|
||||
var a=document.getElementById('deo-artikal');
|
||||
var u=document.getElementById('deo-umagacinu');
|
||||
var l=document.getElementById('deo-kol');
|
||||
var c=document.getElementById('deo-cena');
|
||||
var st=0;
|
||||
function osv(){if(!l||!a)return;var o=a.options[a.selectedIndex];var s=o?parseInt(o.getAttribute('data-stanje'),10):0;st=isNaN(s)?0:s;if(u)u.textContent=a.value?st:'—';if(l)l.classList.toggle('prekoraceno',a.value&&st===0)}
|
||||
function osv(){if(!l||!a)return;var s=parseInt(a.getAttribute('data-stanje'),10);var st=isNaN(s)?0:s;if(u)u.textContent=a.value?st:'\u2014';if(l)l.classList.toggle('prekoraceno',!!a.value&&st===0)}
|
||||
function pop(){
|
||||
if(!a)return;
|
||||
var cust=document.getElementById('deo-artikal-cust');
|
||||
if(!cust)return;
|
||||
var lista=cust.querySelector('.cust-sel-lista');
|
||||
var ka=k?k.value:'';
|
||||
a.innerHTML='<option value="">— odaberi artikal —</option>';
|
||||
var aa=window._ntechArtikli||[];
|
||||
aa.forEach(function(x){if(!ka||(x.KategorijaNaziv||'')===ka)a.innerHTML+='<option value="'+x.ID+'" data-cena="'+x.ProdajnaCena+'" data-stanje="'+(x.Kolicina||0)+'">'+x.Naziv+' — '+parseFloat(x.ProdajnaCena||0).toFixed(2)+' din</option>'})
|
||||
}
|
||||
if(k){var aa2=window._ntechArtikli||[];var kgs=[];aa2.forEach(function(x){var n=x.KategorijaNaziv||'';if(n&&kgs.indexOf(n)===-1)kgs.push(n)})
|
||||
k.innerHTML='<option value="">— sve kategorije —</option>'+kgs.map(function(x){return'<option value="'+x+'">'+x+'</option>'}).join('');k.onchange=pop}
|
||||
var aa=window._ntechArtikli||[];
|
||||
var html='<div class="cso" data-val="" data-cena="0" data-stanje="0"><span class="cso-prazno">\u2014 odaberi artikal \u2014</span></div>';
|
||||
aa.forEach(function(x){if(!ka||(x.KategorijaNaziv||'')===ka)html+='<div class="cso" data-val="'+x.ID+'" data-cena="'+x.ProdajnaCena+'" data-stanje="'+(x.Kolicina||0)+'"><span class="cso-naziv">'+x.Naziv+'</span><span class="cso-cena">'+window._dinFmt(x.ProdajnaCena)+' din</span></div>';});
|
||||
if(lista)lista.innerHTML=html;
|
||||
}
|
||||
if(k){var aa2=window._ntechArtikli||[];var kgs=[];aa2.forEach(function(x){var n=x.KategorijaNaziv||'';if(n&&kgs.indexOf(n)===-1)kgs.push(n)})
|
||||
k.innerHTML='<option value="">\u2014 sve kategorije \u2014</option>'+kgs.map(function(x){return'<option value="'+x+'">'+x+'</option>'}).join('');k.onchange=pop}
|
||||
pop();
|
||||
if(a)a.onchange=function(){var o=a.options[a.selectedIndex];if(!o)return;var v=parseFloat(o.getAttribute('data-cena'));if(c)c.value=(!isNaN(v)&&v>0)?v.toFixed(2):'0';osv()}
|
||||
if(a)a.addEventListener('change',function(){var v=parseFloat(a.getAttribute('data-cena'));if(c)c.value=(!isNaN(v)&&v>0)?v.toFixed(2):'0';osv()});
|
||||
if(l)l.oninput=osv;
|
||||
var fd=document.querySelector('form[action$=\"/delovi\"]');
|
||||
if(fd)fd.addEventListener('reset',function(){if(u){u.textContent='—';u.classList.remove('prekoraceno')}st=0;if(l)l.classList.remove('prekoraceno')})
|
||||
if(fd)fd.addEventListener('reset',function(){if(u){u.textContent='\u2014';u.classList.remove('prekoraceno')}if(l)l.classList.remove('prekoraceno')})
|
||||
})();</script>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -811,15 +855,63 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Radovi: pri izboru usluge iz <select>-a popuni predloženu cenu (data-cena).
|
||||
// Delegacija na document — kartica radova se osvežava AJAX-om (data-refresh),
|
||||
// pa listener vezan za sam <select> ne bi preživeo zamenu elementa.
|
||||
// Custom dropdown: toggle, izbor opcije, zatvaranje klikom van.
|
||||
function _custZatvoriSve() {
|
||||
document.querySelectorAll('.cust-sel.otvoren').forEach(function (el) {
|
||||
el.classList.remove('otvoren');
|
||||
var k = el.closest('.kartica');
|
||||
if (k) k.classList.remove('cust-aktivan');
|
||||
});
|
||||
}
|
||||
document.addEventListener('click', function (e) {
|
||||
var btn = e.target.closest('.cust-sel-btn');
|
||||
if (btn) {
|
||||
var cust = btn.closest('.cust-sel');
|
||||
var bio = cust.classList.contains('otvoren');
|
||||
_custZatvoriSve();
|
||||
if (!bio) {
|
||||
cust.classList.add('otvoren');
|
||||
var k = cust.closest('.kartica');
|
||||
if (k) k.classList.add('cust-aktivan');
|
||||
}
|
||||
return;
|
||||
}
|
||||
var opt = e.target.closest('.cso');
|
||||
if (opt) {
|
||||
var cust = opt.closest('.cust-sel');
|
||||
var selId = cust.getAttribute('data-sel');
|
||||
var inp = selId ? document.getElementById(selId) : null;
|
||||
var val = opt.getAttribute('data-val') || '';
|
||||
var btnEl = cust.querySelector('.cust-sel-btn');
|
||||
var btnTekst = btnEl ? btnEl.querySelector('.cust-sel-tekst') : null;
|
||||
var naziv = opt.querySelector('.cso-naziv');
|
||||
var prazan = opt.querySelector('.cso-prazno');
|
||||
if (btnTekst) {
|
||||
btnTekst.textContent = naziv ? naziv.textContent : (prazan ? prazan.textContent : '');
|
||||
btnTekst.className = 'cust-sel-tekst' + (val ? '' : ' cso-prazno');
|
||||
}
|
||||
cust.querySelectorAll('.cso').forEach(function (o) { o.classList.remove('izabran'); });
|
||||
if (val) opt.classList.add('izabran');
|
||||
if (inp) {
|
||||
inp.value = val;
|
||||
inp.setAttribute('data-cena', opt.getAttribute('data-cena') || '0');
|
||||
inp.setAttribute('data-stanje', opt.getAttribute('data-stanje') || '0');
|
||||
inp.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
cust.classList.remove('otvoren');
|
||||
var k2 = cust.closest('.kartica');
|
||||
if (k2) k2.classList.remove('cust-aktivan');
|
||||
return;
|
||||
}
|
||||
_custZatvoriSve();
|
||||
});
|
||||
|
||||
// Radovi: pri izboru usluge (custom dropdown → hidden input) popuni predloženu cenu.
|
||||
document.addEventListener('change', function (e) {
|
||||
if (!e.target || e.target.id !== 'rad-usluga') return;
|
||||
var cena = document.getElementById('rad-cena');
|
||||
if (!cena) return;
|
||||
var opt = e.target.options[e.target.selectedIndex];
|
||||
var c = parseFloat(opt.getAttribute('data-cena'));
|
||||
var c = parseFloat(e.target.getAttribute('data-cena'));
|
||||
cena.value = (!isNaN(c) && c > 0) ? c.toFixed(2) : '0';
|
||||
});
|
||||
|
||||
@@ -862,12 +954,16 @@ document.addEventListener('change', function (e) {
|
||||
function popuniArtikle() {
|
||||
ARTIKLI = window._ntechArtikli || [];
|
||||
var izabrana = kategSel ? kategSel.value : '';
|
||||
artikalSel.innerHTML = '<option value="">— odaberi artikal —</option>' +
|
||||
ARTIKLI.filter(function (a) { return !izabrana || (a.KategorijaNaziv || '') === izabrana; })
|
||||
.map(function (a) {
|
||||
return '<option value="' + a.ID + '" data-cena="' + a.ProdajnaCena +
|
||||
'" data-stanje="' + a.Kolicina + '">' + a.Naziv + ' — ' + parseFloat(a.ProdajnaCena||0).toFixed(2) + ' din</option>';
|
||||
}).join('');
|
||||
var cust = document.getElementById('deo-artikal-cust');
|
||||
var lista = cust ? cust.querySelector('.cust-sel-lista') : null;
|
||||
var html = '<div class="cso" data-val="" data-cena="0" data-stanje="0"><span class="cso-prazno">— odaberi artikal —</span></div>';
|
||||
ARTIKLI.filter(function (a) { return !izabrana || (a.KategorijaNaziv || '') === izabrana; })
|
||||
.forEach(function (a) {
|
||||
html += '<div class="cso" data-val="' + a.ID + '" data-cena="' + a.ProdajnaCena +
|
||||
'" data-stanje="' + (a.Kolicina || 0) + '"><span class="cso-naziv">' + a.Naziv +
|
||||
'</span><span class="cso-cena">' + window._dinFmt(a.ProdajnaCena) + ' din</span></div>';
|
||||
});
|
||||
if (lista) lista.innerHTML = html;
|
||||
if (umagacinu) umagacinu.textContent = '—';
|
||||
}
|
||||
window._ntechPopuniArtikle = popuniArtikle;
|
||||
@@ -875,10 +971,9 @@ document.addEventListener('change', function (e) {
|
||||
if (kategSel) kategSel.addEventListener('change', popuniArtikle);
|
||||
|
||||
artikalSel.addEventListener('change', function () {
|
||||
var opt = artikalSel.options[artikalSel.selectedIndex];
|
||||
var c = parseFloat(opt.getAttribute('data-cena'));
|
||||
var c = parseFloat(artikalSel.getAttribute('data-cena'));
|
||||
if (cena) cena.value = (!isNaN(c) && c > 0) ? c.toFixed(2) : '0';
|
||||
var st = parseInt(opt.getAttribute('data-stanje'), 10);
|
||||
var st = parseInt(artikalSel.getAttribute('data-stanje'), 10);
|
||||
stanjeIzabrano = isNaN(st) ? 0 : st;
|
||||
if (umagacinu) umagacinu.textContent = artikalSel.value ? stanjeIzabrano : '—';
|
||||
osveziPrekoracenje();
|
||||
|
||||
@@ -243,7 +243,7 @@
|
||||
{{if .ImaPredlog}}
|
||||
<div class="cena-blok" style="margin-top:8px;padding:10px 14px;display:flex;justify-content:space-between;align-items:center;background:#0f172a;border:1px solid #f59e0b;border-radius:8px;">
|
||||
<span class="cena-labela" style="margin:0;color:#fbbf24;">Sa predlogom</span>
|
||||
<span class="cena-vrednost" style="font-size:16px;font-weight:700;color:#fbbf24;">{{dinari .UkupnoSaPredlogom}} din</span>
|
||||
<span class="cena-vrednost" id="sa-predlogom-zbir" data-base="{{printf "%.2f" .UkupnoSve}}" style="font-size:16px;font-weight:700;color:#fbbf24;">{{dinari .UkupnoSaPredlogom}} din</span>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
@@ -267,7 +267,7 @@
|
||||
{{range .PredlozeniRadovi}}
|
||||
<label style="display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 0;border-bottom:0.5px solid #1e293b;cursor:pointer;">
|
||||
<span style="display:flex;align-items:center;gap:10px;flex:1;">
|
||||
<input type="checkbox" name="rad_id" value="{{.ID}}" checked
|
||||
<input type="checkbox" name="rad_id" value="{{.ID}}" checked data-iznos="{{printf "%.2f" .Ukupno}}"
|
||||
style="width:18px;height:18px;accent-color:#f59e0b;flex-shrink:0;">
|
||||
<span style="font-size:13px;color:#e2e8f0;">{{.Naziv}}{{if gt .Kolicina 1.0}} × {{printf "%.0f" .Kolicina}}{{end}}</span>
|
||||
</span>
|
||||
@@ -281,7 +281,7 @@
|
||||
{{range .PredlozeniDelovi}}
|
||||
<label style="display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 0;border-bottom:0.5px solid #1e293b;cursor:pointer;">
|
||||
<span style="display:flex;align-items:center;gap:10px;flex:1;">
|
||||
<input type="checkbox" name="artikal_id" value="{{.ArtikalID}}" checked
|
||||
<input type="checkbox" name="artikal_id" value="{{.ArtikalID}}" checked data-iznos="{{printf "%.2f" .Ukupno}}"
|
||||
style="width:18px;height:18px;accent-color:#f59e0b;flex-shrink:0;">
|
||||
<span style="font-size:13px;color:#e2e8f0;">{{.ArtikalNaziv}}{{if gt .Kolicina 1}} × {{.Kolicina}}{{end}}</span>
|
||||
</span>
|
||||
@@ -292,7 +292,7 @@
|
||||
|
||||
<div class="cena-blok predlog-ukupno" style="margin-top:12px;display:flex;justify-content:space-between;align-items:center;">
|
||||
<span class="cena-labela" style="margin:0;">Predlog ukupno</span>
|
||||
<span class="cena-vrednost">{{dinari .UkupnoPredlog}} din</span>
|
||||
<span class="cena-vrednost" id="predlog-zbir">{{dinari .UkupnoPredlog}} din</span>
|
||||
</div>
|
||||
|
||||
<textarea name="komentar" placeholder="Poruka serviseru (opciono)..." rows="2"
|
||||
@@ -320,5 +320,37 @@
|
||||
{{end}}
|
||||
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
var zbir = document.getElementById('predlog-zbir');
|
||||
if (!zbir) return;
|
||||
var forma = zbir.closest('form');
|
||||
if (!forma) return;
|
||||
function fmt(v) {
|
||||
var s = Math.round(v * 100);
|
||||
var dec = String(s % 100).padStart(2, '0');
|
||||
var cel = String(Math.floor(s / 100));
|
||||
var r = '';
|
||||
for (var i = 0; i < cel.length; i++) {
|
||||
if (i > 0 && (cel.length - i) % 3 === 0) r += '.';
|
||||
r += cel[i];
|
||||
}
|
||||
return r + ',' + dec + ' din';
|
||||
}
|
||||
var saPredlogom = document.getElementById('sa-predlogom-zbir');
|
||||
var bazaUkupno = saPredlogom ? parseFloat(saPredlogom.getAttribute('data-base')) || 0 : 0;
|
||||
function preracunaj() {
|
||||
var suma = 0;
|
||||
forma.querySelectorAll('input[type="checkbox"][data-iznos]').forEach(function (cb) {
|
||||
if (cb.checked) suma += parseFloat(cb.getAttribute('data-iznos')) || 0;
|
||||
});
|
||||
zbir.textContent = fmt(suma);
|
||||
if (saPredlogom) saPredlogom.textContent = fmt(bazaUkupno + suma);
|
||||
}
|
||||
forma.addEventListener('change', function (e) {
|
||||
if (e.target && e.target.type === 'checkbox') preracunaj();
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user