Fortlaufende Zyklus-Planung und Periodenlogik einführen
This commit is contained in:
@@ -13,14 +13,16 @@ func buildTasksForDay(plans []Plan, crops []Crop, products []Product, stepMap ma
|
||||
return tasks
|
||||
}
|
||||
|
||||
func buildCalendar(plans []Plan, crops []Crop, products []Product, stepMap map[int64][]CropStep, customTasks []CustomTask, doneMap map[string]bool, startMonth, startDay, daysPerMonth, spanMonths int) []CalendarMonth {
|
||||
func buildCalendar(plans []Plan, crops []Crop, products []Product, stepMap map[int64][]CropStep, customTasks []CustomTask, doneMap map[string]bool, startPeriod, startDay, daysPerMonth, spanMonths int) []CalendarMonth {
|
||||
var out []CalendarMonth
|
||||
startCycle := periodCycle(startPeriod)
|
||||
for offset := 0; offset < spanMonths; offset++ {
|
||||
month := wrapMonth(startMonth + offset)
|
||||
yearOffset := (startMonth - 1 + offset) / 12
|
||||
period := startPeriod + offset
|
||||
month := periodMonth(period)
|
||||
yearOffset := periodCycle(period) - startCycle
|
||||
label := monthNames[month-1]
|
||||
if yearOffset > 0 {
|
||||
label = fmt.Sprintf("%s (+%d Jahr)", label, yearOffset)
|
||||
label = fmt.Sprintf("%s (+%d Zyklus)", label, yearOffset)
|
||||
}
|
||||
|
||||
fromDay := 1
|
||||
@@ -30,7 +32,7 @@ func buildCalendar(plans []Plan, crops []Crop, products []Product, stepMap map[i
|
||||
|
||||
var days []CalendarDay
|
||||
for d := fromDay; d <= daysPerMonth; d++ {
|
||||
items := collectTasksForDate(plans, crops, products, stepMap, customTasks, month, d, yearOffset)
|
||||
items := collectTasksForDate(plans, crops, products, stepMap, customTasks, period, d, yearOffset)
|
||||
applyCompletion(items, doneMap)
|
||||
sortTasks(items)
|
||||
days = append(days, CalendarDay{Day: d, Tasks: items, Groups: groupTasksByField(items)})
|
||||
@@ -65,11 +67,12 @@ func groupTasksByField(tasks []Task) []FieldTaskGroup {
|
||||
return out
|
||||
}
|
||||
|
||||
func collectTasksForDate(plans []Plan, crops []Crop, products []Product, stepMap map[int64][]CropStep, customTasks []CustomTask, month, day, yearOffset int) []Task {
|
||||
func collectTasksForDate(plans []Plan, crops []Crop, products []Product, stepMap map[int64][]CropStep, customTasks []CustomTask, period, day, yearOffset int) []Task {
|
||||
var tasks []Task
|
||||
month := periodMonth(period)
|
||||
|
||||
for _, p := range plans {
|
||||
tasks = append(tasks, expandPlanDayTasks(p, stepMap[p.CropID], month, day, yearOffset)...)
|
||||
tasks = append(tasks, expandPlanDayTasks(p, stepMap[p.CropID], period, day, yearOffset)...)
|
||||
}
|
||||
if day == 1 {
|
||||
for _, c := range crops {
|
||||
@@ -102,7 +105,7 @@ func collectTasksForDate(plans []Plan, crops []Crop, products []Product, stepMap
|
||||
}
|
||||
}
|
||||
for _, c := range customTasks {
|
||||
if c.Month == month && c.Day == day && c.YearOffset == yearOffset {
|
||||
if c.TaskPeriod == period && c.Day == day {
|
||||
field := c.TargetName
|
||||
if strings.TrimSpace(field) == "" {
|
||||
field = "Allgemein"
|
||||
@@ -116,7 +119,7 @@ func collectTasksForDate(plans []Plan, crops []Crop, products []Product, stepMap
|
||||
Type: "Aufgabe",
|
||||
Field: field,
|
||||
Message: msg,
|
||||
Month: month,
|
||||
Month: periodMonth(period),
|
||||
Day: day,
|
||||
YearOffset: yearOffset,
|
||||
SortOrder: 40,
|
||||
@@ -127,18 +130,18 @@ func collectTasksForDate(plans []Plan, crops []Crop, products []Product, stepMap
|
||||
return tasks
|
||||
}
|
||||
|
||||
func expandPlanDayTasks(p Plan, steps []CropStep, month, day, yearOffset int) []Task {
|
||||
func expandPlanDayTasks(p Plan, steps []CropStep, period, day, yearOffset int) []Task {
|
||||
field := p.TargetName
|
||||
if strings.TrimSpace(field) == "" {
|
||||
field = "Unbekanntes Feld"
|
||||
}
|
||||
month := periodMonth(period)
|
||||
|
||||
var out []Task
|
||||
baseHarvest := p.HarvestMonth
|
||||
|
||||
if p.StartMonth == month && p.StartDay == day && yearOffset == 0 {
|
||||
if p.StartPeriod == period && p.StartDay == day {
|
||||
out = append(out, Task{
|
||||
UID: fmt.Sprintf("plan:%d:sow:0", p.ID),
|
||||
UID: fmt.Sprintf("plan:%d:sow:%d", p.ID, p.StartPeriod),
|
||||
Type: "Aussaat",
|
||||
Field: field,
|
||||
Message: withOptionalNote(fmt.Sprintf("Aussaat %s", p.CropName), p.Notes),
|
||||
@@ -149,21 +152,21 @@ func expandPlanDayTasks(p Plan, steps []CropStep, month, day, yearOffset int) []
|
||||
})
|
||||
}
|
||||
|
||||
harvestMonths := []int{baseHarvest}
|
||||
harvestPeriods := []int{p.HarvestPeriod}
|
||||
if p.RegrowEnabled {
|
||||
maxExtra := p.RegrowCycles
|
||||
if maxExtra == 0 {
|
||||
maxExtra = 24
|
||||
}
|
||||
for i := 1; i <= maxExtra; i++ {
|
||||
harvestMonths = append(harvestMonths, wrapMonth(baseHarvest+(i*p.HarvestDistance())))
|
||||
harvestPeriods = append(harvestPeriods, p.HarvestPeriod+(i*p.HarvestDistance()))
|
||||
}
|
||||
}
|
||||
harvestMonths = uniqueMonths(harvestMonths)
|
||||
harvestPeriods = uniquePeriods(harvestPeriods)
|
||||
|
||||
for idx, hm := range harvestMonths {
|
||||
if hm == month && p.HarvestDay == day {
|
||||
uid := fmt.Sprintf("plan:%d:harvest:%d", p.ID, idx)
|
||||
for _, harvestPeriod := range harvestPeriods {
|
||||
if harvestPeriod == period && p.HarvestDay == day {
|
||||
uid := fmt.Sprintf("plan:%d:harvest:%d", p.ID, harvestPeriod)
|
||||
out = append(out, Task{
|
||||
UID: uid,
|
||||
Type: "Ernte",
|
||||
@@ -180,10 +183,10 @@ func expandPlanDayTasks(p Plan, steps []CropStep, month, day, yearOffset int) []
|
||||
for _, s := range steps {
|
||||
switch s.Phase {
|
||||
case "pre":
|
||||
taskMonth := wrapMonth(p.StartMonth - s.MonthOffset)
|
||||
if taskMonth == month && p.StartDay == day && yearOffset == 0 {
|
||||
taskPeriod := p.StartPeriod - s.MonthOffset
|
||||
if taskPeriod == period && p.StartDay == day {
|
||||
out = append(out, Task{
|
||||
UID: fmt.Sprintf("plan:%d:pre:%d", p.ID, s.ID),
|
||||
UID: fmt.Sprintf("plan:%d:pre:%d:%d", p.ID, s.ID, taskPeriod),
|
||||
Type: "Vorbereitung",
|
||||
Field: field,
|
||||
Message: s.Title,
|
||||
@@ -194,11 +197,11 @@ func expandPlanDayTasks(p Plan, steps []CropStep, month, day, yearOffset int) []
|
||||
})
|
||||
}
|
||||
case "post":
|
||||
for idx, hm := range harvestMonths {
|
||||
taskMonth := wrapMonth(hm + s.MonthOffset)
|
||||
if taskMonth == month && p.HarvestDay == day {
|
||||
for _, harvestPeriod := range harvestPeriods {
|
||||
taskPeriod := harvestPeriod + s.MonthOffset
|
||||
if taskPeriod == period && p.HarvestDay == day {
|
||||
out = append(out, Task{
|
||||
UID: fmt.Sprintf("plan:%d:post:%d:%d", p.ID, s.ID, idx),
|
||||
UID: fmt.Sprintf("plan:%d:post:%d:%d", p.ID, s.ID, taskPeriod),
|
||||
Type: "Nachbereitung",
|
||||
Field: field,
|
||||
Message: s.Title,
|
||||
@@ -252,7 +255,7 @@ func sortTasks(tasks []Task) {
|
||||
})
|
||||
}
|
||||
|
||||
func uniqueMonths(values []int) []int {
|
||||
func uniquePeriods(values []int) []int {
|
||||
seen := make(map[int]bool)
|
||||
var out []int
|
||||
for _, v := range values {
|
||||
|
||||
@@ -11,12 +11,15 @@ func ensureSchema(db *sql.DB) error {
|
||||
id TINYINT PRIMARY KEY,
|
||||
days_per_month INT NOT NULL DEFAULT 2,
|
||||
current_month TINYINT NOT NULL DEFAULT 1,
|
||||
current_cycle INT NOT NULL DEFAULT 0,
|
||||
current_day TINYINT NOT NULL DEFAULT 1
|
||||
)`,
|
||||
`ALTER TABLE settings ADD COLUMN IF NOT EXISTS current_month TINYINT NOT NULL DEFAULT 1`,
|
||||
`ALTER TABLE settings ADD COLUMN IF NOT EXISTS current_cycle INT NOT NULL DEFAULT 0`,
|
||||
`ALTER TABLE settings ADD COLUMN IF NOT EXISTS current_day TINYINT NOT NULL DEFAULT 1`,
|
||||
`INSERT INTO settings(id,days_per_month,current_month,current_day) VALUES (1,2,1,1) ON DUPLICATE KEY UPDATE id=id`,
|
||||
`INSERT INTO settings(id,days_per_month,current_month,current_cycle,current_day) VALUES (1,2,1,0,1) ON DUPLICATE KEY UPDATE id=id`,
|
||||
`UPDATE settings SET current_month=1 WHERE current_month < 1 OR current_month > 12`,
|
||||
`UPDATE settings SET current_cycle=0 WHERE current_cycle < 0`,
|
||||
`UPDATE settings SET current_day=1 WHERE current_day < 1`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS fields(
|
||||
@@ -71,6 +74,7 @@ func ensureSchema(db *sql.DB) error {
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
template_id BIGINT NULL,
|
||||
title VARCHAR(140) NOT NULL,
|
||||
task_period INT NOT NULL DEFAULT 0,
|
||||
month TINYINT NOT NULL,
|
||||
day TINYINT NOT NULL,
|
||||
year_offset SMALLINT NOT NULL DEFAULT 0,
|
||||
@@ -95,8 +99,10 @@ func ensureSchema(db *sql.DB) error {
|
||||
target_ref VARCHAR(80) NOT NULL DEFAULT '',
|
||||
target_name VARCHAR(140) NOT NULL DEFAULT '',
|
||||
crop_id BIGINT NOT NULL,
|
||||
start_period INT NOT NULL DEFAULT 0,
|
||||
start_month TINYINT NOT NULL,
|
||||
start_day TINYINT NOT NULL,
|
||||
harvest_period INT NOT NULL DEFAULT 0,
|
||||
harvest_month TINYINT NOT NULL,
|
||||
harvest_day TINYINT NOT NULL,
|
||||
notes VARCHAR(255) NOT NULL DEFAULT '',
|
||||
@@ -107,6 +113,12 @@ func ensureSchema(db *sql.DB) error {
|
||||
`ALTER TABLE plans MODIFY COLUMN field_id BIGINT NULL`,
|
||||
`ALTER TABLE plans ADD COLUMN IF NOT EXISTS target_ref VARCHAR(80) NOT NULL DEFAULT '' AFTER field_id`,
|
||||
`ALTER TABLE plans ADD COLUMN IF NOT EXISTS target_name VARCHAR(140) NOT NULL DEFAULT '' AFTER target_ref`,
|
||||
`ALTER TABLE plans ADD COLUMN IF NOT EXISTS start_period INT NOT NULL DEFAULT 0 AFTER crop_id`,
|
||||
`ALTER TABLE plans ADD COLUMN IF NOT EXISTS harvest_period INT NOT NULL DEFAULT 0 AFTER start_day`,
|
||||
`UPDATE plans SET start_period = start_month - 1 WHERE start_period = 0 AND start_month <> 1`,
|
||||
`UPDATE plans SET harvest_period = start_period + (CASE WHEN harvest_month >= start_month THEN harvest_month - start_month ELSE harvest_month + 12 - start_month END) WHERE harvest_period = 0`,
|
||||
`ALTER TABLE custom_tasks ADD COLUMN IF NOT EXISTS task_period INT NOT NULL DEFAULT 0 AFTER title`,
|
||||
`UPDATE custom_tasks SET task_period = (year_offset * 12) + (month - 1) WHERE task_period = 0 AND (year_offset <> 0 OR month <> 1)`,
|
||||
}
|
||||
for _, stmt := range stmts {
|
||||
if _, err := db.Exec(stmt); err != nil {
|
||||
@@ -118,8 +130,8 @@ func ensureSchema(db *sql.DB) error {
|
||||
|
||||
func (a *App) getSettings() (Settings, error) {
|
||||
var s Settings
|
||||
err := a.db.QueryRow(`SELECT days_per_month,current_month,current_day FROM settings WHERE id=1`).
|
||||
Scan(&s.DaysPerMonth, &s.CurrentMonth, &s.CurrentDay)
|
||||
err := a.db.QueryRow(`SELECT days_per_month,current_month,current_cycle,current_day FROM settings WHERE id=1`).
|
||||
Scan(&s.DaysPerMonth, &s.CurrentMonth, &s.CurrentCycle, &s.CurrentDay)
|
||||
return s, err
|
||||
}
|
||||
|
||||
@@ -199,10 +211,10 @@ func (a *App) listProducts() ([]Product, error) {
|
||||
|
||||
func (a *App) listPlans() ([]Plan, error) {
|
||||
rows, err := a.db.Query(`
|
||||
SELECT p.id,p.field_id,COALESCE(p.target_ref,''),COALESCE(p.target_name,''),p.crop_id,COALESCE(c.name,''),COALESCE(c.grow_months,1),p.start_month,p.start_day,p.harvest_month,p.harvest_day,COALESCE(p.notes,''),COALESCE(c.regrow_enabled,0),COALESCE(c.regrow_cycles,0)
|
||||
SELECT p.id,p.field_id,COALESCE(p.target_ref,''),COALESCE(p.target_name,''),p.crop_id,COALESCE(c.name,''),COALESCE(c.grow_months,1),p.start_period,p.start_month,p.start_day,p.harvest_period,p.harvest_month,p.harvest_day,COALESCE(p.notes,''),COALESCE(c.regrow_enabled,0),COALESCE(c.regrow_cycles,0)
|
||||
FROM plans p
|
||||
JOIN crops c ON c.id=p.crop_id
|
||||
ORDER BY p.start_month,p.start_day,p.id DESC`)
|
||||
ORDER BY p.start_period,p.start_day,p.id DESC`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -210,9 +222,11 @@ func (a *App) listPlans() ([]Plan, error) {
|
||||
var out []Plan
|
||||
for rows.Next() {
|
||||
var p Plan
|
||||
if err := rows.Scan(&p.ID, &p.FieldID, &p.TargetRef, &p.TargetName, &p.CropID, &p.CropName, &p.GrowMonths, &p.StartMonth, &p.StartDay, &p.HarvestMonth, &p.HarvestDay, &p.Notes, &p.RegrowEnabled, &p.RegrowCycles); err != nil {
|
||||
if err := rows.Scan(&p.ID, &p.FieldID, &p.TargetRef, &p.TargetName, &p.CropID, &p.CropName, &p.GrowMonths, &p.StartPeriod, &p.StartMonth, &p.StartDay, &p.HarvestPeriod, &p.HarvestMonth, &p.HarvestDay, &p.Notes, &p.RegrowEnabled, &p.RegrowCycles); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.StartCycle = periodCycle(p.StartPeriod)
|
||||
p.HarvestCycle = periodCycle(p.HarvestPeriod)
|
||||
out = append(out, p)
|
||||
}
|
||||
return out, rows.Err()
|
||||
@@ -272,9 +286,9 @@ func (a *App) listCustomTaskTemplates() ([]CustomTaskTemplate, error) {
|
||||
|
||||
func (a *App) listCustomTasks() ([]CustomTask, error) {
|
||||
rows, err := a.db.Query(`
|
||||
SELECT id,template_id,title,month,day,year_offset,COALESCE(target_name,''),COALESCE(notes,'')
|
||||
SELECT id,template_id,title,task_period,month,day,year_offset,COALESCE(target_name,''),COALESCE(notes,'')
|
||||
FROM custom_tasks
|
||||
ORDER BY year_offset, month, day, title`)
|
||||
ORDER BY task_period, day, title`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -283,9 +297,11 @@ func (a *App) listCustomTasks() ([]CustomTask, error) {
|
||||
var out []CustomTask
|
||||
for rows.Next() {
|
||||
var t CustomTask
|
||||
if err := rows.Scan(&t.ID, &t.TemplateID, &t.Title, &t.Month, &t.Day, &t.YearOffset, &t.TargetName, &t.Notes); err != nil {
|
||||
if err := rows.Scan(&t.ID, &t.TemplateID, &t.Title, &t.TaskPeriod, &t.Month, &t.Day, &t.YearOffset, &t.TargetName, &t.Notes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.Month = periodMonth(t.TaskPeriod)
|
||||
t.Cycle = periodCycle(t.TaskPeriod)
|
||||
out = append(out, t)
|
||||
}
|
||||
return out, rows.Err()
|
||||
|
||||
@@ -90,6 +90,20 @@ func monthOptions() []MonthOption {
|
||||
return out
|
||||
}
|
||||
|
||||
func cycleOffsetOptions(maxOffset int) []CycleOption {
|
||||
if maxOffset < 0 {
|
||||
maxOffset = 0
|
||||
}
|
||||
out := make([]CycleOption, 0, maxOffset+1)
|
||||
for i := 0; i <= maxOffset; i++ {
|
||||
out = append(out, CycleOption{
|
||||
Value: i,
|
||||
Label: fmt.Sprintf("+%d Zyklus", i),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func monthInWindow(month, start, end int) bool {
|
||||
if start <= end {
|
||||
return month >= start && month <= end
|
||||
@@ -105,6 +119,14 @@ func wrapMonth(v int) int {
|
||||
return m
|
||||
}
|
||||
|
||||
func periodMonth(period int) int {
|
||||
return (period % 12) + 1
|
||||
}
|
||||
|
||||
func periodCycle(period int) int {
|
||||
return period / 12
|
||||
}
|
||||
|
||||
func validateCropInput(name string, start, end, grow, regrowCycles int) error {
|
||||
if name == "" {
|
||||
return errors.New("Name der Feldfrucht fehlt")
|
||||
|
||||
@@ -46,12 +46,13 @@ func (a *App) handleSetCurrentTime(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
month := mustInt(r.FormValue("current_month"), 1)
|
||||
cycle := mustInt(r.FormValue("current_cycle"), settings.CurrentCycle)
|
||||
day := mustInt(r.FormValue("current_day"), 1)
|
||||
if month < 1 || month > 12 || day < 1 || day > settings.DaysPerMonth {
|
||||
if month < 1 || month > 12 || cycle < 0 || day < 1 || day > settings.DaysPerMonth {
|
||||
redirectWithMessage(w, r, "/", "error", "Ingame-Zeit ungültig")
|
||||
return
|
||||
}
|
||||
if _, err := a.db.Exec(`UPDATE settings SET current_month=?, current_day=? WHERE id=1`, month, day); err != nil {
|
||||
if _, err := a.db.Exec(`UPDATE settings SET current_month=?, current_cycle=?, current_day=? WHERE id=1`, month, cycle, day); err != nil {
|
||||
redirectWithMessage(w, r, "/", "error", "Ingame-Zeit nicht gespeichert")
|
||||
return
|
||||
}
|
||||
@@ -369,6 +370,7 @@ func (a *App) handleCreatePlan(w http.ResponseWriter, r *http.Request) {
|
||||
targetRef := strings.TrimSpace(r.FormValue("target_ref"))
|
||||
cropID := mustInt64(r.FormValue("crop_id"), 0)
|
||||
startMonth := mustInt(r.FormValue("start_month"), 1)
|
||||
cycleOffset := mustInt(r.FormValue("cycle_offset"), 0)
|
||||
startDay := mustInt(r.FormValue("start_day"), 1)
|
||||
notes := strings.TrimSpace(r.FormValue("notes"))
|
||||
if targetRef == "" || cropID <= 0 {
|
||||
@@ -381,7 +383,7 @@ func (a *App) handleCreatePlan(w http.ResponseWriter, r *http.Request) {
|
||||
redirectWithMessage(w, r, "/planning", "error", "Einstellungen nicht lesbar")
|
||||
return
|
||||
}
|
||||
if startMonth < 1 || startMonth > 12 || startDay < 1 || startDay > settings.DaysPerMonth {
|
||||
if startMonth < 1 || startMonth > 12 || cycleOffset < 0 || cycleOffset > 20 || startDay < 1 || startDay > settings.DaysPerMonth {
|
||||
redirectWithMessage(w, r, "/planning", "error", "Startdatum ungültig")
|
||||
return
|
||||
}
|
||||
@@ -404,11 +406,13 @@ func (a *App) handleCreatePlan(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
harvestMonth := wrapMonth(startMonth + c.GrowMonths)
|
||||
startPeriod := ((settings.CurrentCycle + cycleOffset) * 12) + (startMonth - 1)
|
||||
harvestPeriod := startPeriod + c.GrowMonths
|
||||
harvestMonth := periodMonth(harvestPeriod)
|
||||
harvestDay := startDay
|
||||
_, err = a.db.Exec(
|
||||
`INSERT INTO plans(field_id,target_ref,target_name,crop_id,start_month,start_day,harvest_month,harvest_day,notes) VALUES (?,?,?,?,?,?,?,?,?)`,
|
||||
fieldID, targetRef, targetName, cropID, startMonth, startDay, harvestMonth, harvestDay, notes,
|
||||
`INSERT INTO plans(field_id,target_ref,target_name,crop_id,start_period,start_month,start_day,harvest_period,harvest_month,harvest_day,notes) VALUES (?,?,?,?,?,?,?,?,?,?,?)`,
|
||||
fieldID, targetRef, targetName, cropID, startPeriod, startMonth, startDay, harvestPeriod, harvestMonth, harvestDay, notes,
|
||||
)
|
||||
if err != nil {
|
||||
redirectWithMessage(w, r, "/planning", "error", "Plan nicht gespeichert")
|
||||
@@ -508,7 +512,7 @@ func (a *App) handleCreateCustomTask(w http.ResponseWriter, r *http.Request) {
|
||||
title := strings.TrimSpace(r.FormValue("title"))
|
||||
month := mustInt(r.FormValue("month"), 0)
|
||||
day := mustInt(r.FormValue("day"), 0)
|
||||
yearOffset := mustInt(r.FormValue("year_offset"), 0)
|
||||
cycleOffset := mustInt(r.FormValue("cycle_offset"), 0)
|
||||
targetName := strings.TrimSpace(r.FormValue("target_name"))
|
||||
notes := strings.TrimSpace(r.FormValue("notes"))
|
||||
settings, err := a.getSettings()
|
||||
@@ -516,7 +520,7 @@ func (a *App) handleCreateCustomTask(w http.ResponseWriter, r *http.Request) {
|
||||
redirectWithMessage(w, r, "/planning", "error", "Einstellungen nicht lesbar")
|
||||
return
|
||||
}
|
||||
if month < 1 || month > 12 || day < 1 || day > settings.DaysPerMonth || yearOffset < 0 || yearOffset > 4 {
|
||||
if month < 1 || month > 12 || day < 1 || day > settings.DaysPerMonth || cycleOffset < 0 || cycleOffset > 20 {
|
||||
redirectWithMessage(w, r, "/planning", "error", "Aufgaben-Datum ungültig")
|
||||
return
|
||||
}
|
||||
@@ -531,8 +535,9 @@ func (a *App) handleCreateCustomTask(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = a.db.Exec(`INSERT INTO custom_tasks(template_id,title,month,day,year_offset,target_name,notes) VALUES (?,?,?,?,?,?,?)`,
|
||||
nullInt64(templateID), title, month, day, yearOffset, targetName, notes); err != nil {
|
||||
taskPeriod := ((settings.CurrentCycle + cycleOffset) * 12) + (month - 1)
|
||||
if _, err = a.db.Exec(`INSERT INTO custom_tasks(template_id,title,task_period,month,day,year_offset,target_name,notes) VALUES (?,?,?,?,?,?,?,?)`,
|
||||
nullInt64(templateID), title, taskPeriod, month, day, cycleOffset, targetName, notes); err != nil {
|
||||
redirectWithMessage(w, r, "/planning", "error", "Aufgabe konnte nicht gespeichert werden")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -64,8 +64,8 @@ func (a *App) handleDashboard(w http.ResponseWriter, r *http.Request) {
|
||||
},
|
||||
Settings: settings,
|
||||
CurrentMonth: monthNames[settings.CurrentMonth-1],
|
||||
TodayTasks: buildTasksForDay(plans, crops, products, stepMap, customTasks, doneMap, settings.CurrentMonth, settings.CurrentDay),
|
||||
Calendar: buildCalendar(plans, crops, products, stepMap, customTasks, doneMap, settings.CurrentMonth, settings.CurrentDay, settings.DaysPerMonth, 14),
|
||||
TodayTasks: buildTasksForDay(plans, crops, products, stepMap, customTasks, doneMap, (settings.CurrentCycle*12)+(settings.CurrentMonth-1), settings.CurrentDay),
|
||||
Calendar: buildCalendar(plans, crops, products, stepMap, customTasks, doneMap, (settings.CurrentCycle*12)+(settings.CurrentMonth-1), settings.CurrentDay, settings.DaysPerMonth, 14),
|
||||
PlanningCount: len(plans),
|
||||
}
|
||||
data.TodayGroups = groupTasksByField(data.TodayTasks)
|
||||
@@ -170,6 +170,7 @@ func (a *App) handlePlanningPage(w http.ResponseWriter, r *http.Request) {
|
||||
},
|
||||
Settings: settings,
|
||||
Months: monthOptions(),
|
||||
CycleOffsets: cycleOffsetOptions(8),
|
||||
Crops: crops,
|
||||
Plans: plans,
|
||||
PlanningTargets: buildPlanningTargets(fields),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
@@ -99,3 +99,4 @@ func withLogging(next http.Handler) http.Handler {
|
||||
log.Printf("%s %s %s", r.Method, r.URL.Path, time.Since(start).Round(time.Millisecond))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import "database/sql"
|
||||
type Settings struct {
|
||||
DaysPerMonth int
|
||||
CurrentMonth int
|
||||
CurrentCycle int
|
||||
CurrentDay int
|
||||
}
|
||||
|
||||
@@ -48,10 +49,14 @@ type Plan struct {
|
||||
CropID int64
|
||||
CropName string
|
||||
GrowMonths int
|
||||
StartPeriod int
|
||||
StartMonth int
|
||||
StartDay int
|
||||
StartCycle int
|
||||
HarvestPeriod int
|
||||
HarvestMonth int
|
||||
HarvestDay int
|
||||
HarvestCycle int
|
||||
Notes string
|
||||
RegrowEnabled bool
|
||||
RegrowCycles int
|
||||
@@ -75,9 +80,11 @@ type CustomTask struct {
|
||||
ID int64
|
||||
TemplateID sql.NullInt64
|
||||
Title string
|
||||
TaskPeriod int
|
||||
Month int
|
||||
Day int
|
||||
YearOffset int
|
||||
Cycle int
|
||||
TargetName string
|
||||
Notes string
|
||||
}
|
||||
@@ -104,6 +111,11 @@ type MonthOption struct {
|
||||
Label string
|
||||
}
|
||||
|
||||
type CycleOption struct {
|
||||
Value int
|
||||
Label string
|
||||
}
|
||||
|
||||
type CalendarDay struct {
|
||||
Day int
|
||||
Tasks []Task
|
||||
@@ -156,6 +168,7 @@ type PlanningPage struct {
|
||||
BasePage
|
||||
Settings Settings
|
||||
Months []MonthOption
|
||||
CycleOffsets []CycleOption
|
||||
Crops []Crop
|
||||
Plans []Plan
|
||||
PlanningTargets []PlanningTarget
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!doctype html>
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
@@ -24,7 +24,7 @@
|
||||
<main class="layout">
|
||||
<section class="card">
|
||||
<h2>Aktuelle Ingame-Zeit</h2>
|
||||
<p><strong>{{.CurrentMonth}} Tag {{.Settings.CurrentDay}}</strong> bei {{.Settings.DaysPerMonth}} Tagen pro Monat.</p>
|
||||
<p><strong>{{.CurrentMonth}} Tag {{.Settings.CurrentDay}}</strong> im Zyklus {{.Settings.CurrentCycle}} bei {{.Settings.DaysPerMonth}} Tagen pro Monat.</p>
|
||||
<p>{{.PlanningCount}} Plan-Einträge insgesamt.</p>
|
||||
<form method="post" action="/settings/time" class="grid">
|
||||
<label>Monat
|
||||
@@ -43,6 +43,9 @@
|
||||
<option value="12" {{if eq .Settings.CurrentMonth 12}}selected{{end}}>Dezember</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>Zyklus
|
||||
<input type="number" name="current_cycle" min="0" value="{{.Settings.CurrentCycle}}">
|
||||
</label>
|
||||
<label>Tag
|
||||
<input type="number" name="current_day" min="1" max="{{.Settings.DaysPerMonth}}" value="{{.Settings.CurrentDay}}">
|
||||
</label>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!doctype html>
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
@@ -44,6 +44,13 @@
|
||||
{{end}}
|
||||
</select>
|
||||
</label>
|
||||
<label>Zyklus-Offset
|
||||
<select name="cycle_offset" required>
|
||||
{{range .CycleOffsets}}
|
||||
<option value="{{.Value}}">{{.Label}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</label>
|
||||
<label>Starttag
|
||||
<input type="number" name="start_day" min="1" max="{{.Settings.DaysPerMonth}}" value="{{.Settings.CurrentDay}}" required>
|
||||
</label>
|
||||
@@ -88,8 +95,12 @@
|
||||
<label>Tag
|
||||
<input type="number" name="day" min="1" max="{{.Settings.DaysPerMonth}}" value="{{.Settings.CurrentDay}}">
|
||||
</label>
|
||||
<label>Jahr-Offset
|
||||
<input type="number" name="year_offset" min="0" max="4" value="0">
|
||||
<label>Zyklus-Offset
|
||||
<select name="cycle_offset">
|
||||
{{range .CycleOffsets}}
|
||||
<option value="{{.Value}}">{{.Label}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</label>
|
||||
<label>Ziel (optional)
|
||||
<input type="text" name="target_name" maxlength="120" placeholder="z.B. Hof oder Feld 1+2">
|
||||
@@ -112,8 +123,8 @@
|
||||
<tr>
|
||||
<td>{{.TargetName}}</td>
|
||||
<td>{{.CropName}}</td>
|
||||
<td>Monat {{.StartMonth}} Tag {{.StartDay}}</td>
|
||||
<td>Monat {{.HarvestMonth}} Tag {{.HarvestDay}}</td>
|
||||
<td>Monat {{.StartMonth}} Tag {{.StartDay}} (Zyklus {{.StartCycle}})</td>
|
||||
<td>Monat {{.HarvestMonth}} Tag {{.HarvestDay}} (Zyklus {{.HarvestCycle}})</td>
|
||||
<td>{{.Notes}}</td>
|
||||
<td>
|
||||
<form method="post" action="/plans/delete">
|
||||
@@ -142,7 +153,7 @@
|
||||
<tr>
|
||||
<td>{{.Title}}</td>
|
||||
<td>{{if .TargetName}}{{.TargetName}}{{else}}Allgemein{{end}}</td>
|
||||
<td>Monat {{.Month}} Tag {{.Day}} (Jahr +{{.YearOffset}})</td>
|
||||
<td>Monat {{.Month}} Tag {{.Day}} (Zyklus {{.Cycle}})</td>
|
||||
<td>{{.Notes}}</td>
|
||||
<td>
|
||||
<form method="post" action="/custom-tasks/delete">
|
||||
|
||||
Reference in New Issue
Block a user