board-scheckbook #687
@ -2,7 +2,7 @@
|
|||||||
secret_key = "/NtVGizglEoyoxBLzsRDWTy4oAG1qDw4J4O+CWJSv+fypD7W9sam8hUY4j90EZsbZk8wEradS5zBoWtWKi3k8w=="
|
secret_key = "/NtVGizglEoyoxBLzsRDWTy4oAG1qDw4J4O+CWJSv+fypD7W9sam8hUY4j90EZsbZk8wEradS5zBoWtWKi3k8w=="
|
||||||
rss_key = "rss-key-for-ci"
|
rss_key = "rss-key-for-ci"
|
||||||
limits = { file = "10 MiB", data-form = "10 MiB"}
|
limits = { file = "10 MiB", data-form = "10 MiB"}
|
||||||
smtp_pw = "8kIjlLH79Ky6D3jQ"
|
smtp_pw = "8kIjlLH79Ky6D3j"
|
||||||
usage_log_path = "./usage.txt"
|
usage_log_path = "./usage.txt"
|
||||||
openweathermap_key = "c8dab8f91b5b815d76e9879cbaecd8d5"
|
openweathermap_key = "c8dab8f91b5b815d76e9879cbaecd8d5"
|
||||||
wordpress_key = "pw-to-allow-sending-notifications"
|
wordpress_key = "pw-to-allow-sending-notifications"
|
||||||
|
@ -648,6 +648,14 @@ ORDER BY last_access DESC
|
|||||||
.is_ok()
|
.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<'_>) {
|
pub async fn update(&self, db: &SqlitePool, data: UserEditForm<'_>) {
|
||||||
let mut family_id = data.family_id;
|
let mut family_id = data.family_id;
|
||||||
|
|
||||||
|
@ -7,13 +7,14 @@ use crate::{
|
|||||||
logbook::Logbook,
|
logbook::Logbook,
|
||||||
role::Role,
|
role::Role,
|
||||||
user::{
|
user::{
|
||||||
AdminUser, AllowedToEditPaymentStatusUser, User, UserWithDetails,
|
AdminUser, AllowedToEditPaymentStatusUser, SchnupperBetreuerUser, User,
|
||||||
UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser,
|
UserWithDetails, UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tera::Config,
|
tera::Config,
|
||||||
};
|
};
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
|
use lettre::Address;
|
||||||
use rocket::{
|
use rocket::{
|
||||||
form::Form,
|
form::Form,
|
||||||
fs::TempFile,
|
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> {
|
pub fn routes() -> Vec<Route> {
|
||||||
routes![
|
routes![
|
||||||
index,
|
index,
|
||||||
@ -362,6 +457,8 @@ pub fn routes() -> Vec<Route> {
|
|||||||
resetpw,
|
resetpw,
|
||||||
update,
|
update,
|
||||||
create,
|
create,
|
||||||
|
create_scheckbuch,
|
||||||
|
schnupper_to_scheckbuch,
|
||||||
delete,
|
delete,
|
||||||
fees,
|
fees,
|
||||||
fees_paid,
|
fees_paid,
|
||||||
|
@ -12,7 +12,12 @@
|
|||||||
{% for user in schnupperanten %}
|
{% for user in schnupperanten %}
|
||||||
<li class="py-1"
|
<li class="py-1"
|
||||||
{% if "paid" in user.roles %}style="background-color: green;"{% endif %}>
|
{% 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>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ol>
|
</ol>
|
||||||
|
@ -4,6 +4,34 @@
|
|||||||
{% block content %}
|
{% 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">
|
<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>
|
<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 -->
|
<!-- START filterBar -->
|
||||||
<div class="search-wrapper">
|
<div class="search-wrapper">
|
||||||
<label for="name" class="sr-only">Suche</label>
|
<label for="name" class="sr-only">Suche</label>
|
||||||
|
@ -53,10 +53,13 @@
|
|||||||
id="destination"
|
id="destination"
|
||||||
name="destination"
|
name="destination"
|
||||||
value=""
|
value=""
|
||||||
data-relation="distance_in_km"
|
data-relation="distance_in_km"
|
||||||
autocomplete="off" />
|
autocomplete="off" />
|
||||||
<datalist id="destinations">
|
<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>
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative col-span-2">
|
<div class="relative col-span-2">
|
||||||
|
@ -35,9 +35,7 @@
|
|||||||
<div>
|
<div>
|
||||||
{% if notification.link %}
|
{% if notification.link %}
|
||||||
<a href="{{ notification.link }}" class="inline-block">
|
<a href="{{ notification.link }}" class="inline-block">
|
||||||
<button class="btn btn-primary" type="button">
|
<button class="btn btn-primary" type="button">🔗</button>
|
||||||
🔗
|
|
||||||
</button>
|
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not notification.read_at %}
|
{% if not notification.read_at %}
|
||||||
@ -65,9 +63,7 @@
|
|||||||
<div class="mt-1">{{ notification.message | safe }}</div>
|
<div class="mt-1">{{ notification.message | safe }}</div>
|
||||||
{% if notification.link %}
|
{% if notification.link %}
|
||||||
<a href="{{ notification.link }}" class="inline-block">
|
<a href="{{ notification.link }}" class="inline-block">
|
||||||
<button class="btn btn-primary" type="button">
|
<button class="btn btn-primary" type="button">🔗</button>
|
||||||
🔗
|
|
||||||
</button>
|
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user