fix etapes duplicate

This commit is contained in:
2026-05-06 21:27:03 +02:00
parent 8a6abe5f8e
commit e099fb80cd
3 changed files with 80 additions and 67 deletions
+33 -40
View File
@@ -115,87 +115,80 @@ function closeHolidayModal() {
function editHoliday(data) { function editHoliday(data) {
const h = data.main; const h = data.main;
const modal = document.getElementById("holidayModal"); const modal = document.getElementById("holidayModal");
if (!modal) return;
if (!modal) {
alert(tr("err_modal_missing"));
return;
}
document.body.appendChild(modal);
if (typeof openHolidayModal === "function") {
openHolidayModal("edit"); openHolidayModal("edit");
}
modal.classList.add("open");
modal.style.setProperty("display", "flex", "important");
modal.style.setProperty("z-index", "999999", "important");
document.body.classList.add("no-scroll");
try {
document.getElementById("inp_id").value = h.id; document.getElementById("inp_id").value = h.id;
document.getElementById("inp_title").value = h.title; document.getElementById("inp_title").value = h.title;
document.getElementById("inp_status").value = h.status; document.getElementById("inp_status").value = h.status;
document.getElementById("inp_period").value = h.period_hint || ""; document.getElementById("inp_period").value = h.period_hint || "";
document.getElementById("inp_start").value = h.start_date || ""; document.getElementById("inp_start").value = h.start_date || "";
document.getElementById("inp_end").value = h.end_date || ""; document.getElementById("inp_end").value = h.end_date || "";
document.getElementById("inp_food").value = document.getElementById("inp_food").value =
h.budget_food > 0 ? h.budget_food : ""; h.budget_food > 0 ? h.budget_food : "";
document.getElementById("inp_extra").value = document.getElementById("inp_extra").value =
h.budget_extra > 0 ? h.budget_extra : ""; h.budget_extra > 0 ? h.budget_extra : "";
document.getElementById("inp_notes").value = h.notes || ""; document.getElementById("inp_notes").value = h.notes || "";
} catch (err) {
console.error("Erreur champs textes :", err);
}
try {
document.getElementById("list_transport").innerHTML = ""; document.getElementById("list_transport").innerHTML = "";
document.getElementById("list_accommodation").innerHTML = ""; document.getElementById("list_accommodation").innerHTML = "";
document.getElementById("list_activity").innerHTML = ""; document.getElementById("list_activity").innerHTML = "";
if (data.items && data.items.length > 0) { if (data.items) {
data.items.forEach((item) => { data.items.forEach((item) => {
if ( if (item.name !== "PF_TECHNICAL_POINT") {
typeof addItem === "function" && // On passe maintenant l'ID et le lieu à addItem
item.name !== "PF_TECHNICAL_POINT" addItem(
) { item.category,
addItem(item.category, item.name, item.amount, item.is_paid); item.name,
item.amount,
item.is_paid,
item.id,
item.location_name || "",
);
} }
}); });
} }
} catch (err) {
console.error("Erreur listes :", err);
}
} }
// --- 2. GESTION DES LISTES DYNAMIQUES DANS LA MODALE --- // --- 2. GESTION DES LISTES DYNAMIQUES DANS LA MODALE ---
function addItem(category, name = "", amount = "", isPaid = 0) { function addItem(
category,
name = "",
amount = "",
isPaid = 0,
id = "",
location = "",
) {
const container = document.getElementById("list_" + category); const container = document.getElementById("list_" + category);
const div = document.createElement("div"); const div = document.createElement("div");
div.className = "savings-line-item"; // Utilisation de ta classe existante
div.style.display = "flex";
div.style.gap = "8px";
div.style.alignItems = "center";
div.style.marginBottom = "10px"; div.style.marginBottom = "10px";
const checkedAttr = isPaid == 1 ? "checked" : ""; const checkedAttr = isPaid == 1 ? "checked" : "";
// Optimisation : Affichage du badge d'étape si existant
const locationBadge = location
? `<span style="font-size:0.65rem; background:#e2e8f0; padding:2px 6px; border-radius:4px; margin-right:5px; color:#64748b; font-weight:bold;">📍 ${location}</span>`
: "";
div.innerHTML = ` div.innerHTML = `
<input type="hidden" name="items[id][]" value="${id}">
<input type="hidden" name="items[cat][]" value="${category}"> <input type="hidden" name="items[cat][]" value="${category}">
<input type="text" name="items[name][]" class="pf-input" placeholder="${tr("hdl_js_ph_expense_name")}" value="${name}" style="flex: 2; padding: 8px; font-size:0.9rem;" required> <input type="hidden" name="items[location][]" value="${location}">
<input type="number" step="0.01" name="items[amount][]" class="pf-input" placeholder="0.00" value="${amount}" style="width: 80px; text-align: right; padding: 8px; font-size:0.9rem;"> <div style="flex: 2; display:flex; flex-direction:column; gap:4px;">
${locationBadge}
<input type="text" name="items[name][]" class="pf-input" placeholder="${tr("hdl_js_ph_expense_name")}" value="${name}" style="padding: 8px; font-size:0.9rem;" required>
</div>
<input type="number" step="0.01" name="items[amount][]" class="pf-input" placeholder="0.00" value="${amount}" style="width: 90px; text-align: right; padding: 8px; font-size:0.9rem;">
<label title="${tr("hdl_paid")}" style="display: flex; align-items: center; cursor: pointer; padding: 0 5px;"> <label title="${tr("hdl_paid")}" style="display: flex; align-items: center; cursor: pointer; padding: 0 5px;">
<input type="checkbox" ${checkedAttr} onchange="this.nextElementSibling.value = this.checked ? 1 : 0" style="margin:0;"> <input type="checkbox" ${checkedAttr} onchange="this.nextElementSibling.value = this.checked ? 1 : 0" style="margin:0;">
<input type="hidden" name="items[paid][]" value="${isPaid}"> <input type="hidden" name="items[paid][]" value="${isPaid}">
<span style="font-size:0.75rem; margin-left:4px; font-weight:bold; color:#64748b;">${tr("hdl_paid")}</span> <span style="font-size:0.75rem; margin-left:4px; font-weight:bold; color:#64748b;">${tr("hdl_paid")}</span>
</label> </label>
<button type="button" onclick="this.parentElement.remove()" class="btn-icon-action delete" title="${tr("btn_delete")}"> <button type="button" onclick="this.parentElement.remove()" class="btn-icon-action delete" title="${tr("btn_delete")}">🗑️</button>
🗑️
</button>
`; `;
container.appendChild(div); container.appendChild(div);
} }
+27 -9
View File
@@ -39,24 +39,42 @@ try {
$id = $pdo->lastInsertId(); $id = $pdo->lastInsertId();
} }
// GESTION DES ITEMS GLOBAUX (On ne supprime QUE les items qui n'ont pas de lieu défini !) // GESTION INTELLIGENTE DES ITEMS
$pdo->prepare("DELETE FROM pf_holidays_items WHERE holiday_id = ? AND (location_name IS NULL OR location_name = '')")->execute([$id]);
if (!empty($_POST['items']['name'])) { if (!empty($_POST['items']['name'])) {
$stmtItem = $pdo->prepare("INSERT INTO pf_holidays_items (holiday_id, category, name, amount, is_paid) VALUES (?, ?, ?, ?, ?)");
$count = count($_POST['items']['name']); $count = count($_POST['items']['name']);
// On prépare une requête qui met à jour si l'ID existe, sinon insère
$stmtItem = $pdo->prepare("
INSERT INTO pf_holidays_items (id, holiday_id, category, name, amount, is_paid, location_name)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
category = VALUES(category),
name = VALUES(name),
amount = VALUES(amount),
is_paid = VALUES(is_paid)
");
$keepIds = [];
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
// Utilisation de "?? ''" pour sécuriser si la donnée n'est pas envoyée $itemId = !empty($_POST['items']['id'][$i]) ? (int)$_POST['items']['id'][$i] : null;
$cat = $_POST['items']['cat'][$i] ?? ''; $cat = $_POST['items']['cat'][$i] ?? 'activity';
$name = trim($_POST['items']['name'][$i] ?? ''); $name = trim($_POST['items']['name'][$i] ?? '');
$amount = floatval($_POST['items']['amount'][$i] ?? 0); $amount = floatval($_POST['items']['amount'][$i] ?? 0);
$paid = isset($_POST['items']['paid'][$i]) ? $_POST['items']['paid'][$i] : 0; $paid = (int)($_POST['items']['paid'][$i] ?? 0);
$loc = !empty($_POST['items']['location'][$i]) ? $_POST['items']['location'][$i] : null;
if (!empty($name)) { if (!empty($name)) {
$stmtItem->execute([$id, $cat, $name, $amount, $paid]); $stmtItem->execute([$itemId, $id, $cat, $name, $amount, $paid, $loc]);
$keepIds[] = $itemId ?: $pdo->lastInsertId();
} }
} }
// Nettoyage : On supprime les items qui ont été retirés de la modale
// (Attention : uniquement ceux du voyage actuel qui ne sont plus dans la liste envoyée)
if (!empty($keepIds)) {
$placeholders = implode(',', array_fill(0, count($keepIds), '?'));
$sqlDel = "DELETE FROM pf_holidays_items WHERE holiday_id = ? AND id NOT IN ($placeholders)";
$pdo->prepare($sqlDel)->execute(array_merge([$id], $keepIds));
}
} }
$pdo->commit(); $pdo->commit();
+2
View File
@@ -391,6 +391,8 @@ window.I18N = {
'hdl_ph_notes': "<?= tr('hdl_ph_notes') ?>", 'hdl_ph_notes': "<?= tr('hdl_ph_notes') ?>",
'hdl_btn_add_step': "<?= tr('hdl_btn_add_step') ?>", 'hdl_btn_add_step': "<?= tr('hdl_btn_add_step') ?>",
'hdl_quick_edit_title': "<?= tr('hdl_quick_edit_title') ?>", 'hdl_quick_edit_title': "<?= tr('hdl_quick_edit_title') ?>",
'hdl_paid': "<?= tr('hdl_paid') ?>",
// --- NOUVELLES CLÉS MÉTÉO ICI --- // --- NOUVELLES CLÉS MÉTÉO ICI ---
'weather_sunny': "<?= tr('weather_sunny') ?>", 'weather_sunny': "<?= tr('weather_sunny') ?>",