feat: logic for new functions system

This commit is contained in:
Max Hohlfeld 2025-03-23 20:23:58 +01:00
parent 736b69d2c3
commit 6ee3ca9e89
20 changed files with 107 additions and 102 deletions

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n availabillity.id,\n availabillity.userId,\n availabillity.date,\n availabillity.startTime,\n availabillity.endTime,\n availabillity.comment,\n user_.name,\n user_.email,\n user_.password,\n user_.salt,\n user_.role AS \"role: Role\",\n user_.function AS \"function: Functions\",\n user_.areaId,\n user_.locked,\n user_.lastLogin,\n user_.receiveNotifications\n FROM availabillity\n JOIN user_ ON availabillity.userId = user_.id\n WHERE availabillity.date = $1\n AND user_.areaId = $2;\n ",
"query": "\n SELECT\n availabillity.id,\n availabillity.userId,\n availabillity.date,\n availabillity.startTime,\n availabillity.endTime,\n availabillity.comment,\n user_.name,\n user_.email,\n user_.password,\n user_.salt,\n user_.role AS \"role: Role\",\n user_.function AS \"function: UserFunction\",\n user_.areaId,\n user_.locked,\n user_.lastLogin,\n user_.receiveNotifications\n FROM availabillity\n JOIN user_ ON availabillity.userId = user_.id\n WHERE availabillity.date = $1\n AND user_.areaId = $2;\n ",
"describe": {
"columns": [
{
@ -71,7 +71,7 @@
},
{
"ordinal": 11,
"name": "function: Functions",
"name": "function: UserFunction",
"type_info": {
"Custom": {
"name": "function[]",
@ -138,5 +138,5 @@
false
]
},
"hash": "a2ff81071585a31385044f022553dc1e5f81fd3727f47825991adcfae5546324"
"hash": "147dc1d57f2ecba26967f4d6b7085ae4d0095c769aa73b698a12fd67a728ea95"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n availabillity.id,\n availabillity.userId,\n availabillity.date,\n availabillity.startTime,\n availabillity.endTime,\n availabillity.comment,\n user_.name,\n user_.email,\n user_.password,\n user_.salt,\n user_.role AS \"role: Role\",\n user_.function AS \"function: Functions\",\n user_.areaId,\n user_.locked,\n user_.lastLogin,\n user_.receiveNotifications\n FROM availabillity\n LEFT JOIN assignment ON availabillity.Id = assignment.availabillityId\n JOIN user_ ON availabillity.userId = user_.id\n WHERE availabillity.id = $1;\n ",
"query": "\n SELECT\n availabillity.id,\n availabillity.userId,\n availabillity.date,\n availabillity.startTime,\n availabillity.endTime,\n availabillity.comment,\n user_.name,\n user_.email,\n user_.password,\n user_.salt,\n user_.role AS \"role: Role\",\n user_.function AS \"function: UserFunction\",\n user_.areaId,\n user_.locked,\n user_.lastLogin,\n user_.receiveNotifications\n FROM availabillity\n LEFT JOIN assignment ON availabillity.Id = assignment.availabillityId\n JOIN user_ ON availabillity.userId = user_.id\n WHERE availabillity.id = $1;\n ",
"describe": {
"columns": [
{
@ -71,7 +71,7 @@
},
{
"ordinal": 11,
"name": "function: Functions",
"name": "function: UserFunction",
"type_info": {
"Custom": {
"name": "function[]",
@ -137,5 +137,5 @@
false
]
},
"hash": "b06a1cfc8ba53c2f95ed5b74e9b6d73e723750b3679e2dbe970ed03e01886c90"
"hash": "1647030e9c5f52326f80dffb1e722e4d700f4a303e43a33af7fe79608fd438fe"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT id,\n name,\n email,\n password,\n salt,\n role AS \"role: Role\",\n function AS \"function: Functions\",\n areaId,\n locked,\n lastLogin,\n receiveNotifications\n FROM user_\n WHERE areaId = $1;\n ",
"query": "\n SELECT id,\n name,\n email,\n password,\n salt,\n role AS \"role: Role\",\n function AS \"function: UserFunction\",\n areaId,\n locked,\n lastLogin,\n receiveNotifications\n FROM user_\n WHERE id = $1;\n ",
"describe": {
"columns": [
{
@ -46,7 +46,7 @@
},
{
"ordinal": 6,
"name": "function: Functions",
"name": "function: UserFunction",
"type_info": {
"Custom": {
"name": "function[]",
@ -107,5 +107,5 @@
false
]
},
"hash": "4da130f5ff77ea010db74c51e5dfc966aeae7ff1c4fa44395a0136bbd4a760c5"
"hash": "24449d7fa57151cc7bbe17b757c4a155eaf85f51b91e0724a100e7eedfa096ce"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT id,\n name,\n email,\n password,\n salt,\n role AS \"role: Role\",\n function AS \"function: Functions\",\n areaId,\n locked,\n lastLogin,\n receiveNotifications\n FROM user_\n WHERE id = $1;\n ",
"query": "\n SELECT id,\n name,\n email,\n password,\n salt,\n role AS \"role: Role\",\n function AS \"function: UserFunction\",\n areaId,\n locked,\n lastLogin,\n receiveNotifications\n FROM user_\n WHERE areaId = $1;\n ",
"describe": {
"columns": [
{
@ -46,7 +46,7 @@
},
{
"ordinal": 6,
"name": "function: Functions",
"name": "function: UserFunction",
"type_info": {
"Custom": {
"name": "function[]",
@ -107,5 +107,5 @@
false
]
},
"hash": "abcd0f243fc7b93ea20c4dbd3c9b18ada4dc45893dd2f8f25873cb6819ee986e"
"hash": "483ad933fa1e935058cbe42b7ff083ceee80f74564ee3e8b7da6ab57e906368b"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT id,\n name,\n email,\n password,\n salt,\n role AS \"role: Role\",\n function AS \"function: Functions\",\n areaId,\n locked,\n lastLogin,\n receiveNotifications\n FROM user_;\n ",
"query": "\n SELECT id,\n name,\n email,\n password,\n salt,\n role AS \"role: Role\",\n function AS \"function: UserFunction\",\n areaId,\n locked,\n lastLogin,\n receiveNotifications\n FROM user_;\n ",
"describe": {
"columns": [
{
@ -46,7 +46,7 @@
},
{
"ordinal": 6,
"name": "function: Functions",
"name": "function: UserFunction",
"type_info": {
"Custom": {
"name": "function[]",
@ -105,5 +105,5 @@
false
]
},
"hash": "aa60c8e32c6c226f0b2f9161fd27bc9bf986cb7a69a7f5449c10a1b5642d9360"
"hash": "5573e93ccc0b6a5ecc6183a5d5c589ccd58f786e70a3ff1efa662085c2035156"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT id,\n name,\n email,\n password,\n salt,\n role AS \"role: Role\",\n function AS \"function: Functions\",\n areaId,\n locked,\n lastLogin,\n receiveNotifications\n FROM user_\n WHERE email = $1 AND locked = FALSE AND password IS NOT NULL AND salt IS NOT NULL;\n ",
"query": "\n SELECT id,\n name,\n email,\n password,\n salt,\n role AS \"role: Role\",\n function AS \"function: UserFunction\",\n areaId,\n locked,\n lastLogin,\n receiveNotifications\n FROM user_\n WHERE email = $1 AND locked = FALSE AND password IS NOT NULL AND salt IS NOT NULL;\n ",
"describe": {
"columns": [
{
@ -46,7 +46,7 @@
},
{
"ordinal": 6,
"name": "function: Functions",
"name": "function: UserFunction",
"type_info": {
"Custom": {
"name": "function[]",
@ -107,5 +107,5 @@
false
]
},
"hash": "db1a44c82fca70aa0a0530a07b75bd2a596a16deb60ca89fedb26c8ad89dbdce"
"hash": "9091186ff6f2e2013cdca9d66c6f5be5207b0e868b6de0f558a469138838a650"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n availabillity.id,\n availabillity.userId,\n availabillity.date,\n availabillity.startTime,\n availabillity.endTime,\n availabillity.comment,\n user_.name,\n user_.email,\n user_.password,\n user_.salt,\n user_.role AS \"role: Role\",\n user_.function AS \"function: Functions\",\n user_.areaId,\n user_.locked,\n user_.lastLogin,\n user_.receiveNotifications,\n area.name AS areaName\n FROM availabillity\n JOIN user_ ON availabillity.userId = user_.id\n JOIN area ON user_.areaId = area.id\n WHERE user_.areaId = $1 AND\n availabillity.date >= $2 AND\n availabillity.date <= $3;\n ",
"query": "\n SELECT\n availabillity.id,\n availabillity.userId,\n availabillity.date,\n availabillity.startTime,\n availabillity.endTime,\n availabillity.comment,\n user_.name,\n user_.email,\n user_.password,\n user_.salt,\n user_.role AS \"role: Role\",\n user_.function AS \"function: UserFunction\",\n user_.areaId,\n user_.locked,\n user_.lastLogin,\n user_.receiveNotifications,\n area.name AS areaName\n FROM availabillity\n JOIN user_ ON availabillity.userId = user_.id\n JOIN area ON user_.areaId = area.id\n WHERE user_.areaId = $1 AND\n availabillity.date >= $2 AND\n availabillity.date <= $3;\n ",
"describe": {
"columns": [
{
@ -71,7 +71,7 @@
},
{
"ordinal": 11,
"name": "function: Functions",
"name": "function: UserFunction",
"type_info": {
"Custom": {
"name": "function[]",
@ -145,5 +145,5 @@
false
]
},
"hash": "6adecc71eae15d0ef809e16cfedf42ab8ec9c1e0c7eb423c0eee5be56f6ddf26"
"hash": "9a560dab87acbc8dc291fe31eb7ad72ef827504555b868e9d4f805cbe81169b0"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n user_.id AS userId,\n user_.name,\n user_.email,\n user_.password,\n user_.salt,\n user_.role AS \"role: Role\",\n function AS \"function: Functions\",\n user_.areaId,\n user_.locked,\n user_.lastLogin,\n user_.receiveNotifications,\n area.id,\n area.name AS areaName\n FROM user_\n JOIN area ON user_.areaId = area.id\n ",
"query": "\n SELECT\n user_.id AS userId,\n user_.name,\n user_.email,\n user_.password,\n user_.salt,\n user_.role AS \"role: Role\",\n function AS \"function: UserFunction\",\n user_.areaId,\n user_.locked,\n user_.lastLogin,\n user_.receiveNotifications,\n area.id,\n area.name AS areaName\n FROM user_\n JOIN area ON user_.areaId = area.id\n ",
"describe": {
"columns": [
{
@ -46,7 +46,7 @@
},
{
"ordinal": 6,
"name": "function: Functions",
"name": "function: UserFunction",
"type_info": {
"Custom": {
"name": "function[]",
@ -117,5 +117,5 @@
false
]
},
"hash": "cd25453504b28008dbe82aa89e8d942ad500b61beb640d2af9030872de069fb6"
"hash": "a7f6e57733c655534c3ae6379b8616fc3aa63ce322cc2d718f4b4e4e23903a61"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n availabillity.id,\n availabillity.userId,\n availabillity.date,\n availabillity.startTime,\n availabillity.endTime,\n availabillity.comment,\n user_.name,\n user_.email,\n user_.password,\n user_.salt,\n user_.role AS \"role: Role\",\n user_.function AS \"function: Functions\",\n user_.areaId,\n user_.locked,\n user_.lastLogin,\n user_.receiveNotifications\n FROM availabillity\n JOIN user_ ON availabillity.userId = user_.id\n WHERE availabillity.date = $1\n AND user_.areaId = $2\n AND ((availabillity.startTime IS NULL AND availabillity.endTime IS NULL)\n OR (availabillity.startTime <= $3 AND availabillity.endTime >= $4));\n ",
"query": "\n SELECT\n availabillity.id,\n availabillity.userId,\n availabillity.date,\n availabillity.startTime,\n availabillity.endTime,\n availabillity.comment,\n user_.name,\n user_.email,\n user_.password,\n user_.salt,\n user_.role AS \"role: Role\",\n user_.function AS \"function: UserFunction\",\n user_.areaId,\n user_.locked,\n user_.lastLogin,\n user_.receiveNotifications\n FROM availabillity\n JOIN user_ ON availabillity.userId = user_.id\n WHERE availabillity.date = $1\n AND user_.areaId = $2\n AND ((availabillity.startTime IS NULL AND availabillity.endTime IS NULL)\n OR (availabillity.startTime <= $3 AND availabillity.endTime >= $4));\n ",
"describe": {
"columns": [
{
@ -71,7 +71,7 @@
},
{
"ordinal": 11,
"name": "function: Functions",
"name": "function: UserFunction",
"type_info": {
"Custom": {
"name": "function[]",
@ -140,5 +140,5 @@
false
]
},
"hash": "efe07f8477c7416eae9c0b433cf24b7dc8abf38ce56c0ad46039a5bbbdd6865b"
"hash": "ddbe7fddaffda51262497eff521bee8113951c282eb4308377fe69ba42948274"
}

View File

@ -23,3 +23,7 @@
- all fields are as they are on the model, all field must be supplied; no way for "partial updates"
- partial updates for only one or two fields get a special method on the model
- validate using garde and custom context with gives access to database pool
TODO:
treat all selects with https://stackoverflow.com/questions/66728451/html-select-not-showing-selected-option-after-refresh-in-firefox

View File

@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use crate::{
models::{Area, Availability, AvailabilityTime, Functions, Role, User},
models::{Area, Availability, AvailabilityTime, UserFunction, Role, User},
utils::ApplicationError,
END_OF_DAY, START_OF_DAY,
};
@ -33,7 +33,7 @@ struct Export {
struct ExportAvailabillity {
name: String,
area: String,
function: Functions,
function: UserFunction,
date: NaiveDate,
whole_day: bool,
start_time: NaiveTime,

View File

@ -46,17 +46,17 @@ pub struct NewOrEditUserTemplate {
area_id: Option<i32>,
}
#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
#[cfg_attr(test, derive(serde::Serialize))]
struct NewOrEditUserForm {
email: String,
name: String,
role: u8,
#[serde(rename(deserialize = "is-posten"))]
#[serde(rename = "is-posten")]
is_posten: Option<bool>,
#[serde(rename(deserialize = "is-wachhabender"))]
#[serde(rename = "is-wachhabender")]
is_wachhabender: Option<bool>,
#[serde(rename(deserialize = "is-fuehrungsassistent"))]
#[serde(rename = "is-fuehrungsassistent")]
is_fuehrungsassistent: Option<bool>,
area: Option<i32>,
}

View File

@ -1,6 +1,6 @@
use maud::html;
use crate::models::Functions;
use crate::models::UserFunction;
pub fn show_area_query(a: &Option<i32>, first: bool) -> rinja::Result<String> {
let char = if first { '?' } else { '&' };
@ -40,7 +40,7 @@ pub fn invert(b: &bool) -> rinja::Result<bool> {
Ok(!b)
}
pub fn show_tree(f: &Functions) -> rinja::Result<String> {
pub fn show_tree(f: &UserFunction) -> rinja::Result<String> {
let html = html! {
div class="tags" {
@if f.is_posten() {

View File

@ -2,7 +2,7 @@ use chrono::NaiveTime;
use garde::Validate;
use super::{
start_time_lies_before_end_time, Assignment, Availability, AvailabilityTime, Event, Function, Functions,
start_time_lies_before_end_time, Assignment, Availability, AvailabilityTime, Event, Function, UserFunction,
};
#[derive(Validate)]
@ -26,7 +26,7 @@ pub struct AssignmentChangeset {
pub struct AssignmentContext {
pub event: Event,
pub availabillity: Availability,
pub user_function: Functions,
pub user_function: UserFunction,
pub assignments_for_event: Vec<Assignment>,
pub assignments_for_availabillity: Vec<Assignment>,
}

View File

@ -3,7 +3,7 @@ use sqlx::{query, PgPool};
use crate::{END_OF_DAY, START_OF_DAY};
use super::{Area, AvailabilityChangeset, Functions, Result, Role, User};
use super::{Area, AvailabilityChangeset, UserFunction, Result, Role, User};
#[derive(Clone, Debug)]
pub struct Availability {
@ -87,7 +87,7 @@ impl Availability {
user_.password,
user_.salt,
user_.role AS "role: Role",
user_.function AS "function: Functions",
user_.function AS "function: UserFunction",
user_.areaId,
user_.locked,
user_.lastLogin,
@ -154,7 +154,7 @@ impl Availability {
user_.password,
user_.salt,
user_.role AS "role: Role",
user_.function AS "function: Functions",
user_.function AS "function: UserFunction",
user_.areaId,
user_.locked,
user_.lastLogin,
@ -220,7 +220,7 @@ impl Availability {
user_.password,
user_.salt,
user_.role AS "role: Role",
user_.function AS "function: Functions",
user_.function AS "function: UserFunction",
user_.areaId,
user_.locked,
user_.lastLogin,
@ -302,7 +302,7 @@ impl Availability {
user_.password,
user_.salt,
user_.role AS "role: Role",
user_.function AS "function: Functions",
user_.function AS "function: UserFunction",
user_.areaId,
user_.locked,
user_.lastLogin,

View File

@ -1,9 +1,7 @@
use std::fmt::Display;
use serde::Serialize;
use sqlx::postgres::{PgHasArrayType, PgTypeInfo};
use crate::utils::ApplicationError;
use serde::Serialize;
#[derive(sqlx::Type, Debug, Clone, Copy, PartialEq, Eq, Serialize, PartialOrd, Ord)]
#[sqlx(type_name = "function", rename_all = "lowercase")]
@ -13,54 +11,6 @@ pub enum Function {
Wachhabender = 10,
}
#[derive(sqlx::Type, Debug, Clone, PartialEq, Eq, Serialize, PartialOrd, Ord)]
#[sqlx(no_pg_array)]
pub struct Functions(Vec<Function>);
//impl sqlx::Type<Postgres> for Functions {
// fn type_info() -> sqlx::postgres::PgTypeInfo {
// // Array type name is the name of the element type prefixed with `_`
// sqlx::postgres::PgTypeInfo::with_name("_functions")
// }
//}
impl PgHasArrayType for Functions {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::with_name("function[]")
}
}
impl Display for Functions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut iterator = self.0.iter().peekable();
while let Some(p) = iterator.next() {
write!(f, "{}", p.to_string());
if iterator.peek().is_some() {
write!(f, ", ");
}
}
todo!()
}
}
impl Functions {
pub fn contains(&self, f: &Function) -> bool {
self.0.contains(f)
}
pub fn is_posten(&self) -> bool {
self.0.contains(&Function::Posten)
}
pub fn is_fuehrungsassistent(&self) -> bool {
self.0.contains(&Function::Fuehrungsassistent)
}
pub fn is_wachhabender(&self) -> bool {
self.0.contains(&Function::Wachhabender)
}
}
impl Display for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {

View File

@ -13,6 +13,7 @@ mod registration;
mod role;
mod user;
mod user_changeset;
mod user_funtion;
mod vehicle;
mod vehicle_assignement;
@ -28,13 +29,14 @@ pub use availabillity_assignment_state::AvailabillityAssignmentState;
use chrono::NaiveTime;
pub use event::Event;
pub use event_changeset::{EventChangeset, EventContext};
pub use function::{Function, Functions};
pub use function::Function;
pub use location::Location;
pub use password_reset::{NoneToken, PasswordReset, Token};
pub use registration::Registration;
pub use role::Role;
pub use user::User;
pub use user_changeset::UserChangeset;
pub use user_funtion::UserFunction;
pub use vehicle::Vehicle;
pub use vehicle_assignement::VehicleAssignement;

View File

@ -1,7 +1,7 @@
use chrono::{DateTime, Utc};
use sqlx::PgPool;
use super::{function::Functions, Area, Function, Result, Role, UserChangeset};
use super::{Area, Function, Result, Role, UserChangeset, UserFunction};
#[derive(Clone, Debug)]
pub struct User {
@ -11,7 +11,7 @@ pub struct User {
pub password: Option<String>,
pub salt: Option<String>,
pub role: Role,
pub function: Functions,
pub function: UserFunction,
pub area_id: i32,
pub area: Option<Area>,
pub locked: bool,
@ -76,7 +76,7 @@ impl User {
password,
salt,
role AS "role: Role",
function AS "function: Functions",
function AS "function: UserFunction",
areaId,
locked,
lastLogin,
@ -116,7 +116,7 @@ impl User {
password,
salt,
role AS "role: Role",
function AS "function: Functions",
function AS "function: UserFunction",
areaId,
locked,
lastLogin,
@ -156,7 +156,7 @@ impl User {
password,
salt,
role AS "role: Role",
function AS "function: Functions",
function AS "function: UserFunction",
areaId,
locked,
lastLogin,
@ -198,7 +198,7 @@ impl User {
user_.password,
user_.salt,
user_.role AS "role: Role",
function AS "function: Functions",
function AS "function: UserFunction",
user_.areaId,
user_.locked,
user_.lastLogin,
@ -245,7 +245,7 @@ impl User {
password,
salt,
role AS "role: Role",
function AS "function: Functions",
function AS "function: UserFunction",
areaId,
locked,
lastLogin,

View File

@ -0,0 +1,47 @@
use std::fmt::Display;
use serde::Serialize;
use sqlx::postgres::{PgHasArrayType, PgTypeInfo};
use super::Function;
#[derive(sqlx::Type, Debug, Clone, PartialEq, Eq, Serialize, PartialOrd, Ord)]
#[sqlx(no_pg_array)]
pub struct UserFunction(Vec<Function>);
impl PgHasArrayType for UserFunction {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::with_name("function[]")
}
}
impl Display for UserFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut iterator = self.0.iter().peekable();
while let Some(p) = iterator.next() {
write!(f, "{}", p.to_string())?;
if iterator.peek().is_some() {
write!(f, ", ")?;
}
}
Ok(())
}
}
impl UserFunction {
pub fn contains(&self, f: &Function) -> bool {
self.0.contains(f)
}
pub fn is_posten(&self) -> bool {
self.0.contains(&Function::Posten)
}
pub fn is_fuehrungsassistent(&self) -> bool {
self.0.contains(&Function::Fuehrungsassistent)
}
pub fn is_wachhabender(&self) -> bool {
self.0.contains(&Function::Wachhabender)
}
}

View File

@ -67,17 +67,19 @@
</div>
<div class="dropdown-menu" id="dropdown-menu" role="menu">
<div class="dropdown-content" hx-target="closest table" hx-swap="outerHTML">
{% if u.function.is_posten() %}
<a class="dropdown-item"
hx-post="/assignments/new?event={{ event.id }}&availabillity={{ availabillity.id }}&function=1" {% if
!further_posten_required || status !=AvailabillityAssignmentState::Unassigned|ref %}disabled{% endif %}>
als Posten planen</a>
{% endif %}
{% if u.function.is_fuehrungsassistent() %}
<a class="dropdown-item"
hx-post="/assignments/new?event={{ event.id }}&availabillity={{ availabillity.id }}&function=5" {% if
!further_fuehrungsassistent_required || status !=AvailabillityAssignmentState::Unassigned|ref
%}disabled{% endif %}>als Führungsassistent planen</a>
{% endif %}
{% if u.function.is_fuehrungsassistent() %}
{% if u.function.is_wachhabender() %}
<a class="dropdown-item"
hx-post="/assignments/new?event={{ event.id }}&availabillity={{ availabillity.id }}&function=10" {% if
!further_wachhabender_required || status !=AvailabillityAssignmentState::Unassigned|ref %}disabled{%