UI improvements for Board Portal dashboard
Build And Push Image / docker (push) Successful in 2m0s Details

- Added profile picture between welcome message and title
- Removed Events and Members boxes from dashboard
- Added distinct borders and icon to Dues Management card
- Moved hamburger menu to the right side of app bar
- Removed notification bell icon from app bar
- Enhanced profile card appearance in sidebar with gradient background
- Fixed Mark as Paid button alignment to be inline with other action buttons
- Added support for displaying multiple nationality flags in dues cards

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Matt 2025-09-04 22:49:03 +02:00
parent b67100df2a
commit 95c253a552
4 changed files with 106 additions and 101 deletions

View File

@ -1,8 +1,8 @@
<template> <template>
<v-card elevation="2" class="dues-management-card"> <v-card elevation="4" class="dues-management-card" style="border: 2px solid #dc2626; border-radius: 16px;">
<v-card-title class="pa-4 bg-warning-lighten-5"> <v-card-title class="pa-4 bg-warning-lighten-5">
<v-icon class="mr-2" color="warning">mdi-cash-clock</v-icon> <v-icon class="mr-3" color="warning" size="28">mdi-cash-multiple</v-icon>
<span class="text-h6">Dues Management</span> <span class="text-h6 font-weight-bold">Dues Management</span>
<v-spacer /> <v-spacer />
<v-chip color="warning" size="small"> <v-chip color="warning" size="small">
{{ overdueMembers.length + upcomingMembers.length }} Action Items {{ overdueMembers.length + upcomingMembers.length }} Action Items

View File

