Files
Mangarr/assets/vue/app/domain/manga/presentation/pages/HomePage.vue
ext.jeremy.guillot@maxicoffee.domains ec1ef8fe68 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
2026-03-12 20:38:29 +01:00

135 lines
4.7 KiB
Vue

<template>
<div>
<Toolbar :config="toolbarConfig" class="sticky top-16 z-10" />
<div class="container mx-auto px-4">
<MangaGrid v-if="viewMode === 'grid'" :mangas="pagedItems" />
<MangaList
v-else-if="viewMode === 'list'"
:mangas="pagedItems"
@manga-click="handleMangaClick" />
<Pagination
v-if="totalPages > 1"
:current-page="currentPage"
:total-pages="totalPages"
:total="sortedCollection.length"
:limit="prefs.itemsPerPage"
:has-next-page="currentPage < totalPages"
:has-previous-page="currentPage > 1"
@page-change="currentPage = $event" />
<div
v-if="isBackgroundLoading"
class="fixed bottom-4 right-4 bg-gray-800 text-white px-4 py-2 rounded-lg shadow-lg">
Mise à jour en cours...
</div>
</div>
</div>
</template>
<script setup>
import {
ArrowPathIcon,
ArrowsUpDownIcon,
Cog6ToothIcon,
EyeIcon,
FunnelIcon,
MagnifyingGlassIcon
} from '@heroicons/vue/24/outline';
import { storeToRefs } from 'pinia';
import { computed, onMounted, ref, watch } from 'vue';
import { useRouter } from 'vue-router';
import { useUserPreferencesStore } from '../../../../domain/setting/application/store/userPreferencesStore';
import Pagination from '../../../../shared/components/ui/Pagination.vue';
import Toolbar from '../../../../shared/components/ui/Toolbar.vue';
import { useMangaStore } from '../../application/store/mangaStore';
import MangaGrid from '../components/MangaGrid.vue';
import MangaList from '../components/MangaList.vue';
const router = useRouter();
const mangaStore = useMangaStore();
const prefs = useUserPreferencesStore();
const {
collection,
loadingCollection: loading,
errorCollection: error,
isBackgroundLoadingCollection: isBackgroundLoading
} = storeToRefs(mangaStore);
const viewMode = ref(prefs.defaultView);
const currentPage = ref(1);
onMounted(() => {
mangaStore.loadCollection();
});
const handleMangaClick = manga => {
router.push({ name: 'manga-details', params: { id: manga.id } });
};
const sortedCollection = computed(() => {
const items = [...(collection.value?.items || [])];
if (prefs.sortBy === 'title') {
items.sort((a, b) => a.title.localeCompare(b.title));
} else if (prefs.sortBy === 'addedAt') {
items.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
}
return items;
});
const pagedItems = computed(() => {
const start = (currentPage.value - 1) * prefs.itemsPerPage;
return sortedCollection.value.slice(start, start + prefs.itemsPerPage);
});
const totalPages = computed(() => Math.ceil(sortedCollection.value.length / prefs.itemsPerPage));
watch(() => prefs.itemsPerPage, () => {
currentPage.value = 1;
});
const toolbarConfig = {
leftSection: [
{
icon: ArrowPathIcon,
label: 'Refresh',
type: 'button',
onClick: () => mangaStore.refreshCollectionInBackground(),
active: isBackgroundLoading.value
},
{ icon: MagnifyingGlassIcon, label: 'Search', type: 'button', onClick: () => {} }
],
rightSection: [
{ icon: Cog6ToothIcon, type: 'button', onClick: () => {} },
{
icon: EyeIcon,
type: 'dropdown',
label: 'View',
items: [
{ label: 'List', onClick: () => { viewMode.value = 'list'; prefs.setDefaultView('list'); } },
{ label: 'Grid', onClick: () => { viewMode.value = 'grid'; prefs.setDefaultView('grid'); } }
]
},
{
icon: ArrowsUpDownIcon,
type: 'dropdown',
label: 'Sort',
items: [
{ label: 'Title', onClick: () => prefs.setSortBy('title') },
{ label: "Date d'ajout", onClick: () => prefs.setSortBy('addedAt') },
{ label: 'Progression', onClick: () => prefs.setSortBy('progress') }
]
},
{
icon: FunnelIcon,
type: 'dropdown',
label: 'Filter',
items: [
{ label: 'All', onClick: () => {} },
{ label: 'Completed', onClick: () => {} },
{ label: 'In Progress', onClick: () => {} }
]
}
]
};
</script>