Merge branch 'allow-reservation-edits' into staging
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions

# Conflicts:
#	src/tera/boatreservation.rs
This commit is contained in:
Marie Birner 2024-04-23 23:04:46 +02:00
commit be24001b4b
22 changed files with 263 additions and 146 deletions

165
Cargo.lock generated
View File

@ -76,9 +76,9 @@ dependencies = [
[[package]]
name = "allocator-api2"
version = "0.2.16"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "android-tzdata"
@ -174,18 +174,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.60",
]
[[package]]
name = "async-trait"
version = "0.1.79"
version = "0.1.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681"
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.60",
]
[[package]]
@ -326,9 +326,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
[[package]]
name = "cc"
version = "1.0.92"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41"
checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7"
[[package]]
name = "cfg-if"
@ -338,9 +338,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.37"
version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
"android-tzdata",
"iana-time-zone",
@ -349,7 +349,7 @@ dependencies = [
"pure-rust-locales",
"serde",
"wasm-bindgen",
"windows-targets 0.52.4",
"windows-targets 0.52.5",
]
[[package]]
@ -591,9 +591,9 @@ dependencies = [
[[package]]
name = "deunicode"
version = "1.4.3"
version = "1.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6e854126756c496b8c81dec88f9a706b15b875c5849d4097a3854476b9fdf94"
checksum = "322ef0094744e63628e6f0eb2295517f79276a5b342a4c2ff3042566ca181d4e"
[[package]]
name = "devise"
@ -625,7 +625,7 @@ dependencies = [
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
"syn 2.0.58",
"syn 2.0.60",
]
[[package]]
@ -648,9 +648,9 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]]
name = "either"
version = "1.10.0"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
dependencies = [
"serde",
]
@ -673,9 +673,9 @@ checksum = "e2153bd83ebc09db15bcbdc3e2194d901804952e3dc96967e1cd3b0c5c32d112"
[[package]]
name = "encoding_rs"
version = "0.8.33"
version = "0.8.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
dependencies = [
"cfg-if",
]
@ -744,9 +744,9 @@ checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
[[package]]
name = "figment"
version = "0.10.15"
version = "0.10.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7270677e7067213e04f323b55084586195f18308cd7546cfac9f873344ccceb6"
checksum = "d032832d74006f99547004d49410a4b4218e4c33382d56ca3ff89df74f86b953"
dependencies = [
"atomic 0.6.0",
"pear",
@ -891,7 +891,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.60",
]
[[package]]
@ -1681,7 +1681,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.60",
]
[[package]]
@ -1787,7 +1787,7 @@ dependencies = [
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
"syn 2.0.58",
"syn 2.0.60",
]
[[package]]
@ -1836,7 +1836,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.60",
]
[[package]]
@ -1953,9 +1953,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.79"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
dependencies = [
"unicode-ident",
]
@ -1968,7 +1968,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.60",
"version_check",
"yansi",
]
@ -1990,9 +1990,9 @@ checksum = "1190fd18ae6ce9e137184f207593877e70f39b015040156b1e05081cdfe3733a"
[[package]]
name = "quote"
version = "1.0.35"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
@ -2059,7 +2059,7 @@ checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.60",
]
[[package]]
@ -2170,7 +2170,7 @@ dependencies = [
"proc-macro2",
"quote",
"rocket_http",
"syn 2.0.58",
"syn 2.0.60",
"unicode-xid",
"version_check",
]
@ -2384,29 +2384,29 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.197"
version = "1.0.198"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.197"
version = "1.0.198"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.60",
]
[[package]]
name = "serde_json"
version = "1.0.115"
version = "1.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
dependencies = [
"itoa",
"ryu",
@ -2815,9 +2815,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.58"
version = "2.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
dependencies = [
"proc-macro2",
"quote",
@ -2875,7 +2875,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.60",
]
[[package]]
@ -2890,9 +2890,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.34"
version = "0.3.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"deranged",
"itoa",
@ -2911,9 +2911,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
version = "0.2.17"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774"
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
dependencies = [
"num-conv",
"time-core",
@ -2960,7 +2960,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.60",
]
[[package]]
@ -3011,9 +3011,9 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.22.9"
version = "0.22.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4"
checksum = "fb686a972ccef8537b39eead3968b0e8616cb5040dbb9bba93007c8e07c9215f"
dependencies = [
"indexmap",
"serde",
@ -3048,7 +3048,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.60",
]
[[package]]
@ -3325,7 +3325,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.60",
"wasm-bindgen-shared",
]
@ -3347,7 +3347,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.60",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -3420,7 +3420,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.4",
"windows-targets 0.52.5",
]
[[package]]
@ -3438,7 +3438,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.4",
"windows-targets 0.52.5",
]
[[package]]
@ -3458,17 +3458,18 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm 0.52.4",
"windows_aarch64_msvc 0.52.4",
"windows_i686_gnu 0.52.4",
"windows_i686_msvc 0.52.4",
"windows_x86_64_gnu 0.52.4",
"windows_x86_64_gnullvm 0.52.4",
"windows_x86_64_msvc 0.52.4",
"windows_aarch64_gnullvm 0.52.5",
"windows_aarch64_msvc 0.52.5",
"windows_i686_gnu 0.52.5",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.5",
"windows_x86_64_gnu 0.52.5",
"windows_x86_64_gnullvm 0.52.5",
"windows_x86_64_msvc 0.52.5",
]
[[package]]
@ -3479,9 +3480,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
@ -3491,9 +3492,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
@ -3503,9 +3504,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
@ -3515,9 +3522,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
@ -3527,9 +3534,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
@ -3539,9 +3546,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
@ -3551,15 +3558,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "winnow"
version = "0.6.5"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8"
checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352"
dependencies = [
"memchr",
]
@ -3590,7 +3597,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.60",
]
[[package]]

