diff --git a/app/Livewire/Admin/Roles.php b/app/Livewire/Admin/Roles.php
index ad24f95..d9bb28d 100644
--- a/app/Livewire/Admin/Roles.php
+++ b/app/Livewire/Admin/Roles.php
@@ -154,6 +154,13 @@ class Roles extends Component
->withCount('users')
->orderBy('name')
->get(),
+ // Nach dem Präfix gruppiert — die Namen tragen ihre Ordnung längst
+ // in sich (`billing.`, `customers.`, `provisioning.`), und
+ // einundzwanzig Kästchen flach untereinander sind eine Wand, in
+ // der niemand das eine findet, das er sucht.
+ 'groups' => collect($this->allPermissions())
+ ->groupBy(fn (string $name) => str_contains($name, '.') ? strtok($name, '.') : 'sonstiges')
+ ->sortKeys(),
'permissions' => $this->allPermissions(),
// Wie viele Menschen an dieser Rolle hängen — damit niemand blind
// etwas wegnimmt, das gerade sechs Leute benutzen.
diff --git a/lang/de/roles.php b/lang/de/roles.php
index 78e33c5..268318f 100644
--- a/lang/de/roles.php
+++ b/lang/de/roles.php
@@ -16,6 +16,8 @@ return [
'in_use' => 'Die Rolle :role wird noch gehalten. Wer sie hat, stünde beim nächsten Anmelden ohne Rolle da und käme nicht mehr in die Konsole.',
'holders' => '{0} Niemand hat diese Rolle|{1} :count Person hat diese Rolle|[2,*] :count Personen haben diese Rolle',
+ 'granted_count' => ':granted von :total Rechten',
+
'can' => [
'billing.manage' => 'Rechnungen, Gutschriften, Zahlungsprobleme',
'console.view' => 'Die Konsole überhaupt betreten',
diff --git a/lang/en/roles.php b/lang/en/roles.php
index 9a01b1e..cba9a4e 100644
--- a/lang/en/roles.php
+++ b/lang/en/roles.php
@@ -16,6 +16,8 @@ return [
'in_use' => 'The role :role is still held. Whoever has it would be left with no role on their next sign-in and could not enter the console.',
'holders' => '{0} Nobody holds this role|{1} :count person holds this role|[2,*] :count people hold this role',
+ 'granted_count' => ':granted of :total capabilities',
+
'can' => [
'billing.manage' => 'Invoices, credit notes, payment problems',
'console.view' => 'Enter the console at all',
diff --git a/resources/views/livewire/admin/roles.blade.php b/resources/views/livewire/admin/roles.blade.php
index 4bcff90..95edf75 100644
--- a/resources/views/livewire/admin/roles.blade.php
+++ b/resources/views/livewire/admin/roles.blade.php
@@ -4,84 +4,113 @@
{{ __('roles.subtitle') }}
- {{-- Neue Rolle. Ein Feld, ein Knopf — die Rechte bekommt sie danach in
- ihrer eigenen Karte, weil eine leere Rolle noch nichts kaputtmachen
- kann und die Matrix im selben Formular unlesbar würde. --}}
-
-
-
-
-
- {{ __('roles.create') }}
-
-
+ {{-- Neue Rolle.
- @foreach ($roles as $role)
- @php $isProtected = $role->name === $protectedRole; @endphp
-
-
-
-
- {{ $role->name }}
+ Vorher stand das Feld auf `flex-1` neben einem Knopf ohne `shrink-0`,
+ und beide auf `items-end` — bei schmalem Fenster schob der Hinweistext
+ unter dem Feld den Knopf aus der Karte heraus. Jetzt ein Raster mit
+ zwei Spuren: das Feld nimmt, was übrig ist, der Knopf behält seine
+ Breite, und der Hinweis steht UNTER der Reihe statt in ihr. --}}
+
+
+ {{-- Eine Karte je Rolle, zugeklappt.
+
+ Sechs Rollen mal einundzwanzig Rechte waren über hundert Kästchen
+ untereinander — man scrollte an der Rolle vorbei, die man ändern
+ wollte. Zugeklappt ist die Seite sechs Zeilen lang, und die Zeile sagt
+ schon, was sie enthält: wie viele Rechte, wie viele Menschen. --}}
+
+ @foreach ($roles as $role)
+ @php
+ $isProtected = $role->name === $protectedRole;
+ // NICHT $granted nennen: das ist die Eigenschaft des
+ // Bauteils, und in der zweiten Runde der Schleife wäre sie
+ // eine Zahl statt der Liste.
+ $grantedCount = count($granted[$role->id] ?? []);
+ @endphp
+
+
+ {{-- Die Kopfzeile IST der Schalter. Ein eigener Pfeil-Knopf
+ daneben wäre ein zweites Ziel für dieselbe Absicht. --}}
+
- @endif
-
- {{-- Die Matrix. Auch für Owner sichtbar, nur nicht änderbar: was
- der Owner darf, ist die Antwort auf „was gibt es überhaupt",
- und die soll man nachlesen können. --}}
-
- @foreach ($permissions as $permission)
-
- @endforeach
-
-
- @endforeach
-
- @error('newRole')
{{ $message }}
@enderror
+ @endforeach
+
diff --git a/tests/Feature/Admin/RoleManagementTest.php b/tests/Feature/Admin/RoleManagementTest.php
index 35914ab..50134c1 100644
--- a/tests/Feature/Admin/RoleManagementTest.php
+++ b/tests/Feature/Admin/RoleManagementTest.php
@@ -121,3 +121,28 @@ it('is not reachable without staff.manage', function () {
->test(Roles::class)
->assertForbidden();
});
+
+it('keeps counting capabilities correctly past the first role', function () {
+ // Ein Fehler, den ich beim Umbau selbst gebaut habe: die Ansicht nannte
+ // die Zählvariable `$granted` — genauso wie die Eigenschaft des Bauteils.
+ // Ab der zweiten Rolle war sie eine Zahl statt der Liste, und die Seite
+ // starb an `count(int)`. Mit einer Rolle wäre es nie aufgefallen.
+ Role::findOrCreate('Buchhaltung', 'operator')->givePermissionTo('console.view');
+ Role::findOrCreate('Werkstatt', 'operator');
+
+ Livewire::actingAs(operator('Owner'), 'operator')
+ ->test(Roles::class)
+ ->assertOk()
+ ->assertSee('Buchhaltung')
+ ->assertSee('Werkstatt');
+});
+
+it('groups the capabilities instead of listing twenty-one in a row', function () {
+ $groups = Livewire::actingAs(operator('Owner'), 'operator')
+ ->test(Roles::class)
+ ->viewData('groups');
+
+ expect($groups)->toHaveKey('billing')
+ ->and($groups)->toHaveKey('provisioning')
+ ->and(count($groups))->toBeLessThan(21);
+});