Initial commit
This commit is contained in:
703
app/(main)/rapports/page.tsx
Normal file
703
app/(main)/rapports/page.tsx
Normal file
@@ -0,0 +1,703 @@
|
||||
'use client';
|
||||
|
||||
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';
|
||||
|
||||
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: new Date('2024-01-15'),
|
||||
dateFinPrevue: new Date('2024-06-15'),
|
||||
dateFinReelle: new Date('2024-06-20'),
|
||||
statut: 'TERMINE',
|
||||
montantPrevu: 850000,
|
||||
montantReel: 820000,
|
||||
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: new Date('2024-01-01'),
|
||||
dateModification: new Date('2024-01-01'),
|
||||
actif: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
nom: 'Immeuble Commercial',
|
||||
description: 'Bureaux commerciaux',
|
||||
adresse: '789 Boulevard Principal, Abidjan',
|
||||
dateDebut: new Date('2024-03-01'),
|
||||
dateFinPrevue: new Date('2024-12-31'),
|
||||
dateFinReelle: null,
|
||||
statut: 'EN_COURS',
|
||||
montantPrevu: 1200000,
|
||||
montantReel: 600000,
|
||||
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: new Date('2024-02-01'),
|
||||
dateModification: new Date('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;
|
||||
Reference in New Issue
Block a user