Optimizacije — SQLite WAL, template keš, gzip kompresija, build skript
This commit is contained in:
@@ -33,7 +33,7 @@ func NadjiSlobodanPort() int {
|
||||
|
||||
// proverava da li je ovo prvo pokretanje programa
|
||||
func JelPrvoPokretanje() bool {
|
||||
_, err := os.Stat(".env")
|
||||
_, err := os.Stat("ntech.env")
|
||||
return os.IsNotExist(err)
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ func StatusPortova() []PortStatus {
|
||||
return rezultat
|
||||
}
|
||||
|
||||
// SacuvajEnv upisuje izabrani port u .env fajl
|
||||
// SacuvajEnv upisuje izabrani port u ntech.env fajl
|
||||
func SacuvajEnv(port int) error {
|
||||
sadrzaj := fmt.Sprintf("NTECH_PORT=%d\n", port)
|
||||
return os.WriteFile(".env", []byte(sadrzaj), 0600)
|
||||
return os.WriteFile("ntech.env", []byte(sadrzaj), 0600)
|
||||
}
|
||||
|
||||
@@ -97,3 +97,19 @@ type SesijeRepository interface {
|
||||
Obrisi(ctx context.Context, token string) error
|
||||
ObrisiIstekle(ctx context.Context) error
|
||||
}
|
||||
|
||||
// PodsetnikFilter definiše parametre za filtriranje liste podsetnika
|
||||
type PodsetnikFilter struct {
|
||||
SamoAktivni bool // true = samo nezavršeni; false = svi
|
||||
}
|
||||
|
||||
// PodsetnikRepository definiše operacije nad podsetnicima
|
||||
type PodsetnikRepository interface {
|
||||
Lista(ctx context.Context, filter PodsetnikFilter) ([]model.Podsetnik, error)
|
||||
DohvatiID(ctx context.Context, id int64) (*model.Podsetnik, error)
|
||||
Kreiraj(ctx context.Context, p *model.Podsetnik) (int64, error)
|
||||
Izmeni(ctx context.Context, p *model.Podsetnik) error
|
||||
OznaciZavrsenim(ctx context.Context, id int64, zavrseno bool) error
|
||||
Obrisi(ctx context.Context, id int64) error
|
||||
BrojAktivnih(ctx context.Context) (int, error)
|
||||
}
|
||||
|
||||
@@ -10,16 +10,23 @@ import (
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
// OtvoriDB otvara konekciju ka SQLite bazi i uključuje strane ključeve
|
||||
// OtvoriDB otvara konekciju ka SQLite bazi i primenjuje performance PRAGMA podešavanja
|
||||
func OtvoriDB(putanja string) (*sql.DB, error) {
|
||||
db, err := sql.Open("sqlite", putanja)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ntech: OtvoriDB: %w", err)
|
||||
}
|
||||
|
||||
// uključujemo podršku za strane ključeve — SQLite je ne uključuje automatski
|
||||
if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil {
|
||||
return nil, fmt.Errorf("ntech: OtvoriDB: foreign_keys: %w", err)
|
||||
pragme := []string{
|
||||
"PRAGMA journal_mode=WAL",
|
||||
"PRAGMA synchronous=NORMAL",
|
||||
"PRAGMA cache_size=10000",
|
||||
"PRAGMA foreign_keys=ON",
|
||||
}
|
||||
for _, p := range pragme {
|
||||
if _, err := db.Exec(p); err != nil {
|
||||
return nil, fmt.Errorf("ntech: OtvoriDB: %s: %w", p, err)
|
||||
}
|
||||
}
|
||||
|
||||
return db, nil
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"ntech/internal/db"
|
||||
"ntech/internal/model"
|
||||
)
|
||||
|
||||
// PodsetnikRepo je SQLite implementacija PodsetnikRepository interfejsa
|
||||
type PodsetnikRepo struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// NoviPodsetnikRepo kreira novi PodsetnikRepo
|
||||
func NoviPodsetnikRepo(db *sql.DB) *PodsetnikRepo {
|
||||
return &PodsetnikRepo{db: db}
|
||||
}
|
||||
|
||||
// Lista vraća listu podsetnika sa opcionim filterom
|
||||
func (r *PodsetnikRepo) Lista(ctx context.Context, filter db.PodsetnikFilter) ([]model.Podsetnik, error) {
|
||||
upit := `
|
||||
SELECT id, naslov, napomena, datum_podsecanja, zavrseno, tip, datum_unosa
|
||||
FROM podsetnici
|
||||
WHERE 1=1`
|
||||
|
||||
if filter.SamoAktivni {
|
||||
upit += " AND zavrseno = 0"
|
||||
}
|
||||
|
||||
upit += " ORDER BY datum_podsecanja ASC"
|
||||
|
||||
redovi, err := r.db.QueryContext(ctx, upit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ntech: PodsetnikRepo.Lista: %w", err)
|
||||
}
|
||||
defer redovi.Close()
|
||||
|
||||
var rezultat []model.Podsetnik
|
||||
for redovi.Next() {
|
||||
var p model.Podsetnik
|
||||
var napomena sql.NullString
|
||||
var zavrseno int
|
||||
err := redovi.Scan(
|
||||
&p.ID, &p.Naslov, &napomena, &p.DatumPodsecanja, &zavrseno, &p.Tip, &p.DatumUnosa,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ntech: PodsetnikRepo.Lista: scan: %w", err)
|
||||
}
|
||||
p.Napomena = napomena.String
|
||||
p.Zavrseno = zavrseno == 1
|
||||
rezultat = append(rezultat, p)
|
||||
}
|
||||
|
||||
return rezultat, nil
|
||||
}
|
||||
|
||||
// DohvatiID vraća jedan podsetnik po ID-u
|
||||
func (r *PodsetnikRepo) DohvatiID(ctx context.Context, id int64) (*model.Podsetnik, error) {
|
||||
var p model.Podsetnik
|
||||
var napomena sql.NullString
|
||||
var zavrseno int
|
||||
|
||||
err := r.db.QueryRowContext(ctx, `
|
||||
SELECT id, naslov, napomena, datum_podsecanja, zavrseno, tip, datum_unosa
|
||||
FROM podsetnici WHERE id = ?`, id).Scan(
|
||||
&p.ID, &p.Naslov, &napomena, &p.DatumPodsecanja, &zavrseno, &p.Tip, &p.DatumUnosa,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ntech: PodsetnikRepo.DohvatiID: %w", err)
|
||||
}
|
||||
|
||||
p.Napomena = napomena.String
|
||||
p.Zavrseno = zavrseno == 1
|
||||
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
// Kreiraj dodaje novi podsetnik u bazu
|
||||
func (r *PodsetnikRepo) Kreiraj(ctx context.Context, p *model.Podsetnik) (int64, error) {
|
||||
rezultat, err := r.db.ExecContext(ctx, `
|
||||
INSERT INTO podsetnici (naslov, napomena, datum_podsecanja, tip)
|
||||
VALUES (?, ?, ?, ?)`,
|
||||
p.Naslov, nullString(p.Napomena), p.DatumPodsecanja, p.Tip,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("ntech: PodsetnikRepo.Kreiraj: %w", err)
|
||||
}
|
||||
|
||||
id, err := rezultat.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("ntech: PodsetnikRepo.Kreiraj: last insert id: %w", err)
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// Izmeni ažurira postojeći podsetnik
|
||||
func (r *PodsetnikRepo) Izmeni(ctx context.Context, p *model.Podsetnik) error {
|
||||
_, err := r.db.ExecContext(ctx, `
|
||||
UPDATE podsetnici SET
|
||||
naslov = ?, napomena = ?, datum_podsecanja = ?, tip = ?
|
||||
WHERE id = ?`,
|
||||
p.Naslov, nullString(p.Napomena), p.DatumPodsecanja, p.Tip, p.ID,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ntech: PodsetnikRepo.Izmeni: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OznaciZavrsenim postavlja ili uklanja oznaku završenosti
|
||||
func (r *PodsetnikRepo) OznaciZavrsenim(ctx context.Context, id int64, zavrseno bool) error {
|
||||
val := 0
|
||||
if zavrseno {
|
||||
val = 1
|
||||
}
|
||||
_, err := r.db.ExecContext(ctx,
|
||||
"UPDATE podsetnici SET zavrseno = ? WHERE id = ?", val, id,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ntech: PodsetnikRepo.OznaciZavrsenim: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Obrisi briše podsetnik po ID-u
|
||||
func (r *PodsetnikRepo) Obrisi(ctx context.Context, id int64) error {
|
||||
_, err := r.db.ExecContext(ctx, "DELETE FROM podsetnici WHERE id = ?", id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ntech: PodsetnikRepo.Obrisi: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BrojAktivnih vraća broj nezavršenih podsetnika
|
||||
func (r *PodsetnikRepo) BrojAktivnih(ctx context.Context) (int, error) {
|
||||
var broj int
|
||||
err := r.db.QueryRowContext(ctx,
|
||||
"SELECT COUNT(*) FROM podsetnici WHERE zavrseno = 0",
|
||||
).Scan(&broj)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("ntech: PodsetnikRepo.BrojAktivnih: %w", err)
|
||||
}
|
||||
|
||||
return broj, nil
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// TipOpsti je jedini trenutno podržani tip podsetnika
|
||||
const TipOpsti = "opsti"
|
||||
|
||||
// Podsetnik predstavlja jedan podsetnik
|
||||
type Podsetnik struct {
|
||||
ID int64
|
||||
Naslov string
|
||||
Napomena string
|
||||
DatumPodsecanja time.Time
|
||||
Zavrseno bool
|
||||
Tip string
|
||||
DatumUnosa time.Time
|
||||
}
|
||||
|
||||
// JePrekoracen vraća true ako datum podsećanja je prošao a podsetnik nije završen
|
||||
func (p Podsetnik) JePrekoracen() bool {
|
||||
return !p.Zavrseno && p.DatumPodsecanja.Before(time.Now())
|
||||
}
|
||||
@@ -40,11 +40,12 @@ type PodaciStranice struct {
|
||||
// PodaciDashboarda su podaci specifični za dashboard stranicu
|
||||
type PodaciDashboarda struct {
|
||||
PodaciStranice
|
||||
BrojArtikala int
|
||||
AktivniServisi int
|
||||
PrihodOvogMeseca float64
|
||||
KriticnaZaliha int
|
||||
PoslednjiServisi []StavkaServisa
|
||||
KriticneZalihe []StavkaZalihe
|
||||
PoslednjeProdaje []StavkaProdajePregled
|
||||
BrojArtikala int
|
||||
AktivniServisi int
|
||||
PrihodOvogMeseca float64
|
||||
KriticnaZaliha int
|
||||
AktivniPodsetnici int
|
||||
PoslednjiServisi []StavkaServisa
|
||||
KriticneZalihe []StavkaZalihe
|
||||
PoslednjeProdaje []StavkaProdajePregled
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user