feat: add analytics
This commit is contained in:
143
frontend/src/pages/AnalyticsPage.vue
Normal file
143
frontend/src/pages/AnalyticsPage.vue
Normal 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>
|
||||
Reference in New Issue
Block a user