generated from corrad-software/corrad-af-2024
444 lines
20 KiB
Vue
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> |