Implement multi-step registration process: Add step indicators, error handling, and payment processing for membership registration. Enhance form validation and user feedback with success and error messages. Refactor state management for improved clarity and maintainability.
This commit is contained in:
parent
a88aa62198
commit
2ca290d6e0
22 changed files with 1994 additions and 213 deletions
|
|
@ -390,40 +390,6 @@
|
|||
</div>
|
||||
|
||||
<div v-if="selectedSeriesId || eventForm.series.id" class="space-y-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Series ID <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
v-model="eventForm.series.id"
|
||||
type="text"
|
||||
placeholder="e.g., coop-dev-fundamentals"
|
||||
required
|
||||
:readonly="selectedSeriesId"
|
||||
class="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-purple-500 focus:border-transparent"
|
||||
:class="{ 'bg-gray-100': selectedSeriesId }"
|
||||
@input="!selectedSeriesId && checkExistingSeries()"
|
||||
/>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
{{ selectedSeriesId ? 'From selected series' : 'Unique identifier to group related events (use lowercase with dashes)' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Position in Series <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
v-model.number="eventForm.series.position"
|
||||
type="number"
|
||||
min="1"
|
||||
required
|
||||
class="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-purple-500 focus:border-transparent"
|
||||
/>
|
||||
<p class="text-xs text-gray-500 mt-1">Order within the series (1, 2, 3, etc.)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||
|
|
@ -457,37 +423,6 @@
|
|||
<p class="text-xs text-gray-500 mt-1">{{ selectedSeriesId ? 'From selected series' : 'Describe what the series covers and its goals' }}</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Series Type</label>
|
||||
<select
|
||||
v-model="eventForm.series.type"
|
||||
:disabled="selectedSeriesId"
|
||||
class="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-purple-500 focus:border-transparent"
|
||||
:class="{ 'bg-gray-100': selectedSeriesId }"
|
||||
>
|
||||
<option value="workshop_series">Workshop Series</option>
|
||||
<option value="recurring_meetup">Recurring Meetup</option>
|
||||
<option value="multi_day">Multi-Day Event</option>
|
||||
<option value="course">Course</option>
|
||||
<option value="tournament">Tournament</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Total Events Planned</label>
|
||||
<input
|
||||
v-model.number="eventForm.series.totalEvents"
|
||||
type="number"
|
||||
min="1"
|
||||
placeholder="e.g., 4"
|
||||
:readonly="selectedSeriesId"
|
||||
class="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-purple-500 focus:border-transparent"
|
||||
:class="{ 'bg-gray-100': selectedSeriesId }"
|
||||
/>
|
||||
<p class="text-xs text-gray-500 mt-1">{{ selectedSeriesId ? 'From selected series' : 'How many events will be in this series?' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="selectedSeriesId" class="p-3 bg-blue-50 rounded-lg">
|
||||
<p class="text-sm text-blue-700">
|
||||
|
|
@ -617,8 +552,6 @@ const editingEvent = ref(null)
|
|||
const showSuccessMessage = ref(false)
|
||||
const formErrors = ref([])
|
||||
const fieldErrors = ref({})
|
||||
const seriesExists = ref(false)
|
||||
const existingSeries = ref(null)
|
||||
const selectedSeriesId = ref('')
|
||||
const availableSeries = ref([])
|
||||
|
||||
|
|
@ -655,10 +588,7 @@ const eventForm = reactive({
|
|||
isSeriesEvent: false,
|
||||
id: '',
|
||||
title: '',
|
||||
description: '',
|
||||
type: 'workshop_series',
|
||||
position: 1,
|
||||
totalEvents: null
|
||||
description: ''
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -680,18 +610,12 @@ const onSeriesSelect = () => {
|
|||
eventForm.series.id = series.id
|
||||
eventForm.series.title = series.title
|
||||
eventForm.series.description = series.description
|
||||
eventForm.series.type = series.type
|
||||
eventForm.series.totalEvents = series.totalEvents
|
||||
eventForm.series.position = (series.eventCount || 0) + 1
|
||||
}
|
||||
} else {
|
||||
// Reset series form when no series is selected
|
||||
eventForm.series.id = ''
|
||||
eventForm.series.title = ''
|
||||
eventForm.series.description = ''
|
||||
eventForm.series.type = 'workshop_series'
|
||||
eventForm.series.position = 1
|
||||
eventForm.series.totalEvents = null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -736,10 +660,7 @@ if (route.query.edit) {
|
|||
isSeriesEvent: false,
|
||||
id: '',
|
||||
title: '',
|
||||
description: '',
|
||||
type: 'workshop_series',
|
||||
position: 1,
|
||||
totalEvents: null
|
||||
description: ''
|
||||
}
|
||||
})
|
||||
// Handle early bird deadline formatting
|
||||
|
|
@ -851,62 +772,6 @@ const validateForm = () => {
|
|||
return formErrors.value.length === 0
|
||||
}
|
||||
|
||||
// Check if a series with this ID already exists
|
||||
const checkExistingSeries = async () => {
|
||||
if (!eventForm.series.id || selectedSeriesId.value) {
|
||||
seriesExists.value = false
|
||||
existingSeries.value = null
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// First check in standalone series
|
||||
const standaloneResponse = await $fetch(`/api/admin/series`)
|
||||
const existingStandalone = standaloneResponse.find(s => s.id === eventForm.series.id)
|
||||
|
||||
if (existingStandalone) {
|
||||
seriesExists.value = true
|
||||
existingSeries.value = existingStandalone
|
||||
// Auto-fill series details
|
||||
if (!eventForm.series.title || eventForm.series.title === '') {
|
||||
eventForm.series.title = existingStandalone.title
|
||||
}
|
||||
if (!eventForm.series.description || eventForm.series.description === '') {
|
||||
eventForm.series.description = existingStandalone.description
|
||||
}
|
||||
if (!eventForm.series.type || eventForm.series.type === 'workshop_series') {
|
||||
eventForm.series.type = existingStandalone.type
|
||||
}
|
||||
if (!eventForm.series.totalEvents || eventForm.series.totalEvents === null) {
|
||||
eventForm.series.totalEvents = existingStandalone.totalEvents
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Fallback to legacy series check (events with series data)
|
||||
const legacyResponse = await $fetch(`/api/series/${eventForm.series.id}`)
|
||||
if (legacyResponse) {
|
||||
seriesExists.value = true
|
||||
existingSeries.value = legacyResponse
|
||||
if (!eventForm.series.title || eventForm.series.title === '') {
|
||||
eventForm.series.title = legacyResponse.title
|
||||
}
|
||||
if (!eventForm.series.description || eventForm.series.description === '') {
|
||||
eventForm.series.description = legacyResponse.description
|
||||
}
|
||||
if (!eventForm.series.type || eventForm.series.type === 'workshop_series') {
|
||||
eventForm.series.type = legacyResponse.type
|
||||
}
|
||||
if (!eventForm.series.totalEvents || eventForm.series.totalEvents === null) {
|
||||
eventForm.series.totalEvents = legacyResponse.totalEvents
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Series doesn't exist yet
|
||||
seriesExists.value = false
|
||||
existingSeries.value = null
|
||||
}
|
||||
}
|
||||
|
||||
const saveEvent = async (redirect = true) => {
|
||||
if (!validateForm()) {
|
||||
|
|
@ -918,24 +783,11 @@ const saveEvent = async (redirect = true) => {
|
|||
creating.value = true
|
||||
try {
|
||||
// If this is a series event and not using an existing series, create the standalone series first
|
||||
if (eventForm.series.isSeriesEvent && eventForm.series.id && !selectedSeriesId.value) {
|
||||
try {
|
||||
await $fetch('/api/admin/series', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
id: eventForm.series.id,
|
||||
title: eventForm.series.title,
|
||||
description: eventForm.series.description,
|
||||
type: eventForm.series.type,
|
||||
totalEvents: eventForm.series.totalEvents
|
||||
}
|
||||
})
|
||||
} catch (seriesError) {
|
||||
// Series might already exist, that's ok
|
||||
if (!seriesError.data?.statusMessage?.includes('already exists')) {
|
||||
throw seriesError
|
||||
}
|
||||
}
|
||||
if (eventForm.series.isSeriesEvent && selectedSeriesId.value) {
|
||||
// Series will be handled by the selected series
|
||||
} else if (eventForm.series.isSeriesEvent) {
|
||||
// For now, series creation requires selecting an existing series
|
||||
// Individual series creation is handled through the series management page
|
||||
}
|
||||
|
||||
if (editingEvent.value) {
|
||||
|
|
@ -1007,10 +859,7 @@ const saveAndCreateAnother = async () => {
|
|||
isSeriesEvent: false,
|
||||
id: '',
|
||||
title: '',
|
||||
description: '',
|
||||
type: 'workshop_series',
|
||||
position: 1,
|
||||
totalEvents: null
|
||||
description: ''
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue