refactor: user management
This commit is contained in:
parent
a975ff7104
commit
e87feca855
@ -98,3 +98,13 @@ CREATE UNLOGGED TABLE passwordReset
|
||||
);
|
||||
|
||||
CREATE INDEX passwordReset_token_idx ON passwordReset (token);
|
||||
|
||||
CREATE UNLOGGED TABLE registration
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
token TEXT UNIQUE NOT NULL,
|
||||
userId INTEGER NOT NULL REFERENCES user_ (id),
|
||||
expires TIMESTAMP NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX registration_token_idx ON registration (token);
|
||||
|
@ -4,7 +4,7 @@ use sqlx::PgPool;
|
||||
|
||||
use crate::{endpoints::IdPath, models::{Role, User}};
|
||||
|
||||
#[actix_web::delete("/users/delete/{id}")]
|
||||
#[actix_web::delete("/users/{id}")]
|
||||
pub async fn delete(user: Identity, pool: web::Data<PgPool>, path: web::Path<IdPath>) -> impl Responder {
|
||||
let current_user = User::read_by_id(pool.get_ref(), user.id().unwrap().parse().unwrap())
|
||||
.await
|
||||
@ -21,7 +21,7 @@ pub async fn delete(user: Identity, pool: web::Data<PgPool>, path: web::Path<IdP
|
||||
|
||||
if user_in_db.locked {
|
||||
if let Ok(_) = User::delete(pool.get_ref(), user_in_db.id).await {
|
||||
return HttpResponse::NoContent().finish();
|
||||
return HttpResponse::Ok().finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +1,39 @@
|
||||
use actix_identity::Identity;
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
use askama::Template;
|
||||
use askama_actix::TemplateToResponse;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{endpoints::IdPath, models::{Area, Role, User}};
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "user/edit.html")]
|
||||
pub struct EditUserTemplate {
|
||||
user: User,
|
||||
areas: Option<Vec<Area>>,
|
||||
id: i32,
|
||||
email: String,
|
||||
name: String,
|
||||
role: u8,
|
||||
function: u8,
|
||||
area_id: i32
|
||||
}
|
||||
use crate::{
|
||||
endpoints::{user::NewOrEditUserTemplate, IdPath},
|
||||
models::{Area, Role, User},
|
||||
};
|
||||
|
||||
#[actix_web::get("/users/edit/{id}")]
|
||||
pub async fn get_edit(user: Identity, pool: web::Data<PgPool>, path: web::Path<IdPath>) -> impl Responder {
|
||||
let current_user = User::read_by_id(pool.get_ref(), user.id().unwrap().parse().unwrap()).await.unwrap();
|
||||
pub async fn get_edit(
|
||||
user: web::ReqData<User>,
|
||||
pool: web::Data<PgPool>,
|
||||
path: web::Path<IdPath>,
|
||||
) -> impl Responder {
|
||||
let mut areas = None;
|
||||
|
||||
if current_user.role != Role::AreaManager && current_user.role != Role::Admin {
|
||||
if user.role != Role::AreaManager && user.role != Role::Admin {
|
||||
return HttpResponse::Unauthorized().finish();
|
||||
}
|
||||
|
||||
if current_user.role == Role::Admin {
|
||||
if user.role == Role::Admin {
|
||||
areas = Some(Area::read_all(pool.get_ref()).await.unwrap());
|
||||
}
|
||||
|
||||
if let Ok(user_in_db) = User::read_by_id(pool.get_ref(), path.id).await {
|
||||
let template = EditUserTemplate { user: current_user, id: user_in_db.id, areas, email: user_in_db.email, name: user_in_db.name, role: user_in_db.role as u8, function: user_in_db.function as u8, area_id: user_in_db.area_id };
|
||||
let template = NewOrEditUserTemplate {
|
||||
user: user.into_inner(),
|
||||
id: Some(user_in_db.id),
|
||||
areas,
|
||||
email: Some(user_in_db.email),
|
||||
name: Some(user_in_db.name),
|
||||
role: Some(user_in_db.role as u8),
|
||||
function: Some(user_in_db.function as u8),
|
||||
area_id: Some(user_in_db.area_id),
|
||||
};
|
||||
|
||||
return template.to_response();
|
||||
}
|
||||
|
@ -1,28 +1,30 @@
|
||||
use actix_identity::Identity;
|
||||
use actix_web::{web, Responder};
|
||||
use askama::Template;
|
||||
use askama_actix::TemplateToResponse;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::models::{Area, Role, User};
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "user/new.html")]
|
||||
pub struct NewUserTemplate {
|
||||
user: User,
|
||||
areas: Option<Vec<Area>>
|
||||
}
|
||||
use crate::{
|
||||
endpoints::user::NewOrEditUserTemplate,
|
||||
models::{Area, Role, User},
|
||||
};
|
||||
|
||||
#[actix_web::get("/users/new")]
|
||||
pub async fn get_new(user: Identity, pool: web::Data<PgPool>) -> impl Responder {
|
||||
let current_user = User::read_by_id(pool.get_ref(), user.id().unwrap().parse().unwrap()).await.unwrap();
|
||||
pub async fn get_new(user: web::ReqData<User>, pool: web::Data<PgPool>) -> impl Responder {
|
||||
let mut areas: Option<Vec<Area>> = None;
|
||||
|
||||
if current_user.role == Role::Admin {
|
||||
if user.role == Role::Admin {
|
||||
areas = Some(Area::read_all(pool.get_ref()).await.unwrap())
|
||||
}
|
||||
|
||||
let template = NewUserTemplate { user: current_user, areas };
|
||||
let template = NewOrEditUserTemplate {
|
||||
user: user.into_inner(),
|
||||
areas,
|
||||
id: None,
|
||||
email: None,
|
||||
name: None,
|
||||
role: None,
|
||||
function: None,
|
||||
area_id: None,
|
||||
};
|
||||
|
||||
template.to_response()
|
||||
}
|
||||
|
@ -1,16 +1,33 @@
|
||||
pub mod get_overview;
|
||||
pub mod get_new;
|
||||
pub mod post_new;
|
||||
pub mod get_edit;
|
||||
pub mod post_edit;
|
||||
pub mod patch;
|
||||
use crate::filters;
|
||||
use crate::models::{Area, Role, User};
|
||||
use askama::Template;
|
||||
|
||||
pub mod delete;
|
||||
pub mod get_logout;
|
||||
pub mod get_login;
|
||||
pub mod post_login;
|
||||
pub mod get_reset;
|
||||
pub mod post_reset;
|
||||
pub mod get_profile;
|
||||
pub mod post_toggle;
|
||||
pub mod get_changepassword;
|
||||
pub mod get_edit;
|
||||
pub mod get_login;
|
||||
pub mod get_logout;
|
||||
pub mod get_new;
|
||||
pub mod get_overview;
|
||||
pub mod get_profile;
|
||||
pub mod get_reset;
|
||||
pub mod patch;
|
||||
pub mod post_changepassword;
|
||||
pub mod post_edit;
|
||||
pub mod post_login;
|
||||
pub mod post_new;
|
||||
pub mod post_reset;
|
||||
pub mod post_toggle;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "user/new_or_edit.html")]
|
||||
pub struct NewOrEditUserTemplate {
|
||||
user: User,
|
||||
areas: Option<Vec<Area>>,
|
||||
id: Option<i32>,
|
||||
email: Option<String>,
|
||||
name: Option<String>,
|
||||
role: Option<u8>,
|
||||
function: Option<u8>,
|
||||
area_id: Option<i32>,
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ pub struct JsonPatchDoc {
|
||||
value: Value,
|
||||
}
|
||||
|
||||
// TODO: deprecated route
|
||||
#[actix_web::patch("/users/edit/{id}")]
|
||||
pub async fn patch(
|
||||
user: web::ReqData<User>,
|
||||
|
@ -11,7 +11,7 @@ pub struct NewUserForm {
|
||||
name: String,
|
||||
password: String,
|
||||
role: u8,
|
||||
function: u8,
|
||||
function: u8,
|
||||
area: Option<i32>
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ Viele Grüße"##, user.name, reset_url))
|
||||
}
|
||||
}
|
||||
|
||||
let user = User::read_by_id(pool.get_ref(), token.as_ref().unwrap().id)
|
||||
let user = User::read_by_id(pool.get_ref(), token.as_ref().unwrap().userid)
|
||||
.await
|
||||
.unwrap();
|
||||
let mut split_names: Vec<&str> = user.name.as_str().split_whitespace().collect();
|
||||
@ -143,7 +143,7 @@ Viele Grüße"##, user.name, reset_url))
|
||||
|
||||
User::update(
|
||||
pool.get_ref(),
|
||||
token.as_ref().unwrap().id,
|
||||
token.as_ref().unwrap().userid,
|
||||
None,
|
||||
None,
|
||||
Some(&hash),
|
||||
|
@ -19,7 +19,7 @@ pub async fn post(
|
||||
path: web::Path<IdPath>,
|
||||
query: web::Query<ToggleQuery>,
|
||||
) -> impl Responder {
|
||||
if user.id != path.id && (user.role != Role::Admin || user.role != Role::AreaManager) {
|
||||
if user.id != path.id && user.role != Role::Admin && user.role != Role::AreaManager {
|
||||
return HttpResponse::Unauthorized().finish();
|
||||
}
|
||||
|
||||
@ -30,21 +30,61 @@ pub async fn post(
|
||||
};
|
||||
|
||||
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(),
|
||||
"locked" => {
|
||||
User::update(
|
||||
pool.get_ref(),
|
||||
user.id,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(!user.locked),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if !user.locked {
|
||||
return HttpResponse::Ok().body(format!(
|
||||
r##"<span class="icon">
|
||||
<svg class="feather">
|
||||
<use href="/static/feather-sprite.svg#unlock" />
|
||||
</svg>
|
||||
</span>
|
||||
<span>Entsperren</span>
|
||||
<div id="user-{id}-locked" hx-swap-oob="true">ja</div>
|
||||
<button id="user-{id}-delete" hx-swap-oob="true" class="button is-danger is-light" hx-delete="/users/{id}" hx-target="closest tr" hx-swap="delete">
|
||||
<span class="icon">
|
||||
<svg class="feather">
|
||||
<use href="/static/feather-sprite.svg#x-circle" />
|
||||
</svg>
|
||||
</span>
|
||||
<span>Löschen</span>
|
||||
</button>"##,
|
||||
id = user.id));
|
||||
} else {
|
||||
return HttpResponse::Ok().body(format!(
|
||||
r##"<span class="icon">
|
||||
<svg class="feather">
|
||||
<use href="/static/feather-sprite.svg#lock" />
|
||||
</svg>
|
||||
</span>
|
||||
<span>Sperren</span>
|
||||
<div id="user-{id}-locked" hx-swap-oob="true">nein</div>
|
||||
<button id="user-{id}-delete" hx-swap-oob="true" class="button is-danger is-light" disabled hx-delete="/users/{id}" hx-target="closest tr" hx-swap="delete">
|
||||
<span class="icon">
|
||||
<svg class="feather">
|
||||
<use href="/static/feather-sprite.svg#x-circle" />
|
||||
</svg>
|
||||
</span>
|
||||
<span>Löschen</span>
|
||||
</button>"##,
|
||||
id = user.id));
|
||||
}
|
||||
}
|
||||
"receiveNotifications" => {
|
||||
User::update(
|
||||
pool.get_ref(),
|
||||
@ -61,7 +101,6 @@ pub async fn post(
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
//return HttpResponse::Ok().body("<input />");
|
||||
}
|
||||
_ => return HttpResponse::BadRequest().body("Other PATCH paths are not supported!"),
|
||||
};
|
||||
|
@ -1,9 +1,5 @@
|
||||
pub fn show_area_query(a: &Option<i32>, first: bool) -> ::askama::Result<String> {
|
||||
let char = if first {
|
||||
'?'
|
||||
} else {
|
||||
'&'
|
||||
};
|
||||
let char = if first { '?' } else { '&' };
|
||||
|
||||
if let Some(a) = a {
|
||||
return Ok(format!("{}area={}", char, a));
|
||||
@ -17,7 +13,25 @@ pub fn cond_show(show: &bool, text: &str) -> askama::Result<String> {
|
||||
Ok(String::from(text))
|
||||
} else {
|
||||
Ok(String::new())
|
||||
};
|
||||
}
|
||||
|
||||
pub fn insert_value(option: &Option<String>) -> askama::Result<String> {
|
||||
if let Some(val) = option {
|
||||
println!("{val}");
|
||||
let s = format!(r#"value="{val}""#);
|
||||
println!("{s}");
|
||||
return Ok(s);
|
||||
}
|
||||
|
||||
Ok(String::new())
|
||||
}
|
||||
|
||||
pub fn is_some_and_eq<T>(option: &Option<T>, other: &T) -> askama::Result<bool>
|
||||
where
|
||||
T: Eq
|
||||
{
|
||||
Ok(option.as_ref().is_some_and(|x| x == other))
|
||||
}
|
||||
|
||||
pub fn invert(b: &bool) -> askama::Result<bool> {
|
||||
|
@ -1,127 +0,0 @@
|
||||
{% extends "nav.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<form method="post" action="/users/edit/{{ id }}">
|
||||
<h1 class="title">Nutzer '{{ name }}' bearbeiten</h1>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">E-Mail</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" type="text" name="email" placeholder="max.mustermann@brasiwa-leipzig.de"
|
||||
value="{{ email }}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Name</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" type="text" name="name" placeholder="Max Mustermann" value="{{ name }}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Password</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" type="password" name="password" placeholder="**********" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Rolle</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field is-narrow">
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select name="role">
|
||||
<option value="1" {% if role==1%}selected{% endif %}>Personal</option>
|
||||
<option value="10" {% if role==10%}selected{% endif %}>Bereichsleiter</option>
|
||||
<option value="100" {% if role==100%}selected{% endif %}>Admin</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Funktion</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field is-narrow">
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select name="function">
|
||||
<option value="1" {% if function==1%}selected{% endif %}>Posten</option>
|
||||
<option value="10" {% if function==10%}selected{% endif %}>Wachhabender</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if user.role == Role::Admin %}
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Bereich</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field is-narrow">
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select name="area">
|
||||
{% for area in areas.as_ref().unwrap() %}
|
||||
<option value="{{ area.id }}" {% if area.id==area_id%}selected{% endif %}>{{ area.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label"></div>
|
||||
<div class="field-body">
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<input class="button is-link" type="submit" value="Erstellen">
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-link is-light" href="/users">Zurück</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
{% endblock %}
|
@ -1,126 +0,0 @@
|
||||
{% extends "nav.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<form method="post" action="/users/new">
|
||||
<h1 class="title">Neuen Nutzer anlegen</h1>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">E-Mail</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" type="text" name="email" placeholder="max.mustermann@brasiwa-leipzig.de" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Name</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" type="text" name="name" placeholder="Max Mustermann" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Password</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" type="password" name="password" placeholder="**********" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Rolle</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field is-narrow">
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select name="role">
|
||||
<option value="1">Personal</option>
|
||||
<option value="10">Bereichsleiter</option>
|
||||
<option value="100">Admin</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Funktion</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field is-narrow">
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select name="function">
|
||||
<option value="1">Posten</option>
|
||||
<option value="10">Wachhabender</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if user.role == Role::Admin %}
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Bereich</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field is-narrow">
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select name="area">
|
||||
{% for area in areas.as_ref().unwrap() %}
|
||||
<option value="{{ area.id }}">{{ area.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label"></div>
|
||||
<div class="field-body">
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<input class="button is-link" type="submit" value="Erstellen">
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-link is-light" href="/users">Zurück</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
{% endblock %}
|
141
templates/user/new_or_edit.html
Normal file
141
templates/user/new_or_edit.html
Normal file
@ -0,0 +1,141 @@
|
||||
{% extends "nav.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
{% if id.is_some() %}
|
||||
<form method="post" action="/users/edit/{{ id.unwrap() }}">
|
||||
<h1 class="title">Nutzer '{{ name.as_ref().unwrap() }}' bearbeiten</h1>
|
||||
{% else %}
|
||||
<form method="post" action="/users/new">
|
||||
<h1 class="title">Neuen Nutzer anlegen</h1>
|
||||
{% endif %}
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">E-Mail</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" type="text" name="email" placeholder="max.mustermann@brasiwa-leipzig.de" {{
|
||||
email|insert_value|safe }} required />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Name</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" type="text" name="name" placeholder="Max Mustermann" {{ name|insert_value|safe }}
|
||||
required />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Rolle</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field is-narrow">
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select name="role">
|
||||
<option value="1" {{ role|is_some_and_eq(1|as_ref)|cond_show("selected") }}>Personal</option>
|
||||
<option value="10" {{ role|is_some_and_eq(10|as_ref)|cond_show("selected") }}>Bereichsleiter
|
||||
</option>
|
||||
<option value="100" {{ role|is_some_and_eq(100|as_ref)|cond_show("selected") }}>Admin</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Funktion</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field is-narrow">
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select name="function">
|
||||
<option value="1" {{ function|is_some_and_eq(1|as_ref)|cond_show("selected") }}>Posten</option>
|
||||
<option value="10" {{ function|is_some_and_eq(10|as_ref)|cond_show("selected") }}>Wachhabender
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if user.role == Role::Admin %}
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Bereich</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field is-narrow">
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select name="area">
|
||||
{% for area in areas.as_ref().unwrap() %}
|
||||
<option value="{{ area.id }}" {{ area_id|is_some_and_eq(area.id)|cond_show("selected") }}>{{
|
||||
area.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label"></div>
|
||||
<div class="field-body">
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button class="button is-success">
|
||||
<span class="icon">
|
||||
<svg class="feather">
|
||||
<use href="/static/feather-sprite.svg#check-circle" />
|
||||
</svg>
|
||||
</span>
|
||||
<span>
|
||||
{% if id.is_some() %}
|
||||
Speichern
|
||||
{% else %}
|
||||
Erstellen
|
||||
{% endif %}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-link is-light" hx-boost="true" href="/users">
|
||||
<span class="icon">
|
||||
<svg class="feather">
|
||||
<use href="/static/feather-sprite.svg#arrow-left" />
|
||||
</svg>
|
||||
</span>
|
||||
<span>Zurück</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% endblock %}
|
@ -10,15 +10,17 @@
|
||||
</h3>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<a class="button" href="/users/new">Neuen Nutzer anlegen</a>
|
||||
<a class="button is-link is-light" hx-boost="true" href="/users/new">
|
||||
<span class="icon">
|
||||
<svg class="feather">
|
||||
<use href="/static/feather-sprite.svg#plus-circle" />
|
||||
</svg>
|
||||
</span>
|
||||
<span>Neuen Nutzer anlegen</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if users.len() == 0 %}
|
||||
<div class="box">
|
||||
<h5 class="title is-5">keine Nutzer vorhanden</h5>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="box">
|
||||
<table class="table is-fullwidth">
|
||||
<thead>
|
||||
@ -36,8 +38,8 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for u in users %}
|
||||
<tr id="user-{{ u.id }}">
|
||||
{% for u in users %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ u.email }}
|
||||
</td>
|
||||
@ -46,22 +48,22 @@
|
||||
</td>
|
||||
<td>
|
||||
{% match u.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 %}
|
||||
{% when Role::Staff %}
|
||||
<span class="tag is-info is-light">Personal</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 %}
|
||||
</td>
|
||||
<td>
|
||||
{% match u.function %}
|
||||
{% when Function::Posten %}
|
||||
<span class="tag is-info is-light">Posten</span>
|
||||
{% when Function::Wachhabender %}
|
||||
<span class="tag is-info">Wachhabender</span>
|
||||
{% else %}
|
||||
{% when Function::Posten %}
|
||||
<span class="tag is-primary is-light">Posten</span>
|
||||
{% when Function::Wachhabender %}
|
||||
<span class="tag is-primary">Wachhabender</span>
|
||||
{% else %}
|
||||
{% endmatch %}
|
||||
</td>
|
||||
{% if user.role == Role::Admin %}
|
||||
@ -70,90 +72,58 @@
|
||||
</td>
|
||||
{% endif %}
|
||||
<td>
|
||||
{% if u.last_login.is_some() %}
|
||||
{{ u.last_login.as_ref().unwrap() }}
|
||||
{% else %}
|
||||
{% if u.last_login.is_some() %}
|
||||
{% let format = "%d.%m.%Y %H:%M:%S" %}
|
||||
{{ u.last_login.as_ref().unwrap().format(format) }}
|
||||
{% else %}
|
||||
nie
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if u.locked %}
|
||||
ja
|
||||
{% else %}
|
||||
nein
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="buttons is-right">
|
||||
<button class="button is-link is-light" name="toggle-lock-user">{% if u.locked %}Entsperren{% else %}Sperren{% endif %}</button>
|
||||
<a class="button is-link" href="/users/edit/{{ u.id }}">Bearbeiten</a>
|
||||
<button class="button is-danger" {% if !u.locked %}disabled{% endif %} name="delete-user">Löschen</button>
|
||||
<div id="user-{{ u.id }}-locked">
|
||||
{% if u.locked %}
|
||||
ja
|
||||
{% else %}
|
||||
nein
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{% if user.id != u.id %}
|
||||
<div class="buttons is-right">
|
||||
<button class="button is-link is-light" hx-post="/users/{{ u.id }}/toggle?field=locked">
|
||||
<span class="icon">
|
||||
<svg class="feather">
|
||||
<use href="/static/feather-sprite.svg#{% if u.locked %}unlock{% else %}lock{% endif %}" />
|
||||
</svg>
|
||||
</span>
|
||||
<span>{% if u.locked %}Entsperren{% else %}Sperren{% endif %}</span>
|
||||
</button>
|
||||
<a class="button is-primary is-light" hx-boost="true" href="/users/edit/{{ u.id }}">
|
||||
<span class="icon">
|
||||
<svg class="feather">
|
||||
<use href="/static/feather-sprite.svg#edit" />
|
||||
</svg>
|
||||
</span>
|
||||
<span>Bearbeiten</span>
|
||||
</a>
|
||||
<button id="user-{{ u.id }}-delete" class="button is-danger is-light" {% if !u.locked %}disabled{% endif
|
||||
%} hx-delete="/users/{{ u.id }}" hx-target="closest tr" hx-swap="delete">
|
||||
<span class="icon">
|
||||
<svg class="feather">
|
||||
<use href="/static/feather-sprite.svg#x-circle" />
|
||||
</svg>
|
||||
</span>
|
||||
<span>Löschen</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
document.getElementsByName("delete-user")
|
||||
.forEach(ele => ele.addEventListener("click", (event) => {
|
||||
const id = event.target.closest("tr").id.split('-')[1];
|
||||
event.target.classList.add("is-loading");
|
||||
|
||||
fetch(`/users/delete/${id}`, { method: "DELETE"})
|
||||
.then(response => {
|
||||
if (response.status == 204) {
|
||||
document.getElementById(`user-${id}`).remove()
|
||||
} else {
|
||||
event.target.classList.remove("is-loading");
|
||||
console.log("Fehler beim Löschen.")
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
document.getElementsByName("toggle-lock-user")
|
||||
.forEach(ele => ele.addEventListener("click", (event) => {
|
||||
const id = event.target.closest("tr").id.split('-')[1];
|
||||
event.target.classList.add("is-loading");
|
||||
|
||||
const locked = event.target.innerHTML == "Sperren" ? false : true;
|
||||
|
||||
fetch(`/users/edit/${id}`, {
|
||||
method: "PATCH",
|
||||
headers: new Headers({
|
||||
"Content-Type": "application/json-patch+json",
|
||||
}),
|
||||
body: JSON.stringify([
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/locked",
|
||||
"value": !locked
|
||||
}
|
||||
])
|
||||
})
|
||||
.then(response => {
|
||||
if (response.status == 200) {
|
||||
event.target.classList.remove("is-loading");
|
||||
if (locked) {
|
||||
event.target.innerHTML = "Sperren";
|
||||
event.target.closest("tr").querySelector("td:nth-last-child(2)").innerHTML = "nein"
|
||||
event.target.closest("div.buttons").querySelector("button:nth-last-child(1)").setAttribute("disabled", "");
|
||||
} else {
|
||||
event.target.innerHTML = "Entsperren";
|
||||
event.target.closest("tr").querySelector("td:nth-last-child(2)").innerHTML = "ja"
|
||||
event.target.closest("div.buttons").querySelector("button:nth-last-child(1)").removeAttribute("disabled");
|
||||
}
|
||||
} else {
|
||||
event.target.classList.remove("is-loading");
|
||||
console.log("Fehler beim PATCH.")
|
||||
}
|
||||
});
|
||||
}));
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -43,7 +43,7 @@
|
||||
<div class="control">
|
||||
{% match user.role %}
|
||||
{% when Role::Staff %}
|
||||
<span class="tag is-info is-light">Nutzer</span>
|
||||
<span class="tag is-info is-light">Personal</span>
|
||||
{% when Role::AreaManager %}
|
||||
<span class="tag is-info is-light">Bereichsleiter</span>
|
||||
{% when Role::Admin %}
|
||||
@ -64,9 +64,9 @@
|
||||
<div class="control">
|
||||
{% match user.function %}
|
||||
{% when Function::Posten %}
|
||||
<span class="tag is-info is-light">Posten</span>
|
||||
<span class="tag is-primary is-light">Posten</span>
|
||||
{% when Function::Wachhabender %}
|
||||
<span class="tag is-info">Wachhabender</span>
|
||||
<span class="tag is-primary">Wachhabender</span>
|
||||
{% else %}
|
||||
{% endmatch %}
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user