V2.0.1 : 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
This commit is contained in:
Ferrovia committed 2026-07-10 01:49:39 +02:00
1 parent 68f0bc2807
commit 71822823f2
9 files changed
+189 -497

No files matched your search

+7 -9
View File
@@ -109,18 +109,16 @@ export default function useAnnonceSonoreRealtime(gareId, affType = null, quaiNam
// Si c'est une annonce qu'on n'a pas encore vue // Si c'est une annonce qu'on n'a pas encore vue
if (latest.id !== lastAnnonceIdRef.current) { if (latest.id !== lastAnnonceIdRef.current) {
const now = new Date(); // S'il s'agit du premier chargement, on mémorise juste l'ID sans la jouer
const createdAt = new Date(latest.created_at); // sauf si on veut vraiment utiliser diffSeconds, mais pour éviter les problèmes d'horloge
const diffSeconds = (now.getTime() - createdAt.getTime()) / 1000; // on se fie au fait que lastAnnonceIdRef.current passe de null à l'ID.
if (lastAnnonceIdRef.current === null) {
// On ne joue l'annonce que si elle a été insérée il y a moins de 15 secondes lastAnnonceIdRef.current = latest.id;
if (diffSeconds < 15) { } else {
// C'est une NOUVELLE annonce apparue pendant qu'on polle
console.debug('[useAnnonceSonoreRealtime] Annonce spontanée détectée :', latest); console.debug('[useAnnonceSonoreRealtime] Annonce spontanée détectée :', latest);
lastAnnonceIdRef.current = latest.id; lastAnnonceIdRef.current = latest.id;
playAnnonce(latest); playAnnonce(latest);
} else {
// Si elle est trop vieille, on l'ignore silencieusement
lastAnnonceIdRef.current = latest.id;
} }
} }
} }
+23 -72
View File
@@ -738,80 +738,31 @@ export default function AFLArrivees(){
tomorrow.setDate(today.getDate()+1); tomorrow.setDate(today.getDate()+1);
const tomorrowStr = localDateStr(tomorrow); const tomorrowStr = localDateStr(tomorrow);
const weekdayIndex = (d) => d.getDay(); // 0 dimanche .. 6 samedi
const weekdayNameForColumn = (d) => {
const i = weekdayIndex(d);
switch(i) {
case 1: return 'circule_lundi';
case 2: return 'circule_mardi';
case 3: return 'circule_mercredi';
case 4: return 'circule_jeudi';
case 5: return 'circule_vendredi';
case 6: return 'circule_samedi';
case 0: return 'circule_dimanche';
default: return null;
}
};
const isPublicHoliday = (d) => {
const y = d.getFullYear();
const m = d.getMonth() + 1;
const day = d.getDate();
const s = `${y}-${String(m).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
const holidays = [
`${y}-01-01`, // Jour de l'an
`${y}-05-01`, // Fête du Travail
`${y}-05-08`, // Victoire 1945
`${y}-07-14`, // Fête nationale
`${y}-08-15`, // Assomption
`${y}-11-01`, // Toussaint
`${y}-11-11`, // Armistice 1918
`${y}-12-25`, // Noël
];
// Dates variables pour 2026
if (y === 2026) {
holidays.push('2026-04-06'); // Lundi de Pâques
holidays.push('2026-05-14'); // Ascension
holidays.push('2026-05-25'); // Lundi de Pentecôte
}
return holidays.includes(s);
};
const parseCirculationFlag = (value, defaultValue) => {
if (value === null || typeof value === 'undefined') {
return defaultValue;
}
if (typeof value === 'boolean') {
return value;
}
if (typeof value === 'number') {
return value === 1;
}
if (typeof value === 'string') {
const s = value.trim().toLowerCase();
if (['true', '1', 't', 'y', 'yes', 'oui'].includes(s)) {
return true;
}
if (['false', '0', 'f', 'n', 'no', 'non'].includes(s)) {
return false;
}
}
return defaultValue;
};
const circulatesOn = (h, d) => { const circulatesOn = (h, d) => {
if (isPublicHoliday(d)) { const pad = (n) => String(n).padStart(2, '0');
// On public holidays, circulation is determined SOLELY by `circule_jours_feries`. const targetDateStr = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
// If the flag is true, it runs. Otherwise, it doesn't.
return parseCirculationFlag(h['circule_jours_feries'], false); const customDaysRaw = h.jours_personnalises;
if (!customDaysRaw) return false;
let customDays = [];
if (Array.isArray(customDaysRaw)) {
customDays = customDaysRaw;
} else if (typeof customDaysRaw === 'string') {
try {
customDays = JSON.parse(customDaysRaw);
} catch (e) {
return false;
}
} }
// On a regular day, check the corresponding weekday column. if (Array.isArray(customDays)) {
const col = weekdayNameForColumn(d); const match = customDays.find(item => item.date === targetDateStr);
if (!col) return true; // Should not happen, but permissive. if (match) {
return Boolean(match.circule);
// For regular weekdays, the default is to run unless specified otherwise. }
return parseCirculationFlag(h[col], true); }
return false;
}; };
// Construire listes pour aujourd'hui et demain en tenant compte des colonnes circule_* // Construire listes pour aujourd'hui et demain en tenant compte des colonnes circule_*
+23 -45
View File
@@ -487,53 +487,31 @@ const AFLArriveesQuais = () => {
tomorrow.setDate(today.getDate() + 1); tomorrow.setDate(today.getDate() + 1);
const tomorrowStr = localDateStr(tomorrow); const tomorrowStr = localDateStr(tomorrow);
const weekdayIndex = (d) => d.getDay();
const weekdayNameForColumn = (d) => {
const i = weekdayIndex(d);
switch (i) {
case 1: return 'circule_lundi';
case 2: return 'circule_mardi';
case 3: return 'circule_mercredi';
case 4: return 'circule_jeudi';
case 5: return 'circule_vendredi';
case 6: return 'circule_samedi';
case 0: return 'circule_dimanche';
default: return null;
}
};
const isPublicHoliday = (d) => {
const y = d.getFullYear();
const m = d.getMonth() + 1;
const day = d.getDate();
const s = `${y}-${String(m).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
const holidays = [
`${y}-01-01`, `${y}-05-01`, `${y}-05-08`, `${y}-07-14`, `${y}-08-15`, `${y}-11-01`, `${y}-11-11`, `${y}-12-25`
];
if (y === 2026) {
holidays.push('2026-04-06');
holidays.push('2026-05-14');
holidays.push('2026-05-25');
}
return holidays.includes(s);
};
const parseCirculationFlag = (val, defaultVal) => {
if (val == null) return defaultVal;
if (typeof val === 'boolean') return val;
const s = String(val).trim().toLowerCase();
if (['true', '1', 't', 'y', 'yes', 'oui'].includes(s)) return true;
if (['false', '0', 'f', 'n', 'no', 'non'].includes(s)) return false;
return defaultVal;
};
const circulatesOn = (h, d) => { const circulatesOn = (h, d) => {
if (isPublicHoliday(d)) { const pad = (n) => String(n).padStart(2, '0');
return parseCirculationFlag(h['circule_jours_feries'], false); const targetDateStr = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
const customDaysRaw = h.jours_personnalises;
if (!customDaysRaw) return false;
let customDays = [];
if (Array.isArray(customDaysRaw)) {
customDays = customDaysRaw;
} else if (typeof customDaysRaw === 'string') {
try {
customDays = JSON.parse(customDaysRaw);
} catch (e) {
return false;
}
} }
const col = weekdayNameForColumn(d);
if (!col) return true; if (Array.isArray(customDays)) {
return parseCirculationFlag(h[col], true); const match = customDays.find(item => item.date === targetDateStr);
if (match) {
return Boolean(match.circule);
}
}
return false;
}; };
const parseDateOnly = (v) => { const parseDateOnly = (v) => {
+23 -72
View File
@@ -753,80 +753,31 @@ export default function AFLDeparts(){
tomorrow.setDate(today.getDate()+1); tomorrow.setDate(today.getDate()+1);
const tomorrowStr = localDateStr(tomorrow); const tomorrowStr = localDateStr(tomorrow);
const weekdayIndex = (d) => d.getDay(); // 0 dimanche .. 6 samedi
const weekdayNameForColumn = (d) => {
const i = weekdayIndex(d);
switch(i) {
case 1: return 'circule_lundi';
case 2: return 'circule_mardi';
case 3: return 'circule_mercredi';
case 4: return 'circule_jeudi';
case 5: return 'circule_vendredi';
case 6: return 'circule_samedi';
case 0: return 'circule_dimanche';
default: return null;
}
};
const isPublicHoliday = (d) => {
const y = d.getFullYear();
const m = d.getMonth() + 1;
const day = d.getDate();
const s = `${y}-${String(m).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
const holidays = [
`${y}-01-01`, // Jour de l'an
`${y}-05-01`, // Fête du Travail
`${y}-05-08`, // Victoire 1945
`${y}-07-14`, // Fête nationale
`${y}-08-15`, // Assomption
`${y}-11-01`, // Toussaint
`${y}-11-11`, // Armistice 1918
`${y}-12-25`, // Noël
];
// Dates variables pour 2026
if (y === 2026) {
holidays.push('2026-04-06'); // Lundi de Pâques
holidays.push('2026-05-14'); // Ascension
holidays.push('2026-05-25'); // Lundi de Pentecôte
}
return holidays.includes(s);
};
const parseCirculationFlag = (value, defaultValue) => {
if (value === null || typeof value === 'undefined') {
return defaultValue;
}
if (typeof value === 'boolean') {
return value;
}
if (typeof value === 'number') {
return value === 1;
}
if (typeof value === 'string') {
const s = value.trim().toLowerCase();
if (['true', '1', 't', 'y', 'yes', 'oui'].includes(s)) {
return true;
}
if (['false', '0', 'f', 'n', 'no', 'non'].includes(s)) {
return false;
}
}
return defaultValue;
};
const circulatesOn = (h, d) => { const circulatesOn = (h, d) => {
if (isPublicHoliday(d)) { const pad = (n) => String(n).padStart(2, '0');
// On public holidays, circulation is determined SOLELY by `circule_jours_feries`. const targetDateStr = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
// If the flag is true, it runs. Otherwise, it doesn't.
return parseCirculationFlag(h['circule_jours_feries'], false); const customDaysRaw = h.jours_personnalises;
if (!customDaysRaw) return false;
let customDays = [];
if (Array.isArray(customDaysRaw)) {
customDays = customDaysRaw;
} else if (typeof customDaysRaw === 'string') {
try {
customDays = JSON.parse(customDaysRaw);
} catch (e) {
return false;
}
} }
// On a regular day, check the corresponding weekday column. if (Array.isArray(customDays)) {
const col = weekdayNameForColumn(d); const match = customDays.find(item => item.date === targetDateStr);
if (!col) return true; // Should not happen, but permissive. if (match) {
return Boolean(match.circule);
// For regular weekdays, the default is to run unless specified otherwise. }
return parseCirculationFlag(h[col], true); }
return false;
}; };
// Construire listes pour aujourd'hui et demain en tenant compte des colonnes circule_* // Construire listes pour aujourd'hui et demain en tenant compte des colonnes circule_*
+23 -45
View File
@@ -488,53 +488,31 @@ const AFLDepartsQuais = () => {
tomorrow.setDate(today.getDate() + 1); tomorrow.setDate(today.getDate() + 1);
const tomorrowStr = localDateStr(tomorrow); const tomorrowStr = localDateStr(tomorrow);
const weekdayIndex = (d) => d.getDay();
const weekdayNameForColumn = (d) => {
const i = weekdayIndex(d);
switch (i) {
case 1: return 'circule_lundi';
case 2: return 'circule_mardi';
case 3: return 'circule_mercredi';
case 4: return 'circule_jeudi';
case 5: return 'circule_vendredi';
case 6: return 'circule_samedi';
case 0: return 'circule_dimanche';
default: return null;
}
};
const isPublicHoliday = (d) => {
const y = d.getFullYear();
const m = d.getMonth() + 1;
const day = d.getDate();
const s = `${y}-${String(m).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
const holidays = [
`${y}-01-01`, `${y}-05-01`, `${y}-05-08`, `${y}-07-14`, `${y}-08-15`, `${y}-11-01`, `${y}-11-11`, `${y}-12-25`
];
if (y === 2026) {
holidays.push('2026-04-06');
holidays.push('2026-05-14');
holidays.push('2026-05-25');
}
return holidays.includes(s);
};
const parseCirculationFlag = (val, defaultVal) => {
if (val == null) return defaultVal;
if (typeof val === 'boolean') return val;
const s = String(val).trim().toLowerCase();
if (['true', '1', 't', 'y', 'yes', 'oui'].includes(s)) return true;
if (['false', '0', 'f', 'n', 'no', 'non'].includes(s)) return false;
return defaultVal;
};
const circulatesOn = (h, d) => { const circulatesOn = (h, d) => {
if (isPublicHoliday(d)) { const pad = (n) => String(n).padStart(2, '0');
return parseCirculationFlag(h['circule_jours_feries'], false); const targetDateStr = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
const customDaysRaw = h.jours_personnalises;
if (!customDaysRaw) return false;
let customDays = [];
if (Array.isArray(customDaysRaw)) {
customDays = customDaysRaw;
} else if (typeof customDaysRaw === 'string') {
try {
customDays = JSON.parse(customDaysRaw);
} catch (e) {
return false;
}
} }
const col = weekdayNameForColumn(d);
if (!col) return true; if (Array.isArray(customDays)) {
return parseCirculationFlag(h[col], true); const match = customDays.find(item => item.date === targetDateStr);
if (match) {
return Boolean(match.circule);
}
}
return false;
}; };
const parseDateOnly = (v) => { const parseDateOnly = (v) => {
@@ -793,80 +793,31 @@ export default function ClassiquesArrivees(){
tomorrow.setDate(today.getDate()+1); tomorrow.setDate(today.getDate()+1);
const tomorrowStr = localDateStr(tomorrow); const tomorrowStr = localDateStr(tomorrow);
const weekdayIndex = (d) => d.getDay(); // 0 dimanche .. 6 samedi
const weekdayNameForColumn = (d) => {
const i = weekdayIndex(d);
switch(i) {
case 1: return 'circule_lundi';
case 2: return 'circule_mardi';
case 3: return 'circule_mercredi';
case 4: return 'circule_jeudi';
case 5: return 'circule_vendredi';
case 6: return 'circule_samedi';
case 0: return 'circule_dimanche';
default: return null;
}
};
const isPublicHoliday = (d) => {
const y = d.getFullYear();
const m = d.getMonth() + 1;
const day = d.getDate();
const s = `${y}-${String(m).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
const holidays = [
`${y}-01-01`, // Jour de l'an
`${y}-05-01`, // Fête du Travail
`${y}-05-08`, // Victoire 1945
`${y}-07-14`, // Fête nationale
`${y}-08-15`, // Assomption
`${y}-11-01`, // Toussaint
`${y}-11-11`, // Armistice 1918
`${y}-12-25`, // Noël
];
// Dates variables pour 2026
if (y === 2026) {
holidays.push('2026-04-06'); // Lundi de Pâques
holidays.push('2026-05-14'); // Ascension
holidays.push('2026-05-25'); // Lundi de Pentecôte
}
return holidays.includes(s);
};
const parseCirculationFlag = (value, defaultValue) => {
if (value === null || typeof value === 'undefined') {
return defaultValue;
}
if (typeof value === 'boolean') {
return value;
}
if (typeof value === 'number') {
return value === 1;
}
if (typeof value === 'string') {
const s = value.trim().toLowerCase();
if (['true', '1', 't', 'y', 'yes', 'oui'].includes(s)) {
return true;
}
if (['false', '0', 'f', 'n', 'no', 'non'].includes(s)) {
return false;
}
}
return defaultValue;
};
const circulatesOn = (h, d) => { const circulatesOn = (h, d) => {
if (isPublicHoliday(d)) { const pad = (n) => String(n).padStart(2, '0');
// On public holidays, circulation is determined SOLELY by `circule_jours_feries`. const targetDateStr = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
// If the flag is true, it runs. Otherwise, it doesn't.
return parseCirculationFlag(h['circule_jours_feries'], false); const customDaysRaw = h.jours_personnalises;
if (!customDaysRaw) return false;
let customDays = [];
if (Array.isArray(customDaysRaw)) {
customDays = customDaysRaw;
} else if (typeof customDaysRaw === 'string') {
try {
customDays = JSON.parse(customDaysRaw);
} catch (e) {
return false;
}
} }
// On a regular day, check the corresponding weekday column. if (Array.isArray(customDays)) {
const col = weekdayNameForColumn(d); const match = customDays.find(item => item.date === targetDateStr);
if (!col) return true; // Should not happen, but permissive. if (match) {
return Boolean(match.circule);
// For regular weekdays, the default is to run unless specified otherwise. }
return parseCirculationFlag(h[col], true); }
return false;
}; };
// Construire listes pour aujourd'hui et demain en tenant compte des colonnes circule_* // Construire listes pour aujourd'hui et demain en tenant compte des colonnes circule_*
@@ -793,80 +793,31 @@ export default function ClassiquesDeparts(){
tomorrow.setDate(today.getDate()+1); tomorrow.setDate(today.getDate()+1);
const tomorrowStr = localDateStr(tomorrow); const tomorrowStr = localDateStr(tomorrow);
const weekdayIndex = (d) => d.getDay(); // 0 dimanche .. 6 samedi
const weekdayNameForColumn = (d) => {
const i = weekdayIndex(d);
switch(i) {
case 1: return 'circule_lundi';
case 2: return 'circule_mardi';
case 3: return 'circule_mercredi';
case 4: return 'circule_jeudi';
case 5: return 'circule_vendredi';
case 6: return 'circule_samedi';
case 0: return 'circule_dimanche';
default: return null;
}
};
const isPublicHoliday = (d) => {
const y = d.getFullYear();
const m = d.getMonth() + 1;
const day = d.getDate();
const s = `${y}-${String(m).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
const holidays = [
`${y}-01-01`, // Jour de l'an
`${y}-05-01`, // Fête du Travail
`${y}-05-08`, // Victoire 1945
`${y}-07-14`, // Fête nationale
`${y}-08-15`, // Assomption
`${y}-11-01`, // Toussaint
`${y}-11-11`, // Armistice 1918
`${y}-12-25`, // Noël
];
// Dates variables pour 2026
if (y === 2026) {
holidays.push('2026-04-06'); // Lundi de Pâques
holidays.push('2026-05-14'); // Ascension
holidays.push('2026-05-25'); // Lundi de Pentecôte
}
return holidays.includes(s);
};
const parseCirculationFlag = (value, defaultValue) => {
if (value === null || typeof value === 'undefined') {
return defaultValue;
}
if (typeof value === 'boolean') {
return value;
}
if (typeof value === 'number') {
return value === 1;
}
if (typeof value === 'string') {
const s = value.trim().toLowerCase();
if (['true', '1', 't', 'y', 'yes', 'oui'].includes(s)) {
return true;
}
if (['false', '0', 'f', 'n', 'no', 'non'].includes(s)) {
return false;
}
}
return defaultValue;
};
const circulatesOn = (h, d) => { const circulatesOn = (h, d) => {
if (isPublicHoliday(d)) { const pad = (n) => String(n).padStart(2, '0');
// On public holidays, circulation is determined SOLELY by `circule_jours_feries`. const targetDateStr = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
// If the flag is true, it runs. Otherwise, it doesn't.
return parseCirculationFlag(h['circule_jours_feries'], false); const customDaysRaw = h.jours_personnalises;
if (!customDaysRaw) return false;
let customDays = [];
if (Array.isArray(customDaysRaw)) {
customDays = customDaysRaw;
} else if (typeof customDaysRaw === 'string') {
try {
customDays = JSON.parse(customDaysRaw);
} catch (e) {
return false;
}
} }
// On a regular day, check the corresponding weekday column. if (Array.isArray(customDays)) {
const col = weekdayNameForColumn(d); const match = customDays.find(item => item.date === targetDateStr);
if (!col) return true; // Should not happen, but permissive. if (match) {
return Boolean(match.circule);
// For regular weekdays, the default is to run unless specified otherwise. }
return parseCirculationFlag(h[col], true); }
return false;
}; };
// Construire listes pour aujourd'hui et demain en tenant compte des colonnes circule_* // Construire listes pour aujourd'hui et demain en tenant compte des colonnes circule_*
@@ -76,65 +76,32 @@ export default function ClassiquesArriveesQuais() {
const [perturbations, setPerturbations] = useState([]); const [perturbations, setPerturbations] = useState([]);
const [, setTravaux] = useState([]); const [, setTravaux] = useState([]);
const weekdayNameForColumn = useCallback((d) => {
const i = d.getDay();
switch(i) {
case 1: return 'circule_lundi';
case 2: return 'circule_mardi';
case 3: return 'circule_mercredi';
case 4: return 'circule_jeudi';
case 5: return 'circule_vendredi';
case 6: return 'circule_samedi';
case 0: return 'circule_dimanche';
default: return null;
}
}, []);
const isPublicHoliday = useCallback((d) => {
const y = d.getFullYear();
const m = d.getMonth() + 1;
const day = d.getDate();
const s = `${y}-${String(m).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
const holidays = [
`${y}-01-01`,
`${y}-05-01`,
`${y}-05-08`,
`${y}-07-14`,
`${y}-08-15`,
`${y}-11-01`,
`${y}-11-11`,
`${y}-12-25`,
];
if (y === 2026) {
holidays.push('2026-04-06');
holidays.push('2026-05-14');
holidays.push('2026-05-25');
}
return holidays.includes(s);
}, []);
const circulatesOn = useCallback((h, d) => { const circulatesOn = useCallback((h, d) => {
if (isPublicHoliday(d)) { const pad = (n) => String(n).padStart(2, '0');
const v = h['circule_jours_feries']; const targetDateStr = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
if (typeof v === 'boolean') return v;
if (typeof v === 'number') return v === 1; const customDaysRaw = h.jours_personnalises;
if (typeof v === 'string') { if (!customDaysRaw) return false;
const s = v.trim().toLowerCase();
return s === 'true' || s === '1' || s === 't' || s === 'y' || s === 'yes'; let customDays = [];
if (Array.isArray(customDaysRaw)) {
customDays = customDaysRaw;
} else if (typeof customDaysRaw === 'string') {
try {
customDays = JSON.parse(customDaysRaw);
} catch (e) {
return false;
} }
return (typeof v === 'undefined' || v === null) ? false : Boolean(v);
} }
const col = weekdayNameForColumn(d);
if (!col) return true; if (Array.isArray(customDays)) {
const v = h[col]; const match = customDays.find(item => item.date === targetDateStr);
if (typeof v === 'boolean') return v; if (match) {
if (typeof v === 'number') return v === 1; return Boolean(match.circule);
if (typeof v === 'string') { }
const s = v.trim().toLowerCase();
return s === 'true' || s === '1' || s === 't' || s === 'y' || s === 'yes';
} }
return (typeof v === 'undefined' || v === null) ? true : Boolean(v); return false;
}, [isPublicHoliday, weekdayNameForColumn]); }, []);
const normalizeName = useCallback((s) => { const normalizeName = useCallback((s) => {
if (!s) return ''; if (!s) return '';
@@ -76,65 +76,32 @@ export default function ClassiquesDepartsQuais() {
const [perturbations, setPerturbations] = useState([]); const [perturbations, setPerturbations] = useState([]);
const [, setTravaux] = useState([]); const [, setTravaux] = useState([]);
const weekdayNameForColumn = useCallback((d) => {
const i = d.getDay();
switch(i) {
case 1: return 'circule_lundi';
case 2: return 'circule_mardi';
case 3: return 'circule_mercredi';
case 4: return 'circule_jeudi';
case 5: return 'circule_vendredi';
case 6: return 'circule_samedi';
case 0: return 'circule_dimanche';
default: return null;
}
}, []);
const isPublicHoliday = useCallback((d) => {
const y = d.getFullYear();
const m = d.getMonth() + 1;
const day = d.getDate();
const s = `${y}-${String(m).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
const holidays = [
`${y}-01-01`,
`${y}-05-01`,
`${y}-05-08`,
`${y}-07-14`,
`${y}-08-15`,
`${y}-11-01`,
`${y}-11-11`,
`${y}-12-25`,
];
if (y === 2026) {
holidays.push('2026-04-06');
holidays.push('2026-05-14');
holidays.push('2026-05-25');
}
return holidays.includes(s);
}, []);
const circulatesOn = useCallback((h, d) => { const circulatesOn = useCallback((h, d) => {
if (isPublicHoliday(d)) { const pad = (n) => String(n).padStart(2, '0');
const v = h['circule_jours_feries']; const targetDateStr = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
if (typeof v === 'boolean') return v;
if (typeof v === 'number') return v === 1; const customDaysRaw = h.jours_personnalises;
if (typeof v === 'string') { if (!customDaysRaw) return false;
const s = v.trim().toLowerCase();
return s === 'true' || s === '1' || s === 't' || s === 'y' || s === 'yes'; let customDays = [];
if (Array.isArray(customDaysRaw)) {
customDays = customDaysRaw;
} else if (typeof customDaysRaw === 'string') {
try {
customDays = JSON.parse(customDaysRaw);
} catch (e) {
return false;
} }
return (typeof v === 'undefined' || v === null) ? false : Boolean(v);
} }
const col = weekdayNameForColumn(d);
if (!col) return true; if (Array.isArray(customDays)) {
const v = h[col]; const match = customDays.find(item => item.date === targetDateStr);
if (typeof v === 'boolean') return v; if (match) {
if (typeof v === 'number') return v === 1; return Boolean(match.circule);
if (typeof v === 'string') { }
const s = v.trim().toLowerCase();
return s === 'true' || s === '1' || s === 't' || s === 'y' || s === 'yes';
} }
return (typeof v === 'undefined' || v === null) ? true : Boolean(v); return false;
}, [isPublicHoliday, weekdayNameForColumn]); }, []);
const normalizeName = useCallback((s) => { const normalizeName = useCallback((s) => {
if (!s) return ''; if (!s) return '';