generated from corrad-software/corrad-af-2024
229 lines
6.6 KiB
Vue
229 lines
6.6 KiB
Vue
<script setup>
|
|
import { ref, computed, onMounted } from 'vue';
|
|
import { useDmsStore } from '~/stores/dms';
|
|
import DMSNavigation from '~/components/dms/navigation/DMSNavigation.vue';
|
|
import DMSExplorer from '~/components/dms/explorer/DMSExplorer.vue';
|
|
|
|
// Define page metadata
|
|
definePageMeta({
|
|
title: "Document Management System",
|
|
middleware: ["auth"],
|
|
requiresAuth: true,
|
|
breadcrumb: [
|
|
{
|
|
name: "Dashboard",
|
|
path: "/dashboard",
|
|
},
|
|
{
|
|
name: "DMS",
|
|
path: "/dms",
|
|
},
|
|
],
|
|
});
|
|
|
|
// Set up store
|
|
const dmsStore = useDmsStore();
|
|
|
|
// Local state
|
|
const showFileViewer = ref(false);
|
|
const currentDocument = ref(null);
|
|
const searchQuery = ref('');
|
|
const isSearching = ref(false);
|
|
const currentPath = ref('JKR Cawangan Kota Bharu, Kelantan');
|
|
const viewMode = ref('explorer'); // explorer, cabinets, list
|
|
const selectedItem = ref(null);
|
|
const activeTab = ref('all');
|
|
|
|
// File selection state
|
|
const selectedFiles = ref([]);
|
|
const isSelecting = ref(false);
|
|
|
|
// Toggle file selection
|
|
const toggleFileSelection = (file) => {
|
|
const index = selectedFiles.value.findIndex(f => f.id === file.id);
|
|
if (index === -1) {
|
|
selectedFiles.value.push(file);
|
|
} else {
|
|
selectedFiles.value.splice(index, 1);
|
|
}
|
|
};
|
|
|
|
// Clear selection
|
|
const clearSelection = () => {
|
|
selectedFiles.value = [];
|
|
isSelecting.value = false;
|
|
};
|
|
|
|
// Select all files
|
|
const selectAllFiles = () => {
|
|
selectedFiles.value = [...dmsStore.currentItems];
|
|
isSelecting.value = true;
|
|
};
|
|
|
|
// Check if a file is selected
|
|
const isFileSelected = (file) => {
|
|
return selectedFiles.value.some(f => f.id === file.id);
|
|
};
|
|
|
|
// Toggle view mode
|
|
const changeViewMode = (mode) => {
|
|
viewMode.value = mode;
|
|
};
|
|
|
|
// View a file
|
|
const viewFile = (file) => {
|
|
if (isSelecting.value) {
|
|
toggleFileSelection(file);
|
|
return;
|
|
}
|
|
|
|
currentDocument.value = file;
|
|
showFileViewer.value = true;
|
|
};
|
|
|
|
// Navigate to a location
|
|
const navigateTo = (path) => {
|
|
currentPath.value = path;
|
|
// In a real app, we would fetch the contents of this location
|
|
clearSelection();
|
|
};
|
|
|
|
// Search functionality
|
|
const handleSearch = async () => {
|
|
if (!searchQuery.value.trim()) return;
|
|
|
|
isSearching.value = true;
|
|
await dmsStore.searchDocuments(searchQuery.value);
|
|
isSearching.value = false;
|
|
};
|
|
|
|
// Clear search
|
|
const clearSearch = () => {
|
|
searchQuery.value = '';
|
|
dmsStore.clearSearch();
|
|
};
|
|
|
|
// Format file size
|
|
const formatFileSize = (size) => {
|
|
if (!size) return '0 B';
|
|
|
|
if (typeof size === 'string') {
|
|
// If already formatted (like "4MB"), return as is
|
|
if (size.endsWith('B')) return size;
|
|
|
|
// Try to parse the size if it's a number in string form
|
|
const parsed = parseFloat(size);
|
|
if (isNaN(parsed)) return size;
|
|
size = parsed;
|
|
}
|
|
|
|
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
let i = 0;
|
|
while (size >= 1024 && i < units.length - 1) {
|
|
size /= 1024;
|
|
i++;
|
|
}
|
|
return `${size.toFixed(2)} ${units[i]}`;
|
|
};
|
|
|
|
// Document category tabs
|
|
const documentTabs = [
|
|
{ id: 'all', label: 'All Documents', icon: 'folder' },
|
|
{ id: 'public', label: 'Public', icon: 'unlock' },
|
|
{ id: 'private', label: 'Private', icon: 'lock' },
|
|
{ id: 'personal', label: 'Personal', icon: 'user' }
|
|
];
|
|
|
|
// Handle events from explorer
|
|
const handleItemSelected = (item) => {
|
|
selectedItem.value = item;
|
|
};
|
|
|
|
const handleViewModeChanged = (mode) => {
|
|
console.log('View mode changed to:', mode);
|
|
};
|
|
|
|
const handlePathChanged = (path) => {
|
|
currentPath.value = path;
|
|
};
|
|
|
|
// Get SVG icon
|
|
const getSvgIcon = (iconType, size = 16) => {
|
|
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[iconType] || icons.folder;
|
|
};
|
|
|
|
// Lifecycle hooks
|
|
onMounted(() => {
|
|
// Any initialization logic
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div class="dms-page">
|
|
<LayoutsBreadcrumb />
|
|
|
|
<rs-card class="h-full">
|
|
<template #body>
|
|
<div class="dms-layout h-full flex flex-col">
|
|
<!-- Document Category Tabs -->
|
|
<div class="tabs-header border-b border-gray-200 dark:border-gray-700 p-4 bg-white dark:bg-gray-800">
|
|
<div class="flex items-center space-x-1">
|
|
<button
|
|
v-for="tab in documentTabs"
|
|
:key="tab.id"
|
|
@click="activeTab = tab.id"
|
|
class="flex items-center space-x-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors"
|
|
:class="activeTab === tab.id
|
|
? 'bg-blue-100 text-blue-700 dark:bg-blue-900/20 dark:text-blue-300'
|
|
: 'text-gray-600 hover:text-gray-900 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-gray-200 dark:hover:bg-gray-700'"
|
|
>
|
|
<span v-html="getSvgIcon(tab.icon, 16)"></span>
|
|
<span>{{ tab.label }}</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Explorer Component -->
|
|
<div class="explorer-container flex-1 overflow-hidden">
|
|
<DMSExplorer
|
|
:initial-path="'/'"
|
|
:view-mode="'list'"
|
|
@item-selected="handleItemSelected"
|
|
@view-mode-changed="handleViewModeChanged"
|
|
@path-changed="handlePathChanged"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.dms-page {
|
|
height: calc(100vh - 64px);
|
|
}
|
|
|
|
.dms-layout {
|
|
height: 100%;
|
|
}
|
|
|
|
.explorer-container {
|
|
min-height: 0;
|
|
}
|
|
|
|
/* Ensure smooth transitions */
|
|
.tabs-header button {
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.tabs-header button:hover {
|
|
transform: translateY(-1px);
|
|
}
|
|
</style> |