trad et modal

This commit is contained in:
2026-05-05 14:56:42 +02:00
parent 7b5c0be72b
commit 4763e6a947
7 changed files with 1049 additions and 676 deletions
+11 -8
View File
@@ -60,9 +60,9 @@ require __DIR__ . '/header.php';
<div class="fc-modal-header"> <div class="fc-modal-header">
<h2><?= tr('fc_modal_snap_title') ?></h2> <h2><?= tr('fc_modal_snap_title') ?></h2>
<button id="btnCloseSnapshot" class="fc-modal-close">×</button> <button id="btnCloseSnapshot" class="fc-modal-close">×</button>
</div> </div>
<div class="fc-modal-body" style="padding: 24px;"> <div class="fc-modal-body" style="padding: 24px;">
<form id="formSnapshot"> <form id="formSnapshot">
<div style="margin-bottom: 16px;"> <div style="margin-bottom: 16px;">
<label class="pf-label"><?= tr('fc_label_person') ?></label> <label class="pf-label"><?= tr('fc_label_person') ?></label>
<select id="snapPerson" class="pf-input" required> <select id="snapPerson" class="pf-input" required>
@@ -79,18 +79,18 @@ require __DIR__ . '/header.php';
</select> </select>
</div> </div>
<div style="margin-bottom: 16px;"> <div style="margin-bottom: 16px;">
<label class="pf-label"><?= tr('fc_label_apply_date') ?></label> <label class="pf-label"><?= tr('fc_label_apply_date') ?></label>
<input type="date" id="snapDate" class="pf-input" required title="<?= tr('fc_snap_date_hint') ?>"> <input type="date" id="snapDate" class="pf-input" required title="<?= tr('fc_snap_date_hint') ?>">
</div> </div>
<div style="margin-bottom: 24px;"> <div style="margin-bottom: 24px;">
<label class="pf-label"><?= tr('fc_label_remaining_balance') ?></label> <label class="pf-label"><?= tr('fc_label_remaining_balance') ?></label>
<input type="number" step="0.5" id="snapBalance" class="pf-input" placeholder="<?= tr('fc_placeholder_balance') ?>" required> <input type="number" step="0.5" id="snapBalance" class="pf-input" placeholder="<?= tr('fc_placeholder_balance') ?>" required>
</div> </div>
<button type="submit" class="pf-btn" style="width: 100%;"><?= tr('fc_btn_save_snap') ?></button> <button type="submit" class="pf-btn" style="width: 100%;"><?= tr('fc_btn_save_snap') ?></button>
</form> </form>
</div>
</div> </div>
</div> </div>
</div>
<section class="pf-section"> <section class="pf-section">
<div class="fc-month-calendar-wrapper"> <div class="fc-month-calendar-wrapper">
@@ -230,7 +230,10 @@ window.I18N = {
'vac_ete': "<?= tr('vac_ete') ?>", 'vac_ete': "<?= tr('vac_ete') ?>",
'leg_school_holidays': "<?= tr('leg_school_holidays') ?>", 'leg_school_holidays': "<?= tr('leg_school_holidays') ?>",
'fc_school_year': "<?= tr('fc_school_year') ?>", 'fc_school_year': "<?= tr('fc_school_year') ?>",
'fc_modal_holidays_title': "<?= tr('fc_modal_holidays_title') ?>" 'fc_modal_holidays_title': "<?= tr('fc_modal_holidays_title') ?>",
'fc_alert_burn_days': "<?= tr('fc_alert_burn_days') ?>",
'fc_alert_burn_jra': "<?= tr('fc_alert_burn_jra') ?>",
'ANNIV': "<?= tr('ANNIV') ?>"
}; };
</script> </script>
<script src="/modules/family-calendar/family-calendar.js"></script> <script src="/modules/family-calendar/family-calendar.js"></script>
+59
View File
@@ -0,0 +1,59 @@
<?php
/**
* PachaFamily - CSS Auditor 🦙
* Génère un rapport complet de tous les styles (fichiers, balises, inline)
*/
header('Content-Type: text/plain; charset=utf-8');
$root = __DIR__;
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($root));
$report = "=== 🦙 RAPPORT CSS COMPLET PACHAFAMILY ===\n";
$report .= "Généré le : " . date('Y-m-d H:i:s') . "\n\n";
foreach ($iterator as $file) {
if ($file->isDir()) continue;
$filePath = $file->getPathname();
$relativePath = str_replace($root, '', $filePath);
$ext = pathinfo($filePath, PATHINFO_EXTENSION);
// 1. EXTRACTION DES FICHIERS .css
if ($ext === 'css') {
$report .= "/* 📄 FICHIER SOURCE : $relativePath */\n";
$report .= file_get_contents($filePath) . "\n";
$report .= str_repeat("-", 40) . "\n\n";
}
// 2. EXTRACTION DES BALISES <style> ET ATTRIBUTS style="" DANS LES .php
if ($ext === 'php') {
$content = file_get_contents($filePath);
$foundInFile = false;
// Extraction des blocs <style>
if (preg_match_all('/<style[^>]*>(.*?)<\/style>/is', $content, $matches)) {
if (!$foundInFile) { $report .= "/* 📄 FICHIER SOURCE : $relativePath */\n"; $foundInFile = true; }
foreach ($matches[1] as $idx => $styleBlock) {
$report .= "/* Bloc <style> #$idx */\n";
$report .= trim($styleBlock) . "\n";
}
}
// Extraction des styles inline style="..."
// On capture le tag pour donner du contexte au style inline
if (preg_match_all('/<([a-z0-9]+)[^>]*?\sstyle=["\']([^"]*?)["\'][^>]*>/i', $content, $matches)) {
if (!$foundInFile) { $report .= "/* 📄 FICHIER SOURCE : $relativePath */\n"; $foundInFile = true; }
foreach ($matches[0] as $idx => $fullTag) {
// On nettoie un peu pour ne garder que l'essentiel du tag et son style
$report .= "/* Style Inline #$idx */\n";
$report .= trim($fullTag) . "\n";
}
}
if ($foundInFile) {
$report .= str_repeat("-", 40) . "\n\n";
}
}
}
echo $report;
+3
View File
@@ -148,6 +148,9 @@ return [
'btn_off' => 'Off', 'btn_off' => 'Off',
'btn_extra' => 'Extra', 'btn_extra' => 'Extra',
'fc_alert_burn_days' => '⚠️ Risc de pèrdua : %s d a gastar abans del %s',
'fc_alert_burn_jra' => '⚠️ JRA: Només 2d es poden reportar després de febrer!',
// ========================================== // ==========================================
// HOLIDAYS (VOYAGES) // HOLIDAYS (VOYAGES)
// ========================================== // ==========================================
+3
View File
@@ -149,6 +149,9 @@ return [
'vac_ete' => "Vacances d'Été", 'vac_ete' => "Vacances d'Été",
'btn_off' => 'Off', 'btn_off' => 'Off',
'btn_extra' => 'Extra', 'btn_extra' => 'Extra',
'fc_alert_burn_days' => '⚠️ Risque de perte : %s j à poser avant le %s',
'fc_alert_burn_jra' => '⚠️ JRA : Seuls 2j sont reportables après février !',
// ========================================== // ==========================================
// HOLIDAYS (VOYAGES) // HOLIDAYS (VOYAGES)
@@ -395,6 +395,37 @@
box-shadow: inset 0 -8px 0 0 var(--c-school-holiday) !important; box-shadow: inset 0 -8px 0 0 var(--c-school-holiday) !important;
} }
.fc-burn-alert {
margin-left: 5px;
cursor: help;
animation: pulseBurn 2s infinite;
font-size: 0.9rem;
display: flex;
align-items: center;
color: #ef4444; /* Rouge danger */
font-weight: bold;
}
@keyframes pulseBurn {
0% {
transform: scale(1);
opacity: 0.8;
}
50% {
transform: scale(1.2);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 0.8;
}
}
/* Tooltip sur le chip pour l'alerte */
.fc-min-chip[title] {
position: relative;
}
/* ========================================================= /* =========================================================
6. PLANNING HEBDOMADAIRE (Desktop Uniquement) 6. PLANNING HEBDOMADAIRE (Desktop Uniquement)
========================================================= */ ========================================================= */
+44 -11
View File
@@ -503,6 +503,7 @@ document.addEventListener("DOMContentLoaded", () => {
LEAVES_CONFIG.JRA.yearlyTotals[currYear] || LEAVES_CONFIG.JRA.yearlyTotals[currYear] ||
LEAVES_CONFIG.JRA.defaultBalance; LEAVES_CONFIG.JRA.defaultBalance;
// Logique de report du reliquat de l'année N-1
if (currMonth <= LEAVES_CONFIG.JRA.toleranceMonths) { if (currMonth <= LEAVES_CONFIG.JRA.toleranceMonths) {
const prevYear = currYear - 1; const prevYear = currYear - 1;
const prevInitial = const prevInitial =
@@ -519,10 +520,14 @@ document.addEventListener("DOMContentLoaded", () => {
0, 0,
prevInitial - usedPrevYear, prevInitial - usedPrevYear,
); );
// Ajout du report plafonné à 2 jours
initialBalance += Math.min( initialBalance += Math.min(
remainingPrevYear, remainingPrevYear,
LEAVES_CONFIG.JRA.maxReport, LEAVES_CONFIG.JRA.maxReport,
); );
console.log(
`[JRA] Personne ${pid}, Année ${currYear}: Report de ${Math.min(remainingPrevYear, LEAVES_CONFIG.JRA.maxReport)}j inclus.`,
);
} }
} else if (type === "JA") { } else if (type === "JA") {
const configJA = LEAVES_CONFIG.JA[pid]; const configJA = LEAVES_CONFIG.JA[pid];
@@ -761,14 +766,12 @@ document.addEventListener("DOMContentLoaded", () => {
const container = document.getElementById("fc-month-balances"); const container = document.getElementById("fc-month-balances");
if (!container) return; if (!container) return;
// 1. Déterminer les mois affichés selon la vue
const monthsToDisplay = []; const monthsToDisplay = [];
const y = this.currentMonth.getFullYear(); const y = this.currentMonth.getFullYear();
const m = this.currentMonth.getMonth(); const m = this.currentMonth.getMonth();
let numMonths = 1; let numMonths =
if (this.viewMode === "2months") numMonths = 2; this.viewMode === "2months" ? 2 : this.viewMode === "3months" ? 3 : 1;
if (this.viewMode === "3months") numMonths = 3;
for (let i = 0; i < numMonths; i++) { for (let i = 0; i < numMonths; i++) {
const d = new Date(y, m + i, 1); const d = new Date(y, m + i, 1);
@@ -780,40 +783,69 @@ document.addEventListener("DOMContentLoaded", () => {
container.style.display = "flex"; container.style.display = "flex";
let html = ""; let html = "";
// 2. Calcul dynamique pour Alex et Laia
[FAMILY.ALEX, FAMILY.LAIA].forEach((person) => { [FAMILY.ALEX, FAMILY.LAIA].forEach((person) => {
html += `<div class="fc-minimal-balance-card"> html += `<div class="fc-minimal-balance-card">
<strong class="${person.prefix}">${person.prefix.toUpperCase()}</strong> <strong class="${person.prefix}">${person.prefix.toUpperCase()}</strong>
<div class="fc-minimal-chips">`; <div class="fc-minimal-chips">`;
["CP", "JRA", "JA"].forEach((type) => { ["CP", "JRA", "JA"].forEach((type) => {
// Solde de départ = celui du PREMIER mois affiché
const startInfo = const startInfo =
this.monthlyLeaveBalances[person.id]?.[type]?.[monthsToDisplay[0]]; this.monthlyLeaveBalances[person.id]?.[type]?.[monthsToDisplay[0]];
const startBal = startInfo ? startInfo.availableAtMonthStart : 0; const startBal = startInfo ? startInfo.availableAtMonthStart : 0;
// Jours posés = Somme sur TOUS les mois affichés
let totalUsed = 0; let totalUsed = 0;
monthsToDisplay.forEach((ym) => { monthsToDisplay.forEach((ym) => {
const info = this.monthlyLeaveBalances[person.id]?.[type]?.[ym]; const info = this.monthlyLeaveBalances[person.id]?.[type]?.[ym];
if (info) totalUsed += info.usedInMonth; if (info) totalUsed += info.usedInMonth;
}); });
// Solde de fin (théorique) à la fin de la période
const endBal = Math.max(0, startBal - totalUsed); const endBal = Math.max(0, startBal - totalUsed);
const fmt = (n) => const fmt = (n) =>
n > 0 ? (Number.isInteger(n) ? n : n.toFixed(1)) : "0"; n > 0 ? (Number.isInteger(n) ? n : n.toFixed(1)) : "0";
// Petit badge rouge uniquement s'il y a des congés posés
const usedHtml = const usedHtml =
totalUsed > 0 totalUsed > 0
? `<span class="fc-used-badge" title="Posé sur la période">-${fmt(totalUsed)}</span>` ? `<span class="fc-used-badge" title="Posé sur la période">-${fmt(totalUsed)}</span>`
: ""; : "";
html += `<div class="fc-min-chip" title="Solde de départ: ${fmt(startBal)}"> // --- LOGIQUE D'ALERTE FIRE 🔥 ---
let alertHtml = "";
const currentYm = monthsToDisplay[0];
const [cYear, cMonth] = currentYm.split("-").map(Number);
if (endBal > 0) {
if (type === "CP" && cMonth >= 6 && cMonth <= 7) {
// CP : Alerte en Juin et Juillet uniquement
let msg = (
window.I18N["fc_alert_burn_days"] || "Perte: %s j avant le %s"
)
.replace("%s", fmt(endBal))
.replace("%s", "31/07");
alertHtml = `<div class="fc-burn-alert" title="${msg}">🔥</div>`;
} else if (type === "JRA" && (cMonth === 1 || cMonth === 2)) {
// JRA : Alerte Janvier/Février si > 2 jours
if (endBal > 2) {
let msg =
window.I18N["fc_alert_burn_jra"] || "Seuls 2j reportables";
alertHtml = `<div class="fc-burn-alert" title="${msg}">🔥 ${fmt(endBal - 2)}</div>`;
}
} else if (type === "JA") {
const configJA = LEAVES_CONFIG.JA[person.id];
let limitMonth = configJA.startMonth - 1 || 12;
if (cMonth === limitMonth || cMonth === configJA.startMonth) {
let msg = (window.I18N["fc_alert_burn_days"] || "Perte: %s j")
.replace("%s", fmt(endBal))
.replace("%s", window.I18N["ANNIV"] || "Anniv");
alertHtml = `<div class="fc-burn-alert" title="${msg}">🔥</div>`;
}
}
}
html += `<div class="fc-min-chip" title="Solde départ: ${fmt(startBal)}">
<span class="type">${type}</span> <span class="type">${type}</span>
<span class="val" title="Restant">${fmt(endBal)}</span> <span class="val" title="Restant">${fmt(endBal)}</span>
${usedHtml} ${usedHtml}
${alertHtml}
</div>`; </div>`;
}); });
html += `</div></div>`; html += `</div></div>`;
@@ -1160,7 +1192,8 @@ document.addEventListener("DOMContentLoaded", () => {
if (btnSnap && modalSnap) { if (btnSnap && modalSnap) {
btnSnap.addEventListener("click", () => { btnSnap.addEventListener("click", () => {
modalSnap.style.display = "flex"; modalSnap.style.display = "flex"; // Utilise flex au lieu de block pour le centrage
document.body.classList.add("no-scroll");
// Pré-remplir la date avec le 1er jour du mois en cours // Pré-remplir la date avec le 1er jour du mois en cours
const today = new Date(); const today = new Date();
const y = today.getFullYear(); const y = today.getFullYear();
+898 -657
View File
File diff suppressed because it is too large Load Diff