37 Commits

Author SHA1 Message Date
55bdca4238 Merge pull request 'add auto-update; Progress at #503' (#1020) from auto-update into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 14m43s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 23m14s
Reviewed-on: #1020
2025-05-14 08:31:49 +02:00
bf7dab235c add auto-update; Progress at #503
All checks were successful
CI/CD Pipeline / test (push) Successful in 25m21s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2025-05-14 08:31:04 +02:00
bb3e8dadb7 Merge pull request 'not so much clutter when addding notes' (#1018) from nicer-notes into main
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #1018
2025-05-14 08:15:57 +02:00
ed6d05eb9e not so much clutter when addding notes
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2025-05-14 08:04:56 +02:00
edcdc74c1c Merge pull request 'start replacing activitybuilder' (#1015) from acitvities-adaption into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 17m32s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 8m54s
Reviewed-on: #1015
2025-05-12 23:09:46 +02:00
3ab1dbd1f1 start replacing activitybuilder
All checks were successful
CI/CD Pipeline / test (push) Successful in 18m49s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2025-05-12 23:08:45 +02:00
6e9367fa07 Merge pull request 'prepare to remove old log, in favor of activities' (#1013) from acitvities-adaption into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #1013
2025-05-12 22:32:19 +02:00
e4a8caf632 prepare to remove old log, in favor of activities
Some checks failed
CI/CD Pipeline / test (push) Successful in 17m41s
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2025-05-12 22:29:34 +02:00
cd39f1a694 Merge pull request 'clean duplicate speicifications' (#1009) from clean-backend-code into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 17m37s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 9m26s
Reviewed-on: #1009
2025-05-11 20:56:11 +02:00
396fc8e659 clean duplicate speicifications
All checks were successful
CI/CD Pipeline / test (push) Successful in 18m54s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2025-05-11 20:52:13 +02:00
f86d2f6307 Merge pull request 'fix notification' (#1007) from fix-notification into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 16m23s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 8m20s
Reviewed-on: #1007
2025-05-10 15:48:39 +02:00
1ecde79593 fix notification
All checks were successful
CI/CD Pipeline / test (push) Successful in 17m3s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2025-05-10 15:47:25 +02:00
e8b8ba393f Merge pull request 'inform new cox about things' (#1004) from informat-new-cox into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 16m8s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 8m24s
Reviewed-on: #1004
2025-05-09 13:06:29 +02:00
3801c7ce8c inform new cox about things
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2025-05-09 13:04:51 +02:00
816257d4be Merge pull request 'remove notes from users (switched to activity)' (#1002) from remove-notes into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 18m20s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 9m39s
Reviewed-on: #1002
2025-05-09 08:57:45 +02:00
23399b7757 remove notes from users (switched to activity)
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2025-05-09 08:44:17 +02:00
0c5812f725 Merge pull request 'show all activities for admin; Fixes #984' (#1000) from show-all-activities into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #1000
2025-05-09 08:32:33 +02:00
d88a35bb82 show all activities for admin; Fixes #984
Some checks failed
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2025-05-09 08:30:09 +02:00
52abcbb3fb Merge pull request 'show proper text for all who have acheived diamond equatorprice' (#998) from fix-diamond-text into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 16m51s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 20m36s
Reviewed-on: #998
2025-05-07 13:44:47 +02:00
29777cdc36 show proper text for all who have acheived diamond equatorprice
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2025-05-07 13:43:57 +02:00
22b9a2e324 Merge pull request '[TASK] add search bar and improve ux' (#996) from improve-role-view into main
Some checks failed
CI/CD Pipeline / test (push) Successful in 15m33s
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #996
2025-05-07 13:06:39 +02:00
a97d515f03 lint
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2025-05-07 13:06:02 +02:00
Marie Birner
72fc3ed91e [TASK] add search bar and improve ux
All checks were successful
CI/CD Pipeline / test (push) Successful in 16m18s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2025-05-07 11:52:58 +02:00
b079eafc3d Merge pull request 'update deps' (#994) from upd into main
Some checks failed
CI/CD Pipeline / test (push) Failing after 24m48s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #994
2025-05-06 22:51:56 +02:00
6e1bfe8635 update deps
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2025-05-06 22:49:51 +02:00
ce28f93d65 Merge pull request 'single-user-edit-page' (#992) from single-user-edit-page into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 14m40s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 13m21s
Reviewed-on: #992
2025-05-06 13:43:23 +02:00
bf3a4c686a Merge branch 'single-user-edit-page' of ssh://git.hofer.link:2222/Ruderverein-Donau-Linz/rowt into single-user-edit-page
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2025-05-06 13:40:39 +02:00
5fb9e0fbba format 2025-05-06 13:40:33 +02:00
Marie Birner
f58e7d1307 Merge commit '374fed9e3bdcee30a3f8315d1f0c392204a885df' into single-user-edit-page
All checks were successful
CI/CD Pipeline / test (push) Successful in 14m45s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2025-05-06 09:57:02 +02:00
374fed9e3b fix tests by clicking on boat name instead of cox
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2025-05-06 08:50:45 +02:00
Marie Birner
b9f2382cba [BUGFIX] buttons mobile margin 2025-05-06 07:45:32 +02:00
Marie Birner
aab3a15488 [BUGFIX] back link margin bottom mobile 2025-05-06 07:44:12 +02:00
83b93fba09 Merge branch 'single-user-edit-page' of ssh://git.hofer.link:2222/Ruderverein-Donau-Linz/rowt into single-user-edit-page
Some checks failed
CI/CD Pipeline / test (push) Failing after 22m20s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2025-05-05 22:24:32 +02:00
3b5ff70d1d don't clutter acitvities toooooo much 2025-05-05 22:23:28 +02:00
2af9ac20b1 Merge pull request '[TASK] add icons to add new user and improve ui in setting a fixed height in activity log' (#989) from improve-user-view into single-user-edit-page
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #989
2025-05-05 22:16:11 +02:00
Marie Birner
5331ac71fa [TASK] add icons to add new user and improve ui in setting a fixed height in activity log
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2025-05-05 22:12:19 +02:00
6098aedb74 fix tests?
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2025-05-05 22:11:56 +02:00
25 changed files with 670 additions and 610 deletions

View File

@@ -0,0 +1,34 @@
name: Update Cargo Dependencies
on:
schedule:
- cron: '0 2 * * 5' # Run weekly on Friday at 2am
workflow_dispatch: # Allow manual triggering
jobs:
update-dependencies:
runs-on: ubuntu-latest
container: git.hofer.link/philipp/ci-images:rust-latest
steps:
- uses: actions/checkout@v3
- name: Update dependencies
run: |
cargo upgrade
cargo update
- name: Create Pull Request
uses: https://git.hofer.link/philipp/create-pull-request@18ef1fdad70eec569ab10292c1fa79c1b5296370
with:
token: ${{ secrets.GITEATOKEN }}
commit-message: Update Cargo dependencies
title: Update Cargo dependencies
body: |
This PR updates Cargo dependencies to their latest versions.
@philipp
- Run `cargo upgrade` to update version requirements in Cargo.toml
- Run `cargo update` to update Cargo.lock
branch: update-cargo-dependencies
delete-branch: true

210
Cargo.lock generated
View File

@@ -221,9 +221,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]] [[package]]
name = "backtrace" name = "backtrace"
version = "0.3.74" version = "0.3.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
dependencies = [ dependencies = [
"addr2line", "addr2line",
"cfg-if", "cfg-if",
@@ -303,9 +303,9 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.22.0" version = "1.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@@ -321,9 +321,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.19" version = "1.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0"
dependencies = [ dependencies = [
"shlex", "shlex",
] ]
@@ -336,9 +336,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.40" version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [ dependencies = [
"android-tzdata", "android-tzdata",
"iana-time-zone", "iana-time-zone",
@@ -615,9 +615,9 @@ dependencies = [
[[package]] [[package]]
name = "der" name = "der"
version = "0.7.9" version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [ dependencies = [
"const-oid", "const-oid",
"pem-rfc7468", "pem-rfc7468",
@@ -635,9 +635,9 @@ dependencies = [
[[package]] [[package]]
name = "deunicode" name = "deunicode"
version = "1.6.1" version = "1.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc55fe0d1f6c107595572ec8b107c0999bb1a2e0b75e37429a4fb0d6474a0e7d" checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04"
[[package]] [[package]]
name = "devise" name = "devise"
@@ -1028,9 +1028,9 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.15" version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
@@ -1126,9 +1126,9 @@ dependencies = [
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.2" version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
dependencies = [ dependencies = [
"allocator-api2", "allocator-api2",
"equivalent", "equivalent",
@@ -1141,7 +1141,7 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
dependencies = [ dependencies = [
"hashbrown 0.15.2", "hashbrown 0.15.3",
] ]
[[package]] [[package]]
@@ -1158,9 +1158,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.5.0" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
[[package]] [[package]]
name = "hex" name = "hex"
@@ -1476,7 +1476,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.15.2", "hashbrown 0.15.3",
"serde", "serde",
] ]
@@ -1521,7 +1521,7 @@ version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
dependencies = [ dependencies = [
"hermit-abi 0.5.0", "hermit-abi 0.5.1",
"libc", "libc",
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
@@ -1549,9 +1549,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]] [[package]]
name = "jiff" name = "jiff"
version = "0.2.8" version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ad87c89110f55e4cd4dc2893a9790820206729eaf221555f742d540b0724a0" checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806"
dependencies = [ dependencies = [
"jiff-static", "jiff-static",
"log", "log",
@@ -1562,9 +1562,9 @@ dependencies = [
[[package]] [[package]]
name = "jiff-static" name = "jiff-static"
version = "0.2.8" version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d076d5b64a7e2fe6f0743f02c43ca4a6725c0f904203bfe276a5b3e793103605" checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1594,9 +1594,9 @@ dependencies = [
[[package]] [[package]]
name = "kqueue" name = "kqueue"
version = "1.0.8" version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a"
dependencies = [ dependencies = [
"kqueue-sys", "kqueue-sys",
"libc", "libc",
@@ -1654,9 +1654,9 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]] [[package]]
name = "libm" name = "libm"
version = "0.2.11" version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" checksum = "a25169bd5913a4b437588a7e3d127cd6e90127b60e0ffbd834a38f1599e016b8"
[[package]] [[package]]
name = "libredox" name = "libredox"
@@ -2002,9 +2002,9 @@ dependencies = [
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.107" version = "0.9.108"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@@ -2267,14 +2267,14 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [ dependencies = [
"zerocopy 0.8.24", "zerocopy 0.8.25",
] ]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.94" version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -2294,9 +2294,9 @@ dependencies = [
[[package]] [[package]]
name = "psm" name = "psm"
version = "0.1.25" version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f58e5423e24c18cc840e1c98370b3993c6649cd1678b4d24318bcf0a083cbe88" checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f"
dependencies = [ dependencies = [
"cc", "cc",
] ]
@@ -2355,14 +2355,14 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [ dependencies = [
"getrandom 0.2.15", "getrandom 0.2.16",
] ]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.11" version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
dependencies = [ dependencies = [
"bitflags 2.9.0", "bitflags 2.9.0",
] ]
@@ -2439,7 +2439,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [ dependencies = [
"cc", "cc",
"cfg-if", "cfg-if",
"getrandom 0.2.15", "getrandom 0.2.16",
"libc", "libc",
"untrusted", "untrusted",
"windows-sys 0.52.0", "windows-sys 0.52.0",
@@ -2594,9 +2594,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "1.0.5" version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
dependencies = [ dependencies = [
"bitflags 2.9.0", "bitflags 2.9.0",
"errno", "errno",
@@ -2607,9 +2607,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.23.26" version = "0.23.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321"
dependencies = [ dependencies = [
"log", "log",
"once_cell", "once_cell",
@@ -2637,9 +2637,9 @@ checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.103.1" version = "0.103.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" checksum = "7149975849f1abb3832b246010ef62ccc80d3a76169517ada7188252b9cfb437"
dependencies = [ dependencies = [
"ring", "ring",
"rustls-pki-types", "rustls-pki-types",
@@ -2777,9 +2777,9 @@ dependencies = [
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.10.8" version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures",
@@ -2803,9 +2803,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.2" version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@@ -2885,9 +2885,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx" name = "sqlx"
version = "0.8.4" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14e22987355fbf8cfb813a0cf8cd97b1b4ec834b94dbd759a9e8679d41fabe83" checksum = "f3c3a85280daca669cfd3bcb68a337882a8bc57ec882f72c5d13a430613a738e"
dependencies = [ dependencies = [
"sqlx-core", "sqlx-core",
"sqlx-macros", "sqlx-macros",
@@ -2898,9 +2898,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-core" name = "sqlx-core"
version = "0.8.4" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55c4720d7d4cd3d5b00f61d03751c685ad09c33ae8290c8a2c11335e0604300b" checksum = "f743f2a3cea30a58cd479013f75550e879009e3a02f616f18ca699335aa248c3"
dependencies = [ dependencies = [
"base64", "base64",
"bytes", "bytes",
@@ -2913,7 +2913,7 @@ dependencies = [
"futures-intrusive", "futures-intrusive",
"futures-io", "futures-io",
"futures-util", "futures-util",
"hashbrown 0.15.2", "hashbrown 0.15.3",
"hashlink", "hashlink",
"indexmap", "indexmap",
"log", "log",
@@ -2930,14 +2930,14 @@ dependencies = [
"tokio-stream", "tokio-stream",
"tracing", "tracing",
"url", "url",
"webpki-roots", "webpki-roots 0.26.11",
] ]
[[package]] [[package]]
name = "sqlx-macros" name = "sqlx-macros"
version = "0.8.4" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175147fcb75f353ac7675509bc58abb2cb291caf0fd24a3623b8f7e3eb0a754b" checksum = "7f4200e0fde19834956d4252347c12a083bdcb237d7a1a1446bffd8768417dce"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -2948,9 +2948,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-macros-core" name = "sqlx-macros-core"
version = "0.8.4" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cde983058e53bfa75998e1982086c5efe3c370f3250bf0357e344fa3352e32b" checksum = "882ceaa29cade31beca7129b6beeb05737f44f82dbe2a9806ecea5a7093d00b7"
dependencies = [ dependencies = [
"dotenvy", "dotenvy",
"either", "either",
@@ -2974,9 +2974,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-mysql" name = "sqlx-mysql"
version = "0.8.4" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "847d2e5393a4f39e47e4f36cab419709bc2b83cbe4223c60e86e1471655be333" checksum = "0afdd3aa7a629683c2d750c2df343025545087081ab5942593a5288855b1b7a7"
dependencies = [ dependencies = [
"atoi", "atoi",
"base64", "base64",
@@ -3017,9 +3017,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-postgres" name = "sqlx-postgres"
version = "0.8.4" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc35947a541b9e0a2e3d85da444f1c4137c13040267141b208395a0d0ca4659f" checksum = "a0bedbe1bbb5e2615ef347a5e9d8cd7680fb63e77d9dafc0f29be15e53f1ebe6"
dependencies = [ dependencies = [
"atoi", "atoi",
"base64", "base64",
@@ -3055,9 +3055,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-sqlite" name = "sqlx-sqlite"
version = "0.8.4" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c48291dac4e5ed32da0927a0b981788be65674aeb62666d19873ab4289febde" checksum = "c26083e9a520e8eb87a06b12347679b142dc2ea29e6e409f805644a7a979a5bc"
dependencies = [ dependencies = [
"atoi", "atoi",
"chrono", "chrono",
@@ -3095,9 +3095,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]] [[package]]
name = "stacker" name = "stacker"
version = "0.1.20" version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "601f9201feb9b09c00266478bf459952b9ef9a6b94edb2f21eba14ab681a60a9" checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b"
dependencies = [ dependencies = [
"cc", "cc",
"cfg-if", "cfg-if",
@@ -3134,9 +3134,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.100" version = "2.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -3145,9 +3145,9 @@ dependencies = [
[[package]] [[package]]
name = "synstructure" name = "synstructure"
version = "0.13.1" version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -3277,9 +3277,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.44.2" version = "1.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
@@ -3316,9 +3316,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.14" version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-core", "futures-core",
@@ -3329,9 +3329,9 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.8.20" version = "0.8.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
dependencies = [ dependencies = [
"serde", "serde",
"serde_spanned", "serde_spanned",
@@ -3341,26 +3341,33 @@ dependencies = [
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.6.8" version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
dependencies = [ dependencies = [
"serde", "serde",
] ]
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.22.24" version = "0.22.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde", "serde",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",
"winnow 0.7.6", "toml_write",
"winnow 0.7.9",
] ]
[[package]]
name = "toml_write"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
[[package]] [[package]]
name = "tower-service" name = "tower-service"
version = "0.3.3" version = "0.3.3"
@@ -3567,9 +3574,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]] [[package]]
name = "ureq" name = "ureq"
version = "3.0.10" version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b0351ca625c7b41a8e4f9bb6c5d9755f67f62c2187ebedecacd9974674b271d" checksum = "b7a3e9af6113ecd57b8c63d3cd76a385b2e3881365f1f489e54f49801d0c83ea"
dependencies = [ dependencies = [
"base64", "base64",
"cookie_store", "cookie_store",
@@ -3583,14 +3590,14 @@ dependencies = [
"serde_json", "serde_json",
"ureq-proto", "ureq-proto",
"utf-8", "utf-8",
"webpki-roots", "webpki-roots 0.26.11",
] ]
[[package]] [[package]]
name = "ureq-proto" name = "ureq-proto"
version = "0.3.5" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae239d0a3341aebc94259414d1dc67cfce87d41cbebc816772c91b77902fafa4" checksum = "fadf18427d33828c311234884b7ba2afb57143e6e7e69fda7ee883b624661e36"
dependencies = [ dependencies = [
"base64", "base64",
"http 1.3.1", "http 1.3.1",
@@ -3766,9 +3773,18 @@ dependencies = [
[[package]] [[package]]
name = "webpki-roots" name = "webpki-roots"
version = "0.26.8" version = "0.26.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
dependencies = [
"webpki-roots 1.0.0",
]
[[package]]
name = "webpki-roots"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb"
dependencies = [ dependencies = [
"rustls-pki-types", "rustls-pki-types",
] ]
@@ -4041,9 +4057,9 @@ dependencies = [
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.7.6" version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
@@ -4113,11 +4129,11 @@ dependencies = [
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.8.24" version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
dependencies = [ dependencies = [
"zerocopy-derive 0.8.24", "zerocopy-derive 0.8.25",
] ]
[[package]] [[package]]
@@ -4133,9 +4149,9 @@ dependencies = [
[[package]] [[package]]
name = "zerocopy-derive" name = "zerocopy-derive"
version = "0.8.24" version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@@ -9,7 +9,7 @@ rowing-tera = ["rocket_dyn_templates", "tera"]
rest = [] rest = []
[dependencies] [dependencies]
rocket = { version = "0.5.0", features = ["secrets"]} rocket = { version = "0.5", features = ["secrets"]}
rocket_dyn_templates = {version = "0.2", features = [ "tera" ], optional = true } rocket_dyn_templates = {version = "0.2", features = [ "tera" ], optional = true }
log = "0.4" log = "0.4"
env_logger = "0.11" env_logger = "0.11"
@@ -19,15 +19,15 @@ serde = { version = "1.0", features = [ "derive" ]}
serde_json = "1.0" serde_json = "1.0"
chrono = { version = "0.4", features = ["serde"]} chrono = { version = "0.4", features = ["serde"]}
chrono-tz = "0.10" chrono-tz = "0.10"
tera = { version = "1.18", features = ["date-locale"], optional = true} tera = { version = "1.20", features = ["date-locale"], optional = true}
ics = "0.5" ics = "0.5"
futures = "0.3" futures = "0.3"
lettre = "0.11" lettre = "0.11"
csv = "1.3" csv = "1.3"
itertools = "0.14" itertools = "0.14"
job_scheduler_ng = "2.0" job_scheduler_ng = "2.2"
ureq = { version = "3.0", features = ["json"] } ureq = { version = "3.0", features = ["json"] }
regex = "1.10" regex = "1.11"
urlencoding = "2.1" urlencoding = "2.1"
[target.'cfg(not(windows))'.dependencies] [target.'cfg(not(windows))'.dependencies]

View File

@@ -115,7 +115,7 @@ test("Cox can start and finish trip", async ({ page }, testInfo) => {
await page.getByPlaceholder("Passwort").press("Enter"); await page.getByPlaceholder("Passwort").press("Enter");
await page.goto("/log/show"); await page.goto("/log/show");
await page.getByText('(cox2)').click(); await page.getByRole('link', { name: 'Joe' }).nth(1).click();
page.once("dialog", (dialog) => { page.once("dialog", (dialog) => {
dialog.accept().catch(() => {}); dialog.accept().catch(() => {});
}); });
@@ -208,7 +208,6 @@ test("Kiosk can start and finish trip", async ({ page }, testInfo) => {
await page.getByRole('link', { name: 'Logbuch' }).click(); await page.getByRole('link', { name: 'Logbuch' }).click();
await expect(page.locator('body')).toContainText('Joe'); await expect(page.locator('body')).toContainText('Joe');
await expect(page.locator('body')).toContainText('(cox2)');
await expect(page.locator('body')).toContainText('Ottensheim (25 km)'); await expect(page.locator('body')).toContainText('Ottensheim (25 km)');
await expect(page.locator('body')).toContainText('Ruderer: cox2, rower2'); await expect(page.locator('body')).toContainText('Ruderer: cox2, rower2');
@@ -225,7 +224,7 @@ test("Kiosk can start and finish trip", async ({ page }, testInfo) => {
await page.getByPlaceholder("Passwort").press("Enter"); await page.getByPlaceholder("Passwort").press("Enter");
await page.goto("/log/show"); await page.goto("/log/show");
await page.getByText('(cox2)').click(); await page.getByRole('link', { name: 'Joe' }).nth(1).click();
page.once("dialog", (dialog) => { page.once("dialog", (dialog) => {
dialog.accept().catch(() => {}); dialog.accept().catch(() => {});
}); });
@@ -286,7 +285,6 @@ test("Cox can start and finish trip with cox steering only", async ({ page }, te
await page.goto('/log/show'); await page.goto('/log/show');
await expect(page.locator('body')).toContainText('cox_only_steering_boat'); await expect(page.locator('body')).toContainText('cox_only_steering_boat');
await expect(page.locator('body')).toContainText('(cox2 - handgesteuert)');
await expect(page.locator('body')).toContainText('Ottensheim (25 km)'); await expect(page.locator('body')).toContainText('Ottensheim (25 km)');
@@ -302,7 +300,7 @@ test("Cox can start and finish trip with cox steering only", async ({ page }, te
await page.getByPlaceholder("Passwort").press("Enter"); await page.getByPlaceholder("Passwort").press("Enter");
await page.goto("/log/show"); await page.goto("/log/show");
await page.getByText('(cox2 - handgesteuert)').click(); await page.getByRole("link", { name: "cox_only_steering_boat" }).click();
page.once("dialog", (dialog) => { page.once("dialog", (dialog) => {
dialog.accept().catch(() => {}); dialog.accept().catch(() => {});
}); });
@@ -371,7 +369,7 @@ test("Kiosk can start and finish trip in one stop", async ({ page }, testInfo) =
await page.getByPlaceholder("Passwort").press("Enter"); await page.getByPlaceholder("Passwort").press("Enter");
await page.goto("/log/show"); await page.goto("/log/show");
await page.getByText('(cox2)').click(); await page.getByRole('link', { name: 'Joe' }).nth(1).click();
page.once("dialog", (dialog) => { page.once("dialog", (dialog) => {
dialog.accept().catch(() => {}); dialog.accept().catch(() => {});
}); });

View File

@@ -1,7 +1,10 @@
use std::ops::DerefMut; use std::ops::DerefMut;
use super::{role::Role, user::User}; use super::{
use chrono::NaiveDateTime; role::Role,
user::{ManageUserUser, User},
};
use chrono::{DateTime, Duration, Local, NaiveDateTime, TimeZone, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
@@ -14,6 +17,35 @@ pub struct Activity {
pub keep_until: Option<NaiveDateTime>, pub keep_until: Option<NaiveDateTime>,
} }
// TODO: add `reason` as additional db field, to be able to query and show this to the users
pub enum Reason<'a> {
// `User` tried to login with `String` as UserAgent
SuccLogin(&'a User, String),
// `User` changed the data of `User`, explanation in `String`
UserDataChange(&'a ManageUserUser, &'a User, String),
// New Note for User
NewUserNote(&'a ManageUserUser, &'a User, String),
}
impl From<Reason<'_>> for ActivityBuilder {
fn from(value: Reason<'_>) -> Self {
match value {
Reason::SuccLogin(user, agent) => {
Self::new(&format!("{user} hat sich eingeloggt (User-Agent: {agent})"))
.relevant_for_user(user)
.keep_until_days(7)
}
Reason::UserDataChange(changed_by, changed_user, explanation) => Self::new(&format!(
"{changed_by} hat die Daten von {changed_user} aktualisiert: {explanation}"
))
.relevant_for_user(changed_user),
Reason::NewUserNote(changed_by, user, explanation) => {
Self::new(&format!("({changed_by}) {explanation}")).relevant_for_user(user)
}
}
}
}
pub struct ActivityBuilder { pub struct ActivityBuilder {
text: String, text: String,
relevant_for: String, relevant_for: String,
@@ -21,6 +53,7 @@ pub struct ActivityBuilder {
} }
impl ActivityBuilder { impl ActivityBuilder {
/// TODO: maybe make this private, and only allow specific acitivites defined in `Reason`
#[must_use] #[must_use]
pub fn new(text: &str) -> Self { pub fn new(text: &str) -> Self {
Self { Self {
@@ -46,6 +79,15 @@ impl ActivityBuilder {
} }
} }
#[must_use]
pub fn keep_until_days(self, days: i64) -> Self {
let now = Utc::now().naive_utc();
Self {
keep_until: Some(now + Duration::days(days)),
..self
}
}
pub async fn save(self, db: &SqlitePool) { pub async fn save(self, db: &SqlitePool) {
Activity::create(db, &self.text, &self.relevant_for, self.keep_until).await; Activity::create(db, &self.text, &self.relevant_for, self.keep_until).await;
} }
@@ -110,4 +152,30 @@ ORDER BY created_at DESC;
.await .await
.unwrap() .unwrap()
} }
async fn last(db: &SqlitePool) -> Vec<Self> {
sqlx::query_as!(
Self,
"
SELECT id, created_at, text, relevant_for, keep_until FROM activity
ORDER BY id DESC
LIMIT 1000
"
)
.fetch_all(db)
.await
.unwrap()
}
pub async fn show(db: &SqlitePool) -> String {
let mut ret = String::new();
for log in Self::last(db).await {
let utc_time: DateTime<Utc> = Utc::from_utc_datetime(&Utc, &log.created_at);
let local_time = utc_time.with_timezone(&Local);
ret.push_str(&format!("- {local_time}: {}\n", log.text));
}
ret
}
} }

View File

@@ -1,9 +1,8 @@
use std::ops::DerefMut; use std::ops::DerefMut;
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use itertools::Itertools;
use rocket::FromForm;
use rocket::serde::{Deserialize, Serialize}; use rocket::serde::{Deserialize, Serialize};
use rocket::FromForm;
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
use crate::model::boathouse::Boathouse; use crate::model::boathouse::Boathouse;
@@ -102,24 +101,10 @@ impl Boat {
} }
pub async fn shipmaster_allowed(&self, db: &SqlitePool, user: &User) -> bool { pub async fn shipmaster_allowed(&self, db: &SqlitePool, user: &User) -> bool {
if let Some(owner_id) = self.owner { let mut tx = db.begin().await.unwrap();
return owner_id == user.id; let ret = self.shipmaster_allowed_tx(&mut tx, user).await;
} tx.commit().await.unwrap();
ret
if user.has_role(db, "Rennrudern").await {
let ottensheim = Location::find_by_name(db, "Ottensheim".into())
.await
.unwrap();
if self.location_id == ottensheim.id {
return true;
}
}
if self.amount_seats == 1 {
return true;
}
user.allowed_to_steer(db).await
} }
pub async fn shipmaster_allowed_tx( pub async fn shipmaster_allowed_tx(
@@ -127,10 +112,27 @@ impl Boat {
db: &mut Transaction<'_, Sqlite>, db: &mut Transaction<'_, Sqlite>,
user: &User, user: &User,
) -> bool { ) -> bool {
if user.has_role_tx(db, "admin").await {
return true;
}
if let Some(owner_id) = self.owner { if let Some(owner_id) = self.owner {
return owner_id == user.id; return owner_id == user.id;
} }
if user.has_role_tx(db, "Rennrudern").await {
let ottensheim = Location::find_by_name_tx(db, "Ottensheim".into())
.await
.unwrap();
if self.location_id == ottensheim.id {
return true;
}
}
if self.name == "Externes Boot" {
return true;
}
if self.amount_seats == 1 { if self.amount_seats == 1 {
return true; return true;
} }
@@ -257,58 +259,16 @@ ORDER BY
} }
pub async fn for_user(db: &SqlitePool, user: &User) -> Vec<BoatWithDetails> { pub async fn for_user(db: &SqlitePool, user: &User) -> Vec<BoatWithDetails> {
if user.has_role(db, "admin").await { let all_boats = Self::all(db).await;
return Self::all(db).await; let mut filtered_boats = Vec::new();
}
let mut boats = if user.allowed_to_steer(db).await {
sqlx::query_as!(
Boat,
"
SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external, deleted, convert_handoperated_possible
FROM boat
WHERE (owner is null or owner = ?) AND deleted = 0
ORDER BY amount_seats DESC
",
user.id
)
.fetch_all(db)
.await
.unwrap() //TODO: fixme
} else {
sqlx::query_as!(
Boat,
"
SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external, deleted, convert_handoperated_possible
FROM boat
WHERE (owner = ? OR (owner is null and amount_seats = 1)) AND deleted = 0
ORDER BY amount_seats DESC
",
user.id
)
.fetch_all(db)
.await
.unwrap() //TODO: fixme
};
if user.has_role(db, "Rennrudern").await { for boat in all_boats {
let ottensheim = Location::find_by_name(db, "Ottensheim".into()) if boat.boat.shipmaster_allowed(db, user).await {
.await filtered_boats.push(boat);
.unwrap(); }
let boats_in_ottensheim = sqlx::query_as!(
Boat,
"SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external, deleted, convert_handoperated_possible
FROM boat
WHERE (owner is null and location_id = ?) AND deleted = 0
ORDER BY amount_seats DESC
",ottensheim.id)
.fetch_all(db)
.await
.unwrap(); //TODO: fixme
boats.extend(boats_in_ottensheim.into_iter());
} }
let boats = boats.into_iter().unique().collect();
Self::boats_to_details(db, boats).await filtered_boats
} }
pub async fn all_at_location(db: &SqlitePool, location: String) -> Vec<BoatWithDetails> { pub async fn all_at_location(db: &SqlitePool, location: String) -> Vec<BoatWithDetails> {

View File

@@ -1,7 +1,7 @@
use std::ops::DerefMut; use std::ops::DerefMut;
use serde::Serialize; use serde::Serialize;
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction, sqlite::SqliteQueryResult}; use sqlx::{sqlite::SqliteQueryResult, FromRow, Sqlite, SqlitePool, Transaction};
use super::user::User; use super::user::User;
@@ -86,7 +86,7 @@ GROUP BY family.id;"
} }
pub async fn members(&self, db: &SqlitePool) -> Vec<User> { pub async fn members(&self, db: &SqlitePool) -> Vec<User> {
sqlx::query_as!(User, "SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, user_token FROM user WHERE family_id = ?", self.id) sqlx::query_as!(User, "SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, phone, address, family_id, user_token FROM user WHERE family_id = ?", self.id)
.fetch_all(db) .fetch_all(db)
.await .await
.unwrap() .unwrap()

View File

@@ -1,5 +1,6 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool}; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
use std::ops::DerefMut;
#[derive(FromRow, Debug, Serialize, Deserialize)] #[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct Location { pub struct Location {
@@ -37,6 +38,20 @@ impl Location {
.await .await
.ok() .ok()
} }
pub async fn find_by_name_tx(db: &mut Transaction<'_, Sqlite>, name: String) -> Option<Self> {
sqlx::query_as!(
Self,
"
SELECT id, name
FROM location
WHERE name=?
",
name
)
.fetch_one(db.deref_mut())
.await
.ok()
}
pub async fn all(db: &SqlitePool) -> Vec<Self> { pub async fn all(db: &SqlitePool) -> Vec<Self> {
sqlx::query_as!(Self, "SELECT id, name FROM location") sqlx::query_as!(Self, "SELECT id, name FROM location")

View File

@@ -1,74 +1,16 @@
use std::ops::DerefMut; use super::activity::ActivityBuilder;
use sqlx::{Sqlite, SqlitePool, Transaction};
use chrono::{DateTime, Local, NaiveDateTime, TimeZone, Utc}; pub struct Log {}
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct Log {
pub msg: String,
pub created_at: NaiveDateTime,
}
// TODO: remove and convert to proper acitvities
impl Log { impl Log {
pub async fn create(db: &SqlitePool, msg: String) -> bool { pub async fn create(db: &SqlitePool, msg: String) -> bool {
sqlx::query!("INSERT INTO log(msg) VALUES (?)", msg,) ActivityBuilder::new(&msg).save(db).await;
.execute(db) true
.await
.is_ok()
} }
pub async fn create_with_tx(db: &mut Transaction<'_, Sqlite>, msg: String) -> bool { pub async fn create_with_tx(db: &mut Transaction<'_, Sqlite>, msg: String) -> bool {
sqlx::query!("INSERT INTO log(msg) VALUES (?)", msg,) ActivityBuilder::new(&msg).save_tx(db).await;
.execute(db.deref_mut()) true
.await
.is_ok()
}
async fn last(db: &SqlitePool) -> Vec<Log> {
sqlx::query_as!(
Log,
"
SELECT msg, created_at
FROM log
ORDER BY id DESC
LIMIT 1000
"
)
.fetch_all(db)
.await
.unwrap()
}
pub async fn generate_feed(db: &SqlitePool) -> String {
let mut ret = String::from(
r#"<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Ruder App Admin Feed</title>
<link>app.rudernlinz.at</link>
<description>An RSS feed with activities from app.rudernlinz.at</description>"#,
);
for log in Self::last(db).await {
let utc_time: DateTime<Utc> = Utc::from_utc_datetime(&Utc, &log.created_at);
let local_time = utc_time.with_timezone(&Local);
ret.push_str("<item><title>");
ret.push_str(&format!("({}) {}", local_time, log.msg));
ret.push_str("</title></item>");
}
ret.push_str("</channel>");
ret.push_str("</rss>");
ret.replace('\n', "")
}
pub async fn show(db: &SqlitePool) -> String {
let mut ret = String::new();
for log in Self::last(db).await {
let utc_time: DateTime<Utc> = Utc::from_utc_datetime(&Utc, &log.created_at);
let local_time = utc_time.with_timezone(&Local);
ret.push_str(&format!("- {} - {}\n", local_time, log.msg));
}
ret
} }
} }

View File

@@ -23,7 +23,7 @@ impl Rower {
sqlx::query_as!( sqlx::query_as!(
User, User,
" "
SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, user_token SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, phone, address, family_id, user_token
FROM user FROM user
WHERE id in (SELECT rower_id FROM rower WHERE logbook_id=?) WHERE id in (SELECT rower_id FROM rower WHERE logbook_id=?)
", ",

View File

@@ -2,7 +2,10 @@
use super::{AllowedToEditPaymentStatusUser, ManageUserUser, User}; use super::{AllowedToEditPaymentStatusUser, ManageUserUser, User};
use crate::model::{ use crate::model::{
activity::ActivityBuilder, family::Family, mail::valid_mails, notification::Notification, activity::{self, ActivityBuilder},
family::Family,
mail::valid_mails,
notification::Notification,
role::Role, role::Role,
}; };
use chrono::NaiveDate; use chrono::NaiveDate;
@@ -14,15 +17,17 @@ impl User {
&self, &self,
db: &SqlitePool, db: &SqlitePool,
updated_by: &ManageUserUser, updated_by: &ManageUserUser,
user: &User,
note: &str, note: &str,
) -> Result<(), String> { ) -> Result<(), String> {
let note = note.trim(); let note = note.trim();
ActivityBuilder::new(&format!("({updated_by}) {note}")) ActivityBuilder::from(activity::Reason::UserDataChange(
.relevant_for_user(user) updated_by,
.save(db) self,
.await; note.to_string(),
))
.save(db)
.await;
Ok(()) Ok(())
} }
@@ -47,18 +52,11 @@ impl User {
.unwrap(); //Okay, because we can only create a User of a valid id .unwrap(); //Okay, because we can only create a User of a valid id
let msg = match &self.mail { let msg = match &self.mail {
Some(old_mail) => { Some(old_mail) => format!("Mail-Adresse von {old_mail} auf {new_mail} geändert."),
format!( None => format!("Neue Mail-Adresse für: {new_mail}"),
"{updated_by} hat die Mail-Adresse von {self} von {old_mail} auf {new_mail} geändert."
)
}
None => {
format!("{updated_by} eine neue Mail-Adresse für {self} hinzugefügt: {new_mail}")
}
}; };
ActivityBuilder::new(&msg) ActivityBuilder::from(activity::Reason::UserDataChange(updated_by, self, msg))
.relevant_for_user(self)
.save(db) .save(db)
.await; .await;
@@ -89,19 +87,16 @@ impl User {
query.execute(db).await.unwrap(); //Okay, because we can only create a User of a valid id query.execute(db).await.unwrap(); //Okay, because we can only create a User of a valid id
let msg = match &self.phone { let msg = match &self.phone {
Some(old_phone) if new_phone.is_empty() => format!( Some(old_phone) if new_phone.is_empty() => {
"{updated_by} hat die Telefonnummer von {self} entfernt (alte Nummer: {old_phone})" format!("Telefonnummer wurde entfernt (alte Nummer: {old_phone})")
), }
Some(old_phone) => format!( Some(old_phone) => {
"{updated_by} hat die Telefonnummer von {self} von {old_phone} auf {new_phone} geändert." format!("Telefonnummer wurde von {old_phone} auf {new_phone} geändert.")
), }
None => format!( None => format!("Neue Telefonnummer hinzugefügt: {new_phone}"),
"{updated_by} hat eine neue Telefonnummer für {self} hinzugefügt: {new_phone}"
),
}; };
ActivityBuilder::new(&msg) ActivityBuilder::from(activity::Reason::UserDataChange(updated_by, self, msg))
.relevant_for_user(self)
.save(db) .save(db)
.await; .await;
} }
@@ -311,6 +306,17 @@ impl User {
None, None,
) )
.await; .await;
Notification::create(
db,
self,
&format!(
"Liebe neue Steuerperson, gratuliere zur geschafften Steuerprüfung 💪. Du kannst ab sofort selber Ausfahrten ausschreiben und der Steuerpersonen Signal-Gruppe beitreten: https://signal.group/#CjQKIHJInNb3zSVW7ipLo7_ygIqVxhxUaaNYx4sy2jdklLsIEhBHJNM2KZM1UnBdQxWy_Gdp"
),
"Gratulation",
None,
None,
)
.await;
ActivityBuilder::new(&format!("{updated_by} hat {self} zur Steuerperson gemacht")) ActivityBuilder::new(&format!("{updated_by} hat {self} zur Steuerperson gemacht"))
.relevant_for_user(self) .relevant_for_user(self)
.save(db) .save(db)
@@ -342,13 +348,13 @@ impl User {
Notification::create_for_role( Notification::create_for_role(
db, db,
&vorstand, &vorstand,
&format!("Lieber Vorstand, {self} ist ab kein {old} mehr."), &format!("Lieber Vorstand, {self} ist ab sofort kein {old} mehr."),
"Steuerperson --", "Steuerperson--;",
None, None,
None, None,
) )
.await; .await;
ActivityBuilder::new(&format!("{updated_by} hat {self} zum normalen Mitlgied gemacht (keine Steuerperson/Schiffsführer mehr)")) ActivityBuilder::new(&format!("{updated_by} hat {self} zum normalen Mitglied gemacht (keine Steuerperson/Schiffsführer mehr)"))
.relevant_for_user(self) .relevant_for_user(self)
.save(db) .save(db)
.await; .await;

View File

@@ -53,7 +53,6 @@ pub struct User {
pub birthdate: Option<String>, pub birthdate: Option<String>,
pub mail: Option<String>, pub mail: Option<String>,
pub nickname: Option<String>, pub nickname: Option<String>,
pub notes: Option<String>,
pub phone: Option<String>, pub phone: Option<String>,
pub address: Option<String>, pub address: Option<String>,
pub family_id: Option<i64>, pub family_id: Option<i64>,
@@ -262,7 +261,7 @@ AND r.cluster = 'skill';
sqlx::query_as!( sqlx::query_as!(
Self, Self,
" "
SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, user_token SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, phone, address, family_id, user_token
FROM user FROM user
WHERE id like ? WHERE id like ?
", ",
@@ -277,7 +276,7 @@ WHERE id like ?
sqlx::query_as!( sqlx::query_as!(
Self, Self,
" "
SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, user_token SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, phone, address, family_id, user_token
FROM user FROM user
WHERE id like ? WHERE id like ?
", ",
@@ -294,7 +293,7 @@ WHERE id like ?
sqlx::query_as!( sqlx::query_as!(
Self, Self,
" "
SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, user_token SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, phone, address, family_id, user_token
FROM user FROM user
WHERE lower(name)=? WHERE lower(name)=?
", ",
@@ -339,7 +338,7 @@ WHERE lower(name)=?
pub async fn all_with_order(db: &SqlitePool, sort: &str, asc: bool) -> Vec<Self> { pub async fn all_with_order(db: &SqlitePool, sort: &str, asc: bool) -> Vec<Self> {
let mut query = format!( let mut query = format!(
" "
SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, user_token SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, phone, address, family_id, user_token
FROM user FROM user
WHERE deleted = 0 WHERE deleted = 0
ORDER BY {} ORDER BY {}
@@ -367,7 +366,7 @@ WHERE lower(name)=?
sqlx::query_as!( sqlx::query_as!(
Self, Self,
" "
SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, user_token SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, phone, address, family_id, user_token
FROM user u FROM user u
JOIN user_role ur ON u.id = ur.user_id JOIN user_role ur ON u.id = ur.user_id
WHERE ur.role_id = ? AND deleted = 0 WHERE ur.role_id = ? AND deleted = 0
@@ -383,14 +382,14 @@ ORDER BY name;
sqlx::query_as!( sqlx::query_as!(
Self, Self,
" "
SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, user_token FROM user SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, phone, address, family_id, user_token FROM user
WHERE family_id IS NOT NULL WHERE family_id IS NOT NULL
GROUP BY family_id GROUP BY family_id
UNION UNION
-- Select users with a null family_id, without grouping -- Select users with a null family_id, without grouping
SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, user_token FROM user SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, phone, address, family_id, user_token FROM user
WHERE family_id IS NULL; WHERE family_id IS NULL;
" "
) )
@@ -408,7 +407,7 @@ WHERE family_id IS NULL;
sqlx::query_as!( sqlx::query_as!(
Self, Self,
" "
SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, user_token SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, phone, address, family_id, user_token
FROM user FROM user
WHERE deleted = 0 AND (SELECT COUNT(*) FROM user_role WHERE user_id=user.id AND role_id = (SELECT id FROM role WHERE name = 'cox')) > 0 WHERE deleted = 0 AND (SELECT COUNT(*) FROM user_role WHERE user_id=user.id AND role_id = (SELECT id FROM role WHERE name = 'cox')) > 0
ORDER BY last_access DESC ORDER BY last_access DESC
@@ -577,10 +576,6 @@ ASKÖ Ruderverein Donau Linz", self.name),
.execute(db) .execute(db)
.await .await
.unwrap(); //Okay, because we can only create a User of a valid id .unwrap(); //Okay, because we can only create a User of a valid id
ActivityBuilder::new(&format!("User {self} hat sich eingeloggt."))
.relevant_for_user(self)
.save(db)
.await;
} }
pub async fn delete(&self, db: &SqlitePool, deleted_by: &ManageUserUser) { pub async fn delete(&self, db: &SqlitePool, deleted_by: &ManageUserUser) {

View File

@@ -2,13 +2,12 @@ use super::foerdernd::FoerderndUser;
use super::regular::RegularUser; use super::regular::RegularUser;
use super::unterstuetzend::UnterstuetzendUser; use super::unterstuetzend::UnterstuetzendUser;
use super::{ManageUserUser, User}; use super::{ManageUserUser, User};
use crate::NonEmptyString;
use crate::model::activity::ActivityBuilder; use crate::model::activity::ActivityBuilder;
use crate::model::role::Role; use crate::model::role::Role;
use crate::NonEmptyString;
use crate::{ use crate::{
SCHECKBUCH,
model::{mail::Mail, notification::Notification}, model::{mail::Mail, notification::Notification},
special_user, special_user, SCHECKBUCH,
}; };
use chrono::NaiveDate; use chrono::NaiveDate;
use rocket::async_trait; use rocket::async_trait;
@@ -75,9 +74,9 @@ impl ScheckbuchUser {
Notification::create_for_steering_people( Notification::create_for_steering_people(
db, db,
&format!( &format!(
"Liebe Steuerberechtigte, {} hatte ein Scheckbuch und ist nun seit {} es ein neues reguläres Mitglied. 🎉", "Liebe Steuerberechtigte, {} hatte ein Scheckbuch und ist nun seit {} ein neues reguläres Mitglied. 🎉",
self.name, self.name,
self.member_since_date.clone().unwrap() member_since
), ),
"Neues Vereinsmitglied", "Neues Vereinsmitglied",
None, None,
@@ -133,9 +132,9 @@ impl ScheckbuchUser {
db, db,
&vorstand, &vorstand,
&format!( &format!(
"Lieber Vorstand, {} hatte ein Scheckbuch und ist nun seit {} es ein neues unterstützendes Mitglied.", "Lieber Vorstand, {} hatte ein Scheckbuch und ist nun seit {} ein neues unterstützendes Mitglied.",
self.name, self.name,
self.member_since_date.clone().unwrap() member_since
), ),
"Neues unterstützendes Vereinsmitglied", "Neues unterstützendes Vereinsmitglied",
None, None,
@@ -187,9 +186,9 @@ impl ScheckbuchUser {
db, db,
&vorstand, &vorstand,
&format!( &format!(
"Lieber Vorstand, {} hatte ein Scheckbuch und ist nun seit {} es ein neues förderndes Mitglied.", "Lieber Vorstand, {} hatte ein Scheckbuch und ist nun seit {} ein neues förderndes Mitglied.",
self.name, self.name,
self.member_since_date.clone().unwrap() member_since
), ),
"Neues förderndes Vereinsmitglied", "Neues förderndes Vereinsmitglied",
None, None,

View File

@@ -1,12 +1,9 @@
use csv::ReaderBuilder; use csv::ReaderBuilder;
use rocket::{FromForm, Route, State, form::Form, get, post, routes}; use rocket::{form::Form, get, post, routes, FromForm, Route, State};
use rocket_dyn_templates::{Template, context}; use rocket_dyn_templates::{context, Template};
use sqlx::SqlitePool; use sqlx::SqlitePool;
use crate::{ use crate::model::{activity::Activity, role::Role, user::AdminUser};
model::{log::Log, role::Role, user::AdminUser},
tera::Config,
};
pub mod boat; pub mod boat;
pub mod event; pub mod event;
@@ -16,18 +13,9 @@ pub mod role;
pub mod schnupper; pub mod schnupper;
pub mod user; pub mod user;
#[get("/rss?<key>")]
async fn rss(db: &State<SqlitePool>, key: &str, config: &State<Config>) -> String {
if key.eq(&config.rss_key) {
Log::generate_feed(db).await
} else {
"Not allowed".into()
}
}
#[get("/rss", rank = 2)] #[get("/rss", rank = 2)]
async fn show_rss(db: &State<SqlitePool>, _admin: AdminUser) -> String { async fn show_activities(db: &State<SqlitePool>, _admin: AdminUser) -> String {
Log::show(db).await Activity::show(db).await
} }
#[get("/list")] #[get("/list")]
@@ -83,6 +71,6 @@ pub fn routes() -> Vec<Route> {
ret.append(&mut mail::routes()); ret.append(&mut mail::routes());
ret.append(&mut event::routes()); ret.append(&mut event::routes());
ret.append(&mut role::routes()); ret.append(&mut role::routes());
ret.append(&mut routes![rss, show_rss, show_list, list]); ret.append(&mut routes![show_activities, show_list, list]);
ret ret
} }

View File

@@ -3,13 +3,14 @@ use crate::model::{
user::{AdminUser, UserWithDetails, VorstandUser}, user::{AdminUser, UserWithDetails, VorstandUser},
}; };
use rocket::{ use rocket::{
FromForm, Route, State,
form::Form, form::Form,
get, post, get, post,
request::FlashMessage, request::FlashMessage,
response::{Flash, Redirect}, response::{Flash, Redirect},
routes, FromForm, Route, State, routes,
}; };
use rocket_dyn_templates::{tera::Context, Template}; use rocket_dyn_templates::{Template, tera::Context};
use sqlx::SqlitePool; use sqlx::SqlitePool;
#[get("/role")] #[get("/role")]

View File

@@ -135,7 +135,7 @@ async fn view(
if user.name == "Externe Steuerperson" { if user.name == "Externe Steuerperson" {
return Err(Flash::error( return Err(Flash::error(
Redirect::to("/admin/user"), Redirect::to("/admin/user"),
"Diese besondere Person kannst du dir leider nicht anschauen, mein lieber neugieriger Ruderant!" "Diese besondere Person kannst du dir leider nicht anschauen, mein lieber neugieriger Ruderant!",
)); ));
} }
@@ -349,7 +349,7 @@ async fn add_note(
); );
}; };
match user.add_note(db, &admin, &user, &data.note).await { match user.add_note(db, &admin, &data.note).await {
Ok(_) => Flash::success( Ok(_) => Flash::success(
Redirect::to(format!("/admin/user/{}", user.id)), Redirect::to(format!("/admin/user/{}", user.id)),
"Notiz hinzugefügt", "Notiz hinzugefügt",

View File

@@ -1,5 +1,4 @@
use rocket::{ use rocket::{
FromForm, Request, Route, State,
form::Form, form::Form,
get, get,
http::{Cookie, CookieJar}, http::{Cookie, CookieJar},
@@ -9,11 +8,13 @@ use rocket::{
response::{Flash, Redirect}, response::{Flash, Redirect},
routes, routes,
time::{Duration, OffsetDateTime}, time::{Duration, OffsetDateTime},
FromForm, Request, Route, State,
}; };
use rocket_dyn_templates::{Template, context, tera}; use rocket_dyn_templates::{context, tera, Template};
use sqlx::SqlitePool; use sqlx::SqlitePool;
use crate::model::{ use crate::model::{
activity::{self, ActivityBuilder},
log::Log, log::Log,
user::{LoginError, User}, user::{LoginError, User},
}; };
@@ -82,14 +83,9 @@ async fn login(
cookies.add_private(Cookie::new("loggedin_user", format!("{}", user.id))); cookies.add_private(Cookie::new("loggedin_user", format!("{}", user.id)));
Log::create( ActivityBuilder::from(activity::Reason::SuccLogin(&user, agent.0))
db, .save(db)
format!( .await;
"Succ login of {} with this useragent: {}",
login.name, agent.0
),
)
.await;
// Check for redirect_url cookie and redirect accordingly // Check for redirect_url cookie and redirect accordingly
match cookies.get_private("redirect_url") { match cookies.get_private("redirect_url") {

View File

@@ -1,6 +1,7 @@
use std::net::IpAddr; use std::net::IpAddr;
use rocket::{ use rocket::{
Request, Route, State,
form::Form, form::Form,
get, get,
http::{Cookie, CookieJar}, http::{Cookie, CookieJar},
@@ -9,9 +10,8 @@ use rocket::{
response::{Flash, Redirect}, response::{Flash, Redirect},
routes, routes,
time::{Duration, OffsetDateTime}, time::{Duration, OffsetDateTime},
Request, Route, State,
}; };
use rocket_dyn_templates::{context, Template}; use rocket_dyn_templates::{Template, context};
use sqlx::SqlitePool; use sqlx::SqlitePool;
use tera::Context; use tera::Context;
@@ -585,7 +585,7 @@ mod test {
use sqlx::SqlitePool; use sqlx::SqlitePool;
use crate::model::logbook::Logbook; use crate::model::logbook::Logbook;
use crate::tera::{log::Boat, User}; use crate::tera::{User, log::Boat};
use crate::testdb; use crate::testdb;
#[sqlx::test] #[sqlx::test]

View File

@@ -3,3 +3,46 @@ INSERT INTO user(name) VALUES('Marie');
INSERT INTO "user_role" (user_id, role_id) VALUES((SELECT id from user where name = 'Marie'),(SELECT id FROM role where name = 'Donau Linz')); INSERT INTO "user_role" (user_id, role_id) VALUES((SELECT id from user where name = 'Marie'),(SELECT id FROM role where name = 'Donau Linz'));
INSERT INTO user(name) VALUES('Philipp'); INSERT INTO user(name) VALUES('Philipp');
INSERT INTO "user_role" (user_id, role_id) VALUES((SELECT id from user where name = 'Philipp'),(SELECT id FROM role where name = 'Donau Linz')); INSERT INTO "user_role" (user_id, role_id) VALUES((SELECT id from user where name = 'Philipp'),(SELECT id FROM role where name = 'Donau Linz'));
-- Step 1: Create a new table without the 'notes' column
CREATE TABLE "user_new" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" text NOT NULL UNIQUE,
"pw" text,
"deleted" boolean NOT NULL DEFAULT FALSE,
"last_access" DATETIME,
"dob" text,
"weight" text,
"sex" text,
"dirty_thirty" text,
"dirty_dozen" text,
"member_since_date" text,
"birthdate" text,
"mail" text,
"nickname" text,
"phone" text,
"address" text,
"family_id" INTEGER REFERENCES family(id),
"membership_pdf" BLOB,
"user_token" TEXT NOT NULL DEFAULT (lower(hex(randomblob(16))))
);
-- Step 2: Copy data from the old table to the new one (excluding 'notes')
INSERT INTO user_new (
id, name, pw, deleted, last_access, dob, weight, sex,
dirty_thirty, dirty_dozen, member_since_date, birthdate,
mail, nickname, phone, address, family_id, membership_pdf, user_token
)
SELECT
id, name, pw, deleted, last_access, dob, weight, sex,
dirty_thirty, dirty_dozen, member_since_date, birthdate,
mail, nickname, phone, address, family_id, membership_pdf, user_token
FROM user;
-- Step 3: Drop the old table
DROP TABLE user;
-- Step 4: Rename the new table to the original name
ALTER TABLE user_new RENAME TO user;

View File

@@ -3,35 +3,42 @@
{% extends "base" %} {% extends "base" %}
{% block content %} {% block content %}
<div class="max-w-screen-lg w-full dark:text-white"> <div class="max-w-screen-lg w-full dark:text-white">
<h1 class="h1">Rolle</h1> <h1 class="h1">Rollen</h1>
<div class="grid "> <div class="search-wrapper">
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5" <label for="name" class="sr-only">Suche</label>
role="alert"> <input type="search"
<h2 class="h2">Rolle</h2> name="name"
{% for role in roles %} id="filter-js"
<div data-filterable="true" class="search-bar"
data-filter="{{ role.name }}" placeholder="Suchen nach Namen...">
class="w-full border-t"> </div>
<form action="/admin/role/{{ role.id }}" <div id="filter-result-js" class="search-result"></div>
data-filterable="true" <div class="border-r border-l border-gray-200 dark:border-primary-600">
method="post" {% for role in roles %}
class="bg-white dark:bg-primary-900 p-4 w-full"> <div data-filterable="true"
<div class="w-full"> data-filter="{{ role.name }} {{ role.formatted_name }}"
<input type="hidden" name="id" value="{{ role.id }}" /> class="w-full border-t">
<div class="font-bold mb-1 text-black dark:text-white"> <form action="/admin/role/{{ role.id }}"
{{ role.name }} data-filterable="true"
<br /> method="post"
class="bg-white dark:bg-primary-900 p-4 w-full">
<div class="w-full">
<input type="hidden" name="id" value="{{ role.id }}" />
<div class="font-bold mb-1 text-black dark:text-white">
{{ role.name }}
<br />
</div>
<div class="grid md:grid-cols-3 gap-3">
{{ macros::input(label='Name (formatiert)', name='formatted_name', type='text', value=role.formatted_name) }}
{{ macros::input(label='Beschreibung', name='desc', type='text', value=role.desc) }}
<div class="flex items-end">
<input value="Ändern" type="submit" class="w-full btn btn-primary" />
</div>
</div>
</div>
</form>
</div> </div>
<div class="grid md:grid-cols-3 gap-3"> {% endfor %}
{{ macros::input(label='Formatierter Name', name='formatted_name', type='text', value=role.formatted_name) }}
{{ macros::input(label='Beschreibung', name='desc', type='text', value=role.desc) }}
<input value="Ändern" type="submit" class="w-28 btn btn-primary" />
</div>
</div>
</form>
</div>
{% endfor %}
</div>
</div> </div>
</div> </div>
{% endblock content %} {% endblock content %}

View File

@@ -8,42 +8,39 @@
<summary class="px-3 cursor-pointer text-md font-bold text-primary-950 dark:text-white"> <summary class="px-3 cursor-pointer text-md font-bold text-primary-950 dark:text-white">
Neue Person hinzufügen Neue Person hinzufügen
</summary> </summary>
<div class="grid sm:grid-cols-3 gap-3 mt-3"> <div class="grid sm:grid-cols-3 gap-3 mt-3">
<button type="button" <button type="button"
onclick="document.getElementById('add-clubuser').showModal()" onclick="document.getElementById('add-clubuser').showModal()"
class="btn btn-primary">Vereinsmitglied</button> class="btn btn-primary">🥳 Vereinsmitglied</button>
<button type="button" <button type="button"
onclick="document.getElementById('add-scheckbuch').showModal()" onclick="document.getElementById('add-scheckbuch').showModal()"
class="btn btn-dark">Scheckbuch</button> class="btn btn-dark">🧑‍🏫 Scheckbuch</button>
<button type="button" <button type="button"
onclick="document.getElementById('add-schnupperkurs').showModal()" onclick="document.getElementById('add-schnupperkurs').showModal()"
class="btn btn-dark">Schnupperkurs</button> class="btn btn-dark">👨‍🎓 Schnupperkurs</button>
</div>
<dialog id="add-clubuser"
</div> class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md"
<dialog id="add-clubuser" onclick="document.getElementById('add-clubuser').close()">
class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md" <div onclick="event.stopPropagation();" class="p-3">
onclick="document.getElementById('add-clubuser').close()"> <button type="button"
<div onclick="event.stopPropagation();" class="p-3"> onclick="document.getElementById('add-clubuser').close()"
<button type="button" title="Schließen"
onclick="document.getElementById('add-clubuser').close()" class="sidebar-close border-0 bg-primary-100 focus:bg-primary-50 text-black flex items-center justify-center transform rotate-45 absolute right-0 mr-3">
title="Schließen" <svg class="inline h-5 w-5"
class="sidebar-close border-0 bg-primary-100 focus:bg-primary-50 text-black flex items-center justify-center transform rotate-45 absolute right-0 mr-3"> width="16"
<svg class="inline h-5 w-5" height="16"
width="16" fill="currentColor"
height="16" viewBox="0 0 16 16">
fill="currentColor" <path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"></path>
viewBox="0 0 16 16"> </svg>
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"></path> </button>
</svg> <div class="mt-8">
</button> <h2 class="h3 mb-3">Neues Vereinsmitglied</h2>
<div class="mt-8"> <form action="/admin/user/new/clubmember"
<h2 class="h3 mb-3">Neues Vereinsmitglied</h2> method="post"
<form action="/admin/user/new/clubmember" enctype="multipart/form-data"
method="post" class="grid gap-3">
enctype="multipart/form-data"
class="grid gap-3">
<div> <div>
<label for="membertype" class="text-sm text-gray-600 dark:text-gray-100">Mitgliedstyp</label> <label for="membertype" class="text-sm text-gray-600 dark:text-gray-100">Mitgliedstyp</label>
<select name="membertype" id="membertype" class="input rounded-md "> <select name="membertype" id="membertype" class="input rounded-md ">
@@ -61,83 +58,80 @@
{{ macros::input(label='Adresse', name='address', type="text", required=true) }} {{ macros::input(label='Adresse', name='address', type="text", required=true) }}
{{ macros::input(label='Beitrittserklärung', name='membership_pdf', type="file", accept='application/pdf', required=true) }} {{ macros::input(label='Beitrittserklärung', name='membership_pdf', type="file", accept='application/pdf', required=true) }}
<input value="Neues Vereinsmitglied anlegen" <input value="Neues Vereinsmitglied anlegen"
type="submit" type="submit"
class="btn btn-primary" /> class="btn btn-primary" />
</form> </form>
</div> </div>
</div>
</dialog>
<dialog id="add-scheckbuch"
class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md"
onclick="document.getElementById('add-scheckbuch').close()">
<div onclick="event.stopPropagation();" class="p-3">
<button type="button"
onclick="document.getElementById('add-scheckbuch').close()"
title="Schließen"
class="sidebar-close border-0 bg-primary-100 focus:bg-primary-50 text-black flex items-center justify-center transform rotate-45 absolute right-0 mr-3">
<svg class="inline h-5 w-5"
width="16"
height="16"
fill="currentColor"
viewBox="0 0 16 16">
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"></path>
</svg>
</button>
<div class="mt-8">
<h2 class="h3 mb-3">Neues Scheckbuch</h2>
<form action="/admin/user/new/scheckbuch"
method="post"
enctype="multipart/form-data"
class="grid gap-3">
{{ macros::input(label='Name', name='name', type="text", required=true) }}
{{ macros::input(label='Mailadresse', name='mail', type="email", required=true, placeholder='user@mail.at') }}
<input value="Neues Scheckbuch anlegen"
type="submit"
class="btn btn-primary" />
</form>
</div> </div>
</dialog> </div>
</dialog>
<dialog id="add-scheckbuch" <dialog id="add-schnupperkurs"
class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md" class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md"
onclick="document.getElementById('add-scheckbuch').close()"> onclick="document.getElementById('add-schnupperkurs').close()">
<div onclick="event.stopPropagation();" class="p-3"> <div onclick="event.stopPropagation();" class="p-3">
<button type="button" <button type="button"
onclick="document.getElementById('add-scheckbuch').close()" onclick="document.getElementById('add-schnupperkurs').close()"
title="Schließen" title="Schließen"
class="sidebar-close border-0 bg-primary-100 focus:bg-primary-50 text-black flex items-center justify-center transform rotate-45 absolute right-0 mr-3"> class="sidebar-close border-0 bg-primary-100 focus:bg-primary-50 text-black flex items-center justify-center transform rotate-45 absolute right-0 mr-3">
<svg class="inline h-5 w-5" <svg class="inline h-5 w-5"
width="16" width="16"
height="16" height="16"
fill="currentColor" fill="currentColor"
viewBox="0 0 16 16"> viewBox="0 0 16 16">
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"></path> <path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"></path>
</svg> </svg>
</button> </button>
<div class="mt-8"> <div class="mt-8">
<h2 class="h3 mb-3">Neues Scheckbuch</h2> <form action="/admin/user/new/schnupper"
<form action="/admin/user/new/scheckbuch" method="post"
method="post" enctype="multipart/form-data"
enctype="multipart/form-data" class="grid gap-3">
class="grid gap-3"> <h2 class="h3 mb-3">Neuer Schnupperant</h2>
{{ macros::input(label='Name', name='name', type="text", required=true) }} <div>
{{ macros::input(label='Mailadresse', name='mail', type="email", required=true, placeholder='user@mail.at') }} <label for="schnupper_type" class="text-sm text-gray-600 dark:text-gray-100">Typ</label>
<input value="Neues Scheckbuch anlegen" <select name="schnupper_type" id="schnupper_type" class="input rounded-md ">
type="submit" <option value="schnupperInterested">Interessiert am Schnupperkurs</option>
class="btn btn-primary" /> <option value="schnupperant">Fixe Schnupperkurs-Anmeldung</option>
</form> </select>
</div> </div>
</div> {{ macros::input(label='Name', name='name', type="text", required=true) }}
</dialog> {{ macros::input(label='Mailadresse', name='mail', type="email", required=true, placeholder='user@mail.at') }}
{{ macros::select(label="Finanzielles", data=financial, name='financial_id', display=['name'], default="Keine Ermäßigung") }}
<dialog id="add-schnupperkurs" <input value="Hinzufügen" type="submit" class="btn btn-primary" />
class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md" </form>
onclick="document.getElementById('add-schnupperkurs').close()">
<div onclick="event.stopPropagation();" class="p-3">
<button type="button"
onclick="document.getElementById('add-schnupperkurs').close()"
title="Schließen"
class="sidebar-close border-0 bg-primary-100 focus:bg-primary-50 text-black flex items-center justify-center transform rotate-45 absolute right-0 mr-3">
<svg class="inline h-5 w-5"
width="16"
height="16"
fill="currentColor"
viewBox="0 0 16 16">
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"></path>
</svg>
</button>
<div class="mt-8">
<form action="/admin/user/new/schnupper"
method="post"
enctype="multipart/form-data"
class="grid gap-3">
<h2 class="h3 mb-3">Neuer Schnupperant</h2>
<div>
<label for="schnupper_type" class="text-sm text-gray-600 dark:text-gray-100">Typ</label>
<select name="schnupper_type" id="schnupper_type" class="input rounded-md ">
<option value="schnupperInterested">Interessiert am Schnupperkurs</option>
<option value="schnupperant">Fixe Schnupperkurs-Anmeldung</option>
</select>
</div> </div>
{{ macros::input(label='Name', name='name', type="text", required=true) }} </div>
{{ macros::input(label='Mailadresse', name='mail', type="email", required=true, placeholder='user@mail.at') }} </dialog>
{{ macros::select(label="Finanzielles", data=financial, name='financial_id', display=['name'], default="Keine Ermäßigung") }}
<input value="Hinzufügen" type="submit" class="btn btn-primary" />
</form>
</div>
</div>
</dialog>
</details> </details>
{% endif %} {% endif %}
<!-- START filterBar --> <!-- START filterBar -->

View File

@@ -4,7 +4,9 @@
{% block content %} {% block content %}
<div class="max-w-screen-lg w-full"> <div class="max-w-screen-lg w-full">
{% if "admin" in loggedin_user.roles or "Vorstand" in loggedin_user.roles %} {% if "admin" in loggedin_user.roles or "Vorstand" in loggedin_user.roles %}
<a href="/admin/user" class="link link-primary link-no-underline">&larr; Userverwaltung</a> <div class="mb-5 lg:mb-0">
<a href="/admin/user" class="link link-primary link-no-underline">&larr; Userverwaltung</a>
</div>
{% endif %} {% endif %}
<h1 class="h1">{{ user.name }}</h1> <h1 class="h1">{{ user.name }}</h1>
<div class="grid sm:grid-cols-2 gap-8 my-8"> <div class="grid sm:grid-cols-2 gap-8 my-8">
@@ -119,12 +121,12 @@
</div> </div>
{% if allowed_to_edit %} {% if allowed_to_edit %}
<div class="py-3"> <div class="py-3">
<div class="mt-3 text-right"> <div class="text-right">
<button type="button" <button type="button"
onclick="document.getElementById('change-member-type').showModal()" onclick="document.getElementById('change-member-type').showModal()"
class="btn btn-dark">Mitgliedsstatus ändern</button> class="btn btn-dark">Mitgliedsstatus ändern</button>
<a href="/admin/user/{{ user.id }}/delete" <a href="/admin/user/{{ user.id }}/delete"
class="btn btn-alert" class="btn btn-alert mt-3"
onclick="return confirm('Ist {{ user.name }} wirklich aus dem Verein ausgetreten?');"> onclick="return confirm('Ist {{ user.name }} wirklich aus dem Verein ausgetreten?');">
{% include "includes/delete-icon" %} {% include "includes/delete-icon" %}
Mitglied ist ausgetreten Mitglied ist ausgetreten
@@ -385,9 +387,11 @@
{% endif %} {% endif %}
{% else %} {% else %}
{% if "paid" in user.roles %} {% if "paid" in user.roles %}
{% for key, value in member %}
{% for key, value in member %}
{% if loop.first %}{{ key }}{% endif %} {% if loop.first %}{{ key }}{% endif %}
{% endfor %} hat schon bezahlt {% endfor %}
hat schon bezahlt
{% else %} {% else %}
{% for key, value in member %} {% for key, value in member %}
@@ -402,11 +406,13 @@
{% endif %} {% endif %}
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow"> <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow">
<h2 class="h2">Aktivitäten</h2> <h2 class="h2">Aktivitäten</h2>
<div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600"> <div class="mx-3 max-h-60 overflow-y-scroll">
<div class="py-3"> <div class="py-3">
<ul class="list-disc ms-4"> <ul class="list-disc ms-4">
{% for activity in activities %} {% for activity in activities %}
<li>{{ activity.created_at | date(format="%d. %m. %Y") }}: {{ activity.text }}</li> <li>
<strong>{{ activity.created_at | date(format="%d. %m. %Y") }}:</strong> <small>{{ activity.text }}</small>
</li>
{% else %} {% else %}
<li>Noch keine Aktivität... Stay tuned 😆</li> <li>Noch keine Aktivität... Stay tuned 😆</li>
{% endfor %} {% endfor %}

View File

@@ -183,139 +183,130 @@
<div class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative" <div class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative"
data-filterable="true" data-filterable="true"
data-filter="{{ log.boat.name }} {% for rower in log.rowers %}{{ rower.name }}{% endfor %}"> data-filter="{{ log.boat.name }} {% for rower in log.rowers %}{{ rower.name }}{% endfor %}">
{% if log.logtype and not hide_type %} {% if log.logtype and not hide_type %}
<div class="absolute top-0 right-0 bg-primary-100 rounded-bl-md text-primary-950 text-xs w-32 px-2 py-1 text-center font-bold"> <div class="absolute top-0 right-0 bg-primary-100 rounded-bl-md text-primary-950 text-xs w-32 px-2 py-1 text-center font-bold">
{% if log.logtype == 1 %} {% if log.logtype == 1 %}
Wanderfahrt Wanderfahrt
{% else %} {% else %}
{% if log.logtype == 2 %} {% if log.logtype == 2 %}
Regatta Regatta
{% else %}
{{ log.logtype }}
{% endif %}
{% endif %}
</div>
{% endif %}
<div {% if log.logtype %}class="mt-4 sm:mt-0"{% endif %}>
{% if allowed_to_edit %}
<a href="#"
onclick="document.getElementById('change-{{ log.id }}').showModal()"
class="link link-black font-bold">{{ log.boat.name }}</a>
{% else %}
<strong class="text-black dark:text-white">
{{ log.boat.name }}
</strong>
{% endif %}
<small class="text-gray-600 dark:text-gray-100">({{ log.shipmaster_user.name -}}
{% if log.shipmaster_only_steering %}
- handgesteuert
{%- endif -%}
)</small>
<small class="block text-gray-600 dark:text-gray-100">
{% if state == "completed" and log.departure | date(format='%d.%m.%Y') == log.arrival | date(format='%d.%m.%Y') %}
{{ log.departure | date(format='%d.%m.%Y') }}
({{ log.departure | date(format='%H:%M') }}
-
{{ log.arrival | date(format='%H:%M') }})
{% else %}
{{ log.departure | date(format='%d.%m.%Y (%H:%M)') }}
{% if state == "completed" %}
-
{{ log.arrival | date(format='%d.%m.%Y (%H:%M)') }}
{% endif %}
{% endif %}
</small>
{% set amount_rowers = log.rowers | length %}
{% set amount_guests = log.boat.amount_seats - amount_rowers %}
{% if allowed_to_close and state == "on_water" %}
{{ log::home(log=log) }}
{% else %} {% else %}
<div class="text-black dark:text-white"> {{ log.logtype }}
{{ log.destination }} {% endif %}
{% if state == "completed" %} {% endif %}
<small class="text-gray-600 dark:text-gray-100">({{ log.distance_in_km }} </div>
km)</small> {% endif %}
{% endif %} <div {% if log.logtype %}class="mt-4 sm:mt-0"{% endif %}>
{% if log.comments %}<span class="text-sm italic">- "{{ log.comments }}"</span>{% endif %}
</div>
{% if amount_guests > 0 or log.rowers | length > 0 %}
{% if not log.boat.amount_seats == 1 %}
<div class="text-sm text-gray-600 dark:text-gray-100">
Ruderer:
{% for rower in log.rowers -%}
{{ rower.name }}
{%- if rower.id == log.steering_user.id and rower.id != log.shipmaster_user.id %}
(Steuerperson){%- endif -%}
{%- if not loop.last or amount_guests > 0 and not log.boat.external %},{% endif %}
{% endfor -%}
{% if amount_guests > 0 and not log.boat.external %}
Gäste
<small class="text-gray-600 dark:text-gray-100">(ohne Account)</small>:
{{ amount_guests }}
{% endif %}
</div>
{% endif %}
{% endif %}
{% endif %}
</div>
{% if allowed_to_edit %} {% if allowed_to_edit %}
<dialog id="change-{{ log.id }}" <a href="#"
class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md" onclick="document.getElementById('change-{{ log.id }}').showModal()"
onclick="document.getElementById('change-{{ log.id }}').close()"> class="link link-black font-bold">{{ log.boat.name }}</a>
<div onclick="event.stopPropagation();" class="p-3"> {% else %}
<button type="button" <strong class="text-black dark:text-white">{{ log.boat.name }}</strong>
onclick="document.getElementById('change-{{ log.id }}').close()" {% endif %}
title="Schließen" <small class="text-gray-600 dark:text-gray-100">({{ log.shipmaster_user.name -}}
class="sidebar-close border-0 bg-primary-100 focus:bg-primary-50 text-black flex items-center justify-center transform rotate-45 absolute right-0 mr-3"> {% if log.shipmaster_only_steering %}
<svg class="inline h-5 w-5" - handgesteuert
width="16" {%- endif -%}
height="16" )</small>
fill="currentColor" <small class="block text-gray-600 dark:text-gray-100">
viewBox="0 0 16 16"> {% if state == "completed" and log.departure | date(format='%d.%m.%Y') == log.arrival | date(format='%d.%m.%Y') %}
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"></path> {{ log.departure | date(format='%d.%m.%Y') }}
</svg> ({{ log.departure | date(format='%H:%M') }}
</button> -
<div class="mt-8"> {{ log.arrival | date(format='%H:%M') }})
<h2 class="h3">Eintrag '{{ log.boat.name }}' ändern </h2> {% else %}
<p class="text-center mb-3">ID: {{ log.id }}</p> {{ log.departure | date(format='%d.%m.%Y (%H:%M)') }}
<form action="/log/update" method="post" class="grid gap-3"> {% if state == "completed" %}
<input type="hidden" name="id" value="{{ log.id }}" /> -
<input type="hidden" {{ log.arrival | date(format='%d.%m.%Y (%H:%M)') }}
name="steering_person" {% endif %}
value="{{ log.steering_person }}" /> {% endif %}
{{ macros::select(label="Boot", data=boats, name="boat_id", id="boat_id{{ log.id }}", selected_id=log.boat.id ,display=["name", " (","amount_seats", " x)"]) }} </small>
{{ macros::select(label="Schiffsführer", data=log.rowers, name="shipmaster", id="shipmaster{{ log.id }}", selected_id=log.shipmaster_user.id) }} {% set amount_rowers = log.rowers | length %}
{% set amount_guests = log.boat.amount_seats - amount_rowers %}
{% if allowed_to_close and state == "on_water" %}
{{ macros::checkbox(label='Handgesteuert', name='shipmaster_only_steering', id=log.shipmaster_only_steering,checked=log.shipmaster_only_steering) }} {{ log::home(log=log) }}
<div> {% else %}
<label for="departure" class=" text-sm text-gray-600 dark:text-white "> <div class="text-black dark:text-white">
Abfahrt {{ log.destination }}
</label> {% if state == "completed" %}
<input type="datetime-local" class="input rounded-md" name="departure" value="{{ log.departure }}" /> <small class="text-gray-600 dark:text-gray-100">({{ log.distance_in_km }}
km)</small>
{% endif %}
{% if log.comments %}<span class="text-sm italic">- "{{ log.comments }}"</span>{% endif %}
</div>
{% if amount_guests > 0 or log.rowers | length > 0 %}
{% if not log.boat.amount_seats == 1 %}
<div class="text-sm text-gray-600 dark:text-gray-100">
Ruderer:
{% for rower in log.rowers -%}
{{ rower.name }}
{%- if rower.id == log.steering_user.id and rower.id != log.shipmaster_user.id %}
(Steuerperson){%- endif -%}
{%- if not loop.last or amount_guests > 0 and not log.boat.external %},{% endif %}
{% endfor -%}
{% if amount_guests > 0 and not log.boat.external %}
Gäste
<small class="text-gray-600 dark:text-gray-100">(ohne Account)</small>:
{{ amount_guests }}
{% endif %}
</div>
{% endif %}
{% endif %}
{% endif %}
</div>
{% if allowed_to_edit %}
<dialog id="change-{{ log.id }}"
class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md"
onclick="document.getElementById('change-{{ log.id }}').close()">
<div onclick="event.stopPropagation();" class="p-3">
<button type="button"
onclick="document.getElementById('change-{{ log.id }}').close()"
title="Schließen"
class="sidebar-close border-0 bg-primary-100 focus:bg-primary-50 text-black flex items-center justify-center transform rotate-45 absolute right-0 mr-3">
<svg class="inline h-5 w-5"
width="16"
height="16"
fill="currentColor"
viewBox="0 0 16 16">
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"></path>
</svg>
</button>
<div class="mt-8">
<h2 class="h3">Eintrag '{{ log.boat.name }}' ändern</h2>
<p class="text-center mb-3">{{ log.id }}</p>
<form action="/log/update" method="post" class="grid gap-3">
<input type="hidden" name="id" value="{{ log.id }}" />
<input type="hidden" name="boat_id" value="{{ log.boat_id }}" />
<input type="hidden" name="shipmaster" value="{{ log.shipmaster }}" />
<input type="hidden"
name="steering_person"
value="{{ log.steering_person }}" />
{{ macros::checkbox(label='Handgesteuert', name='shipmaster_only_steering', id=log.shipmaster_only_steering,checked=log.shipmaster_only_steering) }}
<input type="datetime-local"
class="input rounded-md"
name="departure"
value="{{ log.departure }}" />
<input type="datetime-local"
class="input rounded-md"
name="arrival"
value="{{ log.arrival }}" />
<input type="hidden" name="destination" value="{{ log.destination }}" />
<input type="hidden" name="distance_in_km" value="{{ log.distance_in_km }}" />
<input type="hidden" name="comments" value="{{ log.comments }}" />
<input type="hidden" name="logtype" value="{{ log.logtype }}" />
<input type="submit" class="btn btn-primary" value="Updaten" />
</form>
<a href="/log/{{ log.id }}/delete"
class="w-28 btn btn-alert mt-3"
onclick="return confirm('Willst du diesen Logbucheintrag wirklich löschen?');">
{% include "includes/delete-icon" %}
Löschen
</a>
</div> </div>
<div> </div>
<label for="arrival" class=" text-sm text-gray-600 dark:text-white "> </dialog>
Ankunft
</label>
<input type="datetime-local" class="input rounded-md" name="arrival" value="{{ log.arrival }}" />
</div>
<input type="hidden" name="destination" value="{{ log.destination }}" />
<input type="hidden" name="distance_in_km" value="{{ log.distance_in_km }}" />
<input type="hidden" name="comments" value="{{ log.comments }}" />
<input type="hidden" name="logtype" value="{{ log.logtype }}" />
<input type="submit" class="btn btn-primary" value="Updaten" />
</form>
<a href="/log/{{ log.id }}/delete"
class="w-28 btn btn-alert mt-3"
onclick="return confirm('Willst du diesen Logbucheintrag wirklich löschen?');">
{% include "includes/delete-icon" %}
Löschen
</a>
</div>
</div>
</dialog>
{% endif %} {% endif %}
</div> </div>
{% endmacro show_old %} {% endmacro show_old %}

View File

@@ -212,8 +212,9 @@
</h3> </h3>
</summary> </summary>
<div class="mt-3"> <div class="mt-3">
{% if price.level == "DONE" %} {% if achievements.curr_equatorprice_name == "Diamant" %}
Gratuliere, du hast alles in deinem Rudererleben erreicht, was es (beim Äquatorpreis) zu erreichen gibt. Gratuliere, du hast alles in deinem Rudererleben erreicht, was es (beim Äquatorpreis) zu erreichen gibt.
Insgesamt bist du schon stolze {{ price.rowed_km }} km gerudert.
{% else %} {% else %}
<label for="equatorprice" class="label">{{ price.desc }} ({{ price.rowed_km }} / {{ price.required_km }} km)</label> <label for="equatorprice" class="label">{{ price.desc }} ({{ price.rowed_km }} / {{ price.required_km }} km)</label>
<progress id="equatorprice" <progress id="equatorprice"

View File

@@ -26,7 +26,7 @@
{% for log in logs %} {% for log in logs %}
{% set_global allowed_to_edit = false %} {% set_global allowed_to_edit = false %}
{% if loggedin_user %} {% if loggedin_user %}
{% if "Vorstand" in loggedin_user.roles %} {% if "Vorstand" in loggedin_user.roles or "admin" in loggedin_user.roles %}
{% set_global allowed_to_edit = true %} {% set_global allowed_to_edit = true %}
{% endif %} {% endif %}
{% endif %} {% endif %}