generated from corrad-software/corrad-af-2024
332 lines
12 KiB
Vue
332 lines
12 KiB
Vue
<script setup>
|
||
import { ref, onMounted, nextTick } from 'vue';
|
||
|
||
// Define page metadata
|
||
definePageMeta({
|
||
title: "Document Management System",
|
||
middleware: ["auth"],
|
||
requiresAuth: true,
|
||
breadcrumb: [
|
||
{
|
||
name: "DMS",
|
||
path: "/dms",
|
||
},
|
||
],
|
||
});
|
||
|
||
// Import DMS components dynamically to handle potential import errors
|
||
let DMSExplorer = null;
|
||
let useDmsStore = null;
|
||
|
||
// Basic state
|
||
const activeTab = ref('all');
|
||
const isLoading = ref(true);
|
||
const hasError = ref(false);
|
||
const errorMessage = ref('');
|
||
const componentsLoaded = ref(false);
|
||
|
||
// Tab definitions with icons
|
||
const tabs = [
|
||
{
|
||
id: 'all',
|
||
label: 'All Documents',
|
||
icon: 'folder',
|
||
color: 'blue'
|
||
},
|
||
{
|
||
id: 'public',
|
||
label: 'Public',
|
||
icon: 'unlock',
|
||
color: 'green'
|
||
},
|
||
{
|
||
id: 'private',
|
||
label: 'Private',
|
||
icon: 'lock',
|
||
color: 'red'
|
||
},
|
||
{
|
||
id: 'personal',
|
||
label: 'Personal',
|
||
icon: 'user',
|
||
color: 'purple'
|
||
}
|
||
];
|
||
|
||
// Change active tab
|
||
const changeTab = (tabId) => {
|
||
activeTab.value = tabId;
|
||
};
|
||
|
||
// Get SVG icon function
|
||
const getSvgIcon = (iconName, size = 20) => {
|
||
const icons = {
|
||
folder: `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>`,
|
||
unlock: `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" 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 9.9-1"></path></svg>`,
|
||
lock: `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" 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>`,
|
||
user: `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" 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] || icons.folder;
|
||
};
|
||
|
||
// Load components dynamically
|
||
const loadComponents = async () => {
|
||
try {
|
||
isLoading.value = true;
|
||
hasError.value = false;
|
||
|
||
// Import components
|
||
const dmsStoreModule = await import('~/stores/dms');
|
||
const dmsExplorerModule = await import('~/components/dms/explorer/DMSExplorer.vue');
|
||
|
||
useDmsStore = dmsStoreModule.useDmsStore;
|
||
DMSExplorer = dmsExplorerModule.default;
|
||
|
||
componentsLoaded.value = true;
|
||
|
||
await nextTick();
|
||
|
||
} catch (error) {
|
||
console.error('Failed to load DMS components:', error);
|
||
hasError.value = true;
|
||
errorMessage.value = `Failed to load DMS components: ${error.message}`;
|
||
} finally {
|
||
isLoading.value = false;
|
||
}
|
||
};
|
||
|
||
// Event handlers (placeholder functions for when components load)
|
||
const handleItemSelected = (item) => {
|
||
console.log('Item selected:', item);
|
||
};
|
||
|
||
const handleViewModeChanged = (mode) => {
|
||
console.log('View mode changed to:', mode);
|
||
};
|
||
|
||
const handlePathChanged = (path) => {
|
||
console.log('Path changed to:', path);
|
||
};
|
||
|
||
// Lifecycle hooks
|
||
onMounted(() => {
|
||
loadComponents();
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<div class="dms-page">
|
||
<LayoutsBreadcrumb />
|
||
|
||
<rs-card class="h-full">
|
||
<template #body>
|
||
<div class="dms-layout 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 Document Management System...</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 DMS</h2>
|
||
<p class="text-gray-600 dark:text-gray-400 mb-4">{{ errorMessage }}</p>
|
||
<rs-button @click="loadComponents" variant="primary">
|
||
Retry
|
||
</rs-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Main Content -->
|
||
<div v-else class="dms-content h-full flex flex-col">
|
||
<!-- Enhanced Access Level Tabs -->
|
||
<div class="access-tabs bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
||
<div class="flex items-center px-6 py-4">
|
||
<div class="flex space-x-1">
|
||
<button
|
||
v-for="tab in tabs"
|
||
:key="tab.id"
|
||
@click="changeTab(tab.id)"
|
||
class="access-tab flex items-center space-x-2 px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200"
|
||
:class="[
|
||
activeTab === tab.id
|
||
? `bg-${tab.color}-100 text-${tab.color}-700 border border-${tab.color}-200 shadow-sm dark:bg-${tab.color}-900/20 dark:text-${tab.color}-300 dark:border-${tab.color}-800`
|
||
: 'text-gray-600 hover:text-gray-900 hover:bg-white dark:text-gray-400 dark:hover:text-gray-200 dark:hover:bg-gray-700 border border-transparent hover:border-gray-200 dark:hover:border-gray-600'
|
||
]"
|
||
>
|
||
<span
|
||
v-html="getSvgIcon(tab.icon)"
|
||
:class="[
|
||
activeTab === tab.id
|
||
? `text-${tab.color}-600 dark:text-${tab.color}-400`
|
||
: 'text-gray-500 dark:text-gray-500'
|
||
]"
|
||
></span>
|
||
<span>{{ tab.label }}</span>
|
||
<span
|
||
v-if="activeTab === tab.id"
|
||
:class="`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-${tab.color}-100 text-${tab.color}-800 dark:bg-${tab.color}-900/30 dark:text-${tab.color}-300`"
|
||
>
|
||
Active
|
||
</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Content Area -->
|
||
<div class="content-area flex-1">
|
||
<!-- Action Buttons -->
|
||
<div class="px-6 py-3 bg-white dark:bg-gray-900/10 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||
<div class="flex space-x-2">
|
||
<!-- Placeholder for future actions -->
|
||
</div>
|
||
|
||
<div class="flex space-x-2">
|
||
<!-- Access Management Button -->
|
||
<NuxtLink
|
||
to="/dms/access-management"
|
||
class="inline-flex items-center px-3 py-1.5 border border-gray-300 dark:border-gray-600 rounded-md text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
|
||
>
|
||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<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>
|
||
Access Management
|
||
</NuxtLink>
|
||
|
||
<!-- Role Management Button -->
|
||
<NuxtLink
|
||
to="/dms/role-management"
|
||
class="inline-flex items-center px-3 py-1.5 border border-gray-300 dark:border-gray-600 rounded-md text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
|
||
>
|
||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
|
||
<circle cx="9" cy="7" r="4"></circle>
|
||
<path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
|
||
<path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
|
||
</svg>
|
||
Role Management
|
||
</NuxtLink>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- DMS Explorer Component -->
|
||
<component
|
||
v-if="componentsLoaded && DMSExplorer"
|
||
:is="DMSExplorer"
|
||
:initial-path="'/'"
|
||
:view-mode="'list'"
|
||
:active-document-tab="activeTab"
|
||
@item-selected="handleItemSelected"
|
||
@view-mode-changed="handleViewModeChanged"
|
||
@path-changed="handlePathChanged"
|
||
/>
|
||
|
||
<!-- Fallback Content -->
|
||
<div v-else class="text-center py-12">
|
||
<h2 class="text-2xl font-semibold mb-4">{{ tabs.find(t => t.id === activeTab)?.label }}</h2>
|
||
<p class="text-gray-600 dark:text-gray-400">
|
||
Document explorer is loading...
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</rs-card>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.dms-page {
|
||
height: calc(100vh - 64px);
|
||
}
|
||
|
||
.dms-layout {
|
||
height: 100%;
|
||
}
|
||
|
||
.content-area {
|
||
min-height: 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.dms-content {
|
||
height: 100%;
|
||
}
|
||
|
||
.access-tabs {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.access-tab {
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.access-tab:hover {
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.access-tab.active {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
/* Dynamic color classes */
|
||
.bg-blue-100 { background-color: rgb(219 234 254); }
|
||
.text-blue-700 { color: rgb(29 78 216); }
|
||
.border-blue-200 { border-color: rgb(191 219 254); }
|
||
.text-blue-600 { color: rgb(37 99 235); }
|
||
.bg-blue-100 { background-color: rgb(219 234 254); }
|
||
.text-blue-800 { color: rgb(30 64 175); }
|
||
|
||
.bg-green-100 { background-color: rgb(220 252 231); }
|
||
.text-green-700 { color: rgb(21 128 61); }
|
||
.border-green-200 { border-color: rgb(187 247 208); }
|
||
.text-green-600 { color: rgb(22 163 74); }
|
||
.text-green-800 { color: rgb(22 101 52); }
|
||
|
||
.bg-red-100 { background-color: rgb(254 226 226); }
|
||
.text-red-700 { color: rgb(185 28 28); }
|
||
.border-red-200 { border-color: rgb(254 202 202); }
|
||
.text-red-600 { color: rgb(220 38 38); }
|
||
.text-red-800 { color: rgb(153 27 27); }
|
||
|
||
.bg-purple-100 { background-color: rgb(243 232 255); }
|
||
.text-purple-700 { color: rgb(126 34 206); }
|
||
.border-purple-200 { border-color: rgb(233 213 255); }
|
||
.text-purple-600 { color: rgb(147 51 234); }
|
||
.text-purple-800 { color: rgb(107 33 168); }
|
||
|
||
/* Dark mode colors */
|
||
.dark .bg-blue-900\/20 { background-color: rgba(30, 58, 138, 0.2); }
|
||
.dark .text-blue-300 { color: rgb(147 197 253); }
|
||
.dark .border-blue-800 { border-color: rgb(30 64 175); }
|
||
.dark .text-blue-400 { color: rgb(96 165 250); }
|
||
.dark .bg-blue-900\/30 { background-color: rgba(30, 58, 138, 0.3); }
|
||
|
||
.dark .bg-green-900\/20 { background-color: rgba(20, 83, 45, 0.2); }
|
||
.dark .text-green-300 { color: rgb(134 239 172); }
|
||
.dark .border-green-800 { border-color: rgb(22 101 52); }
|
||
.dark .text-green-400 { color: rgb(74 222 128); }
|
||
.dark .bg-green-900\/30 { background-color: rgba(20, 83, 45, 0.3); }
|
||
|
||
.dark .bg-red-900\/20 { background-color: rgba(127, 29, 29, 0.2); }
|
||
.dark .text-red-300 { color: rgb(252 165 165); }
|
||
.dark .border-red-800 { border-color: rgb(153 27 27); }
|
||
.dark .text-red-400 { color: rgb(248 113 113); }
|
||
.dark .bg-red-900\/30 { background-color: rgba(127, 29, 29, 0.3); }
|
||
|
||
.dark .bg-purple-900\/20 { background-color: rgba(88, 28, 135, 0.2); }
|
||
.dark .text-purple-300 { color: rgb(196 181 253); }
|
||
.dark .border-purple-800 { border-color: rgb(107 33 168); }
|
||
.dark .text-purple-400 { color: rgb(168 85 247); }
|
||
.dark .bg-purple-900\/30 { background-color: rgba(88, 28, 135, 0.3); }
|
||
</style> |