feat: montre la répartition des dépenses par tag
Le résumé calculait déjà `by_tag`, la page Dépenses n'en faisait rien : on lisait la dépense par catégorie comptable, jamais par poste. Les deux répartitions se placent côte à côte — même total, deux découpages — et l'évolution mensuelle prend toute la largeur, un axe de temps serré étant le premier à devenir illisible. Les dépenses sans tag y gardent leur part, en gris : c'est le travail de tagging qui reste à faire, pas un trou dans le graphique. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
93
frontend/src/components/analytics/TagChart.vue
Normal file
93
frontend/src/components/analytics/TagChart.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div class="card card-body">
|
||||
<h3 class="card-title block mb-4">Repartition par tag</h3>
|
||||
<div class="h-64">
|
||||
<Doughnut
|
||||
v-if="chartData.labels.length > 0"
|
||||
:data="chartData"
|
||||
:options="chartOptions"
|
||||
/>
|
||||
<div v-else class="h-full flex items-center justify-center text-gray-500 text-sm">
|
||||
Aucune donnee
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { Doughnut } from 'vue-chartjs'
|
||||
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'
|
||||
|
||||
ChartJS.register(ArcElement, Tooltip, Legend)
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
const colors = [
|
||||
'#3B82F6', // blue
|
||||
'#EF4444', // red
|
||||
'#10B981', // green
|
||||
'#F59E0B', // amber
|
||||
'#8B5CF6', // purple
|
||||
'#EC4899', // pink
|
||||
'#06B6D4', // cyan
|
||||
'#84CC16', // lime
|
||||
'#F97316', // orange
|
||||
'#6366F1', // indigo
|
||||
]
|
||||
|
||||
/** Gris pour les depenses sans tag : elles restent visibles - c'est le
|
||||
travail de tagging qui reste a faire - sans se disputer une couleur
|
||||
avec les postes reels. */
|
||||
const COULEUR_SANS_TAG = '#6B7280'
|
||||
|
||||
const parts = computed(() => props.data.filter((p) => (p.total_debit ?? 0) > 0).slice(0, 10))
|
||||
|
||||
const chartData = computed(() => ({
|
||||
labels: parts.value.map((p) => tronquer(p.tag_nom || 'Non taggue')),
|
||||
datasets: [{
|
||||
data: parts.value.map((p) => p.total_debit),
|
||||
backgroundColor: parts.value.map((p, i) =>
|
||||
p.tag_id === null || p.tag_id === undefined ? COULEUR_SANS_TAG : colors[i % colors.length]
|
||||
),
|
||||
borderColor: '#1F2937',
|
||||
borderWidth: 2
|
||||
}]
|
||||
}))
|
||||
|
||||
const chartOptions = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'right',
|
||||
labels: {
|
||||
color: '#9CA3AF',
|
||||
font: { size: 11 },
|
||||
boxWidth: 12,
|
||||
padding: 8
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: (ctx) => {
|
||||
const value = new Intl.NumberFormat('fr-FR', {
|
||||
style: 'currency',
|
||||
currency: 'EUR'
|
||||
}).format(ctx.raw)
|
||||
return ` ${value}`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function tronquer(nom) {
|
||||
return nom.substring(0, 25)
|
||||
}
|
||||
</script>
|
||||
@@ -21,12 +21,16 @@
|
||||
<!-- KPI Cards -->
|
||||
<KpiCards :summary="summary" />
|
||||
|
||||
<!-- Charts Row -->
|
||||
<!-- Les deux repartitions cote a cote : meme total, deux decoupages. -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<CategoryChart :data="summary.by_category || []" />
|
||||
<MonthlyChart :data="summary.by_month || []" />
|
||||
<TagChart :data="summary.by_tag || []" />
|
||||
</div>
|
||||
|
||||
<!-- L'evolution mensuelle prend toute la largeur : un axe de temps
|
||||
serre est le premier a devenir illisible. -->
|
||||
<MonthlyChart :data="summary.by_month || []" />
|
||||
|
||||
<!-- Top Fournisseurs -->
|
||||
<TopFournisseursChart :data="summary.by_fournisseur || []" />
|
||||
|
||||
@@ -45,6 +49,7 @@ import { ref, onMounted } from 'vue'
|
||||
import FilterPanel from '../components/analytics/FilterPanel.vue'
|
||||
import KpiCards from '../components/analytics/KpiCards.vue'
|
||||
import CategoryChart from '../components/analytics/CategoryChart.vue'
|
||||
import TagChart from '../components/analytics/TagChart.vue'
|
||||
import MonthlyChart from '../components/analytics/MonthlyChart.vue'
|
||||
import TopFournisseursChart from '../components/analytics/TopFournisseursChart.vue'
|
||||
import DepensesTable from '../components/analytics/DepensesTable.vue'
|
||||
|
||||
Reference in New Issue
Block a user