From 11dca3824c202daa7d53bcf5587419d394a32224 Mon Sep 17 00:00:00 2001 From: "[dyad]" Date: Thu, 6 Nov 2025 16:01:23 +0100 Subject: [PATCH] [dyad] wrote 4 file(s) --- public/js/admin.js | 45 ++++++++++++++++------ public/js/app.js | 13 +++++-- public/js/auth.js | 22 ++++++----- public/js/notes.js | 93 ++++++++++++++++++++++++++++++++-------------- 4 files changed, 121 insertions(+), 52 deletions(-) diff --git a/public/js/admin.js b/public/js/admin.js index a19e458..a7b5aaf 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -1,21 +1,30 @@ class AdminService { constructor() { - if (!window.auth.user?.is_admin) return; + // Check if auth exists and user is admin + if (!window.auth || !window.auth.user || !window.auth.user.is_admin) return; this.adminPanel = document.getElementById('admin-panel'); this.closeAdminBtn = document.getElementById('close-admin-btn'); this.addUserBtn = document.getElementById('add-user-btn'); this.usersList = document.getElementById('users-list'); - this.setupEventListeners(); + if (this.adminPanel && this.closeAdminBtn && this.addUserBtn && this.usersList) { + this.setupEventListeners(); + } } setupEventListeners() { - this.closeAdminBtn.addEventListener('click', () => this.closePanel()); - this.addUserBtn.addEventListener('click', () => this.showAddUserForm()); + if (this.closeAdminBtn) { + this.closeAdminBtn.addEventListener('click', () => this.closePanel()); + } + if (this.addUserBtn) { + this.addUserBtn.addEventListener('click', () => this.showAddUserForm()); + } } async loadUsers() { + if (!window.auth || !window.auth.getHeaders) return; + try { const response = await fetch('/api/users', { headers: window.auth.getHeaders() @@ -28,24 +37,28 @@ class AdminService { } renderUsersList(users) { + if (!this.usersList || !Array.isArray(users)) return; + this.usersList.innerHTML = ''; users.forEach(user => { + if (!user || typeof user !== 'object') return; + const userElement = document.createElement('div'); userElement.className = 'user-item'; userElement.innerHTML = `
- ${user.username} + ${user.username || 'Utilisateur'} ${user.is_admin ? 'Admin' : ''}
${user.id !== 1 ? ` - - - ` : ''} @@ -68,6 +81,8 @@ class AdminService { } async createUser(username, password, isAdmin) { + if (!window.auth || !window.auth.getHeaders) return; + try { const response = await fetch('/api/users', { method: 'POST', @@ -77,16 +92,18 @@ class AdminService { if (!response.ok) { const error = await response.json(); - throw new Error(error.message); + throw new Error(error.message || 'Erreur lors de la création de l\'utilisateur'); } this.loadUsers(); } catch (error) { - alert(error.message); + alert(error.message || 'Erreur lors de la création de l\'utilisateur'); } } async changePassword(userId) { + if (!window.auth || !window.auth.getHeaders) return; + const password = prompt('Nouveau mot de passe:'); if (!password) return; @@ -103,6 +120,8 @@ class AdminService { } async toggleAdmin(userId, currentStatus) { + if (!window.auth || !window.auth.getHeaders) return; + try { await fetch(`/api/users/${userId}`, { method: 'PUT', @@ -116,6 +135,8 @@ class AdminService { } async deleteUser(userId) { + if (!window.auth || !window.auth.getHeaders) return; + if (!confirm('Êtes-vous sûr de vouloir supprimer cet utilisateur ?')) return; try { @@ -130,7 +151,9 @@ class AdminService { } closePanel() { - this.adminPanel.classList.add('hidden'); + if (this.adminPanel) { + this.adminPanel.classList.add('hidden'); + } } } diff --git a/public/js/app.js b/public/js/app.js index f1fa2f4..390f39f 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -8,9 +8,12 @@ class App { init() { // Load initial data if authenticated - if (this.auth.token) { - this.notes.loadNotes(); - if (this.auth.user && this.auth.user.is_admin) { + if (this.auth && this.auth.token) { + if (this.notes && typeof this.notes.loadNotes === 'function') { + this.notes.loadNotes(); + } + + if (this.auth.user && this.auth.user.is_admin && this.admin && typeof this.admin.loadUsers === 'function') { this.admin.loadUsers(); } } @@ -22,5 +25,7 @@ window.app = new App(); // Initialize app when DOM is loaded document.addEventListener('DOMContentLoaded', () => { - window.app.init(); + if (window.app && typeof window.app.init === 'function') { + window.app.init(); + } }); \ No newline at end of file diff --git a/public/js/auth.js b/public/js/auth.js index a082787..afac4c3 100644 --- a/public/js/auth.js +++ b/public/js/auth.js @@ -43,7 +43,9 @@ class AuthService { localStorage.setItem('user', JSON.stringify(this.user)); this.updateUI(); - window.app.init(); // Initialize main app after login + if (window.app && typeof window.app.init === 'function') { + window.app.init(); // Initialize main app after login + } } catch (error) { alert(error.message); } @@ -64,30 +66,32 @@ class AuthService { const adminPanel = document.getElementById('admin-panel'); if (this.token && this.user) { - authContainer.classList.add('hidden'); - appContainer.classList.remove('hidden'); - this.userInfo.textContent = this.user.username; + if (authContainer) authContainer.classList.add('hidden'); + if (appContainer) appContainer.classList.remove('hidden'); + if (this.userInfo) this.userInfo.textContent = this.user.username; // Show admin panel button if user is admin if (this.user.is_admin) { const adminBtnExists = document.querySelector('.admin-btn'); - if (!adminBtnExists) { + if (!adminBtnExists && this.userInfo && this.userInfo.parentNode) { const adminBtn = document.createElement('button'); adminBtn.className = 'admin-btn ml-4 text-sm text-gray-600 hover:text-gray-900'; adminBtn.textContent = 'Admin'; - adminBtn.onclick = () => adminPanel.classList.remove('hidden'); + adminBtn.onclick = () => { + if (adminPanel) adminPanel.classList.remove('hidden'); + }; this.userInfo.parentNode.insertBefore(adminBtn, this.logoutBtn); } } } else { - authContainer.classList.remove('hidden'); - appContainer.classList.add('hidden'); + if (authContainer) authContainer.classList.remove('hidden'); + if (appContainer) appContainer.classList.add('hidden'); } } getHeaders() { return { - 'Authorization': `Bearer ${this.token}`, + 'Authorization': `Bearer ${this.token || ''}`, 'Content-Type': 'application/json' }; } diff --git a/public/js/notes.js b/public/js/notes.js index 87b4ec6..eac8488 100644 --- a/public/js/notes.js +++ b/public/js/notes.js @@ -50,7 +50,7 @@ class NotesService { } async loadNotes() { - if (!window.auth.token) { + if (!window.auth || !window.auth.token) { console.log('Not authenticated, skipping notes loading'); return; } @@ -83,14 +83,16 @@ class NotesService { const filteredNotes = searchTerm ? this.notes.filter(note => - note.title.toLowerCase().includes(searchTerm.toLowerCase()) || + (note.title && note.title.toLowerCase().includes(searchTerm.toLowerCase())) || (note.content && note.content.toLowerCase().includes(searchTerm.toLowerCase())) ) : this.notes; filteredNotes.forEach(note => { + if (!note) return; + const noteElement = document.createElement('div'); - noteElement.className = `note-item ${note.id === this.currentNote?.id ? 'active' : ''}`; + noteElement.className = `note-item ${note.id === (this.currentNote && this.currentNote.id) ? 'active' : ''}`; noteElement.innerHTML = `

${note.title || 'Sans titre'}

${note.content || ''}

@@ -101,6 +103,8 @@ class NotesService { } selectNote(note) { + if (!note) return; + this.currentNote = note; if (this.noteTitle) this.noteTitle.value = note.title || ''; if (this.noteContent) this.noteContent.innerHTML = note.content || ''; @@ -110,35 +114,47 @@ class NotesService { } renderTodos() { - if (!this.todosList || !this.currentNote || !this.currentNote.todos) return; + if (!this.todosList || !this.currentNote) return; + + const todos = this.currentNote.todos || []; this.todosList.innerHTML = ''; - this.currentNote.todos.forEach((todo, index) => { + todos.forEach((todo, index) => { + if (!todo) return; + const todoElement = document.createElement('div'); todoElement.className = `todo-item ${todo.completed ? 'completed' : ''}`; todoElement.innerHTML = ` - ${todo.text} + ${todo.text || ''} `; const checkbox = todoElement.querySelector('input'); - checkbox.addEventListener('change', () => this.toggleTodo(index)); + if (checkbox) { + checkbox.addEventListener('change', () => this.toggleTodo(index)); + } const deleteBtn = todoElement.querySelector('button'); - deleteBtn.addEventListener('click', () => this.deleteTodo(index)); + if (deleteBtn) { + deleteBtn.addEventListener('click', () => this.deleteTodo(index)); + } this.todosList.appendChild(todoElement); }); } renderImages() { - if (!this.imagesList || !this.currentNote || !this.currentNote.images) return; + if (!this.imagesList || !this.currentNote) return; + + const images = this.currentNote.images || []; this.imagesList.innerHTML = ''; - this.currentNote.images.forEach((image) => { + images.forEach((image, index) => { + if (!image || !image.filename) return; + const imageElement = document.createElement('div'); imageElement.className = 'image-thumbnail'; imageElement.innerHTML = ` @@ -149,13 +165,17 @@ class NotesService { `; const deleteBtn = imageElement.querySelector('button'); - deleteBtn.addEventListener('click', () => this.deleteImage(image.id)); + if (deleteBtn) { + deleteBtn.addEventListener('click', () => this.deleteImage(image.id)); + } this.imagesList.appendChild(imageElement); }); } async createNewNote() { + if (!window.auth || !window.auth.getHeaders) return; + try { const response = await fetch('/api/notes', { method: 'POST', @@ -168,33 +188,37 @@ class NotesService { }); const newNote = await response.json(); - this.notes.unshift(newNote); - this.renderNotesList(); - this.selectNote(newNote); + if (newNote) { + this.notes.unshift(newNote); + this.renderNotesList(); + this.selectNote(newNote); + } } catch (error) { console.error('Error creating note:', error); } } async autoSave() { - if (!this.currentNote) return; + if (!this.currentNote || !window.auth || !window.auth.getHeaders) return; clearTimeout(this.saveTimeout); this.saveTimeout = setTimeout(async () => { try { + if (!this.noteTitle || !this.noteContent) return; + await fetch(`/api/notes/${this.currentNote.id}`, { method: 'PUT', headers: window.auth.getHeaders(), body: JSON.stringify({ - title: this.noteTitle.value, - content: this.noteContent.innerHTML, - todos: this.currentNote.todos + title: this.noteTitle.value || '', + content: this.noteContent.innerHTML || '', + todos: this.currentNote.todos || [] }) }); // Update local note - this.currentNote.title = this.noteTitle.value; - this.currentNote.content = this.noteContent.innerHTML; + this.currentNote.title = this.noteTitle.value || ''; + this.currentNote.content = this.noteContent.innerHTML || ''; this.renderNotesList(); } catch (error) { console.error('Error saving note:', error); @@ -203,7 +227,7 @@ class NotesService { } async toggleArchiveNote() { - if (!this.currentNote) return; + if (!this.currentNote || !window.auth || !window.auth.getHeaders) return; try { await fetch(`/api/notes/${this.currentNote.id}`, { @@ -223,7 +247,8 @@ class NotesService { } async deleteNote() { - if (!this.currentNote || !confirm('Êtes-vous sûr de vouloir supprimer cette note ?')) return; + if (!this.currentNote || !window.auth || !window.auth.getHeaders) return; + if (!confirm('Êtes-vous sûr de vouloir supprimer cette note ?')) return; try { await fetch(`/api/notes/${this.currentNote.id}`, { @@ -232,7 +257,9 @@ class NotesService { }); const index = this.notes.findIndex(n => n.id === this.currentNote.id); - this.notes.splice(index, 1); + if (index !== -1) { + this.notes.splice(index, 1); + } this.currentNote = null; this.renderNotesList(); if (this.noteTitle) this.noteTitle.value = ''; @@ -261,6 +288,7 @@ class NotesService { async toggleTodo(index) { if (!this.currentNote || !this.currentNote.todos) return; + if (index < 0 || index >= this.currentNote.todos.length) return; this.currentNote.todos[index].completed = !this.currentNote.todos[index].completed; this.renderTodos(); @@ -269,6 +297,7 @@ class NotesService { async deleteTodo(index) { if (!this.currentNote || !this.currentNote.todos) return; + if (index < 0 || index >= this.currentNote.todos.length) return; this.currentNote.todos.splice(index, 1); this.renderTodos(); @@ -276,7 +305,8 @@ class NotesService { } async handleImageUpload(event) { - if (!this.currentNote || !event.target.files.length) return; + if (!this.currentNote || !event.target || !event.target.files || !event.target.files.length) return; + if (!window.auth || !window.auth.getHeaders) return; const formData = new FormData(); formData.append('image', event.target.files[0]); @@ -298,11 +328,15 @@ class NotesService { console.error('Error uploading image:', error); } - event.target.value = ''; // Reset input + if (event.target) { + event.target.value = ''; // Reset input + } } async deleteImage(imageId) { - if (!this.currentNote || !this.currentNote.images || !confirm('Supprimer cette image ?')) return; + if (!this.currentNote || !this.currentNote.images) return; + if (!window.auth || !window.auth.getHeaders) return; + if (!confirm('Supprimer cette image ?')) return; try { await fetch(`/api/notes/${this.currentNote.id}/images/${imageId}`, { @@ -311,7 +345,9 @@ class NotesService { }); const index = this.currentNote.images.findIndex(img => img.id === imageId); - this.currentNote.images.splice(index, 1); + if (index !== -1) { + this.currentNote.images.splice(index, 1); + } this.renderImages(); } catch (error) { console.error('Error deleting image:', error); @@ -319,7 +355,8 @@ class NotesService { } handleSearch(event) { - this.renderNotesList(event.target.value); + if (!event || !event.target) return; + this.renderNotesList(event.target.value || ''); } }