-
-
-
- {{ getStatusIcon(download.status) }}
-
-
-
- {{ getDownloadLabel(download) }}
-
-
- {{ formatRelativeTime(download.created_at) }}
-
- {{ download.file_count }} new file{{ download.file_count !== 1 ? 's' : '' }}
-
-
-
-
- {{ download.status === 'completed' ? 'new content' : download.status }}
-
-
-
+
+
+
+
+
+ {{ getStatusIcon(entry.download.status) }}
+
+
+
+ {{ getDownloadLabel(entry.download) }}
+
+
+ {{ formatRelativeTime(entry.download.created_at) }}
+
+ {{ entry.download.file_count }} new file{{ entry.download.file_count !== 1 ? 's' : '' }}
+
+
+
+
+ {{ entry.download.status === 'completed' ? 'new content' : entry.download.status }}
+
+
+
+
+
+ mdi-alert-circle
+
+ {{ entry.label }}
+
+ {{ entry.items.length }} failure{{ entry.items.length !== 1 ? 's' : '' }} · latest {{ formatRelativeTime(entry.latest.created_at) }}
+
+
+
+ {{ entry.items.length }}× failed
+
+
+
+
mdi-history
@@ -354,6 +379,29 @@ let refreshInterval = null
const stats = computed(() => downloadsStore.stats)
const recentActivity = computed(() => downloadsStore.recentActivity)
+const recentActivityGrouped = computed(() => {
+ const singles = []
+ const groups = new Map()
+ for (const d of recentActivity.value) {
+ if (d.status === 'failed') {
+ const key = `${d.subscription_name || '?'}:${d.platform || '?'}`
+ const g = groups.get(key) || { kind: 'group', key, label: key, items: [], latest: d }
+ g.items.push(d)
+ if (new Date(d.created_at) > new Date(g.latest.created_at)) g.latest = d
+ groups.set(key, g)
+ } else {
+ singles.push({ kind: 'single', key: `d-${d.id}`, download: d })
+ }
+ }
+ const out = [...singles, ...groups.values()]
+ out.sort((a, b) => {
+ const tA = new Date(a.kind === 'single' ? a.download.created_at : a.latest.created_at)
+ const tB = new Date(b.kind === 'single' ? b.download.created_at : b.latest.created_at)
+ return tB - tA
+ })
+ return out
+})
+
// Read threshold from settings, default 5 if unset or invalid
const failureThreshold = computed(() => {
const n = Number(settingsStore.settings['dashboard.failure_threshold'])