Files
decision/frontend/app/composables/useNotifications.ts
Yvv 403b94fa2c Sprint 5 : integration et production -- securite, performance, API publique, documentation
Backend: rate limiter, security headers, blockchain cache service avec RPC,
public API (7 endpoints read-only), WebSocket auth + heartbeat, DB connection
pooling, structured logging, health check DB. Frontend: API retry/timeout,
WebSocket auth + heartbeat + typed events, notifications toast, mobile hamburger
+ drawer, error boundary, offline banner, loading skeletons, dashboard enrichi.
Documentation: guides utilisateur complets (demarrage, vote, sanctuaire, FAQ 30+),
guide deploiement, politique securite. 123 tests, 155 fichiers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 15:12:50 +01:00

181 lines
4.2 KiB
TypeScript

/**
* Composable for toast notifications using Nuxt UI.
*
* Provides typed notification helpers with French messages and
* integration with WebSocket events for real-time notifications.
*/
export type NotificationType = 'success' | 'error' | 'warning' | 'info'
interface NotifyOptions {
/** Toast title. */
title: string
/** Toast description (optional). */
description?: string
/** Notification type. */
type?: NotificationType
/** Auto-close duration in milliseconds (default: 5000). */
duration?: number
}
/** Map notification types to Nuxt UI toast color props. */
const TYPE_COLORS: Record<NotificationType, string> = {
success: 'success',
error: 'error',
warning: 'warning',
info: 'info',
}
/** Map notification types to Lucide icon names. */
const TYPE_ICONS: Record<NotificationType, string> = {
success: 'i-lucide-check-circle',
error: 'i-lucide-alert-circle',
warning: 'i-lucide-alert-triangle',
info: 'i-lucide-info',
}
/** Default duration for toasts (ms). */
const DEFAULT_DURATION = 5_000
export function useNotifications() {
const toast = useToast()
/**
* Show a toast notification.
*
* @param options - Notification options (title, description, type, duration)
*/
function notify(options: NotifyOptions): void
function notify(title: string, description?: string, type?: NotificationType): void
function notify(
titleOrOptions: string | NotifyOptions,
description?: string,
type?: NotificationType,
): void {
let opts: NotifyOptions
if (typeof titleOrOptions === 'string') {
opts = {
title: titleOrOptions,
description,
type: type || 'info',
}
} else {
opts = titleOrOptions
}
const notifType = opts.type || 'info'
toast.add({
title: opts.title,
description: opts.description,
icon: TYPE_ICONS[notifType],
color: TYPE_COLORS[notifType] as any,
duration: opts.duration ?? DEFAULT_DURATION,
})
}
/**
* Show a success toast.
*/
function notifySuccess(message: string, description?: string): void {
notify({
title: message,
description,
type: 'success',
})
}
/**
* Show an error toast.
*/
function notifyError(message: string, description?: string): void {
notify({
title: message,
description,
type: 'error',
duration: 8_000,
})
}
/**
* Show a warning toast.
*/
function notifyWarning(message: string, description?: string): void {
notify({
title: message,
description,
type: 'warning',
})
}
/**
* Show an info toast.
*/
function notifyInfo(message: string, description?: string): void {
notify({
title: message,
description,
type: 'info',
})
}
/**
* Setup WebSocket event listeners that auto-show notifications.
* Call this once in app.vue or a layout component.
*/
function setupWsNotifications(wsComposable: ReturnType<typeof useWebSocket>): void {
wsComposable.onVoteSubmitted((data) => {
notifyInfo(
'Nouveau vote enregistre',
data?.session_title || 'Un vote a ete soumis dans une session active.',
)
})
wsComposable.onDecisionAdvanced((data) => {
notifySuccess(
'Decision avancee',
data?.title
? `La decision "${data.title}" est passee a l'etape suivante.`
: 'Une decision a progresse dans son processus.',
)
})
wsComposable.onMandateUpdated((data) => {
notifyInfo(
'Mandat mis a jour',
data?.title
? `Le mandat "${data.title}" a ete modifie.`
: 'Un mandat a ete mis a jour.',
)
})
wsComposable.onDocumentChanged((data) => {
notifyInfo(
'Document modifie',
data?.title
? `Le document "${data.title}" a ete modifie.`
: 'Un document de reference a ete modifie.',
)
})
wsComposable.onSanctuaryArchived((data) => {
notifySuccess(
'Document archive au sanctuaire',
data?.title
? `"${data.title}" a ete ancre sur IPFS.`
: 'Un document a ete archive de maniere immuable.',
)
})
}
return {
notify,
notifySuccess,
notifyError,
notifyWarning,
notifyInfo,
setupWsNotifications,
}
}