feat: add admin crud for resources and rooms
This commit is contained in:
1 parent
ad5d5acb00
commit
3d67942c62
19 files changed
+1059
-4
No files matched your search
@@ -0,0 +1,41 @@
|
||||
{#
|
||||
Creating and editing a desk or parking spot. Same double duty as the booking modal: the form
|
||||
targets the modal itself, so an invalid submit (422) replaces it with the errors showing, and a
|
||||
valid one answers with HX-Redirect back to the list.
|
||||
#}
|
||||
{% extends 'htmx_modal.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
{%- if resource.id -%}
|
||||
{{ resource.label }} bearbeiten
|
||||
{%- else -%}
|
||||
{{ resource.type.label }} hinzufügen
|
||||
{%- endif -%}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ form_start(form, { attr: {
|
||||
'hx-post': form.vars.action,
|
||||
'hx-target': '#htmx-modal',
|
||||
'hx-swap': 'outerHTML',
|
||||
} }) }}
|
||||
{{ form_errors(form) }}
|
||||
|
||||
<div class="space-y-4">
|
||||
{{ form_row(form.label) }}
|
||||
{{ form_row(form.group) }}
|
||||
{% if form.active is defined %}
|
||||
{{ form_row(form.active) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-2 pt-6">
|
||||
<button type="button" class="button button--neutral" {{ stimulus_action('modal', 'close') }}>
|
||||
Abbrechen
|
||||
</button>
|
||||
<button type="submit" class="button button--primary">
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,34 @@
|
||||
{# Creating and editing a room or area. See admin/_modal_resource.html.twig for how it closes. #}
|
||||
{% extends 'htmx_modal.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
{%- if group.id -%}
|
||||
{{ group.label }} bearbeiten
|
||||
{%- else -%}
|
||||
{{ group.type.groupLabel }} hinzufügen
|
||||
{%- endif -%}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ form_start(form, { attr: {
|
||||
'hx-post': form.vars.action,
|
||||
'hx-target': '#htmx-modal',
|
||||
'hx-swap': 'outerHTML',
|
||||
} }) }}
|
||||
{{ form_errors(form) }}
|
||||
|
||||
<div class="space-y-4">
|
||||
{{ form_row(form.label) }}
|
||||
{{ form_row(form.position) }}
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-2 pt-6">
|
||||
<button type="button" class="button button--neutral" {{ stimulus_action('modal', 'close') }}>
|
||||
Abbrechen
|
||||
</button>
|
||||
<button type="submit" class="button button--primary">
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends 'htmx_confirmation_modal.html.twig' %}
|
||||
|
||||
{% block title %}{{ group.type.groupLabel }} löschen?{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>
|
||||
{{ group.type.groupLabel }} <em>{{ group.label }}</em> wird gelöscht.
|
||||
</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block button_confirm %}Löschen{% endblock %}
|
||||
@@ -0,0 +1,86 @@
|
||||
{#
|
||||
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.
|
||||
#}
|
||||
{% extends 'layout.html.twig' %}
|
||||
|
||||
{% block title %}Plätze & Räume · 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>
|
||||
{{ _self.opener(path('app_admin_resource_edit', { id: resource.id }), 'Bearbeiten') }}
|
||||
</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">Plätze & Räume</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 %}
|
||||
Reference in new issue
Block a user