Files
crewli/apps/app/src/components/event/TemplatePickerDialog.vue
bert.hausmans 7932e53daf security: A01-13 — nest all event routes under organisation prefix
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>
2026-04-14 08:16:36 +02:00

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">
&middot; {{ template.section }}
</template>
<template v-if="template.is_required">
&middot; 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>