`;
datesToDisplay.forEach((dateStr) => {
@@ -775,118 +872,254 @@ function openPlanningModal(step) {
month: "short",
});
+ let unplacedForDay = window.PLANNING_ALL_UNPLACED.filter((it) =>
+ isDateInStep(dateStr, it.step_start_date, it.step_end_date),
+ );
+
+ let badgeHtml = `
- `;
container.innerHTML = html;
- const isMobile = window.innerWidth <= 768;
- const dragAttr = isMobile ? "" : 'draggable="true"';
-
- validItems.forEach((it) => {
- let icon = "🏷️";
- let catClass = "cat-activity";
- if (it.category === "accommodation") {
- icon = "🏨";
- catClass = "cat-accommodation";
- }
- if (it.category === "transport") {
- icon = "🚗";
- catClass = "cat-transport";
- }
-
- const dur = it.duration || 1;
- const noteHtml = it.notes
- ? `
${it.notes}
`
- : "";
-
- const isVirtual = it.is_virtual === true;
- const isTransit = it.expense_context === "transit";
-
- const durControls =
- isVirtual || isTransit
- ? `
${dur}h (Auto)`
- : `
-
${dur}h
-
`;
-
- const bgStyle = isVirtual
- ? "background: repeating-linear-gradient(45deg, #ffffff, #ffffff 10px, #f8fafc 10px, #f8fafc 20px); border: 2px dashed var(--primary);"
- : "";
- const visualName = isTransit
- ? `🛣️ Trajet & ` + it.name
- : `${icon} ${it.name}`;
-
- const elHtml = `
-
-
-
-
${visualName}
-
- ${durControls}
-
-
- ${noteHtml}
-
- `;
-
- if (it.item_date && it.item_time && datesToDisplay.includes(it.item_date)) {
+ // 4. Placement des éléments
+ allPlaced.forEach((it) => {
+ if (datesToDisplay.includes(it.item_date)) {
const hourPrefix = it.item_time.substring(0, 2) + ":00";
const targetSlot = container.querySelector(
`.hol-time-slot[data-date="${it.item_date}"][data-time="${hourPrefix}"]`,
);
- if (targetSlot) {
- targetSlot.insertAdjacentHTML("beforeend", elHtml);
- return;
- }
+ if (targetSlot)
+ targetSlot.insertAdjacentHTML("beforeend", buildDragItemHtml(it));
+ }
+ });
+
+ // 5. Météo et Focus auto
+ datesToDisplay.forEach((dateStr) => {
+ let activeStep = window.MAP_POINTS.find((step) =>
+ isDateInStep(dateStr, step.step_start_date, step.step_end_date),
+ );
+ if (activeStep && activeStep.lat && activeStep.lng) {
+ loadWeatherForPlanning(activeStep.lat, activeStep.lng, dateStr);
}
- document
- .getElementById("unmapped-pool")
- .insertAdjacentHTML("beforeend", elHtml);
});
document.getElementById("planningModal").style.display = "flex";
document.body.classList.add("no-scroll");
- datesToDisplay.forEach((dateStr) => {
- loadWeatherForPlanning(step.lat, step.lng, dateStr);
+ let todayStr = new Date().toISOString().split("T")[0];
+ let defaultFocusDate =
+ todayStr >= holidayData.start_date && todayStr <= holidayData.end_date
+ ? todayStr
+ : holidayData.start_date;
+
+ filterPoolByDate(defaultFocusDate);
+
+ setTimeout(() => {
+ const activeCol = document.getElementById("col-" + defaultFocusDate);
+ if (activeCol) {
+ document.getElementById("calendarZoneContainer").scrollTo({
+ left: activeCol.offsetLeft - 300,
+ behavior: "smooth",
+ });
+ }
+ }, 200);
+
+ // Moteur Scroll Natif (Drag To Scroll)
+ const slider = document.getElementById("calendarZoneContainer");
+ let isDown = false;
+ let startX, startY, scrollLeft, scrollTop;
+
+ slider.addEventListener("mousedown", (e) => {
+ if (e.target.closest(".hol-drag-item") || e.target.closest("button"))
+ return;
+ isDown = true;
+ slider.style.cursor = "grabbing";
+ startX = e.pageX - slider.offsetLeft;
+ startY = e.pageY - slider.offsetTop;
+ scrollLeft = slider.scrollLeft;
+ scrollTop = slider.scrollTop;
});
+ slider.addEventListener("mouseleave", () => {
+ isDown = false;
+ slider.style.cursor = "grab";
+ });
+ slider.addEventListener("mouseup", () => {
+ isDown = false;
+ slider.style.cursor = "grab";
+ });
+ slider.addEventListener("mousemove", (e) => {
+ if (!isDown) return;
+ e.preventDefault();
+ const x = e.pageX - slider.offsetLeft;
+ const y = e.pageY - slider.offsetTop;
+ const walkX = (x - startX) * 1.5;
+ const walkY = (y - startY) * 1.5;
+ slider.scrollLeft = scrollLeft - walkX;
+ slider.scrollTop = scrollTop - walkY;
+ });
+}
+
+function isDateInStep(targetDate, stepStart, stepEnd) {
+ if (!stepStart || !stepEnd) return false;
+ return targetDate >= stepStart && targetDate <= stepEnd;
+}
+
+function filterPoolByDate(dateStr) {
+ window.CURRENT_PLANNING_FILTER_DATE = dateStr;
+ const pool = document.getElementById("unmapped-pool");
+ pool.innerHTML = "";
+
+ let poolItemsHtml = "";
+ let count = 0;
+
+ // Itération sur la MAP, donc l'ordre natif de tri (sort_order/ID) est préservé !
+ Object.values(window.PLANNING_ITEM_MAP).forEach((it) => {
+ const htmlId = it.is_virtual ? it.id : `drag-item-${it.id}`;
+ const isPlaced = document.querySelector(`.hol-time-slot #${htmlId}`);
+
+ if (!isPlaced) {
+ if (
+ !dateStr ||
+ isDateInStep(dateStr, it.step_start_date, it.step_end_date)
+ ) {
+ poolItemsHtml += buildDragItemHtml(it);
+ count++;
+ }
+ }
+ });
+
+ if (count === 0) {
+ pool.innerHTML = `
🎉
Rien à placer${dateStr ? " pour cette journée" : ""}.
`;
+ } else {
+ pool.innerHTML = poolItemsHtml;
+ }
+}
+
+function recalcAllBadges() {
+ const pool = document.getElementById("unmapped-pool");
+ const unmappedIds = Array.from(pool.children).map((el) => el.id);
+
+ document.querySelectorAll(".hol-day-column").forEach((col) => {
+ const dateStr = col.id.replace("col-", "");
+ const badge = document.getElementById("badge-" + dateStr);
+ if (!badge) return;
+
+ let dayCount = 0;
+ unmappedIds.forEach((htmlId) => {
+ const it = window.PLANNING_ITEM_MAP[htmlId];
+ if (it && isDateInStep(dateStr, it.step_start_date, it.step_end_date))
+ dayCount++;
+ });
+
+ badge.innerText = dayCount;
+ badge.style.display = dayCount > 0 ? "inline-block" : "none";
+ });
+}
+
+function buildDragItemHtml(it) {
+ let icon = "🏷️";
+ let catClass = "cat-activity";
+ if (it.category === "accommodation") {
+ icon = "🏨";
+ catClass = "cat-accommodation";
+ }
+ if (it.category === "transport") {
+ icon = "🚗";
+ catClass = "cat-transport";
+ }
+
+ const dur = it.duration || 1;
+ const noteHtml = it.notes
+ ? `
${it.notes}
`
+ : "";
+ const isVirtual = it.is_virtual === true;
+ const isTransit = it.expense_context === "transit";
+
+ const durControls = `
+
${dur}h
+
`;
+
+ const bgStyle =
+ isVirtual || isTransit
+ ? "background: repeating-linear-gradient(45deg, var(--bg-page), var(--bg-page) 10px, rgba(59, 130, 246, 0.05) 10px, rgba(59, 130, 246, 0.05) 20px); border: 2px dashed var(--primary);"
+ : "background: var(--bg-panel); border: 1px solid var(--border-strong);";
+
+ const visualName = isTransit ? `🛣️ Trajet & Essence` : `${icon} ${it.name}`;
+ const isMobile = window.innerWidth <= 768;
+ const dragAttr = isMobile ? "" : 'draggable="true"';
+ const htmlId = isVirtual ? it.id : `drag-item-${it.id}`;
+ const locHint =
+ !it.item_date && it.step_location
+ ? `
📍 ${it.step_location}
`
+ : "";
+
+ // Suppression du calcul de hauteur en ligne, c'est désormais géré via CSS et la variable "--duration"
+ return `
+
+ ${locHint}
+
+
${visualName}
+
+ ${durControls}
+
+
+ ${noteHtml}
+
+ `;
}
function changeDuration(e, itemId, delta) {
e.stopPropagation();
- const itemEl = document.getElementById("drag-item-" + itemId);
+ const itemEl =
+ document.getElementById("drag-item-" + itemId) ||
+ document.getElementById(itemId);
+ if (!itemEl) return;
+
let currentDur = parseInt(itemEl.style.getPropertyValue("--duration")) || 1;
let newDur = currentDur + delta;
if (newDur < 1) newDur = 1;
if (newDur > 12) newDur = 12;
+
itemEl.style.setProperty("--duration", newDur);
document.getElementById(`dur-text-${itemId}`).innerText = newDur + "h";
+
+ if (itemEl.getAttribute("data-virtual") === "true") {
+ const it = window.PLANNING_ITEM_MAP[itemEl.id];
+ if (it) it.duration = newDur;
+ return;
+ }
+
updateItemMemory(itemId, { duration: newDur });
+
const formData = new FormData();
formData.append("action", "update_item_duration");
formData.append("item_id", itemId);
@@ -897,24 +1130,32 @@ function changeDuration(e, itemId, delta) {
});
}
-function handleItemTap(e, itemId) {
+function handleItemTap(e, htmlId) {
e.stopPropagation();
document
.querySelectorAll(".hol-drag-item")
.forEach((el) => el.classList.remove("selected-for-move"));
- if (selectedItemIdForMove === itemId) {
+ if (selectedItemIdForMove === htmlId) {
selectedItemIdForMove = null;
} else {
- selectedItemIdForMove = itemId;
- document
- .getElementById("drag-item-" + itemId)
- .classList.add("selected-for-move");
+ selectedItemIdForMove = htmlId;
+ const el = document.getElementById(htmlId);
+ if (el) el.classList.add("selected-for-move");
}
}
function handleZoneTap(e, dateStr, timeStr) {
if (selectedItemIdForMove) {
- handleDropLogic(selectedItemIdForMove, dateStr, timeStr, e.currentTarget);
+ const itemEl = document.getElementById(selectedItemIdForMove);
+ if (itemEl) {
+ const targetZone = e.currentTarget;
+ if (targetZone.id === "unmapped-pool") {
+ handleDropLogic(selectedItemIdForMove, "", "");
+ } else {
+ targetZone.appendChild(itemEl);
+ handleDropLogic(selectedItemIdForMove, dateStr, timeStr);
+ }
+ }
selectedItemIdForMove = null;
document
.querySelectorAll(".hol-drag-item")
@@ -925,62 +1166,178 @@ function handleZoneTap(e, dateStr, timeStr) {
function dragStart(e) {
e.dataTransfer.setData("text/plain", e.target.id);
e.dataTransfer.effectAllowed = "move";
+ // Mémorisation de la durée pour le survol dynamique (Fix #2)
+ window.CURRENT_DRAG_DURATION =
+ parseInt(e.target.style.getPropertyValue("--duration")) || 1;
}
+
+function dragEnd(e) {
+ window.CURRENT_DRAG_DURATION = 1;
+ document
+ .querySelectorAll(".hol-time-slot")
+ .forEach((s) => s.classList.remove("drag-over-duration"));
+}
+
function allowDrop(e) {
e.preventDefault();
}
+
function dragEnter(e) {
e.preventDefault();
- let s = e.target.closest(".hol-time-slot");
- if (s) s.classList.add("drag-over");
+ let slot = e.target.closest(".hol-time-slot");
+ if (slot) {
+ // Retirer toutes les anciennes surbrillances
+ document
+ .querySelectorAll(".hol-time-slot")
+ .forEach((s) => s.classList.remove("drag-over-duration"));
+
+ // Appliquer la surbrillance sur les N cases consécutives
+ let dur = window.CURRENT_DRAG_DURATION || 1;
+ let currentSlot = slot;
+ for (let i = 0; i < dur; i++) {
+ if (currentSlot) {
+ currentSlot.classList.add("drag-over-duration");
+ currentSlot = currentSlot.nextElementSibling;
+ }
+ }
+ }
}
+
function dragLeave(e) {
- let s = e.target.closest(".hol-time-slot");
- if (s) s.classList.remove("drag-over");
+ // La gestion précise des surbrillances se fait via le dragEnter et dragEnd pour éviter le scintillement (flickering).
}
function handleDropEvent(e, dateStr, timeStr) {
e.preventDefault();
- let slot = e.target.closest(".hol-time-slot");
- if (slot) slot.classList.remove("drag-over");
+ document
+ .querySelectorAll(".hol-time-slot")
+ .forEach((s) => s.classList.remove("drag-over-duration"));
+
const idStr = e.dataTransfer.getData("text/plain");
- const itemId = idStr.replace("drag-item-", "");
- const dropZone = slot || document.getElementById("unmapped-pool");
- handleDropLogic(itemId, dateStr, timeStr, dropZone);
+ const itemEl = document.getElementById(idStr);
+ const dropZone =
+ e.target.closest(".hol-time-slot") ||
+ document.getElementById("unmapped-pool");
+
+ if (itemEl && dropZone) {
+ if (dropZone.id === "unmapped-pool") {
+ // FIX #3 : Contourner l'appendChild (qui met tout en bas) et déléguer à la fonction pour re-trier la colonne !
+ handleDropLogic(idStr, "", "");
+ } else {
+ dropZone.appendChild(itemEl);
+ handleDropLogic(idStr, dateStr, timeStr);
+ }
+ }
}
-function handleDropLogic(itemId, dateStr, timeStr, dropZone) {
- if (itemId === "virtual-transit") {
- const step = window.CURRENT_PLANNING_STEP;
- const tData = window.TRANSIT_DATA[step.sort_order];
- const holidayId = document.querySelector('input[name="holiday_id"]').value;
- const h = Math.max(1, Math.round(tData.sec / 3600));
+function handleDropLogic(htmlId, dateStr, timeStr) {
+ const itemEl = document.getElementById(htmlId);
+ if (!itemEl) return;
+ const isVirtual = itemEl.getAttribute("data-virtual") === "true";
+ const holidayId = document.querySelector('input[name="holiday_id"]').value;
+ const sortOrder = itemEl.getAttribute("data-sort");
+ const dur = parseInt(itemEl.style.getPropertyValue("--duration")) || 1;
+ const realId = itemEl.getAttribute("data-id");
+
+ // DÉSASSIGNER : Si on replace la carte dans la zone de gauche
+ if (dateStr === "" || timeStr === "") {
+ if (!isVirtual) {
+ updateItemMemory(realId, { item_date: null, item_time: null });
+ const fd = new FormData();
+ fd.append("action", "update_item_datetime");
+ fd.append("item_id", realId);
+ fd.append("item_date", "");
+ fd.append("item_time", "");
+ fetch("/modules/holidays/includes/api/save_checkpoint.php", {
+ method: "POST",
+ body: fd,
+ });
+ }
+ // 🔥 FIX #3 : Re-filtrer reconstruit la colonne de gauche dans SON ORDRE D'ORIGINE !
+ filterPoolByDate(window.CURRENT_PLANNING_FILTER_DATE);
+ recalcAllBadges();
+ return;
+ }
+
+ // AFFECTATION CALENDRIER
+ if (isVirtual) {
+ const tData = window.TRANSIT_DATA[sortOrder];
const fd = new FormData();
fd.append("action", "add_single_item");
fd.append("holiday_id", holidayId);
- fd.append("sort_order", step.sort_order);
+ fd.append("sort_order", sortOrder);
fd.append("category", "transport");
fd.append("context", "transit");
+ fd.append("expense_context", "transit");
fd.append("name", `Essence depuis ${tData.from}`);
fd.append("amount", tData.cost);
- fd.append("duration", h);
+ fd.append("duration", dur);
+ fd.append("item_date", dateStr);
+ fd.append("item_time", timeStr);
+ fd.append("ajax", "1");
+
+ itemEl.setAttribute("data-virtual", "false");
+
+ fetch("/modules/holidays/includes/api/save_checkpoint.php", {
+ method: "POST",
+ body: fd,
+ })
+ .then((res) => res.json())
+ .then(async (data) => {
+ if (data && data.id) {
+ itemEl.id = `drag-item-${data.id}`;
+ itemEl.setAttribute("data-id", data.id);
+
+ const fdDate = new FormData();
+ fdDate.append("action", "update_item_datetime");
+ fdDate.append("item_id", data.id);
+ fdDate.append("item_date", dateStr);
+ fdDate.append("item_time", timeStr);
+ await fetch("/modules/holidays/includes/api/save_checkpoint.php", {
+ method: "POST",
+ body: fdDate,
+ });
+
+ window.PLANNING_ITEM_MAP[itemEl.id] = {
+ id: data.id,
+ step_start_date: dateStr,
+ step_end_date: dateStr,
+ expense_context: "transit",
+ category: "transport",
+ is_virtual: false,
+ };
+ } else {
+ window.location.reload();
+ }
+ })
+ .catch(() => {
+ window.location.reload();
+ });
+ } else {
+ updateItemMemory(realId, { item_date: dateStr, item_time: timeStr });
+ const fd = new FormData();
+ fd.append("action", "update_item_datetime");
+ fd.append("item_id", realId);
fd.append("item_date", dateStr);
fd.append("item_time", timeStr);
fetch("/modules/holidays/includes/api/save_checkpoint.php", {
method: "POST",
body: fd,
- }).then(() => window.location.reload());
- return;
+ });
}
+
+ recalcAllBadges();
}
function updateItemMemory(itemId, changes) {
- MAP_POINTS.forEach((step) => {
- let item = step.items.find((i) => i.id == itemId);
- if (item) Object.assign(item, changes);
- });
+ if (typeof MAP_POINTS !== "undefined") {
+ MAP_POINTS.forEach((step) => {
+ let item = step.items.find((i) => i.id == itemId);
+ if (item) Object.assign(item, changes);
+ });
+ }
}
// ============================================================================
@@ -1377,3 +1734,47 @@ window.generateTravelBook = function () {
btn.disabled = false;
});
};
+
+// ============================================================================
+// SAUVEGARDE DES NOTES GLOBALES DU VOYAGE (AJAX)
+// ============================================================================
+async function saveHolidayGlobalNote(holidayId) {
+ const btn = document.getElementById("btnSaveHolidayNote");
+ const textarea = document.getElementById("holidayGlobalNotes");
+ if (!textarea || !btn) return;
+
+ const originalText = btn.innerHTML;
+ btn.innerHTML = "⏳...";
+ btn.disabled = true;
+
+ const fd = new FormData();
+ fd.append("action", "update_holiday_note");
+ fd.append("holiday_id", holidayId);
+ fd.append("notes", textarea.value);
+
+ try {
+ const res = await pachaFetch(
+ "/modules/holidays/includes/api/save_checkpoint.php",
+ {
+ method: "POST",
+ body: fd,
+ },
+ );
+
+ if (res.success) {
+ // L'appel utilise bien ta fonction showToast() globale pour le design des notifications !
+ showToast(
+ window.I18N["bud_prev_saved"] || "Notes sauvegardées !",
+ "success",
+ );
+ } else {
+ showToast(res.error || "Erreur lors de la sauvegarde", "error");
+ }
+ } catch (e) {
+ console.error("Erreur saveHolidayGlobalNote:", e);
+ showToast("Erreur réseau.", "error");
+ } finally {
+ btn.innerHTML = originalText;
+ btn.disabled = false;
+ }
+}
diff --git a/modules/holidays/includes/api/save_checkpoint.php b/modules/holidays/includes/api/save_checkpoint.php
index 87954a9..85fe61e 100644
--- a/modules/holidays/includes/api/save_checkpoint.php
+++ b/modules/holidays/includes/api/save_checkpoint.php
@@ -7,11 +7,20 @@ require_login();
// ---------------------------------------------------------------------------
// INTERCEPTIONS AJAX (Exécution ultra rapide sans rechargement lourd)
// ---------------------------------------------------------------------------
-if (isset($_POST['action']) && in_array($_POST['action'], ['update_item_datetime', 'update_item_duration', 'add_single_item'])) {
+if (isset($_POST['action']) && in_array($_POST['action'], ['update_item_datetime', 'update_item_duration', 'add_single_item', 'update_holiday_note'])) {
header('Content-Type: application/json');
- session_write_close(); // 🚀 LIBÈRE LA SESSION : Permet d'autres requêtes simultanées sans bloquer le navigateur
+ session_write_close();
try {
+ if ($_POST['action'] === 'update_holiday_note') {
+ $hId = (int)$_POST['holiday_id'];
+ $notes = $_POST['notes'] ?? '';
+ $stmt = $pdo->prepare("UPDATE pf_holidays SET notes = ? WHERE id = ?");
+ $stmt->execute([$notes, $hId]);
+ echo json_encode(['success' => true]);
+ exit;
+ }
+
if ($_POST['action'] === 'add_single_item') {
$holiday_id = (int)$_POST['holiday_id'];
$sort_order = (int)$_POST['sort_order'];
@@ -25,16 +34,28 @@ if (isset($_POST['action']) && in_array($_POST['action'], ['update_item_datetime
$date = !empty($_POST['item_date']) ? $_POST['item_date'] : null;
$time = !empty($_POST['item_time']) ? $_POST['item_time'] : null;
+ $context = $_POST['expense_context'] ?? ($_POST['context'] ?? 'local');
+
$pdo->beginTransaction();
+
+ if ($context === 'transit') {
+ $stmtDel = $pdo->prepare("DELETE FROM pf_holidays_items WHERE holiday_id = ? AND sort_order = ? AND expense_context = 'transit'");
+ $stmtDel->execute([$holiday_id, $sort_order]);
+ }
+
$ins = $pdo->prepare("INSERT INTO pf_holidays_items (holiday_id, category, name, amount, is_paid, location_name, lat, lng, sort_order, step_start_date, step_end_date, step_type, expense_context, duration, item_date, item_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$ins->execute([
$holiday_id, $_POST['category'], $_POST['name'], (float)$_POST['amount'], 0,
$stepInfo['location_name'], $stepInfo['lat'], $stepInfo['lng'],
$sort_order, $stepInfo['step_start_date'], $stepInfo['step_end_date'],
- $stepInfo['step_type'], $_POST['context'], $dur, $date, $time
+ $stepInfo['step_type'], $context, $dur, $date, $time
]);
+
+ $newId = $pdo->lastInsertId();
+
$pdo->commit();
- echo json_encode(['success' => true]);
+
+ echo json_encode(['success' => true, 'id' => $newId]);
exit;
}
echo json_encode(['success' => false, 'error' => 'Etape introuvable']);
@@ -61,6 +82,18 @@ if (isset($_POST['action']) && in_array($_POST['action'], ['update_item_datetime
echo json_encode(['success' => true]);
exit;
}
+
+ // Mise à jour de la note globale du voyage
+ if ($_POST['action'] === 'update_holiday_note') {
+ $hId = (int)$_POST['holiday_id'];
+ $notes = $_POST['notes'] ?? '';
+ $stmt = $pdo->prepare("UPDATE pf_holidays SET notes = ? WHERE id = ?");
+ $stmt->execute([$notes, $hId]);
+ echo json_encode(['success' => true]);
+ exit;
+ }
+
+
} catch (Exception $e) {
if ($pdo->inTransaction()) { $pdo->rollBack(); }
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
diff --git a/modules/holidays/views/detail.php b/modules/holidays/views/detail.php
index ac35986..8515312 100644
--- a/modules/holidays/views/detail.php
+++ b/modules/holidays/views/detail.php
@@ -16,7 +16,8 @@ $stmt = $pdo->prepare("
(COALESCE(h.budget_food, 0) + COALESCE(h.budget_extra, 0) + COALESCE((SELECT SUM(amount) FROM pf_holidays_items WHERE holiday_id = h.id), 0)) as total_cost,
(SELECT COALESCE(SUM(amount), 0) FROM pf_holidays_items WHERE holiday_id = h.id AND is_paid = 1) as total_paid,
(SELECT COALESCE(SUM(amount), 0) FROM pf_savings WHERE holiday_id = h.id) as total_saved,
- (SELECT COALESCE(SUM(amount), 0) FROM pf_holidays_items WHERE holiday_id = h.id AND expense_context = 'transit') as total_transit
+ (SELECT COALESCE(SUM(amount), 0) FROM pf_holidays_items WHERE holiday_id = h.id AND (expense_context = 'transit' OR (category = 'transport' AND (name LIKE '%Essence%' OR name LIKE '%Carburant%')))) as total_fuel,
+ (SELECT COALESCE(SUM(amount), 0) FROM pf_holidays_items WHERE holiday_id = h.id AND category = 'transport' AND (name LIKE '%Péage%' OR name LIKE '%Peage%')) as total_tolls
FROM pf_holidays h
LEFT JOIN pf_vehicles v ON h.vehicle_id = v.id
WHERE h.id = ?
@@ -108,6 +109,10 @@ $pctSaved = $cost > 0 ? min(100 - $pctPaid, ($saved / $cost) * 100) : 0;
📖 Carnet de Voyage
+
+
@@ -127,18 +132,30 @@ $pctSaved = $cost > 0 ? min(100 - $pctPaid, ($saved / $cost) * 100) : 0;
- Frais de route (Essence/Péages)
+ Frais de route
-
-
⛽
-
= number_format($holiday['total_transit'], 0) ?> €
+
+
+
+ 💳
+ = number_format($holiday['total_tolls'], 0) ?> €
+
+
+
+
+ ⛽
+ = number_format($holiday['total_fuel'], 0) ?> €
+
+
+
(1.85 €/L) ✏️
- 0): ?>
-
+
+ 0 || $holiday['total_tolls'] > 0): ?>
+
👁️
@@ -201,59 +218,7 @@ $pctSaved = $cost > 0 ? min(100 - $pctPaid, ($saved / $cost) * 100) : 0;
= tr('hdl_no_steps') ?>
- 0 || $holiday['budget_extra'] > 0): ?>
-
-
-
-
- 0): ?>
-
- 🍔 = tr('hdl_food_bev') ?>
- = number_format($holiday['budget_food'], 2, ',', ' ') ?> €⏳
-
-
- 0): ?>
-
- 🎁 = tr('hdl_extras') ?>
- = number_format($holiday['budget_extra'], 2, ',', ' ') ?> €⏳
-
-
-
- '🚗', 'accommodation' => '🏨', 'activity' => '🎫', default => '🏷️' };
- ?>
-
-
-
- = $icon ?> = htmlspecialchars($it['name']) ?>
-
-
-
- = number_format($it['amount'], 2, ',', ' ') ?> €
-
- = $it['is_paid'] ? '✓' : '⏳' ?>
-
-
-
-
-
-
-
-
+
@@ -312,7 +277,6 @@ $pctSaved = $cost > 0 ? min(100 - $pctPaid, ($saved / $cost) * 100) : 0;
-
@@ -349,16 +313,17 @@ $pctSaved = $cost > 0 ? min(100 - $pctPaid, ($saved / $cost) * 100) : 0;
-
-
-
- 📝 = tr('hdl_label_notes') ?>
-
-
- = htmlspecialchars($holiday['notes']) ?>
+
+
📝 = tr('hdl_label_notes') ?>
+
+
+
+
-
+
@@ -454,13 +419,16 @@ $pctSaved = $cost > 0 ? min(100 - $pctPaid, ($saved / $cost) * 100) : 0;
-
-
-
📅 = tr('hdl_planning_title') ?>
+
+
+
📅 Planning Global
-
-
diff --git a/modules/holidays/views/modal.php b/modules/holidays/views/modal.php
index 1b33646..3c8a390 100644
--- a/modules/holidays/views/modal.php
+++ b/modules/holidays/views/modal.php
@@ -62,10 +62,6 @@
-
-
-
-