Servis: javni status nalog + ispravke AJAX čuvanja
- Dodat javni token na servisni nalog (migracija 057), QR kod vodi na /status/{token}
- Nova javna stranica /status/{token} — bez prijave, za klijente
- Sve forme sa "Sačuvaj izmene" koriste ?sacuvano=1 umesto SetFlash za uspeh
- AJAX logika: toast + ostanak samo kad pathname ostaje isti; inače navigacija
- Ispravke: PDV stope, KIR, KPR, podešavanja izgled, storno prodaje, nivelacija, delovi naloga
This commit is contained in:
@@ -239,6 +239,7 @@ func main() {
|
|||||||
r.Get("/setup", h.PrikazSetupa)
|
r.Get("/setup", h.PrikazSetupa)
|
||||||
r.Post("/setup", h.SacuvajSetup)
|
r.Post("/setup", h.SacuvajSetup)
|
||||||
r.Get("/odjava", h.Odjava)
|
r.Get("/odjava", h.Odjava)
|
||||||
|
r.Get("/status/{token}", h.ServisJavniStatus)
|
||||||
|
|
||||||
// zaštićene rute — zahtevaju prijavljenog korisnika
|
// zaštićene rute — zahtevaju prijavljenog korisnika
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ type KlijentRepository interface {
|
|||||||
type ServisRepository interface {
|
type ServisRepository interface {
|
||||||
Lista(ctx context.Context, pretraga, status string) ([]model.ServisniNalogSaKlijentom, error)
|
Lista(ctx context.Context, pretraga, status string) ([]model.ServisniNalogSaKlijentom, error)
|
||||||
DohvatiID(ctx context.Context, id int64) (*model.ServisniNalog, error)
|
DohvatiID(ctx context.Context, id int64) (*model.ServisniNalog, error)
|
||||||
|
DohvatiJavniToken(ctx context.Context, token string) (*model.ServisniNalog, error)
|
||||||
Kreiraj(ctx context.Context, n *model.ServisniNalog) (int64, error)
|
Kreiraj(ctx context.Context, n *model.ServisniNalog) (int64, error)
|
||||||
Izmeni(ctx context.Context, n *model.ServisniNalog) error
|
Izmeni(ctx context.Context, n *model.ServisniNalog) error
|
||||||
AzurirajStatus(ctx context.Context, id int64, status string) error
|
AzurirajStatus(ctx context.Context, id int64, status string) error
|
||||||
|
|||||||
@@ -2,13 +2,24 @@ package sqlite
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"ntech/internal/model"
|
"ntech/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// generisiJavniToken kreira 32-znakovni hex token za javni URL
|
||||||
|
func generisiJavniToken() (string, error) {
|
||||||
|
b := make([]byte, 16)
|
||||||
|
if _, err := rand.Read(b); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
// ServisRepo je SQLite implementacija ServisRepository interfejsa
|
// ServisRepo je SQLite implementacija ServisRepository interfejsa
|
||||||
type ServisRepo struct {
|
type ServisRepo struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
@@ -43,7 +54,7 @@ func (r *ServisRepo) Lista(ctx context.Context, pretraga, status string) ([]mode
|
|||||||
sn.id, sn.klijent_id, sn.tehnicar_id, sn.broj_naloga, sn.uredjaj, sn.serijski_broj,
|
sn.id, sn.klijent_id, sn.tehnicar_id, sn.broj_naloga, sn.uredjaj, sn.serijski_broj,
|
||||||
sn.opis_kvara, sn.status, sn.cena_od, sn.cena_do, sn.cena_konacna,
|
sn.opis_kvara, sn.status, sn.cena_od, sn.cena_do, sn.cena_konacna,
|
||||||
sn.avans, sn.napomena, sn.garancija_do, sn.datum_prijema, sn.datum_zavrsetka,
|
sn.avans, sn.napomena, sn.garancija_do, sn.datum_prijema, sn.datum_zavrsetka,
|
||||||
sn.ostecenja, sn.pin_uredjaja, sn.pribor,
|
sn.ostecenja, sn.pin_uredjaja, sn.pribor, sn.javni_token,
|
||||||
COALESCE(kp.naziv, '') AS klijent_naziv
|
COALESCE(kp.naziv, '') AS klijent_naziv
|
||||||
FROM servisni_nalozi sn
|
FROM servisni_nalozi sn
|
||||||
LEFT JOIN klijent_prikaz kp ON kp.id = sn.klijent_id
|
LEFT JOIN klijent_prikaz kp ON kp.id = sn.klijent_id
|
||||||
@@ -90,7 +101,7 @@ func (r *ServisRepo) DohvatiID(ctx context.Context, id int64) (*model.ServisniNa
|
|||||||
id, klijent_id, tehnicar_id, broj_naloga, uredjaj, serijski_broj,
|
id, klijent_id, tehnicar_id, broj_naloga, uredjaj, serijski_broj,
|
||||||
opis_kvara, status, cena_od, cena_do, cena_konacna,
|
opis_kvara, status, cena_od, cena_do, cena_konacna,
|
||||||
avans, napomena, garancija_do, datum_prijema, datum_zavrsetka,
|
avans, napomena, garancija_do, datum_prijema, datum_zavrsetka,
|
||||||
ostecenja, pin_uredjaja, pribor
|
ostecenja, pin_uredjaja, pribor, javni_token
|
||||||
FROM servisni_nalozi WHERE id = ?`, id)
|
FROM servisni_nalozi WHERE id = ?`, id)
|
||||||
|
|
||||||
var n model.ServisniNalog
|
var n model.ServisniNalog
|
||||||
@@ -102,21 +113,26 @@ func (r *ServisRepo) DohvatiID(ctx context.Context, id int64) (*model.ServisniNa
|
|||||||
return &n, nil
|
return &n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kreiraj upisuje novi servisni nalog u bazu
|
// Kreiraj upisuje novi servisni nalog u bazu i generiše javni token
|
||||||
func (r *ServisRepo) Kreiraj(ctx context.Context, n *model.ServisniNalog) (int64, error) {
|
func (r *ServisRepo) Kreiraj(ctx context.Context, n *model.ServisniNalog) (int64, error) {
|
||||||
|
token, err := generisiJavniToken()
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("ntech: ServisRepo.Kreiraj: token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
rezultat, err := r.db.ExecContext(ctx, `
|
rezultat, err := r.db.ExecContext(ctx, `
|
||||||
INSERT INTO servisni_nalozi
|
INSERT INTO servisni_nalozi
|
||||||
(klijent_id, tehnicar_id, broj_naloga, uredjaj, serijski_broj, opis_kvara,
|
(klijent_id, tehnicar_id, broj_naloga, uredjaj, serijski_broj, opis_kvara,
|
||||||
status, cena_od, cena_do, cena_konacna, avans, napomena, garancija_do, datum_zavrsetka,
|
status, cena_od, cena_do, cena_konacna, avans, napomena, garancija_do, datum_zavrsetka,
|
||||||
ostecenja, pin_uredjaja, pribor, datum_prijema)
|
ostecenja, pin_uredjaja, pribor, datum_prijema, javni_token)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
nullInt64(n.KlijentID), nullInt64(n.TehnicarID), n.BrojNaloga, n.Uredjaj,
|
nullInt64(n.KlijentID), nullInt64(n.TehnicarID), n.BrojNaloga, n.Uredjaj,
|
||||||
nullString(n.SerijskiBroj), n.OpisKvara, n.Status,
|
nullString(n.SerijskiBroj), n.OpisKvara, n.Status,
|
||||||
nullFloat64(n.CenaOd), nullFloat64(n.CenaDo), nullFloat64(n.CenaKonacna),
|
nullFloat64(n.CenaOd), nullFloat64(n.CenaDo), nullFloat64(n.CenaKonacna),
|
||||||
nullFloat64(n.Avans), nullString(n.Napomena),
|
nullFloat64(n.Avans), nullString(n.Napomena),
|
||||||
nullTime(n.GarancijaDo), nullTime(n.DatumZavrsetka),
|
nullTime(n.GarancijaDo), nullTime(n.DatumZavrsetka),
|
||||||
nullString(n.Ostecenja), nullString(n.PinUredjaja), nullString(n.Pribor),
|
nullString(n.Ostecenja), nullString(n.PinUredjaja), nullString(n.Pribor),
|
||||||
n.DatumPrijema,
|
n.DatumPrijema, token,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("ntech: ServisRepo.Kreiraj: %w", err)
|
return 0, fmt.Errorf("ntech: ServisRepo.Kreiraj: %w", err)
|
||||||
@@ -130,6 +146,23 @@ func (r *ServisRepo) Kreiraj(ctx context.Context, n *model.ServisniNalog) (int64
|
|||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DohvatiJavniToken vraća servisni nalog po javnom tokenu — bez autentifikacije
|
||||||
|
func (r *ServisRepo) DohvatiJavniToken(ctx context.Context, token string) (*model.ServisniNalog, error) {
|
||||||
|
red := r.db.QueryRowContext(ctx, `
|
||||||
|
SELECT
|
||||||
|
id, klijent_id, tehnicar_id, broj_naloga, uredjaj, serijski_broj,
|
||||||
|
opis_kvara, status, cena_od, cena_do, cena_konacna,
|
||||||
|
avans, napomena, garancija_do, datum_prijema, datum_zavrsetka,
|
||||||
|
ostecenja, pin_uredjaja, pribor, javni_token
|
||||||
|
FROM servisni_nalozi WHERE javni_token = ?`, token)
|
||||||
|
|
||||||
|
var n model.ServisniNalog
|
||||||
|
if err := scanNalog(red.Scan, &n, nil); err != nil {
|
||||||
|
return nil, fmt.Errorf("ntech: ServisRepo.DohvatiJavniToken: %w", err)
|
||||||
|
}
|
||||||
|
return &n, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Izmeni ažurira postojeći servisni nalog — broj_naloga i datum_prijema se ne menjaju
|
// Izmeni ažurira postojeći servisni nalog — broj_naloga i datum_prijema se ne menjaju
|
||||||
func (r *ServisRepo) Izmeni(ctx context.Context, n *model.ServisniNalog) error {
|
func (r *ServisRepo) Izmeni(ctx context.Context, n *model.ServisniNalog) error {
|
||||||
_, err := r.db.ExecContext(ctx, `
|
_, err := r.db.ExecContext(ctx, `
|
||||||
@@ -184,7 +217,7 @@ func (r *ServisRepo) Obrisi(ctx context.Context, id int64) error {
|
|||||||
// klijentNaziv je opcioni pokazivač, nil kada se čita bez JOIN-a
|
// klijentNaziv je opcioni pokazivač, nil kada se čita bez JOIN-a
|
||||||
func scanNalog(scan func(...any) error, n *model.ServisniNalog, klijentNaziv *string) error {
|
func scanNalog(scan func(...any) error, n *model.ServisniNalog, klijentNaziv *string) error {
|
||||||
var klijentID, tehnicarID sql.NullInt64
|
var klijentID, tehnicarID sql.NullInt64
|
||||||
var serijskiBroj, napomena, ostecenja, pinUredjaja, pribor sql.NullString
|
var serijskiBroj, napomena, ostecenja, pinUredjaja, pribor, javniToken sql.NullString
|
||||||
var cenaOd, cenaDo, cenaKonacna, avans sql.NullFloat64
|
var cenaOd, cenaDo, cenaKonacna, avans sql.NullFloat64
|
||||||
var garancijaDo, datumZavrsetka sql.NullTime
|
var garancijaDo, datumZavrsetka sql.NullTime
|
||||||
|
|
||||||
@@ -192,7 +225,7 @@ func scanNalog(scan func(...any) error, n *model.ServisniNalog, klijentNaziv *st
|
|||||||
&n.ID, &klijentID, &tehnicarID, &n.BrojNaloga, &n.Uredjaj, &serijskiBroj,
|
&n.ID, &klijentID, &tehnicarID, &n.BrojNaloga, &n.Uredjaj, &serijskiBroj,
|
||||||
&n.OpisKvara, &n.Status, &cenaOd, &cenaDo, &cenaKonacna,
|
&n.OpisKvara, &n.Status, &cenaOd, &cenaDo, &cenaKonacna,
|
||||||
&avans, &napomena, &garancijaDo, &n.DatumPrijema, &datumZavrsetka,
|
&avans, &napomena, &garancijaDo, &n.DatumPrijema, &datumZavrsetka,
|
||||||
&ostecenja, &pinUredjaja, &pribor,
|
&ostecenja, &pinUredjaja, &pribor, &javniToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
if klijentNaziv != nil {
|
if klijentNaziv != nil {
|
||||||
@@ -240,6 +273,7 @@ func scanNalog(scan func(...any) error, n *model.ServisniNalog, klijentNaziv *st
|
|||||||
v := datumZavrsetka.Time
|
v := datumZavrsetka.Time
|
||||||
n.DatumZavrsetka = &v
|
n.DatumZavrsetka = &v
|
||||||
}
|
}
|
||||||
|
n.JavniToken = javniToken.String
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-2
@@ -5,6 +5,7 @@ import (
|
|||||||
"html/template"
|
"html/template"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,13 +39,33 @@ var saSidebar = []string{
|
|||||||
|
|
||||||
// standalone su šabloni bez base layouta
|
// standalone su šabloni bez base layouta
|
||||||
var standaloneIme = []string{
|
var standaloneIme = []string{
|
||||||
"prijava", "setup", "totp_provera", "prodaja_stampa", "servis_stampa", "servis_otpremnica",
|
"prijava", "setup", "totp_provera", "prodaja_stampa", "servis_stampa", "servis_otpremnica", "servis_status_javni",
|
||||||
}
|
}
|
||||||
|
|
||||||
// sablonskeFunkcije su pomoćne funkcije dostupne u svim šablonima.
|
// sablonskeFunkcije su pomoćne funkcije dostupne u svim šablonima.
|
||||||
// dict gradi mapu iz parova ključ/vrednost — koristi se da se jednom partialu
|
// dict gradi mapu iz parova ključ/vrednost — koristi se da se jednom partialu
|
||||||
// prosledi više vrednosti (npr. {{template "x" (dict "ID" .ID "Lista" $.Lista)}}).
|
// prosledi više vrednosti (npr. {{template "x" (dict "ID" .ID "Lista" $.Lista)}}).
|
||||||
var sablonskeFunkcije = template.FuncMap{
|
var sablonskeFunkcije = template.FuncMap{
|
||||||
|
// formatBroj formatira float pointer kao ceo broj (zaokružen) — nil vraca ""
|
||||||
|
"formatBroj": func(v *float64) string {
|
||||||
|
if v == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d", int64(math.Round(*v)))
|
||||||
|
},
|
||||||
|
// statusPre vraća true ako je `a` pre `b` u redosledu statusa
|
||||||
|
"statusPre": func(a, b string, statusi []string) bool {
|
||||||
|
ia, ib := -1, -1
|
||||||
|
for i, s := range statusi {
|
||||||
|
if s == a {
|
||||||
|
ia = i
|
||||||
|
}
|
||||||
|
if s == b {
|
||||||
|
ib = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ia >= 0 && ib >= 0 && ia < ib
|
||||||
|
},
|
||||||
"dict": func(parovi ...any) (map[string]any, error) {
|
"dict": func(parovi ...any) (map[string]any, error) {
|
||||||
if len(parovi)%2 != 0 {
|
if len(parovi)%2 != 0 {
|
||||||
return nil, fmt.Errorf("dict: neparan broj argumenata")
|
return nil, fmt.Errorf("dict: neparan broj argumenata")
|
||||||
@@ -77,7 +98,8 @@ func KreirajKes(fsys fs.FS) (map[string]*template.Template, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, ime := range standaloneIme {
|
for _, ime := range standaloneIme {
|
||||||
t, err := template.ParseFS(fsys, "web/templates/stranice/"+ime+".html")
|
// ime+".html" mora biti ime roota da bi Execute() pronašlo sadržaj fajla
|
||||||
|
t, err := template.New(ime+".html").Funcs(sablonskeFunkcije).ParseFS(fsys, "web/templates/stranice/"+ime+".html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("kes: %s: %w", ime, err)
|
return nil, fmt.Errorf("kes: %s: %w", ime, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,10 +93,11 @@ func (h *Handler) PromeniCenuArtikla(w http.ResponseWriter, r *http.Request) {
|
|||||||
switch {
|
switch {
|
||||||
case errors.Is(err, sqlite.ErrArtikalNePostoji):
|
case errors.Is(err, sqlite.ErrArtikalNePostoji):
|
||||||
middleware.SetFlash(w, r, h.DB, "greska", "Artikal nije pronađen.")
|
middleware.SetFlash(w, r, h.DB, "greska", "Artikal nije pronađen.")
|
||||||
|
http.Redirect(w, r, "/magacin", http.StatusSeeOther)
|
||||||
case err != nil:
|
case err != nil:
|
||||||
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri promeni cene.")
|
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri promeni cene.")
|
||||||
default:
|
|
||||||
middleware.SetFlash(w, r, h.DB, "uspeh", "Prodajna cena je izmenjena.")
|
|
||||||
}
|
|
||||||
http.Redirect(w, r, "/magacin", http.StatusSeeOther)
|
http.Redirect(w, r, "/magacin", http.StatusSeeOther)
|
||||||
|
default:
|
||||||
|
http.Redirect(w, r, "/magacin?sacuvano=1", http.StatusSeeOther)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,8 +169,7 @@ func (h *Handler) SacuvajPdvKir(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "Greška pri čuvanju zapisa", http.StatusInternalServerError)
|
http.Error(w, "Greška pri čuvanju zapisa", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
middleware.SetFlash(w, r, h.DB, "uspeh", "Izlazni račun je dodat u KIR.")
|
http.Redirect(w, r, "/pdv/kir?sacuvano=1", http.StatusSeeOther)
|
||||||
http.Redirect(w, r, "/pdv/kir", http.StatusSeeOther)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObrisiPdvKir briše zapis iz KIR
|
// ObrisiPdvKir briše zapis iz KIR
|
||||||
@@ -187,6 +186,5 @@ func (h *Handler) ObrisiPdvKir(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "Greška pri brisanju zapisa", http.StatusInternalServerError)
|
http.Error(w, "Greška pri brisanju zapisa", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
middleware.SetFlash(w, r, h.DB, "uspeh", "Zapis je obrisan iz KIR.")
|
http.Redirect(w, r, "/pdv/kir?obrisan=1", http.StatusSeeOther)
|
||||||
http.Redirect(w, r, "/pdv/kir", http.StatusSeeOther)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,8 +154,7 @@ func (h *Handler) SacuvajPdvKpr(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "Greška pri čuvanju zapisa", http.StatusInternalServerError)
|
http.Error(w, "Greška pri čuvanju zapisa", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
middleware.SetFlash(w, r, h.DB, "uspeh", "Ulazni račun je dodat u KPR.")
|
http.Redirect(w, r, "/pdv/kpr?sacuvano=1", http.StatusSeeOther)
|
||||||
http.Redirect(w, r, "/pdv/kpr", http.StatusSeeOther)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObrisiPdvKpr briše zapis iz KPR
|
// ObrisiPdvKpr briše zapis iz KPR
|
||||||
@@ -172,6 +171,5 @@ func (h *Handler) ObrisiPdvKpr(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "Greška pri brisanju zapisa", http.StatusInternalServerError)
|
http.Error(w, "Greška pri brisanju zapisa", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
middleware.SetFlash(w, r, h.DB, "uspeh", "Zapis je obrisan iz KPR.")
|
http.Redirect(w, r, "/pdv/kpr?obrisan=1", http.StatusSeeOther)
|
||||||
http.Redirect(w, r, "/pdv/kpr", http.StatusSeeOther)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,8 +99,7 @@ func (h *Handler) DodajPdvStopu(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "Greška pri čuvanju PDV stope", http.StatusInternalServerError)
|
http.Error(w, "Greška pri čuvanju PDV stope", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
middleware.SetFlash(w, r, h.DB, "uspeh", "PDV stopa je dodata.")
|
http.Redirect(w, r, "/admin/podesavanja/kalkulacija-pdv?sacuvano=1", http.StatusSeeOther)
|
||||||
http.Redirect(w, r, "/admin/podesavanja/kalkulacija-pdv", http.StatusSeeOther)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IzmeniPdvStopu prima POST i menja postojeću stopu
|
// IzmeniPdvStopu prima POST i menja postojeću stopu
|
||||||
@@ -128,8 +127,7 @@ func (h *Handler) IzmeniPdvStopu(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "Greška pri izmeni PDV stope", http.StatusInternalServerError)
|
http.Error(w, "Greška pri izmeni PDV stope", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
middleware.SetFlash(w, r, h.DB, "uspeh", "PDV stopa je izmenjena.")
|
http.Redirect(w, r, "/admin/podesavanja/kalkulacija-pdv?sacuvano=1", http.StatusSeeOther)
|
||||||
http.Redirect(w, r, "/admin/podesavanja/kalkulacija-pdv", http.StatusSeeOther)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PromeniAktivnostPdvStope arhivira ili vraća stopu u upotrebu (toggle, bez brisanja)
|
// PromeniAktivnostPdvStope arhivira ili vraća stopu u upotrebu (toggle, bez brisanja)
|
||||||
@@ -151,10 +149,5 @@ func (h *Handler) PromeniAktivnostPdvStope(w http.ResponseWriter, r *http.Reques
|
|||||||
http.Error(w, "Greška pri promeni statusa PDV stope", http.StatusInternalServerError)
|
http.Error(w, "Greška pri promeni statusa PDV stope", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
poruka := "PDV stopa je arhivirana."
|
http.Redirect(w, r, "/admin/podesavanja/kalkulacija-pdv?sacuvano=1", http.StatusSeeOther)
|
||||||
if !postojeca.Aktivna {
|
|
||||||
poruka = "PDV stopa je vraćena u upotrebu."
|
|
||||||
}
|
|
||||||
middleware.SetFlash(w, r, h.DB, "uspeh", poruka)
|
|
||||||
http.Redirect(w, r, "/admin/podesavanja/kalkulacija-pdv", http.StatusSeeOther)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -600,8 +600,7 @@ func (h *Handler) UkloniLoginPozadinu(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
middleware.SetFlash(w, r, h.DB, "uspeh", "Pozadinska slika je uklonjena.")
|
http.Redirect(w, r, "/admin/podesavanja/izgled?sacuvano=1", http.StatusSeeOther)
|
||||||
http.Redirect(w, r, "/admin/podesavanja/izgled", http.StatusSeeOther)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SacuvajLoginPozadinaStilove čuva vrednosti zamućenja i prozirnosti pozadine login stranice
|
// SacuvajLoginPozadinaStilove čuva vrednosti zamućenja i prozirnosti pozadine login stranice
|
||||||
@@ -659,8 +658,7 @@ func (h *Handler) SacuvajLoginPozadinaStilove(w http.ResponseWriter, r *http.Req
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
middleware.SetFlash(w, r, h.DB, "uspeh", "Izgled pozadine prijave je sačuvan.")
|
http.Redirect(w, r, "/admin/podesavanja/izgled?sacuvano=1", http.StatusSeeOther)
|
||||||
http.Redirect(w, r, "/admin/podesavanja/izgled", http.StatusSeeOther)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// napuniPodaciPodesavanja učitava sva podešavanja i kreira strukturu za template
|
// napuniPodaciPodesavanja učitava sva podešavanja i kreira strukturu za template
|
||||||
|
|||||||
@@ -432,8 +432,7 @@ func (h *Handler) StornoProdaje(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err := h.PdvKirRepo.ObrisiPoIzvoru(r.Context(), "prodaja", id); err != nil {
|
if err := h.PdvKirRepo.ObrisiPoIzvoru(r.Context(), "prodaja", id); err != nil {
|
||||||
slog.Error("brisanje vezanog KIR zapisa nije uspelo", "prodaja_id", id, "error", err)
|
slog.Error("brisanje vezanog KIR zapisa nije uspelo", "prodaja_id", id, "error", err)
|
||||||
}
|
}
|
||||||
middleware.SetFlash(w, r, h.DB, "uspeh", "Prodajni nalog je storniran.")
|
http.Redirect(w, r, "/prodaja/"+strconv.FormatInt(id, 10)+"?sacuvano=1", http.StatusSeeOther)
|
||||||
http.Redirect(w, r, "/prodaja/"+strconv.FormatInt(id, 10), http.StatusSeeOther)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// renderujFormuProdaje renderuje HTML šablon forme za unos nove prodaje
|
// renderujFormuProdaje renderuje HTML šablon forme za unos nove prodaje
|
||||||
|
|||||||
@@ -444,8 +444,7 @@ func (h *Handler) DodajDeloNalogu(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
middleware.SetFlash(w, r, h.DB, "uspeh", "Deo je dodat.")
|
http.Redirect(w, r, "/servis/"+strconv.FormatInt(nalogID, 10)+"?sacuvano=1", http.StatusSeeOther)
|
||||||
http.Redirect(w, r, "/servis/"+strconv.FormatInt(nalogID, 10), http.StatusSeeOther)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObrisiDeloNaloga prima POST zahtev i uklanja deo iz servisnog naloga
|
// ObrisiDeloNaloga prima POST zahtev i uklanja deo iz servisnog naloga
|
||||||
@@ -470,10 +469,7 @@ func (h *Handler) ObrisiDeloNaloga(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err := h.ServisniDeloviRepo.Obrisi(r.Context(), deoID, &k.ID); err != nil {
|
if err := h.ServisniDeloviRepo.Obrisi(r.Context(), deoID, &k.ID); err != nil {
|
||||||
slog.Error("greška pri brisanju dela", "error", err)
|
slog.Error("greška pri brisanju dela", "error", err)
|
||||||
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri uklanjanju dela.")
|
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri uklanjanju dela.")
|
||||||
} else {
|
|
||||||
middleware.SetFlash(w, r, h.DB, "uspeh", "Deo je uklonjen.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, "/servis/"+strconv.FormatInt(nalogID, 10), http.StatusSeeOther)
|
http.Redirect(w, r, "/servis/"+strconv.FormatInt(nalogID, 10), http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -647,12 +643,12 @@ func (h *Handler) StampaServisa(w http.ResponseWriter, r *http.Request) {
|
|||||||
ukupnoDelovi += d.Ukupno()
|
ukupnoDelovi += d.Ukupno()
|
||||||
}
|
}
|
||||||
|
|
||||||
// QR kod sadrži URL naloga — isti host kao što korisnik koristi
|
// QR kod vodi na javnu status stranicu — dostupnu bez prijave
|
||||||
scheme := "http"
|
scheme := "http"
|
||||||
if r.TLS != nil {
|
if r.TLS != nil {
|
||||||
scheme = "https"
|
scheme = "https"
|
||||||
}
|
}
|
||||||
nalogURL := scheme + "://" + r.Host + "/servis/" + strconv.FormatInt(id, 10)
|
nalogURL := scheme + "://" + r.Host + "/status/" + nalog.JavniToken
|
||||||
var qrKod string
|
var qrKod string
|
||||||
if png, err := qrcode.Encode(nalogURL, qrcode.Medium, 160); err == nil {
|
if png, err := qrcode.Encode(nalogURL, qrcode.Medium, 160); err == nil {
|
||||||
qrKod = base64.StdEncoding.EncodeToString(png)
|
qrKod = base64.StdEncoding.EncodeToString(png)
|
||||||
@@ -762,7 +758,7 @@ func (h *Handler) StampaOtpremnice(w http.ResponseWriter, r *http.Request) {
|
|||||||
if r.TLS != nil {
|
if r.TLS != nil {
|
||||||
nalogURL += "s"
|
nalogURL += "s"
|
||||||
}
|
}
|
||||||
nalogURL += "://" + r.Host + "/servis/" + strconv.FormatInt(id, 10)
|
nalogURL += "://" + r.Host + "/status/" + nalog.JavniToken
|
||||||
var qrKodOtpr string
|
var qrKodOtpr string
|
||||||
if png, err := qrcode.Encode(nalogURL, qrcode.Medium, 160); err == nil {
|
if png, err := qrcode.Encode(nalogURL, qrcode.Medium, 160); err == nil {
|
||||||
qrKodOtpr = base64.StdEncoding.EncodeToString(png)
|
qrKodOtpr = base64.StdEncoding.EncodeToString(png)
|
||||||
@@ -813,3 +809,37 @@ func (h *Handler) PromeniStatus(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
http.Redirect(w, r, "/servis/"+strconv.FormatInt(id, 10)+"?sacuvano=1", http.StatusSeeOther)
|
http.Redirect(w, r, "/servis/"+strconv.FormatInt(id, 10)+"?sacuvano=1", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PodaciJavnogStatusa su podaci za javnu status stranicu servisnog naloga
|
||||||
|
type PodaciJavnogStatusa struct {
|
||||||
|
Nalog model.ServisniNalog
|
||||||
|
NazivFirme string
|
||||||
|
Telefon string
|
||||||
|
Adresa string
|
||||||
|
SviStatusi []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServisJavniStatus prikazuje javnu status stranicu — dostupna bez prijave putem QR koda
|
||||||
|
func (h *Handler) ServisJavniStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
|
token := chi.URLParam(r, "token")
|
||||||
|
if token == "" {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nalog, err := h.ServisRepo.DohvatiJavniToken(r.Context(), token)
|
||||||
|
if err != nil {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
|
||||||
|
|
||||||
|
h.renderujStandalone(w, "servis_status_javni", PodaciJavnogStatusa{
|
||||||
|
Nalog: *nalog,
|
||||||
|
NazivFirme: podesavanja["naziv_firme"],
|
||||||
|
Telefon: podesavanja["telefon"],
|
||||||
|
Adresa: podesavanja["adresa"],
|
||||||
|
SviStatusi: model.SviStatusi,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ type ServisniNalog struct {
|
|||||||
Ostecenja string
|
Ostecenja string
|
||||||
PinUredjaja string
|
PinUredjaja string
|
||||||
Pribor string
|
Pribor string
|
||||||
|
JavniToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServisniDeo predstavlja jedan artikal ugrađen u servisni nalog
|
// ServisniDeo predstavlja jedan artikal ugrađen u servisni nalog
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE servisni_nalozi ADD COLUMN javni_token TEXT;
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_servisni_nalozi_javni_token ON servisni_nalozi(javni_token);
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="sr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Status popravke — {{.Nalog.BrojNaloga}}</title>
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif; font-size: 14px; color: #111; background: #f5f5f7; min-height: 100vh; }
|
||||||
|
|
||||||
|
.omotac { max-width: 480px; margin: 0 auto; padding: 24px 16px 48px; }
|
||||||
|
|
||||||
|
/* zaglavlje firme */
|
||||||
|
.zaglavlje { text-align: center; margin-bottom: 24px; }
|
||||||
|
.firma-naziv { font-size: 18px; font-weight: 700; color: #111; }
|
||||||
|
.firma-info { font-size: 12px; color: #666; margin-top: 4px; line-height: 1.6; }
|
||||||
|
|
||||||
|
/* kartica naloga */
|
||||||
|
.kartica { background: #fff; border-radius: 16px; padding: 20px; margin-bottom: 16px; box-shadow: 0 1px 4px rgba(0,0,0,0.08); }
|
||||||
|
.kartica-naslov { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.6px; color: #999; margin-bottom: 12px; }
|
||||||
|
|
||||||
|
.uredjaj { font-size: 22px; font-weight: 700; color: #111; line-height: 1.2; }
|
||||||
|
.broj-naloga { font-size: 13px; color: #888; margin-top: 4px; font-family: monospace; }
|
||||||
|
|
||||||
|
/* status bedž */
|
||||||
|
.status-bedz {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 6px 14px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
.status-primljeno { background: #e8f0fe; color: #1a56db; }
|
||||||
|
.status-dijagnostika { background: #fef3c7; color: #b45309; }
|
||||||
|
.status-ceka { background: #fee2e2; color: #dc2626; }
|
||||||
|
.status-popravka { background: #fef3c7; color: #b45309; }
|
||||||
|
.status-zavrseno { background: #d1fae5; color: #059669; }
|
||||||
|
.status-preuzeto { background: #f3f4f6; color: #374151; }
|
||||||
|
|
||||||
|
/* napredak */
|
||||||
|
.napredak { margin: 16px 0 4px; }
|
||||||
|
.napredak-labela { font-size: 11px; color: #999; margin-bottom: 8px; }
|
||||||
|
.koraci { display: flex; gap: 4px; }
|
||||||
|
.korak {
|
||||||
|
flex: 1;
|
||||||
|
height: 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: #e5e7eb;
|
||||||
|
}
|
||||||
|
.korak.aktivan { background: #10b981; }
|
||||||
|
.korak.zavrseno { background: #10b981; }
|
||||||
|
.korak.tekuci { background: #f59e0b; }
|
||||||
|
|
||||||
|
/* red podataka */
|
||||||
|
.red { display: flex; justify-content: space-between; align-items: flex-start; padding: 10px 0; border-bottom: 0.5px solid #f0f0f0; gap: 12px; }
|
||||||
|
.red:last-child { border-bottom: none; }
|
||||||
|
.red-labela { font-size: 12px; color: #888; flex-shrink: 0; }
|
||||||
|
.red-vrednost { font-size: 13px; color: #111; font-weight: 500; text-align: right; }
|
||||||
|
|
||||||
|
/* cena */
|
||||||
|
.cena-blok { background: #f9fafb; border-radius: 10px; padding: 14px 16px; margin-top: 4px; }
|
||||||
|
.cena-od-do { display: flex; gap: 8px; align-items: center; }
|
||||||
|
.cena-od-do .cena-vrednost { font-size: 20px; font-weight: 700; color: #111; }
|
||||||
|
.cena-od-do .cena-sep { color: #bbb; }
|
||||||
|
.cena-labela { font-size: 11px; color: #888; margin-top: 2px; }
|
||||||
|
|
||||||
|
/* kontakt */
|
||||||
|
.kontakt { background: #fff; border-radius: 16px; padding: 18px 20px; box-shadow: 0 1px 4px rgba(0,0,0,0.08); }
|
||||||
|
.kontakt-naslov { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.6px; color: #999; margin-bottom: 12px; }
|
||||||
|
.kontakt-tel { font-size: 18px; font-weight: 700; color: #1a56db; text-decoration: none; display: block; }
|
||||||
|
.kontakt-adresa { font-size: 13px; color: #666; margin-top: 6px; line-height: 1.5; }
|
||||||
|
|
||||||
|
/* napomena za klijenta */
|
||||||
|
.napomena-blok { background: #fffbeb; border-radius: 10px; padding: 12px 14px; margin-top: 4px; font-size: 13px; color: #555; line-height: 1.5; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="omotac">
|
||||||
|
|
||||||
|
<!-- zaglavlje firme -->
|
||||||
|
<div class="zaglavlje">
|
||||||
|
<div class="firma-naziv">{{if .NazivFirme}}{{.NazivFirme}}{{else}}Servis{{end}}</div>
|
||||||
|
{{if .Adresa}}<div class="firma-info">{{.Adresa}}</div>{{end}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- kartica: uređaj i status -->
|
||||||
|
<div class="kartica">
|
||||||
|
<div class="kartica-naslov">Vaš uređaj</div>
|
||||||
|
<div class="uredjaj">{{.Nalog.Uredjaj}}</div>
|
||||||
|
<div class="broj-naloga">Nalog: {{.Nalog.BrojNaloga}}</div>
|
||||||
|
|
||||||
|
{{$s := .Nalog.Status}}
|
||||||
|
{{if eq $s "Primljeno"}}
|
||||||
|
<span class="status-bedz status-primljeno">Primljeno</span>
|
||||||
|
{{else if eq $s "U dijagnostici"}}
|
||||||
|
<span class="status-bedz status-dijagnostika">U dijagnostici</span>
|
||||||
|
{{else if eq $s "Čeka delove"}}
|
||||||
|
<span class="status-bedz status-ceka">Čeka delove</span>
|
||||||
|
{{else if eq $s "U popravci"}}
|
||||||
|
<span class="status-bedz status-popravka">U popravci</span>
|
||||||
|
{{else if eq $s "Završeno"}}
|
||||||
|
<span class="status-bedz status-zavrseno">Završeno — možete preuzeti</span>
|
||||||
|
{{else if eq $s "Preuzeto"}}
|
||||||
|
<span class="status-bedz status-preuzeto">Preuzeto</span>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<!-- traka napretka -->
|
||||||
|
<div class="napredak">
|
||||||
|
<div class="napredak-labela">Napredak popravke</div>
|
||||||
|
<div class="koraci">
|
||||||
|
{{range $i, $korak := .SviStatusi}}
|
||||||
|
{{if eq $korak $s}}
|
||||||
|
<div class="korak tekuci"></div>
|
||||||
|
{{else if statusPre $korak $s $.SviStatusi}}
|
||||||
|
<div class="korak zavrseno"></div>
|
||||||
|
{{else}}
|
||||||
|
<div class="korak"></div>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- kartica: detalji -->
|
||||||
|
<div class="kartica">
|
||||||
|
<div class="kartica-naslov">Detalji</div>
|
||||||
|
|
||||||
|
<div class="red">
|
||||||
|
<span class="red-labela">Datum prijema</span>
|
||||||
|
<span class="red-vrednost">{{.Nalog.DatumPrijema.Format "02.01.2006."}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{if .Nalog.DatumZavrsetka}}
|
||||||
|
<div class="red">
|
||||||
|
<span class="red-labela">Datum završetka</span>
|
||||||
|
<span class="red-vrednost">{{.Nalog.DatumZavrsetka.Format "02.01.2006."}}</span>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if .Nalog.SerijskiBroj}}
|
||||||
|
<div class="red">
|
||||||
|
<span class="red-labela">Serijski broj</span>
|
||||||
|
<span class="red-vrednost">{{.Nalog.SerijskiBroj}}</span>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if .Nalog.OpisKvara}}
|
||||||
|
<div class="red">
|
||||||
|
<span class="red-labela">Opis kvara</span>
|
||||||
|
<span class="red-vrednost">{{.Nalog.OpisKvara}}</span>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if .Nalog.GarancijaDo}}
|
||||||
|
<div class="red">
|
||||||
|
<span class="red-labela">Garancija do</span>
|
||||||
|
<span class="red-vrednost">{{.Nalog.GarancijaDo.Format "02.01.2006."}}</span>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<!-- procenjena cena -->
|
||||||
|
{{if or .Nalog.CenaOd .Nalog.CenaDo .Nalog.CenaKonacna}}
|
||||||
|
<div style="margin-top:12px;">
|
||||||
|
<div class="red-labela" style="margin-bottom:8px;">Procena cene</div>
|
||||||
|
<div class="cena-blok">
|
||||||
|
{{if .Nalog.CenaKonacna}}
|
||||||
|
<div class="cena-od-do">
|
||||||
|
<span class="cena-vrednost">{{formatBroj .Nalog.CenaKonacna}} din</span>
|
||||||
|
</div>
|
||||||
|
<div class="cena-labela">Konačna cena popravke</div>
|
||||||
|
{{else if and .Nalog.CenaOd .Nalog.CenaDo}}
|
||||||
|
<div class="cena-od-do">
|
||||||
|
<span class="cena-vrednost">{{formatBroj .Nalog.CenaOd}}</span>
|
||||||
|
<span class="cena-sep">–</span>
|
||||||
|
<span class="cena-vrednost">{{formatBroj .Nalog.CenaDo}} din</span>
|
||||||
|
</div>
|
||||||
|
<div class="cena-labela">Procenjeni raspon cene</div>
|
||||||
|
{{else if .Nalog.CenaOd}}
|
||||||
|
<div class="cena-od-do">
|
||||||
|
<span class="cena-vrednost">od {{formatBroj .Nalog.CenaOd}} din</span>
|
||||||
|
</div>
|
||||||
|
<div class="cena-labela">Procenjena cena od</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if .Nalog.Napomena}}
|
||||||
|
<div style="margin-top:12px;">
|
||||||
|
<div class="red-labela" style="margin-bottom:6px;">Napomena</div>
|
||||||
|
<div class="napomena-blok">{{.Nalog.Napomena}}</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- kontakt -->
|
||||||
|
{{if .Telefon}}
|
||||||
|
<div class="kontakt">
|
||||||
|
<div class="kontakt-naslov">Kontakt</div>
|
||||||
|
<a href="tel:{{.Telefon}}" class="kontakt-tel">{{.Telefon}}</a>
|
||||||
|
{{if .Adresa}}<div class="kontakt-adresa">{{.Adresa}}</div>{{end}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -351,8 +351,10 @@
|
|||||||
redirect: 'follow'
|
redirect: 'follow'
|
||||||
}).then(function(res) {
|
}).then(function(res) {
|
||||||
var finUrl = new URL(res.url);
|
var finUrl = new URL(res.url);
|
||||||
if (finUrl.search.indexOf('sacuvano') !== -1) {
|
var isStiPath = finUrl.pathname === location.pathname;
|
||||||
// uspeh — prikaži toast, ostani na stranici
|
var imaSacuvano = finUrl.search.indexOf('sacuvano') !== -1;
|
||||||
|
if (isStiPath && imaSacuvano) {
|
||||||
|
// uspeh na istoj stranici — prikaži toast, ostani
|
||||||
window.ntechToast('Sačuvano', 'uspeh');
|
window.ntechToast('Sačuvano', 'uspeh');
|
||||||
if (btn) btn.disabled = false;
|
if (btn) btn.disabled = false;
|
||||||
// odmah primeni podešavanja koja menjaju globalne atribute body-ja
|
// odmah primeni podešavanja koja menjaju globalne atribute body-ja
|
||||||
@@ -374,7 +376,7 @@
|
|||||||
// promena teme zahteva reload (menja se ceo CSS fajl)
|
// promena teme zahteva reload (menja se ceo CSS fajl)
|
||||||
if (f.querySelector('[name="lokalna_tema"]')) location.reload();
|
if (f.querySelector('[name="lokalna_tema"]')) location.reload();
|
||||||
} else {
|
} else {
|
||||||
// greška ili redirect na drugu stranicu — navigiraj normalno
|
// redirect na drugu stranicu ili bez sacuvano — navigiraj normalno
|
||||||
location.href = res.url;
|
location.href = res.url;
|
||||||
}
|
}
|
||||||
}).catch(function() {
|
}).catch(function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user