@ -38,9 +38,9 @@
<v-chip size="x-small" color="grey" variant="text" class="pa-0 mr-2"> <v-chip size="x-small" color="grey" variant="text" class="pa-0 mr-2">
ID: {{ member.member_id || `MUSA-${member.Id}` }} ID: {{ member.member_id || `MUSA-${member.Id}` }}
</v-chip> </v-chip>
<CountryFlag <MultipleCountryFlags
v-if="member.nationality" v-if="member.nationality"
:country-code="member.nationality.split(',')[0]" :country-codes="member.nationality"
:show-name="false" :show-name="false"
size="small" size="small"
/> />
@ -117,21 +117,6 @@
</div> </div>
</v-card-text> </v-card-text>
<!-- Action Buttons -->
<v-card-actions class="pa-4 pt-0 d-flex justify-center">
<v-btn
color="success"
variant="elevated"
height="44"
:loading="loading"
@click="showPaymentDateDialog = true"
class="px-4"
>
<v-icon start size="16">mdi-check-circle</v-icon>
Mark as Paid
</v-btn>
</v-card-actions>
<!-- Payment Date Selection Dialog --> <!-- Payment Date Selection Dialog -->
<v-dialog v-model="showPaymentDateDialog" max-width="400"> <v-dialog v-model="showPaymentDateDialog" max-width="400">
<v-card> <v-card>
@ -202,28 +187,39 @@
</v-dialog> </v-dialog>
<!-- Quick Actions --> <!-- Quick Actions -->
<v-card-actions class="pa-4 pt-0"> <v-card-actions class="pa-4 pt-0 d-flex justify-space-between">
<v-btn <div class="d-flex gap-1">
variant="text" <v-btn
size="small" variant="text"
@click="$emit('view-member', member)" size="small"
> @click="$emit('view-member', member)"
<v-icon start size="16">mdi-account</v-icon> >
View Details <v-icon start size="16">mdi-account</v-icon>
</v-btn> View Details
</v-btn>
<v-spacer />
<v-btn
variant="text"
size="small"
:loading="emailLoading"
:disabled="!member.email"
@click="sendDuesReminder"
v-if="member.email"
>
<v-icon start size="16">mdi-email</v-icon>
Email
</v-btn>
</div>
<v-btn <v-btn
variant="text" color="success"
variant="elevated"
size="small" size="small"
:loading="emailLoading" :loading="loading"
:disabled="!member.email" @click="showPaymentDateDialog = true"
@click="sendDuesReminder"
v-if="member.email"
> >
<v-icon start size="16">mdi-email</v-icon> <v-icon start size="16">mdi-check-circle</v-icon>
Email Mark as Paid
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
@ -232,6 +228,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Member } from '~/utils/types'; import type { Member } from '~/utils/types';
import ProfileAvatar from '~/components/ProfileAvatar.vue'; import ProfileAvatar from '~/components/ProfileAvatar.vue';
import MultipleCountryFlags from '~/components/MultipleCountryFlags.vue';
// Extended member type for dues management // Extended member type for dues management
interface DuesMember { interface DuesMember {

View File

@ -252,23 +252,38 @@
<!-- Enhanced Profile Card --> <!-- Enhanced Profile Card -->
<template v-slot:append> <template v-slot:append>
<div class="pa-2"> <div class="pa-2">
<v-card class="glass-profile-card overflow-visible"> <v-card class="glass-profile-card overflow-visible" style="background: linear-gradient(135deg, rgba(220, 38, 38, 0.08), rgba(255, 255, 255, 0.95)); border: 1px solid rgba(220, 38, 38, 0.2);">
<div class="d-flex align-center" :class="miniVariant ? 'flex-column py-3 px-2' : 'pa-3'"> <div class="d-flex align-center" :class="miniVariant ? 'flex-column py-3 px-2' : 'pa-3'">
<!-- Avatar Section --> <!-- Avatar Section -->
<ProfileAvatar <div style="position: relative;">
:member-id="memberData?.member_id || memberData?.Id" <ProfileAvatar
:first-name="memberData?.first_name || user?.firstName" :member-id="memberData?.member_id || memberData?.Id"
:last-name="memberData?.last_name || user?.lastName" :first-name="memberData?.first_name || user?.firstName"
:member-name="memberData?.FullName || user?.name" :last-name="memberData?.last_name || user?.lastName"
:size="miniVariant ? '32' : 'small'" :member-name="memberData?.FullName || user?.name"
:class="miniVariant ? '' : 'mr-3'" :size="miniVariant ? '32' : '48'"
/> :class="miniVariant ? '' : 'mr-3'"
show-border
style="border: 2px solid #dc2626; box-shadow: 0 2px 8px rgba(220, 38, 38, 0.2);"
/>
<v-icon
v-if="!miniVariant"
size="16"
color="green"
style="position: absolute; bottom: 0; right: 12px; background: white; border-radius: 50%; padding: 2px;"
>
mdi-check-circle
</v-icon>
</div>
<!-- Info Section (Hidden in mini mode) --> <!-- Info Section (Hidden in mini mode) -->
<div v-if="!miniVariant" class="flex-grow-1"> <div v-if="!miniVariant" class="flex-grow-1">
<div class="text-subtitle-2 font-weight-bold">{{ user?.name || 'Board Member' }}</div> <div class="text-subtitle-2 font-weight-bold">{{ user?.name || 'Board Member' }}</div>
<div class="text-caption text-medium-emphasis">{{ user?.email?.split('@')[0] || 'board' }}</div> <div class="text-caption text-medium-emphasis">{{ user?.email?.split('@')[0] || 'board' }}</div>
<v-chip size="x-small" class="mt-1 glass-badge">Board</v-chip> <v-chip size="x-small" class="mt-1" style="background: linear-gradient(135deg, #dc2626, #b91c1c); color: white;">
<v-icon start size="12">mdi-shield-check</v-icon>
Board
</v-chip>
</div> </div>
<!-- Action Buttons --> <!-- Action Buttons -->
@ -279,10 +294,12 @@
v-bind="props" v-bind="props"
icon icon
:size="miniVariant ? 'small' : 'small'" :size="miniVariant ? 'small' : 'small'"
variant="text" variant="tonal"
color="primary"
class="profile-menu-btn" class="profile-menu-btn"
style="background: rgba(220, 38, 38, 0.1);"
> >
<v-icon>mdi-dots-vertical</v-icon> <v-icon>mdi-cog</v-icon>
</v-btn> </v-btn>
</template> </template>
<v-list density="compact" class="glass-menu" min-width="200"> <v-list density="compact" class="glass-menu" min-width="200">
@ -321,14 +338,6 @@
</v-navigation-drawer> </v-navigation-drawer>
<v-app-bar elevation="0" flat class="glass-app-bar board-bar"> <v-app-bar elevation="0" flat class="glass-app-bar board-bar">
<v-btn
icon
@click="toggleDrawer"
class="glass-icon-btn mr-2"
>
<v-icon>{{ miniVariant ? 'mdi-menu' : 'mdi-menu-open' }}</v-icon>
</v-btn>
<v-toolbar-title class="font-weight-bold text-white"> <v-toolbar-title class="font-weight-bold text-white">
Board Portal Board Portal
</v-toolbar-title> </v-toolbar-title>
@ -344,14 +353,13 @@
<v-icon>mdi-magnify</v-icon> <v-icon>mdi-magnify</v-icon>
</v-btn> </v-btn>
<v-btn icon class="glass-icon-btn"> <!-- Move hamburger menu to the right side -->
<v-badge <v-btn
:content="notifications" icon
:value="notifications > 0" @click="toggleDrawer"
color="error" class="glass-icon-btn ml-2"
> >
<v-icon>mdi-bell</v-icon> <v-icon>{{ miniVariant ? 'mdi-menu' : 'mdi-menu-open' }}</v-icon>
</v-badge>
</v-btn> </v-btn>
<!-- User Menu --> <!-- User Menu -->

View File

@ -8,6 +8,20 @@
<h1 class="dashboard-title text-gradient"> <h1 class="dashboard-title text-gradient">
Welcome Back, {{ firstName }}! Welcome Back, {{ firstName }}!
</h1> </h1>
<!-- Profile Picture Section -->
<div class="profile-picture-section my-4">
<ProfileAvatar
:member-id="memberData?.member_id || memberData?.Id"
:first-name="memberData?.first_name || user?.firstName"
:last-name="memberData?.last_name || user?.lastName"
:member-name="memberData?.FullName || user?.name"
size="80"
show-border
class="profile-avatar-main"
/>
</div>
<p class="dashboard-subtitle"> <p class="dashboard-subtitle">
MonacoUSA Board Portal MonacoUSA Board Portal
</p> </p>
@ -19,40 +33,6 @@
</div> </div>
</div> </div>
<!-- Board Tools with Bento Grid -->
<div class="bento-grid mb-6">
<div class="bento-item bento-item--large">
<v-card class="glass-card pa-4 text-center animated-entrance">
<v-icon size="48" color="primary" class="mb-2">mdi-calendar</v-icon>
<h3 class="mb-2">Events</h3>
<p class="text-body-2 mb-4">View and manage association events</p>
<v-btn
color="primary"
variant="outlined"
style="border-color: #a31515; color: #a31515;"
@click="navigateToEvents"
>
View Events
</v-btn>
</v-card>
</div>
<div class="bento-item bento-item--large">
<v-card class="glass-card pa-4 text-center animated-entrance">
<v-icon size="48" color="primary" class="mb-2">mdi-account-group</v-icon>
<h3 class="mb-2">Members</h3>
<p class="text-body-2 mb-4">View and manage association members</p>
<v-btn
color="primary"
variant="outlined"
style="border-color: #a31515; color: #a31515;"
@click="navigateToMembers"
>
View Members
</v-btn>
</v-card>
</div>
</div>
<!-- Board Statistics with Bento Grid --> <!-- Board Statistics with Bento Grid -->
<div class="bento-grid mb-6"> <div class="bento-grid mb-6">
@ -142,13 +122,21 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Member } from '~/utils/types'; import type { Member } from '~/utils/types';
import ProfileAvatar from '~/components/ProfileAvatar.vue';
definePageMeta({ definePageMeta({
layout: 'board', layout: 'board',
middleware: 'auth' middleware: 'auth'
}); });
const { firstName, isBoard, isAdmin } = useAuth(); const { firstName, isBoard, isAdmin, user } = useAuth();
// Fetch member data for profile
const { data: sessionData } = await useFetch<{ success: boolean; member: Member | null }>('/api/auth/session', {
server: false
});
const memberData = computed<Member | null>(() => sessionData.value?.member || null);
// Check board access on mount // Check board access on mount
onMounted(() => { onMounted(() => {
@ -338,6 +326,18 @@ const generateReport = () => {
inset 0 1px 2px rgba(255, 255, 255, 0.6); inset 0 1px 2px rgba(255, 255, 255, 0.6);
animation: slide-up 0.6s ease-out; animation: slide-up 0.6s ease-out;
text-align: center; text-align: center;
.profile-picture-section {
display: flex;
justify-content: center;
align-items: center;
.profile-avatar-main {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
border: 3px solid white;
border-radius: 50%;
}
}
} }
.dashboard-title { .dashboard-title {