Fortlaufende Zyklus-Planung und Periodenlogik einführen

This commit is contained in:
Kai
2026-02-16 15:06:34 +01:00
parent 2255318993
commit 67bd87e12a
9 changed files with 138 additions and 63 deletions

View File

@@ -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 {

View File

@@ -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()

View File

@@ -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")

View File

@@ -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
}

View File

@@ -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),

View File

@@ -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))
})
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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">