Files
FabledCurator/frontend/src/components/settings/DownloadsActivityPanel.vue
T
bvandeusen e039689eff
CI / lint (push) Successful in 3s
CI / frontend-build (push) Successful in 19s
CI / backend-lint-and-test (push) Successful in 32s
CI / integration (push) Successful in 3m35s
feat(ia): wave 2 — Activity becomes the whole-app pulse; Overview gets the health strip
The Activity tab only knew Celery — the GPU agent (the majority of processing)
and the download pipeline were invisible there. Two new self-polling panels:

- GpuActivityPanel: queue depths + triage verdicts (defects / file-ok /
  unprobed, top reason buckets) with a jump to Maintenance -> Failed
  processing. The triage detail refetches only when the error count moves.
- DownloadsActivityPanel: 24h stat chips + failing-source names with a jump
  into Subscriptions.

Both panels join the Activity tab under Queues+workers AND double as the
Overview health strip (side-by-side grid under the Celery summary) — one
component set, so Overview answers 'is everything healthy?' across all
systems. SystemStatsCards reviewed: content still accurate, left as-is.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
2026-07-02 17:45:45 -04:00

78 lines
2.3 KiB
Vue

<template>
<v-card class="mb-4">
<CardHeading icon="mdi-download" title="Downloads (last 24h)">
<v-spacer />
<v-btn
variant="text" size="small" rounded="pill"
to="/subscriptions?tab=downloads"
>
Open subscriptions
<v-icon end size="small">mdi-arrow-right</v-icon>
</v-btn>
</CardHeading>
<v-card-text>
<div class="d-flex align-center flex-wrap mb-2" style="gap: 8px">
<v-chip size="small" variant="tonal" color="success">
{{ stats.ok }} ok
</v-chip>
<v-chip
size="small" variant="tonal"
:color="stats.error ? 'error' : undefined"
>
{{ stats.error }} failed
</v-chip>
<v-chip v-if="stats.running" size="small" variant="tonal" color="accent">
{{ stats.running }} running
</v-chip>
<v-chip v-if="stats.pending" size="small" variant="tonal">
{{ stats.pending }} pending
</v-chip>
<v-chip v-if="stats.skipped" size="small" variant="tonal">
{{ stats.skipped }} skipped
</v-chip>
</div>
<p v-if="!failing.length" class="fc-muted text-body-2 mb-0">
All subscription sources healthy.
</p>
<p v-else class="text-body-2 mb-0">
<b class="fc-bad">{{ failing.length }}</b> failing source(s):
<span class="fc-muted">{{ failingNames }}</span>
</p>
</v-card-text>
</v-card>
</template>
<script setup>
import { computed, onMounted, onUnmounted } from 'vue'
import { storeToRefs } from 'pinia'
import CardHeading from '../common/CardHeading.vue'
import { useDownloadsStore } from '../../stores/downloads.js'
const store = useDownloadsStore()
const { stats, failing } = storeToRefs(store)
let pollId = null
const failingNames = computed(() => {
const names = failing.value.map((s) => s.artist_name || s.url).slice(0, 3)
const extra = failing.value.length - names.length
return names.join(', ') + (extra > 0 ? ` +${extra} more` : '')
})
function poll() {
store.loadStats(24)
store.loadFailing()
}
onMounted(() => {
poll()
pollId = setInterval(() => { if (!document.hidden) poll() }, 30000)
})
onUnmounted(() => { if (pollId) clearInterval(pollId) })
</script>
<style scoped>
.fc-muted { color: rgb(var(--v-theme-on-surface-variant)); }
.fc-bad { color: rgb(var(--v-theme-error)); }
</style>