mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-16 04:02:15 +01:00
add reporting submenus to navbar
This commit is contained in:
committed by
Constantin Graf
parent
2560619c15
commit
9c82efdf07
@@ -1,35 +1,109 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Component } from 'vue';
|
import { type Component } from 'vue';
|
||||||
import { Link } from '@inertiajs/vue3';
|
import NavigationSidebarLink from '@/Components/NavigationSidebarLink.vue';
|
||||||
|
import {
|
||||||
|
CollapsibleContent,
|
||||||
|
CollapsibleRoot,
|
||||||
|
CollapsibleTrigger,
|
||||||
|
} from 'radix-vue';
|
||||||
|
import { useSessionStorage } from '@vueuse/core';
|
||||||
|
import { ChevronRightIcon } from '@heroicons/vue/20/solid';
|
||||||
|
|
||||||
defineProps<{
|
const props = defineProps<{
|
||||||
title: string;
|
title: string;
|
||||||
icon: Component;
|
icon?: Component;
|
||||||
current?: boolean;
|
current?: boolean;
|
||||||
href: string;
|
href: string;
|
||||||
|
subItems?: { title: string; route: string }[];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const open = useSessionStorage('nav-collapse-state-' + props.title, true);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<li>
|
<li class="relative">
|
||||||
<Link
|
<NavigationSidebarLink
|
||||||
:href="href"
|
v-if="!subItems"
|
||||||
:class="[
|
class="py-0.5"
|
||||||
current
|
:title
|
||||||
? 'bg-menu-active text-white'
|
:icon
|
||||||
: 'text-muted hover:text-white hover:bg-menu-active ',
|
:current
|
||||||
'group flex gap-x-2 rounded-md px-2 py-1.5 transition leading-6 font-semibold text-sm items-center',
|
:href></NavigationSidebarLink>
|
||||||
]">
|
<CollapsibleRoot v-model:open="open" v-else
|
||||||
<component
|
><CollapsibleTrigger class="w-full group py-0.5">
|
||||||
:is="icon"
|
<div
|
||||||
:class="[
|
class="text-muted group-hover:text-white group-hover:bg-menu-active group flex gap-x-2 rounded-md transition leading-6 py-1 px-2 font-medium text-sm items-center justify-between">
|
||||||
current
|
<div class="flex items-center gap-x-2">
|
||||||
? 'text-icon-active'
|
<component
|
||||||
: 'text-icon-default group-hover:text-icon-active',
|
v-if="icon"
|
||||||
'transition h-5 w-5 shrink-0',
|
:is="icon"
|
||||||
]"
|
:class="[
|
||||||
aria-hidden="true" />
|
current
|
||||||
{{ title }}
|
? 'text-icon-active'
|
||||||
</Link>
|
: 'text-icon-default group-hover:text-icon-active',
|
||||||
|
'transition h-5 w-5 shrink-0',
|
||||||
|
]"
|
||||||
|
aria-hidden="true" />
|
||||||
|
<span>
|
||||||
|
{{ title }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ChevronRightIcon
|
||||||
|
:class="[
|
||||||
|
'w-5 text-text-secondary',
|
||||||
|
{ 'transform rotate-90': open },
|
||||||
|
]"></ChevronRightIcon>
|
||||||
|
</div>
|
||||||
|
</CollapsibleTrigger>
|
||||||
|
<CollapsibleContent class="CollapsibleContent">
|
||||||
|
<div class="px-3.5">
|
||||||
|
<ul
|
||||||
|
class="flex min-w-0 flex-col border-l border-border-secondary px-3 w-full my-0.5"
|
||||||
|
v-if="subItems">
|
||||||
|
<li
|
||||||
|
v-for="subItem in subItems"
|
||||||
|
:key="subItem.title"
|
||||||
|
class="w-full relative">
|
||||||
|
<NavigationSidebarLink
|
||||||
|
:title="subItem.title"
|
||||||
|
:current="route().current(subItem.route)"
|
||||||
|
:href="
|
||||||
|
route(subItem.route)
|
||||||
|
"></NavigationSidebarLink>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</CollapsibleContent>
|
||||||
|
</CollapsibleRoot>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.CollapsibleContent {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.CollapsibleContent[data-state='open'] {
|
||||||
|
animation: slideDown 300ms ease-out;
|
||||||
|
}
|
||||||
|
.CollapsibleContent[data-state='closed'] {
|
||||||
|
animation: slideUp 300ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideDown {
|
||||||
|
from {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
height: var(--radix-collapsible-content-height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideUp {
|
||||||
|
from {
|
||||||
|
height: var(--radix-collapsible-content-height);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
36
resources/js/Components/NavigationSidebarLink.vue
Normal file
36
resources/js/Components/NavigationSidebarLink.vue
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Link } from '@inertiajs/vue3';
|
||||||
|
import type { Component } from 'vue';
|
||||||
|
defineProps<{
|
||||||
|
title: string;
|
||||||
|
icon?: Component;
|
||||||
|
current?: boolean;
|
||||||
|
href: string;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Link :href="href" class="block group">
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
current
|
||||||
|
? 'bg-menu-active text-white'
|
||||||
|
: 'text-muted group-hover:text-white group-hover:bg-menu-active ',
|
||||||
|
'group flex gap-x-2 rounded-md transition leading-6 py-1 px-2 font-medium text-sm items-center',
|
||||||
|
]">
|
||||||
|
<component
|
||||||
|
v-if="icon"
|
||||||
|
:is="icon"
|
||||||
|
:class="[
|
||||||
|
current
|
||||||
|
? 'text-icon-active'
|
||||||
|
: 'text-icon-default group-hover:text-icon-active',
|
||||||
|
'transition h-5 w-5 shrink-0',
|
||||||
|
]"
|
||||||
|
aria-hidden="true" />
|
||||||
|
{{ title }}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -114,13 +114,27 @@ const page = usePage<{
|
|||||||
<NavigationSidebarItem
|
<NavigationSidebarItem
|
||||||
title="Reporting"
|
title="Reporting"
|
||||||
:icon="ChartBarIcon"
|
:icon="ChartBarIcon"
|
||||||
|
:sub-items="[
|
||||||
|
{
|
||||||
|
title: 'Overview',
|
||||||
|
route: 'reporting',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Detailed',
|
||||||
|
route: 'reporting.detailed',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Shared',
|
||||||
|
route: 'reporting.shared',
|
||||||
|
},
|
||||||
|
]"
|
||||||
:current="
|
:current="
|
||||||
route().current('reporting') ||
|
route().current('reporting') ||
|
||||||
route().current('reporting.detailed')
|
route().current('reporting.detailed') ||
|
||||||
|
route().current('reporting.shared')
|
||||||
"
|
"
|
||||||
:href="
|
:href="route('reporting')">
|
||||||
route('reporting')
|
</NavigationSidebarItem>
|
||||||
"></NavigationSidebarItem>
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ import {
|
|||||||
import { type GroupingOption, useReportingStore } from '@/utils/useReporting';
|
import { type GroupingOption, useReportingStore } from '@/utils/useReporting';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import TagDropdown from '@/packages/ui/src/Tag/TagDropdown.vue';
|
import TagDropdown from '@/packages/ui/src/Tag/TagDropdown.vue';
|
||||||
import { type AggregatedTimeEntriesQueryParams, api } from '@/packages/api/src';
|
import {
|
||||||
import type {
|
type AggregatedTimeEntriesQueryParams,
|
||||||
AggregatedTimeEntriesQueryParams,
|
type CreateReportBodyProperties,
|
||||||
CreateReportBodyProperties,
|
api,
|
||||||
} from '@/packages/api/src';
|
} from '@/packages/api/src';
|
||||||
import ReportingFilterBadge from '@/Components/Common/Reporting/ReportingFilterBadge.vue';
|
import ReportingFilterBadge from '@/Components/Common/Reporting/ReportingFilterBadge.vue';
|
||||||
import ProjectMultiselectDropdown from '@/Components/Common/Project/ProjectMultiselectDropdown.vue';
|
import ProjectMultiselectDropdown from '@/Components/Common/Project/ProjectMultiselectDropdown.vue';
|
||||||
|
|||||||
Reference in New Issue
Block a user