2 Commits

Author SHA1 Message Date
a241b9f1d8 fix ci?
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-10-01 23:26:18 +02:00
1de7a6fb33 fix ci?
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-10-01 22:53:59 +02:00
8 changed files with 88 additions and 98 deletions

View File

@@ -17,9 +17,6 @@ jobs:
- name: Run Test DB Script - name: Run Test DB Script
run: ./test_db.sh run: ./test_db.sh
- name: Test
run: npm --version
- name: Cache Cargo dependencies - name: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2

View File

@@ -425,8 +425,10 @@ function initNewChoice(select: HTMLInputElement) {
maxItemText: (maxItemCount) => { maxItemText: (maxItemCount) => {
return `Nur ${maxItemCount} Ruderer können hinzugefügt werden`; return `Nur ${maxItemCount} Ruderer können hinzugefügt werden`;
}, },
callbackOnInit: function () { callbackOnInit: function (this: Choices) {
this._currentState.items.forEach(function (obj) { const items = this.getValue(true); // Get all selected items
const itemsArray = Array.isArray(items) ? items : [items];
itemsArray.forEach((obj: any) => {
if (boat_in_ottensheim && obj.customProperties) { if (boat_in_ottensheim && obj.customProperties) {
if (obj.customProperties.is_racing) { if (obj.customProperties.is_racing) {
const coxSelect = <HTMLSelectElement>( const coxSelect = <HTMLSelectElement>(

View File

@@ -9,20 +9,20 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.40.1", "@playwright/test": "^1.55.1",
"@types/d3": "^7.4.1", "@types/d3": "^7.4.3",
"@types/node": "^20.11.4", "@types/node": "^24.6.1",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.21",
"postcss": "^8.4.21", "postcss": "^8.5.6",
"sass": "^1.60.0", "sass": "^1.93.2",
"tailwindcss": "^3.3.1", "tailwindcss": "^3.4.18",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vite": "^4.2.0", "vite": "^7.1.7",
"vite-plugin-static-copy": "^0.13.1" "vite-plugin-static-copy": "^3.1.3"
}, },
"dependencies": { "dependencies": {
"choices.js": "^10.2.0", "choices.js": "^11.1.0",
"d3": "^7.8.5", "d3": "^7.9.0",
"terser": "^5.21.0" "terser": "^5.44.0"
} }
} }

View File

@@ -18,16 +18,20 @@ test("Cox can start and cancel trip", async ({ page }, testInfo) => {
await page.getByText('2x', { exact: true }).click(); await page.getByText('2x', { exact: true }).click();
await page.getByText("Joe", { exact: true }).click(); await page.getByText("Joe", { exact: true }).click();
} }
await page.getByLabel('Remove item: \'6\'').click(); // remove pre-filled cox2 await page.getByRole('button', { name: 'Remove item:' }).click(); // remove pre-filled cox2
await page.getByPlaceholder("Ruderer auswählen").click(); await page.getByPlaceholder("Ruderer auswählen").click();
await page.getByRole("option", { name: "rower2" }).click(); await page.getByRole("option", { name: "rower2" }).click();
await page.getByRole("option", { name: "cox2" }).click(); await page.getByRole("option", { name: "cox2" }).click();
await expect(page.getByRole("listbox")).toContainText( await expect(page.locator("#form")).toContainText(
"Nur 2 Ruderer können hinzugefügt werden", "Nur 2 Ruderer können hinzugefügt werden",
); );
await expect(page.locator("#shipmaster-newrowerjs")).toContainText("cox"); await expect(page.locator("#shipmaster-newrowerjs")).toContainText("cox");
await expect(page.locator("#steering_person-newrowerjs")).toContainText( await expect(page.locator("#steering_person-newrowerjs")).toContainText(
"rower2 cox", "rower2",
);
await expect(page.locator("#steering_person-newrowerjs")).toContainText(
"cox2",
); );
await page.getByRole("button", { name: "Ausfahrt eintragen" }).click(); await page.getByRole("button", { name: "Ausfahrt eintragen" }).click();
await expect(page.locator("body")).toContainText( await expect(page.locator("body")).toContainText(
@@ -60,11 +64,11 @@ test("Cox can start and finish trip", async ({ page }, testInfo) => {
await page.getByText('2x', { exact: true }).click(); await page.getByText('2x', { exact: true }).click();
await page.getByText("Joe", { exact: true }).click(); await page.getByText("Joe", { exact: true }).click();
} }
await page.getByLabel('Remove item: \'6\'').click(); // remove pre-filled cox2 await page.getByRole('button', { name: 'Remove item:' }).click(); // remove pre-filled cox2
await page.getByPlaceholder("Ruderer auswählen").click(); await page.getByPlaceholder("Ruderer auswählen").click();
await page.getByRole("option", { name: "rower2" }).click(); await page.getByRole("option", { name: "rower2" }).click();
await page.getByRole("option", { name: "cox2" }).click(); await page.getByRole("option", { name: "cox2" }).click();
await expect(page.getByRole("listbox")).toContainText( await expect(page.locator("#form")).toContainText(
"Nur 2 Ruderer können hinzugefügt werden", "Nur 2 Ruderer können hinzugefügt werden",
); );
@@ -79,8 +83,12 @@ test("Cox can start and finish trip", async ({ page }, testInfo) => {
await expect(page.locator("#shipmaster-newrowerjs")).toContainText("cox"); await expect(page.locator("#shipmaster-newrowerjs")).toContainText("cox");
await expect(page.locator("#steering_person-newrowerjs")).toContainText( await expect(page.locator("#steering_person-newrowerjs")).toContainText(
"rower2 cox", "rower2",
); );
await expect(page.locator("#steering_person-newrowerjs")).toContainText(
"cox",
);
await page.getByRole("button", { name: "Ausfahrt eintragen" }).click(); await page.getByRole("button", { name: "Ausfahrt eintragen" }).click();
await expect(page.locator("body")).toContainText( await expect(page.locator("body")).toContainText(
"Ausfahrt erfolgreich hinzugefügt", "Ausfahrt erfolgreich hinzugefügt",

View File

@@ -860,7 +860,6 @@ special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch", +"Förde
special_user!(DonauLinzUser, +"Donau Linz", +"Förderndes Mitglied", -"Unterstützend"); // TODO: special_user!(DonauLinzUser, +"Donau Linz", +"Förderndes Mitglied", -"Unterstützend"); // TODO:
// remove -> // remove ->
// RegularUser // RegularUser
special_user!(ErgoAdminUser, +"ergo-admin", +"admin");
special_user!(SchnupperBetreuerUser, +"schnupper-betreuer"); special_user!(SchnupperBetreuerUser, +"schnupper-betreuer");
special_user!(VorstandUser, +"admin", +"Vorstand"); special_user!(VorstandUser, +"admin", +"Vorstand");
special_user!(EventUser, +"manage_events"); special_user!(EventUser, +"manage_events");

View File

@@ -1,7 +1,8 @@
use std::env; use std::env;
use chrono::{Datelike, Utc}; use chrono::Utc;
use rocket::{ use rocket::{
FromForm, Route, State,
form::Form, form::Form,
fs::TempFile, fs::TempFile,
get, get,
@@ -9,19 +10,18 @@ use rocket::{
post, post,
request::FlashMessage, request::FlashMessage,
response::{Flash, Redirect}, response::{Flash, Redirect},
routes, FromForm, Route, State, routes,
}; };
use rocket_dyn_templates::{context, Template}; use rocket_dyn_templates::{Template, context};
use serde::Serialize; use serde::Serialize;
use sqlx::SqlitePool; use sqlx::SqlitePool;
use tera::Context; use tera::Context;
use crate::model::{ use crate::model::{
activity::ActivityBuilder,
log::Log, log::Log,
notification::Notification, notification::Notification,
role::Role, role::Role,
user::{AdminUser, ErgoAdminUser, User, UserWithDetails}, user::{AdminUser, User, UserWithDetails},
}; };
#[derive(Serialize)] #[derive(Serialize)]
@@ -59,7 +59,7 @@ async fn send(db: &State<SqlitePool>, _user: AdminUser) -> Template {
} }
#[get("/reset")] #[get("/reset")]
async fn reset(db: &State<SqlitePool>, _user: ErgoAdminUser) -> Flash<Redirect> { async fn reset(db: &State<SqlitePool>, _user: AdminUser) -> Flash<Redirect> {
sqlx::query!("UPDATE user SET dirty_thirty = NULL, dirty_dozen = NULL;") sqlx::query!("UPDATE user SET dirty_thirty = NULL, dirty_dozen = NULL;")
.execute(db.inner()) .execute(db.inner())
.await .await
@@ -74,7 +74,7 @@ async fn reset(db: &State<SqlitePool>, _user: ErgoAdminUser) -> Flash<Redirect>
#[get("/<challenge>/user/<user_id>/new?<new>")] #[get("/<challenge>/user/<user_id>/new?<new>")]
async fn update( async fn update(
db: &State<SqlitePool>, db: &State<SqlitePool>,
_admin: ErgoAdminUser, _admin: AdminUser,
challenge: &str, challenge: &str,
user_id: i64, user_id: i64,
new: &str, new: &str,
@@ -146,61 +146,47 @@ pub struct UserAdd {
sex: String, sex: String,
} }
#[post("/set-data", data = "<data>")] //#[post("/set-data", data = "<data>")]
async fn new_user(db: &State<SqlitePool>, data: Form<UserAdd>, user: User) -> Flash<Redirect> { //async fn new_user(db: &State<SqlitePool>, data: Form<UserAdd>, user: User) -> Flash<Redirect> {
if user.has_role(db, "ergo").await { // if user.has_role(db, "ergo").await {
return Flash::error(Redirect::to("/ergo"), "Du hast deine Daten schon eingegeben. Wenn du sie updaten willst, melde dich bitte bei info@rudernlinz.at"); // return Flash::error(Redirect::to("/ergo"), "Du hast deine Daten schon eingegeben. Wenn du sie updaten willst, melde dich bitte bei it@rudernlinz.at");
} // }
//
// check data // // check data
if data.birthyear < 1900 || data.birthyear > chrono::Utc::now().year() - 5 { // if data.birthyear < 1900 || data.birthyear > chrono::Utc::now().year() - 5 {
return Flash::error(Redirect::to("/ergo"), "Bitte überprüfe dein Geburtsjahr..."); // return Flash::error(Redirect::to("/ergo"), "Bitte überprüfe dein Geburtsjahr...");
} // }
if data.weight < 20 || data.weight > 200 { // if data.weight < 20 || data.weight > 200 {
return Flash::error(Redirect::to("/ergo"), "Bitte überprüfe dein Gewicht..."); // return Flash::error(Redirect::to("/ergo"), "Bitte überprüfe dein Gewicht...");
} // }
if &data.sex != "f" && &data.sex != "m" { // if &data.sex != "f" && &data.sex != "m" {
return Flash::error(Redirect::to("/ergo"), "Bitte überprüfe dein Geschlecht..."); // return Flash::error(Redirect::to("/ergo"), "Bitte überprüfe dein Geschlecht...");
} // }
//
// set data // // set data
user.update_ergo(db, data.birthyear, data.weight, &data.sex) // user.update_ergo(db, data.birthyear, data.weight, &data.sex)
.await; // .await;
//
// inform all other `ergo` users // // inform all other `ergo` users
let ergo = Role::find_by_name(db, "ergo").await.unwrap(); // let ergo = Role::find_by_name(db, "ergo").await.unwrap();
Notification::create_for_role( // Notification::create_for_role(
db, // db,
&ergo, // &ergo,
&format!("{} nimmt heuer an der Ergochallenge teil 💪", user.name), // &format!("{} nimmt heuer an der Ergochallenge teil 💪", user.name),
"Ergo Challenge", // "Ergo Challenge",
None, // None,
None, // None,
) // )
.await; // .await;
//
// add to `ergo` group // // add to `ergo` group
sqlx::query!( // user.add_role(db, &ergo).await.unwrap();
"INSERT INTO user_role(user_id, role_id) VALUES (?, ?)", //
user.id, // Flash::success(
ergo.id // Redirect::to("/ergo"),
) // "Du hast deine Daten erfolgreich eingegeben. Viel Spaß beim Schwitzen :-)",
.execute(db.inner()) // )
.await //}
.unwrap();
ActivityBuilder::new(&format!(
"{user} nimmt an der Ergo-Challenge teil und hat gerade die Daten eingegeben."
))
.user(&user)
.save(db)
.await;
Flash::success(
Redirect::to("/ergo"),
"Du hast deine Daten erfolgreich eingegeben. Viel Spaß beim Schwitzen :-)",
)
}
#[derive(FromForm, Debug)] #[derive(FromForm, Debug)]
pub struct ErgoToAdd<'a> { pub struct ErgoToAdd<'a> {
@@ -373,7 +359,10 @@ async fn new_dozen(
} }
pub fn routes() -> Vec<Route> { pub fn routes() -> Vec<Route> {
routes![index, new_thirty, new_dozen, send, reset, update, new_user] routes![
index, new_thirty, new_dozen, send, reset, update,
// new_user
]
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -65,7 +65,7 @@ async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_
let date = chrono::Utc::now(); let date = chrono::Utc::now();
if date.month() <= 3 || date.month() >= 10 { if date.month() <= 3 || date.month() >= 10 {
context.insert("show_quick_ergo_button", "yes"); //context.insert("show_quick_ergo_button", "yes");
} }
context.insert("achievements", &Achievements::for_user(db, &user).await); context.insert("achievements", &Achievements::for_user(db, &user).await);

View File

@@ -15,7 +15,10 @@
class="link-primary">Überblick der Challenges</a> class="link-primary">Überblick der Challenges</a>
</li> </li>
<li class="py-1"> <li class="py-1">
Eintragung ist jederzeit möglich, wenn du sie auch an die offizielle Liste schicken willst, kannst du das <a href="https://data.ergochallenge.at/" target="_blank" style="text-decoration: underline">hier</a> machen Eintragung ist jederzeit möglich, alle Daten die bis Sonntag 23:59 hier hochgeladen wurden, werden gesammelt an die Ister Ergo Challenge geschickt
<li class="py-1">
Montag &rarr; gemeinsames Training; bitte um <a href="/planned" class="link-primary">Anmeldung</a>, damit jeder einen Ergo hat
</li>
<li class="py-1"> <li class="py-1">
<a href="https://data.ergochallenge.at" <a href="https://data.ergochallenge.at"
target="_blank" target="_blank"
@@ -191,7 +194,7 @@
</div> </div>
</details> </details>
</div> </div>
{% if "admin" in loggedin_user.roles or "ergo-admin" in loggedin_user.roles %} {% if "admin" in loggedin_user.roles %}
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow grid gap-3"> <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow grid gap-3">
<h2 class="h2">Update</h2> <h2 class="h2">Update</h2>
<details class="p-2"> <details class="p-2">
@@ -230,14 +233,6 @@
</ol> </ol>
</div> </div>
</details> </details>
<div class="mt-3 text-right">
<a href="/ergo/reset"
class="w-28 btn btn-alert"
onclick="return confirm('Willst du wirklich alle Ergo-Eingaben löschen?');">
{% include "includes/delete-icon" %}
Einträge löschen
</a>
</div>
</div> </div>
</div> </div>
{% endif %} {% endif %}