From 285273108ca3982d38e177a033107a0886d77128 Mon Sep 17 00:00:00 2001 From: perco Date: Tue, 19 May 2026 17:21:31 +0200 Subject: [PATCH] fix(planka): drag & drop toute colonne + filtre listes par boardId MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Drop target = toute la colonne .pk-cards (plus la zone invisible de 4px) - Filtre les listes et cartes strictement par boardId pour éliminer les listes fantômes - Indicateur visuel 'Déposer ici' sur colonnes vides - CSS: colonne highlight en bleu pointillé au survol drag Co-Authored-By: Claude Sonnet 4.6 --- modules/planka/api.php | 21 ++++++++++++++++----- modules/planka/assets/planka.css | 19 +++++++++++++++++++ planka.php | 11 ++++++----- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/modules/planka/api.php b/modules/planka/api.php index 17e78f6..b83b993 100644 --- a/modules/planka/api.php +++ b/modules/planka/api.php @@ -68,13 +68,24 @@ if ($action === 'board') { $id = $_GET['id'] ?? null; if (!$id) tErr('ID manquant'); $resp = planka_api('GET', '/api/boards/' . $id, null, $token); $inc = $resp['included'] ?? []; + + // Filter lists that actually belong to this board (prevents ghost lists from other boards) + $lists = array_values(array_filter($inc['lists'] ?? [], fn($l) => + isset($l['boardId']) ? $l['boardId'] === $id : true + )); + // Filter cards that belong to a list in this board + $listIds = array_column($lists, 'id'); + $cards = array_values(array_filter($inc['cards'] ?? [], fn($c) => + in_array($c['listId'] ?? '', $listIds) + )); + tOk([ - 'lists' => $inc['lists'] ?? [], - 'cards' => $inc['cards'] ?? [], - 'labels' => $inc['labels'] ?? [], - 'members' => $inc['users'] ?? [], + 'lists' => $lists, + 'cards' => $cards, + 'labels' => $inc['labels'] ?? [], + 'members' => $inc['users'] ?? [], 'cardLabels' => $inc['cardLabels'] ?? [], - 'tasks' => $inc['tasks'] ?? [], + 'tasks' => $inc['tasks'] ?? [], ]); } diff --git a/modules/planka/assets/planka.css b/modules/planka/assets/planka.css index bf87f90..0e418f4 100644 --- a/modules/planka/assets/planka.css +++ b/modules/planka/assets/planka.css @@ -444,3 +444,22 @@ .pk-page { height: auto; overflow: visible; } .pk-board-area { overflow-x: auto; height: auto; padding-bottom: 4rem; } } + +/* ── Drag & Drop amélioré ──────────────────────────────────────────────────── */ +.pk-cards.drag-over { + background: rgba(59,130,246,.07); + outline: 2px dashed var(--primary, #4361ee); + outline-offset: -2px; + border-radius: 8px; + min-height: 80px; +} +.pk-card.is-dragging { opacity: .4; } +.pk-drop-hint { + text-align: center; + color: var(--text-muted, #9ca3af); + font-size: .78rem; + padding: .75rem; + border: 2px dashed var(--border-light, #e5e7eb); + border-radius: 8px; + margin-bottom: .35rem; +} diff --git a/planka.php b/planka.php index 4a01f1a..55fb509 100644 --- a/planka.php +++ b/planka.php @@ -151,12 +151,13 @@ function renderBoard() { area.innerHTML = sorted.map(list => renderList(list)).join(''); area.querySelectorAll('.pk-card').forEach(el => { el.addEventListener('click', () => openCardModal(el.dataset.id)); - el.addEventListener('dragstart', e => { dragCardId = el.dataset.id; el.classList.add('is-dragging'); }); + el.addEventListener('dragstart', e => { dragCardId = el.dataset.id; el.classList.add('is-dragging'); e.stopPropagation(); }); el.addEventListener('dragend', e => { el.classList.remove('is-dragging'); dragCardId = null; }); }); - area.querySelectorAll('.pk-drop-zone').forEach(el => { + // Drop target = toute la colonne .pk-cards (pas juste une zone invisible de 4px) + area.querySelectorAll('.pk-cards').forEach(el => { el.addEventListener('dragover', e => { e.preventDefault(); el.classList.add('drag-over'); }); - el.addEventListener('dragleave', () => el.classList.remove('drag-over')); + el.addEventListener('dragleave', e => { if (!el.contains(e.relatedTarget)) el.classList.remove('drag-over'); }); el.addEventListener('drop', e => { e.preventDefault(); el.classList.remove('drag-over'); dropCard(el.dataset.listId); }); }); } @@ -169,8 +170,8 @@ function renderList(list) { ${esc(list.name)} ${cards.length} -
-
+
+ ${cards.length === 0 ? '
Déposer ici
' : ''} ${cards.map(c => renderCard(c)).join('')}