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

@@ -0,0 +1,140 @@
<script setup lang="ts">
import { useEventDetail } from '@/composables/api/useEvents'
import { useAuthStore } from '@/stores/useAuthStore'
import { useOrganisationStore } from '@/stores/useOrganisationStore'
import EditEventDialog from '@/components/events/EditEventDialog.vue'
import type { EventStatus } from '@/types/event'
const route = useRoute()
const authStore = useAuthStore()
const orgStore = useOrganisationStore()
const orgId = computed(() => authStore.currentOrganisation?.id ?? '')
const eventId = computed(() => String((route.params as { id: string }).id))
const { data: event, isLoading, isError, refetch } = useEventDetail(orgId, eventId)
// Set active event in store
watch(eventId, (id) => {
if (id) orgStore.setActiveEvent(id)
}, { immediate: true })
const isEditDialogOpen = ref(false)
const statusColor: Record<EventStatus, string> = {
draft: 'default',
published: 'info',
registration_open: 'cyan',
buildup: 'warning',
showday: 'success',
teardown: 'warning',
closed: 'error',
}
const dateFormatter = new Intl.DateTimeFormat('nl-NL', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
})
function formatDate(iso: string) {
return dateFormatter.format(new Date(iso))
}
const tabs = [
{ label: 'Overzicht', icon: 'tabler-layout-dashboard', route: 'events-id' },
{ label: 'Personen', icon: 'tabler-users', route: 'events-id-persons' },
{ label: 'Secties & Shifts', icon: 'tabler-layout-grid', route: 'events-id-sections' },
{ label: 'Artiesten', icon: 'tabler-music', route: 'events-id-artists' },
{ label: 'Briefings', icon: 'tabler-mail', route: 'events-id-briefings' },
{ label: 'Instellingen', icon: 'tabler-settings', route: 'events-id-settings' },
]
const activeTab = computed(() => {
const name = route.name as string
return tabs.find(t => name === t.route || name?.startsWith(`${t.route}-`))?.route ?? 'events-id'
})
</script>
<template>
<div>
<!-- Loading -->
<VSkeletonLoader
v-if="isLoading"
type="card"
/>
<!-- Error -->
<VAlert
v-else-if="isError"
type="error"
class="mb-4"
>
Kon evenement niet laden.
<template #append>
<VBtn
variant="text"
@click="refetch()"
>
Opnieuw proberen
</VBtn>
</template>
</VAlert>
<template v-else-if="event">
<!-- Header -->
<div class="d-flex justify-space-between align-center mb-6">
<div class="d-flex align-center gap-x-3">
<VBtn
icon="tabler-arrow-left"
variant="text"
:to="{ name: 'events' }"
/>
<h4 class="text-h4">
{{ event.name }}
</h4>
<VChip
:color="statusColor[event.status]"
size="small"
>
{{ event.status }}
</VChip>
<span class="text-body-1 text-disabled">
{{ formatDate(event.start_date) }} {{ formatDate(event.end_date) }}
</span>
</div>
<VBtn
prepend-icon="tabler-edit"
@click="isEditDialogOpen = true"
>
Bewerken
</VBtn>
</div>
<!-- Horizontal tabs -->
<VTabs
:model-value="activeTab"
class="mb-6"
>
<VTab
v-for="tab in tabs"
:key="tab.route"
:value="tab.route"
:prepend-icon="tab.icon"
:to="{ name: tab.route, params: { id: eventId } }"
>
{{ tab.label }}
</VTab>
</VTabs>
<!-- Tab content -->
<slot :event="event" />
<EditEventDialog
v-model="isEditDialogOpen"
:event="event"
:org-id="orgId"
/>
</template>
</div>
</template>