diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 7f927de..e176afd 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -39,34 +39,34 @@ jobs: push: true context: Backend tags: ${{ steps.meta.outputs.tags }} - document-creator: - name: Build Document Creator - 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: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: git.letsstein.de/tom/arbeitszeitmessung-doc-creator - tags: | - type=raw,value=latest - type=pep440,pattern={{version}} - - name: Build and push - uses: docker/build-push-action@v6 - with: - platforms: linux/amd64,linux/arm64 - push: true - context: DocumentCreator - tags: ${{ steps.meta.outputs.tags }} + # document-creator: + # name: Build Document Creator + # 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: Extract metadata (tags, labels) for Docker + # id: meta + # uses: docker/metadata-action@v5 + # with: + # images: git.letsstein.de/tom/arbeitszeitmessung-doc-creator + # tags: | + # type=raw,value=latest + # type=pep440,pattern={{version}} + # - name: Build and push + # uses: docker/build-push-action@v6 + # with: + # platforms: linux/amd64,linux/arm64 + # push: true + # context: DocumentCreator + # tags: ${{ steps.meta.outputs.tags }} diff --git a/Backend/Dockerfile b/Backend/Dockerfile index 3443313..9b77f06 100644 --- a/Backend/Dockerfile +++ b/Backend/Dockerfile @@ -14,9 +14,11 @@ COPY . . RUN go build -o server . FROM alpine:3.22 -RUN apk add --no-cache tzdata +RUN apk add --no-cache tzdata typst WORKDIR /app COPY --from=build /app/server /app/server +COPY ./doc/static /doc/static +COPY ./doc/templates /doc/templates COPY /static /app/static ENTRYPOINT ["./server"] diff --git a/Backend/doc/static/logo.png b/Backend/doc/static/logo.png new file mode 100644 index 0000000..53694f9 Binary files /dev/null and b/Backend/doc/static/logo.png differ diff --git a/Backend/doc/templates/abrechnung.typ b/Backend/doc/templates/abrechnung.typ new file mode 100644 index 0000000..0735264 --- /dev/null +++ b/Backend/doc/templates/abrechnung.typ @@ -0,0 +1,97 @@ +#let table-header(..headers) = { + table.header( + ..headers.pos().map(h => strong(h)) + ) +} + + +#let abrechnung(meta, days) = { + set page(paper: "a4", margin: (x:1.5cm, y:2.25cm), + footer:[#grid( + columns: (3fr, .65fr), + align: left + horizon, + inset: .5em, + [#meta.EmployeeName -- #meta.TimeRange], grid.cell(rowspan: 2)[#image("/static/logo.png")], + [Arbeitszeitrechnung maschinell erstellt am #meta.CurrentTimestamp], + ) + ]) + set text(font: "Noto Sans", size:10pt, fill: luma(10%)) + set table( + stroke: 0.5pt + luma(10%), + inset: .5em, + align: center + horizon, + ) + show text: it => { + if it.text == "0min"{ + text(oklch(70.8%, 0, 0deg))[#it] + }else if it.text.starts-with("-"){ + text(red)[#it] + }else{ + it + } + } + + + [= Abrechnung Arbeitszeit -- #meta.EmployeeName] + + [Zeitraum: #meta.TimeRange] + + table( + columns: (1fr, 1fr, 1fr, 1fr, 1fr, 1fr, .875fr, 1.25fr), + fill: (x, y) => + if y == 0 { oklch(87%, 0, 0deg) }, + table-header( + [Datum], [Kommen], [Gehen], [Arbeitsart], [Stunden], [Kurzarbeit], [Pause], [Überstunden] + ), + .. for day in days { + ( + [#day.Date], + if day.DayParts.len() == 0{ + table.cell(colspan: 3)[Keine Buchungen] + }else if day.DayParts.len() == 1 and not day.DayParts.first().IsWorkDay{ + table.cell(colspan: 3)[#day.DayParts.first().WorkType] + } + else { + table.cell(colspan: 3, inset: 0em)[ + #table( + columns: (1fr, 1fr, 1fr), + .. for Zeit in day.DayParts { + ( + if Zeit.IsWorkDay{ + ( + table.cell()[#Zeit.BookingFrom], + table.cell()[#Zeit.BookingTo], + table.cell()[#Zeit.WorkType], + ) + }else{ + (table.cell(colspan: 3)[#Zeit.WorkType],) + } + ) + }, + ) + ] + }, + [#day.Worktime], + [#day.Kurzarbeit], + [#day.Pausetime], + [#day.Overtime], + ) + if day.IsFriday { + ( table.cell(colspan: 8, fill: oklch(87%, 0, 0deg))[Wochenende], ) // note the trailing comma + } + } + ) + + table( + columns: (3fr, 1fr), + align: right, + inset: (x: .25em, y:.75em), + stroke: none, + table.hline(start: 0, end: 2, stroke: stroke(dash:"dashed", thickness:.5pt)), + [Arbeitszeit :], table.cell(align: left)[#meta.WorkTime], + [Kurzarbeit :], table.cell(align: left)[#meta.Kurzarbeit], + [Überstunden :], table.cell(align: left)[#meta.Overtime], + [Überstunden lfd. :],table.cell(align: left)[#meta.OvertimeTotal], + table.hline(start: 0, end: 2), +) +} diff --git a/Backend/endpoints/pdf-create.go b/Backend/endpoints/pdf-create.go index 2557c0f..c2d2b4b 100644 --- a/Backend/endpoints/pdf-create.go +++ b/Backend/endpoints/pdf-create.go @@ -198,8 +198,9 @@ func renderPDFSingle(data []typstData) (bytes.Buffer, error) { var markup bytes.Buffer var output bytes.Buffer - typstCLI := typst.DockerExec{ - ContainerName: helper.GetEnv("TYPST_CONTAINER", "arbeitszeitmessung-doc-creator"), + typstCLI := typst.CLI{ + WorkingDirectory: "/doc/", + // ContainerName: helper.GetEnv("TYPST_CONTAINER", "arbeitszeitmessung-doc-creator"), } if err := typst.InjectValues(&markup, map[string]any{"data": data}); err != nil { @@ -225,8 +226,9 @@ func renderPDFSingle(data []typstData) (bytes.Buffer, error) { func renderPDFMulti(data []typstData) ([]bytes.Buffer, error) { var outputMulti []bytes.Buffer - typstRender := typst.DockerExec{ - ContainerName: helper.GetEnv("TYPST_CONTAINER", "arbeitszeitmessung-doc-creator"), + typstRender := typst.CLI{ + WorkingDirectory: "/doc/", + // ContainerName: helper.GetEnv("TYPST_CONTAINER", "arbeitszeitmessung-doc-creator"), } for _, d := range data { diff --git a/Backend/main.go b/Backend/main.go index 4832dc0..731173c 100644 --- a/Backend/main.go +++ b/Backend/main.go @@ -17,7 +17,16 @@ import ( func main() { var err error var logLevel slog.LevelVar - logLevel.Set(slog.LevelWarn) + switch helper.GetEnv("LOG_LEVEL", "warn") { + case "debug": + logLevel.Set(slog.LevelDebug) + case "info": + logLevel.Set(slog.LevelInfo) + case "warn": + logLevel.Set(slog.LevelWarn) + case "error": + logLevel.Set(slog.LevelError) + } logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: &logLevel})) slog.SetDefault(logger) diff --git a/Docker/docker-compose.yml b/Docker/docker-compose.yml index a5fc78d..7ef4fa2 100644 --- a/Docker/docker-compose.yml +++ b/Docker/docker-compose.yml @@ -25,12 +25,11 @@ services: - ${WEB_PORT}:8080 depends_on: - db - - document-creator volumes: - - ../logs:/app/Backend/logs + - ${LOG_PATH}:/app/logs restart: unless-stopped - document-creator: - image: git.letsstein.de/tom/arbeitszeitmessung-doc-creator - container_name: ${TYPST_CONTAINER} - restart: unless-stopped + # document-creator: + # image: git.letsstein.de/tom/arbeitszeitmessung-doc-creator + # container_name: ${TYPST_CONTAINER} + # restart: unless-stopped