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