- Correction des erreurs TypeScript dans userService.ts et workflowTester.ts - Ajout des propriétés manquantes aux objets User mockés - Conversion des dates de string vers objets Date - Correction des appels asynchrones et des types incompatibles - Ajout de dynamic rendering pour résoudre les erreurs useSearchParams - Enveloppement de useSearchParams dans Suspense boundary - Configuration de force-dynamic au niveau du layout principal Build réussi: 126 pages générées avec succès 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
710 lines
28 KiB
TypeScript
710 lines
28 KiB
TypeScript
'use client';
|
|
export const dynamic = 'force-dynamic';
|
|
|
|
|
|
import React, { useState, useEffect, useRef } from 'react';
|
|
import { Card } from 'primereact/card';
|
|
import { Button } from 'primereact/button';
|
|
import { Calendar } from 'primereact/calendar';
|
|
import { Chart } from 'primereact/chart';
|
|
import { DataTable } from 'primereact/datatable';
|
|
import { Column } from 'primereact/column';
|
|
import { Toast } from 'primereact/toast';
|
|
import { Toolbar } from 'primereact/toolbar';
|
|
import { Dropdown } from 'primereact/dropdown';
|
|
import { TabView, TabPanel } from 'primereact/tabview';
|
|
import { Knob } from 'primereact/knob';
|
|
import { ProgressBar } from 'primereact/progressbar';
|
|
import { Badge } from 'primereact/badge';
|
|
import { chantierService, clientService, factureService, devisService } from '../../../services/api';
|
|
import { formatCurrency, formatDate } from '../../../utils/formatters';
|
|
import type { Chantier, Client, Facture, Devis } from '../../../types/btp';
|
|
import { StatutChantier } from '../../../types/btp';
|
|
|
|
interface ReportData {
|
|
chantiers: Chantier[];
|
|
clients: Client[];
|
|
factures: Facture[];
|
|
devis: Devis[];
|
|
}
|
|
|
|
interface ChantierStats {
|
|
total: number;
|
|
planifies: number;
|
|
enCours: number;
|
|
termines: number;
|
|
annules: number;
|
|
enRetard: number;
|
|
}
|
|
|
|
interface FinancialStats {
|
|
chiffreAffaires: number;
|
|
benefices: number;
|
|
facturesEnAttente: number;
|
|
devisEnAttente: number;
|
|
tauxReussite: number;
|
|
}
|
|
|
|
const RapportsPage = () => {
|
|
const [reportData, setReportData] = useState<ReportData>({
|
|
chantiers: [],
|
|
clients: [],
|
|
factures: [],
|
|
devis: []
|
|
});
|
|
const [loading, setLoading] = useState(true);
|
|
const [dateDebut, setDateDebut] = useState<Date>(new Date(new Date().getFullYear(), 0, 1));
|
|
const [dateFin, setDateFin] = useState<Date>(new Date());
|
|
const [selectedPeriod, setSelectedPeriod] = useState('annee');
|
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
const [chantierStats, setChantierStats] = useState<ChantierStats>({
|
|
total: 0,
|
|
planifies: 0,
|
|
enCours: 0,
|
|
termines: 0,
|
|
annules: 0,
|
|
enRetard: 0
|
|
});
|
|
const [financialStats, setFinancialStats] = useState<FinancialStats>({
|
|
chiffreAffaires: 0,
|
|
benefices: 0,
|
|
facturesEnAttente: 0,
|
|
devisEnAttente: 0,
|
|
tauxReussite: 0
|
|
});
|
|
const [chartData, setChartData] = useState<any>({});
|
|
const [chartOptions, setChartOptions] = useState<any>({});
|
|
const toast = useRef<Toast>(null);
|
|
|
|
const periodOptions = [
|
|
{ label: 'Cette semaine', value: 'semaine' },
|
|
{ label: 'Ce mois', value: 'mois' },
|
|
{ label: 'Ce trimestre', value: 'trimestre' },
|
|
{ label: 'Cette année', value: 'annee' },
|
|
{ label: 'Personnalisé', value: 'custom' }
|
|
];
|
|
|
|
useEffect(() => {
|
|
loadReportData();
|
|
}, [dateDebut, dateFin]);
|
|
|
|
useEffect(() => {
|
|
calculateStats();
|
|
generateChartData();
|
|
}, [reportData]);
|
|
|
|
const loadReportData = async () => {
|
|
try {
|
|
setLoading(true);
|
|
|
|
// Simuler des données de rapport
|
|
const mockChantiers: Chantier[] = [
|
|
{
|
|
id: '1',
|
|
nom: 'Résidence Les Palmiers',
|
|
description: 'Construction de 20 appartements',
|
|
adresse: '123 Rue des Palmiers, Abidjan',
|
|
dateDebut: '2024-01-15',
|
|
dateFinPrevue: '2024-06-15',
|
|
dateFinReelle: '2024-06-20',
|
|
statut: StatutChantier.TERMINE,
|
|
montantPrevu: 850000,
|
|
montantReel: 820000,
|
|
dateCreation: '2024-01-01',
|
|
dateModification: '2024-06-20',
|
|
actif: true,
|
|
client: {
|
|
id: '1',
|
|
nom: 'Kouassi',
|
|
prenom: 'Jean',
|
|
email: 'jean.kouassi@email.com',
|
|
telephone: '07 12 34 56 78',
|
|
adresse: '456 Rue de la Paix',
|
|
codePostal: '00225',
|
|
ville: 'Abidjan',
|
|
entreprise: 'Entreprise Kouassi',
|
|
dateCreation: '2024-01-01',
|
|
dateModification: '2024-01-01',
|
|
actif: true
|
|
}
|
|
},
|
|
{
|
|
id: '2',
|
|
nom: 'Immeuble Commercial',
|
|
description: 'Bureaux commerciaux',
|
|
adresse: '789 Boulevard Principal, Abidjan',
|
|
dateDebut: '2024-03-01',
|
|
dateFinPrevue: '2024-12-31',
|
|
dateFinReelle: null,
|
|
statut: StatutChantier.EN_COURS,
|
|
montantPrevu: 1200000,
|
|
montantReel: 600000,
|
|
dateCreation: '2024-02-01',
|
|
dateModification: '2024-10-15',
|
|
actif: true,
|
|
client: {
|
|
id: '2',
|
|
nom: 'Traoré',
|
|
prenom: 'Fatou',
|
|
email: 'fatou.traore@email.com',
|
|
telephone: '07 98 76 54 32',
|
|
adresse: '321 Avenue du Commerce',
|
|
codePostal: '00225',
|
|
ville: 'Abidjan',
|
|
entreprise: 'Traoré SARL',
|
|
dateCreation: '2024-02-01',
|
|
dateModification: '2024-02-01',
|
|
actif: true
|
|
}
|
|
}
|
|
];
|
|
|
|
setReportData({
|
|
chantiers: mockChantiers,
|
|
clients: [],
|
|
factures: [],
|
|
devis: []
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors du chargement des données:', error);
|
|
toast.current?.show({
|
|
severity: 'error',
|
|
summary: 'Erreur',
|
|
detail: 'Impossible de charger les données',
|
|
life: 3000
|
|
});
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const calculateStats = () => {
|
|
const { chantiers } = reportData;
|
|
|
|
const stats: ChantierStats = {
|
|
total: chantiers.length,
|
|
planifies: chantiers.filter(c => c.statut === 'PLANIFIE').length,
|
|
enCours: chantiers.filter(c => c.statut === 'EN_COURS').length,
|
|
termines: chantiers.filter(c => c.statut === 'TERMINE').length,
|
|
annules: chantiers.filter(c => c.statut === 'ANNULE').length,
|
|
enRetard: chantiers.filter(c => {
|
|
if (!c.dateFinPrevue) return false;
|
|
const now = new Date();
|
|
return new Date(c.dateFinPrevue) < now && c.statut !== 'TERMINE';
|
|
}).length
|
|
};
|
|
|
|
setChantierStats(stats);
|
|
|
|
const financialStats: FinancialStats = {
|
|
chiffreAffaires: chantiers.reduce((sum, c) => sum + (c.montantReel || 0), 0),
|
|
benefices: chantiers.reduce((sum, c) => sum + ((c.montantReel || 0) - (c.montantPrevu || 0)), 0),
|
|
facturesEnAttente: 0,
|
|
devisEnAttente: 0,
|
|
tauxReussite: Math.round((stats.termines / Math.max(stats.total, 1)) * 100)
|
|
};
|
|
|
|
setFinancialStats(financialStats);
|
|
};
|
|
|
|
const generateChartData = () => {
|
|
const { chantiers } = reportData;
|
|
|
|
// Graphique en secteurs - Statuts des chantiers
|
|
const pieData = {
|
|
labels: ['Planifiés', 'En cours', 'Terminés', 'Annulés'],
|
|
datasets: [
|
|
{
|
|
data: [
|
|
chantierStats.planifies,
|
|
chantierStats.enCours,
|
|
chantierStats.termines,
|
|
chantierStats.annules
|
|
],
|
|
backgroundColor: [
|
|
'#3B82F6',
|
|
'#10B981',
|
|
'#6B7280',
|
|
'#EF4444'
|
|
],
|
|
borderColor: [
|
|
'#1D4ED8',
|
|
'#047857',
|
|
'#374151',
|
|
'#DC2626'
|
|
],
|
|
borderWidth: 1
|
|
}
|
|
]
|
|
};
|
|
|
|
// Graphique en barres - Évolution mensuelle
|
|
const barData = {
|
|
labels: ['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Jun', 'Jul', 'Aoû', 'Sep', 'Oct', 'Nov', 'Déc'],
|
|
datasets: [
|
|
{
|
|
label: 'Chiffre d\'affaires',
|
|
data: [120000, 150000, 180000, 200000, 250000, 300000, 280000, 320000, 350000, 380000, 400000, 450000],
|
|
backgroundColor: '#3B82F6',
|
|
borderColor: '#1D4ED8',
|
|
borderWidth: 1
|
|
},
|
|
{
|
|
label: 'Bénéfices',
|
|
data: [20000, 25000, 30000, 35000, 40000, 45000, 42000, 48000, 52000, 55000, 58000, 62000],
|
|
backgroundColor: '#10B981',
|
|
borderColor: '#047857',
|
|
borderWidth: 1
|
|
}
|
|
]
|
|
};
|
|
|
|
setChartData({ pie: pieData, bar: barData });
|
|
|
|
const options = {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: {
|
|
position: 'bottom'
|
|
}
|
|
}
|
|
};
|
|
|
|
setChartOptions(options);
|
|
};
|
|
|
|
const onPeriodChange = (e: any) => {
|
|
setSelectedPeriod(e.value);
|
|
|
|
const now = new Date();
|
|
let debut = new Date();
|
|
|
|
switch (e.value) {
|
|
case 'semaine':
|
|
debut = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7);
|
|
break;
|
|
case 'mois':
|
|
debut = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
break;
|
|
case 'trimestre':
|
|
debut = new Date(now.getFullYear(), Math.floor(now.getMonth() / 3) * 3, 1);
|
|
break;
|
|
case 'annee':
|
|
debut = new Date(now.getFullYear(), 0, 1);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
setDateDebut(debut);
|
|
setDateFin(now);
|
|
};
|
|
|
|
const exportPDF = () => {
|
|
toast.current?.show({
|
|
severity: 'info',
|
|
summary: 'Export PDF',
|
|
detail: 'Génération du rapport PDF...',
|
|
life: 3000
|
|
});
|
|
};
|
|
|
|
const exportExcel = () => {
|
|
toast.current?.show({
|
|
severity: 'info',
|
|
summary: 'Export Excel',
|
|
detail: 'Génération du rapport Excel...',
|
|
life: 3000
|
|
});
|
|
};
|
|
|
|
const leftToolbarTemplate = () => {
|
|
return (
|
|
<div className="flex flex-wrap gap-2 align-items-center">
|
|
<Dropdown
|
|
value={selectedPeriod}
|
|
options={periodOptions}
|
|
onChange={onPeriodChange}
|
|
placeholder="Sélectionner une période"
|
|
/>
|
|
{selectedPeriod === 'custom' && (
|
|
<>
|
|
<Calendar
|
|
value={dateDebut}
|
|
onChange={(e) => setDateDebut(e.value || new Date())}
|
|
dateFormat="dd/mm/yy"
|
|
placeholder="Date début"
|
|
/>
|
|
<Calendar
|
|
value={dateFin}
|
|
onChange={(e) => setDateFin(e.value || new Date())}
|
|
dateFormat="dd/mm/yy"
|
|
placeholder="Date fin"
|
|
/>
|
|
</>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const rightToolbarTemplate = () => {
|
|
return (
|
|
<div className="flex flex-wrap gap-2">
|
|
<Button
|
|
label="PDF"
|
|
icon="pi pi-file-pdf"
|
|
severity="danger"
|
|
onClick={exportPDF}
|
|
/>
|
|
<Button
|
|
label="Excel"
|
|
icon="pi pi-file-excel"
|
|
severity="success"
|
|
onClick={exportExcel}
|
|
/>
|
|
<Button
|
|
label="Actualiser"
|
|
icon="pi pi-refresh"
|
|
onClick={loadReportData}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const renderVueEnsemble = () => {
|
|
return (
|
|
<div className="grid">
|
|
{/* Indicateurs principaux */}
|
|
<div className="col-12 md:col-3">
|
|
<Card className="text-center">
|
|
<div className="text-6xl text-primary mb-2">
|
|
<i className="pi pi-building"></i>
|
|
</div>
|
|
<div className="text-3xl font-bold text-primary mb-1">
|
|
{chantierStats.total}
|
|
</div>
|
|
<div className="text-lg text-color-secondary">
|
|
Chantiers Total
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
|
|
<div className="col-12 md:col-3">
|
|
<Card className="text-center">
|
|
<div className="text-6xl text-green-500 mb-2">
|
|
<i className="pi pi-check-circle"></i>
|
|
</div>
|
|
<div className="text-3xl font-bold text-green-500 mb-1">
|
|
{chantierStats.termines}
|
|
</div>
|
|
<div className="text-lg text-color-secondary">
|
|
Chantiers Terminés
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
|
|
<div className="col-12 md:col-3">
|
|
<Card className="text-center">
|
|
<div className="text-6xl text-yellow-500 mb-2">
|
|
<i className="pi pi-clock"></i>
|
|
</div>
|
|
<div className="text-3xl font-bold text-yellow-500 mb-1">
|
|
{chantierStats.enCours}
|
|
</div>
|
|
<div className="text-lg text-color-secondary">
|
|
En Cours
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
|
|
<div className="col-12 md:col-3">
|
|
<Card className="text-center">
|
|
<div className="text-6xl text-red-500 mb-2">
|
|
<i className="pi pi-exclamation-triangle"></i>
|
|
</div>
|
|
<div className="text-3xl font-bold text-red-500 mb-1">
|
|
{chantierStats.enRetard}
|
|
</div>
|
|
<div className="text-lg text-color-secondary">
|
|
En Retard
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Indicateurs financiers */}
|
|
<div className="col-12 md:col-6">
|
|
<Card title="Performance Financière">
|
|
<div className="grid">
|
|
<div className="col-12 md:col-6">
|
|
<div className="text-center">
|
|
<div className="text-2xl font-bold text-primary mb-2">
|
|
{formatCurrency(financialStats.chiffreAffaires)}
|
|
</div>
|
|
<div className="text-color-secondary">
|
|
Chiffre d'Affaires
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="col-12 md:col-6">
|
|
<div className="text-center">
|
|
<div className="text-2xl font-bold text-green-500 mb-2">
|
|
{formatCurrency(financialStats.benefices)}
|
|
</div>
|
|
<div className="text-color-secondary">
|
|
Bénéfices
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
|
|
<div className="col-12 md:col-6">
|
|
<Card title="Taux de Réussite">
|
|
<div className="text-center">
|
|
<Knob
|
|
value={financialStats.tauxReussite}
|
|
size={150}
|
|
valueColor="#10B981"
|
|
rangeColor="#E5E7EB"
|
|
textColor="#374151"
|
|
/>
|
|
<div className="text-lg text-color-secondary mt-2">
|
|
Projets Terminés avec Succès
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Graphiques */}
|
|
<div className="col-12 md:col-6">
|
|
<Card title="Répartition des Chantiers">
|
|
<Chart
|
|
type="pie"
|
|
data={chartData.pie}
|
|
options={chartOptions}
|
|
style={{ height: '300px' }}
|
|
/>
|
|
</Card>
|
|
</div>
|
|
|
|
<div className="col-12 md:col-6">
|
|
<Card title="Évolution Mensuelle">
|
|
<Chart
|
|
type="bar"
|
|
data={chartData.bar}
|
|
options={chartOptions}
|
|
style={{ height: '300px' }}
|
|
/>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const renderRapportChantiers = () => {
|
|
return (
|
|
<div className="grid">
|
|
<div className="col-12">
|
|
<Card title="Rapport Détaillé des Chantiers">
|
|
<DataTable
|
|
value={reportData.chantiers}
|
|
paginator
|
|
rows={10}
|
|
dataKey="id"
|
|
loading={loading}
|
|
emptyMessage="Aucun chantier trouvé"
|
|
>
|
|
<Column field="nom" header="Nom" sortable />
|
|
<Column
|
|
field="client"
|
|
header="Client"
|
|
body={(rowData) => rowData.client ?
|
|
`${rowData.client.prenom} ${rowData.client.nom}` :
|
|
'Non défini'
|
|
}
|
|
/>
|
|
<Column
|
|
field="dateDebut"
|
|
header="Date Début"
|
|
body={(rowData) => formatDate(rowData.dateDebut)}
|
|
sortable
|
|
/>
|
|
<Column
|
|
field="dateFinPrevue"
|
|
header="Date Fin Prévue"
|
|
body={(rowData) => formatDate(rowData.dateFinPrevue)}
|
|
sortable
|
|
/>
|
|
<Column
|
|
field="statut"
|
|
header="Statut"
|
|
body={(rowData) => (
|
|
<Badge
|
|
value={rowData.statut}
|
|
severity={
|
|
rowData.statut === 'TERMINE' ? 'success' :
|
|
rowData.statut === 'EN_COURS' ? 'info' :
|
|
rowData.statut === 'PLANIFIE' ? 'warning' : 'danger'
|
|
}
|
|
/>
|
|
)}
|
|
/>
|
|
<Column
|
|
field="montantPrevu"
|
|
header="Montant Prévu"
|
|
body={(rowData) => formatCurrency(rowData.montantPrevu)}
|
|
sortable
|
|
/>
|
|
<Column
|
|
field="montantReel"
|
|
header="Montant Réel"
|
|
body={(rowData) => formatCurrency(rowData.montantReel || 0)}
|
|
sortable
|
|
/>
|
|
<Column
|
|
field="avancement"
|
|
header="Avancement"
|
|
body={(rowData) => {
|
|
const progress = rowData.statut === 'TERMINE' ? 100 :
|
|
rowData.statut === 'EN_COURS' ? 50 :
|
|
rowData.statut === 'PLANIFIE' ? 0 : 0;
|
|
return <ProgressBar value={progress} />;
|
|
}}
|
|
/>
|
|
</DataTable>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const renderRapportFinancier = () => {
|
|
return (
|
|
<div className="grid">
|
|
<div className="col-12 md:col-4">
|
|
<Card title="Revenus">
|
|
<div className="text-center">
|
|
<div className="text-3xl font-bold text-green-500 mb-2">
|
|
{formatCurrency(financialStats.chiffreAffaires)}
|
|
</div>
|
|
<div className="text-color-secondary">
|
|
Total des revenus
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
|
|
<div className="col-12 md:col-4">
|
|
<Card title="Bénéfices">
|
|
<div className="text-center">
|
|
<div className="text-3xl font-bold text-primary mb-2">
|
|
{formatCurrency(financialStats.benefices)}
|
|
</div>
|
|
<div className="text-color-secondary">
|
|
Total des bénéfices
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
|
|
<div className="col-12 md:col-4">
|
|
<Card title="Marge">
|
|
<div className="text-center">
|
|
<div className="text-3xl font-bold text-yellow-500 mb-2">
|
|
{financialStats.chiffreAffaires > 0 ?
|
|
`${Math.round((financialStats.benefices / financialStats.chiffreAffaires) * 100)}%` :
|
|
'0%'
|
|
}
|
|
</div>
|
|
<div className="text-color-secondary">
|
|
Marge bénéficiaire
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
|
|
<div className="col-12">
|
|
<Card title="Analyse Financière Détaillée">
|
|
<DataTable
|
|
value={reportData.chantiers}
|
|
paginator
|
|
rows={10}
|
|
dataKey="id"
|
|
loading={loading}
|
|
emptyMessage="Aucune donnée financière"
|
|
>
|
|
<Column field="nom" header="Chantier" sortable />
|
|
<Column
|
|
field="montantPrevu"
|
|
header="Budget Initial"
|
|
body={(rowData) => formatCurrency(rowData.montantPrevu)}
|
|
sortable
|
|
/>
|
|
<Column
|
|
field="montantReel"
|
|
header="Coût Réel"
|
|
body={(rowData) => formatCurrency(rowData.montantReel || 0)}
|
|
sortable
|
|
/>
|
|
<Column
|
|
field="ecart"
|
|
header="Écart"
|
|
body={(rowData) => {
|
|
const ecart = (rowData.montantReel || 0) - rowData.montantPrevu;
|
|
return (
|
|
<span className={ecart >= 0 ? 'text-green-500' : 'text-red-500'}>
|
|
{formatCurrency(ecart)}
|
|
</span>
|
|
);
|
|
}}
|
|
/>
|
|
<Column
|
|
field="rentabilite"
|
|
header="Rentabilité"
|
|
body={(rowData) => {
|
|
const rentabilite = rowData.montantPrevu > 0 ?
|
|
(((rowData.montantReel || 0) - rowData.montantPrevu) / rowData.montantPrevu * 100) : 0;
|
|
return (
|
|
<span className={rentabilite >= 0 ? 'text-green-500' : 'text-red-500'}>
|
|
{rentabilite.toFixed(1)}%
|
|
</span>
|
|
);
|
|
}}
|
|
/>
|
|
</DataTable>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className="grid">
|
|
<div className="col-12">
|
|
<Card>
|
|
<Toast ref={toast} />
|
|
<Toolbar className="mb-4" left={leftToolbarTemplate} right={rightToolbarTemplate} />
|
|
|
|
<TabView activeIndex={activeIndex} onTabChange={(e) => setActiveIndex(e.index)}>
|
|
<TabPanel header="Vue d'Ensemble" leftIcon="pi pi-chart-line mr-2">
|
|
{renderVueEnsemble()}
|
|
</TabPanel>
|
|
|
|
<TabPanel header="Rapport Chantiers" leftIcon="pi pi-building mr-2">
|
|
{renderRapportChantiers()}
|
|
</TabPanel>
|
|
|
|
<TabPanel header="Rapport Financier" leftIcon="pi pi-money-bill mr-2">
|
|
{renderRapportFinancier()}
|
|
</TabPanel>
|
|
</TabView>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default RapportsPage; |