Add themed UI, favicon, plan deletion, regrow cycles, and crop prep/post tasks

This commit is contained in:
Kai
2026-02-16 13:27:47 +01:00
parent 51181a83c8
commit a1c1ef31a3
14 changed files with 552 additions and 127 deletions

View File

@@ -210,11 +210,16 @@ func (a *App) handleCreateCrop(w http.ResponseWriter, r *http.Request) {
start := mustInt(r.FormValue("sow_start_month"), 0)
end := mustInt(r.FormValue("sow_end_month"), 0)
grow := mustInt(r.FormValue("grow_months"), 0)
if err := validateCropInput(name, start, end, grow); err != nil {
regrowEnabled := r.FormValue("regrow_enabled") == "on"
regrowCycles := mustInt(r.FormValue("regrow_cycles"), 0)
if err := validateCropInput(name, start, end, grow, regrowCycles); err != nil {
redirectWithMessage(w, r, "/crops", "error", err.Error())
return
}
if _, err := a.db.Exec(`INSERT INTO crops(name,sow_start_month,sow_end_month,grow_months) VALUES (?,?,?,?)`, name, start, end, grow); err != nil {
if !regrowEnabled {
regrowCycles = 0
}
if _, err := a.db.Exec(`INSERT INTO crops(name,sow_start_month,sow_end_month,grow_months,regrow_enabled,regrow_cycles) VALUES (?,?,?,?,?,?)`, name, start, end, grow, regrowEnabled, regrowCycles); err != nil {
redirectWithMessage(w, r, "/crops", "error", "Feldfrucht nicht angelegt")
return
}
@@ -235,15 +240,20 @@ func (a *App) handleUpdateCrop(w http.ResponseWriter, r *http.Request) {
start := mustInt(r.FormValue("sow_start_month"), 0)
end := mustInt(r.FormValue("sow_end_month"), 0)
grow := mustInt(r.FormValue("grow_months"), 0)
regrowEnabled := r.FormValue("regrow_enabled") == "on"
regrowCycles := mustInt(r.FormValue("regrow_cycles"), 0)
if id <= 0 {
redirectWithMessage(w, r, "/crops", "error", "Feldfrucht-ID ungueltig")
return
}
if err := validateCropInput(name, start, end, grow); err != nil {
if err := validateCropInput(name, start, end, grow, regrowCycles); err != nil {
redirectWithMessage(w, r, "/crops", "error", err.Error())
return
}
if _, err := a.db.Exec(`UPDATE crops SET name=?,sow_start_month=?,sow_end_month=?,grow_months=? WHERE id=?`, name, start, end, grow, id); err != nil {
if !regrowEnabled {
regrowCycles = 0
}
if _, err := a.db.Exec(`UPDATE crops SET name=?,sow_start_month=?,sow_end_month=?,grow_months=?,regrow_enabled=?,regrow_cycles=? WHERE id=?`, name, start, end, grow, regrowEnabled, regrowCycles, id); err != nil {
redirectWithMessage(w, r, "/crops", "error", "Feldfrucht nicht aktualisiert")
return
}
@@ -331,6 +341,72 @@ func (a *App) handleCreatePlan(w http.ResponseWriter, r *http.Request) {
redirectWithMessage(w, r, "/planning", "info", "Plan gespeichert")
}
func (a *App) handleDeletePlan(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
if err := r.ParseForm(); err != nil {
redirectWithMessage(w, r, "/planning", "error", "Form ungueltig")
return
}
id := mustInt64(r.FormValue("id"), 0)
if id <= 0 {
redirectWithMessage(w, r, "/planning", "error", "Plan-ID ungueltig")
return
}
if _, err := a.db.Exec(`DELETE FROM plans WHERE id=?`, id); err != nil {
redirectWithMessage(w, r, "/planning", "error", "Plan nicht geloescht")
return
}
redirectWithMessage(w, r, "/planning", "info", "Plan geloescht")
}
func (a *App) handleCreateCropStep(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
if err := r.ParseForm(); err != nil {
redirectWithMessage(w, r, "/crops", "error", "Form ungueltig")
return
}
cropID := mustInt64(r.FormValue("crop_id"), 0)
phase := strings.TrimSpace(r.FormValue("phase"))
offset := mustInt(r.FormValue("month_offset"), -1)
title := strings.TrimSpace(r.FormValue("title"))
if cropID <= 0 || (phase != "pre" && phase != "post") || offset < 0 || offset > 24 || title == "" {
redirectWithMessage(w, r, "/crops", "error", "Vor/Nachbereitung ungueltig")
return
}
if _, err := a.db.Exec(`INSERT INTO crop_steps(crop_id,phase,month_offset,title) VALUES (?,?,?,?)`, cropID, phase, offset, title); err != nil {
redirectWithMessage(w, r, "/crops", "error", "Schritt nicht gespeichert")
return
}
redirectWithMessage(w, r, "/crops", "info", "Schritt gespeichert")
}
func (a *App) handleDeleteCropStep(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
if err := r.ParseForm(); err != nil {
redirectWithMessage(w, r, "/crops", "error", "Form ungueltig")
return
}
id := mustInt64(r.FormValue("id"), 0)
if id <= 0 {
redirectWithMessage(w, r, "/crops", "error", "Schritt-ID ungueltig")
return
}
if _, err := a.db.Exec(`DELETE FROM crop_steps WHERE id=?`, id); err != nil {
redirectWithMessage(w, r, "/crops", "error", "Schritt nicht geloescht")
return
}
redirectWithMessage(w, r, "/crops", "info", "Schritt geloescht")
}
func (a *App) resolvePlanningTarget(ref string) (sql.NullInt64, string, error) {
if strings.HasPrefix(ref, "f:") {
id := mustInt64(strings.TrimPrefix(ref, "f:"), 0)