RELEASE V5.5.0 : Voici les modifications apportées :

-> REFONTE TOTALE DE TOUS LES AFFICHEURS CLASSIQUES + QUAIS

FONCTIONNALITÉS EN COURS DE DÉVELOPPEMENT :

-> ADAPTABILITÉ EN FONCTION DES SITUATIONS
This commit is contained in:
mathis committed 2026-08-31 02:02:46 +02:00
1 parent b90c699302
commit 9c6ac798f1
26 files changed
+3040 -2190

No files matched your search

+24 -24
View File
@@ -3,133 +3,133 @@
"logos": [
{
"file": "logo-aleop.svg",
"path": "/img/type/logo-aleop.svg",
"path": "/logo-aleop.svg",
"slug": "aleop",
"name": "Aleop"
},
{
"file": "logo-auvergerhonealpes.svg",
"path": "/img/type/logo-auvergerhonealpes.svg",
"path": "/logo-auvergerhonealpes.svg",
"slug": "auvergerhonealpes",
"name": "Auvergerhonealpes"
},
{
"file": "logo-breizhgo.svg",
"path": "/img/type/logo-breizhgo.svg",
"path": "/logo-breizhgo.svg",,
"slug": "breizhgo",
"name": "Breizhgo"
},
{
"file": "logo-cfl.svg",
"path": "/img/type/logo-cfl.svg",
"path": "/logo-cfl.svg",
"slug": "cfl",
"name": "CFL"
},
{
"file": "logo-db.svg",
"path": "/img/type/logo-db.svg",
"path": "/logo-db.svg",
"slug": "db",
"name": "DB"
},
{
"file": "logo-eurostar.svg",
"path": "/img/type/logo-eurostar.svg",
"path": "/logo-eurostar.svg",
"slug": "eurostar",
"name": "Eurostar"
},
{
"file": "logo-fluo.svg",
"path": "/img/type/logo-fluo.svg",
"path": "/logo-fluo.svg",
"slug": "fluo",
"name": "Fluo"
},
{
"file": "logo-frecciarossa.svg",
"path": "/img/type/logo-frecciarossa.svg",
"path": "/logo-frecciarossa.svg",
"slug": "frecciarossa",
"name": "Frecciarossa"
},
{
"file": "logo-hautsdefrance.svg",
"path": "/img/type/logo-hautsdefrance.svg",
"path": "/logo-hautsdefrance.svg",
"slug": "hautsdefrance",
"name": "Hautsdefrance"
},
{
"file": "logo-ice.svg",
"path": "/img/type/logo-ice.svg",
"path": "/logo-ice.svg",
"slug": "ice",
"name": "ICE"
},
{
"file": "logo-intercites.svg",
"path": "/img/type/logo-intercites.svg",
"path": "/logo-intercites.svg",
"slug": "intercites",
"name": "Intercités"
},
{
"file": "logo-ler.svg",
"path": "/img/type/logo-ler.svg",
"path": "/logo-ler.svg",
"slug": "ler",
"name": "Ler"
},
{
"file": "logo-lio.svg",
"path": "/img/type/logo-lio.svg",
"path": "/logo-lio.svg",
"slug": "lio",
"name": "Lio"
},
{
"file": "logo-lunea.svg",
"path": "/img/type/logo-lunea.svg",
"path": "/logo-lunea.svg",
"slug": "lunea",
"name": "Lunea"
},
{
"file": "logo-metrolor.svg",
"path": "/img/type/logo-metrolor.svg",
"path": "/logo-metrolor.svg",
"slug": "metrolor",
"name": "Metrolor"
},
{
"file": "logo-mobigo.svg",
"path": "/img/type/logo-mobigo.svg",
"path": "/logo-mobigo.svg",
"slug": "mobigo",
"name": "Mobigo"
},
{
"file": "logo-nomad.svg",
"path": "/img/type/logo-nomad.svg",
"path": "/logo-nomad.svg",
"slug": "nomad",
"name": "Nomad"
},
{
"file": "logo-occitanie.svg",
"path": "/img/type/logo-occitanie.svg",
"path": "/logo-occitanie.svg",
"slug": "occitanie",
"name": "Occitanie"
},
{
"file": "logo-ouigo.svg",
"path": "/img/type/logo-ouigo.svg",
"path": "/logo-ouigo.svg",
"slug": "ouigo",
"name": "TGV Ouigo"
},
{
"file": "logo-ouigo-classique.svg",
"path": "/img/type/logo-ouigo-classique.svg",
"path": "/logo-ouigo-classique.svg",
"slug": "ouigo-classique",
"name": "OUIGO Trains Classiques"
},
{
"file": "logo-remi.svg",
"path": "/img/type/logo-remi.svg",
"path": "/logo-remi.svg",
"slug": "remi",
"name": "Remi"
},
{
"file": "logo-renfeave.svg",
"path": "/img/type/logo-renfeave.svg",
"path": "/logo-renfeave.svg",
"slug": "renfeave",
"name": "Renfe AVE"
},
@@ -255,8 +255,8 @@
},
{
"file": "sncf-voyageurs-logo.png",
"path": "/img/type/sncf-voyageurs-logo.png",
"slug": "ter",
"path": "/sncf-voyageurs-logo.png",
"slug": "TER",
"name": "TER"
},
{
+63 -63
View File
@@ -3,379 +3,379 @@
"logos": [
{
"file": "logo-aleop-color.svg",
"path": "/img/type/logo-aleop-color.svg",
"path": "/type/quais/logo-aleop-color.svg",
"slug": "aleop",
"name": "Aleop"
},
{
"file": "logo-auvergerhonealpes-color.svg",
"path": "/img/type/logo-auvergerhonealpes-color.svg",
"path": "/type/quais/logo-auvergerhonealpes-color.svg",
"slug": "auvergerhonealpes",
"name": "Auvergerhonealpes"
},
{
"file": "logo-breizhgo-color.svg",
"path": "/img/type/logo-breizhgo-color.svg",
"path": "/type/quais/logo-breizhgo-color.svg",
"slug": "breizhgo",
"name": "Breizhgo"
},
{
"file": "logo-cfl-color.svg",
"path": "/img/type/logo-cfl-color.svg",
"path": "/type/quais/logo-cfl-color.svg",
"slug": "cfl",
"name": "CFL"
},
{
"file": "logo-db-color.svg",
"path": "/img/type/logo-db-color.svg",
"path": "/type/quais/logo-db-color.svg",
"slug": "db",
"name": "DB"
},
{
"file": "logo-eurostar-color.svg",
"path": "/img/type/logo-eurostar-color.svg",
"path": "/type/quais/logo-eurostar-color.svg",
"slug": "eurostar",
"name": "Eurostar"
},
{
"file": "logo-fluo-color.svg",
"path": "/img/type/logo-fluo-color.svg",
"path": "/type/quais/logo-fluo-color.svg",
"slug": "fluo",
"name": "Fluo"
},
{
"file": "logo-frecciarossa-color.svg",
"path": "/img/type/logo-frecciarossa-color.svg",
"path": "/type/quais/logo-frecciarossa-color.svg",
"slug": "frecciarossa",
"name": "Frecciarossa"
},
{
"file": "logo-hautsdefrance-color.svg",
"path": "/img/type/logo-hautsdefrance-color.svg",
"path": "/type/quais/logo-hautsdefrance-color.svg",
"slug": "hautsdefrance",
"name": "Hautsdefrance"
},
{
"file": "logo-ice-color.svg",
"path": "/img/type/logo-ice-color.svg",
"path": "/type/quais/logo-ice-color.svg",
"slug": "ice",
"name": "ICE"
},
{
"file": "logo-intercites-color.svg",
"path": "/img/type/logo-intercites-color.svg",
"path": "/type/quais/logo-intercites-color.svg",
"slug": "intercites",
"name": "Intercités"
},
{
"file": "logo-ler-color.svg",
"path": "/img/type/logo-ler-color.svg",
"path": "/type/quais/logo-ler-color.svg",
"slug": "ler",
"name": "Ler"
},
{
"file": "logo-lio-color.svg",
"path": "/img/type/logo-lio-color.svg",
"path": "/type/quais/logo-lio-color.svg",
"slug": "lio",
"name": "Lio"
},
{
"file": "logo-lunea-color.svg",
"path": "/img/type/logo-lunea-color.svg",
"path": "/type/quais/logo-lunea-color.svg",
"slug": "lunea",
"name": "Lunea"
},
{
"file": "logo-metrolor-color.svg",
"path": "/img/type/logo-metrolor-color.svg",
"path": "/type/quais/logo-metrolor-color.svg",
"slug": "metrolor",
"name": "Metrolor"
},
{
"file": "logo-mobigo-color.svg",
"path": "/img/type/logo-mobigo-color.svg",
"path": "/type/quais/logo-mobigo-color.svg",
"slug": "mobigo",
"name": "Mobigo"
},
{
"file": "logo-nomad-color.svg",
"path": "/img/type/logo-nomad-color.svg",
"path": "/type/quais/logo-nomad-color.svg",
"slug": "nomad",
"name": "Nomad"
},
{
"file": "logo-occitanie-color.svg",
"path": "/img/type/logo-occitanie-color.svg",
"path": "/type/quais/logo-occitanie-color.svg",
"slug": "occitanie",
"name": "Occitanie"
},
{
"file": "logo-ouigo-color.svg",
"path": "/img/type/logo-ouigo-color.svg",
"path": "/type/quais/logo-ouigo-color.svg",
"slug": "ouigo",
"name": "OUIGO"
},
{
"file": "logo-ouigo-classique-color.svg",
"path": "/img/type/logo-ouigo-classique-color.svg",
"path": "/type/quais/logo-ouigo-classique-color.svg",
"slug": "ouigo-classique",
"name": "OUIGO Trains Classiques"
},
{
"file": "logo-remi-color.svg",
"path": "/img/type/logo-remi-color.svg",
"path": "/type/quais/logo-remi-color.svg",
"slug": "remi",
"name": "Remi"
},
{
"file": "logo-renfeave-color.svg",
"path": "/img/type/logo-renfeave-color.svg",
"path": "/type/quais/logo-renfeave-color.svg",
"slug": "renfeave",
"name": "Renfe AVE"
},
{
"file": "logo-rer-a-color.svg",
"path": "/img/type/logo-rer-a-color.svg",
"path": "/type/quais/logo-rer-a-color.svg",
"slug": "rer-a",
"name": "RER A"
},
{
"file": "logo-rer-b-color.svg",
"path": "/img/type/logo-rer-b-color.svg",
"path": "/type/quais/logo-rer-b-color.svg",
"slug": "rer-b",
"name": "RER B"
},
{
"file": "logo-rer-c-color.svg",
"path": "/img/type/logo-rer-c-color.svg",
"path": "/type/quais/logo-rer-c-color.svg",
"slug": "rer-c",
"name": "RER C"
},
{
"file": "logo-rer-d-color.svg",
"path": "/img/type/logo-rer-d-color.svg",
"path": "/type/quais/logo-rer-d-color.svg",
"slug": "rer-d",
"name": "RER D"
},
{
"file": "logo-rer-e-color.svg",
"path": "/img/type/logo-rer-e-color.svg",
"path": "/type/quais/logo-rer-e-color.svg",
"slug": "rer-e",
"name": "RER E"
},
{
"file": "logo-sbb-color.svg",
"path": "/img/type/logo-sbb-color.svg",
"path": "/type/quais/logo-sbb-color.svg",
"slug": "sbb",
"name": "SBB"
},
{
"file": "logo-sncb-color.svg",
"path": "/img/type/logo-sncb-color.svg",
"path": "/type/quais/logo-sncb-color.svg",
"slug": "sncb",
"name": "SNCB"
},
{
"file": "logo-sncf-color.svg",
"path": "/img/type/logo-sncf-color.svg",
"path": "/type/quais/logo-sncf-color.svg",
"slug": "sncf",
"name": "Sncf"
},
{
"file": "sncf-voyageurs-logo-color.png",
"path": "/img/type/sncf-voyageurs-logo-color.png",
"path": "/type/quais/img/type/sncf-voyageurs-logo-color.png",
"slug": "sncf-voyageurs-logo",
"name": "SNCF"
},
{
"file": "logo-sncf1985.svg",
"path": "/img/type/logo-sncf1985.svg",
"path": "/type/quais/img/type/logo-sncf1985.svg",
"slug": "sncf1985",
"name": "Sncf1985"
},
{
"file": "logo-sncf1992.svg",
"path": "/img/type/logo-sncf1992.svg",
"path": "/type/quais/img/type/logo-sncf1992.svg",
"slug": "sncf1992",
"name": "Sncf1992"
},
{
"file": "logo-t-h.svg",
"path": "/img/type/logo-t-h.svg",
"path": "/type/quais/img/type/logo-t-h.svg",
"slug": "t-h",
"name": "T H"
},
{
"file": "logo-t-j.svg",
"path": "/img/type/logo-t-j.svg",
"path": "/type/quais/img/type/logo-t-j.svg",
"slug": "t-j",
"name": "T J"
},
{
"file": "logo-t-k.svg",
"path": "/img/type/logo-t-k.svg",
"path": "/type/quais/img/type/logo-t-k.svg",
"slug": "t-k",
"name": "T K"
},
{
"file": "logo-t-l.svg",
"path": "/img/type/logo-t-l.svg",
"path": "/type/quais/img/type/logo-t-l.svg",
"slug": "t-l",
"name": "T L"
},
{
"file": "logo-t-n.svg",
"path": "/img/type/logo-t-n.svg",
"path": "/type/quais/img/type/logo-t-n.svg",
"slug": "t-n",
"name": "T N"
},
{
"file": "logo-t-p.svg",
"path": "/img/type/logo-t-p.svg",
"path": "/type/quais/img/type/logo-t-p.svg",
"slug": "t-p",
"name": "T P"
},
{
"file": "logo-t-r.svg",
"path": "/img/type/logo-t-r.svg",
"path": "/type/quais/img/type/logo-t-r.svg",
"slug": "t-r",
"name": "T R"
},
{
"file": "logo-t-u.svg",
"path": "/img/type/logo-t-u.svg",
"path": "/type/quais/img/type/logo-t-u.svg",
"slug": "t-u",
"name": "T U"
},
{
"file": "logo-teoz.svg",
"path": "/img/type/logo-teoz.svg",
"path": "/type/quais/img/type/logo-teoz.svg",
"slug": "teoz",
"name": "Teoz"
},
{
"file": "sncf-voyageurs-logo.png",
"path": "/img/type/sncf-voyageurs-logo.png",
"path": "/type/quais/sncf-voyageurs-logo.png",
"slug": "ter",
"name": "TER"
},
{
"file": "logo-ter-rhone-alpes.svg",
"path": "/img/type/logo-ter-rhone-alpes.svg",
"path": "/type/quais/logo-ter-rhone-alpes.svg",
"slug": "ter-rhone-alpes",
"name": "TER Rhone Alpes"
},
{
"file": "logo-teralsace.svg",
"path": "/img/type/logo-teralsace.svg",
"path": "/type/quais/logo-teralsace.svg",
"slug": "teralsace",
"name": "Teralsace"
},
{
"file": "logo-teraquitaine.svg",
"path": "/img/type/logo-teraquitaine.svg",
"path": "/type/quais/logo-teraquitaine.svg",
"slug": "teraquitaine",
"name": "Teraquitaine"
},
{
"file": "logo-terbassenormandie.svg",
"path": "/img/type/logo-terbassenormandie.svg",
"path": "/type/quais/logo-terbassenormandie.svg",
"slug": "terbassenormandie",
"name": "Terbassenormandie"
},
{
"file": "logo-terbourgogne.svg",
"path": "/img/type/logo-terbourgogne.svg",
"path": "/type/quais/logo-terbourgogne.svg",
"slug": "terbourgogne",
"name": "Terbourgogne"
},
{
"file": "logo-terbretagne.svg",
"path": "/img/type/logo-terbretagne.svg",
"path": "/type/quais/logo-terbretagne.svg",
"slug": "terbretagne",
"name": "Terbretagne"
},
{
"file": "logo-tercentre.svg",
"path": "/img/type/logo-tercentre.svg",
"path": "/type/quais/logo-tercentre.svg",
"slug": "tercentre",
"name": "Tercentre"
},
{
"file": "logo-terlanguedocroussillon.svg",
"path": "/img/type/logo-terlanguedocroussillon.svg",
"path": "/type/quais/logo-terlanguedocroussillon.svg",
"slug": "terlanguedocroussillon",
"name": "Terlanguedocroussillon"
},
{
"file": "logo-termidipyrenees.svg",
"path": "/img/type/logo-termidipyrenees.svg",
"path": "/type/quais/logo-termidipyrenees.svg",
"slug": "termidipyrenees",
"name": "Termidipyrenees"
},
{
"file": "logo-ternordpasdecalais.svg",
"path": "/img/type/logo-ternordpasdecalais.svg",
"path": "/type/quais/logo-ternordpasdecalais.svg",
"slug": "ternordpasdecalais",
"name": "Ternordpasdecalais"
},
{
"file": "logo-terpoitoucharentes.svg",
"path": "/img/type/logo-terpoitoucharentes.svg",
"path": "/type/quais/logo-terpoitoucharentes.svg",
"slug": "terpoitoucharentes",
"name": "Terpoitoucharentes"
},
{
"file": "logo-tgv.svg",
"path": "/img/type/logo-tgv.svg",
"path": "/type/quais/logo-tgv.svg",
"slug": "tgv",
"name": "TGV"
},
{
"file": "logo-inoui.svg",
"path": "/img/type/logo-inoui.svg",
"path": "/type/quais/logo-inoui.svg",
"slug": "inoui",
"name": "TGV InOui"
},
{
"file": "logo-lyria.svg",
"path": "/img/type/logo-lyria.svg",
"path": "/type/quais/logo-lyria.svg",
"slug": "lyria",
"name": "TGV Lyria"
},
{
"file": "logo-thalys.svg",
"path": "/img/type/logo-thalys.svg",
"path": "/type/quais/logo-thalys.svg",
"slug": "thalys",
"name": "Thalys"
},
{
"file": "logo-thello.svg",
"path": "/img/type/logo-thello.svg",
"path": "/type/quais/logo-thello.svg",
"slug": "thello",
"name": "Thello"
},
{
"file": "track-car.svg",
"path": "/img/type/track-car.svg",
"path": "/type/quais/track-car.svg",
"slug": "track-car",
"name": "Track Car"
},
{
"file": "logo-tramtrain.svg",
"path": "/img/type/logo-tramtrain.svg",
"path": "/type/quais/logo-tramtrain.svg",
"slug": "tramtrain",
"name": "Tramtrain"
},
{
"file": "logo-trenitalia.svg",
"path": "/img/type/logo-trenitalia.svg",
"path": "/type/quais/logo-trenitalia.svg",
"slug": "trenitalia",
"name": "Trenitalia"
},
{
"file": "logo-zou.svg",
"path": "/img/type/logo-zou.svg",
"path": "/type/quais/logo-zou.svg",
"slug": "zou",
"name": "Zou"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

+18
View File
@@ -9,6 +9,8 @@ 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';
@@ -198,6 +200,22 @@ function App() {
}
// Routes pour afficheurs : supporte le mode racine ET le mode /ferrovia
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>;
}
@@ -0,0 +1,202 @@
.aff-attentifs__container {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
user-select: none;
font-family: 'Achemine', 'Arial', sans-serif;
overflow: hidden;
box-sizing: border-box;
}
/* COLONNE GAUCHE (36%) */
.aff-attentifs__left-col {
flex: 0 0 36%;
background-color: #313131;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 70px 50px;
box-sizing: border-box;
}
.aff-attentifs__title-group {
display: flex;
flex-direction: column;
gap: 12px;
}
.aff-attentifs__title-word {
font-size: 78px;
font-weight: 900;
color: #fdb913;
letter-spacing: 2px;
line-height: 1.05;
text-transform: uppercase;
}
.aff-attentifs__vigipirate-wrap {
display: flex;
align-items: center;
justify-content: flex-start;
margin-top: auto;
}
.aff-attentifs__vigipirate-svg {
width: 270px;
height: auto;
filter: drop-shadow(0 8px 18px rgba(0, 0, 0, 0.45));
}
/* COLONNE DROITE (64%) */
.aff-attentifs__right-col {
flex: 1;
background-color: #4a4a4a;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 60px 80px;
box-sizing: border-box;
gap: 40px;
}
.aff-attentifs__info-section {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
color: #ffffff;
}
.aff-attentifs__info-desc {
font-size: 27px;
font-weight: 500;
line-height: 1.4;
margin: 0 0 36px 0;
color: #f8f8f8;
}
.aff-attentifs__call-action {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 30px;
}
.aff-attentifs__call-text {
font-size: 44px;
font-weight: 900;
letter-spacing: 0.5px;
color: #ffffff;
}
.aff-attentifs__call-hours {
font-size: 30px;
font-weight: 600;
color: #eaeaea;
}
.aff-attentifs__call-footnote {
font-size: 19px;
font-weight: 400;
color: #d0d0d0;
}
/* SECTION BULLE D'ALERTE 3117 */
.aff-attentifs__bubble-section {
flex: 0 0 460px;
display: flex;
align-items: center;
justify-content: center;
}
.aff-attentifs__bubble-wrap {
position: relative;
display: flex;
align-items: center;
}
.aff-attentifs__bubble-card {
position: relative;
background-color: #ffffff;
border: 7px solid #e2001a;
border-radius: 36px;
padding: 30px 42px;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 14px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
min-width: 360px;
}
.aff-attentifs__bubble-title {
font-size: 28px;
font-weight: 900;
color: #e2001a;
letter-spacing: 1px;
}
.aff-attentifs__bubble-rows {
display: flex;
flex-direction: column;
gap: 8px;
}
.aff-attentifs__bubble-line {
display: flex;
align-items: baseline;
gap: 14px;
}
.aff-attentifs__bubble-label {
font-size: 30px;
font-weight: 800;
color: #e2001a;
min-width: 70px;
}
.aff-attentifs__bubble-number {
font-size: 58px;
font-weight: 900;
color: #e2001a;
letter-spacing: -1px;
}
.aff-attentifs__bubble-tail {
position: absolute;
bottom: -32px;
left: 54px;
width: 0;
height: 0;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
border-top: 32px solid #e2001a;
}
.aff-attentifs__bubble-tail::after {
content: '';
position: absolute;
top: -38px;
left: -13px;
width: 0;
height: 0;
border-left: 13px solid transparent;
border-right: 13px solid transparent;
border-top: 24px solid #ffffff;
}
/* SMARTPHONE BADGE */
.aff-attentifs__phone-badge {
position: absolute;
top: -34px;
right: -36px;
z-index: 10;
filter: drop-shadow(0 8px 16px rgba(0, 0, 0, 0.4));
}
.aff-attentifs__phone-svg {
width: 86px;
height: auto;
}
@@ -0,0 +1,112 @@
import React from 'react';
import './AttentifsEnsemble.css';
export default function AttentifsEnsemble() {
return (
<div className="aff-attentifs__container">
{/* COLONNE GAUCHE : ANTHRACITE FONCÉ (ATTENTIFS ENSEMBLE + VIGIPIRATE) */}
<div className="aff-attentifs__left-col">
<div className="aff-attentifs__title-group">
<div className="aff-attentifs__title-word">ATTENTIFS</div>
<div className="aff-attentifs__title-word">ENSEMBLE</div>
</div>
{/* Insigne Vigipirate Officiel */}
<div className="aff-attentifs__vigipirate-wrap">
<svg
viewBox="0 0 280 240"
className="aff-attentifs__vigipirate-svg"
xmlns="http://www.w3.org/2000/svg"
>
{/* Triangle noir extérieur */}
<polygon points="140,12 268,228 12,228" fill="#1e1e1e" stroke="#1e1e1e" strokeWidth="8" strokeLinejoin="round" />
{/* Triangle rouge intérieur */}
<polygon points="140,42 238,206 42,206" fill="#e2001a" />
{/* Bandeau blanc en bas */}
<polygon points="56,182 224,182 238,206 42,206" fill="#ffffff" />
{/* Texte VIGIPIRATE */}
<text
x="140"
y="201"
fill="#1e1e1e"
fontSize="23"
fontWeight="900"
fontFamily="Achemine, Arial, sans-serif"
textAnchor="middle"
letterSpacing="2.5"
>
VIGIPIRATE
</text>
</svg>
</div>
</div>
{/* COLONNE DROITE : GRIS MOYEN (CONSIGNES + BULLE ALERTE 3117) */}
<div className="aff-attentifs__right-col">
{/* Partie gauche des consignes */}
<div className="aff-attentifs__info-section">
<p className="aff-attentifs__info-desc">
Si vous êtes témoin<br />
d'une situation<br />
qui présente un risque<br />
pour votre sécurité<br />
ou celle des autres<br />
voyageurs,
</p>
<div className="aff-attentifs__call-action">
<span className="aff-attentifs__call-text">APPELER* LE 3117</span>
<span className="aff-attentifs__call-hours">24h/24 · 7j/7</span>
</div>
<div className="aff-attentifs__call-footnote">
* Appel non surtaxé et enregistré
</div>
</div>
{/* Partie droite avec la bulle Alerte SNCF 3117 et le smartphone */}
<div className="aff-attentifs__bubble-section">
<div className="aff-attentifs__bubble-wrap">
{/* Bulle blanche à bordure rouge */}
<div className="aff-attentifs__bubble-card">
<div className="aff-attentifs__bubble-title">ALERTE SNCF</div>
<div className="aff-attentifs__bubble-rows">
<div className="aff-attentifs__bubble-line">
<span className="aff-attentifs__bubble-label">TÉL.</span>
<span className="aff-attentifs__bubble-number">3117*</span>
</div>
<div className="aff-attentifs__bubble-line">
<span className="aff-attentifs__bubble-label">SMS</span>
<span className="aff-attentifs__bubble-number">31177**</span>
</div>
</div>
{/* Pointe de la bulle */}
<div className="aff-attentifs__bubble-tail" />
</div>
{/* Smartphone Rouge d'alerte */}
<div className="aff-attentifs__phone-badge">
<svg viewBox="0 0 68 104" className="aff-attentifs__phone-svg" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* Corps de téléphone rouge arrondi */}
<rect x="4" y="4" width="60" height="96" rx="14" fill="#e2001a" stroke="#ffffff" strokeWidth="4.5" />
{/* Écran blanc */}
<rect x="12" y="14" width="44" height="50" rx="6" fill="#ffffff" />
{/* Point d'exclamation rouge d'urgence */}
<rect x="30.5" y="24" width="7" height="20" rx="3.5" fill="#e2001a" />
<circle cx="34" cy="53" r="4" fill="#e2001a" />
{/* Touches du pavé numérique */}
<circle cx="20" cy="74" r="3" fill="#ffffff" />
<circle cx="34" cy="74" r="3" fill="#ffffff" />
<circle cx="48" cy="74" r="3" fill="#ffffff" />
<circle cx="20" cy="85" r="3" fill="#ffffff" />
<circle cx="34" cy="85" r="3" fill="#ffffff" />
<circle cx="48" cy="85" r="3" fill="#ffffff" />
</svg>
</div>
</div>
</div>
</div>
</div>
);
}
@@ -0,0 +1,90 @@
.aff-dep-afl__container {
width: 100%;
height: 100%;
background-color: #d61e38;
color: #ffffff;
display: flex;
flex-direction: column;
user-select: none;
font-family: 'Achemine', 'Arial', sans-serif;
overflow: hidden;
box-sizing: border-box;
}
/* FRISSE SUPÉRIEURE RAYÉE */
.aff-dep-afl__hazard-stripes {
width: 100%;
height: 48px;
background: repeating-linear-gradient(
-45deg,
#ffffff,
#ffffff 28px,
#d61e38 28px,
#d61e38 56px
);
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
flex-shrink: 0;
}
/* CONTENU PRINCIPAL */
.aff-dep-afl__content {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 40px 100px;
gap: 120px;
box-sizing: border-box;
}
.aff-dep-afl__icon-col {
flex: 0 0 460px;
display: flex;
align-items: center;
justify-content: center;
}
.aff-dep-afl__no-entry-svg {
width: 100%;
max-width: 440px;
height: auto;
filter: drop-shadow(0 10px 24px rgba(0, 0, 0, 0.35));
}
.aff-dep-afl__text-col {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
gap: 48px;
}
.aff-dep-afl__heading-group {
display: flex;
flex-direction: column;
line-height: 1.02;
}
.aff-dep-afl__title-main,
.aff-dep-afl__title-sub {
font-size: 102px;
font-weight: 900;
letter-spacing: 2px;
color: #ffffff;
text-transform: uppercase;
}
.aff-dep-afl__instructions {
display: flex;
flex-direction: column;
gap: 8px;
}
.aff-dep-afl__instruction-line {
font-size: 48px;
font-weight: 600;
line-height: 1.35;
color: #ffffff;
margin: 0;
}
@@ -0,0 +1,63 @@
import React from 'react';
import './DepartEnCoursAFL.css';
export default function DepartEnCoursAFL() {
return (
<div className="aff-dep-afl__container">
{/* RUBAN SUPÉRIEUR RAYÉ BLANC ET ROUGE */}
<div className="aff-dep-afl__hazard-stripes" />
{/* CONTENU PRINCIPAL */}
<div className="aff-dep-afl__content">
{/* Panneau d'interdiction blanc à gauche */}
<div className="aff-dep-afl__icon-col">
<svg
viewBox="0 0 240 240"
className="aff-dep-afl__no-entry-svg"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
{/* Cercle blanc extérieur */}
<circle cx="120" cy="120" r="105" stroke="#ffffff" strokeWidth="18" />
{/* Silhouette du piéton marchant avec valise */}
<g fill="#ffffff">
{/* Tête */}
<circle cx="126" cy="62" r="15" />
{/* Corps marchant */}
<path d="M116 84 C116 84, 134 84, 138 98 L152 144 L140 148 L128 110 L118 132 L112 186 L98 184 L106 126 L96 112 Z" />
{/* Jambe droite enjambant */}
<path d="M122 134 L138 180 C138 180, 146 186, 140 190 C134 194, 126 188, 126 188 L114 148 Z" />
{/* Bras gauche */}
<path d="M128 88 L146 114 L138 120 L122 96 Z" />
{/* Bras droit tenant la valise */}
<path d="M116 86 L98 116 L88 110" stroke="#ffffff" strokeWidth="8" strokeLinecap="round" strokeLinejoin="round" fill="none" />
{/* Valise inclinée */}
<g transform="rotate(-15, 80, 140)">
<rect x="58" y="122" width="38" height="52" rx="7" fill="#ffffff" />
<path d="M70 122 V112 H84 V122" stroke="#ffffff" strokeWidth="6" fill="none" />
</g>
</g>
{/* Barre diagonale blanche d'interdiction */}
<line x1="46" y1="46" x2="194" y2="194" stroke="#ffffff" strokeWidth="18" strokeLinecap="round" />
</svg>
</div>
{/* Textes d'alerte et de consigne à droite */}
<div className="aff-dep-afl__text-col">
<div className="aff-dep-afl__heading-group">
<div className="aff-dep-afl__title-main">DÉPARTS</div>
<div className="aff-dep-afl__title-sub">EN COURS</div>
</div>
<div className="aff-dep-afl__instructions">
<p className="aff-dep-afl__instruction-line">Pour votre sécurité</p>
<p className="aff-dep-afl__instruction-line">merci de vous éloigner</p>
<p className="aff-dep-afl__instruction-line">de la bordure du quai.</p>
</div>
</div>
</div>
</div>
);
}
@@ -0,0 +1,75 @@
.aff-dep-classique__container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
user-select: none;
font-family: 'Achemine', 'Arial', sans-serif;
overflow: hidden;
box-sizing: border-box;
}
/* ZONE DU HAUT : GRIS FONCÉ (~72%) */
.aff-dep-classique__top {
flex: 1;
background-color: #222226;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 160px;
padding: 40px 100px;
box-sizing: border-box;
}
.aff-dep-classique__prohibition-sign {
display: flex;
align-items: center;
justify-content: center;
}
.aff-dep-classique__no-entry-svg {
width: 480px;
height: 480px;
filter: drop-shadow(0 12px 28px rgba(0, 0, 0, 0.45));
}
.aff-dep-classique__train-wrap {
display: flex;
align-items: center;
justify-content: center;
}
.aff-dep-classique__train-gif {
width: 480px;
height: auto;
max-height: 480px;
object-fit: contain;
filter: drop-shadow(0 12px 28px rgba(0, 0, 0, 0.45));
}
.aff-dep-classique__train-svg {
width: 460px;
height: 460px;
filter: drop-shadow(0 12px 28px rgba(0, 0, 0, 0.45));
}
/* BANDEAU ROUGE INFÉRIEUR (~28%) */
.aff-dep-classique__bottom {
flex: 0 0 280px;
background-color: #d0102a;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.35);
}
.aff-dep-classique__banner-text {
font-size: 110px;
font-weight: 900;
color: #ffffff;
margin: 0;
letter-spacing: 1px;
text-align: center;
}
@@ -0,0 +1,81 @@
import React, { useState } from 'react';
import './DepartEnCoursClassique.css';
const GIF_URL = 'https://cdn.discordapp.com/attachments/1265325000290992258/1282018214720835725/SNCF_-_Depart_Train.gif?ex=6a95af01&is=6a945d81&hm=14570f1423217cb52437d767b3125a698ac5955fc088028fbc3d24d820e9a76b&';
export default function DepartEnCoursClassique() {
const [gifError, setGifError] = useState(false);
return (
<div className="aff-dep-classique__container">
{/* ZONE SUPÉRIEURE : PICTOS (FOND GRIS FONCÉ) */}
<div className="aff-dep-classique__top">
{/* Panneau d'interdiction : Ne pas monter (voyageur avec valise barré) */}
<div className="aff-dep-classique__prohibition-sign">
<svg viewBox="0 0 200 200" className="aff-dep-classique__no-entry-svg" xmlns="http://www.w3.org/2000/svg">
{/* Fond blanc du cercle */}
<circle cx="100" cy="100" r="90" fill="#ffffff" stroke="#d0102a" strokeWidth="18" />
{/* Silhouette du voyageur avec valise */}
<g fill="#141416">
{/* Tête */}
<circle cx="92" cy="52" r="14" />
{/* Corps */}
<rect x="76" y="70" width="32" height="52" rx="10" />
{/* Jambe gauche */}
<rect x="80" y="118" width="10" height="38" rx="5" />
{/* Jambe droite */}
<rect x="94" y="118" width="10" height="38" rx="5" />
{/* Bras gauche */}
<rect x="66" y="72" width="9" height="36" rx="4.5" />
{/* Bras droit tenant la poignée */}
<path d="M106 74 L120 102 L120 114" stroke="#141416" strokeWidth="8" strokeLinecap="round" strokeLinejoin="round" fill="none" />
{/* Poignée télescopique de valise */}
<path d="M116 102 V116 H124 V102" stroke="#141416" strokeWidth="4" fill="none" />
{/* Valise à roulettes */}
<rect x="110" y="116" width="24" height="38" rx="4" fill="#141416" />
{/* Roulettes */}
<circle cx="114" cy="156" r="3" fill="#141416" />
<circle cx="130" cy="156" r="3" fill="#141416" />
</g>
{/* Barre diagonale rouge d'interdiction */}
<line x1="36" y1="36" x2="164" y2="164" stroke="#d0102a" strokeWidth="18" strokeLinecap="round" />
</svg>
</div>
{/* Animation du train au départ (GIF ou SVG de secours) */}
<div className="aff-dep-classique__train-wrap">
{!gifError ? (
<img
src={GIF_URL}
alt="Train au départ"
className="aff-dep-classique__train-gif"
onError={() => setGifError(true)}
/>
) : (
<svg viewBox="0 0 240 240" className="aff-dep-classique__train-svg" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* Rails et traverses */}
<path d="M40 200 L95 140 M200 200 L145 140" stroke="#ffffff" strokeWidth="8" strokeLinecap="round" />
<line x1="60" y1="185" x2="180" y2="185" stroke="#ffffff" strokeWidth="8" strokeLinecap="round" />
<line x1="85" y1="160" x2="155" y2="160" stroke="#ffffff" strokeWidth="6" strokeLinecap="round" />
{/* Face avant du train */}
<rect x="55" y="40" width="130" height="110" rx="35" fill="#ffffff" />
{/* Pare-brise */}
<rect x="68" y="52" width="104" height="42" rx="14" fill="#1c1c20" />
{/* Phares rouges */}
<circle cx="82" cy="120" r="10" fill="#d0102a" />
<circle cx="158" cy="120" r="10" fill="#d0102a" />
</svg>
)}
</div>
</div>
{/* BANDEAU INFÉRIEUR ROUGE : DÉPART EN COURS */}
<div className="aff-dep-classique__bottom">
<h1 className="aff-dep-classique__banner-text">Départ en cours</h1>
</div>
</div>
);
}
@@ -0,0 +1,78 @@
.aff-videoprot__container {
width: 100%;
height: 100%;
background-color: #141416;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
padding: 40px 80px;
box-sizing: border-box;
font-family: 'Achemine', 'Arial', sans-serif;
user-select: none;
}
.aff-videoprot__wrapper {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 80px;
width: 100%;
max-width: 1600px;
}
.aff-videoprot__icon-col {
flex: 0 0 420px;
display: flex;
align-items: center;
justify-content: center;
}
.aff-videoprot__camera-svg {
width: 100%;
max-width: 420px;
height: auto;
filter: drop-shadow(0 6px 16px rgba(242, 91, 42, 0.25));
}
.aff-videoprot__text-col {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.aff-videoprot__title-vigi {
font-size: 52px;
font-weight: 800;
color: #f25b2a;
letter-spacing: 1.5px;
margin-bottom: 24px;
text-transform: uppercase;
}
.aff-videoprot__main-heading {
font-size: 46px;
font-weight: 800;
color: #ffffff;
line-height: 1.28;
letter-spacing: 0.5px;
margin: 0 0 54px 0;
text-transform: uppercase;
}
.aff-videoprot__subtext-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.aff-videoprot__legal-line,
.aff-videoprot__access-line {
font-size: 26px;
font-weight: 600;
line-height: 1.45;
color: #f0f0f0;
margin: 0;
}
@@ -0,0 +1,68 @@
import React from 'react';
import './GareVideoprotection.css';
export default function GareVideoprotection() {
return (
<div className="aff-videoprot__container">
<div className="aff-videoprot__wrapper">
{/* Caméra de surveillance stylisée orange */}
<div className="aff-videoprot__icon-col">
<svg
viewBox="0 0 320 240"
className="aff-videoprot__camera-svg"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
{/* Support mural */}
<rect x="20" y="105" width="22" height="75" rx="5" fill="#f25b2a" />
<path
d="M38 132 H80 L95 105 H125"
stroke="#f25b2a"
strokeWidth="20"
strokeLinecap="round"
strokeLinejoin="round"
/>
{/* Corps de la caméra orienté vers le bas à droite */}
<g transform="rotate(18, 140, 110)">
{/* Corps principal */}
<rect x="35" y="55" width="165" height="70" rx="14" fill="#f25b2a" />
{/* Parasoleil / Visière supérieure */}
<path d="M30 48 L210 48 L195 58 L30 58 Z" fill="#f25b2a" />
{/* Cône avant / Objectif */}
<polygon points="198,62 250,38 250,142 198,118" fill="#f25b2a" />
{/* Fentes d'aération & détails noirs */}
<circle cx="65" cy="88" r="10" fill="#141416" />
<rect x="95" y="78" width="10" height="26" rx="4" fill="#141416" />
<rect x="120" y="78" width="10" height="26" rx="4" fill="#141416" />
<rect x="145" y="78" width="10" height="26" rx="4" fill="#141416" />
{/* Bague optique */}
<rect x="250" y="44" width="14" height="92" rx="3" fill="#f25b2a" />
</g>
</svg>
</div>
{/* Textes d'information et réglementaires */}
<div className="aff-videoprot__text-col">
<div className="aff-videoprot__title-vigi">VIGI360</div>
<h1 className="aff-videoprot__main-heading">
POUR VOTRE SÉCURITÉ, CET ESPACE<br />
EST ÉQUIPÉ DE VIDÉOPROTECTION.
</h1>
<div className="aff-videoprot__subtext-group">
<p className="aff-videoprot__legal-line">
En application des dispositions des textes législatifs et<br />
règlementaires en vigueur.
</p>
<p className="aff-videoprot__access-line">
Pour l'exercice du droit d'accès au enregistrements,<br />
s'adresser à un agent d'accueil de la gare.
</p>
</div>
</div>
</div>
</div>
);
}
+67
View File
@@ -408,6 +408,73 @@ export default function SelectAfficheur({ onBack, navigate }) {
🚉 Lancer l'Afficheur de Quai
</button>
</div>
{/* Options 3 & 4 pour le mode Classiques : Flash Info & Informations */}
{typeParam === 'classiques' && (
<>
{/* Option 3 : Flash Information */}
<div className="launcher-select-card" style={{ cursor: 'default' }}>
<div>
<span className="launcher-card-icon">⚡</span>
<span className="launcher-card-tag launcher-card-tag--retro" style={{ background: '#fab23e', color: '#00337f' }}>
Flash Info Trafic
</span>
<h2 className="launcher-card-title">Flash Information</h2>
<p className="launcher-card-desc">
Écran dédié aux perturbations de circulation en temps réel et travaux de maintenance sur le réseau.
</p>
<div style={{ background: '#f8fafc', border: '1px solid #e2ebf6', borderRadius: '10px', padding: '16px', marginBottom: '20px' }}>
<div style={{ fontSize: '13px', color: '#00337f', fontWeight: 600 }}>
⚡ Conforme au design historique <em>Flash Circulation</em> avec fond ambre et bandeau bleu nuit.
</div>
</div>
</div>
<button
type="button"
onClick={() => {
const kebab = gareParam ? String(gareParam).trim() : '';
navigate(`${prefix}/afficheurs/classiques/flash-info?gare=${encodeURIComponent(kebab)}`);
}}
className="launcher-btn"
style={{ background: '#00337f', color: '#fab23e', border: 'none', fontWeight: 700 }}
>
⚡ Lancer le Flash Information
</button>
</div>
{/* Option 4 : Informations (Messages Dynamiques) */}
<div className="launcher-select-card" style={{ cursor: 'default' }}>
<div>
<span className="launcher-card-icon">ℹ️</span>
<span className="launcher-card-tag launcher-card-tag--retro" style={{ background: '#00337f', color: '#ffffff' }}>
Messages Gare
</span>
<h2 className="launcher-card-title">Informations Voyageurs</h2>
<p className="launcher-card-desc">
Écran d'annonces et consignes dynamiques de la gare (Vigipirate, étiquetage des bagages, sécurité en gare).
</p>
<div style={{ background: '#f8fafc', border: '1px solid #e2ebf6', borderRadius: '10px', padding: '16px', marginBottom: '20px' }}>
<div style={{ fontSize: '13px', color: '#00337f', fontWeight: 600 }}>
ℹ️ Conforme au design historique <em>Information</em> avec messages voyageurs de la gare de {decodedGare}.
</div>
</div>
</div>
<button
type="button"
onClick={() => {
const kebab = gareParam ? String(gareParam).trim() : '';
navigate(`${prefix}/afficheurs/classiques/informations?gare=${encodeURIComponent(kebab)}`);
}}
className="launcher-btn"
style={{ background: '#00337f', color: '#ffffff', border: 'none', fontWeight: 700 }}
>
ℹ️ Lancer l'Afficheur Informations
</button>
</div>
</>
)}
</div>
{/* Accès direct aux pages fixes AFL */}
+43 -2
View File
@@ -6,6 +6,10 @@ import useAnnonceSonoreRealtime from '../../../hooks/useAnnonceSonoreRealtime';
import Logo from '../../../components/Logo';
import { getSourceFromPath } from '../../../services/railwayService';
import { getSncfTimetable } from '../../../services/sncfApi';
import GareVideoprotection from '../../../components/afficheurs/GareVideoprotection';
import AttentifsEnsemble from '../../../components/afficheurs/AttentifsEnsemble';
import AFLInformationsPage from './components/AFLInformationsPage';
import AFLArrivees from './AFLArrivees';
// Helper utilitaire : format HH:MM sans secondes (ex: 15:57)
const formatTimeNoSeconds = (v) => {
@@ -28,12 +32,16 @@ const AFLArriveesQuais = () => {
const [currentTime, setCurrentTime] = useState(new Date());
const [nextArrival, setNextArrival] = useState(null);
const [loading, setLoading] = useState(true);
const [idlePhase, setIdlePhase] = useState(0); // 0 = Attentifs (20s), 1 = Quai (20s), 2 = Information (10s)
const searchParams = new URLSearchParams(window.location.search);
const gareParam = searchParams.get('gare') || '';
const quaiParam = searchParams.get('quai') || searchParams.get('voie') || '';
const gareDisplay = gareParam ? decodeURIComponent(String(gareParam).replace(/\+/g, ' ')).replace(/-/g, ' ') : null;
const pageParam = (searchParams.get('page') || searchParams.get('mode') || '').toLowerCase();
const isVideoprotection = pageParam === 'videoprotection' || searchParams.get('videoprotection') === 'true';
useAnnonceSonoreRealtime(null, 'AFLArriveesQuais', quaiParam);
useEffect(() => {
@@ -669,7 +677,9 @@ const AFLArriveesQuais = () => {
stopForStation = stopsList.find(s => normalizeName((s.gare_nom || s.nom || s.name || '').toString()) === normalizeName(gareDisplay));
}
const platform = ((stopForStation && (stopForStation.quai_reel || stopForStation.quai || stopForStation.platform || stopForStation.quay)) || h.quai_reel || h.voie || h.quai || '').toString().trim();
return quaiParam && platform.toLowerCase() === quaiParam.toLowerCase();
if (!quaiParam) return true;
const cleanP = (p) => String(p || '').toLowerCase().replace(/^(?:voie|quai|track|platform)\s*/i, '').replace(/\s+/g, '').trim();
return cleanP(platform) === cleanP(quaiParam);
});
if (candidatesOnPlatform.length > 0) {
@@ -684,13 +694,44 @@ const AFLArriveesQuais = () => {
}
}, [applyPerturbationsToHoraires, buildPerturbMap, gareDisplay, gareParam, getPerturbationJours, getStops, normalizeName, parseTimeHM, quaiParam]);
// Alternance par défaut (20s Attentifs -> 20s Quai -> 10s Information) si aucun train n'est prévu sur le quai
useEffect(() => {
if (nextArrival) return;
let duration = 20000;
if (idlePhase === 2) duration = 10000;
else duration = 20000;
const timer = setTimeout(() => {
setIdlePhase(prev => (prev + 1) % 3);
}, duration);
return () => clearTimeout(timer);
}, [idlePhase, nextArrival]);
useEffect(() => {
fetchData();
const interval = setInterval(fetchData, 15000);
return () => clearInterval(interval);
}, [fetchData]);
if (loading) return <div className="afl-q-arr-container" style={{ background: '#173e31' }}></div>;
// AFFICHAGE 1 : Page Vidéoprotection (via query)
if (isVideoprotection) {
return <GareVideoprotection />;
}
// AFFICHAGE 2 : Alternance si aucun train n'est attribué à ce quai
if (!nextArrival && gareParam) {
if (idlePhase === 0) {
return <AttentifsEnsemble />;
}
if (idlePhase === 1) {
return <AFLArrivees />;
}
if (idlePhase === 2) {
return <AFLInformationsPage mode="arrivees" gareName={gareDisplay || 'Votre Gare'} />;
}
}
if (loading && !nextArrival) return <div className="afl-q-arr-container" style={{ background: '#173e31' }}></div>;
if (!nextArrival) {
return (
+66 -2
View File
@@ -6,6 +6,11 @@ import useAnnonceSonoreRealtime from '../../../hooks/useAnnonceSonoreRealtime';
import Logo from '../../../components/Logo';
import { getSourceFromPath } from '../../../services/railwayService';
import { getSncfTimetable } from '../../../services/sncfApi';
import GareVideoprotection from '../../../components/afficheurs/GareVideoprotection';
import AttentifsEnsemble from '../../../components/afficheurs/AttentifsEnsemble';
import DepartEnCoursAFL from '../../../components/afficheurs/DepartEnCoursAFL';
import AFLInformationsPage from './components/AFLInformationsPage';
import AFLDeparts from './AFLDeparts';
// Helper utilitaire : format HH:MM sans secondes (ex: 15:57)
const formatTimeNoSeconds = (v) => {
@@ -28,6 +33,7 @@ const AFLDepartsQuais = () => {
const [currentTime, setCurrentTime] = useState(new Date());
const [nextDeparture, setNextDeparture] = useState(null);
const [loading, setLoading] = useState(true);
const [idlePhase, setIdlePhase] = useState(0); // 0 = Attentifs (20s), 1 = Quai (20s), 2 = Information (10s)
// Parse URL manually since we are not in a React Router context
const searchParams = new URLSearchParams(window.location.search);
@@ -36,6 +42,10 @@ const AFLDepartsQuais = () => {
const quaiParam = searchParams.get('quai') || searchParams.get('voie') || '';
const gareDisplay = gareParam ? decodeURIComponent(String(gareParam).replace(/\+/g, ' ')).replace(/-/g, ' ') : null;
const pageParam = (searchParams.get('page') || searchParams.get('mode') || '').toLowerCase();
const isVideoprotection = pageParam === 'videoprotection' || searchParams.get('videoprotection') === 'true';
const isForceDepartEnCours = pageParam === 'depart_en_cours' || searchParams.get('depart_en_cours') === 'true' || searchParams.get('test_depart') === 'true';
const [resolvedGareId, setResolvedGareId] = useState(gareIdParam || null);
useAnnonceSonoreRealtime(resolvedGareId, 'AFLDepartsQuais', quaiParam);
@@ -743,7 +753,8 @@ const AFLDepartsQuais = () => {
}
const platform = ((stopForStation && (stopForStation.quai_reel || stopForStation.quai || stopForStation.platform || stopForStation.quay)) || h.quai_reel || h.voie || h.quai || '').toString().trim();
if (!quaiParam) return true;
return platform.toLowerCase() === quaiParam.toLowerCase();
const cleanP = (p) => String(p || '').toLowerCase().replace(/^(?:voie|quai|track|platform)\s*/i, '').replace(/\s+/g, '').trim();
return cleanP(platform) === cleanP(quaiParam);
});
if (candidatesOnPlatform.length > 0) {
@@ -758,13 +769,66 @@ const AFLDepartsQuais = () => {
}
}, [applyPerturbationsToHoraires, buildPerturbMap, gareDisplay, gareParam, getPerturbationJours, getStops, normalizeName, parseTimeHM, quaiParam]);
// Alternance par défaut (20s Attentifs -> 20s Quai -> 10s Information) si aucun train n'est prévu sur le quai
useEffect(() => {
if (nextDeparture) return;
let duration = 20000;
if (idlePhase === 2) duration = 10000;
else duration = 20000;
const timer = setTimeout(() => {
setIdlePhase(prev => (prev + 1) % 3);
}, duration);
return () => clearTimeout(timer);
}, [idlePhase, nextDeparture]);
useEffect(() => {
fetchData();
const interval = setInterval(fetchData, 15000);
return () => clearInterval(interval);
}, [fetchData]);
if (loading) return <div className="afl-q-container" style={{ background: '#0c0f2e' }}></div>;
// 1. Détection "Départ en cours" (2 minutes avant l'heure de départ réelle)
let isDepartEnCours = isForceDepartEnCours;
if (!isDepartEnCours && nextDeparture) {
const rawTime = nextDeparture._rawTime || nextDeparture.heure_depart || nextDeparture.time;
const depHM = parseTimeHM(rawTime);
const delayMin = nextDeparture._status?.delayMinutes || nextDeparture.retard_minutes || 0;
if (depHM != null) {
const nowSec = currentTime.getHours() * 3600 + currentTime.getMinutes() * 60 + currentTime.getSeconds();
const depSec = (depHM + delayMin) * 60;
let diffSec = depSec - nowSec;
if (diffSec < -43200) diffSec += 86400;
if (diffSec <= 120 && diffSec >= 0) {
isDepartEnCours = true;
}
}
}
// AFFICHAGE 1 : Page Vidéoprotection (via query)
if (isVideoprotection) {
return <GareVideoprotection />;
}
// AFFICHAGE 2 : Départ en cours (T - 2 min)
if (isDepartEnCours) {
return <DepartEnCoursAFL />;
}
// AFFICHAGE 3 : Alternance si aucun train n'est attribué à ce quai
if (!nextDeparture && gareParam) {
if (idlePhase === 0) {
return <AttentifsEnsemble />;
}
if (idlePhase === 1) {
return <AFLDeparts />;
}
if (idlePhase === 2) {
return <AFLInformationsPage mode="departs" gareName={gareDisplay || 'Votre Gare'} />;
}
}
if (loading && !nextDeparture) return <div className="afl-q-container" style={{ background: '#0c0f2e' }}></div>;
if (!nextDeparture) {
return (
@@ -143,7 +143,7 @@ function Clock(){
);
}
export default function ClassiquesArrivees(){
export default function ClassiquesArrivees({ defilement = null } = {}){
// lire le paramètre de requête ?gare=Nom-de-la-gare
const params = new URLSearchParams(window.location.search);
const gareParam = params.get('gare');
@@ -151,8 +151,8 @@ export default function ClassiquesArrivees(){
const gareIdParam = params.get('gare_id') || params.get('gareId');
// gérer les +, décoder et remplacer les tirets par des espaces
const gareDisplay = gareParam ? decodeURIComponent(String(gareParam).replace(/\+/g, ' ')).replace(/-/g, ' ') : null;
// Mode défilement (marquee vertical infini) si ?defilement=true dans l'URL
const defilementMode = params.get('defilement') === 'true';
// Mode défilement (marquee vertical infini) si ?defilement=true dans l'URL ou passé en prop
const defilementMode = defilement !== null ? Boolean(defilement) : (params.get('defilement') === 'true');
// debug flag activable via ?debug=1 or ?debug=true
const debugEnabled = (params.get('debug') === '1' || String(params.get('debug')).toLowerCase() === 'true');
@@ -143,7 +143,7 @@ function Clock(){
);
}
export default function ClassiquesDeparts(){
export default function ClassiquesDeparts({ defilement = null } = {}){
// lire le paramètre de requête ?gare=Nom-de-la-gare
const params = new URLSearchParams(window.location.search);
const gareParam = params.get('gare');
@@ -151,8 +151,8 @@ export default function ClassiquesDeparts(){
const gareIdParam = params.get('gare_id') || params.get('gareId');
// gérer les +, décoder et remplacer les tirets par des espaces
const gareDisplay = gareParam ? decodeURIComponent(String(gareParam).replace(/\+/g, ' ')).replace(/-/g, ' ') : null;
// Mode défilement (marquee vertical infini) si ?defilement=true dans l'URL
const defilementMode = params.get('defilement') === 'true';
// Mode défilement (marquee vertical infini) si ?defilement=true dans l'URL ou passé en prop
const defilementMode = defilement !== null ? Boolean(defilement) : (params.get('defilement') === 'true');
// debug flag activable via ?debug=1 or ?debug=true
const debugEnabled = (params.get('debug') === '1' || String(params.get('debug')).toLowerCase() === 'true');
@@ -0,0 +1,215 @@
body, html {
margin: 0;
padding: 0;
background-color: #fab23e;
overflow: hidden;
}
/* =========================================================
CONTAINER (1920x1080)
========================================================= */
.aff-classique-flash__container {
position: relative;
width: 1920px;
height: 1080px;
background-color: #fab23e;
color: #00337f;
font-family: 'Achemine', 'Avenir', 'Helvetica Neue', Arial, sans-serif;
overflow: hidden;
box-sizing: border-box;
display: flex;
}
/* =========================================================
SIDEBAR GAUCHE : BLEU NUIT (Conforme Image 3)
========================================================= */
.aff-classique-flash__sidebar {
width: 320px;
height: 1080px;
background-color: #00337f;
box-sizing: border-box;
position: relative;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
flex-shrink: 0;
}
.aff-classique-flash__sidebar-text {
transform: rotate(-90deg);
transform-origin: center center;
white-space: nowrap;
display: flex;
align-items: baseline;
gap: 24px;
user-select: none;
}
.aff-classique-flash__title-flash {
color: #f5a623;
font-size: 10.5rem; /* ~125px */
font-weight: 700;
letter-spacing: 4px;
line-height: 1;
}
.aff-classique-flash__title-circulation {
color: #ffffff;
font-size: 10.5rem; /* ~125px */
font-weight: 700;
letter-spacing: 4px;
line-height: 1;
}
/* =========================================================
ZONE PRINCIPALE : JAUNE / AMBRE (Conforme Image 3)
========================================================= */
.aff-classique-flash__main {
flex: 1;
height: 1080px;
background-color: #fab23e;
position: relative;
box-sizing: border-box;
padding: 60px 80px;
display: flex;
flex-direction: column;
}
.aff-classique-flash__content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
padding-bottom: 90px;
}
/* AFFICHAGE NOMINAL (TRAFIC NORMAL) */
.aff-classique-flash__nominal {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
max-width: 1300px;
}
.aff-classique-flash__nominal-title {
color: #00337f;
font-size: 6.5rem; /* ~78px */
font-weight: 700;
margin-bottom: 24px;
line-height: 1.1;
text-transform: capitalize;
}
.aff-classique-flash__nominal-subtitle {
color: #00337f;
font-size: 3.8rem; /* ~45px */
font-weight: 500;
line-height: 1.25;
margin-bottom: 24px;
}
.aff-classique-flash__nominal-detail {
color: #00337f;
font-size: 2.8rem; /* ~33px */
font-weight: 400;
line-height: 1.3;
opacity: 0.9;
}
/* CARTES D'ALERTE TRAFIC */
.aff-classique-flash__alerts-container {
display: flex;
flex-direction: column;
gap: 28px;
max-width: 1350px;
}
.aff-classique-flash__alert-card {
background-color: #00337f;
color: #ffffff;
border-radius: 8px;
padding: 32px 40px;
box-shadow: 0 8px 24px rgba(0, 51, 127, 0.2);
}
.aff-classique-flash__alert-card--travaux {
background-color: #1a2332;
}
.aff-classique-flash__alert-header {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 14px;
}
.aff-classique-flash__alert-tag {
background-color: #d32f2f;
color: #ffffff;
font-size: 2rem;
font-weight: 700;
padding: 4px 14px;
border-radius: 4px;
text-transform: uppercase;
}
.aff-classique-flash__alert-tag--travaux {
background-color: #f5a623;
color: #00337f;
}
.aff-classique-flash__alert-line {
font-size: 2.6rem;
font-weight: 700;
color: #fab23e;
}
.aff-classique-flash__alert-title {
font-size: 3.6rem;
font-weight: 700;
margin-bottom: 12px;
line-height: 1.2;
}
.aff-classique-flash__alert-desc {
font-size: 2.8rem;
font-weight: 400;
line-height: 1.35;
color: #e5eaf3;
}
/* =========================================================
HORLOGE BAS DROITE (Conforme Image 3)
========================================================= */
.aff-classique-flash__footer-clock-box {
position: absolute;
bottom: 25px;
right: 35px;
background-color: #00337f;
border: 2.5px solid #ffffff;
border-radius: 4px;
padding: 4px 16px;
display: flex;
align-items: baseline;
z-index: 20;
}
.aff-classique-flash__clock {
display: flex;
align-items: baseline;
gap: 8px;
font-family: 'Achemine', 'Helvetica Neue', Arial, sans-serif;
font-size: 3.4rem;
font-weight: 700;
line-height: 1;
}
.aff-classique-flash__clock-hm {
color: #ffffff;
}
.aff-classique-flash__clock-s {
color: #f5a623;
}
@@ -0,0 +1,117 @@
import React, { useState, useEffect } from 'react';
import './ClassiquesFlashInfo.css';
import useAfficheurData from '../../../hooks/useAfficheurData';
function DigitalClock() {
const [now, setNow] = useState(new Date());
useEffect(() => {
const id = setInterval(() => setNow(new Date()), 1000);
return () => clearInterval(id);
}, []);
const hh = String(now.getHours()).padStart(2, '0');
const mm = String(now.getMinutes()).padStart(2, '0');
const ss = String(now.getSeconds()).padStart(2, '0');
return (
<div className="aff-classique-flash__clock">
<span className="aff-classique-flash__clock-hm">{hh}:{mm}</span>
<span className="aff-classique-flash__clock-s">{ss}</span>
</div>
);
}
export default function ClassiquesFlashInfo() {
const params = new URLSearchParams(window.location.search || '');
const gareParam = params.get('gare');
const gareIdParam = params.get('gare_id') || params.get('gareId');
const gareDisplay = gareParam
? decodeURIComponent(String(gareParam).replace(/\+/g, ' ')).replace(/-/g, ' ')
: 'Votre Gare';
const {
perturbations = [],
travaux = []
} = useAfficheurData({
gareName: gareDisplay,
gareId: gareIdParam,
mode: 'departs',
refreshIntervalMs: 20000
});
const activePerturbations = (perturbations || []).filter(p => {
const t = (p.type_perturbation || p.type || '').toString().toLowerCase();
return Boolean(t);
});
const hasTrafficAlerts = activePerturbations.length > 0 || (travaux && travaux.length > 0);
return (
<div className="aff-classique-flash__container">
{/* BANDEAU GAUCHE : BLEU NUIT (Conforme Image 3) */}
<div className="aff-classique-flash__sidebar">
<div className="aff-classique-flash__sidebar-text">
<span className="aff-classique-flash__title-flash">flash</span>
<span className="aff-classique-flash__title-circulation">circulation</span>
</div>
</div>
{/* ZONE PRINCIPALE : AMBRE / JAUNE ORANGE (Conforme Image 3) */}
<div className="aff-classique-flash__main">
<div className="aff-classique-flash__content">
{hasTrafficAlerts ? (
<div className="aff-classique-flash__alerts-container">
{activePerturbations.map((p, idx) => (
<div key={`p-${idx}`} className="aff-classique-flash__alert-card">
<div className="aff-classique-flash__alert-header">
<span className="aff-classique-flash__alert-tag">Info Trafic</span>
<span className="aff-classique-flash__alert-line">{p.ligne_nom || p.ligne || gareDisplay}</span>
</div>
<div className="aff-classique-flash__alert-title">
{p.type_perturbation || p.titre || 'Perturbation en cours'}
</div>
<div className="aff-classique-flash__alert-desc">
{p.message || p.description || p.detail || p.cause || 'Des adaptations de circulation peuvent survenir sur vos trajets. Veuillez écouter les annonces en gare.'}
</div>
</div>
))}
{(travaux || []).slice(0, 2).map((t, idx) => (
<div key={`t-${idx}`} className="aff-classique-flash__alert-card aff-classique-flash__alert-card--travaux">
<div className="aff-classique-flash__alert-header">
<span className="aff-classique-flash__alert-tag aff-classique-flash__alert-tag--travaux">Travaux</span>
<span className="aff-classique-flash__alert-line">{t.nom || t.intitule || 'Maintenance du réseau'}</span>
</div>
<div className="aff-classique-flash__alert-title">
{t.intitule || t.nom || 'Travaux de modernisation'}
</div>
<div className="aff-classique-flash__alert-desc">
{t.description || t.motif || 'Des substitutions par autocars peuvent être mises en place. Merci de vérifier vos horaires.'}
</div>
</div>
))}
</div>
) : (
<div className="aff-classique-flash__nominal">
<div className="aff-classique-flash__nominal-box">
<div className="aff-classique-flash__nominal-title">
Trafic normal
</div>
<div className="aff-classique-flash__nominal-subtitle">
La circulation des trains s'effectue normalement sur l'ensemble des lignes desservant la gare de {gareDisplay}.
</div>
<div className="aff-classique-flash__nominal-detail">
Pour toute information complémentaire concernant vos trajets, nos agents en gare sont à votre disposition.
</div>
</div>
</div>
)}
</div>
{/* HORLOGE EN BAS À DROITE (Conforme Image 3) */}
<div className="aff-classique-flash__footer-clock-box">
<DigitalClock />
</div>
</div>
</div>
);
}
@@ -0,0 +1,133 @@
body, html {
margin: 0;
padding: 0;
background-color: #fab23e;
overflow: hidden;
}
/* =========================================================
CONTAINER (1920x1080)
========================================================= */
.aff-classique-infos__container {
position: relative;
width: 1920px;
height: 1080px;
background-color: #fab23e;
color: #00337f;
font-family: 'Achemine', 'Avenir', 'Helvetica Neue', Arial, sans-serif;
overflow: hidden;
box-sizing: border-box;
display: flex;
user-select: none;
}
/* =========================================================
SIDEBAR GAUCHE : BLEU NUIT (Conforme Image 4)
========================================================= */
.aff-classique-infos__sidebar {
width: 320px;
height: 1080px;
background-color: #00337f;
box-sizing: border-box;
position: relative;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
flex-shrink: 0;
}
.aff-classique-infos__sidebar-text {
transform: rotate(-90deg);
transform-origin: center center;
white-space: nowrap;
user-select: none;
}
.aff-classique-infos__title-information {
color: #ffffff;
font-size: 10.5rem; /* ~125px */
font-weight: 700;
letter-spacing: 4px;
line-height: 1;
}
/* =========================================================
ZONE PRINCIPALE : CADRE JAUNE DE DROITE
========================================================= */
.aff-classique-infos__main {
flex: 1;
height: 1080px;
background-color: #fab23e;
position: relative;
box-sizing: border-box;
padding: 80px 100px;
display: flex;
flex-direction: column;
justify-content: center;
}
/* CONTENEUR DU MESSAGE UNIQUE (TEXTE BLEU SUR FOND JAUNE) */
.aff-classique-infos__single-message-container {
max-width: 1350px;
display: flex;
flex-direction: column;
gap: 36px;
padding-bottom: 90px;
}
.aff-classique-infos__single-title {
color: #00337f;
font-size: 64px;
font-weight: 900;
line-height: 1.15;
margin: 0;
letter-spacing: -0.5px;
text-transform: none;
}
.aff-classique-infos__single-body {
color: #00337f;
font-size: 46px;
font-weight: 600;
line-height: 1.45;
margin: 0;
max-width: 1250px;
}
/* =========================================================
HORLOGE DIGITALE EN BAS À DROITE (Conforme Image 4)
========================================================= */
.aff-classique-infos__footer-clock-box {
position: absolute;
bottom: 40px;
right: 50px;
display: flex;
align-items: center;
justify-content: center;
}
.aff-classique-infos__clock {
display: inline-flex;
align-items: baseline;
background-color: #00337f;
border: 4px solid #ffffff;
border-radius: 8px;
padding: 10px 22px;
gap: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
}
.aff-classique-infos__clock-hm {
font-size: 3.4rem; /* ~54px */
font-weight: 800;
color: #ffffff;
letter-spacing: 1px;
}
.aff-classique-infos__clock-s {
font-size: 2.2rem; /* ~35px */
font-weight: 800;
color: #ff9e1b;
letter-spacing: 0.5px;
}
@@ -0,0 +1,120 @@
import React, { useState, useEffect } from 'react';
import './ClassiquesInformations.css';
import supabase from '../../../lib/supabase';
function DigitalClock() {
const [now, setNow] = useState(new Date());
useEffect(() => {
const id = setInterval(() => setNow(new Date()), 1000);
return () => clearInterval(id);
}, []);
const hh = String(now.getHours()).padStart(2, '0');
const mm = String(now.getMinutes()).padStart(2, '0');
const ss = String(now.getSeconds()).padStart(2, '0');
return (
<div className="aff-classique-infos__clock">
<span className="aff-classique-infos__clock-hm">{hh}:{mm}</span>
<span className="aff-classique-infos__clock-s">{ss}</span>
</div>
);
}
export default function ClassiquesInformations() {
const params = new URLSearchParams(window.location.search || '');
const gareParam = params.get('gare');
const gareDisplay = gareParam
? decodeURIComponent(String(gareParam).replace(/\+/g, ' ')).replace(/-/g, ' ')
: 'Votre Gare';
const defaultMessages = [
{
title: 'Plan Vigipirate & Sécurité',
content: 'Ne laissez aucun bagage sans surveillance. Tout bagage abandonné sera détruit. Signalez tout colis ou comportement suspect aux agents SNCF ou aux forces de l\'ordre.'
},
{
title: 'Étiquetage obligatoire de vos bagages',
content: 'Pour votre tranquillité et la sécurité de tous, vos valises et sacs doivent impérativement comporter une étiquette visible indiquant vos nom, prénom, téléphone et adresse.'
},
{
title: 'Traversée des voies formellement interdite',
content: 'Pour votre sécurité, il est strictement interdit de descendre sur les voies ou de les traverser. Utilisez systématiquement les passages souterrains ou les passerelles.'
},
{
title: 'Espace sans tabac ni vapotage',
content: 'Conformément à la réglementation, il est strictement interdit de fumer ou de vapoter dans les espaces couverts, les bâtiments de la gare ainsi que sur les quais.'
}
];
const [messagesList, setMessagesList] = useState(defaultMessages);
const [activeMessageIndex, setActiveMessageIndex] = useState(0);
// Chargement éventuel de messages dynamiques depuis Supabase
useEffect(() => {
let mounted = true;
const fetchStationMessages = async () => {
if (!supabase) return;
try {
const { data, error } = await supabase
.from('messages_dynamiques')
.select('*')
.order('id', { ascending: false });
if (!error && Array.isArray(data) && data.length > 0 && mounted) {
const formatted = data.map(m => ({
title: m.titre || m.title || 'Information Voyageurs',
content: m.contenu || m.content || m.message || ''
})).filter(m => Boolean(m.content));
if (formatted.length > 0) {
setMessagesList(formatted);
}
}
} catch (e) {
console.warn('Notice chargement messages_dynamiques:', e);
}
};
fetchStationMessages();
return () => { mounted = false; };
}, []);
// Défilement d'un seul message à la fois toutes les 10s si plusieurs messages
useEffect(() => {
if (messagesList.length <= 1) return;
const timer = setInterval(() => {
setActiveMessageIndex(prev => (prev + 1) % messagesList.length);
}, 10000);
return () => clearInterval(timer);
}, [messagesList.length]);
const activeMsg = messagesList[activeMessageIndex % messagesList.length] || defaultMessages[0];
return (
<div className="aff-classique-infos__container">
{/* BANDEAU GAUCHE : BLEU NUIT (Conforme Image 4) */}
<div className="aff-classique-infos__sidebar">
<div className="aff-classique-infos__sidebar-text">
<span className="aff-classique-infos__title-information">information</span>
</div>
</div>
{/* ZONE PRINCIPALE : CADRE JAUNE DE DROITE AVEC TEXTE BLEU (Conforme Image 4) */}
<div className="aff-classique-infos__main">
<div className="aff-classique-infos__single-message-container">
{activeMsg.title && (
<h1 className="aff-classique-infos__single-title">
{activeMsg.title}
</h1>
)}
<p className="aff-classique-infos__single-body">
{activeMsg.content}
</p>
</div>
{/* HORLOGE EN BAS À DROITE */}
<div className="aff-classique-infos__footer-clock-box">
<DigitalClock />
</div>
</div>
</div>
);
}
@@ -1,337 +1,315 @@
@font-face {
font-family: 'Achemine';
src: url('/public/fonts/achemine_regular.ttf') format('truetype');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Achemine';
src: url('/public/fonts/achemine_bold.ttf') format('truetype');
font-weight: 700;
font-style: normal;
font-display: swap;
}
body, html {
margin: 0;
padding: 0;
background-color: #006330; /* Green theme */
background-color: #116938;
overflow: hidden;
}
.aff-quais-arrivees-redesign__container {
display: flex;
flex-direction: row;
width: 100%;
height: 100%;
position: relative;
font-family: 'Achemine', 'Helvetica Neue', Arial, sans-serif;
background-color: #006330;
color: #fff;
overflow: hidden;
}
.aff-quais-arrivees-redesign__empty {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.aff-quais-arrivees-redesign__empty-text {
font-size: 6rem;
font-weight: 700;
color: #ffffff;
text-align: center;
}
/* =========================================================
LEFT PANEL
CONTAINER (1920x1080)
========================================================= */
.aff-quais-arrivees-redesign__left {
.aff-classique-quai-arr__container {
position: relative;
width: 1920px;
height: 1080px;
background-color: #116938;
color: #ffffff;
font-family: 'Achemine', 'Avenir', 'Helvetica Neue', Arial, sans-serif;
overflow: hidden;
box-sizing: border-box;
}
/* =========================================================
LEFT PANEL (BLANC - Conforme Image 2)
========================================================= */
.aff-classique-quai-arr__left {
position: absolute;
top: 0;
left: 0;
width: 35%;
bottom: 80px;
background-color: #f3eef4; /* Lilac/Light Grey background */
width: 672px;
bottom: 85px;
background-color: #ffffff;
box-sizing: border-box;
padding: 30px 40px;
padding: 42px 48px;
display: flex;
flex-direction: column;
overflow: hidden;
}
.aff-quais-arrivees-redesign__sncf-logo {
width: 90px;
height: auto;
margin-bottom: 25px;
z-index: 2;
}
.aff-quais-arrivees-redesign__time-status {
/* Logo Dynamique (TER Bleu / Opérateur) */
.aff-classique-quai-arr__brand {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-bottom: 24px;
z-index: 3;
}
.aff-classique-quai-arr__brand-logo {
height: 50px;
max-width: 220px;
object-fit: contain;
display: block;
}
.aff-classique-quai-arr__sncf-voyageurs {
font-size: 14px;
font-weight: 800;
color: #116938;
letter-spacing: 2px;
margin-top: 4px;
}
/* Ligne Heure + Statut */
.aff-classique-quai-arr__time-row {
display: flex;
align-items: baseline;
gap: 15px;
margin-bottom: 5px;
z-index: 2;
margin-bottom: 8px;
z-index: 3;
}
.aff-quais-arrivees-redesign__time {
color: #006330; /* Green theme text */
.aff-classique-quai-arr__time {
color: #116938;
font-weight: 700;
font-size: 7.5rem;
font-size: 105px;
line-height: 0.92;
letter-spacing: -1px;
}
.aff-classique-quai-arr__status {
color: #116938;
font-weight: 700;
font-size: 32px;
line-height: 1;
margin-left: 20px;
}
.aff-quais-arrivees-redesign__status {
color: #006330; /* Green theme status */
.aff-classique-quai-arr__time--suppressed,
.aff-classique-quai-arr__status--suppressed {
color: #d32f2f !important;
}
.aff-classique-quai-arr__time--delayed,
.aff-classique-quai-arr__status--delayed {
color: #f5a623 !important;
}
/* Grande Destination / Gare Terminus */
.aff-classique-quai-arr__destination {
color: #116938;
font-weight: 700;
font-size: 2.4rem;
line-height: 1;
font-size: 82px;
line-height: 1.05;
margin-top: 14px;
margin-bottom: 14px;
z-index: 3;
word-break: break-word;
}
.aff-quais-arrivees-redesign__destination {
color: #006330; /* Green theme destination (origin) */
font-weight: 700;
font-size: 5.5rem; /* Slightly smaller to fit 2 lines if needed */
line-height: 1.1;
margin-bottom: 5px;
z-index: 2;
white-space: normal; /* Allow wrapping */
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
max-height: 13rem; /* Prevent overflowing panel */
}
.aff-quais-arrivees-redesign__train-info {
/* Info Train (Type + Numéro) */
.aff-classique-quai-arr__train-info {
display: flex;
align-items: center;
gap: 10px;
z-index: 2;
align-items: baseline;
z-index: 3;
}
.aff-quais-arrivees-redesign__train-type {
color: #006330; /* Green theme train type */
.aff-classique-quai-arr__train-type {
color: #116938;
font-weight: 700;
font-size: 2.2rem;
font-size: 38px;
letter-spacing: 0.5px;
}
.aff-quais-arrivees-redesign__train-number {
color: #7e8a80; /* Grey/Green train number */
.aff-classique-quai-arr__train-number {
color: #7e859b;
font-weight: 400;
font-size: 2.2rem;
font-size: 38px;
letter-spacing: 0.5px;
margin-left: 12px;
}
.aff-quais-arrivees-redesign__icons {
/* Filigrane Vertical "arrivée" géant (orienté bas vers haut) */
.aff-classique-quai-arr__watermark {
position: absolute;
right: -5px;
bottom: 20px;
left: 40px;
width: 0;
height: 0;
display: flex;
align-items: center;
z-index: 2;
}
.aff-quais-arrivees-redesign__watermark {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%) rotate(-90deg);
transform-origin: right center;
font-size: 17rem; /* Massive size */
color: #dbebdf; /* Light green/grey watermark */
font-weight: 700;
z-index: 1;
justify-content: flex-start;
align-items: flex-end;
pointer-events: none;
user-select: none;
letter-spacing: 1.5rem;
z-index: 1;
}
.aff-classique-quai-arr__watermark span {
display: inline-block;
transform: rotate(-90deg);
transform-origin: 0 100%;
font-size: 168px;
font-weight: 700;
color: #d5e5d8;
line-height: 1;
letter-spacing: 0.05em;
white-space: nowrap;
}
/* =========================================================
RIGHT PANEL
RIGHT PANEL (VERT FORÊT - Conforme Image 2)
========================================================= */
.aff-quais-arrivees-redesign__right {
.aff-classique-quai-arr__right {
position: absolute;
top: 0;
left: 672px;
right: 0;
width: 65%;
bottom: 80px;
background-color: #006330; /* Green theme */
bottom: 85px;
background-color: #116938;
box-sizing: border-box;
padding: 30px 0 30px 0;
padding: 42px 0 20px 42px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.aff-quais-arrivees-redesign__provenance-title {
font-size: 3.4rem;
color: #dbeb1a; /* Yellow provenance header */
margin-left: 60px;
margin-bottom: 20px;
font-weight: 700;
.aff-classique-quai-arr__stops-wrapper {
position: relative;
width: 100%;
height: 100%;
}
.aff-quais-arrivees-redesign__stops-list {
.aff-classique-quai-arr__stops-list {
list-style: none;
margin: 0;
padding: 0 0 0 60px; /* Space from left edge */
width: 100%;
position: relative;
z-index: 2;
padding: 0;
display: flex;
flex-direction: column;
gap: 8px; /* Closer spacing between stops */
gap: 12px;
position: relative;
z-index: 2;
}
.aff-quais-arrivees-redesign__stop {
.aff-classique-quai-arr__stop-item {
display: flex;
align-items: center;
font-size: 3.4rem;
font-weight: 400;
color: #ffffff;
position: relative;
min-height: 42px;
padding-left: 50px; /* Space for rail and bullet */
min-height: 56px;
padding-left: 65px;
}
/* Bullet styling */
.aff-quais-arrivees-redesign__stop-bullet {
/* Rail vertical vert sauge (#5c8b6b) */
.aff-classique-quai-arr__stop-item::before {
content: '';
position: absolute;
left: 0; /* Centered on 24px rail: (24-16)/2 = 4 */
margin-left: 4px;
top: 50%;
transform: translateY(-50%);
width: 16px; /* Slightly larger bullet */
height: 16px;
background-color: #fbec22;
left: 20px;
width: 28px;
background-color: #5c8b6b;
top: -15px;
bottom: -15px;
z-index: 1;
}
/* Le rail monte jusqu'en haut au 1er arrêt */
.aff-classique-quai-arr__stop-item:first-child::before {
top: -600px;
}
/* Le dernier arrêt n'a pas de rail qui continue en dessous */
.aff-classique-quai-arr__stop-item:last-child::before {
display: none;
}
/* Pastille Jaune (#ffe500 / #fbec22) */
.aff-classique-quai-arr__bullet {
position: absolute;
left: 24px;
width: 20px;
height: 20px;
background-color: #ffe500;
border-radius: 50%;
z-index: 4;
}
.aff-quais-arrivees-redesign__stop-text {
position: relative;
z-index: 4;
/* Nom de la station */
.aff-classique-quai-arr__stop-name {
font-size: 42px;
font-weight: 600;
color: #ffffff;
white-space: nowrap;
letter-spacing: 0.5px;
padding-left: 10px; /* Space from bullet */
z-index: 4;
}
/* The vertical rail segments drawn behind each bullet */
.aff-quais-arrivees-redesign__stop::before {
content: '';
position: absolute;
left: 0;
width: 24px; /* Much wider rail */
background-color: #8fa993; /* Light green-grey rail color */
top: -10px;
bottom: -10px;
z-index: 1;
/* Gare terminus / mise en avant */
.aff-classique-quai-arr__stop-item--dest {
font-weight: 700;
}
/* Rail starts at the first bullet's center */
.aff-quais-arrivees-redesign__stop:first-child::before {
top: 50%;
}
/* Last stop has no rail continuing below */
.aff-quais-arrivees-redesign__stop:last-child::before {
bottom: 50%;
.aff-classique-quai-arr__stop-item--dest .aff-classique-quai-arr__stop-name {
background-color: #5c8b6b;
padding: 8px 32px 8px 65px;
margin-left: -65px;
border-radius: 6px;
position: relative;
z-index: 3;
display: inline-block;
font-weight: 700;
font-size: 44px;
}
/* =========================================================
FOOTER
FOOTER & HORLOGE (Conforme Image 2)
========================================================= */
.aff-quais-arrivees-redesign__footer {
.aff-classique-quai-arr__footer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 80px;
background-color: #006330; /* Green theme */
width: 1920px;
height: 85px;
background-color: #116938;
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
padding: 0 40px;
z-index: 10;
padding: 0 35px;
z-index: 20;
}
.aff-quais-arrivees-redesign__footer-left {
font-size: 3rem;
.aff-classique-quai-arr__footer-track {
font-size: 34px;
font-weight: 700;
color: #ffffff;
font-style: italic;
font-family: 'Achemine', 'Helvetica Neue', Arial, sans-serif;
letter-spacing: 0.5px;
}
.aff-quais-arrivees-redesign__footer-right {
.aff-classique-quai-arr__footer-clock {
display: flex;
align-items: center;
}
.aff-quais-arrivees-redesign__clock {
border: 2px solid #ffffff;
padding: 4px 14px;
font-size: 3rem;
font-weight: 700;
color: #ffffff;
.aff-classique-quai-arr__clock {
background-color: #00337f;
border: 2.5px solid #ffffff;
padding: 4px 16px;
border-radius: 3px;
display: flex;
align-items: center;
gap: 10px;
align-items: baseline;
gap: 4px;
font-family: 'Achemine', 'Helvetica Neue', Arial, sans-serif;
font-size: 42px;
font-weight: 700;
line-height: 1;
}
.aff-quais-arrivees-redesign__clock-s {
color: #f5a623;
.aff-classique-quai-arr__clock-hm {
color: #ffffff;
}
.aff-quais-arrivees-redesign__time--long-text {
font-size: 3.5rem !important;
line-height: 1.2 !important;
.aff-classique-quai-arr__clock-s {
color: #ff8c00;
margin-left: 2px;
}
.aff-quais-arrivees-redesign__time--suppressed,
.aff-quais-arrivees-redesign__status--suppressed {
color: #ff4c5b !important;
}
.aff-quais-arrivees-redesign__time--delayed,
.aff-quais-arrivees-redesign__status--delayed {
color: #ff9e43 !important;
}
/* Disrupted Stop styling */
.aff-quais-arrivees-redesign__stop-text--suppressed {
text-decoration: line-through;
color: #ff4c5b;
opacity: 0.6;
}
.aff-quais-arrivees-redesign__stop-text--delayed {
color: #ff9e43;
}
.aff-quais-arrivees-redesign__stop-status-badge {
font-size: 2.2rem;
font-weight: bold;
margin-left: 10px;
}
.aff-quais-arrivees-redesign__stop-status-badge--suppressed {
color: #ff4c5b;
}
.aff-quais-arrivees-redesign__stop-status-badge--delayed {
color: #ff9e43;
}
File diff suppressed because it is too large. Load diff
@@ -1,344 +1,315 @@
@font-face {
font-family: 'Achemine';
src: url('/public/fonts/achemine_regular.ttf') format('truetype');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Achemine';
src: url('/public/fonts/achemine_bold.ttf') format('truetype');
font-weight: 700;
font-style: normal;
font-display: swap;
}
body, html {
margin: 0;
padding: 0;
background-color: #0b376d;
background-color: #00337f;
overflow: hidden;
}
.aff-quais-departs-redesign__container {
display: flex;
flex-direction: row;
width: 100%;
height: 100%;
position: relative;
font-family: 'Achemine', 'Helvetica Neue', Arial, sans-serif;
background-color: #0b376d;
color: #fff;
overflow: hidden;
}
.aff-quais-departs-redesign__empty {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.aff-quais-departs-redesign__empty-text {
font-size: 6rem;
font-weight: 700;
color: #ffffff;
text-align: center;
}
/* =========================================================
LEFT PANEL
CONTAINER (1920x1080)
========================================================= */
.aff-quais-departs-redesign__left {
.aff-classique-quai-dep__container {
position: relative;
width: 1920px;
height: 1080px;
background-color: #00337f;
color: #ffffff;
font-family: 'Achemine', 'Avenir', 'Helvetica Neue', Arial, sans-serif;
overflow: hidden;
box-sizing: border-box;
}
/* =========================================================
LEFT PANEL (BLANC - Conforme Image 1)
========================================================= */
.aff-classique-quai-dep__left {
position: absolute;
top: 0;
left: 0;
width: 35%;
bottom: 80px;
width: 672px;
bottom: 85px;
background-color: #ffffff;
box-sizing: border-box;
padding: 30px 40px;
padding: 42px 48px;
display: flex;
flex-direction: column;
overflow: hidden;
}
.aff-quais-departs-redesign__sncf-logo {
width: 90px;
height: auto;
margin-bottom: 25px;
z-index: 2;
}
.aff-quais-departs-redesign__time-status {
/* Logo Dynamique */
.aff-classique-quai-dep__brand {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-bottom: 24px;
z-index: 3;
}
.aff-classique-quai-dep__brand-logo {
height: 50px;
max-width: 220px;
object-fit: contain;
display: block;
}
.aff-classique-quai-dep__sncf-voyageurs {
font-size: 14px;
font-weight: 800;
color: #00337f;
letter-spacing: 2px;
margin-top: 4px;
}
/* Ligne Heure + Statut */
.aff-classique-quai-dep__time-row {
display: flex;
align-items: baseline;
gap: 15px;
margin-bottom: 5px;
z-index: 2;
margin-bottom: 8px;
z-index: 3;
}
.aff-quais-departs-redesign__time {
color: #0b376d;
.aff-classique-quai-dep__time {
color: #00337f;
font-weight: 700;
font-size: 7.5rem;
font-size: 105px;
line-height: 0.92;
letter-spacing: -1px;
}
.aff-classique-quai-dep__status {
color: #00337f;
font-weight: 700;
font-size: 32px;
line-height: 1;
margin-left: 20px;
}
.aff-quais-departs-redesign__status {
color: #0b376d;
.aff-classique-quai-dep__time--suppressed,
.aff-classique-quai-dep__status--suppressed {
color: #d32f2f !important;
}
.aff-classique-quai-dep__time--delayed,
.aff-classique-quai-dep__status--delayed {
color: #f5a623 !important;
}
/* Grande Destination */
.aff-classique-quai-dep__destination {
color: #00337f;
font-weight: 700;
font-size: 2.4rem;
line-height: 1;
font-size: 82px;
line-height: 1.05;
margin-top: 14px;
margin-bottom: 14px;
z-index: 3;
word-break: break-word;
}
.aff-quais-departs-redesign__destination {
color: #0b376d;
font-weight: 700;
font-size: 5.5rem; /* Slightly smaller to fit 2 lines if needed */
line-height: 1.1;
margin-bottom: 5px;
z-index: 2;
white-space: normal; /* Allow wrapping */
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
max-height: 13rem; /* Prevent overflowing panel */
}
.aff-quais-departs-redesign__train-info {
/* Info Train (Type + Numéro) */
.aff-classique-quai-dep__train-info {
display: flex;
align-items: center;
gap: 10px;
z-index: 2;
align-items: baseline;
z-index: 3;
}
.aff-quais-departs-redesign__train-type {
color: #0b376d;
.aff-classique-quai-dep__train-type {
color: #00337f;
font-weight: 700;
font-size: 2.2rem;
font-size: 38px;
letter-spacing: 0.5px;
}
.aff-quais-departs-redesign__train-number {
color: #929bb0;
.aff-classique-quai-dep__train-number {
color: #7e859b;
font-weight: 400;
font-size: 2.2rem;
font-size: 38px;
letter-spacing: 0.5px;
margin-left: 12px;
}
.aff-quais-departs-redesign__icons {
/* Filigrane Vertical "départ" géant (orienté bas vers haut) */
.aff-classique-quai-dep__watermark {
position: absolute;
right: -5px;
bottom: 20px;
left: 40px;
width: 0;
height: 0;
display: flex;
align-items: center;
z-index: 2;
}
.aff-quais-departs-redesign__watermark {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%) rotate(-90deg);
transform-origin: right center;
font-size: 17rem; /* Massive size */
color: #e5e8ed;
font-weight: 700;
z-index: 1;
justify-content: flex-start;
align-items: flex-end;
pointer-events: none;
user-select: none;
letter-spacing: 1.5rem;
z-index: 1;
}
.aff-classique-quai-dep__watermark span {
display: inline-block;
transform: rotate(-90deg);
transform-origin: 0 100%;
font-size: 168px;
font-weight: 700;
color: #e2e8f2;
line-height: 1;
letter-spacing: 0.05em;
white-space: nowrap;
}
/* =========================================================
RIGHT PANEL
RIGHT PANEL (BLEU NUIT - Conforme Image 1)
========================================================= */
.aff-quais-departs-redesign__right {
.aff-classique-quai-dep__right {
position: absolute;
top: 0;
left: 672px;
right: 0;
width: 65%;
bottom: 80px;
background-color: #0b376d;
bottom: 85px;
background-color: #00337f;
box-sizing: border-box;
padding: 30px 0 30px 0;
padding: 42px 0 20px 42px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.aff-quais-departs-redesign__stops-list {
.aff-classique-quai-dep__stops-wrapper {
position: relative;
width: 100%;
height: 100%;
}
.aff-classique-quai-dep__stops-list {
list-style: none;
margin: 0;
padding: 0 0 0 60px; /* Space from left edge */
width: 100%;
position: relative;
z-index: 2;
padding: 0;
display: flex;
flex-direction: column;
gap: 8px; /* Closer spacing between stops */
gap: 12px;
position: relative;
z-index: 2;
}
.aff-quais-departs-redesign__stop {
.aff-classique-quai-dep__stop-item {
display: flex;
align-items: center;
font-size: 3.4rem;
font-weight: 400;
color: #ffffff;
position: relative;
min-height: 42px;
padding-left: 50px; /* Space for rail and bullet */
min-height: 56px;
padding-left: 65px;
}
/* Bullet styling */
.aff-quais-departs-redesign__stop-bullet {
/* Rail vertical violet/indigo (#5c538f) */
.aff-classique-quai-dep__stop-item::before {
content: '';
position: absolute;
left: 0; /* Centered on 24px rail: (24-16)/2 = 4 */
margin-left: 4px;
top: 50%;
transform: translateY(-50%);
width: 16px; /* Slightly larger bullet */
height: 16px;
background-color: #fbec22;
left: 20px;
width: 28px;
background-color: #5c538f;
top: -15px;
bottom: -15px;
z-index: 1;
}
/* Le rail monte jusqu'en haut au 1er arrêt */
.aff-classique-quai-dep__stop-item:first-child::before {
top: -600px;
}
/* Le dernier arrêt n'a pas de rail qui continue en dessous */
.aff-classique-quai-dep__stop-item:last-child::before {
display: none;
}
/* Pastille Jaune (#ffe500 / #fbec22) */
.aff-classique-quai-dep__bullet {
position: absolute;
left: 24px;
width: 20px;
height: 20px;
background-color: #ffe500;
border-radius: 50%;
z-index: 4;
}
.aff-quais-departs-redesign__stop-text {
position: relative;
z-index: 4;
/* Nom de la station */
.aff-classique-quai-dep__stop-name {
font-size: 42px;
font-weight: 600;
color: #ffffff;
white-space: nowrap;
letter-spacing: 0.5px;
padding-left: 10px; /* Space from bullet */
z-index: 4;
}
/* The vertical rail segments drawn behind each bullet */
.aff-quais-departs-redesign__stop::before {
content: '';
position: absolute;
left: 0;
width: 24px; /* Much wider rail */
background-color: #5c538f;
top: -10px;
bottom: -10px;
z-index: 1;
}
/* Rail extends all the way to the top */
.aff-quais-departs-redesign__stop:first-child::before {
top: -1000px;
}
/* Last stop has no rail continuing below */
.aff-quais-departs-redesign__stop:last-child::before {
display: none;
}
/* Highlighted Destination Stop */
.aff-quais-departs-redesign__stop--dest {
/* Gare terminus / mise en avant */
.aff-classique-quai-dep__stop-item--dest {
font-weight: 700;
}
.aff-quais-departs-redesign__stop--dest .aff-quais-departs-redesign__stop-text {
.aff-classique-quai-dep__stop-item--dest .aff-classique-quai-dep__stop-name {
background-color: #5c538f;
padding: 4px 20px 4px 60px; /* Increased left padding to cover bullet */
margin-left: -60px; /* Pull back to exactly align with left:0 */
border-radius: 4px;
padding: 8px 32px 8px 65px;
margin-left: -65px;
border-radius: 6px;
position: relative;
z-index: 3; /* Behind the bullet (z-index 4) */
z-index: 3;
display: inline-block;
font-weight: 700;
font-size: 44px;
}
/* =========================================================
FOOTER
FOOTER & HORLOGE (Conforme Image 1)
========================================================= */
.aff-quais-departs-redesign__footer {
.aff-classique-quai-dep__footer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 80px;
background-color: #0b376d;
width: 1920px;
height: 85px;
background-color: #00337f;
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
padding: 0 40px;
z-index: 10;
padding: 0 35px;
z-index: 20;
}
.aff-quais-departs-redesign__footer-left {
font-size: 3rem;
.aff-classique-quai-dep__footer-track {
font-size: 34px;
font-weight: 700;
color: #ffffff;
font-style: italic;
font-family: 'Achemine', 'Helvetica Neue', Arial, sans-serif;
letter-spacing: 0.5px;
}
.aff-quais-departs-redesign__footer-right {
.aff-classique-quai-dep__footer-clock {
display: flex;
align-items: center;
}
.aff-quais-departs-redesign__clock {
border: 2px solid #ffffff;
padding: 4px 14px;
font-size: 3rem;
font-weight: 700;
color: #ffffff;
.aff-classique-quai-dep__clock {
background-color: #00337f;
border: 2.5px solid #ffffff;
padding: 4px 16px;
border-radius: 3px;
display: flex;
align-items: center;
gap: 10px;
align-items: baseline;
gap: 4px;
font-family: 'Achemine', 'Helvetica Neue', Arial, sans-serif;
font-size: 42px;
font-weight: 700;
line-height: 1;
}
.aff-quais-departs-redesign__clock-s {
color: #f5a623;
.aff-classique-quai-dep__clock-hm {
color: #ffffff;
}
.aff-quais-departs-redesign__time--long-text {
font-size: 3.5rem !important;
line-height: 1.2 !important;
.aff-classique-quai-dep__clock-s {
color: #ff8c00;
margin-left: 2px;
}
.aff-quais-departs-redesign__time--suppressed,
.aff-quais-departs-redesign__status--suppressed {
color: #ff4c5b !important;
}
.aff-quais-departs-redesign__time--delayed,
.aff-quais-departs-redesign__status--delayed {
color: #ff9e43 !important;
}
/* Disrupted Stop styling */
.aff-quais-departs-redesign__stop-text--suppressed {
text-decoration: line-through;
color: #ff4c5b;
opacity: 0.6;
}
.aff-quais-departs-redesign__stop-text--delayed {
color: #ff9e43;
}
.aff-quais-departs-redesign__stop-status-badge {
font-size: 2.2rem;
font-weight: bold;
margin-left: 10px;
}
.aff-quais-departs-redesign__stop-status-badge--suppressed {
color: #ff4c5b;
}
.aff-quais-departs-redesign__stop-status-badge--delayed {
color: #ff9e43;
}
File diff suppressed because it is too large. Load diff
+15 -12
View File
@@ -550,6 +550,14 @@ async function fetchGtfsStationTimetable(uic, mode = 'departs') {
resultList = remainingToday;
}
if (resultList.length === 0) {
resultList = (enrichedToday.length > 0 ? enrichedToday : rawList.map((t, idx) => mapTrain(t, idx, false))).sort((a, b) => {
const aHM = (mode === 'arrivees' ? a._arrHM : a._depHM) || 0;
const bHM = (mode === 'arrivees' ? b._arrHM : b._depHM) || 0;
return aHM - bHM;
});
}
return resultList;
} catch (err) {
console.warn('[sncfApi] Erreur de chargement GTFS station:', err);
@@ -587,19 +595,14 @@ export async function getSncfTimetable({ gareName, mode = 'departs', quai = null
finalTrains = [];
}
// 3. Filtrage par quai si demandé
// 3. Filtrage strict par quai si demandé
if (quai) {
const normQuai = String(quai).trim().toLowerCase();
const matching = finalTrains.filter(t => String(t.quai || t.voie).trim().toLowerCase() === normQuai);
if (matching.length > 0) {
finalTrains = matching;
} else if (finalTrains.length > 0) {
finalTrains[0].quai = quai;
finalTrains[0].voie = quai;
finalTrains[0].quai_reel = quai;
finalTrains[0]._displayPlatform = quai;
finalTrains = [finalTrains[0]];
}
const cleanP = (p) => String(p || '').toLowerCase().replace(/^(?:voie|quai|track|platform)\s*/i, '').replace(/\s+/g, '').trim();
const targetP = cleanP(quai);
finalTrains = finalTrains.filter(t => {
const q = cleanP(t.quai || t.voie || t.quai_reel || t._displayPlatform);
return Boolean(q && q === targetP);
});
}
return { data: finalTrains, error: null };