Files
music-metadata-system/frontend/src/components/dashboard/DashboardOverview.vue

88 lines
3.0 KiB
Vue

<template>
<section class="feature-page__section">
<div class="feature-grid feature-grid--cards">
<StatCard v-for="card in cards" :key="card.label" :label="card.label" :value="card.value" :hint="card.hint" />
</div>
<div class="feature-grid feature-grid--dashboard">
<article class="feature-panel app-panel">
<h3>Recent tasks</h3>
<div v-for="task in tasks" :key="task.id" class="row-card">
<div>
<strong>{{ task.name }}</strong>
<div class="muted">{{ task.processedFiles }} / {{ task.totalFiles }} files</div>
</div>
<StatusBadge :label="task.status" :tone="mapTaskStatusTone(task.status)" />
</div>
</article>
<article class="feature-panel app-panel">
<h3>Processing chart</h3>
<div class="muted">Chart total {{ chartTotal }}</div>
<div ref="chartRef" class="echart-panel" aria-label="Processing chart"></div>
</article>
<article class="feature-panel app-panel">
<h3>Quick entries</h3>
<div v-for="entry in quickEntries" :key="entry.title" class="row-card row-card--stacked">
<strong>{{ entry.title }}</strong>
<div>{{ entry.value }}</div>
<div class="muted">{{ entry.hint }}</div>
<RouterLink :to="entry.to" class="quick-link">{{ entry.actionLabel }}</RouterLink>
</div>
</article>
</div>
</section>
</template>
<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { RouterLink } from 'vue-router'
import * as echarts from 'echarts'
import StatCard from '../common/StatCard.vue'
import StatusBadge from '../common/StatusBadge.vue'
import type { DashboardQuickEntry } from '../../types/dashboard'
import type { TaskSummary } from '../../types/task'
import { mapTaskStatusTone } from '../../utils/task'
const props = defineProps<{
cards: Array<{ label: string; value: string; hint: string }>
tasks: TaskSummary[]
quickEntries: DashboardQuickEntry[]
chartBars: Array<{ label: string; value: number; raw: number }>
}>()
const chartRef = ref<HTMLDivElement | null>(null)
let chartInstance: echarts.ECharts | null = null
const chartTotal = computed(() => props.chartBars.reduce((sum, item) => sum + item.raw, 0))
function renderChart() {
if (!chartRef.value) return
if (typeof HTMLCanvasElement === 'undefined') return
if (!HTMLCanvasElement.prototype.getContext) return
if (chartRef.value.clientWidth === 0 || chartRef.value.clientHeight === 0) return
chartInstance?.dispose()
chartInstance = echarts.init(chartRef.value)
chartInstance.setOption({
tooltip: { trigger: 'item' },
series: [
{
type: 'pie',
radius: ['45%', '70%'],
label: { color: '#e8eef8' },
data: props.chartBars.map((bar) => ({ name: bar.label, value: bar.raw }))
}
]
})
}
onMounted(renderChart)
watch(() => props.chartBars, renderChart, { deep: true })
onBeforeUnmount(() => {
chartInstance?.dispose()
chartInstance = null
})
</script>