Centralisation des constantes
This commit is contained in:
@@ -109,10 +109,14 @@ function getTranslatedMonthName($dateString) {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach (['Alex', 'Laia'] as $p):
|
||||
<?php
|
||||
// Définition des noms à partir de la config
|
||||
$family_members = [ID_ALEX => 'Alex', ID_LAIA => 'Laia'];
|
||||
|
||||
foreach ($family_members as $id => $p):
|
||||
$d = $salaryConfig[$p];
|
||||
$restant = $d['salary'] - ($d['mensualite'] + $d['frais_func'] + $d['eco_perso'] + $d['eco_family']);
|
||||
$borderColor = ($p === 'Alex') ? '#0891b2' : '#f59e0b';
|
||||
$borderColor = ($id === ID_ALEX) ? '#0891b2' : '#f59e0b';
|
||||
?>
|
||||
<tr data-person="<?= $p ?>">
|
||||
<td style="text-align:left; font-weight:bold; color:var(--text-main); border-left:4px solid <?= $borderColor ?>;">
|
||||
@@ -399,7 +403,7 @@ function getTranslatedMonthName($dateString) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="addCatModal" class="pf-modal" style="display:none; position:fixed; inset:0; z-index:9999; background:rgba(15, 23, 42, 0.6); backdrop-filter:blur(4px); align-items:center; justify-content:center;">
|
||||
<div id="addCatModal" class="pf-modal">
|
||||
<div class="pf-modal-content" style="background:white; width:95%; max-width:400px; border-radius:20px; box-shadow:0 20px 25px -5px rgba(0,0,0,0.1); padding:30px;">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
|
||||
<h3 class="pf-modal-title" style="margin:0; border:none; font-size:1.2rem;"><?= tr('bud_prev_new_line_title') ?></h3>
|
||||
@@ -789,33 +793,23 @@ function updateSumResult() {
|
||||
}
|
||||
|
||||
async function deleteCategory(id) {
|
||||
if (!confirm(window.I18N['bud_prev_confirm_del_line'] || "Confirmer la suppression ?")) return;
|
||||
if (!confirm(tr('bud_prev_confirm_del_line'))) return;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('action', 'delete_category');
|
||||
formData.append('id', id);
|
||||
formData.append('ajax', '1');
|
||||
formData.append('ajax', '1'); //
|
||||
|
||||
try {
|
||||
// 💡 CORRECTION : Chemin relatif (sans le "/" au début) pour s'adapter à ton localhost
|
||||
const response = await fetch('modules/budget/includes/api/save-budget.php', {
|
||||
const result = await pachaFetch('modules/budget/includes/api/save-budget.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error(`Erreur réseau HTTP ${response.status}`);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert((window.I18N['bud_err_tech'] || 'Erreur technique') + " : Suppression impossible.");
|
||||
}
|
||||
} catch(e) {
|
||||
console.error("Erreur Fetch Suppression:", e);
|
||||
alert("Erreur de communication avec le serveur. Regarde la console (F12) pour plus de détails.");
|
||||
}
|
||||
} catch(e) { console.error(e); }
|
||||
}
|
||||
|
||||
document.addEventListener('click', function(e) {
|
||||
@@ -843,7 +837,7 @@ document.addEventListener('DOMContentLoaded', recalcAllAllocations);
|
||||
// --- INTERCEPTION ASYNCHRONE DES FORMULAIRES DE MODALES ---
|
||||
document.querySelectorAll('#addCatModal form, #editCatModal form').forEach(form => {
|
||||
form.addEventListener('submit', async (e) => {
|
||||
e.preventDefault(); // 🛑 Bloque le rechargement HTML par défaut
|
||||
e.preventDefault();
|
||||
|
||||
const submitBtn = form.querySelector('button[type="submit"]');
|
||||
const originalText = submitBtn.innerText;
|
||||
@@ -855,31 +849,24 @@ document.querySelectorAll('#addCatModal form, #editCatModal form').forEach(form
|
||||
const formData = new FormData(form);
|
||||
formData.append('ajax', '1');
|
||||
|
||||
// 💡 CORRECTION DU PIÈGE : On utilise getAttribute() pour forcer la lecture de l'URL !
|
||||
const actionUrl = form.getAttribute('action');
|
||||
|
||||
// On s'assure du bon chemin (chemin relatif depuis budget.php)
|
||||
const finalUrl = actionUrl.startsWith('/') ? actionUrl.substring(1) : actionUrl;
|
||||
|
||||
const response = await fetch(finalUrl, {
|
||||
// On utilise pachaFetch au lieu de fetch
|
||||
const result = await pachaFetch(actionUrl, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error(`Erreur HTTP: ${response.status}`);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
form.closest('.pf-modal').style.display = 'none';
|
||||
document.body.classList.remove('no-scroll');
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert((window.I18N['bud_err_tech'] || 'Erreur technique') + " : " + (result.error || "Inconnue"));
|
||||
alert((window.I18N['bud_err_tech'] || 'Erreur') + " : " + (result.error || "Inconnue"));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Erreur Fetch Modale:", error);
|
||||
alert("Une erreur technique est survenue lors de l'enregistrement.");
|
||||
alert("Une erreur technique est survenue.");
|
||||
} finally {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerText = originalText;
|
||||
|
||||
@@ -859,31 +859,22 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
submitBtn.innerText = '⏳ ...';
|
||||
|
||||
const formData = new FormData(formExpense);
|
||||
// Force l'action ici pour être sûr
|
||||
formData.set('action', 'save_expense_manual');
|
||||
|
||||
// 💡 Chemin relatif propre !
|
||||
const response = await fetch('modules/budget/includes/api/manage-item.php', {
|
||||
const actionUrl = formExpense.getAttribute('action') || 'modules/budget/includes/api/manage-item.php';
|
||||
|
||||
const result = await pachaFetch(actionUrl, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error(`Erreur HTTP: ${response.status}`);
|
||||
|
||||
// 🛡️ Double vérification JSON contre les warnings PHP
|
||||
const textResult = await response.text();
|
||||
try {
|
||||
const result = JSON.parse(textResult);
|
||||
if (result.success) {
|
||||
closeSuiviModal('manualExpenseModal');
|
||||
formExpense.reset();
|
||||
window.location.reload();
|
||||
} else {
|
||||
const errorMsg = result.error || 'Erreur inconnue';
|
||||
alert((window.I18N['error_occured'] || 'Erreur') + ' : ' + errorMsg);
|
||||
}
|
||||
} catch (jsonErr) {
|
||||
console.error("Réponse non-JSON :", textResult);
|
||||
alert("Erreur PHP. Vérifie la console (F12).");
|
||||
if (result.success) {
|
||||
closeSuiviModal('manualExpenseModal');
|
||||
formExpense.reset();
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert((window.I18N['error_occured'] || 'Erreur') + ' : ' + (result.error || 'Inconnue'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Erreur réseau :", error);
|
||||
|
||||
@@ -899,38 +899,111 @@ td.col-day {
|
||||
========================================================= */
|
||||
|
||||
@media (max-width: 768px) {
|
||||
/* Plein écran (Full Bleed) pour le tableau sur mobile */
|
||||
/* 1. PLEIN ÉCRAN POUR GAGNER DE L'ESPACE */
|
||||
.pf-family-calendar .pf-container {
|
||||
padding: 0 !important; /* Retire le padding de la page */
|
||||
padding: 0 !important;
|
||||
}
|
||||
.pf-family-calendar .pf-main {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
/* 2. EN-TÊTES ET BOUTONS GLOBAUX (Empilement propre) */
|
||||
.fc-header-row {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 15px;
|
||||
padding: 0 10px; /* On remet un peu de padding juste pour les textes */
|
||||
}
|
||||
.fc-header-row > div {
|
||||
display: flex;
|
||||
flex-direction: column; /* Les boutons s'empilent */
|
||||
width: 100%;
|
||||
gap: 10px;
|
||||
}
|
||||
.pf-btn-icon-text {
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 3. CONTRÔLES DU CALENDRIER MENSUEL */
|
||||
.fc-month-header {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
align-items: stretch;
|
||||
}
|
||||
.fc-view-controls {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
order: 1;
|
||||
}
|
||||
.fc-view-button {
|
||||
flex: 1; /* Les onglets 1 Mois / 2 Mois se partagent l'espace */
|
||||
text-align: center;
|
||||
padding: 8px 4px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
#fc-current-month-year {
|
||||
order: 2;
|
||||
}
|
||||
.fc-nav-controls {
|
||||
width: 100%;
|
||||
justify-content: space-between; /* Les flèches de chaque côté */
|
||||
order: 3;
|
||||
}
|
||||
|
||||
/* 4. CONTRÔLES DU PLANNING HEBDO */
|
||||
.fc-week-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.fc-week-nav-controls {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* 5. LE GRAND TABLEAU (Suppression des bordures extérieures) */
|
||||
#planningTable-wrapper {
|
||||
border-radius: 0;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
margin-bottom: 0;
|
||||
max-height: 75vh; /* Ajusté pour laisser voir un peu le bas */
|
||||
}
|
||||
|
||||
/* Ajustements des panneaux du bas */
|
||||
/* Ajustements pour que les colonnes figées (Mois + Semaine) ne prennent pas tout l'écran */
|
||||
.col-month {
|
||||
width: 40px !important;
|
||||
min-width: 40px !important;
|
||||
max-width: 40px !important;
|
||||
font-size: 0.75rem; /* On réduit le texte "Avril" etc. */
|
||||
}
|
||||
#planningTable tbody td.col-sticky-sem {
|
||||
left: 40px !important; /* Adapté à la nouvelle largeur de la 1ère colonne */
|
||||
}
|
||||
#planningTable thead tr th.col-sticky-sem {
|
||||
left: 40px !important;
|
||||
}
|
||||
|
||||
/* 6. PANNEAUX DU BAS (Légendes et Résumés) */
|
||||
.fc-bottom-grid {
|
||||
grid-template-columns: 1fr;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.fc-two-months-container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.fc-two-months-container,
|
||||
.fc-year-container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.fc-summary-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
gap: 12px;
|
||||
}
|
||||
.fc-summary-controls {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* Modale Bottom Sheet */
|
||||
/* 7. MODALE BOTTOM SHEET (SÉCURITÉ Z-INDEX) */
|
||||
.fc-selection-menu {
|
||||
position: fixed !important;
|
||||
top: auto !important;
|
||||
@@ -938,13 +1011,14 @@ td.col-day {
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
border-radius: 24px 24px 0 0;
|
||||
box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.3);
|
||||
padding: 24px 20px;
|
||||
padding-bottom: env(safe-area-inset-bottom, 24px);
|
||||
transform: translateY(100%);
|
||||
animation: slideUpSheet 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
max-height: 85vh;
|
||||
overflow-y: auto;
|
||||
z-index: 999999 !important; /* Doit passer AU-DESSUS des colonnes figées du tableau ! */
|
||||
}
|
||||
|
||||
@keyframes slideUpSheet {
|
||||
@@ -960,16 +1034,15 @@ td.col-day {
|
||||
height: 5px;
|
||||
background: #cbd5e1;
|
||||
border-radius: 10px;
|
||||
margin: 0 auto 10px auto;
|
||||
margin: 0 auto 15px auto;
|
||||
}
|
||||
|
||||
.fc-menu-btn {
|
||||
padding: 10px;
|
||||
padding: 12px 10px;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Réduction des icônes pour gagner de la place */
|
||||
/* Réduction des icônes pour le tableau hyper dense */
|
||||
.fc-day--centre::after {
|
||||
font-size: 9px !important;
|
||||
top: 1px !important;
|
||||
|
||||
@@ -633,9 +633,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
const dayLeaves = this.leaves.filter((l) => l.leave_date === iso);
|
||||
if (dayLeaves.length) {
|
||||
let html = `<div style="position:absolute; bottom:0; left:0; width:100%; font-size:9px; line-height:1; display:flex; justify-content:center; gap:2px; pointer-events:none;">`;
|
||||
if (dayLeaves.some((l) => l.person_id === 2))
|
||||
if (dayLeaves.some((l) => l.person_id === window.CONFIG.ID_ALEX))
|
||||
html += `<span style="color:#0f766e; font-weight:800;">A</span>`;
|
||||
if (dayLeaves.some((l) => l.person_id === 3))
|
||||
if (dayLeaves.some((l) => l.person_id === window.CONFIG.ID_LAIA))
|
||||
html += `<span style="color:#b45309; font-weight:800;">L</span>`;
|
||||
html += `</div>`;
|
||||
td.innerHTML += html;
|
||||
@@ -995,17 +995,17 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
}
|
||||
|
||||
async fetchApi(url) {
|
||||
return fetch(url).then((r) => r.json());
|
||||
return pachaFetch(url);
|
||||
}
|
||||
|
||||
async postApi(url, data) {
|
||||
const res = await fetch(url, {
|
||||
return pachaFetch(url, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async changeSchoolYear(delta) {
|
||||
@@ -1422,7 +1422,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
payload,
|
||||
);
|
||||
} else if (action === "clear-leaves-person") {
|
||||
// --- NOUVELLE ACTION : Suppression ciblée (Alex OU Laia selon le pid) ---
|
||||
await this.postApi(
|
||||
"/modules/family-calendar/includes/api/manage-leaf.php",
|
||||
{
|
||||
@@ -1432,14 +1431,21 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
},
|
||||
);
|
||||
} else if (action === "clear-leaves") {
|
||||
// --- ACTION GLOBALE : Gardée au cas où vous en auriez besoin ailleurs ---
|
||||
await this.postApi(
|
||||
"/modules/family-calendar/includes/api/manage-leaf.php",
|
||||
{ action: "bulk_delete_day_person", dates, person_id: 2 },
|
||||
{
|
||||
action: "bulk_delete_day_person",
|
||||
dates,
|
||||
person_id: window.CONFIG.ID_ALEX,
|
||||
},
|
||||
);
|
||||
await this.postApi(
|
||||
"/modules/family-calendar/includes/api/manage-leaf.php",
|
||||
{ action: "bulk_delete_day_person", dates, person_id: 3 },
|
||||
{
|
||||
action: "bulk_delete_day_person",
|
||||
dates,
|
||||
person_id: window.CONFIG.ID_LAIA,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user