feat: Reorganize platform into member, board, and admin sections
Major platform reorganization implementing role-based portal sections:
## Infrastructure Changes
- Created role-based middleware for member, board, and admin access
- Updated main dashboard router to redirect based on highest privilege
- Implemented access hierarchy: Admin > Board > Member
## New Layouts
- Member layout: Simplified navigation for regular members
- Board layout: Enhanced tools for board member management
- Admin layout: Full system administration capabilities
## Member Portal (/member/*)
- Dashboard: Profile overview, events, payments, activity tracking
- Events: Browse, register, and manage event participation
- Profile: Complete personal and professional information management
- Resources: Access to documents, guides, FAQs, and quick links
## Board Portal (/board/*)
- Dashboard: Statistics, dues management, board-specific tools
- Members: Comprehensive member management with filtering
## Admin Portal (/admin/*)
- Dashboard: System overview and administrative controls (existing)
## Design Implementation
- Monaco red (#dc2626) as primary accent color
- Modern card-based layouts with consistent spacing
- Responsive design for all screen sizes
- Glass morphism effects for enhanced visual appeal
This reorganization provides clear separation of concerns based on user privileges while maintaining a cohesive user experience across all sections.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-30 22:00:59 +02:00
< template >
2025-09-04 23:06:38 +02:00
< div class = "member-dashboard" >
feat: Reorganize platform into member, board, and admin sections
Major platform reorganization implementing role-based portal sections:
## Infrastructure Changes
- Created role-based middleware for member, board, and admin access
- Updated main dashboard router to redirect based on highest privilege
- Implemented access hierarchy: Admin > Board > Member
## New Layouts
- Member layout: Simplified navigation for regular members
- Board layout: Enhanced tools for board member management
- Admin layout: Full system administration capabilities
## Member Portal (/member/*)
- Dashboard: Profile overview, events, payments, activity tracking
- Events: Browse, register, and manage event participation
- Profile: Complete personal and professional information management
- Resources: Access to documents, guides, FAQs, and quick links
## Board Portal (/board/*)
- Dashboard: Statistics, dues management, board-specific tools
- Members: Comprehensive member management with filtering
## Admin Portal (/admin/*)
- Dashboard: System overview and administrative controls (existing)
## Design Implementation
- Monaco red (#dc2626) as primary accent color
- Modern card-based layouts with consistent spacing
- Responsive design for all screen sizes
- Glass morphism effects for enhanced visual appeal
This reorganization provides clear separation of concerns based on user privileges while maintaining a cohesive user experience across all sections.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-30 22:00:59 +02:00
<!-- 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 - c o l >
< v -col cols = "12" md = "3" >
< v -select
v - model = "selectedCategory"
: items = "categories"
label = "Category"
variant = "outlined"
density = "compact"
clearable
hide - details
/ >
< / v - c o l >
< v -col cols = "12" md = "3" >
< v -select
v - model = "selectedMonth"
: items = "months"
label = "Month"
variant = "outlined"
density = "compact"
clearable
hide - details
/ >
< / v - c o l >
< v -col cols = "12" md = "2" >
< v -btn
color = "error"
variant = "flat"
block
@ click = "resetFilters"
>
Reset Filters
< / v - b t n >
< / v - c o l >
< / v - r o w >
< / v - c a r d - t e x t >
< / v - c a r d >
<!-- Event Tabs -- >
< v -tabs
v - model = "tab"
color = "error"
class = "mb-6"
>
< v -tab value = "upcoming" >
< v -icon start > mdi - calendar - clock < / v - i c o n >
Upcoming Events
< / v - t a b >
< v -tab value = "registered" >
< v -icon start > mdi - calendar - check < / v - i c o n >
My Registrations
< / v - t a b >
< v -tab value = "past" >
< v -icon start > mdi - history < / v - i c o n >
Past Events
< / v - t a b >
< / v - t a b s >
<!-- 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 - c a r d - t i t l e >
< / v - i m g >
< 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 - c h i p >
< / 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 - i c o n >
{ { formatDate ( event . date ) } }
< / div >
< div class = "d-flex align-center mb-1" >
< v -icon size = "x-small" class = "mr-2" > mdi - clock - outline < / v - i c o n >
{ { event . time } }
< / div >
< div class = "d-flex align-center mb-1" >
< v -icon size = "x-small" class = "mr-2" > mdi - map - marker < / v - i c o n >
{ { event . location } }
< / div >
< div class = "d-flex align-center" >
< v -icon size = "x-small" class = "mr-2" > mdi - account - group < / v - i c o n >
{ { event . attendees } } / { { event . capacity } } attendees
< / div >
< / div >
< / v - c a r d - t e x t >
< v -card -actions >
< v -spacer / >
< v -btn
variant = "text"
color = "error"
@ click = "viewEventDetails(event)"
>
Learn More
< / v - b t n >
< v -btn
variant = "flat"
color = "error"
: disabled = "event.attendees >= event.capacity"
@ click = "registerForEvent(event)"
>
{ { event . attendees >= event . capacity ? 'Full' : 'Register' } }
< / v - b t n >
< / v - c a r d - a c t i o n s >
< / v - c a r d >
< / v - c o l >
< / v - r o w >
<!-- 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 - i c o n >
< 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 - c a r d >
< / v - w i n d o w - i t e m >
<!-- 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 - a v a t a r >
< / template >
< v -list -item -title class = "font-weight-medium" >
{ { registration . title } }
< / v - l i s t - i t e m - t i t l e >
< v -list -item -subtitle >
< div class = "d-flex gap-3 mt-1" >
< span > < v -icon size = "x-small" > mdi - calendar < / v - i c o n > { { f o r m a t D a t e ( r e g i s t r a t i o n . d a t e ) } } < / s p a n >
< span > < v -icon size = "x-small" > mdi - clock - outline < / v - i c o n > { { r e g i s t r a t i o n . t i m e } } < / s p a n >
< span > < v -icon size = "x-small" > mdi - map - marker < / v - i c o n > { { r e g i s t r a t i o n . l o c a t i o n } } < / s p a n >
< / div >
< / v - l i s t - i t e m - s u b t i t l e >
< 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 - i c o n >
Registered
< / v - c h i p >
< div >
< v -btn
variant = "text"
size = "small"
color = "error"
@ click = "cancelRegistration(registration)"
>
Cancel
< / v - b t n >
< / div >
< / div >
< / template >
< / v - l i s t - i t e m >
< / v - l i s t >
<!-- 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 - i c o n >
< 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 - c a r d >
< / v - c o l >
< / v - r o w >
< / v - w i n d o w - i t e m >
<!-- 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 - c a r d - t i t l e >
< 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 - i c o n >
{ { event . attendees } } attendees
< / div >
< / v - c a r d - t e x t >
< v -card -actions >
< v -btn
variant = "text"
size = "small"
color = "error"
@ click = "viewEventPhotos(event)"
>
View Photos
< / v - b t n >
< v -btn
variant = "text"
size = "small"
@ click = "viewEventDetails(event)"
>
View Details
< / v - b t n >
< / v - c a r d - a c t i o n s >
< / v - c a r d >
< / v - t i m e l i n e - i t e m >
< / v - t i m e l i n e >
<!-- 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 - i c o n >
< 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 - c a r d >
< / v - c o l >
< / v - r o w >
< / v - w i n d o w - i t e m >
< / v - w i n d o w >
<!-- 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 - c a r d - t i t l e >
< v -card -text >
< v -chip
: color = "getCategoryColor(selectedEvent.category)"
size = "small"
variant = "tonal"
class = "mb-3"
>
{ { selectedEvent . category } }
< / v - c h i p >
< 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 - i c o n >
< / template >
< v -list -item -title > { { formatDate ( selectedEvent . date ) } } < / v - l i s t - i t e m - t i t l e >
< / v - l i s t - i t e m >
< v -list -item >
< template v -slot : prepend >
< v -icon > mdi - clock - outline < / v - i c o n >
< / template >
< v -list -item -title > { { selectedEvent . time } } < / v - l i s t - i t e m - t i t l e >
< / v - l i s t - i t e m >
< v -list -item >
< template v -slot : prepend >
< v -icon > mdi - map - marker < / v - i c o n >
< / template >
< v -list -item -title > { { selectedEvent . location } } < / v - l i s t - i t e m - t i t l e >
< / v - l i s t - i t e m >
< v -list -item >
< template v -slot : prepend >
< v -icon > mdi - account - group < / v - i c o n >
< / template >
< v -list -item -title > { { selectedEvent . attendees } } / { { selectedEvent . capacity } } attendees < / v - l i s t - i t e m - t i t l e >
< / v - l i s t - i t e m >
< / v - l i s t >
< / v - c a r d - t e x t >
< v -card -actions >
< v -spacer / >
< v -btn variant = "text" @ click = "detailsDialog = false" > Close < / v - b t n >
< v -btn
color = "error"
variant = "flat"
: disabled = "selectedEvent.attendees >= selectedEvent.capacity"
@ click = "registerForEvent(selectedEvent)"
>
Register
< / v - b t n >
< / v - c a r d - a c t i o n s >
< / v - c a r d >
< / v - d i a l o g >
< / 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.75 rem ;
}
< / style >