monacousa-portal/layouts/admin.vue

511 lines
14 KiB
Vue

<template>
<v-app>
<v-navigation-drawer v-model="drawer" app width="300">
<!-- Logo Section -->
<v-list-item class="pa-4 text-center">
<v-img
src="/MONACOUSA-Flags_376x376.png"
width="80"
height="80"
class="mx-auto mb-2"
/>
<div class="text-h6 font-weight-bold" style="color: #dc2626;">
MonacoUSA Portal
</div>
<div class="text-caption text-error font-weight-bold">ADMINISTRATOR</div>
</v-list-item>
<v-divider />
<!-- Navigation Menu -->
<v-list nav density="comfortable">
<!-- Admin Overview -->
<v-list-item
to="/admin/dashboard"
prepend-icon="mdi-view-dashboard"
title="Admin Dashboard"
value="dashboard"
/>
<!-- User Management -->
<v-list-group value="users">
<template v-slot:activator="{ props }">
<v-list-item
v-bind="props"
prepend-icon="mdi-account-cog"
title="User Management"
/>
</template>
<v-list-item
to="/admin/users"
title="All Users"
value="users-list"
/>
<v-list-item
to="/admin/users/roles"
title="Roles & Permissions"
value="roles"
/>
<v-list-item
@click="openKeycloak"
title="Keycloak Admin"
value="keycloak"
>
<template v-slot:append>
<v-icon size="small">mdi-open-in-new</v-icon>
</template>
</v-list-item>
</v-list-group>
<!-- Member Management -->
<v-list-group value="members">
<template v-slot:activator="{ props }">
<v-list-item
v-bind="props"
prepend-icon="mdi-account-group"
title="Member Management"
/>
</template>
<v-list-item
to="/admin/members"
title="All Members"
value="members-list"
/>
<v-list-item
to="/admin/members/import"
title="Import Members"
value="import"
/>
<v-list-item
to="/admin/members/export"
title="Export Data"
value="export"
/>
</v-list-group>
<!-- Financial Management -->
<v-list-group value="financial">
<template v-slot:activator="{ props }">
<v-list-item
v-bind="props"
prepend-icon="mdi-currency-usd"
title="Financial"
/>
</template>
<v-list-item
to="/admin/payments"
title="Payment Management"
value="payments"
/>
<v-list-item
to="/admin/payments/stripe"
title="Stripe Dashboard"
value="stripe"
/>
<v-list-item
to="/admin/payments/reports"
title="Financial Reports"
value="financial-reports"
/>
</v-list-group>
<!-- System Configuration -->
<v-list-group value="system">
<template v-slot:activator="{ props }">
<v-list-item
v-bind="props"
prepend-icon="mdi-cog"
title="System"
/>
</template>
<v-list-item
to="/admin/settings"
title="General Settings"
value="settings"
/>
<v-list-item
to="/admin/settings/email"
title="Email Configuration"
value="email"
/>
<v-list-item
to="/admin/settings/security"
title="Security Settings"
value="security"
/>
<v-list-item
to="/admin/logs"
title="System Logs"
value="logs"
/>
<v-list-item
to="/admin/backup"
title="Backup & Restore"
value="backup"
/>
</v-list-group>
<!-- Events Management -->
<v-list-item
to="/admin/events"
prepend-icon="mdi-calendar"
title="Events Management"
value="events"
/>
<!-- Analytics -->
<v-list-item
to="/admin/analytics"
prepend-icon="mdi-chart-line"
title="Analytics & Insights"
value="analytics"
/>
<v-divider class="my-2" />
<!-- Portal Access -->
<v-list-subheader>Portal Access</v-list-subheader>
<v-list-item
to="/board/dashboard"
prepend-icon="mdi-shield-account"
title="Board Portal"
value="board-view"
/>
<v-list-item
to="/member/dashboard"
prepend-icon="mdi-account"
title="Member Portal"
value="member-view"
/>
</v-list>
<!-- Footer -->
<template v-slot:append>
<div class="pa-4">
<v-card
color="error"
variant="flat"
class="text-center pa-3"
>
<v-icon size="small" class="mb-1" color="white">mdi-shield-crown</v-icon>
<div class="text-caption font-weight-bold text-white">ADMINISTRATOR</div>
</v-card>
</div>
</template>
</v-navigation-drawer>
<v-app-bar app elevation="0" flat>
<!-- Admin header with special styling -->
<template v-slot:extension>
<div class="admin-header-gradient"></div>
</template>
<!-- Navigation Toggle -->
<v-btn
icon
@click="drawer = !drawer"
class="mr-2"
color="white"
>
<v-icon>mdi-menu</v-icon>
</v-btn>
<v-toolbar-title class="text-white font-weight-bold d-flex align-center">
Admin Portal
<v-chip
size="x-small"
color="error"
variant="flat"
class="ml-2"
>
FULL ACCESS
</v-chip>
</v-toolbar-title>
<v-spacer />
<!-- System Status Indicator -->
<v-chip
:color="systemStatus === 'healthy' ? 'success' : 'warning'"
variant="flat"
size="small"
class="mr-2"
>
<v-icon start size="small">
{{ systemStatus === 'healthy' ? 'mdi-check-circle' : 'mdi-alert' }}
</v-icon>
System {{ systemStatus }}
</v-chip>
<!-- Quick Actions -->
<v-btn
icon
color="white"
@click="toggleCommandPalette"
>
<v-icon>mdi-console</v-icon>
</v-btn>
<v-btn icon color="white">
<v-badge
:content="alerts"
:value="alerts > 0"
color="error"
>
<v-icon>mdi-bell-alert</v-icon>
</v-badge>
</v-btn>
<!-- User Menu -->
<v-menu offset-y>
<template v-slot:activator="{ props }">
<v-btn icon v-bind="props" color="white">
<ProfileAvatar
:member-id="memberData?.member_id"
:member-name="user?.name"
:first-name="user?.firstName || memberData?.first_name"
:last-name="user?.lastName || memberData?.last_name"
size="small"
:lazy="false"
show-border
/>
</v-btn>
</template>
<v-list min-width="250">
<v-list-item>
<v-list-item-title class="font-weight-bold">
{{ user?.name || 'Administrator' }}
</v-list-item-title>
<v-list-item-subtitle>
{{ user?.email }}
</v-list-item-subtitle>
</v-list-item>
<v-list-item>
<v-chip
color="error"
size="x-small"
variant="flat"
>
ADMINISTRATOR
</v-chip>
</v-list-item>
<v-divider class="my-2" />
<v-list-item to="/admin/profile">
<template v-slot:prepend>
<v-icon>mdi-account</v-icon>
</template>
<v-list-item-title>Admin Profile</v-list-item-title>
</v-list-item>
<v-list-item to="/board/dashboard">
<template v-slot:prepend>
<v-icon>mdi-shield-account</v-icon>
</template>
<v-list-item-title>Board Portal</v-list-item-title>
</v-list-item>
<v-list-item to="/member/dashboard">
<template v-slot:prepend>
<v-icon>mdi-account-switch</v-icon>
</template>
<v-list-item-title>Member Portal</v-list-item-title>
</v-list-item>
<v-list-item to="/admin/settings">
<template v-slot:prepend>
<v-icon>mdi-cog</v-icon>
</template>
<v-list-item-title>System Settings</v-list-item-title>
</v-list-item>
<v-divider class="my-2" />
<v-list-item @click="handleLogout" class="text-error">
<template v-slot:prepend>
<v-icon color="error">mdi-logout</v-icon>
</template>
<v-list-item-title>Logout</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-app-bar>
<!-- Command Palette Dialog -->
<v-dialog v-model="commandPaletteOpen" max-width="600">
<v-card>
<v-card-title class="d-flex align-center">
<v-icon class="mr-2">mdi-console</v-icon>
Admin Command Palette
<v-spacer />
<v-btn icon @click="commandPaletteOpen = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-card-text>
<v-text-field
v-model="commandQuery"
label="Type a command..."
prepend-inner-icon="mdi-chevron-right"
variant="outlined"
autofocus
@keyup.enter="executeCommand"
/>
<div class="text-caption">
Available commands: clear-cache, rebuild-index, sync-users, export-data
</div>
</v-card-text>
</v-card>
</v-dialog>
<v-main>
<v-container fluid class="pa-6">
<!-- System Alerts Banner -->
<v-alert
v-if="systemAlerts.length > 0"
type="warning"
variant="tonal"
closable
class="mb-4"
>
<v-alert-title>System Alerts</v-alert-title>
<ul class="mt-2">
<li v-for="alert in systemAlerts" :key="alert.id">
{{ alert.message }}
</li>
</ul>
</v-alert>
<slot />
</v-container>
</v-main>
</v-app>
</template>
<script setup lang="ts">
import type { Member } from '~/utils/types';
const { user, logout } = useAuth();
const drawer = ref(true);
const alerts = ref(0);
const systemStatus = ref<'healthy' | 'warning' | 'error'>('healthy');
const systemAlerts = ref<Array<{ id: number; message: string }>>([]);
const commandPaletteOpen = ref(false);
const commandQuery = ref('');
// Fetch member data
const { data: sessionData } = await useFetch<{ success: boolean; member: Member | null }>('/api/auth/session', {
server: false
});
const memberData = computed<Member | null>(() => sessionData.value?.member || null);
// Load admin-specific data
onMounted(async () => {
try {
// Check system health
const healthCheck = await $fetch('/api/admin/system/health');
systemStatus.value = healthCheck?.data?.status || 'healthy';
// Get critical alerts
const alertsRes = await $fetch('/api/admin/alerts');
alerts.value = alertsRes?.data?.count || 0;
systemAlerts.value = alertsRes?.data?.alerts || [];
} catch (error) {
console.error('Error fetching admin data:', error);
systemStatus.value = 'warning';
}
});
const openKeycloak = () => {
window.open('https://auth.monacousa.org/admin', '_blank');
};
const toggleCommandPalette = () => {
commandPaletteOpen.value = true;
};
const executeCommand = async () => {
if (commandQuery.value) {
console.log('Executing command:', commandQuery.value);
// Implement command execution logic
commandPaletteOpen.value = false;
commandQuery.value = '';
}
};
const handleLogout = async () => {
await logout();
};
// Responsive drawer behavior
const { width } = useDisplay();
watch(width, (newWidth) => {
drawer.value = newWidth >= 1024;
}, { immediate: true });
</script>
<style scoped>
.admin-header-gradient {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, #991b1b 0%, #450a0a 100%);
z-index: -1;
}
.v-navigation-drawer {
background: linear-gradient(180deg, #ffffff 0%, #fef2f2 100%);
border-right: 2px solid rgba(153, 27, 27, 0.15);
}
.v-list-item {
border-radius: 12px;
margin: 4px 12px;
transition: all 0.2s ease;
}
.v-list-item:hover {
background-color: rgba(153, 27, 27, 0.05);
}
.v-list-item--active {
background: linear-gradient(135deg, rgba(153, 27, 27, 0.2) 0%, rgba(153, 27, 27, 0.1) 100%) !important;
color: #991b1b !important;
border-left: 3px solid #991b1b;
}
.v-list-item--active .v-icon {
color: #991b1b !important;
}
.v-list-group__items .v-list-item {
padding-left: 52px !important;
}
.v-list-subheader {
color: #991b1b !important;
font-weight: 600;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.v-app-bar {
background: transparent !important;
}
.v-main {
background: linear-gradient(180deg, #fafafa 0%, #f5f5f5 100%);
min-height: 100vh;
}
</style>