feat: dark mode complet + préférences utilisateur

- Ajout du store userPreferencesStore (thème, vue, tri, pagination, lecteur)
- Page UserPreferencesPage pour configurer toutes les préférences
- Câblage des prefs dans HomePage (viewMode, sortBy, itemsPerPage), readerStore (fallback prefs), ChapterReader (autoHide, autoFullscreen, sync), useNotifications (toastDuration)
- Thème sombre (dark: Tailwind) sur tous les composants Vue : Layout, Pagination, NotificationToast, MangaCard, MangaVolume, MangaDetails, AddManga, HomePage, ActivityPage, JobItem, MangaDeleteModal, MangaEditModal, MangaPreferredSourcesModal, ManageChaptersModal, MangaChapterList, MangaChapter, ConversionPage, FileUploadArea, ConversionProgress, NewImportPage, FileImportCard, MangaMatchCard, StatusBadge, ImportResults
- i18n partiellement initialisé

Jeremy Guillot
This commit is contained in:
ext.jeremy.guillot@maxicoffee.domains
2026-03-12 20:38:29 +01:00
parent 48d819ba72
commit ec1ef8fe68
36 changed files with 2832 additions and 317 deletions

View File

@@ -1,11 +1,11 @@
<template>
<tr
class="border-b border-gray-200 hover:bg-gray-50 transition duration-150 ease-in-out"
class="border-b border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition duration-150 ease-in-out"
:class="{
'bg-yellow-50': job.status === 'pending',
'bg-blue-50': job.status === 'in_progress',
'bg-green-50': job.status === 'completed',
'bg-red-50': job.status === 'failed'
'bg-yellow-50 dark:bg-yellow-900/20': job.status === 'pending',
'bg-blue-50 dark:bg-blue-900/20': job.status === 'in_progress',
'bg-green-50 dark:bg-green-900/20': job.status === 'completed',
'bg-red-50 dark:bg-red-900/20': job.status === 'failed'
}">
<td class="py-4 px-4 text-center">
<input type="checkbox" class="form-checkbox h-5 w-5 text-green-600" />
@@ -20,37 +20,37 @@
<span
class="px-2 py-1 text-xs rounded-full"
:class="{
'bg-yellow-100 text-yellow-800': job.status === 'pending',
'bg-blue-100 text-blue-800': job.status === 'in_progress',
'bg-green-100 text-green-800': job.status === 'completed',
'bg-red-100 text-red-800': job.status === 'failed'
'bg-yellow-100 dark:bg-yellow-900/40 text-yellow-800 dark:text-yellow-300': job.status === 'pending',
'bg-blue-100 dark:bg-blue-900/40 text-blue-800 dark:text-blue-300': job.status === 'in_progress',
'bg-green-100 dark:bg-green-900/40 text-green-800 dark:text-green-300': job.status === 'completed',
'bg-red-100 dark:bg-red-900/40 text-red-800 dark:text-red-300': job.status === 'failed'
}">
{{ job.status }}
</span>
</td>
<td class="py-4 px-4">
<div v-if="job.error" class="text-sm text-red-600">
<div v-if="job.error" class="text-sm text-red-600 dark:text-red-400">
{{ job.error }}
</div>
<div v-else-if="job.context?.mangaTitle || job.context?.chapterNumber !== undefined || job.context?.sourceId"
class="text-sm text-gray-700 space-y-0.5">
class="text-sm text-gray-700 dark:text-gray-300 space-y-0.5">
<div v-if="job.context.mangaTitle" class="font-medium">
{{ job.context.mangaTitle }}
</div>
<div v-if="job.context.chapterNumber !== undefined" class="text-gray-500">
<div v-if="job.context.chapterNumber !== undefined" class="text-gray-500 dark:text-gray-400">
Chapitre {{ job.context.chapterNumber }}
</div>
<div v-if="job.context.sourceId" class="text-xs text-gray-400">
<div v-if="job.context.sourceId" class="text-xs text-gray-400 dark:text-gray-500">
Source : {{ job.context.sourceId }}
</div>
</div>
<div v-else class="text-sm text-gray-600">
<div v-else class="text-sm text-gray-600 dark:text-gray-400">
{{ formatDate(job.createdAt) }}
</div>
</td>
<td class="py-4 px-4">
<div v-if="job.status === 'in_progress'" class="mt-2">
<div class="relative bg-gray-200 rounded-full h-6 overflow-hidden">
<div class="relative bg-gray-200 dark:bg-gray-700 rounded-full h-6 overflow-hidden">
<div
class="absolute top-0 left-0 h-full bg-green-400 transition-all duration-300 ease-out"
:style="{ width: `${job.progress}%` }"></div>
@@ -59,7 +59,7 @@
</div>
</div>
</div>
<div v-else-if="job.status === 'completed'" class="relative bg-gray-200 rounded-full h-6 overflow-hidden">
<div v-else-if="job.status === 'completed'" class="relative bg-gray-200 dark:bg-gray-700 rounded-full h-6 overflow-hidden">
<div
class="absolute top-0 left-0 h-full bg-green-400 transition-all duration-300 ease-out"
style="width: 100%"></div>
@@ -67,7 +67,7 @@
100%
</div>
</div>
<div v-else-if="job.status === 'failed'" class="relative bg-gray-200 rounded-full h-6 overflow-hidden">
<div v-else-if="job.status === 'failed'" class="relative bg-gray-200 dark:bg-gray-700 rounded-full h-6 overflow-hidden">
<div
class="absolute top-0 left-0 h-full bg-red-400 transition-all duration-300 ease-out"
style="width: 100%"></div>
@@ -75,17 +75,17 @@
Erreur
</div>
</div>
<div v-else class="relative bg-gray-200 rounded-full h-6 overflow-hidden">
<div v-else class="relative bg-gray-200 dark:bg-gray-700 rounded-full h-6 overflow-hidden">
<div
class="absolute top-0 left-0 h-full bg-yellow-400 transition-all duration-300 ease-out"
style="width: 0%"></div>
<div class="absolute inset-0 flex items-center justify-center text-xs font-semibold text-gray-600">
<div class="absolute inset-0 flex items-center justify-center text-xs font-semibold text-gray-600 dark:text-gray-300">
En attente
</div>
</div>
<div v-if="job.maxAttempts > 1 || job.attempts > 0"
class="text-xs text-gray-400 mt-1 text-center">
class="text-xs text-gray-400 dark:text-gray-500 mt-1 text-center">
{{ job.attempts }} / {{ job.maxAttempts }} tentative{{ job.maxAttempts > 1 ? 's' : '' }}
</div>
</td>

