generated from corrad-software/corrad-af-2024
Added File Preview, Hiearchy View, Access Type Display
This commit is contained in:
parent
9b14213beb
commit
1aac9905e3
@ -3,7 +3,7 @@ import { ref, computed } from 'vue';
|
||||
import { useDmsStore } from '~/stores/dms';
|
||||
|
||||
const props = defineProps({
|
||||
document: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
@ -13,63 +13,63 @@ const props = defineProps({
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['close', 'requestSubmitted']);
|
||||
const emit = defineEmits(['close', 'submit']);
|
||||
|
||||
// Store
|
||||
const dmsStore = useDmsStore();
|
||||
|
||||
// Form state
|
||||
const selectedAccessLevel = ref('view');
|
||||
const selectedAccessType = ref('view');
|
||||
const accessDuration = ref('7 days');
|
||||
const justification = ref('');
|
||||
const isSubmitting = ref(false);
|
||||
const formError = ref('');
|
||||
|
||||
// Access level options
|
||||
const accessLevels = [
|
||||
// Access type options
|
||||
const accessTypes = [
|
||||
{ id: 'view', label: 'View Only', description: 'Can only view the document' },
|
||||
{ id: 'download', label: 'Download', description: 'Can view and download the document' },
|
||||
{ id: 'print', label: 'Print', description: 'Can view, download, and print the document' },
|
||||
{ id: 'edit', label: 'Edit', description: 'Can view, download, print, and edit the document' },
|
||||
{ id: 'full', label: 'Full Access', description: 'Has complete control over the document' }
|
||||
{ id: 'download', label: 'Download', description: 'Can view and download' },
|
||||
{ id: 'print', label: 'Print', description: 'Can view and print' },
|
||||
{ id: 'full', label: 'Full Access', description: 'View, download and print' }
|
||||
];
|
||||
|
||||
// Access duration options
|
||||
const durationOptions = [
|
||||
'7 days',
|
||||
'14 days',
|
||||
'30 days',
|
||||
'60 days',
|
||||
'90 days',
|
||||
'Permanent'
|
||||
];
|
||||
|
||||
// Computed properties
|
||||
const documentTitle = computed(() => {
|
||||
return props.document?.name || 'Document';
|
||||
const itemTitle = computed(() => {
|
||||
return props.item?.name || 'Document';
|
||||
});
|
||||
|
||||
const documentIconSvg = computed(() => {
|
||||
const extension = props.document?.extension?.toLowerCase();
|
||||
|
||||
if (extension === 'pdf')
|
||||
return '<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" 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>';
|
||||
|
||||
if (['doc', 'docx'].includes(extension))
|
||||
return '<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" 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><polyline points="10 9 9 9 8 9"></polyline></svg>';
|
||||
|
||||
if (['xls', 'xlsx'].includes(extension))
|
||||
return '<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" 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>';
|
||||
|
||||
if (['ppt', 'pptx'].includes(extension))
|
||||
return '<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" 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></svg>';
|
||||
|
||||
return '<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" 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>';
|
||||
});
|
||||
|
||||
const currentAccessLevel = computed(() => {
|
||||
// This would be determined from actual permissions in production
|
||||
return 'None';
|
||||
const itemFileName = computed(() => {
|
||||
if (props.item?.type === 'file') {
|
||||
return props.item.name;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
// Methods
|
||||
const closeDialog = () => {
|
||||
// Reset form
|
||||
selectedAccessType.value = 'view';
|
||||
accessDuration.value = '7 days';
|
||||
justification.value = '';
|
||||
formError.value = '';
|
||||
|
||||
emit('close');
|
||||
};
|
||||
|
||||
const submitRequest = async () => {
|
||||
// Validate form
|
||||
if (!selectedAccessLevel.value) {
|
||||
formError.value = 'Please select an access level';
|
||||
if (!selectedAccessType.value) {
|
||||
formError.value = 'Please select an access type';
|
||||
return;
|
||||
}
|
||||
|
||||
@ -83,12 +83,17 @@ const submitRequest = async () => {
|
||||
|
||||
try {
|
||||
// Submit the request to the store
|
||||
const request = await dmsStore.requestAccess(props.document.id, selectedAccessLevel.value);
|
||||
const request = await dmsStore.requestAccess(
|
||||
props.item.id,
|
||||
selectedAccessType.value,
|
||||
justification.value,
|
||||
accessDuration.value
|
||||
);
|
||||
|
||||
// Emit success event
|
||||
emit('requestSubmitted', request);
|
||||
emit('submit', request);
|
||||
|
||||
// Close the dialog
|
||||
// Close the dialog (this will also reset the form)
|
||||
closeDialog();
|
||||
} catch (error) {
|
||||
formError.value = 'Failed to submit access request. Please try again.';
|
||||
@ -107,79 +112,121 @@ const submitRequest = async () => {
|
||||
size="md"
|
||||
>
|
||||
<template #body>
|
||||
<div class="p-4">
|
||||
<!-- Document info -->
|
||||
<div class="flex items-center mb-6 bg-gray-50 dark:bg-gray-800 p-4 rounded-lg">
|
||||
<span class="text-primary mr-4" v-html="documentIconSvg"></span>
|
||||
<div>
|
||||
<h3 class="font-medium text-lg">{{ documentTitle }}</h3>
|
||||
<p class="text-sm text-gray-500">
|
||||
Current Access Level: <span class="font-medium">{{ currentAccessLevel }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form error -->
|
||||
<div v-if="formError" class="mb-4 p-3 bg-red-100 text-red-800 rounded-md">
|
||||
{{ formError }}
|
||||
</div>
|
||||
|
||||
<!-- Access level selection -->
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium mb-2">Request Access Level:</label>
|
||||
<div class="space-y-2">
|
||||
<div
|
||||
v-for="level in accessLevels"
|
||||
:key="level.id"
|
||||
class="flex items-start p-3 border rounded-md cursor-pointer"
|
||||
:class="selectedAccessLevel === level.id
|
||||
? 'border-primary bg-primary/10'
|
||||
: 'border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800'"
|
||||
@click="selectedAccessLevel = level.id"
|
||||
>
|
||||
<div class="flex-shrink-0 mt-0.5">
|
||||
<div class="w-4 h-4 rounded-full border-2 flex items-center justify-center"
|
||||
:class="selectedAccessLevel === level.id
|
||||
? 'border-primary'
|
||||
: 'border-gray-400'"
|
||||
>
|
||||
<div
|
||||
v-if="selectedAccessLevel === level.id"
|
||||
class="w-2 h-2 rounded-full bg-primary"
|
||||
></div>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<!-- Document Information Section -->
|
||||
<div class="mb-6">
|
||||
<div class="bg-blue-50 dark:bg-blue-900/10 rounded-lg p-4 border border-blue-200 dark:border-blue-800">
|
||||
<h3 class="text-sm font-medium text-blue-900 dark:text-blue-100 mb-2">Document Information</h3>
|
||||
<div class="space-y-1">
|
||||
<div class="flex">
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-300 w-12">Title:</span>
|
||||
<span class="text-sm text-gray-900 dark:text-gray-100">{{ itemTitle }}</span>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<div class="font-medium">{{ level.label }}</div>
|
||||
<p class="text-sm text-gray-500">{{ level.description }}</p>
|
||||
<div class="flex">
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-300 w-12">File:</span>
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400">{{ itemFileName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Justification -->
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium mb-2">Justification:</label>
|
||||
<!-- Form error -->
|
||||
<div v-if="formError" class="mb-4 p-3 bg-red-50 border border-red-200 text-red-800 rounded-md text-sm">
|
||||
{{ formError }}
|
||||
</div>
|
||||
|
||||
<!-- Access Type Section -->
|
||||
<div class="mb-6">
|
||||
<h3 class="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3">Access Type</h3>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div
|
||||
v-for="accessType in accessTypes"
|
||||
:key="accessType.id"
|
||||
class="relative"
|
||||
>
|
||||
<label
|
||||
:for="accessType.id"
|
||||
class="flex items-start p-3 border rounded-lg cursor-pointer transition-colors"
|
||||
:class="selectedAccessType === accessType.id
|
||||
? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20'
|
||||
: 'border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800'"
|
||||
>
|
||||
<div class="flex items-center h-5">
|
||||
<input
|
||||
:id="accessType.id"
|
||||
v-model="selectedAccessType"
|
||||
:value="accessType.id"
|
||||
type="radio"
|
||||
class="w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ accessType.label }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
{{ accessType.description }}
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Access Duration Section -->
|
||||
<div class="mb-6">
|
||||
<label class="block text-sm font-medium text-gray-900 dark:text-gray-100 mb-2">Access Duration</label>
|
||||
<select
|
||||
v-model="accessDuration"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option v-for="duration in durationOptions" :key="duration" :value="duration">
|
||||
{{ duration }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Justification Section -->
|
||||
<div class="mb-6">
|
||||
<label class="block text-sm font-medium text-gray-900 dark:text-gray-100 mb-2">
|
||||
Justification
|
||||
<span class="text-red-500">*</span>
|
||||
</label>
|
||||
<textarea
|
||||
v-model="justification"
|
||||
rows="3"
|
||||
rows="4"
|
||||
placeholder="Please explain why you need access to this document..."
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-700 rounded-md bg-white dark:bg-gray-800"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none"
|
||||
></textarea>
|
||||
<p class="text-xs text-gray-500 mt-1">Your request will be reviewed by the document owner or administrator.</p>
|
||||
</div>
|
||||
|
||||
<!-- Footer Note -->
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mb-6">
|
||||
Your request will be reviewed by the document owner or administrator.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="flex justify-end gap-3">
|
||||
<rs-button color="secondary" @click="closeDialog" :disabled="isSubmitting">
|
||||
<div class="flex justify-end gap-3 px-6 py-4 bg-gray-50 dark:bg-gray-800">
|
||||
<button
|
||||
@click="closeDialog"
|
||||
:disabled="isSubmitting"
|
||||
class="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-600 focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</rs-button>
|
||||
<rs-button color="primary" @click="submitRequest" :disabled="isSubmitting">
|
||||
<svg v-if="isSubmitting" 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="animate-spin mr-2"><path d="M12 22C6.5 22 2 17.5 2 12S6.5 2 12 2s10 4.5 10 10"></path><path d="M12 2v4"></path><path d="M12 18v4"></path></svg>
|
||||
<span>Submit Request</span>
|
||||
</rs-button>
|
||||
</button>
|
||||
<button
|
||||
@click="submitRequest"
|
||||
:disabled="isSubmitting"
|
||||
class="px-4 py-2 text-sm font-medium text-white bg-gray-900 dark:bg-gray-800 border border-transparent rounded-lg hover:bg-gray-800 dark:hover:bg-gray-700 focus:ring-2 focus:ring-gray-900 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center"
|
||||
>
|
||||
<svg v-if="isSubmitting" class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Submit Request
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</rs-modal>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -194,6 +194,7 @@ onMounted(() => {
|
||||
<DMSExplorer
|
||||
:initial-path="'/'"
|
||||
:view-mode="'list'"
|
||||
:active-document-tab="activeTab"
|
||||
@item-selected="handleItemSelected"
|
||||
@view-mode-changed="handleViewModeChanged"
|
||||
@path-changed="handlePathChanged"
|
||||
|
@ -735,6 +735,57 @@ export const useDmsStore = defineStore('dms', {
|
||||
clearSearch() {
|
||||
this.searchQuery = '';
|
||||
this.searchResults = [];
|
||||
},
|
||||
|
||||
// Access request functionality
|
||||
async requestAccess(itemId, accessLevel, justification, duration = '7 days') {
|
||||
this.isLoading = true;
|
||||
|
||||
try {
|
||||
// Mock API delay
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
// Generate a unique request ID
|
||||
const requestId = `req${Date.now()}`;
|
||||
|
||||
// Create new access request
|
||||
const newRequest = {
|
||||
id: requestId,
|
||||
userId: 'current-user-id', // Would come from auth store
|
||||
userName: 'Current User', // Would come from auth store
|
||||
itemId: itemId,
|
||||
accessLevel: accessLevel,
|
||||
justification: justification,
|
||||
duration: duration,
|
||||
requestDate: new Date().toISOString().split('T')[0],
|
||||
status: 'pending'
|
||||
};
|
||||
|
||||
// Add to access requests
|
||||
this.accessRequests.push(newRequest);
|
||||
|
||||
// Update the item's access request status (for mock data)
|
||||
// In production, this would be handled server-side
|
||||
const updateItemStatus = (items, id) => {
|
||||
for (const item of items) {
|
||||
if (item.id === id) {
|
||||
item.accessRequestStatus = 'pending';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Try to find and update the item in the mock data arrays
|
||||
// This is a simplified approach for demo purposes
|
||||
|
||||
return newRequest;
|
||||
} catch (error) {
|
||||
console.error('Failed to submit access request:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user