refactor: foreign availabilities

This commit is contained in:
Max Hohlfeld 2025-08-17 22:43:23 +02:00
parent d88fe2cd3a
commit c2bc0218f4
8 changed files with 161 additions and 71 deletions

View File

@ -3,11 +3,44 @@ source: web/src/endpoints/availability/put_cross_areal.rs
expression: disable_body
snapshot_kind: text
---
<button class="button is-link is-light" hx-swap="outerHTML"
hx-put="/availability/1/makeCrossAreal"
title="Bereichsübergreifend verfügbar machen">
<svg class="icon">
<use
href="/static/feather-sprite.svg#globe" />
</svg>
</button>
<tr>
<td>Max Mustermann</td>
<td>
<div class="tags"><span class="tag is-primary is-light">Posten</span></div>
</td>
<td>
10:00 Uhr bis
01.02.2025 10:00 Uhr
</td>
<td></td>
<td>nein</td>
<td>
<div class="buttons is-right">
<a class="button is-primary is-light" href="/availability/edit/1"
title="Verfügbarkeit bearbeiten">
<svg class="icon">
<use href="/static/feather-sprite.svg#edit" />
</svg>
</a>
<button class="button is-danger is-light" hx-delete="/availability/delete/1"
hx-target="closest tr" hx-swap="delete" hx-trigger="confirmed"
title="Verfügbarkeit löschen">
<svg class="icon">
<use href="/static/feather-sprite.svg#x-circle" />
</svg>
</button>
<button class="button is-link is-light" hx-swap="outerHTML" hx-target="closest tr"
hx-put="/availability/1/makeCrossAreal"
title="Bereichsübergreifend verfügbar machen">
<svg class="icon">
<use
href="/static/feather-sprite.svg#globe" />
</svg>
</button>
</div>
</td>
</tr>

View File

@ -3,11 +3,44 @@ source: web/src/endpoints/availability/put_cross_areal.rs
expression: enable_body
snapshot_kind: text
---
<button class="button is-link is-light" hx-swap="outerHTML"
hx-put="/availability/1/makeNonCrossAreal"
title="nur im Hauptbereich verfügbar machen">
<svg class="icon">
<use
href="/static/feather-sprite.svg#home" />
</svg>
</button>
<tr>
<td>Max Mustermann</td>
<td>
<div class="tags"><span class="tag is-primary is-light">Posten</span></div>
</td>
<td>
10:00 Uhr bis
01.02.2025 10:00 Uhr
</td>
<td></td>
<td>ja</td>
<td>
<div class="buttons is-right">
<a class="button is-primary is-light" href="/availability/edit/1"
title="Verfügbarkeit bearbeiten">
<svg class="icon">
<use href="/static/feather-sprite.svg#edit" />
</svg>
</a>
<button class="button is-danger is-light" hx-delete="/availability/delete/1"
hx-target="closest tr" hx-swap="delete" hx-trigger="confirmed"
title="Verfügbarkeit löschen">
<svg class="icon">
<use href="/static/feather-sprite.svg#x-circle" />
</svg>
</button>
<button class="button is-link is-light" hx-swap="outerHTML" hx-target="closest tr"
hx-put="/availability/1/makeNonCrossAreal"
title="nur im Hauptbereich verfügbar machen">
<svg class="icon">
<use
href="/static/feather-sprite.svg#home" />
</svg>
</button>
</div>
</td>
</tr>

View File

