feat: add edit note functionality and UI action buttons

This commit is contained in:
kdeterme 2026-09-25 23:36:04 +02:00
parent 205a05cb5b
commit f4460a5323
4 changed files with 130 additions and 106 deletions

View file

@ -146,14 +146,21 @@ body.cms-edit-mode .cms-modal-container {
border: 2px solid #f8e8b6; border: 2px solid #f8e8b6;
} }
/* Delete button hidden by default */ /* Note action buttons */
.delete-note-btn { .note-actions .delete-note-btn,
display: none !important; .note-actions .edit-note-btn {
opacity: 0.7;
transition: opacity 0.2s ease, transform 0.1s ease;
text-decoration: none;
cursor: pointer;
} }
/* Delete button visible in edit mode */ .note-actions .delete-note-btn:hover,
body.cms-edit-mode .delete-note-btn { .note-actions .edit-note-btn:hover,
display: block !important; body.cms-edit-mode .note-actions .delete-note-btn,
body.cms-edit-mode .note-actions .edit-note-btn {
opacity: 1;
transform: scale(1.1);
} }
/* Editable notes in CMS edit mode */ /* Editable notes in CMS edit mode */

View file

@ -5,10 +5,12 @@
'use strict'; 'use strict';
const interventionId = document.querySelector('[data-intervention-id]')?.dataset.interventionId; const interventionId = document.querySelector('[data-intervention-id]')?.dataset.interventionId;
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]')?.value; const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]')?.value
|| (typeof getCookie === 'function' ? getCookie('csrftoken') : null)
|| (typeof csrftoken !== 'undefined' ? csrftoken : null);
if (!interventionId || !csrfToken) { if (!interventionId) {
console.warn('CMS Notes: Missing intervention ID or CSRF token'); console.warn('CMS Notes: Missing intervention ID');
return; return;
} }
@ -92,47 +94,19 @@
// Store original note values for cancel // Store original note values for cancel
window.cmsNotesOriginalValues = {}; window.cmsNotesOriginalValues = {};
// Show delete buttons in edit mode function startEditingNote(noteItem) {
document.addEventListener('cms-edit-mode-enter', function () {
document.querySelectorAll('.delete-note-btn').forEach(btn => {
btn.style.display = 'block';
});
});
// Editable notes - click to edit in CMS mode
if (notesContainer) {
notesContainer.addEventListener('click', function (e) {
// Only allow editing in CMS edit mode
if (!document.body.classList.contains('cms-edit-mode')) {
return;
}
const noteItem = e.target.closest('.editable-note');
if (!noteItem) return; if (!noteItem) return;
// Don't trigger if clicking on buttons
if (e.target.closest('.delete-note-btn')) {
return;
}
// Don't toggle if already editing
if (noteItem.classList.contains('editing')) {
return;
}
// Store original values when entering edit mode
const noteId = noteItem.dataset.noteId; const noteId = noteItem.dataset.noteId;
const textarea = noteItem.querySelector('.edit-note-content'); const textarea = noteItem.querySelector('.edit-note-content');
const select = noteItem.querySelector('.edit-note-type'); const select = noteItem.querySelector('.edit-note-type');
// Force reset textarea value from data attribute to avoid browser cache
const originalContent = decodeHtmlToText(noteItem.dataset.noteContent || ''); const originalContent = decodeHtmlToText(noteItem.dataset.noteContent || '');
const originalType = noteItem.dataset.noteType || ''; const originalType = noteItem.dataset.noteType || '';
if (textarea) textarea.value = originalContent; if (textarea) textarea.value = originalContent;
if (select) select.value = originalType; if (select) select.value = originalType;
// Store these values for cancel operation
if (!window.cmsNotesOriginalValues[noteId]) { if (!window.cmsNotesOriginalValues[noteId]) {
window.cmsNotesOriginalValues[noteId] = { window.cmsNotesOriginalValues[noteId] = {
content: originalContent, content: originalContent,
@ -140,45 +114,71 @@
}; };
} }
// Enter edit mode for this note
noteItem.classList.add('editing'); noteItem.classList.add('editing');
// Focus on the textarea
if (textarea) { if (textarea) {
setTimeout(() => { setTimeout(() => {
textarea.focus(); textarea.focus();
textarea.setSelectionRange(textarea.value.length, textarea.value.length); textarea.setSelectionRange(textarea.value.length, textarea.value.length);
}, 100); }, 100);
} }
}); }
// Delete note button // Notes container click delegation (edit button, delete button, inline edit)
if (notesContainer) {
notesContainer.addEventListener('click', function (e) { notesContainer.addEventListener('click', function (e) {
// Edit note button handler
const editBtn = e.target.closest('.edit-note-btn');
if (editBtn) {
e.preventDefault();
e.stopPropagation();
const noteItem = editBtn.closest('.editable-note');
if (!noteItem) return;
if (!document.body.classList.contains('cms-edit-mode')) {
const cmsEditBtn = document.getElementById('cms-edit-btn');
if (cmsEditBtn) {
cmsEditBtn.click();
}
}
startEditingNote(noteItem);
return;
}
// Delete note button handler
const deleteBtn = e.target.closest('.delete-note-btn'); const deleteBtn = e.target.closest('.delete-note-btn');
if (!deleteBtn) return; if (deleteBtn) {
e.preventDefault();
e.stopPropagation();
const noteId = deleteBtn.dataset.noteId; const noteId = deleteBtn.dataset.noteId;
if (!noteId) return; if (!noteId) return;
if (!window.confirm('Voulez-vous vraiment supprimer cette note ?')) { const confirmMsg = typeof gettext === 'function' ? gettext('Voulez-vous vraiment supprimer cette note ?') : 'Voulez-vous vraiment supprimer cette note ?';
if (!window.confirm(confirmMsg)) {
return; return;
} }
// Disable button during delete
deleteBtn.disabled = true; deleteBtn.disabled = true;
const token = document.querySelector('[name=csrfmiddlewaretoken]')?.value
|| (typeof getCookie === 'function' ? getCookie('csrftoken') : null)
|| (typeof csrftoken !== 'undefined' ? csrftoken : null);
fetch(`/interventions/${interventionId}/notes/${noteId}/delete/`, { fetch(`/interventions/${interventionId}/notes/${noteId}/delete/`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-CSRFToken': csrfToken, 'X-CSRFToken': token,
}, },
}) })
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.success) { if (data.success) {
showAlert('✅ Note supprimée avec succès !', 'success'); const successMsg = typeof gettext === 'function' ? gettext('Note supprimée avec succès !') : 'Note supprimée avec succès !';
showAlert('✅ ' + successMsg, 'success');
// Remove note from DOM
const noteItem = document.getElementById(`note-container-${noteId}`); const noteItem = document.getElementById(`note-container-${noteId}`);
if (noteItem) { if (noteItem) {
noteItem.remove(); noteItem.remove();
@ -190,18 +190,32 @@
}) })
.catch(error => { .catch(error => {
console.error('Error deleting note:', error); console.error('Error deleting note:', error);
showAlert('❌ Une erreur est survenue lors de la suppression.', 'danger'); const errorMsg = typeof gettext === 'function' ? gettext('Une erreur est survenue lors de la suppression.') : 'Une erreur est survenue lors de la suppression.';
showAlert('❌ ' + errorMsg, 'danger');
deleteBtn.disabled = false; deleteBtn.disabled = false;
}); });
return;
}
// Click anywhere on note in CMS edit mode to edit
if (!document.body.classList.contains('cms-edit-mode')) {
return;
}
const noteItem = e.target.closest('.editable-note');
if (!noteItem) return;
// Don't toggle if already editing
if (noteItem.classList.contains('editing')) {
return;
}
startEditingNote(noteItem);
}); });
} }
// Hide new note editor and restore original values when exiting edit mode // Hide new note editor and restore original values when exiting edit mode
document.addEventListener('cms-edit-mode-exit', function () { document.addEventListener('cms-edit-mode-exit', function () {
// Hide delete buttons
document.querySelectorAll('.delete-note-btn').forEach(btn => {
btn.style.display = 'none';
});
// Hide and reset new note editor // Hide and reset new note editor
if (newNoteEditor) { if (newNoteEditor) {

View file

@ -63,8 +63,11 @@
<p class="mb-0 note-text">{{ note.content|safe|linebreaksbr }}</p> <p class="mb-0 note-text">{{ note.content|safe|linebreaksbr }}</p>
</div> </div>
{% if note.id in editable_note_ids %} {% if note.id in editable_note_ids %}
<div class="note-actions ms-auto"> <div class="note-actions ms-auto d-flex align-items-center gap-2 flex-shrink-0">
<button type="button" class="btn btn-link text-danger p-0 delete-note-btn" data-note-id="{{ note.id }}" aria-label="{% translate "Supprimer la note" %}" style="display: none;"> <button type="button" class="btn btn-link text-primary p-0 edit-note-btn" data-note-id="{{ note.id }}" title="{% translate 'Modifier la note' %}" aria-label="{% translate 'Modifier la note' %}">
<i class="bi bi-pencil fs-6"></i>
</button>
<button type="button" class="btn btn-link text-danger p-0 delete-note-btn" data-note-id="{{ note.id }}" title="{% translate 'Supprimer la note' %}" aria-label="{% translate 'Supprimer la note' %}">
<i class="bi bi-trash fs-6"></i> <i class="bi bi-trash fs-6"></i>
</button> </button>
</div> </div>

View file

@ -122,12 +122,12 @@
<p class="mb-0">{{ note.content|linebreaksbr }}</p> <p class="mb-0">{{ note.content|linebreaksbr }}</p>
</div> </div>
{% if note.id in editable_note_ids %} {% if note.id in editable_note_ids %}
<div class="note-actions cms-note-actions" style="display: none;"> <div class="note-actions ms-auto d-flex align-items-center gap-2 flex-shrink-0">
<button type="button" class="btn btn-sm btn-outline-primary btn-edit-note me-1" title="{% translate 'Modifier la note' %}"> <button type="button" class="btn btn-link text-primary p-0 edit-note-btn" data-note-id="{{ note.id }}" title="{% translate 'Modifier la note' %}" aria-label="{% translate 'Modifier la note' %}">
<i class="bi bi-pencil"></i> <i class="bi bi-pencil fs-6"></i>
</button> </button>
<button type="button" class="btn btn-sm btn-outline-danger btn-delete-note" title="{% translate 'Supprimer la note' %}"> <button type="button" class="btn btn-link text-danger p-0 delete-note-btn" data-note-id="{{ note.id }}" title="{% translate 'Supprimer la note' %}" aria-label="{% translate 'Supprimer la note' %}">
<i class="bi bi-trash"></i> <i class="bi bi-trash fs-6"></i>
</button> </button>
</div> </div>
{% endif %} {% endif %}