refactor: improvemets

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-03-24 19:10:52 -04:00
parent b6d6bed2ee
commit da6a407445
16 changed files with 1063 additions and 254 deletions

View File

@@ -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>