V2.0.0 : VOICI LES MODIFICATIONS MAJEURS APPRTÉES DANS CETTE VERSION :

-> REFONTE DU DESIGN DES AFFICHEURS CLASSIQUES (GÉNÉRAL + QUAIS)
-> IMPLÉMENTATION DU DESIGN AFL : GÉNÉRAL ET QUAIS

-> CHOIX DE LA SÉLECTION DES TYPES D'AFFICHEURS (GÉNÉRAL / QUAIS) UNE FOIS LE TYPE D'AFFICHEUR SÉLECTIONNÉS

EN COURS DE DÉVELOPPEMENT :

-> IMPLÉMENTATION DES PERTURBATION DU LANDEMAIN POUR LES HORAIRES DU LANDEMAIN AFFICHÉS AVANT MINUIT.
-> IMPLÉMENTATION DES AFFICHEURS D'INFORMATIONS / INFOS TRAVAUX, etc... (AFFICHEURS AFL)
This commit is contained in:
Ferrovia committed 2026-06-14 22:02:08 +02:00
1 parent f0d9ed9ee7
commit f06ebbaf48
43 files changed
+10519 -3813

No files matched your search

+279
View File
@@ -0,0 +1,279 @@
import React, { useEffect, useState } from 'react';
import supabase from '../lib/supabase';
import WcsButton from '../components/WcsButton';
export default function SelectAfficheur({ onBack, navigate }) {
const params = new URLSearchParams(window.location.search || '');
const gareParam = params.get('gare') || '';
const modeParam = params.get('mode') || 'departs';
const typeParam = params.get('type') || 'afl';
const [platforms, setPlatforms] = useState([]);
const [selectedPlatform, setSelectedPlatform] = useState('');
const [manualPlatform, setManualPlatform] = useState('');
const [loading, setLoading] = useState(true);
// Decode and format station name for display
const decodedGare = gareParam
? decodeURIComponent(String(gareParam).replace(/\+/g, ' ')).replace(/-/g, ' ')
: '';
useEffect(() => {
let mounted = true;
async function loadPlatforms() {
if (!supabase) {
setLoading(false);
return;
}
try {
setLoading(true);
const { data, error } = await supabase.from('horaires').select('stops, quai_reel, voie, quai, gare_depart_nom, gare_arrivee_nom');
if (error) throw error;
if (!mounted) return;
const platformSet = new Set();
const normGare = decodedGare.normalize('NFD').replace(/\p{M}/gu, '').toLowerCase().trim();
(data || []).forEach(h => {
let stopsList = [];
if (Array.isArray(h.stops)) {
stopsList = h.stops;
} else if (typeof h.stops === 'string') {
try { stopsList = JSON.parse(h.stops); } catch (e) {}
}
if (!Array.isArray(stopsList)) stopsList = [];
let foundStop = stopsList.find(s => {
const name = (s.gare_nom || s.nom || s.name || '').toString().normalize('NFD').replace(/\p{M}/gu, '').toLowerCase().trim();
return name === normGare || name.includes(normGare) || normGare.includes(name);
});
if (foundStop) {
const platform = (foundStop.quai_reel || foundStop.quai || foundStop.platform || foundStop.quay || '').toString().trim();
if (platform) platformSet.add(platform);
} else {
const origin = (h.gare_depart_nom || '').toString().normalize('NFD').replace(/\p{M}/gu, '').toLowerCase().trim();
const dest = (h.gare_arrivee_nom || '').toString().normalize('NFD').replace(/\p{M}/gu, '').toLowerCase().trim();
if (origin === normGare || origin.includes(normGare) || normGare.includes(origin) ||
dest === normGare || dest.includes(normGare) || normGare.includes(dest)) {
const platform = (h.quai_reel || h.voie || h.quai || '').toString().trim();
if (platform) platformSet.add(platform);
}
}
});
const sortedPlatforms = Array.from(platformSet).sort((a, b) => a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' }));
setPlatforms(sortedPlatforms);
if (sortedPlatforms.length > 0) {
setSelectedPlatform(sortedPlatforms[0]);
}
} catch (err) {
console.error('Error fetching platforms:', err);
} finally {
if (mounted) setLoading(false);
}
}
loadPlatforms();
return () => {
mounted = false;
};
}, [gareParam, decodedGare]);
const handleGeneral = () => {
if (typeof navigate === 'function') {
const kebab = gareParam ? String(gareParam).trim() : '';
navigate(`/afficheurs/${modeParam}/${typeParam}?gare=${encodeURIComponent(kebab)}`);
}
};
const handlePlatform = () => {
const quai = manualPlatform.trim() || selectedPlatform;
if (!quai) {
alert("Veuillez sélectionner ou saisir une voie.");
return;
}
if (typeof navigate === 'function') {
const kebab = gareParam ? String(gareParam).trim() : '';
navigate(`/afficheurs/${modeParam}/${typeParam}/quais?gare=${encodeURIComponent(kebab)}&quai=${encodeURIComponent(quai)}`);
}
};
return (
<div style={{
maxWidth: '650px',
margin: '40px auto',
padding: '32px',
backgroundColor: '#ffffff',
borderRadius: '12px',
boxShadow: '0 4px 20px rgba(0, 0, 0, 0.08)',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'
}}>
<h2 style={{
marginTop: 0,
marginBottom: '8px',
color: '#002257',
fontSize: '28px',
fontWeight: 700
}}>
Sélection de l'Afficheur
</h2>
<p style={{
margin: '0 0 24px 0',
color: '#555555',
fontSize: '16px',
lineHeight: 1.5
}}>
Pour la gare de <strong>{decodedGare}</strong> en mode <strong>{modeParam === 'arrivees' ? 'Arrivées' : 'Départs'}</strong>.
</p>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '24px', marginBottom: '32px' }}>
{/* General Afficheur Option */}
<div style={{
border: '2px solid #e2ebf6',
borderRadius: '8px',
padding: '24px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
alignItems: 'flex-start',
background: '#fafbfc'
}}>
<div>
<h3 style={{ margin: '0 0 12px 0', color: '#00439e', fontSize: '20px' }}>Afficheur Général</h3>
<p style={{ margin: '0 0 20px 0', color: '#666', fontSize: '14px', lineHeight: 1.4 }}>
Affiche la liste complète de tous les trains programmés pour cette gare.
</p>
</div>
<button
onClick={handleGeneral}
style={{
backgroundColor: '#00439e',
color: '#ffffff',
border: 'none',
borderRadius: '6px',
padding: '12px 20px',
fontSize: '15px',
fontWeight: '600',
cursor: 'pointer',
width: '100%',
transition: 'background-color 0.2s'
}}
>
Accéder à l'Afficheur
</button>
</div>
{/* Platform-specific Afficheur Option */}
<div style={{
border: '2px solid #e2ebf6',
borderRadius: '8px',
padding: '24px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
background: '#fafbfc'
}}>
<div>
<h3 style={{ margin: '0 0 12px 0', color: '#00439e', fontSize: '20px' }}>Afficheur par Quai</h3>
<p style={{ margin: '0 0 16px 0', color: '#666', fontSize: '14px', lineHeight: 1.4 }}>
Affiche le prochain train et sa desserte spécifiquement pour une voie/quai.
</p>
{loading ? (
<div style={{ fontSize: '14px', color: '#888', marginBottom: '16px' }}>Chargement des voies...</div>
) : (
<div style={{ marginBottom: '16px' }}>
{platforms.length > 0 ? (
<div style={{ marginBottom: '12px' }}>
<label style={{ display: 'block', fontSize: '13px', fontWeight: 600, color: '#333', marginBottom: '4px' }}>
Sélectionner une voie existante :
</label>
<select
value={selectedPlatform}
onChange={(e) => {
setSelectedPlatform(e.target.value);
setManualPlatform(''); // Reset manual entry on selection
}}
style={{
width: '100%',
padding: '8px 12px',
borderRadius: '6px',
border: '1px solid #ccc',
fontSize: '14px'
}}
>
{platforms.map(p => (
<option key={p} value={p}>Voie {p}</option>
))}
</select>
</div>
) : (
<div style={{ fontSize: '13px', color: '#ff6b6b', marginBottom: '12px' }}>
Aucune voie détectée automatiquement pour cette gare.
</div>
)}
<div>
<label style={{ display: 'block', fontSize: '13px', fontWeight: 600, color: '#333', marginBottom: '4px' }}>
Saisie libre d'une voie :
</label>
<input
type="text"
placeholder="Ex: A, 2 bis, 4..."
value={manualPlatform}
onChange={(e) => setManualPlatform(e.target.value)}
style={{
width: '100%',
padding: '8px 12px',
borderRadius: '6px',
border: '1px solid #ccc',
fontSize: '14px',
boxSizing: 'border-box'
}}
/>
</div>
</div>
)}
</div>
<button
onClick={handlePlatform}
style={{
backgroundColor: '#008b47',
color: '#ffffff',
border: 'none',
borderRadius: '6px',
padding: '12px 20px',
fontSize: '15px',
fontWeight: '600',
cursor: 'pointer',
width: '100%',
transition: 'background-color 0.2s'
}}
>
Accéder au Quai
</button>
</div>
</div>
<div style={{
borderTop: '1px solid #eeeeee',
paddingTop: '20px',
display: 'flex',
justifyContent: 'flex-start'
}}>
<WcsButton
mode="plain"
onClick={() => {
if (typeof onBack === 'function') {
onBack();
} else if (typeof navigate === 'function') {
navigate(`/afficheurs?gare=${encodeURIComponent(gareParam)}&mode=${modeParam}`);
}
}}
label="Retour aux types d'afficheurs"
/>
</div>
</div>
);
}