18 Commits

Author SHA1 Message Date
66db633dc6 Merge pull request 'dev/ui' (#33) from dev/ui into main
All checks were successful
GoLang Tests / Run Go Tests (push) Successful in 27s
Reviewed-on: #33
2025-09-04 22:12:52 +02:00
fe442e8eef closes #14
All checks were successful
GoLang Tests / Run Go Tests (push) Successful in 33s
2025-09-04 22:07:54 +02:00
9ded540314 closed #25, #32
All checks were successful
GoLang Tests / Run Go Tests (push) Successful in 49s
2025-09-04 21:22:26 +02:00
0dd75c2126 Update build-deploy.yaml
All checks were successful
GoLang Tests / Run Go Tests (push) Successful in 59s
2025-09-04 10:23:37 +02:00
327e47840b Fixed tests and git actions (#30)
All checks were successful
GoLang Tests / Run Go Tests (push) Successful in 2m46s
Arbeitszeitmessung Deploy / Build Go Image and Upload (push) Successful in 2m58s
Reviewed-on: #30
Co-authored-by: Tom Tröger <t.troeger.02@gmail.com>
Co-committed-by: Tom Tröger <t.troeger.02@gmail.com>
2025-09-04 10:16:42 +02:00
e9f8ab0a56 Merge pull request 'dev/main -- added License' (#29) from dev/main into main
Some checks failed
GoLang Tests / Run Go Tests (push) Failing after 48s
GoLang Tests / Build Go Image and Upload (push) Has been skipped
Reviewed-on: #29
2025-09-04 09:24:29 +02:00
bcefd7b630 Merge pull request 'dev/actions feature: overtime' (#27) from dev/actions into main
Some checks failed
GoLang Tests / Run Go Tests (push) Failing after 51s
GoLang Tests / Build Go Image and Upload (push) Has been skipped
Reviewed-on: #27
2025-09-04 00:58:25 +02:00
a2cd118644 addin for sonarcube
Some checks failed
GoLang Tests / Run Go Tests (push) Failing after 36s
GoLang Tests / Build Go Image and Upload (push) Has been skipped
2025-09-04 00:56:20 +02:00
d51b0c12c5 update
Some checks failed
GoLang Tests / Build Go Image and Upload (push) Has been cancelled
GoLang Tests / Run Go Tests (push) Has been cancelled
2025-09-04 00:55:35 +02:00
15e28e1b18 added database migrations 2025-09-04 00:48:30 +02:00
1ae30c11cb added overtime to time and team page + ui improvements + mobile support for team page closed #12 2025-09-04 00:11:33 +02:00
45440b6457 added tests
Some checks failed
GoLang Tests / Run Go Tests (push) Failing after 35s
GoLang Tests / Build Go Image and Upload (push) Has been skipped
2025-09-03 14:31:57 +02:00
483c1e29ba ui update /team + overtime updates 2025-09-03 14:31:57 +02:00
492216b160 added overtime to week report closes #18 2025-09-03 14:31:57 +02:00
1397530cb6 added absence hours 2025-09-03 14:27:27 +02:00
de6da2906f added booking types + working on overtime 2025-09-01 22:41:21 +02:00
aa152866d9 added types 2025-09-01 22:11:37 +02:00
28f832694a redoing migratinos 2025-09-01 22:11:23 +02:00
51 changed files with 3235 additions and 813 deletions

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
# Ensure all text files use LF line endings
* text=auto eol=lf

View File

@@ -0,0 +1,39 @@
name: Arbeitszeitmessung Deploy
run-name: ${{ gitea.actor }} is building and deploying arbeitszeitmesssung
on:
workflow_run:
workflows: ["tests"]
types:
- completed
jobs:
build:
name: Build Go Image and Upload
if: |
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
startsWith(github.event.workflow_run.head_branch, 'main') &&
startsWith(github.event.workflow_run.head_commit.ref, 'refs/tags/')
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: git.letsstein.de
username: ${{ gitea.actor }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
push: true
context: Backend
tags: |
git.letsstein.de/tom/arbeitszeitmessung:latest
git.letsstein.de/tom/arbeitszeitmessung:${{ github.ref_name }}

View File

@@ -1,6 +1,6 @@
name: GoLang Tests
run-name: ${{ gitea.actor }} is testing golang Code
on: [push]
on: push
jobs:
testing:
@@ -46,27 +46,3 @@ jobs:
arbeitszeitmessung-
- name: Run Go Tests
run: cd Backend && go test ./...
build:
needs: testing
name: Build Go Image and Upload
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: git.letsstein.de
username: ${{ gitea.actor }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
push: true
tags: git.letsstein.de/tom/arbeitszeitmessung:latest
context: Backend

1
.gitignore vendored
View File

@@ -38,3 +38,4 @@ DB/pg_data
node_modules
atlas.hcl
.scannerwork
Backend/logs

View File

@@ -3,4 +3,4 @@ test:
go test ./... -coverprofile=.test/coverage.out -json > .test/report.json
scan:
sonar-scanner
sonar-scanner -Dsonar.token=sqa_ca8394c93a728d6cff96703955288d8902c15200

View File

@@ -34,7 +34,7 @@ func submitReport(w http.ResponseWriter, r *http.Request) {
_weekTs := r.FormValue("week")
weekTs, err := time.Parse(time.DateOnly, _weekTs)
user, err := models.GetUserByPersonalNr(userPN)
workWeek := (*models.WorkWeek).GetWeek(nil, user, weekTs, false)
workWeek := models.NewWorkWeek(user, weekTs, true)
if err != nil {
log.Println("Could not get user!")
@@ -43,7 +43,7 @@ func submitReport(w http.ResponseWriter, r *http.Request) {
switch r.FormValue("method") {
case "send":
err = workWeek.Send()
err = workWeek.SendWeek()
case "accept":
err = workWeek.Accept()
default:
@@ -63,14 +63,14 @@ func showWeeks(w http.ResponseWriter, r *http.Request) {
return
}
submissionDate := r.URL.Query().Get("submission_date")
lastSub := user.GetLastSubmission()
lastSub := user.GetLastWorkWeekSubmission()
if submissionDate != "" {
submissionDate, err := time.Parse("2006-01-02", submissionDate)
if err == nil {
lastSub = helper.GetMonday(submissionDate)
}
}
userWeek := (*models.WorkWeek).GetWeek(nil, user, lastSub, true)
userWeek := models.NewWorkWeek(user, lastSub, true)
var workWeeks []models.WorkWeek
teamMembers, err := user.GetTeamMembers()

View File

@@ -50,8 +50,9 @@ func createBooking(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted)
json.NewEncoder(w).Encode(booking)
return
}
w.WriteHeader(http.StatusBadRequest)
http.Error(w, "Cannot verify booking, maybe missing a parameter", http.StatusBadRequest)
}
func verifyToken(r *http.Request) bool {

View File

@@ -71,6 +71,20 @@ func getBookings(w http.ResponseWriter, r *http.Request) {
return workDays[i].Day.After(workDays[j].Day)
})
lastSub := user.GetLastWorkWeekSubmission()
var aggregatedOvertime time.Duration
for _, day := range workDays {
if day.Day.Before(lastSub) {
continue
}
aggregatedOvertime += day.CalcOvertime(user)
}
if reportedOvertime, err := user.GetReportedOvertime(); err == nil {
user.Overtime = (reportedOvertime + aggregatedOvertime).Round(time.Minute)
} else {
log.Println("Cannot calculate overtime: ", err)
}
if r.Header.Get("Accept") == "application/json" {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
@@ -79,7 +93,7 @@ func getBookings(w http.ResponseWriter, r *http.Request) {
}
ctx := context.WithValue(r.Context(), "user", user)
templates.TimePage(workDays).Render(ctx, w)
templates.TimePage(workDays, lastSub).Render(ctx, w)
}
func updateBooking(w http.ResponseWriter, r *http.Request) {
@@ -110,11 +124,11 @@ func updateBooking(w http.ResponseWriter, r *http.Request) {
return
}
newBooking := (*models.Booking).New(nil, user.CardUID, 0, int16(check_in_out))
newBooking := (*models.Booking).New(nil, user.CardUID, 0, int16(check_in_out), 1)
newBooking.Timestamp = timestamp
err = newBooking.InsertWithTimestamp()
if err != nil {
log.Println("Error inserting booking", err)
log.Printf("Error inserting booking %v -> %v\n", newBooking, err)
}
case "change":
absenceType, err := strconv.Atoi(r.FormValue("absence"))

View File

@@ -1,7 +1,6 @@
package endpoints
import (
"arbeitszeitmessung/helper"
"arbeitszeitmessung/models"
"arbeitszeitmessung/templates"
"context"
@@ -21,54 +20,48 @@ func CreateSessionManager(lifetime time.Duration) *scs.SessionManager {
return Session
}
func showLoginPage(w http.ResponseWriter, r *http.Request, failed bool) {
func showLoginPage(w http.ResponseWriter, r *http.Request, success bool, errorMsg string) {
r = r.WithContext(context.WithValue(r.Context(), "session", Session))
if helper.GetEnv("GO_ENV", "production") == "debug" {
// http.Redirect(w, r, "/time", http.StatusSeeOther)
templates.LoginPage(failed).Render(r.Context(), w)
}
if Session.Exists(r.Context(), "user") {
http.Redirect(w, r, "/time", http.StatusSeeOther)
}
templates.LoginPage(failed).Render(r.Context(), w)
templates.LoginPage(success, errorMsg).Render(r.Context(), w)
}
func loginUser(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
log.Println("Error parsing form!", err)
http.Error(w, "Internal error", http.StatusBadRequest)
showLoginPage(w, r, false, "Internal error!")
return
}
_personal_nummer := r.FormValue("personal_nummer")
if _personal_nummer == "" {
log.Println("No personal_nummer provided!")
http.Error(w, "No personal_nummer provided", http.StatusBadRequest)
showLoginPage(w, r, false, "Keine Personalnummer gesetzt.")
return
}
personal_nummer, err := strconv.Atoi(_personal_nummer)
if err != nil {
log.Println("Cannot parse personal nubmer!")
http.Error(w, "Cannot parse number", http.StatusBadRequest)
showLoginPage(w, r, false, "Personalnummer ist nicht valide gesetzt.")
return
}
user, err := models.GetUserByPersonalNr(personal_nummer)
if err != nil {
log.Println("No user found under this personal number!")
http.Error(w, "No user found!", http.StatusNotFound)
log.Println("No user found under this personal number!", err)
showLoginPage(w, r, false, "Nutzer unter dieser Personalnummer nicht gefunden.")
return
}
password := r.FormValue("password")
if user.Login(password) {
log.Printf("New succesfull user login from %s %s!\n", user.Vorname, user.Name)
log.Printf("New succesfull user login from %s %s (%d)!\n", user.Vorname, user.Name, user.PersonalNummer)
Session.Put(r.Context(), "user", user.PersonalNummer)
Session.Commit(r.Context())
http.Redirect(w, r, "/time", http.StatusSeeOther) //with this browser always uses GET
} else {
showLoginPage(w, r, true)
return
}
showLoginPage(w, r, false)
showLoginPage(w, r, false, "")
}
func logoutUser(w http.ResponseWriter, r *http.Request) {

View File

@@ -19,7 +19,7 @@ func UserHandler(w http.ResponseWriter, r *http.Request) {
func LoginHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
showLoginPage(w, r, false)
showLoginPage(w, r, true, "")
case http.MethodPost:
loginUser(w, r)
default:

View File

@@ -0,0 +1,24 @@
package logs
import (
"log"
"os"
"time"
)
type FileLog struct {
Logger *log.Logger
Close func() error
}
var Logs map[string]FileLog = make(map[string]FileLog)
func NewAudit() (i *log.Logger, close func() error) {
LOG_FILE := "logs/" + time.Now().Format("2006-01-02") + ".log"
logFile, err := os.OpenFile(LOG_FILE, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Panic(err)
}
return log.New(logFile, "", log.LstdFlags), logFile.Close
}

View File

@@ -16,6 +16,10 @@ func GetMonday(ts time.Time) time.Time {
return ts
}
func IsWeekend(ts time.Time) bool {
return ts.Weekday() == time.Saturday || ts.Weekday() == time.Sunday
}
// Converts duration to string
func FormatDuration(d time.Duration) string {
hours := int(d.Abs().Hours())

View File

@@ -8,7 +8,7 @@ import (
func TestGetMonday(t *testing.T) {
isMonday, err := time.Parse("2006-01-02", "2025-07-14")
notMonday, err := time.Parse("2006-01-02", "2025-07-16")
if err != nil || isMonday == notMonday {
if err != nil || isMonday.Equal(notMonday) {
t.Errorf("U stupid? %e", err)
}
if GetMonday(isMonday) != isMonday || GetMonday(notMonday) != isMonday {

View File

@@ -9,6 +9,7 @@ import (
type AbsenceType struct {
Id int8
Name string
WorkTime float32
}
type Absence struct {
@@ -23,7 +24,7 @@ func NewAbsence(card_uid string, abwesenheit_typ int, datum time.Time) (Absence,
if abwesenheit_typ < 0 {
return Absence{
CardUID: card_uid,
AbwesenheitTyp: AbsenceType{0, "Custom absence"},
AbwesenheitTyp: AbsenceType{0, "Custom absence", 100},
Datum: datum,
}, nil
}
@@ -44,6 +45,7 @@ func (a *Absence) Insert() error {
log.Println("Error preparing sql Statement", err)
return err
}
defer qStr.Close()
err = qStr.QueryRow(a.CardUID, a.AbwesenheitTyp.Id, a.Datum).Scan(&a.CounterId)
if err != nil {
log.Println("Error executing insert statement", err)
@@ -52,13 +54,53 @@ func (a *Absence) Insert() error {
return nil
}
// func (a *Absence) GetStringType() string {
// return AbsenceTypesLabel[a.AbwesenheitTyp]
// }
func GetAbsenceById(counterId int) (Absence, error) {
var absence Absence = Absence{CounterId: counterId}
qStr, err := DB.Prepare("SELECT card_uid, abwesenheit_typ, datum FROM abwesenheit WHERE counter_id = $1;")
if err != nil {
return absence, err
}
defer qStr.Close()
err = qStr.QueryRow(counterId).Scan(&absence.CardUID, &absence.AbwesenheitTyp.Id, &absence.Datum)
if err != nil {
return absence, err
}
return absence, nil
}
func GetAbsencesByCardUID(card_uid string, tsFrom time.Time, tsTo time.Time) ([]Absence, error) {
var absences []Absence
qStr, err := DB.Prepare("SELECT counter_id, abwesenheit_typ, datum FROM abwesenheit WHERE card_uid = $1 AND datum BETWEEN $2::DATE AND $3::DATE ORDER BY datum;")
if err != nil {
return absences, err
}
defer qStr.Close()
rows, err := qStr.Query(card_uid, tsFrom, tsTo)
if err != nil {
return absences, err
}
defer rows.Close()
for rows.Next() {
var absence Absence
if err := rows.Scan(&absence.CounterId, &absence.AbwesenheitTyp.Id, &absence.Datum); err != nil {
return absences, err
}
absence.AbwesenheitTyp, err = GetAbsenceTypeById(absence.AbwesenheitTyp.Id)
if err == nil {
absences = append(absences, absence)
} else {
log.Println("Cannot populate absence type!", err)
}
}
if err = rows.Err(); err != nil {
return absences, err
}
return absences, nil
}
func GetAbsenceTypes() (map[int8]AbsenceType, error) {
var types = make(map[int8]AbsenceType)
qStr, err := DB.Prepare("SELECT abwesenheit_id, abwesenheit_name FROM s_abwesenheit_typen;")
qStr, err := DB.Prepare("SELECT abwesenheit_id, abwesenheit_name, arbeitszeit_equivalent FROM s_abwesenheit_typen;")
if err != nil {
return types, err
}
@@ -71,7 +113,7 @@ func GetAbsenceTypes() (map[int8]AbsenceType, error) {
defer rows.Close()
for rows.Next() {
var absenceType AbsenceType
if err := rows.Scan(&absenceType.Id, &absenceType.Name); err != nil {
if err := rows.Scan(&absenceType.Id, &absenceType.Name, &absenceType.WorkTime); err != nil {
log.Println("Error scanning absence row!", err)
}
types[absenceType.Id] = absenceType
@@ -86,3 +128,18 @@ func GetAbsenceTypesCached() map[int8]AbsenceType {
}
return types.(map[int8]AbsenceType)
}
func GetAbsenceTypeById(absenceTypeId int8) (AbsenceType, error) {
var absenceType AbsenceType = AbsenceType{Id: absenceTypeId}
qStr, err := DB.Prepare("SELECT abwesenheit_name, arbeitszeit_equivalent FROM s_abwesenheit_typen WHERE abwesenheit_id = $1;")
if err != nil {
return absenceType, err
}
defer qStr.Close()
err = qStr.QueryRow(absenceTypeId).Scan(&absenceType.Name, &absenceType.WorkTime)
if err != nil {
return absenceType, err
}
return absenceType, nil
}

View File

@@ -2,6 +2,7 @@ package models
import (
"arbeitszeitmessung/helper"
"arbeitszeitmessung/helper/logs"
"database/sql"
"fmt"
"log"
@@ -28,6 +29,7 @@ type Booking struct {
CheckInOut int16 `json:"check_in_out"`
Timestamp time.Time `json:"timestamp"`
CounterId int `json:"counter_id"`
BookingType BookingType `json:"booking_type"`
}
type IDatabase interface {
@@ -37,31 +39,52 @@ type IDatabase interface {
var DB IDatabase
func (b *Booking) New(card_uid string, geraet_id int16, check_in_out int16) Booking {
func (b *Booking) New(cardUid string, gereatId int16, checkInOut int16, typeId int8) Booking {
bookingType, err := GetBookingTypeById(typeId)
if err != nil {
log.Printf("Cannot get booking type %d, from database!", typeId)
}
return Booking{
CardUID: card_uid,
GeraetID: geraet_id,
CheckInOut: check_in_out,
CardUID: cardUid,
GeraetID: gereatId,
CheckInOut: checkInOut,
BookingType: bookingType,
}
}
func (b *Booking) FromUrlParams(params url.Values) Booking {
var booking Booking
if _check_in_out, err := strconv.Atoi(params.Get("check_in_out")); err == nil {
booking.CheckInOut = int16(_check_in_out)
}
if _geraet_id, err := strconv.Atoi(params.Get("geraet_id")); err == nil {
booking.GeraetID = int16(_geraet_id)
}
if _booking_type, err := strconv.Atoi(params.Get("booking_type")); err == nil {
booking.BookingType.Id = int8(_booking_type)
}
booking.CardUID = params.Get("card_uid")
return booking
}
func (b *Booking) Verify() bool {
//check for overlapping time + arbeitszeit verstoß
if b.CardUID == "" { //|| b.GeraetID == 0 || b.CheckInOut == 0 {
log.Println("Booking verify failed invalid CardUID!")
return false
}
if b.CheckInOut == 0 {
log.Println("Booking verify failed invalid CheckInOut!")
return false
}
if bookingType, err := GetBookingTypeById(b.BookingType.Id); err != nil {
log.Println("Booking verify failed invalid BookingType.Id!")
return false
} else {
b.BookingType.Name = bookingType.Name
}
return true
}
@@ -69,11 +92,11 @@ func (b *Booking) Insert() error {
if !checkLastBooking(*b) {
return SameBookingError{}
}
stmt, err := DB.Prepare((`INSERT INTO anwesenheit (card_uid, geraet_id, check_in_out) VALUES ($1, $2, $3) RETURNING counter_id, timestamp`))
stmt, err := DB.Prepare((`INSERT INTO anwesenheit (card_uid, geraet_id, check_in_out, anwesenheit_typ) VALUES ($1, $2, $3, $4) RETURNING counter_id, timestamp`))
if err != nil {
return err
}
err = stmt.QueryRow(b.CardUID, b.GeraetID, b.CheckInOut).Scan(&b.CounterId, &b.Timestamp)
err = stmt.QueryRow(b.CardUID, b.GeraetID, b.CheckInOut, b.BookingType.Id).Scan(&b.CounterId, &b.Timestamp)
if err != nil {
return err
}
@@ -84,11 +107,11 @@ func (b *Booking) InsertWithTimestamp() error {
if b.Timestamp.IsZero() {
return b.Insert()
}
stmt, err := DB.Prepare((`INSERT INTO anwesenheit (card_uid, geraet_id, check_in_out, timestamp) VALUES ($1, $2, $3, $4) RETURNING counter_id`))
stmt, err := DB.Prepare((`INSERT INTO anwesenheit (card_uid, geraet_id, check_in_out, anwesenheit_typ, timestamp) VALUES ($1, $2, $3, $4, $5) RETURNING counter_id`))
if err != nil {
return err
}
err = stmt.QueryRow(b.CardUID, b.GeraetID, b.CheckInOut, b.Timestamp).Scan(&b.CounterId)
err = stmt.QueryRow(b.CardUID, b.GeraetID, b.CheckInOut, b.BookingType.Id, b.Timestamp).Scan(&b.CounterId)
if err != nil {
return err
}
@@ -97,18 +120,15 @@ func (b *Booking) InsertWithTimestamp() error {
func (b *Booking) GetBookingById(booking_id int) (Booking, error) {
var booking Booking
qStr, err := DB.Prepare((`SELECT counter_id, timestamp, card_uid, geraet_id, check_in_out FROM anwesenheit WHERE counter_id = $1`))
qStr, err := DB.Prepare((`SELECT counter_id, timestamp, card_uid, geraet_id, check_in_out, anwesenheit_typ FROM anwesenheit WHERE counter_id = $1`))
if err != nil {
return booking, err
}
err = qStr.QueryRow(booking_id).Scan(&booking.CounterId, &booking.Timestamp, &booking.CardUID, &booking.GeraetID, &booking.CheckInOut)
// TODO: also get booking type name
err = qStr.QueryRow(booking_id).Scan(&booking.CounterId, &booking.Timestamp, &booking.CardUID, &booking.GeraetID, &booking.CheckInOut, &booking.BookingType.Id)
if err != nil {
return booking, err
}
// if !booking.Verify() {
// fmt.Printf("Booking verification failed! %d", )
// return booking, nil
// }
return booking, nil
}
@@ -180,6 +200,7 @@ func (b Booking) Save() {
log.Fatalf("Error preparing query: %v", err)
return
}
_, err = qStr.Query(b.CounterId, b.CardUID, b.GeraetID, b.CheckInOut, b.Timestamp)
if err != nil {
log.Fatalf("Error executing query: %v", err)
@@ -212,6 +233,8 @@ func (b *Booking) GetBookingType() string {
}
func (b *Booking) Update(nb Booking) {
auditLog, closeLog := logs.NewAudit()
defer closeLog()
if b.CheckInOut != nb.CheckInOut && nb.CheckInOut != 0 {
b.CheckInOut = nb.CheckInOut
}
@@ -222,6 +245,7 @@ func (b *Booking) Update(nb Booking) {
b.GeraetID = nb.GeraetID
}
if b.Timestamp != nb.Timestamp {
auditLog.Printf("Änderung in Buchung %d von '%s': Buchungszeit (%s -> %s).", b.CounterId, b.CardUID, b.Timestamp.Format("15:04"), nb.Timestamp.Format("15:04)"))
b.Timestamp = nb.Timestamp
}
}
@@ -262,17 +286,22 @@ func (b *Booking) UpdateTime(newTime time.Time) {
if b.CheckInOut == 254 {
newBooking.CheckInOut = 4
}
log.Println("Updating")
b.Update(newBooking)
b.Verify()
// TODO Check verify
if b.Verify() {
b.Save()
} else {
log.Println("Cannot save updated booking!", b.ToString())
}
// b.Verify()
// b.Save()
}
func (b *Booking) ToString() string {
return fmt.Sprintf("Booking %d: at: %s, as type: %d", b.CounterId, b.Timestamp.Format("15:04"), b.CheckInOut)
}
func GetBokkingTypes() ([]BookingType, error) {
func GetBookingTypes() ([]BookingType, error) {
var types []BookingType
qStr, err := DB.Prepare("SELECT anwesenheit_id, anwesenheit_name FROM s_anwesenheit_typen;")
if err != nil {
@@ -295,6 +324,21 @@ func GetBokkingTypes() ([]BookingType, error) {
return types, nil
}
func GetBookingTypeById(bookingTypeId int8) (BookingType, error) {
var bookingType BookingType = BookingType{Id: bookingTypeId}
qStr, err := DB.Prepare("SELECT anwesenheit_name FROM s_anwesenheit_typen WHERE anwesenheit_id = $1;")
if err != nil {
return bookingType, err
}
defer qStr.Close()
err = qStr.QueryRow(bookingTypeId).Scan(&bookingType.Name)
if err != nil {
return bookingType, err
}
return bookingType, nil
}
func GetBookingTypesCached() []BookingType {
types, err := definedTypes.Get("s_anwesenheit_typen")
if err != nil {

View File

@@ -9,7 +9,7 @@ var definedTypes = helper.NewCache(3600, func(key string) (any, error) {
case "s_abwesenheit_typen":
return GetAbsenceTypes()
case "s_anwesenheit_typen":
return GetBokkingTypes()
return GetBookingTypes()
}
return nil, nil
})

View File

@@ -36,10 +36,12 @@ func SetupDBFixture(t *testing.T) *DBFixture {
t.Fatalf("failed to connect to database: %v", err)
}
// err = MigrateDB(db, "file://../../migrations")
// if err != nil && err != migrate.ErrNoChange {
// t.Fatalf("Failed to migrate database: %v", err)
// }
defer db.Close()
err = MigrateDB(db, "file://../../migrations")
if err != nil && err != migrate.ErrNoChange {
t.Fatalf("Failed to migrate database: %v", err)
}
tx, err := db.Begin()
if err != nil {

View File

@@ -13,11 +13,13 @@ import (
)
type User struct {
CardUID string `json:"card_uid"`
CardUID string //`json:"card_uid"`
Name string `json:"name"`
Vorname string `json:"vorname"`
PersonalNummer int `json:"personal_nummer"`
ArbeitszeitPerTag float32 `json:"arbeitszeit"`
PersonalNummer int //`json:"personal_nummer"`
ArbeitszeitPerTag float32 //`json:"arbeitszeit_per_tag"`
ArbeitszeitPerWoche float32 //`json:"arbeitszeit_per_woche"`
Overtime time.Duration
}
func (u *User) GetUserFromSession(Session *scs.SessionManager, ctx context.Context) (User, error) {
@@ -39,6 +41,22 @@ func (u *User) GetUserFromSession(Session *scs.SessionManager, ctx context.Conte
return user, nil
}
// Returns the actual overtime for this moment
func (u *User) GetReportedOvertime() (time.Duration, error) {
var overtime time.Duration
qStr, err := DB.Prepare("SELECT COALESCE(SUM(EXTRACT(EPOCH FROM ueberstunden) * 1000000000)::BIGINT, 0) AS total_ueberstunden_ns FROM wochen_report WHERE personal_nummer = $1;")
if err != nil {
return 0, err
}
defer qStr.Close()
err = qStr.QueryRow(u.PersonalNummer).Scan(&overtime)
if err != nil {
return 0, err
}
return overtime, nil
}
func (u *User) GetAll() ([]User, error) {
qStr, err := DB.Prepare((`SELECT card_uid, vorname, nachname FROM s_personal_daten;`))
var users []User
@@ -86,10 +104,10 @@ func (u *User) CheckAnwesenheit() bool {
// Creates a new booking for the user -> check_in_out will be 254 for automatic check out
func (u *User) CheckOut() error {
booking := (*Booking).New(nil, u.CardUID, 0, 254)
booking := (*Booking).New(nil, u.CardUID, 0, 254, 1)
err := booking.Insert()
if err != nil {
fmt.Printf("Error inserting booking %v\n", err)
fmt.Printf("Error inserting booking %v -> %v\n", booking, err)
return err
}
return nil
@@ -98,11 +116,11 @@ func (u *User) CheckOut() error {
func GetUserByPersonalNr(personalNummer int) (User, error) {
var user User
qStr, err := DB.Prepare((`SELECT personal_nummer, card_uid, vorname, nachname, arbeitszeit_per_tag FROM s_personal_daten WHERE personal_nummer = $1;`))
qStr, err := DB.Prepare((`SELECT personal_nummer, card_uid, vorname, nachname, arbeitszeit_per_tag, arbeitszeit_per_woche FROM s_personal_daten WHERE personal_nummer = $1;`))
if err != nil {
return user, err
}
err = qStr.QueryRow(personalNummer).Scan(&user.PersonalNummer, &user.CardUID, &user.Vorname, &user.Name, &user.ArbeitszeitPerTag)
err = qStr.QueryRow(personalNummer).Scan(&user.PersonalNummer, &user.CardUID, &user.Vorname, &user.Name, &user.ArbeitszeitPerTag, &user.ArbeitszeitPerWoche)
if err != nil {
return user, err
@@ -146,7 +164,7 @@ func (u *User) ChangePass(password, newPassword string) (bool, error) {
func (u *User) GetTeamMembers() ([]User, error) {
var teamMembers []User
qStr, err := DB.Prepare(`SELECT personal_nummer, card_uid, vorname, nachname, arbeitszeit_per_tag FROM s_personal_daten WHERE vorgesetzter_pers_nr = $1`)
qStr, err := DB.Prepare(`SELECT personal_nummer FROM s_personal_daten WHERE vorgesetzter_pers_nr = $1`)
if err != nil {
return teamMembers, err
}
@@ -158,9 +176,11 @@ func (u *User) GetTeamMembers() ([]User, error) {
}
defer rows.Close()
for rows.Next() {
user, err := parseUser(rows)
var personalNr int
err := rows.Scan(&personalNr)
user, err := GetUserByPersonalNr(personalNr)
if err != nil {
log.Println("Error parsing user!")
log.Println("Error getting user!")
return teamMembers, err
}
teamMembers = append(teamMembers, user)
@@ -205,8 +225,8 @@ 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 {
// returns the start of the week, the last submission was made, submission == first booking or last send wochen_report to team leader
func (u *User) GetLastWorkWeekSubmission() time.Time {
var lastSub time.Time
qStr, err := DB.Prepare(`
SELECT COALESCE(
@@ -223,10 +243,8 @@ func (u *User) GetLastSubmission() time.Time {
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
}

View File

@@ -6,16 +6,20 @@ import (
"testing"
)
var testUser models.User = models.User{Vorname: "Kim", Name: "Mustermensch", PersonalNummer: 456, CardUID: "aaaa-aaaa", ArbeitszeitPerTag: 8}
var testUser models.User = models.User{Vorname: "Kim", Name: "Mustermensch", PersonalNummer: 456, CardUID: "aaaa-aaaa", ArbeitszeitPerTag: 8, ArbeitszeitPerWoche: 40}
func SetupUserFixture(t *testing.T, db models.IDatabase) {
t.Helper()
db.Exec(`INSERT INTO "s_personal_daten" ("personal_nummer", "aktiv_beschaeftigt", "vorname", "nachname", "geburtsdatum", "plz", "adresse", "geschlecht", "card_uid", "hauptbeschaeftigungs_ort", "arbeitszeit_per_tag", "arbeitszeit_min_start", "arbeitszeit_max_ende", "vorgesetzter_pers_nr") VALUES
(456, 't', 'Kim', 'Mustermensch', '2003-02-01', '08963', 'Altenburger Str. 44A', 1, 'aaaa-aaaa', 1, 8, '07:00:00', '20:00:00', 0);`)
_, err := db.Exec(`INSERT INTO s_personal_daten (personal_nummer, aktiv_beschaeftigt, vorname, nachname, geburtsdatum, plz, adresse, geschlecht, card_uid, hauptbeschaeftigungs_ort, arbeitszeit_per_tag, arbeitszeit_per_woche, arbeitszeit_min_start, arbeitszeit_max_ende, vorgesetzter_pers_nr) VALUES
(456, 't', 'Kim', 'Mustermensch', '2003-02-01', '08963', 'Altenburger Str. 44A', 1, 'aaaa-aaaa', 1, 8, 40, '07:00:00', '20:00:00', 0);`)
if err != nil {
t.Fatal("SetupUserFixture:", err)
}
}
func TestGetUserByPersonalNr(t *testing.T) {
tc := SetupDBFixture(t)
SetupUserFixture(t, tc.Database)
models.DB = tc.Database
@@ -44,12 +48,12 @@ func TestCheckAnwesenheit(t *testing.T) {
if actual = testUser.CheckAnwesenheit(); actual != false {
t.Errorf("Checkabwesenheit with no booking should be false but is %t", actual)
}
tc.Database.Exec("INSERT INTO anwesenheit (timestamp, card_uid, check_in_out, geraet_id) VALUES (NOW() - INTERVAL '2 minute', 'aaaa-aaaa', 1, 1);")
tc.Database.Exec("INSERT INTO anwesenheit (timestamp, card_uid, check_in_out, geraet_id, anwesenheit_typ) VALUES (NOW() - INTERVAL '2 minute', 'aaaa-aaaa', 1, 1, 1);")
if actual = testUser.CheckAnwesenheit(); actual != true {
t.Errorf("Checkabwesenheit with 'kommen' booking should be true but is %t", actual)
}
tc.Database.Exec("INSERT INTO anwesenheit (timestamp, card_uid, check_in_out, geraet_id) VALUES (NOW() - INTERVAL '1 minute', 'aaaa-aaaa', 2, 1);")
tc.Database.Exec("INSERT INTO anwesenheit (timestamp, card_uid, check_in_out, geraet_id, anwesenheit_typ) VALUES (NOW() - INTERVAL '1 minute', 'aaaa-aaaa', 2, 1, 1);")
if actual = testUser.CheckAnwesenheit(); actual != false {
t.Errorf("Checkabwesenheit with 'gehen' booking should be false but is %t", actual)
}

View File

@@ -81,7 +81,7 @@ func (d *WorkDay) GetWorkDays(card_uid string, tsFrom, tsTo time.Time) []WorkDay
LEFT JOIN ordered_bookings b ON d.work_date = b.work_date
LEFT JOIN abwesenheiten a ON d.work_date = a.work_date
GROUP BY d.work_date, a.abwesenheit_typ
ORDER BY d.work_date;`)
ORDER BY d.work_date ASC;`)
if err != nil {
log.Println("Error preparing SQL statement", err)
@@ -126,10 +126,12 @@ func (d *WorkDay) GetWorkDays(card_uid string, tsFrom, tsTo time.Time) []WorkDay
} else {
workDay.calcPauseTime()
}
if emptyDays || len(workDay.Bookings) > 0 || (workDay.Absence != Absence{}) {
if emptyDays && workDay.Day.Weekday() >= 1 && workDay.Day.Weekday() <= 5 {
workDays = append(workDays, workDay)
} else {
log.Println("no booking on day", workDay.Day.Format("02.01.2006"))
} else if len(workDay.Bookings) > 0 || (workDay.Absence != Absence{}) {
workDays = append(workDays, workDay)
// } else {
// log.Println("no booking on day", workDay.Day.Format("02.01.2006"))
}
}
if err = rows.Err(); err != nil {
@@ -201,6 +203,15 @@ func (d *WorkDay) GetWorkDayProgress(user User) uint8 {
}
func (d *WorkDay) CalcOvertime(user User) time.Duration {
overtime := d.workTime - time.Duration(user.ArbeitszeitPerTag*float32(time.Hour)).Round(time.Minute)
if helper.IsWeekend(d.Day) && len(d.Bookings) == 0 {
return 0
}
var overtime time.Duration
overtime = d.workTime - time.Duration(user.ArbeitszeitPerTag*float32(time.Hour)).Round(time.Minute)
// weekday is WE
if (d.Absence != Absence{}) {
overtime = 0
}
return overtime
}

View File

@@ -8,12 +8,19 @@ import (
"time"
)
// Workweeks are
type WorkWeek struct {
Id int
WorkDays []WorkDay
Absences []Absence
User User
WeekStart time.Time
WorkHours time.Duration
Worktime time.Duration
Overtime time.Duration
Status WeekStatus
overtimeDiff time.Duration
worktimeDiff time.Duration
}
type WeekStatus int8
@@ -22,59 +29,95 @@ const (
WeekStatusNone WeekStatus = iota
WeekStatusSent
WeekStatusAccepted
WeekStatusDifferences
)
func (w *WorkWeek) GetWeek(user User, tsMonday time.Time, populateDays bool) WorkWeek {
var week WorkWeek
if populateDays {
week.WorkDays = (*WorkDay).GetWorkDays(nil, user.CardUID, tsMonday, tsMonday.Add(7*24*time.Hour))
week.WorkHours = aggregateWorkTime(week.WorkDays)
func NewWorkWeek(user User, tsMonday time.Time, populate bool) WorkWeek {
var week WorkWeek = WorkWeek{
User: user,
WeekStart: tsMonday,
Status: WeekStatusNone,
}
if populate {
week.PopulateWithBookings(0, 0)
}
week.User = user
week.WeekStart = tsMonday
return week
}
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))
if absences, err := GetAbsencesByCardUID(w.User.CardUID, w.WeekStart, w.WeekStart.Add(7*24*time.Hour)); err == nil {
w.Absences = absences
} else {
log.Printf("Error populating absences in workWeek (%s): %v", w.WeekStart, err)
}
w.Worktime = w.aggregateWorkTime()
w.Overtime = w.Worktime - time.Duration(w.User.ArbeitszeitPerWoche*float32(time.Hour))
w.Worktime = w.Worktime.Round(time.Minute)
w.Overtime = w.Overtime.Round(time.Minute)
if overtime == 0 && worktime == 0 {
return
}
if overtime != w.Overtime || worktime != w.Worktime {
w.Status = WeekStatusDifferences
w.overtimeDiff = overtime
w.worktimeDiff = worktime
}
}
func (w *WorkWeek) CheckStatus() WeekStatus {
weekStatus := WeekStatusNone
if w.Status != WeekStatusNone {
return w.Status
}
if DB == nil {
log.Println("Cannot access Database!")
return w.Status
}
qStr, err := DB.Prepare(`SELECT bestaetigt FROM wochen_report WHERE woche_start = $1::DATE AND personal_nummer = $2;`)
if err != nil {
log.Println("Error preparing SQL statement", err)
return weekStatus
return w.Status
}
defer qStr.Close()
var beastatigt bool
err = qStr.QueryRow(w.WeekStart, w.User.PersonalNummer).Scan(&beastatigt)
if err == sql.ErrNoRows {
return weekStatus
return w.Status
}
if err != nil {
log.Println("Error querying database", err)
return weekStatus
return w.Status
}
if beastatigt {
weekStatus = WeekStatusAccepted
w.Status = WeekStatusAccepted
} else {
weekStatus = WeekStatusSent
w.Status = WeekStatusSent
}
return weekStatus
return w.Status
}
func (w *WorkWeek) GetWorkHourString() string {
return helper.FormatDuration(w.WorkHours)
return helper.FormatDuration(w.Worktime)
}
func aggregateWorkTime(days []WorkDay) time.Duration {
func (w *WorkWeek) aggregateWorkTime() time.Duration {
var workTime time.Duration
for _, day := range days {
for _, day := range w.WorkDays {
workTime += day.workTime
}
for _, absences := range w.Absences {
absenceWorkTime := absences.AbwesenheitTyp.WorkTime - (absences.AbwesenheitTyp.WorkTime - w.User.ArbeitszeitPerTag) // workTime Equivalent of Absence is capped at user Worktime per Day
workTime += time.Duration(absenceWorkTime * float32(time.Hour)).Round(time.Minute)
}
return workTime
}
func (w *WorkWeek) GetSendWeeks(user User) []WorkWeek {
var weeks []WorkWeek
qStr, err := DB.Prepare(`SELECT id, woche_start::DATE FROM wochen_report WHERE bestaetigt = FALSE AND personal_nummer = $1;`)
qStr, err := DB.Prepare(`SELECT id, woche_start::DATE, (EXTRACT(epoch FROM arbeitszeit)*1000000000)::BIGINT, (EXTRACT(epoch FROM ueberstunden)*1000000000)::BIGINT FROM wochen_report WHERE bestaetigt = FALSE AND personal_nummer = $1;`)
if err != nil {
log.Println("Error preparing SQL statement", err)
return weeks
@@ -88,14 +131,13 @@ func (w *WorkWeek) GetSendWeeks(user User) []WorkWeek {
}
defer rows.Close()
for rows.Next() {
var week WorkWeek
week.User = user
if err := rows.Scan(&week.Id, &week.WeekStart); err != nil {
var week WorkWeek = WorkWeek{User: user}
if err := rows.Scan(&week.Id, &week.WeekStart, &week.Worktime, &week.Overtime); 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)
week.PopulateWithBookings(week.Worktime, week.Overtime)
weeks = append(weeks, week)
}
if err = rows.Err(); err != nil {
@@ -108,33 +150,34 @@ func (w *WorkWeek) GetSendWeeks(user User) []WorkWeek {
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 {
func (w *WorkWeek) SendWeek() error {
var qStr *sql.Stmt
var err error
if time.Since(w.WeekStart) < 5*24*time.Hour {
log.Println("Cannot send week, because it's the running week!")
return ErrRunningWeek
}
if w.CheckStatus() != WeekStatusNone {
qStr, err = DB.Prepare(`UPDATE "wochen_report" SET bestaetigt = FALSE WHERE personal_nummer = $1 AND woche_start = $2;`)
qStr, err = DB.Prepare(`UPDATE "wochen_report" SET bestaetigt = FALSE, arbeitszeit = make_interval(secs => $3::numeric / 1000000000), ueberstunden = make_interval(secs => $4::numeric / 1000000000) WHERE personal_nummer = $1 AND woche_start = $2;`)
if err != nil {
log.Println("Error preparing SQL statement", err)
return err
}
} else {
qStr, err = DB.Prepare(`INSERT INTO wochen_report (personal_nummer, woche_start) VALUES ($1, $2);`)
qStr, err = DB.Prepare(`INSERT INTO wochen_report (personal_nummer, woche_start, arbeitszeit, ueberstunden) VALUES ($1, $2, make_interval(secs => $3::numeric / 1000000000), make_interval(secs => $4::numeric / 1000000000));`)
if err != nil {
log.Println("Error preparing SQL statement", err)
return err
}
}
_, err = qStr.Exec(w.User.PersonalNummer, w.WeekStart)
_, err = qStr.Exec(w.User.PersonalNummer, w.WeekStart, int64(w.Worktime), int64(w.Overtime))
if err != nil {
log.Println("Error executing query!", err)
return err
}
return nil
}
func (w *WorkWeek) Accept() error {

View File

@@ -0,0 +1,50 @@
package models_test
import (
"arbeitszeitmessung/models"
"testing"
"time"
)
func SetupWorkWeekFixture(t *testing.T) models.WorkWeek {
t.Helper()
monday, err := time.Parse("2006-01-02", "2025-01-10")
if err != nil {
t.Fatal(err)
}
return models.WorkWeek{User: testUser, WeekStart: monday, Status: models.WeekStatusSent}
}
func TestNewWorkWeekNoPopulate(t *testing.T) {
monday, err := time.Parse("2006-01-02", "2025-01-10")
if err != nil {
t.Fatal(err)
}
workWeek := models.NewWorkWeek(testUser, monday, false)
if workWeek.User != testUser || workWeek.WeekStart != monday {
t.Error("No populate workweek does not have right values!")
}
}
func TestCheckStatus(t *testing.T) {
testWeek := SetupWorkWeekFixture(t)
testCases := []struct {
name string
weekStatus models.WeekStatus
}{
{"State=None", models.WeekStatusNone},
{"State=Sent", models.WeekStatusSent},
{"State=Accepted", models.WeekStatusAccepted},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
testWeek.Status = tc.weekStatus
if testWeek.CheckStatus() != tc.weekStatus {
t.Error("WorkWeek Status missmatch!")
}
})
}
}

View File

@@ -1,5 +1,8 @@
@import "tailwindcss";
@source "../templates/*.templ";
@plugin "@iconify/tailwind4" {
scale: 1.5;
}
@theme {
--color-accent-50: #e7fdea;
@@ -42,6 +45,11 @@
transition: background-color 0.2s ease-in-out;
}
.grid-sub.responsive {
display: flex;
flex-direction: column;
}
.grid-sub:hover {
background-color: var(--color-neutral-200);
}
@@ -51,13 +59,49 @@
border-color: var(--color-neutral-400);
}
.btn {
width: 100%;
cursor: pointer;
border-radius: var(--radius-md);
color: var(--color-neutral-800);
font-size: var(--text-sm);
text-align: center;
padding: calc(var(--spacing) * 2);
border-style: var(--tw-border-style);
border-width: 1px;
border-color: var(--color-neutral-800);
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
.btn:hover {
color: var(--color-white);
background-color: var(--color-neutral-700);
}
.btn:disabled {
opacity: 50%;
pointer-events: none;
}
.btn:active,
.btn:focus {
background-color: var(--color-neutral-700);
}
@media (width >=48rem) {
.grid-main {
grid-template-columns: repeat(5, 1fr);
margin: 0 10%;
}
.grid-sub {
.grid-sub.responsive {
display: grid;
}
.btn {
padding-inline: calc(var(--spacing) * 4);
}
}
}

View File

@@ -26,6 +26,8 @@
--text-sm--line-height: calc(1.25 / 0.875);
--text-xl: 1.25rem;
--text-xl--line-height: calc(1.75 / 1.25);
--text-2xl: 1.5rem;
--text-2xl--line-height: calc(2 / 1.5);
--font-weight-bold: 700;
--radius-md: 0.375rem;
--default-transition-duration: 150ms;
@@ -184,12 +186,18 @@
}
}
@layer utilities {
.\@container {
container-type: inline-size;
}
.col-span-2 {
grid-column: span 2 / span 2;
}
.col-span-3 {
grid-column: span 3 / span 3;
}
.col-span-full {
grid-column: 1 / -1;
}
.mx-auto {
margin-inline: auto;
}
@@ -202,9 +210,77 @@
.mb-2 {
margin-bottom: calc(var(--spacing) * 2);
}
.icon-\[material-symbols-light--add-circle-outline\] {
display: inline-block;
width: 1.5em;
height: 1.5em;
background-color: currentColor;
-webkit-mask-image: var(--svg);
mask-image: var(--svg);
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='black' d='M11.5 16.5h1v-4h4v-1h-4v-4h-1v4h-4v1h4zm.503 4.5q-1.867 0-3.51-.708q-1.643-.709-2.859-1.924t-1.925-2.856T3 12.003t.709-3.51Q4.417 6.85 5.63 5.634t2.857-1.925T11.997 3t3.51.709q1.643.708 2.859 1.922t1.925 2.857t.709 3.509t-.708 3.51t-1.924 2.859t-2.856 1.925t-3.509.709M12 20q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4T6.325 6.325T4 12t2.325 5.675T12 20m0-8'/%3E%3C/svg%3E");
}
.icon-\[material-symbols-light--check-circle-outline\] {
display: inline-block;
width: 1.5em;
height: 1.5em;
background-color: currentColor;
-webkit-mask-image: var(--svg);
mask-image: var(--svg);
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='black' d='m10.562 15.908l6.396-6.396l-.708-.708l-5.688 5.688l-2.85-2.85l-.708.708zM12.003 21q-1.866 0-3.51-.708q-1.643-.709-2.859-1.924t-1.925-2.856T3 12.003t.709-3.51Q4.417 6.85 5.63 5.634t2.857-1.925T11.997 3t3.51.709q1.643.708 2.859 1.922t1.925 2.857t.709 3.509t-.708 3.51t-1.924 2.859t-2.856 1.925t-3.509.709M12 20q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4T6.325 6.325T4 12t2.325 5.675T12 20m0-8'/%3E%3C/svg%3E");
}
.icon-\[material-symbols-light--circle-outline\] {
display: inline-block;
width: 1.5em;
height: 1.5em;
background-color: currentColor;
-webkit-mask-image: var(--svg);
mask-image: var(--svg);
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='black' d='M12.003 21q-1.866 0-3.51-.708q-1.643-.709-2.859-1.924t-1.925-2.856T3 12.003t.709-3.51Q4.417 6.85 5.63 5.634t2.857-1.925T11.997 3t3.51.709q1.643.708 2.859 1.922t1.925 2.857t.709 3.509t-.708 3.51t-1.924 2.859t-2.856 1.925t-3.509.709M12 20q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4T6.325 6.325T4 12t2.325 5.675T12 20m0-8'/%3E%3C/svg%3E");
}
.icon-\[material-symbols-light--more-time\] {
display: inline-block;
width: 1.5em;
height: 1.5em;
background-color: currentColor;
-webkit-mask-image: var(--svg);
mask-image: var(--svg);
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='black' d='M11.003 20q-1.666 0-3.123-.622t-2.545-1.71t-1.712-2.544T3 12.003t.622-3.123t1.711-2.546q1.09-1.089 2.545-1.711T11 4q.525 0 1.013.063T13 4.25V5.3q-.5-.15-.987-.225T11 5Q8.089 5 6.044 7.044T4 12t2.044 4.956T11 19t4.956-2.044T18 11.996q0-.271-.025-.554t-.094-.557h1.011q.05.236.08.538q.028.302.028.577q0 1.667-.622 3.122t-1.71 2.545q-1.089 1.088-2.544 1.71q-1.455.623-3.121.623m3.143-4.146L10.5 12.208V7h1v4.792l3.354 3.354zM18 8.884v-3h-3v-1h3v-3h1v3h3v1h-3v3z'/%3E%3C/svg%3E");
}
.icon-\[material-symbols-light--motion-photos-paused-outline\] {
display: inline-block;
width: 1.5em;
height: 1.5em;
background-color: currentColor;
-webkit-mask-image: var(--svg);
mask-image: var(--svg);
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='black' d='M9.808 14.616h1V9.385h-1zm3.384 0h1V9.385h-1zM12.003 21q-1.866 0-3.51-.705q-1.643-.706-2.859-1.915t-1.925-2.843T3 12.039q0-.905.167-1.778t.497-1.713l.78.78q-.219.65-.331 1.32T4 12q0 3.35 2.325 5.675T12 20t5.675-2.325T20 12t-2.325-5.675T12 4q-.675 0-1.332.112t-1.3.332l-.776-.775q.789-.315 1.606-.492T11.885 3q1.887 0 3.546.701t2.894 1.926t1.955 2.866t.72 3.505t-.708 3.509t-1.924 2.859t-2.856 1.925t-3.509.709M5.923 6.808q-.356 0-.62-.265q-.264-.264-.264-.62t.264-.62t.62-.264t.62.264t.265.62t-.265.62t-.62.265M12 12'/%3E%3C/svg%3E");
}
.flex {
display: flex;
}
.grid {
display: grid;
}
.hidden {
display: none;
}
@@ -222,6 +298,9 @@
width: calc(var(--spacing) * 4);
height: calc(var(--spacing) * 4);
}
.h-2 {
height: calc(var(--spacing) * 2);
}
.h-4 {
height: calc(var(--spacing) * 4);
}
@@ -234,24 +313,33 @@
.h-full {
height: 100%;
}
.w-1\/3 {
width: calc(1/3 * 100%);
}
.w-2 {
width: calc(var(--spacing) * 2);
}
.w-4 {
width: calc(var(--spacing) * 4);
}
.w-9 {
width: calc(var(--spacing) * 9);
}
.w-9\/10 {
width: calc(9/10 * 100%);
}
.w-\[2px\] {
width: 2px;
}
.w-auto {
width: auto;
}
.w-full {
width: 100%;
}
.flex-shrink {
flex-shrink: 1;
}
.flex-shrink-0 {
flex-shrink: 0;
}
.flex-grow {
flex-grow: 1;
}
@@ -261,18 +349,30 @@
.grow-1 {
flex-grow: 1;
}
.border-collapse {
border-collapse: collapse;
}
.cursor-pointer {
cursor: pointer;
}
.resize {
resize: both;
}
.grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.grid-cols-5 {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.flex-col {
flex-direction: column;
}
.flex-row {
flex-direction: row;
}
.content-baseline {
align-content: baseline;
}
.content-end {
align-content: flex-end;
}
@@ -314,11 +414,6 @@
.justify-self-end {
justify-self: flex-end;
}
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.overflow-hidden {
overflow: hidden;
}
@@ -332,15 +427,16 @@
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);
}
.border-neutral-300 {
border-color: var(--color-neutral-300);
}
.border-neutral-800 {
border-color: var(--color-neutral-800);
}
.border-neutral-900 {
border-color: var(--color-neutral-900);
}
@@ -356,6 +452,9 @@
.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);
}
@@ -365,6 +464,9 @@
.bg-red-600 {
background-color: var(--color-red-600);
}
.mask-repeat {
mask-repeat: repeat;
}
.p-1 {
padding: calc(var(--spacing) * 1);
}
@@ -377,9 +479,16 @@
.py-2 {
padding-block: calc(var(--spacing) * 2);
}
.py-4 {
padding-block: calc(var(--spacing) * 4);
}
.text-center {
text-align: center;
}
.text-2xl {
font-size: var(--text-2xl);
line-height: var(--tw-leading, var(--text-2xl--line-height));
}
.text-sm {
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
@@ -416,6 +525,13 @@
.uppercase {
text-transform: uppercase;
}
.underline {
text-decoration-line: underline;
}
.outline {
outline-style: var(--tw-outline-style);
outline-width: 1px;
}
.filter {
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
}
@@ -500,6 +616,13 @@
}
}
}
.hover\:bg-red-600 {
&:hover {
@media (hover: hover) {
background-color: var(--color-red-600);
}
}
}
.hover\:text-accent {
&:hover {
@media (hover: hover) {
@@ -555,11 +678,44 @@
display: grid;
}
}
.max-md\:hidden {
@media (width < 48rem) {
display: none;
}
}
.max-md\:flex-col {
@media (width < 48rem) {
flex-direction: column;
}
}
.max-md\:divide-y-1 {
@media (width < 48rem) {
:where(& > :not(:last-child)) {
--tw-divide-y-reverse: 0;
border-bottom-style: var(--tw-border-style);
border-top-style: var(--tw-border-style);
border-top-width: calc(1px * var(--tw-divide-y-reverse));
border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
}
}
}
.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);
border-bottom-width: 1px;
}
}
.max-md\:bg-neutral-300 {
@media (width < 48rem) {
background-color: var(--color-neutral-300);
}
}
.md\:col-span-1 {
@media (width >= 48rem) {
grid-column: span 1 / span 1;
@@ -617,6 +773,42 @@
}
}
}
.lg\:hidden {
@media (width >= 64rem) {
display: none;
}
}
.lg\:grid-cols-1 {
@media (width >= 64rem) {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
}
.lg\:divide-x-1 {
@media (width >= 64rem) {
:where(& > :not(:last-child)) {
--tw-divide-x-reverse: 0;
border-inline-style: var(--tw-border-style);
border-inline-start-width: calc(1px * var(--tw-divide-x-reverse));
border-inline-end-width: calc(1px * calc(1 - var(--tw-divide-x-reverse)));
}
}
}
.lg\:border-0 {
@media (width >= 64rem) {
border-style: var(--tw-border-style);
border-width: 0px;
}
}
.\@7xl\:grid {
@container (width >= 80rem) {
display: grid;
}
}
.\@7xl\:grid-cols-5 {
@container (width >= 80rem) {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
}
.print\:hidden {
@media print {
display: none;
@@ -636,6 +828,10 @@
border-color: var(--color-neutral-400);
transition: background-color 0.2s ease-in-out;
}
.grid-sub.responsive {
display: flex;
flex-direction: column;
}
.grid-sub:hover {
background-color: var(--color-neutral-200);
}
@@ -643,11 +839,43 @@
padding: calc(var(--spacing) * 2);
border-color: var(--color-neutral-400);
}
.btn {
width: 100%;
cursor: pointer;
border-radius: var(--radius-md);
color: var(--color-neutral-800);
font-size: var(--text-sm);
text-align: center;
padding: calc(var(--spacing) * 2);
border-style: var(--tw-border-style);
border-width: 1px;
border-color: var(--color-neutral-800);
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
.btn:hover {
color: var(--color-white);
background-color: var(--color-neutral-700);
}
.btn:disabled {
opacity: 50%;
pointer-events: none;
}
.btn:active, .btn:focus {
background-color: var(--color-neutral-700);
}
@media (width >=48rem) {
.grid-main {
grid-template-columns: repeat(5, 1fr);
margin: 0 10%;
}
.grid-sub.responsive {
display: grid;
}
.btn {
padding-inline: calc(var(--spacing) * 4);
}
}
}
@property --tw-divide-x-reverse {
@@ -669,6 +897,11 @@
syntax: "*";
inherits: false;
}
@property --tw-outline-style {
syntax: "*";
inherits: false;
initial-value: solid;
}
@property --tw-blur {
syntax: "*";
inherits: false;
@@ -733,6 +966,7 @@
--tw-border-style: solid;
--tw-divide-y-reverse: 0;
--tw-font-weight: initial;
--tw-outline-style: solid;
--tw-blur: initial;
--tw-brightness: initial;
--tw-contrast: initial;

View File

@@ -1,6 +1,7 @@
package templates
import (
"arbeitszeitmessung/helper"
"arbeitszeitmessung/models"
"fmt"
"strconv"
@@ -17,27 +18,31 @@ templ Base() {
</head>
}
templ TimePage(workDays []models.WorkDay) {
templ TimePage(workDays []models.WorkDay, lastSub time.Time) {
@Base()
@headerComponent()
<div class="grid-main divide-y-1">
@inputForm()
for _, day := range workDays {
@dayComponent(day)
@dayComponent(day, day.Day.Before(lastSub))
if (day.Day.Weekday() == time.Monday) {
<div class="grid-sub responsive bg-neutral-300 h-2"></div>
}
}
</div>
@LegendComponent()
}
templ LoginPage(failed bool) {
templ LoginPage(success bool, errorMsg string) {
@Base()
<div class="w-full h-[100vh] flex flex-col justify-center items-center">
<form method="POST" class="w-9/10 md:w-1/2 flex flex-col gap-4 p-2 mb-2">
<h1 class="font-bold uppercase text-xl text-center mb-2">Benutzer Anmelden</h1>
<input name="personal_nummer" placeholder="Personalnummer" type="text" class="w-full placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-300 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none hover:border-neutral-500"/>
<input name="password" placeholder="Passwort" type="password" class="w-full placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-300 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none hover:border-neutral-500"/>
if failed {
if !success {
<p class="text-red-600 text-sm">Login fehlgeschlagen, bitte erneut versuchen!</p>
<p class="text-red-600 text-sm">{ errorMsg }</p>
}
<button type="submit" class="cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-300 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50">Login</button>
</form>
@@ -48,8 +53,7 @@ templ UserPage(status int) {
@Base()
@headerComponent()
<div class="grid-main divide-y-1">
<div class="grid-sub"></div>
<form method="POST" class="grid-sub divide-x-1">
<form method="POST" class="grid-sub responsive lg:divide-x-1">
<h1 class="grid-cell font-bold uppercase text-xl text-center">Passwort ändern</h1>
<div class="grid-cell col-span-3 flex flex-col gap-2">
<input name="password" placeholder="Aktuelles Passwort" type="password" class="w-full placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-300 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none hover:border-neutral-500"/>
@@ -65,103 +69,102 @@ templ UserPage(status int) {
}
</div>
<div class="grid-cell">
<button name="action" value="change-pass" type="submit" class="w-full cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-300 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50">Ändern</button>
<button name="action" value="change-pass" type="submit" class="btn">Ändern</button>
</div>
</form>
<div class="grid-sub divide-x-1">
<div class="grid-sub responsive lg:divide-x-1">
<h1 class="grid-cell font-bold uppercase text-xl text-center">Nutzer abmelden</h1>
<div class="grid-cell col-span-3">
<p>Nutzer von Weboberfläche abmelden.</p>
</div>
<div class="grid-cell">
<button onclick="logoutUser" type="button" class="w-full cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-300 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50">Abmelden</button>
<button onclick="logoutUser" type="button" class="btn">Abmelden</button>
</div>
</div>
</div>
}
templ statusCheckMark(status models.WeekStatus, target models.WeekStatus) {
if status >= target {
<div class="icon-[material-symbols-light--check-circle-outline]"></div>
} else {
<div class="icon-[material-symbols-light--circle-outline]"></div>
}
}
templ TeamPage(weeks []models.WorkWeek, userWeek models.WorkWeek) {
{{
year, kw := userWeek.WeekStart.ISOWeek()
}}
@Base()
@headerComponent()
{{
progress := (float32(userWeek.Worktime.Hours()) / userWeek.User.ArbeitszeitPerWoche) * 100
}}
<div class="grid-main divide-y-1">
<div class="grid-sub divide-x-1 bg-neutral-300">
<div class="grid-cell font-bold uppercase">
{ fmt.Sprintf("%s %s", userWeek.User.Vorname, userWeek.User.Name) }
<div class="grid-sub lg:divide-x-1 max-md:divide-y-1 responsive @container">
<div class="grid-cell col-span-full bg-neutral-300 lg:border-0">
<h2 class="text-2xl uppercase font-bold">Eigene Abrechnung</h2>
</div>
<div class="grid-cell col-span-3 flex flex-col gap-2">
<div class="grid-cell flex flex-col max-md:border-b-1 max-md:bg-neutral-300 gap-2 ">
<div class="lg:hidden">
@weekPicker(userWeek.WeekStart)
</div>
<h2 class="uppercase font-bold">{ fmt.Sprintf("%s %s", userWeek.User.Vorname, userWeek.User.Name) }</h2>
<div class="grid grid-cols-5 gap-2 lg:grid-cols-1">
<div class="col-span-2">
<span class="flex flex-row gap-2 items-center">
@statusCheckMark(userWeek.CheckStatus(), models.WeekStatusSent)
Gesendet
</span>
<span class="flex flex-row gap-2 items-center">
@statusCheckMark(userWeek.CheckStatus(), models.WeekStatusAccepted)
Akzeptiert
</span>
</div>
<div class="flex flex-row gap-2 col-span-3">
@timeGaugeComponent(uint8(progress), false, false)
<div>
<p>Arbeitszeit: { fmt.Sprintf("%s", helper.FormatDuration(userWeek.Worktime)) }</p>
<p>Überstunden: { fmt.Sprintf("%s", helper.FormatDuration(userWeek.Overtime)) }</p>
</div>
</div>
</div>
</div>
<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 userWeek.WorkDays {
@weekDayComponent(userWeek.User, day)
}
</div>
<div class="grid-cell flex flex-col gap-2">
<form method="get" class="flex flex-row gap-4 items-center justify-around">
<input type="date" class="hidden" name="submission_date" value={ userWeek.WeekStart.Format(time.DateOnly) }/>
<button onclick={ templ.JSFuncCall("navigateWeek", templ.JSExpression("this"), templ.JSExpression("event"), "-1") } class="p-2 w-1/3 cursor-pointer rounded-md text-neutral-800 border text-center text-sm hover:text-white transition-colors border-neutral-800 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="chevron-left size-4 mx-auto" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0"></path>
</svg>
</button>
<p class="whitespace-nowrap">KW { fmt.Sprintf("%02d, %d", kw, year) }</p>
<button disabled?={ time.Since(userWeek.WeekStart) < 24*7*time.Hour } onclick={ templ.JSFuncCall("navigateWeek", templ.JSExpression("this"), templ.JSExpression("event"), "1") } class="p-2 w-1/3 cursor-pointer rounded-md text-neutral-800 border text-center text-sm hover:text-white transition-colors border-neutral-800 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="chevron-right size-4 mx-auto" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708"></path>
</svg>
</button>
</form>
<form method="post">
<div class="grid-cell flex flex-col gap-2 justify-between">
<div class="max-md:hidden">
@weekPicker(userWeek.WeekStart)
</div>
<form method="post" class="flex flex-col gap-2">
<input type="hidden" name="method" value="send"/>
<input type="hidden" name="user" value={ strconv.Itoa(userWeek.User.PersonalNummer) }/>
<input type="hidden" name="week" value={ userWeek.WeekStart.Format(time.DateOnly) }/>
switch userWeek.CheckStatus() {
case models.WeekStatusNone:
<p class="text-sm">an Vorgesetzten senden</p>
<button disabled?={ time.Since(userWeek.WeekStart) < 24*7*time.Hour } type="submit" class="w-full cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-800 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50">Senden</button>
if time.Since(userWeek.WeekStart) < 24*7*time.Hour {
<p class="text-sm text-red-500">Die Woche kann erst am nächsten Montag abgesendet werden!</p>
}
case models.WeekStatusSent:
<p class="text-sm">an Vorgesetzten gesendet</p>
<button type="submit" class="w-full cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-800 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50">Korrigieren</button>
<p class="flex flex-row gap-2 items-center">
akzeptiert:
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-circle" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"></path>
</svg>
</p>
case models.WeekStatusAccepted:
<p class="text-sm">vom Vorgesetzten bestätigt</p>
<button type="submit" class="w-full cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-800 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50">Korrigieren</button>
<p class="flex flex-row gap-2 text-accent items-center">
akzeptiert:
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check-circle" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"></path>
<path d="m10.97 4.97-.02.022-3.473 4.425-2.093-2.094a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-1.071-1.05"></path>
</svg>
</p>
}
<button disabled?={ userWeek.Status < models.WeekStatusSent } type="submit" class="btn">Korrigieren</button>
<button disabled?={ time.Since(userWeek.WeekStart) < 24*7*time.Hour || userWeek.Status >= models.WeekStatusSent } type="submit" class="btn">Senden</button>
</form>
</div>
</div>
if len(weeks) > 0 {
<div class="grid-cell col-span-full bg-neutral-300">
<h2 class="text-2xl uppercase font-bold">Abrechnung Mitarbeiter</h2>
</div>
}
for _, week := range weeks {
@employeComponent(week)
}
</div>
}
templ NavPage() {
@Base()
<div class="w-full h-[100vh] flex flex-col justify-center items-center">
<div class="flex flex-col justify-between w-full md:w-1/2 py-2">
<a class="text-xl hover:text-accent transition-colors1" href="/time">Zeitverwaltung</a>
<a class="text-xl hover:text-accent transition-colors1" href="/team">Mitarbeiter</a>
<a class="text-xl hover:text-accent transition-colors1" href="/user">Nutzer</a>
</div>
</div>
}
templ TeamPresencePage(teamPresence map[bool][]models.User) {
@Base()
@headerComponent()
@@ -186,5 +189,5 @@ templ TeamPresencePage(teamPresence map[bool][]models.User) {
}
templ LogoutButton() {
<button onclick="logoutUser()" type="button" class="cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-300 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50">Abmelden</button>
<button onclick="logoutUser()" type="button" class="cursor-pointer">Abmelden</button>
}

View File

@@ -9,6 +9,7 @@ import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"arbeitszeitmessung/helper"
"arbeitszeitmessung/models"
"fmt"
"strconv"
@@ -44,7 +45,7 @@ func Base() templ.Component {
})
}
func TimePage(workDays []models.WorkDay) templ.Component {
func TimePage(workDays []models.WorkDay, lastSub 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 {
@@ -82,12 +83,22 @@ func TimePage(workDays []models.WorkDay) templ.Component {
return templ_7745c5c3_Err
}
for _, day := range workDays {
templ_7745c5c3_Err = dayComponent(day).Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = dayComponent(day, day.Day.Before(lastSub)).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if day.Day.Weekday() == time.Monday {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"grid-sub responsive bg-neutral-300 h-2\"></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div>")
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -99,7 +110,7 @@ func TimePage(workDays []models.WorkDay) templ.Component {
})
}
func LoginPage(failed bool) templ.Component {
func LoginPage(success bool, errorMsg string) 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 {
@@ -124,17 +135,30 @@ func LoginPage(failed bool) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"w-full h-[100vh] flex flex-col justify-center items-center\"><form method=\"POST\" class=\"w-9/10 md:w-1/2 flex flex-col gap-4 p-2 mb-2\"><h1 class=\"font-bold uppercase text-xl text-center mb-2\">Benutzer Anmelden</h1><input name=\"personal_nummer\" placeholder=\"Personalnummer\" type=\"text\" class=\"w-full placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-300 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none hover:border-neutral-500\"> <input name=\"password\" placeholder=\"Passwort\" type=\"password\" class=\"w-full placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-300 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none hover:border-neutral-500\"> ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<div class=\"w-full h-[100vh] flex flex-col justify-center items-center\"><form method=\"POST\" class=\"w-9/10 md:w-1/2 flex flex-col gap-4 p-2 mb-2\"><h1 class=\"font-bold uppercase text-xl text-center mb-2\">Benutzer Anmelden</h1><input name=\"personal_nummer\" placeholder=\"Personalnummer\" type=\"text\" class=\"w-full placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-300 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none hover:border-neutral-500\"> <input name=\"password\" placeholder=\"Passwort\" type=\"password\" class=\"w-full placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-300 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none hover:border-neutral-500\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if failed {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<p class=\"text-red-600 text-sm\">Login fehlgeschlagen, bitte erneut versuchen!</p>")
if !success {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<p class=\"text-red-600 text-sm\">Login fehlgeschlagen, bitte erneut versuchen!</p><p class=\"text-red-600 text-sm\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(errorMsg)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pages.templ`, Line: 45, Col: 46}
}
_, 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, 8, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<button type=\"submit\" class=\"cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-300 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50\">Login</button></form></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<button type=\"submit\" class=\"cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-300 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50\">Login</button></form></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -158,9 +182,9 @@ func UserPage(status int) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var4 := templ.GetChildren(ctx)
if templ_7745c5c3_Var4 == nil {
templ_7745c5c3_Var4 = templ.NopComponent
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
if templ_7745c5c3_Var5 == nil {
templ_7745c5c3_Var5 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = Base().Render(ctx, templ_7745c5c3_Buffer)
@@ -171,28 +195,28 @@ func UserPage(status int) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"grid-main divide-y-1\"><div class=\"grid-sub\"></div><form method=\"POST\" class=\"grid-sub divide-x-1\"><h1 class=\"grid-cell font-bold uppercase text-xl text-center\">Passwort ändern</h1><div class=\"grid-cell col-span-3 flex flex-col gap-2\"><input name=\"password\" placeholder=\"Aktuelles Passwort\" type=\"password\" class=\"w-full placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-300 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none hover:border-neutral-500\"> <input name=\"new_password\" placeholder=\"Neues Passwort\" type=\"password\" class=\"w-full placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-300 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none hover:border-neutral-500\"> <input name=\"new_password_repeat\" placeholder=\"Neues Passwort wiederholen\" type=\"password\" class=\"w-full placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-300 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none hover:border-neutral-500\"> ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<div class=\"grid-main divide-y-1\"><form method=\"POST\" class=\"grid-sub responsive lg:divide-x-1\"><h1 class=\"grid-cell font-bold uppercase text-xl text-center\">Passwort ändern</h1><div class=\"grid-cell col-span-3 flex flex-col gap-2\"><input name=\"password\" placeholder=\"Aktuelles Passwort\" type=\"password\" class=\"w-full placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-300 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none hover:border-neutral-500\"> <input name=\"new_password\" placeholder=\"Neues Passwort\" type=\"password\" class=\"w-full placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-300 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none hover:border-neutral-500\"> <input name=\"new_password_repeat\" placeholder=\"Neues Passwort wiederholen\" type=\"password\" class=\"w-full placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-300 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none hover:border-neutral-500\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
switch {
case status == 401:
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<p class=\"text-red-600 text-sm\">Aktuelles Passwort nicht korrekt!</p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<p class=\"text-red-600 text-sm\">Aktuelles Passwort nicht korrekt!</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
case status >= 400:
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<p class=\"text-red-600 text-sm\">Passwortwechsel fehlgeschlagen, bitte erneut versuchen!</p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<p class=\"text-red-600 text-sm\">Passwortwechsel fehlgeschlagen, bitte erneut versuchen!</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
case status == 202:
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<p class=\"text-accent text-sm\">Passwortänderung erfolgreich</p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<p class=\"text-accent text-sm\">Passwortänderung erfolgreich</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</div><div class=\"grid-cell\"><button name=\"action\" value=\"change-pass\" type=\"submit\" class=\"w-full cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-300 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50\">Ändern</button></div></form><div class=\"grid-sub divide-x-1\"><h1 class=\"grid-cell font-bold uppercase text-xl text-center\">Nutzer abmelden</h1><div class=\"grid-cell col-span-3\"><p>Nutzer von Weboberfläche abmelden.</p></div><div class=\"grid-cell\"><button onclick=\"logoutUser\" type=\"button\" class=\"w-full cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-300 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50\">Abmelden</button></div></div></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</div><div class=\"grid-cell\"><button name=\"action\" value=\"change-pass\" type=\"submit\" class=\"btn\">Ändern</button></div></form><div class=\"grid-sub responsive lg:divide-x-1\"><h1 class=\"grid-cell font-bold uppercase text-xl text-center\">Nutzer abmelden</h1><div class=\"grid-cell col-span-3\"><p>Nutzer von Weboberfläche abmelden.</p></div><div class=\"grid-cell\"><button onclick=\"logoutUser\" type=\"button\" class=\"btn\">Abmelden</button></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -200,6 +224,42 @@ func UserPage(status int) templ.Component {
})
}
func statusCheckMark(status models.WeekStatus, target models.WeekStatus) 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_Var6 := templ.GetChildren(ctx)
if templ_7745c5c3_Var6 == nil {
templ_7745c5c3_Var6 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
if status >= target {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<div class=\"icon-[material-symbols-light--check-circle-outline]\"></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<div class=\"icon-[material-symbols-light--circle-outline]\"></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return nil
})
}
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
@@ -216,13 +276,11 @@ func TeamPage(weeks []models.WorkWeek, userWeek models.WorkWeek) templ.Component
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
if templ_7745c5c3_Var5 == nil {
templ_7745c5c3_Var5 = templ.NopComponent
templ_7745c5c3_Var7 := templ.GetChildren(ctx)
if templ_7745c5c3_Var7 == nil {
templ_7745c5c3_Var7 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
year, kw := userWeek.WeekStart.ISOWeek()
templ_7745c5c3_Err = Base().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
@@ -231,20 +289,80 @@ func TeamPage(weeks []models.WorkWeek, userWeek models.WorkWeek) templ.Component
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"grid-main divide-y-1\"><div class=\"grid-sub divide-x-1 bg-neutral-300\"><div class=\"grid-cell font-bold uppercase\">")
progress := (float32(userWeek.Worktime.Hours()) / userWeek.User.ArbeitszeitPerWoche) * 100
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<div class=\"grid-main divide-y-1\"><div class=\"grid-sub lg:divide-x-1 max-md:divide-y-1 responsive @container\"><div class=\"grid-cell col-span-full bg-neutral-300 lg:border-0\"><h2 class=\"text-2xl uppercase font-bold\">Eigene Abrechnung</h2></div><div class=\"grid-cell flex flex-col max-md:border-b-1 max-md:bg-neutral-300 gap-2 \"><div class=\"lg:hidden\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
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: 92, Col: 69}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
templ_7745c5c3_Err = weekPicker(userWeek.WeekStart).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</div><div class=\"grid-cell col-span-3 flex flex-col gap-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</div><h2 class=\"uppercase font-bold\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, 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: 110, Col: 101}
}
_, 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, 19, "</h2><div class=\"grid grid-cols-5 gap-2 lg:grid-cols-1\"><div class=\"col-span-2\"><span class=\"flex flex-row gap-2 items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = statusCheckMark(userWeek.CheckStatus(), models.WeekStatusSent).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "Gesendet</span> <span class=\"flex flex-row gap-2 items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = statusCheckMark(userWeek.CheckStatus(), models.WeekStatusAccepted).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "Akzeptiert</span></div><div class=\"flex flex-row gap-2 col-span-3\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = timeGaugeComponent(uint8(progress), false, false).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<div><p>Arbeitszeit: ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s", helper.FormatDuration(userWeek.Worktime)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pages.templ`, Line: 125, Col: 84}
}
_, 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, 23, "</p><p>Überstunden: ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s", helper.FormatDuration(userWeek.Overtime)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pages.templ`, Line: 126, Col: 85}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</p></div></div></div></div><div class=\"grid-cell col-span-3 flex flex-col @7xl:grid @7xl:grid-cols-5 gap-2 py-4 content-baseline\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -254,150 +372,98 @@ func TeamPage(weeks []models.WorkWeek, userWeek models.WorkWeek) templ.Component
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</div><div class=\"grid-cell flex flex-col gap-2\"><form method=\"get\" class=\"flex flex-row gap-4 items-center justify-around\"><input type=\"date\" class=\"hidden\" name=\"submission_date\" value=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</div><div class=\"grid-cell flex flex-col gap-2 justify-between\"><div class=\"max-md:hidden\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(userWeek.WeekStart.Format(time.DateOnly))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pages.templ`, Line: 101, Col: 110}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
templ_7745c5c3_Err = weekPicker(userWeek.WeekStart).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.JSFuncCall("navigateWeek", templ.JSExpression("this"), templ.JSExpression("event"), "-1"))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<button onclick=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 templ.ComponentScript = templ.JSFuncCall("navigateWeek", templ.JSExpression("this"), templ.JSExpression("event"), "-1")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" class=\"p-2 w-1/3 cursor-pointer rounded-md text-neutral-800 border text-center text-sm hover:text-white transition-colors border-neutral-800 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700\"><svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" class=\"chevron-left size-4 mx-auto\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0\"></path></svg></button><p class=\"whitespace-nowrap\">KW ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%02d, %d", kw, year))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pages.templ`, Line: 107, Col: 72}
}
_, 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, 18, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.JSFuncCall("navigateWeek", templ.JSExpression("this"), templ.JSExpression("event"), "1"))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<button")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if time.Since(userWeek.WeekStart) < 24*7*time.Hour {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, " disabled")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, " onclick=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 templ.ComponentScript = templ.JSFuncCall("navigateWeek", templ.JSExpression("this"), templ.JSExpression("event"), "1")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" class=\"p-2 w-1/3 cursor-pointer rounded-md text-neutral-800 border text-center text-sm hover:text-white transition-colors border-neutral-800 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50\"><svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" class=\"chevron-right size-4 mx-auto\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708\"></path></svg></button></form><form method=\"post\"><input type=\"hidden\" name=\"method\" value=\"send\"> <input type=\"hidden\" name=\"user\" value=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</div><form method=\"post\" class=\"flex flex-col gap-2\"><input type=\"hidden\" name=\"method\" value=\"send\"> <input type=\"hidden\" name=\"user\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(userWeek.User.PersonalNummer))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pages.templ`, Line: 116, Col: 88}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pages.templ`, Line: 142, Col: 88}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\"> <input type=\"hidden\" name=\"week\" value=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\"> <input type=\"hidden\" name=\"week\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(userWeek.WeekStart.Format(time.DateOnly))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pages.templ`, Line: 117, Col: 86}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pages.templ`, Line: 143, Col: 86}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\"> ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
switch userWeek.CheckStatus() {
case models.WeekStatusNone:
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<p class=\"text-sm\">an Vorgesetzten senden</p><button")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<p class=\"text-sm\">an Vorgesetzten senden</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if time.Since(userWeek.WeekStart) < 24*7*time.Hour {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, " disabled")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, " type=\"submit\" class=\"w-full cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-800 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50\">Senden</button> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if time.Since(userWeek.WeekStart) < 24*7*time.Hour {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<p class=\"text-sm text-red-500\">Die Woche kann erst am nächsten Montag abgesendet werden!</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
case models.WeekStatusSent:
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<p class=\"text-sm\">an Vorgesetzten gesendet</p><button type=\"submit\" class=\"w-full cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-800 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50\">Korrigieren</button><p class=\"flex flex-row gap-2 items-center\">akzeptiert: <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-circle\" viewBox=\"0 0 16 16\"><path d=\"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16\"></path></svg></p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<p class=\"text-sm\">an Vorgesetzten gesendet</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
case models.WeekStatusAccepted:
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<p class=\"text-sm\">vom Vorgesetzten bestätigt</p><button type=\"submit\" class=\"w-full cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-800 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50\">Korrigieren</button><p class=\"flex flex-row gap-2 text-accent items-center\">akzeptiert: <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-check-circle\" viewBox=\"0 0 16 16\"><path d=\"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16\"></path> <path d=\"m10.97 4.97-.02.022-3.473 4.425-2.093-2.094a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-1.071-1.05\"></path></svg></p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<p class=\"text-sm\">vom Vorgesetzten bestätigt</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</form></div></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<button")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if userWeek.Status < models.WeekStatusSent {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, " disabled")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, " type=\"submit\" class=\"btn\">Korrigieren</button> <button")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if time.Since(userWeek.WeekStart) < 24*7*time.Hour || userWeek.Status >= models.WeekStatusSent {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, " disabled")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, " type=\"submit\" class=\"btn\">Senden</button></form></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(weeks) > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div class=\"grid-cell col-span-full bg-neutral-300\"><h2 class=\"text-2xl uppercase font-bold\">Abrechnung Mitarbeiter</h2></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
for _, week := range weeks {
templ_7745c5c3_Err = employeComponent(week).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -405,7 +471,7 @@ func TeamPage(weeks []models.WorkWeek, userWeek models.WorkWeek) templ.Component
})
}
func NavPage() templ.Component {
func TeamPresencePage(teamPresence map[bool][]models.User) 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 {
@@ -430,44 +496,11 @@ func NavPage() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<div class=\"w-full h-[100vh] flex flex-col justify-center items-center\"><div class=\"flex flex-col justify-between w-full md:w-1/2 py-2\"><a class=\"text-xl hover:text-accent transition-colors1\" href=\"/time\">Zeitverwaltung</a> <a class=\"text-xl hover:text-accent transition-colors1\" href=\"/team\">Mitarbeiter</a> <a class=\"text-xl hover:text-accent transition-colors1\" href=\"/user\">Nutzer</a></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func TeamPresencePage(teamPresence map[bool][]models.User) 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_Var14 := templ.GetChildren(ctx)
if templ_7745c5c3_Var14 == nil {
templ_7745c5c3_Var14 = 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 = headerComponent().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<div class=\"grid-main divide-y-1\"><div class=\"grid-sub divide-x-1\"><h2 class=\"grid-cell font-bold uppercase\">Anwesend</h2><div class=\"flex flex-col col-span-2 md:col-span-4\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<div class=\"grid-main divide-y-1\"><div class=\"grid-sub divide-x-1\"><h2 class=\"grid-cell font-bold uppercase\">Anwesend</h2><div class=\"flex flex-col col-span-2 md:col-span-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -477,7 +510,7 @@ func TeamPresencePage(teamPresence map[bool][]models.User) templ.Component {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</div></div><div class=\"grid-sub divide-x-1\"><h2 class=\"grid-cell font-bold uppercase\">Nicht Anwesend</h2><div class=\"flex flex-col col-span-2 md:col-span-4\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</div></div><div class=\"grid-sub divide-x-1\"><h2 class=\"grid-cell font-bold uppercase\">Nicht Anwesend</h2><div class=\"flex flex-col col-span-2 md:col-span-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -487,7 +520,7 @@ func TeamPresencePage(teamPresence map[bool][]models.User) templ.Component {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</div></div></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -511,12 +544,12 @@ func LogoutButton() templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var15 := templ.GetChildren(ctx)
if templ_7745c5c3_Var15 == nil {
templ_7745c5c3_Var15 = templ.NopComponent
templ_7745c5c3_Var14 := templ.GetChildren(ctx)
if templ_7745c5c3_Var14 == nil {
templ_7745c5c3_Var14 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<button onclick=\"logoutUser()\" type=\"button\" class=\"cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-300 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50\">Abmelden</button>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<button onclick=\"logoutUser()\" type=\"button\" class=\"cursor-pointer\">Abmelden</button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View File

@@ -1,12 +1,36 @@
package templates
import (
"arbeitszeitmessung/helper"
"arbeitszeitmessung/models"
"fmt"
"strconv"
"time"
)
templ weekPicker(weekStart time.Time) {
{{
year, kw := weekStart.ISOWeek()
}}
<form method="get" class="flex flex-row gap-4 items-center justify-around">
<input type="date" class="hidden" name="submission_date" value={ weekStart.Format(time.DateOnly) }/>
<button onclick={ templ.JSFuncCall("navigateWeek", templ.JSExpression("this"), templ.JSExpression("event"), "-1") } class="btn">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="chevron-left size-4 mx-auto" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0"></path>
</svg>
</button>
<p class="whitespace-nowrap">KW { fmt.Sprintf("%02d, %d", kw, year) }</p>
<button disabled?={ time.Since(weekStart) < 24*7*time.Hour } onclick={ templ.JSFuncCall("navigateWeek", templ.JSExpression("this"), templ.JSExpression("event"), "1") } class="btn disabled:pointer-events-none disabled:opacity-50">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="chevron-right size-4 mx-auto" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708"></path>
</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() }}
<div class="flex flex-row gap-2">
@@ -39,14 +63,20 @@ templ weekDayComponent(user models.User, day models.WorkDay) {
templ employeComponent(week models.WorkWeek) {
{{
year, kw := week.WeekStart.ISOWeek()
progress := (float32(week.Worktime.Hours()) / week.User.ArbeitszeitPerWoche) * 100
}}
<div class="employeComponent grid-sub divide-x-1">
<div class="grid-cell">
<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">
<p class="font-bold uppercase">{ week.User.Vorname } { week.User.Name }</p>
<p class="text-sm">Arbeitszeit</p>
<p class="text-accent">{ week.GetWorkHourString() }</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>
<div class="grid-cell col-span-3 flex flex-col gap-2">
</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)
}
@@ -56,9 +86,14 @@ templ employeComponent(week models.WorkWeek) {
<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) }/>
<button type="submit" class="w-full bg-neutral-100 cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-900 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50">
<p class="">Bestätigen</p>
</button>
<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>
}
<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>
</div>
}

View File

@@ -9,13 +9,14 @@ import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"arbeitszeitmessung/helper"
"arbeitszeitmessung/models"
"fmt"
"strconv"
"time"
)
func weekDayComponent(user models.User, day models.WorkDay) templ.Component {
func weekPicker(weekStart 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 {
@@ -36,8 +37,115 @@ func weekDayComponent(user models.User, day models.WorkDay) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
year, kw := weekStart.ISOWeek()
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<form method=\"get\" class=\"flex flex-row gap-4 items-center justify-around\"><input type=\"date\" class=\"hidden\" name=\"submission_date\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(weekStart.Format(time.DateOnly))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 16, Col: 98}
}
_, 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, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.JSFuncCall("navigateWeek", templ.JSExpression("this"), templ.JSExpression("event"), "-1"))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<button onclick=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 templ.ComponentScript = templ.JSFuncCall("navigateWeek", templ.JSExpression("this"), templ.JSExpression("event"), "-1")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\" class=\"btn\"><svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" class=\"chevron-left size-4 mx-auto\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0\"></path></svg></button><p class=\"whitespace-nowrap\">KW ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%02d, %d", kw, year))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 22, Col: 69}
}
_, 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, 5, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.JSFuncCall("navigateWeek", templ.JSExpression("this"), templ.JSExpression("event"), "1"))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<button")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if time.Since(weekStart) < 24*7*time.Hour {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, " disabled")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, " onclick=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 templ.ComponentScript = templ.JSFuncCall("navigateWeek", templ.JSExpression("this"), templ.JSExpression("event"), "1")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\" class=\"btn disabled:pointer-events-none disabled:opacity-50\"><svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" class=\"chevron-right size-4 mx-auto\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708\"></path></svg></button></form>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if time.Since(weekStart) < 24*7*time.Hour {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<p class=\"text-sm text-red-500\">Die Woche kann erst am nächsten Montag gesendet werden!</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return nil
})
}
func weekDayComponent(user models.User, day models.WorkDay) 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_Var6 := templ.GetChildren(ctx)
if templ_7745c5c3_Var6 == nil {
templ_7745c5c3_Var6 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
work, pause := day.GetWorkTimeString()
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"flex flex-row gap-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<div class=\"flex flex-row gap-2\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -45,118 +153,118 @@ func weekDayComponent(user models.User, day models.WorkDay) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"flex flex-col\"><p class=\"\"><span class=\"font-bold uppercase hidden md:inline\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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: 15, Col: 89}
}
_, 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, 3, ":</span> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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: 15, Col: 130}
}
_, 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, 4, "</p><div class=\"flex flex-row gap-2\"><span class=\"text-accent\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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: 17, Col: 36}
}
_, 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, 5, "</span> <span class=\"text-neutral-500\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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: 18, Col: 42}
}
_, 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, 6, "</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> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if day.Absence.Datum.Equal(day.Day) {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(day.Absence.AbwesenheitTyp.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 26, Col: 41}
}
_, 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, 8, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if !day.TimeFrom.Equal(day.TimeTo) {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<span>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"flex flex-col\"><p class=\"\"><span class=\"font-bold uppercase hidden md:inline\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(day.TimeFrom.Format("15:04"))
templ_7745c5c3_Var7, 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: 28, Col: 41}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 39, Col: 89}
}
_, 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, 10, "</span> <span>-</span> <span>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, ":</span> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(day.TimeTo.Format("15:04"))
templ_7745c5c3_Var8, 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: 30, Col: 39}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 39, Col: 130}
}
_, 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, 11, "</span>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</p><div class=\"flex flex-row gap-2\"><span class=\"text-accent\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(work)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 41, Col: 36}
}
_, 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, 15, "</span> <span class=\"text-neutral-500\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(pause)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 42, Col: 42}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</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> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if day.Absence.Datum.Equal(day.Day) {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(day.Absence.AbwesenheitTyp.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 50, Col: 41}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if !day.TimeFrom.Equal(day.TimeTo) {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(day.TimeFrom.Format("15:04"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 52, Col: 41}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</span> <span>-</span> <span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(day.TimeTo.Format("15:04"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 54, Col: 39}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<p>Keine Anwesenheit</p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<p>Keine Anwesenheit</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</div></div></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -180,53 +288,75 @@ func employeComponent(week models.WorkWeek) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var9 := templ.GetChildren(ctx)
if templ_7745c5c3_Var9 == nil {
templ_7745c5c3_Var9 = templ.NopComponent
templ_7745c5c3_Var14 := templ.GetChildren(ctx)
if templ_7745c5c3_Var14 == nil {
templ_7745c5c3_Var14 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
year, kw := week.WeekStart.ISOWeek()
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<div class=\"employeComponent grid-sub divide-x-1\"><div class=\"grid-cell\"><p class=\"font-bold uppercase\">")
progress := (float32(week.Worktime.Hours()) / week.User.ArbeitszeitPerWoche) * 100
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<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\"><p class=\"font-bold uppercase\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(week.User.Vorname)
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, 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: 45, Col: 53}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 70, Col: 53}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, " ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(week.User.Name)
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, 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: 45, Col: 72}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 70, Col: 72}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</p><p class=\"text-sm\">Arbeitszeit</p><p class=\"text-accent\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</p><div class=\"flex flex-row gap-2 col-span-3\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(week.GetWorkHourString())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 47, Col: 52}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
templ_7745c5c3_Err = timeGaugeComponent(uint8(progress), false, false).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</p></div><div class=\"grid-cell col-span-3 flex flex-col gap-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<div><p>Arbeitszeit: ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s", helper.FormatDuration(week.Worktime)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 74, Col: 78}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</p><p>Überstunden: ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s", helper.FormatDuration(week.Overtime)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 75, Col: 79}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</p></div></div></div><div class=\"grid-cell col-span-3 flex flex-col @7xl:grid @7xl:grid-cols-5 gap-2 content-baseline\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -236,46 +366,66 @@ func employeComponent(week models.WorkWeek) templ.Component {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</div><form class=\"grid-cell flex flex-col justify-between gap-2\" method=\"post\"><p class=\"text-sm\"><span class=\"\">Woche:</span> ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</div><form class=\"grid-cell flex flex-col justify-between gap-2\" method=\"post\"><p class=\"text-sm\"><span class=\"\">Woche:</span> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%02d-%d", kw, year))
var templ_7745c5c3_Var19 string
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%02d-%d", kw, year))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 55, Col: 85}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 85, Col: 85}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</p><input type=\"hidden\" name=\"method\" value=\"accept\"> <input type=\"hidden\" name=\"user\" value=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</p><input type=\"hidden\" name=\"method\" value=\"accept\"> <input type=\"hidden\" name=\"user\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(week.User.PersonalNummer))
var templ_7745c5c3_Var20 string
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(week.User.PersonalNummer))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 57, Col: 82}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 87, Col: 82}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\"> <input type=\"hidden\" name=\"week\" value=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\"> <input type=\"hidden\" name=\"week\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(week.WeekStart.Format(time.DateOnly))
var templ_7745c5c3_Var21 string
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(week.WeekStart.Format(time.DateOnly))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 58, Col: 80}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 88, Col: 80}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\"> <button type=\"submit\" class=\"w-full bg-neutral-100 cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-900 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50\"><p class=\"\">Bestätigen</p></button></form></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\"><div class=\"flex flex-col gap-2\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if week.Status == models.WeekStatusDifferences {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<p class=\"text-red-600 text-sm\">Unterschiedliche Arbeitszeit zwischen Abrechnung und individuellen Buchungen</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<button type=\"submit\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if week.Status == models.WeekStatusDifferences {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, " disabled")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, " class=\"btn\">Bestätigen</button></div></form></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -299,53 +449,53 @@ func userPresenceComponent(user models.User, present bool) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var16 := templ.GetChildren(ctx)
if templ_7745c5c3_Var16 == nil {
templ_7745c5c3_Var16 = templ.NopComponent
templ_7745c5c3_Var22 := templ.GetChildren(ctx)
if templ_7745c5c3_Var22 == nil {
templ_7745c5c3_Var22 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<div class=\"grid-cell group flex flex-row gap-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<div class=\"grid-cell group flex flex-row gap-2\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if present {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<div class=\"h-8 bg-accent rounded-md group-hover:text-black md:text-transparent text-center p-1\">Anwesend</div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<div class=\"h-8 bg-accent rounded-md group-hover:text-black md:text-transparent text-center p-1\">Anwesend</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<div class=\"h-8 bg-red-600 rounded-md group-hover:text-white md:text-transparent text-center p-1\">Abwesend</div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<div class=\"h-8 bg-red-600 rounded-md group-hover:text-white md:text-transparent text-center p-1\">Abwesend</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(user.Vorname)
var templ_7745c5c3_Var23 string
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(user.Vorname)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 73, Col: 19}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 108, Col: 19}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, " ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(user.Name)
var templ_7745c5c3_Var24 string
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(user.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 73, Col: 33}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 108, Col: 33}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</p></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</p></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View File

@@ -19,7 +19,7 @@ templ inputForm() {
<p class="font-bold uppercase">{ user.Vorname + " " + user.Name }</p>
<div class="justify-self-end">
<p class="text-sm">Überstunden</p>
<p class="text-accent">0h 0min (statisch)</p>
<p class="text-accent">{ user.Overtime }</p>
</div>
</div>
<form id="timeRangeForm" method="GET" class="grid-cell flex flex-row md:col-span-3 gap-2 ">
@@ -30,14 +30,14 @@ templ inputForm() {
</div>
</form>
<div class="grid-cell content-end">
<button type="submit" form="timeRangeForm" class="w-full bg-neutral-100 cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-900 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50">
<button type="submit" form="timeRangeForm" class="btn bg-neutral-100 hover:bg-neutral-700 color-neutral-700">
<p class="">Anzeigen</p>
</button>
</div>
</div>
}
templ dayComponent(workDay models.WorkDay) {
templ dayComponent(workDay models.WorkDay, submitted bool) {
{{
work, pause := workDay.GetWorkTimeString()
user := ctx.Value("user").(models.User)
@@ -47,7 +47,7 @@ templ dayComponent(workDay models.WorkDay) {
justify = "justify-content: center"
}
}}
<div class="grid-sub divide-x-1 hover:bg-neutral-200 transition-colors">
<div class={ "grid-sub divide-x-1 hover:bg-neutral-200 transition-colors", templ.KV("bg-neutral-100", submitted) }>
<div class="grid-cell md:col-span-1 flex flex-row gap-2">
@timeGaugeComponent(workDay.GetWorkDayProgress(ctx.Value("user").(models.User)), workDay.Day.Equal(time.Now().Truncate(24*time.Hour)), workDay.RequiresAction())
<div>
@@ -59,8 +59,10 @@ templ dayComponent(workDay models.WorkDay) {
} else {
<p class=" text-accent">{ work }</p>
}
<p class="text-neutral-500">{ pause }</p>
<p class="text-neutral-500">{ overtime }</p>
<p class="text-neutral-500 flex flex-row items-center"><span class="icon-[material-symbols-light--motion-photos-paused-outline]"></span>{ pause }</p>
if overtime != "" {
<p class="text-neutral-500 flex flex-row items-center"><span class="icon-[material-symbols-light--more-time]"></span>{ overtime }</p>
}
}
</div>
</div>
@@ -91,7 +93,7 @@ templ dayComponent(workDay models.WorkDay) {
}
templ changeButtonComponent(id string) {
<button class="cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-900 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50 group" type="submit" onclick={ templ.JSFuncCall("editDay", templ.JSExpression("this"), templ.JSExpression("event"), id) }>
<button class="btn w-auto group" type="submit" onclick={ templ.JSFuncCall("editDay", templ.JSExpression("this"), templ.JSExpression("event"), id) }>
<p class="hidden md:block group-[.edit]:hidden">Ändern</p>
<p class="hidden group-[.edit]:md:block">Absenden</p>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4 md:hidden">
@@ -108,13 +110,13 @@ templ timeGaugeComponent(progress uint8, today bool, warning bool) {
case (warning):
bgColor = "bg-red-600"
break
case (progress > 0 && progress < 90):
case (progress > 0 && progress < 95):
bgColor = "bg-orange-500"
break
case (90 <= progress && progress <= 110):
case (95 <= progress && progress <= 105):
bgColor = "bg-accent"
break
case (progress > 110):
case (progress > 105):
bgColor = "bg-purple-600"
break
default:
@@ -127,7 +129,7 @@ templ timeGaugeComponent(progress uint8, today bool, warning bool) {
<div class={ "flex w-full items-center justify-center overflow-hidden rounded-full", bgColor } style={ fmt.Sprintf("height: %d%%", int(progress)) }></div>
</div>
} else {
<div class={ "w-2 h-full bg-accent rounded-md", bgColor }></div>
<div class={ "w-2 h-full bg-accent rounded-md flex-shrink-0", bgColor }></div>
}
}
@@ -156,12 +158,7 @@ templ absenceComponent(d models.WorkDay) {
templ newBookingComponent(d models.WorkDay) {
<div class="new-booking-component hidden group-[.edit]:flex flex-row gap-2 items-center">
<button name="action" value="add" type="submit" class="hover:text-accent cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="size-4 transition-colors" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"></path>
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4"></path>
</svg>
</button>
<button name="action" value="add" type="submit" class="hover:text-accent cursor-pointer icon-[material-symbols-light--add-circle-outline]"></button>
<input name="timestamp" type="time" value={ time.Now().Format("15:04") } class="text-neutral-700 group-[.edit]:inline hidden bg-neutral-100 text-sm border border-neutral-200 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none focus:border-neutral-400 hover:border-neutral-300"/>
<input name="date" type="hidden" value={ d.Day.Format("2006-01-02") }/>
<select name="check_in_out">

View File

@@ -54,7 +54,20 @@ func inputForm() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</p><div class=\"justify-self-end\"><p class=\"text-sm\">Überstunden</p><p class=\"text-accent\">0h 0min (statisch)</p></div></div><form id=\"timeRangeForm\" method=\"GET\" class=\"grid-cell flex flex-row md:col-span-3 gap-2 \">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</p><div class=\"justify-self-end\"><p class=\"text-sm\">Überstunden</p><p class=\"text-accent\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(user.Overtime)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 22, Col: 42}
}
_, 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, "</p></div></div><form id=\"timeRangeForm\" method=\"GET\" class=\"grid-cell flex flex-row md:col-span-3 gap-2 \">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -62,33 +75,33 @@ func inputForm() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<div class=\"flex flex-col gap-2 justify-between grow-1\"><input type=\"date\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(urlParams.Get("time_from"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 28, Col: 57}
}
_, 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, 4, "\" name=\"time_from\" class=\"w-full bg-neutral-100 placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-0 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none focus:border-neutral-400 hover:border-neutral-300\" placeholder=\"Zeitraum von...\"> <input type=\"date\" value=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"flex flex-col gap-2 justify-between grow-1\"><input type=\"date\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(urlParams.Get("time_to"))
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(urlParams.Get("time_from"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 29, Col: 55}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 28, Col: 57}
}
_, 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, 5, "\" name=\"time_to\" class=\"w-full bg-neutral-100 placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-0 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none focus:border-neutral-400 hover:border-neutral-300\" placeholder=\"Zeitraum bis...\"></div></form><div class=\"grid-cell content-end\"><button type=\"submit\" form=\"timeRangeForm\" class=\"w-full bg-neutral-100 cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-900 focus:bg-neutral-700 active:bg-neutral-700 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50\"><p class=\"\">Anzeigen</p></button></div></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\" name=\"time_from\" class=\"w-full bg-neutral-100 placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-0 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none focus:border-neutral-400 hover:border-neutral-300\" placeholder=\"Zeitraum von...\"> <input type=\"date\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(urlParams.Get("time_to"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 29, Col: 55}
}
_, 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, 6, "\" name=\"time_to\" class=\"w-full bg-neutral-100 placeholder:text-neutral-400 text-neutral-700 text-sm border border-neutral-0 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none focus:border-neutral-400 hover:border-neutral-300\" placeholder=\"Zeitraum bis...\"></div></form><div class=\"grid-cell content-end\"><button type=\"submit\" form=\"timeRangeForm\" class=\"btn bg-neutral-100 hover:bg-neutral-700 color-neutral-700\"><p class=\"\">Anzeigen</p></button></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -96,7 +109,7 @@ func inputForm() templ.Component {
})
}
func dayComponent(workDay models.WorkDay) templ.Component {
func dayComponent(workDay models.WorkDay, submitted bool) 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 {
@@ -112,9 +125,9 @@ func dayComponent(workDay models.WorkDay) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
if templ_7745c5c3_Var5 == nil {
templ_7745c5c3_Var5 = templ.NopComponent
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
if templ_7745c5c3_Var6 == nil {
templ_7745c5c3_Var6 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
@@ -125,7 +138,25 @@ func dayComponent(workDay models.WorkDay) templ.Component {
if len(workDay.Bookings) <= 1 {
justify = "justify-content: center"
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<div class=\"grid-sub divide-x-1 hover:bg-neutral-200 transition-colors\"><div class=\"grid-cell md:col-span-1 flex flex-row gap-2\">")
var templ_7745c5c3_Var7 = []any{"grid-sub divide-x-1 hover:bg-neutral-200 transition-colors", templ.KV("bg-neutral-100", submitted)}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var7...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var7).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 1, Col: 0}
}
_, 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, 8, "\"><div class=\"grid-cell md:col-span-1 flex flex-row gap-2\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -133,97 +164,103 @@ func dayComponent(workDay models.WorkDay) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div><p class=\"\"><span class=\"font-bold uppercase hidden md:inline\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(workDay.Day.Format("Mon"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 54, Col: 94}
}
_, 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, 8, ":</span> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(workDay.Day.Format("02.01.2006"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 54, Col: 139}
}
_, 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, 9, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if work != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<p class=\" text-sm mt-1\">Arbeitszeit:</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if workDay.RequiresAction() {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<p class=\"text-red-600\">Bitte anpassen</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<p class=\" text-accent\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(work)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 60, Col: 36}
}
_, 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, 13, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, " <p class=\"text-neutral-500\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<div><p class=\"\"><span class=\"font-bold uppercase hidden md:inline\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(pause)
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(workDay.Day.Format("Mon"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 62, Col: 40}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 54, Col: 94}
}
_, 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, 15, "</p><p class=\"text-neutral-500\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, ":</span> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(overtime)
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(workDay.Day.Format("02.01.2006"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 63, Col: 43}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 54, Col: 139}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if work != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<p class=\" text-sm mt-1\">Arbeitszeit:</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if workDay.RequiresAction() {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<p class=\"text-red-600\">Bitte anpassen</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<p class=\" text-accent\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(work)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 60, Col: 36}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</div></div><div class=\"all-booking-component flex flex-row md:col-span-3 gap-2 w-full grid-cell\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " <p class=\"text-neutral-500 flex flex-row items-center\"><span class=\"icon-[material-symbols-light--motion-photos-paused-outline]\"></span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(pause)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 62, Col: 148}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if overtime != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<p class=\"text-neutral-500 flex flex-row items-center\"><span class=\"icon-[material-symbols-light--more-time]\"></span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(overtime)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 64, Col: 133}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</div></div><div class=\"all-booking-component flex flex-row md:col-span-3 gap-2 w-full grid-cell\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -231,57 +268,57 @@ func dayComponent(workDay models.WorkDay) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<form id=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<form id=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs("time-" + workDay.Day.Format("2006-01-02"))
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs("time-" + workDay.Day.Format("2006-01-02"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 69, Col: 56}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 71, Col: 56}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" class=\"flex flex-col gap-2 group w-full justify-between\" style=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" class=\"flex flex-col gap-2 group w-full justify-between\" style=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(justify)
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(justify)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 69, Col: 131}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 71, Col: 131}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\" method=\"post\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" method=\"post\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if (workDay.Absence != models.Absence{}) {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(workDay.Absence.AbwesenheitTyp.Name)
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(workDay.Absence.AbwesenheitTyp.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 71, Col: 45}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 73, Col: 45}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if len(workDay.Bookings) < 1 && (workDay.Absence == models.Absence{}) {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<p class=\"text group-[.edit]:hidden\">Keine Buchung gefunden. Bitte Arbeitsstunden oder Grund der Abwesenheit eingeben!</p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<p class=\"text group-[.edit]:hidden\">Keine Buchung gefunden. Bitte Arbeitsstunden oder Grund der Abwesenheit eingeben!</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -289,7 +326,7 @@ func dayComponent(workDay models.WorkDay) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, " ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -308,7 +345,7 @@ func dayComponent(workDay models.WorkDay) templ.Component {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, " ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -317,7 +354,7 @@ func dayComponent(workDay models.WorkDay) templ.Component {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<input type=\"hidden\" name=\"action\" value=\"change\"><!-- default action value for ändern button --></form></div><div class=\"grid-cell\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<input type=\"hidden\" name=\"action\" value=\"change\"><!-- default action value for ändern button --></form></div><div class=\"grid-cell\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -325,7 +362,7 @@ func dayComponent(workDay models.WorkDay) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</div></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -349,25 +386,25 @@ func changeButtonComponent(id string) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var14 := templ.GetChildren(ctx)
if templ_7745c5c3_Var14 == nil {
templ_7745c5c3_Var14 = templ.NopComponent
templ_7745c5c3_Var17 := templ.GetChildren(ctx)
if templ_7745c5c3_Var17 == nil {
templ_7745c5c3_Var17 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.JSFuncCall("editDay", templ.JSExpression("this"), templ.JSExpression("event"), id))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<button class=\"cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm hover:text-white transition-colors border-neutral-900 hover:bg-neutral-700 disabled:pointer-events-none disabled:opacity-50 group\" type=\"submit\" onclick=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<button class=\"btn w-auto group\" type=\"submit\" onclick=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var15 templ.ComponentScript = templ.JSFuncCall("editDay", templ.JSExpression("this"), templ.JSExpression("event"), id)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15.Call)
var templ_7745c5c3_Var18 templ.ComponentScript = templ.JSFuncCall("editDay", templ.JSExpression("this"), templ.JSExpression("event"), id)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\"><p class=\"hidden md:block group-[.edit]:hidden\">Ändern</p><p class=\"hidden group-[.edit]:md:block\">Absenden</p><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"w-4 h-4 md:hidden\"><path class=\"group-[.edit]:hidden md:hidden\" d=\"M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325\"></path> <path class=\"hidden group-[.edit]:block md:hidden\" d=\"M12.736 3.97a.733.733 0 0 1 j1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06.733.733 0 0 1 1.047 0l3.052 3.093 5.4-6.425z\"></path></svg></button>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\"><p class=\"hidden md:block group-[.edit]:hidden\">Ändern</p><p class=\"hidden group-[.edit]:md:block\">Absenden</p><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"w-4 h-4 md:hidden\"><path class=\"group-[.edit]:hidden md:hidden\" d=\"M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325\"></path> <path class=\"hidden group-[.edit]:block md:hidden\" d=\"M12.736 3.97a.733.733 0 0 1 j1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06.733.733 0 0 1 1.047 0l3.052 3.093 5.4-6.425z\"></path></svg></button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -391,9 +428,9 @@ func timeGaugeComponent(progress uint8, today bool, warning bool) templ.Componen
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var16 := templ.GetChildren(ctx)
if templ_7745c5c3_Var16 == nil {
templ_7745c5c3_Var16 = templ.NopComponent
templ_7745c5c3_Var19 := templ.GetChildren(ctx)
if templ_7745c5c3_Var19 == nil {
templ_7745c5c3_Var19 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
@@ -402,13 +439,13 @@ func timeGaugeComponent(progress uint8, today bool, warning bool) templ.Componen
case (warning):
bgColor = "bg-red-600"
break
case (progress > 0 && progress < 90):
case (progress > 0 && progress < 95):
bgColor = "bg-orange-500"
break
case (90 <= progress && progress <= 110):
case (95 <= progress && progress <= 105):
bgColor = "bg-accent"
break
case (progress > 110):
case (progress > 105):
bgColor = "bg-purple-600"
break
default:
@@ -416,47 +453,11 @@ func timeGaugeComponent(progress uint8, today bool, warning bool) templ.Componen
break
}
if today {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<div class=\"flex-start flex w-2 h-full overflow-hidden rounded-full bg-neutral-300 print:hidden\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<div class=\"flex-start flex w-2 h-full overflow-hidden rounded-full bg-neutral-300 print:hidden\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 = []any{"flex w-full items-center justify-center overflow-hidden rounded-full", bgColor}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var17...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<div class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var17).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\" style=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var19 string
templ_7745c5c3_Var19, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("height: %d%%", int(progress)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 127, Col: 149}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\"></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
var templ_7745c5c3_Var20 = []any{"w-2 h-full bg-accent rounded-md", bgColor}
var templ_7745c5c3_Var20 = []any{"flex w-full items-center justify-center overflow-hidden rounded-full", bgColor}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var20...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
@@ -474,7 +475,43 @@ func timeGaugeComponent(progress uint8, today bool, warning bool) templ.Componen
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "\"></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "\" style=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var22 string
templ_7745c5c3_Var22, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("height: %d%%", int(progress)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 129, Col: 149}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "\"></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
var templ_7745c5c3_Var23 = []any{"w-2 h-full bg-accent rounded-md flex-shrink-0", bgColor}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var23...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var24 string
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var23).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "\"></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -499,12 +536,12 @@ func lineComponent() templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var22 := templ.GetChildren(ctx)
if templ_7745c5c3_Var22 == nil {
templ_7745c5c3_Var22 = templ.NopComponent
templ_7745c5c3_Var25 := templ.GetChildren(ctx)
if templ_7745c5c3_Var25 == nil {
templ_7745c5c3_Var25 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "<div class=\"flex flex-col w-2 py-2 items-center text-accent print:hidden\"><svg class=\"size-2\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><polygon points=\"12,2 22,12 12,22 2,12\"></polygon></svg><div class=\"w-[2px] bg-accent flex-grow -my-1\"></div><svg class=\"size-2\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><polygon points=\"12,2 22,12 12,22 2,12\"></polygon></svg></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<div class=\"flex flex-col w-2 py-2 items-center text-accent print:hidden\"><svg class=\"size-2\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><polygon points=\"12,2 22,12 12,22 2,12\"></polygon></svg><div class=\"w-[2px] bg-accent flex-grow -my-1\"></div><svg class=\"size-2\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><polygon points=\"12,2 22,12 12,22 2,12\"></polygon></svg></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -528,12 +565,12 @@ func absenceComponent(d models.WorkDay) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var23 := templ.GetChildren(ctx)
if templ_7745c5c3_Var23 == nil {
templ_7745c5c3_Var23 = templ.NopComponent
templ_7745c5c3_Var26 := templ.GetChildren(ctx)
if templ_7745c5c3_Var26 == nil {
templ_7745c5c3_Var26 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div class=\"no-booking-component hidden group-[.edit]:flex flex-col gap-2 align-center\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<div class=\"no-booking-component hidden group-[.edit]:flex flex-col gap-2 align-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -541,52 +578,52 @@ func absenceComponent(d models.WorkDay) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<select name=\"absence\" onchange=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<select name=\"absence\" onchange=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var24 templ.ComponentScript = templ.JSFuncCall("editAbwesenheit", templ.JSExpression("this"), templ.JSExpression("event"))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var24.Call)
var templ_7745c5c3_Var27 templ.ComponentScript = templ.JSFuncCall("editAbwesenheit", templ.JSExpression("this"), templ.JSExpression("event"))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var27.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "\" class=\"grow cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm transition-colors border-neutral-900\" disabled><option value=\"0\">Abwesenheit?</option> ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "\" class=\"grow cursor-pointer rounded-md text-neutral-800 p-2 md:px-4 border text-center text-sm transition-colors border-neutral-900\" disabled><option value=\"0\">Abwesenheit?</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, absence := range models.GetAbsenceTypesCached() {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<option value=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<option value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var25 string
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(int(absence.Id)))
var templ_7745c5c3_Var28 string
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(int(absence.Id)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 151, Col: 49}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 153, Col: 49}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var26 string
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(absence.Name)
var templ_7745c5c3_Var29 string
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(absence.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 151, Col: 66}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 153, Col: 66}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</option>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</select></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "</select></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -610,58 +647,58 @@ func newBookingComponent(d models.WorkDay) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var27 := templ.GetChildren(ctx)
if templ_7745c5c3_Var27 == nil {
templ_7745c5c3_Var27 = templ.NopComponent
templ_7745c5c3_Var30 := templ.GetChildren(ctx)
if templ_7745c5c3_Var30 == nil {
templ_7745c5c3_Var30 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "<div class=\"new-booking-component hidden group-[.edit]:flex flex-row gap-2 items-center\"><button name=\"action\" value=\"add\" type=\"submit\" class=\"hover:text-accent cursor-pointer\"><svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" class=\"size-4 transition-colors\" viewBox=\"0 0 16 16\"><path d=\"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16\"></path> <path d=\"M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4\"></path></svg></button> <input name=\"timestamp\" type=\"time\" value=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<div class=\"new-booking-component hidden group-[.edit]:flex flex-row gap-2 items-center\"><button name=\"action\" value=\"add\" type=\"submit\" class=\"hover:text-accent cursor-pointer icon-[material-symbols-light--add-circle-outline]\"></button> <input name=\"timestamp\" type=\"time\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var28 string
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(time.Now().Format("15:04"))
var templ_7745c5c3_Var31 string
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(time.Now().Format("15:04"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 165, Col: 72}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 162, Col: 72}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "\" class=\"text-neutral-700 group-[.edit]:inline hidden bg-neutral-100 text-sm border border-neutral-200 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none focus:border-neutral-400 hover:border-neutral-300\"> <input name=\"date\" type=\"hidden\" value=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "\" class=\"text-neutral-700 group-[.edit]:inline hidden bg-neutral-100 text-sm border border-neutral-200 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none focus:border-neutral-400 hover:border-neutral-300\"> <input name=\"date\" type=\"hidden\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var29 string
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(d.Day.Format("2006-01-02"))
var templ_7745c5c3_Var32 string
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(d.Day.Format("2006-01-02"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 166, Col: 69}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 163, Col: 69}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\"> <select name=\"check_in_out\"><option value=\"0\" disabled>Kommen/Gehen</option> <option value=\"3\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "\"> <select name=\"check_in_out\"><option value=\"0\" disabled>Kommen/Gehen</option> <option value=\"3\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(d.Bookings) > 0 && d.Bookings[len(d.Bookings)-1].CheckInOut%2 == 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, " selected")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, ">Kommen</option> <option value=\"4\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, ">Kommen</option> <option value=\"4\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(d.Bookings) > 0 && d.Bookings[len(d.Bookings)-1].CheckInOut%2 == 1 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, " selected")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, ">Gehen</option></select></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, ">Gehen</option></select></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -685,64 +722,64 @@ func bookingComponent(booking models.Booking) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var30 := templ.GetChildren(ctx)
if templ_7745c5c3_Var30 == nil {
templ_7745c5c3_Var30 = templ.NopComponent
templ_7745c5c3_Var33 := templ.GetChildren(ctx)
if templ_7745c5c3_Var33 == nil {
templ_7745c5c3_Var33 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<div><p class=\"text-neutral-500\"><span class=\"text-neutral-700 group-[.edit]:hidden inline\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var31 string
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(booking.Timestamp.Format("15:04"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 178, Col: 97}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</span> <input disabled name=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var32 string
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs("booking_" + strconv.Itoa(booking.CounterId))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 179, Col: 70}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "\" type=\"time\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var33 string
templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(booking.Timestamp.Format("15:04"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 179, Col: 126}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "\" class=\"text-neutral-700 group-[.edit]:inline hidden bg-neutral-100 text-sm border border-neutral-200 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none focus:border-neutral-400 hover:border-neutral-300\"> ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "<div><p class=\"text-neutral-500\"><span class=\"text-neutral-700 group-[.edit]:hidden inline\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var34 string
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(booking.GetBookingType())
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(booking.Timestamp.Format("15:04"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 180, Col: 29}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 175, Col: 97}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "</p></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "</span> <input disabled name=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var35 string
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs("booking_" + strconv.Itoa(booking.CounterId))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 176, Col: 70}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\" type=\"time\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var36 string
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(booking.Timestamp.Format("15:04"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 176, Col: 126}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "\" class=\"text-neutral-700 group-[.edit]:inline hidden bg-neutral-100 text-sm border border-neutral-200 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none focus:border-neutral-400 hover:border-neutral-300\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var37 string
templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(booking.GetBookingType())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 177, Col: 29}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</p></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -766,12 +803,12 @@ func LegendComponent() templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var35 := templ.GetChildren(ctx)
if templ_7745c5c3_Var35 == nil {
templ_7745c5c3_Var35 = templ.NopComponent
templ_7745c5c3_Var38 := templ.GetChildren(ctx)
if templ_7745c5c3_Var38 == nil {
templ_7745c5c3_Var38 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "<div class=\"flex flex-row gap-4 md:mx-[10%] print:hidden\"><div class=\"flex flex-row items-center gap-2\"><div class=\"rounded-full size-4 bg-red-600\"></div><span>Fehler</span></div><div class=\"flex flex-row items-center gap-2\"><div class=\"rounded-full size-4 bg-orange-500\"></div><span>Arbeitszeit unter regulär</span></div><div class=\"flex flex-row items-center gap-2\"><div class=\"rounded-full size-4 bg-accent\"></div><span>Arbeitszeit vollständig</span></div><div class=\"flex flex-row items-center gap-2\"><div class=\"rounded-full size-4 bg-purple-600\"></div><span>Überstunden</span></div><div class=\"flex flex-row items-center gap-2\"><div class=\"rounded-full size-4 bg-neutral-400\"></div><span>Keine Buchungen</span></div></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<div class=\"flex flex-row gap-4 md:mx-[10%] print:hidden\"><div class=\"flex flex-row items-center gap-2\"><div class=\"rounded-full size-4 bg-red-600\"></div><span>Fehler</span></div><div class=\"flex flex-row items-center gap-2\"><div class=\"rounded-full size-4 bg-orange-500\"></div><span>Arbeitszeit unter regulär</span></div><div class=\"flex flex-row items-center gap-2\"><div class=\"rounded-full size-4 bg-accent\"></div><span>Arbeitszeit vollständig</span></div><div class=\"flex flex-row items-center gap-2\"><div class=\"rounded-full size-4 bg-purple-600\"></div><span>Überstunden</span></div><div class=\"flex flex-row items-center gap-2\"><div class=\"rounded-full size-4 bg-neutral-400\"></div><span>Keine Buchungen</span></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View File

@@ -3,13 +3,15 @@
-- ----------------------------
DROP TABLE IF EXISTS "anwesenheit";
CREATE TABLE "anwesenheit" (
"counter_id" bigserial PRIMARY KEY,
"timestamp" timestamptz(6) DEFAULT CURRENT_TIMESTAMP,
"card_uid" varchar(255),
"check_in_out" int2,
"geraet_id" int2,
"manuelle_buchung" bool
"counter_id" bigserial NOT NULL,
"timestamp" timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
"card_uid" character varying(255) NOT NULL,
"check_in_out" smallint NOT NULL,
"geraet_id" smallint NOT NULL,
"anwesenheit_typ" int2 NOT NULL,
PRIMARY KEY ("counter_id")
);
COMMENT ON COLUMN "anwesenheit"."check_in_out" IS '1=Check In 2=Check Out , 3=Check in Manuell, 4=Check out manuell255=Automatic Check Out';
COMMENT ON COLUMN "anwesenheit"."geraet_id" IS 'ID des Lesegerätes';
@@ -30,18 +32,19 @@ DROP TABLE IF EXISTS "s_personal_daten";
CREATE TABLE "s_personal_daten" (
"personal_nummer" int4 NOT NULL PRIMARY KEY,
"aktiv_beschaeftigt" bool,
"vorname" varchar(255),
"nachname" varchar(255),
"vorname" varchar(255) NOT NULL,
"nachname" varchar(255) NOT NULL,
"geburtsdatum" date,
"plz" varchar(255),
"adresse" varchar(255),
"geschlecht" int2,
"card_uid" varchar(255),
"hauptbeschaeftigungs_ort" int2,
"arbeitszeit_per_tag" float4,
"arbeitszeit_per_tag" float4 NOT NULL,
"arbeitszeit_per_woche" float4 NOT NULL,
"arbeitszeit_min_start" time(6),
"arbeitszeit_max_ende" time(6),
"vorgesetzter_pers_nr" int4
"vorgesetzter_pers_nr" int4 NOT NULL
);
COMMENT ON COLUMN "s_personal_daten"."geschlecht" IS '1==weiblich, 2==maennlich, 3==divers';
@@ -78,6 +81,8 @@ CREATE TABLE "wochen_report" (
"personal_nummer" int4,
"woche_start" date,
"bestaetigt" bool DEFAULT FALSE,
"arbeitszeit" interval,
"ueberstunden" interval,
UNIQUE ("personal_nummer", "woche_start")
);
@@ -92,7 +97,8 @@ CREATE TABLE "abwesenheit" (
DROP TABLE IF EXISTS "s_abwesenheit_typen";
CREATE TABLE "s_abwesenheit_typen" (
"abwesenheit_id" int2 PRIMARY KEY,
"abwesenheit_name" varchar(255)
"abwesenheit_name" varchar(255),
"arbeitszeit_equivalent" float4
);
-- Adds crypto extension

View File

@@ -5,4 +5,4 @@ INSERT INTO "user_password" ("personal_nummer", "pass_hash") VALUES
(123, crypt('max_pass', gen_salt('bf')));
INSERT INTO "s_anwesenheit_typen" ("anwesenheit_id", "anwesenheit_name") VALUES (1, 'Büro');
INSERT INTO "s_abwesenheit_typen" ("abwesenheit_id", "abwesenheit_name") VALUES (1, 'Urlaub'), (2, 'Krank'), (3, 'Kurzarbeit');
INSERT INTO "s_abwesenheit_typen" ("abwesenheit_id", "abwesenheit_name", "arbeitszeit_equivalent") VALUES (1, 'Urlaub', 10), (2, 'Krank', 10), (3, 'Kurzarbeit', 2);

View File

@@ -9,7 +9,7 @@ services:
PGDATA: /var/lib/postgresql/data/pg_data
volumes:
- ${POSTGRES_PATH}:/var/lib/postgresql/data
- ${POSTGRES_PATH}/initdb:/docker-entrypoint-initdb.d
# - ${POSTGRES_PATH}/initdb:/docker-entrypoint-initdb.d
ports:
- 5432:5432
@@ -19,7 +19,6 @@ services:
ports:
- 8001:8080
backend:
build: ../Backend
image: git.letsstein.de/tom/arbeitszeitmessung
restart: unless-stopped
env_file:

View File

@@ -44,8 +44,8 @@ generateFrontend:
backend: generateFrontend login_registry
docker buildx build --platform linux/amd64,linux/arm64 -t ${IMAGE_REGISTRY}/${PACKAGE_OWNER}/arbeitszeit-backend:latest Backend --push
docker buildx build --platform linux/amd64,linux/arm64 -t ${IMAGE_REGISTRY}/${PACKAGE_OWNER}/arbeitszeit-backend:${GIT_COMMIT} Backend --push
docker buildx build --platform linux/amd64,linux/arm64 -t ${IMAGE_REGISTRY}/${PACKAGE_OWNER}/arbeitszeitmessung:latest Backend --push
docker buildx build --platform linux/amd64,linux/arm64 -t ${IMAGE_REGISTRY}/${PACKAGE_OWNER}/arbeitszeitmessung:${GIT_COMMIT} Backend --push
test:
$(MAKE) -C Backend test

118
db.sql
View File

@@ -146,3 +146,121 @@ SELECT
FROM ordered_bookings
GROUP BY work_date
ORDER BY work_date;
-- Generate weekdays for 2 weeks (MonFri), starting 2 weeks ago
WITH days AS (
SELECT gs::date AS work_date
FROM generate_series(
date_trunc('week', CURRENT_DATE) - interval '14 days', -- start 2 weeks ago Monday
CURRENT_DATE, -- end TODAY (no future days)
interval '1 day'
) gs
WHERE EXTRACT(ISODOW FROM gs) <= 5 -- only MonFri
),
sample_bookings AS (
SELECT
d.work_date,
'aaaa-aaaa'::varchar AS card_uid,
1 AS check_in_out, -- come
101 AS geraet_id,
(d.work_date + make_time(8, floor(random()*50)::int, 0))::timestamptz AS ts,
1 AS anwesenheit_typ
FROM days d
UNION ALL
SELECT
d.work_date,
'aaaa-aaaa'::varchar AS card_uid,
2 AS check_in_out, -- go
101 AS geraet_id,
(d.work_date + make_time(16, floor(random()*50)::int, 0))::timestamptz AS ts,
1 AS anwesenheit_typ
FROM days d
),
ins_anw AS (
-- insert only bookings up to now (prevents future times on today)
INSERT INTO anwesenheit ("timestamp", card_uid, check_in_out, geraet_id)
SELECT ts, card_uid, check_in_out, geraet_id
FROM sample_bookings
WHERE ts <= NOW()
RETURNING 1
)
-- now insert absences (uses the same days CTE)
INSERT INTO abwesenheit (card_uid, abwesenheit_typ, datum)
SELECT
'aaaa-aaaa',
(ARRAY[1, 2])[floor(random()*2 + 1)], -- example types
d.work_date::timestamptz
FROM days d
WHERE random() < 0.2 -- ~20% random absences
ORDER BY d.work_date;
WITH params AS (
SELECT
'acde-edca'::varchar AS card_uid,
101::int AS geraet_id,
14::int AS start_days_ago, -- how many days back to start
0::int AS end_days_ahead, -- how many days forward (0 = today)
0.5::float AS pause_probability,
0.2::float AS absence_probability
),
days AS (
SELECT gs::date AS work_date, p.card_uid, p.geraet_id, p.pause_probability, p.absence_probability
FROM params p,
generate_series(
date_trunc('week', CURRENT_DATE) - (p.start_days_ago || ' days')::interval,
CURRENT_DATE + (p.end_days_ahead || ' days')::interval,
interval '1 day'
) gs
WHERE EXTRACT(ISODOW FROM gs) <= 5 -- only MonFri
),
base_bookings AS (
-- come
SELECT
d.work_date, d.card_uid, 1 AS check_in_out, d.geraet_id,
(d.work_date + make_time(8, floor(random()*40)::int, 0))::timestamptz AS ts
FROM days d
UNION ALL
-- go
SELECT
d.work_date, d.card_uid, 2 AS check_in_out, d.geraet_id,
(d.work_date + make_time(16, floor(random()*40)::int, 0))::timestamptz AS ts
FROM days d
),
pause_bookings AS (
-- pause come
SELECT
d.work_date, d.card_uid, 3 AS check_in_out, d.geraet_id,
(d.work_date + make_time(11, floor(random()*30)::int, 0))::timestamptz AS ts
FROM days d
WHERE random() < d.pause_probability
UNION ALL
-- pause go
SELECT
d.work_date, d.card_uid, 4 AS check_in_out, d.geraet_id,
(d.work_date + make_time(12, floor(random()*30)::int, 0))::timestamptz AS ts
FROM days d
WHERE random() < d.pause_probability
),
all_bookings AS (
SELECT * FROM base_bookings
UNION ALL
SELECT * FROM pause_bookings
),
ins_anw AS (
INSERT INTO anwesenheit ("timestamp", card_uid, check_in_out, geraet_id)
SELECT ts, card_uid, check_in_out, geraet_id
FROM all_bookings
WHERE ts <= NOW()
ORDER BY work_date, ts
RETURNING 1
)
INSERT INTO abwesenheit (card_uid, abwesenheit_typ, datum)
SELECT
d.card_uid,
(ARRAY[1, 2])[floor(random()*2 + 1)], -- example types
d.work_date::timestamptz
FROM days d
WHERE random() < d.absence_probability
ORDER BY d.work_date;

View File

@@ -1,10 +0,0 @@
-- reverse rename "s_personal_daten" table
ALTER TABLE "s_personal_daten" RENAME TO "personal_daten";
DROP TABLE "s_personal_daten";
-- reverse: create "s_anwesenheit_typen" table
DROP TABLE "s_anwesenheit_typen";
-- reverse: create "s_abwesenheit_typen" table
DROP TABLE "s_abwesenheit_typen";
-- reverse: modify "anwesenheit" table
ALTER TABLE "anwesenheit" DROP COLUMN "manuelle_buchung";

View File

@@ -1,7 +1,7 @@
-- reverse: create "wochen_report" table
DROP TABLE "wochen_report";
-- reverse: create "user_password" table
DROP TABLE "user_password";
-- reverse: create "wochen_report" table
DROP TABLE "wochen_report";
-- reverse: set comment to column: "geschlecht" on table: "personal_daten"
COMMENT ON COLUMN "personal_daten"."geschlecht" IS NULL;
-- reverse: create "personal_daten" table

View File

@@ -0,0 +1,14 @@
-- reverse: remame "personal_daten" table
ALTER TABLE "s_personal_daten" RENAME TO "personal_daten";
-- reverse: create "s_anwesenheit_typen" table
DROP TABLE "s_anwesenheit_typen";
-- reverse: create "s_abwesenheit_typen" table
DROP TABLE "s_abwesenheit_typen";
-- reverse: modify "wochen_report" table
ALTER TABLE "wochen_report" DROP COLUMN "ueberstunden";
-- reverse: modify "anwesenheit" table
ALTER TABLE "anwesenheit" DROP COLUMN "anwesenheit_typ", ALTER COLUMN "check_in_out" DROP NOT NULL, ALTER COLUMN "card_uid" DROP NOT NULL;
-- reverse: rename a constraint from "personal_daten_pkey" to "s_personal_daten_pkey"
ALTER TABLE "s_personal_daten" RENAME CONSTRAINT "s_personal_daten_pkey" TO "personal_daten_pkey";

View File

@@ -1,5 +1,7 @@
-- modify "anwesenheit" table
ALTER TABLE "anwesenheit" ADD COLUMN "manuelle_buchung" boolean NULL;
ALTER TABLE "anwesenheit" ALTER COLUMN "card_uid" SET NOT NULL, ALTER COLUMN "check_in_out" SET NOT NULL, ADD COLUMN "anwesenheit_typ" smallint NULL;
-- modify "wochen_report" table
ALTER TABLE "wochen_report" ADD COLUMN "ueberstunden" smallint NULL;
-- create "s_abwesenheit_typen" table
CREATE TABLE "s_abwesenheit_typen" (
"abwesenheit_id" smallint NOT NULL,
@@ -12,5 +14,8 @@ CREATE TABLE "s_anwesenheit_typen" (
"anwesenheit_name" character varying(255) NULL,
PRIMARY KEY ("anwesenheit_id")
);
-- create "s_personal_daten" table
-- rename "s_personal_daten" table
ALTER TABLE "personal_daten" RENAME TO "s_personal_daten";
-- rename a constraint from "personal_daten_pkey" to "s_personal_daten_pkey"
ALTER TABLE "s_personal_daten" RENAME CONSTRAINT "personal_daten_pkey" TO "s_personal_daten_pkey";

View File

@@ -0,0 +1,10 @@
-- update Funktion für pass_hash
DROP FUNCTION update_zuletzt_geandert;
DROP TRIGGER IF EXISTS pass_hash_update ON user_password;
-- revert: Adds crypto extension
DROP EXTENSION IF EXISTS pgcrypto;

View File

@@ -0,0 +1,21 @@
-- update Funktion für pass_hash
CREATE OR REPLACE FUNCTION update_zuletzt_geandert()
RETURNS TRIGGER AS $$
BEGIN
-- Nur wenn hash geändert wurde
IF NEW.pass_hash IS DISTINCT FROM OLD.pass_hash THEN
NEW.zuletzt_geandert = now();
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER pass_hash_update
BEFORE UPDATE ON user_password
FOR EACH ROW
EXECUTE FUNCTION update_zuletzt_geandert();
-- Adds crypto extension
CREATE EXTENSION IF NOT EXISTS pgcrypto;

View File

@@ -0,0 +1,6 @@
-- reverse: modify "wochen_report" table
ALTER TABLE "wochen_report" DROP COLUMN "arbeitszeit", ALTER COLUMN "ueberstunden" TYPE smallint;
-- reverse: modify "s_personal_daten" table
ALTER TABLE "s_personal_daten" DROP COLUMN "arbeitszeit_per_woche";
-- reverse: modify "s_abwesenheit_typen" table
ALTER TABLE "s_abwesenheit_typen" DROP COLUMN "arbeitszeit_equivalent";

View File

@@ -0,0 +1,6 @@
-- modify "s_abwesenheit_typen" table
ALTER TABLE "s_abwesenheit_typen" ADD COLUMN "arbeitszeit_equivalent" real NULL;
-- modify "s_personal_daten" table
ALTER TABLE "s_personal_daten" ADD COLUMN "arbeitszeit_per_woche" real NULL;
-- modify "wochen_report" table
ALTER TABLE "wochen_report" ALTER COLUMN "ueberstunden" TYPE real, ADD COLUMN "arbeitszeit" real NULL;

View File

@@ -0,0 +1,4 @@
-- reverse: modify "s_personal_daten" table
ALTER TABLE "s_personal_daten" ALTER COLUMN "arbeitszeit_per_woche" DROP NOT NULL, ALTER COLUMN "vorgesetzter_pers_nr" DROP NOT NULL, ALTER COLUMN "arbeitszeit_per_tag" DROP NOT NULL, ALTER COLUMN "nachname" DROP NOT NULL, ALTER COLUMN "vorname" DROP NOT NULL;
-- reverse: modify "anwesenheit" table
ALTER TABLE "anwesenheit" ALTER COLUMN "anwesenheit_typ" DROP NOT NULL, ALTER COLUMN "geraet_id" DROP NOT NULL, ALTER COLUMN "timestamp" DROP NOT NULL;

View File

@@ -0,0 +1,4 @@
-- modify "anwesenheit" table
ALTER TABLE "anwesenheit" ALTER COLUMN "timestamp" SET NOT NULL, ALTER COLUMN "geraet_id" SET NOT NULL, ALTER COLUMN "anwesenheit_typ" SET NOT NULL;
-- modify "s_personal_daten" table
ALTER TABLE "s_personal_daten" ALTER COLUMN "vorname" SET NOT NULL, ALTER COLUMN "nachname" SET NOT NULL, ALTER COLUMN "arbeitszeit_per_tag" SET NOT NULL, ALTER COLUMN "vorgesetzter_pers_nr" SET NOT NULL, ALTER COLUMN "arbeitszeit_per_woche" SET NOT NULL;

View File

@@ -0,0 +1,7 @@
ALTER TABLE wochen_report
ALTER COLUMN ueberstunden TYPE float4
USING
extract(epoch from ueberstunden) / 3600.0,
ALTER COLUMN arbeitszeit TYPE float4
USING
extract(epoch from arbeitszeit) / 3600.0;

View File

@@ -0,0 +1,13 @@
ALTER TABLE wochen_report
ALTER COLUMN ueberstunden TYPE interval
USING
make_interval(
hours => floor(ueberstunden)::int,
mins => round((ueberstunden - floor(ueberstunden)) * 60)::int
),
ALTER COLUMN arbeitszeit TYPE interval
USING
make_interval(
hours => floor(arbeitszeit)::int,
mins => round((arbeitszeit - floor(arbeitszeit)) * 60)::int
);

View File

@@ -1,3 +1,7 @@
h1:5gPDmrcQS12KjKLuwN1ycTBHtbHbkzd7rUIj01uJrhA=
20250802065143_initial.up.sql h1:9cUWduWgONRfI5LV+b3nFvei6DKDqPxcNryKVg1xo80=
20250802075213_control_tables.up.sql h1:5vQLBHMM2Sa1FErP5gQUUHAoSiV2RQ0cOlMEEDFcoKA=
h1:31y/DB0ca2cm603nNckNvVCwm+XuMaVsoGUB+JvrOKs=
20250901201159_initial.up.sql h1:Mb1RlVdFvcxqU9HrSK6oNeURqFa3O4KzB3rDa+6+3gc=
20250901201250_control_tables.up.sql h1:a5LATgR/CRiC4GsqxkJ94TyJOxeTcW74eCnodIy+c1E=
20250901201710_triggers_extension.up.sql h1:z9b6Hk9btE2Ns4mU7B16HjvYBP6EEwHAXVlvPpkn978=
20250903221313_overtime.up.sql h1:t/B435ShW5ZEnzC81jRABWVZ5gNm7tPZPnOO6/ZY6ow=
20250903233030_non_null_contraints.up.sql h1:e7d+6ZdpEjPh2cc65N3S06oD2e6diMuG7+klhgYsym8=
20250904114004_intervals.up.sql h1:Sz8FIVvvcCIS3aIuSjyzFYLs32fjMcMMHk62shj6Qpw=

1395
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,10 @@
{
"dependencies": {
"tailwindcss": "^4.1.12",
"@tailwindcss/cli": "^4.1.12"
"@tailwindcss/cli": "^4.1.12",
"tailwindcss": "^4.1.12"
},
"devDependencies": {
"@iconify-json/material-symbols-light": "^1.2.33",
"@iconify/tailwind4": "^1.0.6"
}
}