feat: add analytics

This commit is contained in:
2026-01-19 21:40:10 +01:00
parent 0fd6bdaeb3
commit aac4d194e3
19 changed files with 1940 additions and 347 deletions

View File

@@ -0,0 +1,143 @@
<template>
<div class="flex-1 overflow-auto p-6 bg-gray-800">
<div class="max-w-7xl mx-auto space-y-6">
<!-- Header -->
<div class="flex items-center justify-between">
<div>
<h1 class="text-xl font-semibold text-white">Analyse des depenses</h1>
<p class="text-sm text-gray-400 mt-1">
Visualisez et filtrez les depenses de vos immeubles
</p>
</div>
<div v-if="lastUpdate" class="text-xs text-gray-500">
Derniere mise a jour: {{ lastUpdate }}
</div>
</div>
<!-- Filters -->
<FilterPanel @filter-change="onFiltersChange" />
<!-- KPI Cards -->
<KpiCards :summary="summary" />
<!-- Charts Row -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<CategoryChart :data="summary.by_category || []" />
<MonthlyChart :data="summary.by_month || []" />
</div>
<!-- Top Fournisseurs -->
<TopFournisseursChart :data="summary.by_fournisseur || []" />
<!-- Depenses Table -->
<DepensesTable
:depenses="depenses"
:is-loading="isLoadingDepenses"
/>
</div>
</div>
</template>
<script setup>
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 MonthlyChart from '../components/analytics/MonthlyChart.vue'
import TopFournisseursChart from '../components/analytics/TopFournisseursChart.vue'
import DepensesTable from '../components/analytics/DepensesTable.vue'
// Current filters state
let currentFilters = {
immeuble_id: null,
lot_id: null,
categorie: null,
tag_id: null,
fournisseur: null,
date_debut: null,
date_fin: null
}
const summary = ref({
total_count: 0,
total_debit: 0,
total_credit: 0,
total_tva: 0,
total_locatif: 0,
total_deductible: 0,
by_category: [],
by_tag: [],
by_month: [],
by_fournisseur: []
})
const depenses = ref([])
const isLoadingDepenses = ref(false)
const lastUpdate = ref(null)
// Debounce timer
let debounceTimer = null
function onFiltersChange(newFilters) {
currentFilters = { ...newFilters }
// Debounce
if (debounceTimer) clearTimeout(debounceTimer)
debounceTimer = setTimeout(() => {
loadData()
}, 300)
}
function buildQueryParams() {
const params = new URLSearchParams()
if (currentFilters.immeuble_id !== null) params.append('immeuble_id', currentFilters.immeuble_id)
if (currentFilters.lot_id !== null) params.append('lot_id', currentFilters.lot_id)
if (currentFilters.categorie !== null) params.append('categorie', currentFilters.categorie)
if (currentFilters.tag_id !== null) params.append('tag_id', currentFilters.tag_id)
if (currentFilters.fournisseur) params.append('fournisseur', currentFilters.fournisseur)
if (currentFilters.date_debut) params.append('date_debut', currentFilters.date_debut)
if (currentFilters.date_fin) params.append('date_fin', currentFilters.date_fin)
return params.toString()
}
async function loadData() {
const queryString = buildQueryParams()
const summaryUrl = queryString ? `/api/analytics/depenses/summary?${queryString}` : '/api/analytics/depenses/summary'
const depensesUrl = queryString ? `/api/analytics/depenses?${queryString}&limit=500` : '/api/analytics/depenses?limit=500'
console.log('Loading data with filters:', currentFilters)
console.log('Summary URL:', summaryUrl)
isLoadingDepenses.value = true
try {
const [summaryRes, depensesRes] = await Promise.all([
fetch(summaryUrl),
fetch(depensesUrl)
])
if (summaryRes.ok) {
summary.value = await summaryRes.json()
console.log('Summary loaded:', summary.value.total_count, 'depenses')
}
if (depensesRes.ok) {
depenses.value = await depensesRes.json()
}
lastUpdate.value = new Date().toLocaleTimeString('fr-FR')
} catch (err) {
console.error('Failed to load analytics data:', err)
} finally {
isLoadingDepenses.value = false
}
}
onMounted(() => {
loadData()
})
</script>