feat: overhaul project workspace navigation
linter / quality (pull_request) Successful in 1m28s
tests / ci (8.4) (pull_request) Successful in 1m47s

This commit is contained in:
2026-07-18 17:52:25 +02:00
parent a46695c2eb
commit ff796aa10a
45 changed files with 1790 additions and 995 deletions
-82
View File
@@ -2,17 +2,12 @@
import { Link, usePage } from '@inertiajs/vue3';
import {
ArrowLeft,
BellRing,
BookOpen,
CirclePlus,
Folder,
FolderGit2,
KanbanSquare,
LayoutGrid,
Layers3,
ListTodo,
SlidersHorizontal,
Tags,
Shield,
Users,
} from 'lucide-vue-next';
@@ -36,17 +31,7 @@ import { index as projectTemplatesIndex } from '@/routes/project-templates';
import {
create as projectsCreate,
index as projectsIndex,
show as projectShow,
} from '@/routes/projects';
import { index as projectBacklog } from '@/routes/projects/backlog';
import { index as projectBoard } from '@/routes/projects/board';
import {
board as projectSettingsBoard,
general as projectSettingsGeneral,
metadata as projectSettingsMetadata,
notifications as projectSettingsNotifications,
repositories as projectSettingsRepositories,
} from '@/routes/projects/settings';
import { index as teamsIndex } from '@/routes/teams';
import type { AppSidebarProps, NavItem } from '@/types';
@@ -54,67 +39,6 @@ const props = defineProps<AppSidebarProps>();
const page = usePage();
const isProjectSettings = computed(() => page.url.includes('/settings/'));
const projectNavItems = computed<NavItem[]>(() => {
if (!props.project) {
return [];
}
return [
{
title: 'Overview',
href: projectShow(props.project.id),
icon: LayoutGrid,
isActive: page.url === projectShow.url(props.project.id),
},
{
title: 'Board',
href: projectBoard(props.project.id),
icon: KanbanSquare,
},
{
title: 'Backlog',
href: projectBacklog(props.project.id),
icon: ListTodo,
},
];
});
const projectSettingsNavItems = computed<NavItem[]>(() => {
if (!props.project || !isProjectSettings.value) {
return [];
}
return [
{
title: 'General',
href: projectSettingsGeneral(props.project.id),
icon: SlidersHorizontal,
},
{
title: 'Board',
href: projectSettingsBoard(props.project.id),
icon: KanbanSquare,
},
{
title: 'Metadata',
href: projectSettingsMetadata(props.project.id),
icon: Tags,
},
{
title: 'Repositories',
href: projectSettingsRepositories(props.project.id),
icon: FolderGit2,
},
{
title: 'Notifications',
href: projectSettingsNotifications(props.project.id),
icon: BellRing,
},
];
});
const mainNavItems = computed<NavItem[]>(() => {
const items: NavItem[] = [
{
@@ -198,12 +122,6 @@ const footerNavItems: NavItem[] = [
{{ project.name }}
</div>
</div>
<NavMain :items="projectNavItems" label="Project" />
<NavMain
v-if="projectSettingsNavItems.length > 0"
:items="projectSettingsNavItems"
label="Project settings"
/>
<div class="mt-auto px-3 pb-3">
<SidebarMenu>
<SidebarMenuItem>
@@ -0,0 +1,72 @@
<script setup lang="ts">
import { Link } from '@inertiajs/vue3';
import { KanbanSquare, ListTodo, Settings2 } from 'lucide-vue-next';
import { index as backlogIndex } from '@/routes/projects/backlog';
import { index as boardIndex } from '@/routes/projects/board';
import { index as settingsIndex } from '@/routes/projects/settings';
import type { ProjectTabsProps } from '@/types';
defineProps<ProjectTabsProps>();
</script>
<template>
<nav
aria-label="Project sections"
class="border-outline-variant/70 flex min-w-max items-center gap-1 border-b"
>
<Link
:href="boardIndex(project.id)"
prefetch
:class="[
'group relative inline-flex h-11 items-center gap-2 px-3 text-sm font-semibold transition-colors',
active === 'board'
? 'text-primary'
: 'text-muted-foreground hover:text-foreground',
]"
>
<KanbanSquare class="size-4" />
Board
<span
v-if="active === 'board'"
class="bg-primary absolute inset-x-2 -bottom-px h-0.5"
/>
</Link>
<Link
:href="backlogIndex(project.id)"
prefetch
:class="[
'group relative inline-flex h-11 items-center gap-2 px-3 text-sm font-semibold transition-colors',
active === 'backlog'
? 'text-primary'
: 'text-muted-foreground hover:text-foreground',
]"
>
<ListTodo class="size-4" />
Backlog
<span
v-if="active === 'backlog'"
class="bg-primary absolute inset-x-2 -bottom-px h-0.5"
/>
</Link>
<Link
v-if="canManageProject"
:href="settingsIndex(project.id)"
prefetch
:class="[
'group relative inline-flex h-11 items-center gap-2 px-3 text-sm font-semibold transition-colors',
active === 'settings'
? 'text-primary'
: 'text-muted-foreground hover:text-foreground',
]"
>
<Settings2 class="size-4" />
Settings
<span
v-if="active === 'settings'"
class="bg-primary absolute inset-x-2 -bottom-px h-0.5"
/>
</Link>
</nav>
</template>