feat: user profile page
This commit is contained in:
parent
33baf479b5
commit
7d47b38037
@ -37,6 +37,10 @@ pub fn init(cfg: &mut ServiceConfig) {
|
|||||||
cfg.service(user::post_login::post);
|
cfg.service(user::post_login::post);
|
||||||
cfg.service(user::get_reset::get);
|
cfg.service(user::get_reset::get);
|
||||||
cfg.service(user::post_reset::post);
|
cfg.service(user::post_reset::post);
|
||||||
|
cfg.service(user::get_profile::get);
|
||||||
|
cfg.service(user::post_toggle::post);
|
||||||
|
cfg.service(user::get_changepassword::get);
|
||||||
|
cfg.service(user::post_changepassword::post);
|
||||||
|
|
||||||
cfg.service(availability::delete::delete);
|
cfg.service(availability::delete::delete);
|
||||||
cfg.service(availability::get_new::get);
|
cfg.service(availability::get_new::get);
|
||||||
|
45
src/endpoints/user/get_changepassword.rs
Normal file
45
src/endpoints/user/get_changepassword.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
use actix_web::{HttpRequest, HttpResponse, Responder};
|
||||||
|
|
||||||
|
#[actix_web::get("/users/changepassword")]
|
||||||
|
pub async fn get(request: HttpRequest) -> impl Responder {
|
||||||
|
if let Some(_) = request.headers().get("HX-Request") {
|
||||||
|
return HttpResponse::Ok().body(r##"
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<a href="/profile" hx-boost="true" class="button is-link is-light">Schließen</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<form class="box" hx-post="/users/changepassword" hx-target-400="#error-message" hx-on:change="document.getElementById('error-message').innerHTML = ''">
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="currentpassword">aktuelles Passwort:</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" placeholder="**********" name="currentpassword" type="password" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="password">neues Passwort:</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" placeholder="**********" name="password" type="password" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="passwordretyped">neues Passwort wiederholen:</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" placeholder="**********" name="passwordretyped" type="password" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="error-message" class="mb-3 help is-danger"></div>
|
||||||
|
|
||||||
|
<div class="level">
|
||||||
|
<input class="button is-primary level-left" type="submit" value="Passwort ändern" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
"##);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponse::NotFound().finish()
|
||||||
|
}
|
24
src/endpoints/user/get_profile.rs
Normal file
24
src/endpoints/user/get_profile.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use actix_web::{web, Responder};
|
||||||
|
use askama::Template;
|
||||||
|
use askama_actix::TemplateToResponse;
|
||||||
|
use sqlx::PgPool;
|
||||||
|
|
||||||
|
use crate::models::{Area, Function, Role, User};
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "user/profile.html")]
|
||||||
|
struct ProfileTemplate {
|
||||||
|
user: User
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web::get("/profile")]
|
||||||
|
pub async fn get(user: web::ReqData<User>, pool: web::Data<PgPool>) -> impl Responder {
|
||||||
|
let area = Area::read_by_id(pool.get_ref(), user.area_id).await.unwrap();
|
||||||
|
|
||||||
|
let mut user = user.into_inner();
|
||||||
|
user.area = Some(area);
|
||||||
|
|
||||||
|
let template = ProfileTemplate { user };
|
||||||
|
|
||||||
|
return template.to_response();
|
||||||
|
}
|
@ -10,3 +10,7 @@ pub mod get_login;
|
|||||||
pub mod post_login;
|
pub mod post_login;
|
||||||
pub mod get_reset;
|
pub mod get_reset;
|
||||||
pub mod post_reset;
|
pub mod post_reset;
|
||||||
|
pub mod get_profile;
|
||||||
|
pub mod post_toggle;
|
||||||
|
pub mod get_changepassword;
|
||||||
|
pub mod post_changepassword;
|
||||||
|
@ -13,32 +13,28 @@ use crate::{
|
|||||||
pub struct JsonPatchDoc {
|
pub struct JsonPatchDoc {
|
||||||
op: String,
|
op: String,
|
||||||
path: String,
|
path: String,
|
||||||
value: Value
|
value: Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_web::patch("/users/edit/{id}")]
|
#[actix_web::patch("/users/edit/{id}")]
|
||||||
pub async fn patch(
|
pub async fn patch(
|
||||||
user: Identity,
|
user: web::ReqData<User>,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
path: web::Path<IdPath>,
|
path: web::Path<IdPath>,
|
||||||
patch_docs: web::Json<Vec<JsonPatchDoc>>,
|
patch_docs: web::Json<Vec<JsonPatchDoc>>,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
let current_user = User::read_by_id(pool.get_ref(), user.id().unwrap().parse().unwrap())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if current_user.role != Role::AreaManager && current_user.role != Role::Admin {
|
let is_superuser = user.role != Role::AreaManager && user.role != Role::Admin;
|
||||||
return HttpResponse::Unauthorized().finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(user_in_db) = User::read_by_id(pool.get_ref(), path.id).await {
|
if let Ok(user_in_db) = User::read_by_id(pool.get_ref(), path.id).await {
|
||||||
if current_user.role == Role::AreaManager && current_user.area_id != user_in_db.area_id {
|
if user.role == Role::AreaManager && user.area_id != user_in_db.area_id {
|
||||||
return HttpResponse::Unauthorized().finish();
|
return HttpResponse::Unauthorized().finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
|
|
||||||
let mut locked: Option<bool> = None;
|
let mut locked: Option<bool> = None;
|
||||||
|
let mut receive_notifications: Option<bool> = None;
|
||||||
|
|
||||||
for doc in patch_docs.iter() {
|
for doc in patch_docs.iter() {
|
||||||
if doc.op.as_str() != "replace" {
|
if doc.op.as_str() != "replace" {
|
||||||
@ -47,21 +43,44 @@ pub async fn patch(
|
|||||||
|
|
||||||
match doc.path.as_str() {
|
match doc.path.as_str() {
|
||||||
"/locked" => {
|
"/locked" => {
|
||||||
|
if !is_superuser {
|
||||||
|
return HttpResponse::Unauthorized().finish();
|
||||||
|
}
|
||||||
changed = true;
|
changed = true;
|
||||||
if let Value::Bool(b) = doc.value {
|
if let Value::Bool(b) = doc.value {
|
||||||
locked = Some(b);
|
locked = Some(b);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => panic!("other patch paths are not supported!"),
|
"/receiveNotifications" => {
|
||||||
|
changed = true;
|
||||||
|
if let Value::Bool(b) = doc.value {
|
||||||
|
receive_notifications = Some(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return HttpResponse::BadRequest().body("Other PATCH paths are not supported!")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
if let Ok(_) = User::update(pool.get_ref(), path.id, None, None, None, None, None, None, None, locked).await {
|
if let Ok(_) = User::update(
|
||||||
|
pool.get_ref(),
|
||||||
|
path.id,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
receive_notifications,
|
||||||
|
locked,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
return HttpResponse::Ok().body("");
|
return HttpResponse::Ok().body("");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return HttpResponse::Ok().body("");
|
return HttpResponse::Ok().body("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
49
src/endpoints/user/post_changepassword.rs
Normal file
49
src/endpoints/user/post_changepassword.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use sqlx::PgPool;
|
||||||
|
|
||||||
|
use crate::{auth::utils, models::User};
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct ChangePasswordForm {
|
||||||
|
currentpassword: String,
|
||||||
|
password: String,
|
||||||
|
passwordretyped: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web::post("/users/changepassword")]
|
||||||
|
async fn post(
|
||||||
|
user: web::ReqData<User>,
|
||||||
|
form: web::Form<ChangePasswordForm>,
|
||||||
|
pool: web::Data<PgPool>,
|
||||||
|
) -> impl Responder {
|
||||||
|
if user.password
|
||||||
|
== utils::hash_plain_password_with_salt(&form.currentpassword, &user.salt).unwrap()
|
||||||
|
{
|
||||||
|
if form.password != form.passwordretyped {
|
||||||
|
return HttpResponse::BadRequest().body("Passwörter stimmen nicht überein!");
|
||||||
|
}
|
||||||
|
|
||||||
|
let (hash, salt) = utils::generate_salt_and_hash_plain_password(&form.password).unwrap();
|
||||||
|
|
||||||
|
User::update(
|
||||||
|
pool.get_ref(),
|
||||||
|
user.id,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(&hash),
|
||||||
|
Some(&salt),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
return HttpResponse::Ok().body(r#"<div class="block">Passwort wurde geändert.</div>"#);
|
||||||
|
} else {
|
||||||
|
return HttpResponse::BadRequest().body("Aktuelles Passwort ist nicht korrekt!");
|
||||||
|
}
|
||||||
|
}
|
@ -83,7 +83,7 @@ pub async fn post_edit(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
match User::update(pool.get_ref(), path.id, email, name, None, None, role, function, area, None).await {
|
match User::update(pool.get_ref(), path.id, email, name, None, None, role, function, area, None, None).await {
|
||||||
Ok(_) => return HttpResponse::Found().insert_header((LOCATION, "/users")).finish(),
|
Ok(_) => return HttpResponse::Found().insert_header((LOCATION, "/users")).finish(),
|
||||||
Err(err) => println!("{}", err)
|
Err(err) => println!("{}", err)
|
||||||
}
|
}
|
||||||
|
@ -91,6 +91,7 @@ async fn post(form: web::Form<ResetPasswordForm>, pool: web::Data<PgPool>) -> im
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
70
src/endpoints/user/post_toggle.rs
Normal file
70
src/endpoints/user/post_toggle.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use sqlx::PgPool;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
endpoints::IdPath,
|
||||||
|
models::{Role, User},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct ToggleQuery {
|
||||||
|
field: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web::post("/users/{id}/toggle")]
|
||||||
|
pub async fn post(
|
||||||
|
user: web::ReqData<User>,
|
||||||
|
pool: web::Data<PgPool>,
|
||||||
|
path: web::Path<IdPath>,
|
||||||
|
query: web::Query<ToggleQuery>,
|
||||||
|
) -> impl Responder {
|
||||||
|
if user.id != path.id && (user.role != Role::Admin || user.role != Role::AreaManager) {
|
||||||
|
return HttpResponse::Unauthorized().finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
let user = if user.id != path.id {
|
||||||
|
User::read_by_id(pool.get_ref(), path.id).await.unwrap()
|
||||||
|
} else {
|
||||||
|
user.into_inner()
|
||||||
|
};
|
||||||
|
|
||||||
|
match query.field.as_str() {
|
||||||
|
"locked" => User::update(
|
||||||
|
pool.get_ref(),
|
||||||
|
user.id,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(!user.locked),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
"receiveNotifications" => {
|
||||||
|
User::update(
|
||||||
|
pool.get_ref(),
|
||||||
|
user.id,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(!user.receive_notifications),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
//return HttpResponse::Ok().body("<input />");
|
||||||
|
}
|
||||||
|
_ => return HttpResponse::BadRequest().body("Other PATCH paths are not supported!"),
|
||||||
|
};
|
||||||
|
|
||||||
|
HttpResponse::Ok().finish()
|
||||||
|
}
|
@ -66,7 +66,7 @@ impl User {
|
|||||||
areaId,
|
areaId,
|
||||||
locked,
|
locked,
|
||||||
lastLogin,
|
lastLogin,
|
||||||
receiveNotifications
|
receiveNotifications
|
||||||
FROM user_
|
FROM user_
|
||||||
WHERE id = $1;
|
WHERE id = $1;
|
||||||
"#,
|
"#,
|
||||||
@ -106,7 +106,7 @@ impl User {
|
|||||||
areaId,
|
areaId,
|
||||||
locked,
|
locked,
|
||||||
lastLogin,
|
lastLogin,
|
||||||
receiveNotifications
|
receiveNotifications
|
||||||
FROM user_
|
FROM user_
|
||||||
WHERE email = $1 AND locked = FALSE;
|
WHERE email = $1 AND locked = FALSE;
|
||||||
"#,
|
"#,
|
||||||
@ -146,7 +146,7 @@ impl User {
|
|||||||
areaId,
|
areaId,
|
||||||
locked,
|
locked,
|
||||||
lastLogin,
|
lastLogin,
|
||||||
receiveNotifications
|
receiveNotifications
|
||||||
FROM user_;
|
FROM user_;
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
@ -235,7 +235,7 @@ impl User {
|
|||||||
areaId,
|
areaId,
|
||||||
locked,
|
locked,
|
||||||
lastLogin,
|
lastLogin,
|
||||||
receiveNotifications
|
receiveNotifications
|
||||||
FROM user_
|
FROM user_
|
||||||
WHERE areaId = $1;
|
WHERE areaId = $1;
|
||||||
"#,
|
"#,
|
||||||
@ -275,6 +275,7 @@ impl User {
|
|||||||
role: Option<Role>,
|
role: Option<Role>,
|
||||||
function: Option<Function>,
|
function: Option<Function>,
|
||||||
area_id: Option<i32>,
|
area_id: Option<i32>,
|
||||||
|
receive_notifications: Option<bool>,
|
||||||
locked: Option<bool>,
|
locked: Option<bool>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let mut query_builder = sqlx::QueryBuilder::new("UPDATE user_ SET ");
|
let mut query_builder = sqlx::QueryBuilder::new("UPDATE user_ SET ");
|
||||||
@ -315,6 +316,11 @@ impl User {
|
|||||||
separated.push_bind_unseparated(area_id);
|
separated.push_bind_unseparated(area_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(receive_notifications) = receive_notifications {
|
||||||
|
separated.push("receiveNotifications = ");
|
||||||
|
separated.push_bind_unseparated(receive_notifications);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(locked) = locked {
|
if let Some(locked) = locked {
|
||||||
separated.push("locked = ");
|
separated.push("locked = ");
|
||||||
separated.push_bind_unseparated(locked);
|
separated.push_bind_unseparated(locked);
|
||||||
|
@ -33,6 +33,16 @@
|
|||||||
top: -.05em;
|
top: -.05em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.result {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadeout {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 2s ease-in;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -66,10 +66,10 @@
|
|||||||
<div class="navbar-item">
|
<div class="navbar-item">
|
||||||
angemeldet als {{ user.name }}
|
angemeldet als {{ user.name }}
|
||||||
<div class="buttons ml-3">
|
<div class="buttons ml-3">
|
||||||
<a class="button is-success">
|
<a class="button is-success" hx-boost="true" href="/profile">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<svg class="feather">
|
<svg class="feather">
|
||||||
<use href="/static/feather-sprite.svg#check-circle" />
|
<use href="/static/feather-sprite.svg#user" />
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
<span>Profil</span>
|
<span>Profil</span>
|
||||||
|
@ -7,114 +7,115 @@
|
|||||||
<h1 class="title">Nutzer '{{ name }}' bearbeiten</h1>
|
<h1 class="title">Nutzer '{{ name }}' bearbeiten</h1>
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
<label class="label">E-Mail</label>
|
<label class="label">E-Mail</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input" type="text" name="email" placeholder="max.mustermann@brasiwa-leipzig.de" value="{{ email }}"/>
|
<input class="input" type="text" name="email" placeholder="max.mustermann@brasiwa-leipzig.de"
|
||||||
</div>
|
value="{{ email }}" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
<label class="label">Name</label>
|
<label class="label">Name</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input" type="text" name="name" placeholder="Max Mustermann" value="{{ name }}"/>
|
<input class="input" type="text" name="name" placeholder="Max Mustermann" value="{{ name }}" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
<label class="label">Password</label>
|
<label class="label">Password</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input" type="password" name="password" placeholder="**********"/>
|
<input class="input" type="password" name="password" placeholder="**********" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
<label class="label">Rolle</label>
|
<label class="label">Rolle</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field is-narrow">
|
<div class="field is-narrow">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div class="select is-fullwidth">
|
<div class="select is-fullwidth">
|
||||||
<select name="role">
|
<select name="role">
|
||||||
<option value="1" {% if role == 1%}selected{% endif %}>Personal</option>
|
<option value="1" {% if role==1%}selected{% endif %}>Personal</option>
|
||||||
<option value="10" {% if role == 10%}selected{% endif %}>Bereichsleiter</option>
|
<option value="10" {% if role==10%}selected{% endif %}>Bereichsleiter</option>
|
||||||
<option value="100" {% if role == 100%}selected{% endif %}>Admin</option>
|
<option value="100" {% if role==100%}selected{% endif %}>Admin</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
<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-narrow">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div class="select is-fullwidth">
|
<div class="select is-fullwidth">
|
||||||
<select name="function">
|
<select name="function">
|
||||||
<option value="1" {% if function == 1%}selected{% endif %}>Posten</option>
|
<option value="1" {% if function==1%}selected{% endif %}>Posten</option>
|
||||||
<option value="10" {% if function == 10%}selected{% endif %}>Wachhabender</option>
|
<option value="10" {% if function==10%}selected{% endif %}>Wachhabender</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if user.role == Role::Admin %}
|
{% if user.role == Role::Admin %}
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
<label class="label">Bereich</label>
|
<label class="label">Bereich</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field is-narrow">
|
<div class="field is-narrow">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div class="select is-fullwidth">
|
<div class="select is-fullwidth">
|
||||||
<select name="area">
|
<select name="area">
|
||||||
{% for area in areas.as_ref().unwrap() %}
|
{% for area in areas.as_ref().unwrap() %}
|
||||||
<option value="{{ area.id }}" {% if area.id == area_id%}selected{% endif %}>{{ area.name }}</option>
|
<option value="{{ area.id }}" {% if area.id==area_id%}selected{% endif %}>{{ area.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label"></div>
|
<div class="field-label"></div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="button is-link" type="submit" value="Erstellen">
|
<input class="button is-link" type="submit" value="Erstellen">
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<a class="button is-link is-light" href="/users">Zurück</a>
|
<a class="button is-link is-light" href="/users">Zurück</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
@ -7,114 +7,114 @@
|
|||||||
<h1 class="title">Neuen Nutzer anlegen</h1>
|
<h1 class="title">Neuen Nutzer anlegen</h1>
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
<label class="label">E-Mail</label>
|
<label class="label">E-Mail</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input" type="text" name="email" placeholder="max.mustermann@brasiwa-leipzig.de"/>
|
<input class="input" type="text" name="email" placeholder="max.mustermann@brasiwa-leipzig.de" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
<label class="label">Name</label>
|
<label class="label">Name</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input" type="text" name="name" placeholder="Max Mustermann"/>
|
<input class="input" type="text" name="name" placeholder="Max Mustermann" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
<label class="label">Password</label>
|
<label class="label">Password</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input" type="password" name="password" placeholder="**********"/>
|
<input class="input" type="password" name="password" placeholder="**********" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
<label class="label">Rolle</label>
|
<label class="label">Rolle</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field is-narrow">
|
<div class="field is-narrow">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div class="select is-fullwidth">
|
<div class="select is-fullwidth">
|
||||||
<select name="role">
|
<select name="role">
|
||||||
<option value="1">Personal</option>
|
<option value="1">Personal</option>
|
||||||
<option value="10">Bereichsleiter</option>
|
<option value="10">Bereichsleiter</option>
|
||||||
<option value="100">Admin</option>
|
<option value="100">Admin</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
<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-narrow">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div class="select is-fullwidth">
|
<div class="select is-fullwidth">
|
||||||
<select name="function">
|
<select name="function">
|
||||||
<option value="1">Posten</option>
|
<option value="1">Posten</option>
|
||||||
<option value="10">Wachhabender</option>
|
<option value="10">Wachhabender</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if user.role == Role::Admin %}
|
{% if user.role == Role::Admin %}
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
<label class="label">Bereich</label>
|
<label class="label">Bereich</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field is-narrow">
|
<div class="field is-narrow">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div class="select is-fullwidth">
|
<div class="select is-fullwidth">
|
||||||
<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.name }}</option>
|
<option value="{{ area.id }}">{{ area.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label"></div>
|
<div class="field-label"></div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="button is-link" type="submit" value="Erstellen">
|
<input class="button is-link" type="submit" value="Erstellen">
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<a class="button is-link is-light" href="/users">Zurück</a>
|
<a class="button is-link is-light" href="/users">Zurück</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
@ -124,7 +124,7 @@
|
|||||||
|
|
||||||
const locked = event.target.innerHTML == "Sperren" ? false : true;
|
const locked = event.target.innerHTML == "Sperren" ? false : true;
|
||||||
|
|
||||||
fetch(`/users/edit/${id}`, {
|
fetch(`/users/edit/${id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
headers: new Headers({
|
headers: new Headers({
|
||||||
"Content-Type": "application/json-patch+json",
|
"Content-Type": "application/json-patch+json",
|
||||||
|
89
templates/user/profile.html
Normal file
89
templates/user/profile.html
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
{% extends "nav.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="title">Profil</h1>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Name</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" value="{{ user.name }}" readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">E-Mail</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="email" value="{{ user.name }}" readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Rolle</label>
|
||||||
|
<div class="control">
|
||||||
|
{% match user.role %}
|
||||||
|
{% when Role::Staff %}
|
||||||
|
<span class="tag is-info is-light">Nutzer</span>
|
||||||
|
{% when Role::AreaManager %}
|
||||||
|
<span class="tag is-info is-light">Bereichsleiter</span>
|
||||||
|
{% when Role::Admin %}
|
||||||
|
<span class="tag is-info">Admin</span>
|
||||||
|
{% else %}
|
||||||
|
{% endmatch %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Funktion</label>
|
||||||
|
<div class="control">
|
||||||
|
{% match user.function %}
|
||||||
|
{% when Function::Posten %}
|
||||||
|
<span class="tag is-info is-light">Posten</span>
|
||||||
|
{% when Function::Wachhabender %}
|
||||||
|
<span class="tag is-info">Wachhabender</span>
|
||||||
|
{% else %}
|
||||||
|
{% endmatch %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Bereich</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" value="{{ user.area.as_ref().unwrap().name }}" readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input hx-post="/users/{{ user.id }}/toggle?field=receiveNotifications" type="checkbox"
|
||||||
|
hx-on::before-request="document.getElementById('success').classList.remove('fadeout')"
|
||||||
|
hx-on::after-request="document.getElementById('success').classList.add('fadeout')"
|
||||||
|
checked="{{ user.receive_notifications }}">
|
||||||
|
Ich möchte E-Mail Benachrichtungen zu neuen Brasiwa-Einteilungen erhalten.
|
||||||
|
<span id="success" class="result">
|
||||||
|
<span class="icon mr-2">
|
||||||
|
<svg class="feather">
|
||||||
|
<use href="/static/feather-sprite.svg#check" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
gespeichert
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<button hx-get="/users/changepassword" hx-target="closest div.field" hx-swap="outerHTML"
|
||||||
|
class="button is-link is-light">Passwort ändern</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user