document.addEventListener('DOMContentLoaded', function() { // Récupération des paramètres et variables globales const urlParams = new URLSearchParams(window.location.search); const storyId = document.querySelector('input[name="id"]')?.value || urlParams.get('id'); let currentChapterId = null; let hasUnsavedChanges = false; // Références DOM pour l'éditeur const modal = document.getElementById('chapterEditor'); const coverModal = document.getElementById('chapterCoverEditor'); const addChapterBtn = document.getElementById('addChapter'); const saveChapterBtn = document.getElementById('saveChapter'); const cancelEditBtn = document.getElementById('cancelEdit'); const chapterTitleInput = document.getElementById('chapterTitle'); const chaptersList = document.getElementById('chaptersList'); const chapterDraftToggle = document.getElementById('chapterDraft'); // Éléments pour la modale de couverture const coverPreview = document.querySelector('.current-cover-preview'); const chapterCoverInput = document.getElementById('chapterCover'); const saveCoverBtn = document.getElementById('saveCover'); const cancelCoverBtn = document.getElementById('cancelCoverEdit'); const deleteCoverBtn = document.getElementById('deleteCover'); let currentChapterCover = null; // Notification système function showNotification(message, type = 'success') { const notification = document.createElement('div'); notification.className = `notification ${type}`; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '1'; notification.style.transform = 'translateY(0)'; }, 10); setTimeout(() => { notification.style.opacity = '0'; notification.style.transform = 'translateY(-100%)'; setTimeout(() => notification.remove(), 300); }, 3000); } // Configuration de l'éditeur Quill const quill = new Quill('#editor', { theme: 'snow', modules: { toolbar: { container: [ [{ 'header': [1, 2, 3, false] }], ['bold', 'italic', 'underline', 'strike'], [{ 'color': [] }, { 'background': [] }], [{ 'font': [] }], [{ 'align': [] }], ['blockquote', 'code-block'], [{ 'list': 'ordered'}, { 'list': 'bullet' }], [{ 'script': 'sub'}, { 'script': 'super' }], [{ 'indent': '-1'}, { 'indent': '+1' }], [{ 'direction': 'rtl' }], ['link', 'image', 'video'], ['divider'], ['clean'] ], handlers: { image: function() { const input = document.createElement('input'); input.setAttribute('type', 'file'); input.setAttribute('accept', 'image/*'); input.click(); input.onchange = async () => { const file = input.files[0]; if (file) { const formData = new FormData(); formData.append('image', file); formData.append('storyId', storyId); try { const response = await fetch('api/upload-image.php', { method: 'POST', body: formData }); if (!response.ok) throw new Error('Upload failed'); const result = await response.json(); if (result.success) { const range = quill.getSelection(true); quill.insertEmbed(range.index, 'image', result.url); const insertOp = quill.getContents().ops.find(op => op.insert && op.insert.image === result.url ); if (insertOp) { insertOp.insert.image = result.storage_url; } quill.setSelection(range.index + 1); } else { showNotification(result.error || 'Erreur lors de l\'upload', 'error'); } } catch (error) { console.error('Error:', error); showNotification('Erreur lors de l\'upload de l\'image', 'error'); } } }; } } } }, placeholder: 'Commencez à écrire votre chapitre ici...' }); // Détection des changements non sauvegardés function detectUnsavedChanges() { quill.on('text-change', () => { hasUnsavedChanges = true; }); if (chapterTitleInput) { chapterTitleInput.addEventListener('input', () => { hasUnsavedChanges = true; }); } } // Configuration de Sortable pour la réorganisation des chapitres if (chaptersList) { new Sortable(chaptersList, { animation: 150, handle: '.chapter-number', ghostClass: 'sortable-ghost', onEnd: async function(evt) { const chapters = Array.from(chaptersList.children).map((item, index) => ({ id: item.dataset.id, position: index })); try { const response = await fetch('api/reorder-chapters.php', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ storyId: storyId, chapters: chapters }) }); if (!response.ok) { throw new Error('Erreur lors de la réorganisation'); } chapters.forEach((chapter, index) => { const element = document.querySelector(`[data-id="${chapter.id}"] .chapter-number`); if (element) { element.textContent = index + 1; } }); } catch (error) { console.error('Erreur:', error); showNotification('Erreur lors de la réorganisation des chapitres', 'error'); } } }); } // Gestion de la sauvegarde du chapitre if (saveChapterBtn) { saveChapterBtn.addEventListener('click', async () => { const title = chapterTitleInput.value.trim(); if (!title) { showNotification('Le titre est requis', 'error'); return; } try { const response = await fetch('api/save-chapter.php', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ storyId: storyId, chapterId: currentChapterId, title: title, content: quill.root.innerHTML, draft: chapterDraftToggle?.checked || false }) }); if (response.ok) { const result = await response.json(); if (result.success) { hasUnsavedChanges = false; showNotification('Chapitre sauvegardé avec succès'); setTimeout(() => location.reload(), 500); } else { throw new Error(result.error || 'Erreur lors de la sauvegarde'); } } else { throw new Error('Erreur réseau'); } } catch (error) { console.error('Erreur:', error); showNotification('Erreur lors de la sauvegarde du chapitre', 'error'); } }); } // Gestionnaires d'événements pour l'édition des chapitres if (addChapterBtn) { addChapterBtn.addEventListener('click', () => { currentChapterId = null; chapterTitleInput.value = ''; quill.setContents([]); if (chapterDraftToggle) { chapterDraftToggle.checked = false; } hasUnsavedChanges = false; modal.style.display = 'block'; }); } if (cancelEditBtn) { cancelEditBtn.addEventListener('click', () => { if (hasUnsavedChanges) { confirmDialog.show({ title: 'Modifications non sauvegardées', message: 'Des modifications non sauvegardées seront perdues. Voulez-vous vraiment fermer ?', confirmText: 'Fermer sans sauvegarder', onConfirm: () => { modal.style.display = 'none'; hasUnsavedChanges = false; } }); } else { modal.style.display = 'none'; } }); } // Gestion des clics sur la liste des chapitres if (chaptersList) { chaptersList.addEventListener('click', async (e) => { const target = e.target; if (target.matches('.edit-chapter')) { const chapterItem = target.closest('.chapter-item'); currentChapterId = chapterItem.dataset.id; try { const response = await fetch(`api/get-chapter.php?storyId=${storyId}&chapterId=${currentChapterId}`); if (!response.ok) { throw new Error('Erreur réseau'); } const chapter = await response.json(); // Mise à jour du titre chapterTitleInput.value = chapter.title || ''; // Gestion du contenu if (chapter.html) { quill.root.innerHTML = chapter.html; } else if (chapter.content) { try { const content = typeof chapter.content === 'string' ? JSON.parse(chapter.content) : chapter.content; quill.setContents(content); } catch (contentError) { console.error('Erreur de parsing du contenu:', contentError); quill.setText(chapter.content || ''); } } else { quill.setContents([]); } // Mise à jour du mode brouillon if (chapterDraftToggle) { chapterDraftToggle.checked = chapter.draft || false; } modal.style.display = 'block'; hasUnsavedChanges = false; } catch (error) { console.error('Erreur détaillée:', error); showNotification('Erreur lors du chargement du chapitre', 'error'); } } // Gestion de la suppression if (target.matches('.delete-chapter')) { const chapterItem = target.closest('.chapter-item'); const chapterId = chapterItem.dataset.id; const chapterTitle = chapterItem.querySelector('.chapter-title').textContent; confirmDialog.show({ title: 'Suppression du chapitre', message: `Voulez-vous vraiment supprimer le chapitre "${chapterTitle}" ? Cette action est irréversible.`, confirmText: 'Supprimer', confirmClass: 'danger', onConfirm: async () => { try { const response = await fetch('api/delete-chapter.php', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ storyId: storyId, chapterId: chapterId }) }); if (!response.ok) throw new Error('Erreur réseau'); const result = await response.json(); if (result.success) { chapterItem.style.opacity = '0'; chapterItem.style.transform = 'translateX(-100%)'; setTimeout(() => { chapterItem.remove(); showNotification('Chapitre supprimé avec succès'); }, 300); } else { throw new Error(result.error || 'Erreur lors de la suppression'); } } catch (error) { console.error('Erreur:', error); showNotification(error.message, 'error'); } } }); } // Gestion de la couverture if (target.matches('.edit-cover')) { const chapterItem = target.closest('.chapter-item'); const chapterId = chapterItem.dataset.id; const chapterTitle = chapterItem.querySelector('.chapter-title').textContent; // Mise à jour du titre dans la modale coverModal.querySelector('.chapter-title-display').textContent = chapterTitle; currentChapterCover = chapterId; try { const response = await fetch(`api/get-chapter.php?storyId=${storyId}&chapterId=${chapterId}`); if (!response.ok) throw new Error('Erreur réseau'); const chapter = await response.json(); // Afficher l'image existante si elle existe if (chapter.cover) { coverPreview.innerHTML = `Couverture actuelle`; deleteCoverBtn.style.display = 'block'; } else { coverPreview.innerHTML = ''; deleteCoverBtn.style.display = 'none'; } coverModal.style.display = 'block'; } catch (error) { console.error('Erreur:', error); showNotification('Erreur lors du chargement de la couverture', 'error'); } } }); } // Prévisualisation de l'image de couverture if (chapterCoverInput) { chapterCoverInput.addEventListener('change', (e) => { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = (e) => { coverPreview.innerHTML = `Prévisualisation`; deleteCoverBtn.style.display = 'none'; }; reader.readAsDataURL(file); } }); } // Fermeture de la modale de couverture if (cancelCoverBtn) { cancelCoverBtn.addEventListener('click', () => { coverModal.style.display = 'none'; chapterCoverInput.value = ''; }); } // Sauvegarde de la couverture if (saveCoverBtn) { saveCoverBtn.addEventListener('click', async () => { const file = chapterCoverInput.files[0]; if (!file && !coverPreview.querySelector('img')) { showNotification('Veuillez sélectionner une image', 'error'); return; } const formData = new FormData(); formData.append('storyId', storyId); formData.append('chapterId', currentChapterCover); if (file) { formData.append('cover', file); } try { const response = await fetch('api/update-chapter-cover.php', { method: 'POST', body: formData }); if (!response.ok) throw new Error('Erreur lors de l\'upload'); const result = await response.json(); if (result.success) { showNotification('Couverture mise à jour avec succès'); coverModal.style.display = 'none'; chapterCoverInput.value = ''; setTimeout(() => location.reload(), 500); } else { throw new Error(result.error || 'Erreur lors de la mise à jour'); } } catch (error) { console.error('Erreur:', error); showNotification(error.message, 'error'); } }); } // Suppression de la couverture if (deleteCoverBtn) { deleteCoverBtn.addEventListener('click', () => { confirmDialog.show({ title: 'Supprimer la couverture', message: 'Voulez-vous vraiment supprimer la couverture de ce chapitre ?', confirmText: 'Supprimer', confirmClass: 'danger', onConfirm: async () => { try { const response = await fetch('api/update-chapter-cover.php', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ storyId: storyId, chapterId: currentChapterCover, delete: true }) }); if (!response.ok) throw new Error('Erreur réseau'); const result = await response.json(); if (result.success) { showNotification('Couverture supprimée avec succès'); coverPreview.innerHTML = ''; coverModal.style.display = 'none'; setTimeout(() => location.reload(), 500); } else { throw new Error(result.error || 'Erreur lors de la suppression'); } } catch (error) { console.error('Erreur:', error); showNotification(error.message, 'error'); } } }); }); } // Initialisation detectUnsavedChanges(); });