Extract upload dropzone and queue UI with shared pick validation utils.

This commit is contained in:
2026-10-20 22:20:16 +02:00
parent 3ab66c250c
commit d82d451448
12 changed files with 547 additions and 94 deletions
@@ -0,0 +1,53 @@
<template>
<ul v-if="rows.length > 0" class="divide-y divide-border rounded-lg border border-border">
<li v-for="row in rows" :key="row.key">
<button
type="button"
class="flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/60"
:class="isRowSelected(row) ? 'bg-muted/80' : ''"
@click="emit('select', row.file)"
>
<span class="min-w-0 flex-1 truncate text-sm font-medium">{{ row.file.name }}</span>
<span class="shrink-0 text-xs text-muted-foreground">
{{ prettyBytes(row.file.size, { maximumFractionDigits: 1 }) }}
</span>
<span class="shrink-0 text-xs" :class="statusTextClass(row.preview)">
{{ row.preview.message }}
</span>
</button>
</li>
</ul>
</template>
<script setup lang="ts">
import { type PropType } from 'vue';
import { useMetadataStore } from '@/stores/metadata';
import type { MetadataPreviewEntry } from '@/types/metadata/MetadataPreview';
import type { UploadQueueRow } from '@/types/upload/UploadQueueRow';
import prettyBytes from 'pretty-bytes';
const metadataStore = useMetadataStore();
const { makeKeyFromFile } = metadataStore;
const props = defineProps({
rows: {
type: Array as PropType<UploadQueueRow[]>,
required: true
},
selectedFile: {
type: Object as PropType<File | null>,
default: null
}
});
const emit = defineEmits<{
select: [file: File];
}>();
const isRowSelected = (row: UploadQueueRow): boolean =>
props.selectedFile !== null && makeKeyFromFile(props.selectedFile) === row.key;
const statusTextClass = (entry: MetadataPreviewEntry): string =>
entry.status === 'error' ? 'text-destructive' : 'text-muted-foreground';
</script>