Move all authenticated organiser-facing event sub-resource routes from
/events/{event}/... to /organisations/{organisation}/events/{event}/...
to enforce multi-tenancy at the routing layer.
Changes:
- Routes: restructured api.php to nest all event sub-resources under
the existing organisation prefix group
- Controllers: added Organisation parameter and VerifiesOrganisationEvent
trait to all 12 affected controllers (sections, time-slots, shifts,
persons, crowd-lists, locations, shift-assignments, registration-fields,
availabilities, field-values, section-preferences, stats)
- Tests: updated all 20 feature test files with new route paths
- Frontend: updated 8 API composables and 20 Vue components/pages
- API.md: updated documentation to reflect new route structure
Portal routes, public routes (volunteer-register), and invitation routes
remain unchanged as they operate without organisation context.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
142 lines
4.1 KiB
Vue
142 lines
4.1 KiB
Vue
<script setup lang="ts">
|
|
import { useRegistrationFieldTemplates } from '@/composables/api/useRegistrationFieldTemplates'
|
|
import { useCreateFieldFromTemplate } from '@/composables/api/useRegistrationFormFields'
|
|
import { FIELD_TYPE_LABELS } from '@/types/registration-field-template'
|
|
import type { RegistrationFormField } from '@/types/registration-form-field'
|
|
|
|
const props = defineProps<{
|
|
orgId: string
|
|
eventId: string
|
|
existingFields: RegistrationFormField[]
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
added: []
|
|
}>()
|
|
|
|
const modelValue = defineModel<boolean>({ default: false })
|
|
|
|
const orgIdRef = computed(() => props.orgId)
|
|
const eventIdRef = computed(() => props.eventId)
|
|
|
|
const { data: templates, isLoading } = useRegistrationFieldTemplates(orgIdRef)
|
|
const { mutate: createFromTemplate, isPending } = useCreateFieldFromTemplate(orgIdRef, eventIdRef)
|
|
|
|
const existingSlugs = computed(() =>
|
|
new Set(props.existingFields.map(f => f.slug)),
|
|
)
|
|
|
|
const activeTemplates = computed(() =>
|
|
(templates.value ?? []).filter(t => t.is_active),
|
|
)
|
|
|
|
const addingTemplateId = ref<string | null>(null)
|
|
|
|
function isAlreadyAdded(slug: string): boolean {
|
|
return existingSlugs.value.has(slug)
|
|
}
|
|
|
|
function onAdd(templateId: string) {
|
|
addingTemplateId.value = templateId
|
|
createFromTemplate(templateId, {
|
|
onSuccess: () => {
|
|
addingTemplateId.value = null
|
|
emit('added')
|
|
},
|
|
onError: () => {
|
|
addingTemplateId.value = null
|
|
},
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<VDialog
|
|
v-model="modelValue"
|
|
max-width="600"
|
|
>
|
|
<VCard title="Veld uit template toevoegen">
|
|
<VCardText>
|
|
<VSkeletonLoader
|
|
v-if="isLoading"
|
|
type="list-item@3"
|
|
/>
|
|
|
|
<template v-else-if="activeTemplates.length">
|
|
<VList density="compact">
|
|
<VListItem
|
|
v-for="template in activeTemplates"
|
|
:key="template.id"
|
|
:disabled="isAlreadyAdded(template.slug)"
|
|
class="rounded mb-1"
|
|
:class="{ 'opacity-50': isAlreadyAdded(template.slug) }"
|
|
>
|
|
<template #prepend>
|
|
<VIcon
|
|
:icon="isAlreadyAdded(template.slug) ? 'tabler-check' : 'tabler-forms'"
|
|
size="20"
|
|
:color="isAlreadyAdded(template.slug) ? 'success' : undefined"
|
|
/>
|
|
</template>
|
|
|
|
<VListItemTitle>
|
|
{{ template.label }}
|
|
<span
|
|
v-if="isAlreadyAdded(template.slug)"
|
|
class="text-caption text-disabled ms-2"
|
|
>(al toegevoegd)</span>
|
|
</VListItemTitle>
|
|
<VListItemSubtitle>
|
|
{{ FIELD_TYPE_LABELS[template.field_type] ?? template.field_type }}
|
|
<template v-if="template.section">
|
|
· {{ template.section }}
|
|
</template>
|
|
<template v-if="template.is_required">
|
|
· Verplicht
|
|
</template>
|
|
</VListItemSubtitle>
|
|
|
|
<template #append>
|
|
<VBtn
|
|
v-if="!isAlreadyAdded(template.slug)"
|
|
size="small"
|
|
variant="tonal"
|
|
color="primary"
|
|
:loading="isPending && addingTemplateId === template.id"
|
|
:disabled="isPending"
|
|
@click="onAdd(template.id)"
|
|
>
|
|
Toevoegen
|
|
</VBtn>
|
|
</template>
|
|
</VListItem>
|
|
</VList>
|
|
</template>
|
|
|
|
<div
|
|
v-else
|
|
class="text-center pa-4"
|
|
>
|
|
<VIcon
|
|
icon="tabler-forms"
|
|
size="40"
|
|
class="mb-3 text-disabled"
|
|
/>
|
|
<p class="text-body-2 text-disabled mb-0">
|
|
Geen actieve templates beschikbaar. Maak eerst templates aan in de organisatie-instellingen.
|
|
</p>
|
|
</div>
|
|
</VCardText>
|
|
<VCardActions>
|
|
<VSpacer />
|
|
<VBtn
|
|
variant="text"
|
|
@click="modelValue = false"
|
|
>
|
|
Sluiten
|
|
</VBtn>
|
|
</VCardActions>
|
|
</VCard>
|
|
</VDialog>
|
|
</template>
|