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") }

+ +
+ for bookingI := 0; bookingI < len(day.Bookings); bookingI+= 2 { +

{ 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 := day.GetWorkTimeString() }} +

{ work }

+

{ pause }

+

{ helper.FormatDuration(day.CalcOvertime(e)) }

} +
} diff --git a/Backend/templates/pdf_templ.go b/Backend/templates/pdf_templ.go index 3ebeaa7..5430b0f 100644 --- a/Backend/templates/pdf_templ.go +++ b/Backend/templates/pdf_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. @@ -67,104 +67,142 @@ func PDFReportEmploye(e models.User, workDays []models.WorkDay, tsStart time.Tim if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "

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, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - for i := 0; i < len(day.Bookings); i += 2 { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "

") + 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, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + work, pause := day.GetWorkTimeString() + 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, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/Backend/templates/teamComponents_templ.go b/Backend/templates/teamComponents_templ.go index 0fdead7..9dd6ee3 100644 --- a/Backend/templates/teamComponents_templ.go +++ b/Backend/templates/teamComponents_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/timeComponents_templ.go b/Backend/templates/timeComponents_templ.go index 557daaf..aca6f54 100644 --- a/Backend/templates/timeComponents_templ.go +++ b/Backend/templates/timeComponents_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/DB/initdb/02_sample_data.sql b/DB/initdb/02_sample_data.sql index 5cf0910..a0ee8f3 100644 --- a/DB/initdb/02_sample_data.sql +++ b/DB/initdb/02_sample_data.sql @@ -1,5 +1,5 @@ -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 -(123, 't', 'Kim', 'Mustermensch', '2003-02-01', '08963', 'Altenburger Str. 44A', 1, 'aaaa-aaaa', 1, 8, '07:00:00', '20:00:00', 0); +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 +(123, 't', 'Kim', 'Mustermensch', '2003-02-01', '08963', 'Altenburger Str. 44A', 1, 'aaaa-aaaa', 1, 8, 40, '07:00:00', '20:00:00', 0); INSERT INTO "user_password" ("personal_nummer", "pass_hash") VALUES (123, crypt('max_pass', gen_salt('bf'))); diff --git a/db.sql b/db.sql index 6f4d7fc..4ba7830 100644 --- a/db.sql +++ b/db.sql @@ -198,11 +198,11 @@ ORDER BY d.work_date; WITH params AS ( SELECT - 'acde-edca'::varchar AS card_uid, + 'aaaa-aaaa'::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::float AS pause_probability, 0.2::float AS absence_probability ), days AS ( @@ -249,8 +249,8 @@ all_bookings AS ( 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 + INSERT INTO anwesenheit ("timestamp", "card_uid", "check_in_out", "geraet_id", "anwesenheit_typ") + SELECT ts, card_uid, check_in_out, geraet_id, 1 as anwesenheit_typ FROM all_bookings WHERE ts <= NOW() ORDER BY work_date, ts diff --git a/package-lock.json b/package-lock.json index 7e4924a..479ae0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,8 @@ }, "devDependencies": { "@iconify-json/material-symbols-light": "^1.2.33", - "@iconify/tailwind4": "^1.0.6" + "@iconify/tailwind4": "^1.0.6", + "prettier": "^3.6.2" } }, "node_modules/@antfu/install-pkg": { @@ -1294,6 +1295,22 @@ "pathe": "^2.0.3" } }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/quansync": { "version": "0.2.11", "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", diff --git a/package.json b/package.json index fd0ca1d..7d00931 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ }, "devDependencies": { "@iconify-json/material-symbols-light": "^1.2.33", - "@iconify/tailwind4": "^1.0.6" + "@iconify/tailwind4": "^1.0.6", + "prettier": "^3.6.2" } }