monacousa-portal/pages/dashboard/board.vue

470 lines
13 KiB
Vue

<template>
<v-container>
<!-- Dues Payment Banner -->
<DuesPaymentBanner />
<!-- Welcome Header -->
<v-row class="mb-6">
<v-col>
<h1 class="text-h3 font-weight-bold" style="color: #a31515;">
Welcome Back, {{ firstName }}!
</h1>
<p class="text-h6 text-medium-emphasis">
MonacoUSA Board Portal
</p>
<v-chip color="primary" variant="elevated" class="mt-2">
<v-icon start>mdi-shield-account</v-icon>
Board Member
</v-chip>
</v-col>
</v-row>
<!-- Board Tools -->
<v-row class="mb-6">
<v-col cols="12" md="3">
<v-card class="pa-4 text-center" elevation="2" hover>
<v-icon size="48" color="primary" class="mb-2">mdi-calendar-clock</v-icon>
<h3 class="mb-2">Meetings</h3>
<p class="text-body-2 mb-4">Schedule and manage board meetings</p>
<v-btn
color="primary"
variant="outlined"
style="border-color: #a31515; color: #a31515;"
@click="navigateToMeetings"
>
Manage Meetings
</v-btn>
</v-card>
</v-col>
<v-col cols="12" md="3">
<v-card class="pa-4 text-center" elevation="2" hover>
<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>
</v-col>
<v-col cols="12" md="3">
<v-card class="pa-4 text-center" elevation="2" hover>
<v-icon size="48" color="primary" class="mb-2">mdi-chart-line</v-icon>
<h3 class="mb-2">Reports</h3>
<p class="text-body-2 mb-4">Financial and activity reports</p>
<v-btn
color="primary"
variant="outlined"
style="border-color: #a31515; color: #a31515;"
@click="navigateToReports"
>
View Reports
</v-btn>
</v-card>
</v-col>
<v-col cols="12" md="3">
<v-card class="pa-4 text-center" elevation="2" hover>
<v-icon size="48" color="primary" class="mb-2">mdi-tools</v-icon>
<h3 class="mb-2">Tools</h3>
<p class="text-body-2 mb-4">Board management tools</p>
<v-btn
color="primary"
variant="outlined"
style="border-color: #a31515; color: #a31515;"
@click="navigateToTools"
>
Access Tools
</v-btn>
</v-card>
</v-col>
</v-row>
<!-- Board Statistics -->
<v-row class="mb-6">
<v-col cols="12" md="8">
<v-card elevation="2">
<v-card-title class="pa-4" style="background-color: #f5f5f5;">
<v-icon class="mr-2" color="primary">mdi-chart-box-outline</v-icon>
Board Overview
</v-card-title>
<v-card-text class="pa-4">
<v-row>
<v-col cols="6" md="3" class="text-center">
<div class="text-h4 font-weight-bold" style="color: #a31515;">{{ stats.totalMembers }}</div>
<div class="text-body-2">Total Members</div>
</v-col>
<v-col cols="6" md="3" class="text-center">
<div class="text-h4 font-weight-bold" style="color: #a31515;">{{ stats.activeMembers }}</div>
<div class="text-body-2">Active Members</div>
</v-col>
<v-col cols="6" md="3" class="text-center">
<div class="text-h4 font-weight-bold" style="color: #a31515;">{{ stats.upcomingMeetings }}</div>
<div class="text-body-2">Upcoming Meetings</div>
</v-col>
<v-col cols="6" md="3" class="text-center">
<div class="text-h4 font-weight-bold" style="color: #a31515;">{{ stats.pendingActions }}</div>
<div class="text-body-2">Pending Actions</div>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-col>
<v-col cols="12" md="4">
<v-card elevation="2">
<v-card-title class="pa-4" style="background-color: #f5f5f5;">
<v-icon class="mr-2" color="primary">mdi-calendar-today</v-icon>
Next Meeting
</v-card-title>
<v-card-text class="pa-4">
<div class="text-h6 mb-2">Board Meeting</div>
<div class="text-body-2 mb-2">
<v-icon size="small" class="mr-1">mdi-calendar</v-icon>
{{ nextMeeting.date }}
</div>
<div class="text-body-2 mb-4">
<v-icon size="small" class="mr-1">mdi-clock</v-icon>
{{ nextMeeting.time }}
</div>
<v-btn
color="primary"
variant="outlined"
size="small"
style="border-color: #a31515; color: #a31515;"
@click="viewMeetingDetails"
>
View Details
</v-btn>
</v-card-text>
</v-card>
</v-col>
</v-row>
<!-- Dues Management Section -->
<v-row class="mb-6">
<v-col cols="12">
<BoardDuesManagement
:refresh-trigger="duesRefreshTrigger"
@view-member="handleViewMember"
@view-all-members="navigateToMembers"
@member-updated="handleMemberUpdated"
/>
</v-col>
</v-row>
<!-- Recent Board Activity -->
<v-row class="mb-6">
<v-col cols="12">
<v-card elevation="2">
<v-card-title class="pa-4" style="background-color: #f5f5f5;">
<v-icon class="mr-2" color="primary">mdi-history</v-icon>
Recent Board Activity
</v-card-title>
<v-card-text class="pa-4">
<v-list>
<v-list-item v-for="activity in recentActivity" :key="activity.id">
<v-list-item-content>
<v-list-item-title>{{ activity.title }}</v-list-item-title>
<v-list-item-subtitle>{{ activity.description }}</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-chip :color="activity.type" size="small">{{ activity.status }}</v-chip>
</v-list-item-action>
</v-list-item>
</v-list>
</v-card-text>
</v-card>
</v-col>
</v-row>
<!-- Quick Actions -->
<v-row>
<v-col cols="12">
<v-card elevation="2">
<v-card-title class="pa-4" style="background-color: #f5f5f5;">
<v-icon class="mr-2" color="primary">mdi-lightning-bolt</v-icon>
Quick Actions
</v-card-title>
<v-card-text class="pa-4">
<v-row>
<v-col cols="12" md="4">
<v-btn
color="primary"
variant="outlined"
block
style="border-color: #a31515; color: #a31515;"
@click="scheduleNewMeeting"
>
<v-icon start>mdi-plus</v-icon>
Schedule New Meeting
</v-btn>
</v-col>
<v-col cols="12" md="4">
<v-btn
color="primary"
variant="outlined"
block
style="border-color: #a31515; color: #a31515;"
@click="createAnnouncement"
>
<v-icon start>mdi-bullhorn</v-icon>
Create Announcement
</v-btn>
</v-col>
<v-col cols="12" md="4">
<v-btn
color="primary"
variant="outlined"
block
style="border-color: #a31515; color: #a31515;"
@click="generateReport"
>
<v-icon start>mdi-file-chart</v-icon>
Generate Report
</v-btn>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-col>
</v-row>
<!-- View Member Dialog -->
<ViewMemberDialog
v-model="showViewDialog"
:member="selectedMember"
@edit="handleEditMember"
/>
<!-- Edit Member Dialog -->
<EditMemberDialog
v-model="showEditDialog"
:member="selectedMember"
@member-updated="handleMemberUpdated"
/>
</v-container>
</template>
<script setup lang="ts">
import type { Member } from '~/utils/types';
definePageMeta({
layout: 'dashboard',
middleware: 'auth'
});
const { firstName, isBoard, isAdmin } = useAuth();
// Check board access on mount
onMounted(() => {
if (!isBoard.value && !isAdmin.value) {
throw createError({
statusCode: 403,
statusMessage: 'Access denied. Board membership required.'
});
}
});
// Dues management state
const duesRefreshTrigger = ref(0);
// Member dialog state
const showViewDialog = ref(false);
const showEditDialog = ref(false);
const selectedMember = ref<Member | null>(null);
// Real data for board dashboard
const stats = ref({
totalMembers: 0,
activeMembers: 0,
upcomingMeetings: 0,
pendingActions: 0
});
const nextMeeting = ref({
id: null,
title: 'Board Meeting',
date: 'Loading...',
time: 'Loading...',
location: 'TBD',
description: 'Monthly board meeting'
});
const isLoading = ref(true);
// Load real data on component mount
onMounted(async () => {
await loadBoardData();
});
const loadBoardData = async () => {
try {
isLoading.value = true;
// Load board statistics
const [statsResponse, meetingResponse] = await Promise.allSettled([
$fetch('/api/board/stats'),
$fetch('/api/board/next-meeting')
]);
// Handle stats response
if (statsResponse.status === 'fulfilled') {
const statsData = statsResponse.value as any;
if (statsData?.success) {
stats.value = {
totalMembers: statsData.data.totalMembers || 0,
activeMembers: statsData.data.activeMembers || 0,
upcomingMeetings: statsData.data.upcomingMeetings || 0,
pendingActions: statsData.data.pendingActions || 0
};
}
}
// Handle next meeting response
if (meetingResponse.status === 'fulfilled') {
const meetingData = meetingResponse.value as any;
if (meetingData?.success) {
nextMeeting.value = {
id: meetingData.data.id,
title: meetingData.data.title || 'Board Meeting',
date: meetingData.data.date || 'TBD',
time: meetingData.data.time || 'TBD',
location: meetingData.data.location || 'TBD',
description: meetingData.data.description || 'Monthly board meeting'
};
}
}
} catch (error) {
console.error('Error loading board data:', error);
// Keep fallback values
} finally {
isLoading.value = false;
}
};
const recentActivity = ref([
{
id: 1,
title: 'Monthly Board Meeting',
description: 'Meeting minutes approved and distributed',
type: 'success',
status: 'Completed'
},
{
id: 2,
title: 'Budget Review',
description: 'Q4 financial report under review',
type: 'warning',
status: 'In Progress'
},
{
id: 3,
title: 'Member Application',
description: 'New member application pending approval',
type: 'info',
status: 'Pending'
}
]);
// Dues management handlers
const handleViewMember = (member: Member) => {
// Open the view dialog instead of navigating away
selectedMember.value = member;
showViewDialog.value = true;
};
const handleEditMember = (member: Member) => {
// Close the view dialog and open the edit dialog
showViewDialog.value = false;
selectedMember.value = member;
showEditDialog.value = true;
};
const handleMemberUpdated = (member: Member) => {
console.log('Member updated:', member.FullName || `${member.first_name} ${member.last_name}`);
// Close edit dialog
showEditDialog.value = false;
// Trigger dues refresh to update the lists
duesRefreshTrigger.value += 1;
// You could also update stats here if needed
// stats.value = await fetchUpdatedStats();
};
// Navigation methods (placeholder implementations)
const navigateToMeetings = () => {
console.log('Navigate to meetings');
};
const navigateToMembers = () => {
// Navigate to member list page
navigateTo('/dashboard/member-list');
};
const navigateToReports = () => {
console.log('Navigate to reports');
};
const navigateToTools = () => {
console.log('Navigate to tools');
};
const viewMeetingDetails = () => {
console.log('View meeting details');
};
const scheduleNewMeeting = () => {
console.log('Schedule new meeting');
};
const createAnnouncement = () => {
console.log('Create announcement');
};
const generateReport = () => {
console.log('Generate report');
};
</script>
<style scoped>
.v-card {
border-radius: 12px !important;
}
.v-card:hover {
transform: translateY(-2px);
transition: transform 0.2s ease-in-out;
}
.v-btn {
text-transform: none !important;
}
.v-icon {
color: #a31515 !important;
}
h3 {
color: #333;
font-weight: 600;
}
.text-body-2 {
color: #666;
}
.v-chip {
font-weight: 600;
}
</style>