File size: 2,547 Bytes
cd8bd0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
"use client";

/**
 * Notification Store β€” FASE-07 UX & Microinteractions
 *
 * Zustand-based global notification system for the dashboard.
 * Replaces ad-hoc feedback patterns with a centralized toast system.
 *
 * @module store/notificationStore
 */

import { create } from "zustand";

let idCounter = 0;

type NotificationType = "success" | "error" | "warning" | "info";

interface Notification {
  id: number;
  type: NotificationType;
  message: string;
  title?: string | null;
  duration: number;
  dismissible: boolean;
  createdAt: number;
  onClick?: () => void;
}

interface NotificationStore {
  notifications: Notification[];
  addNotification: (notification: {
    type?: NotificationType;
    message: string;
    title?: string;
    duration?: number;
    dismissible?: boolean;
    onClick?: () => void;
  }) => number;
  removeNotification: (id: number) => void;
  clearAll: () => void;
  success: (message: string, title?: string) => number;
  error: (message: string, title?: string) => number;
  warning: (message: string, title?: string) => number;
  info: (message: string, title?: string) => number;
}

export const useNotificationStore = create<NotificationStore>((set, get) => ({
  notifications: [],

  addNotification: (notification) => {
    const id = ++idCounter;
    const entry: Notification = {
      id,
      type: notification.type || "info",
      message: notification.message,
      title: notification.title || null,
      duration: notification.duration ?? 5000,
      dismissible: notification.dismissible ?? true,
      createdAt: Date.now(),
      onClick: notification.onClick,
    };

    set((s) => ({
      notifications: [...s.notifications, entry],
    }));

    // Auto-dismiss
    if (entry.duration > 0) {
      setTimeout(() => {
        get().removeNotification(id);
      }, entry.duration);
    }

    return id;
  },

  removeNotification: (id) => {
    set((s) => ({
      notifications: s.notifications.filter((n) => n.id !== id),
    }));
  },

  clearAll: () => set({ notifications: [] }),

  // ─── Convenience Methods ─────────────────

  success: (message, title) => get().addNotification({ type: "success", message, title }),

  error: (message, title) =>
    get().addNotification({ type: "error", message, title, duration: 8000 }),

  warning: (message, title) =>
    get().addNotification({ type: "warning", message, title, duration: 10000 }),

  info: (message, title) => get().addNotification({ type: "info", message, title }),
}));