board-scheckbook #687
| @@ -2,7 +2,7 @@ | ||||
| secret_key = "/NtVGizglEoyoxBLzsRDWTy4oAG1qDw4J4O+CWJSv+fypD7W9sam8hUY4j90EZsbZk8wEradS5zBoWtWKi3k8w==" | ||||
| rss_key = "rss-key-for-ci" | ||||
| limits = { file = "10 MiB", data-form = "10 MiB"} | ||||
| smtp_pw = "8kIjlLH79Ky6D3jQ" | ||||
| smtp_pw = "8kIjlLH79Ky6D3j" | ||||
| usage_log_path = "./usage.txt" | ||||
| openweathermap_key = "c8dab8f91b5b815d76e9879cbaecd8d5" | ||||
| wordpress_key = "pw-to-allow-sending-notifications" | ||||
|   | ||||
| @@ -648,6 +648,14 @@ ORDER BY last_access DESC | ||||
|             .is_ok() | ||||
|     } | ||||
|  | ||||
|     pub async fn create_with_mail(db: &SqlitePool, name: &str, mail: &str) -> bool { | ||||
|         let name = name.trim(); | ||||
|         sqlx::query!("INSERT INTO USER(name, mail) VALUES (?, ?)", name, mail) | ||||
|             .execute(db) | ||||
|             .await | ||||
|             .is_ok() | ||||
|     } | ||||
|  | ||||
|     pub async fn update(&self, db: &SqlitePool, data: UserEditForm<'_>) { | ||||
|         let mut family_id = data.family_id; | ||||
|  | ||||
|   | ||||
| @@ -7,13 +7,14 @@ use crate::{ | ||||
|         logbook::Logbook, | ||||
|         role::Role, | ||||
|         user::{ | ||||
|             AdminUser, AllowedToEditPaymentStatusUser, User, UserWithDetails, | ||||
|             UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser, | ||||
|             AdminUser, AllowedToEditPaymentStatusUser, SchnupperBetreuerUser, User, | ||||
|             UserWithDetails, UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser, | ||||
|         }, | ||||
|     }, | ||||
|     tera::Config, | ||||
| }; | ||||
| use futures::future::join_all; | ||||
| use lettre::Address; | ||||
| use rocket::{ | ||||
|     form::Form, | ||||
|     fs::TempFile, | ||||
| @@ -355,6 +356,100 @@ async fn create( | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(FromForm, Debug)] | ||||
| struct UserAddScheckbuchForm<'r> { | ||||
|     name: &'r str, | ||||
|     mail: &'r str, | ||||
| } | ||||
|  | ||||
| #[post("/user/new/scheckbuch", data = "<data>")] | ||||
| async fn create_scheckbuch( | ||||
|     db: &State<SqlitePool>, | ||||
|     data: Form<UserAddScheckbuchForm<'_>>, | ||||
|     admin: VorstandUser, | ||||
|     config: &State<Config>, | ||||
| ) -> Flash<Redirect> { | ||||
|     // 1. Check mail adress | ||||
|     let mail = data.mail.trim(); | ||||
|     if mail.parse::<Address>().is_err() { | ||||
|         return Flash::error( | ||||
|             Redirect::to("/admin/user/scheckbuch"), | ||||
|             format!("Keine gültige Mailadresse"), | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     // 2. Check name | ||||
|     let name = data.name.trim(); | ||||
|     if User::find_by_name(db, name).await.is_some() { | ||||
|         return Flash::error( | ||||
|             Redirect::to("/admin/user/scheckbuch"), | ||||
|             format!( | ||||
|                 "Kann kein Scheckbuch erstellen, der Name wird bereits von einem User verwendet" | ||||
|             ), | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     // 3. Create user | ||||
|     User::create_with_mail(db, name, mail).await; | ||||
|     let user = User::find_by_name(db, name).await.unwrap(); | ||||
|  | ||||
|     // 4. Add 'scheckbuch' role | ||||
|     let scheckbuch = Role::find_by_name(db, "scheckbuch").await.unwrap(); | ||||
|     user.add_role(db, &scheckbuch).await; | ||||
|  | ||||
|     // 4. Send welcome mail (+ notification) | ||||
|     user.send_welcome_email(db, &config.smtp_pw).await.unwrap(); | ||||
|  | ||||
|     Log::create( | ||||
|         db, | ||||
|         format!("{} created new scheckbuch: {data:?}", admin.name), | ||||
|     ) | ||||
|     .await; | ||||
|     Flash::success(Redirect::to("/admin/user/scheckbuch"), &format!("Scheckbuch erfolgreich erstellt. Eine E-Mail in der alles erklärt wird, wurde an {mail} verschickt.")) | ||||
| } | ||||
|  | ||||
| #[get("/user/move/schnupperant/<id>/to/scheckbuch")] | ||||
| async fn schnupper_to_scheckbuch( | ||||
|     db: &State<SqlitePool>, | ||||
|     id: i32, | ||||
|     admin: SchnupperBetreuerUser, | ||||
|     config: &State<Config>, | ||||
| ) -> Flash<Redirect> { | ||||
|     let Some(user) = User::find_by_id(db, id).await else { | ||||
|         return Flash::error( | ||||
|             Redirect::to("/admin/schnupper"), | ||||
|             format!("user id not found"), | ||||
|         ); | ||||
|     }; | ||||
|  | ||||
|     if !user.has_role(db, "schnupperant").await { | ||||
|         return Flash::error( | ||||
|             Redirect::to("/admin/schnupper"), | ||||
|             format!("kein schnupperant..."), | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     let schnupperant = Role::find_by_name(db, "schnupperant").await.unwrap(); | ||||
|     let paid = Role::find_by_name(db, "paid").await.unwrap(); | ||||
|     user.remove_role(db, &schnupperant).await; | ||||
|     user.remove_role(db, &paid).await; | ||||
|  | ||||
|     let scheckbuch = Role::find_by_name(db, "scheckbuch").await.unwrap(); | ||||
|     user.add_role(db, &scheckbuch).await; | ||||
|  | ||||
|     user.send_welcome_email(db, &config.smtp_pw).await.unwrap(); | ||||
|  | ||||
|     Log::create( | ||||
|         db, | ||||
|         format!( | ||||
|             "{} created new scheckbuch (from schnupperant): {}", | ||||
|             admin.name, user.name | ||||
|         ), | ||||
|     ) | ||||
|     .await; | ||||
|     Flash::success(Redirect::to("/admin/schnupper"), &format!("Scheckbuch erfolgreich erstellt. Eine E-Mail in der alles erklärt wird, wurde an {} verschickt.", user.mail.unwrap())) | ||||
| } | ||||
|  | ||||
| pub fn routes() -> Vec<Route> { | ||||
|     routes![ | ||||
|         index, | ||||
| @@ -362,6 +457,8 @@ pub fn routes() -> Vec<Route> { | ||||
|         resetpw, | ||||
|         update, | ||||
|         create, | ||||
|         create_scheckbuch, | ||||
|         schnupper_to_scheckbuch, | ||||
|         delete, | ||||
|         fees, | ||||
|         fees_paid, | ||||
|   | ||||
| @@ -12,7 +12,12 @@ | ||||
|                         {% for user in schnupperanten %} | ||||
|                             <li class="py-1" | ||||
|                                 {% if "paid" in user.roles %}style="background-color: green;"{% endif %}> | ||||
|                                 {{ user.name }} ({{ user.mail }} | {{ user.notes }}) | ||||
|                                 {{ user.name }} ({{ user.mail }} | ||||
|                                 {%- if user.notes %} | {{ user.notes }}{% endif -%} | ||||
|                                 ) | ||||
|                                 <a class="btn btn-primary" | ||||
|                                    href="/admin/user/move/schnupperant/{{ user.id }}/to/scheckbuch" | ||||
|                                    onclick="return confirm('Willst du wirklich ein Scheckbuch erstellen? Die Person erhält ein Mail mit allen Infos.')">Zu Scheckbuch umwandeln</a> | ||||
|                             </li> | ||||
|                         {% endfor %} | ||||
|                     </ol> | ||||
|   | ||||
| @@ -4,6 +4,34 @@ | ||||
| {% block content %} | ||||
|     <div class="max-w-screen-lg w-full bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"> | ||||
|         <h1 class="h1">Scheckbücher</h1> | ||||
|         <form action="/admin/user/new/scheckbuch" | ||||
|               method="post" | ||||
|               class="mt-4 bg-primary-900 rounded-md text-white px-3 pb-3 pt-2 sm:flex items-end justify-between"> | ||||
|             <div class="w-full"> | ||||
|                 <h2 class="text-md font-bold mb-2 uppercase tracking-wide">Neues Scheckbuch hinzufügen</h2> | ||||
|                 <div class="grid md:grid-cols-3"> | ||||
|                     <div> | ||||
|                         <label for="name" class="sr-only">Name</label> | ||||
|                         <input type="text" | ||||
|                                name="name" | ||||
|                                class="relative block rounded-md border-0 py-1.5 px-2 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-primary-600 sm:text-sm sm:leading-6 mb-2 md:mb-0" | ||||
|                                placeholder="Name" /> | ||||
|                     </div> | ||||
|                     <div> | ||||
|                         <label for="name" class="sr-only">Mail</label> | ||||
|                         <input type="mail" | ||||
|                                name="mail" | ||||
|                                class="relative block rounded-md border-0 py-1.5 px-2 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-primary-600 sm:text-sm sm:leading-6 mb-2 md:mb-0" | ||||
|                                placeholder="Mail" /> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="text-right"> | ||||
|                 <input value="Hinzufügen" | ||||
|                        type="submit" | ||||
|                        class="w-28 mt-2 sm:mt-0 rounded-md bg-primary-500 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer" /> | ||||
|             </div> | ||||
|         </form> | ||||
|         <!-- START filterBar --> | ||||
|         <div class="search-wrapper"> | ||||
|             <label for="name" class="sr-only">Suche</label> | ||||
|   | ||||
| @@ -54,9 +54,12 @@ | ||||
|                    name="destination" | ||||
|                    value="" | ||||
|                    data-relation="distance_in_km" | ||||
| 		   autocomplete="off" /> | ||||
|                    autocomplete="off" /> | ||||
|             <datalist id="destinations"> | ||||
|                 {% for distance in distances %}<option value="{{ distance.destination  }}" distance="{{ distance.distance_in_km}}" />{% endfor %} | ||||
|                 {% for distance in distances %} | ||||
|                     <option value="{{ distance.destination }}" | ||||
|                             distance="{{ distance.distance_in_km }}" /> | ||||
|                 {% endfor %} | ||||
|             </datalist> | ||||
|         </div> | ||||
|         <div class="relative col-span-2"> | ||||
|   | ||||
| @@ -35,9 +35,7 @@ | ||||
|                                     <div> | ||||
|                                         {% if notification.link %} | ||||
|                                             <a href="{{ notification.link }}" class="inline-block"> | ||||
|                                                 <button class="btn btn-primary" type="button"> | ||||
|                                                     🔗 | ||||
|                                                 </button> | ||||
|                                                 <button class="btn btn-primary" type="button">🔗</button> | ||||
|                                             </a> | ||||
|                                         {% endif %} | ||||
|                                         {% if not notification.read_at %} | ||||
| @@ -65,9 +63,7 @@ | ||||
|                                         <div class="mt-1">{{ notification.message | safe }}</div> | ||||
|                                         {% if notification.link %} | ||||
|                                             <a href="{{ notification.link }}" class="inline-block"> | ||||
|                                                 <button class="btn btn-primary" type="button"> | ||||
|                                                     🔗 | ||||
|                                                 </button> | ||||
|                                                 <button class="btn btn-primary" type="button">🔗</button> | ||||
|                                             </a> | ||||
|                                         {% endif %} | ||||
|                                     </div> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user