337 lines
13 KiB
TypeScript
337 lines
13 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState, useRef } from 'react';
|
|
import { Card } from 'primereact/card';
|
|
import { DataTable } from 'primereact/datatable';
|
|
import { Column } from 'primereact/column';
|
|
import { InputSwitch } from 'primereact/inputswitch';
|
|
import { Button } from 'primereact/button';
|
|
import { Toast } from 'primereact/toast';
|
|
import { Tag } from 'primereact/tag';
|
|
import { Divider } from 'primereact/divider';
|
|
import { Message } from 'primereact/message';
|
|
|
|
interface NotificationRule {
|
|
id: string;
|
|
nom: string;
|
|
description: string;
|
|
type: 'info' | 'warning' | 'success' | 'error';
|
|
evenement: string;
|
|
actif: boolean;
|
|
categorie: string;
|
|
}
|
|
|
|
const NotificationsAutomatiquesPage = () => {
|
|
const toast = useRef<Toast>(null);
|
|
const [rules, setRules] = useState<NotificationRule[]>([
|
|
{
|
|
id: '1',
|
|
nom: 'Nouveau chantier créé',
|
|
description: 'Notification envoyée lors de la création d\'un nouveau chantier',
|
|
type: 'success',
|
|
evenement: 'chantier.created',
|
|
actif: true,
|
|
categorie: 'Chantiers'
|
|
},
|
|
{
|
|
id: '2',
|
|
nom: 'Chantier terminé',
|
|
description: 'Notification envoyée lorsqu\'un chantier est marqué comme terminé',
|
|
type: 'success',
|
|
evenement: 'chantier.completed',
|
|
actif: true,
|
|
categorie: 'Chantiers'
|
|
},
|
|
{
|
|
id: '3',
|
|
nom: 'Dépassement de budget',
|
|
description: 'Alerte envoyée lorsque le budget d\'un chantier est dépassé',
|
|
type: 'warning',
|
|
evenement: 'budget.exceeded',
|
|
actif: true,
|
|
categorie: 'Budget'
|
|
},
|
|
{
|
|
id: '4',
|
|
nom: 'Retard de chantier',
|
|
description: 'Notification envoyée lorsqu\'un chantier est en retard',
|
|
type: 'warning',
|
|
evenement: 'chantier.delayed',
|
|
actif: true,
|
|
categorie: 'Chantiers'
|
|
},
|
|
{
|
|
id: '5',
|
|
nom: 'Phase terminée',
|
|
description: 'Notification envoyée lorsqu\'une phase de chantier est terminée',
|
|
type: 'success',
|
|
evenement: 'phase.completed',
|
|
actif: false,
|
|
categorie: 'Phases'
|
|
},
|
|
{
|
|
id: '6',
|
|
nom: 'Nouveau client',
|
|
description: 'Notification envoyée lors de l\'ajout d\'un nouveau client',
|
|
type: 'info',
|
|
evenement: 'client.created',
|
|
actif: true,
|
|
categorie: 'Clients'
|
|
},
|
|
{
|
|
id: '7',
|
|
nom: 'Stock faible',
|
|
description: 'Alerte envoyée lorsque le stock d\'un matériel est faible',
|
|
type: 'warning',
|
|
evenement: 'stock.low',
|
|
actif: true,
|
|
categorie: 'Stock'
|
|
},
|
|
{
|
|
id: '8',
|
|
nom: 'Erreur système',
|
|
description: 'Notification envoyée en cas d\'erreur système critique',
|
|
type: 'error',
|
|
evenement: 'system.error',
|
|
actif: true,
|
|
categorie: 'Système'
|
|
},
|
|
{
|
|
id: '9',
|
|
nom: 'Facture créée',
|
|
description: 'Notification envoyée lors de la création d\'une facture',
|
|
type: 'info',
|
|
evenement: 'facture.created',
|
|
actif: false,
|
|
categorie: 'Facturation'
|
|
},
|
|
{
|
|
id: '10',
|
|
nom: 'Paiement reçu',
|
|
description: 'Notification envoyée lors de la réception d\'un paiement',
|
|
type: 'success',
|
|
evenement: 'paiement.received',
|
|
actif: true,
|
|
categorie: 'Facturation'
|
|
}
|
|
]);
|
|
|
|
const toggleRule = (ruleId: string) => {
|
|
setRules(rules.map(rule => {
|
|
if (rule.id === ruleId) {
|
|
const newActif = !rule.actif;
|
|
toast.current?.show({
|
|
severity: newActif ? 'success' : 'info',
|
|
summary: newActif ? 'Activée' : 'Désactivée',
|
|
detail: `La règle "${rule.nom}" a été ${newActif ? 'activée' : 'désactivée'}`,
|
|
life: 3000
|
|
});
|
|
return { ...rule, actif: newActif };
|
|
}
|
|
return rule;
|
|
}));
|
|
};
|
|
|
|
const activerTout = () => {
|
|
setRules(rules.map(rule => ({ ...rule, actif: true })));
|
|
toast.current?.show({
|
|
severity: 'success',
|
|
summary: 'Succès',
|
|
detail: 'Toutes les notifications automatiques ont été activées',
|
|
life: 3000
|
|
});
|
|
};
|
|
|
|
const desactiverTout = () => {
|
|
setRules(rules.map(rule => ({ ...rule, actif: false })));
|
|
toast.current?.show({
|
|
severity: 'info',
|
|
summary: 'Désactivé',
|
|
detail: 'Toutes les notifications automatiques ont été désactivées',
|
|
life: 3000
|
|
});
|
|
};
|
|
|
|
const typeBodyTemplate = (rowData: NotificationRule) => {
|
|
const getSeverity = (type: string) => {
|
|
switch (type) {
|
|
case 'info': return 'info';
|
|
case 'warning': return 'warning';
|
|
case 'success': return 'success';
|
|
case 'error': return 'danger';
|
|
default: return 'info';
|
|
}
|
|
};
|
|
|
|
const getIcon = (type: string) => {
|
|
switch (type) {
|
|
case 'info': return 'pi-info-circle';
|
|
case 'warning': return 'pi-exclamation-triangle';
|
|
case 'success': return 'pi-check-circle';
|
|
case 'error': return 'pi-times-circle';
|
|
default: return 'pi-info-circle';
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Tag
|
|
value={rowData.type.toUpperCase()}
|
|
severity={getSeverity(rowData.type)}
|
|
icon={`pi ${getIcon(rowData.type)}`}
|
|
/>
|
|
);
|
|
};
|
|
|
|
const statusBodyTemplate = (rowData: NotificationRule) => {
|
|
return (
|
|
<Tag
|
|
value={rowData.actif ? 'Activée' : 'Désactivée'}
|
|
severity={rowData.actif ? 'success' : 'secondary'}
|
|
/>
|
|
);
|
|
};
|
|
|
|
const actionBodyTemplate = (rowData: NotificationRule) => {
|
|
return (
|
|
<InputSwitch
|
|
checked={rowData.actif}
|
|
onChange={() => toggleRule(rowData.id)}
|
|
tooltip={rowData.actif ? 'Désactiver' : 'Activer'}
|
|
/>
|
|
);
|
|
};
|
|
|
|
const categorieBodyTemplate = (rowData: NotificationRule) => {
|
|
const getCategorieColor = (categorie: string) => {
|
|
switch (categorie) {
|
|
case 'Chantiers': return 'info';
|
|
case 'Budget': return 'warning';
|
|
case 'Phases': return 'success';
|
|
case 'Clients': return 'info';
|
|
case 'Stock': return 'warning';
|
|
case 'Système': return 'danger';
|
|
case 'Facturation': return 'success';
|
|
default: return 'info';
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Tag value={rowData.categorie} severity={getCategorieColor(rowData.categorie)} />
|
|
);
|
|
};
|
|
|
|
const header = (
|
|
<div className="flex flex-column md:flex-row md:justify-content-between md:align-items-center">
|
|
<h5 className="m-0">Règles de notifications automatiques</h5>
|
|
<div className="flex gap-2 mt-2 md:mt-0">
|
|
<Button
|
|
label="Tout activer"
|
|
icon="pi pi-check-square"
|
|
className="p-button-text p-button-success"
|
|
onClick={activerTout}
|
|
/>
|
|
<Button
|
|
label="Tout désactiver"
|
|
icon="pi pi-times"
|
|
className="p-button-text p-button-secondary"
|
|
onClick={desactiverTout}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
const activeCount = rules.filter(r => r.actif).length;
|
|
const totalCount = rules.length;
|
|
|
|
return (
|
|
<div className="grid">
|
|
<div className="col-12">
|
|
<h2>Notifications Automatiques</h2>
|
|
<p className="text-color-secondary">
|
|
Configurez les notifications automatiques envoyées lors d'événements spécifiques
|
|
</p>
|
|
</div>
|
|
|
|
<div className="col-12">
|
|
<Toast ref={toast} />
|
|
|
|
<Card>
|
|
<Message
|
|
severity="info"
|
|
content={
|
|
<div>
|
|
<div className="font-bold mb-2">Configuration des notifications automatiques</div>
|
|
<div className="text-sm">
|
|
{activeCount} règle{activeCount > 1 ? 's' : ''} activée{activeCount > 1 ? 's' : ''} sur {totalCount}
|
|
</div>
|
|
</div>
|
|
}
|
|
className="mb-4"
|
|
/>
|
|
|
|
<DataTable
|
|
value={rules}
|
|
dataKey="id"
|
|
paginator
|
|
rows={10}
|
|
rowsPerPageOptions={[5, 10, 25]}
|
|
className="datatable-responsive"
|
|
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown"
|
|
currentPageReportTemplate="Affichage de {first} à {last} sur {totalRecords} règles"
|
|
emptyMessage="Aucune règle configurée."
|
|
header={header}
|
|
responsiveLayout="scroll"
|
|
>
|
|
<Column field="nom" header="Nom" sortable style={{ minWidth: '15rem' }} />
|
|
<Column field="description" header="Description" style={{ minWidth: '20rem' }} />
|
|
<Column field="categorie" header="Catégorie" body={categorieBodyTemplate} sortable style={{ minWidth: '10rem' }} />
|
|
<Column field="type" header="Type" body={typeBodyTemplate} sortable style={{ minWidth: '8rem' }} />
|
|
<Column field="actif" header="Statut" body={statusBodyTemplate} sortable style={{ minWidth: '8rem' }} />
|
|
<Column header="Activer/Désactiver" body={actionBodyTemplate} style={{ minWidth: '10rem' }} />
|
|
</DataTable>
|
|
|
|
<Divider />
|
|
|
|
<div className="text-sm text-color-secondary">
|
|
<h5 className="mt-0 mb-3">Informations sur les notifications automatiques</h5>
|
|
<div className="grid">
|
|
<div className="col-12 md:col-6">
|
|
<h6 className="text-color">Événements de chantiers</h6>
|
|
<ul className="pl-3 mt-2">
|
|
<li>Création de nouveau chantier</li>
|
|
<li>Chantier terminé</li>
|
|
<li>Retard détecté</li>
|
|
</ul>
|
|
</div>
|
|
<div className="col-12 md:col-6">
|
|
<h6 className="text-color">Événements de budget</h6>
|
|
<ul className="pl-3 mt-2">
|
|
<li>Dépassement de budget</li>
|
|
<li>Seuil d'alerte atteint</li>
|
|
</ul>
|
|
</div>
|
|
<div className="col-12 md:col-6">
|
|
<h6 className="text-color">Événements de stock</h6>
|
|
<ul className="pl-3 mt-2">
|
|
<li>Stock faible</li>
|
|
<li>Rupture de stock</li>
|
|
</ul>
|
|
</div>
|
|
<div className="col-12 md:col-6">
|
|
<h6 className="text-color">Événements système</h6>
|
|
<ul className="pl-3 mt-2">
|
|
<li>Erreurs critiques</li>
|
|
<li>Mises à jour importantes</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default NotificationsAutomatiquesPage;
|
|
|