@adatechnology/notification-ui 0.1.0-rc.1 → 0.1.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,297 +1,27 @@
1
- var __defProp = Object.defineProperty;
2
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
-
4
- // src/NotificationProvider.tsx
5
- import { createContext, useContext, useMemo } from "react";
6
-
7
- // src/locales/pt-BR.json
8
- var pt_BR_default = {
9
- "bell.label": "Notifica\xE7\xF5es",
10
- "bell.unreadSuffix": "n\xE3o lidas",
11
- "list.title": "Notifica\xE7\xF5es",
12
- "list.empty": "Nenhuma notifica\xE7\xE3o por aqui.",
13
- "list.loading": "Carregando\u2026",
14
- "list.error": "N\xE3o foi poss\xEDvel carregar suas notifica\xE7\xF5es.",
15
- "list.retry": "Tentar de novo",
16
- "list.loadMore": "Carregar mais",
17
- "list.markAllAsRead": "Marcar todas como lidas",
18
- "item.markAsRead": "Marcar como lida",
19
- "item.delete": "Excluir",
20
- "item.unread": "N\xE3o lida",
21
- "preferences.title": "Prefer\xEAncias de notifica\xE7\xE3o",
22
- "preferences.description": "Escolha por onde voc\xEA quer ser avisado em cada assunto.",
23
- "preferences.save": "Salvar prefer\xEAncias",
24
- "preferences.saved": "Prefer\xEAncias salvas.",
25
- "preferences.error": "N\xE3o foi poss\xEDvel salvar. Tente de novo.",
26
- "preferences.empty": "Nenhuma prefer\xEAncia configurada ainda.",
27
- "preferences.quietHours": "Sil\xEAncio entre",
28
- "preferences.quietHoursTo": "e",
29
- "channel.inbox": "No app",
30
- "channel.push": "Push",
31
- "channel.email": "E-mail",
32
- "channel.whatsapp": "WhatsApp",
33
- "channel.sms": "SMS"
34
- };
35
-
36
- // src/locales/en.json
37
- var en_default = {
38
- "bell.label": "Notifications",
39
- "bell.unreadSuffix": "unread",
40
- "list.title": "Notifications",
41
- "list.empty": "Nothing here yet.",
42
- "list.loading": "Loading\u2026",
43
- "list.error": "We couldn't load your notifications.",
44
- "list.retry": "Try again",
45
- "list.loadMore": "Load more",
46
- "list.markAllAsRead": "Mark all as read",
47
- "item.markAsRead": "Mark as read",
48
- "item.delete": "Delete",
49
- "item.unread": "Unread",
50
- "preferences.title": "Notification preferences",
51
- "preferences.description": "Choose how you want to be notified about each topic.",
52
- "preferences.save": "Save preferences",
53
- "preferences.saved": "Preferences saved.",
54
- "preferences.error": "Couldn't save. Try again.",
55
- "preferences.empty": "No preferences configured yet.",
56
- "preferences.quietHours": "Quiet between",
57
- "preferences.quietHoursTo": "and",
58
- "channel.inbox": "In app",
59
- "channel.push": "Push",
60
- "channel.email": "Email",
61
- "channel.whatsapp": "WhatsApp",
62
- "channel.sms": "SMS"
63
- };
64
-
65
- // src/locales/index.ts
66
- var DEFAULT_LOCALE = "pt-BR";
67
- var MESSAGES_BY_LOCALE = {
68
- "pt-BR": pt_BR_default,
69
- en: en_default
70
- };
71
- function resolveMessages(locale) {
72
- return MESSAGES_BY_LOCALE[locale] ?? MESSAGES_BY_LOCALE[DEFAULT_LOCALE];
73
- }
74
- __name(resolveMessages, "resolveMessages");
75
-
76
- // src/NotificationProvider.tsx
77
- var NotificationContext = /* @__PURE__ */ createContext(null);
78
- function NotificationProvider({ client, locale = DEFAULT_LOCALE, messageOverrides, theme = {}, pollIntervalSeconds = 60, children }) {
79
- const value = useMemo(() => ({
80
- client,
81
- messages: {
82
- ...resolveMessages(locale),
83
- ...messageOverrides
84
- },
85
- theme,
86
- pollIntervalSeconds
87
- }), [
88
- client,
89
- locale,
90
- messageOverrides,
91
- theme,
92
- pollIntervalSeconds
93
- ]);
94
- return /* @__PURE__ */ React.createElement(NotificationContext.Provider, {
95
- value
96
- }, children);
97
- }
98
- __name(NotificationProvider, "NotificationProvider");
99
- function useNotificationContext() {
100
- const context = useContext(NotificationContext);
101
- if (!context) throw new Error("notification-ui: componente usado fora de <NotificationProvider>");
102
- return context;
103
- }
104
- __name(useNotificationContext, "useNotificationContext");
105
-
106
- // src/hooks/useNotifications.ts
107
- import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
108
-
109
- // src/hooks/queryKeys.ts
110
- var NOTIFICATION_QUERY_KEYS = {
111
- all: [
112
- "notifications"
113
- ],
114
- list: /* @__PURE__ */ __name((filters) => [
115
- "notifications",
116
- "list",
117
- filters
118
- ], "list"),
119
- unreadCount: /* @__PURE__ */ __name(() => [
120
- "notifications",
121
- "unread-count"
122
- ], "unreadCount"),
123
- preferences: /* @__PURE__ */ __name(() => [
124
- "notifications",
125
- "preferences"
126
- ], "preferences"),
127
- templates: /* @__PURE__ */ __name(() => [
128
- "notifications",
129
- "templates"
130
- ], "templates")
131
- };
132
-
133
- // src/hooks/useNotifications.ts
134
- function useNotifications(options = {}) {
135
- const { client } = useNotificationContext();
136
- return useInfiniteQuery({
137
- queryKey: NOTIFICATION_QUERY_KEYS.list({
138
- category: options.category,
139
- read: options.read
140
- }),
141
- initialPageParam: void 0,
142
- queryFn: /* @__PURE__ */ __name(({ pageParam }) => client.listNotifications({
143
- category: options.category,
144
- read: options.read,
145
- cursor: pageParam,
146
- perPage: options.perPage
147
- }), "queryFn"),
148
- getNextPageParam: /* @__PURE__ */ __name((lastPage) => lastPage.nextCursor, "getNextPageParam")
149
- });
150
- }
151
- __name(useNotifications, "useNotifications");
152
- function useUnreadCount() {
153
- const { client, pollIntervalSeconds } = useNotificationContext();
154
- return useQuery({
155
- queryKey: NOTIFICATION_QUERY_KEYS.unreadCount(),
156
- queryFn: /* @__PURE__ */ __name(() => client.countUnread(), "queryFn"),
157
- // Polling é o fallback de quem não ligou o SSE. Com o stream ativo,
158
- // `useNotificationStream` invalida esta chave e o refetch acontece na hora.
159
- refetchInterval: pollIntervalSeconds > 0 ? pollIntervalSeconds * 1e3 : false
160
- });
161
- }
162
- __name(useUnreadCount, "useUnreadCount");
163
- function useMarkAsRead() {
164
- const { client } = useNotificationContext();
165
- const queryClient = useQueryClient();
166
- return useMutation({
167
- mutationFn: /* @__PURE__ */ __name((id) => client.markAsRead(id), "mutationFn"),
168
- async onMutate(id) {
169
- await queryClient.cancelQueries({
170
- queryKey: NOTIFICATION_QUERY_KEYS.all
171
- });
172
- const previousCount = queryClient.getQueryData(NOTIFICATION_QUERY_KEYS.unreadCount());
173
- queryClient.setQueryData(NOTIFICATION_QUERY_KEYS.unreadCount(), (current) => current === void 0 ? current : Math.max(0, current - 1));
174
- return {
175
- previousCount,
176
- id
177
- };
178
- },
179
- onError(_error, _id, context) {
180
- if (context?.previousCount !== void 0) {
181
- queryClient.setQueryData(NOTIFICATION_QUERY_KEYS.unreadCount(), context.previousCount);
182
- }
183
- },
184
- onSettled() {
185
- void queryClient.invalidateQueries({
186
- queryKey: NOTIFICATION_QUERY_KEYS.all
187
- });
188
- }
189
- });
190
- }
191
- __name(useMarkAsRead, "useMarkAsRead");
192
- function useMarkAllAsRead() {
193
- const { client } = useNotificationContext();
194
- const queryClient = useQueryClient();
195
- return useMutation({
196
- mutationFn: /* @__PURE__ */ __name(() => client.markAllAsRead(), "mutationFn"),
197
- onSettled() {
198
- void queryClient.invalidateQueries({
199
- queryKey: NOTIFICATION_QUERY_KEYS.all
200
- });
201
- }
202
- });
203
- }
204
- __name(useMarkAllAsRead, "useMarkAllAsRead");
205
- function useDeleteNotification() {
206
- const { client } = useNotificationContext();
207
- const queryClient = useQueryClient();
208
- return useMutation({
209
- mutationFn: /* @__PURE__ */ __name((id) => client.deleteNotification(id), "mutationFn"),
210
- onSettled() {
211
- void queryClient.invalidateQueries({
212
- queryKey: NOTIFICATION_QUERY_KEYS.all
213
- });
214
- }
215
- });
216
- }
217
- __name(useDeleteNotification, "useDeleteNotification");
218
- function flattenNotificationPages(pages) {
219
- return pages?.flatMap((page) => page.data) ?? [];
220
- }
221
- __name(flattenNotificationPages, "flattenNotificationPages");
222
-
223
- // src/hooks/useNotificationStream.ts
224
- import { useEffect } from "react";
225
- import { useQueryClient as useQueryClient2 } from "@tanstack/react-query";
226
- import { subscribeToNotificationStream } from "@adatechnology/notification-client";
227
- function useNotificationStream(options = {}) {
228
- const { client } = useNotificationContext();
229
- const queryClient = useQueryClient2();
230
- const enabled = options.enabled ?? false;
231
- const onEvent = options.onEvent;
232
- useEffect(() => {
233
- if (!enabled) return;
234
- let subscription;
235
- let cancelled = false;
236
- void client.resolveStreamRequest().then(({ url, headers }) => {
237
- if (cancelled) return;
238
- subscription = subscribeToNotificationStream({
239
- url,
240
- headers,
241
- onEvent: /* @__PURE__ */ __name((event) => {
242
- onEvent?.(event);
243
- void queryClient.invalidateQueries({
244
- queryKey: NOTIFICATION_QUERY_KEYS.all
245
- });
246
- }, "onEvent")
247
- });
248
- });
249
- return () => {
250
- cancelled = true;
251
- subscription?.close();
252
- };
253
- }, [
254
- client,
255
- queryClient,
256
- enabled,
257
- onEvent
258
- ]);
259
- }
260
- __name(useNotificationStream, "useNotificationStream");
261
-
262
- // src/hooks/usePreferences.ts
263
- import { useMutation as useMutation2, useQuery as useQuery2, useQueryClient as useQueryClient3 } from "@tanstack/react-query";
264
- function usePreferences() {
265
- const { client } = useNotificationContext();
266
- return useQuery2({
267
- queryKey: NOTIFICATION_QUERY_KEYS.preferences(),
268
- queryFn: /* @__PURE__ */ __name(() => client.getPreferences(), "queryFn")
269
- });
270
- }
271
- __name(usePreferences, "usePreferences");
272
- function useUpdatePreferences() {
273
- const { client } = useNotificationContext();
274
- const queryClient = useQueryClient3();
275
- return useMutation2({
276
- mutationFn: /* @__PURE__ */ __name((preferences) => client.updatePreferences(preferences), "mutationFn"),
277
- onSuccess(saved) {
278
- queryClient.setQueryData(NOTIFICATION_QUERY_KEYS.preferences(), saved);
279
- }
280
- });
281
- }
282
- __name(useUpdatePreferences, "useUpdatePreferences");
283
- function useTemplates() {
284
- const { client } = useNotificationContext();
285
- return useQuery2({
286
- queryKey: NOTIFICATION_QUERY_KEYS.templates(),
287
- queryFn: /* @__PURE__ */ __name(() => client.listTemplates(), "queryFn")
288
- });
289
- }
290
- __name(useTemplates, "useTemplates");
1
+ import {
2
+ DEFAULT_LOCALE,
3
+ NOTIFICATION_QUERY_KEYS,
4
+ NotificationProvider,
5
+ flattenNotificationPages,
6
+ resolveMessages,
7
+ useDeleteNotification,
8
+ useMarkAllAsRead,
9
+ useMarkAsRead,
10
+ useNotificationContext,
11
+ useNotificationStream,
12
+ useNotifications,
13
+ usePreferences,
14
+ useTemplateEditor,
15
+ useTemplates,
16
+ useUnreadCount,
17
+ useUpdatePreferences,
18
+ useUpsertTemplate
19
+ } from "./chunk-FNUYTJ36.js";
291
20
 
