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é)
54 lines
1.5 KiB
TypeScript
54 lines
1.5 KiB
TypeScript
import { ref } from 'vue'
|
|
|
|
interface Toast {
|
|
id : number
|
|
message : string
|
|
type : 'success' | 'error' | 'info'
|
|
count : number
|
|
timer : ReturnType<typeof setTimeout>
|
|
}
|
|
|
|
const toasts = ref<Toast[]>([])
|
|
let nextId = 0
|
|
const DURATION = 3500
|
|
const GROUP_WINDOW = 800 // ms — toasts du même type dans cette fenêtre sont groupés
|
|
|
|
function add(message: string, type: Toast['type'] = 'info') {
|
|
const now = Date.now()
|
|
|
|
// Cherche un toast récent du même type à grouper
|
|
const existing = toasts.value.find(t => t.type === type)
|
|
|
|
if (existing) {
|
|
// Grouper — incrémenter le count et mettre à jour le message
|
|
clearTimeout(existing.timer)
|
|
existing.count++
|
|
existing.message = buildGroupMessage(type, existing.count)
|
|
existing.timer = setTimeout(() => remove(existing.id), DURATION)
|
|
return
|
|
}
|
|
|
|
// Nouveau toast
|
|
const id = nextId++
|
|
const timer = setTimeout(() => remove(id), DURATION)
|
|
|
|
toasts.value.push({ id, message, type, count: 1, timer })
|
|
}
|
|
|
|
function buildGroupMessage(type: Toast['type'], count: number): string {
|
|
if (type === 'success') return `${count} opérations réussies`
|
|
if (type === 'error') return `${count} erreurs`
|
|
return `${count} notifications`
|
|
}
|
|
|
|
function remove(id: number) {
|
|
const i = toasts.value.findIndex(t => t.id === id)
|
|
if (i !== -1) {
|
|
clearTimeout(toasts.value[i].timer)
|
|
toasts.value.splice(i, 1)
|
|
}
|
|
}
|
|
|
|
export function useToast() {
|
|
return { toasts, add, remove }
|
|
} |