From 21d8dc310a18c573d982b262c6ad8c1b87006514 Mon Sep 17 00:00:00 2001 From: nexxo Date: Mon, 27 Jul 2026 16:09:39 +0200 Subject: [PATCH] One definition of the navigation, and the country instead of the building MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The breadcrumb said Übersicht on every page. The sidebar and the breadcrumb each built their own copy of the structure, so the layout had no way to know which entry was current and fell back to the first one. Navigation::portal() and ::console() are now the single definition, and currentLabel() matches on the route name — which matters for the console, whose PATH changes between host-bound and fallback mode while its route names do not. The datacentre name is out of everything a customer sees, for the third time and now at the source rather than in a template. Falkenstein is how an operator places an instance; a customer's processing record names the jurisdiction, and putting the building in customer copy means editing that copy every time the estate grows. Also swept the views onto the new scale: --text-faint is 2.8:1 and a decoration token, but it was carrying table headers, hints and timestamps — text people have to read. All of it moved to --muted, which is 5.0:1 and passes AA. Co-Authored-By: Claude Opus 5 --- app/Livewire/Dashboard.php | 10 ++- app/Support/Navigation.php | 87 +++++++++++++++++++ lang/de/cloud.php | 2 +- lang/en/cloud.php | 2 +- resources/views/layouts/admin.blade.php | 23 +---- resources/views/layouts/portal-app.blade.php | 18 +--- .../admin/confirm-delete-datacenter.blade.php | 2 +- .../admin/confirm-remove-host.blade.php | 2 +- .../views/livewire/admin/customers.blade.php | 8 +- .../livewire/admin/datacenters.blade.php | 10 +-- .../livewire/admin/edit-datacenter.blade.php | 2 +- .../livewire/admin/host-create.blade.php | 4 +- .../livewire/admin/host-detail.blade.php | 26 +++--- .../views/livewire/admin/hosts.blade.php | 12 +-- .../admin/instance-admin-access.blade.php | 2 +- .../views/livewire/admin/instances.blade.php | 8 +- .../livewire/admin/maintenance.blade.php | 28 +++--- .../views/livewire/admin/overview.blade.php | 22 ++--- .../livewire/admin/plan-versions.blade.php | 20 ++--- .../views/livewire/admin/plans.blade.php | 14 +-- .../livewire/admin/provisioning.blade.php | 28 +++--- .../views/livewire/admin/revenue.blade.php | 18 ++-- .../views/livewire/admin/secrets.blade.php | 8 +- .../views/livewire/admin/settings.blade.php | 38 ++++---- resources/views/livewire/admin/vpn.blade.php | 26 +++--- resources/views/livewire/backups.blade.php | 8 +- resources/views/livewire/billing.blade.php | 32 +++---- resources/views/livewire/cloud.blade.php | 8 +- .../livewire/confirm-cancel-package.blade.php | 2 +- .../livewire/confirm-close-account.blade.php | 2 +- .../livewire/customer-provisioning.blade.php | 2 +- resources/views/livewire/invoices.blade.php | 12 +-- resources/views/livewire/settings.blade.php | 18 ++-- resources/views/livewire/support.blade.php | 10 +-- resources/views/livewire/users.blade.php | 10 +-- 35 files changed, 291 insertions(+), 233 deletions(-) create mode 100644 app/Support/Navigation.php diff --git a/app/Livewire/Dashboard.php b/app/Livewire/Dashboard.php index 1491d80..40b8a2f 100644 --- a/app/Livewire/Dashboard.php +++ b/app/Livewire/Dashboard.php @@ -86,7 +86,7 @@ class Dashboard extends Component * has to be the place their instance really runs on, not the place we * usually sell. * - * @return array{name: string, note: ?string}|null + * @return array{name: string, note: null}|null */ private function location(?Instance $instance): ?array { @@ -98,9 +98,13 @@ class Dashboard extends Component $datacenter = Datacenter::query()->where('code', $code)->first(); + // The country, not the site. "Falkenstein" is how an operator places an + // instance; a customer's processing record says which jurisdiction the + // data sits in, and naming the building means editing customer-facing + // copy every time the estate grows. return [ - 'name' => $datacenter?->name ?: $code, - 'note' => $datacenter?->location, + 'name' => $datacenter?->location ?: $code, + 'note' => null, ]; } diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php new file mode 100644 index 0000000..7b5ea8f --- /dev/null +++ b/app/Support/Navigation.php @@ -0,0 +1,87 @@ +}> */ + public static function portal(): array + { + return [ + ['label' => null, 'items' => [ + ['dashboard', 'gauge', 'overview', null], + ['cloud', 'cloud', 'cloud', null], + ['users', 'users', 'users', null], + ['backups', 'database', 'backups', null], + ]], + ['label' => __('dashboard.nav_group.contract'), 'items' => [ + ['billing', 'box', 'billing', null], + ['invoices', 'receipt', 'invoices', null], + ['settings', 'settings', 'settings', null], + ['support', 'life-buoy', 'support', null], + ]], + ]; + } + + /** @return array}> */ + public static function console(): array + { + return [ + ['label' => null, 'items' => [ + ['admin.overview', 'gauge', 'overview', null], + ['admin.customers', 'users', 'customers', null], + ['admin.instances', 'box', 'instances', null], + ['admin.hosts', 'server', 'hosts', null], + ['admin.datacenters', 'database', 'datacenters', null], + ['admin.plans', 'tag', 'plans', 'plans.manage'], + ]], + ['label' => __('admin.nav_group.operations'), 'items' => [ + ['admin.provisioning', 'activity', 'provisioning', null], + ['admin.maintenance', 'alert-triangle', 'maintenance', null], + ['admin.vpn', 'shield', 'vpn', null], + ['admin.revenue', 'trending-up', 'revenue', null], + ]], + ['label' => __('admin.nav_group.system'), 'items' => [ + ['admin.secrets', 'lock', 'secrets', 'secrets.manage'], + ['admin.settings', 'settings', 'settings', null], + ]], + ]; + } + + /** + * The label of the entry the current request belongs to. + * + * Matched on the route rather than the path, because the console's path + * changes between host-bound and fallback mode while its route names do + * not. Null when the page is not in the navigation at all — a detail view, + * a modal — and the caller then supplies its own title. + */ + public static function currentLabel(bool $console = false): ?string + { + $prefix = $console ? 'admin.nav.' : 'dashboard.nav.'; + + foreach ($console ? self::console() : self::portal() as $group) { + foreach ($group['items'] as [$route, , $key]) { + $matches = $console + ? AdminArea::routeIs($route) + : request()->routeIs($route); + + if ($matches) { + return __($prefix.$key); + } + } + } + + return null; + } +} diff --git a/lang/de/cloud.php b/lang/de/cloud.php index b7ace03..1652766 100644 --- a/lang/de/cloud.php +++ b/lang/de/cloud.php @@ -4,7 +4,7 @@ return [ 'title' => 'Meine Cloud', 'subtitle' => 'Details und Ressourcen Ihrer Instanz.', 'plan_line' => ':plan · :price €/Monat', - 'datacenter' => 'EU · Rechenzentrum Falkenstein', + 'datacenter' => 'EU — Serverstandort im Angebot festgehalten', 'now' => 'jetzt', 'instance_label' => 'Instanz', 'open' => 'Cloud öffnen', diff --git a/lang/en/cloud.php b/lang/en/cloud.php index 80f832b..d64127b 100644 --- a/lang/en/cloud.php +++ b/lang/en/cloud.php @@ -4,7 +4,7 @@ return [ 'title' => 'My cloud', 'subtitle' => 'Details and resources of your instance.', 'plan_line' => ':plan · €:price/mo', - 'datacenter' => 'EU · Falkenstein data center', + 'datacenter' => 'EU — server location fixed in your offer', 'now' => 'now', 'instance_label' => 'Instance', 'open' => 'Open cloud', diff --git a/resources/views/layouts/admin.blade.php b/resources/views/layouts/admin.blade.php index 14cc0b3..0741e60 100644 --- a/resources/views/layouts/admin.blade.php +++ b/resources/views/layouts/admin.blade.php @@ -18,30 +18,11 @@ prefix="admin.nav" console :footer="$release->label()" - :groups="[ - ['label' => null, 'items' => [ - ['admin.overview', 'gauge', 'overview', null], - ['admin.customers', 'users', 'customers', null], - ['admin.instances', 'box', 'instances', null], - ['admin.hosts', 'server', 'hosts', null], - ['admin.datacenters', 'database', 'datacenters', null], - ['admin.plans', 'tag', 'plans', 'plans.manage'], - ]], - ['label' => __('admin.nav_group.operations'), 'items' => [ - ['admin.provisioning', 'activity', 'provisioning', null], - ['admin.maintenance', 'alert-triangle', 'maintenance', null], - ['admin.vpn', 'shield', 'vpn', null], - ['admin.revenue', 'trending-up', 'revenue', null], - ]], - ['label' => __('admin.nav_group.system'), 'items' => [ - ['admin.secrets', 'lock', 'secrets', 'secrets.manage'], - ['admin.settings', 'settings', 'settings', null], - ]], - ]" + :groups="\App\Support\Navigation::console()" />
- + @auth