292
21
  // src/components/NotificationBell.tsx
293
22
  import { Bell } from "lucide-react";
294
23
  import clsx from "clsx";
24
+ import { jsx, jsxs } from "react/jsx-runtime";
295
25
  var DEFAULT_MAX_BADGE_COUNT = 99;
296
26
  function NotificationBell({ onClick, className, maxBadgeCount = DEFAULT_MAX_BADGE_COUNT }) {
297
27
  const { messages, theme } = useNotificationContext();
@@ -299,25 +29,24 @@ function NotificationBell({ onClick, className, maxBadgeCount = DEFAULT_MAX_BADG
299
29
  const hasUnread = unreadCount > 0;
300
30
  const badgeLabel = unreadCount > maxBadgeCount ? `${maxBadgeCount}+` : String(unreadCount);
301
31
  const accessibleLabel = hasUnread ? `${messages["bell.label"]}: ${unreadCount} ${messages["bell.unreadSuffix"]}` : messages["bell.label"];
302
- return /* @__PURE__ */ React.createElement("button", {
303
- type: "button",
304
- onClick,
305
- className: clsx("adn-bell", theme.rootClassName, className),
306
- // Botão só-ícone exige rótulo acessível (regra de ícones, `web.md` §9).
307
- "aria-label": accessibleLabel
308
- }, /* @__PURE__ */ React.createElement(Bell, {
309
- className: "adn-bell__icon",
310
- "aria-hidden": "true"
311
- }), hasUnread ? (
312
- // `aria-live` para leitor de tela anunciar a mudança do contador sem o usuário reabrir
313
- // o menu; `polite` porque notificação nova não deve interromper o que ele está lendo.
314
- /* @__PURE__ */ React.createElement("span", {
315
- className: "adn-bell__badge",
316
- "aria-live": "polite"
317
- }, badgeLabel)
318
- ) : null);
32
+ return /* @__PURE__ */ jsxs(
33
+ "button",
34
+ {
35
+ type: "button",
36
+ onClick,
37
+ className: clsx("adn-bell", theme.rootClassName, className),
38
+ "aria-label": accessibleLabel,
39
+ children: [
40
+ /* @__PURE__ */ jsx(Bell, { className: "adn-bell__icon", "aria-hidden": "true" }),
41
+ hasUnread ? (
42
+ // `aria-live` para leitor de tela anunciar a mudança do contador sem o usuário reabrir
43
+ // o menu; `polite` porque notificação nova não deve interromper o que ele está lendo.
44
+ /* @__PURE__ */ jsx("span", { className: "adn-bell__badge", "aria-live": "polite", children: badgeLabel })
45
+ ) : null
46
+ ]
47
+ }
48
+ );
319
49
  }
320
- __name(NotificationBell, "NotificationBell");
321
50
 
322
51
  // src/components/NotificationList.tsx
323
52
  import { CheckCheck, Inbox, RotateCw } from "lucide-react";
@@ -326,6 +55,7 @@ import clsx3 from "clsx";
326
55
  // src/components/NotificationItem.tsx
327
56
  import { Check, Trash2 } from "lucide-react";
328
57
  import clsx2 from "clsx";
58
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
329
59
  function NotificationItem({ notification, onSelect, className }) {
330
60
  const { messages } = useNotificationContext();
331
61
  const markAsRead = useMarkAsRead();
@@ -335,110 +65,101 @@ function NotificationItem({ notification, onSelect, className }) {
335
65
  if (isUnread) markAsRead.mutate(notification.id);
336
66
  onSelect?.(notification);
337
67
  }
338
- __name(handleSelect, "handleSelect");
339
- return /* @__PURE__ */ React.createElement("li", {
340
- className: clsx2("adn-item", isUnread && "adn-item--unread", className)
341
- }, /* @__PURE__ */ React.createElement("button", {
342
- type: "button",
343
- className: "adn-item__main",
344
- onClick: handleSelect
345
- }, /* @__PURE__ */ React.createElement("span", {
346
- className: "adn-item__title"
347
- }, notification.title), /* @__PURE__ */ React.createElement("span", {
348
- className: "adn-item__body"
349
- }, notification.body), isUnread ? /* @__PURE__ */ React.createElement("span", {
350
- className: "adn-item__unread-dot",
351
- "aria-label": messages["item.unread"]
352
- }) : null), /* @__PURE__ */ React.createElement("div", {
353
- className: "adn-item__actions"
354
- }, isUnread ? /* @__PURE__ */ React.createElement("button", {
355
- type: "button",
356
- className: "adn-item__action",
357
- onClick: /* @__PURE__ */ __name(() => markAsRead.mutate(notification.id), "onClick"),
358
- disabled: markAsRead.isPending,
359
- "aria-label": messages["item.markAsRead"],
360
- title: messages["item.markAsRead"]
361
- }, /* @__PURE__ */ React.createElement(Check, {
362
- className: "adn-item__action-icon",
363
- "aria-hidden": "true"
364
- })) : null, /* @__PURE__ */ React.createElement("button", {
365
- type: "button",
366
- className: "adn-item__action adn-item__action--danger",
367
- onClick: /* @__PURE__ */ __name(() => deleteNotification.mutate(notification.id), "onClick"),
368
- disabled: deleteNotification.isPending,
369
- "aria-label": messages["item.delete"],
370
- title: messages["item.delete"]
371
- }, /* @__PURE__ */ React.createElement(Trash2, {
372
- className: "adn-item__action-icon",
373
- "aria-hidden": "true"
374
- }))));
68
+ return /* @__PURE__ */ jsxs2("li", { className: clsx2("adn-item", isUnread && "adn-item--unread", className), children: [
69
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "adn-item__main", onClick: handleSelect, children: [
70
+ /* @__PURE__ */ jsx2("span", { className: "adn-item__title", children: notification.title }),
71
+ /* @__PURE__ */ jsx2("span", { className: "adn-item__body", children: notification.body }),
72
+ isUnread ? /* @__PURE__ */ jsx2("span", { className: "adn-item__unread-dot", "aria-label": messages["item.unread"] }) : null
73
+ ] }),
74
+ /* @__PURE__ */ jsxs2("div", { className: "adn-item__actions", children: [
75
+ isUnread ? /* @__PURE__ */ jsx2(
76
+ "button",
77
+ {
78
+ type: "button",
79
+ className: "adn-item__action",
80
+ onClick: () => markAsRead.mutate(notification.id),
81
+ disabled: markAsRead.isPending,
82
+ "aria-label": messages["item.markAsRead"],
83
+ title: messages["item.markAsRead"],
84
+ children: /* @__PURE__ */ jsx2(Check, { className: "adn-item__action-icon", "aria-hidden": "true" })
85
+ }
86
+ ) : null,
87
+ /* @__PURE__ */ jsx2(
88
+ "button",
89
+ {
90
+ type: "button",
91
+ className: "adn-item__action adn-item__action--danger",
92
+ onClick: () => deleteNotification.mutate(notification.id),
93
+ disabled: deleteNotification.isPending,
94
+ "aria-label": messages["item.delete"],
95
+ title: messages["item.delete"],
96
+ children: /* @__PURE__ */ jsx2(Trash2, { className: "adn-item__action-icon", "aria-hidden": "true" })
97
+ }
98
+ )
99
+ ] })
100
+ ] });
375
101
  }
