Files
ferrovia-infogare/src/pages/Departs.js
T
AdminGit 6cc1e1820d V1.1.0 //
-> Refonte du design de sélection d'afficheurs
-> Ajout du Mode PiP
-> Patch du bug du logo de l'horaire pas affiché. Corrigé pour les Horaires TER à 6 numéros.
2026-10-07 15:32:21 +02:00

202 lines
7.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useEffect, useState, useMemo } from 'react';
import { WcsCard, WcsCardBody, WcsButton, WcsInput, WcsBadge, WcsHorizontalStepper } from 'wcs-react';
import sessionMemory from '../lib/sessionMemory';
import { getSourceFromPath, searchGares } from '../services/railwayService';
const POPULAR_GARES = [
'Paris-Gare-de-Lyon',
'Besançon-Viotte',
'Dijon-Ville',
'Lyon-Part-Dieu',
'Marseille-Saint-Charles',
'Lille-Flandres',
'Strasbourg',
'Bordeaux-Saint-Jean',
'Nantes',
'Rennes'
];
export default function Departs({ onBack, navigate }) {
const source = getSourceFromPath(window.location.pathname);
const prefix = source === 'supabase' ? '/ferrovia' : '';
const [allGares, setAllGares] = useState([]);
const [sncfResults, setSncfResults] = useState([]);
const [loading, setLoading] = useState(source === 'supabase');
const [error, setError] = useState(null);
const [search, setSearch] = useState('');
// Pour Supabase : charger les gares une seule fois au montage
useEffect(() => {
let mounted = true;
if (source === 'supabase') {
async function loadSupabaseGares() {
setLoading(true);
setError(null);
try {
const data = await searchGares('', 'supabase');
if (mounted) setAllGares(Array.isArray(data) ? data.map(r => r.nom || r) : []);
} catch (err) {
console.error('Supabase error:', err);
if (mounted) setError(err.message || String(err));
} finally {
if (mounted) setLoading(false);
}
}
loadSupabaseGares();
}
return () => { mounted = false; };
}, [source]);
// Pour SNCF : recherche dynamique avec debounce dès 2 caractères
useEffect(() => {
let active = true;
if (source === 'sncf') {
const q = search.trim();
if (q.length < 2) {
setSncfResults([]);
return;
}
const timer = setTimeout(async () => {
try {
const results = await searchGares(q, 'sncf');
if (active) setSncfResults(results);
} catch (e) {
if (active) setSncfResults([]);
}
}, 180);
return () => {
active = false;
clearTimeout(timer);
};
}
}, [search, source]);
// Liste finale affichée
const displayedGares = useMemo(() => {
if (source === 'supabase') {
const q = search.toLowerCase().trim();
return allGares.filter(nom => String(nom).toLowerCase().includes(q)).map(nom => ({ nom }));
}
return sncfResults;
}, [source, allGares, sncfResults, search]);
const handleSelectGare = (nom) => {
try { sessionMemory.set('afficheurMode', 'departs'); } catch (e) {}
if (navigate) {
const cleanNom = String(nom).trim().replace(/\s+/g, '-');
navigate(`${prefix}/afficheurs/${encodeURIComponent(cleanNom)}?mode=departs`);
}
};
const steps = [
{ text: 'Choix de la gare', button: { kind: 'Text', text: '1' }, complete: false },
{ text: "Type d'afficheur", button: { kind: 'Text', text: '2' }, disable: true },
{ text: 'Options & Affichage', button: { kind: 'Text', text: '3' }, disable: true }
];
return (
<div style={{ maxWidth: '900px', margin: '0 auto', padding: '24px', overflowY: 'auto', maxHeight: 'calc(100vh - 120px)' }}>
<WcsHorizontalStepper steps={steps} currentStep={0} style={{ marginBottom: '24px' }} />
<WcsCard mode="raised">
<WcsCardBody>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '24px' }}>
<div>
<h1 style={{ margin: '0 0 8px 0', fontSize: '24px', color: 'var(--wcs-primary)' }}>🚆 Prochains Départs</h1>
<p style={{ margin: 0, color: 'var(--wcs-text-light)' }}>Recherchez une gare pour afficher le tableau des départs en temps réel.</p>
</div>
<WcsBadge className={source === 'supabase' ? 'wcs-secondary' : 'wcs-primary'} shape="rounded">
{source === 'supabase' ? '🗄️ BDD Ferrovia' : '🌐 SNCF GTFS Live'}
</WcsBadge>
</div>
<div style={{ marginBottom: '24px' }}>
<WcsInput
type="text"
icon="search"
placeholder="Rechercher une gare (ex: Dijon, Paris, Lyon, Besançon...)"
value={search}
onWcsInput={(e) => setSearch(e.target.value)}
style={{ width: '100%' }}
clearable={true}
/>
</div>
<div style={{ marginBottom: '24px' }}>
<p style={{ fontWeight: 'bold', marginBottom: '12px' }}>Gares fréquentes :</p>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px' }}>
{POPULAR_GARES.map((gName) => (
<WcsButton
key={gName}
mode="stroked"
shape="round"
size="s"
onClick={() => handleSelectGare(gName)}
>
{gName.replace(/-/g, ' ')}
</WcsButton>
))}
</div>
</div>
{loading && <p style={{ color: 'var(--wcs-primary)', fontWeight: 600 }}>⏳ Chargement des gares...</p>}
{error && <p style={{ color: 'var(--wcs-danger)', fontWeight: 600 }}>⚠️ Erreur : {error}</p>}
{source === 'sncf' && search.trim().length < 2 && (
<div style={{ textAlign: 'center', padding: '24px 16px', color: 'var(--wcs-text-light)', background: 'var(--wcs-light)', borderRadius: '8px', border: '1px dashed var(--wcs-gray)' }}>
<p style={{ margin: 0, fontSize: '14px' }}>
💡 Saisissez au moins <strong>2 lettres</strong> pour chercher parmi plus de 3 500 gares du réseau français.
</p>
</div>
)}
{source === 'sncf' && search.trim().length >= 2 && displayedGares.length === 0 && (
<div style={{ textAlign: 'center', padding: '24px', color: 'var(--wcs-text-light)', background: 'var(--wcs-white)', borderRadius: '8px' }}>
Aucune gare trouvée pour « <strong>{search}</strong> ».
</div>
)}
{displayedGares.length > 0 && (
<div role="list" style={{ display: 'flex', flexDirection: 'column', gap: '8px', marginBottom: '24px' }}>
{displayedGares.map((g) => {
const nom = g.nom;
return (
<WcsCard
key={g.uic || nom}
mode="flat"
style={{ cursor: 'pointer', border: '1px solid var(--wcs-light)' }}
onClick={() => handleSelectGare(nom)}
>
<WcsCardBody style={{ padding: '12px 16px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<div style={{ fontWeight: 'bold', fontSize: '16px', color: 'var(--wcs-primary)' }}>{nom}</div>
{g.commune && (
<div style={{ fontSize: '13px', color: 'var(--wcs-text-light)' }}>
{g.commune} {g.departement ? `(${g.departement})` : ''}
</div>
)}
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
{g.uic && <WcsBadge>{g.uic}</WcsBadge>}
<span style={{ color: 'var(--wcs-primary)', fontSize: '20px', fontWeight: 'bold' }}>›</span>
</div>
</WcsCardBody>
</WcsCard>
);
})}
</div>
)}
<div style={{ borderTop: '1px solid var(--wcs-light)', paddingTop: '16px' }}>
<WcsButton mode="clear" onClick={onBack}>
← Retour à l'accueil
</WcsButton>
</div>
</WcsCardBody>
</WcsCard>
</div>
);
}