Files
GoNtech/internal/handler/dashboard.go
T
Dasko 9aaafa2358 refactor(izvestaji): direktan SQL dashboard/izveštaja u IzvestajRepository
dashboard.go i izvestaji.go više ne sadrže direktan SQL — svih 12 upita
prebačeno u novi IzvestajRepository (internal/db/sqlite/izvestaj.go). Repo vraća
sirove redove (model.*Red tipovi), a handleri zadržavaju prezentaciju
(formatiranje datuma, boje tačaka, rang, sklapanje niza 12 meseci). Žičenje kroz
Handler.IzvestajRepo (+ reinicijalizuj).

Dobici: testabilnost (dodati integracioni testovi izvestaj_test.go) i put ka
Postgres-u bez prepravke handlera. dashboard.prihod provera ostaje u handleru.

Van obima: middleware/flash.go i backup VACUUM INTO (ne pripadaju repo sloju).
2026-06-12 22:53:15 +02:00

161 lines
4.3 KiB
Go

package handler
import (
"log/slog"
"net/http"
appdb "ntech/internal/db"
"ntech/internal/db/sqlite"
"ntech/internal/middleware"
"ntech/internal/model"
)
// Dashboard renderuje početnu stranicu sa pravim podacima iz baze
func (h *Handler) Dashboard(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// pročitaj i odmah obrišt flash poruku ako postoji
var flashGreska string
if kol, err := r.Cookie("ntech_flash_greska"); err == nil {
flashGreska = kol.Value
http.SetCookie(w, &http.Cookie{
Name: "ntech_flash_greska",
Value: "",
Path: "/",
MaxAge: -1,
})
}
podesavanja, err := sqlite.DohvatiSvaPodesavanja(ctx, h.DB)
if err != nil {
http.Error(w, "Greška pri učitavanju podešavanja", http.StatusInternalServerError)
return
}
var brojArtikala, aktivniServisi, kriticnaZaliha, aktivniPodsetnici int
var prihodOvogMeseca float64
if n, err := h.IzvestajRepo.BrojArtikala(ctx); err != nil {
slog.Error("dashboard: broj artikala", "error", err)
} else {
brojArtikala = n
}
if n, err := h.IzvestajRepo.BrojAktivnihServisa(ctx); err != nil {
slog.Error("dashboard: aktivni servisi", "error", err)
} else {
aktivniServisi = n
}
// prihod se dohvata samo ako korisnik ima dozvolu dashboard.prihod
korisnikDash := middleware.KorisnikIzKonteksta(ctx)
if h.DozvoleRepo.ImaDozvolu(ctx, korisnikDash.Uloga, "dashboard.prihod") {
if v, err := h.IzvestajRepo.PrihodTekuciMesec(ctx); err != nil {
slog.Error("dashboard: prihod ovog meseca", "error", err)
} else {
prihodOvogMeseca = v
}
}
if n, err := h.IzvestajRepo.BrojKriticnihZaliha(ctx); err != nil {
slog.Error("dashboard: kriticna zaliha", "error", err)
} else {
kriticnaZaliha = n
}
korisnikFilter := appdb.PodsetnikFilter{}
if korisnikDash.Uloga == "radnik" {
korisnikFilter.KorisnikID = &korisnikDash.ID
}
if n, err := h.PodsetnikRepo.BrojAktivnih(ctx, korisnikFilter); err != nil {
slog.Error("dashboard: aktivni podsetnici", "error", err)
} else {
aktivniPodsetnici = n
}
// poslednjih 5 servisnih naloga — repo vraća sirove redove, ovde formatiramo
var poslednjiServisi []model.StavkaServisa
if redovi, err := h.IzvestajRepo.PoslednjiServisi(ctx, 5); err != nil {
slog.Error("dashboard: poslednji servisi", "error", err)
} else {
for _, s := range redovi {
poslednjiServisi = append(poslednjiServisi, model.StavkaServisa{
Uredjaj: s.Uredjaj,
Status: s.Status,
BojaTacke: bojaTackeServisa(s.Status),
DatumPrijema: s.DatumPrijema.Format("02.01."),
})
}
}
// artikli sa kritičnom zalihom
var kriticneZalihe []model.StavkaZalihe
if redovi, err := h.IzvestajRepo.KriticneZalihe(ctx, 5); err != nil {
slog.Error("dashboard: kriticne zalihe", "error", err)
} else {
for _, z := range redovi {
boja := "#f97316"
if z.Kolicina == 0 {
boja = "#dc2626"
}
kriticneZalihe = append(kriticneZalihe, model.StavkaZalihe{
Naziv: z.Naziv,
Kolicina: z.Kolicina,
BojaTacke: boja,
})
}
}
// poslednjih 5 prodajnih naloga
var poslednjeProdaje []model.StavkaProdajePregled
if redovi, err := h.IzvestajRepo.PoslednjeProdaje(ctx, 5); err != nil {
slog.Error("dashboard: poslednje prodaje", "error", err)
} else {
for _, p := range redovi {
poslednjeProdaje = append(poslednjeProdaje, model.StavkaProdajePregled{
BrojNaloga: p.BrojNaloga,
Ukupno: p.Ukupno,
Datum: p.Datum.Format("02.01."),
KlijentNaziv: p.KlijentNaziv,
})
}
}
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "dashboard"
ps.NaslovStranice = "Dashboard"
podaci := model.PodaciDashboarda{
PodaciStranice: ps,
BrojArtikala: brojArtikala,
AktivniServisi: aktivniServisi,
PrihodOvogMeseca: prihodOvogMeseca,
KriticnaZaliha: kriticnaZaliha,
AktivniPodsetnici: aktivniPodsetnici,
PoslednjiServisi: poslednjiServisi,
KriticneZalihe: kriticneZalihe,
PoslednjeProdaje: poslednjeProdaje,
FlashGreska: flashGreska,
}
h.renderujTemplate(w, "dashboard", podaci)
}
// bojaTackeServisa vraća hex boju tačke prema statusu naloga
func bojaTackeServisa(status string) string {
switch status {
case "U dijagnostici":
return "#3b82f6"
case "Čeka delove":
return "#f97316"
case "U popravci":
return "#ca8a04"
case "Završeno":
return "#16a34a"
case "Preuzeto":
return "#15803d"
default:
return "#94a3b8"
}
}