refactor: improvemets
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -1,12 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import type { FileUploadProgress } from '@/composables/useFileUpload'
|
||||
import { formatSize, getUploadStatusIcon, getUploadStatusColor } from '@/utils/fileHelpers'
|
||||
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
uploads: Map<string, FileUploadProgress>
|
||||
totalProgress: number
|
||||
isUploading: boolean
|
||||
isPreparing: boolean
|
||||
preparingMessage: string
|
||||
preparingProcessedCount: number
|
||||
preparingTotalCount: number
|
||||
pendingCount: number
|
||||
completedCount: number
|
||||
}>()
|
||||
@@ -23,6 +28,23 @@ const emit = defineEmits<{
|
||||
function handleClose() {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
const uploadEntries = computed(() =>
|
||||
Array.from(props.uploads.entries(), ([id, item]) => ({ id, item }))
|
||||
)
|
||||
|
||||
const failedCount = computed(() =>
|
||||
uploadEntries.value.filter(entry => entry.item.status === 'error').length
|
||||
)
|
||||
|
||||
const queuedCount = computed(() => uploadEntries.value.length)
|
||||
|
||||
const preparingProgress = computed(() => {
|
||||
if (props.preparingTotalCount <= 0) return undefined
|
||||
return Math.round((props.preparingProcessedCount / props.preparingTotalCount) * 100)
|
||||
})
|
||||
|
||||
const listHeight = computed(() => Math.min(Math.max(uploadEntries.value.length, 1), 6) * 64)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -33,6 +55,31 @@ function handleClose() {
|
||||
Upload Files
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<div v-if="isPreparing" class="upload-preparing mb-4">
|
||||
<div class="d-flex align-center ga-3 mb-2">
|
||||
<v-progress-circular indeterminate size="18" width="2" color="primary" />
|
||||
<div>
|
||||
<div class="text-body-2 font-weight-medium">{{ preparingMessage }}</div>
|
||||
<div class="text-caption text-medium-emphasis">
|
||||
<template v-if="preparingTotalCount > 0">
|
||||
{{ preparingProcessedCount }} / {{ preparingTotalCount }} files queued
|
||||
</template>
|
||||
<template v-else>
|
||||
Preparing uploads...
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-progress-linear
|
||||
v-if="preparingProgress !== undefined"
|
||||
:model-value="preparingProgress"
|
||||
color="primary"
|
||||
height="6"
|
||||
rounded
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Upload progress -->
|
||||
<div v-if="totalProgress > 0 && isUploading" class="mb-4">
|
||||
<v-progress-linear
|
||||
@@ -46,47 +93,66 @@ function handleClose() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="queuedCount > 0" class="upload-summary mb-4">
|
||||
<v-chip size="small" variant="tonal" color="primary">
|
||||
{{ queuedCount }} queued
|
||||
</v-chip>
|
||||
<v-chip v-if="pendingCount > 0" size="small" variant="tonal">
|
||||
{{ pendingCount }} pending
|
||||
</v-chip>
|
||||
<v-chip v-if="completedCount > 0" size="small" variant="tonal" color="success">
|
||||
{{ completedCount }} completed
|
||||
</v-chip>
|
||||
<v-chip v-if="failedCount > 0" size="small" variant="tonal" color="error">
|
||||
{{ failedCount }} failed
|
||||
</v-chip>
|
||||
</div>
|
||||
|
||||
<!-- File list -->
|
||||
<v-list density="compact" class="upload-file-list">
|
||||
<v-list-item
|
||||
v-for="[id, item] in uploads"
|
||||
:key="id"
|
||||
class="px-0"
|
||||
>
|
||||
<template #prepend>
|
||||
<v-icon :color="getUploadStatusColor(item.status)" size="small">
|
||||
{{ getUploadStatusIcon(item.status) }}
|
||||
</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">
|
||||
{{ item.relativePath || item.file.name }}
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
{{ formatSize(item.file.size) }}
|
||||
<span v-if="item.error" class="text-error"> — {{ item.error }}</span>
|
||||
</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-btn
|
||||
v-if="item.status === 'pending' || item.status === 'error'"
|
||||
icon="mdi-close"
|
||||
size="x-small"
|
||||
variant="text"
|
||||
@click="emit('remove-upload', id)"
|
||||
/>
|
||||
<v-btn
|
||||
v-if="item.status === 'error'"
|
||||
icon="mdi-refresh"
|
||||
size="x-small"
|
||||
variant="text"
|
||||
color="primary"
|
||||
@click="emit('retry-upload', id)"
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<v-virtual-scroll
|
||||
v-if="queuedCount > 0"
|
||||
:items="uploadEntries"
|
||||
:height="listHeight"
|
||||
:item-height="64"
|
||||
class="upload-file-list"
|
||||
>
|
||||
<template #default="{ item: entry }">
|
||||
<v-list-item :key="entry.id" class="px-0 upload-file-row">
|
||||
<template #prepend>
|
||||
<v-icon :color="getUploadStatusColor(entry.item.status)" size="small">
|
||||
{{ getUploadStatusIcon(entry.item.status) }}
|
||||
</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">
|
||||
{{ entry.item.relativePath || entry.item.file.name }}
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
{{ formatSize(entry.item.file.size) }}
|
||||
<span v-if="entry.item.error" class="text-error"> — {{ entry.item.error }}</span>
|
||||
</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-btn
|
||||
v-if="entry.item.status === 'pending' || entry.item.status === 'error'"
|
||||
icon="mdi-close"
|
||||
size="x-small"
|
||||
variant="text"
|
||||
@click="emit('remove-upload', entry.id)"
|
||||
/>
|
||||
<v-btn
|
||||
v-if="entry.item.status === 'error'"
|
||||
icon="mdi-refresh"
|
||||
size="x-small"
|
||||
variant="text"
|
||||
color="primary"
|
||||
@click="emit('retry-upload', entry.id)"
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</template>
|
||||
</v-virtual-scroll>
|
||||
|
||||
<!-- Empty state -->
|
||||
<div v-if="uploads.size === 0" class="text-center py-8 text-grey">
|
||||
<div v-if="queuedCount === 0 && !isPreparing" class="text-center py-8 text-grey">
|
||||
<v-icon size="48" color="grey-lighten-1">mdi-file-upload-outline</v-icon>
|
||||
<div class="mt-2">No files selected</div>
|
||||
<v-btn
|
||||
@@ -101,7 +167,7 @@ function handleClose() {
|
||||
</div>
|
||||
|
||||
<!-- Add more files button -->
|
||||
<div v-else class="text-center mt-4">
|
||||
<div v-else-if="queuedCount > 0" class="text-center mt-4">
|
||||
<v-btn
|
||||
variant="text"
|
||||
size="small"
|
||||
@@ -117,7 +183,7 @@ function handleClose() {
|
||||
<v-btn
|
||||
variant="text"
|
||||
@click="handleClose"
|
||||
:disabled="isUploading"
|
||||
:disabled="isUploading || isPreparing"
|
||||
>
|
||||
{{ completedCount > 0 ? 'Done' : 'Cancel' }}
|
||||
</v-btn>
|
||||
@@ -127,6 +193,7 @@ function handleClose() {
|
||||
variant="elevated"
|
||||
@click="emit('upload-all')"
|
||||
:loading="isUploading"
|
||||
:disabled="isPreparing"
|
||||
>
|
||||
Upload {{ pendingCount }} File(s)
|
||||
</v-btn>
|
||||
@@ -136,8 +203,25 @@ function handleClose() {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.upload-preparing {
|
||||
padding: 12px;
|
||||
border: 1px solid rgb(var(--v-border-color));
|
||||
border-radius: 8px;
|
||||
background: rgb(var(--v-theme-surface-variant), 0.3);
|
||||
}
|
||||
|
||||
.upload-summary {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.upload-file-list {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.upload-file-row {
|
||||
border-bottom: 1px solid rgb(var(--v-border-color), 0.45);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user