@adatechnology/notification-ui 0.4.1 → 0.6.0

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 @@
1
+ {"version":3,"sources":["../src/NotificationProvider.tsx","../src/locales/pt-BR.json","../src/locales/en.json","../src/locales/index.ts","../src/hooks/useNotificationPermission.ts","../src/hooks/useNotifications.ts","../src/hooks/queryKeys.ts","../src/hooks/useNotificationStream.ts","../src/hooks/useCategoryPolicies.ts","../src/hooks/usePreferences.ts","../src/format/notificationTime.ts","../src/hooks/useTemplateEditor.ts","../src/templateList.constant.ts","../src/standardEmail.util.ts"],"sourcesContent":["/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n *\n * Provider do pacote. **Não instancia `QueryClient`** — usa o do host (regra de módulos\n * plugáveis §4.2). Instanciar o próprio criaria dois caches na mesma página: a inbox invalidaria\n * um, e a tela do produto continuaria lendo o outro.\n */\n\nimport { createContext, useContext, useMemo, type ReactNode } from 'react'\nimport type { NotificationClient } from '@adatechnology/notification-client'\n\nimport { DEFAULT_LOCALE, resolveMessages, type NotificationLocale, type NotificationMessages } from './locales'\n\nexport type NotificationTheme = {\n /** Classe aplicada na raiz de cada componente — é por onde o host injeta os tokens dele. */\n readonly rootClassName?: string\n readonly components?: Partial<Record<string, string>>\n}\n\nexport type NotificationContextValue = {\n readonly client: NotificationClient\n /**\n * O idioma resolvido, e não só os textos dele: data e número não vivem no arquivo de locale —\n * quem os formata é `Intl`, e `Intl` precisa da tag do idioma. Sem isto o item da lista teria de\n * chutar um locale fixo, e a inbox mostraria \"5 minutes ago\" numa tela em português.\n */\n readonly locale: NotificationLocale\n readonly messages: NotificationMessages\n readonly theme: NotificationTheme\n /** Polling de fallback quando o SSE não está disponível (segundos); `0` desliga. */\n readonly pollIntervalSeconds: number\n}\n\nconst NotificationContext = createContext<NotificationContextValue | null>(null)\n\nexport type NotificationProviderProps = {\n readonly client: NotificationClient\n readonly locale?: NotificationLocale\n /** Sobrescreve textos pontuais sem trocar o locale inteiro. */\n readonly messageOverrides?: Partial<NotificationMessages>\n readonly theme?: NotificationTheme\n readonly pollIntervalSeconds?: number\n readonly children: ReactNode\n}\n\nexport function NotificationProvider({\n client,\n locale = DEFAULT_LOCALE,\n messageOverrides,\n theme = {},\n pollIntervalSeconds = 60,\n children,\n}: NotificationProviderProps) {\n const value = useMemo<NotificationContextValue>(\n () => ({\n client,\n locale,\n messages: { ...resolveMessages(locale), ...messageOverrides },\n theme,\n pollIntervalSeconds,\n }),\n [client, locale, messageOverrides, theme, pollIntervalSeconds],\n )\n\n return <NotificationContext.Provider value={value}>{children}</NotificationContext.Provider>\n}\n\nexport function useNotificationContext(): NotificationContextValue {\n const context = useContext(NotificationContext)\n if (!context) throw new Error('notification-ui: componente usado fora de <NotificationProvider>')\n return context\n}\n\nexport function useOptionalNotificationContext(): NotificationContextValue | null {\n return useContext(NotificationContext)\n}\n","{\n \"bell.label\": \"Notificações\",\n \"bell.unreadSuffix\": \"não lidas\",\n \"list.title\": \"Caixa de entrada\",\n \"list.unread\": \"não lidas\",\n \"list.empty\": \"Nenhuma notificação por aqui.\",\n \"list.loading\": \"Carregando…\",\n \"list.error\": \"Não foi possível carregar suas notificações.\",\n \"list.retry\": \"Tentar de novo\",\n \"list.loadMore\": \"Carregar mais\",\n \"list.markAllAsRead\": \"Marcar todas como lidas\",\n \"item.markAsRead\": \"Marcar como lida\",\n \"item.delete\": \"Excluir\",\n \"item.unread\": \"Não lida\",\n \"item.addToCalendar\": \"Adicionar à agenda\",\n \"preferences.title\": \"Preferências de notificação\",\n \"preferences.description\": \"Escolha por onde você quer ser avisado em cada assunto.\",\n \"preferences.save\": \"Salvar preferências\",\n \"preferences.saved\": \"Preferências salvas.\",\n \"preferences.error\": \"Não foi possível salvar. Tente de novo.\",\n \"preferences.empty\": \"Nenhuma preferência configurada ainda.\",\n \"preferences.quietHours\": \"Silêncio entre\",\n \"preferences.quietHoursTo\": \"e\",\n \"channel.inbox\": \"No app\",\n \"channel.push\": \"Push\",\n \"channel.email\": \"E-mail\",\n \"channel.whatsapp\": \"WhatsApp\",\n \"channel.sms\": \"SMS\",\n \"workspace.title\": \"Notificações\",\n \"workspace.description\": \"O que foi avisado, e por quais canais.\",\n \"workspace.settingsLink\": \"Configurações\",\n \"settings.title\": \"Configurações de notificação\",\n \"settings.description\": \"Por onde avisar, e o texto exato que chega.\",\n \"settings.channelsTitle\": \"Canais\",\n \"settings.channelsHint\": \"Desligar um canal não apaga o histórico — o aviso deixa de sair por ali daqui em diante.\",\n \"settings.templatesTitle\": \"Mensagens\",\n \"settings.templatesHint\": \"Uma versão nova é criada a cada gravação; a anterior fica no histórico.\",\n \"settings.templatesEmpty\": \"Nenhuma mensagem cadastrada.\",\n \"settings.pickTemplate\": \"Escolha uma mensagem para editar.\",\n \"settings.subject\": \"Título\",\n \"settings.subjectHint\": \"O que aparece na lista. Vazio, o título é derivado da primeira linha do texto.\",\n \"settings.body\": \"Texto\",\n \"settings.previewTitle\": \"Como o destinatário vê\",\n \"settings.save\": \"Salvar nova versão\",\n \"settings.saving\": \"Salvando…\",\n \"settings.unchanged\": \"Nada mudou ainda.\",\n \"settings.close\": \"Fechar\",\n \"settings.version\": \"v\",\n \"settings.newTemplate\": \"Nova mensagem\",\n \"settings.systemTemplate\": \"Modelo do sistema\",\n \"settings.restoreDefault\": \"Restaurar padrão\",\n \"settings.restoring\": \"Restaurando…\",\n \"settings.standardEmailHint\": \"Moldura de todo e-mail enviado pelo sistema. O assunto é o da conversa; use as variáveis obrigatórias.\",\n \"settings.standardMissing\": \"Variáveis obrigatórias que faltam:\",\n \"settings.standardUnsafe\": \"HTML não permitido neste modelo:\",\n \"settings.standardUnsafe.event\": \"atributo de evento (onclick e similares)\",\n \"settings.standardUnsafe.placeholder_in_tag\": \"variável dentro de uma tag ou atributo\",\n \"settings.standardUnsafe.scheme\": \"endereço com esquema não permitido\",\n \"settings.standardUnsafe.tag\": \"tag não permitida (script, iframe e similares)\",\n \"settings.remove\": \"Remover\",\n \"settings.key\": \"Chave da notificação\",\n \"settings.keyHint\": \"Não muda depois de criada — é por ela que o envio acha a mensagem.\",\n \"settings.channel\": \"Canal\",\n \"settings.locale\": \"Idioma\",\n \"settings.variablesTitle\": \"Variáveis\",\n \"settings.variablesHint\": \"Clique para inserir no ponto onde o cursor estava.\",\n \"settings.unknownVariables\": \"Variáveis que não existem nesta notificação:\",\n \"settings.constraint.title\": \"Assunto passa do limite do canal:\",\n \"settings.constraint.body\": \"Texto passa do limite do canal:\",\n \"settings.tab.messages\": \"Mensagens\",\n \"settings.tab.routing\": \"Roteamento\",\n \"settings.policiesTitle\": \"Canais permitidos por assunto\",\n \"settings.policiesHint\": \"Decisão da empresa. Canal desligado aqui não sai, mesmo que o destinatário tenha pedido.\",\n \"settings.savePolicies\": \"Salvar roteamento\",\n \"settings.discard\": \"Descartar\",\n \"preview.to\": \"para você\",\n \"preview.now\": \"agora\",\n \"preview.mailbox\": \"Buscar no e-mail\",\n \"settings.viewport.browser\": \"Navegador\",\n \"settings.viewport.ios\": \"iPhone\",\n \"settings.viewport.android\": \"Android\",\n \"settings.channelHint\": \"Pode marcar mais de um — o mesmo texto é gravado para cada canal.\",\n \"preview.time\": \"9:41\",\n \"preview.address\": \"caixa de entrada\",\n \"preview.counter\": \"1 de 1.248\",\n \"preview.folder\": \"Caixa de entrada\",\n \"preview.senderAddress\": \"<nao-responda@exemplo.com>\",\n \"preview.unsubscribe\": \"Cancelar inscrição\",\n \"preview.online\": \"online\",\n \"preview.today\": \"hoje\",\n \"preview.compose\": \"Mensagem\",\n \"preview.reply\": \"Responder\",\n \"preview.forward\": \"Encaminhar\",\n \"preview.replyAll\": \"Responder a todos\",\n \"settings.searchLabel\": \"Buscar mensagem\",\n \"settings.searchPlaceholder\": \"Buscar por chave ou texto…\",\n \"settings.clearFilters\": \"Limpar filtros\",\n \"settings.noResults\": \"Nenhuma mensagem para esse filtro.\",\n \"settings.viewLabel\": \"Modo de exibição\",\n \"settings.view.table\": \"Tabela\",\n \"settings.view.list\": \"Lista\",\n \"settings.column.key\": \"Mensagem\",\n \"settings.column.body\": \"Texto\",\n \"settings.column.channel\": \"Canal\",\n \"settings.column.version\": \"Versão\",\n \"settings.column.actions\": \"Ações\",\n \"toolbar.bold\": \"Negrito\",\n \"toolbar.italic\": \"Italico\",\n \"toolbar.strikethrough\": \"Tachado\",\n \"toolbar.monospace\": \"Monoespacado\",\n \"toolbar.emoji\": \"Emoji\",\n \"banner.title\": \"Ativar notificações\",\n \"banner.description\": \"Receba lembretes de atividades, mensagens e avisos importantes diretamente no seu navegador.\",\n \"banner.allow\": \"Permitir notificações\",\n \"banner.dismiss\": \"Agora não\",\n \"banner.activeTitle\": \"Notificações ativas\",\n \"banner.activeDescription\": \"Você está recebendo notificações neste navegador.\",\n \"banner.disable\": \"Desativar notificações\",\n \"banner.disabling\": \"Desativando…\",\n \"banner.deniedTitle\": \"Notificações bloqueadas\",\n \"banner.deniedDescription\": \"As notificações estão bloqueadas no navegador. Para receber alertas, permita nas configurações do site.\",\n \"settings.pager\": \"Paginação das mensagens\",\n \"settings.pagerRange\": \"{from}–{to} de {total}\",\n \"settings.pageSize\": \"Por página\",\n \"settings.pagePrevious\": \"Anterior\",\n \"settings.pageNext\": \"Próxima\",\n \"settings.pageOf\": \"Página {page} de {pages}\"\n}\n","{\n \"bell.label\": \"Notifications\",\n \"bell.unreadSuffix\": \"unread\",\n \"list.title\": \"Inbox\",\n \"list.unread\": \"unread\",\n \"list.empty\": \"Nothing here yet.\",\n \"list.loading\": \"Loading…\",\n \"list.error\": \"We couldn't load your notifications.\",\n \"list.retry\": \"Try again\",\n \"list.loadMore\": \"Load more\",\n \"list.markAllAsRead\": \"Mark all as read\",\n \"item.markAsRead\": \"Mark as read\",\n \"item.delete\": \"Delete\",\n \"item.unread\": \"Unread\",\n \"item.addToCalendar\": \"Add to calendar\",\n \"preferences.title\": \"Notification preferences\",\n \"preferences.description\": \"Choose how you want to be notified about each topic.\",\n \"preferences.save\": \"Save preferences\",\n \"preferences.saved\": \"Preferences saved.\",\n \"preferences.error\": \"Couldn't save. Try again.\",\n \"preferences.empty\": \"No preferences configured yet.\",\n \"preferences.quietHours\": \"Quiet between\",\n \"preferences.quietHoursTo\": \"and\",\n \"channel.inbox\": \"In app\",\n \"channel.push\": \"Push\",\n \"channel.email\": \"Email\",\n \"channel.whatsapp\": \"WhatsApp\",\n \"channel.sms\": \"SMS\",\n \"workspace.title\": \"Notifications\",\n \"workspace.description\": \"What was sent, and through which channels.\",\n \"workspace.settingsLink\": \"Settings\",\n \"settings.title\": \"Notification settings\",\n \"settings.description\": \"Where to notify, and the exact text that arrives.\",\n \"settings.channelsTitle\": \"Channels\",\n \"settings.channelsHint\": \"Turning a channel off does not erase history — notices stop going out through it from now on.\",\n \"settings.templatesTitle\": \"Messages\",\n \"settings.templatesHint\": \"Each save creates a new version; the previous one stays in history.\",\n \"settings.templatesEmpty\": \"No messages registered.\",\n \"settings.pickTemplate\": \"Pick a message to edit.\",\n \"settings.subject\": \"Title\",\n \"settings.subjectHint\": \"Shown in the list. Empty, the title is derived from the first line of the body.\",\n \"settings.body\": \"Body\",\n \"settings.previewTitle\": \"How the recipient sees it\",\n \"settings.save\": \"Save new version\",\n \"settings.saving\": \"Saving…\",\n \"settings.unchanged\": \"Nothing changed yet.\",\n \"settings.close\": \"Close\",\n \"settings.version\": \"v\",\n \"settings.newTemplate\": \"New message\",\n \"settings.systemTemplate\": \"System template\",\n \"settings.restoreDefault\": \"Restore default\",\n \"settings.restoring\": \"Restoring…\",\n \"settings.standardEmailHint\": \"The frame of every email the system sends. The subject comes from the conversation; keep the required variables.\",\n \"settings.standardMissing\": \"Missing required variables:\",\n \"settings.standardUnsafe\": \"HTML not allowed in this template:\",\n \"settings.standardUnsafe.event\": \"event attribute (onclick and similar)\",\n \"settings.standardUnsafe.placeholder_in_tag\": \"variable inside a tag or attribute\",\n \"settings.standardUnsafe.scheme\": \"address with a disallowed scheme\",\n \"settings.standardUnsafe.tag\": \"disallowed tag (script, iframe and similar)\",\n \"settings.remove\": \"Remove\",\n \"settings.key\": \"Notification key\",\n \"settings.keyHint\": \"Fixed once created — sending looks the message up by it.\",\n \"settings.channel\": \"Channel\",\n \"settings.locale\": \"Language\",\n \"settings.variablesTitle\": \"Variables\",\n \"settings.variablesHint\": \"Click to insert where the cursor was.\",\n \"settings.unknownVariables\": \"Variables this notification does not have:\",\n \"settings.constraint.title\": \"Subject exceeds the channel limit:\",\n \"settings.constraint.body\": \"Body exceeds the channel limit:\",\n \"settings.tab.messages\": \"Messages\",\n \"settings.tab.routing\": \"Routing\",\n \"settings.policiesTitle\": \"Allowed channels per topic\",\n \"settings.policiesHint\": \"Company decision. A channel turned off here never sends, even if the recipient opted in.\",\n \"settings.savePolicies\": \"Save routing\",\n \"settings.discard\": \"Discard\",\n \"preview.to\": \"to you\",\n \"preview.now\": \"now\",\n \"preview.mailbox\": \"Search mail\",\n \"settings.viewport.browser\": \"Browser\",\n \"settings.viewport.ios\": \"iPhone\",\n \"settings.viewport.android\": \"Android\",\n \"settings.channelHint\": \"Pick more than one — the same text is saved for each channel.\",\n \"preview.time\": \"9:41\",\n \"preview.address\": \"inbox\",\n \"preview.counter\": \"1 of 1,248\",\n \"preview.folder\": \"Inbox\",\n \"preview.senderAddress\": \"<no-reply@example.com>\",\n \"preview.unsubscribe\": \"Unsubscribe\",\n \"preview.online\": \"online\",\n \"preview.today\": \"today\",\n \"preview.compose\": \"Message\",\n \"preview.reply\": \"Reply\",\n \"preview.forward\": \"Forward\",\n \"preview.replyAll\": \"Reply all\",\n \"settings.searchLabel\": \"Search message\",\n \"settings.searchPlaceholder\": \"Search by key or text…\",\n \"settings.clearFilters\": \"Clear filters\",\n \"settings.noResults\": \"No message matches this filter.\",\n \"settings.viewLabel\": \"View mode\",\n \"settings.view.table\": \"Table\",\n \"settings.view.list\": \"List\",\n \"settings.column.key\": \"Message\",\n \"settings.column.body\": \"Text\",\n \"settings.column.channel\": \"Channel\",\n \"settings.column.version\": \"Version\",\n \"settings.column.actions\": \"Actions\",\n \"toolbar.bold\": \"Bold\",\n \"toolbar.italic\": \"Italic\",\n \"toolbar.strikethrough\": \"Strikethrough\",\n \"toolbar.monospace\": \"Monospace\",\n \"toolbar.emoji\": \"Emoji\",\n \"banner.title\": \"Enable notifications\",\n \"banner.description\": \"Receive activity reminders, messages, and important alerts directly in your browser.\",\n \"banner.allow\": \"Allow notifications\",\n \"banner.dismiss\": \"Not now\",\n \"banner.activeTitle\": \"Notifications enabled\",\n \"banner.activeDescription\": \"You are receiving notifications in this browser.\",\n \"banner.disable\": \"Disable notifications\",\n \"banner.disabling\": \"Disabling…\",\n \"banner.deniedTitle\": \"Notifications blocked\",\n \"banner.deniedDescription\": \"Notifications are blocked in your browser. To receive alerts, allow them in your site settings.\",\n \"settings.pager\": \"Message pagination\",\n \"settings.pagerRange\": \"{from}–{to} of {total}\",\n \"settings.pageSize\": \"Per page\",\n \"settings.pagePrevious\": \"Previous\",\n \"settings.pageNext\": \"Next\",\n \"settings.pageOf\": \"Page {page} of {pages}\"\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n *\n * Nenhum texto visível vive em tag ou prop (`web.md` §6). O host sobrescreve chaves pontuais via\n * `messageOverrides` sem precisar fornecer um locale inteiro.\n */\n\nimport ptBR from './pt-BR.json'\nimport en from './en.json'\n\nexport type NotificationMessages = typeof ptBR\nexport type NotificationLocale = 'pt-BR' | 'en'\n\nexport const DEFAULT_LOCALE: NotificationLocale = 'pt-BR'\n\nconst MESSAGES_BY_LOCALE: Record<NotificationLocale, NotificationMessages> = {\n 'pt-BR': ptBR,\n en: en as NotificationMessages,\n}\n\nexport function resolveMessages(locale: NotificationLocale): NotificationMessages {\n return MESSAGES_BY_LOCALE[locale] ?? MESSAGES_BY_LOCALE[DEFAULT_LOCALE]\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n *\n * Hook headless para gerenciar permissão e ciclo de vida de Web Push no navegador.\n * Isomórfico: seguro em SSR/Node/Bun sem quebrar quando `window` ou `Notification` não existem.\n */\n\nimport { useCallback, useEffect, useState } from 'react'\nimport {\n getWebPushSubscription,\n isWebPushSupported,\n subscribeWebPush,\n unsubscribeWebPush,\n} from '@adatechnology/notification-client'\nimport type { WebPushSubscription } from '@adatechnology/notification-contracts'\n\nimport { useOptionalNotificationContext } from '../NotificationProvider'\n\nexport type NotificationPermissionStatus = 'default' | 'granted' | 'denied' | 'unsupported'\n\nexport type UseNotificationPermissionOptions = {\n /** Chave pública VAPID necessária para inscrição Web Push. Se omitida, tenta buscar via client.getVapidPublicKey(). */\n readonly vapidPublicKey?: string\n /** Chave do localStorage para guardar a dispensa temporária do banner. Padrão: 'adatechnology:notification:permission-dismissed' */\n readonly storageKey?: string\n /** Validade da dispensa em ms (padrão: 7 dias). 0 para permanente. */\n readonly dismissTtlMs?: number\n /** Chave do localStorage onde o id do dispositivo registrado é salvo. Padrão: 'adatechnology:notification:device-id' */\n readonly deviceStorageKey?: string\n /** Callback executado após inscrição bem-sucedida. */\n readonly onSubscribe?: (subscription: WebPushSubscription) => Promise<void> | void\n /** Callback executado após cancelamento/desativação. */\n readonly onUnsubscribe?: () => Promise<void> | void\n /** ServiceWorkerRegistration específico (opcional, senão aguarda navigator.serviceWorker.ready). */\n readonly serviceWorkerRegistration?: ServiceWorkerRegistration\n}\n\nexport type UseNotificationPermissionResult = {\n readonly permission: NotificationPermissionStatus\n readonly isSupported: boolean\n readonly isSubscribed: boolean\n readonly isDismissed: boolean\n readonly isPending: boolean\n readonly error: Error | null\n readonly requestPermission: () => Promise<boolean>\n readonly disableNotifications: () => Promise<boolean>\n readonly dismiss: () => void\n readonly resetDismissed: () => void\n}\n\nconst DEFAULT_STORAGE_KEY = 'adatechnology:notification:permission-dismissed'\nconst DEFAULT_DEVICE_KEY = 'adatechnology:notification:device-id'\nconst DEFAULT_DISMISS_TTL_MS = 7 * 24 * 60 * 60 * 1000 // 7 dias\n\nexport function readCurrentNotificationPermission(): NotificationPermissionStatus {\n if (typeof window === 'undefined' || !('Notification' in window)) {\n return 'unsupported'\n }\n return Notification.permission as NotificationPermissionStatus\n}\n\nexport function checkIsNotificationDismissed(storageKey: string, dismissTtlMs: number): boolean {\n if (typeof window === 'undefined' || typeof window.localStorage === 'undefined') {\n return false\n }\n\n try {\n const raw = window.localStorage.getItem(storageKey)\n if (!raw) return false\n\n const parsed = JSON.parse(raw) as { dismissedAt?: number }\n if (!parsed?.dismissedAt) return false\n\n if (dismissTtlMs > 0 && Date.now() - parsed.dismissedAt > dismissTtlMs) {\n window.localStorage.removeItem(storageKey)\n return false\n }\n\n return true\n } catch {\n return false\n }\n}\n\nexport function useNotificationPermission(options?: UseNotificationPermissionOptions): UseNotificationPermissionResult {\n const context = useOptionalNotificationContext()\n\n const storageKey = options?.storageKey ?? DEFAULT_STORAGE_KEY\n const deviceStorageKey = options?.deviceStorageKey ?? DEFAULT_DEVICE_KEY\n const dismissTtlMs = options?.dismissTtlMs ?? DEFAULT_DISMISS_TTL_MS\n\n const isSupported = isWebPushSupported()\n const [permission, setPermission] = useState<NotificationPermissionStatus>(readCurrentNotificationPermission)\n const [isSubscribed, setIsSubscribed] = useState<boolean>(false)\n const [isDismissed, setIsDismissed] = useState<boolean>(() => checkIsNotificationDismissed(storageKey, dismissTtlMs))\n const [isPending, setIsPending] = useState<boolean>(false)\n const [error, setError] = useState<Error | null>(null)\n\n // Atualiza estado de permissão e inscrição existente no mount\n useEffect(() => {\n setPermission(readCurrentNotificationPermission())\n setIsDismissed(checkIsNotificationDismissed(storageKey, dismissTtlMs))\n\n if (!isSupported) return\n\n let isMounted = true\n getWebPushSubscription(options?.serviceWorkerRegistration)\n .then((sub) => {\n if (isMounted && sub) {\n setIsSubscribed(true)\n }\n })\n .catch(() => {\n // Falha silenciosa em navegadores que restringem push em iframe/modo anônimo\n })\n\n return () => {\n isMounted = false\n }\n }, [isSupported, storageKey, dismissTtlMs, options?.serviceWorkerRegistration])\n\n const requestPermission = useCallback(async (): Promise<boolean> => {\n if (typeof window === 'undefined' || !('Notification' in window)) {\n return false\n }\n\n setIsPending(true)\n setError(null)\n\n try {\n const result = await Notification.requestPermission()\n const updatedStatus = result as NotificationPermissionStatus\n setPermission(updatedStatus)\n\n if (updatedStatus !== 'granted') {\n setIsPending(false)\n return false\n }\n\n // Tenta obter a chave VAPID via options ou client\n let vapidKey = options?.vapidPublicKey\n if (!vapidKey && context?.client) {\n try {\n vapidKey = await context.client.getVapidPublicKey()\n } catch {\n // O backend pode não expor rota pública de VAPID; segue se não houver\n }\n }\n\n if (vapidKey) {\n const subscription = await subscribeWebPush({\n vapidPublicKey: vapidKey,\n serviceWorkerRegistration: options?.serviceWorkerRegistration,\n })\n\n if (subscription) {\n setIsSubscribed(true)\n\n if (context?.client) {\n try {\n const device = await context.client.registerWebPush(subscription)\n if (typeof window !== 'undefined' && window.localStorage) {\n window.localStorage.setItem(deviceStorageKey, device.id)\n }\n } catch {\n // Registro de device pode ser completado posteriormente\n }\n }\n\n if (options?.onSubscribe) {\n await options.onSubscribe(subscription)\n }\n }\n }\n\n // Concedido com sucesso: limpa qualquer flag de dispensa anterior\n if (typeof window !== 'undefined' && window.localStorage) {\n window.localStorage.removeItem(storageKey)\n }\n setIsDismissed(false)\n setIsPending(false)\n return true\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n setIsPending(false)\n return false\n }\n }, [\n context?.client,\n deviceStorageKey,\n options?.onSubscribe,\n options?.serviceWorkerRegistration,\n options?.vapidPublicKey,\n storageKey,\n ])\n\n const disableNotifications = useCallback(async (): Promise<boolean> => {\n setIsPending(true)\n setError(null)\n\n try {\n await unsubscribeWebPush(options?.serviceWorkerRegistration)\n\n if (context?.client && typeof window !== 'undefined' && window.localStorage) {\n const deviceId = window.localStorage.getItem(deviceStorageKey)\n if (deviceId) {\n await context.client.unregisterDevice(deviceId).catch(() => undefined)\n window.localStorage.removeItem(deviceStorageKey)\n }\n }\n\n if (options?.onUnsubscribe) {\n await options.onUnsubscribe()\n }\n\n setIsSubscribed(false)\n setIsPending(false)\n return true\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n setIsPending(false)\n return false\n }\n }, [context?.client, deviceStorageKey, options?.onUnsubscribe, options?.serviceWorkerRegistration])\n\n const dismiss = useCallback((): void => {\n if (typeof window !== 'undefined' && window.localStorage) {\n try {\n window.localStorage.setItem(storageKey, JSON.stringify({ dismissedAt: Date.now() }))\n } catch {\n // Ignora erros de quota de storage\n }\n }\n setIsDismissed(true)\n }, [storageKey])\n\n const resetDismissed = useCallback((): void => {\n if (typeof window !== 'undefined' && window.localStorage) {\n try {\n window.localStorage.removeItem(storageKey)\n } catch {\n // Ignora erros de storage\n }\n }\n setIsDismissed(false)\n }, [storageKey])\n\n return {\n permission,\n isSupported,\n isSubscribed,\n isDismissed,\n isPending,\n error,\n requestPermission,\n disableNotifications,\n dismiss,\n resetDismissed,\n }\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n *\n * Camada headless — a válvula de escape do item 4 da §4 da regra de módulos plugáveis: quando a\n * tela default não serve, o produto monta a própria sobre estes mesmos hooks e continua herdando\n * upgrades de lógica.\n */\n\nimport { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { NotificationSummary } from '@adatechnology/notification-contracts'\n\nimport { useNotificationContext } from '../NotificationProvider'\nimport { NOTIFICATION_QUERY_KEYS } from './queryKeys'\n\nexport type UseNotificationsOptions = {\n readonly category?: string\n readonly read?: boolean\n readonly perPage?: number\n}\n\nexport function useNotifications(options: UseNotificationsOptions = {}) {\n const { client } = useNotificationContext()\n\n return useInfiniteQuery({\n queryKey: NOTIFICATION_QUERY_KEYS.list({ category: options.category, read: options.read }),\n initialPageParam: undefined as string | undefined,\n queryFn: ({ pageParam }) =>\n client.listNotifications({\n category: options.category,\n read: options.read,\n cursor: pageParam,\n perPage: options.perPage,\n }),\n getNextPageParam: (lastPage) => lastPage.nextCursor,\n })\n}\n\nexport function useUnreadCount() {\n const { client, pollIntervalSeconds } = useNotificationContext()\n\n return useQuery({\n queryKey: NOTIFICATION_QUERY_KEYS.unreadCount(),\n queryFn: () => client.countUnread(),\n // Polling é o fallback de quem não ligou o SSE. Com o stream ativo,\n // `useNotificationStream` invalida esta chave e o refetch acontece na hora.\n refetchInterval: pollIntervalSeconds > 0 ? pollIntervalSeconds * 1000 : false,\n })\n}\n\nexport function useMarkAsRead() {\n const { client } = useNotificationContext()\n const queryClient = useQueryClient()\n\n return useMutation({\n mutationFn: (id: string) => client.markAsRead(id),\n async onMutate(id: string) {\n // Atualização otimista: marcar como lida é ação de um clique, e esperar o round-trip para\n // o badge cair faz a interface parecer travada.\n await queryClient.cancelQueries({ queryKey: NOTIFICATION_QUERY_KEYS.all })\n const previousCount = queryClient.getQueryData<number>(NOTIFICATION_QUERY_KEYS.unreadCount())\n\n queryClient.setQueryData<number>(NOTIFICATION_QUERY_KEYS.unreadCount(), (current) =>\n current === undefined ? current : Math.max(0, current - 1),\n )\n return { previousCount, id }\n },\n onError(_error, _id, context) {\n // Rollback: sem isto o badge ficaria mentindo até o próximo refetch.\n if (context?.previousCount !== undefined) {\n queryClient.setQueryData(NOTIFICATION_QUERY_KEYS.unreadCount(), context.previousCount)\n }\n },\n onSettled() {\n void queryClient.invalidateQueries({ queryKey: NOTIFICATION_QUERY_KEYS.all })\n },\n })\n}\n\nexport function useMarkAllAsRead() {\n const { client } = useNotificationContext()\n const queryClient = useQueryClient()\n\n return useMutation({\n mutationFn: () => client.markAllAsRead(),\n onSettled() {\n void queryClient.invalidateQueries({ queryKey: NOTIFICATION_QUERY_KEYS.all })\n },\n })\n}\n\nexport function useDeleteNotification() {\n const { client } = useNotificationContext()\n const queryClient = useQueryClient()\n\n return useMutation({\n mutationFn: (id: string) => client.deleteNotification(id),\n onSettled() {\n void queryClient.invalidateQueries({ queryKey: NOTIFICATION_QUERY_KEYS.all })\n },\n })\n}\n\n/** Achata as páginas do infinite query — a lista quase sempre quer o array direto. */\nexport function flattenNotificationPages(\n pages: readonly { data: readonly NotificationSummary[] }[] | undefined,\n): readonly NotificationSummary[] {\n return pages?.flatMap((page) => page.data) ?? []\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n *\n * Chaves centralizadas: o sino e a lista leem o mesmo `unreadCount`, e uma invalidação precisa\n * atingir os dois. Chave montada inline em cada hook é como badge e lista passam a mostrar\n * números diferentes.\n */\n\nexport const NOTIFICATION_QUERY_KEYS = {\n all: ['notifications'] as const,\n list: (filters: { category?: string; read?: boolean }) => ['notifications', 'list', filters] as const,\n unreadCount: () => ['notifications', 'unread-count'] as const,\n preferences: () => ['notifications', 'preferences'] as const,\n templates: () => ['notifications', 'templates'] as const,\n templateVariables: () => ['notifications', 'template-variables'] as const,\n categoryPolicies: () => ['notifications', 'category-policies'] as const,\n} as const\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n */\n\nimport { useEffect } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { subscribeToNotificationStream } from '@adatechnology/notification-client'\n\nimport { useNotificationContext } from '../NotificationProvider'\nimport { NOTIFICATION_QUERY_KEYS } from './queryKeys'\n\nexport type UseNotificationStreamOptions = {\n /** Desligado por omissão: abrir SSE é decisão do host, que sabe se o backend expõe a rota. */\n readonly enabled?: boolean\n readonly onEvent?: (event: { event: string; data: unknown }) => void\n}\n\n/**\n * Liga o SSE do inbox ao cache do TanStack: cada evento invalida as chaves, e o badge e a lista\n * atualizam juntos. `useEffect` aqui é o uso correto do hook — sincronizar com um sistema externo\n * (`react.md`, \"useEffect só para sincronizar com sistemas externos\"), não transformar dado.\n */\nexport function useNotificationStream(options: UseNotificationStreamOptions = {}): void {\n const { client } = useNotificationContext()\n const queryClient = useQueryClient()\n const enabled = options.enabled ?? false\n const onEvent = options.onEvent\n\n useEffect(() => {\n if (!enabled) return\n\n let subscription: { close(): void } | undefined\n let cancelled = false\n\n void client.resolveStreamRequest().then(({ url, headers }) => {\n // A resolução do token é assíncrona; se o componente desmontou nesse meio-tempo, não abre\n // conexão nenhuma — senão sobraria um stream órfão a cada navegação rápida.\n if (cancelled) return\n\n subscription = subscribeToNotificationStream({\n url,\n headers,\n onEvent: (event) => {\n onEvent?.(event)\n void queryClient.invalidateQueries({ queryKey: NOTIFICATION_QUERY_KEYS.all })\n },\n })\n })\n\n return () => {\n cancelled = true\n subscription?.close()\n }\n }, [client, queryClient, enabled, onEvent])\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n *\n * O roteamento: por quais canais cada categoria PODE sair, decidido pela empresa.\n *\n * É a camada que faltava. O chamador já podia enumerar canais em cada `sendNotification`, e quem\n * recebe já tinha preferência — mas \"cobrança nunca sai por SMS\" não cabia em nenhum dos dois:\n * o primeiro espalha a decisão por todo o código, o segundo deixa ela na mão do destinatário.\n *\n * O modelo aqui é de teto, não de piso: desligar barra o canal para todo mundo; ligar apenas\n * permite, e a preferência de quem recebe continua valendo por cima.\n */\n\nimport { useMemo, useState } from 'react'\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { NotificationCategoryPolicy } from '@adatechnology/notification-contracts'\n\nimport { useNotificationContext } from '../NotificationProvider'\nimport { NOTIFICATION_QUERY_KEYS } from './queryKeys'\n\nexport type UseCategoryPoliciesResult = {\n readonly isLoading: boolean\n readonly isSaving: boolean\n readonly error: string | undefined\n readonly isDirty: boolean\n /** Linha ausente é permitido — o mesmo default do servidor, para a tela não mentir. */\n isAllowed: (params: { category: string; channel: string }) => boolean\n toggle: (params: { category: string; channel: string }) => void\n save: () => void\n reset: () => void\n}\n\nfunction identityOf(params: { category: string; channel: string }): string {\n return `${params.category}:${params.channel}`\n}\n\nexport function useCategoryPolicies(): UseCategoryPoliciesResult {\n const { client } = useNotificationContext()\n const queryClient = useQueryClient()\n\n const policiesQuery = useQuery({\n queryKey: NOTIFICATION_QUERY_KEYS.categoryPolicies(),\n queryFn: () => client.getCategoryPolicies(),\n })\n\n const mutation = useMutation({\n mutationFn: (policies: readonly NotificationCategoryPolicy[]) => client.updateCategoryPolicies(policies),\n onSuccess() {\n void queryClient.invalidateQueries({ queryKey: NOTIFICATION_QUERY_KEYS.categoryPolicies() })\n },\n })\n\n /**\n * O rascunho guarda só o que foi TOCADO, não uma cópia do conjunto inteiro. Com a cópia, salvar\n * gravaria linha para toda combinação exibida na tela — e a ausência de linha, que é o default\n * \"permitido\", viraria centenas de registros de `enabled: true` sem significado.\n */\n const [touched, setTouched] = useState<Record<string, boolean>>({})\n\n const saved = useMemo(() => {\n const byIdentity = new Map<string, boolean>()\n for (const policy of policiesQuery.data ?? []) byIdentity.set(identityOf(policy), policy.enabled)\n return byIdentity\n }, [policiesQuery.data])\n\n function isAllowed(params: { category: string; channel: string }): boolean {\n const identity = identityOf(params)\n return touched[identity] ?? saved.get(identity) ?? true\n }\n\n return {\n isLoading: policiesQuery.isLoading,\n isSaving: mutation.isPending,\n error: mutation.error?.message,\n isDirty: Object.keys(touched).length > 0,\n isAllowed,\n\n toggle(params) {\n const identity = identityOf(params)\n setTouched((current) => ({ ...current, [identity]: !isAllowed(params) }))\n },\n\n save() {\n // Só as categorias tocadas vão no corpo: o servidor substitui categoria a categoria, e\n // mandar o conjunto inteiro apagaria e reescreveria política que ninguém editou.\n const categories = new Set(Object.keys(touched).map((identity) => identity.split(':')[0] ?? ''))\n const policies: NotificationCategoryPolicy[] = []\n for (const category of categories) {\n const channels = new Set([\n ...[...saved.keys()].filter((identity) => identity.startsWith(`${category}:`)),\n ...Object.keys(touched).filter((identity) => identity.startsWith(`${category}:`)),\n ])\n for (const identity of channels) {\n const channel = identity.slice(category.length + 1)\n policies.push({\n category,\n channel: channel as NotificationCategoryPolicy['channel'],\n enabled: isAllowed({ category, channel }),\n })\n }\n }\n mutation.mutate(policies, { onSuccess: () => setTouched({}) })\n },\n\n reset() {\n setTouched({})\n },\n }\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n */\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { NotificationPreference, UpsertTemplateBody } from '@adatechnology/notification-contracts'\n\nimport { useNotificationContext } from '../NotificationProvider'\nimport { NOTIFICATION_QUERY_KEYS } from './queryKeys'\n\nexport function usePreferences() {\n const { client } = useNotificationContext()\n\n return useQuery({\n queryKey: NOTIFICATION_QUERY_KEYS.preferences(),\n queryFn: () => client.getPreferences(),\n })\n}\n\nexport function useUpdatePreferences() {\n const { client } = useNotificationContext()\n const queryClient = useQueryClient()\n\n return useMutation({\n mutationFn: (preferences: readonly NotificationPreference[]) => client.updatePreferences(preferences),\n onSuccess(saved) {\n // A resposta já é o estado persistido — usar direto evita um refetch e o piscar do painel\n // entre o valor otimista e o confirmado.\n queryClient.setQueryData(NOTIFICATION_QUERY_KEYS.preferences(), saved)\n },\n })\n}\n\nexport function useTemplates() {\n const { client } = useNotificationContext()\n\n return useQuery({\n queryKey: NOTIFICATION_QUERY_KEYS.templates(),\n queryFn: () => client.listTemplates(),\n })\n}\n\n/**\n * Salva uma VERSÃO nova do template. É o que a tela de configuração usa para o lojista mudar a copy\n * sem deploy.\n *\n * Invalida em vez de escrever no cache: o servidor decide o número da versão, então o objeto salvo\n * traz um campo que o otimista não teria como prever — e mostrar \"versão 3\" quando o banco gravou 4\n * confunde exatamente quem está tentando conferir o que salvou.\n */\nexport function useUpsertTemplate() {\n const { client } = useNotificationContext()\n const queryClient = useQueryClient()\n\n return useMutation({\n mutationFn: (input: UpsertTemplateBody) => client.upsertTemplate(input),\n onSuccess() {\n void queryClient.invalidateQueries({ queryKey: NOTIFICATION_QUERY_KEYS.templates() })\n },\n })\n}\n\n/**\n * O catálogo é config do host: muda com deploy, não com uso. `staleTime: Infinity` evita refetch a\n * cada foco de janela por um dado que não muda durante a sessão.\n */\nexport function useTemplateVariables() {\n const { client } = useNotificationContext()\n\n return useQuery({\n queryKey: NOTIFICATION_QUERY_KEYS.templateVariables(),\n queryFn: () => client.listTemplateVariables(),\n staleTime: Number.POSITIVE_INFINITY,\n })\n}\n\n/**\n * Desativa o template. Invalida a lista pelo mesmo motivo do upsert: o servidor derruba TODAS as\n * versões ativas da identidade, e um cache otimista que removesse só a linha clicada mostraria a\n * versão anterior de volta na tela.\n */\nexport function useDeactivateTemplate() {\n const { client } = useNotificationContext()\n const queryClient = useQueryClient()\n\n return useMutation({\n mutationFn: (id: string) => client.deactivateTemplate(id),\n onSuccess() {\n void queryClient.invalidateQueries({ queryKey: NOTIFICATION_QUERY_KEYS.templates() })\n },\n })\n}\n\nexport function useRestoreTemplateDefault() {\n const { client } = useNotificationContext()\n const queryClient = useQueryClient()\n\n return useMutation({\n mutationFn: (id: string) => client.restoreDefault(id),\n onSuccess() {\n void queryClient.invalidateQueries({ queryKey: NOTIFICATION_QUERY_KEYS.templates() })\n },\n })\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n *\n * Quando o aviso chegou, dito do jeito que se diz em voz alta.\n *\n * `Intl` faz o trabalho de idioma — o pacote não guarda \"há\", \"ago\" nem nome de mês em lugar nenhum,\n * o que manteria `web.md` §6 de pé e ainda assim quebraria no primeiro locale novo. O que é decisão\n * nossa são as faixas: segundo, minuto, hora, dia, e a virada para data absoluta.\n */\n\nconst SECOND = 1_000\nconst MINUTE = 60 * SECOND\nconst HOUR = 60 * MINUTE\nconst DAY = 24 * HOUR\n\n/**\n * Passada uma semana, o relativo para de informar e passa a exigir conta: \"há 43 dias\" é um convite\n * a abrir o calendário. A data, o leitor lê.\n */\nconst RELATIVE_HORIZON = 7 * DAY\n\nfunction parseInstant(isoTimestamp: string): Date | undefined {\n const instant = new Date(isoTimestamp)\n return Number.isNaN(instant.getTime()) ? undefined : instant\n}\n\nexport function formatNotificationTime(isoTimestamp: string, locale: string, now: Date): string {\n const instant = parseInstant(isoTimestamp)\n if (!instant) return ''\n\n // Relógio do navegador adiantado é rotina; anunciar \"daqui a 2 minutos\" para algo que já chegou\n // não é. O piso em zero transforma o desvio em \"agora\", que é o que de fato aconteceu.\n const elapsed = Math.max(0, now.getTime() - instant.getTime())\n\n if (elapsed >= RELATIVE_HORIZON) {\n return new Intl.DateTimeFormat(locale, { day: '2-digit', month: '2-digit', year: 'numeric' }).format(instant)\n }\n\n // `numeric: 'auto'` é o que troca \"há 1 dia\" por \"ontem\" e \"há 0 segundos\" por \"agora\".\n const relative = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' })\n\n if (elapsed < MINUTE) return relative.format(0, 'second')\n if (elapsed < HOUR) return relative.format(-Math.floor(elapsed / MINUTE), 'minute')\n if (elapsed < DAY) return relative.format(-Math.floor(elapsed / HOUR), 'hour')\n return relative.format(-Math.floor(elapsed / DAY), 'day')\n}\n\n/** O instante inteiro, para o `title` — é a hora que o texto relativo esconde. */\nexport function formatNotificationTimestamp(isoTimestamp: string, locale: string): string {\n const instant = parseInstant(isoTimestamp)\n if (!instant) return ''\n\n return new Intl.DateTimeFormat(locale, { dateStyle: 'short', timeStyle: 'short' }).format(instant)\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n *\n * A lógica do editor de template, que estava reimplementada no host.\n *\n * Ela veio de `quickcart/modules/notifications/hooks/useNotificationSettings.hook.ts` — 207 linhas\n * que o segundo produto reescreveria, com as mesmas quatro decisões sutis para redescobrir. Elas\n * são o motivo de isto ser pacote e não exemplo:\n *\n * 1. **Versão ativa mais alta.** A rota devolve o histórico inteiro, correto para auditoria; na tela\n * isso faz a mesma mensagem aparecer três vezes e ninguém sabe qual está no ar.\n * 2. **Seleção pelo id, não pelo objeto.** Salvar cria versão nova no servidor; segurando o objeto,\n * o editor mostra a versão que já não é a ativa e a gravação seguinte parte de um texto velho.\n * 3. **Campo vazio é ausência.** Gravar `subject: ''` faz o renderer tratar como subject existente e\n * derivar título vazio, em vez de cair no fallback que usa o corpo.\n * 4. **Preview pelo `renderTemplate` do contracts**, o mesmo do envio. Reimplementar a interpolação\n * dá um preview que confere hoje e mente quando o renderer mudar.\n *\n * A criação de template novo trouxe uma quinta: **a identidade é imutável depois de criada**.\n * `key`+`channel`+`locale` é o que o `sendNotification` procura; deixar editar num template\n * existente não seria edição, seria criar outro e deixar o antigo órfão no ar.\n */\n\nimport { useMemo, useState } from 'react'\n\nimport {\n DEFAULT_TEMPLATE_PAGE_SIZE,\n SORT_DIRECTIONS,\n TEMPLATE_SORT_FIELDS,\n TEMPLATE_VIEWS,\n type TemplateSort,\n type TemplateSortField,\n type TemplateView,\n} from '../templateList.constant'\nimport {\n PREVIEW_VIEWPORT_BY_CHANNEL,\n buildPreviewPayload,\n diffTemplateVariables,\n renderTemplate,\n} from '@adatechnology/notification-contracts'\nimport type {\n NotificationChannel,\n StandardEmailAssessment,\n NotificationTemplate,\n PreviewViewportSpec,\n RenderedTemplatePreview,\n TemplateVariableDefinition,\n TemplateVariableDiff,\n UpsertTemplateBody,\n} from '@adatechnology/notification-contracts'\n\nimport { assessStandardEmailDraft, buildStandardEmailPreviewHtml, isStandardEmailKey } from '../standardEmail.util'\nimport {\n useDeactivateTemplate,\n useRestoreTemplateDefault,\n useTemplateVariables,\n useTemplates,\n useUpsertTemplate,\n} from './usePreferences'\n\nexport type TemplateDraft = {\n readonly key: string\n /**\n * Canais, no plural: um aviso costuma sair por mais de um.\n *\n * No banco a identidade continua sendo `key`+`channel`+`locale` — uma linha por canal. Escolher\n * varios aqui grava um template por canal com o MESMO texto, que e o comportamento certo para\n * criar: escrever a mensagem uma vez e mandar por onde precisar. Depois, cada canal e editado\n * sozinho pela lista, porque o texto tende a divergir (o SMS encurta, o WhatsApp perde o titulo).\n */\n readonly channels: readonly string[]\n readonly locale: string\n /**\n * Texto POR CANAL, e não um só copiado para todos.\n *\n * Os canais não são o mesmo recado em molduras diferentes: o WhatsApp ignora o assunto, o SMS\n * cobra por segmento de 160 e encurta, o push corta em duas linhas. Um texto único obrigaria a\n * escrever para o pior canal e mandar isso a todos. No banco cada canal já é uma linha própria\n * (`key`+`channel`+`locale`) — aqui a tela passa a refletir isso.\n */\n readonly byChannel: Readonly<Record<string, ChannelDraft>>\n}\n\nexport type ChannelDraft = {\n readonly subject: string\n readonly body: string\n readonly whatsappTemplateName: string\n}\n\nconst EMPTY_CHANNEL: ChannelDraft = { subject: '', body: '', whatsappTemplateName: '' }\n\nexport type TemplateDraftField = 'subject' | 'body'\n\nexport type TemplatePreviewFrame = PreviewViewportSpec & {\n /** O canal viaja com o quadro: a moldura do preview depende dele, e ler do rascunho dentro do\n * `map` perde a garantia de que o rascunho existe. */\n readonly channel: string\n readonly rendered: RenderedTemplatePreview\n}\n\nfunction toDraft(template: NotificationTemplate): TemplateDraft {\n return {\n key: template.key,\n channels: [template.channel],\n locale: template.locale,\n byChannel: {\n [template.channel]: {\n subject: template.subject ?? '',\n body: template.body,\n whatsappTemplateName: template.whatsappTemplateName ?? '',\n },\n },\n }\n}\n\n/** `auth` em `auth.password_reset`. Chave sem ponto é a própria categoria. */\nfunction categoryOf(key: string): string {\n return key.split('.')[0] ?? key\n}\n\nfunction buildEmptyDraft(params: { channel: string; locale: string }): TemplateDraft {\n return {\n key: '',\n channels: [params.channel],\n locale: params.locale,\n byChannel: { [params.channel]: EMPTY_CHANNEL },\n }\n}\n\nexport type UseTemplateEditorParams = {\n /**\n * Valores de exemplo do preview. Hoje o padrão vem do catálogo de variáveis do servidor; isto\n * continua existindo para o produto sobrescrever um exemplo pontual sem mexer no catálogo.\n */\n readonly previewPayload?: Readonly<Record<string, unknown>>\n /** Canal e locale de um template novo, antes de o operador escolher. */\n readonly defaultChannel?: string\n readonly defaultLocale?: string\n}\n\n/**\n * Um grupo da lista.\n *\n * O prefixo da chave (`auth` em `auth.password_reset`) já é a categoria que o produto escreveu — o\n * módulo não precisa de um campo novo no banco para agrupar, e um campo novo divergiria da chave\n * na primeira vez que alguém renomeasse um sem o outro.\n */\nexport type TemplateGroup = {\n readonly category: string\n readonly templates: readonly NotificationTemplate[]\n}\n\nexport type UseTemplateEditorResult = {\n readonly templates: readonly NotificationTemplate[]\n /** Já filtrados e agrupados por categoria, na ordem em que a tela desenha. */\n readonly groups: readonly TemplateGroup[]\n /** Todas as categorias existentes, inclusive as que o filtro escondeu. */\n readonly categories: readonly string[]\n readonly search: string\n readonly channelFilter: readonly string[]\n readonly categoryFilter: readonly string[]\n readonly hasFilters: boolean\n /** Quantos templates existem antes de filtrar — separa \"nada cadastrado\" de \"nada encontrado\". */\n readonly totalCount: number\n /** Quantos sobram depois de filtrar — é o total que a paginação divide. */\n readonly filteredCount: number\n readonly page: number\n readonly pageCount: number\n readonly pageSize: number\n setPage: (page: number) => void\n setPageSize: (size: number) => void\n /** Tabela para comparar, lista para reconhecer. */\n readonly view: TemplateView\n /** Ausente = ordem natural (categoria, depois chave) — o terceiro estado do cabeçalho. */\n readonly sort: TemplateSort | undefined\n setView: (view: TemplateView) => void\n /** Cicla `asc` → `desc` → neutro na mesma coluna; outra coluna recomeça em `asc`. */\n toggleSort: (field: TemplateSortField) => void\n setSearch: (value: string) => void\n toggleChannelFilter: (channel: string) => void\n toggleCategoryFilter: (category: string) => void\n clearFilters: () => void\n readonly isLoading: boolean\n readonly isSaving: boolean\n readonly isDeactivating: boolean\n readonly isRestoring: boolean\n /** `true` quando o rascunho é o modelo padrão do sistema: sem assunto, sem remover, validado à parte. */\n readonly isStandardEmail: boolean\n /** O laudo do modelo padrão (faltantes e inseguros); `undefined` nos demais modelos. */\n readonly standardAssessment: StandardEmailAssessment | undefined\n /** O modelo padrão renderizado com os exemplos do catálogo; `undefined` nos demais. */\n readonly standardPreviewHtml: string | undefined\n readonly error: string | undefined\n readonly selected: NotificationTemplate | undefined\n readonly draft: TemplateDraft | undefined\n /** `true` enquanto o rascunho é um template que ainda não existe no servidor. */\n readonly isNew: boolean\n /** Identidade travada — `false` só durante a criação. */\n readonly isIdentityLocked: boolean\n /** Os dois quadros do preview, na ordem em que a tela desenha. */\n readonly previews: readonly TemplatePreviewFrame[]\n /** As variáveis que esta `key` declara. Vazio quando o catálogo não conhece a chave. */\n readonly variables: readonly TemplateVariableDefinition[]\n readonly variableDiff: TemplateVariableDiff\n readonly isDirty: boolean\n readonly canSave: boolean\n select: (template: NotificationTemplate) => void\n startNew: () => void\n update: (patch: Partial<TemplateDraft>) => void\n /** Edita o texto de UM canal. */\n updateChannel: (channel: string, patch: Partial<ChannelDraft>) => void\n /** Liga ou desliga um canal do rascunho. Nunca esvazia para nada: sem canal nao ha o que gravar. */\n toggleChannel: (channel: string) => void\n /** Insere `{{nome}}` na posição do cursor. O operador nunca digita o nome à mão. */\n insertVariable: (params: { name: string; field: TemplateDraftField; cursorIndex: number; channel: string }) => void\n save: () => void\n deactivate: (id: string) => void\n /** \"Restaurar padrão\": versão nova com o modelo embutido no SDK. */\n restoreDefault: (id: string) => void\n clear: () => void\n}\n\nexport function useTemplateEditor(params: UseTemplateEditorParams = {}): UseTemplateEditorResult {\n const templatesQuery = useTemplates()\n const variablesQuery = useTemplateVariables()\n const upsert = useUpsertTemplate()\n const deactivateMutation = useDeactivateTemplate()\n const restoreMutation = useRestoreTemplateDefault()\n\n const [search, setSearch] = useState('')\n const [channelFilter, setChannelFilter] = useState<readonly string[]>([])\n const [categoryFilter, setCategoryFilter] = useState<readonly string[]>([])\n const [view, setView] = useState<TemplateView>(TEMPLATE_VIEWS.TABLE)\n const [requestedPage, setRequestedPage] = useState(1)\n const [pageSize, setPageSizeState] = useState<number>(DEFAULT_TEMPLATE_PAGE_SIZE)\n const [sort, setSort] = useState<TemplateSort | undefined>(undefined)\n const [selectedId, setSelectedId] = useState<string | undefined>(undefined)\n const [draft, setDraft] = useState<TemplateDraft | undefined>(undefined)\n const [isNew, setIsNew] = useState(false)\n\n /** Decisão 1: só a ativa mais alta por `key`+`channel`+`locale` — a mesma que o envio faz. */\n const templates = useMemo(() => {\n const activeByIdentity = new Map<string, NotificationTemplate>()\n for (const template of templatesQuery.data ?? []) {\n if (!template.active) continue\n const identity = `${template.key}:${template.channel}:${template.locale}`\n const current = activeByIdentity.get(identity)\n if (!current || template.version > current.version) activeByIdentity.set(identity, template)\n }\n return [...activeByIdentity.values()].sort(\n (left, right) => left.key.localeCompare(right.key) || left.channel.localeCompare(right.channel),\n )\n }, [templatesQuery.data])\n\n const categories = useMemo(\n () => [...new Set(templates.map((template) => categoryOf(template.key)))].sort(),\n [templates],\n )\n\n /**\n * Busca sobre chave E texto: quem procura \"senha\" costuma lembrar do que a mensagem diz, não da\n * chave técnica que alguém escolheu meses atrás.\n */\n const filtered = useMemo(() => {\n const termo = search.trim().toLowerCase()\n\n return templates.filter((template) => {\n if (channelFilter.length > 0 && !channelFilter.includes(template.channel)) return false\n if (categoryFilter.length > 0 && !categoryFilter.includes(categoryOf(template.key))) return false\n if (!termo) return true\n return `${template.key} ${template.subject ?? ''} ${template.body}`.toLowerCase().includes(termo)\n })\n }, [templates, search, channelFilter, categoryFilter])\n\n /**\n * Ordenação só existe quando alguém pediu: sem `sort`, a ordem é a natural (categoria e chave),\n * que é a que o agrupamento desenha. Reordenar por padrão esconderia essa estrutura.\n */\n const sorted = useMemo(() => {\n if (!sort) return filtered\n const sinal = sort.direction === SORT_DIRECTIONS.ASC ? 1 : -1\n\n return [...filtered].sort((left, right) => {\n if (sort.field === TEMPLATE_SORT_FIELDS.VERSION) return (left.version - right.version) * sinal\n const campo = sort.field === TEMPLATE_SORT_FIELDS.CHANNEL ? 'channel' : 'key'\n return left[campo].localeCompare(right[campo]) * sinal\n })\n }, [filtered, sort])\n\n /** A página pedida nunca passa da última: filtrar, remover ou trocar o tamanho não deixa a tela vazia. */\n const pageCount = Math.max(1, Math.ceil(sorted.length / pageSize))\n const page = Math.min(requestedPage, pageCount)\n const paged = useMemo(() => sorted.slice((page - 1) * pageSize, page * pageSize), [sorted, page, pageSize])\n\n const groups = useMemo<readonly TemplateGroup[]>(() => {\n const porCategoria = new Map<string, NotificationTemplate[]>()\n for (const template of paged) {\n const categoria = categoryOf(template.key)\n const atual = porCategoria.get(categoria)\n if (atual) atual.push(template)\n else porCategoria.set(categoria, [template])\n }\n return [...porCategoria.entries()]\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([category, list]) => ({ category, templates: list }))\n }, [paged])\n\n /** Decisão 2: derivado do id. */\n const selected = useMemo(() => templates.find((template) => template.id === selectedId), [templates, selectedId])\n\n const variables = useMemo(() => (draft ? (variablesQuery.data?.[draft.key] ?? []) : []), [variablesQuery.data, draft])\n\n const variableDiff = useMemo(\n () =>\n diffTemplateVariables({\n /** Junta o texto de todos os canais: variavel errada em qualquer um recusa a gravacao. */\n body: draft ? draft.channels.map((c) => draft.byChannel[c]?.body ?? '').join('\\n') : '',\n subject: draft ? draft.channels.map((c) => draft.byChannel[c]?.subject ?? '').join('\\n') : undefined,\n // Chave sem entrada no catálogo é catálogo não declarado, não catálogo vazio — o servidor\n // aceita qualquer variável nesse caso, e a tela não pode acusar o que a rota vai gravar.\n variables: draft && variablesQuery.data?.[draft.key] ? variables : undefined,\n }),\n [draft, variables, variablesQuery.data],\n )\n\n /** Decisão 4: o mesmo render do envio, uma vez por viewport do canal. */\n const previews = useMemo<readonly TemplatePreviewFrame[]>(() => {\n if (!draft) return []\n const payload = { ...buildPreviewPayload(variables), ...(params.previewPayload ?? {}) }\n /** Um conjunto de quadros por canal escolhido: e o que permite comparar como o mesmo texto\n * chega no e-mail e no WhatsApp sem salvar antes. */\n return draft.channels.flatMap((channel) => {\n const content = draft.byChannel[channel] ?? EMPTY_CHANNEL\n const rendered = renderTemplate({\n channel,\n subject: content.subject || undefined,\n body: content.body,\n payload,\n })\n const viewports = PREVIEW_VIEWPORT_BY_CHANNEL[channel as NotificationChannel] ?? []\n return viewports.map((viewport) => ({ ...viewport, channel, rendered }))\n })\n }, [draft, variables, params.previewPayload])\n\n const isStandardEmail = draft !== undefined && isStandardEmailKey(draft.key)\n const standardHtml = isStandardEmail ? (draft?.byChannel[draft.channels[0] ?? '']?.body ?? '') : undefined\n const standardAssessment = useMemo(\n () => (standardHtml === undefined ? undefined : assessStandardEmailDraft(standardHtml)),\n [standardHtml],\n )\n const standardPreviewHtml = useMemo(\n () =>\n standardHtml === undefined || !standardAssessment?.usable\n ? undefined\n : buildStandardEmailPreviewHtml(standardHtml),\n [standardHtml, standardAssessment],\n )\n\n const isDirty = useMemo(() => {\n if (!draft) return false\n const temTexto = draft.channels.some((channel) => (draft.byChannel[channel]?.body ?? '').length > 0)\n if (isNew) return draft.key.length > 0 || temTexto\n if (!selected) return false\n const original = toDraft(selected).byChannel[selected.channel] ?? EMPTY_CHANNEL\n const atual = draft.byChannel[selected.channel] ?? EMPTY_CHANNEL\n return (Object.keys(original) as (keyof ChannelDraft)[]).some((field) => original[field] !== atual[field])\n }, [draft, selected, isNew])\n\n return {\n templates: paged,\n filteredCount: sorted.length,\n page,\n pageCount,\n pageSize,\n setPage: (next: number) => setRequestedPage(Math.min(Math.max(1, next), pageCount)),\n setPageSize: (size: number) => {\n setPageSizeState(size)\n setRequestedPage(1)\n },\n groups,\n view,\n sort,\n categories,\n search,\n channelFilter,\n categoryFilter,\n // Ordenação conta: o botão de limpar existe para desfazer QUALQUER recorte aplicado (web.md §7).\n hasFilters: search.trim().length > 0 || channelFilter.length > 0 || categoryFilter.length > 0 || sort !== undefined,\n totalCount: templates.length,\n isLoading: templatesQuery.isLoading,\n isSaving: upsert.isPending,\n isDeactivating: deactivateMutation.isPending,\n isRestoring: restoreMutation.isPending,\n isStandardEmail,\n standardAssessment,\n standardPreviewHtml,\n error: upsert.error?.message ?? deactivateMutation.error?.message ?? restoreMutation.error?.message,\n selected,\n draft,\n isNew,\n isIdentityLocked: !isNew,\n previews,\n variables,\n variableDiff,\n isDirty,\n // A variável desconhecida é recusada pelo servidor com 400; bloquear o botão evita a ida\n // perdida, sem virar a única defesa — a validação continua sendo a da rota.\n /** Todo canal marcado precisa do proprio texto — senao a gravacao criaria template vazio. */\n canSave:\n Boolean(draft?.key) &&\n (draft?.channels.length ?? 0) > 0 &&\n (draft?.channels.every((channel) => (draft.byChannel[channel]?.body ?? '').length > 0) ?? false) &&\n variableDiff.unknown.length === 0 &&\n // O modelo padrão inutilizável nunca é gravado: o servidor recusa (400) e o envio o ignoraria.\n (standardAssessment?.usable ?? true),\n\n setSearch(value) {\n setSearch(value)\n setRequestedPage(1)\n },\n\n toggleChannelFilter(channel) {\n setRequestedPage(1)\n setChannelFilter((current) =>\n current.includes(channel) ? current.filter((each) => each !== channel) : [...current, channel],\n )\n },\n\n toggleCategoryFilter(category) {\n setRequestedPage(1)\n setCategoryFilter((current) =>\n current.includes(category) ? current.filter((each) => each !== category) : [...current, category],\n )\n },\n\n setView,\n toggleSort(field) {\n setSort((current) => {\n if (current?.field !== field) return { field, direction: SORT_DIRECTIONS.ASC }\n if (current.direction === SORT_DIRECTIONS.ASC) return { field, direction: SORT_DIRECTIONS.DESC }\n return undefined\n })\n },\n clearFilters() {\n setRequestedPage(1)\n setSearch('')\n setChannelFilter([])\n setCategoryFilter([])\n setSort(undefined)\n },\n\n select(template) {\n setSelectedId(template.id)\n setDraft(toDraft(template))\n setIsNew(false)\n },\n\n startNew() {\n setSelectedId(undefined)\n setIsNew(true)\n setDraft(buildEmptyDraft({ channel: params.defaultChannel ?? 'email', locale: params.defaultLocale ?? 'pt-BR' }))\n },\n\n update(patch) {\n setDraft((current) => (current ? { ...current, ...patch } : current))\n },\n\n updateChannel(channel, patch) {\n setDraft((current) => {\n if (!current) return current\n const atual = current.byChannel[channel] ?? EMPTY_CHANNEL\n return { ...current, byChannel: { ...current.byChannel, [channel]: { ...atual, ...patch } } }\n })\n },\n\n toggleChannel(channel) {\n setDraft((current) => {\n if (!current) return current\n if (current.channels.includes(channel)) {\n /** O texto do canal desmarcado fica guardado: remarcar nao pode apagar o que foi escrito. */\n return { ...current, channels: current.channels.filter((each) => each !== channel) }\n }\n return {\n ...current,\n channels: [...current.channels, channel],\n byChannel: { ...current.byChannel, [channel]: current.byChannel[channel] ?? EMPTY_CHANNEL },\n }\n })\n },\n\n insertVariable({ name, field, cursorIndex, channel }) {\n setDraft((current) => {\n if (!current) return current\n const atual = current.byChannel[channel] ?? EMPTY_CHANNEL\n const text = atual[field]\n const at = Math.min(Math.max(cursorIndex, 0), text.length)\n return {\n ...current,\n byChannel: {\n ...current.byChannel,\n [channel]: { ...atual, [field]: `${text.slice(0, at)}{{${name}}}${text.slice(at)}` },\n },\n }\n })\n },\n\n save() {\n if (!draft) return\n\n /** Um `upsert` por canal, em serie: o servidor versiona por identidade, e o numero da versao\n * de um canal nao pode depender da ordem de chegada do outro. */\n const bodies: UpsertTemplateBody[] = draft.channels.map((channel) => {\n const content = draft.byChannel[channel] ?? EMPTY_CHANNEL\n return {\n key: draft.key,\n channel: channel as UpsertTemplateBody['channel'],\n locale: draft.locale,\n active: true,\n body: content.body,\n // Modelo padrão não tem assunto: ele é dado do envio, e um assunto aqui se acumularia.\n ...(content.subject && !isStandardEmail ? { subject: content.subject } : {}),\n ...(content.whatsappTemplateName ? { whatsappTemplateName: content.whatsappTemplateName } : {}),\n }\n })\n\n void bodies\n .reduce(\n (queue, body) => queue.then(() => upsert.mutateAsync(body).then(() => undefined)),\n Promise.resolve<void>(undefined),\n )\n .then(() => {\n setSelectedId(undefined)\n setDraft(undefined)\n setIsNew(false)\n })\n .catch(() => undefined)\n },\n\n deactivate(id) {\n deactivateMutation.mutate(id, {\n onSuccess: () => {\n if (selectedId !== id) return\n setSelectedId(undefined)\n setDraft(undefined)\n },\n })\n },\n\n restoreDefault(id) {\n restoreMutation.mutate(id, {\n onSuccess: () => {\n if (selectedId !== id) return\n setSelectedId(undefined)\n setDraft(undefined)\n },\n })\n },\n\n clear() {\n setSelectedId(undefined)\n setDraft(undefined)\n setIsNew(false)\n },\n }\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n *\n * Modos de exibição e ordenação da lista de mensagens.\n *\n * Duas leituras da mesma coleção: a TABELA compara (canal e versão alinhados em coluna, varredura\n * vertical) e a LISTA reconhece (o texto da mensagem visível em duas linhas). Quem administra trinta\n * templates quer a tabela; quem procura \"aquela que fala de senha\" quer a lista.\n */\n\nexport const TEMPLATE_VIEWS = {\n TABLE: 'table',\n LIST: 'list',\n} as const\nexport type TemplateView = (typeof TEMPLATE_VIEWS)[keyof typeof TEMPLATE_VIEWS]\n\n/** As colunas ordenáveis da tabela — e só elas: cabeçalho clicável que não ordena é mentira. */\nexport const TEMPLATE_SORT_FIELDS = {\n KEY: 'key',\n CHANNEL: 'channel',\n VERSION: 'version',\n} as const\nexport type TemplateSortField = (typeof TEMPLATE_SORT_FIELDS)[keyof typeof TEMPLATE_SORT_FIELDS]\n\nexport const SORT_DIRECTIONS = {\n ASC: 'asc',\n DESC: 'desc',\n} as const\nexport type SortDirection = (typeof SORT_DIRECTIONS)[keyof typeof SORT_DIRECTIONS]\n\nexport type TemplateSort = {\n readonly field: TemplateSortField\n readonly direction: SortDirection\n}\n\n/**\n * As colunas da tabela, na ordem. `field` ausente = coluna que não ordena (texto livre e ações):\n * cabeçalho clicável que não reordena nada é promessa quebrada.\n */\nexport type TemplateColumn = {\n readonly labelKey: string\n readonly field?: TemplateSortField\n /** Cabeçalho só para leitor de tela — a coluna de ação não precisa de título visível. */\n readonly headerHidden?: boolean\n}\n\nexport const TEMPLATE_COLUMNS: readonly TemplateColumn[] = [\n { labelKey: 'settings.column.key', field: TEMPLATE_SORT_FIELDS.KEY },\n { labelKey: 'settings.column.body' },\n { labelKey: 'settings.column.channel', field: TEMPLATE_SORT_FIELDS.CHANNEL },\n { labelKey: 'settings.column.version', field: TEMPLATE_SORT_FIELDS.VERSION },\n { labelKey: 'settings.column.actions', headerHidden: true },\n]\n\n/** Tamanhos de página oferecidos. 25 cabe numa tela sem empurrar o editor para fora dela. */\nexport const TEMPLATE_PAGE_SIZES = [10, 25, 50] as const\nexport const DEFAULT_TEMPLATE_PAGE_SIZE = 25\n\n/**\n * Resumo de uma linha do corpo para a tabela e a lista.\n *\n * O corpo de um modelo de e-mail é o HTML inteiro: despejado na célula, a linha vira um parede de\n * marcação e a tabela deixa de servir para varrer. Aqui sai a marcação (e o que está dentro de\n * `<style>`/`<script>`/comentário), o espaço colapsa e o texto é cortado — o editor mostra o resto.\n */\nexport function summarizeTemplateBody(body: string, maxLength = 140): string {\n const text = body\n .replace(/<!--[\\s\\S]*?-->/g, ' ')\n .replace(/<(style|script|head)[\\s\\S]*?<\\/\\1>/gi, ' ')\n .replace(/<[^>]*>/g, ' ')\n .replace(/&nbsp;/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n return text.length > maxLength ? `${text.slice(0, maxLength - 1).trimEnd()}…` : text\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n *\n * O modelo padrão de e-mail (`notification.standard-email`) na tela: quem é, se está utilizável e\n * como fica com os exemplos do catálogo. A regra mora no `notification-contracts` — aqui só se\n * chama, para a tela e o servidor nunca discordarem do que é \"utilizável\".\n */\n\nimport {\n STANDARD_EMAIL_TEMPLATE_KEY,\n STANDARD_EMAIL_VARIABLES,\n assessStandardEmailTemplate,\n renderStandardEmail,\n type StandardEmailAssessment,\n} from '@adatechnology/notification-contracts'\n\nexport function isStandardEmailKey(key: string): boolean {\n return key === STANDARD_EMAIL_TEMPLATE_KEY\n}\n\n/** Vale para o HTML inteiro do modelo; não depende da área de mensagem. */\nexport function assessStandardEmailDraft(html: string): StandardEmailAssessment {\n return assessStandardEmailTemplate(html)\n}\n\nfunction example(name: string): string {\n return STANDARD_EMAIL_VARIABLES.find((variable) => variable.name === name)?.example ?? ''\n}\n\n/** O modelo renderizado pelo mesmo `renderStandardEmail` do envio, com os exemplos do catálogo. */\nexport function buildStandardEmailPreviewHtml(templateHtml: string): string {\n return renderStandardEmail({\n template: { html: templateHtml, source: 'company', version: 0 },\n input: {\n attachments: [{ name: example('listaAnexos') }],\n bodyText: example('corpoMensagem'),\n footer: { platformNote: example('rodapePlataforma') },\n identity: { displayName: example('nomeEmpresa'), logo: { kind: 'initials' } },\n replyHint: example('faixaResposta'),\n signer: { name: example('nomeAssinante'), role: example('cargoAssinante') },\n },\n }).html\n}\n"],"mappings":"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","names":["useEffect","useQueryClient","useQueryClient","useEffect","useMemo","useState","useMutation","useQuery","useQueryClient","useQueryClient","useQuery","useMutation","useState","useMemo","useMutation","useQuery","useQueryClient","useQuery","useQueryClient","useMutation","useMemo","useState","useState","useMemo"]}
@@ -2,9 +2,9 @@ import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import * as _adatechnology_notification_client from '@adatechnology/notification-client';
4
4
  import { NotificationClient } from '@adatechnology/notification-client';
