feat: set functions for user
This commit is contained in:
parent
c830704fa4
commit
736b69d2c3
@ -8,6 +8,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use actix_web::HttpResponse;
|
use actix_web::HttpResponse;
|
||||||
use rinja::Template;
|
use rinja::Template;
|
||||||
|
use serde::Deserialize;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use zxcvbn::{zxcvbn, Score};
|
use zxcvbn::{zxcvbn, Score};
|
||||||
|
|
||||||
@ -26,9 +27,9 @@ pub mod post_edit;
|
|||||||
pub mod post_login;
|
pub mod post_login;
|
||||||
pub mod post_new;
|
pub mod post_new;
|
||||||
pub mod post_register;
|
pub mod post_register;
|
||||||
|
pub mod post_resend_registration;
|
||||||
pub mod post_reset;
|
pub mod post_reset;
|
||||||
pub mod post_toggle;
|
pub mod post_toggle;
|
||||||
pub mod post_resend_registration;
|
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "user/new_or_edit.html")]
|
#[template(path = "user/new_or_edit.html")]
|
||||||
@ -45,6 +46,21 @@ pub struct NewOrEditUserTemplate {
|
|||||||
area_id: Option<i32>,
|
area_id: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[cfg_attr(test, derive(serde::Serialize))]
|
||||||
|
struct NewOrEditUserForm {
|
||||||
|
email: String,
|
||||||
|
name: String,
|
||||||
|
role: u8,
|
||||||
|
#[serde(rename(deserialize = "is-posten"))]
|
||||||
|
is_posten: Option<bool>,
|
||||||
|
#[serde(rename(deserialize = "is-wachhabender"))]
|
||||||
|
is_wachhabender: Option<bool>,
|
||||||
|
#[serde(rename(deserialize = "is-fuehrungsassistent"))]
|
||||||
|
is_fuehrungsassistent: Option<bool>,
|
||||||
|
area: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "user/change_password.html")]
|
#[template(path = "user/change_password.html")]
|
||||||
struct ResetPasswordTemplate<'a> {
|
struct ResetPasswordTemplate<'a> {
|
||||||
|
@ -1,30 +1,19 @@
|
|||||||
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
||||||
use garde::Validate;
|
use garde::Validate;
|
||||||
use serde::Deserialize;
|
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
endpoints::IdPath,
|
endpoints::{user::NewOrEditUserForm, IdPath},
|
||||||
models::{Area, Role, User, UserChangeset},
|
models::{Area, Function, Role, User, UserChangeset},
|
||||||
utils::ApplicationError,
|
utils::ApplicationError,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
#[cfg_attr(test, derive(serde::Serialize))]
|
|
||||||
pub struct EditUserForm {
|
|
||||||
email: String,
|
|
||||||
name: String,
|
|
||||||
role: u8,
|
|
||||||
function: u8,
|
|
||||||
area: Option<i32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix_web::post("/users/edit/{id}")]
|
#[actix_web::post("/users/edit/{id}")]
|
||||||
pub async fn post_edit(
|
pub async fn post_edit(
|
||||||
user: web::ReqData<User>,
|
user: web::ReqData<User>,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
path: web::Path<IdPath>,
|
path: web::Path<IdPath>,
|
||||||
form: web::Form<EditUserForm>,
|
form: web::Form<NewOrEditUserForm>,
|
||||||
) -> Result<impl Responder, ApplicationError> {
|
) -> Result<impl Responder, ApplicationError> {
|
||||||
if user.role != Role::AreaManager && user.role != Role::Admin {
|
if user.role != Role::AreaManager && user.role != Role::Admin {
|
||||||
return Err(ApplicationError::Unauthorized);
|
return Err(ApplicationError::Unauthorized);
|
||||||
@ -47,11 +36,25 @@ pub async fn post_edit(
|
|||||||
return Err(ApplicationError::Unauthorized);
|
return Err(ApplicationError::Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut functions = Vec::with_capacity(3);
|
||||||
|
|
||||||
|
if form.is_posten.unwrap_or(false) {
|
||||||
|
functions.push(Function::Posten);
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.is_wachhabender.unwrap_or(false) {
|
||||||
|
functions.push(Function::Wachhabender);
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.is_fuehrungsassistent.unwrap_or(false) {
|
||||||
|
functions.push(Function::Fuehrungsassistent);
|
||||||
|
}
|
||||||
|
|
||||||
let changeset = UserChangeset {
|
let changeset = UserChangeset {
|
||||||
name: form.name.clone(),
|
name: form.name.clone(),
|
||||||
email: form.email.clone(),
|
email: form.email.clone(),
|
||||||
role: form.role.try_into()?,
|
role: form.role.try_into()?,
|
||||||
function: form.function.try_into()?,
|
functions,
|
||||||
area_id,
|
area_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -69,8 +72,7 @@ pub async fn post_edit(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use crate::{endpoints::user::NewOrEditUserForm, models::*, utils::test_helper::*};
|
||||||
use crate::{models::*, utils::test_helper::*};
|
|
||||||
use brass_macros::db_test;
|
use brass_macros::db_test;
|
||||||
use fake::{
|
use fake::{
|
||||||
faker::{internet::en::SafeEmail, name::en::Name},
|
faker::{internet::en::SafeEmail, name::en::Name},
|
||||||
@ -94,11 +96,13 @@ mod tests {
|
|||||||
let new_name: String = Name().fake();
|
let new_name: String = Name().fake();
|
||||||
let new_mail: String = SafeEmail().fake();
|
let new_mail: String = SafeEmail().fake();
|
||||||
|
|
||||||
let form = EditUserForm {
|
let form = NewOrEditUserForm {
|
||||||
name: new_name.clone(),
|
name: new_name.clone(),
|
||||||
email: new_mail.clone(),
|
email: new_mail.clone(),
|
||||||
role: Role::AreaManager as u8,
|
role: Role::AreaManager as u8,
|
||||||
function: Function::Fuehrungsassistent as u8,
|
is_posten: None,
|
||||||
|
is_wachhabender: None,
|
||||||
|
is_fuehrungsassistent: Some(true),
|
||||||
area: Some(2),
|
area: Some(2),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -127,11 +131,13 @@ mod tests {
|
|||||||
user_area: 1,
|
user_area: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let form = EditUserForm {
|
let form = NewOrEditUserForm {
|
||||||
name: "".to_string(),
|
name: "".to_string(),
|
||||||
email: "".to_string(),
|
email: "".to_string(),
|
||||||
role: Role::AreaManager as u8,
|
role: Role::AreaManager as u8,
|
||||||
function: Function::Fuehrungsassistent as u8,
|
is_posten: None,
|
||||||
|
is_wachhabender: None,
|
||||||
|
is_fuehrungsassistent: Some(true),
|
||||||
area: Some(1),
|
area: Some(1),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,28 +1,19 @@
|
|||||||
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
||||||
use garde::Validate;
|
use garde::Validate;
|
||||||
use serde::Deserialize;
|
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
endpoints::user::NewOrEditUserForm,
|
||||||
mail::Mailer,
|
mail::Mailer,
|
||||||
models::{Function, Registration, Role, User, UserChangeset},
|
models::{Function, Registration, Role, User, UserChangeset},
|
||||||
utils::ApplicationError,
|
utils::ApplicationError,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct NewUserForm {
|
|
||||||
email: String,
|
|
||||||
name: String,
|
|
||||||
role: u8,
|
|
||||||
function: u8,
|
|
||||||
area: Option<i32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix_web::post("/users/new")]
|
#[actix_web::post("/users/new")]
|
||||||
pub async fn post_new(
|
pub async fn post_new(
|
||||||
user: web::ReqData<User>,
|
user: web::ReqData<User>,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
form: web::Form<NewUserForm>,
|
form: web::Form<NewOrEditUserForm>,
|
||||||
mailer: web::Data<Mailer>,
|
mailer: web::Data<Mailer>,
|
||||||
) -> Result<impl Responder, ApplicationError> {
|
) -> Result<impl Responder, ApplicationError> {
|
||||||
if user.role != Role::AreaManager && user.role != Role::Admin {
|
if user.role != Role::AreaManager && user.role != Role::Admin {
|
||||||
@ -36,29 +27,39 @@ pub async fn post_new(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let role = Role::try_from(form.role)?;
|
let role = Role::try_from(form.role)?;
|
||||||
let function = Function::try_from(form.function)?;
|
let mut functions = Vec::with_capacity(3);
|
||||||
|
|
||||||
|
if form.is_posten.unwrap_or(false) {
|
||||||
|
functions.push(Function::Posten);
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.is_wachhabender.unwrap_or(false) {
|
||||||
|
functions.push(Function::Wachhabender);
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.is_fuehrungsassistent.unwrap_or(false) {
|
||||||
|
functions.push(Function::Fuehrungsassistent);
|
||||||
|
}
|
||||||
|
|
||||||
let changeset = UserChangeset {
|
let changeset = UserChangeset {
|
||||||
name: form.name.clone(),
|
name: form.name.clone(),
|
||||||
email: form.email.clone(),
|
email: form.email.clone(),
|
||||||
role,
|
role,
|
||||||
function,
|
functions,
|
||||||
area_id
|
area_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = changeset.validate() {
|
if let Err(e) = changeset.validate() {
|
||||||
return Ok(HttpResponse::BadRequest().body(e.to_string()));
|
return Ok(HttpResponse::BadRequest().body(e.to_string()));
|
||||||
};
|
};
|
||||||
|
|
||||||
let id = User::create(
|
let id = User::create(pool.get_ref(), changeset).await?;
|
||||||
pool.get_ref(),
|
|
||||||
changeset
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let registration = Registration::insert_new_for_user(pool.get_ref(), id).await?;
|
let registration = Registration::insert_new_for_user(pool.get_ref(), id).await?;
|
||||||
let new_user = User::read_by_id(pool.get_ref(), id).await?.unwrap();
|
let new_user = User::read_by_id(pool.get_ref(), id).await?.unwrap();
|
||||||
mailer.send_registration_mail(&new_user, ®istration.token).await?;
|
mailer
|
||||||
|
.send_registration_mail(&new_user, ®istration.token)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(HttpResponse::Found()
|
Ok(HttpResponse::Found()
|
||||||
.insert_header((LOCATION, "/users"))
|
.insert_header((LOCATION, "/users"))
|
||||||
|
@ -30,7 +30,7 @@ impl User {
|
|||||||
changeset.name,
|
changeset.name,
|
||||||
changeset.email,
|
changeset.email,
|
||||||
changeset.role as Role,
|
changeset.role as Role,
|
||||||
changeset.function as Function,
|
changeset.functions.as_slice() as &[Function],
|
||||||
changeset.area_id
|
changeset.area_id
|
||||||
)
|
)
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
@ -285,7 +285,7 @@ impl User {
|
|||||||
changeset.name,
|
changeset.name,
|
||||||
changeset.email,
|
changeset.email,
|
||||||
changeset.role as Role,
|
changeset.role as Role,
|
||||||
changeset.function as Function,
|
changeset.functions.as_slice() as &[Function],
|
||||||
changeset.area_id,
|
changeset.area_id,
|
||||||
id
|
id
|
||||||
)
|
)
|
||||||
|
@ -5,7 +5,7 @@ use garde::Validate;
|
|||||||
|
|
||||||
use super::{Function, Role};
|
use super::{Function, Role};
|
||||||
|
|
||||||
#[derive(Validate)]
|
#[derive(Debug, Validate)]
|
||||||
#[cfg_attr(test, derive(Dummy))]
|
#[cfg_attr(test, derive(Dummy))]
|
||||||
#[garde(allow_unvalidated)]
|
#[garde(allow_unvalidated)]
|
||||||
pub struct UserChangeset {
|
pub struct UserChangeset {
|
||||||
@ -16,8 +16,8 @@ pub struct UserChangeset {
|
|||||||
pub email: String,
|
pub email: String,
|
||||||
#[cfg_attr(test, dummy(expr = "Role::Staff"))]
|
#[cfg_attr(test, dummy(expr = "Role::Staff"))]
|
||||||
pub role: Role,
|
pub role: Role,
|
||||||
#[cfg_attr(test, dummy(expr = "Function::Posten"))]
|
#[cfg_attr(test, dummy(expr = "vec![Function::Posten]"))]
|
||||||
pub function: Function,
|
pub functions: Vec<Function>,
|
||||||
/// check before: must exist and user can create other user for this area
|
/// check before: must exist and user can create other user for this area
|
||||||
#[cfg_attr(test, dummy(expr = "1"))]
|
#[cfg_attr(test, dummy(expr = "1"))]
|
||||||
pub area_id: i32,
|
pub area_id: i32,
|
||||||
|
@ -64,12 +64,28 @@
|
|||||||
<label class="label">Funktion</label>
|
<label class="label">Funktion</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field is-narrow">
|
<div class="field is-grouped">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div class="select is-fullwidth">
|
<label class="checkbox">
|
||||||
<select name="function">
|
<input id="is-posten" type="checkbox" name="is-posten" value="true" {{
|
||||||
</select>
|
is_posten.unwrap_or(false)|ref|cond_show("checked") }} />
|
||||||
</div>
|
Posten
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox" name="is-wachhabender" value="true" {{
|
||||||
|
is_wachhabender.unwrap_or(false)|ref|cond_show("checked") }}
|
||||||
|
_="on change if me.checked then set checked of previous <input/> to true" />
|
||||||
|
Wachhabender
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox" name="is-fuehrungsassistent" value="true" {{
|
||||||
|
is_fuehrungsassistent.unwrap_or(false)|ref|cond_show("checked") }} />
|
||||||
|
Führungsassistent
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -87,8 +103,8 @@
|
|||||||
<select name="area">
|
<select name="area">
|
||||||
{% for area in areas.as_ref().unwrap() %}
|
{% for area in areas.as_ref().unwrap() %}
|
||||||
<option value="{{ area.id }}" {{ area_id|is_some_and_eq(area.id)|ref|cond_show("selected") }}>{{
|
<option value="{{ area.id }}" {{ area_id|is_some_and_eq(area.id)|ref|cond_show("selected") }}>{{
|
||||||
area.name }}</option>
|
area.name }}</input>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user