Files
btpxpress-frontend/app/(main)/notifications/broadcast/page.tsx
dahoud a8825a058b Fix: Corriger toutes les erreurs de build du frontend
- 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>
2025-10-18 13:23:08 +00:00

286 lines
12 KiB
TypeScript

'use client';
export const dynamic = 'force-dynamic';
import React, { useState, useRef } from 'react';
import { Card } from 'primereact/card';
import { InputText } from 'primereact/inputtext';
import { InputTextarea } from 'primereact/inputtextarea';
import { Dropdown } from 'primereact/dropdown';
import { Button } from 'primereact/button';
import { Toast } from 'primereact/toast';
import { Divider } from 'primereact/divider';
import { Message } from 'primereact/message';
import notificationService from '../../../../services/notificationService';
const NotificationsBroadcastPage = () => {
const [titre, setTitre] = useState('');
const [message, setMessage] = useState('');
const [type, setType] = useState<'info' | 'warning' | 'success' | 'error'>('info');
const [loading, setLoading] = useState(false);
const [submitted, setSubmitted] = useState(false);
const toast = useRef<Toast>(null);
const types = [
{ label: 'Information', value: 'info', icon: 'pi-info-circle', color: '#3B82F6' },
{ label: 'Avertissement', value: 'warning', icon: 'pi-exclamation-triangle', color: '#F59E0B' },
{ label: 'Succès', value: 'success', icon: 'pi-check-circle', color: '#10B981' },
{ label: 'Erreur', value: 'error', icon: 'pi-times-circle', color: '#EF4444' }
];
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setSubmitted(true);
if (!titre.trim() || !message.trim()) {
toast.current?.show({
severity: 'warn',
summary: 'Attention',
detail: 'Veuillez remplir tous les champs obligatoires',
life: 3000
});
return;
}
try {
setLoading(true);
await notificationService.broadcastNotification({
type,
titre: titre.trim(),
message: message.trim()
});
toast.current?.show({
severity: 'success',
summary: 'Succès',
detail: 'Notification diffusée avec succès à tous les utilisateurs',
life: 5000
});
// Réinitialiser le formulaire
setTitre('');
setMessage('');
setType('info');
setSubmitted(false);
} catch (error) {
console.error('Erreur lors de la diffusion:', error);
toast.current?.show({
severity: 'error',
summary: 'Erreur',
detail: 'Impossible de diffuser la notification',
life: 3000
});
} finally {
setLoading(false);
}
};
const handleReset = () => {
setTitre('');
setMessage('');
setType('info');
setSubmitted(false);
};
const typeOptionTemplate = (option: any) => {
return (
<div className="flex align-items-center gap-2">
<i className={`pi ${option.icon}`} style={{ color: option.color }}></i>
<span>{option.label}</span>
</div>
);
};
const selectedTypeTemplate = (option: any, props: any) => {
if (option) {
return (
<div className="flex align-items-center gap-2">
<i className={`pi ${option.icon}`} style={{ color: option.color }}></i>
<span>{option.label}</span>
</div>
);
}
return <span>{props.placeholder}</span>;
};
const getPreviewSeverity = () => {
switch (type) {
case 'info': return 'info';
case 'warning': return 'warn';
case 'success': return 'success';
case 'error': return 'error';
default: return 'info';
}
};
return (
<div className="grid">
<div className="col-12">
<h2>Diffuser une Notification</h2>
<p className="text-color-secondary">
Envoyez une notification à tous les utilisateurs de l'application
</p>
</div>
<div className="col-12 lg:col-8">
<Card>
<Toast ref={toast} />
<Message
severity="info"
text="Cette notification sera envoyée à tous les utilisateurs connectés et apparaîtra dans leur liste de notifications."
className="mb-4"
/>
<form onSubmit={handleSubmit}>
<div className="field">
<label htmlFor="type" className="font-medium">
Type de notification <span className="text-red-500">*</span>
</label>
<Dropdown
id="type"
value={types.find(t => t.value === type)}
options={types}
onChange={(e) => setType(e.value.value)}
optionLabel="label"
placeholder="Sélectionnez un type"
className="w-full"
itemTemplate={typeOptionTemplate}
valueTemplate={selectedTypeTemplate}
/>
</div>
<div className="field">
<label htmlFor="titre" className="font-medium">
Titre <span className="text-red-500">*</span>
</label>
<InputText
id="titre"
value={titre}
onChange={(e) => setTitre(e.target.value)}
placeholder="Entrez le titre de la notification"
className={`w-full ${submitted && !titre.trim() ? 'p-invalid' : ''}`}
maxLength={100}
/>
{submitted && !titre.trim() && (
<small className="p-error">Le titre est obligatoire</small>
)}
<small className="text-color-secondary">
{titre.length}/100 caractères
</small>
</div>
<div className="field">
<label htmlFor="message" className="font-medium">
Message <span className="text-red-500">*</span>
</label>
<InputTextarea
id="message"
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Entrez le message de la notification"
rows={5}
className={`w-full ${submitted && !message.trim() ? 'p-invalid' : ''}`}
maxLength={500}
/>
{submitted && !message.trim() && (
<small className="p-error">Le message est obligatoire</small>
)}
<small className="text-color-secondary">
{message.length}/500 caractères
</small>
</div>
<Divider />
<div className="flex gap-2 justify-content-end">
<Button
label="Réinitialiser"
icon="pi pi-times"
type="button"
className="p-button-text"
onClick={handleReset}
disabled={loading}
/>
<Button
label="Diffuser"
icon="pi pi-megaphone"
type="submit"
loading={loading}
disabled={loading}
/>
</div>
</form>
</Card>
</div>
<div className="col-12 lg:col-4">
<Card title="Aperçu">
<div className="mb-3">
<h4 className="text-color-secondary text-sm font-normal mb-2">
Voici comment apparaîtra votre notification :
</h4>
</div>
{titre || message ? (
<Message
severity={getPreviewSeverity()}
content={
<div>
<div className="font-bold mb-2">
{titre || 'Titre de la notification'}
</div>
<div className="text-sm">
{message || 'Message de la notification'}
</div>
</div>
}
/>
) : (
<div className="text-center p-4 border-1 border-dashed border-300 border-round">
<i className="pi pi-eye-slash text-3xl text-400 mb-2"></i>
<p className="text-color-secondary text-sm m-0">
Remplissez le formulaire pour voir l'aperçu
</p>
</div>
)}
<Divider />
<div className="text-sm text-color-secondary">
<h5 className="mt-0 mb-2">Informations</h5>
<ul className="pl-3 mt-0">
<li className="mb-2">La notification sera visible immédiatement</li>
<li className="mb-2">Tous les utilisateurs recevront cette notification</li>
<li className="mb-2">Elle apparaîtra dans leur liste de notifications</li>
<li>Un badge indiquera qu'elle n'a pas é lue</li>
</ul>
</div>
</Card>
<Card title="Conseils" className="mt-3">
<div className="text-sm text-color-secondary">
<ul className="pl-3 mt-0 mb-0">
<li className="mb-2">
<strong>Info :</strong> Pour les informations générales
</li>
<li className="mb-2">
<strong>Avertissement :</strong> Pour les alertes importantes
</li>
<li className="mb-2">
<strong>Succès :</strong> Pour les bonnes nouvelles
</li>
<li>
<strong>Erreur :</strong> Pour les problèmes critiques
</li>
</ul>
</div>
</Card>
</div>
</div>
);
};
export default NotificationsBroadcastPage;