feat: bpn as single source of truth for role and hotel code assignments

This commit is contained in:
Björn Fromme
2026-08-18 11:33:42 +02:00
parent 1fd0fbc21e
commit f0e850978b
17 changed files with 818 additions and 198 deletions
+4 -3
View File
@@ -1,8 +1,9 @@
{{ form_start(form) }}
<div class="flex flex-col space-y-4 pb-8">
{{ form_row(form.roles) }}
{{ form_row(form.superAdmin) }}
{{ form_row(form.hotelCodes) }}
{# only present for somebody who already holds ROLE_ADMIN, see UserType #}
{% if form.superAdmin is defined %}
{{ form_row(form.superAdmin) }}
{% endif %}
{{ form_row(form.disabled) }}
{{ form_row(form.disabledReason) }}
{{ form_row(form.disabledReasonInternal) }}
@@ -7,6 +7,85 @@
<h1 class="text-2xl font-bold pb-8">
{{ user.fullName }} bearbeiten
</h1>
<div class="pb-10">
<h2 class="text-lg font-bold pb-1">
Aus BusPro
</h2>
<p class="text-sm text-gray-500 pb-2">
Wird bei jeder Anmeldung übernommen und kann hier nicht geändert werden.
</p>
<dl class="divide-y divide-gray-100 border-t border-gray-100">
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4">
<dt class="text-sm font-bold leading-6 text-gray-900">
Zugewiesene Rollen
</dt>
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
{{ grantedRoles|join(', ')|default('keine Rolle') }}
{% if user.superAdmin %}
<div class="text-gray-500">
Superadmin
</div>
{% endif %}
</dd>
</div>
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4">
<dt class="text-sm font-bold leading-6 text-gray-900">
Zugewiesene Häuser
</dt>
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
{% for code, name in houses %}
<div>
{{ name }} <span class="text-gray-500">({{ code }})</span>
</div>
{% else %}
-
{% endfor %}
</dd>
</div>
<div class="py-4 sm:grid sm:grid-cols-3 sm:gap-4">
<dt class="text-sm font-bold leading-6 text-gray-900">
Letzter Login
</dt>
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
{{ user.lastLoginAt ? user.lastLoginAt|date('d.m.Y, H:i') : '-' }}
</dd>
</div>
</dl>
</div>
{# Its own act, with its own confirmation: granting a privilege must not happen as a
side effect of saving something else on this page. #}
{% if user.nominatedRoles is not empty %}
<div class="pb-10">
<h2 class="text-lg font-bold pb-1">
Freischaltung
</h2>
<p class="text-sm text-gray-500 pb-2">
In BusPro beansprucht, aber noch nicht freigeschaltet.
</p>
<ul class="divide-y divide-gray-100 border-t border-gray-100">
{% for role, label in user.nominatedRoles %}
<li class="py-3 flex items-center justify-between">
<span class="text-sm">
{{ label }}
</span>
<button type="button"
class="btn btn--secondary"
hx-get="{{ path('app_admin_system_user_approve_role', { 'id': user.id, 'role': role }) }}"
hx-target="body"
hx-swap="beforeend">
Freischalten
</button>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
<h2 class="text-lg font-bold pb-2">
Account
</h2>
{% include 'admin/system/user/_form.html.twig' %}
</div>
{% endblock %}
+8 -2
View File
@@ -48,7 +48,13 @@
{{ user.email }}
</td>
<td>
{{ user.rolesLabels|join(', ') }}
{% set grantedRoles = user.assignedRoles|map(role => constant('App\\Entity\\User::ROLES')[role]) %}
{{ grantedRoles is empty ? '-' : grantedRoles|join(', ') }}
{% for label in user.nominatedRoles %}
<span class="inline-block mt-1 py-1 px-2 text-xs bg-amber-500 text-gray-700 rounded-md">
{{ label }} angefordert
</span>
{% endfor %}
</td>
<td class="text-center">
{% if user.superAdmin %}
@@ -91,4 +97,4 @@
</table>
</div>
</div>
{% endblock %}
{% endblock %}
@@ -0,0 +1,12 @@
{% extends 'htmx_confirmation_modal.html.twig' %}
{% block content %}
<div class="pb-4">
Möchtest du die Rolle <em>{{ roleLabel }}</em> für <em>{{ user.fullName }}</em> wirklich freischalten?
</div>
<div>
Die Rolle wird damit sofort wirksam. Sie wird automatisch wieder entzogen, sobald sie in BusPro nicht mehr beansprucht wird.
</div>
{% endblock %}
{% block button_confirm %}Freischalten{% endblock %}