BREAKING CHANGE: nouvelle architecture frontend avec layout sidebar, système de thèmes et routes restructurées ### Frontend - nouveau layout global `AppLayout.vue` avec sidebar de navigation inspirée Radarr/Sonarr - sidebar configurable via un objet `NavItem[]` avec support parents/enfants cliquables - 3 thèmes CSS (Indigo Électrique, Crimson Night, Teal Froid) persistés via localStorage - switcher de thème en bas de sidebar avec pastilles colorées - logo FanKarr cliquable, bouton déconnexion discret dans le header sidebar - badge animé sur "Activité" affichant le nombre de téléchargements en cours - migration complète vers Tailwind v4 avec variables CSS `@theme` - classes utilitaires globales : `btn-primary`, `btn-secondary`, `settings-card`, `settings-input` ### Vues - `SeriesView` : barre colorée en bas des affiches (vert/bleu/orange), dropdown filtres, boutons taille S/M/L/XL persistés - `SerieView` : bouton état unique par épisode (↓/⟳/✓/✗), badges simplifiés, toast via `useToast` global - `ActivityView` : onglets pill, stats, toggle "masquer importés", polling silencieux - `DashboardView` : stats catalogue/importées/en cours/erreurs, torrents actifs, derniers imports - `SettingsView` découpée en 3 vues indépendantes : `DownloadClientView`, `MediaManagementView`, `CatalogueView` - `LogsView` déplacée dans `src/views/settings/` - `AuthView` : migré au système de thème ### Composants - `SidebarNavItem` : support badge, chevron masqué si badge présent - `SettingsToggle` : composant réutilisable `v-model` - `FolderPicker` : migré au thème, `Teleport` intégré - `ToastContainer` : déplacé en bas droite, groupement des toasts similaires avec badge count ### Router - `AppLayout` comme layout racine pour toutes les routes protégées - `/auth` reste hors layout - redirections `/` → `/series`, `/downloads` → `/activity` - `SettingsLayout` avec titre dynamique par route - scroll géré sur `<main id="main-scroll">` dans `AppLayout` ### Stores & composables - nouveau store `downloads` partagé pour le badge sidebar - `usePosterSize` : taille des affiches persistée en localStorage - `useToast` : groupement des toasts du même type, timer reset au lieu d'empilement ### Serveur - logs structurés via `logger` dans `auth.ts`, `organize.ts`, `organize-worker.ts`, `torrent-clients/index.ts`, `qbittorrent.ts`, `index.ts` - suppression des `console.log/error/warn` en faveur de `logger.info/warn/error/debug` - messages worker routés vers `logger` via `organize.ts` - logs `debug` pour les événements verbeux (healthcheck, skip, worker terminé)
56 lines
2.4 KiB
Vue
56 lines
2.4 KiB
Vue
<template>
|
||
<Teleport to="body">
|
||
<div class="fixed bottom-4 right-4 z-50 flex flex-col gap-2">
|
||
<TransitionGroup name="toast">
|
||
<div
|
||
v-for="t in toasts"
|
||
:key="t.id"
|
||
@click="remove(t.id)"
|
||
class="flex items-center gap-3 px-4 py-3 rounded-xl border text-sm cursor-pointer min-w-64 max-w-sm"
|
||
:class="{
|
||
'bg-card border-green-500/30 text-green-400' : t.type === 'success',
|
||
'bg-card border-red-500/30 text-red-400' : t.type === 'error',
|
||
'bg-card border-accent/30 text-accent' : t.type === 'info',
|
||
}"
|
||
>
|
||
<svg v-if="t.type === 'success'" width="14" height="14" viewBox="0 0 14 14" stroke="currentColor" stroke-width="2" fill="none" class="shrink-0">
|
||
<polyline points="2 7 5.5 10.5 12 3"/>
|
||
</svg>
|
||
<svg v-else-if="t.type === 'error'" width="14" height="14" viewBox="0 0 14 14" stroke="currentColor" stroke-width="2" fill="none" class="shrink-0">
|
||
<line x1="3" y1="3" x2="11" y2="11"/><line x1="11" y1="3" x2="3" y2="11"/>
|
||
</svg>
|
||
<svg v-else width="14" height="14" viewBox="0 0 14 14" stroke="currentColor" stroke-width="2" fill="none" class="shrink-0">
|
||
<circle cx="7" cy="7" r="5"/><line x1="7" y1="5" x2="7" y2="7.5"/><circle cx="7" cy="9.5" r="0.5" fill="currentColor"/>
|
||
</svg>
|
||
|
||
<span class="flex-1 text-xs leading-snug">{{ t.message }}</span>
|
||
|
||
<!-- Badge count si groupé -->
|
||
<span
|
||
v-if="t.count && t.count > 1"
|
||
class="shrink-0 text-[10px] font-medium px-1.5 py-0.5 rounded-full"
|
||
:class="{
|
||
'bg-green-500/20 text-green-400' : t.type === 'success',
|
||
'bg-red-500/20 text-red-400' : t.type === 'error',
|
||
'bg-accent/20 text-accent' : t.type === 'info',
|
||
}"
|
||
>
|
||
×{{ t.count }}
|
||
</span>
|
||
</div>
|
||
</TransitionGroup>
|
||
</div>
|
||
</Teleport>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { useToast } from '@/composables/useToast'
|
||
const { toasts, remove } = useToast()
|
||
</script>
|
||
|
||
<style scoped>
|
||
.toast-enter-active { transition: all 0.2s ease; }
|
||
.toast-leave-active { transition: all 0.15s ease; }
|
||
.toast-enter-from { opacity: 0; transform: translateY(8px) scale(0.97); }
|
||
.toast-leave-to { opacity: 0; transform: translateY(4px) scale(0.97); }
|
||
</style> |