diff --git a/app/Livewire/Admin/Roles.php b/app/Livewire/Admin/Roles.php index d9bb28d..fa84f93 100644 --- a/app/Livewire/Admin/Roles.php +++ b/app/Livewire/Admin/Roles.php @@ -162,6 +162,13 @@ class Roles extends Component ->groupBy(fn (string $name) => str_contains($name, '.') ? strtok($name, '.') : 'sonstiges') ->sortKeys(), 'permissions' => $this->allPermissions(), + // Der ganze `can`-Block als Array, nicht Eintrag für Eintrag über + // `__()`. Ein Berechtigungsname trägt selbst einen Punkt + // (`billing.manage`), und `__()` liest Punkte als Verschachtelung — + // der Aufruf suchte `roles → can → billing → manage`, während in + // der Sprachdatei ein FLACHER Schlüssel `'billing.manage'` steht. + // Die Seite zeigte deshalb den Schlüssel statt der Beschreibung. + 'permissionLabels' => (array) trans('roles.can'), // Wie viele Menschen an dieser Rolle hängen — damit niemand blind // etwas wegnimmt, das gerade sechs Leute benutzen. 'protectedRole' => 'Owner', diff --git a/resources/views/livewire/admin/open-work.blade.php b/resources/views/livewire/admin/open-work.blade.php index da77d24..1647964 100644 --- a/resources/views/livewire/admin/open-work.blade.php +++ b/resources/views/livewire/admin/open-work.blade.php @@ -33,8 +33,13 @@ das gerade Geld kostet? Der Ton kommt vom dringendsten Zustand, der wirklich vorkommt — nicht von einer festen Farbe. --}} @php([$worstIcon, $worstText] = $tone[$worst]) + {{-- `bg-danger-bg` und `border-danger-border`, nicht `bg-danger/5`: + dieses Haus hat für gedämpfte Töne eigene Tokens + (resources/css/portal-tokens.css). Die Deckkraft-Schreibweise ergab + hier gar keinen sichtbaren Hintergrund — der Kasten stand + durchsichtig da, nur mit Rahmen. --}}
+ {{ $worst === App\Support\OpenWork::SOLD ? 'border-danger-border bg-danger-bg' : 'border-line bg-surface' }}">

{{ trans_choice('open_work.summary', $total, ['n' => $total]) }} diff --git a/resources/views/livewire/admin/roles.blade.php b/resources/views/livewire/admin/roles.blade.php index 95edf75..b4b2d6a 100644 --- a/resources/views/livewire/admin/roles.blade.php +++ b/resources/views/livewire/admin/roles.blade.php @@ -85,7 +85,16 @@ @disabled($isProtected) class="mt-0.5 size-4 shrink-0 rounded border-line-strong accent-[var(--accent-active)] disabled:opacity-50"> - {{ __('roles.can.'.$permission) }} + {{-- Aus dem Array gelesen statt über `__('roles.can.'.$permission)`. + Ein Berechtigungsname trägt selbst einen Punkt + (`billing.manage`), und `__()` liest Punkte als + Verschachtelung — es suchte also `roles → can → + billing → manage`, während in der Sprachdatei ein + FLACHER Schlüssel `'billing.manage'` steht. Gefunden + wurde nie etwas, also stand auf der Seite der + Schlüssel selbst: „roles.can.billing.manage". + Die Übersetzungen waren die ganze Zeit da. --}} + {{ $permissionLabels[$permission] ?? $permission }} {{ $permission }} diff --git a/tests/Feature/Admin/RoleManagementTest.php b/tests/Feature/Admin/RoleManagementTest.php index 50134c1..0ca47bc 100644 --- a/tests/Feature/Admin/RoleManagementTest.php +++ b/tests/Feature/Admin/RoleManagementTest.php @@ -3,6 +3,7 @@ use App\Livewire\Admin\Roles; use App\Models\Operator; use Livewire\Livewire; +use Spatie\Permission\Models\Permission; use Spatie\Permission\Models\Role; /** @@ -146,3 +147,44 @@ it('groups the capabilities instead of listing twenty-one in a row', function () ->and($groups)->toHaveKey('provisioning') ->and(count($groups))->toBeLessThan(21); }); + +/** + * Die Rollenseite zeigte statt der Beschreibungen ihre eigenen Schlüssel: + * „roles.can.billing.manage" stand da, wo „Rechnungen, Gutschriften, + * Zahlungsprobleme" hingehört. + * + * Die Übersetzungen waren die ganze Zeit vorhanden. Der Aufruf lautete + * `__('roles.can.'.$permission)`, und ein Berechtigungsname trägt selbst einen + * Punkt — `__()` liest Punkte als Verschachtelung und suchte deshalb + * `roles → can → billing → manage`, während in der Sprachdatei ein FLACHER + * Schlüssel `'billing.manage'` steht. Gefunden wurde nie etwas. + */ +it('zeigt zu jeder Berechtigung ihre Beschreibung, nicht ihren Schluessel', function () { + foreach (['de', 'en'] as $locale) { + app()->setLocale($locale); + + $labels = (array) trans('roles.can'); + + // Aus der Datenbank, nicht aus einer Liste im Test: so kann eine neu + // angelegte Berechtigung nicht ohne Beschreibung durchrutschen. + $permissions = Permission::query() + ->where('guard_name', 'operator')->pluck('name'); + + foreach ($permissions as $permission) { + expect($labels[$permission] ?? null) + ->not->toBeNull("„{$permission}“ hat in lang/{$locale}/roles.php keine Beschreibung.") + // Und die Beschreibung ist keine Wiederholung des Schlüssels. + ->not->toBe($permission); + } + } +}); + +it('rendert die Beschreibungen wirklich auf der Seite', function () { + // Gegen das ausgelieferte HTML, nicht gegen die Sprachdatei: der Fehler lag + // nicht in den Texten, sondern darin, wie die Seite sie holte. + $html = $this->actingAs(admin(), 'operator')->get(route('admin.roles'))->assertOk()->getContent(); + + expect($html)->toContain(e(trans('roles.can')['billing.manage'])) + // Und kein roher Schlüssel mehr. + ->and($html)->not->toContain('roles.can.billing.manage'); +});