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;
|
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 */
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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 %}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue