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}", 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}/stampa", h.StampaServisa)
|
||||||
r.With(ntechmw.RequireDozvola(h.DozvoleRepo.ImaDozvolu, "servis.pregled")).Get("/servis/{id}/otpremnica", h.StampaOtpremnice)
|
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", h.DodajDeloNalogu)
|
||||||
r.With(doz("servis.izmeni")).Post("/servis/{id}/delovi/{deo_id}/obrisi", h.ObrisiDeloNaloga)
|
r.With(doz("servis.izmeni")).Post("/servis/{id}/delovi/{deo_id}/obrisi", h.ObrisiDeloNaloga)
|
||||||
r.Get("/izvestaji", h.Izvestaji)
|
r.Get("/izvestaji", h.Izvestaji)
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ type ServisRepository interface {
|
|||||||
DohvatiID(ctx context.Context, id int64) (*model.ServisniNalog, error)
|
DohvatiID(ctx context.Context, id int64) (*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
|
||||||
Obrisi(ctx context.Context, id int64) error
|
Obrisi(ctx context.Context, id int64) error
|
||||||
SledeciBroj(ctx context.Context) (string, error)
|
SledeciBroj(ctx context.Context) (string, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,6 +152,24 @@ func (r *ServisRepo) Izmeni(ctx context.Context, n *model.ServisniNalog) error {
|
|||||||
return nil
|
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
|
// Obrisi briše servisni nalog po ID-u
|
||||||
func (r *ServisRepo) Obrisi(ctx context.Context, id int64) error {
|
func (r *ServisRepo) Obrisi(ctx context.Context, id int64) error {
|
||||||
_, err := r.db.ExecContext(ctx, "DELETE FROM servisni_nalozi WHERE id = ?", id)
|
_, err := r.db.ExecContext(ctx, "DELETE FROM servisni_nalozi WHERE id = ?", id)
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ type PodaciDetaljiNaloga struct {
|
|||||||
UkupnoDelovi float64
|
UkupnoDelovi float64
|
||||||
UkupnoSve float64
|
UkupnoSve float64
|
||||||
PreostaloSve float64
|
PreostaloSve float64
|
||||||
|
SviStatusi []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Servis renderuje listu servisnih naloga sa opcionom pretragom i filterom statusa
|
// 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,
|
UkupnoDelovi: ukupnoDelovi,
|
||||||
UkupnoSve: ukupnoSve,
|
UkupnoSve: ukupnoSve,
|
||||||
PreostaloSve: preostaloSve,
|
PreostaloSve: preostaloSve,
|
||||||
|
SviStatusi: model.SviStatusi,
|
||||||
}
|
}
|
||||||
|
|
||||||
h.renderujTemplate(w, "servis_detalji", podaci)
|
h.renderujTemplate(w, "servis_detalji", podaci)
|
||||||
@@ -756,3 +758,31 @@ func (h *Handler) StampaOtpremnice(w http.ResponseWriter, r *http.Request) {
|
|||||||
PIB: podesavanja["pib"],
|
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;">
|
<span style="font-size:20px;font-weight:600;color:var(--tekst-glavni);font-family:monospace;">
|
||||||
{{.Nalog.BrojNaloga}}
|
{{.Nalog.BrojNaloga}}
|
||||||
</span>
|
</span>
|
||||||
{{template "status-badge-detalji" .Nalog.Status}}
|
<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>
|
||||||
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
||||||
<a href="/servis/{{.Nalog.ID}}/stampa" target="_blank" class="btn-sekundarno">
|
<a href="/servis/{{.Nalog.ID}}/stampa" target="_blank" class="btn-sekundarno">
|
||||||
|
|||||||
Reference in New Issue
Block a user