Compare commits

..

3 Commits

Author SHA1 Message Date
59ef93d6fa Merge pull request 'update-logbook-entries' (#633) from update-logbook-entries into staging
Some checks are pending
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) Successful in 9m40s
Reviewed-on: #633
2024-07-23 15:04:47 +02:00
4a3ee5b551 update logbook entries
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
2024-07-23 14:57:09 +02:00
c73b3e94c3 Merge pull request 'new text to make clear who's responsible for fee payment' (#632) from new-text into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m44s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m56s
Reviewed-on: #632
2024-07-23 08:59:05 +02:00
5 changed files with 193 additions and 80 deletions

View File

@ -60,6 +60,22 @@ pub struct LogToFinalize {
pub rowers: Vec<i64>, pub rowers: Vec<i64>,
} }
#[derive(FromForm, Debug, Clone)]
pub struct LogToUpdate {
pub id: i64,
pub boat_id: i64,
pub shipmaster: i64,
pub steering_person: i64,
pub shipmaster_only_steering: bool,
pub departure: String,
pub arrival: Option<String>,
pub destination: Option<String>,
pub distance_in_km: Option<i64>,
pub comments: Option<String>,
pub logtype: Option<i64>,
pub rowers: Vec<i64>,
}
impl TryFrom<LogToAdd> for LogToFinalize { impl TryFrom<LogToAdd> for LogToFinalize {
type Error = String; type Error = String;
@ -94,6 +110,11 @@ pub struct LogbookWithBoatAndRowers {
pub rowers: Vec<User>, pub rowers: Vec<User>,
} }
#[derive(Debug, PartialEq)]
pub enum LogbookAdminUpdateError {
NotAllowed,
}
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum LogbookUpdateError { pub enum LogbookUpdateError {
NotYourEntry, NotYourEntry,
@ -172,7 +193,7 @@ impl Logbook {
.await .await
.ok() .ok()
} }
pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> { pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
sqlx::query_as!( sqlx::query_as!(
Self, Self,
" "
@ -437,6 +458,35 @@ ORDER BY departure DESC
Ok(ret) Ok(ret)
} }
pub async fn update(
&self,
db: &SqlitePool,
data: LogToUpdate,
user: &User,
) -> Result<(), LogbookAdminUpdateError> {
if !user.has_role(db, "Vorstand").await {
return Err(LogbookAdminUpdateError::NotAllowed);
}
sqlx::query!(
"UPDATE logbook SET boat_id=?, shipmaster=?, steering_person=?, shipmaster_only_steering=?, departure=?, arrival=?, destination=?, distance_in_km=?, comments=?, logtype=? WHERE id=?",
data.boat_id,
data.shipmaster,
data.steering_person,
data.shipmaster_only_steering,
data.departure,
data.arrival,
data.destination,
data.distance_in_km,
data.comments,
data.logtype,
self.id
)
.execute(db)
.await.unwrap();
Ok(())
}
pub async fn distances(db: &SqlitePool) -> Vec<(String, i64)> { pub async fn distances(db: &SqlitePool) -> Vec<(String, i64)> {
let result = sqlx::query!("SELECT destination, distance_in_km FROM logbook WHERE id IN (SELECT MIN(id) FROM logbook GROUP BY destination) AND destination IS NOT NULL AND distance_in_km IS NOT NULL;") let result = sqlx::query!("SELECT destination, distance_in_km FROM logbook WHERE id IN (SELECT MIN(id) FROM logbook GROUP BY destination) AND destination IS NOT NULL AND distance_in_km IS NOT NULL;")
.fetch_all(db) .fetch_all(db)

View File

@ -20,11 +20,11 @@ use crate::model::{
boatreservation::BoatReservation, boatreservation::BoatReservation,
log::Log, log::Log,
logbook::{ logbook::{
LogToAdd, LogToFinalize, Logbook, LogbookCreateError, LogbookDeleteError, LogToAdd, LogToFinalize, LogToUpdate, Logbook, LogbookAdminUpdateError, LogbookCreateError,
LogbookUpdateError, LogbookDeleteError, LogbookUpdateError,
}, },
logtype::LogType, logtype::LogType,
user::{AdminUser, DonauLinzUser, User, UserWithDetails}, user::{AdminUser, DonauLinzUser, User, UserWithDetails, VorstandUser},
}; };
pub struct KioskCookie(()); pub struct KioskCookie(());
@ -282,10 +282,40 @@ async fn create_kiosk(
create_logbook(db, data, &DonauLinzUser(creator)).await //TODO: fixme create_logbook(db, data, &DonauLinzUser(creator)).await //TODO: fixme
} }
#[post("/update", data = "<data>")]
async fn update(
db: &State<SqlitePool>,
data: Form<LogToUpdate>,
user: VorstandUser,
) -> Flash<Redirect> {
Log::create(
db,
format!("User {} tries to update log entry={:?}", &user.name, data),
)
.await;
let data = data.into_inner();
let Some(logbook) = Logbook::find_by_id(db, data.id).await else {
return Flash::error(Redirect::to("/log"), &format!("Logbucheintrag kann nicht bearbeitet werden, da es einen Logbuch-Eintrag mit ID={} nicht gibt", data.id));
};
match logbook.update(db, data, &user.0).await {
Ok(()) => Flash::success(
Redirect::to("/log/show"),
format!("Logbucheintrag erfolgreich bearbeitet"),
),
Err(LogbookAdminUpdateError::NotAllowed) => Flash::error(
Redirect::to("/log/show"),
format!("Du hast keine Erlaubnis, diesen Logbucheintrag zu bearbeiten!"),
),
}
}
async fn home_logbook( async fn home_logbook(
db: &SqlitePool, db: &SqlitePool,
data: Form<LogToFinalize>, data: Form<LogToFinalize>,
logbook_id: i32, logbook_id: i64,
user: &DonauLinzUser, user: &DonauLinzUser,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
let logbook: Option<Logbook> = Logbook::find_by_id(db, logbook_id).await; let logbook: Option<Logbook> = Logbook::find_by_id(db, logbook_id).await;
@ -312,7 +342,7 @@ async fn home_logbook(
async fn home_kiosk( async fn home_kiosk(
db: &State<SqlitePool>, db: &State<SqlitePool>,
data: Form<LogToFinalize>, data: Form<LogToFinalize>,
logbook_id: i32, logbook_id: i64,
_kiosk: KioskCookie, _kiosk: KioskCookie,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
let logbook = Logbook::find_by_id(db, logbook_id).await.unwrap(); //TODO: fixme let logbook = Logbook::find_by_id(db, logbook_id).await.unwrap(); //TODO: fixme
@ -340,7 +370,7 @@ async fn home_kiosk(
async fn home( async fn home(
db: &State<SqlitePool>, db: &State<SqlitePool>,
data: Form<LogToFinalize>, data: Form<LogToFinalize>,
logbook_id: i32, logbook_id: i64,
user: DonauLinzUser, user: DonauLinzUser,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
Log::create( Log::create(
@ -356,7 +386,7 @@ async fn home(
} }
#[get("/<logbook_id>/delete", rank = 2)] #[get("/<logbook_id>/delete", rank = 2)]
async fn delete(db: &State<SqlitePool>, logbook_id: i32, user: DonauLinzUser) -> Flash<Redirect> { async fn delete(db: &State<SqlitePool>, logbook_id: i64, user: DonauLinzUser) -> Flash<Redirect> {
let logbook = Logbook::find_by_id(db, logbook_id).await; let logbook = Logbook::find_by_id(db, logbook_id).await;
if let Some(logbook) = logbook { if let Some(logbook) = logbook {
Log::create( Log::create(
@ -385,7 +415,7 @@ async fn delete(db: &State<SqlitePool>, logbook_id: i32, user: DonauLinzUser) ->
#[get("/<logbook_id>/delete")] #[get("/<logbook_id>/delete")]
async fn delete_kiosk( async fn delete_kiosk(
db: &State<SqlitePool>, db: &State<SqlitePool>,
logbook_id: i32, logbook_id: i64,
_kiosk: KioskCookie, _kiosk: KioskCookie,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
let logbook = Logbook::find_by_id(db, logbook_id).await; let logbook = Logbook::find_by_id(db, logbook_id).await;
@ -425,7 +455,8 @@ pub fn routes() -> Vec<Route> {
show_kiosk, show_kiosk,
show_for_year, show_for_year,
delete, delete,
delete_kiosk delete_kiosk,
update
] ]
} }

View File

@ -169,10 +169,12 @@
</div> </div>
</div> </div>
{% endmacro show %} {% endmacro show %}
{% macro show_old(log, state, allowed_to_close=false, index) %} {% macro show_old(log, state, allowed_to_close=false, allowed_to_edit=false, index) %}
<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 %}">
<details>
<summary style="list-style: none;">
{% if log.logtype %} {% if log.logtype %}
<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 %}
@ -240,6 +242,28 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
</div> </div>
</summary>
{% if allowed_to_edit %}
<form action="/log/update" method="post">
<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 }}" />
<input type="hidden"
name="shipmaster_only_steering"
value="{{ log.shipmaster_only_steering }}" />
<input type="datetime-local" name="departure" value="{{ log.departure }}" />
<input type="datetime-local" 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" value="Updaten" />
</form>
{% endif %}
</details>
</div> </div>
{% endmacro show_old %} {% endmacro show_old %}
{% macro home(log) %} {% macro home(log) %}

View File

@ -23,7 +23,15 @@
placeholder="Suchen nach Bootsname oder Ruderer..."> placeholder="Suchen nach Bootsname oder Ruderer...">
</div> </div>
<div id="filter-result-js" class="search-result"></div> <div id="filter-result-js" class="search-result"></div>
{% for log in logs %}{{ log::show_old(log=log, state="completed", only_ones=false, index=loop.index) }}{% endfor %} {% for log in logs %}
{% set_global allowed_to_edit = false %}
{% if loggedin_user %}
{% if "Vorstand" in loggedin_user.roles %}
{% set_global allowed_to_edit = true %}
{% endif %}
{% endif %}
{{ log::show_old(log=log, state="completed", only_ones=false, index=loop.index, allowed_to_edit=allowed_to_edit) }}
{% endfor %}
</div> </div>
</div> </div>
<script> <script>