Adding Functions + Finishing CI config #34

Merged
tom_trgr merged 7 commits from dev/ui into main 2025-09-10 09:59:09 +02:00
12 changed files with 305 additions and 22 deletions
Showing only changes of commit 2eab598348 - Show all commits

29
Backend/endpoints/pdf.go Normal file
View File

@@ -0,0 +1,29 @@
package endpoints
import (
"arbeitszeitmessung/helper"
"arbeitszeitmessung/models"
"arbeitszeitmessung/templates"
"log"
"net/http"
"time"
)
func PDFHandler(w http.ResponseWriter, r *http.Request) {
helper.RequiresLogin(Session, w, r)
startDate, err := time.Parse("2006-01-02", "2025-08-01")
if err != nil {
log.Println("Error")
}
endDate := startDate.AddDate(0, 1, -1)
user, err := models.GetUserFromSession(Session, r.Context())
if err != nil {
log.Println("Error getting user!")
}
weeks := models.GetWorkDays(user.CardUID, startDate, endDate)
log.Printf("Using Dates: %s - %s\n", startDate.String(), endDate.String())
templates.PDFReportEmploye(user, weeks, startDate, endDate).Render(r.Context(), w)
}

View File

@@ -56,7 +56,7 @@ func submitReport(w http.ResponseWriter, r *http.Request) {
}
func showWeeks(w http.ResponseWriter, r *http.Request) {
user, err := (*models.User).GetUserFromSession(nil, Session, r.Context())
user, err := models.GetUserFromSession(Session, r.Context())
if err != nil {
log.Println("No user found with the given personal number!")
http.Redirect(w, r, "/user/login", http.StatusSeeOther)

View File

@@ -23,7 +23,7 @@ func TeamPresenceHandler(w http.ResponseWriter, r *http.Request) {
}
func teamPresence(w http.ResponseWriter, r *http.Request) {
user, err := (*models.User).GetUserFromSession(nil, Session, r.Context())
user, err := models.GetUserFromSession(Session, r.Context())
if err != nil {
log.Println("Error getting user!", err)
}

View File

@@ -44,7 +44,7 @@ func parseTimestamp(r *http.Request, getKey string, fallback string) (time.Time,
// Returns bookings from DB with similar card uid -> checks for card uid in http query params
func getBookings(w http.ResponseWriter, r *http.Request) {
user, err := (*models.User).GetUserFromSession(nil, Session, r.Context())
user, err := models.GetUserFromSession(Session, r.Context())
if err != nil {
log.Println("No user found with the given personal number!")
http.Redirect(w, r, "/user/login", http.StatusSeeOther)
@@ -66,7 +66,7 @@ func getBookings(w http.ResponseWriter, r *http.Request) {
}
tsTo = tsTo.AddDate(0, 0, 1) // so that today is inside
workDays := (*models.WorkDay).GetWorkDays(nil, user.CardUID, tsFrom, tsTo)
workDays := models.GetWorkDays(user.CardUID, tsFrom, tsTo)
sort.Slice(workDays, func(i, j int) bool {
return workDays[i].Day.After(workDays[j].Day)
})
@@ -104,7 +104,7 @@ func updateBooking(w http.ResponseWriter, r *http.Request) {
log.Println("Error loading location", err)
loc = time.Local
}
user, err := (*models.User).GetUserFromSession(nil, Session, r.Context())
user, err := models.GetUserFromSession(Session, r.Context())
if err != nil {
log.Println("No user found!", err)
return

View File

@@ -0,0 +1,9 @@
package helper
func GetFirst[T, U any](val T, _ U) T {
return val
}
func GetSecond[T, U any](_ T, val U) U {
return val
}

View File

@@ -50,6 +50,7 @@ func main() {
// server.HandleFunc("/user/settings", endpoints.UserSettingsHandler)
server.HandleFunc("/team", endpoints.TeamHandler)
server.HandleFunc("/team/presence", endpoints.TeamPresenceHandler)
server.HandleFunc("/pdf", endpoints.PDFHandler)
server.Handle("/", http.RedirectHandler("/time", http.StatusPermanentRedirect))
server.Handle("/static/", http.StripPrefix("/static/", fs))

View File

@@ -22,7 +22,7 @@ type User struct {
Overtime time.Duration
}
func (u *User) GetUserFromSession(Session *scs.SessionManager, ctx context.Context) (User, error) {
func GetUserFromSession(Session *scs.SessionManager, ctx context.Context) (User, error) {
var user User
var err error
if helper.GetEnv("GO_ENV", "production") == "debug" {

View File

@@ -19,7 +19,7 @@ type WorkDay struct {
Absence Absence
}
func (d *WorkDay) GetWorkDays(card_uid string, tsFrom, tsTo time.Time) []WorkDay {
func GetWorkDays(card_uid string, tsFrom, tsTo time.Time) []WorkDay {
var workDays []WorkDay
var workSec, pauseSec float64
@@ -181,7 +181,7 @@ func (d *WorkDay) getWorkTime() {
d.calcPauseTime()
}
func (d *WorkDay) GetWorkTimeString() (string, string) {
func (d *WorkDay) GetWorkTimeString() (work string, pause string) {
workString := helper.FormatDuration(d.workTime)
pauseString := helper.FormatDuration(d.pauseTime)
return workString, pauseString

View File

@@ -45,7 +45,7 @@ func NewWorkWeek(user User, tsMonday time.Time, populate bool) WorkWeek {
}
func (w *WorkWeek) PopulateWithBookings(worktime time.Duration, overtime time.Duration) {
w.WorkDays = (*WorkDay).GetWorkDays(nil, w.User.CardUID, w.WeekStart, w.WeekStart.Add(7*24*time.Hour))
w.WorkDays = GetWorkDays(w.User.CardUID, w.WeekStart, w.WeekStart.Add(7*24*time.Hour))
if absences, err := GetAbsencesByCardUID(w.User.CardUID, w.WeekStart, w.WeekStart.Add(7*24*time.Hour)); err == nil {
w.Absences = absences
} else {

View File

@@ -189,6 +189,9 @@
.\@container {
container-type: inline-size;
}
.relative {
position: relative;
}
.col-span-2 {
grid-column: span 2 / span 2;
}
@@ -364,6 +367,30 @@
.grid-cols-5 {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.grid-cols-6 {
grid-template-columns: repeat(6, minmax(0, 1fr));
}
.grid-cols-7 {
grid-template-columns: repeat(7, minmax(0, 1fr));
}
.grid-cols-\[1fr\] {
grid-template-columns: 1fr;
}
.grid-cols-\[1fr_1fr\] {
grid-template-columns: 1fr 1fr;
}
.grid-cols-\[1fr_1fr_1fr_1fr_1fr_1fr\] {
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
}
.grid-cols-\[auto_1fr_1fr_1fr_1fr_1fr\] {
grid-template-columns: auto 1fr 1fr 1fr 1fr 1fr;
}
.grid-cols-\[auto_1fr_1fr_1fr_1fr_1fr_1fr\] {
grid-template-columns: auto 1fr 1fr 1fr 1fr 1fr 1fr;
}
.grid-rows-6 {
grid-template-rows: repeat(6, minmax(0, 1fr));
}
.flex-col {
flex-direction: column;
}
@@ -427,10 +454,6 @@
border-style: var(--tw-border-style);
border-width: 1px;
}
.border-0 {
border-style: var(--tw-border-style);
border-width: 0px;
}
.border-neutral-200 {
border-color: var(--color-neutral-200);
}
@@ -452,9 +475,6 @@
.bg-neutral-400 {
background-color: var(--color-neutral-400);
}
.bg-neutral-700 {
background-color: var(--color-neutral-700);
}
.bg-orange-500 {
background-color: var(--color-orange-500);
}
@@ -473,6 +493,15 @@
.p-2 {
padding: calc(var(--spacing) * 2);
}
.p-4 {
padding: calc(var(--spacing) * 4);
}
.p-8 {
padding: calc(var(--spacing) * 8);
}
.px-2 {
padding-inline: calc(var(--spacing) * 2);
}
.px-3 {
padding-inline: calc(var(--spacing) * 3);
}
@@ -699,12 +728,6 @@
}
}
}
.max-md\:border-0 {
@media (width < 48rem) {
border-style: var(--tw-border-style);
border-width: 0px;
}
}
.max-md\:border-b-1 {
@media (width < 48rem) {
border-bottom-style: var(--tw-border-style);

View File

@@ -0,0 +1,46 @@
package templates
import (
"arbeitszeitmessung/helper"
"arbeitszeitmessung/models"
"time"
)
templ PDFReportEmploye(e models.User, workDays []models.WorkDay, tsStart time.Time, tsEnd time.Time) {
{{
_, kw := tsStart.ISOWeek()
}}
@Base()
<content class="p-8 relative flex flex-col gap-4">
<div>
<h1 class="text-2xl font-bold">Kim Mustermensch</h1>
<p>Zeitraum: <span>{ tsStart.Format("02.01.2006") }</span> - <span>{ tsEnd.Format("02.01.2006") }</span></p>
<p>Arbeitszeit: <span></span></p>
<p>Überstunden: <span></span></p>
</div>
<div class="grid grid-rows-6 grid-cols-[auto_1fr_1fr_1fr_1fr_1fr] divide-x-1 divide-y-1">
<p class="px-2 ">KW</p>
<p class="px-2 text-center">Montag</p>
<p class="px-2 text-center">Dienstag</p>
<p class="px-2 text-center">Mittwoche</p>
<p class="px-2 text-center">Donnerstag</p>
<p class="px-2 text-center">Freitag</p>
// <p class="px-2 text-center">Samstag</p>
<p>{ kw }</p>
for d := time.Monday; d < tsStart.Weekday(); d++ {
<p></p>
}
for _, day := range workDays {
if day.Day.Weekday() == time.Monday {
<p>{ helper.GetKW(day.Day) } </p>
}
<div class="flex flex-col gap-2">
<p>{ helper.GetFirst(day.GetWorkTimeString()) }</p>
for i := 0; i < len(day.Bookings); i += 2 {
<p>{ day.Bookings[i].Timestamp.Format("15:04") } - { day.Bookings[i+1].Timestamp.Format("15:04") }</p>
}
</div>
}
</div>
</content>
}

View File

@@ -0,0 +1,175 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.924
package templates
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"arbeitszeitmessung/helper"
"arbeitszeitmessung/models"
"time"
)
func PDFReportEmploye(e models.User, workDays []models.WorkDay, tsStart time.Time, tsEnd time.Time) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, kw := tsStart.ISOWeek()
templ_7745c5c3_Err = Base().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<content class=\"p-8 relative flex flex-col gap-4\"><div><h1 class=\"text-2xl font-bold\">Kim Mustermensch</h1><p>Zeitraum: <span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(tsStart.Format("02.01.2006"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 17, Col: 52}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</span> - <span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(tsEnd.Format("02.01.2006"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 17, Col: 98}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</span></p><p>Arbeitszeit: <span></span></p><p>Überstunden: <span></span></p></div><div class=\"grid grid-rows-6 grid-cols-[auto_1fr_1fr_1fr_1fr_1fr] divide-x-1 divide-y-1\"><p class=\"px-2 \">KW</p><p class=\"px-2 text-center\">Montag</p><p class=\"px-2 text-center\">Dienstag</p><p class=\"px-2 text-center\">Mittwoche</p><p class=\"px-2 text-center\">Donnerstag</p><p class=\"px-2 text-center\">Freitag</p><p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(kw)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 29, Col: 10}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for d := time.Monday; d < tsStart.Weekday(); d++ {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<p></p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
for _, day := range workDays {
if day.Day.Weekday() == time.Monday {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(helper.GetKW(day.Day))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 35, Col: 31}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, " <div class=\"flex flex-col gap-2\"><p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(helper.GetFirst(day.GetWorkTimeString()))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 38, Col: 50}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for i := 0; i < len(day.Bookings); i += 2 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(day.Bookings[i].Timestamp.Format("15:04"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 40, Col: 52}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, " - ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(day.Bookings[i+1].Timestamp.Format("15:04"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 40, Col: 102}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</div></content>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate