Dodavanje modula dobavljača i nabavki

This commit is contained in:
2026-06-01 21:42:43 +02:00
parent b9d960a4a0
commit bdf1069fbd
20 changed files with 2148 additions and 6 deletions
+118
View File
@@ -0,0 +1,118 @@
{{template "base" .}}
{{define "naslov"}}{{if .Izmena}}Izmeni dobavljača{{else}}Novi dobavljač{{end}} — NTech{{end}}
{{define "dodatni-css"}}
<style>
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
20% { transform: translateX(-6px); }
40% { transform: translateX(6px); }
60% { transform: translateX(-4px); }
80% { transform: translateX(4px); }
}
.forma-kartica {
animation: fadeInUp 0.3s ease forwards;
}
.greska-animacija {
animation: shake 0.4s ease;
}
</style>
{{end}}
{{define "sadrzaj"}}
<div style="width:100%;">
<!-- nazad dugme -->
<a href="/dobavljaci"
style="display:inline-flex;align-items:center;gap:6px;font-size:13px;color:var(--tekst-sporedni);text-decoration:none;margin-bottom:20px;transition:color 0.2s;"
onmouseover="this.style.color='var(--tekst-glavni)'" onmouseout="this.style.color='var(--tekst-sporedni)'">
<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"><polyline points="15 18 9 12 15 6"/></svg>
Nazad na dobavljače
</a>
<div class="kartica forma-kartica">
<div style="margin-bottom:20px;padding-bottom:14px;border-bottom:0.5px solid var(--ivica);">
<span style="font-size:16px;font-weight:500;color:var(--tekst-glavni);">
{{if .Izmena}}Izmeni dobavljača{{else}}Novi dobavljač{{end}}
</span>
</div>
{{if .Greska}}
<div class="greska-animacija"
style="background:var(--kartica);border:0.5px solid #dc2626;border-radius:8px;padding:10px 14px;margin-bottom:16px;font-size:13px;color:#dc2626;">
{{.Greska}}
</div>
{{end}}
<form method="POST" action="{{if .Izmena}}/dobavljaci/izmeni/{{.Dobavljac.ID}}{{else}}/dobavljaci/novi{{end}}">
<div style="display:flex;flex-direction:column;gap:14px;">
<!-- naziv -->
<div>
<label style="font-size:13px;color:var(--tekst-sporedni);display:block;margin-bottom:6px;">
Naziv <span style="color:#dc2626;">*</span>
</label>
<input type="text" name="naziv" value="{{.Dobavljac.Naziv}}"
placeholder="npr. TechDistrib d.o.o."
style="width:100%;">
</div>
<!-- kontakt osoba i telefon -->
<div class="forma-grid-2" style="display:grid;grid-template-columns:1fr 1fr;gap:12px;">
<div>
<label style="font-size:13px;color:var(--tekst-sporedni);display:block;margin-bottom:6px;">Kontakt osoba</label>
<input type="text" name="kontakt_osoba" value="{{.Dobavljac.KontaktOsoba}}"
placeholder="npr. Marko Petrović"
style="width:100%;">
</div>
<div>
<label style="font-size:13px;color:var(--tekst-sporedni);display:block;margin-bottom:6px;">Telefon</label>
<input type="text" name="telefon" value="{{.Dobavljac.Telefon}}"
placeholder="npr. 011 123 4567"
style="width:100%;">
</div>
</div>
<!-- e-pošta -->
<div>
<label style="font-size:13px;color:var(--tekst-sporedni);display:block;margin-bottom:6px;">E-pošta</label>
<input type="text" name="email" value="{{.Dobavljac.Email}}"
placeholder="npr. nabavka@techdistrib.rs"
style="width:100%;">
</div>
<!-- napomena -->
<div>
<label style="font-size:13px;color:var(--tekst-sporedni);display:block;margin-bottom:6px;">Napomena</label>
<textarea name="napomena" rows="3"
placeholder="Interna napomena o dobavljaču..."
style="width:100%;resize:vertical;">{{.Dobavljac.Napomena}}</textarea>
</div>
<!-- dugmad -->
<div style="display:flex;justify-content:flex-end;gap:10px;margin-top:6px;">
<a href="/dobavljaci"
style="padding:9px 20px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;color:var(--tekst-sporedni);text-decoration:none;transition:background 0.2s;"
onmouseover="this.style.background='var(--pozadina)'" onmouseout="this.style.background=''">
Odustani
</a>
<button type="submit"
style="padding:9px 20px;background:var(--sb-akcent);color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:opacity 0.2s;"
onmouseover="this.style.opacity='0.85'" onmouseout="this.style.opacity='1'">
{{if .Izmena}}Sačuvaj izmene{{else}}Dodaj dobavljača{{end}}
</button>
</div>
</div>
</form>
</div>
</div>
{{end}}
+198
View File
@@ -0,0 +1,198 @@
{{template "base" .}}
{{define "naslov"}}Dobavljači — NTech{{end}}
{{define "dodatni-css"}}
<style>
@keyframes slideDown {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.poruka-animacija {
animation: slideDown 0.3s ease forwards;
}
.dobavljaci-tabela tbody tr {
animation: fadeInUp 0.25s ease forwards;
opacity: 0;
}
/* stagger — svaki red se pojavljuje malo kasnije */
.dobavljaci-tabela tbody tr:nth-child(1) { animation-delay: 0.04s; }
.dobavljaci-tabela tbody tr:nth-child(2) { animation-delay: 0.08s; }
.dobavljaci-tabela tbody tr:nth-child(3) { animation-delay: 0.12s; }
.dobavljaci-tabela tbody tr:nth-child(4) { animation-delay: 0.16s; }
.dobavljaci-tabela tbody tr:nth-child(5) { animation-delay: 0.20s; }
.dobavljaci-tabela tbody tr:nth-child(6) { animation-delay: 0.24s; }
.dobavljaci-tabela tbody tr:nth-child(7) { animation-delay: 0.28s; }
.dobavljaci-tabela tbody tr:nth-child(8) { animation-delay: 0.32s; }
.dobavljaci-tabela tbody tr:nth-child(9) { animation-delay: 0.36s; }
.dobavljaci-tabela tbody tr:nth-child(10) { animation-delay: 0.40s; }
.dobavljaci-kartice {
display: none;
flex-direction: column;
gap: 12px;
}
.dobavljac-kartica {
animation: fadeInUp 0.25s ease forwards;
opacity: 0;
}
.dobavljac-kartica:nth-child(1) { animation-delay: 0.04s; }
.dobavljac-kartica:nth-child(2) { animation-delay: 0.10s; }
.dobavljac-kartica:nth-child(3) { animation-delay: 0.16s; }
.dobavljac-kartica:nth-child(4) { animation-delay: 0.22s; }
.dobavljac-kartica:nth-child(5) { animation-delay: 0.28s; }
@media (max-width: 768px) {
.dobavljaci-tabela { display: none; }
.dobavljaci-kartice { display: flex; }
}
</style>
{{end}}
{{define "sadrzaj"}}
<div style="display:flex;flex-direction:column;gap:16px;">
{{if .Sacuvano}}
<div class="poruka-uspeh poruka-animacija">Dobavljač je uspešno sačuvan.</div>
{{end}}
{{if .Obrisan}}
<div class="poruka-uspeh poruka-animacija">Dobavljač je uspešno obrisan.</div>
{{end}}
<!-- gornja traka: dugme + pretraga -->
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;">
<a href="/dobavljaci/novi"
style="padding:8px 16px;background:var(--sb-akcent);color:#fff;border-radius:8px;font-size:14px;font-weight:500;text-decoration:none;white-space:nowrap;transition:opacity 0.2s;"
onmouseover="this.style.opacity='0.85'" onmouseout="this.style.opacity='1'">
+ Novi dobavljač
</a>
<form method="GET" action="/dobavljaci" style="display:flex;gap:8px;flex:1;min-width:200px;">
<input type="text" name="pretraga" value="{{.Pretraga}}"
placeholder="Pretraži dobavljače..."
style="flex:1;">
<button type="submit"
style="padding:8px 16px;background:var(--sb-aktivan);color:#fff;border:none;border-radius:8px;font-size:14px;cursor:pointer;white-space:nowrap;transition:opacity 0.2s;"
onmouseover="this.style.opacity='0.85'" onmouseout="this.style.opacity='1'">
Traži
</button>
</form>
</div>
<!-- desktop tabela -->
<div class="kartica dobavljaci-tabela" style="padding:0;overflow:hidden;">
<div style="overflow-x:auto;">
<table style="width:100%;border-collapse:collapse;">
<thead>
<tr style="border-bottom:0.5px solid var(--ivica);">
<th style="padding:12px 16px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Naziv</th>
<th style="padding:12px 16px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Kontakt osoba</th>
<th style="padding:12px 16px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Telefon</th>
<th style="padding:12px 16px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">E-pošta</th>
<th style="padding:12px 16px;text-align:center;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Akcije</th>
</tr>
</thead>
<tbody>
{{range .Dobavljaci}}
<tr style="border-bottom:0.5px solid var(--ivica);transition:background 0.15s;"
onmouseover="this.style.background='var(--pozadina)'"
onmouseout="this.style.background=''">
<td style="padding:12px 16px;font-size:14px;font-weight:500;color:var(--tekst-glavni);">{{.Naziv}}</td>
<td style="padding:12px 16px;font-size:13px;color:var(--tekst-sporedni);">
{{if .KontaktOsoba}}{{.KontaktOsoba}}{{else}}—{{end}}
</td>
<td style="padding:12px 16px;font-size:13px;color:var(--tekst-sporedni);">
{{if .Telefon}}{{.Telefon}}{{else}}—{{end}}
</td>
<td style="padding:12px 16px;font-size:13px;color:var(--tekst-sporedni);">
{{if .Email}}{{.Email}}{{else}}—{{end}}
</td>
<td style="padding:12px 16px;text-align:center;">
<div style="display:flex;align-items:center;justify-content:center;gap:8px;">
<a href="/dobavljaci/izmeni/{{.ID}}"
style="padding:4px 10px;background:var(--sb-aktivan);color:#fff;border-radius:6px;font-size:12px;text-decoration:none;transition:opacity 0.2s;"
onmouseover="this.style.opacity='0.8'" onmouseout="this.style.opacity='1'">
Izmeni
</a>
<form method="POST" action="/dobavljaci/obrisi/{{.ID}}" style="display:inline;">
<button type="submit"
onclick="return confirm('Da li ste sigurni da želite da obrišete dobavljača {{.Naziv}}?')"
style="padding:4px 10px;background:#dc2626;color:#fff;border:none;border-radius:6px;font-size:12px;cursor:pointer;transition:opacity 0.2s;"
onmouseover="this.style.opacity='0.8'" onmouseout="this.style.opacity='1'">
Obriši
</button>
</form>
</div>
</td>
</tr>
{{else}}
<tr>
<td colspan="5" style="padding:32px;text-align:center;font-size:14px;color:var(--tekst-sporedni);">
Nema dobavljača. <a href="/dobavljaci/novi" style="color:var(--sb-akcent);">Dodaj prvog dobavljača.</a>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
</div>
<!-- mobilne kartice -->
<div class="dobavljaci-kartice">
{{range .Dobavljaci}}
<div class="kartica dobavljac-kartica">
<div style="display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:10px;">
<span style="font-size:15px;font-weight:500;color:var(--tekst-glavni);">{{.Naziv}}</span>
<div style="display:flex;gap:8px;flex-shrink:0;">
<a href="/dobavljaci/izmeni/{{.ID}}"
style="padding:4px 10px;background:var(--sb-aktivan);color:#fff;border-radius:6px;font-size:12px;text-decoration:none;">
Izmeni
</a>
<form method="POST" action="/dobavljaci/obrisi/{{.ID}}" style="display:inline;">
<button type="submit"
onclick="return confirm('Da li ste sigurni da želite da obrišete dobavljača {{.Naziv}}?')"
style="padding:4px 10px;background:#dc2626;color:#fff;border:none;border-radius:6px;font-size:12px;cursor:pointer;">
Obriši
</button>
</form>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:6px;">
{{if .KontaktOsoba}}
<div style="font-size:13px;color:var(--tekst-sporedni);">
<span style="color:var(--tekst-glavni);font-weight:500;">Kontakt:</span> {{.KontaktOsoba}}
</div>
{{end}}
{{if .Telefon}}
<div style="font-size:13px;color:var(--tekst-sporedni);">
<span style="color:var(--tekst-glavni);font-weight:500;">Telefon:</span> {{.Telefon}}
</div>
{{end}}
{{if .Email}}
<div style="font-size:13px;color:var(--tekst-sporedni);">
<span style="color:var(--tekst-glavni);font-weight:500;">E-pošta:</span> {{.Email}}
</div>
{{end}}
{{if not .KontaktOsoba}}{{if not .Telefon}}{{if not .Email}}
<div style="font-size:13px;color:var(--tekst-sporedni);">Nema dodatnih kontakt podataka.</div>
{{end}}{{end}}{{end}}
</div>
</div>
{{else}}
<div style="padding:32px;text-align:center;font-size:14px;color:var(--tekst-sporedni);">
Nema dobavljača. <a href="/dobavljaci/novi" style="color:var(--sb-akcent);">Dodaj prvog dobavljača.</a>
</div>
{{end}}
</div>
</div>
{{end}}
+181
View File
@@ -0,0 +1,181 @@
{{template "base" .}}
{{define "naslov"}}Detalji nabavke — NTech{{end}}
{{define "dodatni-css"}}
<style>
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.detalji-kartica {
animation: fadeInUp 0.3s ease forwards;
opacity: 0;
}
.detalji-kartica:nth-child(1) { animation-delay: 0.04s; }
.detalji-kartica:nth-child(2) { animation-delay: 0.12s; }
.detalji-kartica:nth-child(3) { animation-delay: 0.20s; }
.stavke-tabela tbody tr {
animation: fadeInUp 0.2s ease forwards;
opacity: 0;
}
.stavke-tabela tbody tr:nth-child(1) { animation-delay: 0.16s; }
.stavke-tabela tbody tr:nth-child(2) { animation-delay: 0.20s; }
.stavke-tabela tbody tr:nth-child(3) { animation-delay: 0.24s; }
.stavke-tabela tbody tr:nth-child(4) { animation-delay: 0.28s; }
.stavke-tabela tbody tr:nth-child(5) { animation-delay: 0.32s; }
.stavke-kartice {
display: none;
flex-direction: column;
gap: 10px;
}
@media (max-width: 768px) {
.stavke-tabela-wrapper { display: none; }
.stavke-kartice { display: flex; }
}
</style>
{{end}}
{{define "sadrzaj"}}
<div style="display:flex;flex-direction:column;gap:16px;">
<!-- nazad dugme -->
<a href="/nabavke"
style="display:inline-flex;align-items:center;gap:6px;font-size:13px;color:var(--tekst-sporedni);text-decoration:none;transition:color 0.2s;"
onmouseover="this.style.color='var(--tekst-glavni)'" onmouseout="this.style.color='var(--tekst-sporedni)'">
<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"><polyline points="15 18 9 12 15 6"/></svg>
Nazad na nabavke
</a>
<!-- zaglavlje nabavke -->
<div class="kartica detalji-kartica">
<div style="margin-bottom:16px;padding-bottom:14px;border-bottom:0.5px solid var(--ivica);display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px;">
<span style="font-size:16px;font-weight:500;color:var(--tekst-glavni);">Detalji nabavke</span>
<span style="font-size:13px;color:var(--tekst-sporedni);">{{.Nabavka.Datum.Format "02.01.2006. u 15:04"}}</span>
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(160px, 1fr));gap:16px;">
<div>
<div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Dobavljač</div>
<div style="font-size:14px;font-weight:500;color:var(--tekst-glavni);">
{{if .DobavljacNaziv}}{{.DobavljacNaziv}}{{else}}—{{end}}
</div>
</div>
<div>
<div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Napomena</div>
<div style="font-size:14px;color:var(--tekst-glavni);">
{{if .Nabavka.Napomena}}{{.Nabavka.Napomena}}{{else}}—{{end}}
</div>
</div>
<div>
<div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Ukupan iznos</div>
<div style="font-size:20px;font-weight:600;color:var(--sb-akcent);">
{{printf "%.2f" .Nabavka.Ukupno}} din
</div>
</div>
</div>
</div>
<!-- stavke -->
<div class="kartica detalji-kartica" style="padding:0;overflow:hidden;">
<div style="padding:14px 16px;border-bottom:0.5px solid var(--ivica);">
<span style="font-size:15px;font-weight:500;color:var(--tekst-glavni);">Stavke nabavke</span>
</div>
<!-- desktop tabela -->
<div class="stavke-tabela-wrapper" style="overflow-x:auto;">
<table class="stavke-tabela" style="width:100%;border-collapse:collapse;">
<thead>
<tr style="border-bottom:0.5px solid var(--ivica);">
<th style="padding:10px 16px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Artikal</th>
<th style="padding:10px 16px;text-align:center;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Količina</th>
<th style="padding:10px 16px;text-align:right;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Cena/kom</th>
<th style="padding:10px 16px;text-align:right;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Ukupno</th>
</tr>
</thead>
<tbody>
{{range .Stavke}}
<tr style="border-bottom:0.5px solid var(--ivica);transition:background 0.15s;"
onmouseover="this.style.background='var(--pozadina)'"
onmouseout="this.style.background=''">
<td style="padding:10px 16px;font-size:14px;color:var(--tekst-glavni);">{{.ArtikalNaziv}}</td>
<td style="padding:10px 16px;text-align:center;font-size:14px;color:var(--tekst-glavni);">{{.Kolicina}}</td>
<td style="padding:10px 16px;text-align:right;font-size:14px;color:var(--tekst-sporedni);">{{printf "%.2f" .CenaPoKomadu}} din</td>
<td style="padding:10px 16px;text-align:right;font-size:14px;font-weight:500;color:var(--tekst-glavni);">{{printf "%.2f" .Ukupno}} din</td>
</tr>
{{else}}
<tr>
<td colspan="4" style="padding:24px;text-align:center;font-size:14px;color:var(--tekst-sporedni);">
Ova nabavka nema stavki.
</td>
</tr>
{{end}}
</tbody>
{{if .Stavke}}
<tfoot>
<tr style="border-top:0.5px solid var(--ivica);">
<td colspan="3" style="padding:10px 16px;text-align:right;font-size:13px;font-weight:500;color:var(--tekst-sporedni);">Ukupno:</td>
<td style="padding:10px 16px;text-align:right;font-size:15px;font-weight:600;color:var(--tekst-glavni);">{{printf "%.2f" .Nabavka.Ukupno}} din</td>
</tr>
</tfoot>
{{end}}
</table>
</div>
<!-- mobilne kartice stavki -->
<div class="stavke-kartice" style="padding:12px;">
{{range .Stavke}}
<div style="border:0.5px solid var(--ivica);border-radius:8px;padding:12px;">
<div style="font-size:14px;font-weight:500;color:var(--tekst-glavni);margin-bottom:8px;">{{.ArtikalNaziv}}</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;">
<div>
<div style="font-size:11px;color:var(--tekst-sporedni);">Količina</div>
<div style="font-size:14px;color:var(--tekst-glavni);">{{.Kolicina}}</div>
</div>
<div>
<div style="font-size:11px;color:var(--tekst-sporedni);">Cena/kom</div>
<div style="font-size:14px;color:var(--tekst-sporedni);">{{printf "%.2f" .CenaPoKomadu}} din</div>
</div>
<div style="text-align:right;">
<div style="font-size:11px;color:var(--tekst-sporedni);">Ukupno</div>
<div style="font-size:14px;font-weight:500;color:var(--tekst-glavni);">{{printf "%.2f" .Ukupno}} din</div>
</div>
</div>
</div>
{{end}}
{{if .Stavke}}
<div style="text-align:right;font-size:15px;font-weight:600;color:var(--tekst-glavni);padding:8px 4px;">
Ukupno: {{printf "%.2f" .Nabavka.Ukupno}} din
</div>
{{end}}
</div>
</div>
<!-- zona za brisanje -->
<div class="kartica detalji-kartica" style="border-color:#dc262633;">
<div style="display:flex;align-items:flex-start;gap:12px;flex-wrap:wrap;">
<div style="flex:1;min-width:200px;">
<div style="font-size:14px;font-weight:500;color:#dc2626;margin-bottom:4px;">Brisanje nabavke</div>
<div style="font-size:13px;color:var(--tekst-sporedni);">
Brisanje je trajno i ne vraća količine artikala u magacin.
</div>
</div>
<form method="POST" action="/nabavke/obrisi/{{.Nabavka.ID}}">
<button type="submit"
onclick="return confirm('Da li ste sigurni da želite da obrišete ovu nabavku?\n\nBrisanje ne vraća količine artikala u magacin.')"
style="padding:9px 20px;background:#dc2626;color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;white-space:nowrap;transition:opacity 0.2s;"
onmouseover="this.style.opacity='0.85'" onmouseout="this.style.opacity='1'">
Obriši nabavku
</button>
</form>
</div>
</div>
</div>
{{end}}
+372
View File
@@ -0,0 +1,372 @@
{{template "base" .}}
{{define "naslov"}}Nova nabavka — NTech{{end}}
{{define "dodatni-css"}}
<style>
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
20% { transform: translateX(-6px); }
40% { transform: translateX(6px); }
60% { transform: translateX(-4px); }
80% { transform: translateX(4px); }
}
@keyframes modalIn {
from { opacity: 0; transform: translateY(-16px) scale(0.97); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
.forma-kartica {
animation: fadeInUp 0.3s ease forwards;
opacity: 0;
}
.forma-kartica:nth-child(1) { animation-delay: 0.04s; }
.forma-kartica:nth-child(2) { animation-delay: 0.12s; }
.greska-animacija { animation: shake 0.4s ease; }
.modal-sadrzaj { animation: modalIn 0.25s ease forwards; }
@media (max-width: 768px) {
.stavke-tabela-wrapper { display: none; }
.stavke-kartice { display: flex !important; }
}
</style>
{{end}}
{{define "sadrzaj"}}
<!-- lista artikala kao JSON — bezbedno serijalizovana na serveru -->
<script>var _ntechArtikli = {{.ArtikliJSON}};</script>
<div style="width:100%;"
x-data="{
stavke: [{artikal_id: '', kolicina: 1, cena: 0}],
artikliOpcije: _ntechArtikli,
dodajStavku() {
this.stavke.push({artikal_id: '', kolicina: 1, cena: 0});
},
ukloniStavku(i) {
if (this.stavke.length > 1) this.stavke.splice(i, 1);
},
ukupnoStavke(s) {
return (parseFloat(s.kolicina) * parseFloat(s.cena) || 0).toFixed(2);
},
ukupnoSvega() {
return this.stavke.reduce((z, s) => z + (parseFloat(s.kolicina) * parseFloat(s.cena) || 0), 0).toFixed(2);
},
modal: false,
modalUcitavanje: false,
modalGreska: '',
modalNaziv: '',
modalKategorijaID: '',
modalCena: '',
otvoriModal() {
this.modal = true;
this.modalGreska = '';
this.modalNaziv = '';
this.modalKategorijaID = '';
this.modalCena = '';
this.$nextTick(() => this.$refs.modalNazivInput && this.$refs.modalNazivInput.focus());
},
zatvoriModal() {
this.modal = false;
},
async sacuvajArtikal() {
if (!this.modalNaziv.trim()) {
this.modalGreska = 'Naziv artikla je obavezan.';
return;
}
this.modalUcitavanje = true;
this.modalGreska = '';
const params = new URLSearchParams();
params.append('naziv', this.modalNaziv.trim());
if (this.modalKategorijaID) params.append('kategorija_id', this.modalKategorijaID);
if (this.modalCena) params.append('prodajna_cena', this.modalCena);
try {
const odgovor = await fetch('/magacin/novi', {
method: 'POST',
headers: {'X-Requested-With': 'fetch', 'Content-Type': 'application/x-www-form-urlencoded'},
body: params
});
if (!odgovor.ok) {
this.modalGreska = 'Greška pri čuvanju artikla. Pokušajte ponovo.';
return;
}
const noviArtikal = await odgovor.json();
// dodajemo u listu — svi dropdown-ovi se automatski ažuriraju
this.artikliOpcije.push({id: noviArtikal.id, naziv: noviArtikal.naziv});
this.zatvoriModal();
} catch {
this.modalGreska = 'Greška pri komunikaciji sa serverom.';
} finally {
this.modalUcitavanje = false;
}
}
}">
<!-- nazad dugme -->
<a href="/nabavke"
style="display:inline-flex;align-items:center;gap:6px;font-size:13px;color:var(--tekst-sporedni);text-decoration:none;margin-bottom:20px;transition:color 0.2s;"
onmouseover="this.style.color='var(--tekst-glavni)'" onmouseout="this.style.color='var(--tekst-sporedni)'">
<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"><polyline points="15 18 9 12 15 6"/></svg>
Nazad na nabavke
</a>
<form method="POST" action="/nabavke/nova">
{{if .Greska}}
<div class="greska-animacija"
style="background:var(--kartica);border:0.5px solid #dc2626;border-radius:8px;padding:10px 14px;margin-bottom:16px;font-size:13px;color:#dc2626;">
{{.Greska}}
</div>
{{end}}
<!-- zaglavlje nabavke -->
<div class="kartica forma-kartica" style="margin-bottom:16px;">
<div style="margin-bottom:16px;padding-bottom:14px;border-bottom:0.5px solid var(--ivica);">
<span style="font-size:16px;font-weight:500;color:var(--tekst-glavni);">Nova nabavka</span>
</div>
<div style="display:flex;flex-direction:column;gap:14px;">
<div>
<label style="font-size:13px;color:var(--tekst-sporedni);display:block;margin-bottom:6px;">Dobavljač</label>
<select name="dobavljac_id" style="width:100%;">
<option value="">— bez dobavljača —</option>
{{range .Dobavljaci}}
<option value="{{.ID}}">{{.Naziv}}</option>
{{end}}
</select>
</div>
<div>
<label style="font-size:13px;color:var(--tekst-sporedni);display:block;margin-bottom:6px;">Napomena</label>
<textarea name="napomena" rows="2"
placeholder="Interna napomena o nabavci..."
style="width:100%;resize:vertical;"></textarea>
</div>
</div>
</div>
<!-- stavke -->
<div class="kartica forma-kartica" style="margin-bottom:16px;">
<div style="margin-bottom:16px;padding-bottom:14px;border-bottom:0.5px solid var(--ivica);display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px;">
<span style="font-size:16px;font-weight:500;color:var(--tekst-glavni);">Stavke</span>
<div style="display:flex;gap:8px;flex-wrap:wrap;">
<button type="button" @click="otvoriModal()"
style="padding:6px 14px;background:var(--kartica);color:var(--tekst-sporedni);border:0.5px solid var(--ivica);border-radius:8px;font-size:13px;cursor:pointer;transition:background 0.2s;"
onmouseover="this.style.background='var(--pozadina)'" onmouseout="this.style.background='var(--kartica)'">
+ Novi artikal
</button>
<button type="button" @click="dodajStavku()"
style="padding:6px 14px;background:var(--sb-aktivan);color:#fff;border:none;border-radius:8px;font-size:13px;cursor:pointer;transition:opacity 0.2s;"
onmouseover="this.style.opacity='0.8'" onmouseout="this.style.opacity='1'">
+ Dodaj stavku
</button>
</div>
</div>
<!-- desktop tabela stavki -->
<div class="stavke-tabela-wrapper" style="overflow-x:auto;">
<table style="width:100%;border-collapse:collapse;">
<thead>
<tr style="border-bottom:0.5px solid var(--ivica);">
<th style="padding:8px 10px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Artikal</th>
<th style="padding:8px 10px;text-align:center;font-size:12px;font-weight:500;color:var(--tekst-sporedni);width:90px;">Količina</th>
<th style="padding:8px 10px;text-align:right;font-size:12px;font-weight:500;color:var(--tekst-sporedni);width:130px;">Cena/kom (din)</th>
<th style="padding:8px 10px;text-align:right;font-size:12px;font-weight:500;color:var(--tekst-sporedni);width:110px;">Ukupno</th>
<th style="width:40px;"></th>
</tr>
</thead>
<tbody>
<template x-for="(stavka, i) in stavke" :key="i">
<tr style="border-bottom:0.5px solid var(--ivica);">
<td style="padding:8px 10px;">
<select :name="'artikal_id[]'" x-model="stavka.artikal_id" style="width:100%;">
<option value="">— odaberi artikal —</option>
<template x-for="a in artikliOpcije" :key="a.id">
<option :value="a.id" x-text="a.naziv"></option>
</template>
</select>
</td>
<td style="padding:8px 10px;">
<input type="number" :name="'kolicina[]'" x-model="stavka.kolicina"
min="1" style="width:100%;text-align:center;">
</td>
<td style="padding:8px 10px;">
<input type="number" :name="'cena_po_komadu[]'" x-model="stavka.cena"
min="0" step="0.01" style="width:100%;text-align:right;">
</td>
<td style="padding:8px 10px;text-align:right;font-size:14px;font-weight:500;color:var(--tekst-glavni);">
<span x-text="ukupnoStavke(stavka) + ' din'"></span>
</td>
<td style="padding:8px 10px;text-align:center;">
<button type="button" @click="ukloniStavku(i)"
x-show="stavke.length > 1"
style="background:none;border:none;cursor:pointer;color:#dc2626;font-size:18px;line-height:1;padding:2px 6px;border-radius:4px;transition:background 0.15s;"
onmouseover="this.style.background='rgba(220,38,38,0.08)'"
onmouseout="this.style.background='none'"
title="Ukloni stavku">×</button>
</td>
</tr>
</template>
</tbody>
<tfoot>
<tr style="border-top:0.5px solid var(--ivica);">
<td colspan="3" style="padding:10px 10px;text-align:right;font-size:13px;color:var(--tekst-sporedni);font-weight:500;">Ukupno:</td>
<td style="padding:10px 10px;text-align:right;font-size:15px;font-weight:600;color:var(--tekst-glavni);">
<span x-text="ukupnoSvega() + ' din'"></span>
</td>
<td></td>
</tr>
</tfoot>
</table>
</div>
<!-- mobilne kartice stavki -->
<div class="stavke-kartice" style="display:none;flex-direction:column;gap:10px;">
<template x-for="(stavka, i) in stavke" :key="i">
<div style="border:0.5px solid var(--ivica);border-radius:8px;padding:12px;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;">
<span style="font-size:13px;font-weight:500;color:var(--tekst-sporedni);"
x-text="'Stavka ' + (i + 1)"></span>
<button type="button" @click="ukloniStavku(i)"
x-show="stavke.length > 1"
style="background:none;border:0.5px solid #dc2626;color:#dc2626;cursor:pointer;font-size:13px;padding:2px 8px;border-radius:4px;">
Ukloni
</button>
</div>
<div style="display:flex;flex-direction:column;gap:10px;">
<div>
<label style="font-size:12px;color:var(--tekst-sporedni);display:block;margin-bottom:4px;">Artikal</label>
<select :name="'artikal_id[]'" x-model="stavka.artikal_id" style="width:100%;">
<option value="">— odaberi artikal —</option>
<template x-for="a in artikliOpcije" :key="a.id">
<option :value="a.id" x-text="a.naziv"></option>
</template>
</select>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
<div>
<label style="font-size:12px;color:var(--tekst-sporedni);display:block;margin-bottom:4px;">Količina</label>
<input type="number" :name="'kolicina[]'" x-model="stavka.kolicina" min="1" style="width:100%;">
</div>
<div>
<label style="font-size:12px;color:var(--tekst-sporedni);display:block;margin-bottom:4px;">Cena/kom (din)</label>
<input type="number" :name="'cena_po_komadu[]'" x-model="stavka.cena" min="0" step="0.01" style="width:100%;">
</div>
</div>
<div style="text-align:right;font-size:14px;font-weight:500;color:var(--tekst-glavni);">
Ukupno: <span x-text="ukupnoStavke(stavka) + ' din'"></span>
</div>
</div>
</div>
</template>
<div style="text-align:right;font-size:15px;font-weight:600;color:var(--tekst-glavni);padding:8px 4px;">
Ukupno: <span x-text="ukupnoSvega() + ' din'"></span>
</div>
</div>
</div>
<!-- dugmad forme -->
<div style="display:flex;justify-content:flex-end;gap:10px;">
<a href="/nabavke"
style="padding:9px 20px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;color:var(--tekst-sporedni);text-decoration:none;transition:background 0.2s;"
onmouseover="this.style.background='var(--pozadina)'" onmouseout="this.style.background=''">
Odustani
</a>
<button type="submit"
style="padding:9px 20px;background:var(--sb-akcent);color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:opacity 0.2s;"
onmouseover="this.style.opacity='0.85'" onmouseout="this.style.opacity='1'">
Sačuvaj nabavku
</button>
</div>
</form>
<!-- modal: novi artikal -->
<div x-show="modal" x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"
style="position:fixed;inset:0;background:rgba(0,0,0,0.5);z-index:50;display:flex;align-items:center;justify-content:center;padding:16px;"
@click.self="zatvoriModal()" @keydown.escape.window="zatvoriModal()">
<div class="kartica modal-sadrzaj" style="width:100%;max-width:440px;padding:24px;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;padding-bottom:14px;border-bottom:0.5px solid var(--ivica);">
<span style="font-size:16px;font-weight:500;color:var(--tekst-glavni);">Novi artikal</span>
<button type="button" @click="zatvoriModal()"
style="background:none;border:none;cursor:pointer;color:var(--tekst-sporedni);font-size:20px;line-height:1;padding:2px 6px;border-radius:4px;transition:background 0.15s;"
onmouseover="this.style.background='var(--pozadina)'" onmouseout="this.style.background='none'">×</button>
</div>
<div x-show="modalGreska" class="greska-animacija"
style="background:var(--kartica);border:0.5px solid #dc2626;border-radius:8px;padding:10px 14px;margin-bottom:14px;font-size:13px;color:#dc2626;"
x-text="modalGreska"></div>
<div style="display:flex;flex-direction:column;gap:14px;">
<div>
<label style="font-size:13px;color:var(--tekst-sporedni);display:block;margin-bottom:6px;">
Naziv <span style="color:#dc2626;">*</span>
</label>
<input type="text" x-model="modalNaziv" x-ref="modalNazivInput"
placeholder="npr. RAM DDR4 8GB Kingston"
style="width:100%;"
@keydown.enter.prevent="sacuvajArtikal()">
</div>
<div>
<label style="font-size:13px;color:var(--tekst-sporedni);display:block;margin-bottom:6px;">Kategorija</label>
<select x-model="modalKategorijaID" style="width:100%;">
<option value="">— bez kategorije —</option>
{{range .Kategorije}}
<option value="{{.ID}}">{{.Naziv}}</option>
{{end}}
</select>
</div>
<div>
<label style="font-size:13px;color:var(--tekst-sporedni);display:block;margin-bottom:6px;">Prodajna cena (din)</label>
<input type="number" x-model="modalCena" min="0" step="0.01"
placeholder="0"
style="width:100%;">
</div>
</div>
<div style="display:flex;justify-content:flex-end;gap:10px;margin-top:20px;padding-top:14px;border-top:0.5px solid var(--ivica);">
<button type="button" @click="zatvoriModal()"
style="padding:9px 20px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;color:var(--tekst-sporedni);background:none;cursor:pointer;transition:background 0.2s;"
onmouseover="this.style.background='var(--pozadina)'" onmouseout="this.style.background='none'">
Odustani
</button>
<button type="button" @click="sacuvajArtikal()" :disabled="modalUcitavanje"
style="padding:9px 20px;background:var(--sb-akcent);color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:opacity 0.2s;"
:style="modalUcitavanje ? 'opacity:0.6;cursor:not-allowed' : ''"
@mouseover="if(!modalUcitavanje) $el.style.opacity='0.85'" @mouseout="$el.style.opacity='1'">
<span x-text="modalUcitavanje ? 'Čuvanje...' : 'Dodaj artikal'"></span>
</button>
</div>
</div>
</div>
</div>
{{end}}
+182
View File
@@ -0,0 +1,182 @@
{{template "base" .}}
{{define "naslov"}}Nabavke — NTech{{end}}
{{define "dodatni-css"}}
<style>
@keyframes slideDown {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.poruka-animacija {
animation: slideDown 0.3s ease forwards;
}
.nabavke-tabela tbody tr {
animation: fadeInUp 0.25s ease forwards;
opacity: 0;
}
.nabavke-tabela tbody tr:nth-child(1) { animation-delay: 0.04s; }
.nabavke-tabela tbody tr:nth-child(2) { animation-delay: 0.08s; }
.nabavke-tabela tbody tr:nth-child(3) { animation-delay: 0.12s; }
.nabavke-tabela tbody tr:nth-child(4) { animation-delay: 0.16s; }
.nabavke-tabela tbody tr:nth-child(5) { animation-delay: 0.20s; }
.nabavke-tabela tbody tr:nth-child(6) { animation-delay: 0.24s; }
.nabavke-tabela tbody tr:nth-child(7) { animation-delay: 0.28s; }
.nabavke-tabela tbody tr:nth-child(8) { animation-delay: 0.32s; }
.nabavke-tabela tbody tr:nth-child(9) { animation-delay: 0.36s; }
.nabavke-tabela tbody tr:nth-child(10) { animation-delay: 0.40s; }
.nabavke-kartice {
display: none;
flex-direction: column;
gap: 12px;
}
.nabavka-kartica {
animation: fadeInUp 0.25s ease forwards;
opacity: 0;
}
.nabavka-kartica:nth-child(1) { animation-delay: 0.04s; }
.nabavka-kartica:nth-child(2) { animation-delay: 0.10s; }
.nabavka-kartica:nth-child(3) { animation-delay: 0.16s; }
.nabavka-kartica:nth-child(4) { animation-delay: 0.22s; }
.nabavka-kartica:nth-child(5) { animation-delay: 0.28s; }
@media (max-width: 768px) {
.nabavke-tabela { display: none; }
.nabavke-kartice { display: flex; }
}
</style>
{{end}}
{{define "sadrzaj"}}
<div style="display:flex;flex-direction:column;gap:16px;">
{{if .Sacuvano}}
<div class="poruka-uspeh poruka-animacija">Nabavka je uspešno sačuvana.</div>
{{end}}
{{if .Obrisan}}
<div class="poruka-uspeh poruka-animacija">Nabavka je uspešno obrisana.</div>
{{end}}
<!-- dugme nova nabavka -->
<div>
<a href="/nabavke/nova"
style="display:inline-block;padding:8px 16px;background:var(--sb-akcent);color:#fff;border-radius:8px;font-size:14px;font-weight:500;text-decoration:none;transition:opacity 0.2s;"
onmouseover="this.style.opacity='0.85'" onmouseout="this.style.opacity='1'">
+ Nova nabavka
</a>
</div>
<!-- desktop tabela -->
<div class="kartica nabavke-tabela" style="padding:0;overflow:hidden;">
<div style="overflow-x:auto;">
<table style="width:100%;border-collapse:collapse;">
<thead>
<tr style="border-bottom:0.5px solid var(--ivica);">
<th style="padding:12px 16px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Datum</th>
<th style="padding:12px 16px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Dobavljač</th>
<th style="padding:12px 16px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Napomena</th>
<th style="padding:12px 16px;text-align:right;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Ukupno</th>
<th style="padding:12px 16px;text-align:center;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Akcije</th>
</tr>
</thead>
<tbody>
{{range .Nabavke}}
<tr style="border-bottom:0.5px solid var(--ivica);transition:background 0.15s;"
onmouseover="this.style.background='var(--pozadina)'"
onmouseout="this.style.background=''">
<td style="padding:12px 16px;font-size:13px;color:var(--tekst-sporedni);white-space:nowrap;">
{{.Datum.Format "02.01.2006."}}
</td>
<td style="padding:12px 16px;font-size:14px;font-weight:500;color:var(--tekst-glavni);">
{{if .DobavljacNaziv}}{{.DobavljacNaziv}}{{else}}—{{end}}
</td>
<td style="padding:12px 16px;font-size:13px;color:var(--tekst-sporedni);">
{{if .Napomena}}{{.Napomena}}{{else}}—{{end}}
</td>
<td style="padding:12px 16px;text-align:right;font-size:14px;font-weight:500;color:var(--tekst-glavni);">
{{printf "%.2f" .Ukupno}} din
</td>
<td style="padding:12px 16px;text-align:center;">
<div style="display:flex;align-items:center;justify-content:center;gap:8px;">
<a href="/nabavke/{{.ID}}"
style="padding:4px 10px;background:var(--sb-aktivan);color:#fff;border-radius:6px;font-size:12px;text-decoration:none;transition:opacity 0.2s;"
onmouseover="this.style.opacity='0.8'" onmouseout="this.style.opacity='1'">
Detalji
</a>
<form method="POST" action="/nabavke/obrisi/{{.ID}}" style="display:inline;">
<button type="submit"
onclick="return confirm('Da li ste sigurni?\n\nBrisanje nabavke ne vraća količine artikala u magacin.')"
style="padding:4px 10px;background:#dc2626;color:#fff;border:none;border-radius:6px;font-size:12px;cursor:pointer;transition:opacity 0.2s;"
onmouseover="this.style.opacity='0.8'" onmouseout="this.style.opacity='1'">
Obriši
</button>
</form>
</div>
</td>
</tr>
{{else}}
<tr>
<td colspan="5" style="padding:32px;text-align:center;font-size:14px;color:var(--tekst-sporedni);">
Nema nabavki. <a href="/nabavke/nova" style="color:var(--sb-akcent);">Dodaj prvu nabavku.</a>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
</div>
<!-- mobilne kartice -->
<div class="nabavke-kartice">
{{range .Nabavke}}
<div class="kartica nabavka-kartica">
<div style="display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:10px;">
<div>
<div style="font-size:15px;font-weight:500;color:var(--tekst-glavni);">
{{if .DobavljacNaziv}}{{.DobavljacNaziv}}{{else}}Bez dobavljača{{end}}
</div>
<div style="font-size:12px;color:var(--tekst-sporedni);margin-top:2px;">
{{.Datum.Format "02.01.2006."}}
</div>
</div>
<div style="font-size:15px;font-weight:500;color:var(--tekst-glavni);white-space:nowrap;">
{{printf "%.2f" .Ukupno}} din
</div>
</div>
{{if .Napomena}}
<div style="font-size:13px;color:var(--tekst-sporedni);margin-bottom:10px;">{{.Napomena}}</div>
{{end}}
<div style="display:flex;gap:8px;">
<a href="/nabavke/{{.ID}}"
style="flex:1;padding:7px;background:var(--sb-aktivan);color:#fff;border-radius:6px;font-size:13px;text-decoration:none;text-align:center;">
Detalji
</a>
<form method="POST" action="/nabavke/obrisi/{{.ID}}" style="flex:1;">
<button type="submit"
onclick="return confirm('Da li ste sigurni?\n\nBrisanje nabavke ne vraća količine artikala u magacin.')"
style="width:100%;padding:7px;background:#dc2626;color:#fff;border:none;border-radius:6px;font-size:13px;cursor:pointer;">
Obriši
</button>
</form>
</div>
</div>
{{else}}
<div style="padding:32px;text-align:center;font-size:14px;color:var(--tekst-sporedni);">
Nema nabavki. <a href="/nabavke/nova" style="color:var(--sb-akcent);">Dodaj prvu nabavku.</a>
</div>
{{end}}
</div>
</div>
{{end}}