View File

@ -72,5 +72,6 @@ export default defineConfig({
webServer: {
timeout: 15 * 60 * 1000,
command: 'cd .. && ./test_db.sh && cargo r',
url: 'http://127.0.0.1:8000'
},
});

View File

@ -13,3 +13,4 @@
@import 'components/search';
@import 'components/important';
@import 'components/searchable-table';
@import 'components/notification';

View File

@ -0,0 +1,5 @@
.notification {
right: -.2rem;
top: -.1rem;
font-size: .5rem;
}

View File

@ -8,7 +8,7 @@ test("cox can create and delete trip", async ({ page }) => {
await page.getByPlaceholder("Passwort").fill("cox");
await page.getByPlaceholder("Passwort").press("Enter");
await page.locator('li').filter({ hasText: 'Geplante Ausfahrten' }).getByRole('link').click();
await page.locator(".relative").first().click();
await page.locator('a[href="#"]:has-text("Ausfahrt")').first().click();
await page.locator("#sidebar #planned_starting_time").click();
await page.locator("#sidebar #planned_starting_time").fill("18:00");
await page.locator("#sidebar #planned_starting_time").press("Tab");
@ -39,7 +39,7 @@ test.describe("cox can edit trips", () => {
await page.getByPlaceholder("Passwort").fill("cox");
await page.getByPlaceholder("Passwort").press("Enter");
await page.locator('li').filter({ hasText: 'Geplante Ausfahrten' }).getByRole('link').click();
await page.locator(".relative").first().click();
await page.locator('a[href="#"]:has-text("Ausfahrt")').first().click();
await page.locator("#sidebar #planned_starting_time").click();
await page.locator("#sidebar #planned_starting_time").fill("18:00");
await page.locator("#sidebar #planned_starting_time").press("Tab");

View File

@ -1,3 +1,5 @@
use std::collections::HashMap;
use crate::model::{boat::Boat, user::User};
use crate::tera::boatreservation::ReservationEditForm;
use chrono::NaiveDate;
@ -25,7 +27,7 @@ pub struct BoatReservation {
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct BoatReservationWithDetails {
#[serde(flatten)]
boat_reservation: BoatReservation,
reservation: BoatReservation,
boat: Boat,
user_applicant: User,
user_confirmation: Option<User>,
@ -85,7 +87,7 @@ WHERE end_date >= CURRENT_DATE ORDER BY end_date
.unwrap();
res.push(BoatReservationWithDetails {
boat_reservation: reservation,
reservation,
boat,
user_applicant,
user_confirmation,
@ -93,6 +95,31 @@ WHERE end_date >= CURRENT_DATE ORDER BY end_date
}
res
}
pub async fn all_future_with_groups(
db: &SqlitePool,
) -> HashMap<String, Vec<BoatReservationWithDetails>> {
let mut grouped_reservations: HashMap<String, Vec<BoatReservationWithDetails>> =
HashMap::new();
let reservations = Self::all_future(db).await;
for reservation in reservations {
let key = format!(
"{}-{}-{}-{}-{}",
reservation.reservation.start_date,
reservation.reservation.end_date,
reservation.reservation.time_desc,
reservation.reservation.usage,
reservation.user_applicant.name
);
grouped_reservations
.entry(key)
.or_insert_with(Vec::new)
.push(reservation);
}
grouped_reservations
}
pub async fn create(
db: &SqlitePool,

View File

@ -26,7 +26,7 @@ const REGULAR: i32 = 22000;
const UNTERSTUETZEND: i32 = 2500;
const FOERDERND: i32 = 8500;
#[derive(FromRow, Serialize, Deserialize, Clone, Debug)]
#[derive(FromRow, Serialize, Deserialize, Clone, Debug, Eq, Hash)]
pub struct User {
pub id: i64,
pub name: String,
@ -47,16 +47,18 @@ pub struct User {
}
#[derive(Debug, Serialize, Deserialize)]
pub struct UserWithRoles {
pub struct UserWithRolesAndNotificationCount {
#[serde(flatten)]
pub user: User,
pub amount_unread_notifications: i32,
pub roles: Vec<String>,
}
impl UserWithRoles {
impl UserWithRolesAndNotificationCount {
pub async fn from_user(user: User, db: &SqlitePool) -> Self {
Self {
roles: user.roles(db).await,
amount_unread_notifications: user.amount_unread_notifications(db).await,
user,
}
}
@ -237,6 +239,17 @@ impl User {
.count
}
pub async fn amount_unread_notifications(&self, db: &SqlitePool) -> i32 {
sqlx::query!(
"SELECT COUNT(*) as count FROM notification WHERE user_id = ? AND read_at IS NULL",
self.id
)
.fetch_one(db)
.await
.unwrap()
.count
}
pub async fn has_role(&self, db: &SqlitePool, role: &str) -> bool {
if sqlx::query!(
"SELECT * FROM user_role WHERE user_id=? AND role_id = (SELECT id FROM role WHERE name = ?)",
@ -977,8 +990,8 @@ pub struct UserWithRolesAndMembershipPdf {
impl UserWithRolesAndMembershipPdf {
pub(crate) async fn from_user(db: &SqlitePool, user: User) -> Self {
let membership_pdf: bool =
sqlx::query_scalar!("SELECT membership_pdf FROM user WHERE id = $1", user.id)
.fetch_optional(db)
sqlx::query_scalar!("SELECT membership_pdf FROM user WHERE id = ?", user.id)
.fetch_one(db)
.await
.unwrap()
.is_some();

View File

@ -2,7 +2,7 @@ use crate::model::{
boat::{Boat, BoatToAdd, BoatToUpdate},
location::Location,
log::Log,
user::{AdminUser, User, UserWithRoles},
user::{AdminUser, User, UserWithRolesAndNotificationCount},
};
use rocket::{
form::Form,
@ -33,7 +33,7 @@ async fn index(
context.insert("users", &users);
context.insert(
"loggedin_user",
&UserWithRoles::from_user(admin.user, db).await,
&UserWithRolesAndNotificationCount::from_user(admin.user, db).await,
);
Template::render("admin/boat/index", context.into_json())

View File

@ -10,7 +10,7 @@ use crate::model::log::Log;
use crate::model::mail::Mail;
use crate::model::role::Role;
use crate::model::user::AdminUser;
use crate::model::user::UserWithRoles;
use crate::model::user::UserWithRolesAndNotificationCount;
use crate::tera::Config;
#[get("/mail")]
@ -27,7 +27,7 @@ async fn index(
context.insert(
"loggedin_user",
&UserWithRoles::from_user(admin.user, db).await,
&UserWithRolesAndNotificationCount::from_user(admin.user, db).await,
);
context.insert("roles", &roles);

View File

@ -2,7 +2,7 @@ use crate::model::{
log::Log,
notification::Notification,
role::Role,
user::{AdminUser, User, UserWithRoles},
user::{AdminUser, User, UserWithRolesAndNotificationCount},
};
use rocket::{
form::Form,
@ -26,7 +26,7 @@ async fn index(
}
context.insert(
"loggedin_user",
&UserWithRoles::from_user(user.user, db).await,
&UserWithRolesAndNotificationCount::from_user(user.user, db).await,
);
context.insert("roles", &Role::all(db).await);

View File

@ -1,6 +1,6 @@
use crate::model::{
role::Role,
user::{SchnupperBetreuerUser, User, UserWithRoles},
user::{SchnupperBetreuerUser, User, UserWithRolesAndNotificationCount},
};
use futures::future::join_all;
use rocket::{
@ -38,9 +38,9 @@ async fn index(
let user_futures: Vec<_> = User::all_with_role(db, &schnupperant)
.await
.into_iter()
.map(|u| async move { UserWithRoles::from_user(u, db).await })
.map(|u| async move { UserWithRolesAndNotificationCount::from_user(u, db).await })
.collect();
let users: Vec<UserWithRoles> = join_all(user_futures).await;
let users: Vec<UserWithRolesAndNotificationCount> = join_all(user_futures).await;
let mut context = Context::new();
if let Some(msg) = flash {
@ -49,7 +49,7 @@ async fn index(
context.insert("schnupperanten", &users);
context.insert(
"loggedin_user",
&UserWithRoles::from_user(user.into(), db).await,
&UserWithRolesAndNotificationCount::from_user(user.into(), db).await,
);
Template::render("admin/schnupper/index", context.into_json())

View File

@ -6,8 +6,8 @@ use crate::model::{
logbook::Logbook,
role::Role,
user::{
AdminUser, User, UserWithMembershipPdf, UserWithRoles, UserWithRolesAndMembershipPdf,
VorstandUser,
AdminUser, User, UserWithMembershipPdf, UserWithRolesAndMembershipPdf,
UserWithRolesAndNotificationCount, VorstandUser,
},
};
use futures::future::join_all;
@ -67,7 +67,10 @@ async fn index(
context.insert("users", &users);
context.insert("roles", &roles);
context.insert("families", &families);
context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await);
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user, db).await,
);
Template::render("admin/user/index", context.into_json())
}
@ -99,7 +102,10 @@ async fn index_admin(
context.insert("users", &users);
context.insert("roles", &roles);
context.insert("families", &families);
context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await);
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user, db).await,
);
Template::render("admin/user/index", context.into_json())
}
@ -127,7 +133,7 @@ async fn fees(
}
context.insert(
"loggedin_user",
&UserWithRoles::from_user(admin.into(), db).await,
&UserWithRolesAndNotificationCount::from_user(admin.into(), db).await,
);
Template::render("admin/user/fees", context.into_json())
@ -147,7 +153,7 @@ async fn scheckbuch(
for s in scheckbooks {
scheckbooks_with_roles.push((
Logbook::completed_with_user(db, &s).await,
UserWithRoles::from_user(s, db).await,
UserWithRolesAndNotificationCount::from_user(s, db).await,
))
}
@ -158,7 +164,7 @@ async fn scheckbuch(
}
context.insert(
"loggedin_user",
&UserWithRoles::from_user(user.into(), db).await,
&UserWithRolesAndNotificationCount::from_user(user.into(), db).await,
);
Template::render("admin/user/scheckbuch", context.into_json())

View File

@ -1,7 +1,7 @@
use crate::model::{
boat::Boat,
boathouse::Boathouse,
user::{AdminUser, UserWithRoles, VorstandUser},
user::{AdminUser, UserWithRolesAndNotificationCount, VorstandUser},
};
use rocket::{
form::Form,
@ -39,7 +39,7 @@ async fn index(
context.insert(
"loggedin_user",
&UserWithRoles::from_user(admin.into(), db).await,
&UserWithRolesAndNotificationCount::from_user(admin.into(), db).await,
);
Template::render("board/boathouse", context.into_json())

View File

@ -13,7 +13,7 @@ use crate::{
model::{
boat::Boat,
boatdamage::{BoatDamage, BoatDamageFixed, BoatDamageToAdd, BoatDamageVerified},
user::{CoxUser, DonauLinzUser, TechUser, User, UserWithRoles},
user::{CoxUser, DonauLinzUser, TechUser, User, UserWithRolesAndNotificationCount},
},
tera::log::KioskCookie,
};
@ -59,7 +59,7 @@ async fn index(
context.insert("boats", &boats);
context.insert(
"loggedin_user",
&UserWithRoles::from_user(user.into(), db).await,
&UserWithRolesAndNotificationCount::from_user(user.into(), db).await,
);
Template::render("boatdamages", context.into_json())

View File

@ -75,7 +75,7 @@ async fn index(
context.insert("user", &User::all(db).await);
context.insert(
"loggedin_user",
&UserWithRoles::from_user(user.into(), db).await,
&UserWithRolesAndNotificationCount::from_user(user.into(), db).await,
);
Template::render("boatreservations", context.into_json())

View File

@ -18,7 +18,7 @@ use tera::Context;
use crate::model::{
log::Log,
user::{AdminUser, User, UserWithRoles},
user::{AdminUser, User, UserWithRolesAndNotificationCount},
};
#[derive(Serialize)]
@ -51,7 +51,7 @@ async fn send(db: &State<SqlitePool>, _user: AdminUser) -> Template {
Template::render(
"ergo.final",
context!(loggedin_user: &UserWithRoles::from_user(_user.user, db).await, thirty, dozen),
context!(loggedin_user: &UserWithRolesAndNotificationCount::from_user(_user.user, db).await, thirty, dozen),
)
}
@ -120,7 +120,10 @@ async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await);
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user, db).await,
);
context.insert("users", &users);
context.insert("thirty", &thirty);
context.insert("dozen", &dozen);

View File

@ -24,7 +24,9 @@ use crate::model::{
LogbookUpdateError,
},
logtype::LogType,
user::{AdminUser, DonauLinzUser, User, UserWithRoles, UserWithWaterStatus},
user::{
AdminUser, DonauLinzUser, User, UserWithRolesAndNotificationCount, UserWithWaterStatus,
},
};
pub struct KioskCookie(String);
@ -78,13 +80,16 @@ async fn index(
}
context.insert("boats", &boats);
context.insert("reservations", &BoatReservation::all_future(db).await);
context.insert(
"reservations",
&BoatReservation::all_future_with_groups(db).await,
);
context.insert("coxes", &coxes);
context.insert("users", &users);
context.insert("logtypes", &logtypes);
context.insert(
"loggedin_user",
&UserWithRoles::from_user(user.into(), db).await,
&UserWithRolesAndNotificationCount::from_user(user.into(), db).await,
);
context.insert("on_water", &on_water);
context.insert("distances", &distances);
@ -98,7 +103,7 @@ async fn show(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
Template::render(
"log.completed",
context!(logs, loggedin_user: &UserWithRoles::from_user(user.into(), db).await),
context!(logs, loggedin_user: &UserWithRolesAndNotificationCount::from_user(user.into(), db).await),
)
}
@ -108,7 +113,7 @@ async fn show_for_year(db: &State<SqlitePool>, user: AdminUser, year: i32) -> Te
Template::render(
"log.completed",
context!(logs, loggedin_user: &UserWithRoles::from_user(user.user, db).await),
context!(logs, loggedin_user: &UserWithRolesAndNotificationCount::from_user(user.user, db).await),
)
}
@ -169,7 +174,10 @@ async fn kiosk(
}
context.insert("boats", &boats);
context.insert("reservations", &BoatReservation::all_future(db).await);
context.insert(
"reservations",
&BoatReservation::all_future_with_groups(db).await,
);
context.insert("coxes", &coxes);
context.insert("users", &users);
context.insert("logtypes", &logtypes);

View File

@ -23,7 +23,7 @@ use tera::Context;
use crate::model::{
notification::Notification,
role::Role,
user::{User, UserWithRoles},
user::{User, UserWithRolesAndNotificationCount},
};
pub(crate) mod admin;
@ -53,7 +53,10 @@ async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_
}
context.insert("notifications", &Notification::for_user(db, &user).await);
context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await);
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user, db).await,
);
Template::render("index", context.into_json())
}
@ -75,7 +78,10 @@ async fn steering(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage
context.insert("coxes", &coxes);
context.insert("bootskundige", &bootskundige);
context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await);
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user, db).await,
);
Template::render("steering", context.into_json())
}

View File

@ -13,7 +13,7 @@ use crate::model::{
logbook::Logbook,
tripdetails::TripDetails,
triptype::TripType,
user::{AllowedForPlannedTripsUser, User, UserWithRoles},
user::{AllowedForPlannedTripsUser, User, UserWithRolesAndNotificationCount},
usertrip::{UserTrip, UserTripDeleteError, UserTripError},
};
@ -44,7 +44,10 @@ async fn index(
}
context.insert("fee", &user.fee(db).await);
context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await);
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user, db).await,
);
context.insert("days", &days);
Template::render("planned", context.into_json())
}

View File

@ -4,7 +4,7 @@ use sqlx::SqlitePool;
use crate::model::{
stat::{self, BoatStat, Stat},
user::{DonauLinzUser, UserWithRoles},
user::{DonauLinzUser, UserWithRolesAndNotificationCount},
};
use super::log::KioskCookie;
@ -16,7 +16,7 @@ async fn index_boat(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
Template::render(
"stat.boats",
context!(loggedin_user: &UserWithRoles::from_user(user.into(), db).await, stat, kiosk),
context!(loggedin_user: &UserWithRolesAndNotificationCount::from_user(user.into(), db).await, stat, kiosk),
)
}
@ -38,7 +38,7 @@ async fn index(db: &State<SqlitePool>, user: DonauLinzUser, year: Option<i32>) -
Template::render(
"stat.people",
context!(loggedin_user: &UserWithRoles::from_user(user.into(), db).await, stat, personal, kiosk, guest_km, club_km),
context!(loggedin_user: &UserWithRolesAndNotificationCount::from_user(user.into(), db).await, stat, personal, kiosk, guest_km, club_km),
)
}

View File

@ -2,7 +2,8 @@
<div class="bg-white dark:bg-primary-900 rounded-md shadow pb-2 mt-3">
<h2 class="h2">Reservierungen ({{ reservations | length }})</h2>
<div class="grid grid-cols-1 gap-3 mb-3 w-full">
{% for reservation in reservations %}
{% for _, reservations_for_event in reservations %}
{% set reservation = reservations_for_event[0] %}
<div class="pt-2 px-3 border-t text-primary-900 dark:text-white">
<strong class="block">
{{ reservation.start_date | date(format="%d.%m.%Y") }}
@ -13,7 +14,10 @@
<small>({{ reservation.time_desc }})</small>
</strong>
<span class="block">
{% for reservation in reservations_for_event %}
{{ reservation.boat.name }}
{% if not loop.last %}+{% endif %}
{% endfor %}
<small>({{ reservation.user_applicant.name }})</small>
</span>
<span class="text-sm italic">{{ reservation.usage }}</span>
@ -33,7 +37,24 @@
{{ loggedin_user.name }}
</a>
</div>
<div>
<div class="flex items-center">
{% if loggedin_user.amount_unread_notifications > 0 %}
<a href="/#notification"
class="relative inline-flex items-end ms-2 me-3">
<svg height="20"
width="24"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 20 24">
<path d="M1.5 8.67v8.58a3 3 0 0 0 3 3h15a3 3 0 0 0 3-3V8.67l-8.928 5.493a3 3 0 0 1-3.144 0L1.5 8.67Z" />
<path d="M22.5 6.908V6.75a3 3 0 0 0-3-3h-15a3 3 0 0 0-3 3v.158l9.714 5.978a1.5 1.5 0 0 0 1.572 0L22.5 6.908Z" />
</svg>
<small class="bg-red-500 rounded-full w-3 h-3 inline-flex justify-center items-center absolute p-1 notification">
{{ loggedin_user.amount_unread_notifications }}
</small>
</a>
{% endif %}
<a href="#"
class="inline-flex justify-center rounded-md bg-primary-600 mx-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer"
data-sidebar="true"
@ -163,18 +184,24 @@
<div class="p-2 border border-t-0 border-{{ bg }} mb-4 rounded-b-md">
{% if participants | length > 0 %}
{% for rower in participants %}
<div class="relative">
{{ rower.name }}
{% if rower.is_guest %}<small class="text-gray-600 dark:text-gray-100">(Scheckbuch)</small>{% endif %}
{% if rower.is_real_guest %}
<small class="text-gray-600 dark:text-gray-100">(Gast)</small>
{% if allow_removing %}
<a href="/planned/remove/{{ trip_details_id }}/{{ rower.name }}"
class="btn btn-attention btn-fw">Abmelden</a>
class="absolute r-0 bg-red-500 w-5 h-5 text-white rounded-full flex items-center justify-center transform rotate-45 top-0 right-0">
<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>
<span class="sr-only">Abmelden</span>
</a>
{% endif %}
{% endif %}
<span class="hidden">(angemeldet seit
{{ rower.registered_at }})</span>
<br />
</div>
{% endfor %}
{% else %}
{{ text }}

View File

@ -2,7 +2,11 @@
{% extends "base" %}
{% block content %}
<div class="max-w-screen-lg w-full">
<h1 class="h1">Ruderassistent</h1>
<h1 class="h1">
Ruder
<wbr />
assistent
</h1>
<div class="grid gap-3 my-5">
<div class="m-auto">
<a href="/planned"
@ -11,9 +15,15 @@
<span class="text-xl px-3">Geplante Ausfahrten</span>
</a>
</div>
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
<div id="notification"
class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
role="alert">
<h2 class="h2">Nachrichten</h2>
{% if loggedin_user.amount_unread_notifications > 10 %}
<div class="text-primary-950 dark:text-white bg-gray-200 dark:bg-primary-950 bg-opacity-80 text-center pb-3 px-3">
Du hast viele ungelesene Benachrichtigungen. Um deine Oberfläche übersichtlich zu halten und wichtige Updates nicht zu verpassen, nimm dir bitte einen Moment Zeit sie zu überprüfen und als gelesen zu markieren (&#10003;).
</div>
{% endif %}
<div class="divide-y">
{% for notification in notifications %}
{% if not notification.read_at %}
@ -38,8 +48,8 @@
{% endif %}
{% endfor %}
</div>
<details class="py-3 bg-gray-200 dark:bg-primary-950 rounded-b-md">
<summary class="px-3">Vergangene Nachrichten (14 Tage)</summary>
<details class="py-3 border-t rounded-b-md">
<summary class="px-3 cursor-pointer">Vergangene Nachrichten (14 Tage)</summary>
<div class="divide-y text-sm">
{% for notification in notifications %}
{% if notification.read_at %}