-> Refonte du design de sélection d'afficheurs -> Ajout du Mode PiP -> Patch du bug du logo de l'horaire pas affiché. Corrigé pour les Horaires TER à 6 numéros.
281 lines
13 KiB
JavaScript
281 lines
13 KiB
JavaScript
import React, { useEffect, useState } from 'react';
|
|
import { WcsCard, WcsCardBody, WcsButton, WcsBadge } from 'wcs-react';
|
|
import './App.css';
|
|
|
|
import { applyPolyfills, defineCustomElements } from 'wcs-core/loader';
|
|
import Departs from './pages/Departs';
|
|
import Arrivees from './pages/Arrivees';
|
|
import Displays from './pages/Displays';
|
|
import ClassiquesDeparts from './pages/afficheurs/classiques/ClassiquesDeparts';
|
|
import ClassiquesArrivees from './pages/afficheurs/classiques/ClassiquesArrivees';
|
|
import ClassiquesDepartsQuais from './pages/afficheurs/classiques/quais/ClassiquesDepartsQuais';
|
|
import ClassiquesArriveesQuais from './pages/afficheurs/classiques/quais/ClassiquesArriveesQuais';
|
|
import ClassiquesFlashInfo from './pages/afficheurs/classiques/ClassiquesFlashInfo';
|
|
import ClassiquesInformations from './pages/afficheurs/classiques/ClassiquesInformations';
|
|
import AFLDeparts from './pages/afficheurs/afl/AFLDeparts';
|
|
import AFLArrivees from './pages/afficheurs/afl/AFLArrivees';
|
|
import AFLDepartsQuais from './pages/afficheurs/afl/AFLDepartsQuais';
|
|
import AFLArriveesQuais from './pages/afficheurs/afl/AFLArriveesQuais';
|
|
import EVAArrivees from './pages/afficheurs/eva/EVAArrivees';
|
|
import EVADeparts from './pages/afficheurs/eva/EVADeparts';
|
|
import SelectAfficheur from './pages/SelectAfficheur';
|
|
import AfficheurViewportWrapper from './components/AfficheurViewportWrapper';
|
|
import sessionMemory from './lib/sessionMemory';
|
|
import { getSourceFromPath } from './services/railwayService';
|
|
|
|
applyPolyfills().then(() => defineCustomElements(window));
|
|
|
|
function useSimpleRouter() {
|
|
function normalizePath(p) {
|
|
if (!p) return '/';
|
|
p = String(p).split(/[?#]/)[0];
|
|
if (p.indexOf('index.html') !== -1) return '/';
|
|
p = p.trim();
|
|
if (p !== '/') p = p.replace(/\/+$/, '');
|
|
return p || '/';
|
|
}
|
|
|
|
const [route, setRoute] = useState(() => normalizePath(window.location.pathname || '/'));
|
|
|
|
useEffect(() => {
|
|
const onPop = () => {
|
|
const p = normalizePath(window.location.pathname || '/');
|
|
console.debug('[router] popstate ->', p);
|
|
setRoute(p);
|
|
};
|
|
window.addEventListener('popstate', onPop);
|
|
return () => window.removeEventListener('popstate', onPop);
|
|
}, []);
|
|
|
|
function navigate(path) {
|
|
const target = normalizePath(path);
|
|
const currentFull = window.location.pathname + (window.location.search || '') + (window.location.hash || '');
|
|
if (currentFull !== path) {
|
|
console.debug('[router] navigate ->', path, '(normalized ->', target, ')');
|
|
try {
|
|
window.history.pushState({}, '', path);
|
|
} catch (e) {
|
|
try { window.history.pushState({}, '', target); } catch (err) {}
|
|
}
|
|
setRoute(target);
|
|
} else {
|
|
setRoute(target);
|
|
}
|
|
}
|
|
|
|
return { route, navigate };
|
|
}
|
|
|
|
function App() {
|
|
const { route, navigate } = useSimpleRouter();
|
|
|
|
const source = getSourceFromPath(route);
|
|
const prefix = source === 'supabase' ? '/ferrovia' : '';
|
|
const cleanRoute = (route.replace(/^\/ferrovia/, '') || '/');
|
|
|
|
function Home({ navigate, prefix, source }) {
|
|
const POPULAR_HOME_GARES = [
|
|
'Paris-Gare-de-Lyon',
|
|
'Besançon-Viotte',
|
|
'Dijon-Ville',
|
|
'Lyon-Part-Dieu',
|
|
'Marseille-Saint-Charles',
|
|
'Lille-Flandres',
|
|
'Strasbourg',
|
|
'Bordeaux-Saint-Jean'
|
|
];
|
|
|
|
return (
|
|
<div className="launcher-container" style={{ padding: '24px', maxWidth: '900px', margin: '0 auto' }}>
|
|
<WcsCard mode="raised">
|
|
<WcsCardBody>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '24px' }}>
|
|
<div>
|
|
<h1 style={{ margin: '0 0 8px 0', fontSize: '24px', color: 'var(--wcs-primary)' }}>🚅 Téléaffichage Ferroviaire SNCF</h1>
|
|
<p style={{ margin: 0, color: 'var(--wcs-text-light)' }}>
|
|
{source === 'supabase'
|
|
? "Consultez les afficheurs basés sur les données de votre BDD Supabase."
|
|
: "Simulateur d'écrans de gare officiels connectés en direct aux flux GTFS & Open Data SNCF."}
|
|
</p>
|
|
</div>
|
|
<WcsBadge className={source === 'supabase' ? 'wcs-secondary' : 'wcs-primary'} shape="rounded">
|
|
{source === 'supabase' ? '🗄️ Mode Supabase' : '🌐 Mode SNCF Live'}
|
|
</WcsBadge>
|
|
</div>
|
|
|
|
<div style={{ display: 'flex', gap: '24px', flexDirection: 'row', flexWrap: 'wrap', marginBottom: '24px' }}>
|
|
<WcsCard style={{ flex: '1 1 300px', cursor: 'pointer', border: '1px solid var(--wcs-light)' }} mode="flat" onClick={() => {
|
|
try { sessionMemory.set('afficheurMode', 'departs'); } catch (e) {}
|
|
navigate(`${prefix}/departs`);
|
|
}}>
|
|
<WcsCardBody>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
|
<span style={{ fontSize: '32px' }}>🚆</span>
|
|
<WcsBadge className="wcs-success">Départs</WcsBadge>
|
|
</div>
|
|
<h2 style={{ fontSize: '18px', marginTop: '16px' }}>Prochains Départs</h2>
|
|
<p style={{ color: 'var(--wcs-text-light)', minHeight: '60px' }}>Consultez les trains au départ, leurs voies, retards et dessertes complètes.</p>
|
|
<WcsButton className="wcs-primary" style={{ width: '100%' }}>Consulter les Départs</WcsButton>
|
|
</WcsCardBody>
|
|
</WcsCard>
|
|
|
|
<WcsCard style={{ flex: '1 1 300px', cursor: 'pointer', border: '1px solid var(--wcs-light)' }} mode="flat" onClick={() => {
|
|
try { sessionMemory.set('afficheurMode', 'arrivees'); } catch (e) {}
|
|
navigate(`${prefix}/arrivees`);
|
|
}}>
|
|
<WcsCardBody>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
|
<span style={{ fontSize: '32px' }}>🏁</span>
|
|
<WcsBadge className="wcs-info">Arrivées</WcsBadge>
|
|
</div>
|
|
<h2 style={{ fontSize: '18px', marginTop: '16px' }}>Prochaines Arrivées</h2>
|
|
<p style={{ color: 'var(--wcs-text-light)', minHeight: '60px' }}>Suivez en direct les trains attendus en gare, provenances et voies assignées.</p>
|
|
<WcsButton className="wcs-secondary" style={{ width: '100%' }}>Consulter les Arrivées</WcsButton>
|
|
</WcsCardBody>
|
|
</WcsCard>
|
|
</div>
|
|
|
|
<div style={{ marginBottom: '24px' }}>
|
|
<p style={{ fontWeight: 'bold', marginBottom: '12px' }}>Accès direct par gare :</p>
|
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px' }}>
|
|
{POPULAR_HOME_GARES.map((gName) => (
|
|
<WcsButton
|
|
key={gName}
|
|
mode="stroked"
|
|
shape="round"
|
|
size="s"
|
|
onClick={() => {
|
|
try { sessionMemory.set('afficheurMode', 'departs'); } catch (e) {}
|
|
navigate(`${prefix}/afficheurs/${encodeURIComponent(gName)}?mode=departs`);
|
|
}}
|
|
>
|
|
📍 {gName.replace(/-/g, ' ')}
|
|
</WcsButton>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<hr style={{ border: 'none', borderTop: '1px solid var(--wcs-light)', margin: '24px 0' }} />
|
|
<div style={{ fontSize: '14px', color: 'var(--wcs-text-light)', lineHeight: 1.6 }}>
|
|
✨ <strong>Formats disponibles :</strong> Afficheurs Fluides & Lumineux (AFL), Téléaffichage historique (Classiques), Écrans Vidéo Voyageurs (EVA) et Écrans par Quai / Voie.
|
|
</div>
|
|
</WcsCardBody>
|
|
</WcsCard>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (
|
|
cleanRoute.startsWith('/afficheurs/classiques/flash-info') ||
|
|
cleanRoute.startsWith('/afficheurs/classiques/flash-circulation') ||
|
|
cleanRoute.startsWith('/afficheurs/departs/classiques/flash-info') ||
|
|
cleanRoute.startsWith('/afficheurs/arrivees/classiques/flash-info')
|
|
) {
|
|
return <AfficheurViewportWrapper><ClassiquesFlashInfo /></AfficheurViewportWrapper>;
|
|
}
|
|
if (
|
|
cleanRoute.startsWith('/afficheurs/classiques/informations') ||
|
|
cleanRoute.startsWith('/afficheurs/classiques/infos') ||
|
|
cleanRoute.startsWith('/afficheurs/departs/classiques/informations') ||
|
|
cleanRoute.startsWith('/afficheurs/arrivees/classiques/informations')
|
|
) {
|
|
return <AfficheurViewportWrapper><ClassiquesInformations /></AfficheurViewportWrapper>;
|
|
}
|
|
if (cleanRoute.startsWith('/afficheurs/departs/classiques/quais')) {
|
|
return <AfficheurViewportWrapper><ClassiquesDepartsQuais /></AfficheurViewportWrapper>;
|
|
}
|
|
if (cleanRoute.startsWith('/afficheurs/arrivees/classiques/quais')) {
|
|
return <AfficheurViewportWrapper><ClassiquesArriveesQuais /></AfficheurViewportWrapper>;
|
|
}
|
|
if (cleanRoute.startsWith('/afficheurs/departs/classiques')) {
|
|
return <AfficheurViewportWrapper><ClassiquesDeparts /></AfficheurViewportWrapper>;
|
|
}
|
|
if (cleanRoute.startsWith('/afficheurs/departs/afl/quais')) {
|
|
return <AfficheurViewportWrapper><AFLDepartsQuais /></AfficheurViewportWrapper>;
|
|
}
|
|
if (cleanRoute.startsWith('/afficheurs/departs/afl')) {
|
|
return <AfficheurViewportWrapper><AFLDeparts /></AfficheurViewportWrapper>;
|
|
}
|
|
if (cleanRoute.startsWith('/afficheurs/departs/eva')) {
|
|
return <AfficheurViewportWrapper><EVADeparts /></AfficheurViewportWrapper>;
|
|
}
|
|
if (cleanRoute.startsWith('/afficheurs/arrivees/afl/quais')) {
|
|
return <AfficheurViewportWrapper><AFLArriveesQuais /></AfficheurViewportWrapper>;
|
|
}
|
|
if (cleanRoute.startsWith('/afficheurs/arrivees/afl')) {
|
|
return <AfficheurViewportWrapper><AFLArrivees /></AfficheurViewportWrapper>;
|
|
}
|
|
if (cleanRoute.startsWith('/afficheurs/arrivees/eva')) {
|
|
return <AfficheurViewportWrapper><EVAArrivees /></AfficheurViewportWrapper>;
|
|
}
|
|
if (cleanRoute.startsWith('/afficheurs/arrivees/classiques')) {
|
|
return <AfficheurViewportWrapper><ClassiquesArrivees /></AfficheurViewportWrapper>;
|
|
}
|
|
|
|
let content;
|
|
if (cleanRoute === '/departs') {
|
|
content = <Departs onBack={() => navigate(prefix || '/')} navigate={navigate} />;
|
|
} else if (cleanRoute === '/arrivees') {
|
|
content = <Arrivees onBack={() => navigate(prefix || '/')} navigate={navigate} />;
|
|
} else if (cleanRoute === '/select-afficheur') {
|
|
content = <SelectAfficheur onBack={() => {
|
|
const params = new URLSearchParams(window.location.search);
|
|
const gare = params.get('gare') || '';
|
|
const mode = params.get('mode') || 'departs';
|
|
navigate(`${prefix}/afficheurs/${encodeURIComponent(gare)}?mode=${mode}`);
|
|
}} navigate={navigate} />;
|
|
} else if (cleanRoute.startsWith('/afficheurs')) {
|
|
content = <Displays onBack={() => navigate(prefix || '/')} navigate={navigate} />;
|
|
} else {
|
|
content = <Home navigate={navigate} prefix={prefix} source={source} />;
|
|
}
|
|
|
|
return (
|
|
<div className="sncf-voyageurs" style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column' }}>
|
|
<wcs-galactic app-name="Affichage Ferroviaire" version="1.0.0">
|
|
{source === 'supabase' ? (
|
|
<wcs-galactic-menu-item>
|
|
<a
|
|
href="/"
|
|
onClick={(e) => { e.preventDefault(); navigate('/'); }}
|
|
style={{ color: '#008b47', fontWeight: 600 }}
|
|
title="Basculer vers le mode SNCF GTFS"
|
|
>
|
|
🌐 Passer en mode SNCF Live
|
|
</a>
|
|
</wcs-galactic-menu-item>
|
|
) : null}
|
|
</wcs-galactic>
|
|
|
|
<header style={{ padding: 12, background: source === 'supabase' ? '#003366' : '#00439e', color: '#fff', transition: 'background 0.3s' }}>
|
|
<div style={{ maxWidth: 1000, margin: '0 auto', display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '0 8px' }}>
|
|
<div>
|
|
<h1 style={{ margin: 0, fontSize: 20 }}>
|
|
{source === 'supabase' ? 'Affichage Ferroviaire — BDD Supabase (/ferrovia)' : 'Affichage Ferroviaire — SNCF GTFS / Temps Réel'}
|
|
</h1>
|
|
<div style={{ fontSize: 12, opacity: 0.9 }}>Route actuelle: {route}</div>
|
|
</div>
|
|
<div style={{ fontSize: '13px', background: 'rgba(255,255,255,0.15)', padding: '6px 12px', borderRadius: '6px' }}>
|
|
Source: <strong>{source === 'supabase' ? 'Supabase Database' : 'SNCF Open Data / GTFS'}</strong>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main style={{ flex: 1, background: '#f3f6fb', overflowY: 'visible', width: '100%' }}>
|
|
<div style={{ maxWidth: 1000, margin: '16px auto', width: '100%' }}>{content}</div>
|
|
</main>
|
|
|
|
<wcs-footer>
|
|
<wcs-footer-item>
|
|
<a href="/contact">Contact</a>
|
|
</wcs-footer-item>
|
|
<wcs-footer-item>
|
|
<a href="/mentions-legales">Mentions Légales</a>
|
|
</wcs-footer-item>
|
|
</wcs-footer>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default App;
|