Many an update!
This commit is contained in:
parent
85195d6c7a
commit
d588c49946
35 changed files with 3528 additions and 1142 deletions
|
|
@ -79,7 +79,7 @@
|
|||
<div class="max-w-4xl mx-auto">
|
||||
<!-- Event Meta Info -->
|
||||
<div class="mb-8">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||
<div>
|
||||
<p class="text-sm text-ghost-400">Date</p>
|
||||
<p class="font-semibold text-ghost-100">
|
||||
|
|
@ -100,6 +100,20 @@
|
|||
{{ event.location }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="text-sm text-ghost-400">Calendar</p>
|
||||
<UButton
|
||||
:href="`/api/events/${route.params.id}/calendar`"
|
||||
download
|
||||
variant="outline"
|
||||
size="sm"
|
||||
class="mt-1"
|
||||
icon="i-heroicons-calendar-days"
|
||||
>
|
||||
Add to Calendar
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -270,28 +284,64 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Logged In - Can Register -->
|
||||
<div
|
||||
v-else-if="memberData && (!event.membersOnly || isMember)"
|
||||
class="text-center"
|
||||
>
|
||||
<p class="text-lg text-ghost-200 mb-6">
|
||||
You are logged in, {{ memberData.name }}.
|
||||
</p>
|
||||
<UButton
|
||||
color="primary"
|
||||
size="xl"
|
||||
@click="handleRegistration"
|
||||
:loading="isRegistering"
|
||||
class="px-12 py-4"
|
||||
<!-- Member Status Check (pending payment, suspended, cancelled) -->
|
||||
<div v-else-if="memberData && !canRSVP" class="text-center">
|
||||
<div
|
||||
:class="[
|
||||
'p-6 rounded-lg border mb-6',
|
||||
statusConfig.bgColor,
|
||||
statusConfig.borderColor,
|
||||
]"
|
||||
>
|
||||
{{ isRegistering ? "Registering..." : "Register Now" }}
|
||||
</UButton>
|
||||
<Icon
|
||||
:name="statusConfig.icon"
|
||||
:class="['w-8 h-8 mx-auto mb-3', statusConfig.textColor]"
|
||||
/>
|
||||
<p
|
||||
:class="[
|
||||
'font-semibold text-lg mb-2',
|
||||
statusConfig.textColor,
|
||||
]"
|
||||
>
|
||||
{{ statusConfig.label }}
|
||||
</p>
|
||||
<p :class="['mb-4', statusConfig.textColor]">
|
||||
{{ getRSVPMessage }}
|
||||
</p>
|
||||
<UButton
|
||||
v-if="isPendingPayment"
|
||||
color="orange"
|
||||
size="lg"
|
||||
class="px-8"
|
||||
:loading="isProcessingPayment"
|
||||
@click="completePayment"
|
||||
>
|
||||
{{
|
||||
isProcessingPayment ? "Processing..." : "Complete Payment"
|
||||
}}
|
||||
</UButton>
|
||||
<NuxtLink
|
||||
v-else-if="isCancelled"
|
||||
to="/member/profile#account"
|
||||
>
|
||||
<UButton color="blue" size="lg" class="px-8">
|
||||
Reactivate Membership
|
||||
</UButton>
|
||||
</NuxtLink>
|
||||
<a
|
||||
v-else-if="isSuspended"
|
||||
href="mailto:support@ghostguild.org"
|
||||
>
|
||||
<UButton color="gray" size="lg" class="px-8">
|
||||
Contact Support
|
||||
</UButton>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Member Gate Warning -->
|
||||
<!-- Member Gate Warning (members-only locked event) -->
|
||||
<div
|
||||
v-else-if="event.membersOnly && !isMember"
|
||||
v-else-if="event.membersOnly && memberData && !isMember"
|
||||
class="text-center"
|
||||
>
|
||||
<div
|
||||
|
|
@ -312,6 +362,25 @@
|
|||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<!-- Logged In - Can Register -->
|
||||
<div
|
||||
v-else-if="memberData && (!event.membersOnly || isMember)"
|
||||
class="text-center"
|
||||
>
|
||||
<p class="text-lg text-ghost-200 mb-6">
|
||||
You are logged in, {{ memberData.name }}.
|
||||
</p>
|
||||
<UButton
|
||||
color="primary"
|
||||
size="xl"
|
||||
@click="handleRegistration"
|
||||
:loading="isRegistering"
|
||||
class="px-12 py-4"
|
||||
>
|
||||
{{ isRegistering ? "Registering..." : "Register Now" }}
|
||||
</UButton>
|
||||
</div>
|
||||
|
||||
<!-- Not Logged In - Show Registration Form -->
|
||||
<div v-else>
|
||||
<h3 class="text-xl font-bold text-ghost-100 mb-6">
|
||||
|
|
@ -403,6 +472,64 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Waitlist Section -->
|
||||
<div
|
||||
v-if="event.tickets?.waitlist?.enabled && isEventFull"
|
||||
class="mt-6 pt-6 border-t border-ghost-700"
|
||||
>
|
||||
<!-- Already on Waitlist -->
|
||||
<div v-if="isOnWaitlist" class="text-center">
|
||||
<div class="p-4 bg-amber-900/20 rounded-lg border border-amber-800 mb-4">
|
||||
<p class="font-semibold text-amber-300">
|
||||
You're on the waitlist!
|
||||
</p>
|
||||
<p class="text-sm text-amber-400 mt-1">
|
||||
Position #{{ waitlistPosition }} - We'll email you if a spot opens up
|
||||
</p>
|
||||
</div>
|
||||
<UButton
|
||||
color="gray"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@click="handleLeaveWaitlist"
|
||||
:loading="isJoiningWaitlist"
|
||||
>
|
||||
Leave Waitlist
|
||||
</UButton>
|
||||
</div>
|
||||
|
||||
<!-- Join Waitlist Form -->
|
||||
<div v-else>
|
||||
<div class="p-4 bg-amber-900/20 rounded-lg border border-amber-800 mb-4">
|
||||
<p class="font-semibold text-amber-300">
|
||||
This event is full
|
||||
</p>
|
||||
<p class="text-sm text-amber-400 mt-1">
|
||||
Join the waitlist and we'll notify you if a spot opens up
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form @submit.prevent="handleJoinWaitlist" class="space-y-4">
|
||||
<div v-if="!memberData">
|
||||
<UInput
|
||||
v-model="waitlistForm.email"
|
||||
type="email"
|
||||
placeholder="Your email address"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<UButton
|
||||
type="submit"
|
||||
color="warning"
|
||||
block
|
||||
:loading="isJoiningWaitlist"
|
||||
>
|
||||
{{ isJoiningWaitlist ? "Joining..." : "Join Waitlist" }}
|
||||
</UButton>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -446,16 +573,21 @@ if (error.value?.statusCode === 404) {
|
|||
|
||||
// Authentication
|
||||
const { isMember, memberData, checkMemberStatus } = useAuth();
|
||||
const {
|
||||
isPendingPayment,
|
||||
isSuspended,
|
||||
isCancelled,
|
||||
canRSVP,
|
||||
statusConfig,
|
||||
getRSVPMessage,
|
||||
} = useMemberStatus();
|
||||
const { completePayment, isProcessingPayment, paymentError } =
|
||||
useMemberPayment();
|
||||
|
||||
// Check member status on mount
|
||||
onMounted(async () => {
|
||||
await checkMemberStatus();
|
||||
|
||||
// Debug: Log series data
|
||||
if (event.value?.series) {
|
||||
console.log("Series data:", event.value.series);
|
||||
}
|
||||
|
||||
// Pre-fill form if member is logged in
|
||||
if (memberData.value) {
|
||||
registrationForm.value.name = memberData.value.name;
|
||||
|
|
@ -465,6 +597,9 @@ onMounted(async () => {
|
|||
|
||||
// Check if user is already registered
|
||||
await checkRegistrationStatus();
|
||||
|
||||
// Check waitlist status
|
||||
checkWaitlistStatus();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -507,6 +642,105 @@ const isRegistering = ref(false);
|
|||
const isCancelling = ref(false);
|
||||
const registrationStatus = ref("not-registered"); // 'not-registered', 'registered'
|
||||
|
||||
// Waitlist state
|
||||
const isJoiningWaitlist = ref(false);
|
||||
const isOnWaitlist = ref(false);
|
||||
const waitlistPosition = ref(0);
|
||||
const waitlistForm = ref({
|
||||
email: "",
|
||||
});
|
||||
|
||||
// Computed: Check if event is full
|
||||
const isEventFull = computed(() => {
|
||||
if (!event.value?.maxAttendees) return false;
|
||||
return (event.value.registeredCount || 0) >= event.value.maxAttendees;
|
||||
});
|
||||
|
||||
// Check waitlist status
|
||||
const checkWaitlistStatus = async () => {
|
||||
const email = memberData.value?.email || waitlistForm.value.email;
|
||||
if (!email || !event.value?.tickets?.waitlist?.enabled) return;
|
||||
|
||||
const entries = event.value.tickets.waitlist.entries || [];
|
||||
const entryIndex = entries.findIndex(
|
||||
(e) => e.email.toLowerCase() === email.toLowerCase()
|
||||
);
|
||||
|
||||
if (entryIndex !== -1) {
|
||||
isOnWaitlist.value = true;
|
||||
waitlistPosition.value = entryIndex + 1;
|
||||
} else {
|
||||
isOnWaitlist.value = false;
|
||||
waitlistPosition.value = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Join waitlist handler
|
||||
const handleJoinWaitlist = async () => {
|
||||
isJoiningWaitlist.value = true;
|
||||
|
||||
try {
|
||||
const email = memberData.value?.email || waitlistForm.value.email;
|
||||
const name = memberData.value?.name || "Guest";
|
||||
|
||||
const response = await $fetch(`/api/events/${route.params.id}/waitlist`, {
|
||||
method: "POST",
|
||||
body: { email, name },
|
||||
});
|
||||
|
||||
isOnWaitlist.value = true;
|
||||
waitlistPosition.value = response.position;
|
||||
|
||||
toast.add({
|
||||
title: "Added to Waitlist",
|
||||
description: `You're #${response.position} on the waitlist. We'll email you if a spot opens up.`,
|
||||
color: "orange",
|
||||
});
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error.data?.statusMessage || "Failed to join waitlist. Please try again.";
|
||||
|
||||
toast.add({
|
||||
title: "Couldn't Join Waitlist",
|
||||
description: errorMessage,
|
||||
color: "red",
|
||||
});
|
||||
} finally {
|
||||
isJoiningWaitlist.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Leave waitlist handler
|
||||
const handleLeaveWaitlist = async () => {
|
||||
isJoiningWaitlist.value = true;
|
||||
|
||||
try {
|
||||
const email = memberData.value?.email || waitlistForm.value.email;
|
||||
|
||||
await $fetch(`/api/events/${route.params.id}/waitlist`, {
|
||||
method: "DELETE",
|
||||
body: { email },
|
||||
});
|
||||
|
||||
isOnWaitlist.value = false;
|
||||
waitlistPosition.value = 0;
|
||||
|
||||
toast.add({
|
||||
title: "Removed from Waitlist",
|
||||
description: "You've been removed from the waitlist.",
|
||||
color: "blue",
|
||||
});
|
||||
} catch (error) {
|
||||
toast.add({
|
||||
title: "Error",
|
||||
description: "Failed to leave waitlist. Please try again.",
|
||||
color: "red",
|
||||
});
|
||||
} finally {
|
||||
isJoiningWaitlist.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Format date for display
|
||||
const formatDate = (dateString) => {
|
||||
const date = new Date(dateString);
|
||||
|
|
@ -671,7 +905,6 @@ const handleCancelRegistration = async () => {
|
|||
|
||||
// Handle ticket purchase success
|
||||
const handleTicketSuccess = (response) => {
|
||||
console.log("Ticket purchased successfully:", response);
|
||||
// Update registered count if needed
|
||||
if (event.value.registeredCount !== undefined) {
|
||||
event.value.registeredCount++;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue