RELEASE V5.0.0 : Voici les modifications apportées :
-> IMPLÉMENTATION DONNÉES GTFS / GTFS-RT / SIRI LTE SNCF -> RESTRUCTURATION DE L'APP. FONCTIONNALITÉS EN COURS DE DÉVELOPPEMENT : -> REFONTE DU DESIGN DES AFFICHEURS CLASSIQUES -> AMÉLIORATION DES
This commit is contained in:
1 parent
0e0d8c435f
commit
72de543dc6
18 files changed
+1550
-544
No files matched your search
@@ -5,21 +5,34 @@ import { fetchAllRows } from '../../../lib/fetchAllRows';
|
||||
import MarqueeText from '../../../components/MarqueeText';
|
||||
import useAnnonceSonoreRealtime from '../../../hooks/useAnnonceSonoreRealtime';
|
||||
import useAutoAnnonce from '../../../hooks/useAutoAnnonce';
|
||||
import { getSourceFromPath } from '../../../services/railwayService';
|
||||
import { getSncfTimetable } from '../../../services/sncfApi';
|
||||
|
||||
// Helper utilitaire : format HH:MM avec deux points (ex: 22:39)
|
||||
const formatTimeWithColon = (v) => {
|
||||
if (v == null) return '';
|
||||
const tryDate = new Date(v);
|
||||
if (!isNaN(tryDate.getTime())) {
|
||||
const hh = String(tryDate.getHours()).padStart(2, '0');
|
||||
const hh = String(tryDate.getHours() % 24).padStart(2, '0');
|
||||
const mm = String(tryDate.getMinutes()).padStart(2, '0');
|
||||
return `${hh}:${mm}`;
|
||||
}
|
||||
if (typeof v === 'string') {
|
||||
const m = v.match(/(\d{1,2}):(\d{2})(?::\d{2})?/);
|
||||
if (m) return `${String(m[1]).padStart(2, '0')}:${m[2]}`;
|
||||
if (m) {
|
||||
const h = parseInt(m[1], 10) % 24;
|
||||
return `${String(h).padStart(2, '0')}:${m[2]}`;
|
||||
}
|
||||
const m2 = v.match(/T(\d{2}):(\d{2})/);
|
||||
if (m2) return `${m2[1]}:${m2[2]}`;
|
||||
if (m2) {
|
||||
const h = parseInt(m2[1], 10) % 24;
|
||||
return `${String(h).padStart(2, '0')}:${m2[2]}`;
|
||||
}
|
||||
const m3 = v.match(/(\d{1,2})h(\d{2})/i);
|
||||
if (m3) {
|
||||
const h = parseInt(m3[1], 10) % 24;
|
||||
return `${String(h).padStart(2, '0')}:${m3[2]}`;
|
||||
}
|
||||
return v.slice(0, 5).replace('h', ':');
|
||||
}
|
||||
return String(v).slice(0, 5).replace('h', ':');
|
||||
@@ -445,7 +458,15 @@ export default function EVAArrivees() {
|
||||
// Récupération type de gare
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
const source = getSourceFromPath(window.location.pathname);
|
||||
const fetchStationType = async () => {
|
||||
if (source === 'sncf') {
|
||||
if (mounted) {
|
||||
setStationType('ville');
|
||||
setResolvedGareId(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let fetchedType = null;
|
||||
let fetchedId = gareIdParam || null;
|
||||
@@ -475,9 +496,7 @@ export default function EVAArrivees() {
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
};
|
||||
if (supabase) {
|
||||
fetchStationType();
|
||||
}
|
||||
fetchStationType();
|
||||
return () => { mounted = false; };
|
||||
}, [gareIdParam, gareDisplay]);
|
||||
|
||||
@@ -831,14 +850,55 @@ export default function EVAArrivees() {
|
||||
});
|
||||
}, [buildPerturbMap, computeStatusForHoraireAtStop, gareDisplay, gareIdParam, getStops, normalizeName]);
|
||||
|
||||
// Récupération Supabase avec polling
|
||||
// Récupération des données (SNCF GTFS ou Supabase) avec polling
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
let inFlight = false;
|
||||
const source = getSourceFromPath(window.location.pathname);
|
||||
|
||||
const fetchData = async () => {
|
||||
if (!mounted || inFlight) return;
|
||||
inFlight = true;
|
||||
|
||||
if (source === 'sncf') {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const { data: sncfData, error: sncfErr } = await getSncfTimetable({
|
||||
gareName: gareDisplay || gareParam,
|
||||
mode: 'arrivees'
|
||||
});
|
||||
if (!mounted) { inFlight = false; return; }
|
||||
if (sncfErr) {
|
||||
setError(sncfErr);
|
||||
} else {
|
||||
const formatted = (sncfData || []).map(t => {
|
||||
const timeStr = t.heure_arrivee || t.heure_depart || t.time || '';
|
||||
return {
|
||||
...t,
|
||||
_displayTime: formatTimeWithColon(timeStr),
|
||||
_displayPlatform: t.quai || t.voie || '',
|
||||
_arrHM: t._arrHM || 0,
|
||||
_status: t._status || {
|
||||
label: t.etat === 'supprime' ? 'supprimé' : (t.retard_minutes > 0 ? `retardé +${t.retard_minutes}min.` : "à l'heure"),
|
||||
detail: '',
|
||||
delayMinutes: t.retard_minutes || null,
|
||||
causeDetails: t.cause_retard || null
|
||||
}
|
||||
};
|
||||
});
|
||||
setHoraires(formatted.slice(0, 10));
|
||||
setAllHoraires(formatted);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) setError(e.message || String(e));
|
||||
} finally {
|
||||
if (mounted) setLoading(false);
|
||||
inFlight = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!supabase) {
|
||||
setLoading(false);
|
||||
inFlight = false;
|
||||
@@ -936,9 +996,12 @@ export default function EVAArrivees() {
|
||||
|
||||
const parseTimeHM = (s) => {
|
||||
if (!s) return null;
|
||||
if (typeof s === 'number') return s;
|
||||
const m = String(s).match(/(\d{1,2}):(\d{2})/);
|
||||
if (m) return parseInt(m[1], 10) * 60 + parseInt(m[2], 10);
|
||||
if (typeof s === 'number') return s % 1440;
|
||||
const m = String(s).match(/(\d{1,2})[:h](\d{2})/i);
|
||||
if (m) {
|
||||
const h = parseInt(m[1], 10) % 24;
|
||||
return h * 60 + parseInt(m[2], 10);
|
||||
}
|
||||
const d = new Date(s);
|
||||
if (!isNaN(d.getTime())) return d.getHours() * 60 + d.getMinutes();
|
||||
return null;
|
||||
@@ -1129,7 +1192,7 @@ export default function EVAArrivees() {
|
||||
fetchData();
|
||||
const intervalId = setInterval(fetchData, 15000);
|
||||
return () => { mounted = false; clearInterval(intervalId); };
|
||||
}, [gareDisplay, gareIdParam, buildPerturbMap, computeStatusForHoraireAtStop, normalizeName, applyPerturbationsToHoraires, getStops, getPerturbationJours]);
|
||||
}, [gareDisplay, gareParam, gareIdParam, buildPerturbMap, computeStatusForHoraireAtStop, normalizeName, applyPerturbationsToHoraires, getStops, getPerturbationJours]);
|
||||
|
||||
// Alternance retard / texte toutes les 2s
|
||||
const [alternateDelay, setAlternateDelay] = useState(false);
|
||||
|
||||
@@ -5,21 +5,34 @@ import { fetchAllRows } from '../../../lib/fetchAllRows';
|
||||
import MarqueeText from '../../../components/MarqueeText';
|
||||
import useAnnonceSonoreRealtime from '../../../hooks/useAnnonceSonoreRealtime';
|
||||
import useAutoAnnonce from '../../../hooks/useAutoAnnonce';
|
||||
import { getSourceFromPath } from '../../../services/railwayService';
|
||||
import { getSncfTimetable } from '../../../services/sncfApi';
|
||||
|
||||
// Helper utilitaire : format HH:MM avec deux points (ex: 22:39)
|
||||
const formatTimeWithColon = (v) => {
|
||||
if (v == null) return '';
|
||||
const tryDate = new Date(v);
|
||||
if (!isNaN(tryDate.getTime())) {
|
||||
const hh = String(tryDate.getHours()).padStart(2, '0');
|
||||
const hh = String(tryDate.getHours() % 24).padStart(2, '0');
|
||||
const mm = String(tryDate.getMinutes()).padStart(2, '0');
|
||||
return `${hh}:${mm}`;
|
||||
}
|
||||
if (typeof v === 'string') {
|
||||
const m = v.match(/(\d{1,2}):(\d{2})(?::\d{2})?/);
|
||||
if (m) return `${String(m[1]).padStart(2, '0')}:${m[2]}`;
|
||||
if (m) {
|
||||
const h = parseInt(m[1], 10) % 24;
|
||||
return `${String(h).padStart(2, '0')}:${m[2]}`;
|
||||
}
|
||||
const m2 = v.match(/T(\d{2}):(\d{2})/);
|
||||
if (m2) return `${m2[1]}:${m2[2]}`;
|
||||
if (m2) {
|
||||
const h = parseInt(m2[1], 10) % 24;
|
||||
return `${String(h).padStart(2, '0')}:${m2[2]}`;
|
||||
}
|
||||
const m3 = v.match(/(\d{1,2})h(\d{2})/i);
|
||||
if (m3) {
|
||||
const h = parseInt(m3[1], 10) % 24;
|
||||
return `${String(h).padStart(2, '0')}:${m3[2]}`;
|
||||
}
|
||||
return v.slice(0, 5).replace('h', ':');
|
||||
}
|
||||
return String(v).slice(0, 5).replace('h', ':');
|
||||
@@ -441,7 +454,15 @@ export default function EVADeparts() {
|
||||
// Récupération type de gare
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
const source = getSourceFromPath(window.location.pathname);
|
||||
const fetchStationType = async () => {
|
||||
if (source === 'sncf') {
|
||||
if (mounted) {
|
||||
setStationType('ville');
|
||||
setResolvedGareId(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let fetchedType = null;
|
||||
let fetchedId = gareIdParam || null;
|
||||
@@ -471,9 +492,7 @@ export default function EVADeparts() {
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
};
|
||||
if (supabase) {
|
||||
fetchStationType();
|
||||
}
|
||||
fetchStationType();
|
||||
return () => { mounted = false; };
|
||||
}, [gareIdParam, gareDisplay]);
|
||||
|
||||
@@ -827,14 +846,52 @@ export default function EVADeparts() {
|
||||
});
|
||||
}, [buildPerturbMap, computeStatusForHoraireAtStop, gareDisplay, gareIdParam, getStops, normalizeName]);
|
||||
|
||||
// Récupération Supabase avec polling
|
||||
// Récupération des données (SNCF GTFS ou Supabase) avec polling
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
let inFlight = false;
|
||||
const source = getSourceFromPath(window.location.pathname);
|
||||
|
||||
const fetchData = async () => {
|
||||
if (!mounted || inFlight) return;
|
||||
inFlight = true;
|
||||
|
||||
if (source === 'sncf') {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const { data: sncfData, error: sncfErr } = await getSncfTimetable({
|
||||
gareName: gareDisplay || gareParam,
|
||||
mode: 'departs'
|
||||
});
|
||||
if (!mounted) { inFlight = false; return; }
|
||||
if (sncfErr) {
|
||||
setError(sncfErr);
|
||||
} else {
|
||||
const formatted = (sncfData || []).map(t => ({
|
||||
...t,
|
||||
_displayTime: formatTimeWithColon(t.heure_depart),
|
||||
_displayPlatform: t.quai || t.voie || '',
|
||||
_depHM: t._depHM || 0,
|
||||
_status: t._status || {
|
||||
label: t.etat === 'supprime' ? 'supprimé' : (t.retard_minutes > 0 ? `retardé +${t.retard_minutes}min.` : "à l'heure"),
|
||||
detail: '',
|
||||
delayMinutes: t.retard_minutes || null,
|
||||
causeDetails: t.cause_retard || null
|
||||
}
|
||||
}));
|
||||
setHoraires(formatted.slice(0, 10));
|
||||
setAllHoraires(formatted);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) setError(e.message || String(e));
|
||||
} finally {
|
||||
if (mounted) setLoading(false);
|
||||
inFlight = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!supabase) {
|
||||
setLoading(false);
|
||||
inFlight = false;
|
||||
@@ -952,9 +1009,12 @@ export default function EVADeparts() {
|
||||
|
||||
const parseTimeHM = (s) => {
|
||||
if (!s) return null;
|
||||
if (typeof s === 'number') return s;
|
||||
const m = String(s).match(/(\d{1,2}):(\d{2})/);
|
||||
if (m) return parseInt(m[1], 10) * 60 + parseInt(m[2], 10);
|
||||
if (typeof s === 'number') return s % 1440;
|
||||
const m = String(s).match(/(\d{1,2})[:h](\d{2})/i);
|
||||
if (m) {
|
||||
const h = parseInt(m[1], 10) % 24;
|
||||
return h * 60 + parseInt(m[2], 10);
|
||||
}
|
||||
const d = new Date(s);
|
||||
if (!isNaN(d.getTime())) return d.getHours() * 60 + d.getMinutes();
|
||||
return null;
|
||||
@@ -1145,7 +1205,7 @@ export default function EVADeparts() {
|
||||
fetchData();
|
||||
const intervalId = setInterval(fetchData, 15000);
|
||||
return () => { mounted = false; clearInterval(intervalId); };
|
||||
}, [gareDisplay, gareIdParam, buildPerturbMap, computeStatusForHoraireAtStop, normalizeName, applyPerturbationsToHoraires, getStops, getPerturbationJours]);
|
||||
}, [gareDisplay, gareParam, gareIdParam, buildPerturbMap, computeStatusForHoraireAtStop, normalizeName, applyPerturbationsToHoraires, getStops, getPerturbationJours]);
|
||||
|
||||
// Alternance retard / texte toutes les 2s
|
||||
const [alternateDelay, setAlternateDelay] = useState(false);
|
||||
|
||||
Reference in new issue
Block a user