EDMS/components/dms/dialogs/DMSAccessRequestDialog.vue
2025-05-31 14:58:52 +08:00

292 lines
10 KiB
Vue

<script setup>
import { ref, computed, watch } from 'vue';
import { useDmsStore } from '~/stores/dms';
const props = defineProps({
item: {
type: Object,
required: true
},
visible: {
type: Boolean,
default: false
}
});
const emit = defineEmits(['close', 'submit']);
// Store
const dmsStore = useDmsStore();
// Form state
const accessType = ref('view');
const justification = ref('');
const accessDuration = ref('7 days');
const isSubmitting = ref(false);
const formError = ref('');
// Durations
const durationOptions = [
{ value: '1 day', label: '1 Day' },
{ value: '7 days', label: '7 Days' },
{ value: '14 days', label: '14 Days' },
{ value: '30 days', label: '30 Days' },
{ value: '90 days', label: '90 Days' }
];
// Access type options
const accessTypeOptions = [
{
value: 'view',
label: 'View Only',
description: 'View documents without the ability to download or edit',
icon: `<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"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>`
},
{
value: 'download',
label: 'Download',
description: 'View and download documents without editing',
icon: `<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"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>`
},
{
value: 'full',
label: 'Full Access',
description: 'View, download, and edit documents',
icon: `<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"><circle cx="12" cy="12" r="10"></circle><circle cx="12" cy="12" r="3"></circle></svg>`
}
];
// Computed properties
const itemName = computed(() => {
return props.item?.name || 'Document';
});
const itemPath = computed(() => {
if (!props.item) return '';
if (props.item.path) return props.item.path;
return props.item.parentPath ? `${props.item.parentPath}/${props.item.name}` : `/${props.item.name}`;
});
const itemType = computed(() => {
if (!props.item) return 'document';
if (props.item.type === 'cabinet' || props.item.type === 'cabinet-group') {
return 'cabinet';
} else if (props.item.type === 'drawer') {
return 'drawer';
} else if (props.item.type === 'folder' || props.item.type === 'subfolder') {
return 'folder';
} else {
return 'document';
}
});
const isValidForm = computed(() => {
return accessType.value && justification.value.length >= 10 && accessDuration.value;
});
// Methods
const resetForm = () => {
accessType.value = 'view';
justification.value = '';
accessDuration.value = '7 days';
formError.value = '';
};
const handleClose = () => {
emit('close');
// Reset form after closing
setTimeout(() => {
resetForm();
}, 300);
};
const handleSubmit = () => {
if (!isValidForm.value) {
formError.value = 'Please complete all required fields';
return;
}
isSubmitting.value = true;
try {
emit('submit', {
accessType: accessType.value,
justification: justification.value,
accessDuration: accessDuration.value
});
} catch (error) {
console.error('Submit error:', error);
formError.value = error.message || 'Failed to submit request';
} finally {
isSubmitting.value = false;
}
};
// Watch for visibility changes
watch(() => props.visible, (newValue) => {
if (newValue) {
resetForm();
}
});
</script>
<template>
<rs-modal
v-model="props.visible"
size="md"
@close="handleClose"
>
<template #header>
<div class="flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2 text-blue-600" viewBox="0 0 20 20" fill="currentColor">
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z" />
<path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd" />
</svg>
<span>Request Access</span>
</div>
</template>
<template #body>
<div class="p-2">
<!-- Item information -->
<div class="mb-6 bg-gray-50 dark:bg-gray-800 p-4 rounded-lg">
<h3 class="text-gray-700 dark:text-gray-300 text-sm font-medium mb-2">Requesting access to:</h3>
<div class="flex items-start">
<div class="mt-1 mr-3">
<span v-if="itemType === 'cabinet'" class="text-blue-600 dark:text-blue-400">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path d="M2 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" />
</svg>
</span>
<span v-else-if="itemType === 'drawer'" class="text-blue-600 dark:text-blue-400">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path d="M2 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" />
</svg>
</span>
<span v-else-if="itemType === 'folder'" class="text-blue-600 dark:text-blue-400">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path d="M2 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" />
</svg>
</span>
<span v-else class="text-gray-600 dark:text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4z" clip-rule="evenodd" />
</svg>
</span>
</div>
<div>
<h4 class="text-gray-900 dark:text-gray-100 font-medium">{{ itemName }}</h4>
<p class="text-gray-500 dark:text-gray-400 text-xs mt-1">{{ itemPath }}</p>
</div>
</div>
</div>
<!-- Form error -->
<div v-if="formError" class="mb-4 p-3 bg-red-50 dark:bg-red-900/20 border-l-4 border-red-500 text-red-700 dark:text-red-400 text-sm">
{{ formError }}
</div>
<!-- Access type -->
<div class="mb-6">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Access Type
</label>
<div class="space-y-3">
<div
v-for="option in accessTypeOptions"
:key="option.value"
class="relative flex items-start"
>
<div class="flex items-center h-5">
<input
:id="`access-type-${option.value}`"
type="radio"
v-model="accessType"
:value="option.value"
class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300 dark:border-gray-600 dark:bg-gray-800"
/>
</div>
<div class="ml-3 flex items-center">
<span v-html="option.icon" class="mr-2 text-gray-600 dark:text-gray-400"></span>
<div>
<label
:for="`access-type-${option.value}`"
class="font-medium text-gray-700 dark:text-gray-300 cursor-pointer"
>
{{ option.label }}
</label>
<p class="text-gray-500 dark:text-gray-400 text-xs">{{ option.description }}</p>
</div>
</div>
</div>
</div>
</div>
<!-- Justification -->
<div class="mb-6">
<label for="justification" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Justification
<span class="text-red-500">*</span>
</label>
<textarea
id="justification"
v-model="justification"
rows="3"
class="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 dark:border-gray-600 dark:bg-gray-800 rounded-md"
placeholder="Please explain why you need access to this resource..."
></textarea>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
Minimum 10 characters. Please provide a detailed explanation.
</p>
</div>
<!-- Access Duration -->
<div class="mb-6">
<label for="duration" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Access Duration
</label>
<select
id="duration"
v-model="accessDuration"
class="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 dark:border-gray-600 dark:bg-gray-800 rounded-md"
>
<option v-for="option in durationOptions" :key="option.value" :value="option.value">
{{ option.label }}
</option>
</select>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
How long do you need access to this resource?
</p>
</div>
</div>
</template>
<template #footer>
<div class="flex justify-between items-center">
<div>
<p class="text-xs text-gray-500 dark:text-gray-400">
* Required fields
</p>
</div>
<div class="flex space-x-2">
<rs-button
variant="light"
@click="handleClose"
:disabled="isSubmitting"
>
Cancel
</rs-button>
<rs-button
variant="primary"
@click="handleSubmit"
:disabled="!isValidForm || isSubmitting"
:loading="isSubmitting"
>
Submit Request
</rs-button>
</div>
</div>
</template>
</rs-modal>
</template>