fix: amélioration du système de notifications

- Correction de l'affichage du texte dans le toast (suppression de w-0/truncate)
- Déplacement des toasts en bas à gauche avec animation slide depuis la gauche
- Inversion de l'ordre des éléments : bouton fermeture > texte > icône > bande couleur
- Fix timing : ChapterScrapingStarted synchrone pour notif "démarrage" avant le scraping
- Ajout make notify-test pour tester les 4 types de notifications

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ext.jeremy.guillot@maxicoffee.domains
2026-03-12 18:55:15 +01:00
parent 41ca08f20e
commit e5c319db79
3 changed files with 29 additions and 22 deletions

View File

@@ -145,6 +145,13 @@ twig-extension: ## Create a new twig extension
stimulus: ## Create a new stimulus controller stimulus: ## Create a new stimulus controller
@$(SYMFONY) make:stimulus-controller @$(SYMFONY) make:stimulus-controller
notify-test: ## Envoie les 4 types de notifications de test avec 2s d'intervalle
@for type in info success error warning; do \
$(SYMFONY) app:notify:test --type=$$type --message="Test $$type depuis Mangarr"; \
echo "[$$type] envoyé"; \
sleep 2; \
done
consume-commands: ## Consume commands messages consume-commands: ## Consume commands messages
@$(SYMFONY) messenger:consume commands -vv @$(SYMFONY) messenger:consume commands -vv

View File

@@ -1,32 +1,21 @@
<template> <template>
<div class="fixed top-4 right-4 z-50 space-y-2"> <div class="fixed bottom-4 left-4 z-50 flex flex-col-reverse gap-2">
<TransitionGroup <TransitionGroup
name="notification" name="notification"
tag="div" tag="div"
class="space-y-2" class="flex flex-col-reverse gap-2"
> >
<div <div
v-for="notification in notifications" v-for="notification in notifications"
:key="notification.id" :key="notification.id"
:class="[ :class="[
'max-w-sm w-full bg-white shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden', 'max-w-md w-full bg-white shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden',
getNotificationClass(notification.type) getNotificationClass(notification.type)
]" ]"
> >
<div class="p-4"> <div class="p-4">
<div class="flex items-start"> <div class="flex items-start">
<div class="flex-shrink-0"> <div class="flex-shrink-0 mr-3">
<component :is="getIcon(notification.type)" :class="[
'h-6 w-6',
getIconClass(notification.type)
]" />
</div>
<div class="ml-3 w-0 flex-1 pt-0.5">
<p class="text-sm font-medium text-gray-900">
{{ notification.message }}
</p>
</div>
<div class="ml-4 flex-shrink-0 flex">
<button <button
@click="removeNotification(notification.id)" @click="removeNotification(notification.id)"
class="bg-white rounded-md inline-flex text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" class="bg-white rounded-md inline-flex text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
@@ -35,6 +24,17 @@
<XMarkIcon class="h-5 w-5" /> <XMarkIcon class="h-5 w-5" />
</button> </button>
</div> </div>
<div class="flex-1 pt-0.5 min-w-0">
<p class="text-sm font-medium text-gray-900 break-words">
{{ notification.message }}
</p>
</div>
<div class="flex-shrink-0 ml-3">
<component :is="getIcon(notification.type)" :class="[
'h-6 w-6',
getIconClass(notification.type)
]" />
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -66,10 +66,10 @@ const getIcon = (type) => {
const getNotificationClass = (type) => { const getNotificationClass = (type) => {
const classes = { const classes = {
success: 'border-l-4 border-green-400', success: 'border-r-4 border-green-400',
error: 'border-l-4 border-red-400', error: 'border-r-4 border-red-400',
warning: 'border-l-4 border-yellow-400', warning: 'border-r-4 border-yellow-400',
info: 'border-l-4 border-blue-400' info: 'border-r-4 border-blue-400'
}; };
return classes[type] || classes.info; return classes[type] || classes.info;
}; };
@@ -93,11 +93,11 @@ const getIconClass = (type) => {
.notification-enter-from { .notification-enter-from {
opacity: 0; opacity: 0;
transform: translateX(100%); transform: translateX(-100%);
} }
.notification-leave-to { .notification-leave-to {
opacity: 0; opacity: 0;
transform: translateX(100%); transform: translateX(-100%);
} }
</style> </style>

View File

@@ -29,7 +29,7 @@ framework:
'App\Domain\Manga\Application\Command\RefreshMangaChapters': commands 'App\Domain\Manga\Application\Command\RefreshMangaChapters': commands
# Events spécifiques (pour compatibilité, peuvent être supprimés si tous implémentent AsyncDomainEvent) # Events spécifiques (pour compatibilité, peuvent être supprimés si tous implémentent AsyncDomainEvent)
'App\Domain\Scraping\Domain\Event\ChapterScrapingStarted': events # ChapterScrapingStarted est synchrone pour que la notif "démarrage" arrive AVANT le scraping
'App\Domain\Scraping\Domain\Event\ChapterScrapingCompleted': events 'App\Domain\Scraping\Domain\Event\ChapterScrapingCompleted': events
'App\Domain\Scraping\Domain\Event\ChapterScrapingFailed': events 'App\Domain\Scraping\Domain\Event\ChapterScrapingFailed': events
'App\Domain\Manga\Domain\Event\ChapterReadyForScraping': events 'App\Domain\Manga\Domain\Event\ChapterReadyForScraping': events