Refactor authentication system with tier-based access control
All checks were successful
Build And Push Image / docker (push) Successful in 2m59s
All checks were successful
Build And Push Image / docker (push) Successful in 2m59s
- Replace group-based auth with user/board/admin tier system - Add direct login functionality alongside OAuth - Implement role-based middleware for route protection - Create dashboard pages and admin API endpoints - Add error handling page and improved user management - Maintain backward compatibility with legacy role methods
This commit is contained in:
518
pages/dashboard/admin.vue
Normal file
518
pages/dashboard/admin.vue
Normal file
@@ -0,0 +1,518 @@
|
||||
<template>
|
||||
<v-container>
|
||||
<!-- 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 Administration Portal
|
||||
</p>
|
||||
<v-chip color="error" variant="elevated" class="mt-2">
|
||||
<v-icon start>mdi-shield-crown</v-icon>
|
||||
Administrator
|
||||
</v-chip>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- System Status -->
|
||||
<v-row class="mb-6">
|
||||
<v-col cols="12">
|
||||
<v-card class="pa-4" elevation="2">
|
||||
<h3 class="mb-3">
|
||||
<v-icon class="mr-2" color="success">mdi-server</v-icon>
|
||||
System Status
|
||||
</h3>
|
||||
<v-row>
|
||||
<v-col cols="6" md="3">
|
||||
<v-chip color="success" variant="elevated">
|
||||
<v-icon start>mdi-check-circle</v-icon>
|
||||
System Healthy
|
||||
</v-chip>
|
||||
</v-col>
|
||||
<v-col cols="6" md="3">
|
||||
<v-chip color="info" variant="elevated">
|
||||
<v-icon start>mdi-account-multiple</v-icon>
|
||||
{{ systemStats.totalUsers }} Users
|
||||
</v-chip>
|
||||
</v-col>
|
||||
<v-col cols="6" md="3">
|
||||
<v-chip color="warning" variant="elevated">
|
||||
<v-icon start>mdi-database</v-icon>
|
||||
{{ systemStats.diskUsage }} Disk
|
||||
</v-chip>
|
||||
</v-col>
|
||||
<v-col cols="6" md="3">
|
||||
<v-chip color="primary" variant="elevated">
|
||||
<v-icon start>mdi-memory</v-icon>
|
||||
{{ systemStats.memoryUsage }} Memory
|
||||
</v-chip>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Admin Tools -->
|
||||
<v-row class="mb-6">
|
||||
<v-col cols="12" md="4">
|
||||
<v-card class="pa-4 text-center" elevation="2" hover>
|
||||
<v-icon size="48" color="error" class="mb-2">mdi-account-cog</v-icon>
|
||||
<h3 class="mb-2">User Management</h3>
|
||||
<p class="text-body-2 mb-4">Add, edit, and manage user accounts</p>
|
||||
<v-btn
|
||||
color="error"
|
||||
variant="outlined"
|
||||
style="border-color: #f44336; color: #f44336;"
|
||||
@click="navigateToUserManagement"
|
||||
>
|
||||
Manage Users
|
||||
</v-btn>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="4">
|
||||
<v-card class="pa-4 text-center" elevation="2" hover>
|
||||
<v-icon size="48" color="error" class="mb-2">mdi-file-document-outline</v-icon>
|
||||
<h3 class="mb-2">Audit Logs</h3>
|
||||
<p class="text-body-2 mb-4">View system and user activity logs</p>
|
||||
<v-btn
|
||||
color="error"
|
||||
variant="outlined"
|
||||
style="border-color: #f44336; color: #f44336;"
|
||||
@click="navigateToAuditLogs"
|
||||
>
|
||||
View Logs
|
||||
</v-btn>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="4">
|
||||
<v-card class="pa-4 text-center" elevation="2" hover>
|
||||
<v-icon size="48" color="error" class="mb-2">mdi-cog</v-icon>
|
||||
<h3 class="mb-2">System Config</h3>
|
||||
<p class="text-body-2 mb-4">Configure system settings</p>
|
||||
<v-btn
|
||||
color="error"
|
||||
variant="outlined"
|
||||
style="border-color: #f44336; color: #f44336;"
|
||||
@click="navigateToSystemConfig"
|
||||
>
|
||||
Settings
|
||||
</v-btn>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- System Metrics -->
|
||||
<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-line</v-icon>
|
||||
System Metrics
|
||||
</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;">{{ systemStats.totalUsers }}</div>
|
||||
<div class="text-body-2">Total Users</div>
|
||||
</v-col>
|
||||
<v-col cols="6" md="3" class="text-center">
|
||||
<div class="text-h4 font-weight-bold" style="color: #a31515;">{{ systemStats.activeUsers }}</div>
|
||||
<div class="text-body-2">Active Users</div>
|
||||
</v-col>
|
||||
<v-col cols="6" md="3" class="text-center">
|
||||
<div class="text-h4 font-weight-bold" style="color: #a31515;">{{ systemStats.totalSessions }}</div>
|
||||
<div class="text-body-2">Active Sessions</div>
|
||||
</v-col>
|
||||
<v-col cols="6" md="3" class="text-center">
|
||||
<div class="text-h4 font-weight-bold" style="color: #a31515;">{{ systemStats.uptime }}</div>
|
||||
<div class="text-body-2">System Uptime</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-backup-restore</v-icon>
|
||||
Last Backup
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-4">
|
||||
<div class="text-h6 mb-2">System Backup</div>
|
||||
<div class="text-body-2 mb-2">
|
||||
<v-icon size="small" class="mr-1">mdi-calendar</v-icon>
|
||||
{{ lastBackup.date }}
|
||||
</div>
|
||||
<div class="text-body-2 mb-4">
|
||||
<v-icon size="small" class="mr-1">mdi-clock</v-icon>
|
||||
{{ lastBackup.time }}
|
||||
</div>
|
||||
<v-btn
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
size="small"
|
||||
style="border-color: #a31515; color: #a31515;"
|
||||
@click="initiateBackup"
|
||||
>
|
||||
Create Backup
|
||||
</v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Recent Admin 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 Admin 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 }} - {{ activity.timestamp }}
|
||||
</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>
|
||||
|
||||
<!-- Security & Monitoring -->
|
||||
<v-row class="mb-6">
|
||||
<v-col cols="12" md="6">
|
||||
<v-card elevation="2">
|
||||
<v-card-title class="pa-4" style="background-color: #f5f5f5;">
|
||||
<v-icon class="mr-2" color="error">mdi-security</v-icon>
|
||||
Security Alerts
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-4">
|
||||
<v-list>
|
||||
<v-list-item v-for="alert in securityAlerts" :key="alert.id">
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{ alert.title }}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ alert.description }}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
<v-list-item-action>
|
||||
<v-chip :color="alert.severity" size="small">{{ alert.level }}</v-chip>
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="6">
|
||||
<v-card elevation="2">
|
||||
<v-card-title class="pa-4" style="background-color: #f5f5f5;">
|
||||
<v-icon class="mr-2" color="primary">mdi-monitor-dashboard</v-icon>
|
||||
System Health
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-4">
|
||||
<div class="mb-3">
|
||||
<div class="d-flex justify-space-between mb-1">
|
||||
<span>CPU Usage</span>
|
||||
<span>{{ systemHealth.cpu }}%</span>
|
||||
</div>
|
||||
<v-progress-linear
|
||||
:model-value="systemHealth.cpu"
|
||||
color="primary"
|
||||
height="8"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="d-flex justify-space-between mb-1">
|
||||
<span>Memory Usage</span>
|
||||
<span>{{ systemHealth.memory }}%</span>
|
||||
</div>
|
||||
<v-progress-linear
|
||||
:model-value="systemHealth.memory"
|
||||
color="warning"
|
||||
height="8"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="d-flex justify-space-between mb-1">
|
||||
<span>Disk Usage</span>
|
||||
<span>{{ systemHealth.disk }}%</span>
|
||||
</div>
|
||||
<v-progress-linear
|
||||
:model-value="systemHealth.disk"
|
||||
color="success"
|
||||
height="8"
|
||||
/>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Quick Admin 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 Admin Actions
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-4">
|
||||
<v-row>
|
||||
<v-col cols="12" md="3">
|
||||
<v-btn
|
||||
color="error"
|
||||
variant="outlined"
|
||||
block
|
||||
style="border-color: #f44336; color: #f44336;"
|
||||
@click="createNewUser"
|
||||
>
|
||||
<v-icon start>mdi-account-plus</v-icon>
|
||||
Create User
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col cols="12" md="3">
|
||||
<v-btn
|
||||
color="error"
|
||||
variant="outlined"
|
||||
block
|
||||
style="border-color: #f44336; color: #f44336;"
|
||||
@click="generateSystemReport"
|
||||
>
|
||||
<v-icon start>mdi-file-chart</v-icon>
|
||||
System Report
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col cols="12" md="3">
|
||||
<v-btn
|
||||
color="error"
|
||||
variant="outlined"
|
||||
block
|
||||
style="border-color: #f44336; color: #f44336;"
|
||||
@click="managePermissions"
|
||||
>
|
||||
<v-icon start>mdi-shield-key</v-icon>
|
||||
Permissions
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col cols="12" md="3">
|
||||
<v-btn
|
||||
color="error"
|
||||
variant="outlined"
|
||||
block
|
||||
style="border-color: #f44336; color: #f44336;"
|
||||
@click="systemMaintenance"
|
||||
>
|
||||
<v-icon start>mdi-wrench</v-icon>
|
||||
Maintenance
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: 'dashboard',
|
||||
middleware: 'auth'
|
||||
});
|
||||
|
||||
const { firstName, isAdmin } = useAuth();
|
||||
|
||||
// Check admin access on mount
|
||||
onMounted(async () => {
|
||||
if (!isAdmin.value) {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
statusMessage: 'Access denied. Administrator privileges required.'
|
||||
});
|
||||
}
|
||||
|
||||
// Load admin dashboard data
|
||||
await loadAdminStats();
|
||||
});
|
||||
|
||||
// Reactive data
|
||||
const systemStats = ref({
|
||||
totalUsers: 0,
|
||||
activeUsers: 0,
|
||||
totalSessions: 0,
|
||||
diskUsage: '0%',
|
||||
memoryUsage: '0%',
|
||||
uptime: '0d'
|
||||
});
|
||||
|
||||
const lastBackup = ref({
|
||||
date: 'January 7, 2025',
|
||||
time: '3:00 AM EST'
|
||||
});
|
||||
|
||||
const systemHealth = ref({
|
||||
cpu: 45,
|
||||
memory: 62,
|
||||
disk: 38
|
||||
});
|
||||
|
||||
const recentActivity = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: 'User Created',
|
||||
description: 'New user account created for john.doe@example.com',
|
||||
timestamp: '2 hours ago',
|
||||
type: 'success',
|
||||
status: 'Completed'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'System Backup',
|
||||
description: 'Automated system backup completed successfully',
|
||||
timestamp: '6 hours ago',
|
||||
type: 'info',
|
||||
status: 'Completed'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Permission Update',
|
||||
description: 'User permissions updated for board member',
|
||||
timestamp: '1 day ago',
|
||||
type: 'warning',
|
||||
status: 'Completed'
|
||||
}
|
||||
]);
|
||||
|
||||
const securityAlerts = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: 'Failed Login Attempts',
|
||||
description: '3 failed login attempts from IP 192.168.1.100',
|
||||
severity: 'warning',
|
||||
level: 'Medium'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'System Update Available',
|
||||
description: 'Security update available for Keycloak',
|
||||
severity: 'info',
|
||||
level: 'Low'
|
||||
}
|
||||
]);
|
||||
|
||||
// Load admin statistics
|
||||
const loadAdminStats = async () => {
|
||||
try {
|
||||
const stats = await $fetch<{
|
||||
totalUsers: number;
|
||||
activeUsers: number;
|
||||
totalSessions: number;
|
||||
diskUsage: string;
|
||||
memoryUsage: string;
|
||||
}>('/api/admin/stats');
|
||||
|
||||
systemStats.value = {
|
||||
totalUsers: stats.totalUsers || 0,
|
||||
activeUsers: stats.activeUsers || 0,
|
||||
totalSessions: stats.totalSessions || 0,
|
||||
diskUsage: stats.diskUsage || '0%',
|
||||
memoryUsage: stats.memoryUsage || '0%',
|
||||
uptime: '5d 12h'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Failed to load admin stats:', error);
|
||||
// Use mock data on error
|
||||
systemStats.value = {
|
||||
totalUsers: 156,
|
||||
activeUsers: 45,
|
||||
totalSessions: 67,
|
||||
diskUsage: '45%',
|
||||
memoryUsage: '62%',
|
||||
uptime: '5d 12h'
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Navigation methods (placeholder implementations)
|
||||
const navigateToUserManagement = () => {
|
||||
console.log('Navigate to user management');
|
||||
};
|
||||
|
||||
const navigateToAuditLogs = () => {
|
||||
console.log('Navigate to audit logs');
|
||||
};
|
||||
|
||||
const navigateToSystemConfig = () => {
|
||||
console.log('Navigate to system config');
|
||||
};
|
||||
|
||||
const initiateBackup = () => {
|
||||
console.log('Initiate system backup');
|
||||
};
|
||||
|
||||
const createNewUser = () => {
|
||||
console.log('Create new user');
|
||||
};
|
||||
|
||||
const generateSystemReport = () => {
|
||||
console.log('Generate system report');
|
||||
};
|
||||
|
||||
const managePermissions = () => {
|
||||
console.log('Manage permissions');
|
||||
};
|
||||
|
||||
const systemMaintenance = () => {
|
||||
console.log('System maintenance');
|
||||
};
|
||||
</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;
|
||||
}
|
||||
|
||||
.v-progress-linear {
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
345
pages/dashboard/board.vue
Normal file
345
pages/dashboard/board.vue
Normal file
@@ -0,0 +1,345 @@
|
||||
<template>
|
||||
<v-container>
|
||||
<!-- 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>
|
||||
|
||||
<!-- 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>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
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.'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Mock data for board dashboard
|
||||
const stats = ref({
|
||||
totalMembers: 156,
|
||||
activeMembers: 142,
|
||||
upcomingMeetings: 3,
|
||||
pendingActions: 7
|
||||
});
|
||||
|
||||
const nextMeeting = ref({
|
||||
date: 'January 15, 2025',
|
||||
time: '7:00 PM EST'
|
||||
});
|
||||
|
||||
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'
|
||||
}
|
||||
]);
|
||||
|
||||
// Navigation methods (placeholder implementations)
|
||||
const navigateToMeetings = () => {
|
||||
console.log('Navigate to meetings');
|
||||
};
|
||||
|
||||
const navigateToMembers = () => {
|
||||
console.log('Navigate to members');
|
||||
};
|
||||
|
||||
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>
|
||||
55
pages/dashboard/index.vue
Normal file
55
pages/dashboard/index.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div class="dashboard-router">
|
||||
<v-container v-if="loading" class="fill-height">
|
||||
<v-row justify="center" align="center">
|
||||
<v-col cols="auto" class="text-center">
|
||||
<v-progress-circular
|
||||
indeterminate
|
||||
color="primary"
|
||||
size="64"
|
||||
width="6"
|
||||
/>
|
||||
<p class="mt-4 text-h6">Loading your dashboard...</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
middleware: 'auth'
|
||||
});
|
||||
|
||||
const { user, userTier, isAuthenticated } = useAuth();
|
||||
const loading = ref(true);
|
||||
|
||||
// Route to tier-specific dashboard
|
||||
watchEffect(async () => {
|
||||
if (isAuthenticated.value && user.value) {
|
||||
const tierRoute = `/dashboard/${userTier.value}`;
|
||||
console.log('🔄 Routing to tier-specific dashboard:', tierRoute);
|
||||
await navigateTo(tierRoute, { replace: true });
|
||||
} else if (!isAuthenticated.value) {
|
||||
console.log('🔄 User not authenticated, redirecting to login');
|
||||
await navigateTo('/login');
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
// Small delay to ensure auth state is loaded
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 500);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dashboard-router {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.v-progress-circular {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
211
pages/dashboard/user.vue
Normal file
211
pages/dashboard/user.vue
Normal file
@@ -0,0 +1,211 @@
|
||||
<template>
|
||||
<v-container>
|
||||
<!-- 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 Member Portal
|
||||
</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Quick Actions -->
|
||||
<v-row class="mb-6">
|
||||
<v-col cols="12" md="4">
|
||||
<v-card class="pa-4 text-center" elevation="2" hover>
|
||||
<v-icon size="48" color="primary" class="mb-2">mdi-account</v-icon>
|
||||
<h3 class="mb-2">My Profile</h3>
|
||||
<p class="text-body-2 mb-4">View and update your information</p>
|
||||
<v-btn
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
style="border-color: #a31515; color: #a31515;"
|
||||
@click="navigateToProfile"
|
||||
>
|
||||
View Profile
|
||||
</v-btn>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="4">
|
||||
<v-card class="pa-4 text-center" elevation="2" hover>
|
||||
<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 upcoming association events</p>
|
||||
<v-btn
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
style="border-color: #a31515; color: #a31515;"
|
||||
@click="navigateToEvents"
|
||||
>
|
||||
View Events
|
||||
</v-btn>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="4">
|
||||
<v-card class="pa-4 text-center" elevation="2" hover>
|
||||
<v-icon size="48" color="primary" class="mb-2">mdi-file-document</v-icon>
|
||||
<h3 class="mb-2">Resources</h3>
|
||||
<p class="text-body-2 mb-4">Access member resources</p>
|
||||
<v-btn
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
style="border-color: #a31515; color: #a31515;"
|
||||
@click="navigateToResources"
|
||||
>
|
||||
View Resources
|
||||
</v-btn>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Recent Activity Section -->
|
||||
<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-clock-outline</v-icon>
|
||||
Recent Activity
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-4">
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Welcome to MonacoUSA Portal!</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
You've successfully logged in to your member dashboard.
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
<v-list-item-action>
|
||||
<v-chip color="success" size="small">New</v-chip>
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Member Information -->
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-card elevation="2">
|
||||
<v-card-title class="pa-4" style="background-color: #f5f5f5;">
|
||||
<v-icon class="mr-2" color="primary">mdi-information-outline</v-icon>
|
||||
Member Information
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-4">
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Name</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ user?.name || 'Not provided' }}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Email</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ user?.email || 'Not provided' }}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Member Type</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
<v-chip color="primary" size="small">{{ userTier.toUpperCase() }}</v-chip>
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="6">
|
||||
<v-card elevation="2">
|
||||
<v-card-title class="pa-4" style="background-color: #f5f5f5;">
|
||||
<v-icon class="mr-2" color="primary">mdi-help-circle-outline</v-icon>
|
||||
Need Help?
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-4">
|
||||
<p class="mb-4">
|
||||
If you need assistance or have questions about your membership,
|
||||
please don't hesitate to contact our support team.
|
||||
</p>
|
||||
<v-btn
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
style="border-color: #a31515; color: #a31515;"
|
||||
@click="contactSupport"
|
||||
>
|
||||
<v-icon start>mdi-email</v-icon>
|
||||
Contact Support
|
||||
</v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: 'dashboard',
|
||||
middleware: 'auth'
|
||||
});
|
||||
|
||||
const { firstName, user, userTier } = useAuth();
|
||||
|
||||
// Navigation methods (placeholder implementations)
|
||||
const navigateToProfile = () => {
|
||||
// TODO: Implement profile navigation
|
||||
console.log('Navigate to profile');
|
||||
};
|
||||
|
||||
const navigateToEvents = () => {
|
||||
// TODO: Implement events navigation
|
||||
console.log('Navigate to events');
|
||||
};
|
||||
|
||||
const navigateToResources = () => {
|
||||
// TODO: Implement resources navigation
|
||||
console.log('Navigate to resources');
|
||||
};
|
||||
|
||||
const contactSupport = () => {
|
||||
// TODO: Implement support contact
|
||||
console.log('Contact support');
|
||||
};
|
||||
</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;
|
||||
}
|
||||
</style>
|
||||
@@ -5,8 +5,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { authenticated } = useAuth();
|
||||
const { isAuthenticated } = useAuth();
|
||||
|
||||
// Redirect based on authentication status
|
||||
await navigateTo(authenticated.value ? '/dashboard' : '/login');
|
||||
await navigateTo(isAuthenticated.value ? '/dashboard' : '/login');
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user