feat: frontend fase 2 sessies 1-3

- Member management pagina + invite flow
- Persons module met filters, KPI tiles, detail panel
- Event horizontale tabs navigatie (EventTabsNav component)
- Route conflict opgelost
- OrganisationSwitcher verbeterd (collapsed staat WIP)
This commit is contained in:
2026-04-08 03:15:45 +02:00
parent 230e11cc8d
commit 6f69b30fb6
19 changed files with 1722 additions and 311 deletions

View File

@@ -1,64 +1,111 @@
<script setup lang="ts">
import { layoutConfig } from '@layouts'
import { useLayoutConfigStore } from '@layouts/stores/config'
import { useAuthStore } from '@/stores/useAuthStore'
const authStore = useAuthStore()
const configStore = useLayoutConfigStore()
const hideTitleAndBadge = configStore.isVerticalNavMini()
const hasMultipleOrgs = computed(() => authStore.organisations.length > 1)
const currentOrgName = computed(() => authStore.currentOrganisation?.name ?? 'Geen organisatie')
const organisations = computed(() => authStore.organisations)
const activeOrg = computed(() => authStore.currentOrganisation)
const hasMultiple = computed(() => organisations.value.length > 1)
const activeOrgName = computed(() => {
const name = activeOrg.value?.name ?? 'Geen organisatie'
return name.length > 18 ? `${name.slice(0, 18)}` : name
})
const isMenuOpen = ref(false)
const roleColor = (role: string) => ({
org_admin: 'primary',
org_member: 'info',
event_manager: 'success',
staff_coordinator: 'warning',
volunteer_coordinator: 'secondary',
}[role] ?? 'default')
function toggleMenu() {
if (hasMultiple.value)
isMenuOpen.value = !isMenuOpen.value
}
function selectOrg(id: string) {
authStore.setActiveOrganisation(id)
isMenuOpen.value = false
}
</script>
<template>
<div class="organisation-switcher mx-4 mb-2">
<!-- Single org: just show the name -->
<div
v-if="!hasMultipleOrgs"
class="d-flex align-center gap-x-2 pa-2"
<li
class="nav-link"
style="margin-block-end: 8px;"
>
<VMenu
v-model="isMenuOpen"
:location="hideTitleAndBadge ? 'end' : 'bottom'"
:offset="hideTitleAndBadge ? 8 : 0"
:width="hideTitleAndBadge ? undefined : 260"
:min-width="hideTitleAndBadge ? 220 : undefined"
:disabled="!hasMultiple"
>
<VIcon
icon="tabler-building"
size="20"
color="primary"
/>
<span class="text-body-2 font-weight-medium text-truncate">
{{ currentOrgName }}
</span>
</div>
<!-- Multiple orgs: dropdown -->
<VBtn
v-else
variant="tonal"
color="primary"
block
class="text-none justify-start"
prepend-icon="tabler-building"
>
<span class="text-truncate">{{ currentOrgName }}</span>
<VMenu
activator="parent"
width="250"
location="bottom start"
>
<VList density="compact">
<VListItem
v-for="org in authStore.organisations"
:key="org.id"
:active="org.id === authStore.currentOrganisation?.id"
@click="authStore.setActiveOrganisation(org.id)"
>
<VListItemTitle>{{ org.name }}</VListItemTitle>
<template #append>
<VChip
size="x-small"
variant="text"
>
{{ org.role }}
</VChip>
</template>
</VListItem>
</VList>
</VMenu>
</VBtn>
</div>
<template #activator="{ props: menuProps }">
<a
v-bind="hasMultiple ? menuProps : undefined"
:class="{ 'cursor-pointer': hasMultiple }"
>
<Component
:is="layoutConfig.app.iconRenderer || 'div'"
v-bind="{ icon: 'tabler-building' }"
class="nav-item-icon"
/>
<TransitionGroup name="transition-slide-x">
<span
v-show="!hideTitleAndBadge"
key="title"
class="nav-item-title"
>
{{ activeOrgName }}
</span>
<Component
v-if="hasMultiple"
v-show="!hideTitleAndBadge"
:is="layoutConfig.app.iconRenderer || 'div'"
v-bind="{ icon: 'tabler-chevron-down' }"
key="chevron"
class="nav-item-badge"
:style="{
transition: 'transform 0.2s ease',
transform: isMenuOpen ? 'rotate(180deg)' : 'rotate(0deg)',
'font-size': '18px',
'margin-inline-start': 'auto',
}"
/>
</TransitionGroup>
</a>
</template>
<VList density="compact">
<VListItem
v-for="org in organisations"
:key="org.id"
:active="org.id === activeOrg?.id"
active-color="primary"
@click="selectOrg(org.id)"
>
<VListItemTitle>{{ org.name }}</VListItemTitle>
<template #append>
<VChip
:color="roleColor(org.role)"
size="x-small"
variant="tonal"
label
>
{{ org.role }}
</VChip>
</template>
</VListItem>
</VList>
</VMenu>
</li>
</template>