Zwei Attrappen: ein Banner ohne Hintergrund und Rechte, die ihren Schluessel statt ihrer Beschreibung zeigten

main
nexxo 2026-08-02 16:47:15 +02:00
parent 462c84ee23
commit d8aee6ec2f
4 changed files with 65 additions and 2 deletions

View File

@ -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',

View File

@ -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. --}}
<div class="flex items-center gap-3 rounded-lg border p-5 shadow-xs animate-rise
{{ $worst === App\Support\OpenWork::SOLD ? 'border-danger/30 bg-danger/5' : 'border-line bg-surface' }}">
{{ $worst === App\Support\OpenWork::SOLD ? 'border-danger-border bg-danger-bg' : 'border-line bg-surface' }}">
<x-ui.icon :name="$worstIcon" class="{{ $worstText }}" />
<p class="text-lg font-semibold {{ $worstText }}">
{{ trans_choice('open_work.summary', $total, ['n' => $total]) }}

View File

@ -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">
<span class="min-w-0">
<span class="block">{{ __('roles.can.'.$permission) }}</span>
{{-- 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. --}}
<span class="block">{{ $permissionLabels[$permission] ?? $permission }}</span>
<span class="block font-mono text-[11px] text-faint">{{ $permission }}</span>
</span>
</label>

View File

@ -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');
});