added interface for workday and absence + multiday absences closes #38, #39

This commit is contained in:
2025-09-23 12:30:02 +02:00
parent 55b0332600
commit db6fc10c28
19 changed files with 1396 additions and 1034 deletions

View File

@@ -26,80 +26,140 @@ templ weekPicker(weekStart time.Time) {
</svg>
</button>
</form>
if time.Since(weekStart) < 24*7*time.Hour {
<p class="text-sm text-red-500">Die Woche kann erst am nächsten Montag gesendet werden!</p>
}
}
templ weekDayComponent(user models.User, day models.WorkDay) {
{{ work, pause := day.GetWorkTimeString() }}
templ defaultWeekDayComponent(u models.User, day models.IWorkDay) {
<div class="flex flex-row gap-2">
@timeGaugeComponent(day.GetWorkDayProgress(user), false, day.RequiresAction())
@timeGaugeComponent(day.GetDayProgress(u), false)
<div class="flex flex-col">
<p class=""><span class="font-bold uppercase hidden md:inline">{ day.Day.Format("Mon") }:</span> { day.Day.Format("02.01.2006") }</p>
if !day.RequiresAction() {
<div class="flex flex-row gap-2">
<span class="text-accent">{ work }</span>
<span class="text-neutral-500">{ pause }</span>
</div>
<div class="flex flex-row gap-2 items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="size-4" viewBox="0 0 16 16">
<path d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71z"></path>
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0"></path>
</svg>
switch {
case day.Absence.Datum.Equal(day.Day):
<p>{ day.Absence.AbwesenheitTyp.Name }</p>
case !day.TimeFrom.Equal(day.TimeTo):
<span>{ day.TimeFrom.Format("15:04") }</span>
<span>-</span>
<span>{ day.TimeTo.Format("15:04") }</span>
default:
<p>Keine Anwesenheit</p>
}
</div>
<p class=""><span class="font-bold uppercase hidden md:inline">{ day.Date().Format("Mon") }:</span> { day.Date().Format("02.01.2006") }</p>
if day.IsWorkDay() {
{{
workDay, _ := day.(*models.WorkDay)
work, pause, _ := workDay.GetAllWorkTimesReal(u)
}}
if !workDay.RequiresAction() {
<div class="flex flex-row gap-2">
<span class="text-accent">{ helper.FormatDuration(work) }</span>
<span class="text-neutral-500">{ helper.FormatDuration(pause) }</span>
</div>
<div class="flex flex-row gap-2 items-center">
<span class="icon-[material-symbols-light--nest-clock-farsight-analog-outline] flex-shrink-0"></span>
switch {
case !workDay.TimeFrom.Equal(workDay.TimeTo):
<span>{ workDay.TimeFrom.Format("15:04") }</span>
<span>-</span>
<span>{ workDay.TimeTo.Format("15:04") }</span>
default:
<p>Keine Anwesenheit</p>
}
</div>
} else {
<p class="text-red-600">Bitte anpassen</p>
}
} else {
<p class="text-red-600">Bitte anpassen</p>
{{
absentDay, _ := day.(*models.Absence)
}}
<div>{ absentDay.AbwesenheitTyp.Name } </div>
}
</div>
</div>
}
templ employeComponent(week models.WorkWeek) {
templ weekDayComponent(user models.User, day models.WorkDay) {
// {{ work, pause, _ := day.GetAllWorkTimesReal(user) }}
<div class="flex flex-row gap-2">
// @timeGaugeComponent(day.GetWorkDayProgress(user), false, day.RequiresAction())
<div class="flex flex-col">
if !day.RequiresAction() {
}
</div>
</div>
}
templ workWeekComponent(week models.WorkWeek, onlyAccept bool) {
{{
year, kw := week.WeekStart.ISOWeek()
progress := (float32(week.Worktime.Hours()) / week.User.ArbeitszeitPerWoche) * 100
}}
<div class="employeComponent grid-sub responsive lg:divide-x-1 max-md:divide-y-1 @container">
<div class="grid-cell flex flex-col max-md:bg-neutral-300 gap-2">
if !onlyAccept {
<div class="lg:hidden">
@weekPicker(week.WeekStart)
</div>
}
<p class="font-bold uppercase">{ week.User.Vorname } { week.User.Name }</p>
<div class="flex flex-row gap-2 col-span-3">
@timeGaugeComponent(uint8(progress), false, false)
<div>
<p>Arbeitszeit: { fmt.Sprintf("%s", helper.FormatDuration(week.Worktime)) }</p>
<p>Überstunden: { fmt.Sprintf("%s", helper.FormatDuration(week.Overtime)) }</p>
<div class="grid grid-cols-5 gap-2 lg:grid-cols-1">
if !onlyAccept {
<div class="col-span-2">
<span class="flex flex-row gap-2 items-center">
@statusCheckMark(week.CheckStatus(), models.WeekStatusSent)
Gesendet
</span>
<span class="flex flex-row gap-2 items-center">
@statusCheckMark(week.CheckStatus(), models.WeekStatusAccepted)
Akzeptiert
</span>
</div>
}
<div class="flex flex-row gap-2 col-span-3">
@timeGaugeComponent(int8(progress), false)
<div>
<p>Arbeitszeit: { fmt.Sprintf("%s", helper.FormatDuration(week.Worktime)) }</p>
<p>Überstunden: { fmt.Sprintf("%s", helper.FormatDurationFill(week.Overtime, true)) }</p>
</div>
</div>
</div>
</div>
<div class="grid-cell col-span-3 flex flex-col @7xl:grid @7xl:grid-cols-5 gap-2 content-baseline">
for _, day := range week.WorkDays {
@weekDayComponent(week.User, day)
<div class="grid-cell col-span-3 flex flex-col @7xl:grid @7xl:grid-cols-5 gap-2 py-4 content-baseline">
for _, day := range week.Days {
@defaultWeekDayComponent(week.User, day)
}
</div>
<form class="grid-cell flex flex-col justify-between gap-2" method="post">
<p class="text-sm"><span class="">Woche:</span> { fmt.Sprintf("%02d-%d", kw, year) }</p>
<input type="hidden" name="method" value="accept"/>
<input type="hidden" name="user" value={ strconv.Itoa(week.User.PersonalNummer) }/>
<input type="hidden" name="week" value={ week.WeekStart.Format(time.DateOnly) }/>
<div class="flex flex-col gap-2">
if week.Status == models.WeekStatusDifferences {
<p class="text-red-600 text-sm">Unterschiedliche Arbeitszeit zwischen Abrechnung und individuellen Buchungen</p>
<div class="grid-cell flex flex-col gap-2 justify-between">
if onlyAccept {
<p class="text-sm"><span class="">Woche:</span> { fmt.Sprintf("%02d-%d", kw, year) }</p>
} else {
<div class="max-md:hidden">
@weekPicker(week.WeekStart)
</div>
}
<form method="post" class="flex flex-col gap-2">
{{
week.CheckStatus()
method := "accept"
if !onlyAccept {
method = "send"
}
}}
<input type="hidden" name="method" value={ method }/>
<input type="hidden" name="user" value={ strconv.Itoa(week.User.PersonalNummer) }/>
<input type="hidden" name="week" value={ week.WeekStart.Format(time.DateOnly) }/>
if onlyAccept {
if week.Status == models.WeekStatusDifferences {
<p class="text-red-600 text-sm">Unterschiedliche Arbeitszeit zwischen Abrechnung und individuellen Buchungen</p>
}
<button type="submit" disabled?={ week.Status == models.WeekStatusDifferences } class="btn">Bestätigen</button>
} else {
switch {
case week.RequiresAction():
<p class="text-sm text-red-500">bitte zuerst Buchungen anpassen</p>
case time.Since(week.WeekStart) < 24*7*time.Hour:
<p class="text-sm text-red-500">Die Woche kann erst am nächsten Montag gesendet werden!</p>
case week.Status == models.WeekStatusNone:
<p class="text-sm">an Vorgesetzten senden</p>
case week.Status == models.WeekStatusSent:
<p class="text-sm">an Vorgesetzten gesendet</p>
case week.Status == models.WeekStatusAccepted:
<p class="text-sm">vom Vorgesetzten bestätigt</p>
}
<button disabled?={ week.Status < models.WeekStatusSent } type="submit" class="btn">Korrigieren</button>
<button disabled?={ time.Since(week.WeekStart) < 24*7*time.Hour || week.Status >= models.WeekStatusSent || week.RequiresAction() } type="submit" class="btn">Senden</button>
}
<button type="submit" disabled?={ week.Status == models.WeekStatusDifferences } class="btn">Bestätigen</button>
// TODO maybe delete function
// <button type="button" disabled?={ week.Status < models.WeekStatusDifferences } class="hover:bg-red-600 btn">Antrag löschen</button>
</div>
</form>
</form>
</div>
</div>
}