376
- __name(NotificationItem, "NotificationItem");
377
102
 
378
103
  // src/components/NotificationList.tsx
104
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
379
105
  function NotificationList({ category, onSelect, className, components }) {
380
106
  const { messages, theme } = useNotificationContext();
381
- const query = useNotifications({
382
- category
383
- });
107
+ const query = useNotifications({ category });
384
108
  const { data: unreadCount = 0 } = useUnreadCount();
385
109
  const markAllAsRead = useMarkAllAsRead();
386
110
  const Item = components?.Item ?? NotificationItem;
387
111
  const notifications = flattenNotificationPages(query.data?.pages);
388
- return /* @__PURE__ */ React.createElement("section", {
389
- className: clsx3("adn-list", theme.rootClassName, className),
390
- "aria-label": messages["list.title"]
391
- }, /* @__PURE__ */ React.createElement("header", {
392
- className: "adn-list__header"
393
- }, /* @__PURE__ */ React.createElement("h2", {
394
- className: "adn-list__title"
395
- }, messages["list.title"]), unreadCount > 0 ? /* @__PURE__ */ React.createElement("button", {
396
- type: "button",
397
- className: "adn-list__bulk-action",
398
- onClick: /* @__PURE__ */ __name(() => markAllAsRead.mutate(), "onClick"),
399
- disabled: markAllAsRead.isPending
400
- }, /* @__PURE__ */ React.createElement(CheckCheck, {
401
- className: "adn-list__bulk-icon",
402
- "aria-hidden": "true"
403
- }), messages["list.markAllAsRead"]) : null), query.isPending ? /* @__PURE__ */ React.createElement("p", {
404
- className: "adn-list__state"
405
- }, messages["list.loading"]) : null, query.isError ? /* @__PURE__ */ React.createElement("div", {
406
- className: "adn-list__state adn-list__state--error"
407
- }, /* @__PURE__ */ React.createElement("p", null, messages["list.error"]), /* @__PURE__ */ React.createElement("button", {
408
- type: "button",
409
- className: "adn-list__retry",
410
- onClick: /* @__PURE__ */ __name(() => void query.refetch(), "onClick")
411
- }, /* @__PURE__ */ React.createElement(RotateCw, {
412
- className: "adn-list__retry-icon",
413
- "aria-hidden": "true"
414
- }), messages["list.retry"])) : null, !query.isPending && !query.isError && notifications.length === 0 ? /* @__PURE__ */ React.createElement("div", {
415
- className: "adn-list__state adn-list__state--empty"
416
- }, /* @__PURE__ */ React.createElement(Inbox, {
417
- className: "adn-list__empty-icon",
418
- "aria-hidden": "true"
419
- }), /* @__PURE__ */ React.createElement("p", null, messages["list.empty"])) : null, notifications.length > 0 ? /* @__PURE__ */ React.createElement("ul", {
420
- className: "adn-list__items"
421
- }, notifications.map((notification) => /* @__PURE__ */ React.createElement(Item, {
422
- key: notification.id,
423
- notification,
424
- onSelect
425
- }))) : null, query.hasNextPage ? /* @__PURE__ */ React.createElement("button", {
426
- type: "button",
427
- className: "adn-list__load-more",
428
- onClick: /* @__PURE__ */ __name(() => void query.fetchNextPage(), "onClick"),
429
- disabled: query.isFetchingNextPage
430
- }, messages["list.loadMore"]) : null);
112
+ return /* @__PURE__ */ jsxs3("section", { className: clsx3("adn-list", theme.rootClassName, className), "aria-label": messages["list.title"], children: [
113
+ /* @__PURE__ */ jsxs3("header", { className: "adn-list__header", children: [
114
+ /* @__PURE__ */ jsx3("h2", { className: "adn-list__title", children: messages["list.title"] }),
115
+ unreadCount > 0 ? /* @__PURE__ */ jsxs3(
116
+ "button",
117
+ {
118
+ type: "button",
119
+ className: "adn-list__bulk-action",
120
+ onClick: () => markAllAsRead.mutate(),
121
+ disabled: markAllAsRead.isPending,
122
+ children: [
123
+ /* @__PURE__ */ jsx3(CheckCheck, { className: "adn-list__bulk-icon", "aria-hidden": "true" }),
124
+ messages["list.markAllAsRead"]
125
+ ]
126
+ }
127
+ ) : null
128
+ ] }),
129
+ query.isPending ? /* @__PURE__ */ jsx3("p", { className: "adn-list__state", children: messages["list.loading"] }) : null,
130
+ query.isError ? /* @__PURE__ */ jsxs3("div", { className: "adn-list__state adn-list__state--error", children: [
131
+ /* @__PURE__ */ jsx3("p", { children: messages["list.error"] }),
132
+ /* @__PURE__ */ jsxs3("button", { type: "button", className: "adn-list__retry", onClick: () => void query.refetch(), children: [
133
+ /* @__PURE__ */ jsx3(RotateCw, { className: "adn-list__retry-icon", "aria-hidden": "true" }),
134
+ messages["list.retry"]
135
+ ] })
136
+ ] }) : null,
137
+ !query.isPending && !query.isError && notifications.length === 0 ? /* @__PURE__ */ jsxs3("div", { className: "adn-list__state adn-list__state--empty", children: [
138
+ /* @__PURE__ */ jsx3(Inbox, { className: "adn-list__empty-icon", "aria-hidden": "true" }),
139
+ /* @__PURE__ */ jsx3("p", { children: messages["list.empty"] })
140
+ ] }) : null,
141
+ notifications.length > 0 ? /* @__PURE__ */ jsx3("ul", { className: "adn-list__items", children: notifications.map((notification) => /* @__PURE__ */ jsx3(Item, { notification, onSelect }, notification.id)) }) : null,
142
+ query.hasNextPage ? /* @__PURE__ */ jsx3(
143
+ "button",
144
+ {
145
+ type: "button",
146
+ className: "adn-list__load-more",
147
+ onClick: () => void query.fetchNextPage(),
148
+ disabled: query.isFetchingNextPage,
149
+ children: messages["list.loadMore"]
150
+ }
151
+ ) : null
152
+ ] });
431
153
  }
