trad et modal
This commit is contained in:
+11
-8
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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)
|
||||||
// ==========================================
|
// ==========================================
|
||||||
|
|||||||
@@ -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)
|
||||||
========================================================= */
|
========================================================= */
|
||||||
|
|||||||
@@ -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
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user