back and breadcrumbs

This commit is contained in:
vadim committed 2026-10-01 09:35:17 +03:00
1 parent c405507603
commit 9b70fb774f
9 files changed
+130 -4

No files matched your search

+15
View File
@@ -0,0 +1,15 @@
<script setup lang="ts">
import { useBreadcrumbs } from '@/composables/useBreadcrumbs.ts'
const { breadcrumbs } = useBreadcrumbs()
</script>
<template>
<v-breadcrumbs v-if="breadcrumbs.length > 0" :items="breadcrumbs" density="compact" class="pa-0 ma-0">
<template #divider>
<v-icon icon="mdi-chevron-right" size="small" />
</template>
</v-breadcrumbs>
</template>
<style scoped lang="scss"></style>
+21
View File
@@ -0,0 +1,21 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const backTo = computed(() => {
return [...route.matched].reverse().find((record) => record.meta.backTo)?.meta.backTo
})
function goBack() {
if (backTo.value) router.push({ name: backTo.value })
}
</script>
<template>
<v-btn v-if="backTo" icon="mdi-arrow-left" variant="text" density="compact" aria-label="Back" @click="goBack" />
</template>
<style scoped lang="scss"></style>
+35
View File
@@ -0,0 +1,35 @@
import { computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
export interface Breadcrumb {
title: string
to?: string
disabled?: boolean
}
/**
* Builds a breadcrumb trail from `meta.breadcrumb` on the matched route chain,
* so pages don't need to know their own position in the hierarchy.
*/
export function useBreadcrumbs() {
const route = useRoute()
const router = useRouter()
const breadcrumbs = computed<Breadcrumb[]>(() => {
const matched = route.matched.filter((record) => record.meta.breadcrumb)
return matched.map((record, index) => {
const { breadcrumb } = record.meta
const title = typeof breadcrumb === 'function' ? breadcrumb(route) : (breadcrumb as string)
const isLast = index === matched.length - 1
const to =
!isLast && record.name
? router.resolve({ name: record.name, params: route.params }).path
: undefined
return { title, to, disabled: isLast }
})
})
return { breadcrumbs }
}
+12 -2
View File
@@ -4,6 +4,8 @@ import { authClient } from '@/lib/auth-client.ts'
import { computed, ref } from 'vue'
import { useTheme } from 'vuetify'
import ProjectMenu from '@/components/ProjectMenu.vue'
import AppBreadcrumbs from '@/components/AppBreadcrumbs.vue'
import BackButton from '@/components/BackButton.vue'
const router = useRouter()
const session = authClient.useSession()
@@ -140,11 +142,19 @@ const goToSettings = () => {
</div>
</v-app-bar>
<v-main>
<v-container :fluid="false">
<v-container :fluid="true">
<div class="nav-row d-flex align-center ga-2 mb-2">
<back-button />
<app-breadcrumbs />
</div>
<slot></slot>
</v-container>
</v-main>
</v-layout>
</template>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.nav-row:empty {
display: none;
}
</style>
+5 -1
View File
@@ -1,4 +1,4 @@
import { createRouter, createWebHistory } from 'vue-router'
import { createRouter, createWebHistory, type RouteLocationNormalizedLoaded } from 'vue-router'
import ProfileView from '@/views/users/ProfileView.vue'
import { issuesRoutes } from '@/router/issues.ts'
import HomeView from '@/views/HomeView.vue'
@@ -12,6 +12,10 @@ import { settingsRoutes } from '@/router/settings.ts'
declare module 'vue-router' {
interface RouteMeta {
layout?: 'Blank' | 'Default'
/** Label shown in the breadcrumbs trail for this route segment. */
breadcrumb?: string | ((route: RouteLocationNormalizedLoaded) => string)
/** Named route the back button should navigate to, when deep-linking means history may be empty. */
backTo?: string
}
}
+7
View File
@@ -8,12 +8,19 @@ export const issuesRoutes: RouteRecordRaw = {
children: [
{
path: '',
name: 'issues',
component: IssuesView,
meta: { breadcrumb: 'Issues' },
},
{
path: ':ckey',
name: 'issue-detail',
component: IssueView,
props: true,
meta: {
breadcrumb: (route) => route.params.ckey as string,
backTo: 'issues',
},
},
],
}
+3 -1
View File
@@ -7,17 +7,19 @@ export const settingsRoutes: RouteRecordRaw = {
path: '/settings',
name: 'settings',
component: SettingsView,
meta: { requiresAuth: true },
meta: { requiresAuth: true, breadcrumb: 'Settings' },
children: [
{
path: '',
name: 'general-settings',
component: GeneralSettingsView,
meta: { breadcrumb: 'General' },
},
{
path: 'projects',
name: 'settings-projects',
component: ProjectsListView,
meta: { breadcrumb: 'Projects' },
},
],
}