91 lines
4.2 KiB
Twig
91 lines
4.2 KiB
Twig
{#
|
|
Everything bookable, per type and room. Every write opens a modal via htmx and navigates back
|
|
here once saved -- the same shape as the sibling apps' admin lists.
|
|
|
|
Retired resources stay listed, muted, because this is the only place they can be reactivated.
|
|
Only an empty room offers "Löschen"; DeleteController refuses the rest regardless. A resource
|
|
can always be deleted, taking its bookings with it -- the modal says how many.
|
|
#}
|
|
{% extends 'layout.html.twig' %}
|
|
|
|
{% block title %}Ressourcen · ephub{% endblock %}
|
|
|
|
{% macro opener(url, label, variant = 'neutral') %}
|
|
<button type="button" class="button button--small button--{{ variant }}"
|
|
hx-get="{{ url }}"
|
|
hx-target="body"
|
|
hx-swap="beforeend">{{ label }}</button>
|
|
{% endmacro %}
|
|
|
|
{% macro resource_list(resources) %}
|
|
{% if resources is empty %}
|
|
<p class="py-3 text-sm text-gray-500">Noch nichts angelegt.</p>
|
|
{% else %}
|
|
<ul class="divide-y divide-gray-200 border-y border-gray-200">
|
|
{% for resource in resources %}
|
|
<li class="flex items-center justify-between gap-4 py-2" data-resource>
|
|
<span class="{{ resource.active ? '' : 'text-gray-400' }}">
|
|
{{ resource.label }}
|
|
{% if not resource.active %}
|
|
<span class="badge bg-gray-100 text-gray-600 ml-1">nicht buchbar</span>
|
|
{% endif %}
|
|
</span>
|
|
<div class="flex gap-2">
|
|
{{ _self.opener(path('app_admin_resource_edit', { id: resource.id }), 'Bearbeiten') }}
|
|
{{ _self.opener(path('app_admin_resource_delete', { id: resource.id }), 'Löschen') }}
|
|
</div>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% endif %}
|
|
{% endmacro %}
|
|
|
|
{% block content %}
|
|
<a class="text-sm text-gray-500 hover:text-gray-900" href="{{ path('app_dashboard') }}">← Übersicht</a>
|
|
|
|
<h1 class="pt-2">Ressourcen</h1>
|
|
<p class="subline">Was gebucht werden kann und wo es steht.</p>
|
|
|
|
{% for section in sections %}
|
|
{% set type = section.type %}
|
|
|
|
<section class="pt-8" data-type="{{ type.value }}">
|
|
<div class="flex flex-wrap items-baseline justify-between gap-2">
|
|
<h2>{{ type.plural }}</h2>
|
|
<div class="flex gap-2">
|
|
{{ _self.opener(path('app_admin_resource_group_new', { type: type.value }), type.groupLabel ~ ' hinzufügen') }}
|
|
{{ _self.opener(path('app_admin_resource_new', { type: type.value }), type.label ~ ' hinzufügen', 'primary') }}
|
|
</div>
|
|
</div>
|
|
|
|
{% for entry in section.groups %}
|
|
<div class="pt-4" data-group>
|
|
<div class="flex flex-wrap items-center justify-between gap-2 pb-1">
|
|
<h3 class="!mb-0 text-base md:text-base">
|
|
{{ entry.group.label }}
|
|
</h3>
|
|
<div class="flex gap-2">
|
|
{{ _self.opener(path('app_admin_resource_group_edit', { id: entry.group.id }), 'Bearbeiten') }}
|
|
{% if entry.resources is empty %}
|
|
{{ _self.opener(path('app_admin_resource_group_delete', { id: entry.group.id }), 'Löschen') }}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{{ _self.resource_list(entry.resources) }}
|
|
</div>
|
|
{% endfor %}
|
|
|
|
{# Only when there is something to show, or when there are no groups at all -- then this
|
|
is simply the list, as for the car park. #}
|
|
{% if section.ungrouped is not empty or section.groups is empty %}
|
|
<div class="pt-4" data-group>
|
|
{% if section.groups is not empty %}
|
|
<h3 class="!mb-0 pb-1 text-base md:text-base">Ohne {{ type.groupLabel }}</h3>
|
|
{% endif %}
|
|
{{ _self.resource_list(section.ungrouped) }}
|
|
</div>
|
|
{% endif %}
|
|
</section>
|
|
{% endfor %}
|
|
{% endblock %}
|