feat: refonte de la modale de gestion des chapitres avec un design Material Design, ajout de nouvelles fonctionnalités pour la séparation des volumes, et amélioration de l'interface utilisateur. Intégration de nouveaux composants pour une meilleure expérience utilisateur lors de la gestion des chapitres et des volumes.
This commit is contained in:
parent
be283833e9
commit
330a0fac34
@@ -1,177 +1,207 @@
|
||||
<template>
|
||||
<div v-if="isOpen" class="fixed inset-0 z-50 overflow-y-auto">
|
||||
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<!-- Overlay -->
|
||||
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" @click="handleClose"></div>
|
||||
<!-- Overlay avec effet de flou Material Design -->
|
||||
<div class="fixed inset-0 bg-black/40 backdrop-blur-sm transition-opacity" @click="handleClose"></div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-5xl sm:w-full">
|
||||
<!-- Header -->
|
||||
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<!-- Modal avec style Material Design -->
|
||||
<div class="inline-block align-bottom bg-white rounded-2xl text-left overflow-hidden shadow-2xl transform transition-all sm:my-8 sm:align-middle sm:max-w-5xl sm:w-full border border-gray-100">
|
||||
<!-- Header Material Design -->
|
||||
<div class="bg-gradient-to-r from-green-50 to-emerald-50 px-6 pt-6 pb-4 sm:px-8 sm:pb-6 border-b border-gray-100">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||
Gérer les chapitres - {{ manga?.title }}
|
||||
</h3>
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center">
|
||||
<FolderIcon class="h-5 w-5 text-green-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-medium text-gray-900 leading-6">
|
||||
Gérer les chapitres
|
||||
</h3>
|
||||
<p class="text-sm text-gray-600 mt-1">{{ manga?.title }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
@click="handleClose"
|
||||
class="text-gray-400 hover:text-gray-600 transition-colors"
|
||||
class="w-8 h-8 rounded-full bg-gray-100 hover:bg-gray-200 flex items-center justify-center transition-colors duration-200"
|
||||
>
|
||||
<XMarkIcon class="h-6 w-6" />
|
||||
<XMarkIcon class="h-5 w-5 text-gray-600" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="bg-white px-4 pb-5 sm:p-6">
|
||||
<!-- Content avec style Material Design -->
|
||||
<div class="bg-white px-6 py-6 sm:px-8 sm:py-8">
|
||||
<div v-if="isLoading" class="flex justify-center items-center h-32">
|
||||
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
|
||||
<div class="relative">
|
||||
<div class="w-8 h-8 border-4 border-green-200 rounded-full"></div>
|
||||
<div class="absolute top-0 left-0 w-8 h-8 border-4 border-green-600 rounded-full border-t-transparent animate-spin"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="error" class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
|
||||
{{ error }}
|
||||
<div v-else-if="error" class="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-xl mb-6 flex items-center space-x-2">
|
||||
<div class="w-5 h-5 bg-red-100 rounded-full flex items-center justify-center">
|
||||
<XMarkIcon class="h-3 w-3 text-red-600" />
|
||||
</div>
|
||||
<span>{{ error }}</span>
|
||||
</div>
|
||||
|
||||
<div v-else class="space-y-6">
|
||||
<!-- Actions -->
|
||||
<div class="flex items-center justify-between">
|
||||
<!-- Actions avec style Material Design -->
|
||||
<div class="flex items-center justify-between bg-gray-50 rounded-xl p-4">
|
||||
<div class="flex items-center space-x-3">
|
||||
<button
|
||||
@click="showCreateVolumeModal = true"
|
||||
class="bg-blue-600 text-white px-4 py-2 rounded text-sm hover:bg-blue-700"
|
||||
class="bg-green-600 text-white px-4 py-2.5 rounded-lg text-sm font-medium hover:bg-green-700 shadow-md hover:shadow-lg transition-all duration-200 flex items-center space-x-2"
|
||||
>
|
||||
<PlusIcon class="h-4 w-4 inline mr-1" />
|
||||
Créer un volume
|
||||
<PlusIcon class="h-4 w-4" />
|
||||
<span>Créer un volume</span>
|
||||
</button>
|
||||
<button
|
||||
@click="showUnassignedChapters = !showUnassignedChapters"
|
||||
class="text-gray-600 hover:text-gray-800 text-sm"
|
||||
class="text-gray-600 hover:text-gray-800 text-sm font-medium hover:bg-gray-100 px-3 py-2 rounded-lg transition-colors duration-200"
|
||||
>
|
||||
{{ showUnassignedChapters ? 'Masquer' : 'Afficher' }} les chapitres non assignés
|
||||
</button>
|
||||
<!-- Bouton de séparation automatique du volume fourre-tout -->
|
||||
<button
|
||||
v-if="hasVolumeZero && canSplitVolumeZero"
|
||||
@click="showSplitVolumeZeroModal = true"
|
||||
class="bg-green-600 text-white px-4 py-2.5 rounded-lg text-sm font-medium hover:bg-green-700 shadow-md hover:shadow-lg transition-all duration-200 flex items-center space-x-2"
|
||||
>
|
||||
<ArrowPathIcon class="h-4 w-4" />
|
||||
<span>Séparer le volume 00</span>
|
||||
</button>
|
||||
<!-- Actions de sélection multiple -->
|
||||
<div v-if="selectedChapters.length > 0" class="flex items-center space-x-2 bg-blue-50 px-3 py-1 rounded-lg">
|
||||
<span class="text-sm text-blue-700">{{ selectedChapters.length }} chapitre(s) sélectionné(s)</span>
|
||||
<div v-if="selectedChapters.length > 0" class="flex items-center space-x-3 bg-green-50 px-4 py-2 rounded-xl border border-green-200">
|
||||
<span class="text-sm font-medium text-green-700">{{ selectedChapters.length }} chapitre(s) sélectionné(s)</span>
|
||||
<button
|
||||
@click="showMoveToVolumeModal = true"
|
||||
class="bg-blue-600 text-white px-2 py-1 rounded text-xs hover:bg-blue-700"
|
||||
class="bg-green-600 text-white px-3 py-1.5 rounded-lg text-xs font-medium hover:bg-green-700 shadow-sm transition-colors duration-200"
|
||||
>
|
||||
Déplacer vers un volume
|
||||
</button>
|
||||
<button
|
||||
@click="clearSelection"
|
||||
class="text-blue-600 hover:text-blue-800 text-xs"
|
||||
class="text-green-600 hover:text-green-800 text-xs font-medium hover:bg-green-100 px-2 py-1 rounded transition-colors duration-200"
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-gray-500">
|
||||
<div class="text-sm text-gray-500 bg-white px-3 py-1.5 rounded-lg border border-gray-200">
|
||||
{{ totalChapters }} chapitres, {{ volumes.length }} volumes
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Arborescence -->
|
||||
<div class="border border-gray-200 rounded-lg overflow-hidden">
|
||||
<!-- Arborescence avec style Material Design -->
|
||||
<div class="bg-white border border-gray-200 rounded-xl overflow-hidden shadow-sm">
|
||||
<!-- Chapitres non assignés -->
|
||||
<div v-if="showUnassignedChapters && unassignedChapters.length > 0" class="bg-gray-50 border-b border-gray-200">
|
||||
<div class="px-4 py-3">
|
||||
<h4 class="text-sm font-medium text-gray-700 mb-2">
|
||||
Chapitres non assignés ({{ unassignedChapters.length }})
|
||||
<div v-if="showUnassignedChapters && unassignedChapters.length > 0" class="bg-gradient-to-r from-gray-50 to-gray-100 border-b border-gray-200">
|
||||
<div class="px-6 py-4">
|
||||
<h4 class="text-sm font-semibold text-gray-700 mb-3 flex items-center space-x-2">
|
||||
<DocumentIcon class="h-4 w-4 text-gray-500" />
|
||||
<span>Chapitres non assignés ({{ unassignedChapters.length }})</span>
|
||||
</h4>
|
||||
<div class="space-y-1">
|
||||
<div class="space-y-2">
|
||||
<div
|
||||
v-for="chapter in unassignedChapters"
|
||||
:key="chapter.id"
|
||||
class="flex items-center space-x-3 p-2 hover:bg-gray-100 rounded"
|
||||
:class="{ 'bg-blue-50 border border-blue-200': isChapterSelected(chapter) }"
|
||||
class="flex items-center space-x-3 p-3 hover:bg-white rounded-lg transition-colors duration-200 border border-transparent hover:border-gray-200"
|
||||
:class="{ 'bg-green-50 border-green-200 shadow-sm': isChapterSelected(chapter) }"
|
||||
>
|
||||
<!-- Checkbox de sélection -->
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="isChapterSelected(chapter)"
|
||||
@change="toggleChapterSelection(chapter)"
|
||||
class="h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
|
||||
/>
|
||||
<DocumentIcon class="h-4 w-4 text-gray-400" />
|
||||
<span class="text-sm font-medium text-gray-700 w-12">{{ chapter.number }}</span>
|
||||
<!-- Checkbox de sélection Material Design -->
|
||||
<div class="relative">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="isChapterSelected(chapter)"
|
||||
@change="toggleChapterSelection(chapter)"
|
||||
class="h-5 w-5 text-green-600 border-gray-300 rounded focus:ring-green-500 focus:ring-2 transition-colors duration-200"
|
||||
/>
|
||||
</div>
|
||||
<DocumentIcon class="h-5 w-5 text-gray-400" />
|
||||
<span class="text-sm font-medium text-gray-700 w-12 bg-gray-100 px-2 py-1 rounded text-center">{{ chapter.number }}</span>
|
||||
<div class="flex-1">
|
||||
<div v-if="!chapter.isEditing" class="flex items-center">
|
||||
<span
|
||||
class="text-sm text-gray-900 cursor-pointer hover:text-blue-600"
|
||||
class="text-sm text-gray-900 cursor-pointer hover:text-green-600 transition-colors duration-200"
|
||||
@click="startEditingTitle(chapter)"
|
||||
>
|
||||
{{ chapter.title || 'Sans titre' }}
|
||||
</span>
|
||||
<button
|
||||
@click="startEditingTitle(chapter)"
|
||||
class="ml-2 text-gray-400 hover:text-gray-600"
|
||||
class="ml-2 text-gray-400 hover:text-gray-600 p-1 rounded-full hover:bg-gray-100 transition-colors duration-200"
|
||||
>
|
||||
<PencilIcon class="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="flex items-center space-x-1">
|
||||
<div v-else class="flex items-center space-x-2">
|
||||
<input
|
||||
v-model="chapter.editingTitle"
|
||||
type="text"
|
||||
class="flex-1 border border-gray-300 rounded px-2 py-1 text-xs"
|
||||
class="flex-1 border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:ring-2 focus:ring-green-500 focus:border-green-500 transition-colors duration-200"
|
||||
@keyup.enter="saveTitle(chapter)"
|
||||
@keyup.esc="cancelEditingTitle(chapter)"
|
||||
ref="titleInput"
|
||||
/>
|
||||
<button
|
||||
@click="saveTitle(chapter)"
|
||||
class="text-green-600 hover:text-green-800"
|
||||
class="text-green-600 hover:text-green-800 p-1 rounded-full hover:bg-green-100 transition-colors duration-200"
|
||||
>
|
||||
<CheckIcon class="h-3 w-3" />
|
||||
<CheckIcon class="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
@click="cancelEditingTitle(chapter)"
|
||||
class="text-red-600 hover:text-red-800"
|
||||
class="text-red-600 hover:text-red-800 p-1 rounded-full hover:bg-red-100 transition-colors duration-200"
|
||||
>
|
||||
<XMarkIcon class="h-3 w-3" />
|
||||
<XMarkIcon class="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center space-x-1">
|
||||
<div class="flex items-center space-x-2">
|
||||
<button
|
||||
@click="assignToVolume(chapter)"
|
||||
class="text-blue-600 hover:text-blue-800 text-xs"
|
||||
class="bg-green-600 text-white px-3 py-1.5 rounded-lg text-xs font-medium hover:bg-green-700 shadow-sm transition-colors duration-200"
|
||||
>
|
||||
Assigner
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="chapter.isModified" class="w-2 h-2 bg-yellow-400 rounded-full"></div>
|
||||
<div v-if="chapter.isModified" class="w-3 h-3 bg-yellow-400 rounded-full shadow-sm"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Volumes -->
|
||||
<div class="divide-y divide-gray-200">
|
||||
<!-- Volumes avec style Material Design -->
|
||||
<div class="divide-y divide-gray-100">
|
||||
<div
|
||||
v-for="volume in volumes"
|
||||
:key="volume.number"
|
||||
class="bg-white"
|
||||
>
|
||||
<!-- En-tête du volume -->
|
||||
<div class="px-4 py-3 bg-blue-50 border-b border-blue-100">
|
||||
<!-- En-tête du volume Material Design -->
|
||||
<div class="px-6 py-4 bg-gradient-to-r from-green-50 to-emerald-50 border-b border-green-100">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-2">
|
||||
<FolderIcon class="h-5 w-5 text-blue-600" />
|
||||
<span class="text-sm font-medium text-blue-900">Volume {{ volume.number }}</span>
|
||||
<span class="text-xs text-blue-600">({{ volume.chapters.length }} chapitres)</span>
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="w-8 h-8 bg-green-100 rounded-full flex items-center justify-center">
|
||||
<FolderIcon class="h-4 w-4 text-green-600" />
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-sm font-semibold text-green-900">Volume {{ volume.number }}</span>
|
||||
<span class="text-xs text-green-600 ml-2">({{ volume.chapters.length }} chapitres)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<button
|
||||
@click="toggleVolumeExpanded(volume)"
|
||||
class="text-blue-600 hover:text-blue-800"
|
||||
class="w-8 h-8 rounded-full bg-green-100 hover:bg-green-200 flex items-center justify-center transition-colors duration-200"
|
||||
>
|
||||
<ChevronDownIcon v-if="volume.isExpanded" class="h-4 w-4" />
|
||||
<ChevronRightIcon v-else class="h-4 w-4" />
|
||||
<ChevronDownIcon v-if="volume.isExpanded" class="h-4 w-4 text-green-600" />
|
||||
<ChevronRightIcon v-else class="h-4 w-4 text-green-600" />
|
||||
</button>
|
||||
<button
|
||||
@click="deleteVolume(volume.number)"
|
||||
class="text-red-600 hover:text-red-800 text-sm"
|
||||
class="text-red-600 hover:text-red-800 text-sm font-medium hover:bg-red-100 px-3 py-1.5 rounded-lg transition-colors duration-200"
|
||||
>
|
||||
Supprimer
|
||||
</button>
|
||||
@@ -180,75 +210,77 @@
|
||||
</div>
|
||||
|
||||
<!-- Chapitres du volume -->
|
||||
<div v-if="volume.isExpanded" class="px-4 py-2">
|
||||
<div v-if="volume.chapters.length === 0" class="text-center py-4 text-gray-500 text-sm">
|
||||
Aucun chapitre assigné à ce volume.
|
||||
<br>
|
||||
<span class="text-xs">Utilisez le bouton "Assigner" sur les chapitres non assignés pour les ajouter.</span>
|
||||
<div v-if="volume.isExpanded" class="px-6 py-4">
|
||||
<div v-if="volume.chapters.length === 0" class="text-center py-8 text-gray-500">
|
||||
<DocumentIcon class="h-12 w-12 text-gray-300 mx-auto mb-3" />
|
||||
<p class="text-sm">Aucun chapitre assigné à ce volume.</p>
|
||||
<p class="text-xs text-gray-400 mt-1">Utilisez le bouton "Assigner" sur les chapitres non assignés pour les ajouter.</p>
|
||||
</div>
|
||||
<div v-else class="space-y-1">
|
||||
<div v-else class="space-y-2">
|
||||
<div
|
||||
v-for="chapter in volume.chapters"
|
||||
:key="chapter.id"
|
||||
class="flex items-center space-x-3 p-2 hover:bg-gray-50 rounded"
|
||||
:class="{ 'bg-blue-50 border border-blue-200': isChapterSelected(chapter) }"
|
||||
class="flex items-center space-x-3 p-3 hover:bg-gray-50 rounded-lg transition-colors duration-200 border border-transparent hover:border-gray-200"
|
||||
:class="{ 'bg-green-50 border-green-200 shadow-sm': isChapterSelected(chapter) }"
|
||||
>
|
||||
<!-- Checkbox de sélection -->
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="isChapterSelected(chapter)"
|
||||
@change="toggleChapterSelection(chapter)"
|
||||
class="h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
|
||||
/>
|
||||
<DocumentIcon class="h-4 w-4 text-gray-400" />
|
||||
<span class="text-sm font-medium text-gray-700 w-12">{{ chapter.number }}</span>
|
||||
<div class="relative">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="isChapterSelected(chapter)"
|
||||
@change="toggleChapterSelection(chapter)"
|
||||
class="h-5 w-5 text-green-600 border-gray-300 rounded focus:ring-green-500 focus:ring-2 transition-colors duration-200"
|
||||
/>
|
||||
</div>
|
||||
<DocumentIcon class="h-5 w-5 text-gray-400" />
|
||||
<span class="text-sm font-medium text-gray-700 w-12 bg-gray-100 px-2 py-1 rounded text-center">{{ chapter.number }}</span>
|
||||
<div class="flex-1">
|
||||
<div v-if="!chapter.isEditing" class="flex items-center">
|
||||
<span
|
||||
class="text-sm text-gray-900 cursor-pointer hover:text-blue-600"
|
||||
class="text-sm text-gray-900 cursor-pointer hover:text-green-600 transition-colors duration-200"
|
||||
@click="startEditingTitle(chapter)"
|
||||
>
|
||||
{{ chapter.title || 'Sans titre' }}
|
||||
</span>
|
||||
<button
|
||||
@click="startEditingTitle(chapter)"
|
||||
class="ml-2 text-gray-400 hover:text-gray-600"
|
||||
class="ml-2 text-gray-400 hover:text-gray-600 p-1 rounded-full hover:bg-gray-100 transition-colors duration-200"
|
||||
>
|
||||
<PencilIcon class="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="flex items-center space-x-1">
|
||||
<div v-else class="flex items-center space-x-2">
|
||||
<input
|
||||
v-model="chapter.editingTitle"
|
||||
type="text"
|
||||
class="flex-1 border border-gray-300 rounded px-2 py-1 text-xs"
|
||||
class="flex-1 border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:ring-2 focus:ring-green-500 focus:border-green-500 transition-colors duration-200"
|
||||
@keyup.enter="saveTitle(chapter)"
|
||||
@keyup.esc="cancelEditingTitle(chapter)"
|
||||
ref="titleInput"
|
||||
/>
|
||||
<button
|
||||
@click="saveTitle(chapter)"
|
||||
class="text-green-600 hover:text-green-800"
|
||||
class="text-green-600 hover:text-green-800 p-1 rounded-full hover:bg-green-100 transition-colors duration-200"
|
||||
>
|
||||
<CheckIcon class="h-3 w-3" />
|
||||
<CheckIcon class="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
@click="cancelEditingTitle(chapter)"
|
||||
class="text-red-600 hover:text-red-800"
|
||||
class="text-red-600 hover:text-red-800 p-1 rounded-full hover:bg-red-100 transition-colors duration-200"
|
||||
>
|
||||
<XMarkIcon class="h-3 w-3" />
|
||||
<XMarkIcon class="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center space-x-1">
|
||||
<div class="flex items-center space-x-2">
|
||||
<button
|
||||
@click="removeFromVolume(chapter)"
|
||||
class="text-red-600 hover:text-red-800 text-xs"
|
||||
class="text-red-600 hover:text-red-800 text-xs font-medium hover:bg-red-100 px-3 py-1.5 rounded-lg transition-colors duration-200"
|
||||
>
|
||||
Retirer
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="chapter.isModified" class="w-2 h-2 bg-yellow-400 rounded-full"></div>
|
||||
<div v-if="chapter.isModified" class="w-3 h-3 bg-yellow-400 rounded-full shadow-sm"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -258,85 +290,108 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||
<button
|
||||
@click="handleSave"
|
||||
:disabled="isSaving || !hasChanges"
|
||||
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm disabled:opacity-50"
|
||||
>
|
||||
<span v-if="isSaving">Sauvegarde...</span>
|
||||
<span v-else>Sauvegarder</span>
|
||||
</button>
|
||||
<button
|
||||
@click="handleClose"
|
||||
:disabled="isSaving"
|
||||
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm disabled:opacity-50"
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
<!-- Footer Material Design -->
|
||||
<div class="bg-gray-50 px-6 py-4 sm:px-8 sm:py-6 border-t border-gray-200">
|
||||
<div class="flex flex-col sm:flex-row sm:justify-end sm:space-x-3 space-y-3 sm:space-y-0">
|
||||
<button
|
||||
@click="handleClose"
|
||||
:disabled="isSaving"
|
||||
class="w-full sm:w-auto inline-flex justify-center items-center rounded-lg border border-gray-300 bg-white px-6 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 transition-all duration-200 shadow-sm hover:shadow-md"
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
<button
|
||||
@click="handleSave"
|
||||
:disabled="isSaving || !hasChanges"
|
||||
class="w-full sm:w-auto inline-flex justify-center items-center rounded-lg border border-transparent bg-green-600 px-6 py-2.5 text-sm font-medium text-white hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 transition-all duration-200 shadow-md hover:shadow-lg"
|
||||
>
|
||||
<span v-if="isSaving" class="flex items-center space-x-2">
|
||||
<div class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
|
||||
<span>Sauvegarde...</span>
|
||||
</span>
|
||||
<span v-else>Sauvegarder</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal de création de volume -->
|
||||
<!-- Modal de création de volume Material Design -->
|
||||
<div v-if="showCreateVolumeModal" class="fixed inset-0 z-60 overflow-y-auto">
|
||||
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" @click="showCreateVolumeModal = false"></div>
|
||||
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full">
|
||||
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-4">Créer un nouveau volume</h3>
|
||||
<div class="fixed inset-0 bg-black/40 backdrop-blur-sm transition-opacity" @click="showCreateVolumeModal = false"></div>
|
||||
<div class="inline-block align-bottom bg-white rounded-2xl text-left overflow-hidden shadow-2xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full border border-gray-100">
|
||||
<div class="bg-gradient-to-r from-green-50 to-emerald-50 px-6 pt-6 pb-4 sm:px-8 sm:pb-6 border-b border-gray-100">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center">
|
||||
<PlusIcon class="h-5 w-5 text-green-600" />
|
||||
</div>
|
||||
<h3 class="text-lg font-medium text-gray-900">Créer un nouveau volume</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white px-6 py-6 sm:px-8 sm:py-6">
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">Numéro du volume</label>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Numéro du volume</label>
|
||||
<input
|
||||
v-model="newVolumeNumber"
|
||||
type="number"
|
||||
min="1"
|
||||
class="mt-1 block w-full border border-gray-300 rounded-md px-3 py-2 text-sm"
|
||||
class="block w-full border border-gray-300 rounded-lg px-4 py-3 text-sm focus:ring-2 focus:ring-green-500 focus:border-green-500 transition-colors duration-200"
|
||||
placeholder="Ex: 1"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="volumeExists" class="text-red-600 text-sm">
|
||||
Ce volume existe déjà.
|
||||
<div v-if="volumeExists" class="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg flex items-center space-x-2">
|
||||
<XMarkIcon class="h-4 w-4 text-red-600" />
|
||||
<span class="text-sm">Ce volume existe déjà.</span>
|
||||
</div>
|
||||
<div v-if="newVolumeNumber && !isValidVolumeNumber" class="text-red-600 text-sm">
|
||||
Le numéro de volume doit être entre 1 et 999.
|
||||
<div v-if="newVolumeNumber && !isValidVolumeNumber" class="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg flex items-center space-x-2">
|
||||
<XMarkIcon class="h-4 w-4 text-red-600" />
|
||||
<span class="text-sm">Le numéro de volume doit être entre 1 et 999.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||
<button
|
||||
@click="createVolume"
|
||||
:disabled="!isValidVolumeNumber || volumeExists"
|
||||
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm disabled:opacity-50"
|
||||
>
|
||||
Créer
|
||||
</button>
|
||||
<button
|
||||
@click="showCreateVolumeModal = false"
|
||||
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
<div class="bg-gray-50 px-6 py-4 sm:px-8 sm:py-6 border-t border-gray-200">
|
||||
<div class="flex flex-col sm:flex-row sm:justify-end sm:space-x-3 space-y-3 sm:space-y-0">
|
||||
<button
|
||||
@click="showCreateVolumeModal = false"
|
||||
class="w-full sm:w-auto inline-flex justify-center items-center rounded-lg border border-gray-300 bg-white px-6 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 transition-all duration-200 shadow-sm hover:shadow-md"
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
<button
|
||||
@click="createVolume"
|
||||
:disabled="!isValidVolumeNumber || volumeExists"
|
||||
class="w-full sm:w-auto inline-flex justify-center items-center rounded-lg border border-transparent bg-green-600 px-6 py-2.5 text-sm font-medium text-white hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 transition-all duration-200 shadow-md hover:shadow-lg"
|
||||
>
|
||||
Créer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal d'assignation -->
|
||||
<!-- Modal d'assignation Material Design -->
|
||||
<div v-if="showAssignModal" class="fixed inset-0 z-60 overflow-y-auto">
|
||||
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" @click="showAssignModal = false"></div>
|
||||
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full">
|
||||
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-4">Assigner le chapitre {{ selectedChapter?.number }}</h3>
|
||||
<div class="fixed inset-0 bg-black/40 backdrop-blur-sm transition-opacity" @click="showAssignModal = false"></div>
|
||||
<div class="inline-block align-bottom bg-white rounded-2xl text-left overflow-hidden shadow-2xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full border border-gray-100">
|
||||
<div class="bg-gradient-to-r from-green-50 to-emerald-50 px-6 pt-6 pb-4 sm:px-8 sm:pb-6 border-b border-gray-100">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center">
|
||||
<DocumentIcon class="h-5 w-5 text-green-600" />
|
||||
</div>
|
||||
<h3 class="text-lg font-medium text-gray-900">Assigner le chapitre {{ selectedChapter?.number }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white px-6 py-6 sm:px-8 sm:py-6">
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">Volume</label>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Volume</label>
|
||||
<select
|
||||
v-model="selectedVolumeForAssignment"
|
||||
class="mt-1 block w-full border border-gray-300 rounded-md px-3 py-2 text-sm"
|
||||
class="block w-full border border-gray-300 rounded-lg px-4 py-3 text-sm focus:ring-2 focus:ring-green-500 focus:border-green-500 transition-colors duration-200"
|
||||
>
|
||||
<option value="">Sélectionner un volume</option>
|
||||
<option v-for="volume in volumes" :key="volume.number" :value="volume.number">
|
||||
@@ -346,43 +401,52 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||
<button
|
||||
@click="confirmAssignToVolume"
|
||||
:disabled="!selectedVolumeForAssignment"
|
||||
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm disabled:opacity-50"
|
||||
>
|
||||
Assigner
|
||||
</button>
|
||||
<button
|
||||
@click="showAssignModal = false"
|
||||
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
<div class="bg-gray-50 px-6 py-4 sm:px-8 sm:py-6 border-t border-gray-200">
|
||||
<div class="flex flex-col sm:flex-row sm:justify-end sm:space-x-3 space-y-3 sm:space-y-0">
|
||||
<button
|
||||
@click="showAssignModal = false"
|
||||
class="w-full sm:w-auto inline-flex justify-center items-center rounded-lg border border-gray-300 bg-white px-6 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 transition-all duration-200 shadow-sm hover:shadow-md"
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
<button
|
||||
@click="confirmAssignToVolume"
|
||||
:disabled="!selectedVolumeForAssignment"
|
||||
class="w-full sm:w-auto inline-flex justify-center items-center rounded-lg border border-transparent bg-green-600 px-6 py-2.5 text-sm font-medium text-white hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 transition-all duration-200 shadow-md hover:shadow-lg"
|
||||
>
|
||||
Assigner
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal de déplacement multiple -->
|
||||
<!-- Modal de déplacement multiple Material Design -->
|
||||
<div v-if="showMoveToVolumeModal" class="fixed inset-0 z-60 overflow-y-auto">
|
||||
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" @click="showMoveToVolumeModal = false"></div>
|
||||
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full">
|
||||
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-4">Déplacer {{ selectedChapters.length }} chapitre(s)</h3>
|
||||
<div class="fixed inset-0 bg-black/40 backdrop-blur-sm transition-opacity" @click="showMoveToVolumeModal = false"></div>
|
||||
<div class="inline-block align-bottom bg-white rounded-2xl text-left overflow-hidden shadow-2xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full border border-gray-100">
|
||||
<div class="bg-gradient-to-r from-green-50 to-emerald-50 px-6 pt-6 pb-4 sm:px-8 sm:pb-6 border-b border-gray-100">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center">
|
||||
<ArrowPathIcon class="h-5 w-5 text-green-600" />
|
||||
</div>
|
||||
<h3 class="text-lg font-medium text-gray-900">Déplacer {{ selectedChapters.length }} chapitre(s)</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white px-6 py-6 sm:px-8 sm:py-6">
|
||||
<div class="space-y-4">
|
||||
<div class="bg-blue-50 p-3 rounded-lg">
|
||||
<p class="text-sm text-blue-800">
|
||||
<div class="bg-green-50 p-4 rounded-lg border border-green-200">
|
||||
<p class="text-sm text-green-800 font-medium">
|
||||
Chapitres sélectionnés : {{ selectedChapters.map(c => c.number).join(', ') }}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">Volume de destination</label>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Volume de destination</label>
|
||||
<select
|
||||
v-model="selectedVolumeForMove"
|
||||
class="mt-1 block w-full border border-gray-300 rounded-md px-3 py-2 text-sm"
|
||||
class="block w-full border border-gray-300 rounded-lg px-4 py-3 text-sm focus:ring-2 focus:ring-green-500 focus:border-green-500 transition-colors duration-200"
|
||||
>
|
||||
<option value="">Sélectionner un volume</option>
|
||||
<option v-for="volume in volumes" :key="volume.number" :value="volume.number">
|
||||
@@ -393,20 +457,81 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||
<button
|
||||
@click="confirmMoveToVolume"
|
||||
:disabled="!selectedVolumeForMove"
|
||||
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm disabled:opacity-50"
|
||||
>
|
||||
Déplacer
|
||||
</button>
|
||||
<button
|
||||
@click="showMoveToVolumeModal = false"
|
||||
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
<div class="bg-gray-50 px-6 py-4 sm:px-8 sm:py-6 border-t border-gray-200">
|
||||
<div class="flex flex-col sm:flex-row sm:justify-end sm:space-x-3 space-y-3 sm:space-y-0">
|
||||
<button
|
||||
@click="showMoveToVolumeModal = false"
|
||||
class="w-full sm:w-auto inline-flex justify-center items-center rounded-lg border border-gray-300 bg-white px-6 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 transition-all duration-200 shadow-sm hover:shadow-md"
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
<button
|
||||
@click="confirmMoveToVolume"
|
||||
:disabled="!selectedVolumeForMove"
|
||||
class="w-full sm:w-auto inline-flex justify-center items-center rounded-lg border border-transparent bg-green-600 px-6 py-2.5 text-sm font-medium text-white hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 transition-all duration-200 shadow-md hover:shadow-lg"
|
||||
>
|
||||
Déplacer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal de séparation du volume fourre-tout Material Design -->
|
||||
<div v-if="showSplitVolumeZeroModal" class="fixed inset-0 z-60 overflow-y-auto">
|
||||
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div class="fixed inset-0 bg-black/40 backdrop-blur-sm transition-opacity" @click="showSplitVolumeZeroModal = false"></div>
|
||||
<div class="inline-block align-bottom bg-white rounded-2xl text-left overflow-hidden shadow-2xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full border border-gray-100">
|
||||
<div class="bg-gradient-to-r from-green-50 to-emerald-50 px-6 pt-6 pb-4 sm:px-8 sm:pb-6 border-b border-gray-100">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center">
|
||||
<ArrowPathIcon class="h-5 w-5 text-green-600" />
|
||||
</div>
|
||||
<h3 class="text-lg font-medium text-gray-900">Séparer le volume 00</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white px-6 py-6 sm:px-8 sm:py-6">
|
||||
<div class="space-y-4">
|
||||
<div class="bg-green-50 p-4 rounded-lg border border-green-200">
|
||||
<p class="text-sm text-green-800 font-medium">
|
||||
Le volume 00 contient {{ volumeZeroChapters.length }} chapitres et sera séparé en {{ numberOfNewVolumes }} nouveaux volumes.
|
||||
</p>
|
||||
</div>
|
||||
<div class="bg-green-50 p-4 rounded-lg border border-green-200">
|
||||
<p class="text-sm text-green-800">
|
||||
<strong>Moyenne des autres volumes :</strong> {{ averageChaptersPerVolume.toFixed(1) }} chapitres par volume
|
||||
</p>
|
||||
<p class="text-sm text-green-800 mt-1">
|
||||
<strong>Chapitres par nouveau volume :</strong> {{ chaptersPerNewVolume }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<h4 class="text-sm font-medium text-gray-700">Répartition proposée :</h4>
|
||||
<div class="space-y-2 max-h-32 overflow-y-auto">
|
||||
<div v-for="(group, index) in proposedVolumeGroups" :key="index" class="text-sm text-gray-600 bg-gray-50 p-3 rounded-lg border border-gray-200">
|
||||
<strong>Volume {{ group.volumeNumber }} :</strong>
|
||||
Chapitres {{ group.chapters.map(c => c.number).join(', ') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 px-6 py-4 sm:px-8 sm:py-6 border-t border-gray-200">
|
||||
<div class="flex flex-col sm:flex-row sm:justify-end sm:space-x-3 space-y-3 sm:space-y-0">
|
||||
<button
|
||||
@click="showSplitVolumeZeroModal = false"
|
||||
class="w-full sm:w-auto inline-flex justify-center items-center rounded-lg border border-gray-300 bg-white px-6 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 transition-all duration-200 shadow-sm hover:shadow-md"
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
<button
|
||||
@click="confirmSplitVolumeZero"
|
||||
class="w-full sm:w-auto inline-flex justify-center items-center rounded-lg border border-transparent bg-green-600 px-6 py-2.5 text-sm font-medium text-white hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 transition-all duration-200 shadow-md hover:shadow-lg"
|
||||
>
|
||||
Confirmer la séparation
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -416,6 +541,7 @@
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ArrowPathIcon,
|
||||
CheckIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronRightIcon,
|
||||
@@ -461,6 +587,7 @@ const localChapters = ref([]);
|
||||
const showCreateVolumeModal = ref(false);
|
||||
const showAssignModal = ref(false);
|
||||
const showMoveToVolumeModal = ref(false);
|
||||
const showSplitVolumeZeroModal = ref(false);
|
||||
const showUnassignedChapters = ref(true);
|
||||
const newVolumeNumber = ref('');
|
||||
const selectedChapter = ref(null);
|
||||
@@ -532,6 +659,66 @@ const hasChanges = computed(() => {
|
||||
return localChapters.value.some(chapter => chapter.isModified);
|
||||
});
|
||||
|
||||
// Computed properties pour la séparation du volume fourre-tout
|
||||
const volumeZeroChapters = computed(() => {
|
||||
return localChapters.value.filter(chapter => chapter.volume === 0);
|
||||
});
|
||||
|
||||
const hasVolumeZero = computed(() => {
|
||||
return volumeZeroChapters.value.length > 0;
|
||||
});
|
||||
|
||||
const otherVolumes = computed(() => {
|
||||
return volumes.value.filter(volume => volume.number !== 0 && volume.chapters.length > 0);
|
||||
});
|
||||
|
||||
const averageChaptersPerVolume = computed(() => {
|
||||
if (otherVolumes.value.length === 0) return 10; // Valeur par défaut si aucun autre volume
|
||||
const totalChapters = otherVolumes.value.reduce((sum, volume) => sum + volume.chapters.length, 0);
|
||||
return totalChapters / otherVolumes.value.length;
|
||||
});
|
||||
|
||||
const canSplitVolumeZero = computed(() => {
|
||||
return hasVolumeZero.value && volumeZeroChapters.value.length > averageChaptersPerVolume.value;
|
||||
});
|
||||
|
||||
const numberOfNewVolumes = computed(() => {
|
||||
if (!canSplitVolumeZero.value) return 0;
|
||||
return Math.ceil(volumeZeroChapters.value.length / averageChaptersPerVolume.value);
|
||||
});
|
||||
|
||||
const chaptersPerNewVolume = computed(() => {
|
||||
if (!canSplitVolumeZero.value) return 0;
|
||||
return Math.ceil(volumeZeroChapters.value.length / numberOfNewVolumes.value);
|
||||
});
|
||||
|
||||
const proposedVolumeGroups = computed(() => {
|
||||
if (!canSplitVolumeZero.value) return [];
|
||||
|
||||
const chapters = [...volumeZeroChapters.value].sort((a, b) => a.number - b.number); // Tri croissant par numéro
|
||||
const groups = [];
|
||||
const chaptersPerGroup = chaptersPerNewVolume.value;
|
||||
|
||||
// Trouver le prochain numéro de volume disponible
|
||||
const existingVolumeNumbers = volumes.value.map(v => v.number).filter(n => n !== 0);
|
||||
const maxVolumeNumber = existingVolumeNumbers.length > 0 ? Math.max(...existingVolumeNumbers) : 0;
|
||||
|
||||
for (let i = 0; i < numberOfNewVolumes.value; i++) {
|
||||
const startIndex = i * chaptersPerGroup;
|
||||
const endIndex = Math.min(startIndex + chaptersPerGroup, chapters.length);
|
||||
const groupChapters = chapters.slice(startIndex, endIndex);
|
||||
|
||||
if (groupChapters.length > 0) {
|
||||
groups.push({
|
||||
volumeNumber: maxVolumeNumber + i + 1,
|
||||
chapters: groupChapters
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return groups;
|
||||
});
|
||||
|
||||
// Méthodes
|
||||
const handleClose = () => {
|
||||
if (!props.isSaving) {
|
||||
@@ -662,6 +849,21 @@ const confirmMoveToVolume = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const confirmSplitVolumeZero = () => {
|
||||
if (!canSplitVolumeZero.value) return;
|
||||
|
||||
// Appliquer la répartition proposée
|
||||
proposedVolumeGroups.value.forEach(group => {
|
||||
group.chapters.forEach(chapter => {
|
||||
chapter.volume = group.volumeNumber;
|
||||
chapter.isModified = true;
|
||||
});
|
||||
});
|
||||
|
||||
// Fermer la modale
|
||||
showSplitVolumeZeroModal.value = false;
|
||||
};
|
||||
|
||||
// Initialiser les chapitres locaux quand les props changent
|
||||
watch(() => props.chapters, (newChapters) => {
|
||||
localChapters.value = newChapters.map(chapter => ({
|
||||
@@ -684,6 +886,7 @@ watch(() => props.isOpen, (isOpen) => {
|
||||
showCreateVolumeModal.value = false;
|
||||
showAssignModal.value = false;
|
||||
showMoveToVolumeModal.value = false;
|
||||
showSplitVolumeZeroModal.value = false;
|
||||
showUnassignedChapters.value = true;
|
||||
newVolumeNumber.value = '';
|
||||
selectedChapter.value = null;
|
||||
|
||||
Reference in New Issue
Block a user