Compare commits
34 Commits
2.0.0-alph
...
ddaf07f15d
| Author | SHA1 | Date | |
|---|---|---|---|
| ddaf07f15d | |||
| bec94deaae | |||
| e781f52f6d | |||
| ba034f1c33 | |||
| 41c34c42cf | |||
| 6998d07c6b | |||
| a5f5c37225 | |||
| fdda0ea669 | |||
| c10ab98997 | |||
| 8dc8c4eed3 | |||
| 3f49da49b6 | |||
| 18b2cbc074 | |||
| 560c539b19 | |||
| 502955d32f | |||
| cfd77ae28d | |||
| 1daf4db167 | |||
| 3322f7e9bc | |||
| 4b9824c714 | |||
| 7ac6c5f9b8 | |||
| f9fc3d91d1 | |||
| f0de9961dc | |||
| 4ded8632e5 | |||
| b2af48463c | |||
| 0b72147e02 | |||
| d1b46cf894 | |||
| 7c45ef6112 | |||
| 2b17eb6854 | |||
| 7fae75be75 | |||
| b7de3ade65 | |||
| 855cd6f34f | |||
| 3439fff841 | |||
| f562ef2a33 | |||
| 177fbdeb3f | |||
| 82eb8018a6 |
@@ -6,10 +6,11 @@ on:
|
||||
- "*"
|
||||
branches:
|
||||
- main
|
||||
- dev/main
|
||||
|
||||
jobs:
|
||||
webserver:
|
||||
name: Build Go Image and Upload
|
||||
name: Build Webserver
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -39,34 +40,34 @@ jobs:
|
||||
push: true
|
||||
context: Backend
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
document-creator:
|
||||
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: 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: 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 }}
|
||||
|
||||
@@ -46,6 +46,8 @@ jobs:
|
||||
key: arbeitszeitmessung-${{ steps.hash-go.outputs.hash }}
|
||||
restore-keys: |-
|
||||
arbeitszeitmessung-
|
||||
- name: Render Templ files
|
||||
run: cd Backend && go install github.com/a-h/templ/cmd/templ@v0.3.977 && templ generate
|
||||
- name: Run Go Tests
|
||||
run: cd Backend && mkdir .test && go test ./... -coverprofile=.test/coverage.out -json > .test/report.json
|
||||
- name: Verify coverage report exists
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -40,3 +40,5 @@ atlas.hcl
|
||||
.scannerwork
|
||||
Backend/logs
|
||||
.worktime.txt
|
||||
|
||||
*_templ.go
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
db
|
||||
Dockerfile
|
||||
*_templ.go
|
||||
|
||||
@@ -1,22 +1,34 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM --platform=$BUILDPLATFORM golang:alpine AS build
|
||||
FROM --platform=$BUILDPLATFORM golang:alpine AS base
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ENV CGO_ENABLED=0 \
|
||||
GOOS=$TARGETOS \
|
||||
GOARCH=$TARGETARCH
|
||||
|
||||
FROM base AS fetch-stage
|
||||
WORKDIR /app
|
||||
COPY go.mod go.sum /app/
|
||||
RUN go mod download && go mod verify
|
||||
|
||||
COPY . .
|
||||
FROM ghcr.io/a-h/templ:v0.3.977 AS generate-stage
|
||||
COPY --chown=65532:65532 --chmod=755 . /app
|
||||
WORKDIR /app
|
||||
RUN ["templ", "generate"]
|
||||
|
||||
FROM base AS build
|
||||
COPY --from=generate-stage /app /app
|
||||
WORKDIR /app
|
||||
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 migrations /app/migrations
|
||||
|
||||
COPY doc /doc
|
||||
|
||||
COPY /static /app/static
|
||||
ENTRYPOINT ["./server"]
|
||||
|
||||
@@ -2,5 +2,16 @@ test:
|
||||
mkdir -p .test
|
||||
go test ./... -coverprofile=.test/coverage.out -json > .test/report.json
|
||||
|
||||
scan:
|
||||
sonar-scanner -Dsonar.token=sqa_ca8394c93a728d6cff96703955288d8902c15200
|
||||
# scan:
|
||||
# sonar-scanner -Dsonar.token=sqa_ca8394c93a728d6cff96703955288d8902c15200
|
||||
|
||||
# complete live run
|
||||
live:
|
||||
make -j2 live/templ live/tailwindcss
|
||||
|
||||
live/templ:
|
||||
templ generate --watch --proxy="http://localhost:8080" --cmd="go run ." --open-browser=false
|
||||
|
||||
live/tailwindcss:
|
||||
npx --yes tailwindcss -i ./src/main.css -o ./static/css/styles.css --watch
|
||||
#--minify
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
package main
|
||||
|
||||
// this file handles the db connection and the
|
||||
// db migration
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
"github.com/golang-migrate/migrate/v4/database/postgres"
|
||||
_ "github.com/golang-migrate/migrate/v4/database/postgres"
|
||||
_ "github.com/golang-migrate/migrate/v4/source/file"
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
@@ -16,6 +24,45 @@ func OpenDatabase() (models.IDatabase, error) {
|
||||
dbPassword := helper.GetEnv("POSTGRES_API_PASS", "password")
|
||||
dbTz := helper.GetEnv("TZ", "Europe/Berlin")
|
||||
|
||||
connStr := fmt.Sprintf("postgres://%s:%s@%s:5432/%s?sslmode=disable&TimeZone=%s", dbUser, dbPassword, dbHost, dbName, dbTz)
|
||||
connStr := fmt.Sprintf(
|
||||
"host=%s user=%s dbname=%s password=%s sslmode=disable TimeZone=%s",
|
||||
dbHost, dbUser, dbName, dbPassword, dbTz)
|
||||
|
||||
return sql.Open("postgres", connStr)
|
||||
}
|
||||
|
||||
func Migrate() error {
|
||||
dbHost := helper.GetEnv("POSTGRES_HOST", "localhost")
|
||||
dbName := helper.GetEnv("POSTGRES_DB", "arbeitszeitmessung")
|
||||
dbPassword := helper.GetEnv("POSTGRES_PASSWORD", "password")
|
||||
dbTz := helper.GetEnv("TZ", "Europe/Berlin")
|
||||
|
||||
connStr := fmt.Sprintf(
|
||||
"host=%s user=%s dbname=%s password=%s sslmode=disable TimeZone=%s",
|
||||
dbHost, "migrate", dbName, dbPassword, dbTz)
|
||||
|
||||
db, err := sql.Open("postgres", connStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
driver, err := postgres.WithInstance(db, &postgres.Config{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := migrate.NewWithDatabaseInstance("file:///app/migrations", "postgres", driver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
slog.Info("Connected to database. Running migrations now.")
|
||||
|
||||
// Migrate all the way up ...
|
||||
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
|
||||
return err
|
||||
}
|
||||
|
||||
slog.Info("Finished migrations starting webserver.")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
53
Backend/endpoints/auto-feiertage.go
Normal file
53
Backend/endpoints/auto-feiertage.go
Normal file
@@ -0,0 +1,53 @@
|
||||
// endpoints contains all http endpoints
|
||||
// for more complex endpoints the *Handler function is executed first
|
||||
// by the main programm and it will then run other functions as needed
|
||||
//
|
||||
// the filenames represent the route/url for the given endpoint
|
||||
// when "-" is a "/" so this file is server at "/auto/feiertage"
|
||||
|
||||
package endpoints
|
||||
|
||||
// this endpoint will be called by crontab and generates the public holidays for a given year
|
||||
// after that manually added holidays with a "wiederholen" flag are copied over to the new year
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper/paramParser"
|
||||
"arbeitszeitmessung/models"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/wlbr/feiertage"
|
||||
)
|
||||
|
||||
func FeiertagsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
pp := paramParser.New(r.URL.Query())
|
||||
slog.Debug("Generating Holidays")
|
||||
from := pp.ParseTimestampFallback("from", "2006", time.Now().AddDate(-1, 0, -time.Now().YearDay()))
|
||||
to := pp.ParseTimestampFallback("to", "2006", time.Now().AddDate(0, 0, -time.Now().YearDay()+1))
|
||||
|
||||
var publicHolidays map[string]models.PublicHoliday = make(map[string]models.PublicHoliday)
|
||||
|
||||
yearDelta := to.Year() - from.Year()
|
||||
var holidays = feiertage.Sachsen(to.Year(), true)
|
||||
for _, f := range holidays.Feiertage {
|
||||
publicHolidays[f.Time.Format(time.DateOnly)] = models.NewHolidayFromFeiertag(f)
|
||||
}
|
||||
|
||||
repeatingHolidays, err := models.GetRepeatingHolidays(from, to.AddDate(0, 0, -1))
|
||||
if err != nil {
|
||||
slog.Warn("Error getting holidays", slog.Any("Error", err))
|
||||
}
|
||||
slog.Debug("Found repeating Holidays", "num", len(repeatingHolidays), "from", from, "to", to, "yeardelta", yearDelta)
|
||||
for _, day := range repeatingHolidays {
|
||||
day.Time = day.Time.AddDate(yearDelta, 0, 0)
|
||||
publicHolidays[day.Date().Format(time.DateOnly)] = day
|
||||
}
|
||||
slog.Debug("Added repeating holidays", "num", len(holidays.Feiertage))
|
||||
for _, feiertag := range publicHolidays {
|
||||
slog.Debug("Found holiday", "holiday", feiertag)
|
||||
if err := feiertag.Insert(); err != nil {
|
||||
slog.Warn("Error inserting Feiertag", slog.Any("Error", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
package endpoints
|
||||
|
||||
// this served as "/auto/kurzarbeit" will add a booking to every kurzarbeitstag
|
||||
// to make them reach the full lenght of workday.
|
||||
//
|
||||
// right now this is not in use because the time is calculated
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/helper/paramParser"
|
||||
@@ -58,8 +63,8 @@ func fillKurzarbeit(r *http.Request, w http.ResponseWriter) {
|
||||
workday, _ := day.(*models.WorkDay)
|
||||
|
||||
lastBookingTime := workday.Bookings[len(workday.Bookings)-1].Timestamp
|
||||
kurzarbeitBegin := (*models.Booking).New(nil, user.CardUID, 0, 1, bookingTypeKurzarbeit.Id)
|
||||
kurzarbeitEnd := (*models.Booking).New(nil, user.CardUID, 0, 2, bookingTypeKurzarbeit.Id)
|
||||
kurzarbeitBegin := (*models.Booking).NewBooking(nil, user.CardUID, 0, 1, bookingTypeKurzarbeit.Id)
|
||||
kurzarbeitEnd := (*models.Booking).NewBooking(nil, user.CardUID, 0, 2, bookingTypeKurzarbeit.Id)
|
||||
kurzarbeitBegin.Timestamp = lastBookingTime.Add(time.Minute)
|
||||
kurzarbeitEnd.Timestamp = lastBookingTime.Add(worktimeKurzarbeit)
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package endpoints
|
||||
|
||||
// this endpoint served at "/auto/logout" will be executed by crontab
|
||||
// and will log out all users that are currently still logged in
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
@@ -21,7 +24,7 @@ func LogoutHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func autoLogout(w http.ResponseWriter) {
|
||||
users, err := models.GetAllUsers()
|
||||
var logged_out_users []models.User
|
||||
var loggedOutUsers []models.User
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting user list %v\n", err)
|
||||
}
|
||||
@@ -31,7 +34,7 @@ func autoLogout(w http.ResponseWriter) {
|
||||
if err != nil {
|
||||
fmt.Printf("Error logging out user %v\n", err)
|
||||
} else {
|
||||
logged_out_users = append(logged_out_users, user)
|
||||
loggedOutUsers = append(loggedOutUsers, user)
|
||||
log.Printf("Automaticaly logged out user %s, %s ", user.Name, user.Vorname)
|
||||
}
|
||||
}
|
||||
@@ -39,6 +42,6 @@ func autoLogout(w http.ResponseWriter) {
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(logged_out_users)
|
||||
json.NewEncoder(w).Encode(loggedOutUsers)
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package endpoints
|
||||
|
||||
// this endpoint served at "/pdf/create" accepts the contents from the pdf form
|
||||
// and renders a pdf according to this form
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/helper/paramParser"
|
||||
"arbeitszeitmessung/models"
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -15,6 +19,7 @@ import (
|
||||
)
|
||||
|
||||
const DE_DATE string = "02.01.2006"
|
||||
const FILE_YEAR_MONTH string = "2006_01"
|
||||
|
||||
func convertDaysToTypst(days []models.IWorkDay, u models.User) ([]typstDay, error) {
|
||||
var typstDays []typstDay
|
||||
@@ -43,12 +48,17 @@ func convertDaysToTypst(days []models.IWorkDay, u models.User) ([]typstDay, erro
|
||||
|
||||
func convertDayToTypstDayParts(day models.IWorkDay, user models.User) []typstDayPart {
|
||||
var typstDayParts []typstDayPart
|
||||
if day.IsWorkDay() {
|
||||
switch day.Type() {
|
||||
case models.DayTypeWorkday:
|
||||
workDay, _ := day.(*models.WorkDay)
|
||||
for i := 0; i < len(workDay.Bookings); i += 2 {
|
||||
var typstDayPart typstDayPart
|
||||
typstDayPart.BookingFrom = workDay.Bookings[i].Timestamp.Format("15:04")
|
||||
if i+1 < len(workDay.Bookings) {
|
||||
typstDayPart.BookingTo = workDay.Bookings[i+1].Timestamp.Format("15:04")
|
||||
} else {
|
||||
typstDayPart.BookingTo = workDay.Bookings[i].Timestamp.Format("15:04")
|
||||
}
|
||||
typstDayPart.WorkType = workDay.Bookings[i].BookingType.Name
|
||||
typstDayPart.IsWorkDay = true
|
||||
typstDayParts = append(typstDayParts, typstDayPart)
|
||||
@@ -62,13 +72,12 @@ func convertDayToTypstDayParts(day models.IWorkDay, user models.User) []typstDay
|
||||
IsWorkDay: true,
|
||||
})
|
||||
}
|
||||
if workdayAbsence := workDay.GetWorktimeAbsence(); (workdayAbsence != models.Absence{}) {
|
||||
typstDayParts = append(typstDayParts, typstDayPart{IsWorkDay: false, WorkType: workdayAbsence.AbwesenheitTyp.Name})
|
||||
case models.DayTypeCompound:
|
||||
for _, c := range day.(*models.CompoundDay).DayParts {
|
||||
typstDayParts = append(typstDayParts, convertDayToTypstDayParts(c, user)...)
|
||||
}
|
||||
|
||||
} else {
|
||||
absentDay, _ := day.(*models.Absence)
|
||||
typstDayParts = append(typstDayParts, typstDayPart{IsWorkDay: false, WorkType: absentDay.AbwesenheitTyp.Name})
|
||||
default:
|
||||
typstDayParts = append(typstDayParts, typstDayPart{IsWorkDay: false, WorkType: day.ToString()})
|
||||
}
|
||||
return typstDayParts
|
||||
}
|
||||
@@ -92,17 +101,43 @@ func PDFCreateController(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
output, err := createReports(user, employes, startDate)
|
||||
if err != nil {
|
||||
slog.Warn("Could not create pdf report", slog.Any("Error", err))
|
||||
n := 0
|
||||
for _, e := range employes {
|
||||
if user.IsSuperior(e) {
|
||||
employes[n] = e
|
||||
n++
|
||||
}
|
||||
}
|
||||
employes = employes[:n]
|
||||
|
||||
reportData := createReports(employes, startDate)
|
||||
|
||||
switch pp.ParseStringFallback("output", "render") {
|
||||
case "render":
|
||||
output, err := renderPDFSingle(reportData)
|
||||
if err != nil {
|
||||
slog.Warn("Could not create pdf report", slog.Any("Error", err))
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
w.Header().Set("Content-type", "application/pdf")
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=Monatsabrechnung_%s", startDate.Format(FILE_YEAR_MONTH)))
|
||||
output.WriteTo(w)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
case "download":
|
||||
panic("Not implemented")
|
||||
pdfReports, err := renderPDFMulti(reportData)
|
||||
if err != nil {
|
||||
slog.Warn("Could not create pdf report", slog.Any("Error", err))
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
output, err := zipPfd(pdfReports, &reportData)
|
||||
if err != nil {
|
||||
slog.Warn("Could not create pdf report", slog.Any("Error", err))
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
w.Header().Set("Content-type", "application/zip")
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachement; filename=Monatsabrechnung_%s", startDate.Format(FILE_YEAR_MONTH)))
|
||||
output.WriteTo(w)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -110,23 +145,24 @@ func PDFCreateController(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func createReports(user models.User, employes []models.User, startDate time.Time) (bytes.Buffer, error) {
|
||||
func createReports(employes []models.User, startDate time.Time) []typstData {
|
||||
startDate = helper.GetFirstOfMonth(startDate)
|
||||
endDate := startDate.AddDate(0, 1, -1)
|
||||
|
||||
var employeData []typstData
|
||||
for _, employe := range employes {
|
||||
if data, err := createEmployeReport(employe, startDate, endDate); err != nil {
|
||||
slog.Warn("Error when creating employeReport", slog.Any("user", employe), slog.Any("error", err))
|
||||
for _, employee := range employes {
|
||||
if data, err := createEmployeReport(employee, startDate, endDate); err != nil {
|
||||
slog.Warn("Error when creating employeReport", slog.Any("user", employee), slog.Any("error", err))
|
||||
} else {
|
||||
employeData = append(employeData, data)
|
||||
}
|
||||
}
|
||||
return renderPDF(employeData)
|
||||
return employeData
|
||||
}
|
||||
|
||||
func createEmployeReport(employee models.User, startDate, endDate time.Time) (typstData, error) {
|
||||
targetHoursThisMonth := employee.ArbeitszeitProWocheFrac(.2) * time.Duration(helper.GetWorkingDays(startDate, endDate))
|
||||
publicHolidays, err := models.GetHolidaysFromTo(startDate, endDate)
|
||||
targetHoursThisMonth := employee.ArbeitszeitProWocheFrac(.2) * time.Duration(helper.GetWorkingDays(startDate, endDate)-len(publicHolidays))
|
||||
workDaysThisMonth := models.GetDays(employee, startDate, endDate.AddDate(0, 0, 1), false)
|
||||
|
||||
slog.Debug("Baseline Working hours", "targetHours", targetHoursThisMonth.Hours())
|
||||
@@ -158,13 +194,18 @@ func createEmployeReport(employee models.User, startDate, endDate time.Time) (ty
|
||||
OvertimeTotal: "",
|
||||
CurrentTimestamp: time.Now().Format("02.01.2006 - 15:04 Uhr"),
|
||||
}
|
||||
return typstData{Meta: metadata, Days: typstDays}, nil
|
||||
return typstData{Meta: metadata, Days: typstDays, FileName: fmt.Sprintf("%s_%s.pdf", startDate.Format(FILE_YEAR_MONTH), employee.Name)}, nil
|
||||
}
|
||||
|
||||
func renderPDF(data []typstData) (bytes.Buffer, error) {
|
||||
func renderPDFSingle(data []typstData) (bytes.Buffer, error) {
|
||||
var markup bytes.Buffer
|
||||
var output bytes.Buffer
|
||||
|
||||
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 {
|
||||
return output, err
|
||||
}
|
||||
@@ -179,16 +220,59 @@ func renderPDF(data []typstData) (bytes.Buffer, error) {
|
||||
`)
|
||||
|
||||
// Compile the prepared markup with Typst and write the result it into `output.pdf`.
|
||||
|
||||
typstCLI := typst.DockerExec{
|
||||
ContainerName: helper.GetEnv("TYPST_CONTAINER", "arbeitszeitmessung-doc-creator"),
|
||||
}
|
||||
if err := typstCLI.Compile(&markup, &output, nil); err != nil {
|
||||
return output, err
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func renderPDFMulti(data []typstData) ([]bytes.Buffer, error) {
|
||||
var outputMulti []bytes.Buffer
|
||||
|
||||
typstRender := typst.CLI{
|
||||
WorkingDirectory: "/doc/",
|
||||
// ContainerName: helper.GetEnv("TYPST_CONTAINER", "arbeitszeitmessung-doc-creator"),
|
||||
}
|
||||
|
||||
for _, d := range data {
|
||||
var markup bytes.Buffer
|
||||
var outputSingle bytes.Buffer
|
||||
if err := typst.InjectValues(&markup, map[string]any{"meta": d.Meta, "days": d.Days}); err != nil {
|
||||
return outputMulti, err
|
||||
}
|
||||
markup.WriteString(`
|
||||
#import "templates/abrechnung.typ": abrechnung
|
||||
#abrechnung(meta, days)
|
||||
`)
|
||||
|
||||
if err := typstRender.Compile(&markup, &outputSingle, nil); err != nil {
|
||||
return outputMulti, err
|
||||
}
|
||||
outputMulti = append(outputMulti, outputSingle)
|
||||
}
|
||||
return outputMulti, nil
|
||||
}
|
||||
|
||||
func zipPfd(pdfReports []bytes.Buffer, reportData *[]typstData) (bytes.Buffer, error) {
|
||||
var zipOutput bytes.Buffer
|
||||
|
||||
zipWriter := zip.NewWriter(&zipOutput)
|
||||
for index, report := range pdfReports {
|
||||
zipFile, err := zipWriter.Create((*reportData)[index].FileName)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
_, err = zipFile.Write(report.Bytes())
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure to check the error on Close.
|
||||
err := zipWriter.Close()
|
||||
return zipOutput, err
|
||||
}
|
||||
|
||||
type typstMetadata struct {
|
||||
TimeRange string `json:"time-range"`
|
||||
EmployeeName string `json:"employee-name"`
|
||||
@@ -219,4 +303,5 @@ type typstDay struct {
|
||||
type typstData struct {
|
||||
Meta typstMetadata `json:"meta"`
|
||||
Days []typstDay `json:"days"`
|
||||
FileName string
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package endpoints
|
||||
|
||||
// this endpoint served at "/pdf" handles the rendering of the pdf form
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package endpoints
|
||||
|
||||
// this endpoint served at "/team/presence" shows the presence page
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
@@ -8,7 +10,7 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func TeamPresenceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
func PresenceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
helper.RequiresLogin(Session, w, r)
|
||||
helper.SetCors(w)
|
||||
switch r.Method {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package endpoints
|
||||
|
||||
// this endpoint served at "/team/report" handles the report page
|
||||
// and also the submission/change of reports
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/helper/paramParser"
|
||||
@@ -12,7 +15,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func TeamHandler(w http.ResponseWriter, r *http.Request) {
|
||||
func ReportHandler(w http.ResponseWriter, r *http.Request) {
|
||||
helper.RequiresLogin(Session, w, r)
|
||||
switch r.Method {
|
||||
case http.MethodPost:
|
||||
@@ -76,5 +79,5 @@ func showWeeks(w http.ResponseWriter, r *http.Request) {
|
||||
workWeeks = append(workWeeks, weeks...)
|
||||
}
|
||||
// isRunningWeek := time.Since(lastSub) < 24*5*time.Hour //the last submission is this week and cannot be send yet
|
||||
templates.TeamPage(workWeeks, userWeek).Render(r.Context(), w)
|
||||
templates.ReportPage(workWeeks, userWeek).Render(r.Context(), w)
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package endpoints
|
||||
|
||||
// this endpoint served at "/time/create" is for the esp api and creates bookings
|
||||
// either via HTTP GET or HTTP PUT
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
@@ -7,6 +10,7 @@ import (
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Relevant for arduino inputs -> creates new Booking from get and put method
|
||||
@@ -36,6 +40,7 @@ func createBooking(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
booking := (*models.Booking).FromUrlParams(nil, r.URL.Query())
|
||||
booking.Timestamp = time.Now()
|
||||
if booking.Verify() {
|
||||
err := booking.Insert()
|
||||
if errors.Is(models.SameBookingError{}, err) {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package endpoints
|
||||
|
||||
// this endpoint served at "/time" handles the time page
|
||||
// this includes normal show + creation of bookings from the webpage +
|
||||
// edit functionality
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/helper/paramParser"
|
||||
@@ -37,8 +41,18 @@ func AbsencHandler(w http.ResponseWriter, r *http.Request) {
|
||||
helper.SetCors(w)
|
||||
switch r.Method {
|
||||
case http.MethodPost:
|
||||
err := updateAbsence(r)
|
||||
r.ParseForm()
|
||||
var err error
|
||||
switch r.FormValue("action") {
|
||||
case "insert":
|
||||
err = updateAbsence(r)
|
||||
case "delete":
|
||||
err = deleteAbsence(r)
|
||||
default:
|
||||
slog.Warn("No action found!")
|
||||
}
|
||||
if err != nil {
|
||||
slog.Warn("Error handling absence route ", "error", err)
|
||||
http.Error(w, "Internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -84,7 +98,7 @@ func getBookings(w http.ResponseWriter, r *http.Request) {
|
||||
if day.Date().Before(lastSub) {
|
||||
continue
|
||||
}
|
||||
aggregatedOvertime += day.GetOvertime(user, models.WorktimeBaseDay, false)
|
||||
aggregatedOvertime += day.GetOvertime(user, models.WorktimeBaseDay, true)
|
||||
}
|
||||
if reportedOvertime, err := user.GetReportedOvertime(); err == nil {
|
||||
user.Overtime = (reportedOvertime + aggregatedOvertime).Round(time.Minute)
|
||||
@@ -104,6 +118,21 @@ func getBookings(w http.ResponseWriter, r *http.Request) {
|
||||
templates.TimePage([]models.WorkDay{}, lastSub).Render(ctx, w)
|
||||
}
|
||||
|
||||
func deleteAbsence(r *http.Request) error {
|
||||
r.ParseForm()
|
||||
pp := paramParser.New(r.Form)
|
||||
counterId, err := pp.ParseInt("aw_id")
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
absence, err := models.GetAbsenceById(counterId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return absence.Delete()
|
||||
}
|
||||
|
||||
func updateBooking(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
pp := paramParser.New(r.Form)
|
||||
@@ -133,21 +162,15 @@ func updateBooking(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
newBooking := (*models.Booking).New(nil, user.CardUID, 0, int16(check_in_out), 1)
|
||||
newBooking := (*models.Booking).NewBooking(nil, user.CardUID, 0, int16(check_in_out), 1)
|
||||
newBooking.Timestamp = timestamp
|
||||
if newBooking.Verify() {
|
||||
err = newBooking.InsertWithTimestamp()
|
||||
if err != nil {
|
||||
log.Printf("Error inserting booking %v -> %v\n", newBooking, err)
|
||||
}
|
||||
}
|
||||
case "change":
|
||||
// absenceType, err := strconv.Atoi(r.FormValue("absence"))
|
||||
// if err != nil {
|
||||
// log.Println("Error parsing absence type.", err)
|
||||
// absenceType = 0
|
||||
// }
|
||||
// if absenceType != 0 {
|
||||
// createAbsence(absenceType, user, loc, r)
|
||||
// }
|
||||
for index, possibleBooking := range r.PostForm {
|
||||
if len(index) > 7 && index[:7] == "booking" {
|
||||
booking_id, err := strconv.Atoi(index[8:])
|
||||
|
||||
17
Backend/endpoints/user-login.go
Normal file
17
Backend/endpoints/user-login.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package endpoints
|
||||
|
||||
// this endpoint server at "/user/login" will show the login page or
|
||||
// directly login the user based on the http method used
|
||||
|
||||
import "net/http"
|
||||
|
||||
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
showLoginPage(w, r, true, "")
|
||||
case http.MethodPost:
|
||||
loginUser(w, r)
|
||||
default:
|
||||
http.Error(w, "Method not allowed!", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package endpoints
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/models"
|
||||
"arbeitszeitmessung/templates"
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/alexedwards/scs/v2"
|
||||
)
|
||||
|
||||
var Session *scs.SessionManager
|
||||
|
||||
func CreateSessionManager(lifetime time.Duration) *scs.SessionManager {
|
||||
Session = scs.New()
|
||||
Session.Lifetime = lifetime
|
||||
return Session
|
||||
}
|
||||
|
||||
func showLoginPage(w http.ResponseWriter, r *http.Request, success bool, errorMsg string) {
|
||||
r = r.WithContext(context.WithValue(r.Context(), "session", Session))
|
||||
if Session.Exists(r.Context(), "user") {
|
||||
http.Redirect(w, r, "/time", http.StatusSeeOther)
|
||||
}
|
||||
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)
|
||||
showLoginPage(w, r, false, "Internal error!")
|
||||
return
|
||||
}
|
||||
_personal_nummer := r.FormValue("personal_nummer")
|
||||
if _personal_nummer == "" {
|
||||
log.Println("No personal_nummer provided!")
|
||||
showLoginPage(w, r, false, "Keine Personalnummer gesetzt.")
|
||||
return
|
||||
}
|
||||
personal_nummer, err := strconv.Atoi(_personal_nummer)
|
||||
if err != nil {
|
||||
log.Println("Cannot parse personal nubmer!")
|
||||
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!", 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 (%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
|
||||
}
|
||||
showLoginPage(w, r, false, "")
|
||||
}
|
||||
|
||||
func logoutUser(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("Loggin out user!")
|
||||
err := Session.Destroy(r.Context())
|
||||
if err != nil {
|
||||
log.Println("Error destroying session!", err)
|
||||
}
|
||||
http.Redirect(w, r, "/user/login", http.StatusSeeOther)
|
||||
}
|
||||
@@ -1,6 +1,11 @@
|
||||
package endpoints
|
||||
|
||||
// this endpoint server at "/user/settings" will show the settings page
|
||||
// depeding on which action is taken the user will be logged out or
|
||||
// the password will be changed
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
"arbeitszeitmessung/templates"
|
||||
"context"
|
||||
@@ -8,6 +13,23 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func UserSettingsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
helper.RequiresLogin(Session, w, r)
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
showUserPage(w, r, 0)
|
||||
case http.MethodPost:
|
||||
switch r.FormValue("action") {
|
||||
case "change-pass":
|
||||
changePassword(w, r)
|
||||
case "logout-user":
|
||||
logoutUser(w, r)
|
||||
}
|
||||
default:
|
||||
http.Error(w, "Method not allowed!", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
|
||||
// change user password and store salted hash in db
|
||||
func changePassword(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseForm()
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
package endpoints
|
||||
|
||||
// this is not directly an endpoint as it servers all requests for "/user"
|
||||
// and routes the furter to "login", "logout", and "settings"
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
"arbeitszeitmessung/templates"
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/alexedwards/scs/v2"
|
||||
)
|
||||
|
||||
func UserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -16,31 +26,63 @@ 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, true, "")
|
||||
case http.MethodPost:
|
||||
loginUser(w, r)
|
||||
default:
|
||||
http.Error(w, "Method not allowed!", http.StatusMethodNotAllowed)
|
||||
}
|
||||
var Session *scs.SessionManager
|
||||
|
||||
func CreateSessionManager(lifetime time.Duration) *scs.SessionManager {
|
||||
Session = scs.New()
|
||||
Session.Lifetime = lifetime
|
||||
return Session
|
||||
}
|
||||
|
||||
func UserSettingsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
helper.RequiresLogin(Session, w, r)
|
||||
func showLoginPage(w http.ResponseWriter, r *http.Request, success bool, errorMsg string) {
|
||||
r = r.WithContext(context.WithValue(r.Context(), "session", Session))
|
||||
if Session.Exists(r.Context(), "user") {
|
||||
http.Redirect(w, r, "/time", http.StatusSeeOther)
|
||||
}
|
||||
templates.LoginPage(success, errorMsg).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
showUserPage(w, r, 0)
|
||||
case http.MethodPost:
|
||||
switch r.FormValue("action") {
|
||||
case "change-pass":
|
||||
changePassword(w, r)
|
||||
case "logout-user":
|
||||
logoutUser(w, r)
|
||||
func loginUser(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
log.Println("Error parsing form!", err)
|
||||
showLoginPage(w, r, false, "Internal error!")
|
||||
return
|
||||
}
|
||||
default:
|
||||
http.Error(w, "Method not allowed!", http.StatusMethodNotAllowed)
|
||||
_personal_nummer := r.FormValue("personal_nummer")
|
||||
if _personal_nummer == "" {
|
||||
log.Println("No personal_nummer provided!")
|
||||
showLoginPage(w, r, false, "Keine Personalnummer gesetzt.")
|
||||
return
|
||||
}
|
||||
personal_nummer, err := strconv.Atoi(_personal_nummer)
|
||||
if err != nil {
|
||||
log.Println("Cannot parse personal nubmer!")
|
||||
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!", 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 (%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
|
||||
}
|
||||
showLoginPage(w, r, false, "")
|
||||
}
|
||||
|
||||
func logoutUser(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("Loggin out user!")
|
||||
err := Session.Destroy(r.Context())
|
||||
if err != nil {
|
||||
log.Println("Error destroying session!", err)
|
||||
}
|
||||
http.Redirect(w, r, "/user/login", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@ go 1.24.7
|
||||
|
||||
require github.com/lib/pq v1.10.9
|
||||
|
||||
require github.com/a-h/templ v0.3.943
|
||||
require github.com/a-h/templ v0.3.960
|
||||
|
||||
require github.com/alexedwards/scs/v2 v2.8.0
|
||||
|
||||
require github.com/wlbr/feiertage v1.17.0
|
||||
|
||||
require (
|
||||
github.com/Dadido3/go-typst v0.8.0
|
||||
github.com/golang-migrate/migrate/v4 v4.18.3
|
||||
|
||||
@@ -4,10 +4,14 @@ github.com/Dadido3/go-typst v0.8.0 h1:uTLYprhkrBjwsCXRRuyYUFL0fpYHa2kIYoOB/CGqVN
|
||||
github.com/Dadido3/go-typst v0.8.0/go.mod h1:QYis9sT70u65kn1SkFfyPRmHsPxgoxWbAixwfPReOZA=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/a-h/templ v0.3.943 h1:o+mT/4yqhZ33F3ootBiHwaY4HM5EVaOJfIshvd5UNTY=
|
||||
github.com/a-h/templ v0.3.943/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
|
||||
github.com/a-h/templ v0.3.960 h1:trshEpGa8clF5cdI39iY4ZrZG8Z/QixyzEyUnA7feTM=
|
||||
github.com/a-h/templ v0.3.960/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
|
||||
github.com/alexedwards/scs/v2 v2.8.0 h1:h31yUYoycPuL0zt14c0gd+oqxfRwIj6SOjHdKRZxhEw=
|
||||
github.com/alexedwards/scs/v2 v2.8.0/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8=
|
||||
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||
github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc=
|
||||
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -33,6 +37,8 @@ github.com/golang-migrate/migrate/v4 v4.18.3 h1:EYGkoOsvgHHfm5U/naS1RP/6PL/Xv3S4
|
||||
github.com/golang-migrate/migrate/v4 v4.18.3/go.mod h1:99BKpIi6ruaaXRM1A77eqZ+FWPQ3cfRa+ZVy5bmWMaY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -40,6 +46,7 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
@@ -59,9 +66,21 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/smasher164/xid v0.1.2 h1:erplXSdBRIIw+MrwjJ/m8sLN2XY16UGzpTA0E2Ru6HA=
|
||||
github.com/smasher164/xid v0.1.2/go.mod h1:tgivm8CQl19fH1c5y+8F4mA+qY6n2i6qDRBlY/6nm+I=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/wlbr/feiertage v1.17.0 h1:AEck/iUQu19iU0xNEoSQTeSTGXF1Ju0tbAwEi/Lmwqk=
|
||||
github.com/wlbr/feiertage v1.17.0/go.mod h1:TVZgmSZgGW/jSxexZ56qdlR6cDj+F/FO8bkw8U6kYxM=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
|
||||
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
|
||||
@@ -72,19 +91,65 @@ go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt3
|
||||
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU=
|
||||
golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Helper package to create audit logs in the logs folder
|
||||
// these are by the time only generated, when a booking is
|
||||
// changed on the /time page
|
||||
package logs
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// this package contains different functions to parse URL.Values or Form.Values
|
||||
// into other datatypes see functionname
|
||||
// on fail the funktions either return a fallback or error
|
||||
|
||||
package paramParser
|
||||
|
||||
import (
|
||||
@@ -26,10 +30,9 @@ func (p ParamsParser) ParseIntListFallback(key string, delimiter string, fallbac
|
||||
if !p.urlParams.Has(key) {
|
||||
return fallback
|
||||
}
|
||||
paramList := p.urlParams.Get(key)
|
||||
list := strings.Split(paramList, delimiter)
|
||||
paramList := p.urlParams[key]
|
||||
parsedList := make([]int, 0)
|
||||
for _, item := range list {
|
||||
for _, item := range paramList {
|
||||
if parsedItem, err := strconv.Atoi(item); err == nil {
|
||||
parsedList = append(parsedList, parsedItem)
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package helper
|
||||
|
||||
func GetFirst[T, U any](val T, _ U) T {
|
||||
return val
|
||||
}
|
||||
|
||||
func GetSecond[T, U any](_ T, val U) U {
|
||||
return val
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package helper
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGetFirst(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
a any
|
||||
b any
|
||||
want any
|
||||
}{
|
||||
{"ints", 10, 20, 10},
|
||||
{"strings", "first", "second", "first"},
|
||||
{"mixed", "abc", 123, "abc"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := GetFirst(tt.a, tt.b)
|
||||
if got != tt.want {
|
||||
t.Errorf("GetFirst(%v, %v) = %v, want %v", tt.a, tt.b, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSecond(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
a any
|
||||
b any
|
||||
want any
|
||||
}{
|
||||
{"ints", 10, 20, 20},
|
||||
{"strings", "first", "second", "second"},
|
||||
{"mixed", "abc", 123, 123},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := GetSecond(tt.a, tt.b)
|
||||
if got != tt.want {
|
||||
t.Errorf("GetSecond(%v, %v) = %v, want %v", tt.a, tt.b, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package helper
|
||||
|
||||
// different system helpers for environment variables + cache
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package helper
|
||||
|
||||
// time helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
@@ -27,11 +29,6 @@ func IsWeekend(ts time.Time) bool {
|
||||
return ts.Weekday() == time.Saturday || ts.Weekday() == time.Sunday
|
||||
}
|
||||
|
||||
func GetKW(t time.Time) int {
|
||||
_, kw := t.ISOWeek()
|
||||
return kw
|
||||
}
|
||||
|
||||
func FormatDuration(d time.Duration) string {
|
||||
return FormatDurationFill(d, false)
|
||||
}
|
||||
|
||||
@@ -132,41 +132,3 @@ func TestFormatGermanDayOfWeek(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetKW(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
date time.Time
|
||||
want int
|
||||
}{
|
||||
{
|
||||
name: "First week of year",
|
||||
date: time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC), // Monday
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "Middle of year",
|
||||
date: time.Date(2023, 6, 15, 0, 0, 0, 0, time.UTC),
|
||||
want: 24,
|
||||
},
|
||||
{
|
||||
name: "Last week of year",
|
||||
date: time.Date(2023, 12, 31, 0, 0, 0, 0, time.UTC),
|
||||
want: 52,
|
||||
},
|
||||
{
|
||||
name: "ISO week crossing into next year",
|
||||
date: time.Date(2020, 12, 31, 0, 0, 0, 0, time.UTC),
|
||||
want: 53,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := GetKW(tt.date)
|
||||
if got != tt.want {
|
||||
t.Errorf("GetKW(%v) = %d, want %d", tt.date, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
package helper
|
||||
|
||||
import "time"
|
||||
|
||||
type TimeFormValue struct {
|
||||
TsFrom time.Time
|
||||
TsTo time.Time
|
||||
CardUID string
|
||||
}
|
||||
// type conversion
|
||||
|
||||
func BoolToInt(b bool) int {
|
||||
var i int = 0
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package helper
|
||||
|
||||
// web helpers to either set Cross Origin Request Security or block all requests without a user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package main
|
||||
|
||||
// this is the main file where the webserver is configured and all endpoints are added with their routes
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/endpoints"
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
"context"
|
||||
"database/sql"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -17,7 +20,16 @@ import (
|
||||
func main() {
|
||||
var err error
|
||||
var logLevel slog.LevelVar
|
||||
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)
|
||||
@@ -35,6 +47,17 @@ func main() {
|
||||
models.DB, err = OpenDatabase()
|
||||
if err != nil {
|
||||
slog.Error("Error while opening the database", "Error", err)
|
||||
return
|
||||
}
|
||||
|
||||
defer models.DB.(*sql.DB).Close()
|
||||
|
||||
if helper.GetEnv("GO_ENV", "production") != "debug" {
|
||||
err = Migrate()
|
||||
if err != nil {
|
||||
slog.Error("Failed to migrate the database to newest version", "Error", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fs := http.FileServer(http.Dir("./static"))
|
||||
@@ -44,32 +67,50 @@ func main() {
|
||||
|
||||
// handles the different http routes
|
||||
server.HandleFunc("/time/new", endpoints.TimeCreateHandler)
|
||||
server.Handle("/absence", ParamsMiddleware(endpoints.AbsencHandler))
|
||||
server.Handle("/time", ParamsMiddleware(endpoints.TimeHandler))
|
||||
server.Handle("/absence", paramsMiddleware(endpoints.AbsencHandler))
|
||||
server.Handle("/time", paramsMiddleware(endpoints.TimeHandler))
|
||||
server.HandleFunc("/auto/logout", endpoints.LogoutHandler)
|
||||
server.HandleFunc("/auto/kurzarbeit", endpoints.KurzarbeitFillHandler)
|
||||
server.HandleFunc("/auto/feiertage", endpoints.FeiertagsHandler)
|
||||
server.HandleFunc("/user/{action}", endpoints.UserHandler)
|
||||
// server.HandleFunc("/user/login", endpoints.LoginHandler)
|
||||
// server.HandleFunc("/user/settings", endpoints.UserSettingsHandler)
|
||||
server.HandleFunc("/team", endpoints.TeamHandler)
|
||||
server.HandleFunc("/presence", endpoints.TeamPresenceHandler)
|
||||
server.Handle("/pdf", ParamsMiddleware(endpoints.PDFFormHandler))
|
||||
server.HandleFunc("/team/report", endpoints.ReportHandler)
|
||||
server.HandleFunc("/team/presence", endpoints.PresenceHandler)
|
||||
server.Handle("/pdf", paramsMiddleware(endpoints.PDFFormHandler))
|
||||
server.HandleFunc("/pdf/generate", endpoints.PDFCreateController)
|
||||
server.Handle("/", http.RedirectHandler("/time", http.StatusPermanentRedirect))
|
||||
server.Handle("/static/", http.StripPrefix("/static/", fs))
|
||||
|
||||
serverSessionMiddleware := endpoints.Session.LoadAndSave(server)
|
||||
|
||||
serverSessionMiddleware = loggingMiddleware(serverSessionMiddleware)
|
||||
|
||||
// starting the http server
|
||||
slog.Info("Server is running at http://localhost:8080")
|
||||
slog.Error("Error starting Server", "Error", http.ListenAndServe(":8080", serverSessionMiddleware))
|
||||
}
|
||||
|
||||
func ParamsMiddleware(next http.HandlerFunc) http.Handler {
|
||||
func paramsMiddleware(next http.HandlerFunc) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
queryParams := r.URL.Query()
|
||||
ctx := context.WithValue(r.Context(), "urlParams", queryParams)
|
||||
if len(queryParams) > 0 {
|
||||
slog.Debug("ParamsMiddleware added urlParams", slog.Any("urlParams", queryParams))
|
||||
}
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
func loggingMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
|
||||
// Log the method and the requested URL
|
||||
slog.Info("Started request", slog.String("Method", r.Method), slog.String("Path", r.URL.Path))
|
||||
|
||||
// Call the next handler in the chain
|
||||
next.ServeHTTP(w, r)
|
||||
|
||||
// Log how long it took
|
||||
slog.Info("Completet Request", slog.String("Time", time.Since(start).String()))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
ALTER DEFAULT PRIVILEGES FOR ROLE migrate
|
||||
IN SCHEMA public
|
||||
GRANT SELECT ON TABLES TO app_base;
|
||||
|
||||
ALTER DEFAULT PRIVILEGES FOR ROLE migrate
|
||||
IN SCHEMA public
|
||||
GRANT USAGE, SELECT ON SEQUENCES TO app_base;
|
||||
|
||||
-- create "abwesenheit" table
|
||||
CREATE TABLE "abwesenheit" (
|
||||
"counter_id" bigserial NOT NULL,
|
||||
@@ -6,6 +14,7 @@ CREATE TABLE "abwesenheit" (
|
||||
"datum" timestamptz NULL DEFAULT (now())::date,
|
||||
PRIMARY KEY ("counter_id")
|
||||
);
|
||||
|
||||
-- create "anwesenheit" table
|
||||
CREATE TABLE "anwesenheit" (
|
||||
"counter_id" bigserial NOT NULL,
|
||||
@@ -55,3 +64,6 @@ CREATE TABLE "wochen_report" (
|
||||
PRIMARY KEY ("id"),
|
||||
CONSTRAINT "wochen_report_personal_nummer_woche_start_key" UNIQUE ("personal_nummer", "woche_start")
|
||||
);
|
||||
|
||||
GRANT INSERT, UPDATE ON abwesenheit, anwesenheit, wochen_report, user_password TO app_base;
|
||||
GRANT DELETE ON abwesenheit to app_base;
|
||||
@@ -3,8 +3,3 @@
|
||||
DROP FUNCTION update_zuletzt_geandert;
|
||||
|
||||
DROP TRIGGER IF EXISTS pass_hash_update ON user_password;
|
||||
|
||||
|
||||
-- revert: Adds crypto extension
|
||||
|
||||
DROP EXTENSION IF EXISTS pgcrypto;
|
||||
@@ -17,5 +17,3 @@ FOR EACH ROW
|
||||
EXECUTE FUNCTION update_zuletzt_geandert();
|
||||
|
||||
-- Adds crypto extension
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
6
Backend/migrations/20251217215955_feiertage.down.sql
Normal file
6
Backend/migrations/20251217215955_feiertage.down.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
-- reverse: create index "feiertage_unique_pro_jahr" to table: "s_feiertage"
|
||||
DROP INDEX "feiertage_unique_pro_jahr";
|
||||
-- reverse: create "s_feiertage" table
|
||||
DROP TABLE "s_feiertage";
|
||||
-- reverse: set comment to column: "arbeitszeit_equivalent" on table: "s_abwesenheit_typen"
|
||||
COMMENT ON COLUMN "s_abwesenheit_typen"."arbeitszeit_equivalent" IS '0=keine Arbeitszeit; 1=Arbeitszeit auffüllen; 2=Arbeitszeit austauschen';
|
||||
15
Backend/migrations/20251217215955_feiertage.up.sql
Normal file
15
Backend/migrations/20251217215955_feiertage.up.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
-- set comment to column: "arbeitszeit_equivalent" on table: "s_abwesenheit_typen"
|
||||
COMMENT ON COLUMN "s_abwesenheit_typen"."arbeitszeit_equivalent" IS '0=keine Arbeitszeit; -1=Arbeitszeit auffüllen; <=1 - 100 => Arbeitszeit pro Tag prozentual';
|
||||
-- create "s_feiertage" table
|
||||
CREATE TABLE "s_feiertage" (
|
||||
"counter_id" serial NOT NULL,
|
||||
"datum" date NOT NULL,
|
||||
"name" character varying(100) NOT NULL,
|
||||
"wiederholen" smallint NOT NULL DEFAULT 0,
|
||||
"arbeitszeit_equivalent" smallint NOT NULL DEFAULT 100,
|
||||
PRIMARY KEY ("counter_id")
|
||||
);
|
||||
-- create index "feiertage_unique_pro_jahr" to table: "s_feiertage"
|
||||
CREATE UNIQUE INDEX "feiertage_unique_pro_jahr" ON "s_feiertage" ((EXTRACT(year FROM datum)), "name");
|
||||
|
||||
GRANT INSERT, UPDATE ON s_feiertage TO app_base;
|
||||
@@ -1,4 +1,4 @@
|
||||
h1:gE7ikkZS7bQbedAjVspQKftSo5ODij2eiQGWbfQEYmI=
|
||||
h1:1lrLZOm9nGe6v1/TrR1Ij8LBRDCY2igXwwUB+XqEIrc=
|
||||
20250901201159_initial.up.sql h1:Mb1RlVdFvcxqU9HrSK6oNeURqFa3O4KzB3rDa+6+3gc=
|
||||
20250901201250_control_tables.up.sql h1:a5LATgR/CRiC4GsqxkJ94TyJOxeTcW74eCnodIy+c1E=
|
||||
20250901201710_triggers_extension.up.sql h1:z9b6Hk9btE2Ns4mU7B16HjvYBP6EEwHAXVlvPpkn978=
|
||||
@@ -7,3 +7,4 @@ h1:gE7ikkZS7bQbedAjVspQKftSo5ODij2eiQGWbfQEYmI=
|
||||
20250904114004_intervals.up.sql h1:gDdN8cJ4xH1vQhAbbhqD5lwdyEO1N9EIqEYkmWGiWIU=
|
||||
20250916093608_kurzarbeit.up.sql h1:yDAAMLyUXz6b7+MI6XK/HZMPzutKoT2NNNOCjFaqSts=
|
||||
20251013212224_buchungs_array.up.sql h1:mbhvnwMUkEFFQQ41NC47auqxbtvNkztziWvpLDFm6tA=
|
||||
20251217215955_feiertage.up.sql h1:PipbYvfL8YtsidgbJ3oEHYrmiNzffQ7veyaGAxINltE=
|
||||
@@ -1,8 +1,19 @@
|
||||
// the models package contains the datamodels for the whole webserver
|
||||
// most of them are also in the DB
|
||||
//
|
||||
// in the future it would be good to change it to create a repository structure,
|
||||
// so that there would be a second pacakge handling db requests
|
||||
|
||||
package models
|
||||
|
||||
// this file has all functions and types to handle absences
|
||||
// the absence type implements the iWorkDay interface so that
|
||||
// it can be used as one workday
|
||||
//
|
||||
// the absence data is based on the entries in the "abwesenheit" database table
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
@@ -22,29 +33,19 @@ type Absence struct {
|
||||
DateTo time.Time
|
||||
}
|
||||
|
||||
func NewAbsence(card_uid string, abwesenheit_typ int, datum time.Time) (Absence, error) {
|
||||
if abwesenheit_typ < 0 {
|
||||
return Absence{
|
||||
CardUID: card_uid,
|
||||
AbwesenheitTyp: AbsenceType{0, "Custom absence", 100},
|
||||
DateFrom: datum,
|
||||
}, nil
|
||||
}
|
||||
_absenceType, ok := GetAbsenceTypesCached()[int8(abwesenheit_typ)]
|
||||
if !ok {
|
||||
return Absence{}, errors.New("Invalid absencetype")
|
||||
}
|
||||
return Absence{
|
||||
CardUID: card_uid,
|
||||
AbwesenheitTyp: _absenceType,
|
||||
DateFrom: datum,
|
||||
}, nil
|
||||
// IsEmpty implements [IWorkDay].
|
||||
func (a *Absence) IsEmpty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *Absence) Date() time.Time {
|
||||
return a.Day.Truncate(24 * time.Hour)
|
||||
}
|
||||
|
||||
func (a *Absence) Type() DayType {
|
||||
return DayTypeAbsence
|
||||
}
|
||||
|
||||
func (a *Absence) IsMultiDay() bool {
|
||||
return !a.DateFrom.Equal(a.DateTo)
|
||||
}
|
||||
@@ -91,7 +92,7 @@ func (a *Absence) GetTimes(u User, base WorktimeBase, includeKurzarbeit bool) (w
|
||||
}
|
||||
|
||||
func (a *Absence) ToString() string {
|
||||
return "Abwesenheit"
|
||||
return a.AbwesenheitTyp.Name
|
||||
}
|
||||
|
||||
func (a *Absence) IsWorkDay() bool {
|
||||
@@ -103,7 +104,7 @@ func (a *Absence) IsKurzArbeit() bool {
|
||||
}
|
||||
|
||||
func (a *Absence) GetDayProgress(u User) int8 {
|
||||
return 100
|
||||
return a.AbwesenheitTyp.WorkTime
|
||||
}
|
||||
|
||||
func (a *Absence) RequiresAction() bool {
|
||||
@@ -285,3 +286,12 @@ func GetAbsenceTypeById(absenceTypeId int8) (AbsenceType, error) {
|
||||
}
|
||||
return absenceType, nil
|
||||
}
|
||||
|
||||
func (a *Absence) Delete() error {
|
||||
qStr, err := DB.Prepare("DELETE from abwesenheit WHERE counter_id = $1;")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = qStr.Exec(a.CounterId)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package models_test
|
||||
|
||||
// all the files with *_test.go are used to test the functions inside the respective file
|
||||
// the tests are partially complete
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package models
|
||||
|
||||
// this file has all functions and types to handle bookings
|
||||
// the bookings itself are later combined to a workday to save on
|
||||
// db requests and computation
|
||||
//
|
||||
// the booking data is based on the entries in the "anwesenheit" database table
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/helper/logs"
|
||||
@@ -39,7 +45,7 @@ type IDatabase interface {
|
||||
|
||||
var DB IDatabase
|
||||
|
||||
func (b *Booking) New(cardUid string, gereatId int16, checkInOut int16, typeId int8) Booking {
|
||||
func (b *Booking) NewBooking(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)
|
||||
@@ -128,6 +134,9 @@ func (b *Booking) InsertWithTimestamp() error {
|
||||
if b.Timestamp.IsZero() {
|
||||
return b.Insert()
|
||||
}
|
||||
if !checkLastBooking(*b) {
|
||||
return SameBookingError{}
|
||||
}
|
||||
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
|
||||
@@ -242,12 +251,13 @@ func (b *Booking) Update(nb Booking) {
|
||||
|
||||
func checkLastBooking(b Booking) bool {
|
||||
var check_in_out int
|
||||
stmt, err := DB.Prepare((`SELECT check_in_out FROM "anwesenheit" WHERE "card_uid" = $1 ORDER BY "timestamp" DESC LIMIT 1;`))
|
||||
slog.Info("Checking with timestamp:", "timestamp", b.Timestamp.String())
|
||||
stmt, err := DB.Prepare((`SELECT check_in_out FROM "anwesenheit" WHERE "card_uid" = $1 AND "timestamp"::DATE <= $2::DATE ORDER BY "timestamp" DESC LIMIT 1;`))
|
||||
if err != nil {
|
||||
log.Fatalf("Error preparing query: %v", err)
|
||||
return false
|
||||
}
|
||||
err = stmt.QueryRow(b.CardUID).Scan(&check_in_out)
|
||||
err = stmt.QueryRow(b.CardUID, b.Timestamp).Scan(&check_in_out)
|
||||
if err == sql.ErrNoRows {
|
||||
return true
|
||||
}
|
||||
|
||||
116
Backend/models/compoundDay.go
Normal file
116
Backend/models/compoundDay.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package models
|
||||
|
||||
// this file contains all functions for the compound day class
|
||||
// the compound can merge all kinds of daytypes together, as long as they
|
||||
// implement the IWorkDay interface. This is used to have an absence + bookings
|
||||
// in one day
|
||||
//
|
||||
// the compound day is a meta type, which means its not in the database
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CompoundDay struct {
|
||||
Day time.Time
|
||||
DayParts []IWorkDay
|
||||
}
|
||||
|
||||
func NewCompondDay(date time.Time, dayParts ...IWorkDay) *CompoundDay {
|
||||
return &CompoundDay{Day: date, DayParts: dayParts}
|
||||
}
|
||||
|
||||
func (c *CompoundDay) AddDayPart(dayPart IWorkDay) {
|
||||
c.DayParts = append(c.DayParts, dayPart)
|
||||
}
|
||||
|
||||
func (c *CompoundDay) GetWorkDay() WorkDay {
|
||||
workday, ok := c.DayParts[0].(*WorkDay)
|
||||
if ok {
|
||||
return *workday
|
||||
}
|
||||
return WorkDay{}
|
||||
}
|
||||
|
||||
// IsEmpty implements [IWorkDay].
|
||||
func (c *CompoundDay) IsEmpty() bool {
|
||||
return len(c.DayParts) == 0
|
||||
}
|
||||
|
||||
// Date implements [IWorkDay].
|
||||
func (c *CompoundDay) Date() time.Time {
|
||||
return c.Day
|
||||
}
|
||||
|
||||
// GetDayProgress implements [IWorkDay].
|
||||
func (c *CompoundDay) GetDayProgress(u User) int8 {
|
||||
var dayProcess int8
|
||||
for _, day := range c.DayParts {
|
||||
dayProcess += day.GetDayProgress(u)
|
||||
}
|
||||
return dayProcess
|
||||
}
|
||||
|
||||
// GetOvertime implements [IWorkDay].
|
||||
func (c *CompoundDay) GetOvertime(u User, base WorktimeBase, includeKurzarbeit bool) time.Duration {
|
||||
work := c.GetWorktime(u, base, includeKurzarbeit)
|
||||
var targetHours time.Duration
|
||||
|
||||
switch base {
|
||||
case WorktimeBaseDay:
|
||||
targetHours = u.ArbeitszeitProTagFrac(1)
|
||||
case WorktimeBaseWeek:
|
||||
targetHours = u.ArbeitszeitProWocheFrac(.2)
|
||||
}
|
||||
return (work - targetHours).Round(time.Minute)
|
||||
}
|
||||
|
||||
// GetPausetime implements [IWorkDay].
|
||||
func (c *CompoundDay) GetPausetime(u User, base WorktimeBase, includeKurzarbeit bool) time.Duration {
|
||||
var pausetime time.Duration
|
||||
for _, day := range c.DayParts {
|
||||
pausetime += day.GetPausetime(u, base, includeKurzarbeit)
|
||||
}
|
||||
return pausetime
|
||||
}
|
||||
|
||||
// GetTimes implements [IWorkDay].
|
||||
func (c *CompoundDay) GetTimes(u User, base WorktimeBase, includeKurzarbeit bool) (work time.Duration, pause time.Duration, overtime time.Duration) {
|
||||
return c.GetWorktime(u, base, includeKurzarbeit), c.GetPausetime(u, base, includeKurzarbeit), c.GetOvertime(u, base, includeKurzarbeit)
|
||||
}
|
||||
|
||||
// GetWorktime implements [IWorkDay].
|
||||
func (c *CompoundDay) GetWorktime(u User, base WorktimeBase, includeKurzarbeit bool) time.Duration {
|
||||
var worktime time.Duration
|
||||
for _, day := range c.DayParts {
|
||||
worktime += day.GetWorktime(u, base, includeKurzarbeit)
|
||||
slog.Info("Calc worktime for day", "day", day, "worktime", worktime.String())
|
||||
}
|
||||
return worktime
|
||||
}
|
||||
|
||||
// IsKurzArbeit implements [IWorkDay].
|
||||
func (c *CompoundDay) IsKurzArbeit() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsWorkDay implements [IWorkDay].
|
||||
func (c *CompoundDay) IsWorkDay() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// RequiresAction implements [IWorkDay].
|
||||
func (c *CompoundDay) RequiresAction() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ToString implements [IWorkDay].
|
||||
func (c *CompoundDay) ToString() string {
|
||||
return "Compound Day"
|
||||
}
|
||||
|
||||
// Type implements [IWorkDay].
|
||||
func (c *CompoundDay) Type() DayType {
|
||||
return DayTypeCompound
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package models
|
||||
|
||||
// this file is only used as cache for the diffenrent absence and booking types,
|
||||
// in the future this should be two chaching variables each in their own place
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
)
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
package models
|
||||
|
||||
// this file desribes the IWorkDay interface which is used, to combine the diffent kinds
|
||||
// of day types (absence, holidy, workday) and make them more compatimble with the rest
|
||||
//
|
||||
// the IWorkDay as an interface is not in the database
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"log"
|
||||
"log/slog"
|
||||
"time"
|
||||
)
|
||||
|
||||
type IWorkDay interface {
|
||||
Date() time.Time
|
||||
ToString() string
|
||||
Type() DayType
|
||||
IsWorkDay() bool
|
||||
IsKurzArbeit() bool
|
||||
GetDayProgress(User) int8
|
||||
@@ -17,37 +22,70 @@ type IWorkDay interface {
|
||||
GetPausetime(User, WorktimeBase, bool) time.Duration
|
||||
GetTimes(User, WorktimeBase, bool) (work, pause, overtime time.Duration)
|
||||
GetOvertime(User, WorktimeBase, bool) time.Duration
|
||||
IsEmpty() bool
|
||||
}
|
||||
|
||||
type DayType int
|
||||
|
||||
const (
|
||||
DayTypeWorkday DayType = 1
|
||||
DayTypeAbsence DayType = 2
|
||||
DayTypeHoliday DayType = 3
|
||||
DayTypeCompound DayType = 4
|
||||
)
|
||||
|
||||
func GetDays(user User, tsFrom, tsTo time.Time, orderedForward bool) []IWorkDay {
|
||||
var allDays map[string]IWorkDay = make(map[string]IWorkDay)
|
||||
|
||||
for _, day := range GetWorkDays(user, tsFrom, tsTo) {
|
||||
allDays[day.Date().Format(time.DateOnly)] = &day
|
||||
}
|
||||
workdays := GetWorkDays(user, tsFrom, tsTo)
|
||||
absences, err := GetAbsencesByCardUID(user.CardUID, tsFrom, tsTo)
|
||||
if err != nil {
|
||||
log.Println("Error gettings absences for all Days!", err)
|
||||
slog.Warn("Error gettings absences!", slog.Any("Error", err))
|
||||
return nil
|
||||
}
|
||||
for _, day := range absences {
|
||||
if helper.IsWeekend(day.Date()) {
|
||||
continue
|
||||
holidays, err := GetHolidaysFromTo(tsFrom, tsTo)
|
||||
if err != nil {
|
||||
slog.Warn("Error getting holidays!", slog.Any("Error", err))
|
||||
return nil
|
||||
}
|
||||
// Absence should be integrated in workday
|
||||
switch {
|
||||
case day.AbwesenheitTyp.WorkTime < 0:
|
||||
if workDay, ok := allDays[day.Date().Format(time.DateOnly)].(*WorkDay); ok {
|
||||
workDay.kurzArbeit = true
|
||||
workDay.kurzArbeitAbsence = day
|
||||
}
|
||||
case day.AbwesenheitTyp.WorkTime < 100:
|
||||
if workDay, ok := allDays[day.Date().Format(time.DateOnly)].(*WorkDay); ok {
|
||||
workDay.worktimeAbsece = day
|
||||
}
|
||||
default:
|
||||
|
||||
for _, day := range workdays {
|
||||
allDays[day.Date().Format(time.DateOnly)] = &day
|
||||
}
|
||||
|
||||
for _, absentDay := range absences {
|
||||
|
||||
// Check if there is already a day
|
||||
existingDay, ok := allDays[absentDay.Date().Format(time.DateOnly)]
|
||||
switch {
|
||||
case absentDay.AbwesenheitTyp.WorkTime < 0:
|
||||
if workDay, ok := allDays[absentDay.Date().Format(time.DateOnly)].(*WorkDay); ok {
|
||||
workDay.kurzArbeit = true
|
||||
workDay.kurzArbeitAbsence = absentDay
|
||||
}
|
||||
case ok && !existingDay.IsEmpty():
|
||||
allDays[absentDay.Date().Format(time.DateOnly)] = NewCompondDay(absentDay.Date(), existingDay, &absentDay)
|
||||
default:
|
||||
allDays[absentDay.Date().Format(time.DateOnly)] = &absentDay
|
||||
}
|
||||
}
|
||||
|
||||
for _, holiday := range holidays {
|
||||
existingDay, ok := allDays[holiday.Date().Format(time.DateOnly)]
|
||||
if !ok {
|
||||
allDays[holiday.Date().Format(time.DateOnly)] = &holiday
|
||||
continue
|
||||
}
|
||||
slog.Info("Existing Day", "day", existingDay)
|
||||
switch {
|
||||
case existingDay.Type() == DayTypeCompound:
|
||||
allDays[holiday.Date().Format(time.DateOnly)].(*CompoundDay).AddDayPart(&holiday)
|
||||
case existingDay.Type() != DayTypeCompound && !existingDay.IsEmpty():
|
||||
allDays[holiday.Date().Format(time.DateOnly)] = NewCompondDay(holiday.Date(), existingDay, &holiday)
|
||||
default:
|
||||
allDays[holiday.Date().Format(time.DateOnly)] = &holiday
|
||||
}
|
||||
slog.Debug("Logging Holiday: ", slog.String("HolidayName", allDays[holiday.Date().Format(time.DateOnly)].ToString()), slog.Any("Overtime", holiday.GetOvertime(user, WorktimeBaseDay, false).String()), "wokrtie", float32(holiday.worktime)/100)
|
||||
}
|
||||
|
||||
sortedDays := sortDays(allDays, orderedForward)
|
||||
|
||||
142
Backend/models/publicHoliday.go
Normal file
142
Backend/models/publicHoliday.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package models
|
||||
|
||||
// the public holiday is used the describe all holidays
|
||||
// the publicholiday implements the IWorkDay interface
|
||||
//
|
||||
// the PublicHoliday data is based on the entries in the "s_feiertage" database table
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/wlbr/feiertage"
|
||||
)
|
||||
|
||||
// type PublicHoliday feiertage.Feiertag
|
||||
|
||||
type PublicHoliday struct {
|
||||
feiertage.Feiertag
|
||||
repeat int8
|
||||
worktime int8
|
||||
}
|
||||
|
||||
// IsEmpty implements [IWorkDay].
|
||||
func (p *PublicHoliday) IsEmpty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func NewHolidayFromFeiertag(f feiertage.Feiertag) PublicHoliday {
|
||||
return PublicHoliday{
|
||||
Feiertag: f,
|
||||
repeat: 0,
|
||||
worktime: 100,
|
||||
}
|
||||
}
|
||||
|
||||
func GetHolidaysFromTo(tsFrom, tsTo time.Time) ([]PublicHoliday, error) {
|
||||
var publicHolidays []PublicHoliday
|
||||
qStr, err := DB.Prepare(`SELECT datum, name, wiederholen, arbeitszeit_equivalent FROM s_feiertage WHERE datum::DATE >= $1::DATE AND datum::DATE <= $2::DATE;`)
|
||||
if err != nil {
|
||||
return publicHolidays, err
|
||||
}
|
||||
|
||||
rows, err := qStr.Query(tsFrom, tsTo)
|
||||
if err != nil {
|
||||
return publicHolidays, err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var publicHoliday PublicHoliday
|
||||
if err := rows.Scan(&publicHoliday.Time, &publicHoliday.Text, &publicHoliday.repeat, &publicHoliday.worktime); err != nil {
|
||||
return publicHolidays, err
|
||||
}
|
||||
publicHolidays = append(publicHolidays, publicHoliday)
|
||||
}
|
||||
return publicHolidays, nil
|
||||
}
|
||||
|
||||
func GetRepeatingHolidays(tsFrom, tsTo time.Time) ([]PublicHoliday, error) {
|
||||
var publicHolidays []PublicHoliday
|
||||
qStr, err := DB.Prepare(`SELECT datum, name, wiederholen, arbeitszeit_equivalent FROM s_feiertage WHERE wiederholen = 1 AND datum::DATE >= $1::DATE AND datum::DATE <= $2::DATE;`)
|
||||
if err != nil {
|
||||
return publicHolidays, err
|
||||
}
|
||||
rows, err := qStr.Query(tsFrom, tsTo)
|
||||
if err != nil {
|
||||
return publicHolidays, err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var publicHoliday PublicHoliday
|
||||
if err := rows.Scan(&publicHoliday.Time, &publicHoliday.Text, &publicHoliday.repeat, &publicHoliday.worktime); err != nil {
|
||||
return publicHolidays, err
|
||||
}
|
||||
publicHolidays = append(publicHolidays, publicHoliday)
|
||||
}
|
||||
return publicHolidays, nil
|
||||
|
||||
}
|
||||
func (p *PublicHoliday) Insert() error {
|
||||
qStr, err := DB.Prepare(`INSERT INTO s_feiertage(name, datum, wiederholen, arbeitszeit_equivalent) VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING;`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = qStr.Exec(p.Text, p.Time, p.repeat, p.worktime)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *PublicHoliday) Type() DayType {
|
||||
return DayTypeHoliday
|
||||
}
|
||||
|
||||
// Interface implementation
|
||||
func (p *PublicHoliday) Date() time.Time {
|
||||
return p.Time
|
||||
}
|
||||
|
||||
func (p *PublicHoliday) ToString() string {
|
||||
return p.Text
|
||||
}
|
||||
|
||||
func (p *PublicHoliday) IsWorkDay() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PublicHoliday) IsKurzArbeit() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PublicHoliday) GetDayProgress(User) int8 {
|
||||
return p.worktime
|
||||
}
|
||||
|
||||
func (p *PublicHoliday) RequiresAction() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PublicHoliday) GetWorktime(u User, base WorktimeBase, includeKurzarbeit bool) time.Duration {
|
||||
switch base {
|
||||
case WorktimeBaseDay:
|
||||
return u.ArbeitszeitProTagFrac(float32(p.worktime) / 100)
|
||||
case WorktimeBaseWeek:
|
||||
return u.ArbeitszeitProWocheFrac(float32(p.worktime) / 500)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (p *PublicHoliday) GetPausetime(User, WorktimeBase, bool) time.Duration {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (p *PublicHoliday) GetOvertime(u User, base WorktimeBase, includeKurzarbeit bool) time.Duration {
|
||||
switch base {
|
||||
case WorktimeBaseDay:
|
||||
return u.ArbeitszeitProTagFrac(float32(p.worktime)/100) - u.ArbeitszeitProTagFrac(1)
|
||||
case WorktimeBaseWeek:
|
||||
return u.ArbeitszeitProWocheFrac(float32(p.worktime)/500) - u.ArbeitszeitProWocheFrac(0.2)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (p *PublicHoliday) GetTimes(u User, base WorktimeBase, includeKurzarbeit bool) (work, pause, overtime time.Duration) {
|
||||
return p.GetWorktime(u, base, includeKurzarbeit), 0, 0
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
package models
|
||||
|
||||
// the user type represents the user of the software
|
||||
// it has functions to request the bookings and other data based
|
||||
// on either the "PersonalNummer" or "CardUID"
|
||||
//
|
||||
// users and their passwords are stored in the "s_personal_daten" and "user_pass" tables
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"context"
|
||||
@@ -149,7 +155,7 @@ 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, 1)
|
||||
booking := (*Booking).NewBooking(nil, u.CardUID, 0, 254, 1)
|
||||
err := booking.Insert()
|
||||
if err != nil {
|
||||
fmt.Printf("Error inserting booking %v -> %v\n", booking, err)
|
||||
@@ -282,15 +288,6 @@ func (u *User) GetNextWeek() WorkWeek {
|
||||
|
||||
}
|
||||
|
||||
func parseUser(rows *sql.Rows) (User, error) {
|
||||
var user User
|
||||
if err := rows.Scan(&user.PersonalNummer, &user.CardUID, &user.Vorname, &user.Name, &user.ArbeitszeitPerTag); err != nil {
|
||||
log.Println("Error scanning row!", err)
|
||||
return user, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -330,6 +327,22 @@ func (u *User) GetFromCardUID(card_uid string) (User, error) {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (u *User) IsSuperior(e User) bool {
|
||||
var isSuperior int
|
||||
qStr, err := DB.Prepare(`SELECT COUNT(1) FROM s_personal_daten WHERE personal_nummer = $1 AND vorgesetzter_pers_nr = $2`)
|
||||
if err != nil {
|
||||
slog.Debug("Error preparing query", "error", err)
|
||||
return false
|
||||
}
|
||||
err = qStr.QueryRow(e.PersonalNummer, u.PersonalNummer).Scan(&isSuperior)
|
||||
if err != nil {
|
||||
slog.Debug("Error executing query", "error", err)
|
||||
return false
|
||||
}
|
||||
return isSuperior == 1
|
||||
|
||||
}
|
||||
|
||||
func getMonday(ts time.Time) time.Time {
|
||||
if ts.Weekday() != time.Monday {
|
||||
if ts.Weekday() == time.Sunday {
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package models
|
||||
|
||||
// the workday combines all bookings of a day into a single type and is the third
|
||||
// type of workday which implements the IWorkDay interface
|
||||
//
|
||||
// this is a meta type and not present in the db
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"encoding/json"
|
||||
@@ -23,11 +28,16 @@ type WorkDay struct {
|
||||
worktimeAbsece Absence
|
||||
}
|
||||
|
||||
type WorktimeBase string
|
||||
// IsEmpty implements [IWorkDay].
|
||||
func (d *WorkDay) IsEmpty() bool {
|
||||
return len(d.Bookings) == 0
|
||||
}
|
||||
|
||||
type WorktimeBase int
|
||||
|
||||
const (
|
||||
WorktimeBaseWeek WorktimeBase = "week"
|
||||
WorktimeBaseDay WorktimeBase = "day"
|
||||
WorktimeBaseWeek WorktimeBase = 5
|
||||
WorktimeBaseDay WorktimeBase = 1
|
||||
)
|
||||
|
||||
func (d *WorkDay) GetWorktimeAbsence() Absence {
|
||||
@@ -126,6 +136,10 @@ func (d *WorkDay) Date() time.Time {
|
||||
return d.Day
|
||||
}
|
||||
|
||||
func (d *WorkDay) Type() DayType {
|
||||
return DayTypeWorkday
|
||||
}
|
||||
|
||||
func (d *WorkDay) GenerateKurzArbeitBookings(u User) (time.Time, time.Time) {
|
||||
var timeFrom, timeTo time.Time
|
||||
if d.GetWorktime(u, WorktimeBaseDay, false) >= u.ArbeitszeitProTag() {
|
||||
@@ -166,21 +180,38 @@ func GetWorkDays(user User, tsFrom, tsTo time.Time) []WorkDay {
|
||||
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 (
|
||||
normalized_bookings AS (
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT
|
||||
a.timestamp::DATE AS work_date,
|
||||
a.card_uid,
|
||||
a.timestamp,
|
||||
a.timestamp::DATE AS work_date,
|
||||
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
|
||||
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
|
||||
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
|
||||
) t
|
||||
WHERE prev_check IS NULL OR prev_check <> check_in_out
|
||||
),
|
||||
ordered_bookings AS (
|
||||
SELECT
|
||||
*,
|
||||
LAG(timestamp) OVER (
|
||||
PARTITION BY card_uid, work_date
|
||||
ORDER BY timestamp
|
||||
) AS prev_timestamp
|
||||
FROM normalized_bookings
|
||||
)
|
||||
SELECT
|
||||
d.work_date,
|
||||
@@ -219,6 +250,62 @@ func GetWorkDays(user User, tsFrom, tsTo time.Time) []WorkDay {
|
||||
GROUP BY d.work_date
|
||||
ORDER BY d.work_date ASC;`)
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// )
|
||||
// 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
|
||||
// FROM all_days d
|
||||
// LEFT JOIN ordered_bookings b ON d.work_date = b.work_date
|
||||
// GROUP BY d.work_date
|
||||
// ORDER BY d.work_date ASC;`)
|
||||
|
||||
if err != nil {
|
||||
log.Println("Error preparing SQL statement", err)
|
||||
return workDays
|
||||
@@ -249,7 +336,7 @@ func GetWorkDays(user User, tsFrom, tsTo time.Time) []WorkDay {
|
||||
if len(workDay.Bookings) == 1 && workDay.Bookings[0].CounterId == 0 {
|
||||
workDay.Bookings = []Booking{}
|
||||
}
|
||||
if len(workDay.Bookings) > 1 || !helper.IsWeekend(workDay.Date()) {
|
||||
if len(workDay.Bookings) >= 1 || !helper.IsWeekend(workDay.Date()) {
|
||||
workDays = append(workDays, workDay)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package models
|
||||
|
||||
// the WorkWeek describes the weekly reports used the check on worktimes and
|
||||
// calculate the running overtime
|
||||
//
|
||||
// this type is based on the "wochen_report" table
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
@@ -78,14 +78,8 @@
|
||||
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-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));
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
--color-red-700: oklch(50.5% 0.213 27.518);
|
||||
--color-orange-500: oklch(70.5% 0.213 47.604);
|
||||
--color-purple-600: oklch(55.8% 0.288 302.321);
|
||||
--color-slate-300: oklch(86.9% 0.022 252.894);
|
||||
--color-slate-600: oklch(44.6% 0.043 257.281);
|
||||
--color-slate-700: oklch(37.2% 0.044 257.287);
|
||||
--color-slate-800: oklch(27.9% 0.041 260.031);
|
||||
@@ -200,15 +199,15 @@
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
.top-1 {
|
||||
top: calc(var(--spacing) * 1);
|
||||
.sticky {
|
||||
position: sticky;
|
||||
}
|
||||
.top-0 {
|
||||
top: calc(var(--spacing) * 0);
|
||||
}
|
||||
.top-1\/2 {
|
||||
top: calc(1/2 * 100%);
|
||||
}
|
||||
.top-2 {
|
||||
top: calc(var(--spacing) * 2);
|
||||
}
|
||||
.top-2\.5 {
|
||||
top: calc(var(--spacing) * 2.5);
|
||||
}
|
||||
@@ -218,18 +217,15 @@
|
||||
.right-1 {
|
||||
right: calc(var(--spacing) * 1);
|
||||
}
|
||||
.right-2 {
|
||||
right: calc(var(--spacing) * 2);
|
||||
}
|
||||
.right-2\.5 {
|
||||
right: calc(var(--spacing) * 2.5);
|
||||
}
|
||||
.left-1 {
|
||||
left: calc(var(--spacing) * 1);
|
||||
}
|
||||
.left-1\/2 {
|
||||
left: calc(1/2 * 100%);
|
||||
}
|
||||
.z-100 {
|
||||
z-index: 100;
|
||||
}
|
||||
.col-span-2 {
|
||||
grid-column: span 2 / span 2;
|
||||
}
|
||||
@@ -387,9 +383,6 @@
|
||||
.h-2 {
|
||||
height: calc(var(--spacing) * 2);
|
||||
}
|
||||
.h-3 {
|
||||
height: calc(var(--spacing) * 3);
|
||||
}
|
||||
.h-3\.5 {
|
||||
height: calc(var(--spacing) * 3.5);
|
||||
}
|
||||
@@ -402,6 +395,9 @@
|
||||
.h-8 {
|
||||
height: calc(var(--spacing) * 8);
|
||||
}
|
||||
.h-10 {
|
||||
height: calc(var(--spacing) * 10);
|
||||
}
|
||||
.h-\[100vh\] {
|
||||
height: 100vh;
|
||||
}
|
||||
@@ -411,9 +407,6 @@
|
||||
.w-2 {
|
||||
width: calc(var(--spacing) * 2);
|
||||
}
|
||||
.w-3 {
|
||||
width: calc(var(--spacing) * 3);
|
||||
}
|
||||
.w-3\.5 {
|
||||
width: calc(var(--spacing) * 3.5);
|
||||
}
|
||||
@@ -423,9 +416,6 @@
|
||||
.w-5 {
|
||||
width: calc(var(--spacing) * 5);
|
||||
}
|
||||
.w-9 {
|
||||
width: calc(var(--spacing) * 9);
|
||||
}
|
||||
.w-9\/10 {
|
||||
width: calc(9/10 * 100%);
|
||||
}
|
||||
@@ -438,9 +428,6 @@
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
.flex-shrink {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
.flex-shrink-0 {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -456,21 +443,10 @@
|
||||
.basis-\[content\] {
|
||||
flex-basis: content;
|
||||
}
|
||||
.border-collapse {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.-translate-x-1 {
|
||||
--tw-translate-x: calc(var(--spacing) * -1);
|
||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||
}
|
||||
.-translate-x-1\/2 {
|
||||
--tw-translate-x: calc(calc(1/2 * 100%) * -1);
|
||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||
}
|
||||
.-translate-y-1 {
|
||||
--tw-translate-y: calc(var(--spacing) * -1);
|
||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||
}
|
||||
.-translate-y-1\/2 {
|
||||
--tw-translate-y: calc(calc(1/2 * 100%) * -1);
|
||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||
@@ -481,9 +457,6 @@
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
.resize {
|
||||
resize: both;
|
||||
}
|
||||
.scroll-m-2 {
|
||||
scroll-margin: calc(var(--spacing) * 2);
|
||||
}
|
||||
@@ -617,12 +590,6 @@
|
||||
.border-neutral-600 {
|
||||
border-color: var(--color-neutral-600);
|
||||
}
|
||||
.border-slate-300 {
|
||||
border-color: var(--color-slate-300);
|
||||
}
|
||||
.border-slate-700 {
|
||||
border-color: var(--color-slate-700);
|
||||
}
|
||||
.border-slate-800 {
|
||||
border-color: var(--color-slate-800);
|
||||
}
|
||||
@@ -647,9 +614,6 @@
|
||||
.bg-red-600 {
|
||||
background-color: var(--color-red-600);
|
||||
}
|
||||
.mask-repeat {
|
||||
mask-repeat: repeat;
|
||||
}
|
||||
.p-1 {
|
||||
padding: calc(var(--spacing) * 1);
|
||||
}
|
||||
@@ -726,16 +690,9 @@
|
||||
.uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.underline {
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
.opacity-0 {
|
||||
opacity: 0%;
|
||||
}
|
||||
.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,);
|
||||
}
|
||||
@@ -1181,11 +1138,6 @@
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-outline-style {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: solid;
|
||||
}
|
||||
@property --tw-blur {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
@@ -1258,7 +1210,6 @@
|
||||
--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;
|
||||
|
||||
@@ -15,7 +15,9 @@ function editWorkday(element, event, id, isWorkDay) {
|
||||
event.preventDefault();
|
||||
let form = document.getElementById(id);
|
||||
if (form == null) {
|
||||
form = element.closest(".grid-sub").querySelector(".all-booking-component > form");
|
||||
form = element
|
||||
.closest(".grid-sub")
|
||||
.querySelector(".all-booking-component > form");
|
||||
}
|
||||
|
||||
clearEditState();
|
||||
@@ -37,10 +39,21 @@ function editWorkday(element, event, id, isWorkDay) {
|
||||
const absenceForm = document.getElementById("absence_form");
|
||||
|
||||
if (id == 0) {
|
||||
absenceForm.querySelector("[name=date_from]").value = form.id.replace("time-", "");
|
||||
absenceForm.querySelector("[name=date_to]").value = form.id.replace("time-", "");
|
||||
absenceForm.querySelector("[name=date_from]").value = form.id.replace(
|
||||
"time-",
|
||||
"",
|
||||
);
|
||||
absenceForm.querySelector("[name=date_to]").value = form.id.replace(
|
||||
"time-",
|
||||
"",
|
||||
);
|
||||
} else {
|
||||
syncFields(form, absenceForm, ["date_from", "date_to", "aw_type", "aw_id"]);
|
||||
syncFields(form, absenceForm, [
|
||||
"date_from",
|
||||
"date_to",
|
||||
"aw_type",
|
||||
"aw_id",
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,11 +62,6 @@ function toggleAbsenceEdit(state) {
|
||||
const form = document.getElementById("absence_form");
|
||||
if (state) {
|
||||
form.classList.remove("hidden");
|
||||
form.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "start",
|
||||
inline: "nearest",
|
||||
});
|
||||
} else {
|
||||
form.classList.add("hidden");
|
||||
}
|
||||
@@ -85,3 +93,12 @@ function checkAll(pattern, state) {
|
||||
input.checked = state;
|
||||
}
|
||||
}
|
||||
|
||||
const bookingForms = document.querySelectorAll("form.bookings");
|
||||
for (let form of bookingForms) {
|
||||
let selectKommenInput = form.querySelector("input[name='select_kommen']");
|
||||
let kommenGehenSelector = form.querySelector("select");
|
||||
if (selectKommenInput) {
|
||||
kommenGehenSelector.value = selectKommenInput.value == "true" ? 3 : 4;
|
||||
}
|
||||
}
|
||||
|
||||
97
Backend/templates/baseComponents.templ
Normal file
97
Backend/templates/baseComponents.templ
Normal file
@@ -0,0 +1,97 @@
|
||||
// templates are used for the templ templates
|
||||
// these will render the html page with data from the webserver
|
||||
package templates
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/models"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// this file includes the basic components, which are used in many other components and pages
|
||||
templ headerComponent() {
|
||||
// {{ user := ctx.Value("user").(models.User) }}
|
||||
<div class="flex flex-row justify-between md:mx-[10%] py-2 items-center">
|
||||
<a href="/time">Zeitverwaltung</a>
|
||||
<a href="/team/report">Abrechnung</a>
|
||||
if true {
|
||||
<a href="/pdf">Monatsabrechnung</a>
|
||||
<a href="/team/presence">Anwesenheit</a>
|
||||
}
|
||||
<a href="/user/settings">Einstellungen</a>
|
||||
<button onclick="logoutUser()" type="button" class="cursor-pointer">Abmelden</button>
|
||||
</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 lineComponent() {
|
||||
<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 timeGaugeComponent(progress int8, today bool) {
|
||||
{{
|
||||
var bgColor string
|
||||
switch {
|
||||
case (0 > progress):
|
||||
bgColor = "bg-red-600"
|
||||
break
|
||||
case (progress > 0 && progress < 95):
|
||||
bgColor = "bg-orange-500"
|
||||
break
|
||||
case (95 <= progress && progress <= 105):
|
||||
bgColor = "bg-accent"
|
||||
break
|
||||
case (progress > 105):
|
||||
bgColor = "bg-purple-600"
|
||||
break
|
||||
default:
|
||||
bgColor = "bg-neutral-400"
|
||||
break
|
||||
}
|
||||
}}
|
||||
if today {
|
||||
<div class="flex-start flex w-2 h-full overflow-hidden rounded-full bg-neutral-300 print:hidden">
|
||||
<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 flex-shrink-0", bgColor }></div>
|
||||
}
|
||||
}
|
||||
|
||||
templ legendComponent() {
|
||||
<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 CheckboxComponent(pNr int, label string) {
|
||||
{{ id := fmt.Sprintf("pdf-%d", pNr) }}
|
||||
<div class="inline-flex items-center">
|
||||
<label class="flex items-center cursor-pointer relative" for={ id }>
|
||||
<input type="checkbox" name="employe_list" value={ pNr } id={ id } class="peer h-5 w-5 cursor-pointer transition-all appearance-none rounded border border-slate-800 checked:bg-slate-800 checked:border-slate-800"/>
|
||||
<span class="absolute text-white opacity-0 peer-checked:opacity-100 top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="currentColor" stroke="currentColor" stroke-width="1">
|
||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</label> <label class="cursor-pointer ml-2 text-slate-600 select-none" for={ id }>{ label }</label>
|
||||
</div>
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
package templates
|
||||
|
||||
import "arbeitszeitmessung/models"
|
||||
// this file includes the basic pages, that have no further complexity,
|
||||
// like the login and settings page, the more complex pages have their own files
|
||||
|
||||
templ Base() {
|
||||
import "arbeitszeitmessung/models"
|
||||
import "arbeitszeitmessung/helper"
|
||||
|
||||
templ BasePage() {
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>Arbeitszeit</title>
|
||||
@@ -13,7 +17,7 @@ templ Base() {
|
||||
}
|
||||
|
||||
templ LoginPage(success bool, errorMsg string) {
|
||||
@Base()
|
||||
@BasePage()
|
||||
<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>
|
||||
@@ -29,10 +33,8 @@ templ LoginPage(success bool, errorMsg string) {
|
||||
}
|
||||
|
||||
templ SettingsPage(status int) {
|
||||
{{
|
||||
user := ctx.Value("user").(models.User)
|
||||
}}
|
||||
@Base()
|
||||
{{ user := ctx.Value("user").(models.User) }}
|
||||
@BasePage()
|
||||
@headerComponent()
|
||||
<div class="grid-main divide-y-1">
|
||||
<form method="POST" class="grid-sub responsive lg:divide-x-1">
|
||||
@@ -59,6 +61,8 @@ templ SettingsPage(status int) {
|
||||
<div class="grid-cell col-span-3">
|
||||
<p>Nutzername: <span class="text-neutral-500">{ user.Vorname } { user.Name }</span></p>
|
||||
<p>Personalnummer: <span class="text-neutral-500">{ user.PersonalNummer }</span></p>
|
||||
<p>Arbeitszeit pro Tag: <span class="text-neutral-500">{ helper.FormatDuration(user.ArbeitszeitProTag()) }</span></p>
|
||||
<p>Arbeitszeit pro Woche: <span class="text-neutral-500">{ helper.FormatDuration(user.ArbeitszeitProWoche()) }</span></p>
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
@@ -73,36 +77,3 @@ templ SettingsPage(status int) {
|
||||
</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) {
|
||||
@Base()
|
||||
@headerComponent()
|
||||
<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-xl uppercase font-bold">Eigene Abrechnung</h2>
|
||||
</div>
|
||||
</div>
|
||||
@workWeekComponent(userWeek, false)
|
||||
if len(weeks) > 0 {
|
||||
<div class="grid-cell col-span-full bg-neutral-300">
|
||||
<h2 class="text-xl uppercase font-bold">Abrechnung Mitarbeiter</h2>
|
||||
</div>
|
||||
}
|
||||
for _, week := range weeks {
|
||||
@workWeekComponent(week, true)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
templ LogoutButton() {
|
||||
<button onclick="logoutUser()" type="button" class="cursor-pointer">Abmelden</button>
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package templates
|
||||
|
||||
templ headerComponent() {
|
||||
<div class="flex flex-row justify-between md:mx-[10%] py-2 items-center">
|
||||
<a href="/time">Zeitverwaltung</a>
|
||||
<a href="/team">Abrechnung</a>
|
||||
<a href="/pdf">PDF</a>
|
||||
if true {
|
||||
<a href="/presence">Anwesenheit</a>
|
||||
}
|
||||
<a href="/user/settings">Einstellungen</a>
|
||||
@LogoutButton()
|
||||
</div>
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.943
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
func headerComponent() 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_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"flex flex-row justify-between md:mx-[10%] py-2 items-center\"><a href=\"/time\">Zeitverwaltung</a> <a href=\"/team\">Abrechnung</a> <a href=\"/pdf\">PDF</a> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if true {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<a href=\"/presence\">Anwesenheit</a> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<a href=\"/user/settings\">Einstellungen</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = LogoutButton().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@@ -1,319 +0,0 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.943
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "arbeitszeitmessung/models"
|
||||
|
||||
func Base() 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_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><head><title>Arbeitszeit</title><link rel=\"stylesheet\" href=\"/static/css/styles.css\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><script src=\"/static/script.js\" defer></script></head>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
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 {
|
||||
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_Var2 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var2 == nil {
|
||||
templ_7745c5c3_Var2 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = Base().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<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 !success {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<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_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(errorMsg)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pages.templ`, Line: 24, Col: 46}
|
||||
}
|
||||
_, 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>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<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
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func SettingsPage(status int) 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_Var4 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var4 == nil {
|
||||
templ_7745c5c3_Var4 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
|
||||
user := ctx.Value("user").(models.User)
|
||||
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, 6, "<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, 7, "<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, 8, "<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, 9, "<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, 10, "</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\">Nutzerdaten</h1><div class=\"grid-cell col-span-3\"><p>Nutzername: <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(user.Vorname)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pages.templ`, Line: 60, Col: 64}
|
||||
}
|
||||
_, 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, 11, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(user.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pages.templ`, Line: 60, Col: 78}
|
||||
}
|
||||
_, 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, 12, "</span></p><p>Personalnummer: <span class=\"text-neutral-500\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(user.PersonalNummer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pages.templ`, Line: 61, Col: 75}
|
||||
}
|
||||
_, 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, 13, "</span></p></div><div></div></div><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
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
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_Var8 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var8 == nil {
|
||||
templ_7745c5c3_Var8 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
if status >= target {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<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, 15, "<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
|
||||
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_Var9 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var9 == nil {
|
||||
templ_7745c5c3_Var9 = 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, 16, "<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-xl uppercase font-bold\">Eigene Abrechnung</h2></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = workWeekComponent(userWeek, false).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if len(weeks) > 0 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<div class=\"grid-cell col-span-full bg-neutral-300\"><h2 class=\"text-xl uppercase font-bold\">Abrechnung Mitarbeiter</h2></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
for _, week := range weeks {
|
||||
templ_7745c5c3_Err = workWeekComponent(week, true).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func LogoutButton() templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var10 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var10 == nil {
|
||||
templ_7745c5c3_Var10 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<button onclick=\"logoutUser()\" type=\"button\" class=\"cursor-pointer\">Abmelden</button>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@@ -1,136 +0,0 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
templ PDFForm(teamMembers []models.User) {
|
||||
@Base()
|
||||
@headerComponent()
|
||||
<form class="grid-main divide-y-1" action="pdf/generate" method="get">
|
||||
<div class="grid-cell col-span-full bg-neutral-300">
|
||||
<h1 class="text-xl uppercase font-bold">PDF Abrechnung erstellen</h1>
|
||||
</div>
|
||||
<div class="grid-sub divide-x-1 responsive">
|
||||
<div class="grid-cell">Zeitraum wählen</div>
|
||||
<div class="grid-cell col-span-3">
|
||||
<label class="block mb-1 text-sm text-neutral-700">Abrechnungsmonat</label>
|
||||
<input name="start_date" type="date" value={ helper.GetFirstOfMonth(time.Now()).Format(time.DateOnly) } class="btn bg-neutral-100"/>
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="grid-sub divide-x-1 responsive">
|
||||
<div class="grid-cell">Mitarbeiter wählen</div>
|
||||
<div class="grid-cell col-span-3 flex flex-col gap-2">
|
||||
<div class="flex flex-row gap-2">
|
||||
<button class="btn" type="button" onclick={ templ.JSFuncCall("checkAll", "pdf-", templ.JSExpression("true")) }>Alle</button>
|
||||
<button class="btn" type="button" onclick={ templ.JSFuncCall("checkAll", "pdf-", templ.JSExpression("false")) }>Keine</button>
|
||||
</div>
|
||||
for _, member := range teamMembers {
|
||||
@CheckboxComponent(member.PersonalNummer, fmt.Sprintf("%s %s", member.Vorname, member.Name))
|
||||
}
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="grid-sub divide-x-1 responsive">
|
||||
<div class="grid-cell">PDFs Bündeln</div>
|
||||
<div class="grid-cell col-span-3 flex gap-2 flex-col md:flex-row">
|
||||
<button class="btn" type="submit" name="output" value="download">Einzeln</button>
|
||||
<button class="btn" type="submit" name="output" value="render" onclick="">Bündel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
||||
templ CheckboxComponent(pNr int, label string) {
|
||||
{{
|
||||
id := fmt.Sprintf("pdf-%d", pNr)
|
||||
}}
|
||||
<div class="inline-flex items-center">
|
||||
<label class="flex items-center cursor-pointer relative" for={ id }>
|
||||
<input type="checkbox" name="employe_list" value={ pNr } id={ id } class="peer h-5 w-5 cursor-pointer transition-all appearance-none rounded border border-slate-800 checked:bg-slate-800 checked:border-slate-800"/>
|
||||
<span class="absolute text-white opacity-0 peer-checked:opacity-100 top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="currentColor" stroke="currentColor" stroke-width="1">
|
||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</label> <label class="cursor-pointer ml-2 text-slate-600 select-none" for={ id }>{ label }</label>
|
||||
</div>
|
||||
}
|
||||
|
||||
// templ PDFReportEmploye(e models.User, overtime, worktime time.Duration, workDays []models.IWorkDay, tsStart time.Time, tsEnd time.Time) {
|
||||
// {{
|
||||
// _, kw := tsStart.ISOWeek()
|
||||
// noBorder := ""
|
||||
// }}
|
||||
// @Base()
|
||||
// <content class="p-8 relative flex flex-col gap-4 break-after-page">
|
||||
// <div>
|
||||
// <h1 class="text-2xl font-bold">{ e.Vorname } { e.Name }</h1>
|
||||
// <p>Zeitraum: <span>{ tsStart.Format("02.01.2006") }</span> - <span>{ tsEnd.Format("02.01.2006") }</span></p>
|
||||
// <p>Arbeitszeit: <span>{ helper.FormatDuration(worktime) }</span></p>
|
||||
// <p>Überstunden: <span>{ helper.FormatDuration(overtime) }</span></p>
|
||||
// </div>
|
||||
// <div class="grid grid-rows-6 grid-cols-[3fr_2fr_2fr_2fr_3fr_3fr_3fr] *:not-print:p-2 *:text-center auto-rows-min divide-neutral-300 divide-x-1 divide-y-1">
|
||||
// <p class="bg-neutral-300 border-neutral-600">{ kw }</p>
|
||||
// <p class="bg-neutral-300 border-neutral-600">Kommen</p>
|
||||
// <p class="bg-neutral-300 border-neutral-600">Gehen</p>
|
||||
// <p class="bg-neutral-300 border-neutral-600">Arbeitsart</p>
|
||||
// <p class="bg-neutral-300 border-neutral-600">Stunden</p>
|
||||
// <p class="bg-neutral-300 border-neutral-600">Pause</p>
|
||||
// <p class="bg-neutral-300 border-neutral-600 border-r-0">Überstunden</p>
|
||||
// for index, day := range workDays {
|
||||
// {{
|
||||
// if index == len(workDays)-1 {
|
||||
// noBorder = "border-b-0"
|
||||
// }
|
||||
// }}
|
||||
// <p class={ noBorder }>{ day.Date().Format("02.01.2006") }</p>
|
||||
// <div class={ "grid grid-cols-subgrid col-span-3 " + noBorder }>
|
||||
// if day.IsWorkDay() {
|
||||
// {{
|
||||
// workDay, _ := day.(*models.WorkDay)
|
||||
// }}
|
||||
// for bookingI := 0; bookingI < len(workDay.Bookings); bookingI+= 2 {
|
||||
// <p>{ workDay.Bookings[bookingI].Timestamp.Format("15:04") }</p>
|
||||
// <p>{ workDay.Bookings[bookingI+1].Timestamp.Format("15:04") }</p>
|
||||
// <p>{ workDay.Bookings[bookingI].BookingType.Name } </p>
|
||||
// }
|
||||
// if workDay.IsKurzArbeit() {
|
||||
// {{
|
||||
// timeFrom, timeTo := workDay.GenerateKurzArbeitBookings(e)
|
||||
// }}
|
||||
// <p>{ timeFrom.Format("15:04") }</p>
|
||||
// <p>{ timeTo.Format("15:04") }</p>
|
||||
// <p>Kurzarbeit</p>
|
||||
// }
|
||||
// } else {
|
||||
// {{
|
||||
// absentDay, _ := day.(*models.Absence)
|
||||
// }}
|
||||
// <p class="col-span-full">{ absentDay.AbwesenheitTyp.Name }</p>
|
||||
// }
|
||||
// </div>
|
||||
// {{ work, pause, overtime := day.GetTimesVirtual(e) }}
|
||||
// @ColorDuration(work, noBorder)
|
||||
// @ColorDuration(pause, noBorder)
|
||||
// @ColorDuration(overtime, noBorder+" border-r-0")
|
||||
// if day.Date().Weekday() == time.Friday {
|
||||
// <p class="col-span-full bg-neutral-300">Wochenende</p>
|
||||
// }
|
||||
// }
|
||||
// </div>
|
||||
// </content>
|
||||
// }
|
||||
templ ColorDuration(d time.Duration, classes string) {
|
||||
{{
|
||||
color := ""
|
||||
if d.Abs() < time.Minute {
|
||||
color = "text-neutral-300"
|
||||
}
|
||||
}}
|
||||
<p class={ color + " " + classes }>{ helper.FormatDurationFill(d, true) }</p>
|
||||
}
|
||||
48
Backend/templates/pdfPage.templ
Normal file
48
Backend/templates/pdfPage.templ
Normal file
@@ -0,0 +1,48 @@
|
||||
package templates
|
||||
|
||||
// this file has all templates for the /pdf page
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
templ PDFForm(teamMembers []models.User) {
|
||||
@BasePage()
|
||||
@headerComponent()
|
||||
<form class="grid-main divide-y-1" action="pdf/generate" method="get">
|
||||
<div class="grid-cell col-span-full bg-neutral-300">
|
||||
<h1 class="text-xl uppercase font-bold">Monatsabrechnung erstellen</h1>
|
||||
</div>
|
||||
<div class="grid-sub divide-x-1 responsive">
|
||||
<div class="grid-cell">Zeitraum wählen</div>
|
||||
<div class="grid-cell col-span-3">
|
||||
<label class="block mb-1 text-sm text-neutral-700">Abrechnungsmonat</label>
|
||||
<input name="start_date" type="date" value={ helper.GetFirstOfMonth(time.Now()).Format(time.DateOnly) } class="btn bg-neutral-100"/>
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="grid-sub divide-x-1 responsive">
|
||||
<div class="grid-cell">Mitarbeiter wählen</div>
|
||||
<div class="grid-cell col-span-3 flex flex-col gap-2">
|
||||
<div class="flex flex-row gap-2">
|
||||
<button class="btn" type="button" onclick={ templ.JSFuncCall("checkAll", "pdf-", templ.JSExpression("true")) }>Alle</button>
|
||||
<button class="btn" type="button" onclick={ templ.JSFuncCall("checkAll", "pdf-", templ.JSExpression("false")) }>Keine</button>
|
||||
</div>
|
||||
for _, member := range teamMembers {
|
||||
@CheckboxComponent(member.PersonalNummer, fmt.Sprintf("%s %s", member.Vorname, member.Name))
|
||||
}
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="grid-sub divide-x-1 responsive">
|
||||
<div class="grid-cell">PDFs Bündeln</div>
|
||||
<div class="grid-cell col-span-3 flex gap-2 flex-col md:flex-row">
|
||||
<button class="btn" type="submit" name="output" value="download">Einzeln</button>
|
||||
<button class="btn" type="submit" name="output" value="render" onclick="">Bündel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
@@ -1,337 +0,0 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.943
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func PDFForm(teamMembers []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_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = 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, 1, "<form class=\"grid-main divide-y-1\" action=\"pdf/generate\" method=\"get\"><div class=\"grid-cell col-span-full bg-neutral-300\"><h1 class=\"text-xl uppercase font-bold\">PDF Abrechnung erstellen</h1></div><div class=\"grid-sub divide-x-1 responsive\"><div class=\"grid-cell\">Zeitraum wählen</div><div class=\"grid-cell col-span-3\"><label class=\"block mb-1 text-sm text-neutral-700\">Abrechnungsmonat</label> <input name=\"start_date\" type=\"date\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(helper.GetFirstOfMonth(time.Now()).Format(time.DateOnly))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 21, Col: 105}
|
||||
}
|
||||
_, 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, "\" class=\"btn bg-neutral-100\"></div><div></div></div><div class=\"grid-sub divide-x-1 responsive\"><div class=\"grid-cell\">Mitarbeiter wählen</div><div class=\"grid-cell col-span-3 flex flex-col gap-2\"><div class=\"flex flex-row gap-2\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.JSFuncCall("checkAll", "pdf-", templ.JSExpression("true")))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<button class=\"btn\" type=\"button\" onclick=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 templ.ComponentScript = templ.JSFuncCall("checkAll", "pdf-", templ.JSExpression("true"))
|
||||
_, 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, "\">Alle</button> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.JSFuncCall("checkAll", "pdf-", templ.JSExpression("false")))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<button class=\"btn\" type=\"button\" onclick=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 templ.ComponentScript = templ.JSFuncCall("checkAll", "pdf-", templ.JSExpression("false"))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4.Call)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\">Keine</button></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, member := range teamMembers {
|
||||
templ_7745c5c3_Err = CheckboxComponent(member.PersonalNummer, fmt.Sprintf("%s %s", member.Vorname, member.Name)).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div><div></div></div><div class=\"grid-sub divide-x-1 responsive\"><div class=\"grid-cell\">PDFs Bündeln</div><div class=\"grid-cell col-span-3 flex gap-2 flex-col md:flex-row\"><button class=\"btn\" type=\"submit\" name=\"output\" value=\"download\">Einzeln</button> <button class=\"btn\" type=\"submit\" name=\"output\" value=\"render\" onclick=\"\">Bündel</button></div></div></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func CheckboxComponent(pNr int, label 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 {
|
||||
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_Var5 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var5 == nil {
|
||||
templ_7745c5c3_Var5 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
|
||||
id := fmt.Sprintf("pdf-%d", pNr)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<div class=\"inline-flex items-center\"><label class=\"flex items-center cursor-pointer relative\" for=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(id)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 53, Col: 67}
|
||||
}
|
||||
_, 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, "\"><input type=\"checkbox\" name=\"employe_list\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(pNr)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 54, Col: 57}
|
||||
}
|
||||
_, 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, "\" id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(id)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 54, Col: 67}
|
||||
}
|
||||
_, 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, "\" class=\"peer h-5 w-5 cursor-pointer transition-all appearance-none rounded border border-slate-800 checked:bg-slate-800 checked:border-slate-800\"> <span class=\"absolute text-white opacity-0 peer-checked:opacity-100 top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2\"><svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-3.5 w-3.5\" viewBox=\"0 0 20 20\" fill=\"currentColor\" stroke=\"currentColor\" stroke-width=\"1\"><path fill-rule=\"evenodd\" d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\" clip-rule=\"evenodd\"></path></svg></span></label> <label class=\"cursor-pointer ml-2 text-slate-600 select-none\" for=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(id)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 60, Col: 81}
|
||||
}
|
||||
_, 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, 12, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var10 string
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(label)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 60, Col: 91}
|
||||
}
|
||||
_, 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, 13, "</label></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// templ PDFReportEmploye(e models.User, overtime, worktime time.Duration, workDays []models.IWorkDay, tsStart time.Time, tsEnd time.Time) {
|
||||
// {{
|
||||
// _, kw := tsStart.ISOWeek()
|
||||
// noBorder := ""
|
||||
// }}
|
||||
// @Base()
|
||||
// <content class="p-8 relative flex flex-col gap-4 break-after-page">
|
||||
// <div>
|
||||
// <h1 class="text-2xl font-bold">{ e.Vorname } { e.Name }</h1>
|
||||
// <p>Zeitraum: <span>{ tsStart.Format("02.01.2006") }</span> - <span>{ tsEnd.Format("02.01.2006") }</span></p>
|
||||
// <p>Arbeitszeit: <span>{ helper.FormatDuration(worktime) }</span></p>
|
||||
// <p>Überstunden: <span>{ helper.FormatDuration(overtime) }</span></p>
|
||||
// </div>
|
||||
// <div class="grid grid-rows-6 grid-cols-[3fr_2fr_2fr_2fr_3fr_3fr_3fr] *:not-print:p-2 *:text-center auto-rows-min divide-neutral-300 divide-x-1 divide-y-1">
|
||||
// <p class="bg-neutral-300 border-neutral-600">{ kw }</p>
|
||||
// <p class="bg-neutral-300 border-neutral-600">Kommen</p>
|
||||
// <p class="bg-neutral-300 border-neutral-600">Gehen</p>
|
||||
// <p class="bg-neutral-300 border-neutral-600">Arbeitsart</p>
|
||||
// <p class="bg-neutral-300 border-neutral-600">Stunden</p>
|
||||
// <p class="bg-neutral-300 border-neutral-600">Pause</p>
|
||||
// <p class="bg-neutral-300 border-neutral-600 border-r-0">Überstunden</p>
|
||||
// for index, day := range workDays {
|
||||
// {{
|
||||
// if index == len(workDays)-1 {
|
||||
// noBorder = "border-b-0"
|
||||
// }
|
||||
// }}
|
||||
// <p class={ noBorder }>{ day.Date().Format("02.01.2006") }</p>
|
||||
// <div class={ "grid grid-cols-subgrid col-span-3 " + noBorder }>
|
||||
// if day.IsWorkDay() {
|
||||
// {{
|
||||
// workDay, _ := day.(*models.WorkDay)
|
||||
// }}
|
||||
// for bookingI := 0; bookingI < len(workDay.Bookings); bookingI+= 2 {
|
||||
// <p>{ workDay.Bookings[bookingI].Timestamp.Format("15:04") }</p>
|
||||
// <p>{ workDay.Bookings[bookingI+1].Timestamp.Format("15:04") }</p>
|
||||
// <p>{ workDay.Bookings[bookingI].BookingType.Name } </p>
|
||||
// }
|
||||
// if workDay.IsKurzArbeit() {
|
||||
// {{
|
||||
// timeFrom, timeTo := workDay.GenerateKurzArbeitBookings(e)
|
||||
// }}
|
||||
// <p>{ timeFrom.Format("15:04") }</p>
|
||||
// <p>{ timeTo.Format("15:04") }</p>
|
||||
// <p>Kurzarbeit</p>
|
||||
// }
|
||||
// } else {
|
||||
// {{
|
||||
// absentDay, _ := day.(*models.Absence)
|
||||
// }}
|
||||
// <p class="col-span-full">{ absentDay.AbwesenheitTyp.Name }</p>
|
||||
// }
|
||||
// </div>
|
||||
// {{ work, pause, overtime := day.GetTimesVirtual(e) }}
|
||||
// @ColorDuration(work, noBorder)
|
||||
// @ColorDuration(pause, noBorder)
|
||||
// @ColorDuration(overtime, noBorder+" border-r-0")
|
||||
// if day.Date().Weekday() == time.Friday {
|
||||
// <p class="col-span-full bg-neutral-300">Wochenende</p>
|
||||
// }
|
||||
// }
|
||||
// </div>
|
||||
// </content>
|
||||
// }
|
||||
func ColorDuration(d time.Duration, classes 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 {
|
||||
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_Var11 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var11 == nil {
|
||||
templ_7745c5c3_Var11 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
|
||||
color := ""
|
||||
if d.Abs() < time.Minute {
|
||||
color = "text-neutral-300"
|
||||
}
|
||||
var templ_7745c5c3_Var12 = []any{color + " " + classes}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var12...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<p class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var13 string
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var12).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, 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, 15, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var14 string
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(helper.FormatDurationFill(d, true))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 135, Col: 72}
|
||||
}
|
||||
_, 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, 16, "</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@@ -1,10 +1,12 @@
|
||||
package templates
|
||||
|
||||
// this file has all the templates for the /team/presence page
|
||||
|
||||
import "arbeitszeitmessung/models"
|
||||
import "arbeitszeitmessung/helper"
|
||||
|
||||
templ TeamPresencePage(teamPresence map[models.User]bool) {
|
||||
@Base()
|
||||
@BasePage()
|
||||
@headerComponent()
|
||||
<div class="grid-main divide-y-1">
|
||||
<div class="grid-sub divide-x-1 bg-neutral-300">
|
||||
@@ -27,3 +29,14 @@ templ TeamPresencePage(teamPresence map[models.User]bool) {
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
templ userPresenceComponent(user models.User, present bool) {
|
||||
<div class="grid-cell group flex flex-row gap-2">
|
||||
if present {
|
||||
<div class="h-8 bg-accent rounded-md group-hover:text-black md:text-transparent text-center p-1">Anwesend</div>
|
||||
} else {
|
||||
<div class="h-8 bg-red-600 rounded-md group-hover:text-white md:text-transparent text-center p-1">Abwesend</div>
|
||||
}
|
||||
<p>{ user.Vorname } { user.Name }</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.943
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "arbeitszeitmessung/models"
|
||||
import "arbeitszeitmessung/helper"
|
||||
|
||||
func TeamPresencePage(teamPresence map[models.User]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 {
|
||||
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_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = 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, 1, "<div class=\"grid-main divide-y-1\"><div class=\"grid-sub divide-x-1 bg-neutral-300\"><h2 class=\"grid-cell font-bold uppercase text-xl\">Mitarbeiter</h2></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for user, present := range teamPresence {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"grid-sub\"><div class=\"grid-cell flex flex-row gap-2 col-span-2 md:col-span-1\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = timeGaugeComponent(helper.BoolToInt8(present)*100-1, false).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(user.Vorname)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/presencePage.templ`, Line: 17, Col: 22}
|
||||
}
|
||||
_, 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, 4, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(user.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/presencePage.templ`, Line: 17, Col: 36}
|
||||
}
|
||||
_, 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, 5, "</p></div><div class=\"grid-cell col-span-2\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if present {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<span class=\"text-neutral-500\">Anwesend</span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<span class=\"text-neutral-500\">Abwesend</span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@@ -1,5 +1,7 @@
|
||||
package templates
|
||||
|
||||
// this file has all the templates for the team/report page
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
@@ -8,73 +10,24 @@ import (
|
||||
"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>
|
||||
}
|
||||
|
||||
templ defaultWeekDayComponent(u models.User, day models.IWorkDay) {
|
||||
<div class="flex flex-row gap-2">
|
||||
@timeGaugeComponent(day.GetDayProgress(u), false)
|
||||
<div class="flex flex-col">
|
||||
<p class=""><span class="font-bold uppercase hidden md:inline">{ helper.FormatGermanDayOfWeek(day.Date()) }:</span> { day.Date().Format("02.01.2006") }</p>
|
||||
if day.IsWorkDay() {
|
||||
{{
|
||||
workDay, _ := day.(*models.WorkDay)
|
||||
work, pause, _ := workDay.GetTimes(u, models.WorktimeBaseDay, false)
|
||||
}}
|
||||
if !workDay.RequiresAction() {
|
||||
<div class="flex flex-row gap-2">
|
||||
<span class="text-accent">{ helper.FormatDuration(work) }</span>
|
||||
<span class="text-neutral-500">{ helper.FormatDuration(pause) }</span>
|
||||
</div>
|
||||
<div class="flex flex-row gap-2 items-center">
|
||||
<span class="icon-[material-symbols-light--schedule-outline] flex-shrink-0"></span>
|
||||
switch {
|
||||
case !workDay.TimeFrom.Equal(workDay.TimeTo):
|
||||
<span>{ workDay.TimeFrom.Format("15:04") }</span>
|
||||
<span>-</span>
|
||||
<span>{ workDay.TimeTo.Format("15:04") }</span>
|
||||
default:
|
||||
<p>Keine Anwesenheit</p>
|
||||
}
|
||||
</div>
|
||||
} else {
|
||||
<p class="text-red-600">Bitte anpassen</p>
|
||||
}
|
||||
} else {
|
||||
{{
|
||||
absentDay, _ := day.(*models.Absence)
|
||||
}}
|
||||
<div>{ absentDay.AbwesenheitTyp.Name } </div>
|
||||
}
|
||||
templ ReportPage(weeks []models.WorkWeek, userWeek models.WorkWeek) {
|
||||
@BasePage()
|
||||
@headerComponent()
|
||||
<div class="grid-main divide-y-1 @container">
|
||||
<div class="grid-sub lg:divide-x-1 max-md:divide-y-1 responsive">
|
||||
<div class="grid-cell col-span-full bg-neutral-300 lg:border-0">
|
||||
<h2 class="text-xl uppercase font-bold">Eigene Abrechnung</h2>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ weekDayComponent(user models.User, day models.WorkDay) {
|
||||
// {{ work, pause, _ := day.GetAllWorkTimesReal(user) }}
|
||||
<div class="flex flex-row gap-2">
|
||||
// @timeGaugeComponent(day.GetWorkDayProgress(user), false, day.RequiresAction())
|
||||
<div class="flex flex-col">
|
||||
if !day.RequiresAction() {
|
||||
}
|
||||
@workWeekComponent(userWeek, false)
|
||||
if len(weeks) > 0 {
|
||||
<div class="grid-cell col-span-full bg-neutral-300">
|
||||
<h2 class="text-xl uppercase font-bold">Abrechnung Mitarbeiter</h2>
|
||||
</div>
|
||||
}
|
||||
for _, week := range weeks {
|
||||
@workWeekComponent(week, true)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -83,7 +36,7 @@ templ workWeekComponent(week models.WorkWeek, onlyAccept bool) {
|
||||
year, kw := week.WeekStart.ISOWeek()
|
||||
progress := (float32(week.WorktimeVirtual.Hours()) / week.User.ArbeitszeitPerWoche) * 100
|
||||
}}
|
||||
<div class="employeComponent grid-sub responsive lg:divide-x-1 max-md:divide-y-1 @container">
|
||||
<div class="employeComponent grid-sub responsive lg:divide-x-1 max-md:divide-y-1">
|
||||
<div class="grid-cell flex flex-col max-md:bg-neutral-300 gap-2">
|
||||
if !onlyAccept {
|
||||
<div class="lg:hidden">
|
||||
@@ -163,13 +116,69 @@ templ workWeekComponent(week models.WorkWeek, onlyAccept bool) {
|
||||
</div>
|
||||
}
|
||||
|
||||
templ userPresenceComponent(user models.User, present bool) {
|
||||
<div class="grid-cell group flex flex-row gap-2">
|
||||
if present {
|
||||
<div class="h-8 bg-accent rounded-md group-hover:text-black md:text-transparent text-center p-1">Anwesend</div>
|
||||
} else {
|
||||
<div class="h-8 bg-red-600 rounded-md group-hover:text-white md:text-transparent text-center p-1">Abwesend</div>
|
||||
}
|
||||
<p>{ user.Vorname } { user.Name }</p>
|
||||
templ defaultWeekDayComponent(u models.User, day models.IWorkDay) {
|
||||
<div class="flex flex-row gap-2">
|
||||
@timeGaugeComponent(day.GetDayProgress(u), false)
|
||||
<div class="flex flex-col">
|
||||
<p class=""><span class="font-bold uppercase hidden md:inline">{ helper.FormatGermanDayOfWeek(day.Date()) }:</span> { day.Date().Format("02.01.2006") }</p>
|
||||
{{ work, pause, _ := day.GetTimes(u, models.WorktimeBaseDay, false) }}
|
||||
if day.IsWorkDay() || day.GetDayProgress(u) < 100 {
|
||||
<div class="flex flex-row gap-2">
|
||||
<span class="text-accent">{ helper.FormatDuration(work) }</span>
|
||||
<span class="text-neutral-500">{ helper.FormatDuration(pause) }</span>
|
||||
</div>
|
||||
}
|
||||
@weekDayTypeSwitcher(day)
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ weekDayTypeSwitcher(day models.IWorkDay) {
|
||||
switch day.Type() {
|
||||
case models.DayTypeWorkday:
|
||||
{{ workDay, _ := day.(*models.WorkDay) }}
|
||||
@workDayWeekComponent(workDay)
|
||||
case models.DayTypeCompound:
|
||||
for _, c := range day.(*models.CompoundDay).DayParts {
|
||||
@weekDayTypeSwitcher(c)
|
||||
}
|
||||
default:
|
||||
<div>{ day.ToString() }</div>
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
}
|
||||
|
||||
templ workDayWeekComponent(workDay *models.WorkDay) {
|
||||
if !workDay.RequiresAction() {
|
||||
<div class="flex flex-row gap-2 items-center">
|
||||
<span class="icon-[material-symbols-light--schedule-outline] flex-shrink-0"></span>
|
||||
switch {
|
||||
case !workDay.TimeFrom.Equal(workDay.TimeTo):
|
||||
<span>{ workDay.TimeFrom.Format("15:04") }</span>
|
||||
<span>-</span>
|
||||
<span>{ workDay.TimeTo.Format("15:04") }</span>
|
||||
default:
|
||||
<p>Keine Anwesenheit</p>
|
||||
}
|
||||
</div>
|
||||
} else {
|
||||
<p class="text-red-600">Bitte anpassen</p>
|
||||
}
|
||||
}
|
||||
@@ -1,705 +0,0 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.943
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
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 {
|
||||
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_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
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
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func defaultWeekDayComponent(u models.User, day models.IWorkDay) 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)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<div class=\"flex flex-row gap-2\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = timeGaugeComponent(day.GetDayProgress(u), false).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<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(helper.FormatGermanDayOfWeek(day.Date()))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 35, Col: 108}
|
||||
}
|
||||
_, 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, 12, ":</span> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(day.Date().Format("02.01.2006"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 35, Col: 152}
|
||||
}
|
||||
_, 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
|
||||
}
|
||||
if day.IsWorkDay() {
|
||||
|
||||
workDay, _ := day.(*models.WorkDay)
|
||||
work, pause, _ := workDay.GetTimes(u, models.WorktimeBaseDay, false)
|
||||
if !workDay.RequiresAction() {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<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(helper.FormatDuration(work))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 43, Col: 61}
|
||||
}
|
||||
_, 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(helper.FormatDuration(pause))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 44, Col: 67}
|
||||
}
|
||||
_, 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\"><span class=\"icon-[material-symbols-light--schedule-outline] flex-shrink-0\"></span> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
switch {
|
||||
case !workDay.TimeFrom.Equal(workDay.TimeTo):
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(workDay.TimeFrom.Format("15:04"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 50, Col: 48}
|
||||
}
|
||||
_, 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, "</span> <span>-</span> <span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var12 string
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(workDay.TimeTo.Format("15:04"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 52, Col: 46}
|
||||
}
|
||||
_, 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, 19, "</span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
default:
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<p>Keine Anwesenheit</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<p class=\"text-red-600\">Bitte anpassen</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
absentDay, _ := day.(*models.Absence)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var13 string
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(absentDay.AbwesenheitTyp.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 64, Col: 40}
|
||||
}
|
||||
_, 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, 24, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</div></div>")
|
||||
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_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, 26, "<div class=\"flex flex-row gap-2\"><div class=\"flex flex-col\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !day.RequiresAction() {
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func workWeekComponent(week models.WorkWeek, onlyAccept 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 {
|
||||
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_Var15 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var15 == nil {
|
||||
templ_7745c5c3_Var15 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
|
||||
year, kw := week.WeekStart.ISOWeek()
|
||||
progress := (float32(week.WorktimeVirtual.Hours()) / week.User.ArbeitszeitPerWoche) * 100
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<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\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !onlyAccept {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<div class=\"lg:hidden\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = weekPicker(week.WeekStart).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<p class=\"font-bold uppercase\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var16 string
|
||||
templ_7745c5c3_Var16, 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: 93, Col: 53}
|
||||
}
|
||||
_, 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, 32, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var17 string
|
||||
templ_7745c5c3_Var17, 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: 93, Col: 72}
|
||||
}
|
||||
_, 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, 33, "</p><div class=\"grid grid-cols-5 gap-2 lg:grid-cols-1\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !onlyAccept {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<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(week.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, 35, "Gesendet</span> <span class=\"flex flex-row gap-2 items-center\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = statusCheckMark(week.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, 36, "Akzeptiert</span></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div class=\"flex flex-row gap-2 col-span-3\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = timeGaugeComponent(int8(progress), false).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<div><p>Arbeitszeit: ")
|
||||
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.Worktime)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 110, 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, 39, "</p><p>Überstunden: ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var19 string
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s", helper.FormatDurationFill(week.Overtime, true)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 111, Col: 90}
|
||||
}
|
||||
_, 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, 40, "</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
|
||||
}
|
||||
for _, day := range week.Days {
|
||||
templ_7745c5c3_Err = defaultWeekDayComponent(week.User, day).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</div><div class=\"grid-cell flex flex-col gap-2 justify-between\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if onlyAccept {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<p class=\"text-sm\"><span class=\"\">Woche:</span> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var20 string
|
||||
templ_7745c5c3_Var20, 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: 123, Col: 86}
|
||||
}
|
||||
_, 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, 43, "</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "<div class=\"max-md:hidden\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = weekPicker(week.WeekStart).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "<form method=\"post\" class=\"flex flex-col gap-2\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
week.CheckStatus()
|
||||
method := "accept"
|
||||
if !onlyAccept {
|
||||
method = "send"
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<input type=\"hidden\" name=\"method\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var21 string
|
||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(method)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 137, Col: 53}
|
||||
}
|
||||
_, 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, 48, "\"> <input type=\"hidden\" name=\"user\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var22 string
|
||||
templ_7745c5c3_Var22, 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: 138, Col: 83}
|
||||
}
|
||||
_, 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, 49, "\"> <input type=\"hidden\" name=\"week\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var23 string
|
||||
templ_7745c5c3_Var23, 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: 139, Col: 81}
|
||||
}
|
||||
_, 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, 50, "\"> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if onlyAccept {
|
||||
if week.Status == models.WeekStatusDifferences {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<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, 52, " <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, 53, " disabled")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, " class=\"btn\">Bestätigen</button>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
switch {
|
||||
case week.RequiresAction():
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "<p class=\"text-sm text-red-500\">bitte zuerst Buchungen anpassen</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
case time.Since(week.WeekStart) < 24*7*time.Hour:
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "<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
|
||||
}
|
||||
case week.Status == models.WeekStatusNone:
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "<p class=\"text-sm\">an Vorgesetzten senden</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
case week.Status == models.WeekStatusSent:
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "<p class=\"text-sm\">an Vorgesetzten gesendet</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
case week.Status == models.WeekStatusAccepted:
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<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, 60, " <button")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if week.Status < models.WeekStatusSent {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, " disabled")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, " type=\"submit\" class=\"btn\">Korrigieren</button> <button")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if time.Since(week.WeekStart) < 24*7*time.Hour || week.Status >= models.WeekStatusSent || week.RequiresAction() {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, " disabled")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, " type=\"submit\" class=\"btn\">Senden</button>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "</form></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func userPresenceComponent(user models.User, present 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 {
|
||||
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_Var24 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var24 == nil {
|
||||
templ_7745c5c3_Var24 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "<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, 67, "<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, 68, "<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, 69, "<p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var25 string
|
||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(user.Vorname)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 173, Col: 19}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var26 string
|
||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(user.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/teamComponents.templ`, Line: 173, Col: 33}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "</p></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@@ -2,63 +2,20 @@ package templates
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/models"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
templ lineComponent() {
|
||||
<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 changeButtonComponent(id string, workDay bool) {
|
||||
<button class="change-button-component btn w-auto group/button" type="button" onclick={ templ.JSFuncCall("editWorkday", templ.JSExpression("this"), templ.JSExpression("event"), id, workDay) }>
|
||||
<button class="h-10 change-button-component btn w-auto group/button" type="button" onclick={ templ.JSFuncCall("editWorkday", templ.JSExpression("this"), templ.JSExpression("event"), id, workDay) }>
|
||||
<p class="hidden md:block group-[.edit]/button:hidden">Ändern</p>
|
||||
<p class="hidden group-[.edit]/button:md:block">Absenden</p>
|
||||
<p class="hidden group-[.edit]/button:md:block">Speichern</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]/button: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]/button: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>
|
||||
<button class="hidden group-[.edit]:flex btn basis-[content] items-center" onclick={ templ.JSFuncCall("clearEditState") }><span class="size-5 icon-[material-symbols-light--cancel-outline]"></span></button>
|
||||
}
|
||||
|
||||
templ timeGaugeComponent(progress int8, today bool) {
|
||||
{{
|
||||
var bgColor string
|
||||
switch {
|
||||
case (0 > progress):
|
||||
bgColor = "bg-red-600"
|
||||
break
|
||||
case (progress > 0 && progress < 95):
|
||||
bgColor = "bg-orange-500"
|
||||
break
|
||||
case (95 <= progress && progress <= 105):
|
||||
bgColor = "bg-accent"
|
||||
break
|
||||
case (progress > 105):
|
||||
bgColor = "bg-purple-600"
|
||||
break
|
||||
default:
|
||||
bgColor = "bg-neutral-400"
|
||||
break
|
||||
}
|
||||
}}
|
||||
if today {
|
||||
<div class="flex-start flex w-2 h-full overflow-hidden rounded-full bg-neutral-300 print:hidden">
|
||||
<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 flex-shrink-0", bgColor }></div>
|
||||
}
|
||||
<button class="h-10 hidden group-[.edit]:flex btn basis-[content] items-center" onclick={ templ.JSFuncCall("clearEditState") }><span class="size-5 icon-[material-symbols-light--cancel-outline]"></span></button>
|
||||
}
|
||||
|
||||
templ newAbsenceComponent() {
|
||||
@@ -77,12 +34,9 @@ templ absenceComponent(a *models.Absence, isKurzarbeit bool) {
|
||||
}
|
||||
}}
|
||||
<div class={ "flex flex-row items-center gap-2", editBox }>
|
||||
<input type="hidden" name="date_from" value={ a.DateFrom.Format(time.DateOnly) }/>
|
||||
<input type="hidden" name="date_to" value={ a.DateTo.Format(time.DateOnly) }/>
|
||||
<input type="hidden" name="aw_type" value={ a.AbwesenheitTyp.Id }/>
|
||||
<input type="hidden" name="aw_id" value={ a.CounterId }/>
|
||||
@absentInput(a)
|
||||
<p class="whitespace-nowrap group-[.edit]:ml-2">
|
||||
{ a.AbwesenheitTyp.Name }
|
||||
{ a.ToString() }
|
||||
if a.IsMultiDay() {
|
||||
<span class="text-neutral-500">bis { a.DateTo.Format("02.01.2006") }</span>
|
||||
}
|
||||
@@ -94,15 +48,23 @@ templ absenceComponent(a *models.Absence, isKurzarbeit bool) {
|
||||
</div>
|
||||
}
|
||||
|
||||
templ newBookingComponent(d *models.WorkDay) {
|
||||
<div class="new-booking-component hidden group-[.edit]:flex flex-row gap-2 items-center edit-box border-dashed">
|
||||
templ absentInput(a *models.Absence) {
|
||||
<input type="hidden" name="date_from" value={ a.DateFrom.Format(time.DateOnly) }/>
|
||||
<input type="hidden" name="date_to" value={ a.DateTo.Format(time.DateOnly) }/>
|
||||
<input type="hidden" name="aw_type" value={ a.AbwesenheitTyp.Id }/>
|
||||
<input type="hidden" name="aw_id" value={ a.CounterId }/>
|
||||
}
|
||||
|
||||
//js function to select the right entry
|
||||
templ newBookingComponent(d models.IWorkDay) {
|
||||
<div class="new-booking-component hidden group-[.edit]:flex flex-row gap-2 items-center edit-box border-dashed" id={ "nb" + d.Date().Format(time.DateOnly) }>
|
||||
<input name="timestamp" type="time" value={ time.Now().Format("15:04") } class="text-neutral-700 group-[.edit]:inline hidden bg-neutral-100 text-sm px-3 py-2 cursor-pointer"/>
|
||||
<input name="date" type="hidden" value={ d.Day.Format(time.DateOnly) }/>
|
||||
<input name="date" type="hidden" value={ d.Date().Format(time.DateOnly) }/>
|
||||
<div class="relative">
|
||||
<select class="cursor-pointer appearance-none" name="check_in_out">
|
||||
<option value="0" disabled>Kommen/Gehen</option>
|
||||
<option value="3" selected?={ len(d.Bookings) > 0 && d.Bookings[len(d.Bookings)-1].CheckInOut%2 == 0 }>Kommen</option>
|
||||
<option value="4" selected?={ len(d.Bookings) > 0 && d.Bookings[len(d.Bookings)-1].CheckInOut%2 == 1 }>Gehen</option>
|
||||
<option value="3">Kommen</option>
|
||||
<option value="4">Gehen</option>
|
||||
</select>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.2" stroke="currentColor" class="h-5 w-5 ml-1 absolute right-1 top-[0.125rem] text-slate-700">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 15 12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9"></path>
|
||||
@@ -126,12 +88,20 @@ templ bookingComponent(booking models.Booking) {
|
||||
</div>
|
||||
}
|
||||
|
||||
templ LegendComponent() {
|
||||
<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 workdayComponent(workDay *models.WorkDay) {
|
||||
if len(workDay.Bookings) < 1 {
|
||||
<p class="text group-[.edit]:hidden">Keine Buchung gefunden. Bitte Arbeitsstunden oder Grund der Abwesenheit eingeben!</p>
|
||||
} else {
|
||||
if workDay.IsKurzArbeit() && len(workDay.Bookings) > 0 {
|
||||
@absenceComponent(workDay.GetKurzArbeit(), true)
|
||||
}
|
||||
for _, booking := range workDay.Bookings {
|
||||
@bookingComponent(booking)
|
||||
}
|
||||
<input type="hidden" name="select_kommen" value={ len(workDay.Bookings) > 0 && workDay.Bookings[len(workDay.Bookings)-1].CheckInOut%2 == 0 }/>
|
||||
}
|
||||
}
|
||||
|
||||
templ holidayComponent(d models.IWorkDay) {
|
||||
<p>{ d.ToString() }</p>
|
||||
}
|
||||
|
||||
@@ -1,618 +0,0 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.943
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/models"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func lineComponent() 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_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<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
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func changeButtonComponent(id string, workDay 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 {
|
||||
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_Var2 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var2 == nil {
|
||||
templ_7745c5c3_Var2 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.JSFuncCall("editWorkday", templ.JSExpression("this"), templ.JSExpression("event"), id, workDay))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<button class=\"change-button-component btn w-auto group/button\" type=\"button\" onclick=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 templ.ComponentScript = templ.JSFuncCall("editWorkday", templ.JSExpression("this"), templ.JSExpression("event"), id, workDay)
|
||||
_, 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, 3, "\"><p class=\"hidden md:block group-[.edit]/button:hidden\">Ändern</p><p class=\"hidden group-[.edit]/button: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]/button: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]/button: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
|
||||
}
|
||||
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.JSFuncCall("clearEditState"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<button class=\"hidden group-[.edit]:flex btn basis-[content] items-center\" onclick=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 templ.ComponentScript = templ.JSFuncCall("clearEditState")
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4.Call)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\"><span class=\"size-5 icon-[material-symbols-light--cancel-outline]\"></span></button>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func timeGaugeComponent(progress int8, today 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 {
|
||||
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_Var5 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var5 == nil {
|
||||
templ_7745c5c3_Var5 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
|
||||
var bgColor string
|
||||
switch {
|
||||
case (0 > progress):
|
||||
bgColor = "bg-red-600"
|
||||
break
|
||||
case (progress > 0 && progress < 95):
|
||||
bgColor = "bg-orange-500"
|
||||
break
|
||||
case (95 <= progress && progress <= 105):
|
||||
bgColor = "bg-accent"
|
||||
break
|
||||
case (progress > 105):
|
||||
bgColor = "bg-purple-600"
|
||||
break
|
||||
default:
|
||||
bgColor = "bg-neutral-400"
|
||||
break
|
||||
}
|
||||
if today {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<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_Var6 = []any{"flex w-full items-center justify-center overflow-hidden rounded-full", bgColor}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var6...)
|
||||
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_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var6).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_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\" style=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, 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: 57, Col: 149}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\"></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
var templ_7745c5c3_Var9 = []any{"w-2 h-full bg-accent rounded-md flex-shrink-0", bgColor}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var9...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var10 string
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var9).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_Var10))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\"></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func newAbsenceComponent() 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_Var11 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var11 == nil {
|
||||
templ_7745c5c3_Var11 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"no-booking-component hidden group-[.edit]:flex flex-col gap-2 align-center \">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.JSFuncCall("editWorkday", templ.JSExpression("this"), templ.JSExpression("event"), 0, false))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<button type=\"button\" name=\"absence\" onclick=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var12 templ.ComponentScript = templ.JSFuncCall("editWorkday", templ.JSExpression("this"), templ.JSExpression("event"), 0, false)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var12.Call)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\" class=\"btn border-neutral-500\">Neue Abwesenheit</button></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func absenceComponent(a *models.Absence, isKurzarbeit 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 {
|
||||
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_Var13 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var13 == nil {
|
||||
templ_7745c5c3_Var13 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
|
||||
editBox := ""
|
||||
if isKurzarbeit {
|
||||
editBox = "edit-box"
|
||||
}
|
||||
var templ_7745c5c3_Var14 = []any{"flex flex-row items-center gap-2", editBox}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var14...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var15 string
|
||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var14).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_Var15))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\"><input type=\"hidden\" name=\"date_from\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var16 string
|
||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(a.DateFrom.Format(time.DateOnly))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 80, Col: 80}
|
||||
}
|
||||
_, 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, 17, "\"> <input type=\"hidden\" name=\"date_to\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var17 string
|
||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(a.DateTo.Format(time.DateOnly))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 81, Col: 76}
|
||||
}
|
||||
_, 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, 18, "\"> <input type=\"hidden\" name=\"aw_type\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var18 string
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(a.AbwesenheitTyp.Id)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 82, Col: 65}
|
||||
}
|
||||
_, 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, 19, "\"> <input type=\"hidden\" name=\"aw_id\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var19 string
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(a.CounterId)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 83, Col: 55}
|
||||
}
|
||||
_, 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, 20, "\"><p class=\"whitespace-nowrap group-[.edit]:ml-2\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var20 string
|
||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(a.AbwesenheitTyp.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 85, Col: 26}
|
||||
}
|
||||
_, 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, 21, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if a.IsMultiDay() {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<span class=\"text-neutral-500\">bis ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var21 string
|
||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(a.DateTo.Format("02.01.2006"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 87, Col: 70}
|
||||
}
|
||||
_, 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, 23, "</span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</p><div class=\"w-full\"></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if isKurzarbeit {
|
||||
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.JSFuncCall("editWorkday", templ.JSExpression("this"), templ.JSExpression("event"), "time-"+a.Date().Format(time.DateOnly), false))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<button type=\"button\" onclick=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var22 templ.ComponentScript = templ.JSFuncCall("editWorkday", templ.JSExpression("this"), templ.JSExpression("event"), "time-"+a.Date().Format(time.DateOnly), false)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var22.Call)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "\" class=\"hidden btn border-0 rounded-none grow-0 w-auto group-[.edit]:inline\">Bearbeiten</button>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func newBookingComponent(d *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_Var23 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var23 == nil {
|
||||
templ_7745c5c3_Var23 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<div class=\"new-booking-component hidden group-[.edit]:flex flex-row gap-2 items-center edit-box border-dashed\"><input name=\"timestamp\" type=\"time\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var24 string
|
||||
templ_7745c5c3_Var24, 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: 99, Col: 72}
|
||||
}
|
||||
_, 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, 29, "\" class=\"text-neutral-700 group-[.edit]:inline hidden bg-neutral-100 text-sm px-3 py-2 cursor-pointer\"> <input name=\"date\" type=\"hidden\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var25 string
|
||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(d.Day.Format(time.DateOnly))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 100, Col: 70}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\"><div class=\"relative\"><select class=\"cursor-pointer appearance-none\" 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, 31, " selected")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, ">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, 33, " selected")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, ">Gehen</option></select> <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.2\" stroke=\"currentColor\" class=\"h-5 w-5 ml-1 absolute right-1 top-[0.125rem] text-slate-700\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M8.25 15 12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9\"></path></svg></div><div class=\"w-full\"></div><button name=\"action\" value=\"add\" type=\"submit\" class=\"hidden btn border-0 rounded-none grow-0 w-auto group-[.edit]:inline\"><span class=\"hidden md:inline\">Hinzufügen</span><span class=\"md:hidden\">+</span></button></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func bookingComponent(booking models.Booking) 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_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, 35, "<div><p class=\"text-neutral-500 edit-box\"><span class=\"text-black group-[.edit]:hidden inline\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var27 string
|
||||
templ_7745c5c3_Var27, 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: 119, Col: 91}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</span> <input disabled name=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var28 string
|
||||
templ_7745c5c3_Var28, 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: 120, Col: 70}
|
||||
}
|
||||
_, 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, 37, "\" type=\"time\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var29 string
|
||||
templ_7745c5c3_Var29, 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: 120, Col: 126}
|
||||
}
|
||||
_, 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, 38, "\" class=\"text-neutral-700 group-[.edit]:inline hidden bg-neutral-100 text-sm px-3 py-2 cursor-pointer\"> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var30 string
|
||||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(booking.GetBookingType())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 121, Col: 29}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if booking.IsSubmittedAndChecked() {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<p>submitted</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func LegendComponent() 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_Var31 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var31 == nil {
|
||||
templ_7745c5c3_Var31 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<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
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@@ -1,5 +1,9 @@
|
||||
package templates
|
||||
|
||||
// this files includes the largest templates from the time page,
|
||||
// because this page is so complex the smaller components are in
|
||||
// the timeComponents.templ file
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
@@ -9,10 +13,8 @@ import (
|
||||
)
|
||||
|
||||
templ TimePage(workDays []models.WorkDay, lastSub time.Time) {
|
||||
{{
|
||||
allDays := ctx.Value("days").([]models.IWorkDay)
|
||||
}}
|
||||
@Base()
|
||||
{{ allDays := ctx.Value("days").([]models.IWorkDay) }}
|
||||
@BasePage()
|
||||
@headerComponent()
|
||||
<div class="grid-main divide-y-1">
|
||||
@inputForm()
|
||||
@@ -23,7 +25,7 @@ templ TimePage(workDays []models.WorkDay, lastSub time.Time) {
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@LegendComponent()
|
||||
@legendComponent()
|
||||
}
|
||||
|
||||
templ inputForm() {
|
||||
@@ -31,6 +33,7 @@ templ inputForm() {
|
||||
urlParams := ctx.Value("urlParams").(url.Values)
|
||||
user := ctx.Value("user").(models.User)
|
||||
}}
|
||||
<div class="sticky top-0 z-100 grid-sub divide-y-1">
|
||||
<div class="grid-sub divide-x-1 bg-neutral-300 responsive">
|
||||
<div class="grid-cell md:col-span-1 max-md:grid grid-cols-2">
|
||||
<p class="font-bold uppercase">{ user.Vorname + " " + user.Name }</p>
|
||||
@@ -83,13 +86,14 @@ templ inputForm() {
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ defaultDayComponent(day models.IWorkDay) {
|
||||
{{
|
||||
user := ctx.Value("user").(models.User)
|
||||
justify := "justify-center"
|
||||
if day.IsWorkDay() && len(day.(*models.WorkDay).Bookings) > 1 {
|
||||
if day.IsWorkDay() && !day.IsEmpty() {
|
||||
justify = "justify-between"
|
||||
}
|
||||
}}
|
||||
@@ -102,9 +106,8 @@ templ defaultDayComponent(day models.IWorkDay) {
|
||||
</p>
|
||||
if day.IsWorkDay() {
|
||||
{{
|
||||
workDay, _ := day.(*models.WorkDay)
|
||||
work, pause, overtime := workDay.GetTimes(user, models.WorktimeBaseDay, true)
|
||||
work = workDay.GetWorktime(user, models.WorktimeBaseDay, false)
|
||||
work, pause, overtime := day.GetTimes(user, models.WorktimeBaseDay, true)
|
||||
work = day.GetWorktime(user, models.WorktimeBaseDay, false)
|
||||
}}
|
||||
if day.RequiresAction() {
|
||||
<p class="text-red-600">Bitte anpassen</p>
|
||||
@@ -116,7 +119,7 @@ templ defaultDayComponent(day models.IWorkDay) {
|
||||
if pause > 0 {
|
||||
<p class="text-neutral-500 flex flex-row items-center"><span class="icon-[material-symbols-light--motion-photos-paused-outline]"></span>{ helper.FormatDuration(pause) }</p>
|
||||
}
|
||||
if overtime != 0 && len(workDay.Bookings) > 0 {
|
||||
if !day.IsEmpty() && overtime != 0 {
|
||||
<p class="text-neutral-500 flex flex-row items-center">
|
||||
<span class="icon-[material-symbols-light--more-time]"></span>
|
||||
{ helper.FormatDuration(overtime) }
|
||||
@@ -128,40 +131,36 @@ templ defaultDayComponent(day models.IWorkDay) {
|
||||
</div>
|
||||
<div class="all-booking-component grid-cell flex flex-row md:col-span-3 col-span-2 gap-2 w-full">
|
||||
@lineComponent()
|
||||
<form id={ "time-" + day.Date().Format(time.DateOnly) } class={ "flex flex-col gap-2 w-full", justify } method="post">
|
||||
if day.IsWorkDay() {
|
||||
{{
|
||||
workDay, _ := day.(*models.WorkDay)
|
||||
}}
|
||||
<form id={ "time-" + day.Date().Format(time.DateOnly) } class={ "bookings flex flex-col gap-2 w-full", justify } method="post">
|
||||
if (day.GetDayProgress(user) < 100 || day.IsWorkDay()) {
|
||||
@newAbsenceComponent()
|
||||
if len(workDay.Bookings) < 1 {
|
||||
<p class="text group-[.edit]:hidden">Keine Buchung gefunden. Bitte Arbeitsstunden oder Grund der Abwesenheit eingeben!</p>
|
||||
}
|
||||
if workDay.IsKurzArbeit() && len(workDay.Bookings) > 0 {
|
||||
@absenceComponent(workDay.GetKurzArbeit(), true)
|
||||
}
|
||||
for _, booking := range workDay.Bookings {
|
||||
@bookingComponent(booking)
|
||||
}
|
||||
@newBookingComponent(workDay)
|
||||
@timeDayTypeSwitch(day, true)
|
||||
@newBookingComponent(day)
|
||||
} else {
|
||||
{{
|
||||
absentDay, _ := day.(*models.Absence)
|
||||
}}
|
||||
@absenceComponent(absentDay, false)
|
||||
@timeDayTypeSwitch(day, true)
|
||||
}
|
||||
<input type="hidden" name="action" value="change"/> <!-- default action value for ändern button -->
|
||||
</form>
|
||||
</div>
|
||||
<div class="grid-cell flex flex-row gap-2 items-end">
|
||||
@changeButtonComponent("time-"+day.Date().Format(time.DateOnly), day.IsWorkDay())
|
||||
@changeButtonComponent("time-"+day.Date().Format(time.DateOnly), true)
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ absentInput(a models.Absence) {
|
||||
<input type="hidden" name="date_from" value={ a.DateFrom.Format(time.DateOnly) }/>
|
||||
<input type="hidden" name="date_to" value={ a.DateTo.Format(time.DateOnly) }/>
|
||||
<input type="hidden" name="aw_type" value={ a.AbwesenheitTyp.Id }/>
|
||||
<input type="hidden" name="aw_id" value={ a.CounterId }/>
|
||||
templ timeDayTypeSwitch(day models.IWorkDay, fromCompound bool) {
|
||||
switch day.Type() {
|
||||
case models.DayTypeWorkday:
|
||||
{{ workDay, _ := day.(*models.WorkDay) }}
|
||||
@workdayComponent(workDay)
|
||||
case models.DayTypeAbsence:
|
||||
{{ absentDay, _ := day.(*models.Absence) }}
|
||||
@absenceComponent(absentDay, fromCompound)
|
||||
case models.DayTypeCompound:
|
||||
for _, c := range day.(*models.CompoundDay).DayParts {
|
||||
@timeDayTypeSwitch(c, true)
|
||||
}
|
||||
default:
|
||||
<p>{ day.ToString() }</p>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,564 +0,0 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.943
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
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 {
|
||||
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_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
|
||||
allDays := ctx.Value("days").([]models.IWorkDay)
|
||||
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, 1, "<div class=\"grid-main divide-y-1\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = inputForm().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, day := range allDays {
|
||||
templ_7745c5c3_Err = defaultDayComponent(day).Render(ctx, templ_7745c5c3_Buffer)
|
||||
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
|
||||
}
|
||||
if day.Date().Weekday() == time.Monday {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<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, 4, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = LegendComponent().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func inputForm() 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_Var2 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var2 == nil {
|
||||
templ_7745c5c3_Var2 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
|
||||
urlParams := ctx.Value("urlParams").(url.Values)
|
||||
user := ctx.Value("user").(models.User)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div class=\"grid-sub divide-x-1 bg-neutral-300 responsive\"><div class=\"grid-cell md:col-span-1 max-md:grid grid-cols-2\"><p class=\"font-bold uppercase\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(user.Vorname + " " + user.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 36, Col: 66}
|
||||
}
|
||||
_, 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, 6, "</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_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(user.Overtime)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 39, Col: 42}
|
||||
}
|
||||
_, 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, 7, "</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
|
||||
}
|
||||
templ_7745c5c3_Err = lineComponent().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<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_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(urlParams.Get("time_from"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 45, Col: 57}
|
||||
}
|
||||
_, 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, "\" name=\"time_from\" class=\"btn bg-neutral-100\" placeholder=\"Zeitraum von...\"> <input type=\"date\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(urlParams.Get("time_to"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 46, Col: 55}
|
||||
}
|
||||
_, 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, 10, "\" name=\"time_to\" class=\"btn bg-neutral-100\" 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><form id=\"absence_form\" method=\"POST\" action=\"/absence\" class=\"grid-sub responsive scroll-m-2 bg-neutral-300 hidden\"><input type=\"hidden\" name=\"aw_id\" value=\"\"><div class=\"grid-cell border-r-1\"><p class=\"font-bold uppercase\">Abwesenheit</p></div><div class=\"grid-cell\"><label class=\"block mb-1 text-sm text-neutral-700\">Abwesenheitsart</label><div class=\"relative\"><select name=\"aw_type\" class=\"btn appearance-none cursor-pointer bg-neutral-100\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, absence := range models.GetAbsenceTypesCached() {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<option value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(int(absence.Id)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 63, Col: 51}
|
||||
}
|
||||
_, 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, 12, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(absence.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 63, Col: 68}
|
||||
}
|
||||
_, 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, "</option>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</select> <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.2\" stroke=\"currentColor\" class=\"h-5 w-5 ml-1 absolute top-2.5 right-2.5 text-slate-700\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M8.25 15 12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9\"></path></svg></div></div><div class=\"grid-cell\"><label class=\"block mb-1 text-sm text-neutral-700\">Abwesenheit ab</label> <input name=\"date_from\" type=\"date\" class=\"btn bg-neutral-100\"></div><div class=\"grid-cell border-r-1\"><label class=\"block mb-1 text-sm text-neutral-700\">Abwesenheit bis</label> <input name=\"date_to\" type=\"date\" class=\"btn bg-neutral-100\"></div><div class=\"grid-cell flex flex-row items-end\"><div class=\"flex flex-row gap-2 w-full\"><button name=\"action\" value=\"insert\" type=\"submit\" class=\"bg-neutral-100 btn hover:bg-neutral-700\">Speichern</button> <button name=\"action\" value=\"delete\" type=\"submit\" class=\"bg-neutral-100 btn hover:bg-red-700 flex basis-[content] items-center\"><span class=\"size-5 icon-[material-symbols-light--delete-outline]\"></span></button></div></div></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func defaultDayComponent(day models.IWorkDay) 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_Var9 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var9 == nil {
|
||||
templ_7745c5c3_Var9 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
|
||||
user := ctx.Value("user").(models.User)
|
||||
justify := "justify-center"
|
||||
if day.IsWorkDay() && len(day.(*models.WorkDay).Bookings) > 1 {
|
||||
justify = "justify-between"
|
||||
}
|
||||
var templ_7745c5c3_Var10 = []any{"grid-sub divide-x-1 hover:bg-neutral-200 transition-colors group"}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var10...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var10).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, 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, "\"><div class=\"grid-cell md:col-span-1 flex flex-row gap-2\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = timeGaugeComponent(day.GetDayProgress(user), day.Date().Equal(time.Now().Truncate(24*time.Hour))).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<div><p><span class=\"font-bold uppercase hidden md:inline\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var12 string
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(helper.FormatGermanDayOfWeek(day.Date()))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 101, Col: 98}
|
||||
}
|
||||
_, 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, 18, ":</span> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var13 string
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(day.Date().Format("02.01.2006"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 101, Col: 142}
|
||||
}
|
||||
_, 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
|
||||
}
|
||||
if day.IsWorkDay() {
|
||||
|
||||
workDay, _ := day.(*models.WorkDay)
|
||||
work, pause, overtime := workDay.GetTimes(user, models.WorktimeBaseDay, true)
|
||||
work = workDay.GetWorktime(user, models.WorktimeBaseDay, false)
|
||||
if day.RequiresAction() {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<p class=\"text-red-600\">Bitte anpassen</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
if work > 0 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<p class=\" text-sm mt-1\">Arbeitszeit:</p><p class=\"text-accent flex flex-row items-center\"><span class=\"icon-[material-symbols-light--schedule-outline]\"></span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var14 string
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(helper.FormatDuration(work))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 114, Col: 155}
|
||||
}
|
||||
_, 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, 22, "</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if pause > 0 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<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_Var15 string
|
||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(helper.FormatDuration(pause))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 117, Col: 173}
|
||||
}
|
||||
_, 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, 25, "</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if overtime != 0 && len(workDay.Bookings) > 0 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<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_Var16 string
|
||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(helper.FormatDuration(overtime))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 122, Col: 41}
|
||||
}
|
||||
_, 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, 28, "</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</div></div><div class=\"all-booking-component grid-cell flex flex-row md:col-span-3 col-span-2 gap-2 w-full\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = lineComponent().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var17 = []any{"flex flex-col gap-2 w-full", justify}
|
||||
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, 30, "<form id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var18 string
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs("time-" + day.Date().Format(time.DateOnly))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 131, Col: 56}
|
||||
}
|
||||
_, 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, 31, "\" class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var19 string
|
||||
templ_7745c5c3_Var19, 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/timePage.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, 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, 32, "\" method=\"post\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if day.IsWorkDay() {
|
||||
|
||||
workDay, _ := day.(*models.WorkDay)
|
||||
templ_7745c5c3_Err = newAbsenceComponent().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if len(workDay.Bookings) < 1 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<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
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if workDay.IsKurzArbeit() && len(workDay.Bookings) > 0 {
|
||||
templ_7745c5c3_Err = absenceComponent(workDay.GetKurzArbeit(), true).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
for _, booking := range workDay.Bookings {
|
||||
templ_7745c5c3_Err = bookingComponent(booking).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = newBookingComponent(workDay).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
|
||||
absentDay, _ := day.(*models.Absence)
|
||||
templ_7745c5c3_Err = absenceComponent(absentDay, false).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<input type=\"hidden\" name=\"action\" value=\"change\"><!-- default action value for ändern button --></form></div><div class=\"grid-cell flex flex-row gap-2 items-end\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = changeButtonComponent("time-"+day.Date().Format(time.DateOnly), day.IsWorkDay()).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func absentInput(a models.Absence) 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_Var20 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var20 == nil {
|
||||
templ_7745c5c3_Var20 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<input type=\"hidden\" name=\"date_from\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var21 string
|
||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(a.DateFrom.Format(time.DateOnly))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 163, Col: 79}
|
||||
}
|
||||
_, 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, 40, "\"> <input type=\"hidden\" name=\"date_to\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var22 string
|
||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(a.DateTo.Format(time.DateOnly))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 164, Col: 75}
|
||||
}
|
||||
_, 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, 41, "\"> <input type=\"hidden\" name=\"aw_type\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var23 string
|
||||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(a.AbwesenheitTyp.Id)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 165, Col: 64}
|
||||
}
|
||||
_, 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, 42, "\"> <input type=\"hidden\" name=\"aw_id\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var24 string
|
||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(a.CounterId)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 166, Col: 54}
|
||||
}
|
||||
_, 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, 43, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
6
Cron/autoBackup.sh
Executable file
6
Cron/autoBackup.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
# cron-timing: 05 01 * * 1
|
||||
container_name="arbeitszeitmessung-main-db-1"
|
||||
filename=backup-$(date '+%d%m%Y').sql
|
||||
database_name=__DATABASE__
|
||||
docker exec $container_name pg_dump $database_name > /home/pi/arbeitszeitmessung-backup/$filename
|
||||
echo "created backup file: "$filename
|
||||
3
Cron/autoHolidays.sh
Executable file
3
Cron/autoHolidays.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
# Calls endpoint to write all public Holidays for the current year inside a database.
|
||||
port=__PORT__
|
||||
curl localhost:$port/auto/feiertage
|
||||
4
Cron/autoLogout.sh
Executable file
4
Cron/autoLogout.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
# cron-timing: 55 23 * * *
|
||||
# Calls endpoint to log out all users, still logged in for today
|
||||
port=__PORT__
|
||||
curl localhost:$port/auto/logout
|
||||
56
DB/initdb/01_create_user.sh
Executable file
56
DB/initdb/01_create_user.sh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
set -e # Exit on error
|
||||
|
||||
echo "Creating PostgreSQL user and setting permissions... $POSTGRES_USER for API user $POSTGRES_API_USER"
|
||||
|
||||
|
||||
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
CREATE ROLE migrate LOGIN ENCRYPTED PASSWORD '$POSTGRES_PASSWORD';
|
||||
GRANT USAGE, CREATE ON SCHEMA public TO migrate;
|
||||
GRANT CONNECT ON DATABASE arbeitszeitmessung TO migrate;
|
||||
EOSQL
|
||||
|
||||
# psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
|
||||
# GRANT SELECT, INSERT, UPDATE ON anwesenheit, abwesenheit, user_password, wochen_report, s_feiertage TO $POSTGRES_API_USER;
|
||||
# GRANT DELETE ON abwesenheit TO $POSTGRES_API_USER;
|
||||
# GRANT SELECT ON s_personal_daten, s_abwesenheit_typen, s_anwesenheit_typen, s_feiertage TO $POSTGRES_API_USER;
|
||||
# GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO $POSTGRES_API_USER;
|
||||
# EOSQL
|
||||
|
||||
echo "User creation and permissions setup complete!"
|
||||
|
||||
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
|
||||
-- privilege roles
|
||||
DO \$\$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'app_base') THEN
|
||||
CREATE ROLE app_base NOLOGIN;
|
||||
END IF;
|
||||
END
|
||||
\$\$;
|
||||
|
||||
-- dynamic login role
|
||||
DO \$\$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '$POSTGRES_API_USER') THEN
|
||||
CREATE ROLE $POSTGRES_API_USER
|
||||
LOGIN
|
||||
ENCRYPTED PASSWORD '$POSTGRES_API_PASS';
|
||||
END IF;
|
||||
END
|
||||
\$\$;
|
||||
|
||||
-- grant base privileges
|
||||
GRANT app_base TO $POSTGRES_API_USER;
|
||||
GRANT CONNECT ON DATABASE $POSTGRES_DB TO $POSTGRES_API_USER;
|
||||
GRANT USAGE ON SCHEMA public TO $POSTGRES_API_USER;
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
EOSQL
|
||||
|
||||
# psql -v ON_ERROR_STOP=1 --username root --dbname arbeitszeitmessung
|
||||
@@ -1,8 +0,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')));
|
||||
|
||||
INSERT INTO "s_anwesenheit_typen" ("anwesenheit_id", "anwesenheit_name") VALUES (1, 'Büro');
|
||||
INSERT INTO "s_abwesenheit_typen" ("abwesenheit_id", "abwesenheit_name", "arbeitszeit_equivalent") VALUES (1, 'Urlaub', 10), (2, 'Krank', 10), (3, 'Kurzarbeit', 2);
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e # Exit on error
|
||||
|
||||
echo "Creating PostgreSQL user and setting permissions... $POSTGRES_USER for API user $POSTGRES_API_USER"
|
||||
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
CREATE USER $POSTGRES_API_USER WITH ENCRYPTED PASSWORD '$POSTGRES_API_PASS';
|
||||
GRANT CONNECT ON DATABASE $POSTGRES_DB TO $POSTGRES_API_USER;
|
||||
GRANT USAGE ON SCHEMA public TO $POSTGRES_API_USER;
|
||||
GRANT SELECT, INSERT, UPDATE ON anwesenheit, abwesenheit, user_password, wochen_report TO $POSTGRES_API_USER;
|
||||
GRANT SELECT ON s_personal_daten, s_abwesenheit_typen, s_anwesenheit_typen TO $POSTGRES_API_USER;
|
||||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO $POSTGRES_API_USER;
|
||||
EOSQL
|
||||
|
||||
echo "User creation and permissions setup complete!"
|
||||
|
||||
# psql -v ON_ERROR_STOP=1 --username root --dbname arbeitszeitmessung
|
||||
56
DBB/initdb/01_create_user.sh
Executable file
56
DBB/initdb/01_create_user.sh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
set -e # Exit on error
|
||||
|
||||
echo "Creating PostgreSQL user and setting permissions... $POSTGRES_USER for API user $POSTGRES_API_USER"
|
||||
|
||||
|
||||
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
CREATE ROLE migrate LOGIN ENCRYPTED PASSWORD '$POSTGRES_PASSWORD';
|
||||
GRANT USAGE, CREATE ON SCHEMA public TO migrate;
|
||||
GRANT CONNECT ON DATABASE arbeitszeitmessung TO migrate;
|
||||
EOSQL
|
||||
|
||||
# psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
|
||||
# GRANT SELECT, INSERT, UPDATE ON anwesenheit, abwesenheit, user_password, wochen_report, s_feiertage TO $POSTGRES_API_USER;
|
||||
# GRANT DELETE ON abwesenheit TO $POSTGRES_API_USER;
|
||||
# GRANT SELECT ON s_personal_daten, s_abwesenheit_typen, s_anwesenheit_typen, s_feiertage TO $POSTGRES_API_USER;
|
||||
# GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO $POSTGRES_API_USER;
|
||||
# EOSQL
|
||||
|
||||
echo "User creation and permissions setup complete!"
|
||||
|
||||
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
|
||||
-- privilege roles
|
||||
DO \$\$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'app_base') THEN
|
||||
CREATE ROLE app_base NOLOGIN;
|
||||
END IF;
|
||||
END
|
||||
\$\$;
|
||||
|
||||
-- dynamic login role
|
||||
DO \$\$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '$POSTGRES_API_USER') THEN
|
||||
CREATE ROLE $POSTGRES_API_USER
|
||||
LOGIN
|
||||
ENCRYPTED PASSWORD '$POSTGRES_API_PASS';
|
||||
END IF;
|
||||
END
|
||||
\$\$;
|
||||
|
||||
-- grant base privileges
|
||||
GRANT app_base TO $POSTGRES_API_USER;
|
||||
GRANT CONNECT ON DATABASE $POSTGRES_DB TO $POSTGRES_API_USER;
|
||||
GRANT USAGE ON SCHEMA public TO $POSTGRES_API_USER;
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
EOSQL
|
||||
|
||||
# psql -v ON_ERROR_STOP=1 --username root --dbname arbeitszeitmessung
|
||||
@@ -3,7 +3,7 @@ services:
|
||||
db:
|
||||
volumes:
|
||||
- ${POSTGRES_PATH}:/var/lib/postgresql/data
|
||||
# - ${POSTGRES_PATH}/initdb:/docker-entrypoint-initdb.d
|
||||
- ${POSTGRES_PATH}/initdb:/docker-entrypoint-initdb.d
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
|
||||
@@ -13,9 +13,14 @@ services:
|
||||
- ${POSTGRES_PATH}/initdb:/docker-entrypoint-initdb.d
|
||||
ports:
|
||||
- ${POSTGRES_PORT}:5432
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready", "-U", "${POSTGRES_USER}", "--dbname", "${POSTGRES_DB}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
backend:
|
||||
image: git.letsstein.de/tom/arbeitszeitmessung-webserver
|
||||
image: git.letsstein.de/tom/arbeitszeitmessung-webserver:dev
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
@@ -24,13 +29,8 @@ services:
|
||||
ports:
|
||||
- ${WEB_PORT}:8080
|
||||
depends_on:
|
||||
- db
|
||||
- document-creator
|
||||
db:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ../logs:/app/Backend/logs
|
||||
restart: unless-stopped
|
||||
|
||||
document-creator:
|
||||
image: git.letsstein.de/tom/arbeitszeitmessung-doc-creator
|
||||
container_name: ${TYPST_CONTAINER}
|
||||
- ${LOG_PATH}:/app/logs
|
||||
restart: unless-stopped
|
||||
|
||||
@@ -2,11 +2,11 @@ POSTGRES_USER=root # Postgres ADMIN Nutzername
|
||||
POSTGRES_PASSWORD=very_secure # Postgres ADMIN Passwort
|
||||
POSTGRES_API_USER=api_nutzer # Postgres API Nutzername (für Arbeitszeitmessung)
|
||||
POSTGRES_API_PASS=password # Postgres API Passwort (für Arbeitszeitmessung)
|
||||
POSTGRES_PATH=../DB # Datebank Pfad (relativ zu Docker Ordner oder absoluter pfad mit /...)
|
||||
POSTGRES_PATH=__ROOT__/DB # Datebank Pfad (relativ zu Docker Ordner oder absoluter pfad mit /...)
|
||||
POSTGRES_DB=arbeitszeitmessung # Postgres Datenbank Name
|
||||
POSTGRES_PORT=127.0.0.1:5432 # Postgres Port will not be exposed by default.
|
||||
POSTGRES_PORT=127.0.0.1:5432 # Postgres Port normalerweise nicht freigegeben. regex:^[0-9]{1,5}$
|
||||
TZ=Europe/Berlin # Zeitzone
|
||||
PGTZ=Europe/Berlin # Zeitzone
|
||||
API_TOKEN=dont_access # API Token für ESP Endpoints
|
||||
WEB_PORT=8000 # Port from which Arbeitszeitmessung should be accessable regex:^[0-9]{1,5}$
|
||||
TYPST_CONTAINER=arbeitszeitmessung-doc-creator # Name of the pdf compiler container
|
||||
WEB_PORT=8000 # Port unter welchem Webserver erreichbar ist. regex:^[0-9]{1,5}$
|
||||
LOG_PATH=__ROOT__/logs # Pfad für Audit Logs
|
||||
LOG_LEVEL=warn # Welche Log-Nachrichten werden in der Konsole erscheinen
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
FROM ghcr.io/typst/typst:0.14.0
|
||||
|
||||
COPY ./templates ./templates
|
||||
COPY ./static ./static
|
||||
|
||||
ENTRYPOINT ["sh", "-c", "while true; do sleep 3600; done"]
|
||||
66
Jenkinsfile
vendored
66
Jenkinsfile
vendored
@@ -1,66 +0,0 @@
|
||||
pipeline {
|
||||
environment {
|
||||
DOCKER_USERNAME = 'jenkins'
|
||||
DOCKER_PASSWORD = credentials('gitea_jenkins')
|
||||
SONAR_TOKEN = credentials('sonarcube_token')
|
||||
POSTGRES_USER = 'postgres'
|
||||
POSTGRES_PASSWORD = 'password'
|
||||
POSTGRES_DB = 'arbeitszeitmessung'
|
||||
}
|
||||
|
||||
agent any
|
||||
|
||||
stages {
|
||||
stage('Test') {
|
||||
agent {
|
||||
docker {
|
||||
image ''
|
||||
args ''
|
||||
args ''
|
||||
}
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
sh '''
|
||||
docker run -d --rm \
|
||||
--name test-db \
|
||||
-e POSTGRES_USER={$POSTGRES_USER} \
|
||||
-e POSTGRES_PASSWORD={$POSTGRES_PASSWORD} \
|
||||
-e POSTGRES_DB={$POSTGRES_DB} \
|
||||
-v ./DB/initdb:/docker-entrypoint-initdb.d\
|
||||
-p "5432:5432" \
|
||||
postgres:16
|
||||
'''
|
||||
// docker.image('golang:1.24.5').withRun(
|
||||
// '-u root:root --network=host'
|
||||
// ) { go ->
|
||||
// // wait for DB to start
|
||||
// sh '''
|
||||
// cd Backend \
|
||||
// go mod download && go mod tidy \
|
||||
// go test ./... -v
|
||||
|
||||
// '''
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('SonarCube Analysis') {
|
||||
steps {
|
||||
sh 'make scan'
|
||||
}
|
||||
}
|
||||
stage('Building image arbeitszeit-backend') {
|
||||
when {
|
||||
anyOf {
|
||||
changeset 'Jenkinsfile'
|
||||
changeset 'Makefile'
|
||||
changeset 'Backend/**'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'make backend'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Makefile
2
Makefile
@@ -44,7 +44,7 @@ generateFrontend:
|
||||
|
||||
|
||||
backend: generateFrontend login_registry
|
||||
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-webserver:dev Backend --push
|
||||
# docker buildx build --platform linux/amd64,linux/arm64 -t ${IMAGE_REGISTRY}/${PACKAGE_OWNER}/arbeitszeitmessung:${GIT_COMMIT} Backend //--push
|
||||
|
||||
test:
|
||||
|
||||
26
Readme.md
26
Readme.md
@@ -120,3 +120,29 @@ Antwort `202` Akzeptiert und eingefügt
|
||||
|
||||
Antwort `409` Konflikt
|
||||
Die vorherige Buchung am selben Tag hat den gleichen Buchungstyp
|
||||
|
||||
# Filestrukture
|
||||
|
||||
```
|
||||
├── Backend (Webserver)
|
||||
│ ├── doc (Templates for Document Creator --> typst used to create PDF Reports)
|
||||
│ │ ├── static
|
||||
│ │ └── templates
|
||||
│ ├── endpoints (HTML Server endpoints (see main.go for Routes))
|
||||
│ ├── helper (Helper classes)
|
||||
│ │ ├── logs
|
||||
│ │ └── paramParser
|
||||
│ ├── logs (Log Folder, no sourcecode)
|
||||
│ ├── migrations (DB Migrations Folder, no direct sourcecode)
|
||||
│ ├── models (DB Models and their function)
|
||||
│ ├── src (Tailwind src --> used to config css formatter)
|
||||
│ ├── static (Webserver static, used to server static content, e.g. JS and CSS files)
|
||||
│ │ └── css
|
||||
│ └── templates (HTML Templates for every page written in templ and compiled to go)
|
||||
├── Cron (all Cron Scripts)
|
||||
├── DB (local Database mount Point)
|
||||
│ └── initdb (initialization scripts for DB)
|
||||
├── Docker (Docker Files, only docker-compose.yaml used)
|
||||
├── docs
|
||||
└── └── images
|
||||
```
|
||||
|
||||
80
install.sh
80
install.sh
@@ -4,6 +4,10 @@ set -e
|
||||
envFile=Docker/.env
|
||||
envExample=Docker/env.example
|
||||
|
||||
autoBackupScript=Cron/autoBackup.sh
|
||||
autoHolidaysScript=Cron/autoHolidays.sh
|
||||
autoLogoutScript=Cron/autoLogout.sh
|
||||
|
||||
echo "Checking Docker installation..."
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
echo "Docker not found. Install Docker? [y/N]"
|
||||
@@ -18,12 +22,16 @@ else
|
||||
echo "Docker is already installed."
|
||||
fi
|
||||
|
||||
###########################################################################
|
||||
|
||||
echo "Checking Docker Compose..."
|
||||
if ! docker compose version >/dev/null 2>&1; then
|
||||
echo "Docker Compose plugin missing. You may need to update Docker."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
###########################################################################
|
||||
|
||||
echo "Preparing .env file..."
|
||||
if [ ! -f $envFile ]; then
|
||||
if [ -f $envExample ]; then
|
||||
@@ -44,6 +52,9 @@ if [ ! -f $envFile ]; then
|
||||
raw_val=$(printf "%s" "$rest" | sed 's/ *#.*//')
|
||||
default_value=$(printf "%s" "$raw_val" | sed 's/"//g')
|
||||
|
||||
# Replace __ROOT__ with script pwd
|
||||
default_value="${default_value/__ROOT__/$(pwd)}"
|
||||
|
||||
regex=""
|
||||
if [[ "$comment" =~ regex:(.*)$ ]]; then
|
||||
regex="${BASH_REMATCH[1]}"
|
||||
@@ -96,13 +107,80 @@ else
|
||||
echo "Using existing .env. (found at $envFile)"
|
||||
fi
|
||||
|
||||
###########################################################################
|
||||
|
||||
LOG_PATH=$(grep -E '^LOG_PATH=' $envFile | cut -d= -f2)
|
||||
if [ -z "$LOG_PATH" ]; then
|
||||
echo "LOG_PATH not found in .env using default $(pwd)/logs"
|
||||
LOG_PATH=$(pwd)/logs
|
||||
else
|
||||
LOG_PATH=Docker/$LOG_PATH
|
||||
fi
|
||||
mkdir -p $LOG_PATH
|
||||
echo "Created logs folder at $LOG_PATH"
|
||||
|
||||
###########################################################################
|
||||
|
||||
echo -e "\n\n"
|
||||
echo "Start containers with docker compose up -d? [y/N]"
|
||||
read -r start_containers
|
||||
if [[ "$start_containers" =~ ^[Yy]$ ]]; then
|
||||
cd Docker
|
||||
mkdir ../logs
|
||||
docker compose up -d
|
||||
echo "Containers started."
|
||||
else
|
||||
echo "You can start them manually with: docker compose up -d"
|
||||
fi
|
||||
|
||||
###########################################################################
|
||||
|
||||
echo -e "\n\n"
|
||||
echo "Setup Crontab for automatic logout, backup and holiday creation? [y/N]"
|
||||
read -r setup_cron
|
||||
if [[ "$setup_cron" =~ ^[Yy]$ ]]; then
|
||||
WEB_PORT=$(grep -E '^WEB_PORT=' $envFile | cut -d= -f2)
|
||||
if [ -z "$WEB_PORT" ]; then
|
||||
echo "WEB_PORT not found in .env using default 8000"
|
||||
WEB_PORT=8000
|
||||
fi
|
||||
|
||||
POSTGRES_DB=$(grep -E '^POSTGRES_DB=' $envFile | cut -d= -f2)
|
||||
if [ -z "$POSTGRES_DB" ]; then
|
||||
echo "arbeitszeitmessung not found in .env using default arbeitszeitmessung"
|
||||
POSTGRES_DB="arbeitszeitmessung"
|
||||
fi
|
||||
|
||||
sed -i "s/__PORT__/$WEB_PORT/" $autoHolidaysScript
|
||||
sed -i "s/__PORT__/$WEB_PORT/" $autoLogoutScript
|
||||
sed -i "s/__DATABASE__/$POSTGRES_DB/" $autoBackupScript
|
||||
|
||||
chmod +x $autoBackupScript $autoHolidaysScript $autoLogoutScript
|
||||
|
||||
# echo "Scripts build with PORT=$WEB_PORT and DATABSE=$POSTGRES_DB!"
|
||||
echo "Adding rules to crontab."
|
||||
|
||||
cron_commands=$(mktemp /tmp/arbeitszeitmessung-cron.XXX)
|
||||
|
||||
for file in Cron/*; do
|
||||
cron_timing=$(grep -E '^# cron-timing:' "$file" | sed 's/^# cron-timing:[[:space:]]*//')
|
||||
|
||||
if [ -z "$cron_timing" ]; then
|
||||
echo "No cron-timing found in $file, so it's not added to crontab."
|
||||
continue
|
||||
fi
|
||||
|
||||
( crontab -l ; echo "$cron_timing $(pwd)/$file" )| awk '!x[$0]++' | crontab -
|
||||
echo "Added entry to crontab: $cron_timing $(pwd)/$file."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if systemctl is-active --quiet cron.service ; then
|
||||
echo "cron.service is running. Everything should be fine now."
|
||||
else
|
||||
echo "cron.service is not running. Please start and enable cron.service."
|
||||
echo "For how to start a service, see: https://wiki.ubuntuusers.de/systemd/systemctl UNITNAME will be cron.service"
|
||||
fi
|
||||
|
||||
else
|
||||
echo "Please setup cron manually by executing crontab -e and adding all files from inside the Cron directory!"
|
||||
fi
|
||||
|
||||
269
pnpm-lock.yaml
generated
269
pnpm-lock.yaml
generated
@@ -14,9 +14,42 @@ importers:
|
||||
tailwindcss:
|
||||
specifier: ^4.1.12
|
||||
version: 4.1.12
|
||||
devDependencies:
|
||||
'@iconify-json/material-symbols-light':
|
||||
specifier: ^1.2.33
|
||||
version: 1.2.50
|
||||
'@iconify/tailwind4':
|
||||
specifier: ^1.0.6
|
||||
version: 1.2.0(tailwindcss@4.1.12)
|
||||
prettier:
|
||||
specifier: ^3.6.2
|
||||
version: 3.7.4
|
||||
|
||||
packages:
|
||||
|
||||
'@antfu/install-pkg@1.1.0':
|
||||
resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==}
|
||||
|
||||
'@cyberalien/svg-utils@1.0.11':
|
||||
resolution: {integrity: sha512-qEE9mnyI+avfGT3emKuRs3ucYkITeaV0Xi7VlYN41f+uGnZBecQP3jwz/AF437H9J4Q7qPClHKm4NiTYpNE6hA==}
|
||||
|
||||
'@iconify-json/material-symbols-light@1.2.50':
|
||||
resolution: {integrity: sha512-Ehvmar2TPoYxmKgB5szeIMlmvA/mIc7gzUoQ5/AWFG+N6d4T53uCHwxnXFf1nXPWlpf0+cv26AXMJC6W5mkdrQ==}
|
||||
|
||||
'@iconify/tailwind4@1.2.0':
|
||||
resolution: {integrity: sha512-+t7XqfojOB0zzZdd8gV7IQZGq1AaIHTlsxMVzagxYR0hAlJCLUD63o3iSlNKRMH3ZR7gZ8y5c9dJ7J431avRbA==}
|
||||
peerDependencies:
|
||||
tailwindcss: '>= 4.0.0'
|
||||
|
||||
'@iconify/tools@5.0.1':
|
||||
resolution: {integrity: sha512-/znhBN9WIpJd9UtKhyEDfRKwNo8rrOy8dShF8bwSZ1i27ukTSHjeS6bmVK4tTYBYriwFhBf70JT6g8GIRwFvbw==}
|
||||
|
||||
'@iconify/types@2.0.0':
|
||||
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
|
||||
|
||||
'@iconify/utils@3.1.0':
|
||||
resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==}
|
||||
|
||||
'@isaacs/fs-minipass@4.0.1':
|
||||
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
@@ -208,6 +241,14 @@ packages:
|
||||
resolution: {integrity: sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw==}
|
||||
engines: {node: '>= 10'}
|
||||
|
||||
acorn@8.15.0:
|
||||
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
boolbase@1.0.0:
|
||||
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
|
||||
|
||||
braces@3.0.3:
|
||||
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -216,6 +257,32 @@ packages:
|
||||
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
commander@11.1.0:
|
||||
resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
confbox@0.1.8:
|
||||
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
|
||||
|
||||
css-select@5.2.2:
|
||||
resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
|
||||
|
||||
css-tree@2.2.1:
|
||||
resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==}
|
||||
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
|
||||
|
||||
css-tree@3.1.0:
|
||||
resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==}
|
||||
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
|
||||
|
||||
css-what@6.2.2:
|
||||
resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
csso@5.0.5:
|
||||
resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
|
||||
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
|
||||
|
||||
detect-libc@1.0.3:
|
||||
resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
|
||||
engines: {node: '>=0.10'}
|
||||
@@ -225,10 +292,30 @@ packages:
|
||||
resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
dom-serializer@2.0.0:
|
||||
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
|
||||
|
||||
domelementtype@2.3.0:
|
||||
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
|
||||
|
||||
domhandler@5.0.3:
|
||||
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
domutils@3.2.2:
|
||||
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
|
||||
|
||||
enhanced-resolve@5.18.3:
|
||||
resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
entities@4.5.0:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
fflate@0.8.2:
|
||||
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
|
||||
|
||||
fill-range@7.1.1:
|
||||
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -319,6 +406,12 @@ packages:
|
||||
magic-string@0.30.17:
|
||||
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
|
||||
|
||||
mdn-data@2.0.28:
|
||||
resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
|
||||
|
||||
mdn-data@2.12.2:
|
||||
resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==}
|
||||
|
||||
micromatch@4.0.8:
|
||||
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||
engines: {node: '>=8.6'}
|
||||
@@ -336,6 +429,13 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
mlly@1.8.0:
|
||||
resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}
|
||||
|
||||
modern-tar@0.7.3:
|
||||
resolution: {integrity: sha512-4W79zekKGyYU4JXVmB78DOscMFaJth2gGhgfTl2alWE4rNe3nf4N2pqenQ0rEtIewrnD79M687Ouba3YGTLOvg==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
mri@1.2.0:
|
||||
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -343,6 +443,15 @@ packages:
|
||||
node-addon-api@7.1.1:
|
||||
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||
|
||||
nth-check@2.1.1:
|
||||
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||
|
||||
package-manager-detector@1.6.0:
|
||||
resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==}
|
||||
|
||||
pathe@2.0.3:
|
||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
@@ -350,10 +459,26 @@ packages:
|
||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
pkg-types@1.3.1:
|
||||
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
|
||||
|
||||
prettier@3.7.4:
|
||||
resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
|
||||
sax@1.4.3:
|
||||
resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==}
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
svgo@4.0.0:
|
||||
resolution: {integrity: sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==}
|
||||
engines: {node: '>=16'}
|
||||
hasBin: true
|
||||
|
||||
tailwindcss@4.1.12:
|
||||
resolution: {integrity: sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==}
|
||||
|
||||
@@ -365,16 +490,61 @@ packages:
|
||||
resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
tinyexec@1.0.2:
|
||||
resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
|
||||
ufo@1.6.1:
|
||||
resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
|
||||
|
||||
yallist@5.0.0:
|
||||
resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@antfu/install-pkg@1.1.0':
|
||||
dependencies:
|
||||
package-manager-detector: 1.6.0
|
||||
tinyexec: 1.0.2
|
||||
|
||||
'@cyberalien/svg-utils@1.0.11':
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
|
||||
'@iconify-json/material-symbols-light@1.2.50':
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
|
||||
'@iconify/tailwind4@1.2.0(tailwindcss@4.1.12)':
|
||||
dependencies:
|
||||
'@iconify/tools': 5.0.1
|
||||
'@iconify/types': 2.0.0
|
||||
'@iconify/utils': 3.1.0
|
||||
tailwindcss: 4.1.12
|
||||
|
||||
'@iconify/tools@5.0.1':
|
||||
dependencies:
|
||||
'@cyberalien/svg-utils': 1.0.11
|
||||
'@iconify/types': 2.0.0
|
||||
'@iconify/utils': 3.1.0
|
||||
fflate: 0.8.2
|
||||
modern-tar: 0.7.3
|
||||
pathe: 2.0.3
|
||||
svgo: 4.0.0
|
||||
|
||||
'@iconify/types@2.0.0': {}
|
||||
|
||||
'@iconify/utils@3.1.0':
|
||||
dependencies:
|
||||
'@antfu/install-pkg': 1.1.0
|
||||
'@iconify/types': 2.0.0
|
||||
mlly: 1.8.0
|
||||
|
||||
'@isaacs/fs-minipass@4.0.1':
|
||||
dependencies:
|
||||
minipass: 7.1.2
|
||||
@@ -532,21 +702,75 @@ snapshots:
|
||||
'@tailwindcss/oxide-win32-arm64-msvc': 4.1.12
|
||||
'@tailwindcss/oxide-win32-x64-msvc': 4.1.12
|
||||
|
||||
acorn@8.15.0: {}
|
||||
|
||||
boolbase@1.0.0: {}
|
||||
|
||||
braces@3.0.3:
|
||||
dependencies:
|
||||
fill-range: 7.1.1
|
||||
|
||||
chownr@3.0.0: {}
|
||||
|
||||
commander@11.1.0: {}
|
||||
|
||||
confbox@0.1.8: {}
|
||||
|
||||
css-select@5.2.2:
|
||||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
css-what: 6.2.2
|
||||
domhandler: 5.0.3
|
||||
domutils: 3.2.2
|
||||
nth-check: 2.1.1
|
||||
|
||||
css-tree@2.2.1:
|
||||
dependencies:
|
||||
mdn-data: 2.0.28
|
||||
source-map-js: 1.2.1
|
||||
|
||||
css-tree@3.1.0:
|
||||
dependencies:
|
||||
mdn-data: 2.12.2
|
||||
source-map-js: 1.2.1
|
||||
|
||||
css-what@6.2.2: {}
|
||||
|
||||
csso@5.0.5:
|
||||
dependencies:
|
||||
css-tree: 2.2.1
|
||||
|
||||
detect-libc@1.0.3: {}
|
||||
|
||||
detect-libc@2.0.4: {}
|
||||
|
||||
dom-serializer@2.0.0:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
domhandler: 5.0.3
|
||||
entities: 4.5.0
|
||||
|
||||
domelementtype@2.3.0: {}
|
||||
|
||||
domhandler@5.0.3:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
|
||||
domutils@3.2.2:
|
||||
dependencies:
|
||||
dom-serializer: 2.0.0
|
||||
domelementtype: 2.3.0
|
||||
domhandler: 5.0.3
|
||||
|
||||
enhanced-resolve@5.18.3:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
tapable: 2.2.2
|
||||
|
||||
entities@4.5.0: {}
|
||||
|
||||
fflate@0.8.2: {}
|
||||
|
||||
fill-range@7.1.1:
|
||||
dependencies:
|
||||
to-regex-range: 5.0.1
|
||||
@@ -612,6 +836,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
mdn-data@2.0.28: {}
|
||||
|
||||
mdn-data@2.12.2: {}
|
||||
|
||||
micromatch@4.0.8:
|
||||
dependencies:
|
||||
braces: 3.0.3
|
||||
@@ -625,16 +853,53 @@ snapshots:
|
||||
|
||||
mkdirp@3.0.1: {}
|
||||
|
||||
mlly@1.8.0:
|
||||
dependencies:
|
||||
acorn: 8.15.0
|
||||
pathe: 2.0.3
|
||||
pkg-types: 1.3.1
|
||||
ufo: 1.6.1
|
||||
|
||||
modern-tar@0.7.3: {}
|
||||
|
||||
mri@1.2.0: {}
|
||||
|
||||
node-addon-api@7.1.1: {}
|
||||
|
||||
nth-check@2.1.1:
|
||||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
|
||||
package-manager-detector@1.6.0: {}
|
||||
|
||||
pathe@2.0.3: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@2.3.1: {}
|
||||
|
||||
pkg-types@1.3.1:
|
||||
dependencies:
|
||||
confbox: 0.1.8
|
||||
mlly: 1.8.0
|
||||
pathe: 2.0.3
|
||||
|
||||
prettier@3.7.4: {}
|
||||
|
||||
sax@1.4.3: {}
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
svgo@4.0.0:
|
||||
dependencies:
|
||||
commander: 11.1.0
|
||||
css-select: 5.2.2
|
||||
css-tree: 3.1.0
|
||||
css-what: 6.2.2
|
||||
csso: 5.0.5
|
||||
picocolors: 1.1.1
|
||||
sax: 1.4.3
|
||||
|
||||
tailwindcss@4.1.12: {}
|
||||
|
||||
tapable@2.2.2: {}
|
||||
@@ -648,8 +913,12 @@ snapshots:
|
||||
mkdirp: 3.0.1
|
||||
yallist: 5.0.0
|
||||
|
||||
tinyexec@1.0.2: {}
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
ufo@1.6.1: {}
|
||||
|
||||
yallist@5.0.0: {}
|
||||
|
||||
@@ -103,9 +103,22 @@ CREATE TABLE "s_abwesenheit_typen" (
|
||||
"abwesenheit_name" varchar(255) NOT NULL,
|
||||
"arbeitszeit_equivalent" float4 NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON COLUMN "s_abwesenheit_typen"."arbeitszeit_equivalent" IS '0=keine Arbeitszeit; -1=Arbeitszeit auffüllen; <=1 - 100 => Arbeitszeit pro Tag prozentual';
|
||||
|
||||
DROP TABLE IF EXISTS "s_feiertage";
|
||||
CREATE TABLE "s_feiertage" (
|
||||
"counter_id" serial PRIMARY KEY NOT NULL,
|
||||
"datum" date NOT NULL,
|
||||
"name" varchar(100) NOT NULL,
|
||||
"wiederholen" smallint NOT NULL DEFAULT 0,
|
||||
"arbeitszeit_equivalent" smallint NOT NULL DEFAULT 100
|
||||
);
|
||||
|
||||
CREATE UNIQUE index feiertage_unique_pro_jahr on s_feiertage (
|
||||
extract ( year from datum ),
|
||||
name
|
||||
);
|
||||
|
||||
-- Adds crypto extension
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
Reference in New Issue
Block a user