feat: festival/series model with sub-events, cross-event sections, tab navigation, SectionsShiftsPanel extraction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,16 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { useEventDetail } from '@/composables/api/useEvents'
|
||||
import { useEventDetail, useEventChildren } from '@/composables/api/useEvents'
|
||||
import { dutchPlural } from '@/lib/dutch-plural'
|
||||
import { useAuthStore } from '@/stores/useAuthStore'
|
||||
import { useOrganisationStore } from '@/stores/useOrganisationStore'
|
||||
import EditEventDialog from '@/components/events/EditEventDialog.vue'
|
||||
import type { EventStatus } from '@/types/event'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
hideTabs?: boolean
|
||||
}>(), {
|
||||
hideTabs: false,
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
const authStore = useAuthStore()
|
||||
const orgStore = useOrganisationStore()
|
||||
@@ -20,6 +15,9 @@ const eventId = computed(() => String((route.params as { id: string }).id))
|
||||
|
||||
const { data: event, isLoading, isError, refetch } = useEventDetail(orgId, eventId)
|
||||
|
||||
// Children count for programmaonderdelen badge — only for festivals
|
||||
const { data: children } = useEventChildren(orgId, eventId)
|
||||
|
||||
// Set active event in store
|
||||
watch(eventId, (id) => {
|
||||
if (id) orgStore.setActiveEvent(id)
|
||||
@@ -52,7 +50,7 @@ function formatDate(iso: string) {
|
||||
return dateFormatter.format(new Date(iso))
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
const baseTabs = [
|
||||
{ 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' },
|
||||
@@ -61,9 +59,38 @@ const tabs = [
|
||||
{ label: 'Instellingen', icon: 'tabler-settings', route: 'events-id-settings' },
|
||||
]
|
||||
|
||||
const programmaonderdelenLabel = computed(() => {
|
||||
const label = event.value?.sub_event_label
|
||||
? dutchPlural(event.value.sub_event_label)
|
||||
: 'Programmaonderdelen'
|
||||
const count = children.value?.length ?? event.value?.children_count ?? 0
|
||||
return `${label} (${count})`
|
||||
})
|
||||
|
||||
const tabs = computed(() => {
|
||||
if (!event.value?.is_festival) return baseTabs
|
||||
|
||||
// Festival tab order: Overzicht | Programmaonderdelen | Secties & Shifts | Personen | Artiesten | Briefings | Instellingen
|
||||
const festivalTab = {
|
||||
label: programmaonderdelenLabel.value,
|
||||
icon: 'tabler-calendar-event',
|
||||
route: 'events-id-programmaonderdelen',
|
||||
}
|
||||
|
||||
return [
|
||||
baseTabs[0], // Overzicht
|
||||
festivalTab,
|
||||
baseTabs[2], // Secties & Shifts
|
||||
baseTabs[1], // Personen
|
||||
baseTabs[3], // Artiesten
|
||||
baseTabs[4], // Briefings
|
||||
baseTabs[5], // Instellingen
|
||||
]
|
||||
})
|
||||
|
||||
const activeTab = computed(() => {
|
||||
const name = route.name as string
|
||||
return tabs.find(t => name === t.route || name?.startsWith(`${t.route}-`))?.route ?? 'events-id'
|
||||
return tabs.value.find(t => name === t.route || name?.startsWith(`${t.route}-`))?.route ?? 'events-id'
|
||||
})
|
||||
|
||||
const backRoute = computed(() => {
|
||||
@@ -100,22 +127,6 @@ const backRoute = computed(() => {
|
||||
</VAlert>
|
||||
|
||||
<template v-else-if="event">
|
||||
<!-- Sub-event breadcrumb -->
|
||||
<div
|
||||
v-if="event.is_sub_event && event.parent && event.parent_event_id"
|
||||
class="text-caption text-medium-emphasis mb-1"
|
||||
>
|
||||
<VIcon size="12">
|
||||
tabler-arrow-left
|
||||
</VIcon>
|
||||
<RouterLink
|
||||
:to="{ name: 'events-id', params: { id: event.parent_event_id } }"
|
||||
class="text-medium-emphasis"
|
||||
>
|
||||
{{ event.parent.name }}
|
||||
</RouterLink>
|
||||
</div>
|
||||
|
||||
<!-- Header -->
|
||||
<div class="d-flex justify-space-between align-center mb-6">
|
||||
<div class="d-flex align-center gap-x-3">
|
||||
@@ -125,6 +136,15 @@ const backRoute = computed(() => {
|
||||
:to="backRoute"
|
||||
/>
|
||||
<h4 class="text-h4">
|
||||
<template v-if="event.is_sub_event && event.parent && event.parent_event_id">
|
||||
<RouterLink
|
||||
:to="{ name: 'events-id', params: { id: event.parent_event_id } }"
|
||||
class="text-medium-emphasis text-decoration-none"
|
||||
>
|
||||
{{ event.parent.name }}
|
||||
</RouterLink>
|
||||
<span class="text-medium-emphasis mx-1">»</span>
|
||||
</template>
|
||||
{{ event.name }}
|
||||
</h4>
|
||||
<VChip
|
||||
@@ -153,9 +173,8 @@ const backRoute = computed(() => {
|
||||
</VBtn>
|
||||
</div>
|
||||
|
||||
<!-- Horizontal tabs (hidden for festival containers) -->
|
||||
<!-- Horizontal tabs -->
|
||||
<VTabs
|
||||
v-if="!hideTabs"
|
||||
:model-value="activeTab"
|
||||
class="mb-6"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user