View File

@@ -6,16 +6,16 @@
<div class="animate-spin rounded-full h-10 w-10 border-t-2 border-b-2 border-indigo-500"></div>
</div>
<div v-else-if="activityStore.error" class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-6">
<div v-else-if="activityStore.error" class="bg-red-100 dark:bg-red-900/20 border-l-4 border-red-500 text-red-700 dark:text-red-400 p-4 mb-6">
<p>{{ activityStore.error }}</p>
</div>
<div v-else class="container mx-auto p-2">
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow rounded-lg">
<div class="overflow-x-auto">
<table class="min-w-full bg-white">
<table class="min-w-full bg-white dark:bg-gray-800">
<thead>
<tr class="bg-gray-200 text-gray-800">
<tr class="bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200">
<th class="w-1/12 py-3 px-4 text-left">
<input
type="checkbox"
@@ -29,14 +29,14 @@
<th class="w-1/12 py-3 px-4 text-left">Actions</th>
</tr>
</thead>
<tbody class="text-gray-700">
<tbody class="text-gray-700 dark:text-gray-300">
<template v-if="activityStore.jobs.length === 0">
<tr>
<td colspan="6" class="py-8 px-4 text-center text-gray-500">
<div class="flex flex-col items-center">
<ClockIcon class="h-12 w-12 text-gray-300 mb-4" />
<p class="text-lg font-medium">Aucune activité trouvée</p>
<p class="text-sm">Aucune activité ne correspond aux filtres actuels.</p>
<ClockIcon class="h-12 w-12 text-gray-300 dark:text-gray-600 mb-4" />
<p class="text-lg font-medium dark:text-gray-300">Aucune activité trouvée</p>
<p class="text-sm dark:text-gray-400">Aucune activité ne correspond aux filtres actuels.</p>
</div>
</td>
</tr>