feat: festival/event model frontend + topbar activeren

- Events lijst: card grid met festival/serie chips
- Festival detail: programmaonderdelen grid
- CreateSubEventDialog voor sub-events binnen festival
- EventTabsNav: breadcrumb terug naar festival
- Sessie A: festival-bewuste EventResource + children endpoint
- Topbar: zoekbalk, theme switcher, shortcuts, notificaties
- Schema v1.7 + BACKLOG.md toegevoegd
- 121 tests groen
This commit is contained in:
2026-04-08 10:06:47 +02:00
parent 6848bc2c49
commit c776331cf8
21 changed files with 1087 additions and 190 deletions

View File

@@ -5,6 +5,12 @@ 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()
@@ -31,6 +37,11 @@ const statusColor: Record<EventStatus, string> = {
closed: 'error',
}
const eventTypeColor: Record<string, string> = {
festival: 'purple',
series: 'info',
}
const dateFormatter = new Intl.DateTimeFormat('nl-NL', {
day: '2-digit',
month: '2-digit',
@@ -54,6 +65,13 @@ const activeTab = computed(() => {
const name = route.name as string
return tabs.find(t => name === t.route || name?.startsWith(`${t.route}-`))?.route ?? 'events-id'
})
const backRoute = computed(() => {
if (event.value?.is_sub_event && event.value.parent_event_id) {
return { name: 'events-id' as const, params: { id: event.value.parent_event_id } }
}
return { name: 'events' as const }
})
</script>
<template>
@@ -82,17 +100,41 @@ const activeTab = 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">
<VBtn
icon="tabler-arrow-left"
variant="text"
:to="{ name: 'events' }"
:to="backRoute"
/>
<h4 class="text-h4">
{{ event.name }}
</h4>
<VChip
v-if="event.event_type === 'festival' || event.event_type === 'series'"
:color="eventTypeColor[event.event_type]"
size="small"
variant="tonal"
>
{{ event.event_type_label ?? (event.event_type === 'festival' ? 'Festival' : 'Serie') }}
</VChip>
<VChip
:color="statusColor[event.status]"
size="small"
@@ -111,8 +153,9 @@ const activeTab = computed(() => {
</VBtn>
</div>
<!-- Horizontal tabs -->
<!-- Horizontal tabs (hidden for festival containers) -->
<VTabs
v-if="!hideTabs"
:model-value="activeTab"
class="mb-6"
>