refactor: align codebase with EventCrew domain and trim legacy band stack

- Update API: events, users, policies, routes, resources, migrations
- Remove deprecated models/resources (customers, setlists, invitations, etc.)
- Refresh admin app and docs; remove apps/band

Made-with: Cursor
This commit is contained in:
2026-03-29 23:19:06 +02:00
parent 34e12e00b3
commit 1cb7674d52
1034 changed files with 7453 additions and 8743 deletions

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { useEvents } from '@/composables/useEvents'
import { useRoute, useRouter } from 'vue-router'
import type { UpdateEventData } from '@/types/events'
import type { EventCrewEventStatus, UpdateEventData } from '@/types/events'
definePage({
meta: {
@@ -17,62 +17,57 @@ const eventId = computed(() => route.params.id as string)
const formData = ref<UpdateEventData>({})
const statusOptions = [
const statusOptions: { title: string; value: EventCrewEventStatus }[] = [
{ title: 'Draft', value: 'draft' },
{ title: 'Pending', value: 'pending' },
{ title: 'Confirmed', value: 'confirmed' },
{ title: 'Completed', value: 'completed' },
{ title: 'Cancelled', value: 'cancelled' },
]
const visibilityOptions = [
{ title: 'Private', value: 'private' },
{ title: 'Members', value: 'members' },
{ title: 'Public', value: 'public' },
{ title: 'Published', value: 'published' },
{ title: 'Registration open', value: 'registration_open' },
{ title: 'Build-up', value: 'buildup' },
{ title: 'Show day', value: 'showday' },
{ title: 'Teardown', value: 'teardown' },
{ title: 'Closed', value: 'closed' },
]
const errors = ref<Record<string, string>>({})
// Load event data
watch(() => eventId.value, async () => {
if (eventId.value) {
await fetchEvent(eventId.value)
if (currentEvent.value) {
formData.value = {
title: currentEvent.value.title,
description: currentEvent.value.description || '',
event_date: currentEvent.value.event_date,
start_time: currentEvent.value.start_time,
end_time: currentEvent.value.end_time || '',
load_in_time: currentEvent.value.load_in_time || '',
soundcheck_time: currentEvent.value.soundcheck_time || '',
location_id: currentEvent.value.location?.id || '',
customer_id: currentEvent.value.customer?.id || '',
setlist_id: currentEvent.value.setlist?.id || '',
fee: currentEvent.value.fee || undefined,
currency: currentEvent.value.currency,
status: currentEvent.value.status,
visibility: currentEvent.value.visibility,
rsvp_deadline: currentEvent.value.rsvp_deadline || '',
notes: currentEvent.value.notes || '',
internal_notes: currentEvent.value.internal_notes || '',
is_public_setlist: currentEvent.value.is_public_setlist,
}
if (!eventId.value) {
return
}
await fetchEvent(eventId.value)
if (currentEvent.value) {
const e = currentEvent.value
formData.value = {
name: e.name,
slug: e.slug,
start_date: e.start_date,
end_date: e.end_date,
timezone: e.timezone,
status: e.status,
}
}
}, { immediate: true })
function flattenValidationErrors(raw: Record<string, string[] | string>): Record<string, string> {
const out: Record<string, string> = {}
for (const [key, val] of Object.entries(raw)) {
out[key] = Array.isArray(val) ? val[0]! : val
}
return out
}
async function handleSubmit() {
errors.value = {}
try {
await updateEvent(eventId.value, formData.value)
router.push(`/events/${eventId.value}`)
} catch (err: any) {
if (err.response?.data?.errors) {
errors.value = err.response.data.errors
} else {
errors.value._general = err.message || 'Failed to update event'
try {
await updateEvent(eventId.value, formData.value)
await router.push(`/events/${eventId.value}`)
}
catch (err: unknown) {
const ax = err as { response?: { data?: { errors?: Record<string, string[] | string>; message?: string } } }
if (ax.response?.data?.errors) {
errors.value = flattenValidationErrors(ax.response.data.errors)
}
else {
errors.value._general = ax.response?.data?.message ?? (err instanceof Error ? err.message : 'Failed to update event')
}
}
}
@@ -98,20 +93,19 @@ async function handleSubmit() {
<VRow>
<VCol cols="12">
<AppTextField
v-model="formData.title"
label="Title"
placeholder="Event Title"
:error-messages="errors.title"
v-model="formData.name"
label="Name"
:error-messages="errors.name"
required
/>
</VCol>
<VCol cols="12">
<AppTextarea
v-model="formData.description"
label="Description"
placeholder="Event Description"
rows="3"
<AppTextField
v-model="formData.slug"
label="Slug"
:error-messages="errors.slug"
required
/>
</VCol>
@@ -120,11 +114,10 @@ async function handleSubmit() {
md="6"
>
<AppTextField
v-model="formData.event_date"
label="Event Date"
v-model="formData.start_date"
label="Start date"
type="date"
:error-messages="errors.event_date"
required
:error-messages="errors.start_date"
/>
</VCol>
@@ -133,126 +126,27 @@ async function handleSubmit() {
md="6"
>
<AppTextField
v-model="formData.start_time"
label="Start Time"
type="time"
:error-messages="errors.start_time"
required
v-model="formData.end_date"
label="End date"
type="date"
:error-messages="errors.end_date"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VCol cols="12">
<AppTextField
v-model="formData.end_time"
label="End Time"
type="time"
v-model="formData.timezone"
label="Timezone"
:error-messages="errors.timezone"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="formData.load_in_time"
label="Load-in Time"
type="time"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="formData.soundcheck_time"
label="Soundcheck Time"
type="time"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="formData.rsvp_deadline"
label="RSVP Deadline"
type="datetime-local"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<AppTextField
v-model.number="formData.fee"
label="Fee"
type="number"
step="0.01"
prefix="€"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<AppSelect
v-model="formData.currency"
label="Currency"
:items="[{ title: 'EUR', value: 'EUR' }, { title: 'USD', value: 'USD' }]"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VCol cols="12">
<AppSelect
v-model="formData.status"
label="Status"
:items="statusOptions"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<AppSelect
v-model="formData.visibility"
label="Visibility"
:items="visibilityOptions"
/>
</VCol>
<VCol cols="12">
<AppTextarea
v-model="formData.notes"
label="Notes"
placeholder="Public notes"
rows="3"
/>
</VCol>
<VCol cols="12">
<AppTextarea
v-model="formData.internal_notes"
label="Internal Notes"
placeholder="Private notes (only visible to admins)"
rows="3"
/>
</VCol>
<VCol cols="12">
<VCheckbox
v-model="formData.is_public_setlist"
label="Public Setlist"
:error-messages="errors.status"
/>
</VCol>
@@ -271,11 +165,10 @@ async function handleSubmit() {
color="primary"
:loading="isLoading"
>
Update Event
Save
</VBtn>
<VBtn
variant="tonal"
color="secondary"
:to="`/events/${eventId}`"
>
Cancel
@@ -287,7 +180,7 @@ async function handleSubmit() {
</VCardText>
</VCard>
<VCard v-else>
<VCard v-else-if="isLoading">
<VCardText class="text-center py-12">
<VProgressCircular
indeterminate
@@ -297,4 +190,3 @@ async function handleSubmit() {
</VCard>
</div>
</template>