RELEASE V.1.2.1 - Voici les fonctionnalités :

->Refonte visuelle + Prise en charge du mode sombre par l'ensemble des composants.
This commit is contained in:
mathis committed 2026-09-22 22:14:21 +02:00
1 parent d61422ddbf
commit 37ab831aac
44 files changed
+17261 -7636

No files matched your search

+113 -28
View File
@@ -146,7 +146,7 @@ export default function CreerPerturbation() {
const handleSelectSingleHoraire = useCallback((h) => {
setSelectedHoraires([h]);
setSearchQuery(`${h.numero_train} - ${h.gare_depart_nom} → ${h.gare_arrivee_nom}`);
setSearchQuery(`${h.numero_train} - ${h.gare_depart_nom} vers ${h.gare_arrivee_nom}`);
setShowResults(false);
setPerturbationsArretsMap({ [h.id]: buildArretFromHoraire(h) });
setOpenTrains({ [h.id]: true });
@@ -184,13 +184,33 @@ export default function CreerPerturbation() {
if (checked) {
if (!types.includes(typeValue)) types.push(typeValue);
if (typeValue === 'retard') { a.manual_retard = true; a.retard_minutes = a.retard_minutes || 0; }
if (typeValue === 'arret_prolonge') { a.manual_arret_prolonge = true; a.arret_prolonge_minutes = a.arret_prolonge_minutes || 0; }
if (typeValue === 'retard') {
a.manual_retard = true;
a.retard_minutes = Number(a.retard_minutes) > 0 ? a.retard_minutes : 15;
}
if (typeValue === 'reduction_retard') {
a.manual_reduction = true;
a.reduction_retard_minutes = Number(a.reduction_retard_minutes) > 0 ? a.reduction_retard_minutes : 5;
}
if (typeValue === 'arret_prolonge') {
a.manual_arret_prolonge = true;
a.arret_prolonge_minutes = Number(a.arret_prolonge_minutes) > 0 ? a.arret_prolonge_minutes : 5;
}
if (typeValue === 'modification_parcours') { a.is_nouveau_terminus = false; a.is_nouvelle_origine = false; }
} else {
types = types.filter(t => t !== typeValue);
if (typeValue === 'retard') { a.manual_retard = false; a.retard_minutes = 0; }
if (typeValue === 'arret_prolonge') { a.manual_arret_prolonge = false; a.arret_prolonge_minutes = 0; }
if (typeValue === 'retard') {
a.manual_retard = false;
a.retard_minutes = 0;
}
if (typeValue === 'reduction_retard') {
a.manual_reduction = false;
a.reduction_retard_minutes = 0;
}
if (typeValue === 'arret_prolonge') {
a.manual_arret_prolonge = false;
a.arret_prolonge_minutes = 0;
}
if (typeValue === 'arret_supprime') { a.is_nouveau_terminus = false; a.is_nouvelle_origine = false; }
if (typeValue === 'modification_parcours') { a.is_nouveau_terminus = false; a.is_nouvelle_origine = false; }
}
@@ -209,10 +229,24 @@ export default function CreerPerturbation() {
nextArr[index] = { ...nextArr[index], [field]: value };
if (field === 'retard_minutes') {
nextArr[index].manual_retard = true;
nextArr[index].retard_minutes = Number(value) || 0;
const mins = Number(value) || 0;
nextArr[index].retard_minutes = mins;
nextArr[index].manual_retard = mins > 0;
const types = [...(nextArr[index].types_perturbation || [])];
if (!types.includes('retard')) types.push('retard');
if (mins > 0 && !types.includes('retard')) types.push('retard');
nextArr[index].types_perturbation = types;
nextArr[index].type_perturbation = getPrimaryType(types);
}
if (field === 'reduction_retard_minutes') {
const mins = Number(value) || 0;
nextArr[index].reduction_retard_minutes = mins;
nextArr[index].manual_reduction = mins > 0;
const types = [...(nextArr[index].types_perturbation || [])];
if (mins > 0 && !types.includes('reduction_retard')) types.push('reduction_retard');
else if (mins === 0) {
const idx = types.indexOf('reduction_retard');
if (idx !== -1) types.splice(idx, 1);
}
nextArr[index].types_perturbation = types;
nextArr[index].type_perturbation = getPrimaryType(types);
}
@@ -538,8 +572,10 @@ export default function CreerPerturbation() {
</div>
<div className="horaire-display-content" style={{ maxHeight: '200px', overflowY: 'auto' }}>
{selectedHoraires.map(h => (
<div key={h.id} style={{ padding: '8px 0', borderBottom: '1px solid #eee' }}>
<strong>Train {h.numero_train}</strong> ({h.ligne_nom || 'N/A'}) - {h.gare_depart_nom} → {h.gare_arrivee_nom}
<div key={h.id} style={{ padding: '8px 0', borderBottom: '1px solid #eee', display: 'flex', alignItems: 'center', gap: '6px' }}>
<strong>Train {h.numero_train}</strong> ({h.ligne_nom || 'N/A'}) - {h.gare_depart_nom}
<wcs-mat-icon icon="arrow_forward" style={{ fontSize: '13px', margin: '0 4px', color: '#94a3b8' }}></wcs-mat-icon>
{h.gare_arrivee_nom}
</div>
))}
</div>
@@ -573,8 +609,8 @@ export default function CreerPerturbation() {
style={{ padding: '12px 16px', background: '#e9ecef', cursor: 'pointer', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}
onClick={() => toggleTrainAccordion(h.id)}
>
<h4 style={{ margin: 0, fontSize: '1rem', color: '#333' }}>
Train {h.numero_train} ({h.gare_depart_nom} → {h.gare_arrivee_nom})
<h4 style={{ margin: 0, fontSize: '1rem', color: '#333', display: 'flex', alignItems: 'center', gap: '6px' }}>
Train {h.numero_train} ({h.gare_depart_nom} <wcs-mat-icon icon="arrow_forward" style={{ fontSize: '13px' }}></wcs-mat-icon> {h.gare_arrivee_nom})
</h4>
<wcs-mat-icon icon={openTrains[h.id] ? 'expand_less' : 'expand_more'} style={{ color: '#555' }} />
</div>
@@ -677,8 +713,10 @@ export default function CreerPerturbation() {
Départ : {formatHeure(h.heure_depart)}
</span>
</div>
<div>
{h.gare_depart_nom} → {h.gare_arrivee_nom}
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
<span>{h.gare_depart_nom}</span>
<wcs-mat-icon icon="arrow_forward" style={{ fontSize: '13px', margin: '0 4px', color: '#94a3b8' }}></wcs-mat-icon>
<span>{h.gare_arrivee_nom}</span>
</div>
<div style={{ fontSize: '0.9rem', color: '#4b5563' }}>
Matériel actuel : {h.composition_train?.map(c => `${c.materiel_nom}`).join(', ') || 'Aucun'}
@@ -717,7 +755,7 @@ export default function CreerPerturbation() {
<select id="sillon" value={sillonSubstitutionId} onChange={e => setSillonSubstitutionId(e.target.value)} className="form-select">
<option value="">Aucun</option>
{horaires.filter(h => h.id !== selectedHoraires[0].id).slice(0, 50).map(h => (
<option key={h.id} value={h.id}>{h.numero_train} - {h.gare_depart_nom} → {h.gare_arrivee_nom}</option>
<option key={h.id} value={h.id}>{h.numero_train} - {h.gare_depart_nom} vers {h.gare_arrivee_nom}</option>
))}
</select>
</div>
@@ -762,29 +800,76 @@ export default function CreerPerturbation() {
// ── Rendu principal ───────────────────────────────────────────────────────
return (
<div className="sncf-groupe">
<div className="sncf-groupe perturbations-page-root">
<div className="panel-layout">
<div className="panel-nav"><NavPanel /></div>
<div className="panel-content">
<header className="page-header">
<h1>Créer une perturbation</h1>
<p className="page-description">
Étape {currentStep === 'horaire' ? '1' : '2'} / 2 —{' '}
{currentStep === 'horaire' ? 'Sélection des horaires' : 'Configuration'}
</p>
<div className="panel-content perturbations-modern-container">
{/* Header Banner */}
<header className="page-header-modern">
<div className="header-info">
<div className="perturbations-hero-tags" style={{ marginBottom: '8px' }}>
<span className="perturbations-kicker-pill">
<wcs-mat-icon icon="add_alert"></wcs-mat-icon>
Déclaration d'incident
</span>
{scopedRegionId && (
<span className="perturbations-region-badge">
<wcs-mat-icon icon="place"></wcs-mat-icon>
Région : {scopedRegionId}
</span>
)}
</div>
<h1>Déclarer une perturbation sur les circulations</h1>
<p>Signalez un retard, une suppression d'arrêt ou une modification de parcours en temps réel.</p>
</div>
<div className="header-actions">
<wcs-button className="wcs-secondary" mode="stroked" onClick={handleCancel}>
<wcs-mat-icon icon="close" style={{ marginRight: '6px' }}></wcs-mat-icon>
Annuler
</wcs-button>
</div>
</header>
<div className="creation-container">
<div className="creation-main">
{/* Stepper */}
<div className="stepper-horizontal">
<div
className={`step-item ${currentStep === 'horaire' ? 'active' : 'completed'}`}
onClick={() => setCurrentStep('horaire')}
style={{ cursor: 'pointer' }}
>
<div className="step-icon">
{currentStep === 'perturbation' ? <wcs-mat-icon icon="check" style={{ fontSize: '14px' }}></wcs-mat-icon> : '1'}
</div>
<span>1. Sélection des trains</span>
</div>
<div className="step-divider"></div>
<div
className={`step-item ${currentStep === 'perturbation' ? 'active' : ''}`}
onClick={() => selectedHoraires.length > 0 && setCurrentStep('perturbation')}
style={{ cursor: selectedHoraires.length > 0 ? 'pointer' : 'not-allowed' }}
>
<div className="step-icon">2</div>
<span>2. Configuration de l'incident & Parcours</span>
</div>
</div>
<div className="creation-layout-modern">
<div className="creation-main-content">
{currentStep === 'horaire' && renderStepHoraire()}
{currentStep === 'perturbation' && renderStepPerturbation()}
</div>
<div className="creation-preview">
<div className="sticky-sidebar">
{selectedHoraires.length === 1 && (
<ParcoursPreview arrets={perturbationsArretsMap[selectedHoraires[0].id]} />
)}
{selectedHoraires.length > 1 && (
<div style={{ padding: '15px', color: '#666', fontStyle: 'italic', background: '#f9f9f9', borderRadius: '8px', border: '1px solid #ddd' }}>
Aperçu du parcours non disponible lors de la sélection multiple. Veuillez vous référer aux accordéons détaillés.
<div className="summary-card">
<h3>Sélection multiple ({selectedHoraires.length} trains)</h3>
<p style={{ fontSize: '13px', color: '#64748b', margin: 0 }}>
Configurez le motif commun et ajustez les arrêts de chaque train via les accordéons.
</p>
</div>
)}
</div>