monacousa-portal/pages/events/mockup.vue

710 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="events-mockup">
<!-- Header -->
<header class="events-header">
<div
v-motion
:initial="{ opacity: 0, y: -20 }"
:enter="{ opacity: 1, y: 0 }"
class="events-header__content"
>
<h1 class="events-header__title">Events</h1>
<p class="events-header__subtitle">Discover and join MonacoUSA events</p>
</div>
<div
v-motion
:initial="{ opacity: 0, y: -20 }"
:enter="{ opacity: 1, y: 0, transition: { delay: 100 } }"
class="events-header__actions"
>
<FloatingInput
v-model="searchQuery"
label="Search events..."
leftIcon="search"
variant="glass"
clearable
/>
<MonacoButton variant="primary" icon="plus">
Create Event
</MonacoButton>
</div>
</header>
<!-- Filter Bar -->
<div
v-motion
:initial="{ opacity: 0, y: 20 }"
:enter="{ opacity: 1, y: 0, transition: { delay: 200 } }"
class="events-filters"
>
<div class="filter-chips">
<button
v-for="filter in filters"
:key="filter.value"
class="filter-chip"
:class="{ 'filter-chip--active': selectedFilter === filter.value }"
@click="selectedFilter = filter.value"
>
{{ filter.label }}
<span v-if="filter.count" class="filter-chip__count">{{ filter.count }}</span>
</button>
</div>
<div class="view-toggles">
<button
class="view-toggle"
:class="{ 'view-toggle--active': viewMode === 'grid' }"
@click="viewMode = 'grid'"
>
<span>⊞</span> Grid
</button>
<button
class="view-toggle"
:class="{ 'view-toggle--active': viewMode === 'list' }"
@click="viewMode = 'list'"
>
<span>☰</span> List
</button>
</div>
</div>
<!-- Events Grid/List -->
<div
class="events-container"
:class="`events-container--${viewMode}`"
>
<div
v-for="(event, index) in events"
:key="event.id"
v-motion
:initial="{ opacity: 0, y: 30 }"
:enter="{
opacity: 1,
y: 0,
transition: {
delay: 300 + (index * 50),
type: 'spring',
stiffness: 200,
damping: 20
}
}"
class="event-card-full"
:class="{ 'event-card-full--featured': event.featured }"
>
<div class="event-card-full__image">
<img :src="event.image" :alt="event.title" />
<div v-if="event.featured" class="event-card-full__badge">Featured</div>
<div class="event-card-full__date-overlay">
<span class="date-day">{{ event.date.day }}</span>
<span class="date-month">{{ event.date.month }}</span>
</div>
</div>
<div class="event-card-full__content">
<div class="event-card-full__header">
<h3 class="event-card-full__title">{{ event.title }}</h3>
<span class="event-card-full__category">{{ event.category }}</span>
</div>
<p class="event-card-full__description">{{ event.description }}</p>
<div class="event-card-full__meta">
<div class="meta-item">
<span class="meta-icon">📍</span>
<span class="meta-text">{{ event.location }}</span>
</div>
<div class="meta-item">
<span class="meta-icon">🕐</span>
<span class="meta-text">{{ event.time }}</span>
</div>
<div class="meta-item">
<span class="meta-icon">👥</span>
<span class="meta-text">{{ event.attendees }} attending</span>
</div>
</div>
<div class="event-card-full__footer">
<div class="event-card-full__price">
<span v-if="event.price === 0" class="price-free">Free</span>
<span v-else class="price-amount">${{ event.price }}</span>
</div>
<div class="event-card-full__actions">
<MonacoButton variant="ghost" size="sm" icon="heart">
Save
</MonacoButton>
<MonacoButton variant="primary" size="sm">
Register
</MonacoButton>
</div>
</div>
</div>
</div>
</div>
<!-- Load More -->
<div
v-motion
:initial="{ opacity: 0 }"
:enter="{ opacity: 1, transition: { delay: 800 } }"
class="load-more"
>
<MonacoButton variant="glass" icon="refresh" block>
Load More Events
</MonacoButton>
</div>
<!-- Floating Calendar Widget -->
<GlassCard
variant="glass"
class="calendar-widget"
:animated="true"
:delay="900"
>
<h4 class="calendar-widget__title">Quick Calendar</h4>
<div class="calendar-mini">
<div class="calendar-mini__header">
<button class="calendar-nav"></button>
<span class="calendar-month">December 2024</span>
<button class="calendar-nav"></button>
</div>
<div class="calendar-mini__grid">
<div
v-for="day in 31"
:key="day"
class="calendar-day"
:class="{
'calendar-day--event': [5, 12, 15, 22, 31].includes(day),
'calendar-day--today': day === 10
}"
>
{{ day }}
</div>
</div>
</div>
</GlassCard>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import GlassCard from '~/components/ui/GlassCard.vue'
import MonacoButton from '~/components/ui/MonacoButton.vue'
import FloatingInput from '~/components/ui/FloatingInput.vue'
const searchQuery = ref('')
const selectedFilter = ref('all')
const viewMode = ref('grid')
const filters = ref([
{ label: 'All Events', value: 'all', count: 24 },
{ label: 'Upcoming', value: 'upcoming', count: 12 },
{ label: 'This Week', value: 'week', count: 5 },
{ label: 'This Month', value: 'month', count: 8 },
{ label: 'Free', value: 'free', count: 7 },
{ label: 'Members Only', value: 'members', count: 10 }
])
const events = ref([
{
id: 1,
title: 'Monaco Winter Gala 2024',
category: 'Social',
description: 'Join us for an elegant evening celebrating the Monaco-US friendship with fine dining, live entertainment, and networking.',
image: '/api/placeholder/400/250',
date: { day: '15', month: 'DEC' },
time: '7:00 PM - 11:00 PM',
location: 'Grand Ballroom, Downtown',
attendees: 120,
price: 150,
featured: true
},
{
id: 2,
title: 'Business Networking Lunch',
category: 'Networking',
description: 'Connect with fellow Monaco-US business professionals over lunch and expand your network.',
image: '/api/placeholder/400/250',
date: { day: '18', month: 'DEC' },
time: '12:00 PM - 2:00 PM',
location: 'Monaco Club',
attendees: 45,
price: 35,
featured: false
},
{
id: 3,
title: 'Cultural Exchange Workshop',
category: 'Education',
description: 'Learn about Monaco culture, history, and traditions in this interactive workshop.',
image: '/api/placeholder/400/250',
date: { day: '20', month: 'DEC' },
time: '3:00 PM - 5:00 PM',
location: 'Community Center',
attendees: 30,
price: 0,
featured: false
},
{
id: 4,
title: 'New Year Celebration',
category: 'Social',
description: 'Ring in the new year with the MonacoUSA community! Champagne toast, live music, and dancing.',
image: '/api/placeholder/400/250',
date: { day: '31', month: 'DEC' },
time: '9:00 PM - 2:00 AM',
location: 'Monaco Club Rooftop',
attendees: 200,
price: 200,
featured: true
},
{
id: 5,
title: 'Wine Tasting Evening',
category: 'Social',
description: 'Discover exceptional wines from Monaco and France guided by our sommelier.',
image: '/api/placeholder/400/250',
date: { day: '22', month: 'DEC' },
time: '6:00 PM - 9:00 PM',
location: 'Wine Gallery',
attendees: 60,
price: 75,
featured: false
},
{
id: 6,
title: 'Board Meeting',
category: 'Meeting',
description: 'Monthly board meeting to discuss club activities and initiatives.',
image: '/api/placeholder/400/250',
date: { day: '28', month: 'DEC' },
time: '5:00 PM - 7:00 PM',
location: 'Conference Room A',
attendees: 15,
price: 0,
featured: false
}
])
</script>
<style scoped lang="scss">
.events-mockup {
padding: 2rem;
max-width: 1400px;
margin: 0 auto;
background: linear-gradient(135deg, #fef2f2 0%, #ffffff 100%);
min-height: 100vh;
position: relative;
}
.events-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
flex-wrap: wrap;
gap: 1.5rem;
&__content {
flex: 1;
}
&__title {
font-size: 2.5rem;
font-weight: 700;
color: #27272a;
margin: 0 0 0.5rem;
}
&__subtitle {
font-size: 1.125rem;
color: #6b7280;
margin: 0;
}
&__actions {
display: flex;
gap: 1rem;
align-items: center;
.floating-input {
width: 300px;
}
}
}
.events-filters {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
padding: 1rem;
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(20px);
border-radius: 16px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
flex-wrap: wrap;
gap: 1rem;
}
.filter-chips {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.filter-chip {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
background: rgba(255, 255, 255, 0.8);
border: 2px solid transparent;
border-radius: 20px;
font-size: 0.875rem;
font-weight: 500;
color: #6b7280;
cursor: pointer;
transition: all 0.2s;
&:hover {
background: rgba(220, 38, 38, 0.05);
border-color: rgba(220, 38, 38, 0.2);
}
&--active {
background: #dc2626;
color: white;
border-color: #dc2626;
.filter-chip__count {
background: rgba(255, 255, 255, 0.2);
}
}
&__count {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 1.25rem;
height: 1.25rem;
padding: 0 0.25rem;
background: rgba(220, 38, 38, 0.1);
border-radius: 10px;
font-size: 0.75rem;
font-weight: 600;
}
}
.view-toggles {
display: flex;
gap: 0.5rem;
}
.view-toggle {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
background: rgba(255, 255, 255, 0.8);
border: 2px solid transparent;
border-radius: 10px;
font-size: 0.875rem;
font-weight: 500;
color: #6b7280;
cursor: pointer;
transition: all 0.2s;
&:hover {
background: rgba(220, 38, 38, 0.05);
border-color: rgba(220, 38, 38, 0.2);
}
&--active {
background: white;
color: #dc2626;
border-color: #dc2626;
}
}
.events-container {
display: grid;
gap: 1.5rem;
margin-bottom: 2rem;
&--grid {
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
}
&--list {
grid-template-columns: 1fr;
}
}
.event-card-full {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(20px);
border-radius: 16px;
overflow: hidden;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&:hover {
transform: translateY(-4px);
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.12);
}
&--featured {
border: 2px solid #dc2626;
box-shadow: 0 4px 16px rgba(220, 38, 38, 0.15);
}
&__image {
position: relative;
height: 200px;
overflow: hidden;
background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
&__badge {
position: absolute;
top: 1rem;
left: 1rem;
padding: 0.25rem 0.75rem;
background: #dc2626;
color: white;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
}
&__date-overlay {
position: absolute;
top: 1rem;
right: 1rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 3.5rem;
height: 3.5rem;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
.date-day {
font-size: 1.25rem;
font-weight: 700;
color: #dc2626;
line-height: 1;
}
.date-month {
font-size: 0.75rem;
color: #6b7280;
text-transform: uppercase;
}
}
&__content {
padding: 1.5rem;
}
&__header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 0.75rem;
}
&__title {
flex: 1;
margin: 0;
font-size: 1.25rem;
font-weight: 600;
color: #27272a;
}
&__category {
padding: 0.25rem 0.75rem;
background: rgba(220, 38, 38, 0.1);
color: #dc2626;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 500;
}
&__description {
margin: 0 0 1rem;
font-size: 0.875rem;
color: #6b7280;
line-height: 1.5;
}
&__meta {
display: flex;
flex-wrap: wrap;
gap: 1rem;
margin-bottom: 1rem;
}
&__footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 1rem;
border-top: 1px solid rgba(220, 38, 38, 0.1);
}
&__price {
.price-free {
padding: 0.25rem 0.75rem;
background: rgba(16, 185, 129, 0.1);
color: #10b981;
border-radius: 8px;
font-size: 0.875rem;
font-weight: 600;
}
.price-amount {
font-size: 1.25rem;
font-weight: 700;
color: #dc2626;
}
}
&__actions {
display: flex;
gap: 0.5rem;
}
}
.meta-item {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.875rem;
color: #6b7280;
.meta-icon {
font-size: 1rem;
}
}
.load-more {
max-width: 400px;
margin: 2rem auto;
}
.calendar-widget {
position: fixed;
bottom: 2rem;
right: 2rem;
width: 280px;
z-index: 10;
&__title {
margin: 0 0 1rem;
font-size: 1rem;
font-weight: 600;
color: #dc2626;
}
}
.calendar-mini {
&__header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
&__grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 0.25rem;
}
}
.calendar-nav {
width: 1.5rem;
height: 1.5rem;
display: flex;
align-items: center;
justify-content: center;
background: transparent;
border: none;
color: #dc2626;
cursor: pointer;
border-radius: 4px;
transition: background 0.2s;
&:hover {
background: rgba(220, 38, 38, 0.1);
}
}
.calendar-month {
font-size: 0.875rem;
font-weight: 600;
color: #27272a;
}
.calendar-day {
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
color: #6b7280;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
&:hover {
background: rgba(220, 38, 38, 0.05);
}
&--event {
background: rgba(220, 38, 38, 0.1);
color: #dc2626;
font-weight: 600;
}
&--today {
background: #dc2626;
color: white;
font-weight: 600;
}
}
// Responsive
@media (max-width: 768px) {
.events-header {
&__actions {
width: 100%;
.floating-input {
flex: 1;
}
}
}
.events-container--grid {
grid-template-columns: 1fr;
}
.calendar-widget {
display: none;
}
}
</style>