generated from corrad-software/corrad-af-2024
407 lines
12 KiB
Vue
407 lines
12 KiB
Vue
<template>
|
|
<div
|
|
v-if="visible"
|
|
class="fixed inset-0 z-50 bg-black bg-opacity-50 flex items-center justify-center"
|
|
@click="closeModal"
|
|
>
|
|
<div
|
|
class="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-lg w-full mx-4 max-h-[80vh] overflow-hidden"
|
|
@click.stop
|
|
>
|
|
<!-- Header -->
|
|
<div class="flex items-center justify-between p-6 border-b border-gray-200 dark:border-gray-700">
|
|
<h2 class="text-xl font-semibold text-gray-900 dark:text-gray-100">
|
|
Share Document
|
|
</h2>
|
|
<button
|
|
@click="closeModal"
|
|
class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
|
>
|
|
<Icon name="mdi:close" class="w-6 h-6" />
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Content -->
|
|
<div class="p-6 space-y-6">
|
|
<!-- Document Info -->
|
|
<div class="flex items-center space-x-3 p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
|
|
<Icon
|
|
:name="getFileTypeIcon(document.name)"
|
|
:class="['w-8 h-8', getFileTypeColor(document.name)]"
|
|
/>
|
|
<div class="flex-1 min-w-0">
|
|
<h3 class="text-sm font-medium text-gray-900 dark:text-gray-100 truncate">
|
|
{{ document.name }}
|
|
</h3>
|
|
<p class="text-xs text-gray-500 dark:text-gray-400">
|
|
{{ formatFileSize(document.size) }} • {{ formatDate(document.lastModified) }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Share Options -->
|
|
<div class="space-y-4">
|
|
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">Share Options</h3>
|
|
|
|
<!-- Share by Email -->
|
|
<div class="space-y-3">
|
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Email Recipients
|
|
</label>
|
|
<div class="flex space-x-2">
|
|
<input
|
|
v-model="newEmail"
|
|
@keydown.enter="addEmail"
|
|
type="email"
|
|
placeholder="Enter email address..."
|
|
class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm
|
|
bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100
|
|
focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
/>
|
|
<button
|
|
@click="addEmail"
|
|
class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 text-sm"
|
|
>
|
|
Add
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Email List -->
|
|
<div v-if="emailList.length > 0" class="space-y-2">
|
|
<div
|
|
v-for="(email, index) in emailList"
|
|
:key="index"
|
|
class="flex items-center justify-between p-2 bg-gray-100 dark:bg-gray-600 rounded-md"
|
|
>
|
|
<span class="text-sm text-gray-900 dark:text-gray-100">{{ email }}</span>
|
|
<button
|
|
@click="removeEmail(index)"
|
|
class="text-gray-500 hover:text-red-500"
|
|
>
|
|
<Icon name="mdi:close" class="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Access Level -->
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
Access Level
|
|
</label>
|
|
<select
|
|
v-model="accessLevel"
|
|
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm
|
|
bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100
|
|
focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
>
|
|
<option value="view">View Only</option>
|
|
<option value="comment">View & Comment</option>
|
|
<option value="edit">View & Edit</option>
|
|
<option value="full">Full Access</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Expiration -->
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
Share Expiration
|
|
</label>
|
|
<select
|
|
v-model="expiration"
|
|
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm
|
|
bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100
|
|
focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
>
|
|
<option value="never">Never</option>
|
|
<option value="1day">1 Day</option>
|
|
<option value="1week">1 Week</option>
|
|
<option value="1month">1 Month</option>
|
|
<option value="custom">Custom Date</option>
|
|
</select>
|
|
|
|
<!-- Custom Date Input -->
|
|
<input
|
|
v-if="expiration === 'custom'"
|
|
v-model="customDate"
|
|
type="datetime-local"
|
|
class="mt-2 w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm
|
|
bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100
|
|
focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Message -->
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
Message (Optional)
|
|
</label>
|
|
<textarea
|
|
v-model="shareMessage"
|
|
rows="3"
|
|
placeholder="Add a message for the recipients..."
|
|
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm
|
|
bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100
|
|
focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
></textarea>
|
|
</div>
|
|
|
|
<!-- Share Link -->
|
|
<div class="border-t border-gray-200 dark:border-gray-600 pt-4">
|
|
<h4 class="text-sm font-medium text-gray-900 dark:text-gray-100 mb-2">Share Link</h4>
|
|
<div class="flex items-center space-x-2">
|
|
<input
|
|
:value="shareLink"
|
|
readonly
|
|
class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm
|
|
bg-gray-50 dark:bg-gray-600 text-gray-700 dark:text-gray-300"
|
|
/>
|
|
<button
|
|
@click="copyLink"
|
|
class="px-3 py-2 bg-gray-600 text-white rounded-md hover:bg-gray-700 text-sm"
|
|
>
|
|
<Icon name="mdi:content-copy" class="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
|
Anyone with this link can {{ accessLevel }} the document
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Notifications -->
|
|
<div class="flex items-center space-x-3">
|
|
<input
|
|
v-model="notifyOnAccess"
|
|
type="checkbox"
|
|
id="notify-access"
|
|
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500"
|
|
/>
|
|
<label for="notify-access" class="text-sm text-gray-700 dark:text-gray-300">
|
|
Notify me when someone accesses this document
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<div class="flex items-center justify-between p-6 border-t border-gray-200 dark:border-gray-700">
|
|
<div class="text-xs text-gray-500 dark:text-gray-400">
|
|
{{ emailList.length }} recipient(s) selected
|
|
</div>
|
|
<div class="flex items-center space-x-3">
|
|
<button
|
|
@click="closeModal"
|
|
class="px-4 py-2 text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
@click="shareDocument"
|
|
:disabled="emailList.length === 0"
|
|
class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
Share Document
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed } from 'vue';
|
|
|
|
const props = defineProps({
|
|
visible: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
document: {
|
|
type: Object,
|
|
required: true
|
|
}
|
|
});
|
|
|
|
const emit = defineEmits([
|
|
'update:visible',
|
|
'shared'
|
|
]);
|
|
|
|
// State
|
|
const newEmail = ref('');
|
|
const emailList = ref([]);
|
|
const accessLevel = ref('view');
|
|
const expiration = ref('never');
|
|
const customDate = ref('');
|
|
const shareMessage = ref('');
|
|
const notifyOnAccess = ref(true);
|
|
|
|
// Computed
|
|
const isVisible = computed({
|
|
get() {
|
|
return props.visible;
|
|
},
|
|
set(value) {
|
|
emit('update:visible', value);
|
|
}
|
|
});
|
|
|
|
const shareLink = computed(() => {
|
|
// Generate a shareable link (mock implementation)
|
|
return `https://dms.example.com/share/${props.document.id}?access=${accessLevel.value}`;
|
|
});
|
|
|
|
// Methods
|
|
const closeModal = () => {
|
|
isVisible.value = false;
|
|
resetForm();
|
|
};
|
|
|
|
const resetForm = () => {
|
|
newEmail.value = '';
|
|
emailList.value = [];
|
|
accessLevel.value = 'view';
|
|
expiration.value = 'never';
|
|
customDate.value = '';
|
|
shareMessage.value = '';
|
|
notifyOnAccess.value = true;
|
|
};
|
|
|
|
const addEmail = () => {
|
|
const email = newEmail.value.trim();
|
|
if (email && isValidEmail(email) && !emailList.value.includes(email)) {
|
|
emailList.value.push(email);
|
|
newEmail.value = '';
|
|
}
|
|
};
|
|
|
|
const removeEmail = (index) => {
|
|
emailList.value.splice(index, 1);
|
|
};
|
|
|
|
const isValidEmail = (email) => {
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
return emailRegex.test(email);
|
|
};
|
|
|
|
const copyLink = async () => {
|
|
try {
|
|
await navigator.clipboard.writeText(shareLink.value);
|
|
// You could show a toast notification here
|
|
console.log('Link copied to clipboard');
|
|
} catch (err) {
|
|
console.error('Failed to copy link:', err);
|
|
}
|
|
};
|
|
|
|
const shareDocument = () => {
|
|
const shareData = {
|
|
documentId: props.document.id,
|
|
recipients: emailList.value,
|
|
accessLevel: accessLevel.value,
|
|
expiration: expiration.value,
|
|
customDate: customDate.value,
|
|
message: shareMessage.value,
|
|
notifyOnAccess: notifyOnAccess.value,
|
|
shareLink: shareLink.value
|
|
};
|
|
|
|
emit('shared', shareData);
|
|
closeModal();
|
|
};
|
|
|
|
// Utility functions
|
|
const getFileTypeIcon = (fileName) => {
|
|
if (!fileName) return 'mdi:file-document';
|
|
const extension = fileName.split('.').pop()?.toLowerCase();
|
|
const iconMap = {
|
|
pdf: 'mdi:file-pdf-box',
|
|
doc: 'mdi:file-word-box',
|
|
docx: 'mdi:file-word-box',
|
|
xls: 'mdi:file-excel-box',
|
|
xlsx: 'mdi:file-excel-box',
|
|
ppt: 'mdi:file-powerpoint-box',
|
|
pptx: 'mdi:file-powerpoint-box',
|
|
txt: 'mdi:file-document-outline',
|
|
md: 'mdi:language-markdown',
|
|
jpg: 'mdi:file-image',
|
|
jpeg: 'mdi:file-image',
|
|
png: 'mdi:file-image',
|
|
gif: 'mdi:file-image',
|
|
default: 'mdi:file-document'
|
|
};
|
|
return iconMap[extension] || iconMap.default;
|
|
};
|
|
|
|
const getFileTypeColor = (fileName) => {
|
|
if (!fileName) return 'text-gray-400';
|
|
const extension = fileName.split('.').pop()?.toLowerCase();
|
|
const colorMap = {
|
|
pdf: 'text-red-400',
|
|
doc: 'text-blue-400',
|
|
docx: 'text-blue-400',
|
|
xls: 'text-green-400',
|
|
xlsx: 'text-green-400',
|
|
ppt: 'text-orange-400',
|
|
pptx: 'text-orange-400',
|
|
txt: 'text-gray-400',
|
|
md: 'text-purple-400',
|
|
jpg: 'text-purple-400',
|
|
jpeg: 'text-purple-400',
|
|
png: 'text-purple-400',
|
|
gif: 'text-purple-400',
|
|
default: 'text-gray-400'
|
|
};
|
|
return colorMap[extension] || colorMap.default;
|
|
};
|
|
|
|
const formatFileSize = (size) => {
|
|
if (typeof size === 'string') return size;
|
|
if (!size) return '0 B';
|
|
const units = ['B', 'KB', 'MB', 'GB'];
|
|
let index = 0;
|
|
while (size >= 1024 && index < units.length - 1) {
|
|
size /= 1024;
|
|
index++;
|
|
}
|
|
return `${size.toFixed(1)} ${units[index]}`;
|
|
};
|
|
|
|
const formatDate = (dateString) => {
|
|
const date = new Date(dateString);
|
|
return date.toLocaleDateString();
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* Custom scrollbar for email list */
|
|
.email-list::-webkit-scrollbar {
|
|
width: 4px;
|
|
}
|
|
|
|
.email-list::-webkit-scrollbar-track {
|
|
background: #f1f5f9;
|
|
}
|
|
|
|
.email-list::-webkit-scrollbar-thumb {
|
|
background: #cbd5e1;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.email-list::-webkit-scrollbar-thumb:hover {
|
|
background: #94a3b8;
|
|
}
|
|
|
|
/* Dark mode scrollbar */
|
|
.dark .email-list::-webkit-scrollbar-track {
|
|
background: #334155;
|
|
}
|
|
|
|
.dark .email-list::-webkit-scrollbar-thumb {
|
|
background: #475569;
|
|
}
|
|
|
|
.dark .email-list::-webkit-scrollbar-thumb:hover {
|
|
background: #64748b;
|
|
}
|
|
</style> |