Merge branch 'staging' into 'main'

Staging

See merge request PhilippHofer/rot!40
This commit is contained in:
PhilippHofer 2023-10-24 06:31:21 +00:00
commit 9087fa9453
13 changed files with 120 additions and 62 deletions

View File

@ -28,6 +28,7 @@ pub struct BoatDamageWithDetails {
user_fixed: Option<User>,
user_verified: Option<User>,
boat: Boat,
verified: bool,
}
#[derive(Debug)]
@ -102,6 +103,7 @@ ORDER BY created_at DESC
.await
.unwrap(),
user_fixed,
verified: user_verified.is_some(),
user_verified,
boat_damage,
});

View File

@ -33,7 +33,7 @@ WHERE id in (SELECT rower_id FROM rower WHERE logbook_id=?)
//TODO: Check if rower is allowed to row
sqlx::query!(
"INSERT INTO rower(logbook_id, rower_id) VALUES (?,?)",
"INSERT INTO rower(logbook_id, rower_id) VALUES (?,?);",
logbook_id,
rower_id
)

View File

@ -43,7 +43,7 @@ async fn delete(db: &State<SqlitePool>, _admin: AdminUser, boat: i32) -> Flash<R
boat.delete(db).await;
Flash::success(
Redirect::to("/admin/boat"),
format!("Sucessfully deleted boat {}", boat.name),
format!("Boot {} gelöscht", boat.name),
)
}
None => Flash::error(Redirect::to("/admin/boat"), "Boat does not exist"),
@ -63,7 +63,7 @@ async fn update(
};
match boat.update(db, data.into_inner()).await {
Ok(_) => Flash::success(Redirect::to("/admin/boat"), "Successfully updated boat"),
Ok(_) => Flash::success(Redirect::to("/admin/boat"), "Boot bearbeitet"),
Err(e) => Flash::error(Redirect::to("/admin/boat"), e),
}
}
@ -75,7 +75,7 @@ async fn create(
_admin: AdminUser,
) -> Flash<Redirect> {
match Boat::create(db, data.into_inner()).await {
Ok(_) => Flash::success(Redirect::to("/admin/boat"), "Successfully created boat"),
Ok(_) => Flash::success(Redirect::to("/admin/boat"), "Boot hinzugefügt"),
Err(e) => Flash::error(Redirect::to("/admin/boat"), e),
}
}
@ -153,7 +153,7 @@ mod test {
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(flash_cookie.value(), "7:successSuccessfully updated boat");
assert_eq!(flash_cookie.value(), "7:successBoot bearbeitet");
let boat = Boat::find_by_id(&db, 1).await.unwrap();
assert_eq!(boat.name, "Haichiii");
@ -267,7 +267,7 @@ mod test {
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(flash_cookie.value(), "7:successSuccessfully created boat");
assert_eq!(flash_cookie.value(), "7:successBoot hinzugefügt");
Boat::find_by_name(&db, "completely-new-boat".into())
.await

View File

@ -36,7 +36,7 @@ async fn create(
PlannedEvent::create(db, data.name, data.planned_amount_cox, trip_details).await;
Flash::success(Redirect::to("/"), "Successfully planned the event")
Flash::success(Redirect::to("/"), "Event hinzugefügt")
}
//TODO: add constraints (e.g. planned_amount_cox > 0)
@ -79,7 +79,7 @@ async fn delete(db: &State<SqlitePool>, id: i64, _admin: AdminUser) -> Flash<Red
match PlannedEvent::find_by_id(db, id).await {
Some(planned_event) => {
planned_event.delete(db).await;
Flash::success(Redirect::to("/"), "Successfully deleted the event")
Flash::success(Redirect::to("/"), "Event gelöscht")
}
None => Flash::error(Redirect::to("/"), "PlannedEvent does not exist"),
}
@ -127,10 +127,7 @@ mod test {
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(
flash_cookie.value(),
"7:successSuccessfully deleted the event"
);
assert_eq!(flash_cookie.value(), "7:successEvent gelöscht");
let event = PlannedEvent::find_by_id(&db, 1).await;
assert_eq!(event, None);
@ -265,10 +262,7 @@ mod test {
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(
flash_cookie.value(),
"7:successSuccessfully planned the event"
);
assert_eq!(flash_cookie.value(), "7:successEvent hinzugefügt");
let event = PlannedEvent::find_by_id(&db, 2).await.unwrap();
assert_eq!(event.name, "my-cool-new-event");

View File

@ -35,7 +35,7 @@ async fn resetpw(db: &State<SqlitePool>, _admin: AdminUser, user: i32) -> Flash<
user.reset_pw(db).await;
Flash::success(
Redirect::to("/admin/user"),
format!("Successfully reset pw of {}", user.name),
format!("Passwort von {} zurückgesetzt", user.name),
)
}
None => Flash::error(Redirect::to("/admin/user"), "User does not exist"),
@ -50,7 +50,7 @@ async fn delete(db: &State<SqlitePool>, _admin: AdminUser, user: i32) -> Flash<R
user.delete(db).await;
Flash::success(
Redirect::to("/admin/user"),
format!("Sucessfully deleted user {}", user.name),
format!("Benutzer {} gelöscht", user.name),
)
}
None => Flash::error(Redirect::to("/admin/user"), "User does not exist"),

View File

@ -11,7 +11,6 @@ use rocket::{
FromForm, Request, Route, State,
};
use rocket_dyn_templates::{context, tera, Template};
use serde_json::json;
use sqlx::SqlitePool;
use crate::model::{

View File

@ -26,7 +26,7 @@ async fn index_kiosk(
) -> Template {
let boatdamages = BoatDamage::all(db).await;
let boats = Boat::all(db).await;
let coxes = User::cox(db).await;
let user = User::all(db).await;
let mut context = Context::new();
if let Some(msg) = flash {
@ -35,7 +35,8 @@ async fn index_kiosk(
context.insert("boatdamages", &boatdamages);
context.insert("boats", &boats);
context.insert("coxes", &coxes);
context.insert("user", &user);
context.insert("show_kiosk_header", &true);
Template::render("boatdamages", context.into_json())
}
@ -132,7 +133,7 @@ async fn fixed<'r>(
user_id_fixed: coxuser.id as i32,
};
match boatdamage.fixed(db, boatdamage_fixed).await {
Ok(_) => Flash::success(Redirect::to("/boatdamage"), "Successfully fixed the boat."),
Ok(_) => Flash::success(Redirect::to("/boatdamage"), "Bootsschaden behoben."),
Err(e) => Flash::error(Redirect::to("/boatdamage"), format!("Error: {e}")),
}
}
@ -157,7 +158,7 @@ async fn verified<'r>(
match boatdamage.verified(db, boatdamage_verified).await {
Ok(_) => Flash::success(
Redirect::to("/boatdamage"),
"Successfully verified the boat.",
"Bootsschaden verifiziert",
),
Err(e) => Flash::error(Redirect::to("/boatdamage"), format!("Error: {e}")),
}

View File

@ -90,7 +90,7 @@ async fn show(db: &State<SqlitePool>, user: User) -> Template {
async fn show_kiosk(db: &State<SqlitePool>, _kiosk: KioskCookie) -> Template {
let logs = Logbook::completed(db).await;
Template::render("log.completed", context!(logs))
Template::render("log.completed", context!(logs, show_kiosk_header: true))
}
#[get("/kiosk/ekrv2019/<loc>")]
@ -148,6 +148,7 @@ async fn kiosk(
context.insert("logtypes", &logtypes);
context.insert("on_water", &on_water);
context.insert("distances", &distances);
context.insert("show_kiosk_header", &true);
Template::render("kiosk", context.into_json())
}
@ -207,11 +208,11 @@ async fn home_logbook(
};
match logbook.home(db, user, data.into_inner()).await {
Ok(_) => Flash::success(Redirect::to("/log"), "Successfully updated log"),
Ok(_) => Flash::success(Redirect::to("/log"), "Ausfahrt korrekt eingetragen"),
Err(LogbookUpdateError::TooManyRowers(expected, actual)) => Flash::error(Redirect::to("/log"), format!("Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)")),
Err(_) => Flash::error(
Redirect::to("/log"),
format!("Logbook with ID {} could not be updated!", logbook_id),
format!("Eintrag {} konnte nicht abgesendet werden!", logbook_id),
),
}
}
@ -245,14 +246,43 @@ async fn home(
home_logbook(db, data, logbook_id, &user).await
}
#[get("/<logbook_id>/delete")]
#[get("/<logbook_id>/delete", rank = 2)]
async fn delete(db: &State<SqlitePool>, logbook_id: i32, user: User) -> Flash<Redirect> {
let logbook = Logbook::find_by_id(db, logbook_id).await;
if let Some(logbook) = logbook {
match logbook.delete(db, &user).await {
Ok(_) => Flash::success(
Redirect::to("/log"),
format!("Logbook with ID {} successfully deleted!", logbook_id),
format!("Eintrag {} gelöscht!", logbook_id),
),
Err(LogbookDeleteError::NotYourEntry) => Flash::error(
Redirect::to("/log"),
"Du hast nicht die Berechtigung, den Eintrag zu löschen!",
),
}
} else {
Flash::error(
Redirect::to("/log"),
format!("Logbook with ID {} could not be found!", logbook_id),
)
}
}
#[get("/<logbook_id>/delete")]
async fn delete_kiosk(
db: &State<SqlitePool>,
logbook_id: i32,
_kiosk: KioskCookie,
) -> Flash<Redirect> {
let logbook = Logbook::find_by_id(db, logbook_id).await;
if let Some(logbook) = logbook {
let cox = User::find_by_id(db, logbook.shipmaster as i32)
.await
.unwrap();
match logbook.delete(db, &cox).await {
Ok(_) => Flash::success(
Redirect::to("/log"),
format!("Eintrag {} gelöscht!", logbook_id),
),
Err(LogbookDeleteError::NotYourEntry) => Flash::error(
Redirect::to("/log"),
@ -278,7 +308,8 @@ pub fn routes() -> Vec<Route> {
new_kiosk,
show,
show_kiosk,
delete
delete,
delete_kiosk
]
}
@ -471,7 +502,10 @@ mod test {
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(flash_cookie.value(), "7:successSuccessfully updated log");
assert_eq!(
flash_cookie.value(),
"7:successAusfahrt korrekt eingetragen"
);
}
//Kiosk mode
@ -606,7 +640,10 @@ mod test {
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(flash_cookie.value(), "7:successSuccessfully updated log");
assert_eq!(
flash_cookie.value(),
"7:successAusfahrt korrekt eingetragen"
);
}
#[sqlx::test]
@ -796,7 +833,7 @@ mod test {
assert_eq!(
flash_cookie.value(),
"5:errorLogbook with ID 1 could not be updated!"
"5:errorEintrag 1 konnte nicht abgesendet werden!"
);
}
@ -843,7 +880,10 @@ mod test {
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(flash_cookie.value(), "7:successSuccessfully updated log");
assert_eq!(
flash_cookie.value(),
"7:successAusfahrt korrekt eingetragen"
);
}
async fn cant_start_trip(

View File

@ -26,7 +26,7 @@ async fn index_kiosk(db: &State<SqlitePool>, _kiosk: KioskCookie) -> Template {
let stat = Stat::get_rowed_km(db).await;
let kiosk = true;
Template::render("stat", context!(stat, kiosk))
Template::render("stat", context!(stat, kiosk, show_kiosk_header: true))
}
pub fn routes() -> Vec<Route> {

View File

@ -12,9 +12,24 @@
<body class="bg-gray-100">
{% if loggedin_user %}
{{ macros::header(loggedin_user=loggedin_user) }}
{% endif %}
{% endif %}
{% if show_kiosk_header %}
<header class="bg-primary-900 text-white flex justify-between px-4 py-3 w-full z-10 ">
<div>
<a href="/">
</a>
</div>
<div>
<a href="/log" class="px-2">Ausfahrt eintragen</a>
<a href="/log/show" class="px-2">Logbuch</a>
<a href="/stat" class="px-2">Statistik</a>
<a href="/boatdamage" class="px-2">Bootsschaden</a>
</div>
</header>
{% endif %}
<div class="flex min-h-screen {%if not loggedin_user %} items-center {% else %} items-start {% endif %} justify-center px-4 py-12 sm:px-6 lg:px-8"> {% block content %}{% endblock content %}
<div class="flex min-h-screen {%if not loggedin_user and not show_kiosk_header %} items-center {% else %} items-start {% endif %} justify-center px-4 py-12 sm:px-6 lg:px-8"> {% block content %}{% endblock content %}
</div>
{% if loggedin_user %}

View File

@ -27,7 +27,7 @@
<form action="/boatdamage" method="post" class="grid gap-3">
{{ log::boat_select(only_ones=false, id='boat') }}
{% if not loggedin_user %}
{{ macros::select(label='Gemeldet von', data=coxes, name='user_id') }}
{{ macros::select(label='Gemeldet von', data=user, name='user_id') }}
{% endif %}
{{ macros::input(label='Beschreibung des Schadens', name='desc', type='text', required=true, wrapper_class='col-span-4') }}
<div class="col-span-4">
@ -45,9 +45,9 @@
<div id="filter-result-js" class="bg-gray-200 text-primary-950 pb-3 px-3 text-right"></div>
{% for boatdamage in boatdamages %}
{% for boatdamage in boatdamages | sort(attribute="verified") %}
<div data-filterable="true" data-filter="{{ boatdamage.boat.name }} {{ boatdamage.user_created.name }}" class="w-full border-t bg-white p-3 {% if boatdamage.verified_at %} opacity-50 {% endif %}">
<div>
<div class="w-full">
<strong>{{ boatdamage.created_at | date(format='%d.%m.%Y') }} <span class="font-normal text-gray-600">({{ boatdamage.boat.name }})</span></strong>{% if boatdamage.boat.damage %}<small class="block text-gray-600">(Boot gesperrt)</small>{% endif %}
<div>{{ boatdamage.desc }}</div>
<small class="block text-gray-600">
@ -58,12 +58,12 @@
<small class="block text-gray-600">Repariert von {{ boatdamage.user_fixed.name }} am/um {{ boatdamage.fixed_at | date(format='%d.%m.%Y (%H:%M)') }}</small>
{% else %}
{% if loggedin_user.is_cox %}
<form action="/boatdamage/{{ boatdamage.id }}/fixed" method="post" class="mt-3">
<input type="text" name="desc" value="{{ boatdamage.desc }}" />
<form action="/boatdamage/{{ boatdamage.id }}/fixed" method="post" class="flex justify-between mt-3">
<input type="text" name="desc" value="{{ boatdamage.desc }}" class="grow input rounded-s" />
{% if loggedin_user.is_tech %}
<input type="submit" class="btn btn-primary" value="Repariert und verifiziert" />
<input type="submit" class="btn btn-primary" style="border-top-left-radius: 0; border-bottom-left-radius: 0;" value="Repariert und verifiziert" />
{% else %}
<input type="submit" class="btn btn-primary" value="Repariert" />
<input type="submit" class="btn btn-primary" style="border-top-left-radius: 0; border-bottom-left-radius: 0;" value="Repariert" />
{% endif %}
</form>
{% endif %}
@ -73,9 +73,9 @@
<small class="block text-gray-600">Verifziert von {{ boatdamage.user_verified.name }} am/um {{ boatdamage.verified_at | date(format='%d.%m.%Y (%H:%M)') }}</small>
{% else %}
{% if loggedin_user.is_tech and boatdamage.fixed_at %}
<form action="/boatdamage/{{ boatdamage.id }}/verified" method="post" class="mt-3">
<input type="text" name="desc" value="{{ boatdamage.desc }}" />
<input type="submit" class="btn btn-dark" value="Verifiziert" />
<form action="/boatdamage/{{ boatdamage.id }}/verified" method="post" class="flex justify-between mt-3">
<input type="text" name="desc" value="{{ boatdamage.desc }}" class="grow input rounded-s"/>
<input type="submit" class="btn btn-dark" style="border-top-left-radius: 0; border-bottom-left-radius: 0;" value="Verifiziert" />
</form>
{% endif %}
{% endif %}

View File

@ -162,25 +162,31 @@
</div>
{% endif %}
<div id="log{{ log.id }}">
{% if log.destination %}
{{ log.destination }}
{% endif %}
{% if log.destination %}
{{ log.destination }}
{% endif %}
{% for user in users %}
{% if user.id == log.shipmaster %}
<p>
<strong>{{ user.name }}</strong>
</p>
{% endif %}
{% endfor %}
{% for user in users %}
{% if user.id == log.shipmaster %}
<p>
<strong>{{ user.name }}</strong>
</p>
{% endif %}
{% endfor %}
{% for rower in log.rowers %}
<p>{{ rower.name }}</p>
{% endfor %}
{% if allowed_to_close and state == "on_water" %}
<a href="/log/{{ log.id }}/delete" onclick="return confirm('Willst du diesen Eintrag wirklich löschen? Die Daten gehen verloren :O');">LÖSCHEN</a>
{% set amount_rowers = log.rowers | length %}
{% set amount_guests = log.boat.amount_seats - amount_rowers -1 %}
{% if amount_guests > 0 %}
Gäste (ohne Account): {{ amount_guests }}
{% endif %}
{% if allowed_to_close and state == "on_water" %}
<a href="/log/{{ log.id }}/delete" class="btn btn-alert w-full absolute bottom-0 left-0" style="border-radius: 0;" onclick="return confirm('Willst du diesen Eintrag wirklich löschen? Die Daten gehen verloren');">Löschen</a>
{% endif %}
</div>
</div>
</div>
@ -235,8 +241,7 @@
{{ rower.name }}{% if not loop.last or amount_guests > 0 %}, {% endif %}
{% endfor %}
{% if amount_guests > 0 %}
{{ amount_guests }}
Gäste (ohne Account)
Gäste (ohne Account): {{ amount_guests }}
{% endif %}
</div>
{% endif %}

View File

@ -9,7 +9,9 @@
<h1 class="h1">Logbuch</h1>
{% if flash %}
{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}
<div class="pt-3 max-w-lg m-auto">
{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}
</div>
{% endif %}
<div class="w-full grid md:grid-cols-5 gap-3 mt-5">