diff --git a/assets/vue/app/domain/manga/application/store/mangaStore.js b/assets/vue/app/domain/manga/application/store/mangaStore.js index 48cdc88..b8c4926 100644 --- a/assets/vue/app/domain/manga/application/store/mangaStore.js +++ b/assets/vue/app/domain/manga/application/store/mangaStore.js @@ -209,6 +209,28 @@ export const useMangaStore = defineStore('manga', { console.error('Erreur lors de la suppression du chapitre:', error); throw error; } + }, + + // --- Download Chapter Action --- + async downloadChapter(chapterId) { + try { + await mangaRepository.downloadChapter(chapterId); + } catch (error) { + console.error('Erreur lors du téléchargement du chapitre:', error); + throw error; + } + }, + + // --- Hide Chapter Action --- + async hideChapter(chapterId, mangaId) { + try { + await mangaRepository.hideChapter(chapterId); + // Recharger la liste des chapitres depuis l'API + await this.loadChapters(mangaId); + } catch (error) { + console.error('Erreur lors du masquage du chapitre:', error); + throw error; + } } } }); diff --git a/assets/vue/app/domain/manga/infrastructure/api/apiMangaRepository.js b/assets/vue/app/domain/manga/infrastructure/api/apiMangaRepository.js index d9272fc..c86c67c 100644 --- a/assets/vue/app/domain/manga/infrastructure/api/apiMangaRepository.js +++ b/assets/vue/app/domain/manga/infrastructure/api/apiMangaRepository.js @@ -187,5 +187,60 @@ export class ApiMangaRepository { console.error('API Error:', error); throw error; } + } + + async downloadChapter(chapterId) { + try { + const response = await fetch(`/api/manga/chapters/${chapterId}/download`); + if (!response.ok) { + throw new Error('Failed to download chapter'); + } + + // Récupérer le nom du fichier depuis les headers + const contentDisposition = response.headers.get('Content-Disposition'); + let filename = `chapter-${chapterId}.cbz`; + + if (contentDisposition) { + const filenameMatch = contentDisposition.match(/filename="?(.+)"?/); + if (filenameMatch) { + filename = filenameMatch[1]; + } + } + + // Créer un blob à partir de la réponse + const blob = await response.blob(); + + // Créer un lien de téléchargement temporaire + const url = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + + // Nettoyer + document.body.removeChild(link); + window.URL.revokeObjectURL(url); + + return true; + } catch (error) { + console.error('API Error:', error); + throw error; + } + } + + async hideChapter(chapterId) { + try { + const response = await fetch(`/api/manga/chapters/${chapterId}`, { + method: 'DELETE' + }); + if (!response.ok) { + throw new Error('Failed to hide chapter'); + } + return true; + } catch (error) { + console.error('API Error:', error); + throw error; + } } } diff --git a/assets/vue/app/domain/manga/presentation/components/MangaChapter.vue b/assets/vue/app/domain/manga/presentation/components/MangaChapter.vue index 7be15d8..aab28fd 100644 --- a/assets/vue/app/domain/manga/presentation/components/MangaChapter.vue +++ b/assets/vue/app/domain/manga/presentation/components/MangaChapter.vue @@ -23,10 +23,10 @@ - - @@ -46,16 +46,36 @@ import { useMangaStore } from '../../application/store/mangaStore'; mangaSlug: { type: String, required: true + }, + mangaId: { + type: Number, + required: true } }); const store = useMangaStore(); const isLoading = ref(false); + const isDownloading = ref(false); + const isHiding = ref(false); const buttonClass = computed(() => { return isLoading.value ? 'text-yellow-500 cursor-wait' : 'text-gray-500 hover:text-green-500'; }); + const downloadButtonClass = computed(() => { + if (isDownloading.value) { + return 'text-yellow-500 cursor-wait'; + } + if (!props.chapter.isAvailable) { + return 'text-gray-300 cursor-not-allowed'; + } + return 'text-gray-500 hover:text-green-500'; + }); + + const hideButtonClass = computed(() => { + return isHiding.value ? 'text-yellow-500 cursor-wait' : 'text-gray-500 hover:text-green-500'; + }); + // Surveiller les changements d'état du chapitre watch( () => props.chapter.isAvailable, @@ -96,12 +116,32 @@ import { useMangaStore } from '../../application/store/mangaStore'; }; const handleDownload = async () => { - // TODO: Implémenter le téléchargement du chapitre - console.log('Téléchargement du chapitre:', props.chapter.id); + try { + console.log(`MangaChapter: Téléchargement du chapitre ${props.chapter.number} (ID: ${props.chapter.id})`); + // Montrer l'indicateur de chargement + isDownloading.value = true; + + await store.downloadChapter(props.chapter.id); + } catch (error) { + console.error('Erreur lors du téléchargement du chapitre:', error); + } finally { + // Arrêter l'indicateur de chargement + isDownloading.value = false; + } }; const handleHide = async () => { - // TODO: Implémenter le masquage du chapitre - console.log('Masquage du chapitre:', props.chapter.id); + try { + console.log(`MangaChapter: Masquage du chapitre ${props.chapter.number} (ID: ${props.chapter.id})`); + // Montrer l'indicateur de chargement + isHiding.value = true; + + await store.hideChapter(props.chapter.id, props.mangaId); + } catch (error) { + console.error('Erreur lors du masquage du chapitre:', error); + } finally { + // Arrêter l'indicateur de chargement + isHiding.value = false; + } }; diff --git a/assets/vue/app/domain/manga/presentation/components/MangaChapterList.vue b/assets/vue/app/domain/manga/presentation/components/MangaChapterList.vue index 6ce92a4..2248cbe 100644 --- a/assets/vue/app/domain/manga/presentation/components/MangaChapterList.vue +++ b/assets/vue/app/domain/manga/presentation/components/MangaChapterList.vue @@ -13,7 +13,8 @@ v-for="chapter in chapters" :key="chapter.id" :chapter="chapter" - :manga-slug="mangaSlug" /> + :manga-slug="mangaSlug" + :manga-id="mangaId" /> @@ -30,6 +31,10 @@ mangaSlug: { type: String, required: true + }, + mangaId: { + type: Number, + required: true } }); diff --git a/assets/vue/app/domain/manga/presentation/components/MangaVolume.vue b/assets/vue/app/domain/manga/presentation/components/MangaVolume.vue index 4934fa2..abc1da6 100644 --- a/assets/vue/app/domain/manga/presentation/components/MangaVolume.vue +++ b/assets/vue/app/domain/manga/presentation/components/MangaVolume.vue @@ -48,7 +48,7 @@ - +
@@ -61,15 +61,15 @@ diff --git a/assets/vue/app/domain/manga/presentation/pages/MangaDetails.vue b/assets/vue/app/domain/manga/presentation/pages/MangaDetails.vue index 5a4a41b..ae83d46 100644 --- a/assets/vue/app/domain/manga/presentation/pages/MangaDetails.vue +++ b/assets/vue/app/domain/manga/presentation/pages/MangaDetails.vue @@ -22,7 +22,7 @@
{{ errorVolumes.message || 'Une erreur est survenue lors du chargement des volumes.' }}
- +
diff --git a/src/Domain/Manga/Application/CommandHandler/DeleteChapterHandler.php b/src/Domain/Manga/Application/CommandHandler/DeleteChapterHandler.php index d4ad5c7..4a96ea7 100644 --- a/src/Domain/Manga/Application/CommandHandler/DeleteChapterHandler.php +++ b/src/Domain/Manga/Application/CommandHandler/DeleteChapterHandler.php @@ -5,6 +5,8 @@ namespace App\Domain\Manga\Application\CommandHandler; use App\Domain\Manga\Application\Command\DeleteChapter; use App\Domain\Manga\Domain\Contract\Repository\ChapterRepositoryInterface; use App\Domain\Manga\Domain\Exception\ChapterNotFoundException; +use App\Domain\Manga\Domain\Model\Chapter; +use App\Domain\Manga\Domain\Model\ValueObject\ChapterId; use App\Domain\Shared\Domain\Contract\CommandHandlerInterface; use App\Domain\Shared\Domain\Contract\CommandInterface; @@ -24,16 +26,15 @@ readonly class DeleteChapterHandler implements CommandHandlerInterface throw new ChapterNotFoundException($command->chapterId); } - // Soft delete by setting isVisible to false - $updatedChapter = new \App\Domain\Manga\Domain\Model\Chapter( - new \App\Domain\Manga\Domain\Model\ValueObject\ChapterId($chapter->getId()), - $chapter->getMangaId(), - $chapter->getNumber(), - $chapter->getTitle(), - $chapter->getVolume(), - false, // isVisible = false (soft delete) - $chapter->isAvailable(), - $chapter->getCreatedAt() + $updatedChapter = new Chapter( + id: new ChapterId($chapter->getId()), + mangaId: $chapter->getMangaId(), + number: $chapter->getNumber(), + title: $chapter->getTitle(), + volume: $chapter->getVolume(), + isVisible: false, + cbzPath: $chapter->getCbzPath(), + createdAt: $chapter->getCreatedAt() ); $this->chapterRepository->save($updatedChapter);