chore(f3.5): AppShell mockup parity — sidebar, topbar, plugin fixes #26

Merged
bert.hausmans merged 8 commits from chore/f3.5-appshell-mockup-parity into main 2026-05-14 13:38:52 +02:00
Showing only changes of commit 3df55b4d1c - Show all commits

View File

@@ -16,11 +16,13 @@
// PrimeVue Drawer overlay. Content area renders the default slot
// (a RouterView from the wrapping layout file).
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { computed, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import Drawer from 'primevue/drawer'
import Button from 'primevue/button'
import { useToast } from 'primevue/usetoast'
import Icon from '@/components/Icon.vue'
import { useAuthStore } from '@/stores/useAuthStore'
interface NavHeading {
heading: string
@@ -37,11 +39,14 @@ interface Props {
title?: string
}
withDefaults(defineProps<Props>(), {
const props = withDefaults(defineProps<Props>(), {
title: 'Crewli',
})
const router = useRouter()
const route = useRoute()
const toast = useToast()
const authStore = useAuthStore()
const mobileNavOpen = ref(false)
@@ -53,6 +58,54 @@ function navigate(item: NavLink) {
mobileNavOpen.value = false
router.push(item.to)
}
// Breadcrumb: "Organisation / Page title". Page title resolves from
// route.meta.title → matching navItems entry → humanized route name.
// Org name is omitted on portal / pre-org users (currentOrganisation
// is null) so the breadcrumb reads as just the page title there.
const orgName = computed(() => authStore.currentOrganisation?.name ?? '')
const pageTitle = computed(() => {
const metaTitle = route.meta.title
if (typeof metaTitle === 'string' && metaTitle.length > 0)
return metaTitle
const match = props.navItems.find(
(item): item is NavLink => 'to' in item && item.to.name === route.name,
)
if (match)
return match.title
const raw = String(route.name ?? '')
if (!raw)
return ''
return raw.charAt(0).toUpperCase() + raw.slice(1).replace(/-/g, ' ')
})
function onNotificationsClick() {
toast.add({
severity: 'info',
summary: 'Notificaties',
detail: 'Notificaties komen binnenkort beschikbaar.',
life: 3000,
})
}
// TODO(docs-url): https://docs.crewli.app currently serves with a TLS
// cert that does not cover the host (ERR_TLS_CERT_ALTNAME_INVALID),
// so a browser hit shows a security warning instead of the docs.
// When the cert is fixed, switch this handler to:
// window.open('https://docs.crewli.app', '_blank', 'noopener,noreferrer')
function onHelpClick() {
toast.add({
severity: 'info',
summary: 'Help',
detail: 'Documentatie komt binnenkort beschikbaar.',
life: 3000,
})
}
</script>
<template>
@@ -149,6 +202,49 @@ function navigate(item: NavLink) {
/>
</Button>
<span class="text-base font-medium text-surface-700 lg:hidden">{{ title }}</span>
<nav
class="hidden items-center gap-2 text-sm lg:flex"
aria-label="Kruimelpad"
>
<span
v-if="orgName"
class="text-surface-500"
>{{ orgName }}</span>
<Icon
v-if="orgName && pageTitle"
name="tabler-chevron-right"
size="14"
class="text-surface-400"
/>
<span class="font-medium text-surface-900">{{ pageTitle }}</span>
</nav>
</div>
<div class="flex items-center gap-1">
<Button
severity="secondary"
text
rounded
aria-label="Notificaties"
@click="onNotificationsClick"
>
<Icon
name="tabler-bell"
size="22"
/>
</Button>
<Button
severity="secondary"
text
rounded
aria-label="Help"
@click="onHelpClick"
>
<Icon
name="tabler-help"
size="22"
/>
</Button>
</div>
</header>