feat: refonte UX du dashboard (coquille, tri/filtre/pagination, ⌘K, toasts)
Refonte front (packages/web) : coquille applicative partagée (sidebar desktop + barre d'onglets mobile + PageHeader, langue/push/logout centralisés) pilotée par route.meta.layout ; design system (BaseButton/BaseBadge/SegmentedControl/EmptyState/ SkeletonRow, anneau de focus, icônes @lucide/vue) ; tri/filtre/recherche/pagination côté client via useListControls (+ synchro URL) sur sessions/worktrees/groupes ; section « À traiter », palette de commandes ⌘K, toasts. Aucune modif serveur/protocole/DB. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
45
packages/web/src/stores/toasts.ts
Normal file
45
packages/web/src/stores/toasts.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
|
||||
export type ToastKind = 'success' | 'error' | 'info';
|
||||
|
||||
export interface Toast {
|
||||
id: number;
|
||||
kind: ToastKind;
|
||||
message: string;
|
||||
timeout: number;
|
||||
}
|
||||
|
||||
/** File de notifications éphémères. Aucun couplage : les vues appellent success/error/info. */
|
||||
export const useToastsStore = defineStore('toasts', () => {
|
||||
const toasts = ref<Toast[]>([]);
|
||||
let seq = 0;
|
||||
const timers = new Map<number, ReturnType<typeof setTimeout>>();
|
||||
|
||||
function dismiss(id: number): void {
|
||||
toasts.value = toasts.value.filter((t) => t.id !== id);
|
||||
const timer = timers.get(id);
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timers.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
function push(kind: ToastKind, message: string, timeout = 4000): number {
|
||||
const id = ++seq;
|
||||
toasts.value = [...toasts.value, { id, kind, message, timeout }];
|
||||
if (timeout > 0) timers.set(id, setTimeout(() => dismiss(id), timeout));
|
||||
return id;
|
||||
}
|
||||
|
||||
const success = (message: string): number => push('success', message);
|
||||
const info = (message: string): number => push('info', message);
|
||||
|
||||
/** Normalise n'importe quelle erreur (ApiError/Error/inconnu) en message lisible. Sticky par défaut. */
|
||||
function error(err: unknown, timeout = 0): number {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
return push('error', message, timeout);
|
||||
}
|
||||
|
||||
return { toasts, push, success, info, error, dismiss };
|
||||
});
|
||||
Reference in New Issue
Block a user