93 lines
3.2 KiB
Vue
93 lines
3.2 KiB
Vue
<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-start gap-3 px-3 py-3 text-left transition-colors hover:bg-muted/60 sm:items-center sm:px-4"
|
|
:class="isRowSelected(row) ? 'bg-muted/80' : ''"
|
|
@click="emit('select', row.file)"
|
|
>
|
|
<img
|
|
v-if="row.thumbnailUrl"
|
|
:src="row.thumbnailUrl"
|
|
class="h-10 w-10 shrink-0 rounded border border-border object-cover"
|
|
loading="lazy"
|
|
decoding="async"
|
|
/>
|
|
<span
|
|
v-else
|
|
class="h-10 w-10 shrink-0 rounded border border-border bg-muted/60"
|
|
aria-hidden="true"
|
|
/>
|
|
<div class="min-w-0 flex-1 space-y-1">
|
|
<div class="flex min-w-0 items-baseline justify-between gap-2">
|
|
<span class="min-w-0 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>
|
|
</div>
|
|
<div class="flex min-w-0 flex-col gap-0.5 text-xs">
|
|
<span class="min-w-0 truncate" :class="metadataStatusClass(row.metadata)">
|
|
{{ row.metadata.message }}
|
|
</span>
|
|
<span class="min-w-0 truncate" :class="uploadStatusClass(row.upload)">
|
|
{{ row.upload.message }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { type PropType } from 'vue';
|
|
import type { MetadataPreviewEntry } from '@/types/metadata/MetadataPreview';
|
|
import { makeKeyFromFile } from '@/utils/file/makeKeyFromFile';
|
|
import type { UploadQueueEntry } from '@/types/upload/UploadQueueEntry';
|
|
import type { UploadQueueRow } from '@/types/upload/UploadQueueRow';
|
|
import prettyBytes from 'pretty-bytes';
|
|
|
|
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 metadataStatusClass = (entry: MetadataPreviewEntry): string => {
|
|
if (entry.status === 'error') {
|
|
return 'text-destructive';
|
|
}
|
|
|
|
if (entry.status === 'ready') {
|
|
return 'text-success';
|
|
}
|
|
|
|
return 'text-muted-foreground';
|
|
};
|
|
|
|
const uploadStatusClass = (entry: UploadQueueEntry): string => {
|
|
if (entry.status === 'failed') {
|
|
return 'text-destructive';
|
|
}
|
|
|
|
if (entry.status === 'uploaded') {
|
|
return 'text-success';
|
|
}
|
|
|
|
return 'text-muted-foreground';
|
|
};
|
|
</script>
|