Files
ferrovia-infogare/src/App.js
T
mathis b1f180508e RELEASE V4.1.0 : Voici les modifications apportées :
-> Refonte complète des afficheurs AFL départ et arrivée (+ Quais)
-> EVA DÉPARTS + ARRIVÉES

EN COURS DE DÉVELOPPEMENT :
-> REFONTE AFFICHEURS CLASSIQUES
-> IMPLÉMENTATION DONNÉES GTFS / GTFS-RT / SIRI LTE
2026-08-29 18:24:44 +02:00

216 lines
9.1 KiB
JavaScript

import React, { useEffect, useState } from '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 sessionMemory from './lib/sessionMemory';
import ClassiquesArriveesQuais from "./pages/afficheurs/classiques/quais/ClassiquesArriveesQuais";
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';
applyPolyfills().then(() => defineCustomElements(window));
// Petit routeur client sans dépendances externes
function useSimpleRouter() {
function normalizePath(p) {
if (!p) return '/';
// retirer query string et hash pour normaliser le chemin utilisé en internal route
p = String(p).split(/[?#]/)[0];
// traiter les cas comme /index.html
if (p.indexOf('index.html') !== -1) return '/';
// supprimer espaces en début/fin
p = p.trim();
// supprimer slashs finaux sauf pour la racine
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);
// toujours pousser l'URL complète (path peut contenir query/hash),
// mais stocker en internal state la version normalisée (sans query/hash)
// comparer l'URL complète (pathname + search + hash) afin de permettre
// des navigations qui ne changent que la querystring (ex. ?gare=...)
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) {
// fallback si path est invalide
try { window.history.pushState({}, '', target); } catch (err) { /* ignore */ }
}
// mettre à jour la route interne (sans query/hash)
setRoute(target);
} else {
// si l'URL complète est identique, forcer un update de state pour
// déclencher un rendu si besoin (rare)
setRoute(target);
}
}
return { route, navigate };
}
function App() {
const { route, navigate } = useSimpleRouter();
// Composant Home explicite (panels cliquables)
function Home({ navigate }) {
useEffect(() => {
console.debug('[Home] monté, pathname =', window.location.pathname);
}, []);
const panelStyle = {
width: '40%',
cursor: 'pointer',
padding: 24,
borderRadius: 8,
boxShadow: '0 2px 8px rgba(0,0,0,0.12)',
background: '#fff',
textAlign: 'center'
};
return (
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', padding: '40px', alignItems: 'center', background: '#e9f0ff', paddingTop: 32, paddingBottom: 32 }}>
<div style={{ width: '100%', textAlign: 'center', marginBottom: 16 }}>
<h1 style={{ margin: 0, fontSize: 28 }}>Accueil</h1>
<p style={{ margin: '8px 0 20px', color: '#333' }}>Choisissez une section ci-dessous</p>
</div>
<div style={{ display: 'flex', justifyContent: 'space-around', width: '100%' }}>
<div
role="button"
tabIndex={0}
onClick={() => { try { sessionMemory.set('afficheurMode', 'departs'); } catch (e) {} navigate('/departs'); }}
onKeyDown={(e) => (e.key === 'Enter' || e.key === ' ' ? (function(){ try { sessionMemory.set('afficheurMode', 'departs'); } catch (e) {} navigate('/departs'); })() : null)}
style={panelStyle}
aria-label="Accéder aux départs"
>
<h2>Départs</h2>
<p>Consultez les prochains départs d'une gare. Cliquez pour choisir une gare et voir les horaires.</p>
</div>
<div
role="button"
tabIndex={0}
onClick={() => { try { sessionMemory.set('afficheurMode', 'arrivees'); } catch (e) {} navigate('/arrivees'); }}
onKeyDown={(e) => (e.key === 'Enter' || e.key === ' ' ? (function(){ try { sessionMemory.set('afficheurMode', 'arrivees'); } catch (e) {} navigate('/arrivees'); })() : null)}
style={panelStyle}
aria-label="Accéder aux arrivées"
>
<h2>Arrivées</h2>
<p>Consultez les prochaines arrivées d'une gare. Cliquez pour choisir une gare et voir les informations.</p>
</div>
</div>
</div>
);
}
// Routes pour afficheurs : format /afficheurs/{mode}/{type}?gare=...
// route plus spécifique pour l'afficheur "quais"
if (route.startsWith('/afficheurs/departs/classiques/quais')) {
return <AfficheurViewportWrapper><ClassiquesDepartsQuais /></AfficheurViewportWrapper>;
}
if (route.startsWith('/afficheurs/arrivees/classiques/quais')) {
return <AfficheurViewportWrapper><ClassiquesArriveesQuais /></AfficheurViewportWrapper>;
}
if (route.startsWith('/afficheurs/departs/classiques')) {
return <AfficheurViewportWrapper><ClassiquesDeparts /></AfficheurViewportWrapper>;
}
if (route.startsWith('/afficheurs/departs/afl/quais')) {
return <AfficheurViewportWrapper><AFLDepartsQuais /></AfficheurViewportWrapper>;
}
if (route.startsWith('/afficheurs/departs/afl')) {
return <AfficheurViewportWrapper><AFLDeparts /></AfficheurViewportWrapper>;
}
if (route.startsWith('/afficheurs/departs/eva')) {
return <AfficheurViewportWrapper><EVADeparts /></AfficheurViewportWrapper>;
}
if (route.startsWith('/afficheurs/arrivees/afl/quais')) {
return <AfficheurViewportWrapper><AFLArriveesQuais /></AfficheurViewportWrapper>;
}
if (route.startsWith('/afficheurs/arrivees/afl')) {
return <AfficheurViewportWrapper><AFLArrivees /></AfficheurViewportWrapper>;
}
if (route.startsWith('/afficheurs/arrivees/eva')) {
return <AfficheurViewportWrapper><EVAArrivees /></AfficheurViewportWrapper>;
}
if (route.startsWith('/afficheurs/arrivees/classiques')) {
return <AfficheurViewportWrapper><ClassiquesArrivees /></AfficheurViewportWrapper>;
}
let content;
if (route === '/departs') {
content = <Departs onBack={() => navigate('/')} navigate={navigate} />;
} else if (route === '/arrivees') {
content = <Arrivees onBack={() => navigate('/')} navigate={navigate} />;
} else if (route === '/select-afficheur') {
content = <SelectAfficheur onBack={() => {
const params = new URLSearchParams(window.location.search);
const gare = params.get('gare') || '';
const mode = params.get('mode') || 'departs';
navigate(`/afficheurs/${encodeURIComponent(gare)}?mode=${mode}`);
}} navigate={navigate} />;
} else if (route.startsWith('/afficheurs')) {
content = <Displays onBack={() => navigate('/')} navigate={navigate} />;
} else {
content = <Home navigate={navigate} />;
}
return (
<div className="sncf-voyageurs" style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column' }}>
<wcs-galactic app-name="Affichage Ferroviaire" version="1.0.0">
<wcs-galactic-menu-item>
<a href="/departs" onClick={(e) => { e.preventDefault(); navigate('/departs'); }}>Départs</a>
</wcs-galactic-menu-item>
<wcs-galactic-menu-item>
<a href="/arrivees" onClick={(e) => { e.preventDefault(); navigate('/arrivees'); }}>Arrivées</a>
</wcs-galactic-menu-item>
</wcs-galactic>
<header style={{ padding: 12, background: '#00439e', color: '#fff' }}>
<div style={{ maxWidth: 1000, margin: '0 auto' }}>
<h1 style={{ margin: 0, fontSize: 20 }}>Affichage Ferroviaire — Accueil</h1>
<div style={{ fontSize: 12, opacity: 0.9 }}>Route actuelle: {route}</div>
</div>
</header>
<main style={{ flex: 1, background: '#f3f6fb' }}>
<div style={{ maxWidth: 1000, margin: '24px auto' }}>{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;