typhon
Deploy HouseHub / deploy (push) Successful in 2s

This commit is contained in:
2026-06-19 11:13:37 +02:00
parent 89d8ad114b
commit aad528d15b
18 changed files with 2991 additions and 2845 deletions
+316 -190
View File
@@ -13,7 +13,7 @@ let localCareModes = [];
async function openCalendarSettings() {
try {
const res = await pachaFetch(
"/modules/family-calendar/includes/api/settings.php?action=get_all",
"/modules/family-calendar/includes/api/calendar-settings.php?action=get_all",
);
if (!res.success) throw new Error(res.error);
@@ -63,29 +63,34 @@ function closeCalendarSettings() {
document.body.classList.remove("no-scroll");
}
function switchCalendarTab(tabName) {
document
.querySelectorAll(".cal-settings-pane")
.forEach((p) => (p.style.display = "none"));
function switchCalendarTab(tabId) {
// 1. On réinitialise l'affichage
document
.querySelectorAll(".bs-tab-btn")
.forEach((b) => b.classList.remove("active"));
.forEach((btn) => btn.classList.remove("active"));
document
.querySelectorAll(".cal-settings-pane")
.forEach((pane) => (pane.style.display = "none"));
if (tabName === "foyer") {
document.getElementById("cal-pane-foyer").style.display = "block";
document.getElementById("tab-btn-foyer").classList.add("active");
} else if (tabName === "affichage") {
document.getElementById("cal-pane-affichage").style.display = "block";
document.getElementById("tab-btn-affichage").classList.add("active");
} else {
document.getElementById("cal-pane-membres").style.display = "block";
document.getElementById("tab-btn-membres").classList.add("active");
loadMemberConfigView();
// 2. On active l'onglet cliqué
document.getElementById(`tab-btn-${tabId}`).classList.add("active");
document.getElementById(`cal-pane-${tabId}`).style.display = "block";
// 3. Actions spécifiques
if (tabId === "foyer") {
loadLeaveCatalog();
} else if (tabId === "membres") {
// On charge la vue du membre sélectionné par défaut dans la liste
if (document.getElementById("selectCalMember").value) {
loadMemberConfigView();
}
}
}
function renderCareModeTags() {
const container = document.getElementById("careModesContainer");
if (!container) return; // 🛑 LA BARRIÈRE DE SÉCURITÉ EST ICI
container.innerHTML = localCareModes
.map(
(mode, i) => `
@@ -97,8 +102,11 @@ function renderCareModeTags() {
.join("");
}
// Ajoutons la même sécurité pour l'ajout au cas où :
function addCareModeTag() {
const input = document.getElementById("inputNewCareMode");
if (!input) return; // 🛑 SÉCURITÉ
const val = input.value.trim();
if (val && !localCareModes.includes(val)) {
localCareModes.push(val);
@@ -124,7 +132,7 @@ async function submitCalFoyer(e) {
formData.append("care_modes", JSON.stringify(localCareModes));
const res = await pachaFetch(
"/modules/family-calendar/includes/api/settings.php",
"/modules/family-calendar/includes/api/calendar-settings.php",
{ method: "POST", body: formData },
);
if (!res.success) throw new Error(res.error);
@@ -142,139 +150,195 @@ async function submitCalFoyer(e) {
}
}
window.addLeaveRowToMatrix = function () {
const tbody = document.getElementById("tbodyLeaveMatrix");
const empty = document.getElementById("emptyMatrixRow");
if (empty) empty.remove();
if (!tbody) return;
window.submitChildCareModes = async function () {
if (!window.currentSelectedMemberId) return;
const checkboxes = document.querySelectorAll(".js-care-mode-cb:checked");
const selectedModes = Array.from(checkboxes).map((cb) => cb.value);
try {
const formData = new FormData();
formData.append("action", "save_child_care_modes");
formData.append("person_id", window.currentSelectedMemberId);
formData.append("care_modes", JSON.stringify(selectedModes));
const rowTypeOptions = (calGlobalData.leave_types || [])
.map((lt) => `<option value="${lt.code}">${lt.code} - ${lt.label}</option>`)
.join("");
const res = await pachaFetch(
"/modules/family-calendar/includes/api/calendar-settings.php",
{ method: "POST", body: formData },
);
if (!res.success) throw new Error(res.error);
if (window.showToast)
showToast(tr("fc_child_saved") || "Sauvegardé !", "success");
else alert(tr("fc_child_saved") || "Sauvegardé !");
const trRow = document.createElement("tr");
trRow.className = "js-leave-row";
trRow.style.borderBottom = "1px solid var(--border-light)";
trRow.innerHTML = `
<td style="padding: 6px 2px;">
<select class="pf-input js-leave-type" required style="width:100%; padding:6px 2px; font-size:0.8rem; box-sizing: border-box;">
<option value="">${tr("fc_leave_code_placeholder") || "Code..."}</option>
${rowTypeOptions}
</select>
</td>
<td style="padding: 6px 2px;">
<select class="pf-input js-leave-method" style="width:100%; padding:6px 2px; font-size:0.8rem; box-sizing: border-box;">
<option value="FIXED">${tr("fc_leave_method_fixed") || "Fixe"}</option>
<option value="ACCUMULATED">${tr("fc_leave_method_accumulated") || "Cumul"}</option>
</select>
</td>
<td style="padding: 6px 2px;"><input type="number" step="0.5" class="pf-input js-leave-allowance" placeholder="0" required style="width:100%; padding:6px 4px; font-size:0.85rem; text-align:center; box-sizing: border-box;"></td>
<td style="padding: 6px 2px;"><input type="text" class="pf-input js-leave-date" placeholder="${tr("fc_date_format_placeholder") || "JJ/MM"}" pattern="(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[012])" maxlength="5" required style="width:100%; padding:6px 4px; font-size:0.85rem; text-align:center; box-sizing: border-box;" title="${tr("fc_date_format_title") || "Format JJ/MM"}"></td>
<td style="padding: 6px 2px; text-align:right;"><button type="button" class="btn-icon-action delete" style="padding:4px; margin:0;" onclick="this.closest('tr').remove()" title="${tr("btn_delete") || "Supprimer"}">🗑️</button></td>
`;
tbody.appendChild(trRow);
const currentPerson = calGlobalData.people.find(
(k) => parseInt(k.id) === window.currentSelectedMemberId,
);
if (currentPerson) {
currentPerson.care_modes = JSON.stringify(selectedModes);
}
} catch (err) {
alert("Erreur: " + err.message);
}
};
window.populateCalendarSettings = function (settings) {
if (!settings) return;
const viewSelect = document.getElementById("setCalView");
if (viewSelect && settings.calendar_default_view)
viewSelect.value = settings.calendar_default_view;
const firstDaySelect = document.getElementById("setCalFirstDay");
if (firstDaySelect && settings.calendar_first_day !== undefined)
firstDaySelect.value = settings.calendar_first_day;
const hoursInput = document.getElementById("setCalHours");
if (hoursInput && settings.calendar_working_hours)
hoursInput.value = settings.calendar_working_hours;
};
// ==========================================
// NOUVEAU CATALOGUE GLOBAL (FOYER)
// ==========================================
let globalLeaveCatalog = [];
window.loadAdultConfigView = function (memberId, zoneElement) {
let userLeaves = calGlobalData.leaves[memberId] || [];
async function loadLeaveCatalog() {
try {
const res = await pachaFetch(
"/modules/family-calendar/includes/api/calendar-settings.php?action=get_leave_types",
);
globalLeaveCatalog = res.data || [];
renderLeaveCatalog(globalLeaveCatalog);
} catch (err) {
console.error("Erreur chargement catalogue", err);
}
}
const tableRows = userLeaves
.map((l) => {
let dateDisp = "";
if (l.date) {
const parts = l.date.split("-");
if (parts.length >= 3) dateDisp = `${parts[2]}/${parts[1]}`;
}
function renderLeaveCatalog(types) {
const container = document.getElementById("leaveTypesContainer");
if (!container) return;
container.innerHTML = "";
let rowTypeOptions = (calGlobalData.leave_types || [])
.map(
(lt) =>
`<option value="${lt.code}" ${lt.code === l.type ? "selected" : ""}>${lt.code} - ${lt.label}</option>`,
)
.join("");
if (types.length === 0) {
container.innerHTML = `<p class="pf-muted-note">Aucun congé configuré.</p>`;
return;
}
if (!calGlobalData.leave_types.some((lt) => lt.code === l.type)) {
rowTypeOptions += `<option value="${l.type}" selected>${l.type} (${tr("fc_leave_obsolete") || "Obsolète"})</option>`;
}
return `
<tr class="js-leave-row" style="border-bottom: 1px solid var(--border-light);">
<td style="padding: 6px 2px;">
<select class="pf-input js-leave-type" required style="width:100%; padding:6px 2px; font-size:0.8rem; box-sizing: border-box;">
<option value="">${tr("fc_leave_code_placeholder") || "Code..."}</option>
${rowTypeOptions}
</select>
</td>
<td style="padding: 6px 2px;">
<select class="pf-input js-leave-method" style="width:100%; padding:6px 2px; font-size:0.8rem; box-sizing: border-box;">
<option value="FIXED" ${l.method === "FIXED" ? "selected" : ""}>${tr("fc_leave_method_fixed") || "Fixe"}</option>
<option value="ACCUMULATED" ${l.method === "ACCUMULATED" ? "selected" : ""}>${tr("fc_leave_method_accumulated") || "Cumul"}</option>
</select>
</td>
<td style="padding: 6px 2px;"><input type="number" step="0.5" class="pf-input js-leave-allowance" value="${l.allowance || 0}" placeholder="0" required style="width:100%; padding:6px 4px; font-size:0.85rem; text-align:center; box-sizing: border-box;"></td>
<td style="padding: 6px 2px;"><input type="text" class="pf-input js-leave-date" value="${dateDisp}" placeholder="${tr("fc_date_format_placeholder") || "JJ/MM"}" pattern="(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[012])" maxlength="5" required style="width:100%; padding:6px 4px; font-size:0.85rem; text-align:center; box-sizing: border-box;" title="${tr("fc_date_format_title") || "Format JJ/MM"}"></td>
<td style="padding: 6px 2px; text-align:right;"><button type="button" class="btn-icon-action delete" style="padding:4px; margin:0;" onclick="this.closest('tr').remove()" title="${tr("btn_delete") || "Supprimer"}">🗑️</button></td>
</tr>
`;
})
.join("");
zoneElement.innerHTML = `
<div style="display:flex; justify-content:space-between; align-items:flex-start; margin-bottom:12px;">
<div>
<h5 style="margin:0 0 4px 0; color:var(--text-main); font-size:1rem;">🗓️ ${tr("fc_matrix_title") || "Matrice des Congés"}</h5>
<p style="font-size:0.8rem; color:var(--text-muted); margin:0; line-height:1.3;">${tr("fc_matrix_desc") || "Définissez les quotas et la date de renouvellement."}</p>
types.forEach((lt) => {
container.innerHTML += `
<div style="display: flex; justify-content: space-between; align-items: center; background: var(--bg-page); padding: 8px 12px; border-radius: 6px; border: 1px solid var(--border-light); margin-bottom: 6px;">
<div>
<span style="font-weight: bold; color: var(--text-main);">${lt.label}</span>
<span style="background: var(--bg-subtle); border: 1px solid var(--border-main); border-radius: 4px; padding: 2px 6px; font-size: 0.8rem; margin-left: 8px; color: var(--text-muted);">${lt.code}</span>
</div>
<div style="display: flex; gap: 4px;">
<button type="button" class="pf-btn btn-secondary" style="padding: 4px 8px;" onclick="editLeaveType('${lt.code}', '${lt.label.replace(/'/g, "\\'")}')">✏️</button>
<button type="button" class="pf-btn btn-secondary" style="padding: 4px 8px; color: var(--danger);" onclick="deleteLeaveType('${lt.code}', '${lt.label.replace(/'/g, "\\'")}')">🗑️</button>
</div>
</div>
<button type="button" class="pf-btn pf-btn-secondary" style="padding:6px 10px; font-size:0.8rem; white-space:nowrap;" onclick="addLeaveRowToMatrix()"> ${tr("btn_add") || "Ajouter"}</button>
</div>
<div style="overflow-x:hidden; margin: 0 -5px;">
<table style="width:100%; border-collapse:collapse; margin-bottom:15px; table-layout: fixed;">
<thead>
<tr style="font-size:0.75rem; text-align:left; color:var(--text-muted); text-transform:uppercase;">
<th style="padding:0 2px 8px; width: 33%;">${tr("fc_table_code") || "Code"}</th>
<th style="padding:0 2px 8px; width: 22%;">${tr("fc_table_method") || "Méthode"}</th>
<th style="padding:0 2px 8px; width: 15%;">${tr("fc_table_quota") || "Quota"}</th>
<th style="padding:0 2px 8px; width: 20%;">${tr("fc_table_renewal") || "Renouv."}</th>
<th style="padding:0 2px 8px; width: 10%;"></th>
</tr>
</thead>
<tbody id="tbodyLeaveMatrix">
${tableRows || `<tr><td colspan="5" id="emptyMatrixRow" style="text-align:center; padding:20px; color:var(--text-muted); font-style:italic; font-size:0.9rem;">${tr("fc_matrix_empty") || "Aucun compteur défini."}</td></tr>`}
</tbody>
</table>
</div>
<div style="display:flex; justify-content:flex-end; border-top:1px solid var(--border-light); padding-top:12px;">
<button type="button" class="pf-btn pf-btn-primary" style="padding:8px 16px;" onclick="submitMemberLeaves()">${tr("btn_save") || "Enregistrer"}</button>
</div>
`;
};
`;
});
}
window.loadMemberConfigView = function () {
function editLeaveType(code, label) {
document.getElementById("leaveTypeFormTitle").innerText =
"✏️ Modifier : " + label;
document.getElementById("lt-mode").value = "edit";
const codeInput = document.getElementById("lt-code");
codeInput.value = code;
codeInput.readOnly = true;
codeInput.style.backgroundColor = "var(--bg-subtle)";
codeInput.style.opacity = "0.6";
codeInput.style.cursor = "not-allowed";
document.getElementById("lt-code-note").innerText = "Non modifiable";
document.getElementById("lt-label").value = label;
}
function resetLeaveTypeForm() {
document.getElementById("leaveTypeFormTitle").innerText = "+ Ajouter";
document.getElementById("lt-mode").value = "add";
const codeInput = document.getElementById("lt-code");
codeInput.value = "";
codeInput.readOnly = false;
codeInput.style.backgroundColor = "";
codeInput.style.opacity = "1";
codeInput.style.cursor = "text";
document.getElementById("lt-code-note").innerText = "Irréversible";
document.getElementById("lt-label").value = "";
}
// NOUVELLE FONCTION DE SUPPRESSION
async function deleteLeaveType(code, label) {
if (
!confirm(
`Supprimer définitivement le congé "${label}" du catalogue ? Cela le retirera également des membres qui l'utilisent.`,
)
)
return;
const fd = new FormData();
fd.append("action", "delete_leave_type");
fd.append("code", code);
try {
const res = await pachaFetch(
"/modules/family-calendar/includes/api/calendar-settings.php",
{ method: "POST", body: fd },
);
if (!res.success) throw new Error(res.error);
if (window.showToast) showToast("Congé supprimé avec succès !", "success");
resetLeaveTypeForm();
loadLeaveCatalog();
// On recharge l'onglet membre au cas où le membre affiché utilisait ce congé
if (document.getElementById("selectCalMember").value) {
loadMemberConfigView();
}
} catch (err) {
alert("Erreur de suppression : " + err.message);
}
}
async function saveLeaveType() {
const code = document.getElementById("lt-code").value.toUpperCase();
const label = document.getElementById("lt-label").value;
if (!code || !label)
return window.showToast
? showToast("Code et Label requis", "error")
: alert("Code et Label requis");
const fd = new FormData();
fd.append("action", "save_leave_type");
fd.append("mode", document.getElementById("lt-mode").value);
fd.append("code", code);
fd.append("label", label);
try {
const res = await pachaFetch(
"/modules/family-calendar/includes/api/calendar-settings.php",
{ method: "POST", body: fd },
);
if (!res.success) throw new Error(res.error);
if (window.showToast) showToast("Catalogue mis à jour !", "success");
resetLeaveTypeForm();
loadLeaveCatalog();
} catch (err) {
alert("Erreur d'enregistrement : " + err.message);
}
}
// ==========================================
// MODALE SETTINGS : AFFECTATION (MEMBRES)
// ==========================================
// Quand on choisit un membre dans la liste déroulante
async function loadMemberConfigView() {
const select = document.getElementById("selectCalMember");
const zone = document.getElementById("memberConfigZone");
if (!select || !select.value || !zone) return;
window.currentSelectedMemberId = parseInt(select.value);
const personId = parseInt(select.value);
window.currentSelectedMemberId = personId;
const role = (
select.options[select.selectedIndex].dataset.role || ""
).toLowerCase();
zone.innerHTML = '<p class="pf-muted-note">Chargement...</p>';
// === CAS 1 : C'EST UN ENFANT (On gère les modes de garde) ===
if (role === "child" || role === "enfant") {
const currentPerson = calGlobalData.people.find(
(k) => parseInt(k.id) === window.currentSelectedMemberId,
(k) => parseInt(k.id) === personId,
);
let savedModes = [];
try {
@@ -296,83 +360,145 @@ window.loadMemberConfigView = function () {
.join("");
zone.innerHTML = `
<h5 style="margin:0 0 8px 0; color:var(--text-main);">${tr("fc_care_modes_title") || "Modes de Garde"}</h5>
<p style="font-size:0.85rem; color:var(--text-muted); margin-bottom:12px;">Quels modes de garde s'appliquent à cet enfant ?</p>
<div style="display:flex; flex-direction:column; gap:6px;">
${modesHtml || `<em>Aucun mode de garde configuré dans le foyer.</em>`}
</div>
<button class="pf-btn pf-btn-primary" style="margin-top:15px; width:100%;" onclick="submitChildCareModes()">${tr("btn_save_rights") || "Enregistrer"}</button>
`;
} else {
window.loadAdultConfigView(window.currentSelectedMemberId, zone);
<h5 style="margin:0 0 8px 0; color:var(--text-main);">${tr("fc_care_modes_title") || "Modes de Garde"}</h5>
<p style="font-size:0.85rem; color:var(--text-muted); margin-bottom:12px;">Quels modes de garde s'appliquent à cet enfant ?</p>
<div style="display:flex; flex-direction:column; gap:6px;">
${modesHtml || `<em>Aucun mode de garde configuré dans le foyer.</em>`}
</div>
<button class="pf-btn pf-btn-primary" style="margin-top:15px; width:100%;" onclick="submitChildCareModes()">${tr("btn_save_rights") || "Enregistrer"}</button>
`;
}
};
// === CAS 2 : C'EST UN ADULTE (On gère les congés) ===
else {
try {
const res = await pachaFetch(
`/modules/family-calendar/includes/api/calendar-settings.php?action=get_person_leaves&person_id=${personId}`,
);
const memberLeaves = res.data || [];
renderMemberLeavesView(personId, memberLeaves);
} catch (err) {
zone.innerHTML =
'<p class="pf-muted-note" style="color:var(--danger);">Erreur de chargement.</p>';
}
}
}
// Affichage des congés du membre et du formulaire d'ajout
function renderMemberLeavesView(personId, memberLeaves) {
const zone = document.getElementById("memberConfigZone");
let html = `<h5 style="margin: 0 0 10px 0; color: var(--text-main);">Congés attribués</h5>`;
if (memberLeaves.length === 0) {
html += `<p class="pf-muted-note" style="font-size: 0.85rem;">Aucun congé attribué à ce membre.</p>`;
} else {
html += `<div style="display: flex; flex-direction: column; gap: 8px; margin-bottom: 15px;">`;
memberLeaves.forEach((ml) => {
// On extrait le mois de la date anniversaire (ex: "2000-06-01" -> 6)
const moisRenouv = ml.anniversary_date
? parseInt(ml.anniversary_date.split("-")[1])
: 1;
html += `
<div style="display: flex; justify-content: space-between; align-items: center; background: var(--bg-subtle); padding: 8px; border-radius: 6px; border: 1px solid var(--border-light);">
<div>
<strong style="color: var(--text-main);">${ml.leave_type}</strong>
<span style="font-size: 0.8rem; color: var(--text-muted); margin-left: 5px;">(Quota: <b>${ml.allowance}j</b> - Renouv: <b>Mois ${moisRenouv}</b>)</span>
</div>
<button class="pf-btn btn-secondary" style="padding: 2px 6px; color: var(--danger);" onclick="deleteMemberLeave(${ml.id}, ${personId})">🗑️</button>
</div>
`;
});
html += `</div>`;
}
// Ajout du selecteur de mois dans l'attribution
html += `
<hr style="border: 0; border-top: 1px solid var(--border-light); margin: 15px 0;">
<h5 style="margin: 0 0 10px 0;">+ Attribuer un congé</h5>
<div style="display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap;">
<div style="flex: 1; min-width: 120px;">
<label class="pf-label" style="font-size: 0.8rem;">Type de congé</label>
<select id="new-member-leave-type" class="pf-input" style="width: 100%;">
<option value="" disabled selected>-- Sélectionner --</option>
${globalLeaveCatalog.map((lt) => `<option value="${lt.code}">${lt.label}</option>`).join("")}
</select>
</div>
<div style="flex: 0 0 70px;">
<label class="pf-label" style="font-size: 0.8rem;">Quota</label>
<input type="number" step="0.5" id="new-member-leave-allowance" class="pf-input" value="0" style="width: 100%;">
</div>
<div style="flex: 0 0 100px;">
<label class="pf-label" style="font-size: 0.8rem;">Renouvellement</label>
<select id="new-member-leave-reset" class="pf-input" style="width: 100%;">
<option value="1">${tr("month_jan") || "Janvier"}</option>
<option value="2">${tr("month_feb") || "Février"}</option>
<option value="3">${tr("month_mar") || "Mars"}</option>
<option value="4">${tr("month_apr") || "Avril"}</option>
<option value="5">${tr("month_may") || "Mai"}</option>
<option value="6">${tr("month_jun") || "Juin"}</option>
<option value="7">${tr("month_jul") || "Juillet"}</option>
<option value="8">${tr("month_aug") || "Août"}</option>
<option value="9">${tr("month_sep") || "Septembre"}</option>
<option value="10">${tr("month_oct") || "Octobre"}</option>
<option value="11">${tr("month_nov") || "Novembre"}</option>
<option value="12">${tr("month_dec") || "Décembre"}</option>
</select>
</div>
<button class="pf-btn pf-btn-primary" onclick="addMemberLeave(${personId})">Ajouter</button>
</div>
`;
zone.innerHTML = html;
}
async function addMemberLeave(personId) {
const leaveCode = document.getElementById("new-member-leave-type").value;
const allowance = document.getElementById("new-member-leave-allowance").value;
const resetMonth = document.getElementById("new-member-leave-reset").value;
if (!leaveCode) return showToast("Veuillez sélectionner un type", "error");
const fd = new FormData();
fd.append("action", "add_person_leave");
fd.append("person_id", personId);
fd.append("leave_type", leaveCode);
fd.append("allowance", allowance);
fd.append("reset_month", resetMonth);
window.submitChildCareModes = async function () {
if (!window.currentSelectedMemberId) return;
const checkboxes = document.querySelectorAll(".js-care-mode-cb:checked");
const selectedModes = Array.from(checkboxes).map((cb) => cb.value);
try {
const formData = new FormData();
formData.append("action", "save_child_care_modes");
formData.append("person_id", window.currentSelectedMemberId);
formData.append("care_modes", JSON.stringify(selectedModes));
const res = await pachaFetch(
"/modules/family-calendar/includes/api/settings.php",
{ method: "POST", body: formData },
"/modules/family-calendar/includes/api/calendar-settings.php",
{ method: "POST", body: fd },
);
if (!res.success) throw new Error(res.error);
if (window.showToast)
showToast(tr("fc_child_saved") || "Sauvegardé !", "success");
else alert(tr("fc_child_saved") || "Sauvegardé !");
const currentPerson = calGlobalData.people.find(
(k) => parseInt(k.id) === window.currentSelectedMemberId,
);
if (currentPerson) {
currentPerson.care_modes = JSON.stringify(selectedModes);
}
if (window.showToast) showToast("Congé attribué avec succès !", "success");
loadMemberConfigView();
} catch (err) {
alert("Erreur: " + err.message);
}
};
}
async function submitMemberLeaves() {
if (!currentSelectedMemberId || isNaN(currentSelectedMemberId)) {
alert("Erreur technique : Aucun membre valide sélectionné.");
// Supprimer un congé d'un membre
async function deleteMemberLeave(leaveId, personId) {
if (!confirm(tr("confirm_delete") || "Retirer ce congé pour ce membre ?"))
return;
}
const fd = new FormData();
fd.append("action", "delete_person_leave");
fd.append("id", leaveId);
try {
const rows = document.querySelectorAll(".js-leave-row");
const leavesPayload = Array.from(rows)
.map((row) => ({
type: row.querySelector(".js-leave-type").value.trim().toUpperCase(),
method: row.querySelector(".js-leave-method").value,
allowance: row.querySelector(".js-leave-allowance").value,
date: row.querySelector(".js-leave-date").value,
}))
.filter((l) => l.type && l.date);
const formData = new FormData();
formData.append("action", "save_member_leaves");
formData.append("person_id", currentSelectedMemberId);
formData.append("leaves", JSON.stringify(leavesPayload));
const res = await pachaFetch(
"/modules/family-calendar/includes/api/settings.php",
{ method: "POST", body: formData },
"/modules/family-calendar/includes/api/calendar-settings.php",
{ method: "POST", body: fd },
);
if (!res.success) throw new Error(res.error);
if (window.showToast)
showToast(tr("fc_matrix_saved") || "Matrice enregistrée", "success");
else alert(tr("fc_matrix_saved") || "Matrice enregistrée");
closeCalendarSettings();
setTimeout(openCalendarSettings, 300);
if (window.showToast) showToast("Congé retiré !", "success");
loadMemberConfigView();
} catch (err) {
alert("Erreur : " + err.message);
alert("Erreur: " + err.message);
}
}
@@ -450,7 +576,7 @@ document.addEventListener("DOMContentLoaded", () => {
try {
const res = await pachaFetch(
"/modules/family-calendar/includes/api/settings.php?action=get_all",
"/modules/family-calendar/includes/api/calendar-settings.php?action=get_all",
);
if (res && res.success && res.data) {
window.calGlobalData = res.data;