monacousa-portal/pages/member/events/index.vue

552 lines
17 KiB
Vue

<template>
<div>
<!-- Header -->
<div class="mb-6">
<h1 class="text-h4 font-weight-bold mb-2">Events</h1>
<p class="text-body-1 text-medium-emphasis">Discover and register for upcoming MonacoUSA events</p>
</div>
<!-- Filters -->
<v-card class="mb-6" elevation="1">
<v-card-text>
<v-row align="center">
<v-col cols="12" md="4">
<v-text-field
v-model="searchQuery"
prepend-inner-icon="mdi-magnify"
label="Search events"
variant="outlined"
density="compact"
clearable
hide-details
/>
</v-col>
<v-col cols="12" md="3">
<v-select
v-model="selectedCategory"
:items="categories"
label="Category"
variant="outlined"
density="compact"
clearable
hide-details
/>
</v-col>
<v-col cols="12" md="3">
<v-select
v-model="selectedMonth"
:items="months"
label="Month"
variant="outlined"
density="compact"
clearable
hide-details
/>
</v-col>
<v-col cols="12" md="2">
<v-btn
color="error"
variant="flat"
block
@click="resetFilters"
>
Reset Filters
</v-btn>
</v-col>
</v-row>
</v-card-text>
</v-card>
<!-- Event Tabs -->
<v-tabs
v-model="tab"
color="error"
class="mb-6"
>
<v-tab value="upcoming">
<v-icon start>mdi-calendar-clock</v-icon>
Upcoming Events
</v-tab>
<v-tab value="registered">
<v-icon start>mdi-calendar-check</v-icon>
My Registrations
</v-tab>
<v-tab value="past">
<v-icon start>mdi-history</v-icon>
Past Events
</v-tab>
</v-tabs>
<!-- Tab Content -->
<v-window v-model="tab">
<!-- Upcoming Events Tab -->
<v-window-item value="upcoming">
<v-row>
<v-col
v-for="event in upcomingEvents"
:key="event.id"
cols="12"
md="6"
lg="4"
>
<v-card elevation="2" hover class="h-100 d-flex flex-column">
<!-- Event Image -->
<v-img
:src="event.image"
height="200"
cover
gradient="to bottom, rgba(0,0,0,.1), rgba(0,0,0,.5)"
>
<v-card-title class="text-white">
{{ event.title }}
</v-card-title>
</v-img>
<v-card-text class="flex-grow-1">
<!-- Event Details -->
<div class="mb-3">
<v-chip
:color="getCategoryColor(event.category)"
size="small"
variant="tonal"
class="mb-2"
>
{{ event.category }}
</v-chip>
</div>
<p class="text-body-2 mb-3">{{ event.description }}</p>
<div class="text-caption text-medium-emphasis">
<div class="d-flex align-center mb-1">
<v-icon size="x-small" class="mr-2">mdi-calendar</v-icon>
{{ formatDate(event.date) }}
</div>
<div class="d-flex align-center mb-1">
<v-icon size="x-small" class="mr-2">mdi-clock-outline</v-icon>
{{ event.time }}
</div>
<div class="d-flex align-center mb-1">
<v-icon size="x-small" class="mr-2">mdi-map-marker</v-icon>
{{ event.location }}
</div>
<div class="d-flex align-center">
<v-icon size="x-small" class="mr-2">mdi-account-group</v-icon>
{{ event.attendees }} / {{ event.capacity }} attendees
</div>
</div>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
variant="text"
color="error"
@click="viewEventDetails(event)"
>
Learn More
</v-btn>
<v-btn
variant="flat"
color="error"
:disabled="event.attendees >= event.capacity"
@click="registerForEvent(event)"
>
{{ event.attendees >= event.capacity ? 'Full' : 'Register' }}
</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
<!-- Empty State -->
<v-card v-if="upcomingEvents.length === 0" class="text-center pa-8" elevation="0">
<v-icon size="64" color="grey-lighten-1">mdi-calendar-blank</v-icon>
<h3 class="text-h6 mt-4">No upcoming events</h3>
<p class="text-body-2 text-medium-emphasis">Check back later for new events</p>
</v-card>
</v-window-item>
<!-- Registered Events Tab -->
<v-window-item value="registered">
<v-row>
<v-col cols="12">
<v-card elevation="1">
<v-list lines="three">
<v-list-item
v-for="registration in registeredEvents"
:key="registration.id"
class="py-3"
>
<template v-slot:prepend>
<v-avatar size="60" rounded="lg">
<v-img :src="registration.image" cover />
</v-avatar>
</template>
<v-list-item-title class="font-weight-medium">
{{ registration.title }}
</v-list-item-title>
<v-list-item-subtitle>
<div class="d-flex gap-3 mt-1">
<span><v-icon size="x-small">mdi-calendar</v-icon> {{ formatDate(registration.date) }}</span>
<span><v-icon size="x-small">mdi-clock-outline</v-icon> {{ registration.time }}</span>
<span><v-icon size="x-small">mdi-map-marker</v-icon> {{ registration.location }}</span>
</div>
</v-list-item-subtitle>
<template v-slot:append>
<div class="text-right">
<v-chip
color="success"
size="small"
variant="tonal"
class="mb-2"
>
<v-icon start size="x-small">mdi-check</v-icon>
Registered
</v-chip>
<div>
<v-btn
variant="text"
size="small"
color="error"
@click="cancelRegistration(registration)"
>
Cancel
</v-btn>
</div>
</div>
</template>
</v-list-item>
</v-list>
<!-- Empty State -->
<div v-if="registeredEvents.length === 0" class="text-center pa-8">
<v-icon size="64" color="grey-lighten-1">mdi-calendar-remove</v-icon>
<h3 class="text-h6 mt-4">No registered events</h3>
<p class="text-body-2 text-medium-emphasis">Browse upcoming events to find something interesting</p>
</div>
</v-card>
</v-col>
</v-row>
</v-window-item>
<!-- Past Events Tab -->
<v-window-item value="past">
<v-row>
<v-col cols="12">
<v-timeline side="end" density="compact">
<v-timeline-item
v-for="event in pastEvents"
:key="event.id"
dot-color="grey"
size="small"
>
<template v-slot:opposite>
<div class="text-caption text-medium-emphasis">
{{ formatDate(event.date) }}
</div>
</template>
<v-card elevation="1">
<v-card-title class="text-h6">{{ event.title }}</v-card-title>
<v-card-text>
<p class="text-body-2 mb-2">{{ event.description }}</p>
<div class="text-caption text-medium-emphasis">
<v-icon size="x-small">mdi-account-group</v-icon>
{{ event.attendees }} attendees
</div>
</v-card-text>
<v-card-actions>
<v-btn
variant="text"
size="small"
color="error"
@click="viewEventPhotos(event)"
>
View Photos
</v-btn>
<v-btn
variant="text"
size="small"
@click="viewEventDetails(event)"
>
View Details
</v-btn>
</v-card-actions>
</v-card>
</v-timeline-item>
</v-timeline>
<!-- Empty State -->
<v-card v-if="pastEvents.length === 0" class="text-center pa-8" elevation="0">
<v-icon size="64" color="grey-lighten-1">mdi-history</v-icon>
<h3 class="text-h6 mt-4">No past events</h3>
<p class="text-body-2 text-medium-emphasis">Past events will appear here</p>
</v-card>
</v-col>
</v-row>
</v-window-item>
</v-window>
<!-- Event Details Dialog -->
<v-dialog v-model="detailsDialog" max-width="600">
<v-card v-if="selectedEvent">
<v-img
:src="selectedEvent.image"
height="200"
cover
/>
<v-card-title>{{ selectedEvent.title }}</v-card-title>
<v-card-text>
<v-chip
:color="getCategoryColor(selectedEvent.category)"
size="small"
variant="tonal"
class="mb-3"
>
{{ selectedEvent.category }}
</v-chip>
<p class="mb-4">{{ selectedEvent.fullDescription || selectedEvent.description }}</p>
<v-list density="compact">
<v-list-item>
<template v-slot:prepend>
<v-icon>mdi-calendar</v-icon>
</template>
<v-list-item-title>{{ formatDate(selectedEvent.date) }}</v-list-item-title>
</v-list-item>
<v-list-item>
<template v-slot:prepend>
<v-icon>mdi-clock-outline</v-icon>
</template>
<v-list-item-title>{{ selectedEvent.time }}</v-list-item-title>
</v-list-item>
<v-list-item>
<template v-slot:prepend>
<v-icon>mdi-map-marker</v-icon>
</template>
<v-list-item-title>{{ selectedEvent.location }}</v-list-item-title>
</v-list-item>
<v-list-item>
<template v-slot:prepend>
<v-icon>mdi-account-group</v-icon>
</template>
<v-list-item-title>{{ selectedEvent.attendees }} / {{ selectedEvent.capacity }} attendees</v-list-item-title>
</v-list-item>
</v-list>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn variant="text" @click="detailsDialog = false">Close</v-btn>
<v-btn
color="error"
variant="flat"
:disabled="selectedEvent.attendees >= selectedEvent.capacity"
@click="registerForEvent(selectedEvent)"
>
Register
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script setup lang="ts">
definePageMeta({
layout: 'member',
middleware: 'member'
});
// State
const tab = ref('upcoming');
const searchQuery = ref('');
const selectedCategory = ref(null);
const selectedMonth = ref(null);
const detailsDialog = ref(false);
const selectedEvent = ref(null);
// Filter options
const categories = ref([
'Networking',
'Workshop',
'Social',
'Cultural',
'Business',
'Charity'
]);
const months = ref([
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
]);
// Mock event data
const upcomingEvents = ref([
{
id: 1,
title: 'Monaco Business Networking',
description: 'Connect with fellow Monaco entrepreneurs and business leaders',
fullDescription: 'Join us for an evening of networking with Monaco\'s business community. This event brings together entrepreneurs, executives, and professionals from various industries.',
category: 'Networking',
date: '2024-02-15',
time: '6:00 PM - 8:00 PM',
location: 'Monaco Yacht Club',
image: 'https://picsum.photos/400/300?random=1',
attendees: 45,
capacity: 100
},
{
id: 2,
title: 'Digital Marketing Workshop',
description: 'Learn the latest digital marketing strategies and techniques',
category: 'Workshop',
date: '2024-02-22',
time: '2:00 PM - 5:00 PM',
location: 'Conference Center',
image: 'https://picsum.photos/400/300?random=2',
attendees: 28,
capacity: 50
},
{
id: 3,
title: 'Annual Gala Dinner',
description: 'Celebrate the year with an elegant evening of dining and entertainment',
category: 'Social',
date: '2024-03-05',
time: '7:00 PM - 11:00 PM',
location: 'Hotel Hermitage',
image: 'https://picsum.photos/400/300?random=3',
attendees: 120,
capacity: 150
},
{
id: 4,
title: 'Monaco Grand Prix Viewing',
description: 'Watch the Monaco Grand Prix from our exclusive viewing area',
category: 'Social',
date: '2024-05-26',
time: '12:00 PM - 6:00 PM',
location: 'Private Terrace',
image: 'https://picsum.photos/400/300?random=4',
attendees: 75,
capacity: 75
}
]);
const registeredEvents = ref([
{
id: 1,
title: 'Monaco Business Networking',
date: '2024-02-15',
time: '6:00 PM',
location: 'Monaco Yacht Club',
image: 'https://picsum.photos/400/300?random=1'
},
{
id: 3,
title: 'Annual Gala Dinner',
date: '2024-03-05',
time: '7:00 PM',
location: 'Hotel Hermitage',
image: 'https://picsum.photos/400/300?random=3'
}
]);
const pastEvents = ref([
{
id: 5,
title: 'New Year Celebration',
description: 'Welcomed 2024 with a spectacular celebration',
date: '2024-01-01',
attendees: 200
},
{
id: 6,
title: 'Investment Seminar',
description: 'Expert insights on investment strategies for 2024',
date: '2024-01-15',
attendees: 65
}
]);
// Methods
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
});
};
const getCategoryColor = (category: string) => {
const colors: Record<string, string> = {
'Networking': 'blue',
'Workshop': 'purple',
'Social': 'green',
'Cultural': 'orange',
'Business': 'indigo',
'Charity': 'pink'
};
return colors[category] || 'grey';
};
const resetFilters = () => {
searchQuery.value = '';
selectedCategory.value = null;
selectedMonth.value = null;
};
const viewEventDetails = (event: any) => {
selectedEvent.value = event;
detailsDialog.value = true;
};
const registerForEvent = (event: any) => {
console.log('Registering for event:', event.title);
// Add to registered events
if (!registeredEvents.value.find(e => e.id === event.id)) {
registeredEvents.value.push({
id: event.id,
title: event.title,
date: event.date,
time: event.time,
location: event.location,
image: event.image
});
}
detailsDialog.value = false;
};
const cancelRegistration = (event: any) => {
console.log('Canceling registration for:', event.title);
const index = registeredEvents.value.findIndex(e => e.id === event.id);
if (index > -1) {
registeredEvents.value.splice(index, 1);
}
};
const viewEventPhotos = (event: any) => {
console.log('Viewing photos for:', event.title);
};
</script>
<style scoped>
.gap-3 {
gap: 0.75rem;
}
</style>