Initial commit

This commit is contained in:
dahoud
2025-10-01 01:39:07 +00:00
commit b430bf3b96
826 changed files with 255287 additions and 0 deletions

View File

@@ -0,0 +1,137 @@
/**
* Composant widget d'alertes (factures en retard, devis expirant)
*/
import React from 'react';
import { Card } from 'primereact/card';
import { Message } from 'primereact/message';
import { Button } from 'primereact/button';
import { Skeleton } from 'primereact/skeleton';
import { FactureEnRetard, DevisEnAttente } from '../../types/btp';
interface AlertsWidgetProps {
facturesEnRetard: FactureEnRetard[];
devisEnAttente: DevisEnAttente[];
loading?: boolean;
onViewFacture?: (id: string) => void;
onViewDevis?: (id: string) => void;
}
const AlertsWidget: React.FC<AlertsWidgetProps> = ({
facturesEnRetard,
devisEnAttente,
loading = false,
onViewFacture,
onViewDevis
}) => {
const getAlertSeverity = (jours: number) => {
if (jours <= 3) return 'warn';
if (jours <= 7) return 'info';
return 'error';
};
const header = (
<div className="flex align-items-center">
<i className="pi pi-exclamation-triangle text-orange-500 mr-2" />
<h5 className="m-0">Alertes</h5>
</div>
);
if (loading) {
return (
<Card>
<div className="flex align-items-center mb-3">
<Skeleton width="1.5rem" height="1.5rem" className="mr-2" />
<Skeleton width="4rem" height="1.5rem" />
</div>
<div className="space-y-2">
{[...Array(3)].map((_, i) => (
<div key={i} className="p-3 border-1 border-200 border-round">
<Skeleton width="100%" height="1rem" className="mb-2" />
<Skeleton width="60%" height="0.8rem" />
</div>
))}
</div>
</Card>
);
}
const hasAlerts = facturesEnRetard.length > 0 || devisEnAttente.length > 0;
return (
<Card>
<div className="flex align-items-center mb-3">
<i className="pi pi-exclamation-triangle text-orange-500 mr-2" />
<h5 className="m-0">Alertes</h5>
</div>
{!hasAlerts ? (
<Message
severity="success"
text="Aucune alerte pour le moment"
className="w-full"
/>
) : (
<div className="space-y-3">
{/* Factures en retard */}
{facturesEnRetard.map((facture) => (
<div
key={facture.id}
className="flex align-items-center justify-content-between p-3 border-1 border-red-200 border-round bg-red-50"
>
<div className="flex-1">
<div className="font-medium text-900 mb-1">
Facture {facture.numero} en retard
</div>
<div className="text-500 text-sm">
{facture.client} {facture.montantTTC.toLocaleString()}
{facture.joursRetard} jour{facture.joursRetard > 1 ? 's' : ''} de retard
</div>
</div>
{onViewFacture && (
<Button
icon="pi pi-eye"
className="p-button-text p-button-sm"
onClick={() => onViewFacture(facture.id)}
tooltip="Voir la facture"
/>
)}
</div>
))}
{/* Devis expirant bientôt */}
{devisEnAttente.map((devis) => (
<div
key={devis.id}
className={`flex align-items-center justify-content-between p-3 border-1 border-round ${
devis.joursRestants <= 3
? 'border-red-200 bg-red-50'
: 'border-orange-200 bg-orange-50'
}`}
>
<div className="flex-1">
<div className="font-medium text-900 mb-1">
Devis {devis.numero} expire bientôt
</div>
<div className="text-500 text-sm">
{devis.client} {devis.montantTTC.toLocaleString()}
{devis.joursRestants} jour{devis.joursRestants > 1 ? 's' : ''} restant{devis.joursRestants > 1 ? 's' : ''}
</div>
</div>
{onViewDevis && (
<Button
icon="pi pi-eye"
className="p-button-text p-button-sm"
onClick={() => onViewDevis(devis.id)}
tooltip="Voir le devis"
/>
)}
</div>
))}
</div>
)}
</Card>
);
};
export default AlertsWidget;