Rebuild General Layout to Nuxt UI PRO Dashboard
This commit is contained in:
@@ -3,9 +3,7 @@ const supabase = useSupabaseClient()
|
||||
const user = useSupabaseUser()
|
||||
const route = useRoute()
|
||||
const tenants = (await supabase.from("tenants").select()).data
|
||||
|
||||
const dataStore = useDataStore()
|
||||
|
||||
const viewport = useViewport()
|
||||
|
||||
/*watch(viewport.breakpoint, (newBreakpoint, oldBreakpoint) => {
|
||||
@@ -13,6 +11,10 @@ const viewport = useViewport()
|
||||
})*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
useHead({
|
||||
meta: [
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
|
||||
@@ -45,6 +47,28 @@ useSeoMeta({
|
||||
</template>
|
||||
|
||||
<style>
|
||||
/* width */
|
||||
::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgb(2,6,23);
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: grey;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #69c350;
|
||||
}
|
||||
|
||||
|
||||
#logo img{
|
||||
height: 15vh;
|
||||
@@ -58,24 +82,12 @@ useSeoMeta({
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
overflow-y: scroll;
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
.documentList::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scrollList {
|
||||
overflow-y: scroll;
|
||||
height: 85vh;
|
||||
margin-top: 1em;
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
.scrollList::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -101,16 +113,4 @@ useSeoMeta({
|
||||
}
|
||||
|
||||
|
||||
|
||||
.table > div {
|
||||
width: 78vw;
|
||||
height: 90vh;
|
||||
overflow: scroll !important;
|
||||
-ms-overflow-style: none; /*!* IE and Edge *!*/
|
||||
scrollbar-width: none; /*!* Firefox *!*/
|
||||
}
|
||||
|
||||
.table > div::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
107
spaces/components/HelpSlideover.vue
Normal file
107
spaces/components/HelpSlideover.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<script setup lang="ts">
|
||||
const { isHelpSlideoverOpen } = useDashboard()
|
||||
const { metaSymbol } = useShortcuts()
|
||||
|
||||
const shortcuts = ref(false)
|
||||
const query = ref('')
|
||||
|
||||
const links = [{
|
||||
label: 'Shortcuts',
|
||||
icon: 'i-heroicons-key',
|
||||
trailingIcon: 'i-heroicons-arrow-right-20-solid',
|
||||
color: 'gray',
|
||||
onClick: () => {
|
||||
shortcuts.value = true
|
||||
}
|
||||
}, {
|
||||
label: 'Documentation',
|
||||
icon: 'i-heroicons-book-open',
|
||||
to: 'https://ui.nuxt.com/pro/guide',
|
||||
target: '_blank'
|
||||
}, {
|
||||
label: 'GitHub repository',
|
||||
icon: 'i-simple-icons-github',
|
||||
to: 'https://github.com/nuxt/ui-pro',
|
||||
target: '_blank'
|
||||
}, {
|
||||
label: 'Buy Nuxt UI Pro',
|
||||
icon: 'i-heroicons-credit-card',
|
||||
to: 'https://ui.nuxt.com/pro/purchase',
|
||||
target: '_blank'
|
||||
}]
|
||||
|
||||
const categories = computed(() => [{
|
||||
title: 'General',
|
||||
items: [
|
||||
{ shortcuts: [metaSymbol.value, 'K'], name: 'Command menu' },
|
||||
{ shortcuts: ['N'], name: 'Notifications' },
|
||||
{ shortcuts: ['?'], name: 'Help & Support' },
|
||||
{ shortcuts: ['/'], name: 'Search' }
|
||||
]
|
||||
}, {
|
||||
title: 'Navigation',
|
||||
items: [
|
||||
{ shortcuts: ['G', 'H'], name: 'Go to Home' },
|
||||
{ shortcuts: ['G', 'I'], name: 'Go to Inbox' },
|
||||
{ shortcuts: ['G', 'U'], name: 'Go to Users' },
|
||||
{ shortcuts: ['G', 'S'], name: 'Go to Settings' }
|
||||
]
|
||||
}, {
|
||||
title: 'Inbox',
|
||||
items: [
|
||||
{ shortcuts: ['↑'], name: 'Prev notification' },
|
||||
{ shortcuts: ['↓'], name: 'Next notification' }
|
||||
]
|
||||
}])
|
||||
|
||||
const filteredCategories = computed(() => {
|
||||
return categories.value.map(category => ({
|
||||
title: category.title,
|
||||
items: category.items.filter(item => {
|
||||
return item.name.search(new RegExp(query.value, 'i')) !== -1
|
||||
})
|
||||
})).filter(category => !!category.items.length)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDashboardSlideover v-model="isHelpSlideoverOpen">
|
||||
<template #title>
|
||||
<UButton
|
||||
v-if="shortcuts"
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
icon="i-heroicons-arrow-left-20-solid"
|
||||
@click="shortcuts = false"
|
||||
/>
|
||||
|
||||
{{ shortcuts ? 'Shortcuts' : 'Hilfe & Support' }}
|
||||
</template>
|
||||
|
||||
<div v-if="shortcuts" class="space-y-6">
|
||||
<UInput v-model="query" icon="i-heroicons-magnifying-glass" placeholder="Search..." autofocus color="gray" />
|
||||
|
||||
<div v-for="(category, index) in filteredCategories" :key="index">
|
||||
<p class="mb-3 text-sm text-gray-900 dark:text-white font-semibold">
|
||||
{{ category.title }}
|
||||
</p>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div v-for="(item, i) in category.items" :key="i" class="flex items-center justify-between">
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400">{{ item.name }}</span>
|
||||
|
||||
<div class="flex items-center justify-end flex-shrink-0 gap-0.5">
|
||||
<UKbd v-for="(shortcut, j) in item.shortcuts" :key="j">
|
||||
{{ shortcut }}
|
||||
</UKbd>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-y-3">
|
||||
<UButton v-for="(link, index) in links" :key="index" color="white" v-bind="link" />
|
||||
</div>
|
||||
</UDashboardSlideover>
|
||||
</template>
|
||||
29
spaces/components/NotificationsSlideover.vue
Normal file
29
spaces/components/NotificationsSlideover.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import { formatTimeAgo } from '@vueuse/core'
|
||||
import type { Notification } from '~/types'
|
||||
|
||||
const { isNotificationsSlideoverOpen } = useDashboard()
|
||||
|
||||
const { data: notifications } = await useFetch<Notification[]>('/api/notifications')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDashboardSlideover v-model="isNotificationsSlideoverOpen" title="Notifications">
|
||||
<NuxtLink v-for="notification in notifications" :key="notification.id" :to="`/inbox?id=${notification.id}`" class="p-3 rounded-md hover:bg-gray-50 dark:hover:bg-gray-800/50 cursor-pointer flex items-center gap-3 relative">
|
||||
<UChip color="red" :show="!!notification.unread" inset>
|
||||
<UAvatar v-bind="notification.sender.avatar" :alt="notification.sender.name" size="md" />
|
||||
</UChip>
|
||||
|
||||
<div class="text-sm flex-1">
|
||||
<p class="flex items-center justify-between">
|
||||
<span class="text-gray-900 dark:text-white font-medium">{{ notification.sender.name }}</span>
|
||||
|
||||
<time :datetime="notification.date" class="text-gray-500 dark:text-gray-400 text-xs" v-text="formatTimeAgo(new Date(notification.date))" />
|
||||
</p>
|
||||
<p class="text-gray-500 dark:text-gray-400">
|
||||
{{ notification.body }}
|
||||
</p>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</UDashboardSlideover>
|
||||
</template>
|
||||
46
spaces/components/TeamsDropdown.vue
Normal file
46
spaces/components/TeamsDropdown.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<script setup lang="ts">
|
||||
const teams = [{
|
||||
label: 'Nuxt',
|
||||
/*avatar: {
|
||||
src: 'https://avatars.githubusercontent.com/u/23360933?s=200&v=4'
|
||||
},*/
|
||||
click: () => {
|
||||
team.value = teams[0]
|
||||
}
|
||||
}, {
|
||||
label: 'NuxtLabs',
|
||||
/*avatar: {
|
||||
src: 'https://avatars.githubusercontent.com/u/62017400?s=200&v=4'
|
||||
},*/
|
||||
click: () => {
|
||||
team.value = teams[1]
|
||||
}
|
||||
}]
|
||||
|
||||
const actions = [{
|
||||
label: 'Create team',
|
||||
icon: 'i-heroicons-plus-circle'
|
||||
}, {
|
||||
label: 'Manage teams',
|
||||
icon: 'i-heroicons-cog-8-tooth'
|
||||
}]
|
||||
|
||||
const team = ref(teams[0])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDropdown
|
||||
v-slot="{ open }"
|
||||
mode="hover"
|
||||
:items="[teams]"
|
||||
class="w-full"
|
||||
:ui="{ width: 'w-full' }"
|
||||
:popper="{ strategy: 'absolute' }"
|
||||
>
|
||||
<UButton color="gray" variant="ghost" :class="[open && 'bg-gray-50 dark:bg-gray-800']" class="w-full">
|
||||
<UAvatar v-if="team.avatar" :src="team.avatar.src" size="2xs" />
|
||||
|
||||
<span class="truncate text-gray-900 dark:text-white font-semibold">{{ team.label }}</span>
|
||||
</UButton>
|
||||
</UDropdown>
|
||||
</template>
|
||||
84
spaces/components/UserDropdown.vue
Normal file
84
spaces/components/UserDropdown.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<script setup lang="ts">
|
||||
const { isHelpSlideoverOpen } = useDashboard()
|
||||
const { isDashboardSearchModalOpen } = useUIState()
|
||||
const { metaSymbol } = useShortcuts()
|
||||
const user = useSupabaseUser()
|
||||
const dataStore = useDataStore()
|
||||
const supabase = useSupabaseClient()
|
||||
const router = useRouter()
|
||||
|
||||
const items = computed(() => [
|
||||
[{
|
||||
slot: 'account',
|
||||
label: '',
|
||||
disabled: true
|
||||
}], [{
|
||||
label: 'Settings',
|
||||
icon: 'i-heroicons-cog-8-tooth',
|
||||
to: '/settings'
|
||||
}, {
|
||||
label: 'Command menu',
|
||||
icon: 'i-heroicons-command-line',
|
||||
shortcuts: [metaSymbol.value, 'K'],
|
||||
click: () => {
|
||||
isDashboardSearchModalOpen.value = true
|
||||
}
|
||||
}, {
|
||||
label: 'Help & Support',
|
||||
icon: 'i-heroicons-question-mark-circle',
|
||||
shortcuts: ['?'],
|
||||
click: () => isHelpSlideoverOpen.value = true
|
||||
}], [{
|
||||
label: 'Documentation',
|
||||
icon: 'i-heroicons-book-open',
|
||||
to: 'https://ui.nuxt.com/pro/guide',
|
||||
target: '_blank'
|
||||
},/* {
|
||||
label: 'GitHub repository',
|
||||
icon: 'i-simple-icons-github',
|
||||
to: 'https://github.com/nuxt/ui-pro',
|
||||
target: '_blank'
|
||||
}, {
|
||||
label: 'Buy Nuxt UI Pro',
|
||||
icon: 'i-heroicons-credit-card',
|
||||
to: 'https://ui.nuxt.com/pro/purchase',
|
||||
target: '_blank'
|
||||
}*/], [{
|
||||
label: 'Abmelden',
|
||||
icon: 'i-heroicons-arrow-left-on-rectangle',
|
||||
click: async () => {
|
||||
await supabase.auth.signOut()
|
||||
await dataStore.clearStore()
|
||||
await router.push('/login')
|
||||
|
||||
}
|
||||
}]
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDropdown mode="hover" :items="items" :ui="{ width: 'w-full', item: { disabled: 'cursor-text select-text' } }" :popper="{ strategy: 'absolute', placement: 'top' }" class="w-full">
|
||||
<template #default="{ open }">
|
||||
<UButton color="gray" variant="ghost" class="w-full" :label="dataStore.getProfileById(user.id).fullName" :class="[open && 'bg-gray-50 dark:bg-gray-800']">
|
||||
<template #leading>
|
||||
<UAvatar :alt="dataStore.getProfileById(user.id) ? dataStore.getProfileById(user.id).fullName : ''" size="xs" />
|
||||
</template>
|
||||
|
||||
<template #trailing>
|
||||
<UIcon name="i-heroicons-ellipsis-vertical" class="w-5 h-5 ml-auto" />
|
||||
</template>
|
||||
</UButton>
|
||||
</template>
|
||||
|
||||
<template #account>
|
||||
<div class="text-left">
|
||||
<p>
|
||||
Angemeldet als
|
||||
</p>
|
||||
<p class="truncate font-medium text-gray-900 dark:text-white">
|
||||
{{dataStore.getProfileById(user.id).email}}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</UDropdown>
|
||||
</template>
|
||||
29
spaces/composables/useDashboard.ts
Normal file
29
spaces/composables/useDashboard.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { createSharedComposable } from '@vueuse/core'
|
||||
|
||||
const _useDashboard = () => {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const isHelpSlideoverOpen = ref(false)
|
||||
const isNotificationsSlideoverOpen = ref(false)
|
||||
|
||||
defineShortcuts({
|
||||
'g-h': () => router.push('/'),
|
||||
'g-i': () => router.push('/inbox'),
|
||||
'g-u': () => router.push('/users'),
|
||||
'g-s': () => router.push('/settings'),
|
||||
'?': () => isHelpSlideoverOpen.value = true,
|
||||
n: () => isNotificationsSlideoverOpen.value = true
|
||||
})
|
||||
|
||||
watch(() => route.fullPath, () => {
|
||||
isHelpSlideoverOpen.value = false
|
||||
isNotificationsSlideoverOpen.value = false
|
||||
})
|
||||
|
||||
return {
|
||||
isHelpSlideoverOpen,
|
||||
isNotificationsSlideoverOpen
|
||||
}
|
||||
}
|
||||
|
||||
export const useDashboard = createSharedComposable(_useDashboard)
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
const dataStore = useDataStore()
|
||||
const colorMode = useColorMode()
|
||||
|
||||
const { isHelpSlideoverOpen } = useDashboard()
|
||||
const userProfile = dataStore.getOwnProfile
|
||||
const supabase = useSupabaseClient()
|
||||
const router = useRouter()
|
||||
@@ -10,6 +10,7 @@ const route = useRoute()
|
||||
|
||||
dataStore.initializeData((await supabase.auth.getUser()).data.user.id)
|
||||
|
||||
|
||||
const isLight = computed({
|
||||
get() {
|
||||
return colorMode.value !== 'dark'
|
||||
@@ -196,21 +197,16 @@ const userMenuItems = ref([
|
||||
}
|
||||
])
|
||||
|
||||
const links = [[{
|
||||
label: 'Profil',
|
||||
avatar: {
|
||||
alt: userProfile ? userProfile.fullName : "XY"
|
||||
}
|
||||
},{
|
||||
const linksold = [[{
|
||||
label: "Dashboard",
|
||||
to: "/",
|
||||
icon: "i-heroicons-home"
|
||||
}], [{
|
||||
}],
|
||||
[{
|
||||
label: "Aufgaben",
|
||||
to: "/tasks",
|
||||
icon: "i-heroicons-rectangle-stack"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
label: "Plantafel",
|
||||
to: "/calendar/timeline",
|
||||
icon: "i-heroicons-calendar-days"
|
||||
@@ -314,147 +310,227 @@ const links = [[{
|
||||
}]
|
||||
]
|
||||
|
||||
let links = [
|
||||
{
|
||||
id: 'dashboard',
|
||||
label: "Dashboard",
|
||||
to: "/",
|
||||
icon: "i-heroicons-home"
|
||||
},
|
||||
{
|
||||
label: "Organisation",
|
||||
icon: "i-heroicons-rectangle-stack",
|
||||
defaultOpen: false,
|
||||
children: [
|
||||
{
|
||||
label: "Aufgaben",
|
||||
to: "/tasks",
|
||||
icon: "i-heroicons-rectangle-stack"
|
||||
},
|
||||
{
|
||||
label: "Plantafel",
|
||||
to: "/calendar/timeline",
|
||||
icon: "i-heroicons-calendar-days"
|
||||
},
|
||||
{
|
||||
label: "Kalender",
|
||||
to: "/calendar/grid",
|
||||
icon: "i-heroicons-calendar-days"
|
||||
},
|
||||
{
|
||||
label: "Dokumente",
|
||||
to: "/documents",
|
||||
icon: "i-heroicons-document"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "E-Mail",
|
||||
to: "/email",
|
||||
icon: "i-heroicons-envelope"
|
||||
},
|
||||
{
|
||||
label: "Kontakte",
|
||||
defaultOpen: false,
|
||||
icon: "i-heroicons-user-group",
|
||||
children: [
|
||||
{
|
||||
label: "Kunden",
|
||||
to: "/customers",
|
||||
icon: "i-heroicons-user-group"
|
||||
},
|
||||
{
|
||||
label: "Lieferanten",
|
||||
to: "/vendors",
|
||||
icon: "i-heroicons-truck"
|
||||
},
|
||||
{
|
||||
label: "Ansprechpartner",
|
||||
to: "/contacts",
|
||||
icon: "i-heroicons-user-group"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Mitarbeiter",
|
||||
defaultOpen:false,
|
||||
icon: "i-heroicons-user-group",
|
||||
children: [
|
||||
{
|
||||
label: "Zeiterfassung",
|
||||
to: "/employees/timetracking",
|
||||
icon: "i-heroicons-clock"
|
||||
},
|
||||
{
|
||||
label: "Anwesenheiten",
|
||||
to: "/workingtimes",
|
||||
icon: "i-heroicons-clock"
|
||||
},
|
||||
{
|
||||
label: "Abwesenheiten",
|
||||
to: "/absenceRequests",
|
||||
icon: "i-heroicons-document-text"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Lager",
|
||||
icon: "i-heroicons-puzzle-piece",
|
||||
defaultOpen: false,
|
||||
children: [
|
||||
{
|
||||
label: "Steuerung",
|
||||
to: "/inventory",
|
||||
icon: "i-heroicons-square-3-stack-3d"
|
||||
},
|
||||
{
|
||||
label: "Lagerplätze",
|
||||
to: "/spaces",
|
||||
icon: "i-heroicons-square-3-stack-3d"
|
||||
},
|
||||
{
|
||||
label: "Inventar",
|
||||
to: "/inventoryitems",
|
||||
icon: "i-heroicons-puzzle-piece"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Stammdaten",
|
||||
defaultOpen: false,
|
||||
icon: "i-heroicons-clipboard-document",
|
||||
children: [
|
||||
{
|
||||
label: "Artikelstamm",
|
||||
to: "/products",
|
||||
icon: "i-heroicons-puzzle-piece"
|
||||
},{
|
||||
label: "Leistungsstamm",
|
||||
to: "/services",
|
||||
icon: "i-heroicons-puzzle-piece"
|
||||
},{
|
||||
label: "Fahrzeuge",
|
||||
to: "/vehicles",
|
||||
icon: "i-heroicons-truck"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Belege",
|
||||
to: "/receipts",
|
||||
icon: "i-heroicons-document-text"
|
||||
},{
|
||||
label: "Projekte",
|
||||
to: "/projects",
|
||||
icon: "i-heroicons-clipboard-document-check"
|
||||
},
|
||||
{
|
||||
label: "Verträge",
|
||||
to: "/contracts",
|
||||
icon: "i-heroicons-clipboard-document"
|
||||
},
|
||||
{
|
||||
label: "Objekte",
|
||||
to: "/plants",
|
||||
icon: "i-heroicons-clipboard-document"
|
||||
},
|
||||
]
|
||||
|
||||
const groups = [{
|
||||
key: 'links',
|
||||
label: 'Go to',
|
||||
commands: links.map(link => ({ ...link, shortcuts: link.tooltip?.shortcuts }))
|
||||
}, {
|
||||
key: 'code',
|
||||
label: 'Code',
|
||||
commands: [{
|
||||
id: 'source',
|
||||
label: 'View page source',
|
||||
icon: 'i-simple-icons-github',
|
||||
click: () => {
|
||||
window.open(`https://github.com/nuxt-ui-pro/dashboard/blob/main/pages${route.path === '/' ? '/index' : route.path}.vue`, '_blank')
|
||||
}
|
||||
}]
|
||||
}]
|
||||
const footerLinks = [/*{
|
||||
label: 'Invite people',
|
||||
icon: 'i-heroicons-plus',
|
||||
to: '/settings/members'
|
||||
}, */{
|
||||
label: 'Hilfe & Support',
|
||||
icon: 'i-heroicons-question-mark-circle',
|
||||
click: () => isHelpSlideoverOpen.value = true
|
||||
}]
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="dataStore.loaded" class="flex justify-center flex-row">
|
||||
<!-- <UHeader :links="navLinks" :to="null">
|
||||
<template #logo>
|
||||
<div id="logo">
|
||||
<img
|
||||
:src="!isLight ? '/spaces.svg' : '/spaces_hell.svg'"
|
||||
alt="Logo"
|
||||
/>
|
||||
</div>
|
||||
<UDashboardLayout v-if="dataStore.loaded">
|
||||
|
||||
<UDashboardPanel :width="250" :resizable="{ min: 200, max: 300 }" collapsible>
|
||||
<UDashboardNavbar class="!border-transparent" :ui="{ left: 'flex-1' }">
|
||||
<template #left>
|
||||
<TeamsDropdown />
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
|
||||
<UDashboardSidebar id="sidebar">
|
||||
<template #header>
|
||||
<UDashboardSearchButton />
|
||||
</template>
|
||||
|
||||
<template #panel>
|
||||
<UNavigationTree :links="navLinks"/>
|
||||
</template>
|
||||
<UDashboardSidebarLinks :links="links" />
|
||||
|
||||
<template #right>
|
||||
<!--
|
||||
<UDivider />
|
||||
|
||||
<GlobalSearch/>
|
||||
<UDashboardSidebarLinks :links="[{ label: 'Colors', draggable: true, children: colors }]" @update:links="colors => defaultColors = colors" />
|
||||
|
||||
<UButton
|
||||
@click="showUserMenu = true"
|
||||
variant="ghost"
|
||||
color="gray"
|
||||
>
|
||||
<UAvatar
|
||||
:alt="userProfile ? userProfile.firstName + ' ' + userProfile.lastName : '' "
|
||||
icon="i-heroicons-user-20-solid"
|
||||
:chip-color="dataStore.notifications.filter(item => !item.read).length > 0 ? 'primary' : null"
|
||||
|
||||
/>
|
||||
</UButton>
|
||||
|
||||
<USlideover
|
||||
v-model="showUserMenu"
|
||||
>
|
||||
<UCard
|
||||
class="h-full"
|
||||
>
|
||||
|
||||
<div v-if="dataStore.getOwnProfile.tenants.length > 1">
|
||||
<UDivider
|
||||
class="my-3"
|
||||
label="Tenant"
|
||||
/>
|
||||
|
||||
<USelectMenu
|
||||
:options="dataStore.getOwnProfile.tenants"
|
||||
option-attribute="name"
|
||||
value-attribute="id"
|
||||
v-model="dataStore.currentTenant"
|
||||
@change="dataStore.changeTenant()"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<UDivider
|
||||
class="my-3"
|
||||
label="Menü"
|
||||
/>
|
||||
|
||||
<UVerticalNavigation
|
||||
:links="userMenuItems"
|
||||
/>
|
||||
|
||||
<!– <UDivider
|
||||
class="my-3"
|
||||
label="Benachrichtigungen"
|
||||
/>
|
||||
|
||||
<UAlert
|
||||
class="mb-3"
|
||||
v-for="(notification) in dataStore.notifications"
|
||||
:color="!notification.read ? 'primary' : 'white'"
|
||||
:variant="!notification.read ? 'outline' : 'soft'"
|
||||
:description="notification.text"
|
||||
:title="notification.title"
|
||||
:close-button="!notification.read ? { icon: 'i-heroicons-x-mark-20-solid', color: 'gray', variant: 'link', padded: false } : null"
|
||||
@close="async () => {
|
||||
const {error} = await supabase.from('notifications').update({read:true}).eq('id', notification.id)
|
||||
if(error) console.log(error)
|
||||
dataStore.fetchNotifications()
|
||||
}"
|
||||
/>–>
|
||||
-->
|
||||
<div class="flex-1" />
|
||||
|
||||
<UDashboardSidebarLinks :links="footerLinks" />
|
||||
|
||||
<UDivider class="sticky bottom-0" />
|
||||
|
||||
<template #footer>
|
||||
<InputGroup>
|
||||
<UButton
|
||||
:icon="!isLight ? 'i-heroicons-moon-20-solid' : 'i-heroicons-sun-20-solid'"
|
||||
color="white"
|
||||
variant="outline"
|
||||
aria-label="Theme"
|
||||
@click="isLight = !isLight"
|
||||
/>
|
||||
<UButton
|
||||
color="rose"
|
||||
variant="outline"
|
||||
@click="async () => {
|
||||
showUserMenu = false
|
||||
await supabase.auth.signOut()
|
||||
await dataStore.clearStore()
|
||||
await router.push('/login')
|
||||
|
||||
}"
|
||||
>
|
||||
Ausloggen
|
||||
</UButton>
|
||||
</InputGroup>
|
||||
|
||||
<!-- ~/components/UserDropdown.vue -->
|
||||
<UserDropdown />
|
||||
</template>
|
||||
</UDashboardSidebar>
|
||||
</UDashboardPanel>
|
||||
<slot />
|
||||
|
||||
</UCard>
|
||||
</USlideover>
|
||||
<!-- ~/components/HelpSlideover.vue -->
|
||||
<HelpSlideover />
|
||||
<!-- ~/components/NotificationsSlideover.vue -->
|
||||
<NotificationsSlideover />
|
||||
|
||||
</template>
|
||||
</UHeader>
|
||||
<UDivider />-->
|
||||
<div class="ml-2 mt-3" id="menuLeft">
|
||||
|
||||
<UVerticalNavigation
|
||||
:links="links"
|
||||
>
|
||||
<template #avatar="{link}">
|
||||
<UAvatar
|
||||
v-if="link.avatar"
|
||||
v-bind="link.avatar"
|
||||
|
||||
/>
|
||||
</template>
|
||||
</UVerticalNavigation>
|
||||
</div>
|
||||
|
||||
<div class="pl-3 pr-3 mt-3" id="contentContainer">
|
||||
<slot id="content"/>
|
||||
</div>
|
||||
</div>
|
||||
<ClientOnly>
|
||||
<LazyUDashboardSearch :groups="groups" />
|
||||
</ClientOnly>
|
||||
</UDashboardLayout>
|
||||
<div
|
||||
v-else
|
||||
class="flex-col mx-auto my-auto mt-10 w-3/4"
|
||||
@@ -466,31 +542,9 @@ const links = [[{
|
||||
/>
|
||||
<UProgress animation="carousel"/>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
#menuLeft {
|
||||
height: 95vh;
|
||||
width: 20vw;
|
||||
overflow-y: scroll;
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
#menuLeft::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#contentContainer {
|
||||
width: 77vw;
|
||||
height: 95vh;
|
||||
overflow-y: scroll;
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
#contentContainer::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -287,19 +287,28 @@ const calendarOptionsTimeline = reactive({
|
||||
|
||||
</UModal>
|
||||
|
||||
<div v-if="mode === 'grid'">
|
||||
<UDashboardPage>
|
||||
<UDashboardPanel grow>
|
||||
<UDashboardNavbar :title="currentItem ? currentItem.name : ''">
|
||||
<template #right>
|
||||
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
|
||||
|
||||
<div v-if="mode === 'grid'" class="p-5">
|
||||
<FullCalendar
|
||||
:options="calendarOptionsGrid"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="mode === 'timeline'">
|
||||
<div v-else-if="mode === 'timeline'" class="p-5">
|
||||
<FullCalendar
|
||||
:options="calendarOptionsTimeline"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</UDashboardPanel>
|
||||
</UDashboardPage>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,26 +1,45 @@
|
||||
<template>
|
||||
<div class="cardHolder">
|
||||
<div class="card">
|
||||
<h1 class="text-center text-4xl">Aufgaben</h1>
|
||||
<p class="text-center text-6xl mt-5">{{openTasks}}</p>
|
||||
</div><div class="card">
|
||||
<UDashboardPage>
|
||||
<UDashboardPanel grow>
|
||||
<UDashboardNavbar title="Home">
|
||||
<template #right>
|
||||
<UTooltip text="Notifications" :shortcuts="['N']">
|
||||
<UButton color="gray" variant="ghost" square @click="isNotificationsSlideoverOpen = true">
|
||||
<UChip color="red" inset>
|
||||
<UIcon name="i-heroicons-bell" class="w-5 h-5" />
|
||||
</UChip>
|
||||
</UButton>
|
||||
</UTooltip>
|
||||
|
||||
</div><div class="card">
|
||||
<UDropdown :items="items">
|
||||
<UButton icon="i-heroicons-plus" size="md" class="ml-1.5 rounded-full" />
|
||||
</UDropdown>
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
|
||||
</div><div class="card">
|
||||
<!-- <UDashboardToolbar>
|
||||
<template #left>
|
||||
<!– ~/components/home/HomeDateRangePicker.vue –>
|
||||
<!– <HomeDateRangePicker v-model="range" class="-ml-2.5" />–>
|
||||
|
||||
</div><div class="card">
|
||||
<!– ~/components/home/HomePeriodSelect.vue –>
|
||||
<!– <HomePeriodSelect v-model="period" :range="range" />–>
|
||||
</template>
|
||||
</UDashboardToolbar>-->
|
||||
|
||||
</div><div class="card">
|
||||
<UDashboardPanelContent>
|
||||
<!-- ~/components/home/HomeChart.vue -->
|
||||
<!-- <HomeChart :period="period" :range="range" />
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
</div>
|
||||
<div class="grid lg:grid-cols-2 lg:items-start gap-8 mt-8">
|
||||
<!– ~/components/home/HomeSales.vue –>
|
||||
<HomeSales />
|
||||
<!– ~/components/home/HomeCountries.vue –>
|
||||
<HomeCountries />
|
||||
</div>-->
|
||||
</UDashboardPanelContent>
|
||||
</UDashboardPanel>
|
||||
</UDashboardPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -28,6 +47,18 @@ definePageMeta({
|
||||
middleware: "auth"
|
||||
})
|
||||
|
||||
const { isNotificationsSlideoverOpen } = useDashboard()
|
||||
const items = [[{
|
||||
label: 'Aufgabe',
|
||||
icon: 'i-heroicons-paper-airplane',
|
||||
to: '/tasks/create'
|
||||
}, {
|
||||
label: 'Kunde',
|
||||
icon: 'i-heroicons-user-plus',
|
||||
to: '/customers/create'
|
||||
}]]
|
||||
|
||||
|
||||
const {getOpenTasksCount} = useDataStore()
|
||||
const openTasks = getOpenTasksCount
|
||||
|
||||
@@ -38,7 +69,7 @@ const user = useSupabaseUser()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.cardHolder {
|
||||
/*.cardHolder {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
@@ -49,5 +80,5 @@ const user = useSupabaseUser()
|
||||
width: 22vw;
|
||||
height: 40vh;
|
||||
border: 1px solid white
|
||||
}
|
||||
}*/
|
||||
</style>
|
||||
@@ -122,6 +122,28 @@ const calendarOptionsTimeline = reactive({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDashboardPage>
|
||||
<UDashboardPanel grow>
|
||||
<UDashboardNavbar :title="currentItem ? currentItem.name : ''">
|
||||
<template #right>
|
||||
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
<div v-if="viewport.isLessThan('tablet')">
|
||||
<FullCalendar
|
||||
:options="calendarOptionsList"
|
||||
/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<FullCalendar
|
||||
:options="calendarOptionsTimeline"
|
||||
/>
|
||||
</div>
|
||||
</UDashboardPanel>
|
||||
</UDashboardPage>
|
||||
|
||||
|
||||
|
||||
<div>
|
||||
<UModal
|
||||
v-model="openNewEventModal"
|
||||
@@ -229,16 +251,7 @@ const calendarOptionsTimeline = reactive({
|
||||
|
||||
|
||||
</UModal>
|
||||
<div v-if="viewport.isLessThan('tablet')">
|
||||
<FullCalendar
|
||||
:options="calendarOptionsList"
|
||||
/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<FullCalendar
|
||||
:options="calendarOptionsTimeline"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup>
|
||||
import dayjs from "dayjs";
|
||||
|
||||
definePageMeta({
|
||||
middleware: "auth"
|
||||
})
|
||||
@@ -75,7 +77,177 @@ setupPage()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1
|
||||
<UDashboardPage>
|
||||
<UDashboardPanel grow>
|
||||
<UDashboardNavbar :title="currentItem ? currentItem.name : ''">
|
||||
<template #right>
|
||||
<UButton
|
||||
v-if="mode === 'edit'"
|
||||
@click="dataStore.updateItem('tasks',itemInfo)"
|
||||
>
|
||||
Speichern
|
||||
</UButton>
|
||||
<UButton
|
||||
v-else-if="mode === 'create'"
|
||||
@click="dataStore.createNewItem('tasks',itemInfo)"
|
||||
>
|
||||
Erstellen
|
||||
</UButton>
|
||||
<UButton
|
||||
@click="cancelEditorCreate"
|
||||
color="red"
|
||||
class="ml-2"
|
||||
v-if="mode === 'edit' || mode === 'create'"
|
||||
>
|
||||
Abbrechen
|
||||
</UButton>
|
||||
<UButton
|
||||
v-if="mode === 'show' && currentItem.id"
|
||||
@click="editItem"
|
||||
>
|
||||
Bearbeiten
|
||||
</UButton>
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
|
||||
<!-- <UDashboardToolbar>
|
||||
<template #left>
|
||||
<!– <UButton @click="router.push(`/tasks/create`)">+ Aufgabe</UButton>
|
||||
|
||||
<UInput
|
||||
v-model="searchString"
|
||||
placeholder="Suche..."
|
||||
/>–>
|
||||
|
||||
<UCheckbox
|
||||
label="Erledigte Anzeigen"
|
||||
v-model="showDone"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #right>
|
||||
<USelectMenu
|
||||
v-model="selectedColumns"
|
||||
icon="i-heroicons-adjustments-horizontal-solid"
|
||||
:options="templateColumns"
|
||||
multiple
|
||||
class="hidden lg:block"
|
||||
>
|
||||
<template #label>
|
||||
Spalten
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</template>
|
||||
</UDashboardToolbar>-->
|
||||
|
||||
|
||||
<UTabs
|
||||
:items="[{label: 'Informationen'},{label: 'Logbuch'}]"
|
||||
v-if="currentItem && mode === 'show'"
|
||||
class="p-5"
|
||||
>
|
||||
<template #item="{item}">
|
||||
<UCard class="mt-5">
|
||||
<div v-if="item.label === 'Informationen'">
|
||||
|
||||
|
||||
<div class="truncate">
|
||||
<p>Kategorie: {{currentItem.categorie}}</p>
|
||||
<p v-if="currentItem.project">Projekt: <nuxt-link :to="`/projects/show/${currentItem.project}`">{{dataStore.getProjectById(currentItem.project).name}}</nuxt-link></p>
|
||||
<p>Beschreibung: {{currentItem.description}}</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- TODO: Logbuch Tasks -->
|
||||
</UCard>
|
||||
</template>
|
||||
</UTabs>
|
||||
|
||||
<UForm v-else-if="mode === 'edit' || mode === 'create' " class="p-5">
|
||||
<UFormGroup
|
||||
label="Name:"
|
||||
>
|
||||
<UInput
|
||||
v-model="itemInfo.name"
|
||||
/>
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup
|
||||
label="Kategorie:"
|
||||
>
|
||||
<USelectMenu
|
||||
v-model="itemInfo.categorie"
|
||||
:options="categories"
|
||||
/>
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup
|
||||
label="Benutzer:"
|
||||
>
|
||||
<USelectMenu
|
||||
v-model="itemInfo.user"
|
||||
:options="dataStore.profiles"
|
||||
option-attribute="fullName"
|
||||
value-attribute="id"
|
||||
searchable-placeholder="Suche..."
|
||||
searchable
|
||||
:search-attributes="['fullName']"
|
||||
>
|
||||
<template #label>
|
||||
{{dataStore.getProfileById(itemInfo.user) ? dataStore.getProfileById(itemInfo.user).fullName : "Kein Benutzer ausgewählt"}}
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</UFormGroup>
|
||||
<UFormGroup
|
||||
label="Projekt:"
|
||||
>
|
||||
<USelectMenu
|
||||
v-model="itemInfo.project"
|
||||
:options="dataStore.projects"
|
||||
option-attribute="name"
|
||||
value-attribute="id"
|
||||
searchable-placeholder="Suche..."
|
||||
searchable
|
||||
:search-attributes="['name']"
|
||||
>
|
||||
<template #label>
|
||||
{{dataStore.getProjectById(itemInfo.project) ? dataStore.getProjectById(itemInfo.project).name : "Kein Projekt ausgewählt"}}
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</UFormGroup>
|
||||
<UFormGroup
|
||||
label="Objekt:"
|
||||
>
|
||||
<USelectMenu
|
||||
v-model="itemInfo.plant"
|
||||
:options="dataStore.plants"
|
||||
option-attribute="name"
|
||||
value-attribute="id"
|
||||
searchable-placeholder="Suche..."
|
||||
searchable
|
||||
:search-attributes="['name']"
|
||||
>
|
||||
<template #label>
|
||||
{{dataStore.getPlantById(itemInfo.plant) ? dataStore.getPlantById(itemInfo.plant).name : "Kein Objekt ausgewählt"}}
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup
|
||||
label="Beschreibung:"
|
||||
>
|
||||
<UTextarea
|
||||
v-model="itemInfo.description"
|
||||
/>
|
||||
</UFormGroup>
|
||||
</UForm>
|
||||
|
||||
|
||||
|
||||
</UDashboardPanel>
|
||||
</UDashboardPage>
|
||||
|
||||
<!-- <h1
|
||||
class="mb-3 truncate font-bold text-2xl"
|
||||
v-if="currentItem"
|
||||
>Aufgabe: {{currentItem.name}}</h1>
|
||||
@@ -108,7 +280,7 @@ setupPage()
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- TODO: Logbuch Tasks -->
|
||||
<!– TODO: Logbuch Tasks –>
|
||||
</UCard>
|
||||
</template>
|
||||
</UTabs>
|
||||
@@ -220,7 +392,7 @@ setupPage()
|
||||
</UButton>
|
||||
</template>
|
||||
|
||||
</UCard>
|
||||
</UCard>-->
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,25 +1,64 @@
|
||||
<template>
|
||||
<UDashboardPage>
|
||||
<UDashboardPanel grow>
|
||||
<UDashboardNavbar title="Aufgaben" :badge="filteredRows.length">
|
||||
<template #right>
|
||||
<UInput
|
||||
ref="input"
|
||||
v-model="searchString"
|
||||
icon="i-heroicons-funnel"
|
||||
autocomplete="off"
|
||||
placeholder="Suche..."
|
||||
class="hidden lg:block"
|
||||
@keydown.esc="$event.target.blur()"
|
||||
>
|
||||
<template #trailing>
|
||||
<UKbd value="/" />
|
||||
</template>
|
||||
</UInput>
|
||||
|
||||
<div>
|
||||
<Toolbar>
|
||||
<UButton @click="router.push(`/tasks/create`)">+ Aufgabe</UButton>
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
|
||||
<UDashboardToolbar>
|
||||
<template #left>
|
||||
<!-- <UButton @click="router.push(`/tasks/create`)">+ Aufgabe</UButton>
|
||||
|
||||
<UInput
|
||||
v-model="searchString"
|
||||
placeholder="Suche..."
|
||||
/>
|
||||
/>-->
|
||||
|
||||
<UCheckbox
|
||||
label="Erledigte Anzeigen"
|
||||
v-model="showDone"
|
||||
/>
|
||||
</Toolbar>
|
||||
<div class="table">
|
||||
</template>
|
||||
|
||||
<template #right>
|
||||
<USelectMenu
|
||||
v-model="selectedColumns"
|
||||
icon="i-heroicons-adjustments-horizontal-solid"
|
||||
:options="templateColumns"
|
||||
multiple
|
||||
class="hidden lg:block"
|
||||
>
|
||||
<template #label>
|
||||
Spalten
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</template>
|
||||
</UDashboardToolbar>
|
||||
|
||||
<UTable
|
||||
v-model:sort="sort"
|
||||
:rows="filteredRows"
|
||||
:columns="columns"
|
||||
@select="selectItem"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
||||
sort-mode="manual"
|
||||
class="w-full"
|
||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||
@select="(i) => router.push(`/tasks/show/${i.id}`) "
|
||||
>
|
||||
<template #finish-data="{row}">
|
||||
<UButton
|
||||
@@ -44,27 +83,37 @@
|
||||
{{dataStore.getPlantById(row.plant) ? dataStore.getPlantById(row.plant).name : "" }}
|
||||
</template>
|
||||
</UTable>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</UDashboardPanel>
|
||||
</UDashboardPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script lang="ts" setup>
|
||||
import dayjs from "dayjs";
|
||||
|
||||
definePageMeta({
|
||||
middleware: "auth"
|
||||
})
|
||||
const input = ref<{ input: HTMLInputElement }>()
|
||||
|
||||
const dataStore = useDataStore()
|
||||
const router = useRouter()
|
||||
const mode = ref("show")
|
||||
|
||||
const columns = [
|
||||
|
||||
{
|
||||
defineShortcuts({
|
||||
'/': () => {
|
||||
input.value?.input?.focus()
|
||||
},
|
||||
'+': () => {
|
||||
router.push("/tasks/create")
|
||||
}
|
||||
})
|
||||
|
||||
const templateColumns = [
|
||||
|
||||
/*{
|
||||
key:"finish"
|
||||
},{
|
||||
},*/{
|
||||
key: "created_at",
|
||||
label: "Erstellt am:",
|
||||
sortable: true
|
||||
@@ -95,6 +144,10 @@ const columns = [
|
||||
}
|
||||
]
|
||||
|
||||
const selectedColumns = ref(templateColumns)
|
||||
const columns = computed(() => templateColumns.filter((column) => selectedColumns.value.includes(column)))
|
||||
|
||||
|
||||
const searchString = ref('')
|
||||
const showDone = ref(false)
|
||||
|
||||
|
||||
11
spaces/pages/test.vue
Normal file
11
spaces/pages/test.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
test
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -795,7 +795,7 @@ export const useDataStore = defineStore('data', () => {
|
||||
|
||||
const getResources = computed(() => {
|
||||
return [
|
||||
...profiles.value.map(profile => {
|
||||
...profiles.value.filter(i => i.tenant === currentTenant.value).map(profile => {
|
||||
return {
|
||||
type: 'Mitarbeiter',
|
||||
title: profile.fullName,
|
||||
|
||||
Reference in New Issue
Block a user