monacousa-portal/pages/board/dashboard-v2.vue

886 lines
20 KiB
Vue

<template>
<div class="board-dashboard-v2">
<!-- Executive Header -->
<div class="executive-header">
<h1 class="dashboard-title">Executive Dashboard</h1>
<p class="dashboard-subtitle">Strategic insights and governance overview</p>
</div>
<!-- KPI Cards with Neumorphic Design -->
<div class="kpi-grid">
<div class="kpi-card neumorphic-card" v-for="kpi in kpis" :key="kpi.id">
<div class="kpi-header">
<div class="kpi-icon-wrapper neumorphic-inset">
<Icon :name="kpi.icon" class="kpi-icon" :style="{ color: kpi.color }" />
</div>
<div class="kpi-trend" :class="kpi.trendType">
<Icon :name="kpi.trendIcon" class="trend-icon" />
<span>{{ kpi.trendValue }}</span>
</div>
</div>
<div class="kpi-content">
<div class="kpi-value">{{ kpi.value }}</div>
<div class="kpi-label">{{ kpi.label }}</div>
<div class="kpi-progress">
<div class="progress-bar neumorphic-inset">
<div class="progress-fill" :style="{ width: kpi.progress + '%', background: kpi.color }"></div>
</div>
<span class="progress-text">{{ kpi.progress }}% of target</span>
</div>
</div>
</div>
</div>
<!-- Strategic Initiatives & Governance -->
<div class="governance-grid">
<!-- Strategic Initiatives -->
<div class="initiative-card neumorphic-card">
<div class="card-header">
<Icon name="mdi:target" class="header-icon" />
<h2>Strategic Initiatives</h2>
<div class="morphing-select-wrapper">
<button class="select-trigger neumorphic-button small" @click="toggleQuarter">
<span>{{ selectedQuarter }}</span>
<Icon name="mdi:chevron-down" class="dropdown-icon" :class="{ 'rotate': showQuarter }" />
</button>
<Transition name="morph">
<div v-if="showQuarter" class="morphing-dropdown">
<div
v-for="quarter in quarters"
:key="quarter"
class="dropdown-option"
@click="selectQuarter(quarter)"
>
{{ quarter }}
</div>
</div>
</Transition>
</div>
</div>
<div class="initiatives-list">
<div v-for="initiative in strategicInitiatives" :key="initiative.id" class="initiative-item">
<div class="initiative-header">
<span class="initiative-name">{{ initiative.name }}</span>
<span class="initiative-status" :class="initiative.status">{{ initiative.statusText }}</span>
</div>
<div class="initiative-progress neumorphic-inset">
<div class="progress-bar-slim">
<div class="progress-fill-slim" :style="{ width: initiative.progress + '%' }"></div>
</div>
</div>
<div class="initiative-meta">
<span class="initiative-owner">Owner: {{ initiative.owner }}</span>
<span class="initiative-deadline">Due: {{ initiative.deadline }}</span>
</div>
</div>
</div>
</div>
<!-- Committee Overview -->
<div class="committee-card neumorphic-card">
<div class="card-header">
<Icon name="mdi:account-group-outline" class="header-icon" />
<h2>Committee Activities</h2>
</div>
<div class="committee-grid">
<div v-for="committee in committees" :key="committee.id" class="committee-item neumorphic-inset">
<div class="committee-header">
<Icon :name="committee.icon" class="committee-icon" :style="{ color: committee.color }" />
<h3>{{ committee.name }}</h3>
</div>
<div class="committee-stats">
<div class="stat">
<span class="stat-value">{{ committee.members }}</span>
<span class="stat-label">Members</span>
</div>
<div class="stat">
<span class="stat-value">{{ committee.meetings }}</span>
<span class="stat-label">Meetings</span>
</div>
</div>
<button class="neumorphic-button small full-width">View Details</button>
</div>
</div>
</div>
</div>
<!-- Financial Overview -->
<div class="financial-section neumorphic-card">
<div class="card-header">
<Icon name="mdi:finance" class="header-icon" />
<h2>Financial Overview</h2>
<div class="time-selector">
<button
v-for="period in timePeriods"
:key="period"
class="time-button neumorphic-button small"
:class="{ 'active': selectedPeriod === period }"
@click="selectedPeriod = period"
>
{{ period }}
</button>
</div>
</div>
<div class="financial-grid">
<div class="revenue-chart">
<h3>Revenue Trend</h3>
<div class="chart-placeholder neumorphic-inset">
<Icon name="mdi:chart-line" class="chart-icon" />
<span>Revenue chart visualization</span>
</div>
</div>
<div class="financial-metrics">
<div v-for="metric in financialMetrics" :key="metric.id" class="metric-item">
<div class="metric-label">{{ metric.label }}</div>
<div class="metric-value" :class="metric.type">{{ metric.value }}</div>
<div class="metric-change">
<Icon :name="metric.changeIcon" class="change-icon" />
<span>{{ metric.change }} from last period</span>
</div>
</div>
</div>
</div>
</div>
<!-- Governance Actions -->
<div class="governance-actions">
<div class="action-card neumorphic-card">
<Icon name="mdi:calendar-check" class="action-icon" />
<h3>Board Meetings</h3>
<p>Schedule and manage board meetings</p>
<button class="neumorphic-button primary">Schedule Meeting</button>
</div>
<div class="action-card neumorphic-card">
<Icon name="mdi:file-document-outline" class="action-icon" />
<h3>Documents</h3>
<p>Access governance documents</p>
<button class="neumorphic-button primary">View Documents</button>
</div>
<div class="action-card neumorphic-card">
<Icon name="mdi:vote" class="action-icon" />
<h3>Resolutions</h3>
<p>Review and vote on resolutions</p>
<button class="neumorphic-button primary">View Resolutions</button>
</div>
<div class="action-card neumorphic-card">
<Icon name="mdi:chart-box-outline" class="action-icon" />
<h3>Reports</h3>
<p>Generate executive reports</p>
<button class="neumorphic-button primary">Generate Report</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
// Define page meta
definePageMeta({
layout: 'board',
middleware: 'auth'
})
// KPIs
const kpis = ref([
{
id: 1,
label: 'Member Growth',
value: '24.8%',
icon: 'mdi:account-multiple-plus',
color: '#10B981',
trendType: 'positive',
trendIcon: 'mdi:trending-up',
trendValue: '+5.2%',
progress: 82
},
{
id: 2,
label: 'Revenue YTD',
value: '$2.4M',
icon: 'mdi:cash-multiple',
color: '#3B82F6',
trendType: 'positive',
trendIcon: 'mdi:trending-up',
trendValue: '+12.3%',
progress: 68
},
{
id: 3,
label: 'Member Retention',
value: '94.5%',
icon: 'mdi:account-heart',
color: '#CC0000',
trendType: 'positive',
trendIcon: 'mdi:trending-up',
trendValue: '+2.1%',
progress: 95
},
{
id: 4,
label: 'NPS Score',
value: '72',
icon: 'mdi:emoticon-happy',
color: '#F59E0B',
trendType: 'neutral',
trendIcon: 'mdi:minus',
trendValue: '0%',
progress: 72
}
])
// Strategic Initiatives
const strategicInitiatives = ref([
{
id: 1,
name: 'Digital Transformation Initiative',
status: 'on-track',
statusText: 'On Track',
progress: 65,
owner: 'John Smith',
deadline: 'Q2 2024'
},
{
id: 2,
name: 'Member Experience Enhancement',
status: 'ahead',
statusText: 'Ahead',
progress: 78,
owner: 'Sarah Johnson',
deadline: 'Q1 2024'
},
{
id: 3,
name: 'International Expansion',
status: 'at-risk',
statusText: 'At Risk',
progress: 42,
owner: 'Mike Chen',
deadline: 'Q3 2024'
}
])
// Committees
const committees = ref([
{
id: 1,
name: 'Finance',
icon: 'mdi:calculator',
color: '#3B82F6',
members: 7,
meetings: 12
},
{
id: 2,
name: 'Governance',
icon: 'mdi:gavel',
color: '#CC0000',
members: 5,
meetings: 8
},
{
id: 3,
name: 'Audit',
icon: 'mdi:magnify',
color: '#F59E0B',
members: 4,
meetings: 10
},
{
id: 4,
name: 'Compensation',
icon: 'mdi:cash',
color: '#10B981',
members: 6,
meetings: 6
}
])
// Financial Metrics
const financialMetrics = ref([
{
id: 1,
label: 'Total Revenue',
value: '$2.4M',
type: 'positive',
changeIcon: 'mdi:arrow-up',
change: '+12.3%'
},
{
id: 2,
label: 'Operating Expenses',
value: '$1.8M',
type: 'neutral',
changeIcon: 'mdi:arrow-up',
change: '+8.1%'
},
{
id: 3,
label: 'Net Profit',
value: '$620K',
type: 'positive',
changeIcon: 'mdi:arrow-up',
change: '+24.5%'
},
{
id: 4,
label: 'Cash Flow',
value: '$450K',
type: 'positive',
changeIcon: 'mdi:arrow-up',
change: '+15.2%'
}
])
// Dropdown states
const showQuarter = ref(false)
const selectedQuarter = ref('Q4 2023')
const quarters = ref(['Q1 2023', 'Q2 2023', 'Q3 2023', 'Q4 2023', 'Q1 2024'])
// Time period selector
const selectedPeriod = ref('YTD')
const timePeriods = ref(['MTD', 'QTD', 'YTD'])
// Methods
const toggleQuarter = () => {
showQuarter.value = !showQuarter.value
}
const selectQuarter = (quarter) => {
selectedQuarter.value = quarter
showQuarter.value = false
}
onMounted(() => {
// Close dropdown when clicking outside
document.addEventListener('click', (e) => {
if (!e.target.closest('.morphing-select-wrapper')) {
showQuarter.value = false
}
})
})
</script>
<style lang="scss" scoped>
@import '@/assets/scss/design-system-v2.scss';
.board-dashboard-v2 {
padding: 2rem;
background: linear-gradient(135deg, $neutral-50 0%, $neutral-100 100%);
min-height: 100vh;
}
// Executive Header
.executive-header {
text-align: center;
margin-bottom: 3rem;
.dashboard-title {
font-size: $text-4xl;
font-weight: $font-bold;
background: linear-gradient(135deg, $primary-600, $primary-800);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 0.5rem;
}
.dashboard-subtitle {
color: $neutral-600;
font-size: $text-lg;
}
}
// KPI Grid
.kpi-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
margin-bottom: 3rem;
}
.kpi-card {
@include neumorphic-card('md');
padding: 1.5rem;
.kpi-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
}
.kpi-icon-wrapper {
width: 48px;
height: 48px;
border-radius: $radius-lg;
display: flex;
align-items: center;
justify-content: center;
box-shadow: $shadow-inset-sm;
.kpi-icon {
width: 24px;
height: 24px;
}
}
.kpi-trend {
display: flex;
align-items: center;
gap: 0.25rem;
font-size: $text-sm;
font-weight: $font-semibold;
&.positive { color: $success-500; }
&.negative { color: $error-500; }
&.neutral { color: $neutral-600; }
.trend-icon {
width: 16px;
height: 16px;
}
}
.kpi-value {
font-size: $text-3xl;
font-weight: $font-bold;
color: $neutral-800;
margin-bottom: 0.5rem;
}
.kpi-label {
color: $neutral-600;
font-size: $text-sm;
margin-bottom: 1rem;
}
.kpi-progress {
.progress-bar {
height: 8px;
border-radius: $radius-full;
overflow: hidden;
margin-bottom: 0.5rem;
.progress-fill {
height: 100%;
border-radius: $radius-full;
transition: width 0.5s $spring-smooth;
}
}
.progress-text {
font-size: $text-xs;
color: $neutral-500;
}
}
}
// Governance Grid
.governance-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
margin-bottom: 2rem;
@media (max-width: $breakpoint-lg) {
grid-template-columns: 1fr;
}
}
.initiative-card,
.committee-card {
@include neumorphic-card('md');
padding: 2rem;
.card-header {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 1.5rem;
position: relative;
.header-icon {
width: 24px;
height: 24px;
color: $primary-600;
}
h2 {
font-size: $text-xl;
font-weight: $font-semibold;
color: $neutral-800;
flex: 1;
}
}
}
// Strategic Initiatives
.initiatives-list {
display: flex;
flex-direction: column;
gap: 1rem;
}
.initiative-item {
padding: 1rem;
border-radius: $radius-lg;
background: $neutral-50;
.initiative-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.75rem;
}
.initiative-name {
font-weight: $font-medium;
color: $neutral-800;
font-size: $text-sm;
}
.initiative-status {
padding: 0.25rem 0.75rem;
border-radius: $radius-full;
font-size: $text-xs;
font-weight: $font-medium;
&.on-track {
background: rgba($success-500, 0.1);
color: $success-500;
}
&.ahead {
background: rgba($blue-500, 0.1);
color: $blue-500;
}
&.at-risk {
background: rgba($warning-500, 0.1);
color: $warning-500;
}
}
.initiative-progress {
margin-bottom: 0.75rem;
padding: 0.25rem;
border-radius: $radius-full;
.progress-bar-slim {
height: 4px;
border-radius: $radius-full;
background: rgba($neutral-300, 0.3);
.progress-fill-slim {
height: 100%;
background: linear-gradient(135deg, $primary-600, $primary-700);
border-radius: $radius-full;
transition: width 0.5s $spring-smooth;
}
}
}
.initiative-meta {
display: flex;
justify-content: space-between;
font-size: $text-xs;
color: $neutral-600;
}
}
// Committee Grid
.committee-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
}
.committee-item {
padding: 1rem;
border-radius: $radius-lg;
.committee-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
.committee-icon {
width: 20px;
height: 20px;
}
h3 {
font-size: $text-sm;
font-weight: $font-semibold;
color: $neutral-800;
}
}
.committee-stats {
display: flex;
justify-content: space-around;
margin-bottom: 1rem;
.stat {
text-align: center;
.stat-value {
display: block;
font-size: $text-xl;
font-weight: $font-bold;
color: $neutral-800;
}
.stat-label {
font-size: $text-xs;
color: $neutral-600;
}
}
}
}
// Financial Section
.financial-section {
@include neumorphic-card('lg');
padding: 2rem;
margin-bottom: 2rem;
.time-selector {
display: flex;
gap: 0.5rem;
.time-button {
&.active {
background: linear-gradient(145deg, $primary-600, $primary-700);
color: white;
}
}
}
}
.financial-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
@media (max-width: $breakpoint-md) {
grid-template-columns: 1fr;
}
}
.revenue-chart {
h3 {
font-size: $text-lg;
margin-bottom: 1rem;
color: $neutral-800;
}
.chart-placeholder {
height: 200px;
border-radius: $radius-lg;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: $neutral-500;
.chart-icon {
width: 48px;
height: 48px;
margin-bottom: 0.5rem;
opacity: 0.5;
}
}
}
.financial-metrics {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
}
.metric-item {
padding: 1rem;
.metric-label {
font-size: $text-xs;
color: $neutral-600;
margin-bottom: 0.5rem;
}
.metric-value {
font-size: $text-xl;
font-weight: $font-bold;
margin-bottom: 0.5rem;
&.positive { color: $success-500; }
&.negative { color: $error-500; }
&.neutral { color: $neutral-800; }
}
.metric-change {
display: flex;
align-items: center;
gap: 0.25rem;
font-size: $text-xs;
color: $neutral-600;
.change-icon {
width: 14px;
height: 14px;
}
}
}
// Governance Actions
.governance-actions {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
}
.action-card {
@include neumorphic-card('md');
padding: 2rem;
text-align: center;
transition: all $transition-base;
&:hover {
@include neumorphic-card('lg');
transform: translateY(-2px);
}
.action-icon {
width: 48px;
height: 48px;
color: $primary-600;
margin-bottom: 1rem;
}
h3 {
font-size: $text-lg;
font-weight: $font-semibold;
color: $neutral-800;
margin-bottom: 0.5rem;
}
p {
color: $neutral-600;
font-size: $text-sm;
margin-bottom: 1.5rem;
}
}
// Morphing Dropdown
.morphing-select-wrapper {
position: relative;
.select-trigger {
display: flex;
align-items: center;
gap: 0.5rem;
.dropdown-icon {
width: 16px;
height: 16px;
transition: transform 0.3s $spring-smooth;
&.rotate {
transform: rotate(180deg);
}
}
}
}
.morphing-dropdown {
@include morphing-dropdown();
position: absolute;
top: calc(100% + 8px);
right: 0;
min-width: 150px;
z-index: $z-dropdown;
.dropdown-option {
padding: 0.5rem 1rem;
cursor: pointer;
transition: all $transition-fast;
color: $neutral-700;
font-size: $text-sm;
&:hover {
background: rgba($blue-500, 0.1);
color: $blue-600;
padding-left: 1.25rem;
}
}
}
// Neumorphic Elements
.neumorphic-card {
background: linear-gradient(145deg, #ffffff, #f0f0f0);
border-radius: $radius-xl;
box-shadow: $shadow-soft-md;
}
.neumorphic-button {
@include neumorphic-button();
padding: 0.75rem 1.5rem;
border: none;
border-radius: $radius-lg;
font-weight: $font-medium;
color: $neutral-700;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 0.5rem;
&.primary {
background: linear-gradient(145deg, $primary-600, $primary-700);
color: white;
&:hover {
background: linear-gradient(145deg, $primary-700, $primary-800);
}
}
&.small {
padding: 0.5rem 0.75rem;
font-size: $text-sm;
}
&.full-width {
width: 100%;
justify-content: center;
}
}
.neumorphic-inset {
box-shadow: $shadow-inset-sm;
background: linear-gradient(145deg, #e6e6e6, #ffffff);
}
// Transitions
.morph-enter-active,
.morph-leave-active {
transition: all 0.3s $spring-smooth;
}
.morph-enter-from {
opacity: 0;
transform: scale(0.95) translateY(-10px);
}
.morph-leave-to {
opacity: 0;
transform: scale(0.95) translateY(-10px);
}
</style>