feat(fc3c): /downloads view + DownloadEventRow + DownloadDetailModal + FilterPills
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div class="fc-dl-row" @click="$emit('open', event.id)">
|
||||
<v-icon :icon="statusIcon" :color="statusColor" size="small" />
|
||||
<RouterLink
|
||||
v-if="event.artist_slug"
|
||||
:to="`/artist/${event.artist_slug}`"
|
||||
class="fc-dl-row__artist"
|
||||
@click.stop
|
||||
>{{ event.artist_name }}</RouterLink>
|
||||
<span v-else class="fc-dl-row__artist">—</span>
|
||||
<v-chip size="x-small" variant="tonal">{{ event.platform || '—' }}</v-chip>
|
||||
<span class="fc-dl-row__time">{{ fmtTime(event.started_at) }}</span>
|
||||
<span class="fc-dl-row__files">{{ event.files_count }} files</span>
|
||||
<span class="fc-dl-row__duration">{{ fmtDuration(event.summary?.duration_seconds) }}</span>
|
||||
<span v-if="event.error" class="fc-dl-row__error">{{ event.error }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
|
||||
const props = defineProps({ event: { type: Object, required: true } })
|
||||
defineEmits(['open'])
|
||||
|
||||
const statusIcon = computed(() => ({
|
||||
ok: 'mdi-check-circle',
|
||||
error: 'mdi-alert-circle',
|
||||
running: 'mdi-progress-clock',
|
||||
pending: 'mdi-clock-outline',
|
||||
skipped: 'mdi-minus-circle',
|
||||
}[props.event.status] || 'mdi-help-circle'))
|
||||
|
||||
const statusColor = computed(() => ({
|
||||
ok: 'success',
|
||||
error: 'error',
|
||||
running: 'info',
|
||||
pending: 'secondary',
|
||||
skipped: 'warning',
|
||||
}[props.event.status] || undefined))
|
||||
|
||||
function fmtTime(iso) {
|
||||
if (!iso) return '—'
|
||||
return iso.slice(0, 19).replace('T', ' ')
|
||||
}
|
||||
function fmtDuration(sec) {
|
||||
if (sec == null) return '—'
|
||||
if (sec < 60) return `${sec.toFixed(1)}s`
|
||||
const m = Math.floor(sec / 60), s = Math.floor(sec % 60)
|
||||
return `${m}m ${s}s`
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fc-dl-row {
|
||||
display: grid;
|
||||
grid-template-columns: 24px 1fr 96px 160px 80px 80px 1fr;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-bottom: 1px solid rgb(var(--v-theme-on-surface-variant) / 0.18);
|
||||
cursor: pointer;
|
||||
}
|
||||
.fc-dl-row:hover { background: rgb(var(--v-theme-surface) / 0.5); }
|
||||
.fc-dl-row__artist {
|
||||
color: rgb(var(--v-theme-on-surface));
|
||||
text-decoration: none;
|
||||
}
|
||||
.fc-dl-row__artist:hover { color: rgb(var(--v-theme-accent)); }
|
||||
.fc-dl-row__time, .fc-dl-row__files, .fc-dl-row__duration {
|
||||
color: rgb(var(--v-theme-on-surface-variant));
|
||||
font-size: 0.85rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.fc-dl-row__error {
|
||||
color: rgb(var(--v-theme-error));
|
||||
font-size: 0.85rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user