refactor: user management

This commit is contained in:
Max Hohlfeld 2024-08-29 22:33:22 +02:00
parent a975ff7104
commit e87feca855
15 changed files with 367 additions and 426 deletions

View File

@ -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);

View File

@ -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();
}
}
}

View File

@ -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();
}

View File

@ -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()
}

View File

@ -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>,
}

View File

@ -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>,

View File

@ -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),

View File

@ -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!"),
};

View File

@ -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> {

View File

@ -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 %}

View File

@ -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 %}

View 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 %}

View File

@ -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 %}

View File

@ -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>