Files
Lesstime/frontend/modules/reporting/components/ReportingDoughnut.vue
T
Matthieu f4ffc02028 feat(reporting) : add Reporting dashboard front layer
LST-59 (3.1) front. Completes the Reporting module.

- New frontend/modules/reporting/ layer (auto-detected): /reporting page
  (admin middleware) consuming the 4 read-only report endpoints.
- Filters (period presets + custom dates, project, user). 4 sections (time per
  project, time per user, tasks by status, absences by type) each with a
  DataTable + a Chart.js chart (reused global registration).
- Front-side CSV export per section (useCsvExport: BOM UTF-8, ; separator).
- i18n keys (reporting.*, sidebar.admin.reporting).

nuxt build passes; /reporting routed; no route regression.
2026-06-21 00:16:03 +02:00

57 lines
1.3 KiB
Vue

<template>
<div class="h-64">
<Doughnut
v-if="hasData"
:data="chartData"
:options="options"
/>
<p v-else class="flex h-full items-center justify-center text-sm text-neutral-400">
{{ emptyMessage ?? $t('reporting.empty') }}
</p>
</div>
</template>
<script setup lang="ts">
import { Doughnut } from 'vue-chartjs'
const props = defineProps<{
labels: string[]
values: number[]
colors?: string[]
emptyMessage?: string
}>()
const palette = [
'#6366f1', '#10b981', '#f59e0b', '#ef4444', '#3b82f6',
'#8b5cf6', '#ec4899', '#14b8a6', '#f97316', '#84cc16',
]
const hasData = computed(() => props.values.some(v => v > 0))
const chartData = computed(() => ({
labels: props.labels,
datasets: [{
data: props.values,
backgroundColor: props.colors ?? props.labels.map((_, i) => palette[i % palette.length]),
borderWidth: 0,
}],
}))
const options = {
responsive: true,
maintainAspectRatio: false,
cutout: '65%',
plugins: {
legend: {
position: 'bottom' as const,
labels: {
padding: 16,
usePointStyle: true,
pointStyle: 'circle',
font: { size: 12 },
},
},
},
}
</script>