432
- __name(NotificationList, "NotificationList");
433
154
 
434
155
  // src/components/PreferencesPanel.tsx
435
156
  import { useState } from "react";
436
157
  import { Save } from "lucide-react";
437
158
  import clsx4 from "clsx";
159
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
438
160
  function channelLabelKey(channel) {
439
161
  return `channel.${channel}`;
440
162
  }
441
- __name(channelLabelKey, "channelLabelKey");
442
163
  function PreferencesPanel({ className }) {
443
164
  const { messages, theme } = useNotificationContext();
444
165
  const { data: saved = [], isPending } = usePreferences();
@@ -446,73 +167,257 @@ function PreferencesPanel({ className }) {
446
167
  const [draft, setDraft] = useState(void 0);
447
168
  const preferences = draft ?? saved;
448
169
  function toggleChannel(target) {
449
- setDraft(preferences.map((preference) => preference.category === target.category && preference.channel === target.channel ? {
450
- ...preference,
451
- enabled: !preference.enabled
452
- } : preference));
170
+ setDraft(
171
+ preferences.map(
172
+ (preference) => preference.category === target.category && preference.channel === target.channel ? { ...preference, enabled: !preference.enabled } : preference
173
+ )
174
+ );
453
175
  }
454
- __name(toggleChannel, "toggleChannel");
455
176
  function handleSave() {
456
- updatePreferences.mutate(preferences, {
457
- onSuccess: /* @__PURE__ */ __name(() => setDraft(void 0), "onSuccess")
458
- });
177
+ updatePreferences.mutate(preferences, { onSuccess: () => setDraft(void 0) });
178
+ }
179
+ if (isPending) return /* @__PURE__ */ jsx4("p", { className: "adn-preferences__state", children: messages["list.loading"] });
180
+ return /* @__PURE__ */ jsxs4("section", { className: clsx4("adn-preferences", theme.rootClassName, className), children: [
181
+ /* @__PURE__ */ jsxs4("header", { className: "adn-preferences__header", children: [
182
+ /* @__PURE__ */ jsx4("h2", { className: "adn-preferences__title", children: messages["preferences.title"] }),
183
+ /* @__PURE__ */ jsx4("p", { className: "adn-preferences__description", children: messages["preferences.description"] })
184
+ ] }),
185
+ preferences.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "adn-preferences__state", children: messages["preferences.empty"] }) : /* @__PURE__ */ jsx4("ul", { className: "adn-preferences__items", children: preferences.map((preference) => {
186
+ const inputId = `adn-pref-${preference.category}-${preference.channel}`;
187
+ return /* @__PURE__ */ jsxs4("li", { className: "adn-preferences__item", children: [
188
+ /* @__PURE__ */ jsxs4("label", { className: "adn-preferences__label", htmlFor: inputId, children: [
189
+ /* @__PURE__ */ jsx4(
190
+ "input",
191
+ {
192
+ id: inputId,
193
+ type: "checkbox",
194
+ className: "adn-preferences__checkbox",
195
+ checked: preference.enabled,
196
+ onChange: () => toggleChannel(preference)
197
+ }
198
+ ),
199
+ /* @__PURE__ */ jsx4("span", { className: "adn-preferences__category", children: preference.category }),
200
+ /* @__PURE__ */ jsx4("span", { className: "adn-preferences__channel", children: messages[channelLabelKey(preference.channel)] ?? preference.channel })
201
+ ] }),
202
+ preference.quietHoursStart && preference.quietHoursEnd ? /* @__PURE__ */ jsxs4("span", { className: "adn-preferences__quiet-hours", children: [
203
+ messages["preferences.quietHours"],
204
+ " ",
205
+ preference.quietHoursStart,
206
+ " ",
207
+ messages["preferences.quietHoursTo"],
208
+ " ",
209
+ preference.quietHoursEnd
210
+ ] }) : null
211
+ ] }, inputId);
212
+ }) }),
213
+ /* @__PURE__ */ jsxs4("footer", { className: "adn-preferences__footer", children: [
214
+ /* @__PURE__ */ jsxs4(
215
+ "button",
216
+ {
217
+ type: "button",
218
+ className: "adn-preferences__save",
219
+ onClick: handleSave,
220
+ disabled: updatePreferences.isPending || draft === void 0,
221
+ children: [
222
+ /* @__PURE__ */ jsx4(Save, { className: "adn-preferences__save-icon", "aria-hidden": "true" }),
223
+ messages["preferences.save"]
224
+ ]
225
+ }
226
+ ),
227
+ updatePreferences.isSuccess && draft === void 0 ? /* @__PURE__ */ jsx4("span", { className: "adn-preferences__feedback", role: "status", children: messages["preferences.saved"] }) : null,
228
+ updatePreferences.isError ? /* @__PURE__ */ jsx4("span", { className: "adn-preferences__feedback adn-preferences__feedback--error", role: "alert", children: messages["preferences.error"] }) : null
229
+ ] })
230
+ ] });
231
+ }
232
+
233
+ // src/components/NotificationsWorkspace.tsx
234
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
235
+ function NotificationsWorkspace({
236
+ labels: labelsOverride,
237
+ category,
238
+ showPreferences = true,
239
+ settingsHref,
240
+ onSelectNotification,
241
+ renderHeaderActions,
242
+ renderAboveList,
243
+ renderEmpty,
244
+ className
245
+ }) {
246
+ const { messages } = useNotificationContext();
247
+ const label = (key) => labelsOverride?.[key] ?? messages[key] ?? key;
248
+ return /* @__PURE__ */ jsxs5("div", { className: `adn-workspace ${className ?? ""}`, children: [
249
+ /* @__PURE__ */ jsxs5("header", { className: "adn-workspace__header", children: [
250
+ /* @__PURE__ */ jsxs5("div", { children: [
251
+ /* @__PURE__ */ jsx5("h1", { className: "adn-workspace__title", children: label("workspace.title") }),
252
+ /* @__PURE__ */ jsx5("p", { className: "adn-workspace__description", children: label("workspace.description") })
253
+ ] }),
254
+ /* @__PURE__ */ jsxs5("div", { className: "adn-workspace__actions", children: [
255
+ renderHeaderActions?.(),
256
+ settingsHref && /* @__PURE__ */ jsx5("a", { href: settingsHref, className: "adn-button adn-button--outline", children: label("workspace.settingsLink") })
257
+ ] })
258
+ ] }),
259
+ renderAboveList?.(),
260
+ /* @__PURE__ */ jsxs5("div", { className: showPreferences ? "adn-workspace__grid" : "adn-workspace__grid--single", children: [
261
+ /* @__PURE__ */ jsxs5("section", { className: "adn-workspace__inbox", children: [
262
+ /* @__PURE__ */ jsx5(
263
+ NotificationList,
264
+ {
265
+ ...category ? { category } : {},
266
+ ...onSelectNotification ? { onSelect: onSelectNotification } : {}
267
+ }
268
+ ),
269
+ renderEmpty?.()
270
+ ] }),
271
+ showPreferences && /* @__PURE__ */ jsx5("aside", { className: "adn-workspace__aside", children: /* @__PURE__ */ jsx5(PreferencesPanel, {}) })
272
+ ] })
273
+ ] });
274
+ }
275
+
276
+ // src/components/NotificationSettingsWorkspace.tsx
277
+ import { Fragment, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
278
+ function NotificationSettingsWorkspace({
279
+ labels: labelsOverride,
280
+ channels,
281
+ categories,
282
+ previewPayload,
283
+ templateLabelOf,
284
+ renderChannelFields,
285
+ renderHeaderActions,
286
+ className
287
+ }) {
288
+ const { messages } = useNotificationContext();
289
+ const label = (key) => labelsOverride?.[key] ?? messages[key] ?? key;
290
+ const preferencesQuery = usePreferences();
291
+ const updatePreferences = useUpdatePreferences();
292
+ const editor = useTemplateEditor(previewPayload ? { previewPayload } : {});
293
+ function isEnabled(category, channel) {
294
+ const row = (preferencesQuery.data ?? []).find(
295
+ (preference) => preference.category === category && preference.channel === channel
296
+ );
297
+ return row?.enabled ?? true;
298
+ }
299
+ function toggle(category, channel) {
300
+ const next = categories.flatMap(
301
+ (eachCategory) => channels.map((eachChannel) => ({
302
+ category: eachCategory.id,
303
+ channel: eachChannel.id,
304
+ enabled: eachCategory.id === category && eachChannel.id === channel ? !isEnabled(category, channel) : isEnabled(eachCategory.id, eachChannel.id)
305
+ }))
306
+ );
307
+ updatePreferences.mutate(next);
459
308
  }
460
- __name(handleSave, "handleSave");
461
- if (isPending) return /* @__PURE__ */ React.createElement("p", {
462
- className: "adn-preferences__state"
463
- }, messages["list.loading"]);
464
- return /* @__PURE__ */ React.createElement("section", {
465
- className: clsx4("adn-preferences", theme.rootClassName, className)
466
- }, /* @__PURE__ */ React.createElement("header", {
467
- className: "adn-preferences__header"
468
- }, /* @__PURE__ */ React.createElement("h2", {
469
- className: "adn-preferences__title"
470
- }, messages["preferences.title"]), /* @__PURE__ */ React.createElement("p", {
471
- className: "adn-preferences__description"
472
- }, messages["preferences.description"])), preferences.length === 0 ? /* @__PURE__ */ React.createElement("p", {
473
- className: "adn-preferences__state"
474
- }, messages["preferences.empty"]) : /* @__PURE__ */ React.createElement("ul", {
475
- className: "adn-preferences__items"
476
- }, preferences.map((preference) => {
477
- const inputId = `adn-pref-${preference.category}-${preference.channel}`;
478
- return /* @__PURE__ */ React.createElement("li", {
479
- key: inputId,
480
- className: "adn-preferences__item"
481
- }, /* @__PURE__ */ React.createElement("label", {
482
- className: "adn-preferences__label",
483
- htmlFor: inputId
484
- }, /* @__PURE__ */ React.createElement("input", {
485
- id: inputId,
486
- type: "checkbox",
487
- className: "adn-preferences__checkbox",
488
- checked: preference.enabled,
489
- onChange: /* @__PURE__ */ __name(() => toggleChannel(preference), "onChange")
490
- }), /* @__PURE__ */ React.createElement("span", {
491
- className: "adn-preferences__category"
492
- }, preference.category), /* @__PURE__ */ React.createElement("span", {
493
- className: "adn-preferences__channel"
494
- }, messages[channelLabelKey(preference.channel)] ?? preference.channel)), preference.quietHoursStart && preference.quietHoursEnd ? /* @__PURE__ */ React.createElement("span", {
495
- className: "adn-preferences__quiet-hours"
496
- }, messages["preferences.quietHours"], " ", preference.quietHoursStart, " ", messages["preferences.quietHoursTo"], " ", preference.quietHoursEnd) : null);
497
- })), /* @__PURE__ */ React.createElement("footer", {
498
- className: "adn-preferences__footer"
499
- }, /* @__PURE__ */ React.createElement("button", {
500
- type: "button",
501
- className: "adn-preferences__save",
502
- onClick: handleSave,
503
- disabled: updatePreferences.isPending || draft === void 0
504
- }, /* @__PURE__ */ React.createElement(Save, {
505
- className: "adn-preferences__save-icon",
506
- "aria-hidden": "true"
507
- }), messages["preferences.save"]), updatePreferences.isSuccess && draft === void 0 ? /* @__PURE__ */ React.createElement("span", {
508
- className: "adn-preferences__feedback",
509
- role: "status"
510
- }, messages["preferences.saved"]) : null, updatePreferences.isError ? /* @__PURE__ */ React.createElement("span", {
511
- className: "adn-preferences__feedback adn-preferences__feedback--error",
512
- role: "alert"
513
- }, messages["preferences.error"]) : null));
309
+ return /* @__PURE__ */ jsxs6("div", { className: `adn-settings ${className ?? ""}`, children: [
310
+ /* @__PURE__ */ jsxs6("header", { className: "adn-settings__header", children: [
311
+ /* @__PURE__ */ jsxs6("div", { children: [
312
+ /* @__PURE__ */ jsx6("h1", { className: "adn-settings__title", children: label("settings.title") }),
313
+ /* @__PURE__ */ jsx6("p", { className: "adn-settings__description", children: label("settings.description") })
314
+ ] }),
315
+ renderHeaderActions?.()
316
+ ] }),
317
+ /* @__PURE__ */ jsxs6("section", { className: "adn-settings__channels", children: [
318
+ /* @__PURE__ */ jsx6("h2", { className: "adn-settings__section-title", children: label("settings.channelsTitle") }),
319
+ /* @__PURE__ */ jsx6("p", { className: "adn-settings__hint", children: label("settings.channelsHint") }),
320
+ categories.map((category) => /* @__PURE__ */ jsxs6("div", { className: "adn-settings__category", children: [
321
+ /* @__PURE__ */ jsx6("p", { className: "adn-settings__category-label", children: category.label }),
322
+ category.hint && /* @__PURE__ */ jsx6("p", { className: "adn-settings__hint", children: category.hint }),
323
+ /* @__PURE__ */ jsx6("div", { className: "adn-settings__channel-grid", children: channels.map((channel) => /* @__PURE__ */ jsxs6("label", { className: "adn-settings__channel", children: [
324
+ /* @__PURE__ */ jsx6(
325
+ "input",
326
+ {
327
+ type: "checkbox",
328
+ checked: isEnabled(category.id, channel.id),
329
+ disabled: updatePreferences.isPending,
330
+ onChange: () => toggle(category.id, channel.id)
331
+ }
332
+ ),
333
+ /* @__PURE__ */ jsxs6("span", { children: [
334
+ /* @__PURE__ */ jsx6("span", { className: "adn-settings__channel-label", children: channel.label }),
335
+ channel.hint && /* @__PURE__ */ jsx6("span", { className: "adn-settings__hint", children: channel.hint })
336
+ ] })
337
+ ] }, channel.id)) })
338
+ ] }, category.id))
339
+ ] }),
340
+ /* @__PURE__ */ jsxs6("div", { className: "adn-settings__templates", children: [
341
+ /* @__PURE__ */ jsxs6("section", { className: "adn-settings__template-list", children: [
342
+ /* @__PURE__ */ jsxs6("header", { children: [
343
+ /* @__PURE__ */ jsx6("h2", { className: "adn-settings__section-title", children: label("settings.templatesTitle") }),
344
+ /* @__PURE__ */ jsx6("p", { className: "adn-settings__hint", children: label("settings.templatesHint") })
345
+ ] }),
346
+ editor.isLoading && /* @__PURE__ */ jsx6("p", { className: "adn-settings__hint", children: label("list.loading") }),
347
+ !editor.isLoading && editor.templates.length === 0 && /* @__PURE__ */ jsx6("p", { className: "adn-settings__hint", children: label("settings.templatesEmpty") }),
348
+ /* @__PURE__ */ jsx6("ul", { children: editor.templates.map((template) => /* @__PURE__ */ jsx6("li", { children: /* @__PURE__ */ jsxs6(
349
+ "button",
350
+ {
351
+ type: "button",
352
+ onClick: () => editor.select(template),
353
+ className: editor.selected?.id === template.id ? "adn-settings__row--active" : void 0,
354
+ children: [
355
+ /* @__PURE__ */ jsxs6("span", { className: "adn-settings__row-main", children: [
356
+ /* @__PURE__ */ jsx6("span", { className: "adn-settings__row-title", children: templateLabelOf?.(template) ?? template.key }),
357
+ /* @__PURE__ */ jsx6("span", { className: "adn-settings__row-body", children: template.body })
358
+ ] }),
359
+ /* @__PURE__ */ jsx6("span", { className: `adn-settings__badge adn-settings__badge--${template.channel}`, children: label(`channel.${template.channel}`) }),
360
+ /* @__PURE__ */ jsxs6("span", { className: "adn-settings__version", children: [
361
+ label("settings.version"),
362
+ template.version
363
+ ] })
364
+ ]
365
+ }
366
+ ) }, template.id)) })
367
+ ] }),
368
+ /* @__PURE__ */ jsxs6("section", { className: "adn-settings__editor", children: [
369
+ !editor.draft && /* @__PURE__ */ jsx6("p", { className: "adn-settings__hint", children: label("settings.pickTemplate") }),
370
+ editor.draft && /* @__PURE__ */ jsxs6(Fragment, { children: [
371
+ /* @__PURE__ */ jsxs6("header", { className: "adn-settings__editor-header", children: [
372
+ /* @__PURE__ */ jsxs6("div", { children: [
373
+ /* @__PURE__ */ jsx6("h2", { className: "adn-settings__section-title", children: editor.selected ? templateLabelOf?.(editor.selected) ?? editor.selected.key : "" }),
374
+ /* @__PURE__ */ jsxs6("p", { className: "adn-settings__hint", children: [
375
+ editor.draft.channel,
376
+ " \xB7 ",
377
+ editor.draft.locale,
378
+ " \xB7 ",
379
+ label("settings.version"),
380
+ editor.selected?.version
381
+ ] })
382
+ ] }),
383
+ /* @__PURE__ */ jsx6("button", { type: "button", onClick: editor.clear, children: label("settings.close") })
384
+ ] }),
385
+ /* @__PURE__ */ jsxs6("label", { className: "adn-settings__field", children: [
386
+ /* @__PURE__ */ jsx6("span", { className: "adn-settings__field-label", children: label("settings.subject") }),
387
+ /* @__PURE__ */ jsx6("span", { className: "adn-settings__hint", children: label("settings.subjectHint") }),
388
+ /* @__PURE__ */ jsx6("input", { value: editor.draft.subject, onChange: (event) => editor.update({ subject: event.target.value }) })
389
+ ] }),
390
+ /* @__PURE__ */ jsxs6("label", { className: "adn-settings__field", children: [
391
+ /* @__PURE__ */ jsx6("span", { className: "adn-settings__field-label", children: label("settings.body") }),
392
+ /* @__PURE__ */ jsx6(
393
+ "textarea",
394
+ {
395
+ rows: 4,
396
+ value: editor.draft.body,
397
+ onChange: (event) => editor.update({ body: event.target.value })
398
+ }
399
+ )
400
+ ] }),
401
+ renderChannelFields?.({
402
+ channel: editor.draft.channel,
403
+ value: editor.draft.whatsappTemplateName,
404
+ onChange: (value) => editor.update({ whatsappTemplateName: value })
405
+ }),
406
+ editor.preview && /* @__PURE__ */ jsxs6("div", { className: "adn-settings__preview", children: [
407
+ /* @__PURE__ */ jsx6("p", { className: "adn-settings__preview-title", children: label("settings.previewTitle") }),
408
+ /* @__PURE__ */ jsx6("p", { className: "adn-settings__preview-subject", children: editor.preview.title }),
409
+ /* @__PURE__ */ jsx6("p", { className: "adn-settings__preview-body", children: editor.preview.body })
410
+ ] }),
411
+ editor.error && /* @__PURE__ */ jsx6("p", { className: "adn-settings__error", children: editor.error }),
412
+ /* @__PURE__ */ jsxs6("div", { className: "adn-settings__actions", children: [
413
+ /* @__PURE__ */ jsx6("button", { type: "button", onClick: editor.save, disabled: !editor.isDirty || editor.isSaving, children: editor.isSaving ? label("settings.saving") : label("settings.save") }),
414
+ !editor.isDirty && /* @__PURE__ */ jsx6("span", { className: "adn-settings__hint", children: label("settings.unchanged") })
415
+ ] })
416
+ ] })
417
+ ] })
418
+ ] })
419
+ ] });
514
420
  }
515
- __name(PreferencesPanel, "PreferencesPanel");
516
421
  export {
517
422
  DEFAULT_LOCALE,
518
423
  NOTIFICATION_QUERY_KEYS,
@@ -520,6 +425,8 @@ export {
520
425
  NotificationItem,
521
426
  NotificationList,
522
427
  NotificationProvider,
428
+ NotificationSettingsWorkspace,
429
+ NotificationsWorkspace,
523
430
  PreferencesPanel,
524
431
  flattenNotificationPages,
525
432
  resolveMessages,
@@ -530,8 +437,10 @@ export {
530
437
  useNotificationStream,
531
438
  useNotifications,
532
439
  usePreferences,
440
+ useTemplateEditor,
533
441
  useTemplates,
534
442
  useUnreadCount,
535
- useUpdatePreferences
443
+ useUpdatePreferences,
444
+ useUpsertTemplate
536
445
  };
537
446
  //# sourceMappingURL=index.js.map