start with activity + fix tests
This commit is contained in:
		| @@ -225,6 +225,15 @@ CREATE TABLE IF NOT EXISTS "distance" ( | ||||
| ); | ||||
|  | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "activity" ( | ||||
|     id INTEGER PRIMARY KEY AUTOINCREMENT, | ||||
|     created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|     text TEXT NOT NULL, | ||||
|     relevant_for TEXT NOT NULL,  -- e.g. user_id=123;trip_id=456 | ||||
|     keep_until DATETIME | ||||
| ); | ||||
|  | ||||
|  | ||||
| CREATE TRIGGER IF NOT EXISTS prevent_multiple_roles_same_cluster | ||||
| BEFORE INSERT ON user_role | ||||
| BEGIN | ||||
|   | ||||
							
								
								
									
										54
									
								
								src/model/activity.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/model/activity.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| use std::ops::DerefMut; | ||||
|  | ||||
| use chrono::NaiveDateTime; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; | ||||
|  | ||||
| #[derive(FromRow, Debug, Serialize, Deserialize, Clone)] | ||||
| pub struct Activity { | ||||
|     pub id: i64, | ||||
|     pub created_at: NaiveDateTime, | ||||
|     pub text: String, | ||||
|     pub relevant_for: String, | ||||
|     pub keep_until: Option<NaiveDateTime>, | ||||
| } | ||||
|  | ||||
| impl Activity { | ||||
|     pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> { | ||||
|         sqlx::query_as!( | ||||
|             Self, | ||||
|             "SELECT id, created_at, text, relevant_for, keep_until FROM activity WHERE id like ?", | ||||
|             id | ||||
|         ) | ||||
|         .fetch_one(db) | ||||
|         .await | ||||
|         .ok() | ||||
|     } | ||||
|     pub async fn create_with_tx( | ||||
|         db: &mut Transaction<'_, Sqlite>, | ||||
|         text: &str, | ||||
|         relevant_for: &str, | ||||
|         keep_until: Option<NaiveDateTime>, | ||||
|     ) { | ||||
|         sqlx::query!( | ||||
|             "INSERT INTO activity(text, relevant_for, keep_until) VALUES (?, ?, ?)", | ||||
|             text, | ||||
|             relevant_for, | ||||
|             keep_until | ||||
|         ) | ||||
|         .execute(db.deref_mut()) | ||||
|         .await | ||||
|         .unwrap(); | ||||
|     } | ||||
|  | ||||
|     pub async fn create( | ||||
|         db: &SqlitePool, | ||||
|         text: &str, | ||||
|         relevant_for: &str, | ||||
|         keep_until: Option<NaiveDateTime>, | ||||
|     ) { | ||||
|         let mut tx = db.begin().await.unwrap(); | ||||
|         Self::create_with_tx(&mut tx, text, relevant_for, keep_until).await; | ||||
|         tx.commit().await.unwrap(); | ||||
|     } | ||||
| } | ||||
| @@ -14,6 +14,7 @@ use self::{ | ||||
| use boatreservation::{BoatReservation, BoatReservationWithDetails}; | ||||
| use std::collections::HashMap; | ||||
|  | ||||
| pub mod activity; | ||||
| pub mod boat; | ||||
| pub mod boatdamage; | ||||
| pub mod boathouse; | ||||
|   | ||||
| @@ -1,12 +1,25 @@ | ||||
| // TODO: put back in `src/model/user/mod.rs` once that is cleaned up | ||||
|  | ||||
| use super::{AllowedToEditPaymentStatusUser, ManageUserUser, User}; | ||||
| use crate::model::{family::Family, log::Log, mail::valid_mails, role::Role}; | ||||
| use crate::model::{activity::Activity, family::Family, log::Log, mail::valid_mails, role::Role}; | ||||
| use chrono::NaiveDate; | ||||
| use rocket::{fs::TempFile, tokio::io::AsyncReadExt}; | ||||
| use sqlx::SqlitePool; | ||||
|  | ||||
| impl User { | ||||
|     pub(crate) async fn add_note( | ||||
|         &self, | ||||
|         db: &SqlitePool, | ||||
|         updated_by: &ManageUserUser, | ||||
|         note: &str, | ||||
|     ) -> Result<(), String> { | ||||
|         let note = note.trim(); | ||||
|  | ||||
|         Activity::create(db, note, "relevant_for", None).await; | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub(crate) async fn update_mail( | ||||
|         &self, | ||||
|         db: &SqlitePool, | ||||
|   | ||||
| @@ -951,9 +951,7 @@ impl UserWithMembershipPdf { | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod test { | ||||
|     use std::collections::HashMap; | ||||
|  | ||||
|     use crate::{tera::admin::user::UserEditForm, testdb}; | ||||
|     use crate::testdb; | ||||
|  | ||||
|     use super::User; | ||||
|     use sqlx::SqlitePool; | ||||
| @@ -1014,38 +1012,6 @@ mod test { | ||||
|         assert_eq!(User::create(&pool, "admin".into()).await, false); | ||||
|     } | ||||
|  | ||||
|     #[sqlx::test] | ||||
|     fn test_update() { | ||||
|         let pool = testdb!(); | ||||
|  | ||||
|         let user = User::find_by_id(&pool, 1).await.unwrap(); | ||||
|         user.update( | ||||
|             &pool, | ||||
|             UserEditForm { | ||||
|                 id: 1, | ||||
|                 dob: None, | ||||
|                 weight: None, | ||||
|                 sex: Some("m".into()), | ||||
|                 roles: HashMap::new(), | ||||
|                 member_since_date: None, | ||||
|                 birthdate: None, | ||||
|                 mail: None, | ||||
|                 nickname: None, | ||||
|                 notes: None, | ||||
|                 phone: None, | ||||
|                 address: None, | ||||
|                 family_id: None, | ||||
|                 membership_pdf: None, | ||||
|             }, | ||||
|         ) | ||||
|         .await | ||||
|         .unwrap(); | ||||
|  | ||||
|         let user = User::find_by_id(&pool, 1).await.unwrap(); | ||||
|  | ||||
|         assert_eq!(user.sex, Some("m".into())); | ||||
|     } | ||||
|  | ||||
|     #[sqlx::test] | ||||
|     fn succ_login_with_test_db() { | ||||
|         let pool = testdb!(); | ||||
|   | ||||
| @@ -327,6 +327,34 @@ async fn update_mail( | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(FromForm, Debug)] | ||||
| pub struct AddNoteForm { | ||||
|     note: String, | ||||
| } | ||||
|  | ||||
| #[post("/user/<id>/add-note", data = "<data>")] | ||||
| async fn add_note( | ||||
|     db: &State<SqlitePool>, | ||||
|     data: Form<AddNoteForm>, | ||||
|     admin: ManageUserUser, | ||||
|     id: i32, | ||||
| ) -> Flash<Redirect> { | ||||
|     let Some(user) = User::find_by_id(db, id).await else { | ||||
|         return Flash::error( | ||||
|             Redirect::to("/admin/user"), | ||||
|             format!("User with ID {} does not exist!", id), | ||||
|         ); | ||||
|     }; | ||||
|  | ||||
|     match user.add_note(db, &admin, &data.note).await { | ||||
|         Ok(_) => Flash::success( | ||||
|             Redirect::to(format!("/admin/user/{}", user.id)), | ||||
|             "Notiz hinzugefügt", | ||||
|         ), | ||||
|         Err(e) => Flash::error(Redirect::to(format!("/admin/user/{}", user.id)), e), | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(FromForm, Debug)] | ||||
| pub struct PhoneUpdateForm { | ||||
|     phone: String, | ||||
| @@ -1147,6 +1175,7 @@ pub fn routes() -> Vec<Route> { | ||||
|         update_family, | ||||
|         add_membership_pdf, | ||||
|         add_role, | ||||
|         add_note, | ||||
|         remove_role, | ||||
|         // | ||||
|         scheckbook_to_regular, | ||||
|   | ||||
| @@ -25,3 +25,11 @@ UPDATE role SET desc='Es können Logbucheinträge im Nachhinein hinzugefügt wer | ||||
| UPDATE role SET desc='Erlaubt den Login auf der Wordpress-Website um zB Artikel zu schreiben.' WHERE name='allow_website_login'; | ||||
| UPDATE role SET desc='Muss nur den halben Rennruderbeitrag bezahlen (da zB erst in der 2. Jahreshälfte dazugestoßen wurde)' WHERE name='half-rennrudern'; | ||||
| UPDATE role SET desc='Muss keinen Rennruderbeitrag bezahlen, obwohl man in Rennruder-Gruppe ist.' WHERE name='renntrainer'; | ||||
|  | ||||
| CREATE TABLE activity ( | ||||
|     id INTEGER PRIMARY KEY AUTOINCREMENT, | ||||
|     created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|     text TEXT NOT NULL, | ||||
|     relevant_for TEXT NOT NULL,  -- e.g. user_id=123;trip_id=456 | ||||
|     keep_until DATETIME          -- OPTIONAL field | ||||
| ); | ||||
|   | ||||
| @@ -31,6 +31,11 @@ | ||||
|                         <form action="/admin/user/{{ user.id }}/change-nickname" method="post"> | ||||
|                             {{ macros::inputgroup(label='Spitzname', name='nickname', type="text", value=user.nickname, readonly=not allowed_to_edit) }} | ||||
|                         </form> | ||||
|                         {% if allowed_to_edit %} | ||||
|                         <form action="/admin/user/{{ user.id }}/new-note" method="post"> | ||||
|                             {{ macros::inputgroup(label='Neue Notiz', name='note', type="text") }} | ||||
|                         </form> | ||||
|                         {% endif %} | ||||
|                         <span>Notizen: to be replaced with activity :-)</span> | ||||
|                         {% if user.pw and allowed_to_edit %} | ||||
|                             <div> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user