Refonte complète et fidèle des afficheurs Classiques Départs et Arrivées selon les images
This commit is contained in:
1 parent
62f268e677
commit
326e3e7c8b
4 files changed
+408
-765
No files matched your search
@@ -1,7 +1,6 @@
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import './ClassiquesDeparts.css';
|
||||
import supabase from '../../../lib/supabase';
|
||||
import Logo from '../../../components/Logo';
|
||||
import MarqueeText from '../../../components/MarqueeText';
|
||||
import useAnnonceSonoreRealtime from '../../../hooks/useAnnonceSonoreRealtime';
|
||||
import useAutoAnnonce from '../../../hooks/useAutoAnnonce';
|
||||
@@ -49,8 +48,71 @@ const isSubstitution = (h) => {
|
||||
return Boolean(subFlag || subStr || typeMatch || commentMatch);
|
||||
};
|
||||
|
||||
// (La fonction TrainType a été retirée car la colonne affiche désormais directement les
|
||||
// informations via .train-meta dans la colonne .col-train-type)
|
||||
const formatStationName = (name) => {
|
||||
if (!name) return '';
|
||||
let str = String(name).trim();
|
||||
str = str.replace(/^Gare de\s+/i, '').replace(/^Gare d'\s*/i, '');
|
||||
str = str.replace(/([a-zA-ZÀ-ÿ])-(sur|en|les|sous|au|aux)-([a-zA-ZÀ-ÿ])/gi, '$1-$2-$3');
|
||||
str = str.replace(/-(Viotte|Bercy|Part[- ]Dieu|Perrache|Gare[- ]de[- ]Lyon|Saint[- ]Lazare|Austerlitz|Montparnasse|Est|Nord|Ville|Centre|Rive[- ]Droite|Rive[- ]Gauche|Chantiers|Matabiau|Saint[- ]Jean|Saint[- ]Charles|Flandres|Europe|TGV|Alésia|Saint[- ]Gervais)/gi, ' $1');
|
||||
str = str.replace(/Lyon-Part-Dieu/gi, 'Lyon Part Dieu');
|
||||
str = str.replace(/Paris-Gare-de-Lyon/gi, 'Paris Gare de Lyon');
|
||||
str = str.replace(/Paris-Bercy/gi, 'Paris Bercy');
|
||||
str = str.replace(/Besançon-Viotte/gi, 'Besançon Viotte');
|
||||
str = str.replace(/Besancon-Viotte/gi, 'Besançon Viotte');
|
||||
str = str.replace(/Les-Laumes-Alésia/gi, 'Les Laumes Alésia');
|
||||
str = str.replace(/Les-Laumes-Alesia/gi, 'Les Laumes Alésia');
|
||||
str = str.replace(/Auxerre-Saint-Gervais/gi, 'Auxerre Saint-Gervais');
|
||||
str = str.replace(/Lyon-Perrache/gi, 'Lyon Perrache');
|
||||
return str;
|
||||
};
|
||||
|
||||
const getTrainMeta = (horaire) => {
|
||||
const opRaw = String(horaire.type_train || horaire.op || horaire.mode || '').trim();
|
||||
const numTrain = String(horaire.numero_train || horaire.numero || horaire.number || horaire.trip_short_name || '').trim();
|
||||
const opUpper = opRaw.toUpperCase();
|
||||
|
||||
let logoSrc = '/type/logo-sncf.svg';
|
||||
let typeLabel = 'TER';
|
||||
|
||||
if (opUpper.includes('INOUI') || opUpper.includes('TGV')) {
|
||||
logoSrc = '/type/logo-inoui.svg';
|
||||
typeLabel = 'TGV inOUI';
|
||||
} else if (opUpper.includes('OUIGO')) {
|
||||
logoSrc = '/type/logo-ouigo.svg';
|
||||
typeLabel = 'OUIGO';
|
||||
} else if (opUpper.includes('MOBIGO')) {
|
||||
logoSrc = '/type/logo-sncf.svg';
|
||||
typeLabel = 'MOBIGO';
|
||||
} else if (opUpper.includes('ICE')) {
|
||||
logoSrc = '/type/logo-ice.svg';
|
||||
typeLabel = 'ICE';
|
||||
} else if (opUpper.includes('LYRIA')) {
|
||||
logoSrc = '/type/logo-lyria.svg';
|
||||
typeLabel = 'TGV Lyria';
|
||||
} else if (opUpper.includes('INTERCIT') || opUpper.includes('IC')) {
|
||||
logoSrc = '/type/logo-sncf.svg';
|
||||
typeLabel = 'INTERCITÉS';
|
||||
} else if (opUpper.includes('CAR') || opUpper.includes('AUTOCAR') || opUpper.includes('BUS')) {
|
||||
logoSrc = '/type/logo-sncf.svg';
|
||||
typeLabel = 'AUTOCAR';
|
||||
} else if (opRaw) {
|
||||
logoSrc = '/type/logo-sncf.svg';
|
||||
typeLabel = opRaw;
|
||||
}
|
||||
|
||||
// Si c'est un numéro typique MOBIGO en région BFC (séries 89xxxx, 84xxxx)
|
||||
if (typeLabel === 'TER' && (numTrain.startsWith('89') || numTrain.startsWith('84'))) {
|
||||
typeLabel = 'MOBIGO';
|
||||
}
|
||||
|
||||
return { logoSrc, typeLabel, numTrain };
|
||||
};
|
||||
|
||||
const formatPlatform = (p) => {
|
||||
if (!p) return '';
|
||||
return String(p).toUpperCase().replace(/VOIE|QUAI|\s+/gi, '').trim();
|
||||
};
|
||||
|
||||
|
||||
function Clock(){
|
||||
const [now, setNow] = useState(new Date());
|
||||
@@ -1145,67 +1207,35 @@ export default function ClassiquesDeparts(){
|
||||
|
||||
const renderRow = (h, idx) => {
|
||||
if (h._isEmpty) {
|
||||
return <li className="aff-classiques-departs__row" key={`empty-${idx}`} style={{ background: 'transparent' }}></li>;
|
||||
return <li className="aff-classiques-departs__row aff-classiques-departs__row--empty" key={`empty-${idx}`} style={{ opacity: 0 }}></li>;
|
||||
}
|
||||
|
||||
const horaire = h || {};
|
||||
const op = horaire.type_train || horaire.op || '';
|
||||
const isCarTER = (op === 'Car TER' || op === 'Autocar');
|
||||
const time = horaire._displayTime || formatTimeNoSeconds(horaire.heure_depart || horaire.time || horaire.heure || '');
|
||||
const destination = horaire.gare_arrivee_nom || '';
|
||||
const platform = (horaire._displayPlatform || '').toString().trim();
|
||||
const { logoSrc, typeLabel, numTrain } = getTrainMeta(horaire);
|
||||
const time = horaire._displayTime || formatTimeNoSeconds(horaire.heure_depart || horaire.departure_time || horaire.time || horaire.heure || '');
|
||||
const destination = horaire.gare_arrivee_nom || horaire.gare_arrivee || horaire.destination || '';
|
||||
const formattedDestination = formatStationName(destination);
|
||||
const rawPlatform = (horaire._displayPlatform || horaire.voie || horaire.quai || '').toString().trim();
|
||||
const cleanPlatform = formatPlatform(rawPlatform);
|
||||
|
||||
const stationId = horaire.gare_depart_id || horaire.gare_id || horaire.origin_id || (gareIdParam || null);
|
||||
const stationName = gareDisplay || horaire.gare_depart_nom || horaire.gare_depart || horaire.origin_name || null;
|
||||
const status = horaire._status || computeStatusForHoraireAtStop(horaire, stationId, stationName, buildPerturbMap(perturbations));
|
||||
const isSuppressed = status && status.label === 'supprimé';
|
||||
const isDelayed = status && status.label && String(status.label).startsWith('retardé');
|
||||
|
||||
let showPlatform = !isSuppressed && Boolean(platform);
|
||||
|
||||
const isSub = isSubstitution(horaire);
|
||||
const infoText = isSub
|
||||
? (horaire.motif_substitution && horaire.motif_substitution !== 'TRAVAUX' && horaire.motif_substitution !== 'Travaux.' ? horaire.motif_substitution : 'Autocar cour de la gare Plateforme 14')
|
||||
: (status && status.causeDetails ? status.causeDetails : '');
|
||||
const trainNumber = horaire.numero_train || horaire.numero || horaire.number || '';
|
||||
const showPlatform = !isSuppressed && Boolean(cleanPlatform);
|
||||
|
||||
return (
|
||||
<li className="aff-classiques-departs__row" key={idx}>
|
||||
|
||||
{/* COL 1 : Logo opérateur */}
|
||||
<div className="aff-classiques-departs__col-logo">
|
||||
{isCarTER ? (
|
||||
<div className="aff-classiques-departs__logo-box">
|
||||
<img src="/type/sncf-voyageurs-logo.png" alt="SNCF Voyageurs" className="aff-classiques-departs__op-logo" />
|
||||
</div>
|
||||
) : (
|
||||
<Logo
|
||||
op={op}
|
||||
classNameBox="aff-classiques-departs__logo-box"
|
||||
classNameImg="aff-classiques-departs__op-logo"
|
||||
classNameText="aff-classiques-departs__logo-text"
|
||||
/>
|
||||
)}
|
||||
<img src={logoSrc} alt={typeLabel} className="aff-classiques-departs__op-logo" />
|
||||
</div>
|
||||
|
||||
{/* COL 2 : Type + Numéro (4s) OU Statut (4s) */}
|
||||
{/* COL 2 : Type + Numéro */}
|
||||
<div className="aff-classiques-departs__col-meta">
|
||||
{showStatus ? (
|
||||
isSuppressed ? (
|
||||
<div className="aff-classiques-departs__status-suppressed">supprimé</div>
|
||||
) : isDelayed ? (
|
||||
<div className="aff-classiques-departs__status-delayed">
|
||||
<div className="aff-classiques-departs__status-label">retard</div>
|
||||
<div className="aff-classiques-departs__status-value">{status.delayMinutes} min.</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="aff-classiques-departs__status-ontime">à l'heure</div>
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<div className="aff-classiques-departs__train-type">{op}</div>
|
||||
<div className="aff-classiques-departs__train-number">{trainNumber}</div>
|
||||
</>
|
||||
)}
|
||||
<div className="aff-classiques-departs__train-type">{typeLabel}</div>
|
||||
<div className="aff-classiques-departs__train-number">{numTrain}</div>
|
||||
</div>
|
||||
|
||||
{/* COL 3 : Heure (grande, jaune) */}
|
||||
@@ -1213,26 +1243,16 @@ export default function ClassiquesDeparts(){
|
||||
<div className="aff-classiques-departs__time">{time}</div>
|
||||
</div>
|
||||
|
||||
{/* COL 4 : Destination + Info cause (slide-in 5s/5s) */}
|
||||
{/* COL 4 : Destination */}
|
||||
<div className="aff-classiques-departs__col-destination">
|
||||
<MarqueeText text={destination} className="aff-classiques-departs__destination" />
|
||||
{showInfoCause && infoText ? (
|
||||
<div className="aff-classiques-departs__info-wrapper">
|
||||
<div className="aff-classiques-departs__info-inner">
|
||||
<div className="aff-classiques-departs__separator">|</div>
|
||||
<div className="aff-classiques-departs__info-text">{infoText}</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<MarqueeText text={formattedDestination} className="aff-classiques-departs__destination" />
|
||||
</div>
|
||||
|
||||
{/* COL 5 : Quai OU icône bus */}
|
||||
{/* COL 5 : Quai / Voie */}
|
||||
<div className="aff-classiques-departs__col-platform">
|
||||
{isSub || isCarTER ? (
|
||||
<img src="/type/track-car.svg" alt="Car" style={{ width: '70px', height: '70px' }} />
|
||||
) : showPlatform ? (
|
||||
{showPlatform ? (
|
||||
<div className="aff-classiques-departs__platform-box">
|
||||
<div className="aff-classiques-departs__platform-text">{platform}</div>
|
||||
<div className="aff-classiques-departs__platform-text">{cleanPlatform}</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -1241,20 +1261,25 @@ export default function ClassiquesDeparts(){
|
||||
);
|
||||
};
|
||||
|
||||
const displayList = [...horaires.slice(0, 10)];
|
||||
while (displayList.length < 10) {
|
||||
displayList.push({ _isEmpty: true });
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="aff-classiques-departs__container">
|
||||
|
||||
{/* Watermark "départs" — diagonal droite */}
|
||||
<div className="aff-classiques-departs__watermark">départs</div>
|
||||
{/* Watermark vertical "départs" sur la droite */}
|
||||
<div className="aff-classiques-departs__sidebar" aria-hidden>
|
||||
<div className="aff-classiques-departs__sidebar-text">départs</div>
|
||||
</div>
|
||||
|
||||
{defilementMode ? (
|
||||
/* ── MODE DÉFILEMENT : marquee vertical infini ── */
|
||||
<div className="aff-classiques-departs__scroll-container">
|
||||
{horaires.length === 0 ? (
|
||||
<ul className="aff-classiques-departs__list">
|
||||
<li className="aff-classiques-departs__row aff-classiques-departs__row--empty">
|
||||
<div>Aucun horaire disponible.</div>
|
||||
</li>
|
||||
{displayList.map((h, idx) => renderRow(h, idx))}
|
||||
</ul>
|
||||
) : (
|
||||
<ul
|
||||
@@ -1267,26 +1292,10 @@ export default function ClassiquesDeparts(){
|
||||
) : (
|
||||
/* ── MODE NORMAL : 10 lignes statiques ── */
|
||||
<ul className="aff-classiques-departs__list">
|
||||
{horaires.length === 0 ? (
|
||||
<li className="aff-classiques-departs__row aff-classiques-departs__row--empty" key="empty">
|
||||
<div>Aucun horaire disponible.</div>
|
||||
<div style={{fontSize:'0.8rem', opacity:0.8}}>lignes brutes renvoyées : {rawDebug.count}</div>
|
||||
{rawDebug.count > 0 && rawDebug.sample && rawDebug.sample.length > 0 ? (
|
||||
<pre style={{maxHeight:160, overflow:'auto', fontSize:'0.75rem', marginTop:8, background:'#111', color:'#fff', padding:8}}>
|
||||
{JSON.stringify(rawDebug.sample.slice(0,2), null, 2).slice(0,1000)}
|
||||
{JSON.stringify(rawDebug.sample.slice(0,2), null, 2).length > 1000 ? '…(truncated)' : ''}
|
||||
</pre>
|
||||
) : null}
|
||||
</li>
|
||||
) : horaires.map((h, idx) => renderRow(h, idx))}
|
||||
{displayList.map((h, idx) => renderRow(h, idx))}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{/* Horloge — superposée en bas à droite */}
|
||||
<div className="aff-classiques-departs__footer" aria-hidden>
|
||||
<Clock />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user