1 parent
a69a8950b3
commit
693b277002
8 files changed
+190
-21
No files matched your search
+10
-2
@@ -586,7 +586,6 @@ return [
|
||||
'bs_currency_label' => 'Divisa de la llar',
|
||||
'bs_currency_desc' => 'Divisa principal utilitzada per mostrar els vostres comptes i pressupostos.',
|
||||
'bs_currency_updated' => 'Divisa actualitzada correctament!',
|
||||
'bud_settings_dyn_title' => 'Estimacions Dinàmiques (Nens)',
|
||||
'bud_dyn_nanny_fixed' => 'Sou fix Cangur (€)',
|
||||
'bud_dyn_nanny_daily' => 'Despeses diàries de manteniment (€)',
|
||||
'bud_dyn_nanny_aid' => 'Ajuda del govern (€)',
|
||||
@@ -600,7 +599,16 @@ return [
|
||||
'bud_audit_child' => '👦 %s :',
|
||||
'bud_audit_school_fee' => 'Menjador (%dd) + Acollida (%dd) + Casal (%dd) = %.2f€',
|
||||
'bud_audit_sick' => 'Dies de malaltia deduïts : %dd',
|
||||
'bud_audit_nanny_full' => '💼 %s : Fix (%.2f€) + Despeses (%dd x %.2f€ = %.2f€) - Ajut (%.2f€) - CESU (%.2f€) = %.2f€',
|
||||
'bud_audit_nanny_full' => '💼 %s : Fix (%.2f€) + Despeses (%dd x %.2f€ = %.2f€) - CMG (%.2f€) - %s (%.2f€) = %.2f€',
|
||||
'bud_audit_cesu_avg' => 'CESU (Mitj.)',
|
||||
'bud_audit_cesu_adj' => 'CESU (Ajustat)',
|
||||
'bud_dyn_cesu_overrides' => 'Ajustaments puntuals del CESU',
|
||||
'bud_dyn_cesu_add' => 'Afegir un mes',
|
||||
'bud_dyn_cesu_empty' => 'Cap ajustament registrat.',
|
||||
'bud_settings_dyn_title' => 'Cura i Escola',
|
||||
'bud_settings_dyn_desc' => 'Tarifes per al càlcul automatitzat de les despeses.',
|
||||
'bud_dyn_sub_nanny' => 'Cangur',
|
||||
'bud_dyn_sub_school' => 'Extraescolars i Casal',
|
||||
|
||||
// CSV
|
||||
'bs_csv_title' => 'Format de la importació CSV',
|
||||
|
||||
+10
-2
@@ -588,7 +588,6 @@ return [
|
||||
'bs_currency_label' => 'Household Currency',
|
||||
'bs_currency_desc' => 'Main currency used for displaying your accounts and budgets.',
|
||||
'bs_currency_updated' => 'Currency updated successfully!',
|
||||
'bud_settings_dyn_title' => 'Dynamic Estimates (Children)',
|
||||
'bud_dyn_nanny_fixed' => 'Nanny fixed salary (€)',
|
||||
'bud_dyn_nanny_daily' => 'Daily maintenance fee (€)',
|
||||
'bud_dyn_nanny_aid' => 'Government aid (€)',
|
||||
@@ -602,7 +601,16 @@ return [
|
||||
'bud_audit_child' => '👦 %s :',
|
||||
'bud_audit_school_fee' => 'Canteen (%dd) + Aftercare (%dd) + Center (%dd) = %.2f€',
|
||||
'bud_audit_sick' => 'Sick days deducted : %dd',
|
||||
'bud_audit_nanny_full' => '💼 %s : Fixed (%.2f€) + Fees (%dd x %.2f€ = %.2f€) - Aid (%.2f€) - CESU (%.2f€) = %.2f€',
|
||||
'bud_audit_nanny_full' => '💼 %s : Fixed (%.2f€) + Fees (%dd x %.2f€ = %.2f€) - Aid (%.2f€) - %s (%.2f€) = %.2f€',
|
||||
'bud_audit_cesu_avg' => 'CESU (Avg.)',
|
||||
'bud_audit_cesu_adj' => 'CESU (Adjusted)',
|
||||
'bud_dyn_cesu_overrides' => 'Specific CESU adjustments',
|
||||
'bud_dyn_cesu_add' => 'Add a month',
|
||||
'bud_dyn_cesu_empty' => 'No adjustments recorded.',
|
||||
'bud_settings_dyn_title' => 'Care & School',
|
||||
'bud_settings_dyn_desc' => 'Rates for automated childcare fee calculations.',
|
||||
'bud_dyn_sub_nanny' => 'Nanny',
|
||||
'bud_dyn_sub_school' => 'Afterschool & Center',
|
||||
|
||||
// CSV Mapping (Budget)
|
||||
'bs_csv_title' => 'CSV Import Format',
|
||||
|
||||
+10
-2
@@ -588,7 +588,6 @@ return [
|
||||
'bs_currency_label' => 'Devise du Foyer',
|
||||
'bs_currency_desc' => 'Devise principale utilisée pour l\'affichage de vos comptes et budgets.',
|
||||
'bs_currency_updated' => 'Devise mise à jour avec succès !',
|
||||
'bud_settings_dyn_title' => 'Estimations Dynamiques (Enfants)',
|
||||
'bud_dyn_nanny_fixed' => 'Salaire fixe Assistante Maternelle (€)',
|
||||
'bud_dyn_nanny_daily' => 'Frais d\'entretien journaliers (€)',
|
||||
'bud_dyn_nanny_aid' => 'Aide de l\'État (CMG) (€)',
|
||||
@@ -602,7 +601,16 @@ return [
|
||||
'bud_audit_child' => '👦 %s :',
|
||||
'bud_audit_school_fee' => 'Cantine (%dj) + Garderie (%dj) + Centre (%dj) = %.2f€',
|
||||
'bud_audit_sick' => 'Jours maladie déduits : %dj',
|
||||
'bud_audit_nanny_full' => '💼 %s : Fixe (%.2f€) + Frais (%dj x %.2f€ = %.2f€) - Aide (%.2f€) - CESU (%.2f€) = %.2f€',
|
||||
'bud_audit_nanny_full' => '💼 %s : Fixe (%.2f€) + Frais (%dj x %.2f€ = %.2f€) - CMG (%.2f€) - %s (%.2f€) = %.2f€',
|
||||
'bud_audit_cesu_avg' => 'CESU (Moy.)',
|
||||
'bud_audit_cesu_adj' => 'CESU (Ajusté)',
|
||||
'bud_dyn_cesu_overrides' => 'Ajustements ponctuels du CESU',
|
||||
'bud_dyn_cesu_add' => 'Ajouter un mois',
|
||||
'bud_dyn_cesu_empty' => 'Aucun ajustement enregistré.',
|
||||
'bud_settings_dyn_title' => 'Garde & École',
|
||||
'bud_settings_dyn_desc' => 'Tarifs pour le calcul automatisé des frais de garde.',
|
||||
'bud_dyn_sub_nanny' => 'Nounou',
|
||||
'bud_dyn_sub_school' => 'Périscolaire',
|
||||
|
||||
// CSV Mapping (Budget)
|
||||
'bs_csv_title' => 'Format de l\'import CSV',
|
||||
|
||||
@@ -2701,3 +2701,36 @@ body.sum-mode-active .sum-target {
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* --- 16. AJUSTEMENTS CESU (JSON OVERRIDES) --- */
|
||||
.bs-cesu-wrapper {
|
||||
margin-top: 20px;
|
||||
padding: 16px;
|
||||
background: var(--bg-page);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border-light);
|
||||
}
|
||||
.bs-cesu-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.bs-cesu-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.bs-cesu-row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
.bs-cesu-row input[type="month"] {
|
||||
width: 140px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.bs-cesu-row input[type="number"] {
|
||||
width: 100px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -32,6 +32,10 @@ class DynamicBudgetCalculator {
|
||||
return isset($this->settings[$key]) ? (float)$this->settings[$key] : (float)$default;
|
||||
}
|
||||
|
||||
private function getSettingString(string $key, string $default = ''): string {
|
||||
return isset($this->settings[$key]) ? (string)$this->settings[$key] : $default;
|
||||
}
|
||||
|
||||
private function loadPeople(): void {
|
||||
// 1. Chargement des enfants sans doublons
|
||||
$stmt = $this->pdo->query("SELECT id, name, care_modes FROM pf_people WHERE role IN ('enfant', 'child') AND is_active = 1");
|
||||
@@ -85,10 +89,11 @@ class DynamicBudgetCalculator {
|
||||
|
||||
$zone = $this->foyerSettings['zone_scolaire'] ?? 'C';
|
||||
try {
|
||||
// 🔥 FIX : On récupère les DEUX années scolaires qui chevauchent l'année calendaire (ex: 2025-2026 ET 2026-2027)
|
||||
// 🔥 FIX STATE OF THE ART : On cible uniquement la population "Élèves"
|
||||
$prevYear = $this->year - 1;
|
||||
$nextYear = $this->year + 1;
|
||||
$where = "(annee_scolaire='{$prevYear}-{$this->year}' OR annee_scolaire='{$this->year}-{$nextYear}') AND zones LIKE '%Zone {$zone}%'";
|
||||
$where = "(annee_scolaire='{$prevYear}-{$this->year}' OR annee_scolaire='{$this->year}-{$nextYear}') AND zones LIKE '%Zone {$zone}%' AND population = 'Élèves'";
|
||||
|
||||
$url = "https://data.education.gouv.fr/api/explore/v2.1/catalog/datasets/fr-en-calendrier-scolaire/records?where=" . urlencode($where) . "&limit=100";
|
||||
|
||||
$jsonVacances = @file_get_contents($url);
|
||||
@@ -127,7 +132,16 @@ class DynamicBudgetCalculator {
|
||||
$nannyFixed = $this->getSetting('budget_nanny_fixed', 700);
|
||||
$nannyDaily = $this->getSetting('budget_nanny_daily', 4);
|
||||
$nannyAid = $this->getSetting('budget_nanny_aid', 120);
|
||||
$cesuAvg = $this->getSetting('budget_nanny_cesu_avg', 170);
|
||||
|
||||
$cesuAvg = $this->getSetting('budget_nanny_cesu_avg', 170);
|
||||
$overridesJson = $this->getSettingString('budget_cesu_overrides', '{}');
|
||||
$cesuOverrides = json_decode($overridesJson, true) ?: [];
|
||||
|
||||
$monthKey = sprintf("%04d-%02d", $this->year, $this->month);
|
||||
$isCesuOverride = isset($cesuOverrides[$monthKey]);
|
||||
$cesuApplied = $isCesuOverride ? (float)$cesuOverrides[$monthKey] : $cesuAvg;
|
||||
$cesuLabelText = $isCesuOverride ? tr('bud_audit_cesu_adj') : tr('bud_audit_cesu_avg');
|
||||
// ------------------------------------
|
||||
|
||||
$schoolMeal = $this->getSetting('budget_school_meal', 5.62);
|
||||
$schoolAftercare = $this->getSetting('budget_school_aftercare', 1.97);
|
||||
@@ -168,6 +182,8 @@ class DynamicBudgetCalculator {
|
||||
if (in_array($dateStr, $offDays['feries'])) continue;
|
||||
|
||||
$isVacances = in_array($dateStr, $offDays['vacances']);
|
||||
|
||||
|
||||
$events = $this->calendarEvents[$dateStr] ?? [];
|
||||
|
||||
$nannyOffToday = false;
|
||||
@@ -216,13 +232,15 @@ class DynamicBudgetCalculator {
|
||||
}
|
||||
|
||||
$totalNannyFees = $totalNannyDays * $nannyDaily;
|
||||
$nannyGlobalCost = $nannyFixed + $totalNannyFees - $nannyAid - $cesuAvg;
|
||||
// On soustrait $cesuApplied au lieu de $cesuAvg
|
||||
$nannyGlobalCost = $nannyFixed + $totalNannyFees - $nannyAid - $cesuApplied;
|
||||
|
||||
// 3. Rédaction du Bilan
|
||||
if ($hasNanny) {
|
||||
$totalCost += max(0, $nannyGlobalCost);
|
||||
// On passe $cesuLabelText au sprintf
|
||||
$details .= sprintf(tr('bud_audit_nanny_full') . "\n",
|
||||
$this->helperName, $nannyFixed, $totalNannyDays, $nannyDaily, $totalNannyFees, $nannyAid, $cesuAvg, max(0, $nannyGlobalCost));
|
||||
$this->helperName, $nannyFixed, $totalNannyDays, $nannyDaily, $totalNannyFees, $nannyAid, $cesuLabelText, $cesuApplied, max(0, $nannyGlobalCost));
|
||||
|
||||
if ($nannyOffDays > 0) {
|
||||
$details .= sprintf(tr('bud_audit_nanny_abs') . "\n", $this->helperName, $nannyOffDays);
|
||||
|
||||
@@ -205,7 +205,8 @@ try {
|
||||
if ($action === 'save_dynamic_estimates') {
|
||||
$keys = [
|
||||
'budget_nanny_fixed', 'budget_nanny_daily', 'budget_nanny_aid', 'budget_nanny_cesu_avg',
|
||||
'budget_school_meal', 'budget_school_aftercare', 'budget_school_fullday'
|
||||
'budget_school_meal', 'budget_school_aftercare', 'budget_school_fullday',
|
||||
'budget_cesu_overrides'
|
||||
];
|
||||
|
||||
$pdo->beginTransaction();
|
||||
|
||||
@@ -224,10 +224,11 @@ if (!defined('CURRENCY')) {
|
||||
<!-- NOUVEAU PANE ENFANTS -->
|
||||
<div id="pane-children" class="bs-pane">
|
||||
<h4 class="bs-section-title">👶 <?= tr('bud_settings_dyn_title') ?></h4>
|
||||
<p class="pf-muted-tiny bs-desc">Configurez ici les tarifs pour le calcul dynamique des frais de garde et de cantine.</p>
|
||||
<p class="pf-muted-tiny bs-desc"><?= tr('bud_settings_dyn_desc') ?></p>
|
||||
|
||||
<form id="form-dynamic-estimates" onsubmit="saveDynamicEstimates(event)">
|
||||
<h5 class="bs-section-subtitle bordered">Assistante Maternelle</h5>
|
||||
|
||||
<h5 class="bs-section-subtitle bordered"><?= tr('bud_dyn_sub_nanny') ?></h5>
|
||||
<div class="bs-grid-2">
|
||||
<div>
|
||||
<label class="pf-label"><?= tr('bud_dyn_nanny_fixed') ?></label>
|
||||
@@ -247,7 +248,17 @@ if (!defined('CURRENCY')) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h5 class="bs-section-subtitle bordered" style="margin-top: 20px;">École & Centre de loisirs</h5>
|
||||
<!-- Ajustements ponctuels du CESU -->
|
||||
<div style="margin-top: 20px;">
|
||||
<div class="bs-cesu-header" style="display:flex; justify-content:space-between; margin-bottom:12px;">
|
||||
<label class="pf-label pf-m-0"><?= tr('bud_dyn_cesu_overrides') ?></label>
|
||||
<button type="button" class="pf-btn btn-secondary pf-btn-sm" onclick="addCesuOverride()">+ <?= tr('bud_dyn_cesu_add') ?></button>
|
||||
</div>
|
||||
<div id="cesu-overrides-list" class="bs-cesu-list"></div>
|
||||
<input type="hidden" name="budget_cesu_overrides" id="budget_cesu_overrides_input" value="{}">
|
||||
</div>
|
||||
|
||||
<h5 class="bs-section-subtitle bordered pf-mt-md"><?= tr('bud_dyn_sub_school') ?></h5>
|
||||
<div class="bs-grid-2">
|
||||
<div>
|
||||
<label class="pf-label"><?= tr('bud_dyn_school_meal') ?></label>
|
||||
@@ -263,8 +274,8 @@ if (!defined('CURRENCY')) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bs-text-right" style="margin-top: 20px;">
|
||||
<button type="submit" class="btn btn-secondary">💾 <?= tr('btn_save') ?></button>
|
||||
<div class="bs-text-right pf-mt-md">
|
||||
<button type="submit" class="pf-btn btn-secondary">💾 <?= tr('btn_save') ?></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -341,6 +352,10 @@ async function loadBudgetSettingsData() {
|
||||
if (document.getElementById('budget_school_aftercare')) document.getElementById('budget_school_aftercare').value = bs.budget_school_aftercare || 1.97;
|
||||
if (document.getElementById('budget_school_fullday')) document.getElementById('budget_school_fullday').value = bs.budget_school_fullday || 21.89;
|
||||
|
||||
try { cesuOverrides = JSON.parse(bs.budget_cesu_overrides || '{}'); }
|
||||
catch (e) { cesuOverrides = {}; }
|
||||
renderCesuOverrides();
|
||||
|
||||
} catch (err) {
|
||||
console.error("Erreur chargement paramètres :", err);
|
||||
const errorMsg = `<div class="pf-alert pf-alert--error bs-input-mb">❌ ${tr('error_loading_settings')} : ${err.message}</div>`;
|
||||
@@ -657,4 +672,79 @@ async function saveDynamicEstimates(e) {
|
||||
btn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// --- LOGIQUE DES AJUSTEMENTS CESU ---
|
||||
let cesuOverrides = {};
|
||||
|
||||
function renderCesuOverrides() {
|
||||
const list = document.getElementById('cesu-overrides-list');
|
||||
const input = document.getElementById('budget_cesu_overrides_input');
|
||||
if (!list || !input) return;
|
||||
|
||||
list.innerHTML = '';
|
||||
// Tri décroissant pour afficher le mois le plus lointain en haut
|
||||
const keys = Object.keys(cesuOverrides).sort().reverse();
|
||||
|
||||
if (keys.length === 0) {
|
||||
list.innerHTML = `<div class="pf-empty-dashed" style="margin:0; padding:12px;"><em class="pf-muted-tiny">${window.I18N['bud_dyn_cesu_empty'] || 'Aucun ajustement enregistré.'}</em></div>`;
|
||||
} else {
|
||||
keys.forEach(k => {
|
||||
const btnDelText = window.I18N['btn_delete'] || 'Supprimer';
|
||||
// UI : Design aéré type "Carte HouseHub" avec des inputs sans bordures agressives
|
||||
list.innerHTML += `
|
||||
<div style="display: flex; gap: 10px; align-items: center; background: var(--bg-panel); padding: 8px 12px; border-radius: 8px; border: 1px solid var(--border-light); box-shadow: var(--shadow-sm);">
|
||||
<input type="month" class="pf-input" style="width: 150px; margin: 0; padding: 6px 10px;" value="${k}" onchange="updateCesuOverrideKey('${k}', this.value)">
|
||||
<input type="number" step="0.01" class="pf-input" style="flex: 1; margin: 0; padding: 6px 10px; text-align: right;" value="${cesuOverrides[k]}" onchange="updateCesuOverrideValue('${k}', this.value)">
|
||||
<span style="color: var(--text-muted); font-weight: 600; margin-right: 5px;">€</span>
|
||||
<button type="button" class="btn-icon-action delete" style="margin: 0; background: var(--bg-danger-soft); width: 32px; height: 32px;" onclick="removeCesuOverride('${k}')" title="${btnDelText}">🗑️</button>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
}
|
||||
input.value = JSON.stringify(cesuOverrides);
|
||||
}
|
||||
|
||||
function addCesuOverride() {
|
||||
const keys = Object.keys(cesuOverrides).sort();
|
||||
let nextDate = new Date();
|
||||
nextDate.setMonth(nextDate.getMonth() + 1); // Base : le mois prochain
|
||||
|
||||
// S'il y a déjà des mois dans la liste, on prend le plus lointain et on ajoute 1 mois
|
||||
if (keys.length > 0) {
|
||||
const lastKey = keys[keys.length - 1]; // ex: "2026-10"
|
||||
const parts = lastKey.split('-');
|
||||
// L'astuce : Le mois en JS commence à 0. Donc en lui passant "10", on pointe déjà sur le mois d'après (Novembre) !
|
||||
nextDate = new Date(parts[0], parseInt(parts[1], 10), 1);
|
||||
}
|
||||
|
||||
// Sécurité anti-boucle infinie
|
||||
let m = nextDate.getFullYear() + '-' + String(nextDate.getMonth() + 1).padStart(2, '0');
|
||||
let safety = 0;
|
||||
while (cesuOverrides[m] !== undefined && safety < 24) {
|
||||
nextDate.setMonth(nextDate.getMonth() + 1);
|
||||
m = nextDate.getFullYear() + '-' + String(nextDate.getMonth() + 1).padStart(2, '0');
|
||||
safety++;
|
||||
}
|
||||
|
||||
cesuOverrides[m] = 0;
|
||||
renderCesuOverrides();
|
||||
}
|
||||
|
||||
function updateCesuOverrideKey(oldK, newK) {
|
||||
if (oldK !== newK && newK) {
|
||||
cesuOverrides[newK] = cesuOverrides[oldK];
|
||||
delete cesuOverrides[oldK];
|
||||
renderCesuOverrides();
|
||||
}
|
||||
}
|
||||
|
||||
function updateCesuOverrideValue(k, val) {
|
||||
cesuOverrides[k] = parseFloat(val) || 0;
|
||||
renderCesuOverrides();
|
||||
}
|
||||
|
||||
function removeCesuOverride(k) {
|
||||
delete cesuOverrides[k];
|
||||
renderCesuOverrides();
|
||||
}
|
||||
</script>
|
||||
@@ -1078,7 +1078,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
),
|
||||
this.fetchApi("/modules/family-calendar/includes/api/get-events.php"),
|
||||
this.fetchPublicHolidays(),
|
||||
this.fetchSchoolHolidays(zone), // 🏖️ NOUVEAU : On appelle l'API
|
||||
this.fetchSchoolHolidays(zone),
|
||||
this.fetchApi("/modules/family-calendar/includes/api/get-leaves.php"),
|
||||
this.fetchApi(
|
||||
"/modules/family-calendar/includes/api/get-leave-snapshots.php",
|
||||
@@ -1132,8 +1132,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
if (!zone || zone === "Autre") return [];
|
||||
try {
|
||||
const yearStr = `${this.currentSchoolYearStart}-${this.currentSchoolYearStart + 1}`;
|
||||
// Ton URL qui utilise le LIKE, beaucoup plus robuste !
|
||||
const url = `https://data.education.gouv.fr/api/explore/v2.1/catalog/datasets/fr-en-calendrier-scolaire/records?where=annee_scolaire='${yearStr}' AND zones LIKE '%Zone ${zone}%'&limit=100`;
|
||||
|
||||
const whereClause = `annee_scolaire='${yearStr}' AND zones LIKE '%Zone ${zone}%' AND population='Élèves'`;
|
||||
const url = `https://data.education.gouv.fr/api/explore/v2.1/catalog/datasets/fr-en-calendrier-scolaire/records?where=${encodeURIComponent(whereClause)}&limit=100`;
|
||||
|
||||
const res = await fetch(url);
|
||||
const data = await res.json();
|
||||
@@ -1196,7 +1197,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
try {
|
||||
const year = this.modalSelectedYear || this.currentSchoolYearStart;
|
||||
const yearStr = `${year}-${year + 1}`;
|
||||
const url = `https://data.education.gouv.fr/api/explore/v2.1/catalog/datasets/fr-en-calendrier-scolaire/records?where=annee_scolaire='${yearStr}' AND zones LIKE '%Zone ${zone}%'&limit=100&order_by=start_date`;
|
||||
|
||||
const whereClause = `annee_scolaire='${yearStr}' AND zones LIKE '%Zone ${zone}%' AND population='Élèves'`;
|
||||
const url = `https://data.education.gouv.fr/api/explore/v2.1/catalog/datasets/fr-en-calendrier-scolaire/records?where=${encodeURIComponent(whereClause)}&limit=100&order_by=start_date`;
|
||||
|
||||
const res = await fetch(url);
|
||||
const data = await res.json();
|
||||
|
||||
Reference in new issue
Block a user