EDMS/pages/dms/access-management.vue
2025-05-31 14:58:52 +08:00

236 lines
8.8 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import { ref, computed, onMounted, defineAsyncComponent } from 'vue';
import { useDmsStore } from '~/stores/dms';
// Define page metadata
definePageMeta({
title: "Access Management",
middleware: ["auth"],
requiresAuth: true,
breadcrumb: [
{
name: "DMS",
path: "/dms",
},
{
name: "Access Management",
path: "/dms/access-management",
},
],
});
// Lazy load components to improve initial page load
const DMSApprovalQueue = defineAsyncComponent(() =>
import('~/components/dms/workflows/DMSApprovalQueue.vue')
);
const DMSAccessRequestTracker = defineAsyncComponent(() =>
import('~/components/dms/workflows/DMSAccessRequestTracker.vue')
);
// Store
const dmsStore = useDmsStore();
// Component state
const activeTab = ref('requests');
const isLoading = ref(true);
const hasError = ref(false);
const errorMessage = ref('');
// User role and permissions
const userPermissions = ref({
canApprove: false,
canReject: false,
canViewAll: false
});
// Tabs definition
const tabs = [
{
id: 'requests',
label: 'Access Requests',
icon: 'lock-access',
description: 'Manage pending and historical access requests'
},
{
id: 'metrics',
label: 'Performance Metrics',
icon: 'chart',
description: 'Track access request KPIs and time metrics'
}
];
// Methods
const changeTab = (tabId) => {
activeTab.value = tabId;
};
// Load user permissions
const loadUserPermissions = async () => {
try {
isLoading.value = true;
// In a real implementation, this would be fetched from an authentication service
// For now we'll simulate it with the store
const userId = dmsStore.currentUser.id;
const userRole = dmsStore.currentUser.role;
// Get permissions from the store (simulated Authentik integration)
const permissions = await dmsStore.getRbacPermissions(userId);
// Set the permissions
userPermissions.value = {
canApprove: permissions.permissions.accessRequests.approve || false,
canReject: permissions.permissions.accessRequests.reject || false,
canViewAll: permissions.permissions.accessRequests.viewAll || false
};
} catch (error) {
console.error('Failed to load user permissions:', error);
hasError.value = true;
errorMessage.value = 'Failed to load user permissions. Please try again.';
} finally {
isLoading.value = false;
}
};
// Get SVG icon
const getSvgIcon = (iconName) => {
const icons = {
'lock-access': `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>`,
'chart': `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="20" x2="18" y2="10"></line><line x1="12" y1="20" x2="12" y2="4"></line><line x1="6" y1="20" x2="6" y2="14"></line></svg>`,
'user': `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>`
};
return icons[iconName] || '';
};
// Lifecycle hooks
onMounted(() => {
loadUserPermissions();
});
</script>
<template>
<div class="dms-access-management">
<LayoutsBreadcrumb />
<rs-card class="h-full">
<template #body>
<div class="h-full flex flex-col">
<!-- Loading State -->
<div v-if="isLoading" class="flex items-center justify-center h-full">
<div class="text-center">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto mb-4"></div>
<p class="text-gray-600 dark:text-gray-400">Loading access management...</p>
</div>
</div>
<!-- Error State -->
<div v-else-if="hasError" class="flex items-center justify-center h-full">
<div class="text-center p-6">
<div class="text-red-500 text-5xl mb-4"></div>
<h2 class="text-xl font-semibold text-red-600 mb-2">Error Loading Access Management</h2>
<p class="text-gray-600 dark:text-gray-400 mb-4">{{ errorMessage }}</p>
<rs-button @click="loadUserPermissions" variant="primary">
Retry
</rs-button>
</div>
</div>
<!-- Main Content -->
<div v-else class="h-full flex flex-col">
<!-- Header with user information -->
<div class="bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
<div class="px-6 py-4 flex items-center justify-between">
<div class="flex items-center">
<div class="w-10 h-10 rounded-full bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center mr-3">
<span v-html="getSvgIcon('user')" class="text-blue-600 dark:text-blue-400"></span>
</div>
<div>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ dmsStore.currentUser.name }}</h2>
<p class="text-sm text-gray-600 dark:text-gray-400">{{ dmsStore.currentUser.department }}</p>
</div>
</div>
<div class="flex items-center space-x-2">
<span
v-if="userPermissions.canApprove"
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300"
>
Approver
</span>
<span
v-if="userPermissions.canViewAll"
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300"
>
Administrator
</span>
</div>
</div>
</div>
<!-- Tabs Navigation -->
<div class="border-b border-gray-200 dark:border-gray-700">
<div class="px-6">
<nav class="-mb-px flex space-x-6" aria-label="Tabs">
<button
v-for="tab in tabs"
:key="tab.id"
@click="changeTab(tab.id)"
class="py-4 px-1 border-b-2 font-medium text-sm transition-colors"
:class="[
activeTab === tab.id
? 'border-blue-500 text-blue-600 dark:border-blue-400 dark:text-blue-400'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-200 dark:hover:border-gray-600'
]"
>
<div class="flex items-center">
<span v-html="getSvgIcon(tab.icon)" class="mr-2"></span>
<span>{{ tab.label }}</span>
</div>
</button>
</nav>
</div>
</div>
<!-- Tab Description -->
<div class="bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 px-6 py-2">
<p class="text-sm text-gray-600 dark:text-gray-400">
{{ tabs.find(t => t.id === activeTab)?.description }}
</p>
</div>
<!-- Tab Content -->
<div class="flex-1 p-6 overflow-auto">
<!-- Access Requests Tab -->
<div v-if="activeTab === 'requests'" class="h-full">
<DMSApprovalQueue
:showClosed="userPermissions.canViewAll"
:filterByUser="userPermissions.canViewAll ? null : dmsStore.currentUser.id"
:maxItems="0"
/>
</div>
<!-- Metrics Tab -->
<div v-else-if="activeTab === 'metrics'" class="h-full">
<DMSAccessRequestTracker
:userId="userPermissions.canViewAll ? null : dmsStore.currentUser.id"
:timeRange="'30days'"
:showPersonal="true"
:showDepartmental="userPermissions.canViewAll"
/>
</div>
</div>
</div>
</div>
</template>
</rs-card>
</div>
</template>
<style scoped>
.dms-access-management {
height: calc(100vh - 64px);
}
</style>