Optimizacije — SQLite WAL, template keš, gzip kompresija, build skript

This commit is contained in:
2026-06-03 21:18:12 +02:00
parent 2401f6d5ec
commit 974d76360a
46 changed files with 1470 additions and 579 deletions
+4 -20
View File
@@ -56,7 +56,7 @@ func (h *Handler) AdminKorisnici(w http.ResponseWriter, r *http.Request) {
Sacuvano: r.URL.Query().Get("sacuvano") == "1",
}
renderujAdminTemplate(w, "web/templates/stranice/admin_korisnici.html", podaci)
h.renderujTemplate(w, "admin_korisnici", podaci)
}
// AdminSacuvajKorisnika kreira novog korisnika
@@ -196,7 +196,7 @@ func (h *Handler) AdminProfil(w http.ResponseWriter, r *http.Request) {
TotpAktivan: svezi.TotpTajna != "",
}
renderujAdminTemplate(w, "web/templates/stranice/admin_profil.html", podaci)
h.renderujTemplate(w, "admin_profil", podaci)
}
// AdminPromeniLozinku menja lozinku prijavljenog korisnika
@@ -265,7 +265,7 @@ func (h *Handler) AdminTotpPokreni(w http.ResponseWriter, r *http.Request) {
TotpQR: template.URL("data:image/png;base64," + totp.QRBase64),
}
renderujAdminTemplate(w, "web/templates/stranice/admin_profil.html", podaci)
h.renderujTemplate(w, "admin_profil", podaci)
}
// AdminTotpAktivacija verifikuje TOTP kod i čuva tajnu
@@ -305,7 +305,7 @@ func (h *Handler) AdminTotpAktivacija(w http.ResponseWriter, r *http.Request) {
TotpQR: template.URL("data:image/png;base64," + qr),
Greska: "totp",
}
renderujAdminTemplate(w, "web/templates/stranice/admin_profil.html", podaci)
h.renderujTemplate(w, "admin_profil", podaci)
return
}
@@ -333,22 +333,6 @@ func (h *Handler) AdminTotpDeaktivacija(w http.ResponseWriter, r *http.Request)
http.Redirect(w, r, "/admin/profil?sacuvano=totp_off", http.StatusSeeOther)
}
func renderujAdminTemplate(w http.ResponseWriter, stranica string, podaci any) {
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
stranica,
)
if err != nil {
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
}
// parseBoolForm čita boolean vrednost iz forme
func parseBoolForm(s string) bool {
b, _ := strconv.ParseBool(s)
+16 -25
View File
@@ -1,7 +1,6 @@
package handler
import (
"html/template"
"log"
"net/http"
"time"
@@ -20,7 +19,7 @@ func (h *Handler) Dashboard(w http.ResponseWriter, r *http.Request) {
return
}
var brojArtikala, aktivniServisi, kriticnaZaliha int
var brojArtikala, aktivniServisi, kriticnaZaliha, aktivniPodsetnici int
var prihodOvogMeseca float64
if err := h.DB.QueryRowContext(ctx,
@@ -49,6 +48,12 @@ func (h *Handler) Dashboard(w http.ResponseWriter, r *http.Request) {
log.Printf("dashboard: kriticna zaliha: %v", err)
}
if n, err := h.PodsetniciFRepo.BrojAktivnih(ctx); err != nil {
log.Printf("dashboard: aktivni podsetnici: %v", err)
} else {
aktivniPodsetnici = n
}
// poslednjih 5 servisnih naloga sa datumom prijema
servisRedovi, err := h.DB.QueryContext(ctx, `
SELECT uredjaj, status, datum_prijema FROM servisni_nalozi
@@ -132,31 +137,17 @@ func (h *Handler) Dashboard(w http.ResponseWriter, r *http.Request) {
LogoPutanja: podesavanja["logo_putanja"],
Korisnik: "Admin",
},
BrojArtikala: brojArtikala,
AktivniServisi: aktivniServisi,
PrihodOvogMeseca: prihodOvogMeseca,
KriticnaZaliha: kriticnaZaliha,
PoslednjiServisi: poslednjiServisi,
KriticneZalihe: kriticneZalihe,
PoslednjeProdaje: poslednjeProdaje,
BrojArtikala: brojArtikala,
AktivniServisi: aktivniServisi,
PrihodOvogMeseca: prihodOvogMeseca,
KriticnaZaliha: kriticnaZaliha,
AktivniPodsetnici: aktivniPodsetnici,
PoslednjiServisi: poslednjiServisi,
KriticneZalihe: kriticneZalihe,
PoslednjeProdaje: poslednjeProdaje,
}
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/dashboard.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
h.renderujTemplate(w, "dashboard", podaci)
}
// bojaTackeServisa vraća hex boju tačke prema statusu naloga
+7 -39
View File
@@ -1,8 +1,6 @@
package handler
import (
"html/template"
"log"
"net/http"
"strings"
@@ -62,22 +60,7 @@ func (h *Handler) Dobavljaci(w http.ResponseWriter, r *http.Request) {
Obrisan: r.URL.Query().Get("obrisan") == "1",
}
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/dobavljaci.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
h.renderujTemplate(w, "dobavljaci", podaci)
}
// NoviDobavljac prikazuje praznu formu za unos novog dobavljača
@@ -88,7 +71,7 @@ func (h *Handler) NoviDobavljac(w http.ResponseWriter, r *http.Request) {
return
}
renderujFormuDobavljaca(w, PodaciFormeDobavljaca{
h.renderujFormuDobavljaca(w, PodaciFormeDobavljaca{
PodaciStranice: model.PodaciStranice{
Stranica: "dobavljaci",
NaslovStranice: "Novi dobavljač",
@@ -113,7 +96,7 @@ func (h *Handler) SacuvajDobavljaca(w http.ResponseWriter, r *http.Request) {
dobavljac, greska := parseFormuDobavljaca(r)
if greska != "" {
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
renderujFormuDobavljaca(w, PodaciFormeDobavljaca{
h.renderujFormuDobavljaca(w, PodaciFormeDobavljaca{
PodaciStranice: model.PodaciStranice{
Stranica: "dobavljaci",
NaslovStranice: "Novi dobavljač",
@@ -159,7 +142,7 @@ func (h *Handler) IzmeniDobavljaca(w http.ResponseWriter, r *http.Request) {
return
}
renderujFormuDobavljaca(w, PodaciFormeDobavljaca{
h.renderujFormuDobavljaca(w, PodaciFormeDobavljaca{
PodaciStranice: model.PodaciStranice{
Stranica: "dobavljaci",
NaslovStranice: "Izmeni dobavljača",
@@ -192,7 +175,7 @@ func (h *Handler) SacuvajIzmeneDobavljaca(w http.ResponseWriter, r *http.Request
if greska != "" {
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
dobavljac.ID = id
renderujFormuDobavljaca(w, PodaciFormeDobavljaca{
h.renderujFormuDobavljaca(w, PodaciFormeDobavljaca{
PodaciStranice: model.PodaciStranice{
Stranica: "dobavljaci",
NaslovStranice: "Izmeni dobavljača",
@@ -257,21 +240,6 @@ func parseFormuDobavljaca(r *http.Request) (model.Dobavljac, string) {
}
// renderujFormuDobavljaca renderuje HTML šablon forme za unos ili izmenu dobavljača
func renderujFormuDobavljaca(w http.ResponseWriter, podaci PodaciFormeDobavljaca) {
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/dobavljac_forma.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
func (h *Handler) renderujFormuDobavljaca(w http.ResponseWriter, podaci PodaciFormeDobavljaca) {
h.renderujTemplate(w, "dobavljac_forma", podaci)
}
+9 -4
View File
@@ -2,6 +2,7 @@ package handler
import (
"database/sql"
"html/template"
"ntech/internal/db"
"ntech/internal/db/sqlite"
@@ -20,8 +21,11 @@ type Handler struct {
KlijentiRepo db.KlijentRepository
ServisRepo db.ServisRepository
ProdajaRepo db.ProdajaRepository
KorisniciRepo db.KorisniciRepository
SesijeRepo db.SesijeRepository
KorisniciRepo db.KorisniciRepository
SesijeRepo db.SesijeRepository
PodsetniciFRepo db.PodsetnikRepository
Verzija string
Templates map[string]*template.Template
}
// Novi kreira novi Handler sa datom bazom
@@ -35,8 +39,9 @@ func Novi(baza *sql.DB) *Handler {
KlijentiRepo: sqlite.NoviKlijentRepo(baza),
ServisRepo: sqlite.NoviServisRepo(baza),
ProdajaRepo: sqlite.NoviProdajaRepo(baza),
KorisniciRepo: sqlite.NoviKorisniciRepo(baza),
SesijeRepo: sqlite.NoviSesijeRepo(baza),
KorisniciRepo: sqlite.NoviKorisniciRepo(baza),
SesijeRepo: sqlite.NoviSesijeRepo(baza),
PodsetniciFRepo: sqlite.NoviPodsetnikRepo(baza),
}
}
+1 -15
View File
@@ -257,19 +257,5 @@ func (h *Handler) Izvestaji(w http.ResponseWriter, r *http.Request) {
TopKlijenti: topKlijenti,
}
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/izvestaji.html",
)
if err != nil {
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
return
}
h.renderujTemplate(w, "izvestaji", podaci)
}
+1 -18
View File
@@ -1,8 +1,6 @@
package handler
import (
"html/template"
"log"
"net/http"
"strconv"
@@ -50,22 +48,7 @@ func (h *Handler) Kategorije(w http.ResponseWriter, r *http.Request) {
Obrisana: r.URL.Query().Get("obrisana") == "1",
}
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/kategorije.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
h.renderujTemplate(w, "kategorije", podaci)
}
// DodajKategoriju prima POST i čuva novu kategoriju
+119
View File
@@ -0,0 +1,119 @@
package handler
import (
"fmt"
"html/template"
"log"
"net/http"
)
var bazniSabloni = []string{
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
}
// saSidebar su šabloni koji koriste base layout (sidebar + topbar)
var saSidebar = []string{
"admin_korisnici", "admin_profil",
"dashboard",
"dobavljaci", "dobavljac_forma",
"izvestaji",
"kategorije",
"klijenti", "klijent_forma",
"magacin", "magacin_forma",
"nabavke", "nabavka_forma", "nabavka_detalji",
"podesavanja",
"podsetnici", "podsetnik_forma",
"prodaja", "prodaja_detalji", "prodaja_forma",
"servis", "servis_forma", "servis_detalji",
}
// standalone su šabloni bez base layouta
var standaloneIme = []string{
"prijava", "setup", "totp_provera", "prodaja_stampa",
}
// KreirajKes parsuje sve šablone i vraća ih keširane u mapi
func KreirajKes() (map[string]*template.Template, error) {
kes := make(map[string]*template.Template)
for _, ime := range saSidebar {
fajlovi := make([]string, len(bazniSabloni), len(bazniSabloni)+1)
copy(fajlovi, bazniSabloni)
fajlovi = append(fajlovi, "web/templates/stranice/"+ime+".html")
t, err := template.ParseFiles(fajlovi...)
if err != nil {
return nil, fmt.Errorf("kes: %s: %w", ime, err)
}
kes[ime] = t
}
for _, ime := range standaloneIme {
t, err := template.ParseFiles("web/templates/stranice/" + ime + ".html")
if err != nil {
return nil, fmt.Errorf("kes: %s: %w", ime, err)
}
kes[ime] = t
}
return kes, nil
}
// renderujTemplate renderuje šablon sa base layoutom
// U produkciji koristi keš; u razvoju parsuje svaki put (hot reload)
func (h *Handler) renderujTemplate(w http.ResponseWriter, ime string, podaci any) {
var tmpl *template.Template
if h.Templates != nil {
t, ok := h.Templates[ime]
if !ok {
log.Printf("kes: šablon '%s' nije pronađen", ime)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
tmpl = t
} else {
fajlovi := make([]string, len(bazniSabloni), len(bazniSabloni)+1)
copy(fajlovi, bazniSabloni)
fajlovi = append(fajlovi, "web/templates/stranice/"+ime+".html")
var err error
if tmpl, err = template.ParseFiles(fajlovi...); err != nil {
log.Printf("greška pri parsiranju šablona %s: %v", ime, err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju šablona %s: %v", ime, err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
}
// renderujStandalone renderuje šablon bez base layouta (prijava, setup, itd.)
func (h *Handler) renderujStandalone(w http.ResponseWriter, ime string, podaci any) {
var tmpl *template.Template
if h.Templates != nil {
t, ok := h.Templates[ime]
if !ok {
log.Printf("kes: standalone šablon '%s' nije pronađen", ime)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
tmpl = t
} else {
var err error
if tmpl, err = template.ParseFiles("web/templates/stranice/" + ime + ".html"); err != nil {
log.Printf("greška pri parsiranju šablona %s: %v", ime, err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
}
if err := tmpl.Execute(w, podaci); err != nil {
log.Printf("greška pri renderovanju šablona %s: %v", ime, err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
}
+9 -40
View File
@@ -1,7 +1,6 @@
package handler
import (
"html/template"
"log"
"net/http"
"strings"
@@ -62,22 +61,7 @@ func (h *Handler) Klijenti(w http.ResponseWriter, r *http.Request) {
Obrisan: r.URL.Query().Get("obrisan") == "1",
}
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/klijenti.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
h.renderujTemplate(w, "klijenti", podaci)
}
// NoviKlijent prikazuje praznu formu za unos novog klijenta
@@ -88,7 +72,7 @@ func (h *Handler) NoviKlijent(w http.ResponseWriter, r *http.Request) {
return
}
renderujFormuKlijenta(w, PodaciFormeKlijenta{
h.renderujFormuKlijenta(w, PodaciFormeKlijenta{
PodaciStranice: model.PodaciStranice{
Stranica: "klijenti",
NaslovStranice: "Novi klijent",
@@ -113,7 +97,7 @@ func (h *Handler) SacuvajKlijenta(w http.ResponseWriter, r *http.Request) {
klijent, greska := parseFormuKlijenta(r)
if greska != "" {
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
renderujFormuKlijenta(w, PodaciFormeKlijenta{
h.renderujFormuKlijenta(w, PodaciFormeKlijenta{
PodaciStranice: model.PodaciStranice{
Stranica: "klijenti",
NaslovStranice: "Novi klijent",
@@ -134,7 +118,7 @@ func (h *Handler) SacuvajKlijenta(w http.ResponseWriter, r *http.Request) {
if _, err := h.KlijentiRepo.Kreiraj(r.Context(), &klijent); err != nil {
log.Printf("greška pri čuvanju klijenta: %v", err)
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
renderujFormuKlijenta(w, PodaciFormeKlijenta{
h.renderujFormuKlijenta(w, PodaciFormeKlijenta{
PodaciStranice: model.PodaciStranice{
Stranica: "klijenti",
NaslovStranice: "Novi klijent",
@@ -175,7 +159,7 @@ func (h *Handler) IzmeniKlijenta(w http.ResponseWriter, r *http.Request) {
return
}
renderujFormuKlijenta(w, PodaciFormeKlijenta{
h.renderujFormuKlijenta(w, PodaciFormeKlijenta{
PodaciStranice: model.PodaciStranice{
Stranica: "klijenti",
NaslovStranice: "Izmeni klijenta",
@@ -208,7 +192,7 @@ func (h *Handler) SacuvajIzmenuKlijenta(w http.ResponseWriter, r *http.Request)
if greska != "" {
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
klijent.ID = id
renderujFormuKlijenta(w, PodaciFormeKlijenta{
h.renderujFormuKlijenta(w, PodaciFormeKlijenta{
PodaciStranice: model.PodaciStranice{
Stranica: "klijenti",
NaslovStranice: "Izmeni klijenta",
@@ -230,7 +214,7 @@ func (h *Handler) SacuvajIzmenuKlijenta(w http.ResponseWriter, r *http.Request)
if err := h.KlijentiRepo.Izmeni(r.Context(), &klijent); err != nil {
log.Printf("greška pri čuvanju izmene klijenta: %v", err)
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
renderujFormuKlijenta(w, PodaciFormeKlijenta{
h.renderujFormuKlijenta(w, PodaciFormeKlijenta{
PodaciStranice: model.PodaciStranice{
Stranica: "klijenti",
NaslovStranice: "Izmeni klijenta",
@@ -293,21 +277,6 @@ func parseFormuKlijenta(r *http.Request) (model.Klijent, string) {
}
// renderujFormuKlijenta renderuje HTML šablon forme za unos ili izmenu klijenta
func renderujFormuKlijenta(w http.ResponseWriter, podaci PodaciFormeKlijenta) {
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/klijent_forma.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
func (h *Handler) renderujFormuKlijenta(w http.ResponseWriter, podaci PodaciFormeKlijenta) {
h.renderujTemplate(w, "klijent_forma", podaci)
}
+1 -19
View File
@@ -1,8 +1,6 @@
package handler
import (
"html/template"
"log"
"net/http"
"strconv"
@@ -77,23 +75,7 @@ func (h *Handler) Magacin(w http.ResponseWriter, r *http.Request) {
Obrisan: r.URL.Query().Get("obrisan") == "1",
}
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/magacin.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
return
}
h.renderujTemplate(w, "magacin", podaci)
}
// ObrisiArtikal briše artikal po ID-u
+6 -23
View File
@@ -2,8 +2,6 @@ package handler
import (
"fmt"
"html/template"
"log"
"net/http"
"strconv"
@@ -52,7 +50,7 @@ func (h *Handler) NoviArtikal(w http.ResponseWriter, r *http.Request) {
Izmena: false,
}
renderujFormuArtikla(w, podaci)
h.renderujFormuArtikla(w, podaci)
}
// SacuvajArtikal prima POST formu i čuva novi artikal
@@ -70,7 +68,7 @@ func (h *Handler) SacuvajArtikal(w http.ResponseWriter, r *http.Request) {
if artikal.KategorijaID != nil {
katIDStr = strconv.FormatInt(*artikal.KategorijaID, 10)
}
renderujFormuArtikla(w, PodaciFormeArtikla{
h.renderujFormuArtikla(w, PodaciFormeArtikla{
PodaciStranice: model.PodaciStranice{
Stranica: "magacin",
NaslovStranice: "Novi artikal",
@@ -155,7 +153,7 @@ func (h *Handler) IzmeniArtikal(w http.ResponseWriter, r *http.Request) {
Izmena: true,
}
renderujFormuArtikla(w, podaci)
h.renderujFormuArtikla(w, podaci)
}
// SacuvajIzmenuArtikla prima POST formu i čuva izmenu artikla
@@ -181,7 +179,7 @@ func (h *Handler) SacuvajIzmenuArtikla(w http.ResponseWriter, r *http.Request) {
if artikal.KategorijaID != nil {
katIDStr = strconv.FormatInt(*artikal.KategorijaID, 10)
}
renderujFormuArtikla(w, PodaciFormeArtikla{
h.renderujFormuArtikla(w, PodaciFormeArtikla{
PodaciStranice: model.PodaciStranice{
Stranica: "magacin",
NaslovStranice: "Izmeni artikal",
@@ -258,21 +256,6 @@ func parseFormuArtikla(r *http.Request) (model.Artikal, string) {
}
// renderujFormuArtikla renderuje HTML formu za artikal
func renderujFormuArtikla(w http.ResponseWriter, podaci PodaciFormeArtikla) {
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/magacin_forma.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
func (h *Handler) renderujFormuArtikla(w http.ResponseWriter, podaci PodaciFormeArtikla) {
h.renderujTemplate(w, "magacin_forma", podaci)
}
+6 -52
View File
@@ -3,7 +3,6 @@ package handler
import (
"encoding/json"
"html/template"
"log"
"net/http"
"strconv"
"strings"
@@ -85,22 +84,7 @@ func (h *Handler) Nabavke(w http.ResponseWriter, r *http.Request) {
Obrisan: r.URL.Query().Get("obrisan") == "1",
}
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/nabavke.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
h.renderujTemplate(w, "nabavke", podaci)
}
// NovaNabavka prikazuje formu za unos nove nabavke
@@ -129,7 +113,7 @@ func (h *Handler) NovaNabavka(w http.ResponseWriter, r *http.Request) {
return
}
renderujFormuNabavke(w, PodaciFormeNabavke{
h.renderujFormuNabavke(w, PodaciFormeNabavke{
PodaciStranice: model.PodaciStranice{
Stranica: "nabavke",
NaslovStranice: "Nova nabavka",
@@ -160,7 +144,7 @@ func (h *Handler) SacuvajNabavku(w http.ResponseWriter, r *http.Request) {
artikli, _ := h.Artikli.Lista(r.Context(), db.ArtikalFilter{})
dobavljaci, _ := h.DobavljaciRepo.Lista(r.Context(), "")
kategorije, _ := h.KategorijeRepo.Lista(r.Context())
renderujFormuNabavke(w, PodaciFormeNabavke{
h.renderujFormuNabavke(w, PodaciFormeNabavke{
PodaciStranice: model.PodaciStranice{
Stranica: "nabavke",
NaslovStranice: "Nova nabavka",
@@ -240,22 +224,7 @@ func (h *Handler) DetaljiNabavke(w http.ResponseWriter, r *http.Request) {
DobavljacNaziv: dobavljacNaziv,
}
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/nabavka_detalji.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
h.renderujTemplate(w, "nabavka_detalji", podaci)
}
// ObrisiNabavku prima POST zahtev i briše nabavku po ID-u
@@ -328,21 +297,6 @@ func parseFormuNabavke(r *http.Request) (model.Nabavka, []model.StavkaNabavke, s
}
// renderujFormuNabavke renderuje HTML šablon forme za unos nove nabavke
func renderujFormuNabavke(w http.ResponseWriter, podaci PodaciFormeNabavke) {
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/nabavka_forma.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
func (h *Handler) renderujFormuNabavke(w http.ResponseWriter, podaci PodaciFormeNabavke) {
h.renderujTemplate(w, "nabavka_forma", podaci)
}
+85 -17
View File
@@ -2,9 +2,12 @@ package handler
import (
"fmt"
"html/template"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"ntech/internal/db/sqlite"
@@ -25,6 +28,8 @@ type PodaciPodesavanja struct {
LogoPutanja string
Tema string
Sacuvano bool
Verzija string
LogoGreska string
}
// Podesavanja renderuje stranicu podešavanja
@@ -55,23 +60,11 @@ func (h *Handler) Podesavanja(w http.ResponseWriter, r *http.Request) {
LogoPutanja: podesavanja["logo_putanja"],
Tema: podesavanja["tema"],
Sacuvano: r.URL.Query().Get("sacuvano") == "1",
Verzija: h.Verzija,
LogoGreska: r.URL.Query().Get("logo_greska"),
}
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/podesavanja.html",
)
if err != nil {
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
return
}
h.renderujTemplate(w, "podesavanja", podaci)
}
// SacuvajPodesavanja prima POST i čuva podešavanja u bazu
@@ -120,13 +113,88 @@ func (h *Handler) BackupBaze(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, privremeni)
}
// OtpremiLogo prima multipart upload slike loga i čuva je u web/static/uploads/
func (h *Handler) OtpremiLogo(w http.ResponseWriter, r *http.Request) {
if err := r.ParseMultipartForm(2 << 20); err != nil {
http.Redirect(w, r, "/podesavanja?logo_greska=Fajl+je+prevelik+%28maksimum+2+MB%29", http.StatusSeeOther)
return
}
fajl, zaglavlje, err := r.FormFile("logo")
if err != nil {
http.Redirect(w, r, "/podesavanja?logo_greska=Nije+odabran+fajl", http.StatusSeeOther)
return
}
defer fajl.Close()
// proveravamo ekstenziju
ext := strings.ToLower(filepath.Ext(zaglavlje.Filename))
dozvoljenoExt := map[string]string{
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".svg": "image/svg+xml",
}
ocekivaniMime, ok := dozvoljenoExt[ext]
if !ok {
http.Redirect(w, r, "/podesavanja?logo_greska=Dozvoljeni+formati+su+PNG%2C+JPG+i+SVG", http.StatusSeeOther)
return
}
// za binarne formate proveravamo i stvarni tip fajla (SVG je tekstualni, preskačemo)
if ext != ".svg" {
buf := make([]byte, 512)
n, _ := fajl.Read(buf)
stvarniMime := http.DetectContentType(buf[:n])
if !strings.HasPrefix(stvarniMime, ocekivaniMime) {
http.Redirect(w, r, "/podesavanja?logo_greska=Sadržaj+fajla+ne+odgovara+odabranoj+ekstenziji", http.StatusSeeOther)
return
}
// vraćamo kursor na početak
if _, err := fajl.Seek(0, io.SeekStart); err != nil {
http.Redirect(w, r, "/podesavanja?logo_greska=Greška+pri+obradi+fajla", http.StatusSeeOther)
return
}
}
// brišemo stare logo fajlove
stari, _ := filepath.Glob("web/static/uploads/logo.*")
for _, s := range stari {
os.Remove(s)
}
odrediste := "web/static/uploads/logo" + ext
dst, err := os.Create(odrediste)
if err != nil {
log.Printf("upload loga: ne mogu kreirati fajl: %v", err)
http.Redirect(w, r, "/podesavanja?logo_greska=Greška+pri+čuvanju+fajla", http.StatusSeeOther)
return
}
defer dst.Close()
if _, err := io.Copy(dst, fajl); err != nil {
log.Printf("upload loga: greška pri kopiranju: %v", err)
http.Redirect(w, r, "/podesavanja?logo_greska=Greška+pri+čuvanju+fajla", http.StatusSeeOther)
return
}
putanja := "/static/uploads/logo" + ext
if err := sqlite.SacuvajPodesavanje(r.Context(), h.DB, "logo_putanja", putanja); err != nil {
log.Printf("upload loga: greška pri čuvanju putanje: %v", err)
http.Redirect(w, r, "/podesavanja?logo_greska=Greška+pri+čuvanju+podešavanja", http.StatusSeeOther)
return
}
http.Redirect(w, r, "/podesavanja?sacuvano=1", http.StatusSeeOther)
}
// PromeniTemu menja aktivnu temu i vraća na prethodnu stranicu
func (h *Handler) PromeniTemu(w http.ResponseWriter, r *http.Request) {
tema := chi.URLParam(r, "tema")
// proveravamo da li je validna tema
validne := map[string]bool{
"tamna": true, "svetla": true, "zelena": true, "ljubicasta": true,
"tamna": true, "svetla": true,
}
if !validne[tema] {
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
+267
View File
@@ -0,0 +1,267 @@
package handler
import (
"log"
"net/http"
"strings"
"time"
"ntech/internal/db"
"ntech/internal/db/sqlite"
"ntech/internal/model"
"github.com/go-chi/chi/v5"
)
// podaciPodsetnici su podaci za stranicu sa listom podsetnika
type podaciPodsetnici struct {
model.PodaciStranice
Podsetnici []model.Podsetnik
SamoAktivni bool
Sacuvano bool
Obrisan bool
}
// podaciPodsetnikForma su podaci za formu novog/izmenjenog podsetnika
type podaciPodsetnikForma struct {
model.PodaciStranice
Podsetnik model.Podsetnik
Greska string
Izmena bool
}
// Podsetnici renderuje listu podsetnika
func (h *Handler) Podsetnici(w http.ResponseWriter, r *http.Request) {
podesavanja, err := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
if err != nil {
http.Error(w, "Greška pri učitavanju podešavanja", http.StatusInternalServerError)
return
}
samoAktivni := r.URL.Query().Get("samo_aktivni") == "1"
lista, err := h.PodsetniciFRepo.Lista(r.Context(), db.PodsetnikFilter{SamoAktivni: samoAktivni})
if err != nil {
http.Error(w, "Greška pri učitavanju podsetnika", http.StatusInternalServerError)
return
}
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "podsetnici"
ps.NaslovStranice = "Podsetnici"
podaci := podaciPodsetnici{
PodaciStranice: ps,
Podsetnici: lista,
SamoAktivni: samoAktivni,
Sacuvano: r.URL.Query().Get("sacuvano") == "1",
Obrisan: r.URL.Query().Get("obrisan") == "1",
}
h.renderujTemplate(w, "podsetnici", podaci)
}
// NoviPodsetnik prikazuje praznu formu za unos novog podsetnika
func (h *Handler) NoviPodsetnik(w http.ResponseWriter, r *http.Request) {
podesavanja, err := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
if err != nil {
http.Error(w, "Greška pri učitavanju podešavanja", http.StatusInternalServerError)
return
}
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "podsetnici"
ps.NaslovStranice = "Novi podsetnik"
h.renderujFormuPodsetnika(w, podaciPodsetnikForma{
PodaciStranice: ps,
Izmena: false,
})
}
// SacuvajPodsetnik prima POST formu i upisuje novi podsetnik u bazu
func (h *Handler) SacuvajPodsetnik(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
http.Error(w, "Greška pri čitanju forme", http.StatusBadRequest)
return
}
podsetnik, greska := parseFormuPodsetnika(r)
if greska != "" {
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "podsetnici"
ps.NaslovStranice = "Novi podsetnik"
h.renderujFormuPodsetnika(w, podaciPodsetnikForma{
PodaciStranice: ps,
Podsetnik: podsetnik,
Greska: greska,
Izmena: false,
})
return
}
if _, err := h.PodsetniciFRepo.Kreiraj(r.Context(), &podsetnik); err != nil {
log.Printf("greška pri čuvanju podsetnika: %v", err)
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "podsetnici"
ps.NaslovStranice = "Novi podsetnik"
h.renderujFormuPodsetnika(w, podaciPodsetnikForma{
PodaciStranice: ps,
Podsetnik: podsetnik,
Greska: "Došlo je do greške pri čuvanju. Pokušajte ponovo.",
Izmena: false,
})
return
}
http.Redirect(w, r, "/podsetnici?sacuvano=1", http.StatusSeeOther)
}
// IzmeniPodsetnik učitava podsetnik po ID-u i prikazuje popunjenu formu za izmenu
func (h *Handler) IzmeniPodsetnik(w http.ResponseWriter, r *http.Request) {
id, err := parseID(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Neispravan ID podsetnika", http.StatusBadRequest)
return
}
podsetnik, err := h.PodsetniciFRepo.DohvatiID(r.Context(), id)
if err != nil {
http.Error(w, "Podsetnik nije pronađen", http.StatusNotFound)
return
}
podesavanja, err := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
if err != nil {
http.Error(w, "Greška pri učitavanju podešavanja", http.StatusInternalServerError)
return
}
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "podsetnici"
ps.NaslovStranice = "Izmeni podsetnik"
h.renderujFormuPodsetnika(w, podaciPodsetnikForma{
PodaciStranice: ps,
Podsetnik: *podsetnik,
Izmena: true,
})
}
// SacuvajIzmenePodsetnika prima POST formu i ažurira postojeći podsetnik u bazi
func (h *Handler) SacuvajIzmenePodsetnika(w http.ResponseWriter, r *http.Request) {
id, err := parseID(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Neispravan ID podsetnika", http.StatusBadRequest)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, "Greška pri čitanju forme", http.StatusBadRequest)
return
}
podsetnik, greska := parseFormuPodsetnika(r)
podsetnik.ID = id
if greska != "" {
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "podsetnici"
ps.NaslovStranice = "Izmeni podsetnik"
h.renderujFormuPodsetnika(w, podaciPodsetnikForma{
PodaciStranice: ps,
Podsetnik: podsetnik,
Greska: greska,
Izmena: true,
})
return
}
if err := h.PodsetniciFRepo.Izmeni(r.Context(), &podsetnik); err != nil {
log.Printf("greška pri čuvanju izmene podsetnika: %v", err)
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "podsetnici"
ps.NaslovStranice = "Izmeni podsetnik"
h.renderujFormuPodsetnika(w, podaciPodsetnikForma{
PodaciStranice: ps,
Podsetnik: podsetnik,
Greska: "Došlo je do greške pri čuvanju. Pokušajte ponovo.",
Izmena: true,
})
return
}
http.Redirect(w, r, "/podsetnici?sacuvano=1", http.StatusSeeOther)
}
// OznaciPodsetnik prima POST zahtev i menja status završenosti podsetnika
func (h *Handler) OznaciPodsetnik(w http.ResponseWriter, r *http.Request) {
id, err := parseID(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Neispravan ID podsetnika", http.StatusBadRequest)
return
}
// učitamo trenutni status pa ga preokrenemo
podsetnik, err := h.PodsetniciFRepo.DohvatiID(r.Context(), id)
if err != nil {
http.Error(w, "Podsetnik nije pronađen", http.StatusNotFound)
return
}
if err := h.PodsetniciFRepo.OznaciZavrsenim(r.Context(), id, !podsetnik.Zavrseno); err != nil {
http.Error(w, "Greška pri ažuriranju statusa", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/podsetnici", http.StatusSeeOther)
}
// ObrisiPodsetnik prima POST zahtev i briše podsetnik po ID-u
func (h *Handler) ObrisiPodsetnik(w http.ResponseWriter, r *http.Request) {
id, err := parseID(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Neispravan ID podsetnika", http.StatusBadRequest)
return
}
if err := h.PodsetniciFRepo.Obrisi(r.Context(), id); err != nil {
http.Error(w, "Greška pri brisanju podsetnika", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/podsetnici?obrisan=1", http.StatusSeeOther)
}
// parseFormuPodsetnika čita polja iz HTTP forme, validira ih i vraća model i eventualnu grešku
func parseFormuPodsetnika(r *http.Request) (model.Podsetnik, string) {
naslov := strings.TrimSpace(r.FormValue("naslov"))
if naslov == "" {
return model.Podsetnik{}, "Naslov je obavezan."
}
datumStr := strings.TrimSpace(r.FormValue("datum_podsecanja"))
if datumStr == "" {
return model.Podsetnik{Naslov: naslov}, "Datum podsećanja je obavezan."
}
datum, err := time.Parse("2006-01-02", datumStr)
if err != nil {
return model.Podsetnik{Naslov: naslov}, "Datum podsećanja nije u ispravnom formatu."
}
return model.Podsetnik{
Naslov: naslov,
Napomena: strings.TrimSpace(r.FormValue("napomena")),
DatumPodsecanja: datum,
Tip: model.TipOpsti,
}, ""
}
// renderujFormuPodsetnika renderuje HTML šablon forme za unos ili izmenu podsetnika
func (h *Handler) renderujFormuPodsetnika(w http.ResponseWriter, podaci podaciPodsetnikForma) {
h.renderujTemplate(w, "podsetnik_forma", podaci)
}
+3 -14
View File
@@ -1,7 +1,6 @@
package handler
import (
"html/template"
"net/http"
"time"
@@ -22,7 +21,7 @@ func (h *Handler) PrikazPrijave(w http.ResponseWriter, r *http.Request) {
}
greska := r.URL.Query().Get("greska")
renderujStandaloneTemplate(w, "web/templates/stranice/prijava.html", map[string]any{
h.renderujStandalone(w, "prijava", map[string]any{
"Greska": greska,
})
}
@@ -84,7 +83,7 @@ func (h *Handler) PrikazTotp(w http.ResponseWriter, r *http.Request) {
}
greska := r.URL.Query().Get("greska")
renderujStandaloneTemplate(w, "web/templates/stranice/totp_provera.html", map[string]any{
h.renderujStandalone(w, "totp_provera", map[string]any{
"Greska": greska,
})
}
@@ -133,7 +132,7 @@ func (h *Handler) PrikazSetupa(w http.ResponseWriter, r *http.Request) {
return
}
greska := r.URL.Query().Get("greska")
renderujStandaloneTemplate(w, "web/templates/stranice/setup.html", map[string]any{
h.renderujStandalone(w, "setup", map[string]any{
"Greska": greska,
})
}
@@ -200,13 +199,3 @@ func napraviKolacic(token string, istice time.Time) *http.Cookie {
}
}
func renderujStandaloneTemplate(w http.ResponseWriter, putanja string, podaci any) {
tmpl, err := template.ParseFiles(putanja)
if err != nil {
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.Execute(w, podaci); err != nil {
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
}
+7 -62
View File
@@ -105,22 +105,7 @@ func (h *Handler) Prodaja(w http.ResponseWriter, r *http.Request) {
Pretraga: pretraga,
}
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/prodaja.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
h.renderujTemplate(w, "prodaja", podaci)
}
// NovaProdaja prikazuje formu za unos novog prodajnog naloga
@@ -143,7 +128,7 @@ func (h *Handler) NovaProdaja(w http.ResponseWriter, r *http.Request) {
return
}
renderujFormuProdaje(w, PodaciFormeProdaje{
h.renderujFormuProdaje(w, PodaciFormeProdaje{
PodaciStranice: model.PodaciStranice{
Stranica: "prodaja",
NaslovStranice: "Nova prodaja",
@@ -173,7 +158,7 @@ func (h *Handler) SacuvajProdaju(w http.ResponseWriter, r *http.Request) {
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
artikli, _ := h.Artikli.Lista(r.Context(), appdb.ArtikalFilter{})
klijenti, _ := h.KlijentiRepo.Lista(r.Context(), "")
renderujFormuProdaje(w, PodaciFormeProdaje{
h.renderujFormuProdaje(w, PodaciFormeProdaje{
PodaciStranice: model.PodaciStranice{
Stranica: "prodaja",
NaslovStranice: "Nova prodaja",
@@ -282,22 +267,7 @@ func (h *Handler) DetaljiProdaje(w http.ResponseWriter, r *http.Request) {
Sacuvano: r.URL.Query().Get("sacuvano") == "1",
}
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/prodaja_detalji.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
h.renderujTemplate(w, "prodaja_detalji", podaci)
}
// StampaProdaje renderuje print-friendly stranicu za dati prodajni nalog
@@ -349,17 +319,7 @@ func (h *Handler) StampaProdaje(w http.ResponseWriter, r *http.Request) {
PIB: podesavanja["pib"],
}
tmpl, err := template.ParseFiles("web/templates/stranice/prodaja_stampa.html")
if err != nil {
log.Printf("greška pri učitavanju šablona za štampu: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "prodaja_stampa.html", podaci); err != nil {
log.Printf("greška pri renderovanju štampe: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
h.renderujStandalone(w, "prodaja_stampa", podaci)
}
// ObrisiProdaju prima POST zahtev, vraća stanje na magacin i briše nalog
@@ -430,21 +390,6 @@ func parseFormuProdaje(r *http.Request) (model.ProdajniNalog, []model.StavkaProd
}
// renderujFormuProdaje renderuje HTML šablon forme za unos nove prodaje
func renderujFormuProdaje(w http.ResponseWriter, podaci PodaciFormeProdaje) {
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/prodaja_forma.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
func (h *Handler) renderujFormuProdaje(w http.ResponseWriter, podaci PodaciFormeProdaje) {
h.renderujTemplate(w, "prodaja_forma", podaci)
}
+10 -56
View File
@@ -1,7 +1,6 @@
package handler
import (
"html/template"
"log"
"net/http"
"strconv"
@@ -79,22 +78,7 @@ func (h *Handler) Servis(w http.ResponseWriter, r *http.Request) {
Obrisan: r.URL.Query().Get("obrisan") == "1",
}
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/servis.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
h.renderujTemplate(w, "servis", podaci)
}
// NoviNalog generiše broj naloga i prikazuje praznu formu za unos
@@ -117,7 +101,7 @@ func (h *Handler) NoviNalog(w http.ResponseWriter, r *http.Request) {
return
}
renderujFormuNaloga(w, PodaciFormeNaloga{
h.renderujFormuNaloga(w, PodaciFormeNaloga{
PodaciStranice: model.PodaciStranice{
Stranica: "servis",
NaslovStranice: "Novi nalog",
@@ -146,7 +130,7 @@ func (h *Handler) SacuvajNalog(w http.ResponseWriter, r *http.Request) {
if greska != "" {
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
klijenti, _ := h.KlijentiRepo.Lista(r.Context(), "")
renderujFormuNaloga(w, PodaciFormeNaloga{
h.renderujFormuNaloga(w, PodaciFormeNaloga{
PodaciStranice: model.PodaciStranice{
Stranica: "servis",
NaslovStranice: "Novi nalog",
@@ -171,7 +155,7 @@ func (h *Handler) SacuvajNalog(w http.ResponseWriter, r *http.Request) {
log.Printf("greška pri čuvanju naloga: %v", err)
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
klijenti, _ := h.KlijentiRepo.Lista(r.Context(), "")
renderujFormuNaloga(w, PodaciFormeNaloga{
h.renderujFormuNaloga(w, PodaciFormeNaloga{
PodaciStranice: model.PodaciStranice{
Stranica: "servis",
NaslovStranice: "Novi nalog",
@@ -220,7 +204,7 @@ func (h *Handler) IzmeniNalog(w http.ResponseWriter, r *http.Request) {
return
}
renderujFormuNaloga(w, PodaciFormeNaloga{
h.renderujFormuNaloga(w, PodaciFormeNaloga{
PodaciStranice: model.PodaciStranice{
Stranica: "servis",
NaslovStranice: "Izmeni nalog",
@@ -256,7 +240,7 @@ func (h *Handler) SacuvajIzmenaNaloga(w http.ResponseWriter, r *http.Request) {
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
klijenti, _ := h.KlijentiRepo.Lista(r.Context(), "")
nalog.ID = id
renderujFormuNaloga(w, PodaciFormeNaloga{
h.renderujFormuNaloga(w, PodaciFormeNaloga{
PodaciStranice: model.PodaciStranice{
Stranica: "servis",
NaslovStranice: "Izmeni nalog",
@@ -281,7 +265,7 @@ func (h *Handler) SacuvajIzmenaNaloga(w http.ResponseWriter, r *http.Request) {
log.Printf("greška pri čuvanju izmene naloga: %v", err)
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
klijenti, _ := h.KlijentiRepo.Lista(r.Context(), "")
renderujFormuNaloga(w, PodaciFormeNaloga{
h.renderujFormuNaloga(w, PodaciFormeNaloga{
PodaciStranice: model.PodaciStranice{
Stranica: "servis",
NaslovStranice: "Izmeni nalog",
@@ -368,22 +352,7 @@ func (h *Handler) DetaljiNaloga(w http.ResponseWriter, r *http.Request) {
Sacuvano: r.URL.Query().Get("sacuvano") == "1",
}
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/servis_detalji.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
h.renderujTemplate(w, "servis_detalji", podaci)
}
// parseFormuNaloga čita i validira polja iz HTTP forme
@@ -448,21 +417,6 @@ func parseOpcionuCenu(s string) *float64 {
}
// renderujFormuNaloga renderuje HTML šablon forme za unos ili izmenu servisnog naloga
func renderujFormuNaloga(w http.ResponseWriter, podaci PodaciFormeNaloga) {
tmpl, err := template.ParseFiles(
"web/templates/teme/podrazumevana/base.html",
"web/templates/komponente/sidebar.html",
"web/templates/komponente/topbar.html",
"web/templates/stranice/servis_forma.html",
)
if err != nil {
log.Printf("greška pri učitavanju šablona: %v", err)
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
return
}
if err := tmpl.ExecuteTemplate(w, "base", podaci); err != nil {
log.Printf("greška pri renderovanju: %v", err)
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
}
func (h *Handler) renderujFormuNaloga(w http.ResponseWriter, podaci PodaciFormeNaloga) {
h.renderujTemplate(w, "servis_forma", podaci)
}