146 lines
5.7 KiB
Vue
146 lines
5.7 KiB
Vue
<script setup lang="ts">
|
|
import { Link, usePage } from '@inertiajs/vue3';
|
|
import { Monitor, Moon, Plus, Sun } from 'lucide-vue-next';
|
|
import { computed } from 'vue';
|
|
import Breadcrumbs from '@/components/Breadcrumbs.vue';
|
|
import UserMenuContent from '@/components/UserMenuContent.vue';
|
|
import { UserAvatar } from '@/components/ui/avatar';
|
|
import { Button } from '@/components/ui/button';
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuLabel,
|
|
DropdownMenuRadioGroup,
|
|
DropdownMenuRadioItem,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from '@/components/ui/dropdown-menu';
|
|
import { SidebarTrigger } from '@/components/ui/sidebar';
|
|
import { useAppNavigation } from '@/composables/useAppNavigation';
|
|
import {
|
|
normalizeAppearance,
|
|
useAppearance,
|
|
} from '@/composables/useAppearance';
|
|
import { useCurrentUrl } from '@/composables/useCurrentUrl';
|
|
import { useLocalization } from '@/composables/useLocalization';
|
|
import { create as createProject } from '@/routes/projects';
|
|
import { index as board } from '@/routes/projects/board';
|
|
import { create as createTicket } from '@/routes/projects/tickets';
|
|
import type { BreadcrumbItem } from '@/types';
|
|
|
|
withDefaults(defineProps<{ breadcrumbs?: BreadcrumbItem[] }>(), {
|
|
breadcrumbs: () => [],
|
|
});
|
|
const page = usePage();
|
|
const { project } = useAppNavigation();
|
|
const { isCurrentUrl } = useCurrentUrl();
|
|
const { t } = useLocalization();
|
|
const { appearance, updateAppearance } = useAppearance();
|
|
const themeIcon = computed(
|
|
() => ({ light: Sun, dark: Moon, system: Monitor })[appearance.value],
|
|
);
|
|
const createAction = computed(() => {
|
|
if (project.value?.canCreateTickets) {
|
|
return {
|
|
title: t('navigation.createTicket'),
|
|
href: createTicket(project.value.id, {
|
|
query: {
|
|
source: isCurrentUrl(board(project.value.id))
|
|
? 'board'
|
|
: 'backlog',
|
|
},
|
|
}),
|
|
};
|
|
}
|
|
if (!project.value && page.props.auth.permissions.projectCreate) {
|
|
return { title: t('navigation.createProject'), href: createProject() };
|
|
}
|
|
return null;
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<header
|
|
class="bg-background md:px-page sticky top-0 z-20 flex min-h-16 min-w-0 shrink-0 items-center justify-between gap-2 border-b px-3 md:gap-4"
|
|
>
|
|
<div class="flex min-w-0 flex-1 items-center gap-2">
|
|
<SidebarTrigger class="shrink-0" />
|
|
<Breadcrumbs v-if="breadcrumbs.length" :breadcrumbs="breadcrumbs" />
|
|
<span v-else class="truncate font-semibold">KanbanGit</span>
|
|
</div>
|
|
<div class="flex shrink-0 items-center gap-1 sm:gap-2">
|
|
<Button
|
|
v-if="createAction"
|
|
as-child
|
|
:aria-label="createAction.title"
|
|
class="px-3"
|
|
>
|
|
<Link :href="createAction.href"
|
|
><Plus aria-hidden="true" /><span
|
|
class="hidden sm:inline"
|
|
>{{ createAction.title }}</span
|
|
></Link
|
|
>
|
|
</Button>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger as-child>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
:aria-label="t('appearance.label')"
|
|
><component :is="themeIcon" aria-hidden="true"
|
|
/></Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end">
|
|
<DropdownMenuLabel>{{
|
|
t('appearance.label')
|
|
}}</DropdownMenuLabel>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuRadioGroup
|
|
:model-value="appearance"
|
|
@update:model-value="
|
|
(value) =>
|
|
updateAppearance(normalizeAppearance(value))
|
|
"
|
|
>
|
|
<DropdownMenuRadioItem value="light"
|
|
><Sun aria-hidden="true" />{{
|
|
t('appearance.light')
|
|
}}</DropdownMenuRadioItem
|
|
>
|
|
<DropdownMenuRadioItem value="dark"
|
|
><Moon aria-hidden="true" />{{
|
|
t('appearance.dark')
|
|
}}</DropdownMenuRadioItem
|
|
>
|
|
<DropdownMenuRadioItem value="system"
|
|
><Monitor aria-hidden="true" />{{
|
|
t('appearance.system')
|
|
}}</DropdownMenuRadioItem
|
|
>
|
|
</DropdownMenuRadioGroup>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger as-child>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
:aria-label="
|
|
t('navigation.userMenu', {
|
|
name: page.props.auth.user.name,
|
|
})
|
|
"
|
|
data-test="sidebar-menu-button"
|
|
>
|
|
<UserAvatar :user="page.props.auth.user" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" class="min-w-60">
|
|
<UserMenuContent :user="page.props.auth.user" />
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
</header>
|
|
</template>
|