From 8214b2e9ac24a97b808ef3f59be142db4612d601 Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Tue, 26 May 2026 21:01:00 +0200 Subject: [PATCH] Implement task edit (inline with double click or edit button) and task delete features --- widget/src/app.js | 118 +++++++++++++++++++++++++++++++++++++++++++ widget/src/style.css | 55 ++++++++++++++++++++ 2 files changed, 173 insertions(+) diff --git a/widget/src/app.js b/widget/src/app.js index 960568f..2a0f8ed 100644 --- a/widget/src/app.js +++ b/widget/src/app.js @@ -294,6 +294,39 @@ function renderTasksList() { content.appendChild(title); content.appendChild(date); + // Container d'actions (Edit / Delete) + const actions = document.createElement("div"); + actions.className = "task-actions"; + + // Bouton Modifier + const btnEdit = document.createElement("button"); + btnEdit.className = "btn-task-action btn-edit"; + btnEdit.title = "Modifier la tâche"; + btnEdit.innerHTML = ``; + btnEdit.addEventListener("click", (e) => { + e.stopPropagation(); + startEditingTask(task.id, card, title, task.title); + }); + + // Double-clic sur le titre pour éditer également + title.addEventListener("dblclick", (e) => { + e.stopPropagation(); + startEditingTask(task.id, card, title, task.title); + }); + + // Bouton Supprimer + const btnDelete = document.createElement("button"); + btnDelete.className = "btn-task-action btn-delete"; + btnDelete.title = "Supprimer la tâche"; + btnDelete.innerHTML = ``; + btnDelete.addEventListener("click", (e) => { + e.stopPropagation(); + deleteTask(task.id); + }); + + actions.appendChild(btnEdit); + actions.appendChild(btnDelete); + // Badge de statut (En cours / À faire) const badge = document.createElement("span"); @@ -314,6 +347,7 @@ function renderTasksList() { card.appendChild(checkbox); card.appendChild(content); + card.appendChild(actions); card.appendChild(badge); tasksList.appendChild(card); @@ -337,3 +371,87 @@ function formatDate(dateString) { return dateString; } } + +// --- NOUVELLES FONCTIONS DE SUPPRESSION ET MODIFICATION --- + +function startEditingTask(taskId, card, titleElement, currentTitle) { + if (card.classList.contains("editing")) return; + card.classList.add("editing"); + + const input = document.createElement("input"); + input.type = "text"; + input.className = "task-edit-input"; + input.value = currentTitle; + input.maxLength = 100; + + titleElement.innerHTML = ""; + titleElement.appendChild(input); + input.focus(); + input.select(); + + const finishEditing = async (save) => { + const newTitle = input.value.trim(); + card.classList.remove("editing"); + + if (save && newTitle && newTitle !== currentTitle) { + titleElement.textContent = newTitle; + await updateTaskTitle(taskId, newTitle); + } else { + titleElement.textContent = currentTitle; + } + }; + + input.addEventListener("keydown", (e) => { + if (e.key === "Enter") { + finishEditing(true); + } else if (e.key === "Escape") { + finishEditing(false); + } + }); + + input.addEventListener("blur", () => { + finishEditing(true); + }); + + input.addEventListener("click", (e) => e.stopPropagation()); +} + +async function updateTaskTitle(taskId, newTitle) { + try { + const response = await fetch(`${state.apiUrl}/api/v1/tasks/${taskId}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + "X-API-Key": state.apiKey + }, + body: JSON.stringify({ + title: newTitle + }) + }); + + if (!response.ok) throw new Error("Échec de la mise à jour"); + fetchTasks(); + } catch (error) { + console.error("Erreur de mise à jour du titre :", error); + alert("Impossible de modifier la tâche."); + } +} + +async function deleteTask(taskId) { + if (!confirm("Voulez-vous vraiment supprimer cette tâche ?")) return; + + try { + const response = await fetch(`${state.apiUrl}/api/v1/tasks/${taskId}`, { + method: "DELETE", + headers: { + "X-API-Key": state.apiKey + } + }); + + if (!response.ok) throw new Error("Échec de la suppression"); + fetchTasks(); + } catch (error) { + console.error("Erreur de suppression de la tâche :", error); + alert("Impossible de supprimer la tâche."); + } +} diff --git a/widget/src/style.css b/widget/src/style.css index 72c20cd..93c9327 100644 --- a/widget/src/style.css +++ b/widget/src/style.css @@ -573,3 +573,58 @@ body { background: hsl(250, 85%, 58%); box-shadow: 0 0 10px var(--accent-indigo-glow); } + +/* --- BOUTONS ACTIONS DES TÂCHES (MODIFIER / SUPPRIMER) --- */ +.task-actions { + display: flex; + gap: 6px; + margin-right: 8px; + opacity: 0; + transition: opacity 0.2s ease; +} + +.task-card:hover .task-actions { + opacity: 1; +} + +.btn-task-action { + background: transparent; + border: none; + color: var(--text-secondary); + cursor: pointer; + width: 22px; + height: 22px; + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; +} + +.btn-task-action:hover { + background: rgba(255, 255, 255, 0.08); + color: var(--text-primary); +} + +.btn-task-action.btn-delete:hover { + background: rgba(239, 68, 68, 0.15); + color: hsl(0, 80%, 65%); +} + +.btn-task-action svg { + pointer-events: none; /* Évite d'interférer avec le clic */ +} + +/* Style de l'input d'édition en ligne */ +.task-edit-input { + background: rgba(255, 255, 255, 0.1); + border: 1px solid var(--accent-indigo); + border-radius: 6px; + color: var(--text-primary); + font-size: 13px; + font-family: inherit; + padding: 2px 6px; + width: 100%; + outline: none; + box-shadow: 0 0 8px var(--accent-indigo-glow); +}