5
- import * as _tanstack_react_query from '@tanstack/react-query';
6
5
  import * as _adatechnology_notification_contracts from '@adatechnology/notification-contracts';
7
- import { NotificationSummary, NotificationPreference, PreviewViewportSpec, RenderedTemplatePreview, NotificationTemplate, StandardEmailAssessment, TemplateVariableDefinition, TemplateVariableDiff } from '@adatechnology/notification-contracts';
6
+ import { WebPushSubscription, NotificationSummary, NotificationPreference, PreviewViewportSpec, RenderedTemplatePreview, NotificationTemplate, StandardEmailAssessment, TemplateVariableDefinition, TemplateVariableDiff } from '@adatechnology/notification-contracts';
7
+ import * as _tanstack_react_query from '@tanstack/react-query';
8
8
 
9
9
  var ptBR = {
10
10
  "bell.label": "Notificações",
@@ -20,6 +20,7 @@ var ptBR = {
20
20
  "item.markAsRead": "Marcar como lida",
21
21
  "item.delete": "Excluir",
22
22
  "item.unread": "Não lida",
23
+ "item.addToCalendar": "Adicionar à agenda",
23
24
  "preferences.title": "Preferências de notificação",
24
25
  "preferences.description": "Escolha por onde você quer ser avisado em cada assunto.",
25
26
  "preferences.save": "Salvar preferências",
@@ -115,7 +116,23 @@ var ptBR = {
115
116
  "toolbar.italic": "Italico",
116
117
  "toolbar.strikethrough": "Tachado",
117
118
  "toolbar.monospace": "Monoespacado",
118
- "toolbar.emoji": "Emoji"
119
+ "toolbar.emoji": "Emoji",
120
+ "banner.title": "Ativar notificações",
121
+ "banner.description": "Receba lembretes de atividades, mensagens e avisos importantes diretamente no seu navegador.",
122
+ "banner.allow": "Permitir notificações",
123
+ "banner.dismiss": "Agora não",
124
+ "banner.activeTitle": "Notificações ativas",
125
+ "banner.activeDescription": "Você está recebendo notificações neste navegador.",
126
+ "banner.disable": "Desativar notificações",
127
+ "banner.disabling": "Desativando…",
128
+ "banner.deniedTitle": "Notificações bloqueadas",
129
+ "banner.deniedDescription": "As notificações estão bloqueadas no navegador. Para receber alertas, permita nas configurações do site.",
130
+ "settings.pager": "Paginação das mensagens",
131
+ "settings.pagerRange": "{from}–{to} de {total}",
132
+ "settings.pageSize": "Por página",
133
+ "settings.pagePrevious": "Anterior",
134
+ "settings.pageNext": "Próxima",
135
+ "settings.pageOf": "Página {page} de {pages}"
119
136
  };
120
137
 
121
138
  /**
@@ -159,6 +176,45 @@ type NotificationProviderProps = {
159
176
  };
160
177
  declare function NotificationProvider({ client, locale, messageOverrides, theme, pollIntervalSeconds, children, }: NotificationProviderProps): react.JSX.Element;
161
178
  declare function useNotificationContext(): NotificationContextValue;
179
+ declare function useOptionalNotificationContext(): NotificationContextValue | null;
180
+
181
+ /**
182
+ * Copyright (c) 2026 Ada Technology. MIT License.
183
+ *
184
+ * Hook headless para gerenciar permissão e ciclo de vida de Web Push no navegador.
185
+ * Isomórfico: seguro em SSR/Node/Bun sem quebrar quando `window` ou `Notification` não existem.
186
+ */
187
+
188
+ type NotificationPermissionStatus = 'default' | 'granted' | 'denied' | 'unsupported';
189
+ type UseNotificationPermissionOptions = {
190
+ /** Chave pública VAPID necessária para inscrição Web Push. Se omitida, tenta buscar via client.getVapidPublicKey(). */
191
+ readonly vapidPublicKey?: string;
192
+ /** Chave do localStorage para guardar a dispensa temporária do banner. Padrão: 'adatechnology:notification:permission-dismissed' */
193
+ readonly storageKey?: string;
194
+ /** Validade da dispensa em ms (padrão: 7 dias). 0 para permanente. */
195
+ readonly dismissTtlMs?: number;
196
+ /** Chave do localStorage onde o id do dispositivo registrado é salvo. Padrão: 'adatechnology:notification:device-id' */
197
+ readonly deviceStorageKey?: string;
198
+ /** Callback executado após inscrição bem-sucedida. */
199
+ readonly onSubscribe?: (subscription: WebPushSubscription) => Promise<void> | void;
200
+ /** Callback executado após cancelamento/desativação. */
201
+ readonly onUnsubscribe?: () => Promise<void> | void;
202
+ /** ServiceWorkerRegistration específico (opcional, senão aguarda navigator.serviceWorker.ready). */
203
+ readonly serviceWorkerRegistration?: ServiceWorkerRegistration;
204
+ };
205
+ type UseNotificationPermissionResult = {
206
+ readonly permission: NotificationPermissionStatus;
207
+ readonly isSupported: boolean;
208
+ readonly isSubscribed: boolean;
209
+ readonly isDismissed: boolean;
210
+ readonly isPending: boolean;
211
+ readonly error: Error | null;
212
+ readonly requestPermission: () => Promise<boolean>;
213
+ readonly disableNotifications: () => Promise<boolean>;
214
+ readonly dismiss: () => void;
215
+ readonly resetDismissed: () => void;
216
+ };
217
+ declare function useNotificationPermission(options?: UseNotificationPermissionOptions): UseNotificationPermissionResult;
162
218
 
163
219
  type UseNotificationsOptions = {
164
220
  readonly category?: string;
@@ -428,6 +484,13 @@ type UseTemplateEditorResult = {
428
484
  readonly hasFilters: boolean;
429
485
  /** Quantos templates existem antes de filtrar — separa "nada cadastrado" de "nada encontrado". */
430
486
  readonly totalCount: number;
487
+ /** Quantos sobram depois de filtrar — é o total que a paginação divide. */
488
+ readonly filteredCount: number;
489
+ readonly page: number;
490
+ readonly pageCount: number;
491
+ readonly pageSize: number;
492
+ setPage: (page: number) => void;
493
+ setPageSize: (size: number) => void;
431
494
  /** Tabela para comparar, lista para reconhecer. */
432
495
  readonly view: TemplateView;
433
496
  /** Ausente = ordem natural (categoria, depois chave) — o terceiro estado do cabeçalho. */
@@ -485,4 +548,4 @@ type UseTemplateEditorResult = {
485
548
  };
486
549
  declare function useTemplateEditor(params?: UseTemplateEditorParams): UseTemplateEditorResult;
487
550
 
488
- 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, useRestoreTemplateDefault as I, useTemplateEditor as J, useTemplateVariables as K, useTemplates as L, useUnreadCount as M, NOTIFICATION_QUERY_KEYS as N, useUpdatePreferences as O, useUpsertTemplate as P, 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 };
551
+ export { resolveMessages as A, useCategoryPolicies as B, useDeactivateTemplate as C, DEFAULT_LOCALE as D, useDeleteNotification as E, useMarkAllAsRead as F, useMarkAsRead as G, useNotificationContext as H, useNotificationPermission as I, useNotificationStream as J, useNotifications as K, useOptionalNotificationContext as L, usePreferences as M, NOTIFICATION_QUERY_KEYS as N, useRestoreTemplateDefault as O, useTemplateEditor as P, useTemplateVariables as Q, useTemplates as R, SORT_DIRECTIONS as S, TEMPLATE_COLUMNS as T, type UseCategoryPoliciesResult as U, useUnreadCount as V, useUpdatePreferences as W, useUpsertTemplate as X, type NotificationContextValue as a, type NotificationLocale as b, type NotificationMessages as c, type NotificationPermissionStatus as d, NotificationProvider as e, type NotificationProviderProps as f, type NotificationTheme as g, type SortDirection as h, TEMPLATE_SORT_FIELDS as i, TEMPLATE_VIEWS as j, type TemplateColumn as k, type TemplateDraft as l, type TemplateDraftField as m, type TemplatePreviewFrame as n, type TemplateSort as o, type TemplateSortField as p, type TemplateView as q, type UseNotificationPermissionOptions as r, type UseNotificationPermissionResult as s, type UseNotificationStreamOptions as t, type UseNotificationsOptions as u, type UseTemplateEditorParams as v, type UseTemplateEditorResult as w, flattenNotificationPages as x, formatNotificationTime as y, formatNotificationTimestamp as z };
@@ -1,5 +1,5 @@
1
- export { D as DEFAULT_LOCALE, N as NOTIFICATION_QUERY_KEYS, a as NotificationContextValue, b as NotificationLocale, c as NotificationMessages, d as NotificationProvider, e as NotificationProviderProps, f as NotificationTheme, k as TemplateDraft, l as TemplateDraftField, m as TemplatePreviewFrame, U as UseCategoryPoliciesResult, q as UseNotificationStreamOptions, r as UseNotificationsOptions, s as UseTemplateEditorParams, t as UseTemplateEditorResult, u as flattenNotificationPages, v as formatNotificationTime, w as formatNotificationTimestamp, x as resolveMessages, y as useCategoryPolicies, z as useDeactivateTemplate, A as useDeleteNotification, B as useMarkAllAsRead, C as useMarkAsRead, E as useNotificationContext, F as useNotificationStream, G as useNotifications, H as usePreferences, I as useRestoreTemplateDefault, J as useTemplateEditor, K as useTemplateVariables, L as useTemplates, M as useUnreadCount, O as useUpdatePreferences, P as useUpsertTemplate } from './headless-03_gfBEI.js';
1
+ export { D as DEFAULT_LOCALE, N as NOTIFICATION_QUERY_KEYS, a as NotificationContextValue, b as NotificationLocale, c as NotificationMessages, d as NotificationPermissionStatus, e as NotificationProvider, f as NotificationProviderProps, g as NotificationTheme, l as TemplateDraft, m as TemplateDraftField, n as TemplatePreviewFrame, U as UseCategoryPoliciesResult, r as UseNotificationPermissionOptions, s as UseNotificationPermissionResult, t as UseNotificationStreamOptions, u as UseNotificationsOptions, v as UseTemplateEditorParams, w as UseTemplateEditorResult, x as flattenNotificationPages, y as formatNotificationTime, z as formatNotificationTimestamp, A as resolveMessages, B as useCategoryPolicies, C as useDeactivateTemplate, E as useDeleteNotification, F as useMarkAllAsRead, G as useMarkAsRead, H as useNotificationContext, I as useNotificationPermission, J as useNotificationStream, K as useNotifications, L as useOptionalNotificationContext, M as usePreferences, O as useRestoreTemplateDefault, P as useTemplateEditor, Q as useTemplateVariables, R as useTemplates, V as useUnreadCount, W as useUpdatePreferences, X as useUpsertTemplate } from './headless-N8NdZSQW.js';
2
2
  import 'react';
3
3
  import '@adatechnology/notification-client';
4
- import '@tanstack/react-query';
5
4
  import '@adatechnology/notification-contracts';
5
+ import '@tanstack/react-query';
package/dist/headless.js CHANGED
@@ -12,8 +12,10 @@ import {
12
12
  useMarkAllAsRead,
13
13
  useMarkAsRead,
14
14
  useNotificationContext,
15
+ useNotificationPermission,
15
16
  useNotificationStream,
16
17
  useNotifications,
18
+ useOptionalNotificationContext,
17
19
  usePreferences,
18
20
  useRestoreTemplateDefault,
19
21
  useTemplateEditor,
@@ -22,7 +24,7 @@ import {
22
24
  useUnreadCount,
23
25
  useUpdatePreferences,
24
26
  useUpsertTemplate
25
- } from "./chunk-SFMOFCHV.js";
27
+ } from "./chunk-Y65PYMEH.js";
26
28
  export {
27
29
  DEFAULT_LOCALE,
28
30
  NOTIFICATION_QUERY_KEYS,
@@ -37,8 +39,10 @@ export {
37
39
  useMarkAllAsRead,
38
40
  useMarkAsRead,
39
41
  useNotificationContext,
42
+ useNotificationPermission,
40
43
  useNotificationStream,
41
44
  useNotifications,
45
+ useOptionalNotificationContext,
42
46
  usePreferences,
43
47
  useRestoreTemplateDefault,
44
48
  useTemplateEditor,
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- export { D as DEFAULT_LOCALE, N as NOTIFICATION_QUERY_KEYS, a as NotificationContextValue, b as NotificationLocale, c as NotificationMessages, d as NotificationProvider, e as NotificationProviderProps, f as NotificationTheme, S as SORT_DIRECTIONS, g as SortDirection, T as TEMPLATE_COLUMNS, h as TEMPLATE_SORT_FIELDS, i as TEMPLATE_VIEWS, j as TemplateColumn, k as TemplateDraft, l as TemplateDraftField, m as TemplatePreviewFrame, n as TemplateSort, o as TemplateSortField, p as TemplateView, U as UseCategoryPoliciesResult, q as UseNotificationStreamOptions, r as UseNotificationsOptions, s as UseTemplateEditorParams, t as UseTemplateEditorResult, u as flattenNotificationPages, v as formatNotificationTime, w as formatNotificationTimestamp, x as resolveMessages, y as useCategoryPolicies, z as useDeactivateTemplate, A as useDeleteNotification, B as useMarkAllAsRead, C as useMarkAsRead, E as useNotificationContext, F as useNotificationStream, G as useNotifications, H as usePreferences, I as useRestoreTemplateDefault, J as useTemplateEditor, K as useTemplateVariables, L as useTemplates, M as useUnreadCount, O as useUpdatePreferences, P as useUpsertTemplate } from './headless-03_gfBEI.js';
1
+ export { D as DEFAULT_LOCALE, N as NOTIFICATION_QUERY_KEYS, a as NotificationContextValue, b as NotificationLocale, c as NotificationMessages, d as NotificationPermissionStatus, e as NotificationProvider, f as NotificationProviderProps, g as NotificationTheme, S as SORT_DIRECTIONS, h as SortDirection, T as TEMPLATE_COLUMNS, i as TEMPLATE_SORT_FIELDS, j as TEMPLATE_VIEWS, k as TemplateColumn, l as TemplateDraft, m as TemplateDraftField, n as TemplatePreviewFrame, o as TemplateSort, p as TemplateSortField, q as TemplateView, U as UseCategoryPoliciesResult, r as UseNotificationPermissionOptions, s as UseNotificationPermissionResult, t as UseNotificationStreamOptions, u as UseNotificationsOptions, v as UseTemplateEditorParams, w as UseTemplateEditorResult, x as flattenNotificationPages, y as formatNotificationTime, z as formatNotificationTimestamp, A as resolveMessages, B as useCategoryPolicies, C as useDeactivateTemplate, E as useDeleteNotification, F as useMarkAllAsRead, G as useMarkAsRead, H as useNotificationContext, I as useNotificationPermission, J as useNotificationStream, K as useNotifications, L as useOptionalNotificationContext, M as usePreferences, O as useRestoreTemplateDefault, P as useTemplateEditor, Q as useTemplateVariables, R as useTemplates, V as useUnreadCount, W as useUpdatePreferences, X as useUpsertTemplate } from './headless-N8NdZSQW.js';
2
2
  import * as react from 'react';
3
- import { ReactNode } from 'react';
4
- import { NotificationSummary, NotificationTemplate, RenderedTemplatePreview } from '@adatechnology/notification-contracts';
3
+ import react__default, { ReactNode } from 'react';
4
+ import { NotificationSummary, WebPushSubscription, NotificationTemplate, RenderedTemplatePreview } from '@adatechnology/notification-contracts';
5
5
  import '@adatechnology/notification-client';
6
6
  import '@tanstack/react-query';
7
7
 
@@ -47,6 +47,32 @@ type PreferencesPanelProps = {
47
47
  };
48
48
  declare function PreferencesPanel({ className }: PreferencesPanelProps): react.JSX.Element;
49
49
 
50
+ /**
51
+ * Copyright (c) 2026 Ada Technology. MIT License.
52
+ *
53
+ * Componente acessível de aviso para permitir ou desativar notificações do navegador (Web Push).
54
+ */
55
+
56
+ type NotificationPermissionBannerProps = {
57
+ /** Chave pública VAPID para registro do Web Push. */
58
+ readonly vapidPublicKey?: string;
59
+ /** Chave do localStorage para persistir a dispensa do banner. */
60
+ readonly storageKey?: string;
61
+ /** Mostra o banner quando as notificações já estão ativas (permite desativação). Padrão: false */
62
+ readonly showWhenGranted?: boolean;
63
+ /** Mostra o banner informativo quando as notificações foram bloqueadas no navegador. Padrão: true */
64
+ readonly showWhenDenied?: boolean;
65
+ /** Callback opcional ao se inscrever com sucesso. */
66
+ readonly onSubscribe?: (subscription: WebPushSubscription) => void | Promise<void>;
67
+ /** Callback opcional ao desativar notificações. */
68
+ readonly onUnsubscribe?: () => void | Promise<void>;
69
+ /** Callback disparado quando o usuário clica em dispensar/fechar. */
70
+ readonly onDismiss?: () => void;
71
+ /** Classe CSS customizada para o contêiner raiz. */
72
+ readonly className?: string;
73
+ };
74
+ declare function NotificationPermissionBanner({ vapidPublicKey, storageKey, showWhenGranted, showWhenDenied, onSubscribe, onUnsubscribe, onDismiss, className, }: NotificationPermissionBannerProps): react__default.JSX.Element | null;
75
+
50
76
  type NotificationsWorkspaceProps = {
51
77
  /** Sobrescreve textos pontuais sem trocar o locale inteiro. */
52
78
  readonly labels?: Partial<Record<string, string>>;
@@ -359,4 +385,4 @@ declare function insertAt({ text, insertion, selectionStart, selectionEnd, }: {
359
385
  readonly selectionEnd: number;
360
386
  }): ApplyMarkResult;
361
387
 
362
- export { HTML_TOKEN, HtmlCodeField, type HtmlCodeFieldProblem, type HtmlCodeFieldProps, type HtmlToken, type HtmlTokenKind, MESSAGE_EMOJI, MESSAGE_FORMAT, MESSAGE_FORMAT_MARKS, MessageBodyField, type MessageBodyFieldProps, type MessageFormat, type MessageFormatMark, MessageToolbar, type MessageToolbarProps, NotificationBell, type NotificationBellProps, type NotificationCategoryOption, type NotificationChannelOption, NotificationItem, type NotificationItemProps, NotificationList, type NotificationListProps, NotificationSettingsWorkspace, type NotificationSettingsWorkspaceProps, NotificationsWorkspace, type NotificationsWorkspaceProps, PreferencesPanel, type PreferencesPanelProps, type PreviewAttachment, type PreviewLabels, TemplatePreview, type TemplatePreviewProps, applyMark, insertAt, tokenizeHtml };
388
+ export { HTML_TOKEN, HtmlCodeField, type HtmlCodeFieldProblem, type HtmlCodeFieldProps, type HtmlToken, type HtmlTokenKind, MESSAGE_EMOJI, MESSAGE_FORMAT, MESSAGE_FORMAT_MARKS, MessageBodyField, type MessageBodyFieldProps, type MessageFormat, type MessageFormatMark, MessageToolbar, type MessageToolbarProps, NotificationBell, type NotificationBellProps, type NotificationCategoryOption, type NotificationChannelOption, NotificationItem, type NotificationItemProps, NotificationList, type NotificationListProps, NotificationPermissionBanner, type NotificationPermissionBannerProps, NotificationSettingsWorkspace, type NotificationSettingsWorkspaceProps, NotificationsWorkspace, type NotificationsWorkspaceProps, PreferencesPanel, type PreferencesPanelProps, type PreviewAttachment, type PreviewLabels, TemplatePreview, type TemplatePreviewProps, applyMark, insertAt, tokenizeHtml };