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:
1 parent
f0d9ed9ee7
commit
f06ebbaf48
43 files changed
+10519
-3813
No files matched your search
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user