Compare commits
16 Commits
7fae75be75
...
2.0.0-rc.3
| Author | SHA1 | Date | |
|---|---|---|---|
| 18b2cbc074 | |||
| 560c539b19 | |||
| 502955d32f | |||
| cfd77ae28d | |||
| 1daf4db167 | |||
| 3322f7e9bc | |||
| 4b9824c714 | |||
| 7ac6c5f9b8 | |||
| f9fc3d91d1 | |||
| f0de9961dc | |||
| 4ded8632e5 | |||
| b2af48463c | |||
| 0b72147e02 | |||
| d1b46cf894 | |||
| 7c45ef6112 | |||
| 2b17eb6854 |
@@ -39,34 +39,34 @@ jobs:
|
|||||||
push: true
|
push: true
|
||||||
context: Backend
|
context: Backend
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
document-creator:
|
# document-creator:
|
||||||
name: Build Document Creator
|
# name: Build Document Creator
|
||||||
runs-on: ubuntu-latest
|
# runs-on: ubuntu-latest
|
||||||
steps:
|
# steps:
|
||||||
- name: Checkout
|
# - name: Checkout
|
||||||
uses: actions/checkout@v4
|
# uses: actions/checkout@v4
|
||||||
- name: Login to GitHub Container Registry
|
# - name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
# uses: docker/login-action@v3
|
||||||
with:
|
# with:
|
||||||
registry: git.letsstein.de
|
# registry: git.letsstein.de
|
||||||
username: ${{ gitea.actor }}
|
# username: ${{ gitea.actor }}
|
||||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
# password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
- name: Set up QEMU
|
# - name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
# uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
# - name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
# uses: docker/setup-buildx-action@v3
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
# - name: Extract metadata (tags, labels) for Docker
|
||||||
id: meta
|
# id: meta
|
||||||
uses: docker/metadata-action@v5
|
# uses: docker/metadata-action@v5
|
||||||
with:
|
# with:
|
||||||
images: git.letsstein.de/tom/arbeitszeitmessung-doc-creator
|
# images: git.letsstein.de/tom/arbeitszeitmessung-doc-creator
|
||||||
tags: |
|
# tags: |
|
||||||
type=raw,value=latest
|
# type=raw,value=latest
|
||||||
type=pep440,pattern={{version}}
|
# type=pep440,pattern={{version}}
|
||||||
- name: Build and push
|
# - name: Build and push
|
||||||
uses: docker/build-push-action@v6
|
# uses: docker/build-push-action@v6
|
||||||
with:
|
# with:
|
||||||
platforms: linux/amd64,linux/arm64
|
# platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
# push: true
|
||||||
context: Backend
|
# context: DocumentCreator
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
# tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
|||||||
@@ -14,9 +14,11 @@ COPY . .
|
|||||||
RUN go build -o server .
|
RUN go build -o server .
|
||||||
|
|
||||||
FROM alpine:3.22
|
FROM alpine:3.22
|
||||||
RUN apk add --no-cache tzdata
|
RUN apk add --no-cache tzdata typst
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=build /app/server /app/server
|
COPY --from=build /app/server /app/server
|
||||||
|
COPY ./doc/static /doc/static
|
||||||
|
COPY ./doc/templates /doc/templates
|
||||||
|
|
||||||
COPY /static /app/static
|
COPY /static /app/static
|
||||||
ENTRYPOINT ["./server"]
|
ENTRYPOINT ["./server"]
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ import (
|
|||||||
"arbeitszeitmessung/models"
|
"arbeitszeitmessung/models"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/golang-migrate/migrate/v4"
|
||||||
|
_ "github.com/golang-migrate/migrate/v4/database/postgres"
|
||||||
|
_ "github.com/golang-migrate/migrate/v4/source/file"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,3 +23,29 @@ func OpenDatabase() (models.IDatabase, error) {
|
|||||||
connStr := fmt.Sprintf("postgres://%s:%s@%s:5432/%s?sslmode=disable&TimeZone=%s", dbUser, dbPassword, dbHost, dbName, dbTz)
|
connStr := fmt.Sprintf("postgres://%s:%s@%s:5432/%s?sslmode=disable&TimeZone=%s", dbUser, dbPassword, dbHost, dbName, dbTz)
|
||||||
return sql.Open("postgres", connStr)
|
return sql.Open("postgres", connStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Migrate() error {
|
||||||
|
dbHost := helper.GetEnv("POSTGRES_HOST", "localhost")
|
||||||
|
dbName := helper.GetEnv("POSTGRES_DB", "arbeitszeitmessung")
|
||||||
|
// dbUser := helper.GetEnv("POSTGRES_USER", "api_nutzer")
|
||||||
|
dbPassword := helper.GetEnv("POSTGRES_PASSWORD", "password")
|
||||||
|
dbTz := helper.GetEnv("TZ", "Europe/Berlin")
|
||||||
|
|
||||||
|
migrations := helper.GetEnv("MIGRATIONS_PATH", "../migrations")
|
||||||
|
|
||||||
|
connStr := fmt.Sprintf("postgres://%s:%s@%s:5432/%s?sslmode=disable&TimeZone=%s", "migrate", dbPassword, dbHost, dbName, dbTz)
|
||||||
|
m, err := migrate.New(fmt.Sprintf("file://%s", migrations), connStr)
|
||||||
|
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 |
@@ -21,7 +21,7 @@ func LogoutHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func autoLogout(w http.ResponseWriter) {
|
func autoLogout(w http.ResponseWriter) {
|
||||||
users, err := models.GetAllUsers()
|
users, err := models.GetAllUsers()
|
||||||
var logged_out_users []models.User
|
var loggedOutUsers []models.User
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error getting user list %v\n", err)
|
fmt.Printf("Error getting user list %v\n", err)
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ func autoLogout(w http.ResponseWriter) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error logging out user %v\n", err)
|
fmt.Printf("Error logging out user %v\n", err)
|
||||||
} else {
|
} 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)
|
log.Printf("Automaticaly logged out user %s, %s ", user.Name, user.Vorname)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,6 +39,6 @@ func autoLogout(w http.ResponseWriter) {
|
|||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode(logged_out_users)
|
json.NewEncoder(w).Encode(loggedOutUsers)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,11 @@ func convertDayToTypstDayParts(day models.IWorkDay, user models.User) []typstDay
|
|||||||
for i := 0; i < len(workDay.Bookings); i += 2 {
|
for i := 0; i < len(workDay.Bookings); i += 2 {
|
||||||
var typstDayPart typstDayPart
|
var typstDayPart typstDayPart
|
||||||
typstDayPart.BookingFrom = workDay.Bookings[i].Timestamp.Format("15:04")
|
typstDayPart.BookingFrom = workDay.Bookings[i].Timestamp.Format("15:04")
|
||||||
typstDayPart.BookingTo = workDay.Bookings[i+1].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.WorkType = workDay.Bookings[i].BookingType.Name
|
||||||
typstDayPart.IsWorkDay = true
|
typstDayPart.IsWorkDay = true
|
||||||
typstDayParts = append(typstDayParts, typstDayPart)
|
typstDayParts = append(typstDayParts, typstDayPart)
|
||||||
@@ -194,8 +198,9 @@ func renderPDFSingle(data []typstData) (bytes.Buffer, error) {
|
|||||||
var markup bytes.Buffer
|
var markup bytes.Buffer
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
|
|
||||||
typstCLI := typst.DockerExec{
|
typstCLI := typst.CLI{
|
||||||
ContainerName: helper.GetEnv("TYPST_CONTAINER", "arbeitszeitmessung-doc-creator"),
|
WorkingDirectory: "/doc/",
|
||||||
|
// ContainerName: helper.GetEnv("TYPST_CONTAINER", "arbeitszeitmessung-doc-creator"),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := typst.InjectValues(&markup, map[string]any{"data": data}); err != nil {
|
if err := typst.InjectValues(&markup, map[string]any{"data": data}); err != nil {
|
||||||
@@ -221,8 +226,9 @@ func renderPDFSingle(data []typstData) (bytes.Buffer, error) {
|
|||||||
func renderPDFMulti(data []typstData) ([]bytes.Buffer, error) {
|
func renderPDFMulti(data []typstData) ([]bytes.Buffer, error) {
|
||||||
var outputMulti []bytes.Buffer
|
var outputMulti []bytes.Buffer
|
||||||
|
|
||||||
typstRender := typst.DockerExec{
|
typstRender := typst.CLI{
|
||||||
ContainerName: helper.GetEnv("TYPST_CONTAINER", "arbeitszeitmessung-doc-creator"),
|
WorkingDirectory: "/doc/",
|
||||||
|
// ContainerName: helper.GetEnv("TYPST_CONTAINER", "arbeitszeitmessung-doc-creator"),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, d := range data {
|
for _, d := range data {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Relevant for arduino inputs -> creates new Booking from get and put method
|
// Relevant for arduino inputs -> creates new Booking from get and put method
|
||||||
@@ -36,6 +37,7 @@ func createBooking(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
booking := (*models.Booking).FromUrlParams(nil, r.URL.Query())
|
booking := (*models.Booking).FromUrlParams(nil, r.URL.Query())
|
||||||
|
booking.Timestamp = time.Now()
|
||||||
if booking.Verify() {
|
if booking.Verify() {
|
||||||
err := booking.Insert()
|
err := booking.Insert()
|
||||||
if errors.Is(models.SameBookingError{}, err) {
|
if errors.Is(models.SameBookingError{}, err) {
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ func getBookings(w http.ResponseWriter, r *http.Request) {
|
|||||||
if day.Date().Before(lastSub) {
|
if day.Date().Before(lastSub) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
aggregatedOvertime += day.GetOvertime(user, models.WorktimeBaseDay, false)
|
aggregatedOvertime += day.GetOvertime(user, models.WorktimeBaseDay, true)
|
||||||
}
|
}
|
||||||
if reportedOvertime, err := user.GetReportedOvertime(); err == nil {
|
if reportedOvertime, err := user.GetReportedOvertime(); err == nil {
|
||||||
user.Overtime = (reportedOvertime + aggregatedOvertime).Round(time.Minute)
|
user.Overtime = (reportedOvertime + aggregatedOvertime).Round(time.Minute)
|
||||||
@@ -160,19 +160,13 @@ func updateBooking(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
newBooking := (*models.Booking).New(nil, user.CardUID, 0, int16(check_in_out), 1)
|
newBooking := (*models.Booking).New(nil, user.CardUID, 0, int16(check_in_out), 1)
|
||||||
newBooking.Timestamp = timestamp
|
newBooking.Timestamp = timestamp
|
||||||
err = newBooking.InsertWithTimestamp()
|
if newBooking.Verify() {
|
||||||
if err != nil {
|
err = newBooking.InsertWithTimestamp()
|
||||||
log.Printf("Error inserting booking %v -> %v\n", newBooking, err)
|
if err != nil {
|
||||||
|
log.Printf("Error inserting booking %v -> %v\n", newBooking, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case "change":
|
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 {
|
for index, possibleBooking := range r.PostForm {
|
||||||
if len(index) > 7 && index[:7] == "booking" {
|
if len(index) > 7 && index[:7] == "booking" {
|
||||||
booking_id, err := strconv.Atoi(index[8:])
|
booking_id, err := strconv.Atoi(index[8:])
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ 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/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 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
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 h1:trshEpGa8clF5cdI39iY4ZrZG8Z/QixyzEyUnA7feTM=
|
||||||
github.com/a-h/templ v0.3.960/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
|
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 h1:h31yUYoycPuL0zt14c0gd+oqxfRwIj6SOjHdKRZxhEw=
|
||||||
@@ -39,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/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 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
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.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 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
@@ -78,7 +78,6 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8
|
|||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
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/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/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/wlbr/feiertage v1.10.0/go.mod h1:wJOHvMa6sI5L1FkrTOX/GSoO0hpK3S2YqGLPi8Q84I0=
|
|
||||||
github.com/wlbr/feiertage v1.17.0 h1:AEck/iUQu19iU0xNEoSQTeSTGXF1Ju0tbAwEi/Lmwqk=
|
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/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=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"arbeitszeitmessung/helper"
|
"arbeitszeitmessung/helper"
|
||||||
"arbeitszeitmessung/models"
|
"arbeitszeitmessung/models"
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -17,7 +18,16 @@ import (
|
|||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
var logLevel slog.LevelVar
|
var logLevel slog.LevelVar
|
||||||
logLevel.Set(slog.LevelWarn)
|
switch helper.GetEnv("LOG_LEVEL", "warn") {
|
||||||
|
case "debug":
|
||||||
|
logLevel.Set(slog.LevelDebug)
|
||||||
|
case "info":
|
||||||
|
logLevel.Set(slog.LevelInfo)
|
||||||
|
case "warn":
|
||||||
|
logLevel.Set(slog.LevelWarn)
|
||||||
|
case "error":
|
||||||
|
logLevel.Set(slog.LevelError)
|
||||||
|
}
|
||||||
|
|
||||||
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: &logLevel}))
|
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: &logLevel}))
|
||||||
slog.SetDefault(logger)
|
slog.SetDefault(logger)
|
||||||
@@ -35,6 +45,15 @@ func main() {
|
|||||||
models.DB, err = OpenDatabase()
|
models.DB, err = OpenDatabase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Error while opening the database", "Error", err)
|
slog.Error("Error while opening the database", "Error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer models.DB.(*sql.DB).Close()
|
||||||
|
|
||||||
|
err = Migrate()
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Failed to migrate the database to newest version", "Error", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fs := http.FileServer(http.Dir("./static"))
|
fs := http.FileServer(http.Dir("./static"))
|
||||||
@@ -61,6 +80,8 @@ func main() {
|
|||||||
|
|
||||||
serverSessionMiddleware := endpoints.Session.LoadAndSave(server)
|
serverSessionMiddleware := endpoints.Session.LoadAndSave(server)
|
||||||
|
|
||||||
|
serverSessionMiddleware = loggingMiddleware(serverSessionMiddleware)
|
||||||
|
|
||||||
// starting the http server
|
// starting the http server
|
||||||
slog.Info("Server is running at http://localhost:8080")
|
slog.Info("Server is running at http://localhost:8080")
|
||||||
slog.Error("Error starting Server", "Error", http.ListenAndServe(":8080", serverSessionMiddleware))
|
slog.Error("Error starting Server", "Error", http.ListenAndServe(":8080", serverSessionMiddleware))
|
||||||
@@ -70,7 +91,24 @@ func ParamsMiddleware(next http.HandlerFunc) http.Handler {
|
|||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
queryParams := r.URL.Query()
|
queryParams := r.URL.Query()
|
||||||
ctx := context.WithValue(r.Context(), "urlParams", queryParams)
|
ctx := context.WithValue(r.Context(), "urlParams", queryParams)
|
||||||
slog.Debug("ParamsMiddleware added urlParams", slog.Any("urlParams", queryParams))
|
if len(queryParams) > 0 {
|
||||||
|
slog.Debug("ParamsMiddleware added urlParams", slog.Any("urlParams", queryParams))
|
||||||
|
}
|
||||||
next.ServeHTTP(w, r.WithContext(ctx))
|
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()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -128,6 +128,9 @@ func (b *Booking) InsertWithTimestamp() error {
|
|||||||
if b.Timestamp.IsZero() {
|
if b.Timestamp.IsZero() {
|
||||||
return b.Insert()
|
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`))
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -242,12 +245,13 @@ func (b *Booking) Update(nb Booking) {
|
|||||||
|
|
||||||
func checkLastBooking(b Booking) bool {
|
func checkLastBooking(b Booking) bool {
|
||||||
var check_in_out int
|
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 {
|
if err != nil {
|
||||||
log.Fatalf("Error preparing query: %v", err)
|
log.Fatalf("Error preparing query: %v", err)
|
||||||
return false
|
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 {
|
if err == sql.ErrNoRows {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func (c *CompoundDay) GetWorkDay() WorkDay {
|
|||||||
|
|
||||||
// IsEmpty implements [IWorkDay].
|
// IsEmpty implements [IWorkDay].
|
||||||
func (c *CompoundDay) IsEmpty() bool {
|
func (c *CompoundDay) IsEmpty() bool {
|
||||||
return len(c.DayParts) > 0
|
return len(c.DayParts) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Date implements [IWorkDay].
|
// Date implements [IWorkDay].
|
||||||
@@ -47,12 +47,16 @@ func (c *CompoundDay) GetDayProgress(u User) int8 {
|
|||||||
|
|
||||||
// GetOvertime implements [IWorkDay].
|
// GetOvertime implements [IWorkDay].
|
||||||
func (c *CompoundDay) GetOvertime(u User, base WorktimeBase, includeKurzarbeit bool) time.Duration {
|
func (c *CompoundDay) GetOvertime(u User, base WorktimeBase, includeKurzarbeit bool) time.Duration {
|
||||||
|
work := c.GetWorktime(u, base, includeKurzarbeit)
|
||||||
|
var targetHours time.Duration
|
||||||
|
|
||||||
var overtime time.Duration
|
switch base {
|
||||||
for _, day := range c.DayParts {
|
case WorktimeBaseDay:
|
||||||
overtime += day.GetOvertime(u, base, includeKurzarbeit)
|
targetHours = u.ArbeitszeitProTagFrac(1)
|
||||||
|
case WorktimeBaseWeek:
|
||||||
|
targetHours = u.ArbeitszeitProWocheFrac(.2)
|
||||||
}
|
}
|
||||||
return overtime
|
return (work - targetHours).Round(time.Minute)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPausetime implements [IWorkDay].
|
// GetPausetime implements [IWorkDay].
|
||||||
|
|||||||
@@ -49,19 +49,16 @@ func GetDays(user User, tsFrom, tsTo time.Time, orderedForward bool) []IWorkDay
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, absentDay := range absences {
|
for _, absentDay := range absences {
|
||||||
// Kurzarbeit should be integrated in workday
|
|
||||||
|
// Check if there is already a day
|
||||||
existingDay, ok := allDays[absentDay.Date().Format(time.DateOnly)]
|
existingDay, ok := allDays[absentDay.Date().Format(time.DateOnly)]
|
||||||
if !ok {
|
|
||||||
allDays[absentDay.Date().Format(time.DateOnly)] = &absentDay
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch {
|
switch {
|
||||||
case absentDay.AbwesenheitTyp.WorkTime < 0:
|
case absentDay.AbwesenheitTyp.WorkTime < 0:
|
||||||
if workDay, ok := allDays[absentDay.Date().Format(time.DateOnly)].(*WorkDay); ok {
|
if workDay, ok := allDays[absentDay.Date().Format(time.DateOnly)].(*WorkDay); ok {
|
||||||
workDay.kurzArbeit = true
|
workDay.kurzArbeit = true
|
||||||
workDay.kurzArbeitAbsence = absentDay
|
workDay.kurzArbeitAbsence = absentDay
|
||||||
}
|
}
|
||||||
case !existingDay.IsEmpty():
|
case ok && !existingDay.IsEmpty():
|
||||||
allDays[absentDay.Date().Format(time.DateOnly)] = NewCompondDay(absentDay.Date(), existingDay, &absentDay)
|
allDays[absentDay.Date().Format(time.DateOnly)] = NewCompondDay(absentDay.Date(), existingDay, &absentDay)
|
||||||
default:
|
default:
|
||||||
allDays[absentDay.Date().Format(time.DateOnly)] = &absentDay
|
allDays[absentDay.Date().Format(time.DateOnly)] = &absentDay
|
||||||
|
|||||||
@@ -175,22 +175,39 @@ func GetWorkDays(user User, tsFrom, tsTo time.Time) []WorkDay {
|
|||||||
qStr, err := DB.Prepare(`
|
qStr, err := DB.Prepare(`
|
||||||
WITH all_days AS (
|
WITH all_days AS (
|
||||||
SELECT generate_series($2::DATE, $3::DATE - INTERVAL '1 day', INTERVAL '1 day')::DATE AS work_date),
|
SELECT generate_series($2::DATE, $3::DATE - INTERVAL '1 day', INTERVAL '1 day')::DATE AS work_date),
|
||||||
ordered_bookings AS (
|
normalized_bookings AS (
|
||||||
SELECT
|
SELECT *
|
||||||
a.timestamp::DATE AS work_date,
|
FROM (
|
||||||
a.timestamp,
|
SELECT
|
||||||
a.check_in_out,
|
a.card_uid,
|
||||||
a.counter_id,
|
a.timestamp,
|
||||||
a.anwesenheit_typ,
|
a.timestamp::DATE AS work_date,
|
||||||
sat.anwesenheit_name AS anwesenheit_typ_name,
|
a.check_in_out,
|
||||||
LAG(a.timestamp) OVER (PARTITION BY a.card_uid, a.timestamp::DATE ORDER BY a.timestamp) AS prev_timestamp,
|
a.counter_id,
|
||||||
LAG(a.check_in_out) OVER (PARTITION BY a.card_uid, a.timestamp::DATE ORDER BY a.timestamp) AS prev_check
|
a.anwesenheit_typ,
|
||||||
FROM anwesenheit a
|
sat.anwesenheit_name AS anwesenheit_typ_name,
|
||||||
LEFT JOIN s_anwesenheit_typen sat ON a.anwesenheit_typ = sat.anwesenheit_id
|
LAG(a.check_in_out) OVER (
|
||||||
WHERE a.card_uid = $1
|
PARTITION BY a.card_uid, a.timestamp::DATE
|
||||||
AND a.timestamp::DATE >= $2
|
ORDER BY a.timestamp
|
||||||
AND a.timestamp::DATE <= $3
|
) 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
|
||||||
|
) 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
|
SELECT
|
||||||
d.work_date,
|
d.work_date,
|
||||||
COALESCE(MIN(b.timestamp), NOW()) AS time_from,
|
COALESCE(MIN(b.timestamp), NOW()) AS time_from,
|
||||||
@@ -228,6 +245,62 @@ func GetWorkDays(user User, tsFrom, tsTo time.Time) []WorkDay {
|
|||||||
GROUP BY d.work_date
|
GROUP BY d.work_date
|
||||||
ORDER BY d.work_date ASC;`)
|
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 {
|
if err != nil {
|
||||||
log.Println("Error preparing SQL statement", err)
|
log.Println("Error preparing SQL statement", err)
|
||||||
return workDays
|
return workDays
|
||||||
@@ -258,7 +331,7 @@ func GetWorkDays(user User, tsFrom, tsTo time.Time) []WorkDay {
|
|||||||
if len(workDay.Bookings) == 1 && workDay.Bookings[0].CounterId == 0 {
|
if len(workDay.Bookings) == 1 && workDay.Bookings[0].CounterId == 0 {
|
||||||
workDay.Bookings = []Booking{}
|
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)
|
workDays = append(workDays, workDay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -15,7 +15,9 @@ function editWorkday(element, event, id, isWorkDay) {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let form = document.getElementById(id);
|
let form = document.getElementById(id);
|
||||||
if (form == null) {
|
if (form == null) {
|
||||||
form = element.closest(".grid-sub").querySelector(".all-booking-component > form");
|
form = element
|
||||||
|
.closest(".grid-sub")
|
||||||
|
.querySelector(".all-booking-component > form");
|
||||||
}
|
}
|
||||||
|
|
||||||
clearEditState();
|
clearEditState();
|
||||||
@@ -37,10 +39,21 @@ function editWorkday(element, event, id, isWorkDay) {
|
|||||||
const absenceForm = document.getElementById("absence_form");
|
const absenceForm = document.getElementById("absence_form");
|
||||||
|
|
||||||
if (id == 0) {
|
if (id == 0) {
|
||||||
absenceForm.querySelector("[name=date_from]").value = form.id.replace("time-", "");
|
absenceForm.querySelector("[name=date_from]").value = form.id.replace(
|
||||||
absenceForm.querySelector("[name=date_to]").value = form.id.replace("time-", "");
|
"time-",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
absenceForm.querySelector("[name=date_to]").value = form.id.replace(
|
||||||
|
"time-",
|
||||||
|
"",
|
||||||
|
);
|
||||||
} else {
|
} 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");
|
const form = document.getElementById("absence_form");
|
||||||
if (state) {
|
if (state) {
|
||||||
form.classList.remove("hidden");
|
form.classList.remove("hidden");
|
||||||
form.scrollIntoView({
|
|
||||||
behavior: "smooth",
|
|
||||||
block: "start",
|
|
||||||
inline: "nearest",
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
form.classList.add("hidden");
|
form.classList.add("hidden");
|
||||||
}
|
}
|
||||||
@@ -85,3 +93,12 @@ function checkAll(pattern, state) {
|
|||||||
input.checked = 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
package templates
|
package templates
|
||||||
|
|
||||||
templ headerComponent() {
|
templ headerComponent() {
|
||||||
|
// {{ user := ctx.Value("user").(models.User) }}
|
||||||
<div class="flex flex-row justify-between md:mx-[10%] py-2 items-center">
|
<div class="flex flex-row justify-between md:mx-[10%] py-2 items-center">
|
||||||
<a href="/time">Zeitverwaltung</a>
|
<a href="/time">Zeitverwaltung</a>
|
||||||
<a href="/team">Abrechnung</a>
|
<a href="/team">Abrechnung</a>
|
||||||
<a href="/pdf">PDF</a>
|
|
||||||
if true {
|
if true {
|
||||||
|
<a href="/pdf">Monatsabrechnung</a>
|
||||||
<a href="/presence">Anwesenheit</a>
|
<a href="/presence">Anwesenheit</a>
|
||||||
}
|
}
|
||||||
<a href="/user/settings">Einstellungen</a>
|
<a href="/user/settings">Einstellungen</a>
|
||||||
|
|||||||
@@ -29,12 +29,12 @@ func headerComponent() templ.Component {
|
|||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
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> ")
|
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> ")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
if true {
|
if true {
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<a href=\"/presence\">Anwesenheit</a> ")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<a href=\"/pdf\">Monatsabrechnung</a> <a href=\"/presence\">Anwesenheit</a> ")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ templ PDFForm(teamMembers []models.User) {
|
|||||||
@headerComponent()
|
@headerComponent()
|
||||||
<form class="grid-main divide-y-1" action="pdf/generate" method="get">
|
<form class="grid-main divide-y-1" action="pdf/generate" method="get">
|
||||||
<div class="grid-cell col-span-full bg-neutral-300">
|
<div class="grid-cell col-span-full bg-neutral-300">
|
||||||
<h1 class="text-xl uppercase font-bold">PDF Abrechnung erstellen</h1>
|
<h1 class="text-xl uppercase font-bold">Monatsabrechnung erstellen</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-sub divide-x-1 responsive">
|
<div class="grid-sub divide-x-1 responsive">
|
||||||
<div class="grid-cell">Zeitraum wählen</div>
|
<div class="grid-cell">Zeitraum wählen</div>
|
||||||
@@ -46,9 +46,7 @@ templ PDFForm(teamMembers []models.User) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
templ CheckboxComponent(pNr int, label string) {
|
templ CheckboxComponent(pNr int, label string) {
|
||||||
{{
|
{{ id := fmt.Sprintf("pdf-%d", pNr) }}
|
||||||
id := fmt.Sprintf("pdf-%d", pNr)
|
|
||||||
}}
|
|
||||||
<div class="inline-flex items-center">
|
<div class="inline-flex items-center">
|
||||||
<label class="flex items-center cursor-pointer relative" for={ id }>
|
<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"/>
|
<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"/>
|
||||||
@@ -127,10 +125,10 @@ templ CheckboxComponent(pNr int, label string) {
|
|||||||
// }
|
// }
|
||||||
templ ColorDuration(d time.Duration, classes string) {
|
templ ColorDuration(d time.Duration, classes string) {
|
||||||
{{
|
{{
|
||||||
color := ""
|
color := ""
|
||||||
if d.Abs() < time.Minute {
|
if d.Abs() < time.Minute {
|
||||||
color = "text-neutral-300"
|
color = "text-neutral-300"
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
<p class={ color + " " + classes }>{ helper.FormatDurationFill(d, true) }</p>
|
<p class={ color + " " + classes }>{ helper.FormatDurationFill(d, true) }</p>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func PDFForm(teamMembers []models.User) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
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=\"")
|
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\">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=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@@ -138,7 +138,7 @@ func CheckboxComponent(pNr int, label string) templ.Component {
|
|||||||
var templ_7745c5c3_Var6 string
|
var templ_7745c5c3_Var6 string
|
||||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(id)
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(id)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 53, Col: 67}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 51, Col: 67}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -151,7 +151,7 @@ func CheckboxComponent(pNr int, label string) templ.Component {
|
|||||||
var templ_7745c5c3_Var7 string
|
var templ_7745c5c3_Var7 string
|
||||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(pNr)
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(pNr)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 54, Col: 57}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 52, Col: 57}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -164,7 +164,7 @@ func CheckboxComponent(pNr int, label string) templ.Component {
|
|||||||
var templ_7745c5c3_Var8 string
|
var templ_7745c5c3_Var8 string
|
||||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(id)
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(id)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 54, Col: 67}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 52, Col: 67}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -177,7 +177,7 @@ func CheckboxComponent(pNr int, label string) templ.Component {
|
|||||||
var templ_7745c5c3_Var9 string
|
var templ_7745c5c3_Var9 string
|
||||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(id)
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(id)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 60, Col: 81}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 58, Col: 81}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -190,7 +190,7 @@ func CheckboxComponent(pNr int, label string) templ.Component {
|
|||||||
var templ_7745c5c3_Var10 string
|
var templ_7745c5c3_Var10 string
|
||||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(label)
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(label)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 60, Col: 91}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 58, Col: 91}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -318,7 +318,7 @@ func ColorDuration(d time.Duration, classes string) templ.Component {
|
|||||||
var templ_7745c5c3_Var14 string
|
var templ_7745c5c3_Var14 string
|
||||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(helper.FormatDurationFill(d, true))
|
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(helper.FormatDurationFill(d, true))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 135, Col: 72}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/pdf.templ`, Line: 133, Col: 72}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ templ lineComponent() {
|
|||||||
templ changeButtonComponent(id string, workDay bool) {
|
templ changeButtonComponent(id string, workDay bool) {
|
||||||
<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) }>
|
<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 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">
|
<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="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>
|
<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>
|
||||||
@@ -98,15 +98,16 @@ templ absentInput(a *models.Absence) {
|
|||||||
<input type="hidden" name="aw_id" value={ a.CounterId }/>
|
<input type="hidden" name="aw_id" value={ a.CounterId }/>
|
||||||
}
|
}
|
||||||
|
|
||||||
templ newBookingComponent(d *models.WorkDay) {
|
//js function to select the right entry
|
||||||
<div class="new-booking-component hidden group-[.edit]:flex flex-row gap-2 items-center edit-box border-dashed">
|
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="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">
|
<div class="relative">
|
||||||
<select class="cursor-pointer appearance-none" name="check_in_out">
|
<select class="cursor-pointer appearance-none" name="check_in_out">
|
||||||
<option value="0" disabled>Kommen/Gehen</option>
|
<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="3">Kommen</option>
|
||||||
<option value="4" selected?={ len(d.Bookings) > 0 && d.Bookings[len(d.Bookings)-1].CheckInOut%2 == 1 }>Gehen</option>
|
<option value="4">Gehen</option>
|
||||||
</select>
|
</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">
|
<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>
|
<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>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ func changeButtonComponent(id string, workDay bool) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
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> ")
|
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\">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> ")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@@ -455,7 +455,8 @@ func absentInput(a *models.Absence) templ.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBookingComponent(d *models.WorkDay) templ.Component {
|
// js function to select the right entry
|
||||||
|
func newBookingComponent(d models.IWorkDay) templ.Component {
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
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
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
@@ -476,53 +477,46 @@ func newBookingComponent(d *models.WorkDay) templ.Component {
|
|||||||
templ_7745c5c3_Var24 = templ.NopComponent
|
templ_7745c5c3_Var24 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<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=\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<div class=\"new-booking-component hidden group-[.edit]:flex flex-row gap-2 items-center edit-box border-dashed\" id=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var25 string
|
var templ_7745c5c3_Var25 string
|
||||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(time.Now().Format("15:04"))
|
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs("nb" + d.Date().Format(time.DateOnly))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 103, Col: 72}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 103, Col: 155}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\" 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=\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\"><input name=\"timestamp\" type=\"time\" value=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var26 string
|
var templ_7745c5c3_Var26 string
|
||||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(d.Day.Format(time.DateOnly))
|
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(time.Now().Format("15:04"))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 104, Col: 70}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 104, Col: 72}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\"><div class=\"relative\"><select class=\"cursor-pointer appearance-none\" name=\"check_in_out\"><option value=\"0\" disabled>Kommen/Gehen</option> <option value=\"3\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\" 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 {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
if len(d.Bookings) > 0 && d.Bookings[len(d.Bookings)-1].CheckInOut%2 == 0 {
|
var templ_7745c5c3_Var27 string
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, " selected")
|
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(d.Date().Format(time.DateOnly))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 105, Col: 73}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, ">Kommen</option> <option value=\"4\"")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
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, "\"><div class=\"relative\"><select class=\"cursor-pointer appearance-none\" name=\"check_in_out\"><option value=\"0\" disabled>Kommen/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></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>")
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, " selected")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, ">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 {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@@ -546,74 +540,74 @@ func bookingComponent(booking models.Booking) templ.Component {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
ctx = templ.InitializeContext(ctx)
|
ctx = templ.InitializeContext(ctx)
|
||||||
templ_7745c5c3_Var27 := templ.GetChildren(ctx)
|
templ_7745c5c3_Var28 := templ.GetChildren(ctx)
|
||||||
if templ_7745c5c3_Var27 == nil {
|
if templ_7745c5c3_Var28 == nil {
|
||||||
templ_7745c5c3_Var27 = templ.NopComponent
|
templ_7745c5c3_Var28 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div><p class=\"text-neutral-500 edit-box\"><span class=\"text-black group-[.edit]:hidden inline\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<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_Var28 string
|
|
||||||
templ_7745c5c3_Var28, 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: 123, Col: 91}
|
|
||||||
}
|
|
||||||
_, 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, 38, "</span> <input disabled name=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var29 string
|
var templ_7745c5c3_Var29 string
|
||||||
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs("booking_" + strconv.Itoa(booking.CounterId))
|
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(booking.Timestamp.Format("15:04"))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 124, Col: 70}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 124, Col: 91}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "\" type=\"time\" value=\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</span> <input disabled name=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var30 string
|
var templ_7745c5c3_Var30 string
|
||||||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(booking.Timestamp.Format("15:04"))
|
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs("booking_" + strconv.Itoa(booking.CounterId))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 124, Col: 126}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 125, Col: 70}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "\" class=\"text-neutral-700 group-[.edit]:inline hidden bg-neutral-100 text-sm px-3 py-2 cursor-pointer\"> ")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "\" type=\"time\" value=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var31 string
|
var templ_7745c5c3_Var31 string
|
||||||
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(booking.GetBookingType())
|
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(booking.Timestamp.Format("15:04"))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 125, Col: 29}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 125, Col: 126}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</p>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "\" 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_Var32 string
|
||||||
|
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(booking.GetBookingType())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 126, Col: 29}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</p>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
if booking.IsSubmittedAndChecked() {
|
if booking.IsSubmittedAndChecked() {
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<p>submitted</p>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<p>submitted</p>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</div>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</div>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@@ -637,12 +631,12 @@ func LegendComponent() templ.Component {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
ctx = templ.InitializeContext(ctx)
|
ctx = templ.InitializeContext(ctx)
|
||||||
templ_7745c5c3_Var32 := templ.GetChildren(ctx)
|
templ_7745c5c3_Var33 := templ.GetChildren(ctx)
|
||||||
if templ_7745c5c3_Var32 == nil {
|
if templ_7745c5c3_Var33 == nil {
|
||||||
templ_7745c5c3_Var32 = templ.NopComponent
|
templ_7745c5c3_Var33 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "<div class=\"flex flex-row gap-4 md:mx-[10%] print:hidden\"><div class=\"flex flex-row items-center gap-2\"><div class=\"rounded-full size-4 bg-red-600\"></div><span>Fehler</span></div><div class=\"flex flex-row items-center gap-2\"><div class=\"rounded-full size-4 bg-orange-500\"></div><span>Arbeitszeit unter regulär</span></div><div class=\"flex flex-row items-center gap-2\"><div class=\"rounded-full size-4 bg-accent\"></div><span>Arbeitszeit vollständig</span></div><div class=\"flex flex-row items-center gap-2\"><div class=\"rounded-full size-4 bg-purple-600\"></div><span>Überstunden</span></div><div class=\"flex flex-row items-center gap-2\"><div class=\"rounded-full size-4 bg-neutral-400\"></div><span>Keine Buchungen</span></div></div>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<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 {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,58 +29,60 @@ templ inputForm() {
|
|||||||
urlParams := ctx.Value("urlParams").(url.Values)
|
urlParams := ctx.Value("urlParams").(url.Values)
|
||||||
user := ctx.Value("user").(models.User)
|
user := ctx.Value("user").(models.User)
|
||||||
}}
|
}}
|
||||||
<div class="grid-sub divide-x-1 bg-neutral-300 responsive">
|
<div class="sticky top-0 z-100 grid-sub divide-y-1">
|
||||||
<div class="grid-cell md:col-span-1 max-md:grid grid-cols-2">
|
<div class="grid-sub divide-x-1 bg-neutral-300 responsive">
|
||||||
<p class="font-bold uppercase">{ user.Vorname + " " + user.Name }</p>
|
<div class="grid-cell md:col-span-1 max-md:grid grid-cols-2">
|
||||||
<div class="justify-self-end">
|
<p class="font-bold uppercase">{ user.Vorname + " " + user.Name }</p>
|
||||||
<p class="text-sm">Überstunden</p>
|
<div class="justify-self-end">
|
||||||
<p class="text-accent">{ user.Overtime }</p>
|
<p class="text-sm">Überstunden</p>
|
||||||
|
<p class="text-accent">{ user.Overtime }</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<form id="timeRangeForm" method="GET" class="grid-cell flex flex-row md:col-span-3 gap-2 ">
|
||||||
|
@lineComponent()
|
||||||
|
<div class="flex flex-col gap-2 justify-between grow-1">
|
||||||
|
<input type="date" value={ urlParams.Get("time_from") } name="time_from" class="btn bg-neutral-100" placeholder="Zeitraum von..."/>
|
||||||
|
<input type="date" value={ urlParams.Get("time_to") } 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>
|
||||||
</div>
|
</div>
|
||||||
<form id="timeRangeForm" method="GET" class="grid-cell flex flex-row md:col-span-3 gap-2 ">
|
<form id="absence_form" method="POST" action="/absence" class="grid-sub responsive scroll-m-2 bg-neutral-300 hidden">
|
||||||
@lineComponent()
|
<input type="hidden" name="aw_id" value=""/>
|
||||||
<div class="flex flex-col gap-2 justify-between grow-1">
|
<div class="grid-cell border-r-1"><p class="font-bold uppercase">Abwesenheit</p></div>
|
||||||
<input type="date" value={ urlParams.Get("time_from") } name="time_from" class="btn bg-neutral-100" placeholder="Zeitraum von..."/>
|
<div class="grid-cell">
|
||||||
<input type="date" value={ urlParams.Get("time_to") } name="time_to" class="btn bg-neutral-100" placeholder="Zeitraum bis..."/>
|
<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">
|
||||||
|
for _, absence := range models.GetAbsenceTypesCached() {
|
||||||
|
<option value={ strconv.Itoa(int(absence.Id)) }>{ absence.Name }</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 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>
|
</div>
|
||||||
</form>
|
</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>
|
</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">
|
|
||||||
for _, absence := range models.GetAbsenceTypesCached() {
|
|
||||||
<option value={ strconv.Itoa(int(absence.Id)) }>{ absence.Name }</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 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>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templ defaultDayComponent(day models.IWorkDay) {
|
templ defaultDayComponent(day models.IWorkDay) {
|
||||||
@@ -113,7 +115,7 @@ templ defaultDayComponent(day models.IWorkDay) {
|
|||||||
if pause > 0 {
|
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>
|
<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 && day.IsEmpty() == false {
|
if !day.IsEmpty() && overtime != 0 {
|
||||||
<p class="text-neutral-500 flex flex-row items-center">
|
<p class="text-neutral-500 flex flex-row items-center">
|
||||||
<span class="icon-[material-symbols-light--more-time]"></span>
|
<span class="icon-[material-symbols-light--more-time]"></span>
|
||||||
{ helper.FormatDuration(overtime) }
|
{ helper.FormatDuration(overtime) }
|
||||||
@@ -125,14 +127,19 @@ templ defaultDayComponent(day models.IWorkDay) {
|
|||||||
</div>
|
</div>
|
||||||
<div class="all-booking-component grid-cell flex flex-row md:col-span-3 col-span-2 gap-2 w-full">
|
<div class="all-booking-component grid-cell flex flex-row md:col-span-3 col-span-2 gap-2 w-full">
|
||||||
@lineComponent()
|
@lineComponent()
|
||||||
<form id={ "time-" + day.Date().Format(time.DateOnly) } class={ "flex flex-col gap-2 w-full", justify } method="post">
|
<form id={ "time-" + day.Date().Format(time.DateOnly) } class={ "bookings flex flex-col gap-2 w-full", justify } method="post">
|
||||||
@newAbsenceComponent()
|
if (day.GetDayProgress(user) < 100 || day.IsWorkDay()) {
|
||||||
@timeDayTypeSwitch(day, false)
|
@newAbsenceComponent()
|
||||||
|
@timeDayTypeSwitch(day, true)
|
||||||
|
@newBookingComponent(day)
|
||||||
|
} else {
|
||||||
|
@timeDayTypeSwitch(day, true)
|
||||||
|
}
|
||||||
<input type="hidden" name="action" value="change"/> <!-- default action value for ändern button -->
|
<input type="hidden" name="action" value="change"/> <!-- default action value for ändern button -->
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-cell flex flex-row gap-2 items-end">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -157,14 +164,15 @@ templ timeDayTypeSwitch(day models.IWorkDay, fromCompound bool) {
|
|||||||
templ workdayComponent(workDay *models.WorkDay) {
|
templ workdayComponent(workDay *models.WorkDay) {
|
||||||
if len(workDay.Bookings) < 1 {
|
if len(workDay.Bookings) < 1 {
|
||||||
<p class="text group-[.edit]:hidden">Keine Buchung gefunden. Bitte Arbeitsstunden oder Grund der Abwesenheit eingeben!</p>
|
<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 }/>
|
||||||
}
|
}
|
||||||
if workDay.IsKurzArbeit() && len(workDay.Bookings) > 0 {
|
|
||||||
@absenceComponent(workDay.GetKurzArbeit(), true)
|
|
||||||
}
|
|
||||||
for _, booking := range workDay.Bookings {
|
|
||||||
@bookingComponent(booking)
|
|
||||||
}
|
|
||||||
@newBookingComponent(workDay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templ holidayComponent(d models.IWorkDay) {
|
templ holidayComponent(d models.IWorkDay) {
|
||||||
|
|||||||
@@ -105,14 +105,14 @@ func inputForm() templ.Component {
|
|||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
urlParams := ctx.Value("urlParams").(url.Values)
|
urlParams := ctx.Value("urlParams").(url.Values)
|
||||||
user := ctx.Value("user").(models.User)
|
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\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<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\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var3 string
|
var templ_7745c5c3_Var3 string
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(user.Vorname + " " + user.Name)
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(user.Vorname + " " + user.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 34, Col: 66}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 35, Col: 67}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -125,7 +125,7 @@ func inputForm() templ.Component {
|
|||||||
var templ_7745c5c3_Var4 string
|
var templ_7745c5c3_Var4 string
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(user.Overtime)
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(user.Overtime)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 37, Col: 42}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 38, Col: 43}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -146,7 +146,7 @@ func inputForm() templ.Component {
|
|||||||
var templ_7745c5c3_Var5 string
|
var templ_7745c5c3_Var5 string
|
||||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(urlParams.Get("time_from"))
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(urlParams.Get("time_from"))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 43, Col: 57}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 44, Col: 58}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -159,7 +159,7 @@ func inputForm() templ.Component {
|
|||||||
var templ_7745c5c3_Var6 string
|
var templ_7745c5c3_Var6 string
|
||||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(urlParams.Get("time_to"))
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(urlParams.Get("time_to"))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 44, Col: 55}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 45, Col: 56}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -177,7 +177,7 @@ func inputForm() templ.Component {
|
|||||||
var templ_7745c5c3_Var7 string
|
var templ_7745c5c3_Var7 string
|
||||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(int(absence.Id)))
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(int(absence.Id)))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 61, Col: 51}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 62, Col: 52}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -190,7 +190,7 @@ func inputForm() templ.Component {
|
|||||||
var templ_7745c5c3_Var8 string
|
var templ_7745c5c3_Var8 string
|
||||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(absence.Name)
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(absence.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 61, Col: 68}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 62, Col: 69}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -201,7 +201,7 @@ func inputForm() templ.Component {
|
|||||||
return templ_7745c5c3_Err
|
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>")
|
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></div>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@@ -268,7 +268,7 @@ func defaultDayComponent(day models.IWorkDay) templ.Component {
|
|||||||
var templ_7745c5c3_Var12 string
|
var templ_7745c5c3_Var12 string
|
||||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(helper.FormatGermanDayOfWeek(day.Date()))
|
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(helper.FormatGermanDayOfWeek(day.Date()))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 99, Col: 98}
|
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))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -281,7 +281,7 @@ func defaultDayComponent(day models.IWorkDay) templ.Component {
|
|||||||
var templ_7745c5c3_Var13 string
|
var templ_7745c5c3_Var13 string
|
||||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(day.Date().Format("02.01.2006"))
|
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(day.Date().Format("02.01.2006"))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 99, Col: 142}
|
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))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -308,7 +308,7 @@ func defaultDayComponent(day models.IWorkDay) templ.Component {
|
|||||||
var templ_7745c5c3_Var14 string
|
var templ_7745c5c3_Var14 string
|
||||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(helper.FormatDuration(work))
|
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(helper.FormatDuration(work))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 111, Col: 155}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 113, Col: 155}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -331,7 +331,7 @@ func defaultDayComponent(day models.IWorkDay) templ.Component {
|
|||||||
var templ_7745c5c3_Var15 string
|
var templ_7745c5c3_Var15 string
|
||||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(helper.FormatDuration(pause))
|
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(helper.FormatDuration(pause))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 114, Col: 173}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 116, Col: 173}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -346,7 +346,7 @@ func defaultDayComponent(day models.IWorkDay) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
if overtime != 0 && day.IsEmpty() == false {
|
if !day.IsEmpty() && overtime != 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> ")
|
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 {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
@@ -354,7 +354,7 @@ func defaultDayComponent(day models.IWorkDay) templ.Component {
|
|||||||
var templ_7745c5c3_Var16 string
|
var templ_7745c5c3_Var16 string
|
||||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(helper.FormatDuration(overtime))
|
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(helper.FormatDuration(overtime))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 119, Col: 41}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 121, Col: 41}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -375,7 +375,7 @@ func defaultDayComponent(day models.IWorkDay) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var17 = []any{"flex flex-col gap-2 w-full", justify}
|
var templ_7745c5c3_Var17 = []any{"bookings flex flex-col gap-2 w-full", justify}
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var17...)
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var17...)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
@@ -387,7 +387,7 @@ func defaultDayComponent(day models.IWorkDay) templ.Component {
|
|||||||
var templ_7745c5c3_Var18 string
|
var templ_7745c5c3_Var18 string
|
||||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs("time-" + day.Date().Format(time.DateOnly))
|
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs("time-" + day.Date().Format(time.DateOnly))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 128, Col: 56}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 130, Col: 56}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -410,23 +410,42 @@ func defaultDayComponent(day models.IWorkDay) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = newAbsenceComponent().Render(ctx, templ_7745c5c3_Buffer)
|
if day.GetDayProgress(user) < 100 || day.IsWorkDay() {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = timeDayTypeSwitch(day, true).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, " ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = newBookingComponent(day).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = timeDayTypeSwitch(day, true).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<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 {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = timeDayTypeSwitch(day, false).Render(ctx, templ_7745c5c3_Buffer)
|
templ_7745c5c3_Err = changeButtonComponent("time-"+day.Date().Format(time.DateOnly), true).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<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\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</div></div>")
|
||||||
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, 34, "</div></div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@@ -476,20 +495,20 @@ func timeDayTypeSwitch(day models.IWorkDay, fromCompound bool) templ.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<p>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<p>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var21 string
|
var templ_7745c5c3_Var21 string
|
||||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(day.ToString())
|
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(day.ToString())
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 153, Col: 22}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 160, Col: 22}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</p>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</p>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@@ -520,26 +539,40 @@ func workdayComponent(workDay *models.WorkDay) templ.Component {
|
|||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
if len(workDay.Bookings) < 1 {
|
if len(workDay.Bookings) < 1 {
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<p class=\"text group-[.edit]:hidden\">Keine Buchung gefunden. Bitte Arbeitsstunden oder Grund der Abwesenheit eingeben!</p>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<p class=\"text group-[.edit]:hidden\">Keine Buchung gefunden. Bitte Arbeitsstunden oder Grund der Abwesenheit eingeben!</p>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
if workDay.IsKurzArbeit() && len(workDay.Bookings) > 0 {
|
if workDay.IsKurzArbeit() && len(workDay.Bookings) > 0 {
|
||||||
templ_7745c5c3_Err = absenceComponent(workDay.GetKurzArbeit(), true).Render(ctx, templ_7745c5c3_Buffer)
|
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, 40, " <input type=\"hidden\" name=\"select_kommen\" value=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
var templ_7745c5c3_Var23 string
|
||||||
for _, booking := range workDay.Bookings {
|
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(len(workDay.Bookings) > 0 && workDay.Bookings[len(workDay.Bookings)-1].CheckInOut%2 == 0)
|
||||||
templ_7745c5c3_Err = bookingComponent(booking).Render(ctx, templ_7745c5c3_Buffer)
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 174, Col: 140}
|
||||||
|
}
|
||||||
|
_, 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, 41, "\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = newBookingComponent(workDay).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -561,25 +594,25 @@ func holidayComponent(d models.IWorkDay) templ.Component {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
ctx = templ.InitializeContext(ctx)
|
ctx = templ.InitializeContext(ctx)
|
||||||
templ_7745c5c3_Var23 := templ.GetChildren(ctx)
|
templ_7745c5c3_Var24 := templ.GetChildren(ctx)
|
||||||
if templ_7745c5c3_Var23 == nil {
|
if templ_7745c5c3_Var24 == nil {
|
||||||
templ_7745c5c3_Var23 = templ.NopComponent
|
templ_7745c5c3_Var24 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<p>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<p>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var24 string
|
var templ_7745c5c3_Var25 string
|
||||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(d.ToString())
|
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(d.ToString())
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 171, Col: 18}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timePage.templ`, Line: 179, Col: 18}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</p>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</p>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|||||||
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
|
||||||
55
DB/initdb/01_create_user.sh
Executable file
55
DB/initdb/01_create_user.sh
Executable file
@@ -0,0 +1,55 @@
|
|||||||
|
#!/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;
|
||||||
|
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', 100), (2, 'Krank', 100), (3, 'Kurzarbeit', -1), (4, 'Urlaub untertags', 50);
|
|
||||||
@@ -1,21 +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';
|
|
||||||
EOSQL
|
|
||||||
|
|
||||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
|
||||||
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, 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 root --dbname arbeitszeitmessung
|
|
||||||
@@ -3,7 +3,7 @@ services:
|
|||||||
db:
|
db:
|
||||||
volumes:
|
volumes:
|
||||||
- ${POSTGRES_PATH}:/var/lib/postgresql/data
|
- ${POSTGRES_PATH}:/var/lib/postgresql/data
|
||||||
# - ${POSTGRES_PATH}/initdb:/docker-entrypoint-initdb.d
|
- ${POSTGRES_PATH}/initdb:/docker-entrypoint-initdb.d
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
|
|
||||||
|
|||||||
@@ -25,12 +25,11 @@ services:
|
|||||||
- ${WEB_PORT}:8080
|
- ${WEB_PORT}:8080
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
- document-creator
|
|
||||||
volumes:
|
volumes:
|
||||||
- ../logs:/app/Backend/logs
|
- ${LOG_PATH}:/app/logs
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
document-creator:
|
# document-creator:
|
||||||
image: git.letsstein.de/tom/arbeitszeitmessung-doc-creator
|
# image: git.letsstein.de/tom/arbeitszeitmessung-doc-creator
|
||||||
container_name: ${TYPST_CONTAINER}
|
# container_name: ${TYPST_CONTAINER}
|
||||||
restart: unless-stopped
|
# restart: unless-stopped
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ POSTGRES_USER=root # Postgres ADMIN Nutzername
|
|||||||
POSTGRES_PASSWORD=very_secure # Postgres ADMIN Passwort
|
POSTGRES_PASSWORD=very_secure # Postgres ADMIN Passwort
|
||||||
POSTGRES_API_USER=api_nutzer # Postgres API Nutzername (für Arbeitszeitmessung)
|
POSTGRES_API_USER=api_nutzer # Postgres API Nutzername (für Arbeitszeitmessung)
|
||||||
POSTGRES_API_PASS=password # Postgres API Passwort (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_DB=arbeitszeitmessung # Postgres Datenbank Name
|
||||||
POSTGRES_PORT=127.0.0.1:5432 # Postgres Port will not be exposed by default. regex:^[0-9]{1,5}$
|
POSTGRES_PORT=127.0.0.1:5432 # Postgres Port normalerweise nicht freigegeben. regex:^[0-9]{1,5}$
|
||||||
|
MIGRATIONS_PATH=__ROOT__/migrations # Pfad zu DB migrations (wenn nicht verändert wurde, bei default bleiben)
|
||||||
TZ=Europe/Berlin # Zeitzone
|
TZ=Europe/Berlin # Zeitzone
|
||||||
API_TOKEN=dont_access # API Token für ESP Endpoints
|
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}$
|
WEB_PORT=8000 # Port unter welchem Webserver erreichbar ist. regex:^[0-9]{1,5}$
|
||||||
TYPST_CONTAINER=arbeitszeitmessung-doc-creator # Name of the pdf compiler container
|
LOG_PATH=__ROOT__/logs # Pfad für Audit Logs
|
||||||
|
LOG_LEVEL=warn # Welche Log-Nachrichten werden in der Konsole erscheinen
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
FROM ghcr.io/typst/typst:0.14.0
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
COPY ./templates /app/templates
|
|
||||||
COPY ./static /app/static
|
|
||||||
|
|
||||||
ENTRYPOINT ["sh", "-c", "while true; do sleep 3600; done"]
|
|
||||||
82
install.sh
82
install.sh
@@ -4,6 +4,10 @@ set -e
|
|||||||
envFile=Docker/.env
|
envFile=Docker/.env
|
||||||
envExample=Docker/env.example
|
envExample=Docker/env.example
|
||||||
|
|
||||||
|
autoBackupScript=Cron/autoBackup.sh
|
||||||
|
autoHolidaysScript=Cron/autoHolidays.sh
|
||||||
|
autoLogoutScript=Cron/autoLogout.sh
|
||||||
|
|
||||||
echo "Checking Docker installation..."
|
echo "Checking Docker installation..."
|
||||||
if ! command -v docker >/dev/null 2>&1; then
|
if ! command -v docker >/dev/null 2>&1; then
|
||||||
echo "Docker not found. Install Docker? [y/N]"
|
echo "Docker not found. Install Docker? [y/N]"
|
||||||
@@ -18,12 +22,16 @@ else
|
|||||||
echo "Docker is already installed."
|
echo "Docker is already installed."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
echo "Checking Docker Compose..."
|
echo "Checking Docker Compose..."
|
||||||
if ! docker compose version >/dev/null 2>&1; then
|
if ! docker compose version >/dev/null 2>&1; then
|
||||||
echo "Docker Compose plugin missing. You may need to update Docker."
|
echo "Docker Compose plugin missing. You may need to update Docker."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
echo "Preparing .env file..."
|
echo "Preparing .env file..."
|
||||||
if [ ! -f $envFile ]; then
|
if [ ! -f $envFile ]; then
|
||||||
if [ -f $envExample ]; then
|
if [ -f $envExample ]; then
|
||||||
@@ -44,6 +52,9 @@ if [ ! -f $envFile ]; then
|
|||||||
raw_val=$(printf "%s" "$rest" | sed 's/ *#.*//')
|
raw_val=$(printf "%s" "$rest" | sed 's/ *#.*//')
|
||||||
default_value=$(printf "%s" "$raw_val" | sed 's/"//g')
|
default_value=$(printf "%s" "$raw_val" | sed 's/"//g')
|
||||||
|
|
||||||
|
# Replace __ROOT__ with script pwd
|
||||||
|
default_value="${default_value/__ROOT__/$(pwd)}"
|
||||||
|
|
||||||
regex=""
|
regex=""
|
||||||
if [[ "$comment" =~ regex:(.*)$ ]]; then
|
if [[ "$comment" =~ regex:(.*)$ ]]; then
|
||||||
regex="${BASH_REMATCH[1]}"
|
regex="${BASH_REMATCH[1]}"
|
||||||
@@ -96,13 +107,80 @@ else
|
|||||||
echo "Using existing .env. (found at $envFile)"
|
echo "Using existing .env. (found at $envFile)"
|
||||||
fi
|
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]"
|
echo "Start containers with docker compose up -d? [y/N]"
|
||||||
read -r start_containers
|
read -r start_containersmkdi
|
||||||
if [[ "$start_containers" =~ ^[Yy]$ ]]; then
|
if [[ "$start_containers" =~ ^[Yy]$ ]]; then
|
||||||
|
|
||||||
cd Docker
|
cd Docker
|
||||||
mkdir ../logs
|
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
echo "Containers started."
|
echo "Containers started."
|
||||||
else
|
else
|
||||||
echo "You can start them manually with: docker compose up -d"
|
echo "You can start them manually with: docker compose up -d"
|
||||||
fi
|
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."
|
||||||
|
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
|
||||||
|
|||||||
@@ -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 "abwesenheit" table
|
||||||
CREATE TABLE "abwesenheit" (
|
CREATE TABLE "abwesenheit" (
|
||||||
"counter_id" bigserial NOT NULL,
|
"counter_id" bigserial NOT NULL,
|
||||||
@@ -6,6 +14,7 @@ CREATE TABLE "abwesenheit" (
|
|||||||
"datum" timestamptz NULL DEFAULT (now())::date,
|
"datum" timestamptz NULL DEFAULT (now())::date,
|
||||||
PRIMARY KEY ("counter_id")
|
PRIMARY KEY ("counter_id")
|
||||||
);
|
);
|
||||||
|
|
||||||
-- create "anwesenheit" table
|
-- create "anwesenheit" table
|
||||||
CREATE TABLE "anwesenheit" (
|
CREATE TABLE "anwesenheit" (
|
||||||
"counter_id" bigserial NOT NULL,
|
"counter_id" bigserial NOT NULL,
|
||||||
@@ -55,3 +64,6 @@ CREATE TABLE "wochen_report" (
|
|||||||
PRIMARY KEY ("id"),
|
PRIMARY KEY ("id"),
|
||||||
CONSTRAINT "wochen_report_personal_nummer_woche_start_key" UNIQUE ("personal_nummer", "woche_start")
|
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 FUNCTION update_zuletzt_geandert;
|
||||||
|
|
||||||
DROP TRIGGER IF EXISTS pass_hash_update ON user_password;
|
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();
|
EXECUTE FUNCTION update_zuletzt_geandert();
|
||||||
|
|
||||||
-- Adds crypto extension
|
-- Adds crypto extension
|
||||||
|
|
||||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
|
||||||
|
|||||||
@@ -11,3 +11,5 @@ CREATE TABLE "s_feiertage" (
|
|||||||
);
|
);
|
||||||
-- create index "feiertage_unique_pro_jahr" to table: "s_feiertage"
|
-- 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");
|
CREATE UNIQUE INDEX "feiertage_unique_pro_jahr" ON "s_feiertage" ((EXTRACT(year FROM datum)), "name");
|
||||||
|
|
||||||
|
GRANT INSERT, UPDATE ON s_feiertage TO app_base;
|
||||||
|
|||||||
Reference in New Issue
Block a user