Complete infrastructure reorganization to role-based structure
All checks were successful
Build And Push Image / docker (push) Successful in 1m50s
All checks were successful
Build And Push Image / docker (push) Successful in 1m50s
- Created all missing admin pages (users, settings, events, members, payments) - Created board pages (governance, meetings) - Updated dashboard router to use new /admin, /board, /member structure - Added isMember alias to useAuth composable for consistency - All pages now use correct role-based layouts and middleware - Build verified successfully The platform now has a clean separation: - /admin/* - Administrator dashboard and tools - /board/* - Board member governance and meetings - /member/* - Member portal and resources Next steps: Complete remaining member pages and clean up old dashboard files
This commit is contained in:
350
pages/board/governance/index.vue
Normal file
350
pages/board/governance/index.vue
Normal file
@@ -0,0 +1,350 @@
|
||||
<template>
|
||||
<v-container fluid>
|
||||
<!-- Header -->
|
||||
<v-row class="mb-6">
|
||||
<v-col>
|
||||
<h1 class="text-h3 font-weight-bold mb-2">Governance Documents</h1>
|
||||
<p class="text-body-1 text-medium-emphasis">Access bylaws, policies, and governance materials</p>
|
||||
</v-col>
|
||||
<v-col cols="auto">
|
||||
<v-btn
|
||||
color="primary"
|
||||
variant="flat"
|
||||
prepend-icon="mdi-file-upload"
|
||||
@click="showUploadDialog = true"
|
||||
>
|
||||
Upload Document
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Document Categories -->
|
||||
<v-row class="mb-6">
|
||||
<v-col cols="12">
|
||||
<v-chip-group
|
||||
v-model="selectedCategory"
|
||||
selected-class="bg-primary"
|
||||
mandatory
|
||||
>
|
||||
<v-chip
|
||||
v-for="category in categories"
|
||||
:key="category.value"
|
||||
:value="category.value"
|
||||
variant="outlined"
|
||||
>
|
||||
<v-icon start>{{ category.icon }}</v-icon>
|
||||
{{ category.title }}
|
||||
<v-badge
|
||||
:content="category.count"
|
||||
color="primary"
|
||||
inline
|
||||
class="ml-2"
|
||||
/>
|
||||
</v-chip>
|
||||
</v-chip-group>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Documents List -->
|
||||
<v-row>
|
||||
<v-col
|
||||
v-for="document in filteredDocuments"
|
||||
:key="document.id"
|
||||
cols="12"
|
||||
md="6"
|
||||
lg="4"
|
||||
>
|
||||
<v-card elevation="2" hover>
|
||||
<v-card-text>
|
||||
<div class="d-flex align-start">
|
||||
<v-icon
|
||||
:icon="getDocumentIcon(document.type)"
|
||||
size="40"
|
||||
:color="getDocumentColor(document.type)"
|
||||
class="mr-3"
|
||||
/>
|
||||
<div class="flex-grow-1">
|
||||
<h3 class="text-body-1 font-weight-medium mb-1">
|
||||
{{ document.title }}
|
||||
</h3>
|
||||
<p class="text-caption text-medium-emphasis mb-2">
|
||||
{{ document.description }}
|
||||
</p>
|
||||
<div class="d-flex align-center text-caption">
|
||||
<v-icon size="x-small" class="mr-1">mdi-calendar</v-icon>
|
||||
<span class="text-medium-emphasis">
|
||||
Updated {{ formatDate(document.updatedAt) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="d-flex align-center text-caption mt-1">
|
||||
<v-icon size="x-small" class="mr-1">mdi-file-outline</v-icon>
|
||||
<span class="text-medium-emphasis">
|
||||
{{ document.fileSize }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-divider />
|
||||
<v-card-actions>
|
||||
<v-btn
|
||||
variant="text"
|
||||
color="primary"
|
||||
size="small"
|
||||
@click="viewDocument(document)"
|
||||
>
|
||||
<v-icon start>mdi-eye</v-icon>
|
||||
View
|
||||
</v-btn>
|
||||
<v-btn
|
||||
variant="text"
|
||||
color="primary"
|
||||
size="small"
|
||||
@click="downloadDocument(document)"
|
||||
>
|
||||
<v-icon start>mdi-download</v-icon>
|
||||
Download
|
||||
</v-btn>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
icon="mdi-dots-vertical"
|
||||
size="small"
|
||||
variant="text"
|
||||
>
|
||||
<v-menu activator="parent">
|
||||
<v-list density="compact">
|
||||
<v-list-item @click="shareDocument(document)">
|
||||
<v-list-item-title>
|
||||
<v-icon size="small" class="mr-2">mdi-share-variant</v-icon>
|
||||
Share
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="editDocument(document)">
|
||||
<v-list-item-title>
|
||||
<v-icon size="small" class="mr-2">mdi-pencil</v-icon>
|
||||
Edit Details
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="archiveDocument(document)">
|
||||
<v-list-item-title>
|
||||
<v-icon size="small" class="mr-2">mdi-archive</v-icon>
|
||||
Archive
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
<v-list-item @click="deleteDocument(document)" class="text-error">
|
||||
<v-list-item-title>
|
||||
<v-icon size="small" class="mr-2">mdi-delete</v-icon>
|
||||
Delete
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Upload Dialog -->
|
||||
<v-dialog v-model="showUploadDialog" max-width="600">
|
||||
<v-card>
|
||||
<v-card-title>Upload Document</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form ref="uploadForm">
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-file-input
|
||||
v-model="uploadForm.file"
|
||||
label="Select Document"
|
||||
accept=".pdf,.doc,.docx"
|
||||
variant="outlined"
|
||||
prepend-icon="mdi-file-document"
|
||||
required
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
v-model="uploadForm.title"
|
||||
label="Document Title"
|
||||
variant="outlined"
|
||||
required
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-textarea
|
||||
v-model="uploadForm.description"
|
||||
label="Description"
|
||||
variant="outlined"
|
||||
rows="2"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-select
|
||||
v-model="uploadForm.category"
|
||||
label="Category"
|
||||
:items="categories"
|
||||
item-title="title"
|
||||
item-value="value"
|
||||
variant="outlined"
|
||||
required
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn variant="text" @click="showUploadDialog = false">Cancel</v-btn>
|
||||
<v-btn color="primary" variant="flat" @click="uploadDocument">Upload</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: 'board',
|
||||
middleware: 'board'
|
||||
});
|
||||
|
||||
// State
|
||||
const showUploadDialog = ref(false);
|
||||
const selectedCategory = ref('all');
|
||||
|
||||
// Form data
|
||||
const uploadForm = ref({
|
||||
file: null,
|
||||
title: '',
|
||||
description: '',
|
||||
category: ''
|
||||
});
|
||||
|
||||
// Categories
|
||||
const categories = [
|
||||
{ title: 'All Documents', value: 'all', icon: 'mdi-file-multiple', count: 12 },
|
||||
{ title: 'Bylaws', value: 'bylaws', icon: 'mdi-gavel', count: 2 },
|
||||
{ title: 'Policies', value: 'policies', icon: 'mdi-shield-check', count: 4 },
|
||||
{ title: 'Minutes', value: 'minutes', icon: 'mdi-clock-outline', count: 3 },
|
||||
{ title: 'Reports', value: 'reports', icon: 'mdi-chart-box', count: 3 }
|
||||
];
|
||||
|
||||
// Mock documents
|
||||
const documents = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: 'Association Bylaws 2024',
|
||||
description: 'Updated bylaws governing the association operations',
|
||||
type: 'bylaws',
|
||||
fileSize: '2.4 MB',
|
||||
updatedAt: new Date('2024-01-01')
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Code of Conduct Policy',
|
||||
description: 'Member code of conduct and ethics guidelines',
|
||||
type: 'policies',
|
||||
fileSize: '548 KB',
|
||||
updatedAt: new Date('2023-12-15')
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Board Meeting Minutes - January 2024',
|
||||
description: 'Minutes from the January board meeting',
|
||||
type: 'minutes',
|
||||
fileSize: '128 KB',
|
||||
updatedAt: new Date('2024-01-10')
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Annual Financial Report 2023',
|
||||
description: 'Comprehensive financial report for fiscal year 2023',
|
||||
type: 'reports',
|
||||
fileSize: '4.2 MB',
|
||||
updatedAt: new Date('2024-01-05')
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'Conflict of Interest Policy',
|
||||
description: 'Policy for managing conflicts of interest',
|
||||
type: 'policies',
|
||||
fileSize: '315 KB',
|
||||
updatedAt: new Date('2023-11-20')
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: 'Strategic Plan 2024-2026',
|
||||
description: 'Three-year strategic planning document',
|
||||
type: 'reports',
|
||||
fileSize: '1.8 MB',
|
||||
updatedAt: new Date('2023-12-01')
|
||||
}
|
||||
]);
|
||||
|
||||
// Computed
|
||||
const filteredDocuments = computed(() => {
|
||||
if (selectedCategory.value === 'all') {
|
||||
return documents.value;
|
||||
}
|
||||
return documents.value.filter(d => d.type === selectedCategory.value);
|
||||
});
|
||||
|
||||
// Methods
|
||||
const getDocumentIcon = (type: string) => {
|
||||
switch (type) {
|
||||
case 'bylaws': return 'mdi-gavel';
|
||||
case 'policies': return 'mdi-shield-check';
|
||||
case 'minutes': return 'mdi-clock-outline';
|
||||
case 'reports': return 'mdi-chart-box';
|
||||
default: return 'mdi-file-document';
|
||||
}
|
||||
};
|
||||
|
||||
const getDocumentColor = (type: string) => {
|
||||
switch (type) {
|
||||
case 'bylaws': return 'error';
|
||||
case 'policies': return 'warning';
|
||||
case 'minutes': return 'info';
|
||||
case 'reports': return 'success';
|
||||
default: return 'primary';
|
||||
}
|
||||
};
|
||||
|
||||
const formatDate = (date: Date) => {
|
||||
return new Date(date).toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
});
|
||||
};
|
||||
|
||||
const viewDocument = (document: any) => {
|
||||
console.log('View document:', document);
|
||||
};
|
||||
|
||||
const downloadDocument = (document: any) => {
|
||||
console.log('Download document:', document);
|
||||
};
|
||||
|
||||
const shareDocument = (document: any) => {
|
||||
console.log('Share document:', document);
|
||||
};
|
||||
|
||||
const editDocument = (document: any) => {
|
||||
console.log('Edit document:', document);
|
||||
};
|
||||
|
||||
const archiveDocument = (document: any) => {
|
||||
console.log('Archive document:', document);
|
||||
};
|
||||
|
||||
const deleteDocument = (document: any) => {
|
||||
console.log('Delete document:', document);
|
||||
};
|
||||
|
||||
const uploadDocument = () => {
|
||||
console.log('Upload document:', uploadForm.value);
|
||||
showUploadDialog.value = false;
|
||||
};
|
||||
</script>
|
||||
293
pages/board/meetings/index.vue
Normal file
293
pages/board/meetings/index.vue
Normal file
@@ -0,0 +1,293 @@
|
||||
<template>
|
||||
<v-container fluid>
|
||||
<!-- Header -->
|
||||
<v-row class="mb-6">
|
||||
<v-col>
|
||||
<h1 class="text-h3 font-weight-bold mb-2">Board Meetings</h1>
|
||||
<p class="text-body-1 text-medium-emphasis">Schedule and manage board meetings</p>
|
||||
</v-col>
|
||||
<v-col cols="auto">
|
||||
<v-btn
|
||||
color="primary"
|
||||
variant="flat"
|
||||
prepend-icon="mdi-calendar-plus"
|
||||
@click="showScheduleDialog = true"
|
||||
>
|
||||
Schedule Meeting
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Meeting Tabs -->
|
||||
<v-tabs v-model="activeTab" color="primary" class="mb-6">
|
||||
<v-tab value="upcoming">Upcoming</v-tab>
|
||||
<v-tab value="past">Past Meetings</v-tab>
|
||||
<v-tab value="calendar">Calendar View</v-tab>
|
||||
</v-tabs>
|
||||
|
||||
<v-window v-model="activeTab">
|
||||
<!-- Upcoming Meetings -->
|
||||
<v-window-item value="upcoming">
|
||||
<v-row>
|
||||
<v-col
|
||||
v-for="meeting in upcomingMeetings"
|
||||
:key="meeting.id"
|
||||
cols="12"
|
||||
>
|
||||
<v-card elevation="2" class="mb-3">
|
||||
<v-card-text>
|
||||
<v-row align="center">
|
||||
<v-col cols="auto">
|
||||
<v-avatar color="primary" size="56">
|
||||
<v-icon>mdi-calendar</v-icon>
|
||||
</v-avatar>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<h3 class="text-h6 mb-1">{{ meeting.title }}</h3>
|
||||
<div class="text-body-2 text-medium-emphasis mb-2">
|
||||
<v-icon size="small" class="mr-1">mdi-calendar</v-icon>
|
||||
{{ formatDate(meeting.date) }}
|
||||
<v-icon size="small" class="ml-3 mr-1">mdi-clock</v-icon>
|
||||
{{ meeting.time }}
|
||||
<v-icon size="small" class="ml-3 mr-1">mdi-map-marker</v-icon>
|
||||
{{ meeting.location }}
|
||||
</div>
|
||||
<div class="text-body-2">
|
||||
<v-chip size="small" variant="tonal" class="mr-2">
|
||||
<v-icon start size="small">mdi-account-group</v-icon>
|
||||
{{ meeting.attendees }} Confirmed
|
||||
</v-chip>
|
||||
<v-chip size="small" variant="tonal" color="info">
|
||||
{{ meeting.type }}
|
||||
</v-chip>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="auto">
|
||||
<v-btn variant="outlined" color="primary" class="mr-2" @click="viewMeeting(meeting)">
|
||||
View Details
|
||||
</v-btn>
|
||||
<v-btn variant="flat" color="primary" @click="joinMeeting(meeting)">
|
||||
Join Meeting
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-window-item>
|
||||
|
||||
<!-- Past Meetings -->
|
||||
<v-window-item value="past">
|
||||
<v-data-table
|
||||
:headers="pastMeetingHeaders"
|
||||
:items="pastMeetings"
|
||||
class="elevation-2"
|
||||
hover
|
||||
>
|
||||
<template v-slot:item.title="{ item }">
|
||||
<div class="font-weight-medium">{{ item.title }}</div>
|
||||
</template>
|
||||
<template v-slot:item.date="{ item }">
|
||||
{{ formatDate(item.date) }}
|
||||
</template>
|
||||
<template v-slot:item.attendees="{ item }">
|
||||
<v-chip size="small" variant="tonal">
|
||||
{{ item.attendees }}/{{ item.totalInvited }}
|
||||
</v-chip>
|
||||
</template>
|
||||
<template v-slot:item.actions="{ item }">
|
||||
<v-btn icon="mdi-file-document" size="small" variant="text" @click="viewMinutes(item)" />
|
||||
<v-btn icon="mdi-download" size="small" variant="text" @click="downloadMaterials(item)" />
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-window-item>
|
||||
|
||||
<!-- Calendar View -->
|
||||
<v-window-item value="calendar">
|
||||
<v-card elevation="2">
|
||||
<v-card-text>
|
||||
<div class="text-center py-12">
|
||||
<v-icon size="64" color="primary" class="mb-4">mdi-calendar-month</v-icon>
|
||||
<h3 class="text-h5 mb-2">Calendar View</h3>
|
||||
<p class="text-body-1 text-medium-emphasis">
|
||||
Interactive calendar view coming soon
|
||||
</p>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
|
||||
<!-- Schedule Meeting Dialog -->
|
||||
<v-dialog v-model="showScheduleDialog" max-width="600">
|
||||
<v-card>
|
||||
<v-card-title>Schedule Board Meeting</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form ref="meetingForm">
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
v-model="meetingForm.title"
|
||||
label="Meeting Title"
|
||||
variant="outlined"
|
||||
required
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-select
|
||||
v-model="meetingForm.type"
|
||||
label="Meeting Type"
|
||||
:items="['Regular', 'Special', 'Emergency', 'Annual']"
|
||||
variant="outlined"
|
||||
required
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="meetingForm.date"
|
||||
label="Date"
|
||||
type="date"
|
||||
variant="outlined"
|
||||
required
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="meetingForm.time"
|
||||
label="Time"
|
||||
type="time"
|
||||
variant="outlined"
|
||||
required
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
v-model="meetingForm.location"
|
||||
label="Location"
|
||||
variant="outlined"
|
||||
required
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-textarea
|
||||
v-model="meetingForm.agenda"
|
||||
label="Agenda"
|
||||
variant="outlined"
|
||||
rows="3"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn variant="text" @click="showScheduleDialog = false">Cancel</v-btn>
|
||||
<v-btn color="primary" variant="flat" @click="scheduleMeeting">Schedule</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: 'board',
|
||||
middleware: 'board'
|
||||
});
|
||||
|
||||
// State
|
||||
const activeTab = ref('upcoming');
|
||||
const showScheduleDialog = ref(false);
|
||||
|
||||
// Form data
|
||||
const meetingForm = ref({
|
||||
title: '',
|
||||
type: '',
|
||||
date: '',
|
||||
time: '',
|
||||
location: '',
|
||||
agenda: ''
|
||||
});
|
||||
|
||||
// Mock data
|
||||
const upcomingMeetings = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: 'Monthly Board Meeting - February',
|
||||
date: new Date('2024-02-15'),
|
||||
time: '10:00 AM',
|
||||
location: 'Board Room / Zoom',
|
||||
type: 'Regular',
|
||||
attendees: 8
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Strategic Planning Session',
|
||||
date: new Date('2024-02-28'),
|
||||
time: '2:00 PM',
|
||||
location: 'Conference Center',
|
||||
type: 'Special',
|
||||
attendees: 12
|
||||
}
|
||||
]);
|
||||
|
||||
const pastMeetings = ref([
|
||||
{
|
||||
id: 3,
|
||||
title: 'Monthly Board Meeting - January',
|
||||
date: new Date('2024-01-15'),
|
||||
time: '10:00 AM',
|
||||
attendees: 9,
|
||||
totalInvited: 10
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Annual General Meeting',
|
||||
date: new Date('2024-01-05'),
|
||||
time: '6:00 PM',
|
||||
attendees: 45,
|
||||
totalInvited: 50
|
||||
}
|
||||
]);
|
||||
|
||||
// Table headers
|
||||
const pastMeetingHeaders = [
|
||||
{ title: 'Meeting', key: 'title' },
|
||||
{ title: 'Date', key: 'date' },
|
||||
{ title: 'Time', key: 'time' },
|
||||
{ title: 'Attendance', key: 'attendees' },
|
||||
{ title: 'Actions', key: 'actions', align: 'end' }
|
||||
];
|
||||
|
||||
// Methods
|
||||
const formatDate = (date: Date) => {
|
||||
return new Date(date).toLocaleDateString('en-US', {
|
||||
weekday: 'long',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
});
|
||||
};
|
||||
|
||||
const viewMeeting = (meeting: any) => {
|
||||
console.log('View meeting:', meeting);
|
||||
};
|
||||
|
||||
const joinMeeting = (meeting: any) => {
|
||||
console.log('Join meeting:', meeting);
|
||||
};
|
||||
|
||||
const viewMinutes = (meeting: any) => {
|
||||
console.log('View minutes:', meeting);
|
||||
};
|
||||
|
||||
const downloadMaterials = (meeting: any) => {
|
||||
console.log('Download materials:', meeting);
|
||||
};
|
||||
|
||||
const scheduleMeeting = () => {
|
||||
console.log('Schedule meeting:', meetingForm.value);
|
||||
showScheduleDialog.value = false;
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user