Add EOI automation system with email processing and document management
- Implement automated EOI processing from sales emails - Add EOI document upload and management capabilities - Enhance email thread handling with better parsing and grouping - Add retry logic and error handling for file operations - Introduce Documeso integration for document processing - Create server tasks and plugins infrastructure - Update email composer with improved attachment handling
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<v-container fluid class="pa-6">
|
||||
<!-- Header -->
|
||||
<v-row class="mb-6">
|
||||
<v-row class="mb-6" v-if="!props.selectionMode">
|
||||
<v-col>
|
||||
<h1 class="text-h4 font-weight-bold">
|
||||
File Browser
|
||||
@@ -12,14 +12,26 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Selection Mode Header -->
|
||||
<v-row v-if="props.selectionMode" class="mb-4">
|
||||
<v-col>
|
||||
<h2 class="text-h5 font-weight-bold">
|
||||
Select Files to Attach
|
||||
</h2>
|
||||
<p class="text-subtitle-2 text-grey mt-1">
|
||||
Click on files to attach them to your email
|
||||
</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Breadcrumb Navigation -->
|
||||
<v-row class="mb-4" v-if="currentPath">
|
||||
<v-row class="mb-4" v-if="currentPath && !props.selectionMode">
|
||||
<v-col>
|
||||
<v-breadcrumbs :items="breadcrumbItems" class="pa-0">
|
||||
<template v-slot:item="{ item }">
|
||||
<v-breadcrumbs-item
|
||||
:to="item.to"
|
||||
@click="navigateToFolder(item.path)"
|
||||
@click="navigateToFolder((item as any).path)"
|
||||
class="cursor-pointer"
|
||||
>
|
||||
{{ item.title }}
|
||||
</v-breadcrumbs-item>
|
||||
@@ -41,7 +53,7 @@
|
||||
@update:model-value="filterFiles"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6" class="d-flex justify-end ga-2">
|
||||
<v-col cols="12" md="6" class="d-flex justify-end ga-2" v-if="!props.selectionMode">
|
||||
<v-btn
|
||||
color="secondary"
|
||||
size="large"
|
||||
@@ -63,7 +75,7 @@
|
||||
</v-row>
|
||||
|
||||
<!-- Bulk Actions Bar (shown when items selected) -->
|
||||
<v-row v-if="selectedItems.length > 0" class="mb-4">
|
||||
<v-row v-if="selectedItems.length > 0 && !props.selectionMode" class="mb-4">
|
||||
<v-col>
|
||||
<v-alert
|
||||
type="info"
|
||||
@@ -312,7 +324,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { ref, computed, onMounted, watch } from 'vue';
|
||||
import FileUploader from '~/components/FileUploader.vue';
|
||||
import FilePreviewModal from '~/components/FilePreviewModal.vue';
|
||||
|
||||
@@ -325,8 +337,20 @@ interface FileItem {
|
||||
icon: string;
|
||||
displayName: string;
|
||||
isFolder: boolean;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
selectionMode?: boolean;
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'file-selected', file: FileItem): void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
// Data
|
||||
@@ -383,22 +407,42 @@ const breadcrumbItems = computed(() => {
|
||||
return items;
|
||||
});
|
||||
|
||||
// Load files
|
||||
const loadFiles = async () => {
|
||||
// Load files with retry logic
|
||||
const loadFiles = async (retryCount = 0) => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await $fetch('/api/files/list', {
|
||||
params: {
|
||||
prefix: currentPath.value,
|
||||
recursive: false,
|
||||
}
|
||||
},
|
||||
timeout: 15000 // 15 second timeout
|
||||
});
|
||||
files.value = response.files;
|
||||
filteredFiles.value = response.files;
|
||||
} catch (error) {
|
||||
toast.error('Failed to load files');
|
||||
files.value = response.files || [];
|
||||
filteredFiles.value = response.files || [];
|
||||
} catch (error: any) {
|
||||
console.error(`Failed to load files (attempt ${retryCount + 1}/3):`, error);
|
||||
|
||||
// Retry on certain errors
|
||||
if (retryCount < 2 && (
|
||||
error.message?.includes('Failed to fetch') ||
|
||||
error.message?.includes('Network') ||
|
||||
error.statusCode === 500 ||
|
||||
error.statusCode === 503
|
||||
)) {
|
||||
console.log('Retrying file load...');
|
||||
setTimeout(() => {
|
||||
loadFiles(retryCount + 1);
|
||||
}, (retryCount + 1) * 1000); // Exponential backoff
|
||||
} else {
|
||||
toast.error('Failed to load files. Please refresh the page.');
|
||||
files.value = [];
|
||||
filteredFiles.value = [];
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
if (retryCount === 0 || retryCount === 2) {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -417,6 +461,15 @@ const filterFiles = () => {
|
||||
|
||||
// Handle file/folder click
|
||||
const handleFileClick = (item: FileItem) => {
|
||||
if (props.selectionMode && !item.isFolder) {
|
||||
// In selection mode, emit the file for attachment
|
||||
emit('file-selected', {
|
||||
...item,
|
||||
path: item.name
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.isFolder) {
|
||||
navigateToFolder(item.name);
|
||||
} else if (canPreview(item)) {
|
||||
|
||||
Reference in New Issue
Block a user