@@ -0,0 +1,244 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gardomatic.kleiax.de/lib/client"
|
||||
)
|
||||
|
||||
func (app *application) savePlantTasks(r *http.Request, gardenID, plantID int, forms []plantTaskForm) error {
|
||||
apiClient := client.FromContext(r.Context())
|
||||
for _, form := range forms {
|
||||
if form.ID > 0 {
|
||||
existing, _, err := apiClient.Task(r.Context(), gardenID, form.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existing.PlantID == nil || *existing.PlantID != plantID || existing.TemplateID != nil {
|
||||
return fmt.Errorf("task %d does not belong to plant %d or was generated from a template", form.ID, plantID)
|
||||
}
|
||||
if form.Delete {
|
||||
if _, err = apiClient.DeleteTask(r.Context(), gardenID, form.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
if form.Delete {
|
||||
continue
|
||||
}
|
||||
start, _ := parseTaskTime(form.DueAtStart, "", map[string]string{})
|
||||
end, _ := parseTaskTime(form.DueAtEnd, "", map[string]string{})
|
||||
active := form.Active
|
||||
input := client.TaskInput{PlantID: &plantID, Title: &form.Title, Description: &form.Description, DueAtStart: start, DueAtEnd: end, ClearDueAtStart: start == nil, ClearDueAtEnd: end == nil, Priority: &form.Priority, Active: &active, Recurrence: &form.Recurrence, RecurrenceInterval: &form.RecurrenceInterval, Tags: parseKeywords(form.Keywords), PlantStatusOnCompletion: &form.PlantStatusOnCompletion}
|
||||
if form.LocationID > 0 {
|
||||
input.LocationID = &form.LocationID
|
||||
} else {
|
||||
input.ClearLocationID = true
|
||||
}
|
||||
if form.ID > 0 {
|
||||
if _, _, err := apiClient.UpdateTask(r.Context(), gardenID, form.ID, input); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if _, _, err := apiClient.CreateTask(r.Context(), gardenID, input); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *application) savePlantTemplateStates(r *http.Request, gardenID, plantID, speciesID int) error {
|
||||
templates, _, err := client.FromContext(r.Context()).SpeciesTaskTemplates(r.Context(), gardenID, speciesID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range templates {
|
||||
values, present := r.PostForm["plant_template_active_"+strconv.Itoa(item.ID)]
|
||||
if !present {
|
||||
continue
|
||||
}
|
||||
enabled := len(values) > 0 && values[len(values)-1] == "true"
|
||||
if _, err = client.FromContext(r.Context()).SetTaskTemplateOptOut(r.Context(), gardenID, plantID, item.ID, !enabled); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *application) plantTaskRow(w http.ResponseWriter, r *http.Request) {
|
||||
gardenID, err := app.readPathID(r, "gardenID")
|
||||
if err != nil {
|
||||
app.notFound(w)
|
||||
return
|
||||
}
|
||||
form := plantTaskFromValues(r.URL.Query())
|
||||
newTask := r.URL.Query().Get("new") == "true" || form.RowKey == ""
|
||||
if form.RowKey == "" {
|
||||
form.RowKey = "task-new-" + strconv.FormatInt(time.Now().UnixNano(), 36)
|
||||
form.Active = true
|
||||
}
|
||||
form.Errors = map[string]string{}
|
||||
data := app.newTemplateData(r)
|
||||
if !app.loadPlantTaskDialogData(w, r, gardenID, data) {
|
||||
return
|
||||
}
|
||||
data.PendingPlantName = form.PendingPlantName
|
||||
data.Garden = &client.Garden{ID: gardenID}
|
||||
data.Form, data.TaskDialogNew = form, newTask
|
||||
app.renderTemplate(w, http.StatusOK, "plant_form.tmpl", "plant_task_dialog", data)
|
||||
}
|
||||
|
||||
func (app *application) plantTaskRowPost(w http.ResponseWriter, r *http.Request) {
|
||||
gardenID, err := app.readPathID(r, "gardenID")
|
||||
if err != nil {
|
||||
app.notFound(w)
|
||||
return
|
||||
}
|
||||
if err = r.ParseForm(); err != nil {
|
||||
app.clientError(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
form := plantTaskFromValues(r.PostForm)
|
||||
form.Title, form.Description = strings.TrimSpace(form.Title), strings.TrimSpace(form.Description)
|
||||
form.Errors = map[string]string{}
|
||||
if form.Title == "" {
|
||||
form.Errors["title"] = "Ein Titel ist erforderlich."
|
||||
}
|
||||
start, _ := parseTaskTime(form.DueAtStart, "due_at_start", form.Errors)
|
||||
end, _ := parseTaskTime(form.DueAtEnd, "due_at_end", form.Errors)
|
||||
if start != nil && end != nil && start.After(*end) {
|
||||
form.Errors["due_at_end"] = "Das Ende darf nicht vor dem Beginn liegen."
|
||||
}
|
||||
newTask := r.PostForm.Get("new") == "true"
|
||||
data := app.newTemplateData(r)
|
||||
data.Garden, data.Form, data.TaskDialogNew = &client.Garden{ID: gardenID}, form, newTask
|
||||
data.PendingPlantName = form.PendingPlantName
|
||||
if len(form.Errors) > 0 {
|
||||
if !app.loadPlantTaskDialogData(w, r, gardenID, data) {
|
||||
return
|
||||
}
|
||||
w.Header().Set("HX-Retarget", "#plant-task-dialog-host")
|
||||
w.Header().Set("HX-Reswap", "innerHTML")
|
||||
app.renderTemplate(w, http.StatusOK, "plant_form.tmpl", "plant_task_dialog", data)
|
||||
return
|
||||
}
|
||||
w.Header().Set("HX-Trigger", "plantTaskSaved")
|
||||
data.PlantTasks = []plantTaskForm{form}
|
||||
app.renderTemplate(w, http.StatusOK, "plant_form.tmpl", "plant_task_row_fragment", data)
|
||||
}
|
||||
|
||||
func (app *application) loadPlantTaskDialogData(w http.ResponseWriter, r *http.Request, gardenID int, data *templateData) bool {
|
||||
apiClient := client.FromContext(r.Context())
|
||||
var err error
|
||||
data.TaskPriorities, _, err = apiClient.TaskPriorities(r.Context())
|
||||
if err == nil {
|
||||
data.Locations, _, err = apiClient.Locations(r.Context(), gardenID)
|
||||
}
|
||||
if err == nil {
|
||||
data.Plants, _, err = apiClient.Plants(r.Context(), gardenID)
|
||||
}
|
||||
if err != nil {
|
||||
app.handleAPIError(w, r, err)
|
||||
return false
|
||||
}
|
||||
data.TagSuggestions = app.loadTagSuggestions(r, gardenID)
|
||||
return true
|
||||
}
|
||||
|
||||
func plantTaskFromValues(values url.Values) plantTaskForm {
|
||||
id, _ := strconv.Atoi(values.Get("plant_task_id"))
|
||||
priority, _ := strconv.Atoi(values.Get("plant_task_priority"))
|
||||
plantID, _ := strconv.Atoi(values.Get("plant_task_plant_id"))
|
||||
if plantID == 0 {
|
||||
plantID, _ = strconv.Atoi(values.Get("plant_id"))
|
||||
}
|
||||
locationID, _ := strconv.Atoi(values.Get("plant_task_location_id"))
|
||||
if locationID == 0 {
|
||||
locationID, _ = strconv.Atoi(values.Get("location_id"))
|
||||
}
|
||||
recurrenceInterval, _ := strconv.Atoi(values.Get("plant_task_recurrence_interval"))
|
||||
if recurrenceInterval < 1 {
|
||||
recurrenceInterval = 1
|
||||
}
|
||||
pendingPlantName := values.Get("pending_plant_name")
|
||||
if pendingPlantName == "" {
|
||||
pendingPlantName = values.Get("name")
|
||||
}
|
||||
return plantTaskForm{
|
||||
ID: id, RowKey: values.Get("plant_task_row_key"), Title: values.Get("plant_task_title"),
|
||||
Description: values.Get("plant_task_description"), DueAtStart: values.Get("plant_task_due_at_start"), PlantID: plantID,
|
||||
DueAtEnd: values.Get("plant_task_due_at_end"), Priority: priority, LocationID: locationID, Recurrence: values.Get("plant_task_recurrence"), RecurrenceInterval: recurrenceInterval, Keywords: values.Get("plant_task_keywords"), PlantStatusOnCompletion: values.Get("plant_task_status_on_completion"),
|
||||
PendingPlantName: strings.TrimSpace(pendingPlantName),
|
||||
Active: values.Get("plant_task_active") != "false", Delete: values.Get("plant_task_delete") == "true",
|
||||
}
|
||||
}
|
||||
|
||||
func (app *application) plantTemplateTasks(w http.ResponseWriter, r *http.Request) {
|
||||
gardenID, err := app.readPathID(r, "gardenID")
|
||||
if err != nil {
|
||||
app.notFound(w)
|
||||
return
|
||||
}
|
||||
speciesID, _ := strconv.Atoi(r.URL.Query().Get("species_id"))
|
||||
plantID, _ := strconv.Atoi(r.URL.Query().Get("plant_id"))
|
||||
data := app.newTemplateData(r)
|
||||
data.Garden, data.SpeciesID, data.PlantID = &client.Garden{ID: gardenID}, speciesID, plantID
|
||||
data.TaskTemplates, data.TemplateOptOuts = map[int][]client.SpeciesTaskTemplate{}, map[int]bool{}
|
||||
if speciesID > 0 {
|
||||
templates, _, loadErr := client.FromContext(r.Context()).SpeciesTaskTemplates(r.Context(), gardenID, speciesID)
|
||||
if loadErr != nil {
|
||||
app.handleAPIError(w, r, loadErr)
|
||||
return
|
||||
}
|
||||
data.TaskTemplates[speciesID] = templates
|
||||
}
|
||||
if plantID > 0 {
|
||||
ids, _, loadErr := client.FromContext(r.Context()).TaskTemplateOptOuts(r.Context(), gardenID, plantID)
|
||||
if loadErr != nil {
|
||||
app.handleAPIError(w, r, loadErr)
|
||||
return
|
||||
}
|
||||
for _, id := range ids {
|
||||
data.TemplateOptOuts[id] = true
|
||||
}
|
||||
}
|
||||
app.renderTemplate(w, http.StatusOK, "plant_form.tmpl", "plant_template_tasks", data)
|
||||
}
|
||||
|
||||
func (app *application) plantTemplateTaskView(w http.ResponseWriter, r *http.Request) {
|
||||
gardenID, err := app.readPathID(r, "gardenID")
|
||||
if err != nil {
|
||||
app.notFound(w)
|
||||
return
|
||||
}
|
||||
templateID, err := app.readPathID(r, "templateID")
|
||||
if err != nil {
|
||||
app.notFound(w)
|
||||
return
|
||||
}
|
||||
speciesID, _ := strconv.Atoi(r.URL.Query().Get("species_id"))
|
||||
if speciesID < 1 {
|
||||
app.notFound(w)
|
||||
return
|
||||
}
|
||||
item, _, err := client.FromContext(r.Context()).SpeciesTaskTemplate(r.Context(), gardenID, speciesID, templateID)
|
||||
if err != nil {
|
||||
app.handleAPIError(w, r, err)
|
||||
return
|
||||
}
|
||||
data := app.newTemplateData(r)
|
||||
data.Garden, data.SpeciesID = &client.Garden{ID: gardenID}, speciesID
|
||||
priorities, _, err := client.FromContext(r.Context()).TaskPriorities(r.Context())
|
||||
if err != nil {
|
||||
app.handleAPIError(w, r, err)
|
||||
return
|
||||
}
|
||||
data.TaskPriorities = priorities
|
||||
data.TaskTemplates = map[int][]client.SpeciesTaskTemplate{speciesID: {item}}
|
||||
app.renderTemplate(w, http.StatusOK, "plant_form.tmpl", "plant_template_task_dialog", data)
|
||||
}
|
||||
Reference in New Issue
Block a user