@ -1,18 +1,26 @@
use actix_web::{web, HttpResponse, Responder};
use askama::Template;
use askama::{filters::urlencode, Template};
use serde_json::json;
use sqlx::PgPool;
use crate::{
endpoints::IdPath,
utils::{ApplicationError, TemplateResponse},
filters,
utils::{
ApplicationError,
DateTimeFormat::{DayMonthYearHourMinute, HourMinute},
TemplateResponse,
},
};
use brass_db::models::{Assignment, Availability, Role, User};
#[derive(Template)]
#[template(path = "calendar_cross_areal_button.html")]
struct CalendarPartialCrossArealButtonTemplate {
#[template(path = "calendar_tr_availability.html")]
struct CalendarAvailabilityTableRowTemplate {
user: User,
availability: Availability,
u: User,
}
#[actix_web::put("/availability/{id}/makeNonCrossAreal")]
@ -60,7 +68,7 @@ async fn handle_cross_areal(
let trigger = json!({
"showToast": {
"type": "danger",
"message": "Verfügbarkeit bereits verplant!"
"message": urlencode("Verfügbarkeit bereits verplant!").unwrap().to_string()
}
})
.to_string();
@ -75,7 +83,13 @@ async fn handle_cross_areal(
availability.cross_areal = cross_areal;
}
let template = CalendarPartialCrossArealButtonTemplate { availability };
let u = availability.user.as_ref().unwrap().clone();
let template = CalendarAvailabilityTableRowTemplate {
availability,
user: user.into_inner(),
u,
};
Ok(template.to_response()?)
}
@ -102,7 +116,9 @@ mod tests {
RequestConfig::new("/availability/1/makeCrossAreal").with_role(Role::AreaManager);
create_test_login_user(&context.db_pool, &config).await;
User::create(&context.db_pool, &Faker.fake()).await.unwrap();
let mut changeset: UserChangeset = Faker.fake();
changeset.name = "Max Mustermann".to_string();
User::create(&context.db_pool, &changeset).await.unwrap();
Availability::create(
&context.db_pool,
2,

View File

@ -60,7 +60,7 @@ setThemeSwitcherIconTo(isCurrentlyLight ? "moon" : "sun");
htmx.on("showToast", (e) => {
const toast = document.getElementById("toast");
const toastProgress = document.getElementById("toast-progress");
document.getElementById("toast-message").innerText = e.detail.message;
document.getElementById("toast-message").innerText = decodeURI(e.detail.message);
toast.classList.add(`is-${e.detail.type}`);
toastProgress.classList.add(`has-background-${e.detail.type}-90`)

View File

@ -13,6 +13,8 @@
<input type="hidden" name="enddate" value="{{ enddate.as_ref().unwrap_or(date) }}" id="enddate">
{% if other_users.len() != 0 %}
<input type="hidden" name="user" id="otheruser" value="{{ other_user.unwrap_or_default() }}">
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Nutzer</label>
@ -21,12 +23,13 @@
<div class="field is-narrow">
<div class="control">
<div class="select is-fullwidth">
<select name="user" required {{ id.is_some()|ref|cond_show("disabled") }}>
<select required {{ id.is_some()|ref|cond_show("disabled") }}>
{% for u in other_users %}
<option value="{{ u.id }}" hx-get="/availability/new-other?date={{ date }}&user={{ u.id }}"
<option hx-get="/availability/new-other?date={{ date }}&user={{ u.id }}"
{{ (other_user.is_some() && *other_user.as_ref().unwrap()==u.id)|cond_show("selected") }}>
{{ u.name }}
{% if user.role == Role::Admin && u.area.is_some() %} - ({{ u.area.as_ref().unwrap().name }}){% endif %}
{% if user.role == Role::Admin && u.area.is_some() %} - ({{ u.area.as_ref().unwrap().name }}){%
endif %}
</option>
{% endfor %}
</select>

View File

@ -305,48 +305,16 @@
<th>Funktion</th>
<th>Zeitraum</th>
<th>Kommentar</th>
{% if user.role == Role::AreaManager || user.role == Role::Admin %}
<th>bereichsübergreifend</th>
{% endif %}
<th></th>
</tr>
</thead>
<tbody>
{% for availability in availabilities %}
{% let u = availability.user.as_ref().unwrap() %}
<tr>
<td>{{ u.name }}</td>
<td>
{{ u.function|show_tree|safe }}
</td>
<td>
{{ availability.start|fmt_datetime(HourMinute) }} Uhr bis
{{ availability.end|fmt_datetime(DayMonthYearHourMinute) }} Uhr
</td>
<td>
{{ availability.comment.as_deref().unwrap_or_default() }}
</td>
<td>
{% if availability.user_id == user.id || user.role == Role::Admin || user.role == Role::AreaManager %}
<div class="buttons is-right">
<a class="button is-primary is-light" href="/availability/edit/{{ availability.id }}"
title="Verfügbarkeit bearbeiten">
<svg class="icon">
<use href="/static/feather-sprite.svg#edit" />
</svg>
</a>
<button class="button is-danger is-light" hx-delete="/availability/delete/{{ availability.id }}"
hx-target="closest tr" hx-swap="delete" hx-trigger="confirmed"
title="Verfügbarkeit löschen">
<svg class="icon">
<use href="/static/feather-sprite.svg#x-circle" />
</svg>
</button>
{% if user.role == Role::Admin || (user.role == Role::AreaManager && user.area_id ==
availability.user.as_ref().unwrap().area_id) %}
{% include "calendar_cross_areal_button.html" %}
{% endif %}
</div>
{% endif %}
</td>
</tr>
{% include "calendar_tr_availability.html" %}
{% endfor %}
</tbody>
</table>

View File

@ -1,8 +0,0 @@
<button class="button is-link is-light" hx-swap="outerHTML"
hx-put="/availability/{{ availability.id }}/make{% if availability.cross_areal %}Non{% endif %}CrossAreal"
title="{% if availability.cross_areal %}nur im Hauptbereich{% else %}Bereichsübergreifend{% endif %} verfügbar machen">
<svg class="icon">
<use
href="/static/feather-sprite.svg#{% if availability.cross_areal %}home{% else %}globe{% endif %}" />
</svg>
</button>

View File

@ -0,0 +1,45 @@
<tr>
<td>{{ u.name }}</td>
<td>
{{ u.function|show_tree|safe }}
</td>
<td>
{{ availability.start|fmt_datetime(HourMinute) }} Uhr bis
{{ availability.end|fmt_datetime(DayMonthYearHourMinute) }} Uhr
</td>
<td>
{{- availability.comment.as_deref().unwrap_or_default() -}}
</td>
{%- if user.role == Role::AreaManager || user.role == Role::Admin %}
<td>{% if availability.cross_areal %}ja{% else %}nein{% endif %}</td>
{% endif -%}
<td>
{% if availability.user_id == user.id || user.role == Role::Admin || user.role == Role::AreaManager %}
<div class="buttons is-right">
<a class="button is-primary is-light" href="/availability/edit/{{ availability.id }}"
title="Verfügbarkeit bearbeiten">
<svg class="icon">
<use href="/static/feather-sprite.svg#edit" />
</svg>
</a>
<button class="button is-danger is-light" hx-delete="/availability/delete/{{ availability.id }}"
hx-target="closest tr" hx-swap="delete" hx-trigger="confirmed"
title="Verfügbarkeit löschen">
<svg class="icon">
<use href="/static/feather-sprite.svg#x-circle" />
</svg>
</button>
{% if user.role == Role::Admin || (user.role == Role::AreaManager && user.area_id == u.area_id) %}
<button class="button is-link is-light" hx-swap="outerHTML" hx-target="closest tr"
hx-put="/availability/{{ availability.id }}/make{% if availability.cross_areal %}Non{% endif %}CrossAreal"
title="{% if availability.cross_areal %}nur im Hauptbereich{% else %}Bereichsübergreifend{% endif %} verfügbar machen">
<svg class="icon">
<use
href="/static/feather-sprite.svg#{% if availability.cross_areal %}home{% else %}globe{% endif %}" />
</svg>
</button>
{% endif %}
</div>
{% endif %}
</td>
</tr>