MIGRATION VERS GITEA
This commit is contained in:
commit
5f9d991c15
333 files changed
+108514
No files matched your search
@@ -0,0 +1,339 @@
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { getAllHoraires } from '../../api/horaires';
|
||||
import { getAllPerturbations } from '../../api/perturbations';
|
||||
import { getCurrentUser, getScopedRegionId } from '../../utils/userUtils';
|
||||
import './Dashboard.css';
|
||||
|
||||
export default function Dashboard() {
|
||||
// État des statistiques
|
||||
const [stats, setStats] = useState({
|
||||
horaires: { total: 0, actifs: 0 },
|
||||
perturbations: { total: 0, actives: 0, retards: 0, suppressions: 0 },
|
||||
gares: { total: 0 },
|
||||
lignes: { total: 0 },
|
||||
typesTrains: { total: 0 }
|
||||
});
|
||||
|
||||
// État du chargement
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
// Perturbations récentes
|
||||
const [recentPerturbations, setRecentPerturbations] = useState([]);
|
||||
|
||||
// Horaires récents
|
||||
const [recentHoraires, setRecentHoraires] = useState([]);
|
||||
const [scopedRegionId, setScopedRegionId] = useState(null);
|
||||
|
||||
// Fonction de chargement des données
|
||||
const loadDashboardData = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const user = getCurrentUser();
|
||||
const regionId = getScopedRegionId(user);
|
||||
setScopedRegionId(regionId || null);
|
||||
|
||||
// Charger les horaires
|
||||
const { data: horairesData } = await getAllHoraires(regionId);
|
||||
const horaires = horairesData || [];
|
||||
|
||||
// Charger les perturbations
|
||||
const { data: perturbationsData } = await getAllPerturbations(regionId);
|
||||
const perturbations = perturbationsData || [];
|
||||
|
||||
// Calculer les statistiques des horaires
|
||||
const horaireStats = {
|
||||
total: horaires.length,
|
||||
actifs: horaires.length
|
||||
};
|
||||
|
||||
// Calculer les statistiques des perturbations
|
||||
const perturbationStats = {
|
||||
total: perturbations.length,
|
||||
actives: perturbations.filter(p => p.actif).length,
|
||||
retards: perturbations.filter(p => p.type_perturbation === 'retard').length,
|
||||
suppressions: perturbations.filter(p => p.type_perturbation === 'suppression').length
|
||||
};
|
||||
|
||||
// Extraire les gares uniques
|
||||
const garesSet = new Set();
|
||||
horaires.forEach(h => {
|
||||
if (h.gare_depart_nom) garesSet.add(h.gare_depart_nom);
|
||||
if (h.gare_arrivee_nom) garesSet.add(h.gare_arrivee_nom);
|
||||
if (h.gares_desservies) {
|
||||
h.gares_desservies.forEach(g => {
|
||||
if (g.gare_nom) garesSet.add(g.gare_nom);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Extraire les lignes uniques
|
||||
const lignesSet = new Set();
|
||||
horaires.forEach(h => {
|
||||
if (h.ligne_nom) lignesSet.add(h.ligne_nom);
|
||||
});
|
||||
|
||||
// Extraire les types de trains uniques
|
||||
const typesTrainsSet = new Set();
|
||||
horaires.forEach(h => {
|
||||
if (h.type_train) typesTrainsSet.add(h.type_train);
|
||||
});
|
||||
|
||||
// Mettre à jour les stats
|
||||
setStats({
|
||||
horaires: horaireStats,
|
||||
perturbations: perturbationStats,
|
||||
gares: { total: garesSet.size },
|
||||
lignes: { total: lignesSet.size },
|
||||
typesTrains: { total: typesTrainsSet.size }
|
||||
});
|
||||
|
||||
// Perturbations récentes (5 dernières)
|
||||
setRecentPerturbations(perturbations.slice(0, 5));
|
||||
|
||||
// Horaires récents (5 derniers)
|
||||
setRecentHoraires(horaires.slice(0, 5));
|
||||
|
||||
} catch (error) {
|
||||
console.error('Erreur lors du chargement du dashboard:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Charger les données au montage
|
||||
useEffect(() => {
|
||||
loadDashboardData();
|
||||
}, [loadDashboardData]);
|
||||
|
||||
// Fonction pour formater les heures
|
||||
const formatHeure = (heure) => {
|
||||
if (!heure) return '—';
|
||||
return heure.substring(0, 5);
|
||||
};
|
||||
|
||||
const navigate = (path) => {
|
||||
window.location.href = path;
|
||||
};
|
||||
|
||||
// Rendu des cartes de statistiques
|
||||
const renderStatCard = (title, value, icon, color = 'primary', subtitle = null) => (
|
||||
<div className={`stat-card stat-card-${color}`}>
|
||||
<div className="stat-card-header">
|
||||
<div className="stat-card-icon-wrap">
|
||||
<wcs-mat-icon icon={icon}></wcs-mat-icon>
|
||||
</div>
|
||||
<div className="stat-card-content">
|
||||
<span className="stat-card-title">{title}</span>
|
||||
<span className="stat-card-value">{loading ? '...' : value}</span>
|
||||
{subtitle && <span className="stat-card-subtitle">{subtitle}</span>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Rendu de la liste des perturbations récentes
|
||||
const renderRecentPerturbations = () => (
|
||||
<div className="dashboard-widget widget-panel">
|
||||
<div className="widget-header widget-header-panel">
|
||||
<h3>
|
||||
<span className="widget-title-icon"><wcs-mat-icon icon="warning"></wcs-mat-icon></span>
|
||||
Perturbations récentes
|
||||
</h3>
|
||||
<wcs-button
|
||||
className="wcs-secondary wcs-small widget-link-btn"
|
||||
onClick={() => navigate('/perturbations')}
|
||||
>
|
||||
Voir tout
|
||||
</wcs-button>
|
||||
</div>
|
||||
<div className="widget-content widget-content-panel">
|
||||
{loading ? (
|
||||
<div className="dashboard-state widget-state-panel">
|
||||
<wcs-spinner mode="border" />
|
||||
<p>Chargement...</p>
|
||||
</div>
|
||||
) : recentPerturbations.length === 0 ? (
|
||||
<div className="dashboard-state dashboard-state-empty widget-state-panel">
|
||||
<wcs-mat-icon className="dashboard-state-icon" icon="check_circle"></wcs-mat-icon>
|
||||
<p>Aucune perturbation</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="perturbations-list widget-list-panel">
|
||||
{recentPerturbations.map(p => (
|
||||
<div key={p.id} className="perturbation-item widget-list-item">
|
||||
<div className="perturbation-item-header widget-item-main">
|
||||
<div className="widget-item-text">
|
||||
<strong>{p.horaire?.numero_train || 'N/A'}</strong>
|
||||
<span className="perturbation-item-route">
|
||||
{p.horaire?.gare_depart_nom} → {p.horaire?.gare_arrivee_nom}
|
||||
</span>
|
||||
</div>
|
||||
<wcs-badge className={
|
||||
p.type_perturbation === 'retard' ? 'wcs-warning' :
|
||||
p.type_perturbation === 'suppression' ? 'wcs-danger' :
|
||||
'wcs-info'
|
||||
}>
|
||||
{p.type_perturbation === 'retard' ? `Retard +${p.duree_retard_minutes || 0} min` :
|
||||
p.type_perturbation === 'suppression' ? 'Supprime' :
|
||||
'Modifié'}
|
||||
</wcs-badge>
|
||||
</div>
|
||||
<div className="perturbation-item-details">
|
||||
<span>
|
||||
<wcs-mat-icon icon="info"></wcs-mat-icon>
|
||||
{p.cause || 'Non spécifiée'}
|
||||
</span>
|
||||
<span className={p.actif ? 'status-active' : 'status-inactive'}>
|
||||
{p.actif ? '● Active' : '○ Inactive'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Rendu de la liste des horaires récents
|
||||
const renderRecentHoraires = () => (
|
||||
<div className="dashboard-widget widget-panel">
|
||||
<div className="widget-header widget-header-panel">
|
||||
<h3>
|
||||
<span className="widget-title-icon"><wcs-mat-icon icon="schedule"></wcs-mat-icon></span>
|
||||
Horaires récents
|
||||
</h3>
|
||||
<wcs-button
|
||||
className="wcs-secondary wcs-small widget-link-btn"
|
||||
onClick={() => navigate('/horaires')}
|
||||
>
|
||||
Voir tout
|
||||
</wcs-button>
|
||||
</div>
|
||||
<div className="widget-content widget-content-panel">
|
||||
{loading ? (
|
||||
<div className="dashboard-state widget-state-panel">
|
||||
<wcs-spinner mode="border" />
|
||||
<p>Chargement...</p>
|
||||
</div>
|
||||
) : recentHoraires.length === 0 ? (
|
||||
<div className="dashboard-state dashboard-state-empty widget-state-panel">
|
||||
<wcs-mat-icon className="dashboard-state-icon" icon="train"></wcs-mat-icon>
|
||||
<p>Aucun horaire</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="horaires-list widget-list-panel">
|
||||
{recentHoraires.map(h => (
|
||||
<div key={h.id} className="horaire-item widget-list-item">
|
||||
<div className="horaire-item-header widget-item-main">
|
||||
<div className="widget-item-text">
|
||||
<strong>{h.numero_train}</strong>
|
||||
{h.type_train && (
|
||||
<wcs-badge className="wcs-info horaire-type-badge">
|
||||
{h.type_train}
|
||||
</wcs-badge>
|
||||
)}
|
||||
</div>
|
||||
{h.ligne_nom && (
|
||||
<wcs-badge className="wcs-secondary">{h.ligne_nom}</wcs-badge>
|
||||
)}
|
||||
</div>
|
||||
<div className="horaire-item-route">
|
||||
<span>
|
||||
{h.gare_depart_nom} ({formatHeure(h.heure_depart)})
|
||||
</span>
|
||||
<wcs-mat-icon icon="arrow_forward"></wcs-mat-icon>
|
||||
<span>
|
||||
{h.gare_arrivee_nom} ({formatHeure(h.heure_arrivee)})
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Rendu des actions rapides
|
||||
const renderQuickActions = () => (
|
||||
<div className="dashboard-widget widget-panel">
|
||||
<div className="widget-header widget-header-panel">
|
||||
<h3>
|
||||
<span className="widget-title-icon"><wcs-mat-icon icon="bolt"></wcs-mat-icon></span>
|
||||
Actions rapides
|
||||
</h3>
|
||||
</div>
|
||||
<div className="widget-content widget-content-panel">
|
||||
<div className="quick-actions">
|
||||
<a className="quick-action-row" href="/perturbations/creer">
|
||||
<span className="quick-action-row-left">
|
||||
<span className="quick-action-icon"><wcs-mat-icon icon="add"></wcs-mat-icon></span>
|
||||
<span>Créer une perturbation</span>
|
||||
</span>
|
||||
<wcs-mat-icon icon="arrow_forward"></wcs-mat-icon>
|
||||
</a>
|
||||
<a className="quick-action-row" href="/horaires">
|
||||
<span className="quick-action-row-left">
|
||||
<span className="quick-action-icon"><wcs-mat-icon icon="schedule"></wcs-mat-icon></span>
|
||||
<span>Gérer les horaires</span>
|
||||
</span>
|
||||
<wcs-mat-icon icon="arrow_forward"></wcs-mat-icon>
|
||||
</a>
|
||||
<a className="quick-action-row" href="/gares">
|
||||
<span className="quick-action-row-left">
|
||||
<span className="quick-action-icon"><wcs-mat-icon icon="location_on"></wcs-mat-icon></span>
|
||||
<span>Gérer les gares</span>
|
||||
</span>
|
||||
<wcs-mat-icon icon="arrow_forward"></wcs-mat-icon>
|
||||
</a>
|
||||
<button className="quick-action-row quick-action-row-refresh" type="button" onClick={loadDashboardData}>
|
||||
<span className="quick-action-row-left">
|
||||
<span className="quick-action-icon"><wcs-mat-icon icon="refresh"></wcs-mat-icon></span>
|
||||
<span>Actualiser les données</span>
|
||||
</span>
|
||||
<wcs-mat-icon icon="refresh"></wcs-mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="sncf-groupe dashboard-page panel-layout">
|
||||
<div className="panel-content">
|
||||
<header className="dashboard-hero">
|
||||
<div>
|
||||
<p className="dashboard-kicker">Panel de gestion</p>
|
||||
<h1>Tableau de bord</h1>
|
||||
<p className="dashboard-subtitle">Suivez l'activite des horaires, perturbations et donnees d'exploitation en temps reel.</p>
|
||||
</div>
|
||||
<div className="dashboard-hero-side">
|
||||
<wcs-button className="wcs-primary" onClick={loadDashboardData}>
|
||||
<wcs-mat-icon icon="refresh"></wcs-mat-icon>
|
||||
Actualiser
|
||||
</wcs-button>
|
||||
{scopedRegionId && <p>Region courante : <strong>{scopedRegionId}</strong></p>}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="stats-grid">
|
||||
{renderStatCard('Horaires', stats.horaires.total, 'schedule', 'primary', `${stats.horaires.actifs} actifs`)}
|
||||
{renderStatCard('Perturbations', stats.perturbations.total, 'warning', 'warning', `${stats.perturbations.actives} actives`)}
|
||||
{renderStatCard('Gares', stats.gares.total, 'location_on', 'info', 'Desservies')}
|
||||
{renderStatCard('Lignes', stats.lignes.total, 'route', 'secondary', 'Actives')}
|
||||
{renderStatCard('Types de trains', stats.typesTrains.total, 'train', 'success', 'Disponibles')}
|
||||
{renderStatCard('Retards', stats.perturbations.retards, 'schedule', 'warning', 'En cours')}
|
||||
</div>
|
||||
|
||||
<div className="widgets-grid">
|
||||
<div className="widgets-column">
|
||||
{renderRecentPerturbations()}
|
||||
{renderQuickActions()}
|
||||
</div>
|
||||
<div className="widgets-column">{renderRecentHoraires()}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user