From 4adda6e53bfb657804b0255cced2c4f002404ec0 Mon Sep 17 00:00:00 2001 From: Twyco Date: Wed, 9 Sep 2026 23:09:25 +0200 Subject: [PATCH] feat(KAN-19): add responsive permission-aware navigation --- app/Http/Middleware/HandleInertiaRequests.php | 31 +++ resources/js/components/AppLogo.vue | 21 +- resources/js/components/AppSidebar.vue | 176 +++++++------- resources/js/components/AppSidebarHeader.vue | 144 ++++++++++- resources/js/components/Breadcrumbs.vue | 31 ++- resources/js/components/NavMain.vue | 35 ++- resources/js/components/ProjectNavigation.vue | 89 +++++++ resources/js/components/UserInfo.vue | 35 +-- resources/js/components/UserMenuContent.vue | 6 +- .../js/components/ui/sidebar/Sidebar.vue | 186 +++++++------- .../components/ui/sidebar/SidebarContent.vue | 27 +- .../js/components/ui/sidebar/SidebarInset.vue | 30 +-- .../ui/sidebar/SidebarMenuButton.vue | 86 ++++--- .../components/ui/sidebar/SidebarProvider.vue | 136 +++++++---- .../components/ui/sidebar/SidebarTrigger.vue | 56 +++-- resources/js/components/ui/sidebar/utils.ts | 21 +- resources/js/composables/useAppNavigation.ts | 25 ++ resources/js/composables/useCurrentUrl.ts | 46 ++-- resources/js/layouts/app/AppSidebarLayout.vue | 19 +- resources/js/layouts/settings/Layout.vue | 44 ++-- resources/js/lib/navigation.ts | 166 +++++++++++++ resources/js/locales/en.ts | 39 +++ resources/js/types/global.d.ts | 2 + resources/js/types/navigation.ts | 13 + tests/Feature/NavigationTest.php | 107 ++++++++ tests/Fixtures/navigation-preview.mjs | 86 +++++++ tests/Fixtures/navigation-server.mjs | 88 +++++++ tests/Fixtures/navigation.mjs | 230 ++++++++++++++++++ 28 files changed, 1547 insertions(+), 428 deletions(-) create mode 100644 resources/js/components/ProjectNavigation.vue create mode 100644 resources/js/composables/useAppNavigation.ts create mode 100644 resources/js/lib/navigation.ts create mode 100644 tests/Feature/NavigationTest.php create mode 100644 tests/Fixtures/navigation-preview.mjs create mode 100644 tests/Fixtures/navigation-server.mjs create mode 100644 tests/Fixtures/navigation.mjs diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index d980988..4c3cb3e 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -4,6 +4,7 @@ namespace App\Http\Middleware; use App\Actions\Permissions\PermissionService; use App\Enums\Permission; +use App\Models\Project; use Illuminate\Http\Request; use Inertia\Middleware; @@ -60,6 +61,36 @@ class HandleInertiaRequests extends Middleware 'user' => $user, ], 'sidebarOpen' => ! $request->hasCookie('sidebar_state') || $request->cookie('sidebar_state') === 'true', + 'navigation' => fn (): array => [ + 'project' => $this->projectNavigation($request), + ], + ]; + } + + /** + * @return array{id: int, name: string, key: string, canManage: bool, canCreateTickets: bool}|null + */ + private function projectNavigation(Request $request): ?array + { + $project = $request->route('project'); + $user = $request->user(); + + if (! $project instanceof Project || $user === null) { + return null; + } + + $permissions = $this->permissionService->effectivePermissions($user, $project); + + if (! in_array(Permission::ProjectRead, $permissions, true)) { + return null; + } + + return [ + 'id' => $project->id, + 'name' => $project->name, + 'key' => $project->key, + 'canManage' => in_array(Permission::ProjectAdmin, $permissions, true), + 'canCreateTickets' => in_array(Permission::ProjectWrite, $permissions, true), ]; } } diff --git a/resources/js/components/AppLogo.vue b/resources/js/components/AppLogo.vue index 2de902d..1aa0df1 100644 --- a/resources/js/components/AppLogo.vue +++ b/resources/js/components/AppLogo.vue @@ -1,16 +1,17 @@ diff --git a/resources/js/components/AppSidebar.vue b/resources/js/components/AppSidebar.vue index e3e470a..c8942b8 100644 --- a/resources/js/components/AppSidebar.vue +++ b/resources/js/components/AppSidebar.vue @@ -1,19 +1,10 @@ diff --git a/resources/js/components/AppSidebarHeader.vue b/resources/js/components/AppSidebarHeader.vue index cfe6480..9120c68 100644 --- a/resources/js/components/AppSidebarHeader.vue +++ b/resources/js/components/AppSidebarHeader.vue @@ -1,27 +1,145 @@ diff --git a/resources/js/components/Breadcrumbs.vue b/resources/js/components/Breadcrumbs.vue index 0f96ff4..a22b444 100644 --- a/resources/js/components/Breadcrumbs.vue +++ b/resources/js/components/Breadcrumbs.vue @@ -9,29 +9,48 @@ import { BreadcrumbSeparator, } from '@/components/ui/breadcrumb'; import type { BreadcrumbItem as BreadcrumbItemType } from '@/types'; +import { useLocalization } from '@/composables/useLocalization'; type Props = { breadcrumbs: BreadcrumbItemType[]; }; defineProps(); +const { t } = useLocalization();