- 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>
652 lines
30 KiB
TypeScript
652 lines
30 KiB
TypeScript
'use client';
|
|
export const dynamic = 'force-dynamic';
|
|
|
|
|
|
import React, { useState, useEffect } 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 { Toolbar } from 'primereact/toolbar';
|
|
import { Message } from 'primereact/message';
|
|
import { FileUpload } from 'primereact/fileupload';
|
|
import { Tag } from 'primereact/tag';
|
|
import { Steps } from 'primereact/steps';
|
|
import { useRouter } from 'next/navigation';
|
|
import { apiClient } from '../../../../services/api-client';
|
|
|
|
interface SignalementPanne {
|
|
materielId: number;
|
|
chantierImpacte?: number;
|
|
gravite: 'MINEURE' | 'MODEREE' | 'MAJEURE' | 'CRITIQUE';
|
|
impactProduction: 'AUCUN' | 'FAIBLE' | 'MOYEN' | 'ELEVE' | 'ARRET_TOTAL';
|
|
problemeSignale: string;
|
|
symptomesObserves: string;
|
|
circonstancesApparition: string;
|
|
mesuresTemporaires?: string;
|
|
personneSignalement: string;
|
|
contactUrgence: string;
|
|
photos: File[];
|
|
prioriteUrgence: boolean;
|
|
interventionImmediate: boolean;
|
|
}
|
|
|
|
interface Materiel {
|
|
id: number;
|
|
nom: string;
|
|
type: string;
|
|
marque: string;
|
|
modele: string;
|
|
localisation: string;
|
|
statut: string;
|
|
}
|
|
|
|
interface Chantier {
|
|
id: number;
|
|
nom: string;
|
|
localisation: string;
|
|
statut: string;
|
|
}
|
|
|
|
const SignalerPannePage = () => {
|
|
const [signalement, setSignalement] = useState<SignalementPanne>({
|
|
materielId: 0,
|
|
gravite: 'MODEREE',
|
|
impactProduction: 'MOYEN',
|
|
problemeSignale: '',
|
|
symptomesObserves: '',
|
|
circonstancesApparition: '',
|
|
personneSignalement: '',
|
|
contactUrgence: '',
|
|
photos: [],
|
|
prioriteUrgence: false,
|
|
interventionImmediate: false
|
|
});
|
|
const [materiels, setMateriels] = useState<Materiel[]>([]);
|
|
const [chantiers, setChantiers] = useState<Chantier[]>([]);
|
|
const [loading, setLoading] = useState(false);
|
|
const [errors, setErrors] = useState<{ [key: string]: string }>({});
|
|
const [etapeActuelle, setEtapeActuelle] = useState(0);
|
|
const [signalementEnvoye, setSignalementEnvoye] = useState(false);
|
|
const router = useRouter();
|
|
|
|
const graviteOptions = [
|
|
{ label: 'Mineure - Fonctionnement dégradé', value: 'MINEURE' },
|
|
{ label: 'Modérée - Dysfonctionnement notable', value: 'MODEREE' },
|
|
{ label: 'Majeure - Panne importante', value: 'MAJEURE' },
|
|
{ label: 'Critique - Arrêt complet', value: 'CRITIQUE' }
|
|
];
|
|
|
|
const impactOptions = [
|
|
{ label: 'Aucun impact', value: 'AUCUN' },
|
|
{ label: 'Impact faible', value: 'FAIBLE' },
|
|
{ label: 'Impact moyen', value: 'MOYEN' },
|
|
{ label: 'Impact élevé', value: 'ELEVE' },
|
|
{ label: 'Arrêt total de production', value: 'ARRET_TOTAL' }
|
|
];
|
|
|
|
const etapes = [
|
|
{ label: 'Matériel' },
|
|
{ label: 'Problème' },
|
|
{ label: 'Impact' },
|
|
{ label: 'Contact' },
|
|
{ label: 'Confirmation' }
|
|
];
|
|
|
|
useEffect(() => {
|
|
loadMateriels();
|
|
loadChantiers();
|
|
}, []);
|
|
|
|
const loadMateriels = async () => {
|
|
try {
|
|
console.log('🔄 Chargement des matériels...');
|
|
const response = await apiClient.get('/api/materiels');
|
|
console.log('✅ Matériels chargés:', response.data);
|
|
setMateriels(response.data || []);
|
|
} catch (error) {
|
|
console.error('❌ Erreur lors du chargement des matériels:', error);
|
|
}
|
|
};
|
|
|
|
const loadChantiers = async () => {
|
|
try {
|
|
const response = await apiClient.get('/api/chantiers');
|
|
setChantiers(response.data || []);
|
|
} catch (error) {
|
|
console.error('❌ Erreur lors du chargement des chantiers:', error);
|
|
}
|
|
};
|
|
|
|
const validateEtape = (etape: number) => {
|
|
const newErrors: { [key: string]: string } = {};
|
|
|
|
switch (etape) {
|
|
case 0: // Matériel
|
|
if (!signalement.materielId) {
|
|
newErrors.materielId = 'Le matériel est requis';
|
|
}
|
|
break;
|
|
case 1: // Problème
|
|
if (!signalement.problemeSignale.trim()) {
|
|
newErrors.problemeSignale = 'La description du problème est requise';
|
|
}
|
|
if (!signalement.symptomesObserves.trim()) {
|
|
newErrors.symptomesObserves = 'Les symptômes observés sont requis';
|
|
}
|
|
break;
|
|
case 2: // Impact
|
|
if (!signalement.circonstancesApparition.trim()) {
|
|
newErrors.circonstancesApparition = 'Les circonstances d\'apparition sont requises';
|
|
}
|
|
break;
|
|
case 3: // Contact
|
|
if (!signalement.personneSignalement.trim()) {
|
|
newErrors.personneSignalement = 'Le nom de la personne est requis';
|
|
}
|
|
if (!signalement.contactUrgence.trim()) {
|
|
newErrors.contactUrgence = 'Le contact d\'urgence est requis';
|
|
}
|
|
break;
|
|
}
|
|
|
|
setErrors(newErrors);
|
|
return Object.keys(newErrors).length === 0;
|
|
};
|
|
|
|
const etapeSuivante = () => {
|
|
if (validateEtape(etapeActuelle)) {
|
|
setEtapeActuelle(etapeActuelle + 1);
|
|
}
|
|
};
|
|
|
|
const etapePrecedente = () => {
|
|
setEtapeActuelle(etapeActuelle - 1);
|
|
};
|
|
|
|
const handleSubmit = async () => {
|
|
if (!validateEtape(3)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setLoading(true);
|
|
console.log('🔄 Envoi du signalement de panne...', signalement);
|
|
|
|
// Créer FormData pour inclure les photos
|
|
const formData = new FormData();
|
|
Object.entries(signalement).forEach(([key, value]) => {
|
|
if (key === 'photos') {
|
|
signalement.photos.forEach((photo, index) => {
|
|
formData.append(`photo_${index}`, photo);
|
|
});
|
|
} else {
|
|
formData.append(key, value.toString());
|
|
}
|
|
});
|
|
|
|
const response = await apiClient.post('/api/maintenances/signaler-panne', formData, {
|
|
headers: {
|
|
'Content-Type': 'multipart/form-data'
|
|
}
|
|
});
|
|
console.log('✅ Signalement envoyé avec succès:', response.data);
|
|
|
|
setSignalementEnvoye(true);
|
|
setEtapeActuelle(4);
|
|
} catch (error) {
|
|
console.error('❌ Erreur lors de l\'envoi du signalement:', error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const materielOptionTemplate = (option: Materiel) => {
|
|
return (
|
|
<div className="flex align-items-center gap-2">
|
|
<div>
|
|
<div className="font-medium">{option.nom}</div>
|
|
<div className="text-sm text-500">{option.type} - {option.marque} {option.modele}</div>
|
|
<div className="text-sm text-500">📍 {option.localisation}</div>
|
|
</div>
|
|
<Tag
|
|
value={option.statut}
|
|
severity={option.statut === 'DISPONIBLE' ? 'success' : 'warning'}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const chantierOptionTemplate = (option: Chantier) => {
|
|
return (
|
|
<div className="flex align-items-center gap-2">
|
|
<div>
|
|
<div className="font-medium">{option.nom}</div>
|
|
<div className="text-sm text-500">📍 {option.localisation}</div>
|
|
</div>
|
|
<Tag value={option.statut} severity="info" />
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const onPhotoUpload = (event: any) => {
|
|
const files = Array.from(event.files) as File[];
|
|
setSignalement({ ...signalement, photos: [...signalement.photos, ...files] });
|
|
};
|
|
|
|
const supprimerPhoto = (index: number) => {
|
|
const nouvellesPhotos = signalement.photos.filter((_, i) => i !== index);
|
|
setSignalement({ ...signalement, photos: nouvellesPhotos });
|
|
};
|
|
|
|
const getGraviteSeverity = (gravite: string) => {
|
|
switch (gravite) {
|
|
case 'MINEURE': return 'info';
|
|
case 'MODEREE': return 'warning';
|
|
case 'MAJEURE': return 'danger';
|
|
case 'CRITIQUE': return 'danger';
|
|
default: return 'secondary';
|
|
}
|
|
};
|
|
|
|
const getImpactSeverity = (impact: string) => {
|
|
switch (impact) {
|
|
case 'AUCUN': return 'success';
|
|
case 'FAIBLE': return 'info';
|
|
case 'MOYEN': return 'warning';
|
|
case 'ELEVE': return 'danger';
|
|
case 'ARRET_TOTAL': return 'danger';
|
|
default: return 'secondary';
|
|
}
|
|
};
|
|
|
|
const leftToolbarTemplate = () => {
|
|
return (
|
|
<div className="flex flex-wrap gap-2">
|
|
<Button
|
|
label="Retour Maintenance"
|
|
icon="pi pi-arrow-left"
|
|
className="p-button-outlined"
|
|
onClick={() => router.push('/maintenance')}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const rightToolbarTemplate = () => {
|
|
return (
|
|
<div className="flex gap-2">
|
|
<Button
|
|
label="Urgence"
|
|
icon="pi pi-exclamation-triangle"
|
|
className="p-button-danger"
|
|
onClick={() => router.push('/maintenance/urgence')}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
if (signalementEnvoye) {
|
|
return (
|
|
<div className="grid">
|
|
<div className="col-12">
|
|
<Toolbar
|
|
className="mb-4"
|
|
left={leftToolbarTemplate}
|
|
right={rightToolbarTemplate}
|
|
/>
|
|
</div>
|
|
<div className="col-12">
|
|
<Card>
|
|
<div className="text-center">
|
|
<i className="pi pi-check-circle text-green-500 text-6xl mb-4" />
|
|
<h2 className="text-green-600 mb-4">Signalement envoyé avec succès !</h2>
|
|
<p className="text-lg mb-4">
|
|
Votre signalement de panne a été transmis à l'équipe de maintenance.
|
|
</p>
|
|
<p className="text-500 mb-4">
|
|
Un technicien sera assigné dans les plus brefs délais selon la priorité définie.
|
|
</p>
|
|
<div className="flex gap-2 justify-content-center">
|
|
<Button
|
|
label="Retour à la maintenance"
|
|
icon="pi pi-arrow-left"
|
|
className="p-button-outlined"
|
|
onClick={() => router.push('/maintenance')}
|
|
/>
|
|
<Button
|
|
label="Nouveau signalement"
|
|
icon="pi pi-plus"
|
|
className="p-button-success"
|
|
onClick={() => {
|
|
setSignalementEnvoye(false);
|
|
setEtapeActuelle(0);
|
|
setSignalement({
|
|
materielId: 0,
|
|
gravite: 'MODEREE',
|
|
impactProduction: 'MOYEN',
|
|
problemeSignale: '',
|
|
symptomesObserves: '',
|
|
circonstancesApparition: '',
|
|
personneSignalement: '',
|
|
contactUrgence: '',
|
|
photos: [],
|
|
prioriteUrgence: false,
|
|
interventionImmediate: false
|
|
});
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="grid">
|
|
<div className="col-12">
|
|
<Toolbar
|
|
className="mb-4"
|
|
left={leftToolbarTemplate}
|
|
right={rightToolbarTemplate}
|
|
/>
|
|
</div>
|
|
|
|
<div className="col-12">
|
|
<Card title="🚨 Signaler une Panne">
|
|
<Steps model={etapes} activeIndex={etapeActuelle} className="mb-4" />
|
|
|
|
{etapeActuelle === 0 && (
|
|
<div className="p-fluid">
|
|
<h4>Sélection du matériel</h4>
|
|
<div className="field">
|
|
<label htmlFor="materiel" className="font-medium">
|
|
Matériel en panne *
|
|
</label>
|
|
<Dropdown
|
|
id="materiel"
|
|
value={signalement.materielId}
|
|
options={materiels}
|
|
onChange={(e) => setSignalement({ ...signalement, materielId: e.value })}
|
|
optionLabel="nom"
|
|
optionValue="id"
|
|
placeholder="Sélectionner le matériel en panne"
|
|
itemTemplate={materielOptionTemplate}
|
|
className={errors.materielId ? 'p-invalid' : ''}
|
|
filter
|
|
/>
|
|
{errors.materielId && <small className="p-error">{errors.materielId}</small>}
|
|
</div>
|
|
<div className="field">
|
|
<label htmlFor="chantier" className="font-medium">
|
|
Chantier impacté (optionnel)
|
|
</label>
|
|
<Dropdown
|
|
id="chantier"
|
|
value={signalement.chantierImpacte}
|
|
options={chantiers}
|
|
onChange={(e) => setSignalement({ ...signalement, chantierImpacte: e.value })}
|
|
optionLabel="nom"
|
|
optionValue="id"
|
|
placeholder="Sélectionner le chantier impacté"
|
|
itemTemplate={chantierOptionTemplate}
|
|
filter
|
|
showClear
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{etapeActuelle === 1 && (
|
|
<div className="p-fluid">
|
|
<h4>Description du problème</h4>
|
|
<div className="field">
|
|
<label htmlFor="probleme" className="font-medium">
|
|
Description du problème *
|
|
</label>
|
|
<InputTextarea
|
|
id="probleme"
|
|
value={signalement.problemeSignale}
|
|
onChange={(e) => setSignalement({ ...signalement, problemeSignale: e.target.value })}
|
|
rows={4}
|
|
placeholder="Décrivez précisément le problème rencontré..."
|
|
className={errors.problemeSignale ? 'p-invalid' : ''}
|
|
/>
|
|
{errors.problemeSignale && <small className="p-error">{errors.problemeSignale}</small>}
|
|
</div>
|
|
<div className="field">
|
|
<label htmlFor="symptomes" className="font-medium">
|
|
Symptômes observés *
|
|
</label>
|
|
<InputTextarea
|
|
id="symptomes"
|
|
value={signalement.symptomesObserves}
|
|
onChange={(e) => setSignalement({ ...signalement, symptomesObserves: e.target.value })}
|
|
rows={3}
|
|
placeholder="Bruits anormaux, fumée, vibrations, arrêt soudain..."
|
|
className={errors.symptomesObserves ? 'p-invalid' : ''}
|
|
/>
|
|
{errors.symptomesObserves && <small className="p-error">{errors.symptomesObserves}</small>}
|
|
</div>
|
|
<div className="field">
|
|
<label className="font-medium">Photos (optionnel)</label>
|
|
<FileUpload
|
|
mode="basic"
|
|
accept="image/*"
|
|
maxFileSize={5000000}
|
|
multiple
|
|
onSelect={onPhotoUpload}
|
|
chooseLabel="Ajouter des photos"
|
|
/>
|
|
{signalement.photos.length > 0 && (
|
|
<div className="flex flex-wrap gap-2 mt-2">
|
|
{signalement.photos.map((photo, index) => (
|
|
<div key={index} className="relative">
|
|
<img
|
|
src={URL.createObjectURL(photo)}
|
|
alt={`Photo ${index + 1}`}
|
|
className="w-6rem h-4rem object-cover border-round"
|
|
/>
|
|
<Button
|
|
icon="pi pi-times"
|
|
className="p-button-rounded p-button-danger p-button-sm absolute"
|
|
style={{ top: '-0.5rem', right: '-0.5rem' }}
|
|
onClick={() => supprimerPhoto(index)}
|
|
/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{etapeActuelle === 2 && (
|
|
<div className="p-fluid">
|
|
<h4>Évaluation de l'impact</h4>
|
|
<div className="field">
|
|
<label htmlFor="gravite" className="font-medium">
|
|
Gravité de la panne *
|
|
</label>
|
|
<Dropdown
|
|
id="gravite"
|
|
value={signalement.gravite}
|
|
options={graviteOptions}
|
|
onChange={(e) => setSignalement({ ...signalement, gravite: e.value })}
|
|
placeholder="Évaluer la gravité"
|
|
/>
|
|
</div>
|
|
<div className="field">
|
|
<label htmlFor="impact" className="font-medium">
|
|
Impact sur la production *
|
|
</label>
|
|
<Dropdown
|
|
id="impact"
|
|
value={signalement.impactProduction}
|
|
options={impactOptions}
|
|
onChange={(e) => setSignalement({ ...signalement, impactProduction: e.value })}
|
|
placeholder="Évaluer l'impact"
|
|
/>
|
|
</div>
|
|
<div className="field">
|
|
<label htmlFor="circonstances" className="font-medium">
|
|
Circonstances d'apparition *
|
|
</label>
|
|
<InputTextarea
|
|
id="circonstances"
|
|
value={signalement.circonstancesApparition}
|
|
onChange={(e) => setSignalement({ ...signalement, circonstancesApparition: e.target.value })}
|
|
rows={3}
|
|
placeholder="Quand et comment le problème est-il apparu ? Conditions météo, charge de travail..."
|
|
className={errors.circonstancesApparition ? 'p-invalid' : ''}
|
|
/>
|
|
{errors.circonstancesApparition && <small className="p-error">{errors.circonstancesApparition}</small>}
|
|
</div>
|
|
<div className="field">
|
|
<label htmlFor="mesures" className="font-medium">
|
|
Mesures temporaires prises (optionnel)
|
|
</label>
|
|
<InputTextarea
|
|
id="mesures"
|
|
value={signalement.mesuresTemporaires || ''}
|
|
onChange={(e) => setSignalement({ ...signalement, mesuresTemporaires: e.target.value })}
|
|
rows={2}
|
|
placeholder="Actions déjà entreprises pour limiter l'impact..."
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{etapeActuelle === 3 && (
|
|
<div className="p-fluid">
|
|
<h4>Informations de contact</h4>
|
|
<div className="field">
|
|
<label htmlFor="personne" className="font-medium">
|
|
Personne signalant la panne *
|
|
</label>
|
|
<InputText
|
|
id="personne"
|
|
value={signalement.personneSignalement}
|
|
onChange={(e) => setSignalement({ ...signalement, personneSignalement: e.target.value })}
|
|
placeholder="Nom et prénom"
|
|
className={errors.personneSignalement ? 'p-invalid' : ''}
|
|
/>
|
|
{errors.personneSignalement && <small className="p-error">{errors.personneSignalement}</small>}
|
|
</div>
|
|
<div className="field">
|
|
<label htmlFor="contact" className="font-medium">
|
|
Contact d'urgence *
|
|
</label>
|
|
<InputText
|
|
id="contact"
|
|
value={signalement.contactUrgence}
|
|
onChange={(e) => setSignalement({ ...signalement, contactUrgence: e.target.value })}
|
|
placeholder="Numéro de téléphone ou email"
|
|
className={errors.contactUrgence ? 'p-invalid' : ''}
|
|
/>
|
|
{errors.contactUrgence && <small className="p-error">{errors.contactUrgence}</small>}
|
|
</div>
|
|
<div className="field-checkbox">
|
|
<input
|
|
type="checkbox"
|
|
id="priorite"
|
|
checked={signalement.prioriteUrgence}
|
|
onChange={(e) => setSignalement({ ...signalement, prioriteUrgence: e.target.checked })}
|
|
/>
|
|
<label htmlFor="priorite" className="ml-2">
|
|
🚨 Marquer comme priorité urgente
|
|
</label>
|
|
</div>
|
|
<div className="field-checkbox">
|
|
<input
|
|
type="checkbox"
|
|
id="intervention"
|
|
checked={signalement.interventionImmediate}
|
|
onChange={(e) => setSignalement({ ...signalement, interventionImmediate: e.target.checked })}
|
|
/>
|
|
<label htmlFor="intervention" className="ml-2">
|
|
⚡ Demander une intervention immédiate
|
|
</label>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{etapeActuelle === 4 && (
|
|
<div>
|
|
<h4>Récapitulatif du signalement</h4>
|
|
<div className="grid">
|
|
<div className="col-12 md:col-6">
|
|
<div className="field">
|
|
<label className="font-medium">Matériel:</label>
|
|
<p>{materiels.find(m => m.id === signalement.materielId)?.nom}</p>
|
|
</div>
|
|
<div className="field">
|
|
<label className="font-medium">Gravité:</label>
|
|
<p><Tag value={signalement.gravite} severity={getGraviteSeverity(signalement.gravite)} /></p>
|
|
</div>
|
|
<div className="field">
|
|
<label className="font-medium">Impact:</label>
|
|
<p><Tag value={signalement.impactProduction} severity={getImpactSeverity(signalement.impactProduction)} /></p>
|
|
</div>
|
|
</div>
|
|
<div className="col-12 md:col-6">
|
|
<div className="field">
|
|
<label className="font-medium">Signalé par:</label>
|
|
<p>{signalement.personneSignalement}</p>
|
|
</div>
|
|
<div className="field">
|
|
<label className="font-medium">Contact:</label>
|
|
<p>{signalement.contactUrgence}</p>
|
|
</div>
|
|
{signalement.prioriteUrgence && (
|
|
<div className="field">
|
|
<Tag value="PRIORITÉ URGENTE" severity="danger" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="col-12">
|
|
<div className="field">
|
|
<label className="font-medium">Problème:</label>
|
|
<p>{signalement.problemeSignale}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex justify-content-between mt-4">
|
|
<Button
|
|
label="Précédent"
|
|
icon="pi pi-chevron-left"
|
|
className="p-button-outlined"
|
|
onClick={etapePrecedente}
|
|
disabled={etapeActuelle === 0}
|
|
/>
|
|
{etapeActuelle < 4 ? (
|
|
<Button
|
|
label="Suivant"
|
|
icon="pi pi-chevron-right"
|
|
iconPos="right"
|
|
onClick={etapeSuivante}
|
|
/>
|
|
) : (
|
|
<Button
|
|
label="Envoyer le signalement"
|
|
icon="pi pi-send"
|
|
className="p-button-success"
|
|
loading={loading}
|
|
onClick={handleSubmit}
|
|
/>
|
|
)}
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default SignalerPannePage;
|