diff --git a/assets/vue/app/domain/manga/application/store/mangaStore.js b/assets/vue/app/domain/manga/application/store/mangaStore.js index 2cb9c19..156c78a 100644 --- a/assets/vue/app/domain/manga/application/store/mangaStore.js +++ b/assets/vue/app/domain/manga/application/store/mangaStore.js @@ -195,11 +195,37 @@ export const useMangaStore = defineStore('manga', { this.chaptersError = null; try { + // Déclenche la récupération initiale des chapitres depuis la source externe await mangaRepository.fetchMangaChapters(mangaId); - this.mangaChapters[mangaId] = chaptersData; - console.log('Chapitres récupérés avec succès'); + console.log('Récupération initiale des chapitres déclenchée avec succès'); + + // Note: Les nouveaux chapitres seront disponibles après traitement asynchrone + // Le MercureListener se chargera de mettre à jour l'interface } catch (err) { this.chaptersError = err.message; + console.error('Erreur lors de la récupération des chapitres:', err); + throw err; + } finally { + this.loadingChapters = false; + } + }, + + async refreshMangaChapters(mangaId) { + if (this.loadingChapters) return; + this.loadingChapters = true; + this.chaptersError = null; + + try { + // Déclenche la synchronisation incrémentale avec scraping automatique + await mangaRepository.refreshMangaChapters(mangaId); + console.log('Synchronisation incrémentale déclenchée avec succès'); + + // Note: Les chapitres mis à jour seront disponibles après traitement asynchrone + // Le MercureListener se chargera de mettre à jour l'interface + } catch (err) { + this.chaptersError = err.message; + console.error('Erreur lors de la synchronisation des chapitres:', err); + throw err; } finally { this.loadingChapters = false; } diff --git a/assets/vue/app/domain/manga/infrastructure/api/apiMangaRepository.js b/assets/vue/app/domain/manga/infrastructure/api/apiMangaRepository.js index dfe22d1..4eac98c 100644 --- a/assets/vue/app/domain/manga/infrastructure/api/apiMangaRepository.js +++ b/assets/vue/app/domain/manga/infrastructure/api/apiMangaRepository.js @@ -142,6 +142,26 @@ export class ApiMangaRepository { } } + async refreshMangaChapters(mangaId) { + try { + const response = await fetch(`/api/manga/${mangaId}/chapters/refresh`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({}) + }); + if (!response.ok) { + throw new Error('Failed to refresh manga chapters'); + } + // L'endpoint retourne 202 (Accepted), pas de contenu JSON à parser + return true; + } catch (error) { + console.error('API Error:', error); + throw error; + } + } + async searchChapter(chapterId) { try { const response = await fetch('/api/scraping/chapters', { @@ -321,4 +341,40 @@ export class ApiMangaRepository { throw error; } } + + async toggleMonitoring(mangaId, enabled) { + try { + const response = await fetch(`/api/manga/${mangaId}/monitoring/toggle`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ enabled }) + }); + + if (!response.ok) { + // Tenter de récupérer le message d'erreur détaillé de l'API + let errorMessage = 'Failed to toggle monitoring'; + try { + const errorData = await response.json(); + if (errorData.detail) { + errorMessage = errorData.detail; + } else if (errorData.message) { + errorMessage = errorData.message; + } else if (errorData.violations && errorData.violations.length > 0) { + errorMessage = errorData.violations.map(v => v.message).join(', '); + } + } catch (parseError) { + console.warn('Could not parse error response:', parseError); + } + throw new Error(errorMessage); + } + + // L'endpoint retourne un statut 204 (No Content), donc pas de données à retourner + return true; + } catch (error) { + console.error('API Error:', error); + throw error; + } + } } diff --git a/assets/vue/app/domain/manga/presentation/composables/useMangaMonitoring.js b/assets/vue/app/domain/manga/presentation/composables/useMangaMonitoring.js new file mode 100644 index 0000000..2935e7d --- /dev/null +++ b/assets/vue/app/domain/manga/presentation/composables/useMangaMonitoring.js @@ -0,0 +1,67 @@ +import { ref } from 'vue'; +import { useNotifications } from '../../../../shared/composables/useNotifications'; +import { ApiMangaRepository } from '../../infrastructure/api/apiMangaRepository'; + +const mangaRepository = new ApiMangaRepository(); + +export function useMangaMonitoring() { + const { showSuccess, showError } = useNotifications(); + + const isToggling = ref(false); + const toggleError = ref(null); + + const toggleMonitoring = async (mangaId, enabled) => { + if (isToggling.value || !mangaId) return; + + isToggling.value = true; + toggleError.value = null; + + try { + console.log(`${enabled ? 'Activation' : 'Désactivation'} du monitoring pour le manga ${mangaId}`); + + await mangaRepository.toggleMonitoring(mangaId, enabled); + + const message = enabled + ? 'Monitoring activé avec succès. Vous recevrez les nouveaux chapitres automatiquement.' + : 'Monitoring désactivé avec succès. Les nouveaux chapitres ne seront plus téléchargés automatiquement.'; + + showSuccess(message); + + console.log(`Monitoring ${enabled ? 'activé' : 'désactivé'} avec succès`); + return true; + } catch (error) { + console.error('Erreur lors du changement de monitoring:', error); + toggleError.value = error.message || 'Erreur lors du changement de monitoring'; + + const errorMessage = enabled + ? `Erreur lors de l'activation du monitoring: ${error.message || 'Une erreur inattendue est survenue'}` + : `Erreur lors de la désactivation du monitoring: ${error.message || 'Une erreur inattendue est survenue'}`; + + showError(errorMessage); + throw error; + } finally { + isToggling.value = false; + } + }; + + const enableMonitoring = async (mangaId) => { + return await toggleMonitoring(mangaId, true); + }; + + const disableMonitoring = async (mangaId) => { + return await toggleMonitoring(mangaId, false); + }; + + const clearError = () => { + toggleError.value = null; + }; + + return { + isToggling, + toggleError, + toggleMonitoring, + enableMonitoring, + disableMonitoring, + clearError + }; +} diff --git a/assets/vue/app/domain/manga/presentation/composables/useMangaRefresh.js b/assets/vue/app/domain/manga/presentation/composables/useMangaRefresh.js new file mode 100644 index 0000000..93944c5 --- /dev/null +++ b/assets/vue/app/domain/manga/presentation/composables/useMangaRefresh.js @@ -0,0 +1,48 @@ +import { ref } from 'vue'; +import { useNotifications } from '../../../../shared/composables/useNotifications'; +import { useMangaStore } from '../../application/store/mangaStore'; + +export function useMangaRefresh() { + const mangaStore = useMangaStore(); + const { showSuccess, showError } = useNotifications(); + + const isRefreshing = ref(false); + const refreshError = ref(null); + + const refreshMetadata = async (mangaId) => { + if (isRefreshing.value || !mangaId) return; + + isRefreshing.value = true; + refreshError.value = null; + + try { + console.log(`Début du refresh des métadonnées pour le manga ${mangaId}`); + + // Appel à l'endpoint de refresh des chapitres + await mangaStore.refreshMangaChapters(mangaId); + + showSuccess('Refresh des métadonnées lancé avec succès. Les nouveaux chapitres apparaîtront sous peu.'); + + console.log('Refresh des métadonnées déclenché avec succès'); + return true; + } catch (error) { + console.error('Erreur lors du refresh des métadonnées:', error); + refreshError.value = error.message || 'Erreur lors du refresh des métadonnées'; + showError(`Erreur lors du refresh: ${error.message || 'Une erreur inattendue est survenue'}`); + throw error; + } finally { + isRefreshing.value = false; + } + }; + + const clearError = () => { + refreshError.value = null; + }; + + return { + isRefreshing, + refreshError, + refreshMetadata, + clearError + }; +} diff --git a/assets/vue/app/domain/manga/presentation/pages/MangaDetails.vue b/assets/vue/app/domain/manga/presentation/pages/MangaDetails.vue index bc00818..c09c20a 100644 --- a/assets/vue/app/domain/manga/presentation/pages/MangaDetails.vue +++ b/assets/vue/app/domain/manga/presentation/pages/MangaDetails.vue @@ -1,5 +1,8 @@