feat: add edit note functionality and UI action buttons
This commit is contained in:
parent
205a05cb5b
commit
f4460a5323
4 changed files with 130 additions and 106 deletions
|
|
@ -146,14 +146,21 @@ body.cms-edit-mode .cms-modal-container {
|
|||
border: 2px solid #f8e8b6;
|
||||
}
|
||||
|
||||
/* Delete button hidden by default */
|
||||
.delete-note-btn {
|
||||
display: none !important;
|
||||
/* Note action buttons */
|
||||
.note-actions .delete-note-btn,
|
||||
.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 */
|
||||
body.cms-edit-mode .delete-note-btn {
|
||||
display: block !important;
|
||||
.note-actions .delete-note-btn:hover,
|
||||
.note-actions .edit-note-btn:hover,
|
||||
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 */
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@
|
|||
'use strict';
|
||||
|
||||
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) {
|
||||
console.warn('CMS Notes: Missing intervention ID or CSRF token');
|
||||
if (!interventionId) {
|
||||
console.warn('CMS Notes: Missing intervention ID');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -92,17 +94,110 @@
|
|||
// Store original note values for cancel
|
||||
window.cmsNotesOriginalValues = {};
|
||||
|
||||
// Show delete buttons in edit mode
|
||||
document.addEventListener('cms-edit-mode-enter', function () {
|
||||
document.querySelectorAll('.delete-note-btn').forEach(btn => {
|
||||
btn.style.display = 'block';
|
||||
});
|
||||
});
|
||||
function startEditingNote(noteItem) {
|
||||
if (!noteItem) return;
|
||||
|
||||
// Editable notes - click to edit in CMS mode
|
||||
const noteId = noteItem.dataset.noteId;
|
||||
const textarea = noteItem.querySelector('.edit-note-content');
|
||||
const select = noteItem.querySelector('.edit-note-type');
|
||||
|
||||
const originalContent = decodeHtmlToText(noteItem.dataset.noteContent || '');
|
||||
const originalType = noteItem.dataset.noteType || '';
|
||||
|
||||
if (textarea) textarea.value = originalContent;
|
||||
if (select) select.value = originalType;
|
||||
|
||||
if (!window.cmsNotesOriginalValues[noteId]) {
|
||||
window.cmsNotesOriginalValues[noteId] = {
|
||||
content: originalContent,
|
||||
type: originalType
|
||||
};
|
||||
}
|
||||
|
||||
noteItem.classList.add('editing');
|
||||
|
||||
if (textarea) {
|
||||
setTimeout(() => {
|
||||
textarea.focus();
|
||||
textarea.setSelectionRange(textarea.value.length, textarea.value.length);
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
// Notes container click delegation (edit button, delete button, inline edit)
|
||||
if (notesContainer) {
|
||||
notesContainer.addEventListener('click', function (e) {
|
||||
// Only allow editing in CMS edit mode
|
||||
// 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');
|
||||
if (deleteBtn) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const noteId = deleteBtn.dataset.noteId;
|
||||
if (!noteId) return;
|
||||
|
||||
const confirmMsg = typeof gettext === 'function' ? gettext('Voulez-vous vraiment supprimer cette note ?') : 'Voulez-vous vraiment supprimer cette note ?';
|
||||
if (!window.confirm(confirmMsg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRFToken': token,
|
||||
},
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const successMsg = typeof gettext === 'function' ? gettext('Note supprimée avec succès !') : 'Note supprimée avec succès !';
|
||||
showAlert('✅ ' + successMsg, 'success');
|
||||
|
||||
const noteItem = document.getElementById(`note-container-${noteId}`);
|
||||
if (noteItem) {
|
||||
noteItem.remove();
|
||||
}
|
||||
} else {
|
||||
showAlert(`❌ ${data.error || 'Une erreur est survenue.'}`, 'danger');
|
||||
deleteBtn.disabled = false;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error deleting note:', error);
|
||||
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;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Click anywhere on note in CMS edit mode to edit
|
||||
if (!document.body.classList.contains('cms-edit-mode')) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -110,98 +205,17 @@
|
|||
const noteItem = e.target.closest('.editable-note');
|
||||
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 textarea = noteItem.querySelector('.edit-note-content');
|
||||
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 originalType = noteItem.dataset.noteType || '';
|
||||
|
||||
if (textarea) textarea.value = originalContent;
|
||||
if (select) select.value = originalType;
|
||||
|
||||
// Store these values for cancel operation
|
||||
if (!window.cmsNotesOriginalValues[noteId]) {
|
||||
window.cmsNotesOriginalValues[noteId] = {
|
||||
content: originalContent,
|
||||
type: originalType
|
||||
};
|
||||
}
|
||||
|
||||
// Enter edit mode for this note
|
||||
noteItem.classList.add('editing');
|
||||
|
||||
// Focus on the textarea
|
||||
if (textarea) {
|
||||
setTimeout(() => {
|
||||
textarea.focus();
|
||||
textarea.setSelectionRange(textarea.value.length, textarea.value.length);
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
// Delete note button
|
||||
notesContainer.addEventListener('click', function (e) {
|
||||
const deleteBtn = e.target.closest('.delete-note-btn');
|
||||
if (!deleteBtn) return;
|
||||
|
||||
const noteId = deleteBtn.dataset.noteId;
|
||||
if (!noteId) return;
|
||||
|
||||
if (!window.confirm('Voulez-vous vraiment supprimer cette note ?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable button during delete
|
||||
deleteBtn.disabled = true;
|
||||
|
||||
fetch(`/interventions/${interventionId}/notes/${noteId}/delete/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken,
|
||||
},
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showAlert('✅ Note supprimée avec succès !', 'success');
|
||||
|
||||
// Remove note from DOM
|
||||
const noteItem = document.getElementById(`note-container-${noteId}`);
|
||||
if (noteItem) {
|
||||
noteItem.remove();
|
||||
}
|
||||
} else {
|
||||
showAlert(`❌ ${data.error || 'Une erreur est survenue.'}`, 'danger');
|
||||
deleteBtn.disabled = false;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error deleting note:', error);
|
||||
showAlert('❌ Une erreur est survenue lors de la suppression.', 'danger');
|
||||
deleteBtn.disabled = false;
|
||||
});
|
||||
startEditingNote(noteItem);
|
||||
});
|
||||
}
|
||||
|
||||
// Hide new note editor and restore original values when exiting edit mode
|
||||
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
|
||||
if (newNoteEditor) {
|
||||
|
|
|
|||
|
|
@ -63,8 +63,11 @@
|
|||
<p class="mb-0 note-text">{{ note.content|safe|linebreaksbr }}</p>
|
||||
</div>
|
||||
{% if note.id in editable_note_ids %}
|
||||
<div class="note-actions ms-auto">
|
||||
<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;">
|
||||
<div class="note-actions ms-auto d-flex align-items-center gap-2 flex-shrink-0">
|
||||
<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>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -122,12 +122,12 @@
|
|||
<p class="mb-0">{{ note.content|linebreaksbr }}</p>
|
||||
</div>
|
||||
{% if note.id in editable_note_ids %}
|
||||
<div class="note-actions cms-note-actions" style="display: none;">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary btn-edit-note me-1" title="{% translate 'Modifier la note' %}">
|
||||
<i class="bi bi-pencil"></i>
|
||||
<div class="note-actions ms-auto d-flex align-items-center gap-2 flex-shrink-0">
|
||||
<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-sm btn-outline-danger btn-delete-note" title="{% translate 'Supprimer la note' %}">
|
||||
<i class="bi bi-trash"></i>
|
||||
<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>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
|||
Loading…
Reference in a new issue