diff --git a/.gitea/workflows/build-deploy.yaml b/.gitea/workflows/build-deploy.yaml deleted file mode 100644 index 87ac133..0000000 --- a/.gitea/workflows/build-deploy.yaml +++ /dev/null @@ -1,39 +0,0 @@ -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 }} diff --git a/.gitea/workflows/build_tags.yaml b/.gitea/workflows/build_tags.yaml new file mode 100644 index 0000000..a73ba14 --- /dev/null +++ b/.gitea/workflows/build_tags.yaml @@ -0,0 +1,77 @@ +name: Arbeitszeitmessung Deploy +run-name: ${{ gitea.actor }} is building and deploying arbeitszeitmesssung +on: + push: + tags: + - "*" + +jobs: + testing: + name: Run Go Tests + runs-on: ubuntu-latest + services: + postgres: + image: postgres:16 + env: + POSTGRES_USER: root + POSTGRES_PASSWORD: password + POSTGRES_DB: arbeitszeitmessung + env: + POSTGRES_HOST: postgres + POSTGRES_USER: root + POSTGRES_PASSWORD: password + POSTGRES_DB: arbeitszeitmessung + POSTGRES_PORT: 5432 + RUNNER_TOOL_CACHE: /toolcache # Runner Tool Cache + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup go + uses: actions/setup-go@v5 + with: + go-version-file: Backend/go.mod + - uses: https://gitea.com/actions/go-hashfiles@v0.0.1 + id: hash-go + with: + patterns: | + go.mod + go.sum + - name: cache go + id: cache-go + uses: actions/cache@v4 + with: + path: |- + /go_path + /go_cache + key: arbeitszeitmessung-${{ steps.hash-go.outputs.hash }} + restore-keys: |- + arbeitszeitmessung- + - name: Run Go Tests + run: cd Backend && go test ./... + build: + name: Build Go Image and Upload + runs-on: ubuntu-latest + needs: [testing] + 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 }} diff --git a/.gitea/workflows/tests/go-test.yaml b/.gitea/workflows/go_test.yaml similarity index 96% rename from .gitea/workflows/tests/go-test.yaml rename to .gitea/workflows/go_test.yaml index 654faa2..c36d1e4 100644 --- a/.gitea/workflows/tests/go-test.yaml +++ b/.gitea/workflows/go_test.yaml @@ -1,6 +1,9 @@ name: Tests run-name: ${{ gitea.actor }} is testing golang Code -on: push +on: + push: + tags-ignore: + - "*" jobs: testing: diff --git a/Backend/Dockerfile b/Backend/Dockerfile index 098f454..c9ef35d 100644 --- a/Backend/Dockerfile +++ b/Backend/Dockerfile @@ -14,6 +14,7 @@ COPY . . RUN go build -o server . FROM alpine +RUN apk add --no-cache tzdata WORKDIR /app COPY --from=build /app/server /app/server diff --git a/Backend/go.mod b/Backend/go.mod index d5c6953..2472dc8 100644 --- a/Backend/go.mod +++ b/Backend/go.mod @@ -1,6 +1,6 @@ module arbeitszeitmessung -go 1.24.5 +go 1.24.7 require github.com/lib/pq v1.10.9 diff --git a/Backend/models/absence.go b/Backend/models/absence.go index c5184d0..bda305e 100644 --- a/Backend/models/absence.go +++ b/Backend/models/absence.go @@ -17,7 +17,6 @@ type Absence struct { CardUID string AbwesenheitTyp AbsenceType Datum time.Time - // Comment string } func NewAbsence(card_uid string, abwesenheit_typ int, datum time.Time) (Absence, error) { diff --git a/Backend/models/booking.go b/Backend/models/booking.go index 8b1140e..ba758f8 100644 --- a/Backend/models/booking.go +++ b/Backend/models/booking.go @@ -15,8 +15,8 @@ import ( type SameBookingError struct{} type BookingType struct { - Id int8 - Name string + Id int8 `json:"anwesenheit_id"` + Name string `json:"anwesenheit_name"` } func (e SameBookingError) Error() string { @@ -29,7 +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"` + BookingType BookingType `json:"anwesenheit_typ"` } type IDatabase interface { @@ -298,7 +298,7 @@ func (b *Booking) UpdateTime(newTime time.Time) { } func (b *Booking) ToString() string { - return fmt.Sprintf("Booking %d: at: %s, as type: %d", b.CounterId, b.Timestamp.Format("15:04"), b.CheckInOut) + return fmt.Sprintf("Booking %d: at: %s, CheckInOut: %d, TypeId: %d", b.CounterId, b.Timestamp.Format("15:04"), b.CheckInOut, b.BookingType.Id) } func GetBookingTypes() ([]BookingType, error) { diff --git a/Backend/models/workDay.go b/Backend/models/workDay.go index 31da794..cf8df46 100644 --- a/Backend/models/workDay.go +++ b/Backend/models/workDay.go @@ -24,64 +24,72 @@ func GetWorkDays(card_uid string, tsFrom, tsTo time.Time) []WorkDay { var workSec, pauseSec float64 qStr, err := DB.Prepare(` - WITH all_days AS ( - SELECT generate_series($2::DATE, $3::DATE - INTERVAL '1 day', INTERVAL '1 day')::DATE AS work_date - ), - ordered_bookings AS ( - SELECT - timestamp::DATE AS work_date, - timestamp, - check_in_out, - counter_id, - LAG(timestamp) OVER (PARTITION BY card_uid, timestamp::DATE ORDER BY timestamp) AS prev_timestamp, - LAG(check_in_out) OVER (PARTITION BY card_uid, timestamp::DATE ORDER BY timestamp) AS prev_check - FROM anwesenheit - WHERE card_uid = $1 - AND timestamp::DATE >= $2 - AND timestamp::DATE <= $3 - ), - abwesenheiten AS ( - SELECT - datum::DATE AS work_date, - abwesenheit_typ - FROM abwesenheit - WHERE card_uid = $1 - AND datum::DATE >= $2 - AND datum::DATE <= $3 - ) + WITH all_days AS ( + SELECT generate_series($2::DATE, $3::DATE - INTERVAL '1 day', INTERVAL '1 day')::DATE AS work_date +), +ordered_bookings AS ( SELECT - d.work_date, - COALESCE(MIN(b.timestamp), NOW()) AS time_from, - COALESCE(MAX(b.timestamp), NOW()) AS time_to, - COALESCE( - EXTRACT(EPOCH FROM SUM( - CASE - WHEN b.prev_check IN (1, 3) AND b.check_in_out IN (2, 4, 254) - THEN b.timestamp - b.prev_timestamp - ELSE INTERVAL '0' - END - )), 0 - ) AS total_work_seconds, - COALESCE( - EXTRACT(EPOCH FROM SUM( - CASE - WHEN b.prev_check IN (2, 4, 254) AND b.check_in_out IN (1, 3) - THEN b.timestamp - b.prev_timestamp - ELSE INTERVAL '0' - END - )), 0 - ) AS total_pause_seconds, - COALESCE(jsonb_agg(jsonb_build_object( - 'check_in_out', b.check_in_out, - 'timestamp', b.timestamp, - 'counter_id', b.counter_id - ) ORDER BY b.timestamp), '[]'::jsonb) AS bookings, - a.abwesenheit_typ - FROM all_days d - 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 ASC;`) + a.timestamp::DATE AS work_date, + a.timestamp, + a.check_in_out, + a.counter_id, + a.anwesenheit_typ, + sat.anwesenheit_name AS anwesenheit_typ_name, + LAG(a.timestamp) OVER (PARTITION BY a.card_uid, a.timestamp::DATE ORDER BY a.timestamp) AS prev_timestamp, + LAG(a.check_in_out) OVER (PARTITION BY a.card_uid, a.timestamp::DATE ORDER BY a.timestamp) AS prev_check + FROM anwesenheit a + LEFT JOIN s_anwesenheit_typen sat ON a.anwesenheit_typ = sat.anwesenheit_id + WHERE a.card_uid = $1 + AND a.timestamp::DATE >= $2 + AND a.timestamp::DATE <= $3 +), +abwesenheiten AS ( + SELECT + datum::DATE AS work_date, + abwesenheit_typ + FROM abwesenheit + WHERE card_uid = $1 + AND datum::DATE >= $2 + AND datum::DATE <= $3 +) +SELECT + d.work_date, + COALESCE(MIN(b.timestamp), NOW()) AS time_from, + COALESCE(MAX(b.timestamp), NOW()) AS time_to, + COALESCE( + EXTRACT(EPOCH FROM SUM( + CASE + WHEN b.prev_check IN (1, 3) AND b.check_in_out IN (2, 4, 254) + THEN b.timestamp - b.prev_timestamp + ELSE INTERVAL '0' + END + )), 0 + ) AS total_work_seconds, + COALESCE( + EXTRACT(EPOCH FROM SUM( + CASE + WHEN b.prev_check IN (2, 4, 254) AND b.check_in_out IN (1, 3) + THEN b.timestamp - b.prev_timestamp + ELSE INTERVAL '0' + END + )), 0 + ) AS total_pause_seconds, + COALESCE(jsonb_agg(jsonb_build_object( + 'check_in_out', b.check_in_out, + 'timestamp', b.timestamp, + 'counter_id', b.counter_id, + 'anwesenheit_typ', b.anwesenheit_typ, + 'anwesenheit_typ', jsonb_build_object( + 'anwesenheit_id', b.anwesenheit_typ, + 'anwesenheit_name', b.anwesenheit_typ_name + ) + ) ORDER BY b.timestamp), '[]'::jsonb) AS bookings, + a.abwesenheit_typ +FROM all_days d +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 ASC;`) if err != nil { log.Println("Error preparing SQL statement", err) diff --git a/Backend/static/css/styles.css b/Backend/static/css/styles.css index 97a4852..93e47a6 100644 --- a/Backend/static/css/styles.css +++ b/Backend/static/css/styles.css @@ -198,6 +198,12 @@ .col-span-3 { grid-column: span 3 / span 3; } + .col-span-6 { + grid-column: span 6 / span 6; + } + .col-span-7 { + grid-column: span 7 / span 7; + } .col-span-full { grid-column: 1 / -1; } @@ -367,27 +373,15 @@ .grid-cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); } - .grid-cols-6 { - grid-template-columns: repeat(6, minmax(0, 1fr)); - } - .grid-cols-7 { - grid-template-columns: repeat(7, minmax(0, 1fr)); - } - .grid-cols-\[1fr\] { - grid-template-columns: 1fr; - } - .grid-cols-\[1fr_1fr\] { - grid-template-columns: 1fr 1fr; - } - .grid-cols-\[1fr_1fr_1fr_1fr_1fr_1fr\] { - grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr; - } .grid-cols-\[auto_1fr_1fr_1fr_1fr_1fr\] { grid-template-columns: auto 1fr 1fr 1fr 1fr 1fr; } .grid-cols-\[auto_1fr_1fr_1fr_1fr_1fr_1fr\] { grid-template-columns: auto 1fr 1fr 1fr 1fr 1fr 1fr; } + .grid-cols-subgrid { + grid-template-columns: subgrid; + } .grid-rows-6 { grid-template-rows: repeat(6, minmax(0, 1fr)); } @@ -487,15 +481,15 @@ .mask-repeat { mask-repeat: repeat; } + .p-0 { + padding: calc(var(--spacing) * 0); + } .p-1 { padding: calc(var(--spacing) * 1); } .p-2 { padding: calc(var(--spacing) * 2); } - .p-4 { - padding: calc(var(--spacing) * 4); - } .p-8 { padding: calc(var(--spacing) * 8); } diff --git a/Backend/templates/headerComponent_templ.go b/Backend/templates/headerComponent_templ.go index aeef6ec..44846c8 100644 --- a/Backend/templates/headerComponent_templ.go +++ b/Backend/templates/headerComponent_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.924 +// templ: version: v0.3.833 package templates //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/Backend/templates/pages_templ.go b/Backend/templates/pages_templ.go index b7915ec..c0d73e3 100644 --- a/Backend/templates/pages_templ.go +++ b/Backend/templates/pages_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.924 +// templ: version: v0.3.833 package templates //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/Backend/templates/pdf.templ b/Backend/templates/pdf.templ index 2e87875..2aacb05 100644 --- a/Backend/templates/pdf.templ +++ b/Backend/templates/pdf.templ @@ -1,8 +1,8 @@ package templates import ( - "arbeitszeitmessung/helper" "arbeitszeitmessung/models" + "arbeitszeitmessung/helper" "time" ) @@ -18,29 +18,33 @@ templ PDFReportEmploye(e models.User, workDays []models.WorkDay, tsStart time.Ti
Arbeitszeit:
Überstunden:
-KW
-Montag
-Dienstag
-Mittwoche
-Donnerstag
-Freitag
- //Samstag
-{ kw }
- for d := time.Monday; d < tsStart.Weekday(); d++ { - - } - for _, day := range workDays { +{ kw }
+Kommen
+Gehen
+Arbeitsart
+Stunden gesamt
+Pause
+Überstunden
+ for _, day := range workDays{ if day.Day.Weekday() == time.Monday { -{ helper.GetKW(day.Day) }
+Wochenende
+ } +{ day.Day.Format("02.01.2006") }
+ +{ day.Bookings[bookingI].Timestamp.Format("15:04") }
+{ day.Bookings[bookingI+1].Timestamp.Format("15:04") }
+{ day.Bookings[bookingI].BookingType.Name }
} -{ helper.GetFirst(day.GetWorkTimeString()) }
- for i := 0; i < len(day.Bookings); i += 2 { -{ day.Bookings[i].Timestamp.Format("15:04") } - { day.Bookings[i+1].Timestamp.Format("15:04") }
- }{ work }
+{ pause }
+{ helper.FormatDuration(day.CalcOvertime(e)) }
} +Arbeitszeit:
Überstunden:
KW
Montag
Dienstag
Mittwoche
Donnerstag
Freitag
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "
Arbeitszeit:
Überstunden:
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var4 string templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(kw) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 29, Col: 10} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 22, Col: 34} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "Kommen
Gehen
Arbeitsart
Stunden gesamt
Pause
Überstunden
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - for d := time.Monday; d < tsStart.Weekday(); d++ { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } for _, day := range workDays { if day.Day.Weekday() == time.Monday { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var5 string - templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(helper.GetKW(day.Day)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 35, Col: 31} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "Wochenende
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var6 string - templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(helper.GetFirst(day.GetWorkTimeString())) + var templ_7745c5c3_Var5 string + templ_7745c5c3_Var5, 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/pdf.templ`, Line: 38, Col: 50} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 33, Col: 61} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) + _, 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, 9, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "") + for bookingI := 0; bookingI < len(day.Bookings); bookingI += 2 { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var6 string + templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(day.Bookings[bookingI].Timestamp.Format("15:04")) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 37, Col: 82} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var7 string - templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(day.Bookings[i].Timestamp.Format("15:04")) + templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(day.Bookings[bookingI+1].Timestamp.Format("15:04")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 40, Col: 52} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 38, Col: 84} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, " - ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var8 string - templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(day.Bookings[i+1].Timestamp.Format("15:04")) + templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(day.Bookings[bookingI].BookingType.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 40, Col: 102} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 39, Col: 73} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "") + 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/pdf.templ`, Line: 43, Col: 38} + } + _, 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, 14, "
") + 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/pdf.templ`, Line: 44, Col: 39} + } + _, 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, 15, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var11 string + templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(helper.FormatDuration(day.CalcOvertime(e))) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 45, Col: 76} + } + _, 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, 16, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "