EDMS/pages/dms/upload-document.vue
2025-05-30 16:16:59 +08:00

444 lines
20 KiB
Vue

<script setup>
import { ref, reactive } from 'vue';
import { useDmsStore } from '~/stores/dms';
import DMSNavigation from '~/components/dms/navigation/DMSNavigation.vue';
// Define page metadata
definePageMeta({
title: "Upload Document",
middleware: ["auth"],
requiresAuth: true,
breadcrumb: [
{
name: "Dashboard",
path: "/dashboard",
},
{
name: "DMS",
path: "/dms",
},
{
name: "Upload Document",
path: "/dms/upload-document",
},
],
});
// Set up store
const dmsStore = useDmsStore();
// Local state
const isUploading = ref(false);
const selectedFiles = ref([]);
const uploadProgress = ref(0);
const currentLocation = ref('/JKR Cawangan Kota Bharu, Kelantan');
// Document metadata form
const documentForm = reactive({
title: '',
description: '',
keywords: '',
category: 'Technical Specification',
status: 'Draft',
retention: '7 years',
department: '',
accessLevel: 'private'
});
// Available categories
const categories = [
'Technical Specification',
'Project Proposal',
'Contract',
'Invoice',
'Report',
'Memo',
'Letter',
'Other'
];
// Document statuses
const statuses = [
'Draft',
'Under Review',
'Approved',
'Rejected',
'Archived'
];
// Retention periods
const retentionPeriods = [
'1 year',
'3 years',
'5 years',
'7 years',
'10 years',
'Permanent'
];
// Access levels
const accessLevels = [
{ value: 'private', label: 'Private (Only me)' },
{ value: 'department', label: 'Department' },
{ value: 'organization', label: 'Organization' },
{ value: 'public', label: 'Public' }
];
// Handle file selection
const handleFileSelect = (event) => {
const files = event.target.files;
selectedFiles.value = Array.from(files);
// Auto-populate title based on filename (without extension)
if (files.length === 1) {
const fileName = files[0].name;
const nameWithoutExtension = fileName.split('.').slice(0, -1).join('.');
documentForm.title = nameWithoutExtension.replace(/_/g, ' ');
}
};
// Clear selected files
const clearFiles = () => {
selectedFiles.value = [];
// Reset form
Object.keys(documentForm).forEach(key => {
documentForm[key] = key === 'category' ? 'Technical Specification' :
key === 'status' ? 'Draft' :
key === 'retention' ? '7 years' :
key === 'accessLevel' ? 'private' : '';
});
};
// Simulate upload process
const uploadFiles = async () => {
if (selectedFiles.value.length === 0) return;
isUploading.value = true;
uploadProgress.value = 0;
// Simulate upload progress
const interval = setInterval(() => {
uploadProgress.value += 10;
if (uploadProgress.value >= 100) {
clearInterval(interval);
setTimeout(() => {
isUploading.value = false;
// Show success message
alert('Files uploaded successfully!');
// Clear form and selected files
clearFiles();
}, 500);
}
}, 300);
};
// Create dummy folder structure for folder picker
const folderStructure = [
{
id: 'root',
name: 'JKR Cawangan Kota Bharu, Kelantan',
path: '/JKR Cawangan Kota Bharu, Kelantan',
children: [
{
id: 'tech',
name: 'Technical Documents',
path: '/JKR Cawangan Kota Bharu, Kelantan/Technical Documents',
children: []
},
{
id: 'projects',
name: 'Projects',
path: '/JKR Cawangan Kota Bharu, Kelantan/Projects',
children: [
{
id: 'proj1',
name: 'Project MRT3',
path: '/JKR Cawangan Kota Bharu, Kelantan/Projects/Project MRT3',
children: []
},
{
id: 'proj2',
name: 'Empangan Nenggiri',
path: '/JKR Cawangan Kota Bharu, Kelantan/Projects/Empangan Nenggiri',
children: []
}
]
},
{
id: 'admin',
name: 'Administrative',
path: '/JKR Cawangan Kota Bharu, Kelantan/Administrative',
children: []
}
]
}
];
// Change destination folder
const selectFolder = (path) => {
currentLocation.value = path;
};
</script>
<template>
<div class="dms-page">
<LayoutsBreadcrumb />
<rs-card class="h-full">
<template #header>
<div class="flex flex-wrap items-center justify-between gap-4">
<h1 class="text-xl font-bold text-primary">Upload Document</h1>
</div>
</template>
<template #body>
<div class="explorer-layout h-full flex overflow-hidden">
<!-- Left sidebar navigation -->
<DMSNavigation />
<!-- Main content area -->
<div class="flex-1 overflow-y-auto p-6">
<div class="max-w-4xl mx-auto">
<!-- File upload section -->
<div class="bg-white dark:bg-gray-800 shadow-sm rounded-lg overflow-hidden mb-6">
<div class="bg-gray-50 dark:bg-gray-700 px-6 py-4 border-b border-gray-200 dark:border-gray-600">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100 flex items-center">
<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" class="mr-2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>
Select Files
</h2>
</div>
<div class="px-6 py-4">
<div v-if="selectedFiles.length === 0" class="border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg p-12 text-center">
<input
type="file"
id="file-upload"
multiple
class="hidden"
@change="handleFileSelect"
/>
<label
for="file-upload"
class="cursor-pointer flex flex-col items-center"
>
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="mb-4 text-gray-400"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>
<span class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-1">Drag and drop files here</span>
<span class="text-sm text-gray-500 dark:text-gray-400 mb-4">or click to browse</span>
<rs-button color="primary" size="sm">Select Files</rs-button>
</label>
</div>
<div v-else>
<div class="mb-4 flex justify-between items-center">
<h3 class="font-medium">Selected Files ({{ selectedFiles.length }})</h3>
<rs-button size="sm" color="secondary" @click="clearFiles">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-1"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
Clear
</rs-button>
</div>
<div class="border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden">
<ul class="divide-y divide-gray-200 dark:divide-gray-700">
<li v-for="(file, index) in selectedFiles" :key="index" class="p-4 flex items-center">
<div class="mr-3 text-gray-400">
<svg v-if="file.type.includes('pdf')" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
<polyline points="14 2 14 8 20 8"></polyline>
<path d="M9 15h6"></path>
<path d="M9 11h6"></path>
</svg>
<svg v-else-if="file.type.includes('spreadsheet') || file.type.includes('excel')" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
<polyline points="14 2 14 8 20 8"></polyline>
<rect x="8" y="12" width="8" height="6"></rect>
<line x1="8" y1="16" x2="16" y2="16"></line>
<line x1="11" y1="12" x2="11" y2="18"></line>
</svg>
<svg v-else-if="file.type.includes('word') || file.type.includes('document')" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
<polyline points="14 2 14 8 20 8"></polyline>
<line x1="16" y1="13" x2="8" y2="13"></line>
<line x1="16" y1="17" x2="8" y2="17"></line>
<line x1="10" y1="9" x2="8" y2="9"></line>
</svg>
<svg v-else xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
<polyline points="14 2 14 8 20 8"></polyline>
</svg>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-gray-900 dark:text-gray-100 truncate">
{{ file.name }}
</p>
<p class="text-xs text-gray-500 dark:text-gray-400">
{{ Math.round(file.size / 1024) }} KB
</p>
</div>
<button
@click="selectedFiles.splice(index, 1)"
class="p-1 text-gray-400 hover:text-red-500"
>
<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="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
</button>
</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Upload destination -->
<div class="bg-white dark:bg-gray-800 shadow-sm rounded-lg overflow-hidden mb-6">
<div class="bg-gray-50 dark:bg-gray-700 px-6 py-4 border-b border-gray-200 dark:border-gray-600">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100 flex items-center">
<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" class="mr-2"><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>
Upload Destination
</h2>
</div>
<div class="px-6 py-4">
<div class="flex items-center p-2 border border-gray-200 dark:border-gray-700 rounded">
<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" class="mr-2 text-gray-400"><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>
<span>{{ currentLocation }}</span>
<rs-button size="sm" color="secondary" class="ml-auto">
Change
</rs-button>
</div>
</div>
</div>
<!-- Document metadata -->
<div class="bg-white dark:bg-gray-800 shadow-sm rounded-lg overflow-hidden mb-6">
<div class="bg-gray-50 dark:bg-gray-700 px-6 py-4 border-b border-gray-200 dark:border-gray-600">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100 flex items-center">
<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" class="mr-2"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>
Document Metadata
</h2>
</div>
<div class="px-6 py-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Title</label>
<input
v-model="documentForm.title"
type="text"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-primary focus:ring focus:ring-primary focus:ring-opacity-50"
/>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Description</label>
<textarea
v-model="documentForm.description"
rows="3"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-primary focus:ring focus:ring-primary focus:ring-opacity-50"
></textarea>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Keywords (comma separated)</label>
<input
v-model="documentForm.keywords"
type="text"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-primary focus:ring focus:ring-primary focus:ring-opacity-50"
/>
</div>
</div>
<div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Category</label>
<select
v-model="documentForm.category"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-primary focus:ring focus:ring-primary focus:ring-opacity-50"
>
<option v-for="category in categories" :key="category" :value="category">
{{ category }}
</option>
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Status</label>
<select
v-model="documentForm.status"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-primary focus:ring focus:ring-primary focus:ring-opacity-50"
>
<option v-for="status in statuses" :key="status" :value="status">
{{ status }}
</option>
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Retention Period</label>
<select
v-model="documentForm.retention"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-primary focus:ring focus:ring-primary focus:ring-opacity-50"
>
<option v-for="period in retentionPeriods" :key="period" :value="period">
{{ period }}
</option>
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Department</label>
<input
v-model="documentForm.department"
type="text"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-primary focus:ring focus:ring-primary focus:ring-opacity-50"
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Access Level</label>
<select
v-model="documentForm.accessLevel"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-primary focus:ring focus:ring-primary focus:ring-opacity-50"
>
<option v-for="level in accessLevels" :key="level.value" :value="level.value">
{{ level.label }}
</option>
</select>
</div>
</div>
</div>
</div>
</div>
<!-- Upload progress -->
<div v-if="isUploading" class="bg-white dark:bg-gray-800 shadow-sm rounded-lg overflow-hidden mb-6">
<div class="px-6 py-4">
<h3 class="font-medium mb-2">Uploading Files...</h3>
<div class="w-full bg-gray-200 rounded-full h-2.5 mb-2">
<div
class="bg-primary h-2.5 rounded-full"
:style="{ width: `${uploadProgress}%` }"
></div>
</div>
<p class="text-sm text-gray-500">{{ uploadProgress }}% complete</p>
</div>
</div>
<!-- Actions -->
<div class="flex justify-end gap-3">
<rs-button color="secondary" @click="clearFiles">Cancel</rs-button>
<rs-button
color="primary"
:disabled="selectedFiles.length === 0 || isUploading"
@click="uploadFiles"
>
<svg v-if="!isUploading" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>
Upload {{ selectedFiles.length }} {{ selectedFiles.length === 1 ? 'File' : 'Files' }}
</rs-button>
</div>
</div>
</div>
</div>
</template>
</rs-card>
</div>
</template>
<style scoped>
.dms-page {
height: calc(100vh - 64px - 48px - 32px); /* Adjust based on your layout */
}
.explorer-layout {
height: calc(100vh - 200px); /* Adjust based on your layout */
}
</style>