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

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.
@@ -4,12 +4,13 @@ import * as _adatechnology_notification_client from '@adatechnology/notification
4
4
  import { NotificationClient } from '@adatechnology/notification-client';
5
5
  import * as _tanstack_react_query from '@tanstack/react-query';
6
6
  import * as _adatechnology_notification_contracts from '@adatechnology/notification-contracts';
7
- import { NotificationSummary, NotificationPreference, NotificationTemplate, RenderedTemplatePreview } from '@adatechnology/notification-contracts';
7
+ import { NotificationSummary, NotificationPreference, PreviewViewportSpec, RenderedTemplatePreview, NotificationTemplate, TemplateVariableDefinition, TemplateVariableDiff } from '@adatechnology/notification-contracts';
8
8
 
9
9
  var ptBR = {
10
10
  "bell.label": "Notificações",
11
11
  "bell.unreadSuffix": "não lidas",
12
- "list.title": "Notificações",
12
+ "list.title": "Caixa de entrada",
13
+ "list.unread": "não lidas",
13
14
  "list.empty": "Nenhuma notificação por aqui.",
14
15
  "list.loading": "Carregando…",
15
16
  "list.error": "Não foi possível carregar suas notificações.",
@@ -51,7 +52,26 @@ var ptBR = {
51
52
  "settings.saving": "Salvando…",
52
53
  "settings.unchanged": "Nada mudou ainda.",
53
54
  "settings.close": "Fechar",
54
- "settings.version": "v"
55
+ "settings.version": "v",
56
+ "settings.newTemplate": "Nova mensagem",
57
+ "settings.remove": "Remover",
58
+ "settings.key": "Chave da notificação",
59
+ "settings.keyHint": "Não muda depois de criada — é por ela que o envio acha a mensagem.",
60
+ "settings.channel": "Canal",
61
+ "settings.locale": "Idioma",
62
+ "settings.variablesTitle": "Variáveis",
63
+ "settings.variablesHint": "Clique para inserir no ponto onde o cursor estava.",
64
+ "settings.unknownVariables": "Variáveis que não existem nesta notificação:",
65
+ "settings.viewport.desktop": "Computador",
66
+ "settings.viewport.mobile": "Celular",
67
+ "settings.constraint.title": "Assunto passa do limite do canal:",
68
+ "settings.constraint.body": "Texto passa do limite do canal:",
69
+ "settings.tab.messages": "Mensagens",
70
+ "settings.tab.routing": "Roteamento",
71
+ "settings.policiesTitle": "Canais permitidos por assunto",
72
+ "settings.policiesHint": "Decisão da empresa. Canal desligado aqui não sai, mesmo que o destinatário tenha pedido.",
73
+ "settings.savePolicies": "Salvar roteamento",
74
+ "settings.discard": "Descartar"
55
75
  };
56
76
 
57
77
  /**
@@ -73,6 +93,12 @@ type NotificationTheme = {
73
93
  };
74
94
  type NotificationContextValue = {
75
95
  readonly client: NotificationClient;
96
+ /**
97
+ * O idioma resolvido, e não só os textos dele: data e número não vivem no arquivo de locale —
98
+ * quem os formata é `Intl`, e `Intl` precisa da tag do idioma. Sem isto o item da lista teria de
99
+ * chutar um locale fixo, e a inbox mostraria "5 minutes ago" numa tela em português.
100
+ */
101
+ readonly locale: NotificationLocale;
76
102
  readonly messages: NotificationMessages;
77
103
  readonly theme: NotificationTheme;
78
104
  /** Polling de fallback quando o SSE não está disponível (segundos); `0` desliga. */
@@ -126,6 +152,37 @@ type UseNotificationStreamOptions = {
126
152
  */
127
153
  declare function useNotificationStream(options?: UseNotificationStreamOptions): void;
128
154
 
155
+ /**
156
+ * Copyright (c) 2026 Ada Technology. MIT License.
157
+ *
158
+ * O roteamento: por quais canais cada categoria PODE sair, decidido pela empresa.
159
+ *
160
+ * É a camada que faltava. O chamador já podia enumerar canais em cada `sendNotification`, e quem
161
+ * recebe já tinha preferência — mas "cobrança nunca sai por SMS" não cabia em nenhum dos dois:
162
+ * o primeiro espalha a decisão por todo o código, o segundo deixa ela na mão do destinatário.
163
+ *
164
+ * O modelo aqui é de teto, não de piso: desligar barra o canal para todo mundo; ligar apenas
165
+ * permite, e a preferência de quem recebe continua valendo por cima.
166
+ */
167
+ type UseCategoryPoliciesResult = {
168
+ readonly isLoading: boolean;
169
+ readonly isSaving: boolean;
170
+ readonly error: string | undefined;
171
+ readonly isDirty: boolean;
172
+ /** Linha ausente é permitido — o mesmo default do servidor, para a tela não mentir. */
173
+ isAllowed: (params: {
174
+ category: string;
175
+ channel: string;
176
+ }) => boolean;
177
+ toggle: (params: {
178
+ category: string;
179
+ channel: string;
180
+ }) => void;
181
+ save: () => void;
182
+ reset: () => void;
183
+ };
184
+ declare function useCategoryPolicies(): UseCategoryPoliciesResult;
185
+
129
186
  declare function usePreferences(): _tanstack_react_query.UseQueryResult<NoInfer<readonly NotificationPreference[]>, Error>;
130
187
  declare function useUpdatePreferences(): _tanstack_react_query.UseMutationResult<readonly NotificationPreference[], Error, readonly NotificationPreference[], unknown>;
131
188
  declare function useTemplates(): _tanstack_react_query.UseQueryResult<NoInfer<readonly _adatechnology_notification_contracts.NotificationTemplate[]>, Error>;
@@ -146,6 +203,17 @@ declare function useUpsertTemplate(): _tanstack_react_query.UseMutationResult<_a
146
203
  subject?: string | undefined;
147
204
  whatsappTemplateName?: string | undefined;
148
205
  }, unknown>;
206
+ /**
207
+ * O catálogo é config do host: muda com deploy, não com uso. `staleTime: Infinity` evita refetch a
208
+ * cada foco de janela por um dado que não muda durante a sessão.
209
+ */
210
+ declare function useTemplateVariables(): _tanstack_react_query.UseQueryResult<NoInfer<Readonly<Record<string, readonly _adatechnology_notification_contracts.TemplateVariableDefinition[]>>>, Error>;
211
+ /**
212
+ * Desativa o template. Invalida a lista pelo mesmo motivo do upsert: o servidor derruba TODAS as
213
+ * versões ativas da identidade, e um cache otimista que removesse só a linha clicada mostraria a
214
+ * versão anterior de volta na tela.
215
+ */
216
+ declare function useDeactivateTemplate(): _tanstack_react_query.UseMutationResult<void, Error, string, unknown>;
149
217
 
150
218
  /**
151
219
  * Copyright (c) 2026 Ada Technology. MIT License.
@@ -166,8 +234,23 @@ declare const NOTIFICATION_QUERY_KEYS: {
166
234
  readonly unreadCount: () => readonly ["notifications", "unread-count"];
167
235
  readonly preferences: () => readonly ["notifications", "preferences"];
168
236
  readonly templates: () => readonly ["notifications", "templates"];
237
+ readonly templateVariables: () => readonly ["notifications", "template-variables"];
238
+ readonly categoryPolicies: () => readonly ["notifications", "category-policies"];
169
239
  };
170
240
 
241
+ /**
242
+ * Copyright (c) 2026 Ada Technology. MIT License.
243
+ *
244
+ * Quando o aviso chegou, dito do jeito que se diz em voz alta.
245
+ *
246
+ * `Intl` faz o trabalho de idioma — o pacote não guarda "há", "ago" nem nome de mês em lugar nenhum,
247
+ * o que manteria `web.md` §6 de pé e ainda assim quebraria no primeiro locale novo. O que é decisão
248
+ * nossa são as faixas: segundo, minuto, hora, dia, e a virada para data absoluta.
249
+ */
250
+ declare function formatNotificationTime(isoTimestamp: string, locale: string, now: Date): string;
251
+ /** O instante inteiro, para o `title` — é a hora que o texto relativo esconde. */
252
+ declare function formatNotificationTimestamp(isoTimestamp: string, locale: string): string;
253
+
171
254
  /**
172
255
  * Copyright (c) 2026 Ada Technology. MIT License.
173
256
  *
@@ -185,6 +268,10 @@ declare const NOTIFICATION_QUERY_KEYS: {
185
268
  * derivar título vazio, em vez de cair no fallback que usa o corpo.
186
269
  * 4. **Preview pelo `renderTemplate` do contracts**, o mesmo do envio. Reimplementar a interpolação
187
270
  * dá um preview que confere hoje e mente quando o renderer mudar.
271
+ *
272
+ * A criação de template novo trouxe uma quinta: **a identidade é imutável depois de criada**.
273
+ * `key`+`channel`+`locale` é o que o `sendNotification` procura; deixar editar num template
274
+ * existente não seria edição, seria criar outro e deixar o antigo órfão no ar.
188
275
  */
189
276
 
190
277
  type TemplateDraft = {
@@ -195,27 +282,52 @@ type TemplateDraft = {
195
282
  readonly body: string;
196
283
  readonly whatsappTemplateName: string;
197
284
  };
285
+ type TemplateDraftField = 'subject' | 'body';
286
+ type TemplatePreviewFrame = PreviewViewportSpec & {
287
+ readonly rendered: RenderedTemplatePreview;
288
+ };
198
289
  type UseTemplateEditorParams = {
199
290
  /**
200
- * Valores de exemplo do preview. É do produto: só ele sabe que `{{shortCode}}` é "QC-1042".
201
- * Ausente, o preview mostra os campos vazios — que é o que o destinatário veria mesmo.
291
+ * Valores de exemplo do preview. Hoje o padrão vem do catálogo de variáveis do servidor; isto
292
+ * continua existindo para o produto sobrescrever um exemplo pontual sem mexer no catálogo.
202
293
  */
203
294
  readonly previewPayload?: Readonly<Record<string, unknown>>;
295
+ /** Canal e locale de um template novo, antes de o operador escolher. */
296
+ readonly defaultChannel?: string;
297
+ readonly defaultLocale?: string;
204
298
  };
205
299
  type UseTemplateEditorResult = {
206
300
  readonly templates: readonly NotificationTemplate[];
207
301
  readonly isLoading: boolean;
208
302
  readonly isSaving: boolean;
303
+ readonly isDeactivating: boolean;
209
304
  readonly error: string | undefined;
210
305
  readonly selected: NotificationTemplate | undefined;
211
306
  readonly draft: TemplateDraft | undefined;
212
- readonly preview: RenderedTemplatePreview | undefined;
307
+ /** `true` enquanto o rascunho é um template que ainda não existe no servidor. */
308
+ readonly isNew: boolean;
309
+ /** Identidade travada — `false` só durante a criação. */
310
+ readonly isIdentityLocked: boolean;
311
+ /** Os dois quadros do preview, na ordem em que a tela desenha. */
312
+ readonly previews: readonly TemplatePreviewFrame[];
313
+ /** As variáveis que esta `key` declara. Vazio quando o catálogo não conhece a chave. */
314
+ readonly variables: readonly TemplateVariableDefinition[];
315
+ readonly variableDiff: TemplateVariableDiff;
213
316
  readonly isDirty: boolean;
317
+ readonly canSave: boolean;
214
318
  select: (template: NotificationTemplate) => void;
319
+ startNew: () => void;
215
320
  update: (patch: Partial<TemplateDraft>) => void;
321
+ /** Insere `{{nome}}` na posição do cursor. O operador nunca digita o nome à mão. */
322
+ insertVariable: (params: {
323
+ name: string;
324
+ field: TemplateDraftField;
325
+ cursorIndex: number;
326
+ }) => void;
216
327
  save: () => void;
328
+ deactivate: (id: string) => void;
217
329
  clear: () => void;
218
330
  };
219
331
  declare function useTemplateEditor(params?: UseTemplateEditorParams): UseTemplateEditorResult;
220
332
 
221
- export { DEFAULT_LOCALE, NOTIFICATION_QUERY_KEYS, type NotificationContextValue, type NotificationLocale, type NotificationMessages, NotificationProvider, type NotificationProviderProps, type NotificationTheme, type TemplateDraft, type UseNotificationStreamOptions, type UseNotificationsOptions, type UseTemplateEditorParams, type UseTemplateEditorResult, flattenNotificationPages, resolveMessages, useDeleteNotification, useMarkAllAsRead, useMarkAsRead, useNotificationContext, useNotificationStream, useNotifications, usePreferences, useTemplateEditor, useTemplates, useUnreadCount, useUpdatePreferences, useUpsertTemplate };
333
+ export { DEFAULT_LOCALE, NOTIFICATION_QUERY_KEYS, type NotificationContextValue, type NotificationLocale, type NotificationMessages, NotificationProvider, type NotificationProviderProps, type NotificationTheme, type TemplateDraft, type TemplateDraftField, type TemplatePreviewFrame, type UseCategoryPoliciesResult, type UseNotificationStreamOptions, type UseNotificationsOptions, type UseTemplateEditorParams, type UseTemplateEditorResult, flattenNotificationPages, formatNotificationTime, formatNotificationTimestamp, resolveMessages, useCategoryPolicies, useDeactivateTemplate, useDeleteNotification, useMarkAllAsRead, useMarkAsRead, useNotificationContext, useNotificationStream, useNotifications, usePreferences, useTemplateEditor, useTemplateVariables, useTemplates, useUnreadCount, useUpdatePreferences, useUpsertTemplate };
package/dist/headless.js CHANGED
@@ -3,7 +3,11 @@ import {
3
3
  NOTIFICATION_QUERY_KEYS,
4
4
  NotificationProvider,
5
5
  flattenNotificationPages,
6
+ formatNotificationTime,
7
+ formatNotificationTimestamp,
6
8
  resolveMessages,
9
+ useCategoryPolicies,
10
+ useDeactivateTemplate,
7
11
  useDeleteNotification,
8
12
  useMarkAllAsRead,
9
13
  useMarkAsRead,
@@ -12,17 +16,22 @@ import {
12
16
  useNotifications,
13
17
  usePreferences,
14
18
  useTemplateEditor,
19
+ useTemplateVariables,
15
20
  useTemplates,
16
21
  useUnreadCount,
17
22
  useUpdatePreferences,
18
23
  useUpsertTemplate
19
- } from "./chunk-FNUYTJ36.js";
24
+ } from "./chunk-QBQ62VZ3.js";
20
25
  export {
21
26
  DEFAULT_LOCALE,
22
27
  NOTIFICATION_QUERY_KEYS,
23
28
  NotificationProvider,
24
29
  flattenNotificationPages,
30
+ formatNotificationTime,
31
+ formatNotificationTimestamp,
25
32
  resolveMessages,
33
+ useCategoryPolicies,
34
+ useDeactivateTemplate,
26
35
  useDeleteNotification,
27
36
  useMarkAllAsRead,
28
37
  useMarkAsRead,
@@ -31,6 +40,7 @@ export {
31
40
  useNotifications,
32
41
  usePreferences,
33
42
  useTemplateEditor,
43
+ useTemplateVariables,
34
44
  useTemplates,
35
45
  useUnreadCount,
36
46
  useUpdatePreferences,
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { DEFAULT_LOCALE, NOTIFICATION_QUERY_KEYS, NotificationContextValue, NotificationLocale, NotificationMessages, NotificationProvider, NotificationProviderProps, NotificationTheme, TemplateDraft, UseNotificationStreamOptions, UseNotificationsOptions, UseTemplateEditorParams, UseTemplateEditorResult, flattenNotificationPages, resolveMessages, useDeleteNotification, useMarkAllAsRead, useMarkAsRead, useNotificationContext, useNotificationStream, useNotifications, usePreferences, useTemplateEditor, useTemplates, useUnreadCount, useUpdatePreferences, useUpsertTemplate } from './headless.js';
1
+ export { DEFAULT_LOCALE, NOTIFICATION_QUERY_KEYS, NotificationContextValue, NotificationLocale, NotificationMessages, NotificationProvider, NotificationProviderProps, NotificationTheme, TemplateDraft, TemplateDraftField, TemplatePreviewFrame, UseCategoryPoliciesResult, UseNotificationStreamOptions, UseNotificationsOptions, UseTemplateEditorParams, UseTemplateEditorResult, flattenNotificationPages, formatNotificationTime, formatNotificationTimestamp, resolveMessages, useCategoryPolicies, useDeactivateTemplate, useDeleteNotification, useMarkAllAsRead, useMarkAsRead, useNotificationContext, useNotificationStream, useNotifications, usePreferences, useTemplateEditor, useTemplateVariables, useTemplates, useUnreadCount, useUpdatePreferences, useUpsertTemplate } from './headless.js';
2
2
  import * as react from 'react';
3
3
  import { ReactNode } from 'react';
4
4
  import { NotificationSummary, NotificationTemplate } from '@adatechnology/notification-contracts';
@@ -14,7 +14,7 @@ type NotificationBellProps = {
14
14
  /** Acima disso o badge mostra `99+`; um número de 4 dígitos estoura o desenho do badge. */
15
15
  readonly maxBadgeCount?: number;
16
16
  };
17
- declare function NotificationBell({ onClick, className, maxBadgeCount }: NotificationBellProps): react.JSX.Element;
17
+ declare function NotificationBell({ onClick, className, maxBadgeCount, }: NotificationBellProps): react.JSX.Element;
18
18
 
19
19
  type NotificationItemProps = {
20
20
  readonly notification: NotificationSummary;
@@ -27,12 +27,17 @@ type NotificationListProps = {
27
27
  readonly category?: string;
28
28
  readonly onSelect?: (notification: NotificationSummary) => void;
29
29
  readonly className?: string;
30
+ /**
31
+ * Substitui o vazio padrão. Quem decide se está vazio é a lista — o host que desenhava o próprio
32
+ * vazio ao lado dela mostrava "nada por aqui" embaixo das notificações que estavam ali.
33
+ */
34
+ readonly renderEmpty?: () => ReactNode;
30
35
  /** Slot: o host troca a linha sem reimplementar a lista (regra §4.3, slots e overrides). */
31
36
  readonly components?: {
32
37
  readonly Item?: typeof NotificationItem;
33
38
  };
34
39
  };
35
- declare function NotificationList({ category, onSelect, className, components }: NotificationListProps): react.JSX.Element;
40
+ declare function NotificationList({ category, onSelect, className, renderEmpty, components }: NotificationListProps): react.JSX.Element;
36
41
 
37
42
  /**
38
43
  * Copyright (c) 2026 Ada Technology. MIT License.
@@ -58,6 +63,14 @@ type NotificationsWorkspaceProps = {
58
63
  readonly settingsHref?: string | undefined;
59
64
  /** Clique numa notificação. Ausente, a linha não é clicável. */
60
65
  readonly onSelectNotification?: (notification: NotificationSummary) => void;
66
+ /**
67
+ * Substitui o cabeçalho padrão inteiro.
68
+ *
69
+ * O workspace desenha um `<h1>` porque ele é a tela; a página que já tem o título dela passava a
70
+ * ter dois `<h1>` dizendo a mesma palavra, e para leitor de tela duas primeiras manchetes é o
71
+ * mesmo que nenhuma. Ausente, o padrão continua desenhando — slot é substituição, não interruptor.
72
+ */
73
+ readonly renderHeader?: () => ReactNode;
61
74
  /** Ações do produto no cabeçalho, ao lado do link de configuração. */
62
75
  readonly renderHeaderActions?: () => ReactNode;
63
76
  /** Aviso ou filtro do produto, entre o cabeçalho e a lista. */
@@ -66,7 +79,7 @@ type NotificationsWorkspaceProps = {
66
79
  readonly renderEmpty?: () => ReactNode;
67
80
  readonly className?: string;
68
81
  };
69
- declare function NotificationsWorkspace({ labels: labelsOverride, category, showPreferences, settingsHref, onSelectNotification, renderHeaderActions, renderAboveList, renderEmpty, className, }: NotificationsWorkspaceProps): react.JSX.Element;
82
+ declare function NotificationsWorkspace({ labels: labelsOverride, category, showPreferences, settingsHref, onSelectNotification, renderHeader, renderHeaderActions, renderAboveList, renderEmpty, className, }: NotificationsWorkspaceProps): react.JSX.Element;
70
83
 
71
84
  type NotificationChannelOption = {
72
85
  readonly id: string;
@@ -101,10 +114,12 @@ type NotificationSettingsWorkspaceProps = {
101
114
  readonly value: string;
102
115
  readonly onChange: (value: string) => void;
103
116
  }) => ReactNode;
117
+ /** Substitui o cabeçalho padrão — a página que já tem título próprio evita o segundo `<h1>`. */
118
+ readonly renderHeader?: () => ReactNode;
104
119
  /** Ações do produto no cabeçalho. */
105
120
  readonly renderHeaderActions?: () => ReactNode;
106
121
  readonly className?: string;
107
122
  };
108
- declare function NotificationSettingsWorkspace({ labels: labelsOverride, channels, categories, previewPayload, templateLabelOf, renderChannelFields, renderHeaderActions, className, }: NotificationSettingsWorkspaceProps): react.JSX.Element;
123
+ declare function NotificationSettingsWorkspace({ labels: labelsOverride, channels, categories, previewPayload, templateLabelOf, renderChannelFields, renderHeader, renderHeaderActions, className, }: NotificationSettingsWorkspaceProps): react.JSX.Element;
109
124
 
110
125
  export { NotificationBell, type NotificationBellProps, type NotificationCategoryOption, type NotificationChannelOption, NotificationItem, type NotificationItemProps, NotificationList, type NotificationListProps, NotificationSettingsWorkspace, type NotificationSettingsWorkspaceProps, NotificationsWorkspace, type NotificationsWorkspaceProps, PreferencesPanel, type PreferencesPanelProps };