mirror of
https://github.com/R0m1k3/noteflow.git
synced 2026-10-11 17:29:37 +02:00
Fix: Suppression des gestionnaires inline pour conformité CSP
- Remplacement de tous les onclick/onchange par event delegation - Utilisation de data attributes (data-todo-id, data-note-todo-id, data-user-id) - Event listeners attachés aux conteneurs (#todoList, #noteTodosList, #usersTableBody) - Plus besoin d'exposer les fonctions sur window - Conforme à la Content Security Policy de Helmet.js - Résout: 'script-src-attr' violations bloquant les actions de la todo list
This commit is contained in:
1 file changed
+59
-14
+59
-14
@@ -420,10 +420,9 @@ function renderNoteTodos(todos) {
|
|||||||
const item = document.createElement('div');
|
const item = document.createElement('div');
|
||||||
item.className = 'todo-item';
|
item.className = 'todo-item';
|
||||||
item.innerHTML = `
|
item.innerHTML = `
|
||||||
<input type="checkbox" class="todo-checkbox" ${todo.completed ? 'checked' : ''}
|
<input type="checkbox" class="todo-checkbox" data-note-todo-id="${todo.id}" ${todo.completed ? 'checked' : ''}>
|
||||||
onchange="toggleNoteTodo(${todo.id}, this.checked)">
|
|
||||||
<span class="todo-text">${escapeHtml(todo.text)}</span>
|
<span class="todo-text">${escapeHtml(todo.text)}</span>
|
||||||
<button class="todo-delete" onclick="deleteNoteTodo(${todo.id})">✕</button>
|
<button class="todo-delete" data-note-todo-id="${todo.id}">✕</button>
|
||||||
`;
|
`;
|
||||||
if (todo.completed) item.classList.add('completed');
|
if (todo.completed) item.classList.add('completed');
|
||||||
list.appendChild(item);
|
list.appendChild(item);
|
||||||
@@ -573,10 +572,9 @@ function renderTodos() {
|
|||||||
const item = document.createElement('div');
|
const item = document.createElement('div');
|
||||||
item.className = 'todo-item' + (todo.completed ? ' completed' : '');
|
item.className = 'todo-item' + (todo.completed ? ' completed' : '');
|
||||||
item.innerHTML = `
|
item.innerHTML = `
|
||||||
<input type="checkbox" class="todo-checkbox" ${todo.completed ? 'checked' : ''}
|
<input type="checkbox" class="todo-checkbox" data-todo-id="${todo.id}" ${todo.completed ? 'checked' : ''}>
|
||||||
onchange="toggleTodo(${todo.id}, this.checked)">
|
|
||||||
<span class="todo-text">${escapeHtml(todo.text)}</span>
|
<span class="todo-text">${escapeHtml(todo.text)}</span>
|
||||||
<button class="todo-delete" onclick="deleteTodo(${todo.id})">✕</button>
|
<button class="todo-delete" data-todo-id="${todo.id}">✕</button>
|
||||||
`;
|
`;
|
||||||
todoList.appendChild(item);
|
todoList.appendChild(item);
|
||||||
});
|
});
|
||||||
@@ -599,17 +597,16 @@ async function addTodo() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exposer globalement pour les event handlers inline
|
async function toggleTodo(id, completed) {
|
||||||
window.toggleTodo = async function(id, completed) {
|
|
||||||
try {
|
try {
|
||||||
await api.put(`/api/todos/${id}`, { completed });
|
await api.put(`/api/todos/${id}`, { completed });
|
||||||
await loadTodos();
|
await loadTodos();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erreur toggle todo:', error);
|
console.error('Erreur toggle todo:', error);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
window.deleteTodo = async function(id) {
|
async function deleteTodo(id) {
|
||||||
const confirmed = await confirmDialog.show({
|
const confirmed = await confirmDialog.show({
|
||||||
icon: '🗑️',
|
icon: '🗑️',
|
||||||
title: 'Supprimer cette tâche',
|
title: 'Supprimer cette tâche',
|
||||||
@@ -625,7 +622,7 @@ window.deleteTodo = async function(id) {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erreur suppression todo:', error);
|
console.error('Erreur suppression todo:', error);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
function setTodoFilter(filter) {
|
function setTodoFilter(filter) {
|
||||||
state.filter = filter;
|
state.filter = filter;
|
||||||
@@ -696,7 +693,7 @@ function renderUsersTable(users) {
|
|||||||
<td>${new Date(user.created_at).toLocaleDateString('fr-FR')}</td>
|
<td>${new Date(user.created_at).toLocaleDateString('fr-FR')}</td>
|
||||||
<td>
|
<td>
|
||||||
${user.id !== state.user.id ? `
|
${user.id !== state.user.id ? `
|
||||||
<button class="btn-delete-user" onclick="deleteUser(${user.id}, '${user.username}')">
|
<button class="btn-delete-user" data-user-id="${user.id}" data-username="${escapeHtml(user.username)}">
|
||||||
🗑️ Supprimer
|
🗑️ Supprimer
|
||||||
</button>
|
</button>
|
||||||
` : '<span style="color: var(--text-secondary);">Vous-même</span>'}
|
` : '<span style="color: var(--text-secondary);">Vous-même</span>'}
|
||||||
@@ -759,8 +756,7 @@ async function createUser() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fonction globale pour la suppression d'utilisateur (appelée depuis onclick)
|
async function deleteUser(id, username) {
|
||||||
window.deleteUser = async function(id, username) {
|
|
||||||
const confirmed = await confirmDialog.show({
|
const confirmed = await confirmDialog.show({
|
||||||
icon: '⚠️',
|
icon: '⚠️',
|
||||||
title: 'Supprimer cet utilisateur',
|
title: 'Supprimer cet utilisateur',
|
||||||
@@ -867,6 +863,55 @@ async function init() {
|
|||||||
btn.addEventListener('click', () => setTodoFilter(btn.dataset.filter));
|
btn.addEventListener('click', () => setTodoFilter(btn.dataset.filter));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Event delegation pour les todos de la sidebar
|
||||||
|
const todoList = document.getElementById('todoList');
|
||||||
|
if (todoList) {
|
||||||
|
todoList.addEventListener('change', (e) => {
|
||||||
|
if (e.target.classList.contains('todo-checkbox')) {
|
||||||
|
const todoId = parseInt(e.target.dataset.todoId);
|
||||||
|
toggleTodo(todoId, e.target.checked);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
todoList.addEventListener('click', (e) => {
|
||||||
|
if (e.target.classList.contains('todo-delete')) {
|
||||||
|
const todoId = parseInt(e.target.dataset.todoId);
|
||||||
|
deleteTodo(todoId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event delegation pour les todos des notes
|
||||||
|
const noteTodosList = document.getElementById('noteTodosList');
|
||||||
|
if (noteTodosList) {
|
||||||
|
noteTodosList.addEventListener('change', (e) => {
|
||||||
|
if (e.target.classList.contains('todo-checkbox')) {
|
||||||
|
const todoId = parseInt(e.target.dataset.noteTodoId);
|
||||||
|
toggleNoteTodo(todoId, e.target.checked);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
noteTodosList.addEventListener('click', (e) => {
|
||||||
|
if (e.target.classList.contains('todo-delete')) {
|
||||||
|
const todoId = parseInt(e.target.dataset.noteTodoId);
|
||||||
|
deleteNoteTodo(todoId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event delegation pour les boutons de suppression d'utilisateurs
|
||||||
|
const usersTableBody = document.getElementById('usersTableBody');
|
||||||
|
if (usersTableBody) {
|
||||||
|
usersTableBody.addEventListener('click', (e) => {
|
||||||
|
if (e.target.classList.contains('btn-delete-user') || e.target.closest('.btn-delete-user')) {
|
||||||
|
const btn = e.target.classList.contains('btn-delete-user') ? e.target : e.target.closest('.btn-delete-user');
|
||||||
|
const userId = parseInt(btn.dataset.userId);
|
||||||
|
const username = btn.dataset.username;
|
||||||
|
deleteUser(userId, username);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Close modal on backdrop click
|
// Close modal on backdrop click
|
||||||
const noteModal = document.getElementById('noteModal');
|
const noteModal = document.getElementById('noteModal');
|
||||||
if (noteModal) {
|
if (noteModal) {
|
||||||
|
|||||||
Reference in new issue
Block a user