Files
hermione-fe/src/components/ScenarioFileUpload.vue
dalia.florinda 9c254c9c28 commit fix bugs
2025-12-19 10:01:43 +01:00

216 lines
6.8 KiB
Vue

<script setup>
import { FileUploadStore } from '@/stores/FileUploadStore';
import { useAuth } from '@websanova/vue-auth/src/v3.js';
import { usePrimeVue } from 'primevue/config';
import { useToast } from 'primevue/usetoast';
import { ref, watch } from 'vue';
const props = defineProps({
inputName: {
type: String,
required: true
},
label: {
type: String,
required: true
},
tooltipText: {
type: String,
default: 'Upload files'
},
uploadUrl: {
type: String,
required: true
},
isMultiple: {
type: Boolean,
default: false
},
acceptedFormats: {
type: String,
default: '.docx'
},
folderName: {
type: String,
required: true
},
uploadedFiles: {
type: Array,
default: () => []
}
});
const emit = defineEmits(['upload', 'remove', 'update:uploadedFiles']);
const fileUploadStore = FileUploadStore();
const toast = useToast();
const auth = useAuth();
const $primevue = usePrimeVue();
const localUploadedFiles = ref(props.uploadedFiles);
// Sync uploaded files with parent
watch(
localUploadedFiles,
(newFiles) => {
emit('update:uploadedFiles', newFiles);
},
{ deep: true }
);
watch(
() => props.uploadedFiles,
(newFiles) => {
localUploadedFiles.value = newFiles;
},
{ deep: true }
);
const onBeforeSend = (event) => {
const { xhr } = event;
const token = auth.token();
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
};
const handleUpload = (event) => {
const { xhr } = event;
if (xhr.status === 200) {
const uploadedFileName = event.files && event.files.length > 0 ? event.files[0].name : 'UnknownFile';
toast.add({
severity: 'success',
summary: 'Success',
detail: 'File uploaded successfully!',
life: 3000
});
emit('upload', {
fileName: uploadedFileName,
response: xhr.response,
files: event.files,
inputName: props.inputName
});
} else {
toast.add({
severity: 'error',
summary: 'Error',
detail: `Failed to upload file. Status: ${xhr.status}`,
life: 3000
});
}
};
const handleRemove = async (event, removeUploadedFileCallback) => {
const { file, index } = event;
try {
const response = await fileUploadStore.deleteFile(file.name, props.folderName);
if (response.status === 200) {
toast.add({
severity: 'success',
summary: 'Success',
detail: 'File removed successfully!',
life: 3000
});
removeUploadedFileCallback(index);
emit('remove', { fileName: file.name, inputName: props.inputName });
} else {
toast.add({
severity: 'error',
summary: 'Error',
detail: `Failed to remove file. Status: ${response.statusText}`,
life: 3000
});
}
} catch (error) {
toast.add({
severity: 'error',
summary: 'Error',
detail: `Error while removing file: ${error.message}`,
life: 3000
});
}
};
const formatSize = (bytes) => {
const k = 1024;
const sizes = $primevue.config.locale.fileSizeTypes;
if (bytes === 0) {
return `0 ${sizes[0]}`;
}
const i = Math.floor(Math.log(bytes) / Math.log(k));
const truncatedSize = Math.trunc(bytes / Math.pow(k, i));
return `${truncatedSize} ${sizes[i]}`;
};
</script>
<template>
<div>
<label :for="inputName">
<b>{{ label }}</b>
<i class="pi pi-info-circle text-violet-600 cursor-pointer" v-tooltip="tooltipText"></i>
</label>
<div>
<FileUpload
name="MultiFileUpload"
:customUpload="false"
:url="uploadUrl"
@upload="handleUpload"
:multiple="isMultiple"
:accept="acceptedFormats"
auto
:showUploadButton="false"
:showCancelButton="false"
:maxFileSize="52428800"
:invalidFileSizeMessage="'Invalid file size, file size should be smaller than 20 MB'"
v-model:files="localUploadedFiles"
@before-send="onBeforeSend"
>
<template #content="{ uploadedFiles, removeUploadedFileCallback }">
<div class="pt-4">
<div v-if="uploadedFiles.length > 0">
<table class="table-auto w-full border-collapse border border-gray-200">
<thead>
<tr>
<th class="border border-gray-300 p-2">Name</th>
<th class="border border-gray-300 p-2">Dimension</th>
<th class="border border-gray-300 p-2">Status</th>
<th class="border border-gray-300 p-2">Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="(file, index) in uploadedFiles" :key="file.name + file.size" class="hover:bg-gray-50">
<td class="border border-gray-300 p-2">{{ file.name }}</td>
<td class="border border-gray-300 p-2">{{ formatSize(file.size) }}</td>
<td class="border border-gray-300 p-2">
<Badge value="UPLOADED" severity="success" />
</td>
<td class="border border-gray-300 p-2">
<Button label="Remove" @click="handleRemove({ file, index }, removeUploadedFileCallback)" />
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<template #empty>
<div class="flex items-center justify-center flex-col">
<div class="!border !border-violet-600 !rounded-full !w-24 !h-24 flex items-center justify-center">
<i class="pi pi-cloud-upload !text-4xl !-violet-600"></i>
</div>
<p class="mt-2 mb-2 text-m">Drag and drop files here to upload.</p>
</div>
</template>
</FileUpload>
</div>
</div>
</template>