feat(ui): refonte de l'interface

This commit is contained in:
ElPouki committed 2026-09-15 15:30:20 +02:00
1 parent 3ede3e808c
commit 09eb1fa3aa
79 files changed
+5323 -3514

No files matched your search

+72 -17
View File
@@ -1,27 +1,82 @@
<template>
<div>
<div class="px-8 py-8 max-w-4xl">
<h1 class="text-lg font-semibold text-primary mb-6">{{ pageTitle }}</h1>
<RouterView />
<div class="flex flex-col md:flex-row min-h-full">
<nav
v-if="groups.length > 1"
ref="navRef"
data-tour="settings-menu"
class="md:w-[236px] shrink-0 md:sticky md:top-0 md:self-start md:h-screen md:overflow-y-auto border-b md:border-b-0 md:border-r border-card
px-4 md:px-3.5 py-4 md:py-[26px] flex md:flex-col gap-1 md:gap-[18px] overflow-x-auto"
>
<h1 class="hidden md:block font-display text-2xl font-bold text-primary ml-2.5">Paramètres</h1>
<div v-for="group in groups" :key="group.label" class="flex md:flex-col gap-1 md:gap-0.5 shrink-0">
<span class="hidden md:block tag-label px-2.5 pb-2">{{ group.label }}</span>
<RouterLink
v-for="link in group.links"
:key="link.to"
:to="link.to"
class="flex items-center h-9 pointer-coarse:h-10 px-2.5 rounded-lg text-body whitespace-nowrap transition-colors"
:class="route.path === link.to ? 'bg-hover text-primary font-semibold' : 'text-secondary hover:text-primary hover:bg-hover/60'"
>
{{ link.label }}
</RouterLink>
</div>
</nav>
<div class="flex-1 min-w-0 px-4 md:px-10 pt-[26px] pb-16">
<div class="max-w-[960px] flex flex-col gap-5">
<header class="flex items-end justify-between gap-5 flex-wrap">
<div class="flex flex-col gap-1 min-w-0">
<h2 class="font-display text-[26px] font-bold text-primary leading-tight">{{ page.title }}</h2>
<p v-if="page.description" class="text-body text-muted">{{ page.description }}</p>
</div>
<div id="settings-actions" class="flex items-center gap-2.5 empty:hidden" />
</header>
<RouterView />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import { ref, computed, watch, nextTick } from 'vue'
import { RouterLink, useRoute } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
const route = useRoute()
const route = useRoute()
const auth = useAuthStore()
const navRef = ref<HTMLElement | null>(null)
const titles: Record<string, string> = {
'settings-profile' : 'Mon profil',
'settings-download' : 'Clients de téléchargement',
'settings-media' : 'Gestion des médias',
'settings-catalogue' : 'Catalogue Fankai',
'settings-logs' : 'Journaux',
'settings-users' : 'Utilisateurs & Invitations',
'settings-jellyfin' : 'Jellyfin & API',
watch(() => route.path, async () => {
await nextTick()
const nav = navRef.value
if (!nav || nav.scrollWidth <= nav.clientWidth) return
nav.querySelector<HTMLElement>('[aria-current="page"]')?.scrollIntoView({ inline: 'center', block: 'nearest' })
}, { immediate: true })
const pages: Record<string, { title: string; description: string }> = {
'/settings/profile' : { title: 'Mon profil', description: 'Votre compte, votre mot de passe et l’apparence de l’application.' },
'/settings/users' : { title: 'Utilisateurs et invitations', description: 'Les invités parcourent la médiathèque et demandent des séries. Ils ne voient ni l’activité ni les paramètres.' },
'/settings/media-management' : { title: 'Gestion des médias', description: 'Où FanKarr prend les fichiers, où il les range, et comment il les importe.' },
'/settings/import-management': { title: 'Gestion des séries', description: 'Les opérations d’entretien sur les fichiers déjà importés.' },
'/settings/catalogue' : { title: 'Catalogue Fankai', description: 'D’où viennent les séries, les épisodes et les torrents.' },
'/settings/download-client' : { title: 'Clients de téléchargement', description: 'Les clients qui reçoivent les torrents envoyés par FanKarr.' },
'/settings/jellyfin' : { title: 'Jellyfin et API', description: 'Le serveur média qui lit la bibliothèque, et l’API ouverte aux applications tierces.' },
'/settings/logs' : { title: 'Journaux', description: 'Ce que le serveur a fait, du plus récent au plus ancien.' },
'/settings/advanced' : { title: 'Avancé', description: 'Mises à jour, diagnostic et opérations à ne sortir qu’en cas de besoin.' },
}
const pageTitle = computed(() => titles[route.name as string] ?? 'Paramètres')
</script>
const page = computed(() => pages[route.path] ?? { title: 'Paramètres', description: '' })
const groups = computed(() => {
const link = (to: string) => ({ to, label: pages[to]!.title })
if (!auth.isAdmin) return [{ label: 'Compte', links: [link('/settings/profile')] }]
return [
{ label: 'Compte', links: [link('/settings/profile'), link('/settings/users')] },
{ label: 'Médias', links: [link('/settings/media-management'), link('/settings/import-management'), link('/settings/catalogue')] },
{ label: 'Services', links: [link('/settings/download-client'), link('/settings/jellyfin')] },
{ label: 'Système', links: [link('/settings/logs'), link('/settings/advanced')] },
]
})
</script>