refactor(app): event header status menu and volunteer share dialog
Replace separate status chips/buttons with one status dropdown next to edit, move dates under the title, add share dialog for registration URL, and remove RegistrationLinkCard. Made-with: Cursor
This commit is contained in:
1
apps/app/components.d.ts
vendored
1
apps/app/components.d.ts
vendored
@@ -77,7 +77,6 @@ declare module 'vue' {
|
||||
RegistrationFieldCard: typeof import('./src/components/event/RegistrationFieldCard.vue')['default']
|
||||
RegistrationFieldFormDialog: typeof import('./src/components/event/RegistrationFieldFormDialog.vue')['default']
|
||||
RegistrationFieldTemplatesTab: typeof import('./src/components/organisation/RegistrationFieldTemplatesTab.vue')['default']
|
||||
RegistrationLinkCard: typeof import('./src/components/events/RegistrationLinkCard.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
ScrollToTop: typeof import('./src/@core/components/ScrollToTop.vue')['default']
|
||||
|
||||
@@ -7,7 +7,6 @@ import { useAuthStore } from '@/stores/useAuthStore'
|
||||
import { useNotificationStore } from '@/stores/useNotificationStore'
|
||||
import { useOrganisationStore } from '@/stores/useOrganisationStore'
|
||||
import EditEventDialog from '@/components/events/EditEventDialog.vue'
|
||||
import RegistrationLinkCard from '@/components/events/RegistrationLinkCard.vue'
|
||||
import type { EventStatus } from '@/types/event'
|
||||
|
||||
const route = useRoute()
|
||||
@@ -39,10 +38,6 @@ function transitionButtonColor(current: EventStatus, target: EventStatus): strin
|
||||
return 'primary'
|
||||
}
|
||||
|
||||
function transitionButtonVariant(current: EventStatus, target: EventStatus): 'flat' | 'outlined' {
|
||||
return transitionVisualKind(current, target) === 'backward' ? 'outlined' : 'flat'
|
||||
}
|
||||
|
||||
function openTransitionConfirm(target: EventStatus) {
|
||||
pendingTransitionStatus.value = target
|
||||
confirmTransitionOpen.value = true
|
||||
@@ -96,9 +91,23 @@ watch(eventId, (id) => {
|
||||
}, { immediate: true })
|
||||
|
||||
const isEditDialogOpen = ref(false)
|
||||
const statusMenuOpen = ref(false)
|
||||
const shareDialogOpen = ref(false)
|
||||
const linkCopied = ref(false)
|
||||
|
||||
const statusColor: Record<EventStatus, string> = {
|
||||
draft: 'default',
|
||||
const portalBaseUrl = import.meta.env.VITE_PORTAL_URL || 'https://portal.crewli.app'
|
||||
|
||||
const registrationUrl = computed(() =>
|
||||
event.value ? `${portalBaseUrl}/register/${event.value.slug}` : '',
|
||||
)
|
||||
|
||||
const isRegistrationOpen = computed(() => event.value?.status === 'registration_open')
|
||||
|
||||
const showVolunteerShare = computed(() => Boolean(event.value && !event.value.parent_event_id))
|
||||
|
||||
/** Kleuren voor statusknop / icoon (VBtn; concept = secondary i.p.v. chip-default). */
|
||||
const statusActionColor: Record<EventStatus, string> = {
|
||||
draft: 'secondary',
|
||||
published: 'info',
|
||||
registration_open: 'cyan',
|
||||
buildup: 'warning',
|
||||
@@ -122,6 +131,21 @@ function formatDate(iso: string) {
|
||||
return dateFormatter.format(new Date(iso))
|
||||
}
|
||||
|
||||
function onSelectTransition(target: EventStatus) {
|
||||
statusMenuOpen.value = false
|
||||
openTransitionConfirm(target)
|
||||
}
|
||||
|
||||
async function copyRegistrationLink() {
|
||||
if (!registrationUrl.value)
|
||||
return
|
||||
await navigator.clipboard.writeText(registrationUrl.value)
|
||||
linkCopied.value = true
|
||||
setTimeout(() => {
|
||||
linkCopied.value = false
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
const baseTabs = [
|
||||
{ label: 'Overzicht', icon: 'tabler-layout-dashboard', route: 'events-id' },
|
||||
{ label: 'Personen', icon: 'tabler-users', route: 'events-id-persons' },
|
||||
@@ -204,65 +228,106 @@ const backRoute = computed(() => {
|
||||
|
||||
<template v-else-if="event">
|
||||
<!-- Header -->
|
||||
<div class="d-flex flex-wrap justify-space-between align-center gap-y-4 mb-6">
|
||||
<div class="d-flex flex-wrap align-center gap-x-3 gap-y-2">
|
||||
<div class="d-flex flex-wrap justify-space-between align-start gap-y-4 gap-x-4 mb-6">
|
||||
<div class="d-flex align-start gap-x-3 min-w-0">
|
||||
<VBtn
|
||||
icon="tabler-arrow-left"
|
||||
variant="text"
|
||||
class="mt-1 flex-shrink-0"
|
||||
: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"
|
||||
<div class="min-w-0">
|
||||
<div class="d-flex flex-wrap align-center gap-x-2 gap-y-1">
|
||||
<h4 class="text-h4 mb-0">
|
||||
<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
|
||||
v-if="event.event_type === 'festival' || event.event_type === 'series'"
|
||||
:color="eventTypeColor[event.event_type]"
|
||||
size="small"
|
||||
variant="tonal"
|
||||
class="flex-shrink-0"
|
||||
>
|
||||
{{ event.parent.name }}
|
||||
</RouterLink>
|
||||
<span class="text-medium-emphasis mx-1">»</span>
|
||||
</template>
|
||||
{{ 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"
|
||||
>
|
||||
{{ eventStatusLabelNl(event.status) }}
|
||||
</VChip>
|
||||
<div
|
||||
v-if="allowedTransitions.length"
|
||||
class="d-flex flex-wrap align-center gap-2"
|
||||
>
|
||||
<VBtn
|
||||
v-for="target in allowedTransitions"
|
||||
:key="target"
|
||||
size="small"
|
||||
:color="transitionButtonColor(event.status, target)"
|
||||
:variant="transitionButtonVariant(event.status, target)"
|
||||
:disabled="isTransitionPending"
|
||||
@click="openTransitionConfirm(target)"
|
||||
>
|
||||
{{ eventStatusLabelNl(target) }}
|
||||
</VBtn>
|
||||
{{ event.event_type_label ?? (event.event_type === 'festival' ? 'Festival' : 'Serie') }}
|
||||
</VChip>
|
||||
</div>
|
||||
<div class="text-body-1 text-medium-emphasis mt-2">
|
||||
{{ formatDate(event.start_date) }} – {{ formatDate(event.end_date) }}
|
||||
</div>
|
||||
</div>
|
||||
<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 class="d-flex align-center gap-2 flex-shrink-0 ms-auto">
|
||||
<VTooltip
|
||||
v-if="showVolunteerShare"
|
||||
:disabled="isRegistrationOpen"
|
||||
location="top"
|
||||
max-width="320"
|
||||
>
|
||||
<template #activator="{ props: tooltipProps }">
|
||||
<span v-bind="tooltipProps">
|
||||
<VBtn
|
||||
prepend-icon="tabler-share-3"
|
||||
variant="outlined"
|
||||
:disabled="!isRegistrationOpen"
|
||||
@click="shareDialogOpen = true"
|
||||
>
|
||||
Delen
|
||||
</VBtn>
|
||||
</span>
|
||||
</template>
|
||||
Zet de status op 'Registratie open' om het formulier te activeren.
|
||||
</VTooltip>
|
||||
<VMenu
|
||||
v-model="statusMenuOpen"
|
||||
location="bottom end"
|
||||
>
|
||||
<template #activator="{ props: menuProps }">
|
||||
<VBtn
|
||||
v-bind="menuProps"
|
||||
:color="statusActionColor[event.status]"
|
||||
:variant="allowedTransitions.length ? 'flat' : 'tonal'"
|
||||
:disabled="!allowedTransitions.length || isTransitionPending"
|
||||
append-icon="tabler-chevron-down"
|
||||
>
|
||||
{{ eventStatusLabelNl(event.status) }}
|
||||
</VBtn>
|
||||
</template>
|
||||
<VList
|
||||
density="compact"
|
||||
class="py-1"
|
||||
>
|
||||
<VListItem
|
||||
v-for="target in allowedTransitions"
|
||||
:key="target"
|
||||
:title="eventStatusLabelNl(target)"
|
||||
@click="onSelectTransition(target)"
|
||||
>
|
||||
<template #prepend>
|
||||
<VIcon
|
||||
:icon="transitionVisualKind(event.status, target) === 'dangerous' ? 'tabler-alert-triangle' : 'tabler-arrow-right'"
|
||||
:color="transitionButtonColor(event.status, target)"
|
||||
size="20"
|
||||
/>
|
||||
</template>
|
||||
</VListItem>
|
||||
</VList>
|
||||
</VMenu>
|
||||
<VBtn
|
||||
prepend-icon="tabler-edit"
|
||||
@click="isEditDialogOpen = true"
|
||||
>
|
||||
Bewerken
|
||||
</VBtn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<VDialog
|
||||
@@ -297,11 +362,57 @@ const backRoute = computed(() => {
|
||||
</VCard>
|
||||
</VDialog>
|
||||
|
||||
<!-- Registration link (top-level events only) -->
|
||||
<RegistrationLinkCard
|
||||
v-if="!event.parent_event_id"
|
||||
:event="event"
|
||||
/>
|
||||
<VDialog
|
||||
v-if="showVolunteerShare"
|
||||
v-model="shareDialogOpen"
|
||||
max-width="520"
|
||||
>
|
||||
<VCard>
|
||||
<VCardTitle class="text-h6">
|
||||
Vrijwilligersformulier delen
|
||||
</VCardTitle>
|
||||
<VCardText>
|
||||
<p class="text-body-2 text-medium-emphasis mb-4">
|
||||
Deel deze link met vrijwilligers om zich aan te melden.
|
||||
</p>
|
||||
<VTextField
|
||||
:model-value="registrationUrl"
|
||||
readonly
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
hide-details
|
||||
class="mb-4"
|
||||
/>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<VBtn
|
||||
variant="outlined"
|
||||
prepend-icon="tabler-copy"
|
||||
@click="copyRegistrationLink"
|
||||
>
|
||||
{{ linkCopied ? 'Gekopieerd!' : 'Kopieer link' }}
|
||||
</VBtn>
|
||||
<VBtn
|
||||
color="primary"
|
||||
prepend-icon="tabler-external-link"
|
||||
:href="registrationUrl"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Open formulier
|
||||
</VBtn>
|
||||
</div>
|
||||
</VCardText>
|
||||
<VCardActions>
|
||||
<VSpacer />
|
||||
<VBtn
|
||||
variant="text"
|
||||
@click="shareDialogOpen = false"
|
||||
>
|
||||
Sluiten
|
||||
</VBtn>
|
||||
</VCardActions>
|
||||
</VCard>
|
||||
</VDialog>
|
||||
|
||||
<!-- Horizontal tabs -->
|
||||
<VTabs
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { EventItem } from '@/types/event'
|
||||
|
||||
const props = defineProps<{
|
||||
event: EventItem
|
||||
}>()
|
||||
|
||||
const portalBaseUrl = import.meta.env.VITE_PORTAL_URL || 'https://portal.crewli.app'
|
||||
|
||||
const registrationUrl = computed(() =>
|
||||
`${portalBaseUrl}/register/${props.event.slug}`,
|
||||
)
|
||||
|
||||
const isRegistrationOpen = computed(() =>
|
||||
props.event.status === 'registration_open',
|
||||
)
|
||||
|
||||
const copied = ref(false)
|
||||
|
||||
async function copyLink() {
|
||||
await navigator.clipboard.writeText(registrationUrl.value)
|
||||
copied.value = true
|
||||
setTimeout(() => { copied.value = false }, 2000)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VCard
|
||||
v-if="event"
|
||||
variant="outlined"
|
||||
class="mb-4"
|
||||
>
|
||||
<VCardText>
|
||||
<div class="d-flex align-center justify-space-between flex-wrap ga-2">
|
||||
<div>
|
||||
<div class="text-subtitle-2 text-medium-emphasis mb-1">
|
||||
Vrijwilligersregistratie
|
||||
</div>
|
||||
<template v-if="isRegistrationOpen">
|
||||
<code class="text-body-2">{{ registrationUrl }}</code>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="text-body-2 text-medium-emphasis">
|
||||
Zet de status op 'Registratie open' om het formulier te activeren.
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
<div
|
||||
v-if="isRegistrationOpen"
|
||||
class="d-flex ga-2"
|
||||
>
|
||||
<VBtn
|
||||
size="small"
|
||||
variant="outlined"
|
||||
prepend-icon="tabler-copy"
|
||||
@click="copyLink"
|
||||
>
|
||||
{{ copied ? 'Gekopieerd!' : 'Kopieer link' }}
|
||||
</VBtn>
|
||||
<VBtn
|
||||
size="small"
|
||||
variant="outlined"
|
||||
prepend-icon="tabler-external-link"
|
||||
:href="registrationUrl"
|
||||
target="_blank"
|
||||
>
|
||||
Open formulier
|
||||
</VBtn>
|
||||
</div>
|
||||
</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</template>
|
||||
Reference in New Issue
Block a user