Simplify member dashboard - remove points, payments, stats, and social features
Build And Push Image / docker (push) Successful in 1m57s
Details
Build And Push Image / docker (push) Successful in 1m57s
Details
This commit is contained in:
parent
41eeb8650c
commit
696b321373
|
|
@ -65,7 +65,7 @@ import { computed } from 'vue';
|
||||||
|
|
||||||
interface TimelineActivity {
|
interface TimelineActivity {
|
||||||
id: string | number;
|
id: string | number;
|
||||||
type: 'event' | 'payment' | 'achievement' | 'profile' | 'system';
|
type: 'event' | 'profile';
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
timestamp: string | Date;
|
timestamp: string | Date;
|
||||||
|
|
@ -92,10 +92,7 @@ const visibleActivities = computed(() => {
|
||||||
const getIconColor = (type: string) => {
|
const getIconColor = (type: string) => {
|
||||||
const colors: Record<string, string> = {
|
const colors: Record<string, string> = {
|
||||||
event: 'error',
|
event: 'error',
|
||||||
payment: 'success',
|
profile: 'info'
|
||||||
achievement: 'warning',
|
|
||||||
profile: 'info',
|
|
||||||
system: 'grey'
|
|
||||||
};
|
};
|
||||||
return colors[type] || 'grey';
|
return colors[type] || 'grey';
|
||||||
};
|
};
|
||||||
|
|
@ -184,26 +181,11 @@ const formatTime = (timestamp: string | Date) => {
|
||||||
background: linear-gradient(135deg, rgba(220, 38, 38, 0.1), rgba(220, 38, 38, 0.05));
|
background: linear-gradient(135deg, rgba(220, 38, 38, 0.1), rgba(220, 38, 38, 0.05));
|
||||||
}
|
}
|
||||||
|
|
||||||
&--payment {
|
|
||||||
border-color: rgb(34, 197, 94);
|
|
||||||
background: linear-gradient(135deg, rgba(34, 197, 94, 0.1), rgba(34, 197, 94, 0.05));
|
|
||||||
}
|
|
||||||
|
|
||||||
&--achievement {
|
|
||||||
border-color: rgb(245, 158, 11);
|
|
||||||
background: linear-gradient(135deg, rgba(245, 158, 11, 0.1), rgba(245, 158, 11, 0.05));
|
|
||||||
}
|
|
||||||
|
|
||||||
&--profile {
|
&--profile {
|
||||||
border-color: rgb(59, 130, 246);
|
border-color: rgb(59, 130, 246);
|
||||||
background: linear-gradient(135deg, rgba(59, 130, 246, 0.1), rgba(59, 130, 246, 0.05));
|
background: linear-gradient(135deg, rgba(59, 130, 246, 0.1), rgba(59, 130, 246, 0.05));
|
||||||
}
|
}
|
||||||
|
|
||||||
&--system {
|
|
||||||
border-color: rgb(156, 163, 175);
|
|
||||||
background: linear-gradient(135deg, rgba(156, 163, 175, 0.1), rgba(156, 163, 175, 0.05));
|
|
||||||
}
|
|
||||||
|
|
||||||
&--pulse {
|
&--pulse {
|
||||||
&::after {
|
&::after {
|
||||||
content: '';
|
content: '';
|
||||||
|
|
|
||||||
|
|
@ -80,15 +80,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="events-footer">
|
<div class="events-footer">
|
||||||
<div class="footer-stats">
|
<div class="footer-message">
|
||||||
<span class="stat-item">
|
<v-icon size="16" color="grey">mdi-information</v-icon>
|
||||||
<v-icon size="16" color="error">mdi-check-circle</v-icon>
|
<span>{{ events.length }} upcoming event{{ events.length !== 1 ? 's' : '' }}</span>
|
||||||
{{ confirmedCount }} confirmed
|
|
||||||
</span>
|
|
||||||
<span class="stat-item">
|
|
||||||
<v-icon size="16" color="warning">mdi-clock</v-icon>
|
|
||||||
{{ pendingCount }} pending
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -291,19 +285,13 @@ const formatDay = (dateString: string) => {
|
||||||
border-top: 1px solid rgba(220, 38, 38, 0.1);
|
border-top: 1px solid rgba(220, 38, 38, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-stats {
|
.footer-message {
|
||||||
display: flex;
|
|
||||||
gap: 1.5rem;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-item {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.375rem;
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
font-size: 0.8125rem;
|
font-size: 0.8125rem;
|
||||||
color: rgb(107, 114, 128);
|
color: rgb(107, 114, 128);
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,296 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-motion
|
||||||
|
:initial="{ opacity: 0, scale: 0.98 }"
|
||||||
|
:enter="{
|
||||||
|
opacity: 1,
|
||||||
|
scale: 1,
|
||||||
|
transition: {
|
||||||
|
duration: 500,
|
||||||
|
type: 'spring',
|
||||||
|
stiffness: 200
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
class="simple-profile-card"
|
||||||
|
>
|
||||||
|
<!-- Header with Avatar -->
|
||||||
|
<div class="profile-header">
|
||||||
|
<div class="profile-avatar-wrapper">
|
||||||
|
<ProfileAvatar
|
||||||
|
v-if="member"
|
||||||
|
:member-id="member.member_id"
|
||||||
|
:first-name="member.first_name"
|
||||||
|
:last-name="member.last_name"
|
||||||
|
size="x-large"
|
||||||
|
:show-badge="false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="profile-title">
|
||||||
|
<h2 class="profile-name">{{ fullName }}</h2>
|
||||||
|
<p class="profile-member-id">{{ member?.member_id || 'MUSA-0000' }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Profile Information -->
|
||||||
|
<div class="profile-info-section">
|
||||||
|
<h3 class="section-title">Contact Information</h3>
|
||||||
|
|
||||||
|
<div class="info-grid">
|
||||||
|
<div class="info-item">
|
||||||
|
<v-icon size="18" color="grey-darken-1">mdi-email</v-icon>
|
||||||
|
<div class="info-content">
|
||||||
|
<span class="info-label">Email</span>
|
||||||
|
<span class="info-value">{{ member?.email || 'Not provided' }}</span>
|
||||||
|
<v-chip
|
||||||
|
v-if="emailVerified"
|
||||||
|
size="x-small"
|
||||||
|
color="success"
|
||||||
|
variant="tonal"
|
||||||
|
class="ml-2"
|
||||||
|
>
|
||||||
|
Verified
|
||||||
|
</v-chip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-item">
|
||||||
|
<v-icon size="18" color="grey-darken-1">mdi-phone</v-icon>
|
||||||
|
<div class="info-content">
|
||||||
|
<span class="info-label">Phone</span>
|
||||||
|
<span class="info-value">{{ member?.phone || 'Not provided' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-item">
|
||||||
|
<v-icon size="18" color="grey-darken-1">mdi-map-marker</v-icon>
|
||||||
|
<div class="info-content">
|
||||||
|
<span class="info-label">Address</span>
|
||||||
|
<span class="info-value">{{ member?.address || 'Not provided' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Personal Information -->
|
||||||
|
<div class="profile-info-section">
|
||||||
|
<h3 class="section-title">Personal Information</h3>
|
||||||
|
|
||||||
|
<div class="info-grid">
|
||||||
|
<div class="info-item">
|
||||||
|
<v-icon size="18" color="grey-darken-1">mdi-flag</v-icon>
|
||||||
|
<div class="info-content">
|
||||||
|
<span class="info-label">Nationality</span>
|
||||||
|
<span class="info-value">{{ formatNationality(member?.nationality) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-item">
|
||||||
|
<v-icon size="18" color="grey-darken-1">mdi-cake</v-icon>
|
||||||
|
<div class="info-content">
|
||||||
|
<span class="info-label">Date of Birth</span>
|
||||||
|
<span class="info-value">{{ formatDate(member?.date_of_birth) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-item">
|
||||||
|
<v-icon size="18" color="grey-darken-1">mdi-calendar-account</v-icon>
|
||||||
|
<div class="info-content">
|
||||||
|
<span class="info-label">Member Since</span>
|
||||||
|
<span class="info-value">{{ formatDate(member?.member_since) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bio Section (if available) -->
|
||||||
|
<div v-if="member?.bio" class="profile-info-section">
|
||||||
|
<h3 class="section-title">About Me</h3>
|
||||||
|
<p class="bio-text">{{ member.bio }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Action Button -->
|
||||||
|
<v-btn
|
||||||
|
color="error"
|
||||||
|
variant="flat"
|
||||||
|
block
|
||||||
|
class="profile-action"
|
||||||
|
prepend-icon="mdi-account-edit"
|
||||||
|
@click="$emit('edit-profile')"
|
||||||
|
>
|
||||||
|
Edit Profile
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import type { Member } from '~/utils/types';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
member: Member | null;
|
||||||
|
emailVerified?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
emailVerified: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'edit-profile': [];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// Computed properties
|
||||||
|
const fullName = computed(() => {
|
||||||
|
if (props.member) {
|
||||||
|
return `${props.member.first_name} ${props.member.last_name}`;
|
||||||
|
}
|
||||||
|
return 'Member';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Format nationality (handles multiple nationalities)
|
||||||
|
const formatNationality = (nationality?: string) => {
|
||||||
|
if (!nationality) return 'Not provided';
|
||||||
|
|
||||||
|
// Split by comma if multiple nationalities
|
||||||
|
const nationalities = nationality.split(',').map(n => n.trim());
|
||||||
|
|
||||||
|
// Map country codes to full names if needed
|
||||||
|
const countryMap: Record<string, string> = {
|
||||||
|
'US': 'United States',
|
||||||
|
'FR': 'France',
|
||||||
|
'MC': 'Monaco',
|
||||||
|
'IT': 'Italy',
|
||||||
|
'UK': 'United Kingdom',
|
||||||
|
'DE': 'Germany',
|
||||||
|
'ES': 'Spain'
|
||||||
|
};
|
||||||
|
|
||||||
|
return nationalities.map(n => countryMap[n] || n).join(', ');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Format date
|
||||||
|
const formatDate = (dateString?: string) => {
|
||||||
|
if (!dateString) return 'Not provided';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
return date.toLocaleDateString('en-US', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric'
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return dateString;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.simple-profile-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 1rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1.5rem;
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-avatar-wrapper {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-title {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-name {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: rgb(31, 41, 55);
|
||||||
|
margin: 0 0 0.25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-member-id {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: rgb(107, 114, 128);
|
||||||
|
margin: 0;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-info-section {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: rgb(107, 114, 128);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: rgb(156, 163, 175);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.025em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: rgb(31, 41, 55);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bio-text {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: rgb(75, 85, 99);
|
||||||
|
line-height: 1.6;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-action {
|
||||||
|
margin-top: auto;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: none;
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.profile-header {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
src="/MONACOUSA-Flags_376x376.png"
|
src="/MONACOUSA-Flags_376x376.png"
|
||||||
width="80"
|
width="80"
|
||||||
height="80"
|
height="80"
|
||||||
class="mx-auto mb-2 float-animation"
|
class="mx-auto mb-2"
|
||||||
/>
|
/>
|
||||||
<div class="text-h6 font-weight-bold monaco-red-text">
|
<div class="text-h6 font-weight-bold monaco-red-text">
|
||||||
MonacoUSA Portal
|
MonacoUSA Portal
|
||||||
|
|
@ -63,14 +63,6 @@
|
||||||
value="resources"
|
value="resources"
|
||||||
class="glass-nav-item"
|
class="glass-nav-item"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<v-list-item
|
|
||||||
to="/member/directory"
|
|
||||||
prepend-icon="mdi-contacts"
|
|
||||||
title="Member Directory"
|
|
||||||
value="directory"
|
|
||||||
class="glass-nav-item"
|
|
||||||
/>
|
|
||||||
</v-list>
|
</v-list>
|
||||||
|
|
||||||
<!-- Footer with Glass Card -->
|
<!-- Footer with Glass Card -->
|
||||||
|
|
@ -240,19 +232,6 @@ watch(width, (newWidth) => {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.float-animation {
|
|
||||||
animation: float 3s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes float {
|
|
||||||
0%, 100% {
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: translateY(-10px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Monaco Text Colors
|
// Monaco Text Colors
|
||||||
.monaco-red-text {
|
.monaco-red-text {
|
||||||
color: #dc2626 !important;
|
color: #dc2626 !important;
|
||||||
|
|
|
||||||
|
|
@ -1,182 +1,55 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="member-dashboard">
|
<div class="member-dashboard">
|
||||||
<!-- Animated Header -->
|
<!-- Simple Header -->
|
||||||
<div
|
<div class="dashboard-header">
|
||||||
v-motion
|
<h1 class="dashboard-title">Welcome back, {{ firstName }}</h1>
|
||||||
:initial="{ opacity: 0, y: -20 }"
|
<p class="dashboard-subtitle">Member Dashboard</p>
|
||||||
:enter="{
|
|
||||||
opacity: 1,
|
|
||||||
y: 0,
|
|
||||||
transition: {
|
|
||||||
duration: 600,
|
|
||||||
type: 'spring'
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
class="dashboard-header"
|
|
||||||
>
|
|
||||||
<div class="header-content">
|
|
||||||
<h1 class="dashboard-title">
|
|
||||||
<span class="title-gradient">Welcome back,</span>
|
|
||||||
<span class="title-name">{{ firstName }}</span>
|
|
||||||
</h1>
|
|
||||||
<p class="dashboard-subtitle">Here's what's happening with your account today</p>
|
|
||||||
</div>
|
|
||||||
<div class="header-actions">
|
|
||||||
<v-btn
|
|
||||||
color="white"
|
|
||||||
variant="outlined"
|
|
||||||
prepend-icon="mdi-bell"
|
|
||||||
class="notification-btn"
|
|
||||||
>
|
|
||||||
<v-badge
|
|
||||||
color="error"
|
|
||||||
dot
|
|
||||||
offset-x="-8"
|
|
||||||
offset-y="-8"
|
|
||||||
>
|
|
||||||
Notifications
|
|
||||||
</v-badge>
|
|
||||||
</v-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Bento Grid Dashboard -->
|
<!-- Simple Two-Column Layout -->
|
||||||
<BentoGrid gap="lg" class="dashboard-grid">
|
<v-row class="dashboard-content">
|
||||||
<!-- Profile Card -->
|
<!-- Left Column - Profile -->
|
||||||
<div class="bento-item bento-item--medium bento-item--tall">
|
<v-col cols="12" md="4">
|
||||||
<ProfileCard
|
<SimpleProfileCard
|
||||||
:member="memberData"
|
:member="memberData"
|
||||||
:member-points="memberPoints"
|
:email-verified="emailVerified"
|
||||||
:events-attended="12"
|
|
||||||
:connections="48"
|
|
||||||
@edit-profile="navigateTo('/member/profile')"
|
@edit-profile="navigateTo('/member/profile')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</v-col>
|
||||||
|
|
||||||
<!-- Statistics Cards -->
|
<!-- Right Column - Events -->
|
||||||
<div class="bento-item bento-item--small">
|
<v-col cols="12" md="8">
|
||||||
<StatsCard
|
|
||||||
label="Total Points"
|
|
||||||
:value="memberPoints"
|
|
||||||
icon="mdi-star"
|
|
||||||
icon-color="warning"
|
|
||||||
:icon-background="'linear-gradient(135deg, rgba(245, 158, 11, 0.1), rgba(245, 158, 11, 0.05))'"
|
|
||||||
:change="12"
|
|
||||||
suffix="pts"
|
|
||||||
:delay="100"
|
|
||||||
decoration-color="#f59e0b"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bento-item bento-item--small">
|
|
||||||
<StatsCard
|
|
||||||
label="Events Attended"
|
|
||||||
:value="upcomingEvents.length"
|
|
||||||
icon="mdi-calendar-check"
|
|
||||||
icon-color="success"
|
|
||||||
:icon-background="'linear-gradient(135deg, rgba(34, 197, 94, 0.1), rgba(34, 197, 94, 0.05))'"
|
|
||||||
subtitle="This month"
|
|
||||||
:delay="200"
|
|
||||||
decoration-color="#22c55e"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bento-item bento-item--small">
|
|
||||||
<StatsCard
|
|
||||||
label="Active Membership"
|
|
||||||
:value="membershipDays"
|
|
||||||
icon="mdi-crown"
|
|
||||||
icon-color="error"
|
|
||||||
:change="100"
|
|
||||||
suffix="days"
|
|
||||||
subtitle="Premium member"
|
|
||||||
:delay="300"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Upcoming Events Card -->
|
|
||||||
<div class="bento-item bento-item--large">
|
|
||||||
<EventsCard
|
<EventsCard
|
||||||
:events="upcomingEvents"
|
:events="upcomingEvents"
|
||||||
@view-all="navigateTo('/member/events')"
|
@view-all="navigateTo('/member/events')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
<!-- Payment Status Card -->
|
<!-- Activity Timeline -->
|
||||||
<div class="bento-item bento-item--medium">
|
<v-row class="mt-6">
|
||||||
<PaymentCard
|
<v-col cols="12">
|
||||||
:membership-type="membershipType"
|
<div class="activity-section">
|
||||||
:next-payment-date="nextPaymentDate"
|
<div class="section-header">
|
||||||
:membership-amount="membershipAmount"
|
<h2 class="section-title">Recent Activity</h2>
|
||||||
:payment-history="paymentHistory"
|
|
||||||
@update-payment="navigateTo('/member/payments')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Activity Timeline -->
|
|
||||||
<div class="bento-item bento-item--medium bento-item--tall">
|
|
||||||
<div class="glass-card activity-card">
|
|
||||||
<div class="card-header">
|
|
||||||
<v-icon color="error" size="20">mdi-history</v-icon>
|
|
||||||
<h3 class="card-title">Recent Activity</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<ActivityTimeline
|
<ActivityTimeline
|
||||||
:activities="formattedActivities"
|
:activities="filteredActivities"
|
||||||
:max-items="5"
|
:max-items="10"
|
||||||
/>
|
/>
|
||||||
<v-btn
|
|
||||||
variant="outlined"
|
|
||||||
color="error"
|
|
||||||
block
|
|
||||||
class="mt-4"
|
|
||||||
@click="navigateTo('/member/activity')"
|
|
||||||
>
|
|
||||||
View All Activity
|
|
||||||
</v-btn>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</v-col>
|
||||||
</BentoGrid>
|
</v-row>
|
||||||
|
|
||||||
|
|
||||||
<!-- Quick Actions Section -->
|
|
||||||
<div
|
|
||||||
v-motion
|
|
||||||
:initial="{ opacity: 0, y: 20 }"
|
|
||||||
:enter="{
|
|
||||||
opacity: 1,
|
|
||||||
y: 0,
|
|
||||||
transition: {
|
|
||||||
delay: 800,
|
|
||||||
duration: 600
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
class="quick-actions-section"
|
|
||||||
>
|
|
||||||
<h3 class="section-title">Quick Actions</h3>
|
|
||||||
<div class="quick-actions-grid">
|
|
||||||
<QuickActionCard
|
|
||||||
v-for="(action, index) in quickActions"
|
|
||||||
:key="action.title"
|
|
||||||
:icon="action.icon"
|
|
||||||
:title="action.title"
|
|
||||||
:color="action.color"
|
|
||||||
:delay="900 + (index * 100)"
|
|
||||||
@click="navigateTo(action.route)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Member } from '~/utils/types';
|
import type { Member } from '~/utils/types';
|
||||||
import BentoGrid from '~/components/dashboard/BentoGrid.vue';
|
import SimpleProfileCard from '~/components/dashboard/SimpleProfileCard.vue';
|
||||||
import StatsCard from '~/components/dashboard/StatsCard.vue';
|
|
||||||
import ProfileCard from '~/components/dashboard/ProfileCard.vue';
|
|
||||||
import ActivityTimeline from '~/components/dashboard/ActivityTimeline.vue';
|
import ActivityTimeline from '~/components/dashboard/ActivityTimeline.vue';
|
||||||
import EventsCard from '~/components/dashboard/EventsCard.vue';
|
import EventsCard from '~/components/dashboard/EventsCard.vue';
|
||||||
import PaymentCard from '~/components/dashboard/PaymentCard.vue';
|
|
||||||
import QuickActionCard from '~/components/dashboard/QuickActionCard.vue';
|
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: 'member',
|
layout: 'member',
|
||||||
|
|
@ -201,28 +74,11 @@ const fullName = computed(() => {
|
||||||
return user.value?.name || 'Member';
|
return user.value?.name || 'Member';
|
||||||
});
|
});
|
||||||
const email = computed(() => memberData.value?.email || user.value?.email || '');
|
const email = computed(() => memberData.value?.email || user.value?.email || '');
|
||||||
const membershipType = computed(() => 'Premium');
|
const emailVerified = computed(() => {
|
||||||
const memberLevel = computed(() => 'Gold');
|
// Check if email is verified (you can add actual verification logic here)
|
||||||
const memberSince = computed(() => {
|
return true;
|
||||||
if (memberData.value?.join_date) {
|
|
||||||
return new Date(memberData.value.join_date).toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
|
|
||||||
}
|
|
||||||
return 'January 2023';
|
|
||||||
});
|
});
|
||||||
const memberPoints = ref(2450);
|
|
||||||
const nextPaymentDate = ref('Feb 15, 2024');
|
|
||||||
const membershipAmount = ref('99.00');
|
|
||||||
|
|
||||||
// Calculate membership days
|
|
||||||
const membershipDays = computed(() => {
|
|
||||||
if (memberData.value?.join_date) {
|
|
||||||
const joinDate = new Date(memberData.value.join_date);
|
|
||||||
const today = new Date();
|
|
||||||
const diffTime = Math.abs(today.getTime() - joinDate.getTime());
|
|
||||||
return Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
||||||
}
|
|
||||||
return 365;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Mock data - replace with actual API calls
|
// Mock data - replace with actual API calls
|
||||||
const upcomingEvents = ref([
|
const upcomingEvents = ref([
|
||||||
|
|
@ -252,83 +108,45 @@ const upcomingEvents = ref([
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const paymentHistory = ref([
|
|
||||||
{ id: 1, date: 'Jan 15, 2024', amount: '99.00' },
|
|
||||||
{ id: 2, date: 'Dec 15, 2023', amount: '99.00' },
|
|
||||||
{ id: 3, date: 'Nov 15, 2023', amount: '99.00' }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const recentActivity = ref([
|
const recentActivity = ref([
|
||||||
{
|
{
|
||||||
id: "1",
|
id: "1",
|
||||||
type: "event",
|
type: "event",
|
||||||
description: "Attended Leadership Summit",
|
description: "Registered for Monthly Networking Event",
|
||||||
timestamp: "2 days ago",
|
timestamp: "2 days ago",
|
||||||
icon: "mdi-account-group",
|
icon: "mdi-calendar-check",
|
||||||
color: "error"
|
color: "error"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "2",
|
id: "2",
|
||||||
type: "payment",
|
type: "profile",
|
||||||
description: "Membership renewal completed",
|
description: "Updated profile information",
|
||||||
timestamp: "1 week ago",
|
timestamp: "1 week ago",
|
||||||
icon: "mdi-credit-card",
|
icon: "mdi-account",
|
||||||
color: "success"
|
color: "info"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "3",
|
id: "3",
|
||||||
type: "achievement",
|
type: "event",
|
||||||
description: "Earned Gold Level status",
|
description: "Attended Workshop: Digital Marketing",
|
||||||
timestamp: "2 weeks ago",
|
timestamp: "2 weeks ago",
|
||||||
icon: "mdi-trophy",
|
icon: "mdi-account-group",
|
||||||
color: "warning"
|
color: "error"
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "4",
|
|
||||||
type: "profile",
|
|
||||||
description: "Updated profile information",
|
|
||||||
timestamp: "3 weeks ago",
|
|
||||||
icon: "mdi-account",
|
|
||||||
color: "info"
|
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Format activities for the timeline component
|
// Filter activities to exclude payment and achievement types
|
||||||
const formattedActivities = computed(() => {
|
const filteredActivities = computed(() => {
|
||||||
return recentActivity.value.map(activity => ({
|
return recentActivity.value
|
||||||
...activity,
|
.filter(activity => activity.type === 'event' || activity.type === 'profile')
|
||||||
title: activity.description.split(' ').slice(0, 3).join(' '),
|
.map(activity => ({
|
||||||
description: activity.description
|
...activity,
|
||||||
}));
|
title: activity.description.split(' ').slice(0, 3).join(' '),
|
||||||
|
description: activity.description
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Quick actions data
|
|
||||||
const quickActions = ref([
|
|
||||||
{
|
|
||||||
title: "View Events",
|
|
||||||
icon: "mdi-calendar",
|
|
||||||
color: "error",
|
|
||||||
route: "/member/events"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Update Profile",
|
|
||||||
icon: "mdi-account-edit",
|
|
||||||
color: "primary",
|
|
||||||
route: "/member/profile"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Payments",
|
|
||||||
icon: "mdi-credit-card",
|
|
||||||
color: "success",
|
|
||||||
route: "/member/payments"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Resources",
|
|
||||||
icon: "mdi-book-open-variant",
|
|
||||||
color: "warning",
|
|
||||||
route: "/member/resources"
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
const formatDate = (dateString: string) => {
|
const formatDate = (dateString: string) => {
|
||||||
|
|
@ -352,16 +170,6 @@ onMounted(async () => {
|
||||||
console.error('Error loading events:', error);
|
console.error('Error loading events:', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load payment information
|
|
||||||
try {
|
|
||||||
const paymentsRes = await $fetch('/api/member/payments/status');
|
|
||||||
if (paymentsRes?.success && paymentsRes?.data) {
|
|
||||||
// Update payment data
|
|
||||||
console.log('Loaded payment status:', paymentsRes.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error loading payment status:', error);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue