Servis: inline promena statusa direktno iz detalja naloga
- Novi POST /servis/{id}/status ruta sa dozvolom servis.izmeni
- AzurirajStatus metoda u repou — menja samo status; pri prelasku u
Završeno/Preuzeto automatski postavlja datum_zavrsetka ako nije već setovan
- Dropdown sa svim statusima i dugme „Promeni" u zaglavlju stranice detalja
This commit is contained in:
@@ -339,6 +339,7 @@ func main() {
|
||||
r.With(ntechmw.RequireDozvola(h.DozvoleRepo.ImaDozvolu, "servis.pregled")).Get("/servis/{id}", h.DetaljiNaloga)
|
||||
r.With(ntechmw.RequireDozvola(h.DozvoleRepo.ImaDozvolu, "servis.pregled")).Get("/servis/{id}/stampa", h.StampaServisa)
|
||||
r.With(ntechmw.RequireDozvola(h.DozvoleRepo.ImaDozvolu, "servis.pregled")).Get("/servis/{id}/otpremnica", h.StampaOtpremnice)
|
||||
r.With(doz("servis.izmeni")).Post("/servis/{id}/status", h.PromeniStatus)
|
||||
r.With(doz("servis.izmeni")).Post("/servis/{id}/delovi", h.DodajDeloNalogu)
|
||||
r.With(doz("servis.izmeni")).Post("/servis/{id}/delovi/{deo_id}/obrisi", h.ObrisiDeloNaloga)
|
||||
r.Get("/izvestaji", h.Izvestaji)
|
||||
|
||||
@@ -114,6 +114,7 @@ type ServisRepository interface {
|
||||
DohvatiID(ctx context.Context, id int64) (*model.ServisniNalog, error)
|
||||
Kreiraj(ctx context.Context, n *model.ServisniNalog) (int64, error)
|
||||
Izmeni(ctx context.Context, n *model.ServisniNalog) error
|
||||
AzurirajStatus(ctx context.Context, id int64, status string) error
|
||||
Obrisi(ctx context.Context, id int64) error
|
||||
SledeciBroj(ctx context.Context) (string, error)
|
||||
}
|
||||
|
||||
@@ -152,6 +152,24 @@ func (r *ServisRepo) Izmeni(ctx context.Context, n *model.ServisniNalog) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AzurirajStatus menja samo status naloga; ako nalog prelazi u završno stanje
|
||||
// i datum_zavrsetka još nije postavljen, automatski ga postavlja na danas.
|
||||
func (r *ServisRepo) AzurirajStatus(ctx context.Context, id int64, status string) error {
|
||||
var upit string
|
||||
if status == model.StatusZavrseno || status == model.StatusPreuzeto {
|
||||
upit = `UPDATE servisni_nalozi SET status = ?,
|
||||
datum_zavrsetka = COALESCE(datum_zavrsetka, date('now', 'localtime'))
|
||||
WHERE id = ?`
|
||||
} else {
|
||||
upit = `UPDATE servisni_nalozi SET status = ? WHERE id = ?`
|
||||
}
|
||||
_, err := r.db.ExecContext(ctx, upit, status, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ntech: ServisRepo.AzurirajStatus: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Obrisi briše servisni nalog po ID-u
|
||||
func (r *ServisRepo) Obrisi(ctx context.Context, id int64) error {
|
||||
_, err := r.db.ExecContext(ctx, "DELETE FROM servisni_nalozi WHERE id = ?", id)
|
||||
|
||||
@@ -49,6 +49,7 @@ type PodaciDetaljiNaloga struct {
|
||||
UkupnoDelovi float64
|
||||
UkupnoSve float64
|
||||
PreostaloSve float64
|
||||
SviStatusi []string
|
||||
}
|
||||
|
||||
// Servis renderuje listu servisnih naloga sa opcionom pretragom i filterom statusa
|
||||
@@ -389,6 +390,7 @@ func (h *Handler) DetaljiNaloga(w http.ResponseWriter, r *http.Request) {
|
||||
UkupnoDelovi: ukupnoDelovi,
|
||||
UkupnoSve: ukupnoSve,
|
||||
PreostaloSve: preostaloSve,
|
||||
SviStatusi: model.SviStatusi,
|
||||
}
|
||||
|
||||
h.renderujTemplate(w, "servis_detalji", podaci)
|
||||
@@ -756,3 +758,31 @@ func (h *Handler) StampaOtpremnice(w http.ResponseWriter, r *http.Request) {
|
||||
PIB: podesavanja["pib"],
|
||||
})
|
||||
}
|
||||
|
||||
// PromeniStatus obrađuje POST /servis/{id}/status i menja samo status naloga
|
||||
func (h *Handler) PromeniStatus(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := parseID(chi.URLParam(r, "id"))
|
||||
if err != nil {
|
||||
http.Error(w, "Neispravan ID naloga", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "Greška pri čitanju forme", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
noviStatus := strings.TrimSpace(r.FormValue("status"))
|
||||
dozvoljenStatusi := map[string]bool{}
|
||||
for _, s := range model.SviStatusi {
|
||||
dozvoljenStatusi[s] = true
|
||||
}
|
||||
if !dozvoljenStatusi[noviStatus] {
|
||||
http.Error(w, "Nepoznat status", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := h.ServisRepo.AzurirajStatus(r.Context(), id, noviStatus); err != nil {
|
||||
slog.Error("greška pri promeni statusa naloga", "id", id, "error", err)
|
||||
http.Error(w, "Greška pri promeni statusa", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/servis/"+strconv.FormatInt(id, 10)+"?sacuvano=1", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
@@ -43,7 +43,18 @@
|
||||
<span style="font-size:20px;font-weight:600;color:var(--tekst-glavni);font-family:monospace;">
|
||||
{{.Nalog.BrojNaloga}}
|
||||
</span>
|
||||
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
|
||||
{{template "status-badge-detalji" .Nalog.Status}}
|
||||
<form method="post" action="/servis/{{.Nalog.ID}}/status" style="display:flex;align-items:center;gap:6px;">
|
||||
<input type="hidden" name="csrf_token" value="{{.CsrfToken}}">
|
||||
<select name="status" style="font-size:13px;padding:4px 8px;border-radius:6px;border:0.5px solid var(--ivica);background:var(--kartica-pozadina);color:var(--tekst-glavni);cursor:pointer;">
|
||||
{{range .SviStatusi}}
|
||||
<option value="{{.}}"{{if eq . $.Nalog.Status}} selected{{end}}>{{.}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
<button type="submit" class="btn-sekundarno" style="font-size:13px;padding:4px 12px;">Promeni</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
||||
<a href="/servis/{{.Nalog.ID}}/stampa" target="_blank" class="btn-sekundarno">
|
||||
|
||||
Reference in New Issue
Block a user