Planificateur « Suggérer » : 4 stratégies (smart / meilleurs d'abord / équilibré / juste ce qu'il faut), compétences+niveaux par tech (édition inline), niveau requis par compétence + par job, carte des tournées (1 couleur/tech, domicile→arrêts, sélecteur de jour), fenêtre de dispatch auj.+demain (dates sélectionnées), règle week-end + placeholder « en attente du quart », clustering + lasso + filtre-date sur la carte, accès rapide « À assigner » (badge). Boîte : liste /conversations allégée (45 Mo → ~1 Mo, 17×) + messages chargés à l'ouverture. Rapports : cache SWR sur revenue-explorer (22×). Session : keep-alive + timeout fetch global + authFetch durci → fin des rechargements manuels. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
59 lines
3.5 KiB
JavaScript
59 lines
3.5 KiB
JavaScript
// ── Jours fériés statutaires du QUÉBEC — DÉTERMINISTE (pas d'IA : ce sont des règles fixes) ──
|
|
// Base = Loi sur les normes du travail (CNESST). 8 fériés. L'employeur peut AJOUTER des observances
|
|
// (lendemain, congé mobile…) via `extra`, et choisir Vendredi saint OU Lundi de Pâques.
|
|
// ⚠️ Tout en UTC pour éviter toute dérive de fuseau (les dates de fériés sont des jours civils).
|
|
|
|
// Dimanche de Pâques (algorithme de Meeus/Jones/Butcher).
|
|
function easterSunday (year) {
|
|
const a = year % 19, b = Math.floor(year / 100), c = year % 100
|
|
const d = Math.floor(b / 4), e = b % 4, f = Math.floor((b + 8) / 25)
|
|
const g = Math.floor((b - f + 1) / 3), h = (19 * a + b - d - g + 15) % 30
|
|
const i = Math.floor(c / 4), k = c % 4, l = (32 + 2 * e + 2 * i - h - k) % 7
|
|
const m = Math.floor((a + 11 * h + 22 * l) / 451)
|
|
const month = Math.floor((h + l - 7 * m + 114) / 31) // 3 = mars, 4 = avril
|
|
const day = ((h + l - 7 * m + 114) % 31) + 1
|
|
return new Date(Date.UTC(year, month - 1, day))
|
|
}
|
|
// n-ième `weekday` (0=dim … 1=lun) du mois (month0 0-based).
|
|
function nthWeekday (year, month0, weekday, n) {
|
|
const first = new Date(Date.UTC(year, month0, 1))
|
|
const day = 1 + ((7 + weekday - first.getUTCDay()) % 7) + (n - 1) * 7
|
|
return new Date(Date.UTC(year, month0, day))
|
|
}
|
|
const iso = (d) => d.toISOString().slice(0, 10)
|
|
|
|
// Liste des fériés QC pour une année. easterChoice: 'good_friday' (défaut) | 'easter_monday'.
|
|
function holidaysForYear (year, { easterChoice = 'good_friday', extra = [] } = {}) {
|
|
const easter = easterSunday(year)
|
|
const goodFriday = new Date(easter); goodFriday.setUTCDate(easter.getUTCDate() - 2)
|
|
const easterMonday = new Date(easter); easterMonday.setUTCDate(easter.getUTCDate() + 1)
|
|
// Journée nationale des patriotes = le LUNDI qui précède le 25 mai (donc lundi ≤ 24 mai).
|
|
const patriotes = new Date(Date.UTC(year, 4, 24)); while (patriotes.getUTCDay() !== 1) patriotes.setUTCDate(patriotes.getUTCDate() - 1)
|
|
const list = [
|
|
{ date: iso(new Date(Date.UTC(year, 0, 1))), name: "Jour de l'An" },
|
|
{ date: iso(easterChoice === 'easter_monday' ? easterMonday : goodFriday), name: easterChoice === 'easter_monday' ? 'Lundi de Pâques' : 'Vendredi saint' },
|
|
{ date: iso(patriotes), name: 'Journée nationale des patriotes' },
|
|
{ date: iso(new Date(Date.UTC(year, 5, 24))), name: 'Saint-Jean-Baptiste' },
|
|
{ date: iso(new Date(Date.UTC(year, 6, 1))), name: 'Fête du Canada' },
|
|
{ date: iso(nthWeekday(year, 8, 1, 1)), name: 'Fête du travail' }, // 1er lundi de septembre
|
|
{ date: iso(nthWeekday(year, 9, 1, 2)), name: 'Action de grâce' }, // 2e lundi d'octobre
|
|
{ date: iso(new Date(Date.UTC(year, 11, 25))), name: 'Noël' },
|
|
]
|
|
for (const x of (extra || [])) if (x && x.date) list.push({ date: x.date, name: x.name || 'Congé', custom: true })
|
|
return list.sort((p, q) => p.date.localeCompare(q.date))
|
|
}
|
|
|
|
// Fériés entre deux dates ISO (incluses), traversant les années.
|
|
function holidaysBetween (fromIso, toIso, opts = {}) {
|
|
const y0 = parseInt(String(fromIso).slice(0, 4), 10); const y1 = parseInt(String(toIso).slice(0, 4), 10)
|
|
const out = []
|
|
for (let y = y0; y <= y1; y++) for (const h of holidaysForYear(y, opts)) if (h.date >= fromIso && h.date <= toIso) out.push(h)
|
|
return out
|
|
}
|
|
function isHoliday (dateIso, opts = {}) {
|
|
const y = parseInt(String(dateIso).slice(0, 4), 10)
|
|
return holidaysForYear(y, opts).find(h => h.date === dateIso) || null
|
|
}
|
|
|
|
module.exports = { holidaysForYear, holidaysBetween, isHoliday, easterSunday }
|