init projet

This commit is contained in:
2026-05-20 10:43:18 +02:00
parent 51f27903bb
commit 9a402b0b34
57 changed files with 5622 additions and 0 deletions
@@ -0,0 +1,149 @@
// src/pages/MonitoringDetail/MonitoringDetail.jsx
// Page de détail d'un monitoring avec fil d'Ariane, tableau d'erreurs, export CSV
import { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { Download, TrendingUp } from 'lucide-react';
import { getMonitoringById, getMonitoringDetails, getMonitoringCount, getMonitoringColumns, exportToCSV } from '../../services/api';
import DataTable from '../../components/common/DataTable';
import LoadingSpinner from '../../components/common/LoadingSpinner';
import ErrorMessage from '../../components/common/ErrorMessage';
import Badge from '../../components/common/Badge';
import Button from '../../components/common/Button';
import styles from './MonitoringDetail.module.css';
/* Détermine le statut selon le nombre d'erreurs */
function getStatut(nbErreurs) {
if (nbErreurs === 0) return { label: 'OK', variant: 'ok' };
if (nbErreurs < 10) return { label: 'Attention', variant: 'warning' };
return { label: 'Critique', variant: 'critical' };
}
export default function MonitoringDetail() {
const { id } = useParams();
const navigate = useNavigate();
const [monitoring, setMonitoring] = useState(null);
const [details, setDetails] = useState([]);
const [columns, setColumns] = useState([]);
const [count, setCount] = useState(0);
const [loading, setLoading] = useState(true);
const [erreur, setErreur] = useState('');
const [search, setSearch] = useState('');
useEffect(() => {
async function loadData() {
setLoading(true);
setErreur('');
try {
const [monit, det, cnt, cols] = await Promise.all([
getMonitoringById(id),
getMonitoringDetails(id, { limit: 5000 }), // Charge maximum pour export complet
getMonitoringCount(id),
getMonitoringColumns(id)
]);
setMonitoring(monit);
setDetails(Array.isArray(det) ? det : []);
setCount(cnt.count ?? cnt.nb_erreurs ?? 0);
// Traite les colonnes de l'API
if (cols?.columns) {
const colsList = cols.columns.map(col => ({
key: col.COLUMN_NAME,
label: col.COLUMN_NAME
}));
setColumns(colsList);
}
} catch (e) {
setErreur(e.message || 'Erreur lors du chargement du monitoring.');
} finally {
setLoading(false);
}
}
loadData();
}, [id]);
/* Filtrage des détails par recherche */
const filteredDetails = details.filter(row => {
if (!search) return true;
return Object.values(row).some(val =>
String(val || '').toLowerCase().includes(search.toLowerCase())
);
});
if (loading) return <LoadingSpinner message="Chargement du monitoring..." />;
if (erreur) return <ErrorMessage message={erreur} />;
const statut = getStatut(count);
return (
<div className={styles.page}>
{/* Fil d'Ariane */}
<div className={styles.breadcrumb}>
<span onClick={() => navigate('/dashboard')} className={styles.breadcrumbLink}>Dashboard</span>
<span className={styles.separator}></span>
<span>{monitoring?.service || 'Service'}</span>
<span className={styles.separator}></span>
<span>{monitoring?.nom || 'Monitoring'}</span>
</div>
{/* En-tête */}
<div className={styles.header}>
<div className={styles.titleSection}>
<h1 className={styles.title}>{monitoring?.nom || monitoring?.monito_intitule || 'Monitoring'}</h1>
<div className={styles.badges}>
{monitoring?.bdd_source && <Badge variant={monitoring.bdd_source.toLowerCase() === 'sage' ? 'sage' : 'crm'}>{monitoring.bdd_source}</Badge>}
{monitoring?.id_categorie && <Badge variant="info">Catégorie {monitoring.id_categorie}</Badge>}
<Badge variant={statut.variant}>{statut.label}</Badge>
</div>
</div>
<div className={styles.count}>
<span className={styles.countNumber}>{count}</span>
<span className={styles.countLabel}>erreur{count !== 1 ? 's' : ''}</span>
</div>
</div>
{/* Actions */}
<div className={styles.actions}>
<Button variant="secondary" onClick={() => exportToCSV(filteredDetails, `monitoring_${id}_${new Date().toISOString().split('T')[0]}.csv`)}>
<Download size={14} />
Exporter CSV ({filteredDetails.length} lignes)
</Button>
<Button variant="primary" onClick={() => navigate(`/historique?id=${id}`)}>
<TrendingUp size={14} />
Voir l'évolution
</Button>
</div>
{/* Filtres */}
<div className={styles.filtres}>
<input
type="text"
placeholder="Rechercher dans les erreurs..."
className={styles.searchInput}
value={search}
onChange={e => setSearch(e.target.value)}
/>
</div>
{/* Résumé des résultats */}
<div style={{ marginBottom: '1rem', fontSize: '0.9rem', color: 'var(--color-text-secondary)' }}>
{filteredDetails.length} / {details.length} erreur{details.length !== 1 ? 's' : ''}
{search && ` (filtré: ${search})`}
</div>
{/* Tableau */}
{columns.length > 0 && (
<DataTable
columns={columns}
data={filteredDetails}
pageSize={50}
emptyMessage="Aucune erreur trouvée."
/>
)}
{/* Encart aide */}
<div className={styles.helpBox}>
<p><strong>Aide :</strong> Ce tableau affiche les erreurs détectées pour ce monitoring. Utilisez la barre de recherche pour filtrer. L'export CSV permet de télécharger toutes les données filtrées.</p>
</div>
</div>
);
}