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

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.
@@ -0,0 +1,468 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import * as _adatechnology_notification_client from '@adatechnology/notification-client';
4
+ import { NotificationClient } from '@adatechnology/notification-client';
5
+ import * as _tanstack_react_query from '@tanstack/react-query';
6
+ import * as _adatechnology_notification_contracts from '@adatechnology/notification-contracts';
7
+ import { NotificationSummary, NotificationPreference, PreviewViewportSpec, RenderedTemplatePreview, NotificationTemplate, TemplateVariableDefinition, TemplateVariableDiff } from '@adatechnology/notification-contracts';
8
+
9
+ var ptBR = {
10
+ "bell.label": "Notificações",
11
+ "bell.unreadSuffix": "não lidas",
12
+ "list.title": "Caixa de entrada",
13
+ "list.unread": "não lidas",
14
+ "list.empty": "Nenhuma notificação por aqui.",
15
+ "list.loading": "Carregando…",
16
+ "list.error": "Não foi possível carregar suas notificações.",
17
+ "list.retry": "Tentar de novo",
18
+ "list.loadMore": "Carregar mais",
19
+ "list.markAllAsRead": "Marcar todas como lidas",
20
+ "item.markAsRead": "Marcar como lida",
21
+ "item.delete": "Excluir",
22
+ "item.unread": "Não lida",
23
+ "preferences.title": "Preferências de notificação",
24
+ "preferences.description": "Escolha por onde você quer ser avisado em cada assunto.",
25
+ "preferences.save": "Salvar preferências",
26
+ "preferences.saved": "Preferências salvas.",
27
+ "preferences.error": "Não foi possível salvar. Tente de novo.",
28
+ "preferences.empty": "Nenhuma preferência configurada ainda.",
29
+ "preferences.quietHours": "Silêncio entre",
30
+ "preferences.quietHoursTo": "e",
31
+ "channel.inbox": "No app",
32
+ "channel.push": "Push",
33
+ "channel.email": "E-mail",
34
+ "channel.whatsapp": "WhatsApp",
35
+ "channel.sms": "SMS",
36
+ "workspace.title": "Notificações",
37
+ "workspace.description": "O que foi avisado, e por quais canais.",
38
+ "workspace.settingsLink": "Configurações",
39
+ "settings.title": "Configurações de notificação",
40
+ "settings.description": "Por onde avisar, e o texto exato que chega.",
41
+ "settings.channelsTitle": "Canais",
42
+ "settings.channelsHint": "Desligar um canal não apaga o histórico — o aviso deixa de sair por ali daqui em diante.",
43
+ "settings.templatesTitle": "Mensagens",
44
+ "settings.templatesHint": "Uma versão nova é criada a cada gravação; a anterior fica no histórico.",
45
+ "settings.templatesEmpty": "Nenhuma mensagem cadastrada.",
46
+ "settings.pickTemplate": "Escolha uma mensagem para editar.",
47
+ "settings.subject": "Título",
48
+ "settings.subjectHint": "O que aparece na lista. Vazio, o título é derivado da primeira linha do texto.",
49
+ "settings.body": "Texto",
50
+ "settings.previewTitle": "Como o destinatário vê",
51
+ "settings.save": "Salvar nova versão",
52
+ "settings.saving": "Salvando…",
53
+ "settings.unchanged": "Nada mudou ainda.",
54
+ "settings.close": "Fechar",
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.constraint.title": "Assunto passa do limite do canal:",
66
+ "settings.constraint.body": "Texto passa do limite do canal:",
67
+ "settings.tab.messages": "Mensagens",
68
+ "settings.tab.routing": "Roteamento",
69
+ "settings.policiesTitle": "Canais permitidos por assunto",
70
+ "settings.policiesHint": "Decisão da empresa. Canal desligado aqui não sai, mesmo que o destinatário tenha pedido.",
71
+ "settings.savePolicies": "Salvar roteamento",
72
+ "settings.discard": "Descartar",
73
+ "preview.to": "para você",
74
+ "preview.now": "agora",
75
+ "preview.mailbox": "Buscar no e-mail",
76
+ "settings.viewport.browser": "Navegador",
77
+ "settings.viewport.ios": "iPhone",
78
+ "settings.viewport.android": "Android",
79
+ "settings.channelHint": "Pode marcar mais de um — o mesmo texto é gravado para cada canal.",
80
+ "preview.time": "9:41",
81
+ "preview.address": "caixa de entrada",
82
+ "preview.counter": "1 de 1.248",
83
+ "preview.folder": "Caixa de entrada",
84
+ "preview.senderAddress": "<nao-responda@exemplo.com>",
85
+ "preview.unsubscribe": "Cancelar inscrição",
86
+ "preview.online": "online",
87
+ "preview.today": "hoje",
88
+ "preview.compose": "Mensagem",
89
+ "preview.reply": "Responder",
90
+ "preview.forward": "Encaminhar",
91
+ "preview.replyAll": "Responder a todos",
92
+ "settings.searchLabel": "Buscar mensagem",
93
+ "settings.searchPlaceholder": "Buscar por chave ou texto…",
94
+ "settings.clearFilters": "Limpar filtros",
95
+ "settings.noResults": "Nenhuma mensagem para esse filtro.",
96
+ "settings.viewLabel": "Modo de exibição",
97
+ "settings.view.table": "Tabela",
98
+ "settings.view.list": "Lista",
99
+ "settings.column.key": "Mensagem",
100
+ "settings.column.body": "Texto",
101
+ "settings.column.channel": "Canal",
102
+ "settings.column.version": "Versão",
103
+ "settings.column.actions": "Ações",
104
+ "toolbar.bold": "Negrito",
105
+ "toolbar.italic": "Italico",
106
+ "toolbar.strikethrough": "Tachado",
107
+ "toolbar.monospace": "Monoespacado",
108
+ "toolbar.emoji": "Emoji"
109
+ };
110
+
111
+ /**
112
+ * Copyright (c) 2026 Ada Technology. MIT License.
113
+ *
114
+ * Nenhum texto visível vive em tag ou prop (`web.md` §6). O host sobrescreve chaves pontuais via
115
+ * `messageOverrides` sem precisar fornecer um locale inteiro.
116
+ */
117
+
118
+ type NotificationMessages = typeof ptBR;
119
+ type NotificationLocale = 'pt-BR' | 'en';
120
+ declare const DEFAULT_LOCALE: NotificationLocale;
121
+ declare function resolveMessages(locale: NotificationLocale): NotificationMessages;
122
+
123
+ type NotificationTheme = {
124
+ /** Classe aplicada na raiz de cada componente — é por onde o host injeta os tokens dele. */
125
+ readonly rootClassName?: string;
126
+ readonly components?: Partial<Record<string, string>>;
127
+ };
128
+ type NotificationContextValue = {
129
+ readonly client: NotificationClient;
130
+ /**
131
+ * O idioma resolvido, e não só os textos dele: data e número não vivem no arquivo de locale —
132
+ * quem os formata é `Intl`, e `Intl` precisa da tag do idioma. Sem isto o item da lista teria de
133
+ * chutar um locale fixo, e a inbox mostraria "5 minutes ago" numa tela em português.
134
+ */
135
+ readonly locale: NotificationLocale;
136
+ readonly messages: NotificationMessages;
137
+ readonly theme: NotificationTheme;
138
+ /** Polling de fallback quando o SSE não está disponível (segundos); `0` desliga. */
139
+ readonly pollIntervalSeconds: number;
140
+ };
141
+ type NotificationProviderProps = {
142
+ readonly client: NotificationClient;
143
+ readonly locale?: NotificationLocale;
144
+ /** Sobrescreve textos pontuais sem trocar o locale inteiro. */
145
+ readonly messageOverrides?: Partial<NotificationMessages>;
146
+ readonly theme?: NotificationTheme;
147
+ readonly pollIntervalSeconds?: number;
148
+ readonly children: ReactNode;
149
+ };
150
+ declare function NotificationProvider({ client, locale, messageOverrides, theme, pollIntervalSeconds, children, }: NotificationProviderProps): react.JSX.Element;
151
+ declare function useNotificationContext(): NotificationContextValue;
152
+
153
+ type UseNotificationsOptions = {
154
+ readonly category?: string;
155
+ readonly read?: boolean;
156
+ readonly perPage?: number;
157
+ };
158
+ declare function useNotifications(options?: UseNotificationsOptions): _tanstack_react_query.UseInfiniteQueryResult<_tanstack_react_query.InfiniteData<_adatechnology_notification_client.ListNotificationsPage, unknown>, Error>;
159
+ declare function useUnreadCount(): _tanstack_react_query.UseQueryResult<number, Error>;
160
+ declare function useMarkAsRead(): _tanstack_react_query.UseMutationResult<NotificationSummary, Error, string, {
161
+ previousCount: number | undefined;
162
+ id: string;
163
+ }>;
164
+ declare function useMarkAllAsRead(): _tanstack_react_query.UseMutationResult<number, Error, void, unknown>;
165
+ declare function useDeleteNotification(): _tanstack_react_query.UseMutationResult<void, Error, string, unknown>;
166
+ /** Achata as páginas do infinite query — a lista quase sempre quer o array direto. */
167
+ declare function flattenNotificationPages(pages: readonly {
168
+ data: readonly NotificationSummary[];
169
+ }[] | undefined): readonly NotificationSummary[];
170
+
171
+ /**
172
+ * Copyright (c) 2026 Ada Technology. MIT License.
173
+ */
174
+ type UseNotificationStreamOptions = {
175
+ /** Desligado por omissão: abrir SSE é decisão do host, que sabe se o backend expõe a rota. */
176
+ readonly enabled?: boolean;
177
+ readonly onEvent?: (event: {
178
+ event: string;
179
+ data: unknown;
180
+ }) => void;
181
+ };
182
+ /**
183
+ * Liga o SSE do inbox ao cache do TanStack: cada evento invalida as chaves, e o badge e a lista
184
+ * atualizam juntos. `useEffect` aqui é o uso correto do hook — sincronizar com um sistema externo
185
+ * (`react.md`, "useEffect só para sincronizar com sistemas externos"), não transformar dado.
186
+ */
187
+ declare function useNotificationStream(options?: UseNotificationStreamOptions): void;
188
+
189
+ /**
190
+ * Copyright (c) 2026 Ada Technology. MIT License.
191
+ *
192
+ * O roteamento: por quais canais cada categoria PODE sair, decidido pela empresa.
193
+ *
194
+ * É a camada que faltava. O chamador já podia enumerar canais em cada `sendNotification`, e quem
195
+ * recebe já tinha preferência — mas "cobrança nunca sai por SMS" não cabia em nenhum dos dois:
196
+ * o primeiro espalha a decisão por todo o código, o segundo deixa ela na mão do destinatário.
197
+ *
198
+ * O modelo aqui é de teto, não de piso: desligar barra o canal para todo mundo; ligar apenas
199
+ * permite, e a preferência de quem recebe continua valendo por cima.
200
+ */
201
+ type UseCategoryPoliciesResult = {
202
+ readonly isLoading: boolean;
203
+ readonly isSaving: boolean;
204
+ readonly error: string | undefined;
205
+ readonly isDirty: boolean;
206
+ /** Linha ausente é permitido — o mesmo default do servidor, para a tela não mentir. */
207
+ isAllowed: (params: {
208
+ category: string;
209
+ channel: string;
210
+ }) => boolean;
211
+ toggle: (params: {
212
+ category: string;
213
+ channel: string;
214
+ }) => void;
215
+ save: () => void;
216
+ reset: () => void;
217
+ };
218
+ declare function useCategoryPolicies(): UseCategoryPoliciesResult;
219
+
220
+ declare function usePreferences(): _tanstack_react_query.UseQueryResult<NoInfer<readonly NotificationPreference[]>, Error>;
221
+ declare function useUpdatePreferences(): _tanstack_react_query.UseMutationResult<readonly NotificationPreference[], Error, readonly NotificationPreference[], unknown>;
222
+ declare function useTemplates(): _tanstack_react_query.UseQueryResult<NoInfer<readonly _adatechnology_notification_contracts.NotificationTemplate[]>, Error>;
223
+ /**
224
+ * Salva uma VERSÃO nova do template. É o que a tela de configuração usa para o lojista mudar a copy
225
+ * sem deploy.
226
+ *
227
+ * Invalida em vez de escrever no cache: o servidor decide o número da versão, então o objeto salvo
228
+ * traz um campo que o otimista não teria como prever — e mostrar "versão 3" quando o banco gravou 4
229
+ * confunde exatamente quem está tentando conferir o que salvou.
230
+ */
231
+ declare function useUpsertTemplate(): _tanstack_react_query.UseMutationResult<_adatechnology_notification_contracts.NotificationTemplate, Error, {
232
+ locale: string;
233
+ channel: "inbox" | "push" | "email" | "whatsapp" | "sms";
234
+ key: string;
235
+ body: string;
236
+ active: boolean;
237
+ subject?: string | undefined;
238
+ whatsappTemplateName?: string | undefined;
239
+ }, unknown>;
240
+ /**
241
+ * O catálogo é config do host: muda com deploy, não com uso. `staleTime: Infinity` evita refetch a
242
+ * cada foco de janela por um dado que não muda durante a sessão.
243
+ */
244
+ declare function useTemplateVariables(): _tanstack_react_query.UseQueryResult<NoInfer<Readonly<Record<string, readonly _adatechnology_notification_contracts.TemplateVariableDefinition[]>>>, Error>;
245
+ /**
246
+ * Desativa o template. Invalida a lista pelo mesmo motivo do upsert: o servidor derruba TODAS as
247
+ * versões ativas da identidade, e um cache otimista que removesse só a linha clicada mostraria a
248
+ * versão anterior de volta na tela.
249
+ */
250
+ declare function useDeactivateTemplate(): _tanstack_react_query.UseMutationResult<void, Error, string, unknown>;
251
+
252
+ /**
253
+ * Copyright (c) 2026 Ada Technology. MIT License.
254
+ *
255
+ * Chaves centralizadas: o sino e a lista leem o mesmo `unreadCount`, e uma invalidação precisa
256
+ * atingir os dois. Chave montada inline em cada hook é como badge e lista passam a mostrar
257
+ * números diferentes.
258
+ */
259
+ declare const NOTIFICATION_QUERY_KEYS: {
260
+ readonly all: readonly ["notifications"];
261
+ readonly list: (filters: {
262
+ category?: string;
263
+ read?: boolean;
264
+ }) => readonly ["notifications", "list", {
265
+ category?: string;
266
+ read?: boolean;
267
+ }];
268
+ readonly unreadCount: () => readonly ["notifications", "unread-count"];
269
+ readonly preferences: () => readonly ["notifications", "preferences"];
270
+ readonly templates: () => readonly ["notifications", "templates"];
271
+ readonly templateVariables: () => readonly ["notifications", "template-variables"];
272
+ readonly categoryPolicies: () => readonly ["notifications", "category-policies"];
273
+ };
274
+
275
+ /**
276
+ * Copyright (c) 2026 Ada Technology. MIT License.
277
+ *
278
+ * Quando o aviso chegou, dito do jeito que se diz em voz alta.
279
+ *
280
+ * `Intl` faz o trabalho de idioma — o pacote não guarda "há", "ago" nem nome de mês em lugar nenhum,
281
+ * o que manteria `web.md` §6 de pé e ainda assim quebraria no primeiro locale novo. O que é decisão
282
+ * nossa são as faixas: segundo, minuto, hora, dia, e a virada para data absoluta.
283
+ */
284
+ declare function formatNotificationTime(isoTimestamp: string, locale: string, now: Date): string;
285
+ /** O instante inteiro, para o `title` — é a hora que o texto relativo esconde. */
286
+ declare function formatNotificationTimestamp(isoTimestamp: string, locale: string): string;
287
+
288
+ /**
289
+ * Copyright (c) 2026 Ada Technology. MIT License.
290
+ *
291
+ * Modos de exibição e ordenação da lista de mensagens.
292
+ *
293
+ * Duas leituras da mesma coleção: a TABELA compara (canal e versão alinhados em coluna, varredura
294
+ * vertical) e a LISTA reconhece (o texto da mensagem visível em duas linhas). Quem administra trinta
295
+ * templates quer a tabela; quem procura "aquela que fala de senha" quer a lista.
296
+ */
297
+ declare const TEMPLATE_VIEWS: {
298
+ readonly TABLE: "table";
299
+ readonly LIST: "list";
300
+ };
301
+ type TemplateView = (typeof TEMPLATE_VIEWS)[keyof typeof TEMPLATE_VIEWS];
302
+ /** As colunas ordenáveis da tabela — e só elas: cabeçalho clicável que não ordena é mentira. */
303
+ declare const TEMPLATE_SORT_FIELDS: {
304
+ readonly KEY: "key";
305
+ readonly CHANNEL: "channel";
306
+ readonly VERSION: "version";
307
+ };
308
+ type TemplateSortField = (typeof TEMPLATE_SORT_FIELDS)[keyof typeof TEMPLATE_SORT_FIELDS];
309
+ declare const SORT_DIRECTIONS: {
310
+ readonly ASC: "asc";
311
+ readonly DESC: "desc";
312
+ };
313
+ type SortDirection = (typeof SORT_DIRECTIONS)[keyof typeof SORT_DIRECTIONS];
314
+ type TemplateSort = {
315
+ readonly field: TemplateSortField;
316
+ readonly direction: SortDirection;
317
+ };
318
+ /**
319
+ * As colunas da tabela, na ordem. `field` ausente = coluna que não ordena (texto livre e ações):
320
+ * cabeçalho clicável que não reordena nada é promessa quebrada.
321
+ */
322
+ type TemplateColumn = {
323
+ readonly labelKey: string;
324
+ readonly field?: TemplateSortField;
325
+ /** Cabeçalho só para leitor de tela — a coluna de ação não precisa de título visível. */
326
+ readonly headerHidden?: boolean;
327
+ };
328
+ declare const TEMPLATE_COLUMNS: readonly TemplateColumn[];
329
+
330
+ /**
331
+ * Copyright (c) 2026 Ada Technology. MIT License.
332
+ *
333
+ * A lógica do editor de template, que estava reimplementada no host.
334
+ *
335
+ * Ela veio de `quickcart/modules/notifications/hooks/useNotificationSettings.hook.ts` — 207 linhas
336
+ * que o segundo produto reescreveria, com as mesmas quatro decisões sutis para redescobrir. Elas
337
+ * são o motivo de isto ser pacote e não exemplo:
338
+ *
339
+ * 1. **Versão ativa mais alta.** A rota devolve o histórico inteiro, correto para auditoria; na tela
340
+ * isso faz a mesma mensagem aparecer três vezes e ninguém sabe qual está no ar.
341
+ * 2. **Seleção pelo id, não pelo objeto.** Salvar cria versão nova no servidor; segurando o objeto,
342
+ * o editor mostra a versão que já não é a ativa e a gravação seguinte parte de um texto velho.
343
+ * 3. **Campo vazio é ausência.** Gravar `subject: ''` faz o renderer tratar como subject existente e
344
+ * derivar título vazio, em vez de cair no fallback que usa o corpo.
345
+ * 4. **Preview pelo `renderTemplate` do contracts**, o mesmo do envio. Reimplementar a interpolação
346
+ * dá um preview que confere hoje e mente quando o renderer mudar.
347
+ *
348
+ * A criação de template novo trouxe uma quinta: **a identidade é imutável depois de criada**.
349
+ * `key`+`channel`+`locale` é o que o `sendNotification` procura; deixar editar num template
350
+ * existente não seria edição, seria criar outro e deixar o antigo órfão no ar.
351
+ */
352
+
353
+ type TemplateDraft = {
354
+ readonly key: string;
355
+ /**
356
+ * Canais, no plural: um aviso costuma sair por mais de um.
357
+ *
358
+ * No banco a identidade continua sendo `key`+`channel`+`locale` — uma linha por canal. Escolher
359
+ * varios aqui grava um template por canal com o MESMO texto, que e o comportamento certo para
360
+ * criar: escrever a mensagem uma vez e mandar por onde precisar. Depois, cada canal e editado
361
+ * sozinho pela lista, porque o texto tende a divergir (o SMS encurta, o WhatsApp perde o titulo).
362
+ */
363
+ readonly channels: readonly string[];
364
+ readonly locale: string;
365
+ /**
366
+ * Texto POR CANAL, e não um só copiado para todos.
367
+ *
368
+ * Os canais não são o mesmo recado em molduras diferentes: o WhatsApp ignora o assunto, o SMS
369
+ * cobra por segmento de 160 e encurta, o push corta em duas linhas. Um texto único obrigaria a
370
+ * escrever para o pior canal e mandar isso a todos. No banco cada canal já é uma linha própria
371
+ * (`key`+`channel`+`locale`) — aqui a tela passa a refletir isso.
372
+ */
373
+ readonly byChannel: Readonly<Record<string, ChannelDraft>>;
374
+ };
375
+ type ChannelDraft = {
376
+ readonly subject: string;
377
+ readonly body: string;
378
+ readonly whatsappTemplateName: string;
379
+ };
380
+ type TemplateDraftField = 'subject' | 'body';
381
+ type TemplatePreviewFrame = PreviewViewportSpec & {
382
+ /** O canal viaja com o quadro: a moldura do preview depende dele, e ler do rascunho dentro do
383
+ * `map` perde a garantia de que o rascunho existe. */
384
+ readonly channel: string;
385
+ readonly rendered: RenderedTemplatePreview;
386
+ };
387
+ type UseTemplateEditorParams = {
388
+ /**
389
+ * Valores de exemplo do preview. Hoje o padrão vem do catálogo de variáveis do servidor; isto
390
+ * continua existindo para o produto sobrescrever um exemplo pontual sem mexer no catálogo.
391
+ */
392
+ readonly previewPayload?: Readonly<Record<string, unknown>>;
393
+ /** Canal e locale de um template novo, antes de o operador escolher. */
394
+ readonly defaultChannel?: string;
395
+ readonly defaultLocale?: string;
396
+ };
397
+ /**
398
+ * Um grupo da lista.
399
+ *
400
+ * O prefixo da chave (`auth` em `auth.password_reset`) já é a categoria que o produto escreveu — o
401
+ * módulo não precisa de um campo novo no banco para agrupar, e um campo novo divergiria da chave
402
+ * na primeira vez que alguém renomeasse um sem o outro.
403
+ */
404
+ type TemplateGroup = {
405
+ readonly category: string;
406
+ readonly templates: readonly NotificationTemplate[];
407
+ };
408
+ type UseTemplateEditorResult = {
409
+ readonly templates: readonly NotificationTemplate[];
410
+ /** Já filtrados e agrupados por categoria, na ordem em que a tela desenha. */
411
+ readonly groups: readonly TemplateGroup[];
412
+ /** Todas as categorias existentes, inclusive as que o filtro escondeu. */
413
+ readonly categories: readonly string[];
414
+ readonly search: string;
415
+ readonly channelFilter: readonly string[];
416
+ readonly categoryFilter: readonly string[];
417
+ readonly hasFilters: boolean;
418
+ /** Quantos templates existem antes de filtrar — separa "nada cadastrado" de "nada encontrado". */
419
+ readonly totalCount: number;
420
+ /** Tabela para comparar, lista para reconhecer. */
421
+ readonly view: TemplateView;
422
+ /** Ausente = ordem natural (categoria, depois chave) — o terceiro estado do cabeçalho. */
423
+ readonly sort: TemplateSort | undefined;
424
+ setView: (view: TemplateView) => void;
425
+ /** Cicla `asc` → `desc` → neutro na mesma coluna; outra coluna recomeça em `asc`. */
426
+ toggleSort: (field: TemplateSortField) => void;
427
+ setSearch: (value: string) => void;
428
+ toggleChannelFilter: (channel: string) => void;
429
+ toggleCategoryFilter: (category: string) => void;
430
+ clearFilters: () => void;
431
+ readonly isLoading: boolean;
432
+ readonly isSaving: boolean;
433
+ readonly isDeactivating: boolean;
434
+ readonly error: string | undefined;
435
+ readonly selected: NotificationTemplate | undefined;
436
+ readonly draft: TemplateDraft | undefined;
437
+ /** `true` enquanto o rascunho é um template que ainda não existe no servidor. */
438
+ readonly isNew: boolean;
439
+ /** Identidade travada — `false` só durante a criação. */
440
+ readonly isIdentityLocked: boolean;
441
+ /** Os dois quadros do preview, na ordem em que a tela desenha. */
442
+ readonly previews: readonly TemplatePreviewFrame[];
443
+ /** As variáveis que esta `key` declara. Vazio quando o catálogo não conhece a chave. */
444
+ readonly variables: readonly TemplateVariableDefinition[];
445
+ readonly variableDiff: TemplateVariableDiff;
446
+ readonly isDirty: boolean;
447
+ readonly canSave: boolean;
448
+ select: (template: NotificationTemplate) => void;
449
+ startNew: () => void;
450
+ update: (patch: Partial<TemplateDraft>) => void;
451
+ /** Edita o texto de UM canal. */
452
+ updateChannel: (channel: string, patch: Partial<ChannelDraft>) => void;
453
+ /** Liga ou desliga um canal do rascunho. Nunca esvazia para nada: sem canal nao ha o que gravar. */
454
+ toggleChannel: (channel: string) => void;
455
+ /** Insere `{{nome}}` na posição do cursor. O operador nunca digita o nome à mão. */
456
+ insertVariable: (params: {
457
+ name: string;
458
+ field: TemplateDraftField;
459
+ cursorIndex: number;
460
+ channel: string;
461
+ }) => void;
462
+ save: () => void;
463
+ deactivate: (id: string) => void;
464
+ clear: () => void;
465
+ };
466
+ declare function useTemplateEditor(params?: UseTemplateEditorParams): UseTemplateEditorResult;
467
+
468
+ export { useDeleteNotification as A, useMarkAllAsRead as B, useMarkAsRead as C, DEFAULT_LOCALE as D, useNotificationContext as E, useNotificationStream as F, useNotifications as G, usePreferences as H, useTemplateEditor as I, useTemplateVariables as J, useTemplates as K, useUnreadCount as L, useUpdatePreferences as M, NOTIFICATION_QUERY_KEYS as N, useUpsertTemplate as O, SORT_DIRECTIONS as S, TEMPLATE_COLUMNS as T, type UseCategoryPoliciesResult as U, type NotificationContextValue as a, type NotificationLocale as b, type NotificationMessages as c, NotificationProvider as d, type NotificationProviderProps as e, type NotificationTheme as f, type SortDirection as g, TEMPLATE_SORT_FIELDS as h, TEMPLATE_VIEWS as i, type TemplateColumn as j, type TemplateDraft as k, type TemplateDraftField as l, type TemplatePreviewFrame as m, type TemplateSort as n, type TemplateSortField as o, type TemplateView as p, type UseNotificationStreamOptions as q, type UseNotificationsOptions as r, type UseTemplateEditorParams as s, type UseTemplateEditorResult as t, flattenNotificationPages as u, formatNotificationTime as v, formatNotificationTimestamp as w, resolveMessages as x, useCategoryPolicies as y, useDeactivateTemplate as z };