15 Commits

Author SHA1 Message Date
philipp 0f8a06d62b Merge pull request 'Merge pull request 'clean families; Fixes #1171' (#1212) from clean-families into main' (#1216) from unfix-damage into staging
CI/CD Pipeline / test (push) Successful in 26m45s
CI/CD Pipeline / deploy-staging (push) Successful in 33m17s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #1216
2026-05-24 10:43:47 +02:00
philipp 743b296895 be able to unfix a boat damage for tech
CI/CD Pipeline / test (push) Successful in 26m39s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2026-05-24 10:38:54 +02:00
philipp c862b28552 Merge pull request 'clean families; Fixes #1171' (#1212) from clean-families into main
CI/CD Pipeline / test (push) Successful in 24m15s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #1212
2026-05-03 12:10:07 +02:00
philipp e65c34688f Merge pull request 'Merge pull request 'fix ci' (#1210) from fix-cii into main' (#1211) from clean-families into staging
CI/CD Pipeline / test (push) Successful in 22m30s
CI/CD Pipeline / deploy-staging (push) Successful in 10m22s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #1211
2026-05-03 12:10:05 +02:00
philipp 60a6a99380 clean families; Fixes #1171
CI/CD Pipeline / test (push) Successful in 15m58s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2026-05-03 12:08:40 +02:00
philipp dc822585cd Merge pull request 'fix ci' (#1210) from fix-cii into main
CI/CD Pipeline / test (push) Successful in 22m2s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 30m49s
Reviewed-on: #1210
2026-05-02 22:56:46 +02:00
philipp 987feacbbe Merge pull request 'clean /log by only showing boat reservation for the next 3 days' (#1209) from fix-cii into staging
CI/CD Pipeline / test (push) Successful in 25m7s
CI/CD Pipeline / deploy-staging (push) Successful in 39m56s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #1209
2026-05-02 22:56:39 +02:00
philipp ad0da68628 fix ci
CI/CD Pipeline / test (push) Successful in 37m6s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2026-05-02 22:55:29 +02:00
philipp 629c107277 Merge pull request 'fix ci' (#1208) from fix-ci into main
CI/CD Pipeline / test (push) Failing after 28m30s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #1208
2026-05-01 08:35:40 +02:00
philipp c1fc75b187 Merge pull request 'fix ci' (#1207) from fix-ci into staging
CI/CD Pipeline / test (push) Failing after 26m35s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #1207
2026-05-01 08:35:29 +02:00
philipp fbd9115eb2 Merge pull request 'clean /log by only showing boat reservation for the next 3 days' (#1205) from clean-log into main
CI/CD Pipeline / test (push) Failing after 27m51s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #1205
2026-05-01 08:33:35 +02:00
philipp 910c51d01f fix ci
CI/CD Pipeline / test (push) Failing after 28m52s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2026-05-01 08:32:51 +02:00
philipp c2d8427e1a Merge pull request 'Merge pull request 'be able to delete nomembership user' (#1198) from delete-no-membership-user into main' (#1204) from clean-log into staging
CI/CD Pipeline / test (push) Failing after 31m5s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #1204
2026-04-30 11:40:46 +02:00
Philipp Hofer 79687807f2 clean /log by only showing boat reservation for the next 3 days
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
2026-04-30 11:39:54 +02:00
philipp 09defdc1f4 Merge pull request 'be able to delete nomembership user' (#1198) from delete-no-membership-user into main
CI/CD Pipeline / test (push) Failing after 34m43s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #1198
2026-03-20 08:40:13 +01:00
14 changed files with 950 additions and 821 deletions
+1
View File
@@ -31,6 +31,7 @@ jobs:
run: cd frontend && npx playwright install && npx playwright test --workers 1 --reporter html,line
- uses: actions/upload-artifact@v3
if: always()
continue-on-error: true
with:
name: playwright-report
path: frontend/playwright-report/
Generated
+844 -791
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -23,11 +23,11 @@ tera = { version = "1.20", features = ["date-locale"], optional = true}
ics = "0.5"
futures = "0.3"
lettre = "0.11"
csv = "1.4"
csv = "1.3"
itertools = "0.14"
job_scheduler_ng = "2.4"
ureq = { version = "3.3", features = ["json"] }
regex = "1.12"
job_scheduler_ng = "2.2"
ureq = { version = "3.0", features = ["json"] }
regex = "1.11"
urlencoding = "2.1"
[target.'cfg(not(windows))'.dependencies]
+1
View File
@@ -11,6 +11,7 @@ import { defineConfig, devices } from '@playwright/test';
*/
export default defineConfig({
testDir: './tests',
timeout: process.env.CI ? 120000 : 30000,
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
+2 -13
View File
@@ -9,21 +9,10 @@ export async function resetDatabase(): Promise<void> {
}
export async function login(page: Page, username: string, password: string): Promise<void> {
// Clear cookies to ensure clean state
await page.context().clearCookies();
// Navigate to auth page and wait for it to fully load
await page.goto("/auth", { waitUntil: 'load' });
await page.waitForLoadState('networkidle');
await page.getByPlaceholder("Name").click();
await page.getByPlaceholder("Name").fill(username);
await page.getByPlaceholder("Passwort").click();
await page.getByPlaceholder("Passwort").fill(password);
// Wait for navigation after form submission
await Promise.all([
page.waitForURL(/\/(planned|log|$)/, { timeout: 10000 }),
page.getByPlaceholder("Passwort").press("Enter")
]);
await page.getByPlaceholder("Passwort").press("Enter");
await page.waitForURL(/\/(planned|log|$)/);
}
+1 -1
View File
@@ -8,7 +8,7 @@ use rot::rest;
use rot::tera;
use rot::{scheduled, tera::Config};
use sqlx::{ConnectOptions, pool::PoolOptions, sqlite::SqliteConnectOptions};
use sqlx::{pool::PoolOptions, sqlite::SqliteConnectOptions, ConnectOptions};
#[macro_use]
extern crate rocket;
+46
View File
@@ -271,6 +271,52 @@ ORDER BY created_at DESC
Ok(())
}
pub async fn unfix(&self, db: &SqlitePool, tech_user: &User) -> Result<(), String> {
if self.user_id_verified.is_some() {
return Err("Reparatur wurde bereits verifiziert und kann nicht mehr rückgängig gemacht werden.".into());
}
if self.user_id_fixed.is_none() {
return Err("Reparatur wurde noch nicht eingetragen.".into());
}
let boat = Boat::find_by_id(db, self.boat_id as i32)
.await
.ok_or("Boot gibt's ned")?;
Log::create(
db,
format!("Unfix boat damage id={} by user {:?}", self.id, tech_user),
)
.await;
sqlx::query!(
"UPDATE boat_damage SET user_id_fixed=NULL, fixed_at=NULL WHERE id=?",
self.id
)
.execute(db)
.await
.map_err(|e| e.to_string())?;
let technicals =
User::all_with_role(db, &Role::find_by_name(db, "tech").await.unwrap()).await;
for technical in technicals {
Notification::create(
db,
&technical,
&format!(
"{} hat die Reparatur des Bootschadens '{}' beim Boot '{}' als fehlerhaft eingetragen zurückgesetzt.",
tech_user.name, self.desc, boat.name,
),
"Bootsschaden-Reparatur rückgängig gemacht",
None,
None,
)
.await;
}
Ok(())
}
pub async fn verified(
&self,
db: &SqlitePool,
+4 -4
View File
@@ -95,13 +95,13 @@ WHERE end_date >= ? AND start_date <= ?
res
}
pub async fn all_future(db: &SqlitePool) -> Vec<BoatReservationWithDetails> {
pub async fn next_future(db: &SqlitePool) -> Vec<BoatReservationWithDetails> {
let boatreservations = sqlx::query_as!(
Self,
"
SELECT id, boat_id, start_date, end_date, time_desc, usage, user_id_applicant, user_id_confirmation, created_at
FROM boat_reservation
WHERE end_date >= CURRENT_DATE ORDER BY end_date
WHERE end_date >= CURRENT_DATE AND end_date <= date(CURRENT_DATE, '+3 days') ORDER BY end_date
"
)
.fetch_all(db)
@@ -158,10 +158,10 @@ WHERE end_date >= CURRENT_DATE ORDER BY end_date
grouped_reservations
}
pub async fn all_future_with_groups(
pub async fn next_future_with_groups(
db: &SqlitePool,
) -> HashMap<String, Vec<BoatReservationWithDetails>> {
let reservations = Self::all_future(db).await;
let reservations = Self::next_future(db).await;
Self::with_groups(reservations)
}
+15 -2
View File
@@ -93,11 +93,24 @@ GROUP BY family.id;"
}
pub async fn clean_families_without_members(db: &SqlitePool) {
sqlx::query(
"UPDATE user SET family_id = NULL
WHERE family_id IN (
SELECT family_id FROM user
WHERE family_id IS NOT NULL
GROUP BY family_id
HAVING COUNT(*) = 1
);",
)
.execute(db)
.await
.unwrap();
sqlx::query(
"DELETE FROM family
WHERE id NOT IN (
SELECT DISTINCT family_id
FROM user
SELECT DISTINCT family_id
FROM user
WHERE family_id IS NOT NULL
);",
)
+20
View File
@@ -152,6 +152,25 @@ pub struct FormBoatDamageVerified<'r> {
desc: &'r str,
}
#[post("/<boatdamage_id>/unfix")]
async fn unfix(
db: &State<SqlitePool>,
boatdamage_id: i32,
techuser: TechUser,
) -> Flash<Redirect> {
let Some(boatdamage) = BoatDamage::find_by_id(db, boatdamage_id).await else {
return Flash::error(Redirect::to("/boatdamage"), "Bootsschaden nicht gefunden.");
};
let user: User = techuser.into_inner();
match boatdamage.unfix(db, &user).await {
Ok(_) => Flash::success(
Redirect::to("/boatdamage"),
"Reparatur wurde zurückgesetzt.",
),
Err(e) => Flash::error(Redirect::to("/boatdamage"), format!("Fehler: {e}")),
}
}
#[post("/<boatdamage_id>/verified", data = "<data>")]
async fn verified<'r>(
db: &State<SqlitePool>,
@@ -176,6 +195,7 @@ pub fn routes() -> Vec<Route> {
index_kiosk,
create,
fixed,
unfix,
verified,
create_from_kiosk
]
+3 -4
View File
@@ -1,11 +1,10 @@
use chrono::NaiveDate;
use rocket::{
FromForm, Route, State,
form::Form,
get, post,
request::FlashMessage,
response::{Flash, Redirect},
routes,
routes, FromForm, Route, State,
};
use rocket_dyn_templates::Template;
use sqlx::SqlitePool;
@@ -27,7 +26,7 @@ async fn index_kiosk(
flash: Option<FlashMessage<'_>>,
_kiosk: KioskCookie,
) -> Template {
let boatreservations = BoatReservation::all_future(db).await;
let boatreservations = BoatReservation::next_future(db).await;
let mut context = Context::new();
if let Some(msg) = flash {
@@ -56,7 +55,7 @@ async fn index(
flash: Option<FlashMessage<'_>>,
user: DonauLinzUser,
) -> Template {
let boatreservations = BoatReservation::all_future(db).await;
let boatreservations = BoatReservation::next_future(db).await;
let mut context = Context::new();
if let Some(msg) = flash {
+1 -1
View File
@@ -114,7 +114,7 @@ async fn index(db: &SqlitePool, flash: Option<FlashMessage<'_>>, mut context: Co
context.insert("planned_trips", &Trip::get_for_today(db).await);
context.insert(
"reservations",
&BoatReservation::all_future_with_groups(db).await,
&BoatReservation::next_future_with_groups(db).await,
);
context.insert("coxes", &coxes);
context.insert("users", &users);
+7
View File
@@ -55,6 +55,13 @@
</small>
{% if boatdamage.fixed_at %}
<small class="block text-gray-600 dark:text-gray-100">Repariert von {{ boatdamage.user_fixed.name }} am/um {{ boatdamage.fixed_at | date(format='%d.%m.%Y (%H:%M)') }}</small>
{% if loggedin_user and "tech" in loggedin_user.roles and not boatdamage.verified_at %}
<form action="/boatdamage/{{ boatdamage.id }}/unfix" method="post" class="mt-1">
<input type="submit"
class="btn btn-dark text-sm"
value="Reparatur rückgängig" />
</form>
{% endif %}
{% else %}
{% if loggedin_user and loggedin_user.allowed_to_steer %}
<form action="/boatdamage/{{ boatdamage.id }}/fixed"
+1 -1
View File
@@ -40,7 +40,7 @@ function setChoiceByLabel(choicesInstance, label) {
{% endmacro plannedtrips %}
{% macro boatreservation() %}
<div class="bg-white dark:bg-primary-900 rounded-md shadow pb-2 mt-3">
<h2 class="h2">Reservierungen ({{ reservations | length }})</h2>
<h2 class="h2">Reservierungen<br /><small>in den nächsten 3 Tagen</small></h2>
<div class="grid grid-cols-1 gap-3 mb-3 w-full">
{% for _, reservations_for_event in reservations %}
{% set reservation = reservations_for_event[0] %}