diff --git a/admin/api/save-chapter.php b/admin/api/save-chapter.php
new file mode 100644
index 0000000..dd05f0e
--- /dev/null
+++ b/admin/api/save-chapter.php
@@ -0,0 +1,49 @@
+ uniqid(),
+ 'title' => $title,
+ 'content' => $content,
+ 'created' => date('Y-m-d'),
+ 'updated' => date('Y-m-d')
+ ];
+ }
+
+ Stories::save($story);
+ echo json_encode(['success' => true]);
+} catch (Exception $e) {
+ http_response_code(500);
+ echo json_encode(['error' => $e->getMessage()]);
+}
\ No newline at end of file
diff --git a/admin/story-edit.php b/admin/story-edit.php
new file mode 100644
index 0000000..df9f88d
--- /dev/null
+++ b/admin/story-edit.php
@@ -0,0 +1,161 @@
+ $_POST['id'] ?? generateSlug($_POST['title']),
+ 'title' => $_POST['title'],
+ 'description' => $_POST['description'],
+ 'cover' => $story['cover'] ?? '', // Géré séparément par l'upload
+ 'created' => $story['created'] ?? date('Y-m-d'),
+ 'updated' => date('Y-m-d'),
+ 'chapters' => $story['chapters'] ?? []
+ ];
+
+ // Gestion de l'upload de couverture
+ if (isset($_FILES['cover']) && $_FILES['cover']['error'] === UPLOAD_ERR_OK) {
+ $uploadDir = '../assets/images/covers/';
+ $extension = strtolower(pathinfo($_FILES['cover']['name'], PATHINFO_EXTENSION));
+ $filename = $storyData['id'] . '.' . $extension;
+
+ if (move_uploaded_file($_FILES['cover']['tmp_name'], $uploadDir . $filename)) {
+ $storyData['cover'] = 'assets/images/covers/' . $filename;
+ }
+ }
+
+ Stories::save($storyData);
+ $success = 'Roman sauvegardé avec succès';
+ $story = $storyData;
+ } catch (Exception $e) {
+ $error = 'Erreur lors de la sauvegarde : ' . $e->getMessage();
+ }
+}
+
+function generateSlug($title) {
+ $slug = strtolower($title);
+ $slug = preg_replace('/[^a-z0-9-]/', '-', $slug);
+ $slug = preg_replace('/-+/', '-', $slug);
+ return trim($slug, '-');
+}
+?>
+
+
+
+
+
+ = $story ? 'Modifier' : 'Nouveau' ?> roman - Administration
+
+
+
+
+
+
+
+ = $story ? 'Modifier' : 'Nouveau' ?> roman
+
+
+ = htmlspecialchars($error) ?>
+
+
+
+ = htmlspecialchars($success) ?>
+
+
+
+
+
+
+ Chapitres
+
+
+
+ $chapter): ?>
+
+
+
+
= $chapter['content'] ?>
+
+
+
+
+
+
+
+
+
Éditer le chapitre
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/css/admin.css b/assets/css/admin.css
index e9b3ae9..88a5cbf 100644
--- a/assets/css/admin.css
+++ b/assets/css/admin.css
@@ -137,4 +137,270 @@ body {
.delete-story:hover {
background-color: #b71c1c;
+}
+
+/* Formulaire d'édition du roman */
+.story-form {
+ background: white;
+ padding: 2rem;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ margin-bottom: 2rem;
+}
+
+.form-group {
+ margin-bottom: 1.5rem;
+}
+
+.form-group label {
+ display: block;
+ margin-bottom: 0.5rem;
+ color: #2c1810;
+ font-weight: 500;
+}
+
+.form-group input[type="text"],
+.form-group textarea {
+ width: 100%;
+ padding: 0.75rem;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ font-size: 1rem;
+}
+
+.form-group textarea {
+ resize: vertical;
+ min-height: 100px;
+}
+
+.current-cover {
+ display: block;
+ max-width: 200px;
+ margin: 0.5rem 0;
+ border-radius: 4px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+/* Section des chapitres */
+.chapters-section {
+ background: white;
+ padding: 2rem;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.chapters-section h2 {
+ margin-top: 0;
+ color: #2c1810;
+}
+
+.chapters-list {
+ margin-top: 1.5rem;
+}
+
+.chapter-item {
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ margin-bottom: 1rem;
+ background: #f9f9f9;
+}
+
+.chapter-header {
+ display: flex;
+ align-items: center;
+ padding: 1rem;
+ background: #fff;
+ border-bottom: 1px solid #ddd;
+ border-radius: 4px 4px 0 0;
+}
+
+.chapter-number {
+ width: 30px;
+ height: 30px;
+ background: #8b4513;
+ color: white;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: bold;
+ cursor: move;
+ margin-right: 1rem;
+}
+
+.chapter-title {
+ flex: 1;
+ padding: 0.5rem;
+ margin-right: 1rem;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+}
+
+.chapter-content {
+ padding: 1rem;
+ background: white;
+}
+
+.chapter-content .editor {
+ min-height: 100px;
+ cursor: pointer;
+}
+
+/* Modal d'édition */
+.modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 1000;
+}
+
+.modal-content {
+ background: white;
+ padding: 2rem;
+ border-radius: 8px;
+ width: 90%;
+ max-width: 800px;
+ max-height: 90vh;
+ overflow-y: auto;
+}
+
+.modal-content h2 {
+ margin-top: 0;
+ color: #2c1810;
+}
+
+#chapterTitle {
+ width: 100%;
+ padding: 0.75rem;
+ margin-bottom: 1rem;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ font-size: 1rem;
+}
+
+/* Éditeur Quill personnalisé */
+.ql-container {
+ min-height: 300px;
+ font-size: 1rem;
+ margin-bottom: 1rem;
+}
+
+.ql-toolbar {
+ border-radius: 4px 4px 0 0;
+ background: #f9f9f9;
+}
+
+.ql-container {
+ border-radius: 0 0 4px 4px;
+}
+
+/* Boutons et actions */
+.modal-actions {
+ display: flex;
+ gap: 1rem;
+ margin-top: 1rem;
+}
+
+.success-message {
+ background-color: #e8f5e9;
+ color: #2e7d32;
+ padding: 1rem;
+ border-radius: 4px;
+ margin-bottom: 1rem;
+}
+
+/* Styles pour le drag & drop */
+.chapter-item.sortable-ghost {
+ opacity: 0.4;
+}
+
+.chapter-item.sortable-drag {
+ cursor: move;
+ opacity: 0.9;
+}
+
+/* Responsive */
+@media (max-width: 768px) {
+ .story-form,
+ .chapters-section {
+ padding: 1rem;
+ }
+
+ .chapter-header {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .chapter-number {
+ margin-bottom: 0.5rem;
+ }
+
+ .chapter-title {
+ margin: 0.5rem 0;
+ }
+
+ .modal-content {
+ width: 95%;
+ padding: 1rem;
+ }
+
+ .ql-container {
+ min-height: 200px;
+ }
+}
+
+/* Thème personnalisé pour l'éditeur */
+.ql-snow .ql-toolbar button:hover,
+.ql-snow .ql-toolbar button.ql-active {
+ color: #8b4513;
+}
+
+.ql-snow .ql-toolbar button:hover .ql-stroke,
+.ql-snow .ql-toolbar button.ql-active .ql-stroke {
+ stroke: #8b4513;
+}
+
+.ql-snow .ql-toolbar button:hover .ql-fill,
+.ql-snow .ql-toolbar button.ql-active .ql-fill {
+ fill: #8b4513;
+}
+
+/* Animation de transition */
+.chapter-item {
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
+}
+
+.chapter-item:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+}
+
+.button {
+ transition: background-color 0.2s ease;
+}
+
+/* États de survol pour les éléments interactifs */
+.chapter-title:hover,
+.chapter-title:focus {
+ border-color: #8b4513;
+ outline: none;
+}
+
+.delete-chapter {
+ background-color: #dc3545;
+ color: white;
+ padding: 0.5rem 1rem;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.delete-chapter:hover {
+ background-color: #c82333;
}
\ No newline at end of file
diff --git a/assets/js/story-edit.js b/assets/js/story-edit.js
new file mode 100644
index 0000000..f89f503
--- /dev/null
+++ b/assets/js/story-edit.js
@@ -0,0 +1,144 @@
+document.addEventListener('DOMContentLoaded', function() {
+ // Initialisation de l'éditeur Quill
+ const quill = new Quill('#editor', {
+ theme: 'snow',
+ modules: {
+ toolbar: [
+ [{ 'header': [1, 2, 3, false] }],
+ ['bold', 'italic', 'underline'],
+ [{ 'list': 'ordered'}, { 'list': 'bullet' }],
+ ['link', 'blockquote'],
+ ['clean']
+ ]
+ }
+ });
+
+ // Gestion du tri des chapitres
+ const chaptersList = document.getElementById('chaptersList');
+ if (chaptersList) {
+ new Sortable(chaptersList, {
+ animation: 150,
+ handle: '.chapter-number',
+ onEnd: updateChaptersOrder
+ });
+ }
+
+ // Gestion des événements
+ const modal = document.getElementById('chapterEditor');
+ const addChapterBtn = document.getElementById('addChapter');
+ const saveChapterBtn = document.getElementById('saveChapter');
+ const cancelEditBtn = document.getElementById('cancelEdit');
+ let currentEditingChapter = null;
+
+ if (addChapterBtn) {
+ addChapterBtn.addEventListener('click', () => {
+ currentEditingChapter = null;
+ document.getElementById('chapterTitle').value = '';
+ quill.setContents([]);
+ modal.style.display = 'block';
+ });
+ }
+
+ // Sauvegarde d'un chapitre
+ if (saveChapterBtn) {
+ saveChapterBtn.addEventListener('click', async () => {
+ const title = document.getElementById('chapterTitle').value;
+ const content = quill.root.innerHTML;
+ const storyId = document.querySelector('input[name="id"]').value;
+
+ try {
+ const response = await fetch('api/save-chapter.php', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ storyId,
+ chapterId: currentEditingChapter,
+ title,
+ content
+ })
+ });
+
+ if (response.ok) {
+ window.location.reload();
+ } else {
+ throw new Error('Erreur lors de la sauvegarde');
+ }
+ } catch (error) {
+ alert('Erreur lors de la sauvegarde du chapitre');
+ }
+ });
+ }
+
+ // Fermeture du modal
+ if (cancelEditBtn) {
+ cancelEditBtn.addEventListener('click', () => {
+ modal.style.display = 'none';
+ });
+ }
+
+ // Écoute des clics sur la liste des chapitres
+ if (chaptersList) {
+ chaptersList.addEventListener('click', (e) => {
+ if (e.target.matches('.delete-chapter')) {
+ if (confirm('Voulez-vous vraiment supprimer ce chapitre ?')) {
+ const chapterId = e.target.closest('.chapter-item').dataset.id;
+ deleteChapter(chapterId);
+ }
+ } else if (e.target.matches('.chapter-title')) {
+ const chapterItem = e.target.closest('.chapter-item');
+ currentEditingChapter = chapterItem.dataset.id;
+ document.getElementById('chapterTitle').value = e.target.value;
+ quill.root.innerHTML = chapterItem.querySelector('.editor').innerHTML;
+ modal.style.display = 'block';
+ }
+ });
+ }
+});
+
+// Mise à jour de l'ordre des chapitres
+async function updateChaptersOrder() {
+ const chapters = Array.from(document.querySelectorAll('.chapter-item'))
+ .map((item, index) => ({
+ id: item.dataset.id,
+ order: index
+ }));
+
+ try {
+ const response = await fetch('api/update-chapters-order.php', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(chapters)
+ });
+
+ if (!response.ok) {
+ throw new Error('Erreur lors de la mise à jour');
+ }
+ } catch (error) {
+ alert('Erreur lors de la mise à jour de l\'ordre des chapitres');
+ }
+}
+
+// Suppression d'un chapitre
+async function deleteChapter(chapterId) {
+ try {
+ const response = await fetch('api/delete-chapter.php', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ chapterId })
+ });
+
+ if (response.ok) {
+ window.location.reload();
+ } else {
+ throw new Error('Erreur lors de la suppression');
+ }
+ } catch (error) {
+ alert('Erreur lors de la suppression du chapitre');
+ }
+}
\ No newline at end of file