Kartica artikla: prikaz vezanih dobavljača sa dodavanjem/uklanjanjem; helper telefon (064/123-4567); CLAUDE.md frontend i formatiranje sekcije

This commit is contained in:
2026-06-21 01:34:15 +02:00
parent ac6deeeba4
commit 7eb472b9e6
13 changed files with 198 additions and 18 deletions
+60
View File
@@ -7,6 +7,7 @@ import (
"log/slog"
"math"
"net/http"
"strings"
)
var bazniSabloni = []string{
@@ -62,6 +63,8 @@ var sablonskeFunkcije = template.FuncMap{
"dinariCeli": func(v float64) string {
return formatirajDinare(v, 0)
},
// telefon formatira srpski broj telefona radi lakšeg čitanja: "0641234567" → "064 123 4567"
"telefon": formatirajTelefon,
// statusPre vraća true ako je `a` pre `b` u redosledu statusa
"statusPre": func(a, b string, statusi []string) bool {
ia, ib := -1, -1
@@ -219,3 +222,60 @@ func formatirajDinare(v float64, decimale int) string {
}
return rezultat
}
// formatirajTelefon formatira srpski broj telefona radi lakšeg čitanja:
// pozivni broj odvojen kosom crtom, ostatak grupisan crticom.
// Primeri: "0641234567" → "064/123-4567", "+381641234567" → "+381 64/123-4567".
// Ako format nije prepoznat, vraća original.
func formatirajTelefon(s string) string {
s = strings.TrimSpace(s)
if s == "" {
return ""
}
// izdvoj cifre i zapamti da li je međunarodni (+)
medjunarodni := strings.HasPrefix(s, "+") || strings.HasPrefix(s, "00")
var cifre []rune
for _, c := range s {
if c >= '0' && c <= '9' {
cifre = append(cifre, c)
}
}
d := string(cifre)
// međunarodni srpski prefiks (381): "+381 64/123-4567"
if medjunarodni {
d = strings.TrimPrefix(d, "00")
if strings.HasPrefix(d, "381") {
ostatak := d[3:] // bez vodeće nule, npr. "641234567"
if len(ostatak) < 7 || len(ostatak) > 9 {
return s
}
return "+381 " + ostatak[:2] + "/" + grupisiTelefon(ostatak[2:])
}
return s // strani broj — ne diramo
}
// lokalni format: očekujemo vodeću nulu i 810 cifara ukupno
if !strings.HasPrefix(d, "0") || len(d) < 8 || len(d) > 10 {
return s
}
// pozivni (3 cifre, npr. 064/011) "/" ostatak grupisan crticom
return d[:3] + "/" + grupisiTelefon(d[3:])
}
// grupisiTelefon deli niz cifara u grupe od po 3 crticom (poslednja može 4) — "1234567" → "123-4567"
func grupisiTelefon(d string) string {
if len(d) <= 4 {
return d
}
var delovi []string
delovi = append(delovi, d[:3])
ostatak := d[3:]
for len(ostatak) > 4 {
delovi = append(delovi, ostatak[:3])
ostatak = ostatak[3:]
}
delovi = append(delovi, ostatak)
return strings.Join(delovi, "-")
}
+62 -5
View File
@@ -2,6 +2,7 @@ package handler
import (
"errors"
"log/slog"
"net/http"
"strconv"
@@ -220,8 +221,10 @@ func (h *Handler) VratiArtikal(w http.ResponseWriter, r *http.Request) {
// PodaciMagacinskeKartice su podaci za karticu jednog artikla
type PodaciMagacinskeKartice struct {
model.PodaciStranice
Artikal model.Artikal
Promene []model.MagacinskaPromenaSaDetaljem
Artikal model.Artikal
Promene []model.MagacinskaPromenaSaDetaljem
Dobavljaci []model.Dobavljac // dobavljači vezani za artikal
DostupniDobavljaci []model.Dobavljac // dobavljači koji još nisu vezani (za dodavanje)
}
// MagacinskaKartica prikazuje sve promene stanja za jedan artikal
@@ -250,13 +253,67 @@ func (h *Handler) MagacinskaKartica(w http.ResponseWriter, r *http.Request) {
return
}
// dobavljači: vezani za artikal i oni koji još nisu vezani (za padajući izbor)
sviDobavljaci, _ := h.DobavljaciRepo.Lista(r.Context(), "")
vezaniIDs, _ := h.Artikli.DobavljaciArtikla(r.Context(), id)
vezanSet := map[int64]bool{}
for _, did := range vezaniIDs {
vezanSet[did] = true
}
var vezani, dostupni []model.Dobavljac
for _, d := range sviDobavljaci {
if vezanSet[d.ID] {
vezani = append(vezani, d)
} else {
dostupni = append(dostupni, d)
}
}
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "magacin"
ps.NaslovStranice = "Kartica: " + artikal.Naziv
h.renderujTemplate(w, "magacin_kartica", PodaciMagacinskeKartice{
PodaciStranice: ps,
Artikal: *artikal,
Promene: promene,
PodaciStranice: ps,
Artikal: *artikal,
Promene: promene,
Dobavljaci: vezani,
DostupniDobavljaci: dostupni,
})
}
// DodajDobavljacaArtiklu veže izabranog dobavljača za artikal
func (h *Handler) DodajDobavljacaArtiklu(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
if err != nil {
http.Error(w, "Neispravan ID artikla", http.StatusBadRequest)
return
}
dobID, err := strconv.ParseInt(r.FormValue("dobavljac_id"), 10, 64)
if err != nil {
http.Redirect(w, r, "/magacin/kartica/"+chi.URLParam(r, "id"), http.StatusSeeOther)
return
}
if e := h.Artikli.PoveziDobavljaca(r.Context(), id, dobID); e != nil {
slog.Error("vezivanje dobavljača nije uspelo", "artikal_id", id, "error", e)
}
http.Redirect(w, r, "/magacin/kartica/"+chi.URLParam(r, "id"), http.StatusSeeOther)
}
// ObrisiDobavljacaArtikla uklanja vezu dobavljača sa artiklom
func (h *Handler) ObrisiDobavljacaArtikla(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
if err != nil {
http.Error(w, "Neispravan ID artikla", http.StatusBadRequest)
return
}
dobID, err := strconv.ParseInt(r.FormValue("dobavljac_id"), 10, 64)
if err != nil {
http.Redirect(w, r, "/magacin/kartica/"+chi.URLParam(r, "id"), http.StatusSeeOther)
return
}
if e := h.Artikli.OdveziDobavljaca(r.Context(), id, dobID); e != nil {
slog.Error("uklanjanje dobavljača nije uspelo", "artikal_id", id, "error", e)
}
http.Redirect(w, r, "/magacin/kartica/"+chi.URLParam(r, "id"), http.StatusSeeOther)
}