From 54104d5a0e5026db9ac5bf8194d2062f351fc330 Mon Sep 17 00:00:00 2001
From: tom
Date: Wed, 26 Feb 2025 15:05:50 +0100
Subject: [PATCH] CHANGE: added report section
---
Backend/endpoints/team.go | 62 ++++++++++++++---
Backend/endpoints/time.go | 13 ++--
Backend/endpoints/time_create.go | 4 ++
Backend/endpoints/user.go | 28 ++++----
Backend/helper/web.go | 12 ++++
Backend/main.go | 3 +
Backend/models/booking.go | 28 ++++----
Backend/models/user.go | 25 ++++++-
Backend/models/workWeek.go | 63 ++++++++++++++++--
Backend/static/css/styles.css | 21 ++++--
Backend/templates/pages.templ | 33 +++++++--
Backend/templates/pages_templ.go | 81 ++++++++++++++++++++---
Backend/templates/teamComponents.templ | 18 +++--
Backend/templates/teamComponents_templ.go | 74 ++++++++++++++++-----
DB/initdb/01_create_tables.sql | 7 +-
15 files changed, 376 insertions(+), 96 deletions(-)
diff --git a/Backend/endpoints/team.go b/Backend/endpoints/team.go
index 30135da..843f899 100644
--- a/Backend/endpoints/team.go
+++ b/Backend/endpoints/team.go
@@ -1,15 +1,30 @@
package endpoints
import (
+ "arbeitszeitmessung/helper"
"arbeitszeitmessung/models"
"arbeitszeitmessung/templates"
+ "context"
+ "errors"
"log"
"net/http"
+ "strconv"
"time"
)
func TeamHandler(w http.ResponseWriter, r *http.Request) {
- showWeeks(w, r)
+ helper.RequiresLogin(Session, w, r)
+ switch r.Method {
+ case http.MethodPost:
+ submitReport(w, r)
+ break
+ case http.MethodGet:
+ showWeeks(w, r)
+ break
+ default:
+ http.Error(w, "Method not allowed!", http.StatusMethodNotAllowed)
+ break
+ }
// user, err := (*models.User).GetUserFromSession(nil, Session, r.Context())
// if err != nil {
// log.Println("No user found with the given personal number!")
@@ -24,6 +39,39 @@ func TeamHandler(w http.ResponseWriter, r *http.Request) {
// templates.TeamPage(teamMembers, userWorkDays).Render(r.Context(), w)
}
+func submitReport(w http.ResponseWriter, r *http.Request) {
+ err := r.ParseForm()
+ if err != nil {
+ log.Println("Error parsing form", err)
+ return
+ }
+ userPN, _ := strconv.Atoi(r.FormValue("user"))
+ _weekTs := r.FormValue("week")
+ weekTs, err := time.Parse(time.DateOnly, _weekTs)
+ user, err := (*models.User).GetByPersonalNummer(nil, userPN)
+ workWeek := (*models.WorkWeek).GetWeek(nil, user, weekTs, false)
+
+ if err != nil {
+ log.Println("Could not get user!")
+ return
+ }
+
+ switch r.FormValue("method") {
+ case "send":
+ err = workWeek.Send()
+ break
+ case "accept":
+ err = workWeek.Accept()
+ break
+ default:
+ break
+ }
+ if errors.Is(err, models.ErrRunningWeek) {
+ showWeeks(w, r.WithContext(context.WithValue(r.Context(), "error", true)))
+ }
+ showWeeks(w, r)
+}
+
func showWeeks(w http.ResponseWriter, r *http.Request) {
user, err := (*models.User).GetUserFromSession(nil, Session, r.Context())
if err != nil {
@@ -37,16 +85,10 @@ func showWeeks(w http.ResponseWriter, r *http.Request) {
weeks := (*models.WorkWeek).GetSendWeeks(nil, member)
workWeeks = append(workWeeks, weeks...)
}
- // Somehow use this for the own users weeks
- // lastSub := member.GetLastSubmission()
- // weeks := getWeeksTillNow(lastSub)
- // for _, week := range weeks {
- // workWeek := (*models.WorkWeek).GetWeek(nil, member, week)
- // workWeeks = append(workWeeks, workWeek)
- // }
lastSub := user.GetLastSubmission()
- // userWorkDays := (*models.WorkDay).GetWorkDays(nil, user.CardUID, lastSub, lastSub.AddDate(0, 0, 7))
- userWeek := (*models.WorkWeek).GetWeek(nil, user, lastSub)
+ log.Println(lastSub)
+ userWeek := (*models.WorkWeek).GetWeek(nil, user, lastSub, true)
+ // isRunningWeek := time.Since(lastSub) < 24*5*time.Hour //the last submission is this week and cannot be send yet
templates.TeamPage(workWeeks, userWeek).Render(r.Context(), w)
}
diff --git a/Backend/endpoints/time.go b/Backend/endpoints/time.go
index a90458a..0e11a8e 100644
--- a/Backend/endpoints/time.go
+++ b/Backend/endpoints/time.go
@@ -14,17 +14,22 @@ import (
// Frontend relevant backend functionality -> not used by the arduino devices
func TimeHandler(w http.ResponseWriter, r *http.Request) {
+ helper.RequiresLogin(Session, w, r)
helper.SetCors(w)
switch r.Method {
- case "GET":
+ case http.MethodGet:
getBookings(w, r)
- case "POST":
+ break
+ case http.MethodPost:
updateBooking(w, r)
- case "OPTIONS":
+ break
+ case http.MethodOptions:
// just support options header for non GET Requests from SWAGGER
w.WriteHeader(http.StatusOK)
+ break
default:
- http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ http.Error(w, "Method not allowed!", http.StatusMethodNotAllowed)
+ break
}
}
diff --git a/Backend/endpoints/time_create.go b/Backend/endpoints/time_create.go
index e7e7c2e..449df4a 100644
--- a/Backend/endpoints/time_create.go
+++ b/Backend/endpoints/time_create.go
@@ -16,13 +16,17 @@ func TimeCreateHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPut:
createBooking(w, r)
+ break
case http.MethodGet:
createBooking(w, r)
+ break
case http.MethodOptions:
// just support options header for non GET Requests from SWAGGER
w.WriteHeader(http.StatusOK)
+ break
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ break
}
}
diff --git a/Backend/endpoints/user.go b/Backend/endpoints/user.go
index 7d8dafd..b225f39 100644
--- a/Backend/endpoints/user.go
+++ b/Backend/endpoints/user.go
@@ -1,6 +1,7 @@
package endpoints
import (
+ "arbeitszeitmessung/helper"
"arbeitszeitmessung/models"
"arbeitszeitmessung/templates"
"log"
@@ -18,27 +19,26 @@ func CreateSessionManager(lifetime time.Duration) *scs.SessionManager {
Session.Lifetime = lifetime
return Session
}
+
func LoginHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
- showLoginForm(w, r, false)
+ showLoginPage(w, r, false)
break
case http.MethodPost:
loginUser(w, r)
break
default:
- showLoginForm(w, r, false)
+ http.Error(w, "Method not allowed!", http.StatusMethodNotAllowed)
break
}
}
func UserHandler(w http.ResponseWriter, r *http.Request) {
- // if !Session.Exists(r.Context(), "user") {
- // http.Redirect(w, r, "/user/login", http.StatusSeeOther)
- // }
+ helper.RequiresLogin(Session, w, r)
switch r.Method {
case http.MethodGet:
- showPWForm(w, r, 0)
+ showUserPage(w, r, 0)
break
case http.MethodPost:
changePassword(w, r)
@@ -49,7 +49,7 @@ func UserHandler(w http.ResponseWriter, r *http.Request) {
}
}
-func showLoginForm(w http.ResponseWriter, r *http.Request, failed bool) {
+func showLoginPage(w http.ResponseWriter, r *http.Request, failed bool) {
templates.LoginPage(failed).Render(r.Context(), w)
}
@@ -85,10 +85,10 @@ func loginUser(w http.ResponseWriter, r *http.Request) {
Session.Put(r.Context(), "user", user.PersonalNummer)
http.Redirect(w, r, "/time", http.StatusSeeOther) //with this browser always uses GET
} else {
- showLoginForm(w, r, true)
+ showLoginPage(w, r, true)
return
}
- showLoginForm(w, r, false)
+ showLoginPage(w, r, false)
return
}
@@ -103,26 +103,26 @@ func changePassword(w http.ResponseWriter, r *http.Request) {
password := r.FormValue("password")
newPassword := r.FormValue("new_password")
if password == "" || newPassword == "" || newPassword != r.FormValue("new_password_repeat") {
- showPWForm(w, r, http.StatusBadRequest)
+ showUserPage(w, r, http.StatusBadRequest)
return
}
user, err := (*models.User).GetByPersonalNummer(nil, Session.GetInt(r.Context(), "user"))
if err != nil {
log.Println("Error getting user!", err)
- showPWForm(w, r, http.StatusBadRequest)
+ showUserPage(w, r, http.StatusBadRequest)
}
auth, err := user.ChangePass(password, newPassword)
if err != nil {
log.Println("Error when changing password!", err)
}
if auth {
- showPWForm(w, r, http.StatusOK)
+ showUserPage(w, r, http.StatusOK)
return
}
- showPWForm(w, r, http.StatusUnauthorized)
+ showUserPage(w, r, http.StatusUnauthorized)
}
-func showPWForm(w http.ResponseWriter, r *http.Request, status int) {
+func showUserPage(w http.ResponseWriter, r *http.Request, status int) {
templates.UserPage(status).Render(r.Context(), w)
return
}
diff --git a/Backend/helper/web.go b/Backend/helper/web.go
index 70a2288..4759f4d 100644
--- a/Backend/helper/web.go
+++ b/Backend/helper/web.go
@@ -3,6 +3,8 @@ package helper
import (
"net/http"
"os"
+
+ "github.com/alexedwards/scs/v2"
)
// setting cors, important for later frontend use
@@ -15,3 +17,13 @@ func SetCors(w http.ResponseWriter) {
w.Header().Set("Access-Control-Allow-Headers", "*")
}
}
+
+func RequiresLogin(session *scs.SessionManager, w http.ResponseWriter, r *http.Request) {
+ if GetEnv("GO_ENV", "production") == "debug" {
+ return
+ }
+ if session.Exists(r.Context(), "user") {
+ return
+ }
+ http.Redirect(w, r, "/user/login", http.StatusSeeOther)
+}
diff --git a/Backend/main.go b/Backend/main.go
index e1e88fb..0e40b50 100644
--- a/Backend/main.go
+++ b/Backend/main.go
@@ -4,12 +4,14 @@ import (
"arbeitszeitmessung/endpoints"
"arbeitszeitmessung/helper"
"arbeitszeitmessung/models"
+ "arbeitszeitmessung/templates"
"context"
"fmt"
"log"
"net/http"
"time"
+ "github.com/a-h/templ"
"github.com/joho/godotenv"
_ "github.com/lib/pq"
)
@@ -40,6 +42,7 @@ func main() {
server.HandleFunc("/user/login", endpoints.LoginHandler)
server.HandleFunc("/user", endpoints.UserHandler)
server.HandleFunc("/team", endpoints.TeamHandler)
+ server.Handle("/", templ.Handler(templates.NavPage()))
server.Handle("/static/", http.StripPrefix("/static/", fs))
serverSessionMiddleware := endpoints.Session.LoadAndSave(server)
diff --git a/Backend/models/booking.go b/Backend/models/booking.go
index 192fd34..65c24d4 100644
--- a/Backend/models/booking.go
+++ b/Backend/models/booking.go
@@ -116,11 +116,11 @@ func (b *Booking) GetBookingsByCardID(card_uid string, tsFrom time.Time, tsTo ti
return bookings, nil
}
-func (b *Booking) GetBookingsGrouped(card_uid string, tsFrom time.Time, tsTo time.Time) ([]WorkDay, error){
+func (b *Booking) GetBookingsGrouped(card_uid string, tsFrom time.Time, tsTo time.Time) ([]WorkDay, error) {
var grouped = make(map[string][]Booking)
bookings, err := b.GetBookingsByCardID(card_uid, tsFrom, tsTo)
- if (err != nil){
- log.Println("Failed to get bookings",err)
+ if err != nil {
+ log.Println("Failed to get bookings", err)
return []WorkDay{}, nil
}
for _, booking := range bookings {
@@ -161,14 +161,14 @@ func (b Booking) Save() {
}
func (b *Booking) GetBookingType() string {
- switch b.CheckInOut{
- case 1,3: //manuelle Änderung
+ switch b.CheckInOut {
+ case 1, 3: //manuelle Änderung
return "kommen"
- case 2, 4: //manuelle Änderung
+ case 2, 4: //manuelle Änderung
return "gehen"
- case 255:
+ case 255:
return "abgemeldet"
- default:
+ default:
return "Buchungs Typ unbekannt"
}
}
@@ -183,7 +183,7 @@ func (b *Booking) Update(nb Booking) {
if b.GeraetID != nb.GeraetID && nb.GeraetID != 0 {
b.GeraetID = nb.GeraetID
}
- if(b.Timestamp != nb.Timestamp){
+ if b.Timestamp != nb.Timestamp {
b.Timestamp = nb.Timestamp
}
}
@@ -209,19 +209,19 @@ func checkLastBooking(b Booking) bool {
return true
}
-func (b *Booking) UpdateTime(newTime time.Time){
+func (b *Booking) UpdateTime(newTime time.Time) {
hour, minute, _ := newTime.Clock()
- if(hour == b.Timestamp.Hour() && minute == b.Timestamp.Minute()){
+ if hour == b.Timestamp.Hour() && minute == b.Timestamp.Minute() {
return
}
// TODO: add check for time overlap
var newBooking Booking
- newBooking.Timestamp = time.Date(b.Timestamp.Year(), b.Timestamp.Month(), b.Timestamp.Day(), hour, minute, 0, 0, time.Local)
- if(b.CheckInOut < 3){
+ newBooking.Timestamp = time.Date(b.Timestamp.Year(), b.Timestamp.Month(), b.Timestamp.Day(), hour, minute, 0, 0, time.Local)
+ if b.CheckInOut < 3 {
newBooking.CheckInOut = b.CheckInOut + 2
}
- if(b.CheckInOut == 255){
+ if b.CheckInOut == 255 {
newBooking.CheckInOut = 4
}
b.Update(newBooking)
diff --git a/Backend/models/user.go b/Backend/models/user.go
index 3c0f0d0..94fd3d0 100644
--- a/Backend/models/user.go
+++ b/Backend/models/user.go
@@ -194,17 +194,38 @@ func parseUser(rows *sql.Rows) (User, error) {
return user, nil
}
+// returns the start of the week, the last submission was made, submission == first booking or last send booking_report to team leader
func (u *User) GetLastSubmission() time.Time {
var lastSub time.Time
- qStr, err := DB.Prepare("SELECT woche_start FROM buchung_wochen WHERE personal_nummer = $1 ORDER BY woche_start DESC LIMIT 1")
+ qStr, err := DB.Prepare(`
+ SELECT COALESCE(
+ (SELECT woche_start + INTERVAL '1 week' FROM wochen_report WHERE personal_nummer = $1 ORDER BY woche_start DESC LIMIT 1),
+ (SELECT timestamp FROM anwesenheit WHERE card_uid = $2 ORDER BY timestamp LIMIT 1)
+ ) AS letzte_buchung;
+`)
if err != nil {
log.Println("Error preparing statement!", err)
return lastSub
}
- err = qStr.QueryRow(u.PersonalNummer).Scan(&lastSub)
+ err = qStr.QueryRow(u.PersonalNummer, u.CardUID).Scan(&lastSub)
if err != nil {
log.Println("Error executing query!", err)
return lastSub
}
+ log.Println("From DB: ", lastSub)
+ lastSub = getMonday(lastSub)
+ lastSub = lastSub.Round(24 * time.Hour)
+ log.Println("After truncate: ", lastSub)
return lastSub
}
+
+func getMonday(ts time.Time) time.Time {
+ if ts.Weekday() != time.Monday {
+ if ts.Weekday() == time.Sunday {
+ ts = ts.AddDate(0, 0, -6)
+ } else {
+ ts = ts.AddDate(0, 0, -int(ts.Weekday()-1))
+ }
+ }
+ return ts
+}
diff --git a/Backend/models/workWeek.go b/Backend/models/workWeek.go
index 57da188..7d84436 100644
--- a/Backend/models/workWeek.go
+++ b/Backend/models/workWeek.go
@@ -1,27 +1,45 @@
package models
import (
+ "errors"
"log"
"time"
)
type WorkWeek struct {
+ Id int
WorkDays []WorkDay
User User
WeekStart time.Time
+ WorkHours time.Duration
}
-func (w *WorkWeek) GetWeek(user User, tsMonday time.Time) WorkWeek {
+func (w *WorkWeek) GetWeek(user User, tsMonday time.Time, populateDays bool) WorkWeek {
var week WorkWeek
- week.WorkDays = (*WorkDay).GetWorkDays(nil, user.CardUID, tsMonday, tsMonday.Add(7*24*time.Hour))
+ if populateDays {
+ week.WorkDays = (*WorkDay).GetWorkDays(nil, user.CardUID, tsMonday, tsMonday.Add(7*24*time.Hour))
+ week.WorkHours = aggregateWorkTime(week.WorkDays)
+ }
week.User = user
week.WeekStart = tsMonday
return week
}
+func (w *WorkWeek) GetWorkHourString() string {
+ return formatDuration(w.WorkHours)
+}
+
+func aggregateWorkTime(days []WorkDay) time.Duration {
+ var workTime time.Duration
+ for _, day := range days {
+ workTime += day.workTime
+ }
+ return workTime
+}
+
func (w *WorkWeek) GetSendWeeks(user User) []WorkWeek {
var weeks []WorkWeek
- qStr, err := DB.Prepare(`SELECT woche_start::DATE FROM buchung_wochen WHERE bestaetigt = FALSE AND personal_nummer = $1;`)
+ qStr, err := DB.Prepare(`SELECT id, woche_start::DATE FROM wochen_report WHERE bestaetigt = FALSE AND personal_nummer = $1;`)
if err != nil {
log.Println("Error preparing SQL statement", err)
return weeks
@@ -37,11 +55,12 @@ func (w *WorkWeek) GetSendWeeks(user User) []WorkWeek {
for rows.Next() {
var week WorkWeek
week.User = user
- if err := rows.Scan(&week.WeekStart); err != nil {
+ if err := rows.Scan(&week.Id, &week.WeekStart); err != nil {
log.Println("Error scanning row!", err)
return weeks
}
week.WorkDays = (*WorkDay).GetWorkDays(nil, user.CardUID, week.WeekStart, week.WeekStart.Add(7*24*time.Hour))
+ week.WorkHours = aggregateWorkTime(week.WorkDays)
weeks = append(weeks, week)
}
if err = rows.Err(); err != nil {
@@ -50,3 +69,39 @@ func (w *WorkWeek) GetSendWeeks(user User) []WorkWeek {
return weeks
}
+
+var ErrRunningWeek = errors.New("Week is in running week")
+
+// creates a new entry in the woche_report table with the given workweek
+func (w *WorkWeek) Send() error {
+ if time.Since(w.WeekStart) < 5*24*time.Hour {
+ log.Println("Cannot send week, because it's the running week!")
+ return ErrRunningWeek
+ }
+ qStr, err := DB.Prepare(`INSERT INTO wochen_report (personal_nummer, woche_start) VALUES ($1, $2);`)
+ if err != nil {
+ log.Println("Error preparing SQL statement", err)
+ return err
+ }
+ _, err = qStr.Exec(w.User.PersonalNummer, w.WeekStart)
+ if err != nil {
+ log.Println("Error executing query!", err)
+ return err
+ }
+ return nil
+
+}
+
+func (w *WorkWeek) Accept() error {
+ qStr, err := DB.Prepare(`UPDATE "wochen_report" SET bestaetigt = TRUE WHERE personal_nummer = $1 AND woche_start = $2;`)
+ if err != nil {
+ log.Println("Error preparing SQL statement", err)
+ return err
+ }
+ _, err = qStr.Exec(w.User.PersonalNummer, w.WeekStart)
+ if err != nil {
+ log.Println("Error executing query!", err)
+ return err
+ }
+ return nil
+}
diff --git a/Backend/static/css/styles.css b/Backend/static/css/styles.css
index 962e212..881cfaa 100644
--- a/Backend/static/css/styles.css
+++ b/Backend/static/css/styles.css
@@ -562,9 +562,6 @@
.mt-1 {
margin-top: calc(var(--spacing) * 1);
}
- .-mb-1 {
- margin-bottom: calc(var(--spacing) * -1);
- }
.mb-2 {
margin-bottom: calc(var(--spacing) * 2);
}
@@ -666,9 +663,6 @@
.justify-center {
justify-content: center;
}
- .justify-end {
- justify-content: flex-end;
- }
.gap-2 {
gap: calc(var(--spacing) * 2);
}
@@ -695,6 +689,11 @@
.justify-self-end {
justify-self: flex-end;
}
+ .truncate {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
.overflow-hidden {
overflow: hidden;
}
@@ -777,6 +776,9 @@
.text-neutral-800 {
color: var(--color-neutral-800);
}
+ .text-red-500 {
+ color: var(--color-red-500);
+ }
.text-red-600 {
color: var(--color-red-600);
}
@@ -858,6 +860,13 @@
}
}
}
+ .hover\:text-accent {
+ &:hover {
+ @media (hover: hover) {
+ color: var(--color-accent);
+ }
+ }
+ }
.hover\:text-white {
&:hover {
@media (hover: hover) {
diff --git a/Backend/templates/pages.templ b/Backend/templates/pages.templ
index 95ce082..6cea2c1 100644
--- a/Backend/templates/pages.templ
+++ b/Backend/templates/pages.templ
@@ -3,6 +3,8 @@ package templates
import (
"arbeitszeitmessung/models"
"fmt"
+ "strconv"
+ "time"
)
templ Base() {
@@ -69,30 +71,47 @@ templ UserPage(status int) {
}
-templ TeamPage(weeks []models.WorkWeek, week models.WorkWeek) {
+templ TeamPage(weeks []models.WorkWeek, userWeek models.WorkWeek) {
{{
- year, kw := week.WeekStart.ISOWeek()
+ year, kw := userWeek.WeekStart.ISOWeek()
}}
@Base()
@headerComponent()
-
{ fmt.Sprintf("%s %s", week.User.Vorname, week.User.Name) }
+
{ fmt.Sprintf("%s %s", userWeek.User.Vorname, userWeek.User.Name) }
- for _, day := range week.WorkDays {
- @weekDayComponent(week.User, day)
+ for _, day := range userWeek.WorkDays {
+ @weekDayComponent(userWeek.User, day)
}
-
+
for _, week := range weeks {
@employeComponent(week)
}
}
+
+templ NavPage() {
+ @Base()
+
+}
diff --git a/Backend/templates/pages_templ.go b/Backend/templates/pages_templ.go
index 74efc84..f72b12e 100644
--- a/Backend/templates/pages_templ.go
+++ b/Backend/templates/pages_templ.go
@@ -11,6 +11,8 @@ import templruntime "github.com/a-h/templ/runtime"
import (
"arbeitszeitmessung/models"
"fmt"
+ "strconv"
+ "time"
)
func Base() templ.Component {
@@ -198,7 +200,7 @@ func UserPage(status int) templ.Component {
})
}
-func TeamPage(weeks []models.WorkWeek, week models.WorkWeek) templ.Component {
+func TeamPage(weeks []models.WorkWeek, userWeek models.WorkWeek) 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 {
@@ -220,7 +222,7 @@ func TeamPage(weeks []models.WorkWeek, week models.WorkWeek) templ.Component {
}
ctx = templ.ClearChildren(ctx)
- year, kw := week.WeekStart.ISOWeek()
+ year, kw := userWeek.WeekStart.ISOWeek()
templ_7745c5c3_Err = Base().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
@@ -234,9 +236,9 @@ func TeamPage(weeks []models.WorkWeek, week models.WorkWeek) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
- templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s %s", week.User.Vorname, week.User.Name))
+ templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s %s", userWeek.User.Vorname, userWeek.User.Name))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pages.templ`, Line: 80, Col: 103}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pages.templ`, Line: 82, Col: 111}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
@@ -246,26 +248,52 @@ func TeamPage(weeks []models.WorkWeek, week models.WorkWeek) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- for _, day := range week.WorkDays {
- templ_7745c5c3_Err = weekDayComponent(week.User, day).Render(ctx, templ_7745c5c3_Buffer)
+ for _, day := range userWeek.WorkDays {
+ templ_7745c5c3_Err = weekDayComponent(userWeek.User, day).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "Woche: ")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "
")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "
an Vorgesetzten senden
Senden ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -275,7 +303,40 @@ func TeamPage(weeks []models.WorkWeek, week models.WorkWeek) templ.Component {
return templ_7745c5c3_Err
}
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return nil
+ })
+}
+
+func NavPage() 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_Var10 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var10 == nil {
+ templ_7745c5c3_Var10 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ 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, 19, "")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
diff --git a/Backend/templates/teamComponents.templ b/Backend/templates/teamComponents.templ
index 132142f..1ac4879 100644
--- a/Backend/templates/teamComponents.templ
+++ b/Backend/templates/teamComponents.templ
@@ -1,8 +1,11 @@
package templates
-import "arbeitszeitmessung/models"
-
-import "fmt"
+import (
+ "arbeitszeitmessung/models"
+ "fmt"
+ "strconv"
+ "time"
+)
templ weekDayComponent(user models.User, day models.WorkDay) {
{{ work, pause := day.GetWorkTimeString() }}
@@ -26,18 +29,21 @@ templ employeComponent(week models.WorkWeek) {
{ week.User.Vorname } { week.User.Name }
Arbeitszeit
-
40h 12min
+
{ week.GetWorkHourString() }
for _, day := range week.WorkDays {
@weekDayComponent(week.User, day)
}
-
+
}
diff --git a/Backend/templates/teamComponents_templ.go b/Backend/templates/teamComponents_templ.go
index 66175aa..6171e40 100644
--- a/Backend/templates/teamComponents_templ.go
+++ b/Backend/templates/teamComponents_templ.go
@@ -8,9 +8,12 @@ package templates
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
-import "arbeitszeitmessung/models"
-
-import "fmt"
+import (
+ "arbeitszeitmessung/models"
+ "fmt"
+ "strconv"
+ "time"
+)
func weekDayComponent(user models.User, day models.WorkDay) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
@@ -49,7 +52,7 @@ func weekDayComponent(user models.User, day models.WorkDay) templ.Component {
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(day.Day.Format("Mon"))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 12, Col: 89}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 15, Col: 89}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
@@ -62,7 +65,7 @@ func weekDayComponent(user models.User, day models.WorkDay) templ.Component {
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(day.Day.Format("02.01.2006"))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 12, Col: 130}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 15, Col: 130}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
@@ -75,7 +78,7 @@ func weekDayComponent(user models.User, day models.WorkDay) templ.Component {
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(work)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 14, Col: 36}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 17, Col: 36}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
@@ -88,7 +91,7 @@ func weekDayComponent(user models.User, day models.WorkDay) templ.Component {
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(pause)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 15, Col: 42}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 18, Col: 42}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
@@ -132,7 +135,7 @@ func employeComponent(week models.WorkWeek) templ.Component {
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(week.User.Vorname)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 27, Col: 53}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 30, Col: 53}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
@@ -145,13 +148,26 @@ func employeComponent(week models.WorkWeek) templ.Component {
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(week.User.Name)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 27, Col: 72}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 30, Col: 72}
}
_, 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, 9, "Arbeitszeit
40h 12min
")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "
Arbeitszeit
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var9 string
+ templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(week.GetWorkHourString())
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 32, Col: 52}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -161,20 +177,46 @@ func employeComponent(week models.WorkWeek) templ.Component {
return templ_7745c5c3_Err
}
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "
Woche: ")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
diff --git a/DB/initdb/01_create_tables.sql b/DB/initdb/01_create_tables.sql
index 1136f19..839b396 100644
--- a/DB/initdb/01_create_tables.sql
+++ b/DB/initdb/01_create_tables.sql
@@ -61,12 +61,13 @@ EXECUTE FUNCTION update_zuletzt_geandert();
-- audittabelle für arbeitsstunden bestätigung
-DROP TABLE IF EXISTS "buchung_wochen";
-CREATE TABLE "buchung_wochen" (
+DROP TABLE IF EXISTS "wochen_report";
+CREATE TABLE "wochen_report" (
"id" serial PRIMARY KEY,
"personal_nummer" int4,
"woche_start" date,
- "bestaetigt" bool DEFAULT FALSE
+ "bestaetigt" bool DEFAULT FALSE,
+ UNIQUE ("personal_nummer", "woche_start")
);
-- Adds crypto extension