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

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.
@@ -5,7 +5,8 @@ import { createContext, useContext, useMemo } from "react";
5
5
  var pt_BR_default = {
6
6
  "bell.label": "Notifica\xE7\xF5es",
7
7
  "bell.unreadSuffix": "n\xE3o lidas",
8
- "list.title": "Notifica\xE7\xF5es",
8
+ "list.title": "Caixa de entrada",
9
+ "list.unread": "n\xE3o lidas",
9
10
  "list.empty": "Nenhuma notifica\xE7\xE3o por aqui.",
10
11
  "list.loading": "Carregando\u2026",
11
12
  "list.error": "N\xE3o foi poss\xEDvel carregar suas notifica\xE7\xF5es.",
@@ -54,7 +55,8 @@ var pt_BR_default = {
54
55
  var en_default = {
55
56
  "bell.label": "Notifications",
56
57
  "bell.unreadSuffix": "unread",
57
- "list.title": "Notifications",
58
+ "list.title": "Inbox",
59
+ "list.unread": "unread",
58
60
  "list.empty": "Nothing here yet.",
59
61
  "list.loading": "Loading\u2026",
60
62
  "list.error": "We couldn't load your notifications.",
@@ -123,6 +125,7 @@ function NotificationProvider({
123
125
  const value = useMemo(
124
126
  () => ({
125
127
  client,
128
+ locale,
126
129
  messages: { ...resolveMessages(locale), ...messageOverrides },
127
130
  theme,
128
131
  pollIntervalSeconds
@@ -290,6 +293,35 @@ function useUpsertTemplate() {
290
293
  });
291
294
  }
292
295
 
296
+ // src/format/notificationTime.ts
297
+ var SECOND = 1e3;
298
+ var MINUTE = 60 * SECOND;
299
+ var HOUR = 60 * MINUTE;
300
+ var DAY = 24 * HOUR;
301
+ var RELATIVE_HORIZON = 7 * DAY;
302
+ function parseInstant(isoTimestamp) {
303
+ const instant = new Date(isoTimestamp);
304
+ return Number.isNaN(instant.getTime()) ? void 0 : instant;
305
+ }
306
+ function formatNotificationTime(isoTimestamp, locale, now) {
307
+ const instant = parseInstant(isoTimestamp);
308
+ if (!instant) return "";
309
+ const elapsed = Math.max(0, now.getTime() - instant.getTime());
310
+ if (elapsed >= RELATIVE_HORIZON) {
311
+ return new Intl.DateTimeFormat(locale, { day: "2-digit", month: "2-digit", year: "numeric" }).format(instant);
312
+ }
313
+ const relative = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
314
+ if (elapsed < MINUTE) return relative.format(0, "second");
315
+ if (elapsed < HOUR) return relative.format(-Math.floor(elapsed / MINUTE), "minute");
316
+ if (elapsed < DAY) return relative.format(-Math.floor(elapsed / HOUR), "hour");
317
+ return relative.format(-Math.floor(elapsed / DAY), "day");
318
+ }
319
+ function formatNotificationTimestamp(isoTimestamp, locale) {
320
+ const instant = parseInstant(isoTimestamp);
321
+ if (!instant) return "";
322
+ return new Intl.DateTimeFormat(locale, { dateStyle: "short", timeStyle: "short" }).format(instant);
323
+ }
324
+
293
325
  // src/hooks/useTemplateEditor.ts
294
326
  import { useMemo as useMemo2, useState } from "react";
295
327
  import { renderTemplate } from "@adatechnology/notification-contracts";
@@ -389,6 +421,8 @@ export {
389
421
  useUpdatePreferences,
390
422
  useTemplates,
391
423
  useUpsertTemplate,
424
+ formatNotificationTime,
425
+ formatNotificationTimestamp,
392
426
  useTemplateEditor
393
427
  };
394
- //# sourceMappingURL=chunk-FNUYTJ36.js.map
428
+ //# sourceMappingURL=chunk-I6WQKOCP.js.map
@@ -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/useNotifications.ts","../src/hooks/queryKeys.ts","../src/hooks/useNotificationStream.ts","../src/hooks/usePreferences.ts","../src/format/notificationTime.ts","../src/hooks/useTemplateEditor.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","{\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 \"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}\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 \"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}\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 * 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} 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\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 * 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\nimport { useMemo, useState } from 'react'\nimport { renderTemplate } from '@adatechnology/notification-contracts'\nimport type {\n NotificationTemplate,\n RenderedTemplatePreview,\n UpsertTemplateBody,\n} from '@adatechnology/notification-contracts'\n\nimport { useTemplates, useUpsertTemplate } from './usePreferences'\n\nexport type TemplateDraft = {\n readonly key: string\n readonly channel: string\n readonly locale: string\n readonly subject: string\n readonly body: string\n readonly whatsappTemplateName: string\n}\n\nfunction toDraft(template: NotificationTemplate): TemplateDraft {\n return {\n key: template.key,\n channel: template.channel,\n locale: template.locale,\n subject: template.subject ?? '',\n body: template.body,\n whatsappTemplateName: template.whatsappTemplateName ?? '',\n }\n}\n\nexport type UseTemplateEditorParams = {\n /**\n * Valores de exemplo do preview. É do produto: só ele sabe que `{{shortCode}}` é \"QC-1042\".\n * Ausente, o preview mostra os campos vazios — que é o que o destinatário veria mesmo.\n */\n readonly previewPayload?: Readonly<Record<string, unknown>>\n}\n\nexport type UseTemplateEditorResult = {\n readonly templates: readonly NotificationTemplate[]\n readonly isLoading: boolean\n readonly isSaving: boolean\n readonly error: string | undefined\n readonly selected: NotificationTemplate | undefined\n readonly draft: TemplateDraft | undefined\n readonly preview: RenderedTemplatePreview | undefined\n readonly isDirty: boolean\n select: (template: NotificationTemplate) => void\n update: (patch: Partial<TemplateDraft>) => void\n save: () => void\n clear: () => void\n}\n\nexport function useTemplateEditor(params: UseTemplateEditorParams = {}): UseTemplateEditorResult {\n const templatesQuery = useTemplates()\n const upsert = useUpsertTemplate()\n\n const [selectedId, setSelectedId] = useState<string | undefined>(undefined)\n const [draft, setDraft] = useState<TemplateDraft | undefined>(undefined)\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 /** Decisão 2: derivado do id. */\n const selected = useMemo(() => templates.find((template) => template.id === selectedId), [templates, selectedId])\n\n /** Decisão 4: o mesmo render do envio. */\n const preview = useMemo(() => {\n if (!draft) return undefined\n return renderTemplate({\n channel: draft.channel,\n subject: draft.subject || undefined,\n body: draft.body,\n payload: params.previewPayload ?? {},\n })\n }, [draft, params.previewPayload])\n\n const isDirty = useMemo(() => {\n if (!draft || !selected) return false\n const original = toDraft(selected)\n return (Object.keys(original) as (keyof TemplateDraft)[]).some((field) => original[field] !== draft[field])\n }, [draft, selected])\n\n return {\n templates,\n isLoading: templatesQuery.isLoading,\n isSaving: upsert.isPending,\n error: upsert.error?.message,\n selected,\n draft,\n preview,\n isDirty,\n\n select(template) {\n setSelectedId(template.id)\n setDraft(toDraft(template))\n },\n\n update(patch) {\n setDraft((current) => (current ? { ...current, ...patch } : current))\n },\n\n save() {\n if (!draft) return\n const body: UpsertTemplateBody = {\n key: draft.key,\n channel: draft.channel as UpsertTemplateBody['channel'],\n locale: draft.locale,\n active: true,\n body: draft.body,\n // Decisão 3: chave omitida quando vazia.\n ...(draft.subject ? { subject: draft.subject } : {}),\n ...(draft.whatsappTemplateName ? { whatsappTemplateName: draft.whatsappTemplateName } : {}),\n }\n // A versão nova tem outro id: soltar a seleção evita o editor ficar preso na anterior.\n upsert.mutate(body, { onSuccess: () => setSelectedId(undefined) })\n },\n\n clear() {\n setSelectedId(undefined)\n setDraft(undefined)\n },\n }\n}\n"],"mappings":"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","names":["useQueryClient","useQueryClient","useMutation","useQuery","useQueryClient","useQuery","useQueryClient","useMutation","useMemo","useMemo"]}
@@ -9,7 +9,8 @@ import { NotificationSummary, NotificationPreference, NotificationTemplate, Rend
9
9
  var ptBR = {
10
10
  "bell.label": "Notificações",
11
11
  "bell.unreadSuffix": "não lidas",
12
- "list.title": "Notificações",
12
+ "list.title": "Caixa de entrada",
13
+ "list.unread": "não lidas",
13
14
  "list.empty": "Nenhuma notificação por aqui.",
14
15
  "list.loading": "Carregando…",
15
16
  "list.error": "Não foi possível carregar suas notificações.",
@@ -73,6 +74,12 @@ type NotificationTheme = {
73
74
  };
74
75
  type NotificationContextValue = {
75
76
  readonly client: NotificationClient;
77
+ /**
78
+ * O idioma resolvido, e não só os textos dele: data e número não vivem no arquivo de locale —
79
+ * quem os formata é `Intl`, e `Intl` precisa da tag do idioma. Sem isto o item da lista teria de
80
+ * chutar um locale fixo, e a inbox mostraria "5 minutes ago" numa tela em português.
81
+ */
82
+ readonly locale: NotificationLocale;
76
83
  readonly messages: NotificationMessages;
77
84
  readonly theme: NotificationTheme;
78
85
  /** Polling de fallback quando o SSE não está disponível (segundos); `0` desliga. */
@@ -168,6 +175,19 @@ declare const NOTIFICATION_QUERY_KEYS: {
168
175
  readonly templates: () => readonly ["notifications", "templates"];
169
176
  };
170
177
 
178
+ /**
179
+ * Copyright (c) 2026 Ada Technology. MIT License.
180
+ *
181
+ * Quando o aviso chegou, dito do jeito que se diz em voz alta.
182
+ *
183
+ * `Intl` faz o trabalho de idioma — o pacote não guarda "há", "ago" nem nome de mês em lugar nenhum,
184
+ * o que manteria `web.md` §6 de pé e ainda assim quebraria no primeiro locale novo. O que é decisão
185
+ * nossa são as faixas: segundo, minuto, hora, dia, e a virada para data absoluta.
186
+ */
187
+ declare function formatNotificationTime(isoTimestamp: string, locale: string, now: Date): string;
188
+ /** O instante inteiro, para o `title` — é a hora que o texto relativo esconde. */
189
+ declare function formatNotificationTimestamp(isoTimestamp: string, locale: string): string;
190
+
171
191
  /**
172
192
  * Copyright (c) 2026 Ada Technology. MIT License.
173
193
  *
@@ -218,4 +238,4 @@ type UseTemplateEditorResult = {
218
238
  };
219
239
  declare function useTemplateEditor(params?: UseTemplateEditorParams): UseTemplateEditorResult;
220
240
 
221
- export { DEFAULT_LOCALE, NOTIFICATION_QUERY_KEYS, type NotificationContextValue, type NotificationLocale, type NotificationMessages, NotificationProvider, type NotificationProviderProps, type NotificationTheme, type TemplateDraft, type UseNotificationStreamOptions, type UseNotificationsOptions, type UseTemplateEditorParams, type UseTemplateEditorResult, flattenNotificationPages, resolveMessages, useDeleteNotification, useMarkAllAsRead, useMarkAsRead, useNotificationContext, useNotificationStream, useNotifications, usePreferences, useTemplateEditor, useTemplates, useUnreadCount, useUpdatePreferences, useUpsertTemplate };
241
+ export { DEFAULT_LOCALE, NOTIFICATION_QUERY_KEYS, type NotificationContextValue, type NotificationLocale, type NotificationMessages, NotificationProvider, type NotificationProviderProps, type NotificationTheme, type TemplateDraft, type UseNotificationStreamOptions, type UseNotificationsOptions, type UseTemplateEditorParams, type UseTemplateEditorResult, flattenNotificationPages, formatNotificationTime, formatNotificationTimestamp, resolveMessages, useDeleteNotification, useMarkAllAsRead, useMarkAsRead, useNotificationContext, useNotificationStream, useNotifications, usePreferences, useTemplateEditor, useTemplates, useUnreadCount, useUpdatePreferences, useUpsertTemplate };
package/dist/headless.js CHANGED
@@ -3,6 +3,8 @@ import {
3
3
  NOTIFICATION_QUERY_KEYS,
4
4
  NotificationProvider,
5
5
  flattenNotificationPages,
6
+ formatNotificationTime,
7
+ formatNotificationTimestamp,
6
8
  resolveMessages,
7
9
  useDeleteNotification,
8
10
  useMarkAllAsRead,
@@ -16,12 +18,14 @@ import {
16
18
  useUnreadCount,
17
19
  useUpdatePreferences,
18
20
  useUpsertTemplate
19
- } from "./chunk-FNUYTJ36.js";
21
+ } from "./chunk-I6WQKOCP.js";
20
22
  export {
21
23
  DEFAULT_LOCALE,
22
24
  NOTIFICATION_QUERY_KEYS,
23
25
  NotificationProvider,
24
26
  flattenNotificationPages,
27
+ formatNotificationTime,
28
+ formatNotificationTimestamp,
25
29
  resolveMessages,
26
30
  useDeleteNotification,
27
31
  useMarkAllAsRead,
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { DEFAULT_LOCALE, NOTIFICATION_QUERY_KEYS, NotificationContextValue, NotificationLocale, NotificationMessages, NotificationProvider, NotificationProviderProps, NotificationTheme, TemplateDraft, UseNotificationStreamOptions, UseNotificationsOptions, UseTemplateEditorParams, UseTemplateEditorResult, flattenNotificationPages, resolveMessages, useDeleteNotification, useMarkAllAsRead, useMarkAsRead, useNotificationContext, useNotificationStream, useNotifications, usePreferences, useTemplateEditor, useTemplates, useUnreadCount, useUpdatePreferences, useUpsertTemplate } from './headless.js';
1
+ export { DEFAULT_LOCALE, NOTIFICATION_QUERY_KEYS, NotificationContextValue, NotificationLocale, NotificationMessages, NotificationProvider, NotificationProviderProps, NotificationTheme, TemplateDraft, UseNotificationStreamOptions, UseNotificationsOptions, UseTemplateEditorParams, UseTemplateEditorResult, flattenNotificationPages, formatNotificationTime, formatNotificationTimestamp, resolveMessages, useDeleteNotification, useMarkAllAsRead, useMarkAsRead, useNotificationContext, useNotificationStream, useNotifications, usePreferences, useTemplateEditor, useTemplates, useUnreadCount, useUpdatePreferences, useUpsertTemplate } from './headless.js';
2
2
  import * as react from 'react';
3
3
  import { ReactNode } from 'react';
4
4
  import { NotificationSummary, NotificationTemplate } from '@adatechnology/notification-contracts';
@@ -14,7 +14,7 @@ type NotificationBellProps = {
14
14
  /** Acima disso o badge mostra `99+`; um número de 4 dígitos estoura o desenho do badge. */
15
15
  readonly maxBadgeCount?: number;
16
16
  };
17
- declare function NotificationBell({ onClick, className, maxBadgeCount }: NotificationBellProps): react.JSX.Element;
17
+ declare function NotificationBell({ onClick, className, maxBadgeCount, }: NotificationBellProps): react.JSX.Element;
18
18
 
19
19
  type NotificationItemProps = {
20
20
  readonly notification: NotificationSummary;
@@ -27,12 +27,17 @@ type NotificationListProps = {
27
27
  readonly category?: string;
28
28
  readonly onSelect?: (notification: NotificationSummary) => void;
29
29
  readonly className?: string;
30
+ /**
31
+ * Substitui o vazio padrão. Quem decide se está vazio é a lista — o host que desenhava o próprio
32
+ * vazio ao lado dela mostrava "nada por aqui" embaixo das notificações que estavam ali.
33
+ */
34
+ readonly renderEmpty?: () => ReactNode;
30
35
  /** Slot: o host troca a linha sem reimplementar a lista (regra §4.3, slots e overrides). */
31
36
  readonly components?: {
32
37
  readonly Item?: typeof NotificationItem;
33
38
  };
34
39
  };
35
- declare function NotificationList({ category, onSelect, className, components }: NotificationListProps): react.JSX.Element;
40
+ declare function NotificationList({ category, onSelect, className, renderEmpty, components }: NotificationListProps): react.JSX.Element;
36
41
 
37
42
  /**
38
43
  * Copyright (c) 2026 Ada Technology. MIT License.
@@ -58,6 +63,14 @@ type NotificationsWorkspaceProps = {
58
63
  readonly settingsHref?: string | undefined;
59
64
  /** Clique numa notificação. Ausente, a linha não é clicável. */
60
65
  readonly onSelectNotification?: (notification: NotificationSummary) => void;
66
+ /**
67
+ * Substitui o cabeçalho padrão inteiro.
68
+ *
69
+ * O workspace desenha um `<h1>` porque ele é a tela; a página que já tem o título dela passava a
70
+ * ter dois `<h1>` dizendo a mesma palavra, e para leitor de tela duas primeiras manchetes é o
71
+ * mesmo que nenhuma. Ausente, o padrão continua desenhando — slot é substituição, não interruptor.
72
+ */
73
+ readonly renderHeader?: () => ReactNode;
61
74
  /** Ações do produto no cabeçalho, ao lado do link de configuração. */
62
75
  readonly renderHeaderActions?: () => ReactNode;
63
76
  /** Aviso ou filtro do produto, entre o cabeçalho e a lista. */
@@ -66,7 +79,7 @@ type NotificationsWorkspaceProps = {
66
79
  readonly renderEmpty?: () => ReactNode;
67
80
  readonly className?: string;
68
81
  };
69
- declare function NotificationsWorkspace({ labels: labelsOverride, category, showPreferences, settingsHref, onSelectNotification, renderHeaderActions, renderAboveList, renderEmpty, className, }: NotificationsWorkspaceProps): react.JSX.Element;
82
+ declare function NotificationsWorkspace({ labels: labelsOverride, category, showPreferences, settingsHref, onSelectNotification, renderHeader, renderHeaderActions, renderAboveList, renderEmpty, className, }: NotificationsWorkspaceProps): react.JSX.Element;
70
83
 
71
84
  type NotificationChannelOption = {
72
85
  readonly id: string;
@@ -101,10 +114,12 @@ type NotificationSettingsWorkspaceProps = {
101
114
  readonly value: string;
102
115
  readonly onChange: (value: string) => void;
103
116
  }) => ReactNode;
117
+ /** Substitui o cabeçalho padrão — a página que já tem título próprio evita o segundo `<h1>`. */
118
+ readonly renderHeader?: () => ReactNode;
104
119
  /** Ações do produto no cabeçalho. */
105
120
  readonly renderHeaderActions?: () => ReactNode;
106
121
  readonly className?: string;
107
122
  };
108
- declare function NotificationSettingsWorkspace({ labels: labelsOverride, channels, categories, previewPayload, templateLabelOf, renderChannelFields, renderHeaderActions, className, }: NotificationSettingsWorkspaceProps): react.JSX.Element;
123
+ declare function NotificationSettingsWorkspace({ labels: labelsOverride, channels, categories, previewPayload, templateLabelOf, renderChannelFields, renderHeader, renderHeaderActions, className, }: NotificationSettingsWorkspaceProps): react.JSX.Element;
109
124
 
110
125
  export { NotificationBell, type NotificationBellProps, type NotificationCategoryOption, type NotificationChannelOption, NotificationItem, type NotificationItemProps, NotificationList, type NotificationListProps, NotificationSettingsWorkspace, type NotificationSettingsWorkspaceProps, NotificationsWorkspace, type NotificationsWorkspaceProps, PreferencesPanel, type PreferencesPanelProps };
package/dist/index.js CHANGED
@@ -3,6 +3,8 @@ import {
3
3
  NOTIFICATION_QUERY_KEYS,
4
4
  NotificationProvider,
5
5
  flattenNotificationPages,
6
+ formatNotificationTime,
7
+ formatNotificationTimestamp,
6
8
  resolveMessages,
7
9
  useDeleteNotification,
8
10
  useMarkAllAsRead,
@@ -16,14 +18,18 @@ import {
16
18
  useUnreadCount,
17
19
  useUpdatePreferences,
18
20
  useUpsertTemplate
19
- } from "./chunk-FNUYTJ36.js";
21
+ } from "./chunk-I6WQKOCP.js";
20
22
 
21
23
  // src/components/NotificationBell.tsx
22
24
  import { Bell } from "lucide-react";
23
25
  import clsx from "clsx";
24
26
  import { jsx, jsxs } from "react/jsx-runtime";
25
27
  var DEFAULT_MAX_BADGE_COUNT = 99;
26
- function NotificationBell({ onClick, className, maxBadgeCount = DEFAULT_MAX_BADGE_COUNT }) {
28
+ function NotificationBell({
29
+ onClick,
30
+ className,
31
+ maxBadgeCount = DEFAULT_MAX_BADGE_COUNT
32
+ }) {
27
33
  const { messages, theme } = useNotificationContext();
28
34
  const { data: unreadCount = 0 } = useUnreadCount();
29
35
  const hasUnread = unreadCount > 0;
@@ -57,7 +63,7 @@ import { Check, Trash2 } from "lucide-react";
57
63
  import clsx2 from "clsx";
58
64
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
59
65
  function NotificationItem({ notification, onSelect, className }) {
60
- const { messages } = useNotificationContext();
66
+ const { locale, messages } = useNotificationContext();
61
67
  const markAsRead = useMarkAsRead();
62
68
  const deleteNotification = useDeleteNotification();
63
69
  const isUnread = notification.readAt === void 0;
@@ -67,9 +73,20 @@ function NotificationItem({ notification, onSelect, className }) {
67
73
  }
68
74
  return /* @__PURE__ */ jsxs2("li", { className: clsx2("adn-item", isUnread && "adn-item--unread", className), children: [
69
75
  /* @__PURE__ */ jsxs2("button", { type: "button", className: "adn-item__main", onClick: handleSelect, children: [
70
- /* @__PURE__ */ jsx2("span", { className: "adn-item__title", children: notification.title }),
71
- /* @__PURE__ */ jsx2("span", { className: "adn-item__body", children: notification.body }),
72
- isUnread ? /* @__PURE__ */ jsx2("span", { className: "adn-item__unread-dot", "aria-label": messages["item.unread"] }) : null
76
+ /* @__PURE__ */ jsxs2("span", { className: "adn-item__heading", children: [
77
+ isUnread ? /* @__PURE__ */ jsx2("span", { className: "adn-item__unread-dot", "aria-label": messages["item.unread"] }) : null,
78
+ /* @__PURE__ */ jsx2("span", { className: "adn-item__title", children: notification.title }),
79
+ /* @__PURE__ */ jsx2(
80
+ "time",
81
+ {
82
+ className: "adn-item__time",
83
+ dateTime: notification.createdAt,
84
+ title: formatNotificationTimestamp(notification.createdAt, locale),
85
+ children: formatNotificationTime(notification.createdAt, locale, /* @__PURE__ */ new Date())
86
+ }
87
+ )
88
+ ] }),
89
+ /* @__PURE__ */ jsx2("span", { className: "adn-item__body", children: notification.body })
73
90
  ] }),
74
91
  /* @__PURE__ */ jsxs2("div", { className: "adn-item__actions", children: [
75
92
  isUnread ? /* @__PURE__ */ jsx2(
@@ -102,16 +119,21 @@ function NotificationItem({ notification, onSelect, className }) {
102
119
 
103
120
  // src/components/NotificationList.tsx
104
121
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
105
- function NotificationList({ category, onSelect, className, components }) {
122
+ var SKELETON_ROWS = [0, 1, 2];
123
+ function NotificationList({ category, onSelect, className, renderEmpty, components }) {
106
124
  const { messages, theme } = useNotificationContext();
107
125
  const query = useNotifications({ category });
108
126
  const { data: unreadCount = 0 } = useUnreadCount();
109
127
  const markAllAsRead = useMarkAllAsRead();
110
128
  const Item = components?.Item ?? NotificationItem;
111
129
  const notifications = flattenNotificationPages(query.data?.pages);
130
+ const isEmpty = !query.isPending && !query.isError && notifications.length === 0;
112
131
  return /* @__PURE__ */ jsxs3("section", { className: clsx3("adn-list", theme.rootClassName, className), "aria-label": messages["list.title"], children: [
113
132
  /* @__PURE__ */ jsxs3("header", { className: "adn-list__header", children: [
114
- /* @__PURE__ */ jsx3("h2", { className: "adn-list__title", children: messages["list.title"] }),
133
+ /* @__PURE__ */ jsxs3("div", { className: "adn-list__heading", children: [
134
+ /* @__PURE__ */ jsx3("h2", { className: "adn-list__title", children: messages["list.title"] }),
135
+ unreadCount > 0 ? /* @__PURE__ */ jsx3("span", { className: "adn-list__count", role: "status", "aria-label": `${unreadCount} ${messages["list.unread"]}`, children: unreadCount }) : null
136
+ ] }),
115
137
  unreadCount > 0 ? /* @__PURE__ */ jsxs3(
116
138
  "button",
117
139
  {
@@ -126,20 +148,20 @@ function NotificationList({ category, onSelect, className, components }) {
126
148
  }
127
149
  ) : null
128
150
  ] }),
129
- query.isPending ? /* @__PURE__ */ jsx3("p", { className: "adn-list__state", children: messages["list.loading"] }) : null,
130
- query.isError ? /* @__PURE__ */ jsxs3("div", { className: "adn-list__state adn-list__state--error", children: [
151
+ query.isPending ? /* @__PURE__ */ jsx3("div", { className: "adn-list__skeleton", role: "status", "aria-label": messages["list.loading"], children: SKELETON_ROWS.map((row) => /* @__PURE__ */ jsx3("span", { className: "adn-list__skeleton-row" }, row)) }) : null,
152
+ query.isError ? /* @__PURE__ */ jsxs3("div", { className: "adn-list__state adn-list__state--error", role: "alert", children: [
131
153
  /* @__PURE__ */ jsx3("p", { children: messages["list.error"] }),
132
154
  /* @__PURE__ */ jsxs3("button", { type: "button", className: "adn-list__retry", onClick: () => void query.refetch(), children: [
133
155
  /* @__PURE__ */ jsx3(RotateCw, { className: "adn-list__retry-icon", "aria-hidden": "true" }),
134
156
  messages["list.retry"]
135
157
  ] })
136
158
  ] }) : null,
137
- !query.isPending && !query.isError && notifications.length === 0 ? /* @__PURE__ */ jsxs3("div", { className: "adn-list__state adn-list__state--empty", children: [
159
+ isEmpty ? renderEmpty?.() ?? /* @__PURE__ */ jsxs3("div", { className: "adn-list__state adn-list__state--empty", children: [
138
160
  /* @__PURE__ */ jsx3(Inbox, { className: "adn-list__empty-icon", "aria-hidden": "true" }),
139
161
  /* @__PURE__ */ jsx3("p", { children: messages["list.empty"] })
140
162
  ] }) : null,
141
163
  notifications.length > 0 ? /* @__PURE__ */ jsx3("ul", { className: "adn-list__items", children: notifications.map((notification) => /* @__PURE__ */ jsx3(Item, { notification, onSelect }, notification.id)) }) : null,
142
- query.hasNextPage ? /* @__PURE__ */ jsx3(
164
+ query.hasNextPage ? /* @__PURE__ */ jsx3("div", { className: "adn-list__footer", children: /* @__PURE__ */ jsx3(
143
165
  "button",
144
166
  {
145
167
  type: "button",
@@ -148,7 +170,7 @@ function NotificationList({ category, onSelect, className, components }) {
148
170
  disabled: query.isFetchingNextPage,
149
171
  children: messages["list.loadMore"]
150
172
  }
151
- ) : null
173
+ ) }) : null
152
174
  ] });
153
175
  }
154
176
 
@@ -238,6 +260,7 @@ function NotificationsWorkspace({
238
260
  showPreferences = true,
239
261
  settingsHref,
240
262
  onSelectNotification,
263
+ renderHeader,
241
264
  renderHeaderActions,
242
265
  renderAboveList,
243
266
  renderEmpty,
@@ -246,7 +269,7 @@ function NotificationsWorkspace({
246
269
  const { messages } = useNotificationContext();
247
270
  const label = (key) => labelsOverride?.[key] ?? messages[key] ?? key;
248
271
  return /* @__PURE__ */ jsxs5("div", { className: `adn-workspace ${className ?? ""}`, children: [
249
- /* @__PURE__ */ jsxs5("header", { className: "adn-workspace__header", children: [
272
+ renderHeader ? renderHeader() : /* @__PURE__ */ jsxs5("header", { className: "adn-workspace__header", children: [
250
273
  /* @__PURE__ */ jsxs5("div", { children: [
251
274
  /* @__PURE__ */ jsx5("h1", { className: "adn-workspace__title", children: label("workspace.title") }),
252
275
  /* @__PURE__ */ jsx5("p", { className: "adn-workspace__description", children: label("workspace.description") })
@@ -258,16 +281,14 @@ function NotificationsWorkspace({
258
281
  ] }),
259
282
  renderAboveList?.(),
260
283
  /* @__PURE__ */ jsxs5("div", { className: showPreferences ? "adn-workspace__grid" : "adn-workspace__grid--single", children: [
261
- /* @__PURE__ */ jsxs5("section", { className: "adn-workspace__inbox", children: [
262
- /* @__PURE__ */ jsx5(
263
- NotificationList,
264
- {
265
- ...category ? { category } : {},
266
- ...onSelectNotification ? { onSelect: onSelectNotification } : {}
267
- }
268
- ),
269
- renderEmpty?.()
270
- ] }),
284
+ /* @__PURE__ */ jsx5("section", { className: "adn-workspace__inbox", children: /* @__PURE__ */ jsx5(
285
+ NotificationList,
286
+ {
287
+ ...category ? { category } : {},
288
+ ...onSelectNotification ? { onSelect: onSelectNotification } : {},
289
+ ...renderEmpty ? { renderEmpty } : {}
290
+ }
291
+ ) }),
271
292
  showPreferences && /* @__PURE__ */ jsx5("aside", { className: "adn-workspace__aside", children: /* @__PURE__ */ jsx5(PreferencesPanel, {}) })
272
293
  ] })
273
294
  ] });
@@ -282,6 +303,7 @@ function NotificationSettingsWorkspace({
282
303
  previewPayload,
283
304
  templateLabelOf,
284
305
  renderChannelFields,
306
+ renderHeader,
285
307
  renderHeaderActions,
286
308
  className
287
309
  }) {
@@ -307,7 +329,7 @@ function NotificationSettingsWorkspace({
307
329
  updatePreferences.mutate(next);
308
330
  }
309
331
  return /* @__PURE__ */ jsxs6("div", { className: `adn-settings ${className ?? ""}`, children: [
310
- /* @__PURE__ */ jsxs6("header", { className: "adn-settings__header", children: [
332
+ renderHeader ? renderHeader() : /* @__PURE__ */ jsxs6("header", { className: "adn-settings__header", children: [
311
333
  /* @__PURE__ */ jsxs6("div", { children: [
312
334
  /* @__PURE__ */ jsx6("h1", { className: "adn-settings__title", children: label("settings.title") }),
313
335
  /* @__PURE__ */ jsx6("p", { className: "adn-settings__description", children: label("settings.description") })
@@ -385,7 +407,13 @@ function NotificationSettingsWorkspace({
385
407
  /* @__PURE__ */ jsxs6("label", { className: "adn-settings__field", children: [
386
408
  /* @__PURE__ */ jsx6("span", { className: "adn-settings__field-label", children: label("settings.subject") }),
387
409
  /* @__PURE__ */ jsx6("span", { className: "adn-settings__hint", children: label("settings.subjectHint") }),
388
- /* @__PURE__ */ jsx6("input", { value: editor.draft.subject, onChange: (event) => editor.update({ subject: event.target.value }) })
410
+ /* @__PURE__ */ jsx6(
411
+ "input",
412
+ {
413
+ value: editor.draft.subject,
414
+ onChange: (event) => editor.update({ subject: event.target.value })
415
+ }
416
+ )
389
417
  ] }),
390
418
  /* @__PURE__ */ jsxs6("label", { className: "adn-settings__field", children: [
391
419
  /* @__PURE__ */ jsx6("span", { className: "adn-settings__field-label", children: label("settings.body") }),
@@ -429,6 +457,8 @@ export {
429
457
  NotificationsWorkspace,
430
458
  PreferencesPanel,
431
459
  flattenNotificationPages,
460
+ formatNotificationTime,
461
+ formatNotificationTimestamp,
432
462
  resolveMessages,
433
463
  useDeleteNotification,
434
464
  useMarkAllAsRead,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/NotificationBell.tsx","../src/components/NotificationList.tsx","../src/components/NotificationItem.tsx","../src/components/PreferencesPanel.tsx","../src/components/NotificationsWorkspace.tsx","../src/components/NotificationSettingsWorkspace.tsx"],"sourcesContent":["/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n */\n\nimport { Bell } from 'lucide-react'\nimport clsx from 'clsx'\n\nimport { useNotificationContext } from '../NotificationProvider'\nimport { useUnreadCount } from '../hooks/useNotifications'\n\nexport type NotificationBellProps = {\n readonly onClick?: () => void\n readonly className?: string\n /** Acima disso o badge mostra `99+`; um número de 4 dígitos estoura o desenho do badge. */\n readonly maxBadgeCount?: number\n}\n\nconst DEFAULT_MAX_BADGE_COUNT = 99\n\nexport function NotificationBell({ onClick, className, maxBadgeCount = DEFAULT_MAX_BADGE_COUNT }: NotificationBellProps) {\n const { messages, theme } = useNotificationContext()\n const { data: unreadCount = 0 } = useUnreadCount()\n\n const hasUnread = unreadCount > 0\n const badgeLabel = unreadCount > maxBadgeCount ? `${maxBadgeCount}+` : String(unreadCount)\n const accessibleLabel = hasUnread\n ? `${messages['bell.label']}: ${unreadCount} ${messages['bell.unreadSuffix']}`\n : messages['bell.label']\n\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className={clsx('adn-bell', theme.rootClassName, className)}\n // Botão só-ícone exige rótulo acessível (regra de ícones, `web.md` §9).\n aria-label={accessibleLabel}\n >\n {/* Decorativo: o rótulo acessível já está no botão, e anunciar o ícone repetiria a ação. */}\n <Bell className=\"adn-bell__icon\" aria-hidden=\"true\" />\n {hasUnread ? (\n // `aria-live` para leitor de tela anunciar a mudança do contador sem o usuário reabrir\n // o menu; `polite` porque notificação nova não deve interromper o que ele está lendo.\n <span className=\"adn-bell__badge\" aria-live=\"polite\">\n {badgeLabel}\n </span>\n ) : null}\n </button>\n )\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n */\n\nimport { CheckCheck, Inbox, RotateCw } from 'lucide-react'\nimport clsx from 'clsx'\nimport type { NotificationSummary } from '@adatechnology/notification-contracts'\n\nimport { useNotificationContext } from '../NotificationProvider'\nimport { flattenNotificationPages, useMarkAllAsRead, useNotifications, useUnreadCount } from '../hooks/useNotifications'\nimport { NotificationItem } from './NotificationItem'\n\nexport type NotificationListProps = {\n readonly category?: string\n readonly onSelect?: (notification: NotificationSummary) => void\n readonly className?: string\n /** Slot: o host troca a linha sem reimplementar a lista (regra §4.3, slots e overrides). */\n readonly components?: { readonly Item?: typeof NotificationItem }\n}\n\nexport function NotificationList({ category, onSelect, className, components }: NotificationListProps) {\n const { messages, theme } = useNotificationContext()\n const query = useNotifications({ category })\n const { data: unreadCount = 0 } = useUnreadCount()\n const markAllAsRead = useMarkAllAsRead()\n\n const Item = components?.Item ?? NotificationItem\n const notifications = flattenNotificationPages(query.data?.pages)\n\n return (\n <section className={clsx('adn-list', theme.rootClassName, className)} aria-label={messages['list.title']}>\n <header className=\"adn-list__header\">\n <h2 className=\"adn-list__title\">{messages['list.title']}</h2>\n {unreadCount > 0 ? (\n <button\n type=\"button\"\n className=\"adn-list__bulk-action\"\n onClick={() => markAllAsRead.mutate()}\n disabled={markAllAsRead.isPending}\n >\n <CheckCheck className=\"adn-list__bulk-icon\" aria-hidden=\"true\" />\n {messages['list.markAllAsRead']}\n </button>\n ) : null}\n </header>\n\n {query.isPending ? <p className=\"adn-list__state\">{messages['list.loading']}</p> : null}\n\n {query.isError ? (\n <div className=\"adn-list__state adn-list__state--error\">\n <p>{messages['list.error']}</p>\n <button type=\"button\" className=\"adn-list__retry\" onClick={() => void query.refetch()}>\n <RotateCw className=\"adn-list__retry-icon\" aria-hidden=\"true\" />\n {messages['list.retry']}\n </button>\n </div>\n ) : null}\n\n {!query.isPending && !query.isError && notifications.length === 0 ? (\n <div className=\"adn-list__state adn-list__state--empty\">\n <Inbox className=\"adn-list__empty-icon\" aria-hidden=\"true\" />\n <p>{messages['list.empty']}</p>\n </div>\n ) : null}\n\n {notifications.length > 0 ? (\n <ul className=\"adn-list__items\">\n {notifications.map((notification) => (\n <Item key={notification.id} notification={notification} onSelect={onSelect} />\n ))}\n </ul>\n ) : null}\n\n {query.hasNextPage ? (\n <button\n type=\"button\"\n className=\"adn-list__load-more\"\n onClick={() => void query.fetchNextPage()}\n disabled={query.isFetchingNextPage}\n >\n {messages['list.loadMore']}\n </button>\n ) : null}\n </section>\n )\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n */\n\nimport { Check, Trash2 } from 'lucide-react'\nimport clsx from 'clsx'\nimport type { NotificationSummary } from '@adatechnology/notification-contracts'\n\nimport { useNotificationContext } from '../NotificationProvider'\nimport { useDeleteNotification, useMarkAsRead } from '../hooks/useNotifications'\n\nexport type NotificationItemProps = {\n readonly notification: NotificationSummary\n readonly onSelect?: (notification: NotificationSummary) => void\n readonly className?: string\n}\n\nexport function NotificationItem({ notification, onSelect, className }: NotificationItemProps) {\n const { messages } = useNotificationContext()\n const markAsRead = useMarkAsRead()\n const deleteNotification = useDeleteNotification()\n\n const isUnread = notification.readAt === undefined\n\n function handleSelect(): void {\n if (isUnread) markAsRead.mutate(notification.id)\n onSelect?.(notification)\n }\n\n return (\n <li className={clsx('adn-item', isUnread && 'adn-item--unread', className)}>\n {/* Semântico: a linha inteira é clicável e precisa ser alcançável por teclado — `button`\n entrega Enter, Espaço e foco de graça, e um `div` com onClick não (web.md §Acessibilidade). */}\n <button type=\"button\" className=\"adn-item__main\" onClick={handleSelect}>\n <span className=\"adn-item__title\">{notification.title}</span>\n <span className=\"adn-item__body\">{notification.body}</span>\n {isUnread ? <span className=\"adn-item__unread-dot\" aria-label={messages['item.unread']} /> : null}\n </button>\n\n <div className=\"adn-item__actions\">\n {isUnread ? (\n <button\n type=\"button\"\n className=\"adn-item__action\"\n onClick={() => markAsRead.mutate(notification.id)}\n disabled={markAsRead.isPending}\n aria-label={messages['item.markAsRead']}\n title={messages['item.markAsRead']}\n >\n <Check className=\"adn-item__action-icon\" aria-hidden=\"true\" />\n </button>\n ) : null}\n\n <button\n type=\"button\"\n className=\"adn-item__action adn-item__action--danger\"\n onClick={() => deleteNotification.mutate(notification.id)}\n disabled={deleteNotification.isPending}\n aria-label={messages['item.delete']}\n title={messages['item.delete']}\n >\n {/* Ação destrutiva: ícone reforça o peso antes do clique (regra de ícones). */}\n <Trash2 className=\"adn-item__action-icon\" aria-hidden=\"true\" />\n </button>\n </div>\n </li>\n )\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n */\n\nimport { useState } from 'react'\nimport { Save } from 'lucide-react'\nimport clsx from 'clsx'\nimport type { NotificationPreference } from '@adatechnology/notification-contracts'\n\nimport { useNotificationContext } from '../NotificationProvider'\nimport { usePreferences, useUpdatePreferences } from '../hooks/usePreferences'\n\nexport type PreferencesPanelProps = {\n readonly className?: string\n}\n\nfunction channelLabelKey(channel: string): string {\n return `channel.${channel}`\n}\n\nexport function PreferencesPanel({ className }: PreferencesPanelProps) {\n const { messages, theme } = useNotificationContext()\n const { data: saved = [], isPending } = usePreferences()\n const updatePreferences = useUpdatePreferences()\n\n // Rascunho local só existe entre abrir e salvar: `undefined` significa \"ainda não mexeu\", e é o\n // que faz o painel refletir o servidor sem sobrescrever edição em andamento a cada refetch.\n const [draft, setDraft] = useState<NotificationPreference[] | undefined>(undefined)\n const preferences = draft ?? saved\n\n function toggleChannel(target: NotificationPreference): void {\n setDraft(\n preferences.map((preference) =>\n preference.category === target.category && preference.channel === target.channel\n ? { ...preference, enabled: !preference.enabled }\n : preference,\n ),\n )\n }\n\n function handleSave(): void {\n updatePreferences.mutate(preferences, { onSuccess: () => setDraft(undefined) })\n }\n\n if (isPending) return <p className=\"adn-preferences__state\">{messages['list.loading']}</p>\n\n return (\n <section className={clsx('adn-preferences', theme.rootClassName, className)}>\n <header className=\"adn-preferences__header\">\n <h2 className=\"adn-preferences__title\">{messages['preferences.title']}</h2>\n <p className=\"adn-preferences__description\">{messages['preferences.description']}</p>\n </header>\n\n {preferences.length === 0 ? (\n <p className=\"adn-preferences__state\">{messages['preferences.empty']}</p>\n ) : (\n <ul className=\"adn-preferences__items\">\n {preferences.map((preference) => {\n const inputId = `adn-pref-${preference.category}-${preference.channel}`\n return (\n <li key={inputId} className=\"adn-preferences__item\">\n {/* `label` associado por `htmlFor`: aumenta a área de toque e é o que faz o\n leitor de tela anunciar canal e estado juntos. */}\n <label className=\"adn-preferences__label\" htmlFor={inputId}>\n <input\n id={inputId}\n type=\"checkbox\"\n className=\"adn-preferences__checkbox\"\n checked={preference.enabled}\n onChange={() => toggleChannel(preference)}\n />\n <span className=\"adn-preferences__category\">{preference.category}</span>\n <span className=\"adn-preferences__channel\">\n {messages[channelLabelKey(preference.channel) as keyof typeof messages] ?? preference.channel}\n </span>\n </label>\n\n {preference.quietHoursStart && preference.quietHoursEnd ? (\n <span className=\"adn-preferences__quiet-hours\">\n {messages['preferences.quietHours']} {preference.quietHoursStart}{' '}\n {messages['preferences.quietHoursTo']} {preference.quietHoursEnd}\n </span>\n ) : null}\n </li>\n )\n })}\n </ul>\n )}\n\n <footer className=\"adn-preferences__footer\">\n {/* Ação primária de formulário com convenção clara → ícone (regra de ícones, web.md §9). */}\n <button\n type=\"button\"\n className=\"adn-preferences__save\"\n onClick={handleSave}\n disabled={updatePreferences.isPending || draft === undefined}\n >\n <Save className=\"adn-preferences__save-icon\" aria-hidden=\"true\" />\n {messages['preferences.save']}\n </button>\n\n {updatePreferences.isSuccess && draft === undefined ? (\n <span className=\"adn-preferences__feedback\" role=\"status\">\n {messages['preferences.saved']}\n </span>\n ) : null}\n {updatePreferences.isError ? (\n <span className=\"adn-preferences__feedback adn-preferences__feedback--error\" role=\"alert\">\n {messages['preferences.error']}\n </span>\n ) : null}\n </footer>\n </section>\n )\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n *\n * A tela de notificações COMPOSTA, no molde do `ConversationsWorkspace`.\n *\n * O pacote exportava só as peças (`NotificationList`, `PreferencesPanel`), e o primeiro consumidor\n * remontou o grid à mão — 56 linhas na página da inbox e 221 na de configuração, contra 35 da página\n * de Documentos, que consome workspace. É exatamente o que a regra de módulos plugáveis (§4) rejeita:\n * cada produto remontando o layout é como as telas divergiram antes.\n *\n * Customização por contrato, nunca por fork:\n * - vocabulário → `labels`\n * - UI do produto → slots `renderHeaderActions`, `renderAboveList`, `renderEmpty`\n * - regra de negócio → callbacks (`onSelectNotification`)\n * - **capacidade por ausência**: sem `settingsHref` não aparece link de configuração, sem\n * `showPreferences` não aparece a coluna. Nunca flag `hasX`.\n */\n\nimport type { ReactNode } from 'react'\nimport type { NotificationSummary } from '@adatechnology/notification-contracts'\n\nimport { useNotificationContext } from '../NotificationProvider'\nimport { NotificationList } from './NotificationList'\nimport { PreferencesPanel } from './PreferencesPanel'\n\nexport type NotificationsWorkspaceProps = {\n /** Sobrescreve textos pontuais sem trocar o locale inteiro. */\n readonly labels?: Partial<Record<string, string>>\n /** Filtra a inbox por assunto. Ausente, mostra tudo. */\n readonly category?: string\n /**\n * Coluna de preferências ao lado da inbox.\n *\n * Ligada por padrão porque é o caso comum; um produto que gerencia preferência noutra tela passa\n * `false` e fica só com a inbox, em largura cheia.\n */\n readonly showPreferences?: boolean\n /** Ausente, o link de configuração não aparece — o produto pode não ter essa tela. */\n readonly settingsHref?: string | undefined\n /** Clique numa notificação. Ausente, a linha não é clicável. */\n readonly onSelectNotification?: (notification: NotificationSummary) => void\n /** Ações do produto no cabeçalho, ao lado do link de configuração. */\n readonly renderHeaderActions?: () => ReactNode\n /** Aviso ou filtro do produto, entre o cabeçalho e a lista. */\n readonly renderAboveList?: () => ReactNode\n /** Substitui o vazio padrão — útil quando o produto sabe explicar por que está vazio. */\n readonly renderEmpty?: () => ReactNode\n readonly className?: string\n}\n\nexport function NotificationsWorkspace({\n labels: labelsOverride,\n category,\n showPreferences = true,\n settingsHref,\n onSelectNotification,\n renderHeaderActions,\n renderAboveList,\n renderEmpty,\n className,\n}: NotificationsWorkspaceProps) {\n const { messages } = useNotificationContext()\n const label = (key: string): string => labelsOverride?.[key] ?? (messages as Record<string, string>)[key] ?? key\n\n return (\n <div className={`adn-workspace ${className ?? ''}`}>\n <header className=\"adn-workspace__header\">\n <div>\n <h1 className=\"adn-workspace__title\">{label('workspace.title')}</h1>\n <p className=\"adn-workspace__description\">{label('workspace.description')}</p>\n </div>\n\n <div className=\"adn-workspace__actions\">\n {renderHeaderActions?.()}\n {/* Capacidade por ausência: sem href, sem link. */}\n {settingsHref && (\n <a href={settingsHref} className=\"adn-button adn-button--outline\">\n {label('workspace.settingsLink')}\n </a>\n )}\n </div>\n </header>\n\n {renderAboveList?.()}\n\n <div className={showPreferences ? 'adn-workspace__grid' : 'adn-workspace__grid--single'}>\n <section className=\"adn-workspace__inbox\">\n <NotificationList\n {...(category ? { category } : {})}\n {...(onSelectNotification ? { onSelect: onSelectNotification } : {})}\n />\n {renderEmpty?.()}\n </section>\n\n {showPreferences && (\n <aside className=\"adn-workspace__aside\">\n <PreferencesPanel />\n </aside>\n )}\n </div>\n </div>\n )\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n *\n * A tela de configuração COMPOSTA: canais por assunto e editor de mensagem com preview.\n *\n * Substitui as 221 linhas de página + 207 de hook que o primeiro consumidor escreveu. O que sobra\n * para o produto é dizer quais canais e assuntos existem — o resto é layout, e layout duplicado é\n * como as telas divergem.\n *\n * `channels` e `categories` são OBRIGATÓRIOS e vêm do produto porque o pacote não tem opinião sobre\n * eles: uma loja avisa status de pedido por WhatsApp, um banco avisa vencimento por e-mail, e o\n * `hint` de cada canal (\"exige template aprovado\", \"exige aparelho registrado\") é conhecimento do\n * produto sobre a própria operação.\n */\n\nimport type { ReactNode } from 'react'\nimport type { NotificationPreference, NotificationTemplate } from '@adatechnology/notification-contracts'\n\nimport { useNotificationContext } from '../NotificationProvider'\nimport { usePreferences, useUpdatePreferences } from '../hooks/usePreferences'\nimport { useTemplateEditor } from '../hooks/useTemplateEditor'\n\nexport type NotificationChannelOption = {\n readonly id: string\n readonly label: string\n /** O custo do canal, em uma linha. É o que faz alguém decidir em vez de chutar. */\n readonly hint?: string\n}\n\nexport type NotificationCategoryOption = {\n readonly id: string\n readonly label: string\n readonly hint?: string\n}\n\nexport type NotificationSettingsWorkspaceProps = {\n readonly labels?: Partial<Record<string, string>>\n /** Os canais que ESTE produto oferece — o pacote despacha em cinco e não opina sobre quais usar. */\n readonly channels: readonly NotificationChannelOption[]\n /** Os assuntos que este produto dispara. */\n readonly categories: readonly NotificationCategoryOption[]\n /**\n * Valores de exemplo do preview, por assunto do template. Ausente, o preview mostra campo vazio —\n * que é honesto, mas menos útil para quem está escrevendo o texto.\n */\n readonly previewPayload?: Readonly<Record<string, unknown>>\n /** Traduz `order.status.preparing` no rótulo que o produto usa. Ausente, mostra a chave crua. */\n readonly templateLabelOf?: (template: NotificationTemplate) => string\n /**\n * Campo extra por canal — é onde entra o nome do template aprovado na Meta, que só o WhatsApp tem.\n * Ausente, nenhum campo extra aparece.\n */\n readonly renderChannelFields?: (params: {\n readonly channel: string\n readonly value: string\n readonly onChange: (value: string) => void\n }) => ReactNode\n /** Ações do produto no cabeçalho. */\n readonly renderHeaderActions?: () => ReactNode\n readonly className?: string\n}\n\nexport function NotificationSettingsWorkspace({\n labels: labelsOverride,\n channels,\n categories,\n previewPayload,\n templateLabelOf,\n renderChannelFields,\n renderHeaderActions,\n className,\n}: NotificationSettingsWorkspaceProps) {\n const { messages } = useNotificationContext()\n const label = (key: string): string => labelsOverride?.[key] ?? (messages as Record<string, string>)[key] ?? key\n\n const preferencesQuery = usePreferences()\n const updatePreferences = useUpdatePreferences()\n const editor = useTemplateEditor(previewPayload ? { previewPayload } : {})\n\n /**\n * Preferência ausente é o estado inicial normal — o módulo devolve só o que foi gravado. O default\n * é ligado: comportamento de fábrica é avisar, e um destinatário que não recebeu porque a linha não\n * existia é pior que um que recebeu demais.\n */\n function isEnabled(category: string, channel: string): boolean {\n const row = (preferencesQuery.data ?? []).find(\n (preference) => preference.category === category && preference.channel === channel,\n )\n return row?.enabled ?? true\n }\n\n function toggle(category: string, channel: string): void {\n // Conjunto inteiro: a rota é `PUT` em lote, e enviar só o que mudou apagaria o resto.\n const next: NotificationPreference[] = categories.flatMap((eachCategory) =>\n channels.map((eachChannel) => ({\n category: eachCategory.id,\n channel: eachChannel.id as NotificationPreference['channel'],\n enabled:\n eachCategory.id === category && eachChannel.id === channel\n ? !isEnabled(category, channel)\n : isEnabled(eachCategory.id, eachChannel.id),\n })),\n )\n updatePreferences.mutate(next)\n }\n\n return (\n <div className={`adn-settings ${className ?? ''}`}>\n <header className=\"adn-settings__header\">\n <div>\n <h1 className=\"adn-settings__title\">{label('settings.title')}</h1>\n <p className=\"adn-settings__description\">{label('settings.description')}</p>\n </div>\n {renderHeaderActions?.()}\n </header>\n\n <section className=\"adn-settings__channels\">\n <h2 className=\"adn-settings__section-title\">{label('settings.channelsTitle')}</h2>\n <p className=\"adn-settings__hint\">{label('settings.channelsHint')}</p>\n\n {categories.map((category) => (\n <div key={category.id} className=\"adn-settings__category\">\n <p className=\"adn-settings__category-label\">{category.label}</p>\n {category.hint && <p className=\"adn-settings__hint\">{category.hint}</p>}\n\n <div className=\"adn-settings__channel-grid\">\n {channels.map((channel) => (\n <label key={channel.id} className=\"adn-settings__channel\">\n <input\n type=\"checkbox\"\n checked={isEnabled(category.id, channel.id)}\n disabled={updatePreferences.isPending}\n onChange={() => toggle(category.id, channel.id)}\n />\n <span>\n <span className=\"adn-settings__channel-label\">{channel.label}</span>\n {channel.hint && <span className=\"adn-settings__hint\">{channel.hint}</span>}\n </span>\n </label>\n ))}\n </div>\n </div>\n ))}\n </section>\n\n <div className=\"adn-settings__templates\">\n <section className=\"adn-settings__template-list\">\n <header>\n <h2 className=\"adn-settings__section-title\">{label('settings.templatesTitle')}</h2>\n <p className=\"adn-settings__hint\">{label('settings.templatesHint')}</p>\n </header>\n\n {editor.isLoading && <p className=\"adn-settings__hint\">{label('list.loading')}</p>}\n {!editor.isLoading && editor.templates.length === 0 && (\n <p className=\"adn-settings__hint\">{label('settings.templatesEmpty')}</p>\n )}\n\n <ul>\n {editor.templates.map((template) => (\n <li key={template.id}>\n <button\n type=\"button\"\n onClick={() => editor.select(template)}\n className={editor.selected?.id === template.id ? 'adn-settings__row--active' : undefined}\n >\n <span className=\"adn-settings__row-main\">\n <span className=\"adn-settings__row-title\">{templateLabelOf?.(template) ?? template.key}</span>\n <span className=\"adn-settings__row-body\">{template.body}</span>\n </span>\n <span className={`adn-settings__badge adn-settings__badge--${template.channel}`}>\n {label(`channel.${template.channel}`)}\n </span>\n <span className=\"adn-settings__version\">\n {label('settings.version')}\n {template.version}\n </span>\n </button>\n </li>\n ))}\n </ul>\n </section>\n\n <section className=\"adn-settings__editor\">\n {!editor.draft && <p className=\"adn-settings__hint\">{label('settings.pickTemplate')}</p>}\n\n {editor.draft && (\n <>\n <header className=\"adn-settings__editor-header\">\n <div>\n <h2 className=\"adn-settings__section-title\">\n {editor.selected ? (templateLabelOf?.(editor.selected) ?? editor.selected.key) : ''}\n </h2>\n <p className=\"adn-settings__hint\">\n {editor.draft.channel} · {editor.draft.locale} · {label('settings.version')}\n {editor.selected?.version}\n </p>\n </div>\n <button type=\"button\" onClick={editor.clear}>\n {label('settings.close')}\n </button>\n </header>\n\n <label className=\"adn-settings__field\">\n <span className=\"adn-settings__field-label\">{label('settings.subject')}</span>\n <span className=\"adn-settings__hint\">{label('settings.subjectHint')}</span>\n <input value={editor.draft.subject} onChange={(event) => editor.update({ subject: event.target.value })} />\n </label>\n\n <label className=\"adn-settings__field\">\n <span className=\"adn-settings__field-label\">{label('settings.body')}</span>\n <textarea\n rows={4}\n value={editor.draft.body}\n onChange={(event) => editor.update({ body: event.target.value })}\n />\n </label>\n\n {/* Slot: campo que só um canal tem, e que o pacote não deve conhecer por nome. */}\n {renderChannelFields?.({\n channel: editor.draft.channel,\n value: editor.draft.whatsappTemplateName,\n onChange: (value) => editor.update({ whatsappTemplateName: value }),\n })}\n\n {editor.preview && (\n <div className=\"adn-settings__preview\">\n <p className=\"adn-settings__preview-title\">{label('settings.previewTitle')}</p>\n <p className=\"adn-settings__preview-subject\">{editor.preview.title}</p>\n <p className=\"adn-settings__preview-body\">{editor.preview.body}</p>\n </div>\n )}\n\n {editor.error && <p className=\"adn-settings__error\">{editor.error}</p>}\n\n <div className=\"adn-settings__actions\">\n <button type=\"button\" onClick={editor.save} disabled={!editor.isDirty || editor.isSaving}>\n {editor.isSaving ? label('settings.saving') : label('settings.save')}\n </button>\n {/* Diz por que está desabilitado, em vez de deixar o botão inerte sem explicação. */}\n {!editor.isDirty && <span className=\"adn-settings__hint\">{label('settings.unchanged')}</span>}\n </div>\n </>\n )}\n </section>\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["clsx","clsx","jsx","jsxs","clsx","jsx","jsxs","clsx","clsx","jsx","jsxs","clsx","jsx","jsxs","jsx","jsxs"]}
1
+ {"version":3,"sources":["../src/components/NotificationBell.tsx","../src/components/NotificationList.tsx","../src/components/NotificationItem.tsx","../src/components/PreferencesPanel.tsx","../src/components/NotificationsWorkspace.tsx","../src/components/NotificationSettingsWorkspace.tsx"],"sourcesContent":["/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n */\n\nimport { Bell } from 'lucide-react'\nimport clsx from 'clsx'\n\nimport { useNotificationContext } from '../NotificationProvider'\nimport { useUnreadCount } from '../hooks/useNotifications'\n\nexport type NotificationBellProps = {\n readonly onClick?: () => void\n readonly className?: string\n /** Acima disso o badge mostra `99+`; um número de 4 dígitos estoura o desenho do badge. */\n readonly maxBadgeCount?: number\n}\n\nconst DEFAULT_MAX_BADGE_COUNT = 99\n\nexport function NotificationBell({\n onClick,\n className,\n maxBadgeCount = DEFAULT_MAX_BADGE_COUNT,\n}: NotificationBellProps) {\n const { messages, theme } = useNotificationContext()\n const { data: unreadCount = 0 } = useUnreadCount()\n\n const hasUnread = unreadCount > 0\n const badgeLabel = unreadCount > maxBadgeCount ? `${maxBadgeCount}+` : String(unreadCount)\n const accessibleLabel = hasUnread\n ? `${messages['bell.label']}: ${unreadCount} ${messages['bell.unreadSuffix']}`\n : messages['bell.label']\n\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className={clsx('adn-bell', theme.rootClassName, className)}\n // Botão só-ícone exige rótulo acessível (regra de ícones, `web.md` §9).\n aria-label={accessibleLabel}\n >\n {/* Decorativo: o rótulo acessível já está no botão, e anunciar o ícone repetiria a ação. */}\n <Bell className=\"adn-bell__icon\" aria-hidden=\"true\" />\n {hasUnread ? (\n // `aria-live` para leitor de tela anunciar a mudança do contador sem o usuário reabrir\n // o menu; `polite` porque notificação nova não deve interromper o que ele está lendo.\n <span className=\"adn-bell__badge\" aria-live=\"polite\">\n {badgeLabel}\n </span>\n ) : null}\n </button>\n )\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n */\n\nimport { CheckCheck, Inbox, RotateCw } from 'lucide-react'\nimport clsx from 'clsx'\nimport type { ReactNode } from 'react'\nimport type { NotificationSummary } from '@adatechnology/notification-contracts'\n\nimport { useNotificationContext } from '../NotificationProvider'\nimport { flattenNotificationPages, useMarkAllAsRead, useNotifications, useUnreadCount } from '../hooks/useNotifications'\nimport { NotificationItem } from './NotificationItem'\n\n/** Três linhas: o bastante para a lista já ter a forma dela antes de ter conteúdo. */\nconst SKELETON_ROWS = [0, 1, 2]\n\nexport type NotificationListProps = {\n readonly category?: string\n readonly onSelect?: (notification: NotificationSummary) => void\n readonly className?: string\n /**\n * Substitui o vazio padrão. Quem decide se está vazio é a lista — o host que desenhava o próprio\n * vazio ao lado dela mostrava \"nada por aqui\" embaixo das notificações que estavam ali.\n */\n readonly renderEmpty?: () => ReactNode\n /** Slot: o host troca a linha sem reimplementar a lista (regra §4.3, slots e overrides). */\n readonly components?: { readonly Item?: typeof NotificationItem }\n}\n\nexport function NotificationList({ category, onSelect, className, renderEmpty, components }: NotificationListProps) {\n const { messages, theme } = useNotificationContext()\n const query = useNotifications({ category })\n const { data: unreadCount = 0 } = useUnreadCount()\n const markAllAsRead = useMarkAllAsRead()\n\n const Item = components?.Item ?? NotificationItem\n const notifications = flattenNotificationPages(query.data?.pages)\n const isEmpty = !query.isPending && !query.isError && notifications.length === 0\n\n return (\n <section className={clsx('adn-list', theme.rootClassName, className)} aria-label={messages['list.title']}>\n <header className=\"adn-list__header\">\n <div className=\"adn-list__heading\">\n <h2 className=\"adn-list__title\">{messages['list.title']}</h2>\n {/* O contador estava só no sino. Quem abriu a tela pelo menu lateral não passou pelo sino,\n e ficava sem saber quantas ainda pedem atenção. */}\n {unreadCount > 0 ? (\n <span className=\"adn-list__count\" role=\"status\" aria-label={`${unreadCount} ${messages['list.unread']}`}>\n {unreadCount}\n </span>\n ) : null}\n </div>\n\n {unreadCount > 0 ? (\n <button\n type=\"button\"\n className=\"adn-list__bulk-action\"\n onClick={() => markAllAsRead.mutate()}\n disabled={markAllAsRead.isPending}\n >\n <CheckCheck className=\"adn-list__bulk-icon\" aria-hidden=\"true\" />\n {messages['list.markAllAsRead']}\n </button>\n ) : null}\n </header>\n\n {/* Esqueleto com a forma da linha real, em vez de \"Carregando…\" centralizado: o texto some e o\n conteúdo entra de altura diferente, e a tela pisca a cada carregamento. */}\n {query.isPending ? (\n <div className=\"adn-list__skeleton\" role=\"status\" aria-label={messages['list.loading']}>\n {SKELETON_ROWS.map((row) => (\n <span key={row} className=\"adn-list__skeleton-row\" />\n ))}\n </div>\n ) : null}\n\n {query.isError ? (\n <div className=\"adn-list__state adn-list__state--error\" role=\"alert\">\n <p>{messages['list.error']}</p>\n <button type=\"button\" className=\"adn-list__retry\" onClick={() => void query.refetch()}>\n <RotateCw className=\"adn-list__retry-icon\" aria-hidden=\"true\" />\n {messages['list.retry']}\n </button>\n </div>\n ) : null}\n\n {isEmpty\n ? (renderEmpty?.() ?? (\n <div className=\"adn-list__state adn-list__state--empty\">\n <Inbox className=\"adn-list__empty-icon\" aria-hidden=\"true\" />\n <p>{messages['list.empty']}</p>\n </div>\n ))\n : null}\n\n {notifications.length > 0 ? (\n <ul className=\"adn-list__items\">\n {notifications.map((notification) => (\n <Item key={notification.id} notification={notification} onSelect={onSelect} />\n ))}\n </ul>\n ) : null}\n\n {query.hasNextPage ? (\n <div className=\"adn-list__footer\">\n <button\n type=\"button\"\n className=\"adn-list__load-more\"\n onClick={() => void query.fetchNextPage()}\n disabled={query.isFetchingNextPage}\n >\n {messages['list.loadMore']}\n </button>\n </div>\n ) : null}\n </section>\n )\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n */\n\nimport { Check, Trash2 } from 'lucide-react'\nimport clsx from 'clsx'\nimport type { NotificationSummary } from '@adatechnology/notification-contracts'\n\nimport { useNotificationContext } from '../NotificationProvider'\nimport { formatNotificationTime, formatNotificationTimestamp } from '../format/notificationTime'\nimport { useDeleteNotification, useMarkAsRead } from '../hooks/useNotifications'\n\nexport type NotificationItemProps = {\n readonly notification: NotificationSummary\n readonly onSelect?: (notification: NotificationSummary) => void\n readonly className?: string\n}\n\nexport function NotificationItem({ notification, onSelect, className }: NotificationItemProps) {\n const { locale, messages } = useNotificationContext()\n const markAsRead = useMarkAsRead()\n const deleteNotification = useDeleteNotification()\n\n const isUnread = notification.readAt === undefined\n\n function handleSelect(): void {\n if (isUnread) markAsRead.mutate(notification.id)\n onSelect?.(notification)\n }\n\n return (\n <li className={clsx('adn-item', isUnread && 'adn-item--unread', className)}>\n {/* Semântico: a linha inteira é clicável e precisa ser alcançável por teclado — `button`\n entrega Enter, Espaço e foco de graça, e um `div` com onClick não (web.md §Acessibilidade). */}\n <button type=\"button\" className=\"adn-item__main\" onClick={handleSelect}>\n <span className=\"adn-item__heading\">\n {/* Marcador à esquerda, na altura do título: sobreposto ao canto, ele cobria a última\n palavra do texto em linha comprida. */}\n {isUnread ? <span className=\"adn-item__unread-dot\" aria-label={messages['item.unread']} /> : null}\n <span className=\"adn-item__title\">{notification.title}</span>\n {/* `<time>` e não `<span>`: a data legível por máquina é o que um leitor de tela e um\n agregador conseguem interpretar; o texto visível é o relativo, que é o que se lê. */}\n <time\n className=\"adn-item__time\"\n dateTime={notification.createdAt}\n title={formatNotificationTimestamp(notification.createdAt, locale)}\n >\n {formatNotificationTime(notification.createdAt, locale, new Date())}\n </time>\n </span>\n <span className=\"adn-item__body\">{notification.body}</span>\n </button>\n\n <div className=\"adn-item__actions\">\n {isUnread ? (\n <button\n type=\"button\"\n className=\"adn-item__action\"\n onClick={() => markAsRead.mutate(notification.id)}\n disabled={markAsRead.isPending}\n aria-label={messages['item.markAsRead']}\n title={messages['item.markAsRead']}\n >\n <Check className=\"adn-item__action-icon\" aria-hidden=\"true\" />\n </button>\n ) : null}\n\n <button\n type=\"button\"\n className=\"adn-item__action adn-item__action--danger\"\n onClick={() => deleteNotification.mutate(notification.id)}\n disabled={deleteNotification.isPending}\n aria-label={messages['item.delete']}\n title={messages['item.delete']}\n >\n {/* Ação destrutiva: ícone reforça o peso antes do clique (regra de ícones). */}\n <Trash2 className=\"adn-item__action-icon\" aria-hidden=\"true\" />\n </button>\n </div>\n </li>\n )\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n */\n\nimport { useState } from 'react'\nimport { Save } from 'lucide-react'\nimport clsx from 'clsx'\nimport type { NotificationPreference } from '@adatechnology/notification-contracts'\n\nimport { useNotificationContext } from '../NotificationProvider'\nimport { usePreferences, useUpdatePreferences } from '../hooks/usePreferences'\n\nexport type PreferencesPanelProps = {\n readonly className?: string\n}\n\nfunction channelLabelKey(channel: string): string {\n return `channel.${channel}`\n}\n\nexport function PreferencesPanel({ className }: PreferencesPanelProps) {\n const { messages, theme } = useNotificationContext()\n const { data: saved = [], isPending } = usePreferences()\n const updatePreferences = useUpdatePreferences()\n\n // Rascunho local só existe entre abrir e salvar: `undefined` significa \"ainda não mexeu\", e é o\n // que faz o painel refletir o servidor sem sobrescrever edição em andamento a cada refetch.\n const [draft, setDraft] = useState<NotificationPreference[] | undefined>(undefined)\n const preferences = draft ?? saved\n\n function toggleChannel(target: NotificationPreference): void {\n setDraft(\n preferences.map((preference) =>\n preference.category === target.category && preference.channel === target.channel\n ? { ...preference, enabled: !preference.enabled }\n : preference,\n ),\n )\n }\n\n function handleSave(): void {\n updatePreferences.mutate(preferences, { onSuccess: () => setDraft(undefined) })\n }\n\n if (isPending) return <p className=\"adn-preferences__state\">{messages['list.loading']}</p>\n\n return (\n <section className={clsx('adn-preferences', theme.rootClassName, className)}>\n <header className=\"adn-preferences__header\">\n <h2 className=\"adn-preferences__title\">{messages['preferences.title']}</h2>\n <p className=\"adn-preferences__description\">{messages['preferences.description']}</p>\n </header>\n\n {preferences.length === 0 ? (\n <p className=\"adn-preferences__state\">{messages['preferences.empty']}</p>\n ) : (\n <ul className=\"adn-preferences__items\">\n {preferences.map((preference) => {\n const inputId = `adn-pref-${preference.category}-${preference.channel}`\n return (\n <li key={inputId} className=\"adn-preferences__item\">\n {/* `label` associado por `htmlFor`: aumenta a área de toque e é o que faz o\n leitor de tela anunciar canal e estado juntos. */}\n <label className=\"adn-preferences__label\" htmlFor={inputId}>\n <input\n id={inputId}\n type=\"checkbox\"\n className=\"adn-preferences__checkbox\"\n checked={preference.enabled}\n onChange={() => toggleChannel(preference)}\n />\n <span className=\"adn-preferences__category\">{preference.category}</span>\n <span className=\"adn-preferences__channel\">\n {messages[channelLabelKey(preference.channel) as keyof typeof messages] ?? preference.channel}\n </span>\n </label>\n\n {preference.quietHoursStart && preference.quietHoursEnd ? (\n <span className=\"adn-preferences__quiet-hours\">\n {messages['preferences.quietHours']} {preference.quietHoursStart}{' '}\n {messages['preferences.quietHoursTo']} {preference.quietHoursEnd}\n </span>\n ) : null}\n </li>\n )\n })}\n </ul>\n )}\n\n <footer className=\"adn-preferences__footer\">\n {/* Ação primária de formulário com convenção clara → ícone (regra de ícones, web.md §9). */}\n <button\n type=\"button\"\n className=\"adn-preferences__save\"\n onClick={handleSave}\n disabled={updatePreferences.isPending || draft === undefined}\n >\n <Save className=\"adn-preferences__save-icon\" aria-hidden=\"true\" />\n {messages['preferences.save']}\n </button>\n\n {updatePreferences.isSuccess && draft === undefined ? (\n <span className=\"adn-preferences__feedback\" role=\"status\">\n {messages['preferences.saved']}\n </span>\n ) : null}\n {updatePreferences.isError ? (\n <span className=\"adn-preferences__feedback adn-preferences__feedback--error\" role=\"alert\">\n {messages['preferences.error']}\n </span>\n ) : null}\n </footer>\n </section>\n )\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n *\n * A tela de notificações COMPOSTA, no molde do `ConversationsWorkspace`.\n *\n * O pacote exportava só as peças (`NotificationList`, `PreferencesPanel`), e o primeiro consumidor\n * remontou o grid à mão — 56 linhas na página da inbox e 221 na de configuração, contra 35 da página\n * de Documentos, que consome workspace. É exatamente o que a regra de módulos plugáveis (§4) rejeita:\n * cada produto remontando o layout é como as telas divergiram antes.\n *\n * Customização por contrato, nunca por fork:\n * - vocabulário → `labels`\n * - UI do produto → slots `renderHeaderActions`, `renderAboveList`, `renderEmpty`\n * - regra de negócio → callbacks (`onSelectNotification`)\n * - **capacidade por ausência**: sem `settingsHref` não aparece link de configuração, sem\n * `showPreferences` não aparece a coluna. Nunca flag `hasX`.\n */\n\nimport type { ReactNode } from 'react'\nimport type { NotificationSummary } from '@adatechnology/notification-contracts'\n\nimport { useNotificationContext } from '../NotificationProvider'\nimport { NotificationList } from './NotificationList'\nimport { PreferencesPanel } from './PreferencesPanel'\n\nexport type NotificationsWorkspaceProps = {\n /** Sobrescreve textos pontuais sem trocar o locale inteiro. */\n readonly labels?: Partial<Record<string, string>>\n /** Filtra a inbox por assunto. Ausente, mostra tudo. */\n readonly category?: string\n /**\n * Coluna de preferências ao lado da inbox.\n *\n * Ligada por padrão porque é o caso comum; um produto que gerencia preferência noutra tela passa\n * `false` e fica só com a inbox, em largura cheia.\n */\n readonly showPreferences?: boolean\n /** Ausente, o link de configuração não aparece — o produto pode não ter essa tela. */\n readonly settingsHref?: string | undefined\n /** Clique numa notificação. Ausente, a linha não é clicável. */\n readonly onSelectNotification?: (notification: NotificationSummary) => void\n /**\n * Substitui o cabeçalho padrão inteiro.\n *\n * O workspace desenha um `<h1>` porque ele é a tela; a página que já tem o título dela passava a\n * ter dois `<h1>` dizendo a mesma palavra, e para leitor de tela duas primeiras manchetes é o\n * mesmo que nenhuma. Ausente, o padrão continua desenhando — slot é substituição, não interruptor.\n */\n readonly renderHeader?: () => ReactNode\n /** Ações do produto no cabeçalho, ao lado do link de configuração. */\n readonly renderHeaderActions?: () => ReactNode\n /** Aviso ou filtro do produto, entre o cabeçalho e a lista. */\n readonly renderAboveList?: () => ReactNode\n /** Substitui o vazio padrão — útil quando o produto sabe explicar por que está vazio. */\n readonly renderEmpty?: () => ReactNode\n readonly className?: string\n}\n\nexport function NotificationsWorkspace({\n labels: labelsOverride,\n category,\n showPreferences = true,\n settingsHref,\n onSelectNotification,\n renderHeader,\n renderHeaderActions,\n renderAboveList,\n renderEmpty,\n className,\n}: NotificationsWorkspaceProps) {\n const { messages } = useNotificationContext()\n const label = (key: string): string => labelsOverride?.[key] ?? (messages as Record<string, string>)[key] ?? key\n\n return (\n <div className={`adn-workspace ${className ?? ''}`}>\n {renderHeader ? (\n renderHeader()\n ) : (\n <header className=\"adn-workspace__header\">\n <div>\n <h1 className=\"adn-workspace__title\">{label('workspace.title')}</h1>\n <p className=\"adn-workspace__description\">{label('workspace.description')}</p>\n </div>\n\n <div className=\"adn-workspace__actions\">\n {renderHeaderActions?.()}\n {/* Capacidade por ausência: sem href, sem link. */}\n {settingsHref && (\n <a href={settingsHref} className=\"adn-button adn-button--outline\">\n {label('workspace.settingsLink')}\n </a>\n )}\n </div>\n </header>\n )}\n\n {renderAboveList?.()}\n\n <div className={showPreferences ? 'adn-workspace__grid' : 'adn-workspace__grid--single'}>\n <section className=\"adn-workspace__inbox\">\n {/* O vazio desce para a lista, que é quem sabe se está vazia. Desenhado aqui, ele aparecia\n ao lado das notificações — \"nada por aqui\" embaixo do que estava ali. */}\n <NotificationList\n {...(category ? { category } : {})}\n {...(onSelectNotification ? { onSelect: onSelectNotification } : {})}\n {...(renderEmpty ? { renderEmpty } : {})}\n />\n </section>\n\n {showPreferences && (\n <aside className=\"adn-workspace__aside\">\n <PreferencesPanel />\n </aside>\n )}\n </div>\n </div>\n )\n}\n","/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n *\n * A tela de configuração COMPOSTA: canais por assunto e editor de mensagem com preview.\n *\n * Substitui as 221 linhas de página + 207 de hook que o primeiro consumidor escreveu. O que sobra\n * para o produto é dizer quais canais e assuntos existem — o resto é layout, e layout duplicado é\n * como as telas divergem.\n *\n * `channels` e `categories` são OBRIGATÓRIOS e vêm do produto porque o pacote não tem opinião sobre\n * eles: uma loja avisa status de pedido por WhatsApp, um banco avisa vencimento por e-mail, e o\n * `hint` de cada canal (\"exige template aprovado\", \"exige aparelho registrado\") é conhecimento do\n * produto sobre a própria operação.\n */\n\nimport type { ReactNode } from 'react'\nimport type { NotificationPreference, NotificationTemplate } from '@adatechnology/notification-contracts'\n\nimport { useNotificationContext } from '../NotificationProvider'\nimport { usePreferences, useUpdatePreferences } from '../hooks/usePreferences'\nimport { useTemplateEditor } from '../hooks/useTemplateEditor'\n\nexport type NotificationChannelOption = {\n readonly id: string\n readonly label: string\n /** O custo do canal, em uma linha. É o que faz alguém decidir em vez de chutar. */\n readonly hint?: string\n}\n\nexport type NotificationCategoryOption = {\n readonly id: string\n readonly label: string\n readonly hint?: string\n}\n\nexport type NotificationSettingsWorkspaceProps = {\n readonly labels?: Partial<Record<string, string>>\n /** Os canais que ESTE produto oferece — o pacote despacha em cinco e não opina sobre quais usar. */\n readonly channels: readonly NotificationChannelOption[]\n /** Os assuntos que este produto dispara. */\n readonly categories: readonly NotificationCategoryOption[]\n /**\n * Valores de exemplo do preview, por assunto do template. Ausente, o preview mostra campo vazio —\n * que é honesto, mas menos útil para quem está escrevendo o texto.\n */\n readonly previewPayload?: Readonly<Record<string, unknown>>\n /** Traduz `order.status.preparing` no rótulo que o produto usa. Ausente, mostra a chave crua. */\n readonly templateLabelOf?: (template: NotificationTemplate) => string\n /**\n * Campo extra por canal — é onde entra o nome do template aprovado na Meta, que só o WhatsApp tem.\n * Ausente, nenhum campo extra aparece.\n */\n readonly renderChannelFields?: (params: {\n readonly channel: string\n readonly value: string\n readonly onChange: (value: string) => void\n }) => ReactNode\n /** Substitui o cabeçalho padrão — a página que já tem título próprio evita o segundo `<h1>`. */\n readonly renderHeader?: () => ReactNode\n /** Ações do produto no cabeçalho. */\n readonly renderHeaderActions?: () => ReactNode\n readonly className?: string\n}\n\nexport function NotificationSettingsWorkspace({\n labels: labelsOverride,\n channels,\n categories,\n previewPayload,\n templateLabelOf,\n renderChannelFields,\n renderHeader,\n renderHeaderActions,\n className,\n}: NotificationSettingsWorkspaceProps) {\n const { messages } = useNotificationContext()\n const label = (key: string): string => labelsOverride?.[key] ?? (messages as Record<string, string>)[key] ?? key\n\n const preferencesQuery = usePreferences()\n const updatePreferences = useUpdatePreferences()\n const editor = useTemplateEditor(previewPayload ? { previewPayload } : {})\n\n /**\n * Preferência ausente é o estado inicial normal — o módulo devolve só o que foi gravado. O default\n * é ligado: comportamento de fábrica é avisar, e um destinatário que não recebeu porque a linha não\n * existia é pior que um que recebeu demais.\n */\n function isEnabled(category: string, channel: string): boolean {\n const row = (preferencesQuery.data ?? []).find(\n (preference) => preference.category === category && preference.channel === channel,\n )\n return row?.enabled ?? true\n }\n\n function toggle(category: string, channel: string): void {\n // Conjunto inteiro: a rota é `PUT` em lote, e enviar só o que mudou apagaria o resto.\n const next: NotificationPreference[] = categories.flatMap((eachCategory) =>\n channels.map((eachChannel) => ({\n category: eachCategory.id,\n channel: eachChannel.id as NotificationPreference['channel'],\n enabled:\n eachCategory.id === category && eachChannel.id === channel\n ? !isEnabled(category, channel)\n : isEnabled(eachCategory.id, eachChannel.id),\n })),\n )\n updatePreferences.mutate(next)\n }\n\n return (\n <div className={`adn-settings ${className ?? ''}`}>\n {renderHeader ? (\n renderHeader()\n ) : (\n <header className=\"adn-settings__header\">\n <div>\n <h1 className=\"adn-settings__title\">{label('settings.title')}</h1>\n <p className=\"adn-settings__description\">{label('settings.description')}</p>\n </div>\n {renderHeaderActions?.()}\n </header>\n )}\n\n <section className=\"adn-settings__channels\">\n <h2 className=\"adn-settings__section-title\">{label('settings.channelsTitle')}</h2>\n <p className=\"adn-settings__hint\">{label('settings.channelsHint')}</p>\n\n {categories.map((category) => (\n <div key={category.id} className=\"adn-settings__category\">\n <p className=\"adn-settings__category-label\">{category.label}</p>\n {category.hint && <p className=\"adn-settings__hint\">{category.hint}</p>}\n\n <div className=\"adn-settings__channel-grid\">\n {channels.map((channel) => (\n <label key={channel.id} className=\"adn-settings__channel\">\n <input\n type=\"checkbox\"\n checked={isEnabled(category.id, channel.id)}\n disabled={updatePreferences.isPending}\n onChange={() => toggle(category.id, channel.id)}\n />\n <span>\n <span className=\"adn-settings__channel-label\">{channel.label}</span>\n {channel.hint && <span className=\"adn-settings__hint\">{channel.hint}</span>}\n </span>\n </label>\n ))}\n </div>\n </div>\n ))}\n </section>\n\n <div className=\"adn-settings__templates\">\n <section className=\"adn-settings__template-list\">\n <header>\n <h2 className=\"adn-settings__section-title\">{label('settings.templatesTitle')}</h2>\n <p className=\"adn-settings__hint\">{label('settings.templatesHint')}</p>\n </header>\n\n {editor.isLoading && <p className=\"adn-settings__hint\">{label('list.loading')}</p>}\n {!editor.isLoading && editor.templates.length === 0 && (\n <p className=\"adn-settings__hint\">{label('settings.templatesEmpty')}</p>\n )}\n\n <ul>\n {editor.templates.map((template) => (\n <li key={template.id}>\n <button\n type=\"button\"\n onClick={() => editor.select(template)}\n className={editor.selected?.id === template.id ? 'adn-settings__row--active' : undefined}\n >\n <span className=\"adn-settings__row-main\">\n <span className=\"adn-settings__row-title\">{templateLabelOf?.(template) ?? template.key}</span>\n <span className=\"adn-settings__row-body\">{template.body}</span>\n </span>\n <span className={`adn-settings__badge adn-settings__badge--${template.channel}`}>\n {label(`channel.${template.channel}`)}\n </span>\n <span className=\"adn-settings__version\">\n {label('settings.version')}\n {template.version}\n </span>\n </button>\n </li>\n ))}\n </ul>\n </section>\n\n <section className=\"adn-settings__editor\">\n {!editor.draft && <p className=\"adn-settings__hint\">{label('settings.pickTemplate')}</p>}\n\n {editor.draft && (\n <>\n <header className=\"adn-settings__editor-header\">\n <div>\n <h2 className=\"adn-settings__section-title\">\n {editor.selected ? (templateLabelOf?.(editor.selected) ?? editor.selected.key) : ''}\n </h2>\n <p className=\"adn-settings__hint\">\n {editor.draft.channel} · {editor.draft.locale} · {label('settings.version')}\n {editor.selected?.version}\n </p>\n </div>\n <button type=\"button\" onClick={editor.clear}>\n {label('settings.close')}\n </button>\n </header>\n\n <label className=\"adn-settings__field\">\n <span className=\"adn-settings__field-label\">{label('settings.subject')}</span>\n <span className=\"adn-settings__hint\">{label('settings.subjectHint')}</span>\n <input\n value={editor.draft.subject}\n onChange={(event) => editor.update({ subject: event.target.value })}\n />\n </label>\n\n <label className=\"adn-settings__field\">\n <span className=\"adn-settings__field-label\">{label('settings.body')}</span>\n <textarea\n rows={4}\n value={editor.draft.body}\n onChange={(event) => editor.update({ body: event.target.value })}\n />\n </label>\n\n {/* Slot: campo que só um canal tem, e que o pacote não deve conhecer por nome. */}\n {renderChannelFields?.({\n channel: editor.draft.channel,\n value: editor.draft.whatsappTemplateName,\n onChange: (value) => editor.update({ whatsappTemplateName: value }),\n })}\n\n {editor.preview && (\n <div className=\"adn-settings__preview\">\n <p className=\"adn-settings__preview-title\">{label('settings.previewTitle')}</p>\n <p className=\"adn-settings__preview-subject\">{editor.preview.title}</p>\n <p className=\"adn-settings__preview-body\">{editor.preview.body}</p>\n </div>\n )}\n\n {editor.error && <p className=\"adn-settings__error\">{editor.error}</p>}\n\n <div className=\"adn-settings__actions\">\n <button type=\"button\" onClick={editor.save} disabled={!editor.isDirty || editor.isSaving}>\n {editor.isSaving ? label('settings.saving') : label('settings.save')}\n </button>\n {/* Diz por que está desabilitado, em vez de deixar o botão inerte sem explicação. */}\n {!editor.isDirty && <span className=\"adn-settings__hint\">{label('settings.unchanged')}</span>}\n </div>\n </>\n )}\n </section>\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["clsx","clsx","jsx","jsxs","clsx","jsx","jsxs","clsx","clsx","jsx","jsxs","clsx","jsx","jsxs","jsx","jsxs"]}
package/dist/styles.css CHANGED
@@ -67,9 +67,10 @@
67
67
  .adn-list {
68
68
  display: flex;
69
69
  flex-direction: column;
70
- gap: var(--adn-space-2);
71
70
  background: var(--adn-color-surface);
71
+ border: 1px solid var(--adn-color-border);
72
72
  border-radius: var(--adn-radius);
73
+ overflow: hidden;
73
74
  }
74
75
  .adn-list__header {
75
76
  display: flex;
@@ -80,11 +81,28 @@
80
81
  padding: var(--adn-space-3);
81
82
  border-bottom: 1px solid var(--adn-color-border);
82
83
  }
84
+ .adn-list__heading {
85
+ display: flex;
86
+ align-items: center;
87
+ gap: var(--adn-space-2);
88
+ }
83
89
  .adn-list__title {
84
90
  margin: 0;
85
91
  font-size: 1rem;
86
92
  font-weight: 600;
87
93
  }
94
+ .adn-list__count {
95
+ min-width: 20px;
96
+ padding: 0 var(--adn-space-1);
97
+ border-radius: 9999px;
98
+ background: color-mix(in srgb, var(--adn-color-accent) 16%, transparent);
99
+ color: var(--adn-color-accent);
100
+ font-size: 0.75rem;
101
+ font-weight: 600;
102
+ line-height: 20px;
103
+ text-align: center;
104
+ font-variant-numeric: tabular-nums;
105
+ }
88
106
  .adn-list__bulk-action,
89
107
  .adn-list__retry,
90
108
  .adn-list__load-more {
@@ -114,6 +132,38 @@
114
132
  height: 16px;
115
133
  color: currentColor;
116
134
  }
135
+ .adn-list__skeleton {
136
+ display: flex;
137
+ flex-direction: column;
138
+ gap: var(--adn-space-3);
139
+ padding: var(--adn-space-3);
140
+ }
141
+ .adn-list__skeleton-row {
142
+ height: var(--adn-touch-target);
143
+ border-radius: var(--adn-radius);
144
+ background:
145
+ linear-gradient(
146
+ 90deg,
147
+ color-mix(in srgb, var(--adn-color-muted) 10%, transparent),
148
+ color-mix(in srgb, var(--adn-color-muted) 22%, transparent),
149
+ color-mix(in srgb, var(--adn-color-muted) 10%, transparent));
150
+ background-size: 200% 100%;
151
+ animation: adn-skeleton 1.4s ease-in-out infinite;
152
+ }
153
+ @keyframes adn-skeleton {
154
+ from {
155
+ background-position: 200% 0;
156
+ }
157
+ to {
158
+ background-position: -200% 0;
159
+ }
160
+ }
161
+ .adn-list__footer {
162
+ display: flex;
163
+ justify-content: center;
164
+ padding: var(--adn-space-3);
165
+ border-top: 1px solid var(--adn-color-border);
166
+ }
117
167
  .adn-list__items {
118
168
  display: flex;
119
169
  flex-direction: column;
@@ -146,9 +196,18 @@
146
196
  gap: var(--adn-space-2);
147
197
  border-bottom: 1px solid var(--adn-color-border);
148
198
  }
199
+ .adn-item:last-child {
200
+ border-bottom: none;
201
+ }
202
+ .adn-item:hover {
203
+ background: color-mix(in srgb, var(--adn-color-muted) 8%, transparent);
204
+ }
149
205
  .adn-item--unread {
150
206
  background: color-mix(in srgb, var(--adn-color-accent) 6%, transparent);
151
207
  }
208
+ .adn-item--unread:hover {
209
+ background: color-mix(in srgb, var(--adn-color-accent) 12%, transparent);
210
+ }
152
211
  .adn-item__main {
153
212
  position: relative;
154
213
  display: flex;
@@ -164,17 +223,37 @@
164
223
  color: inherit;
165
224
  text-align: left;
166
225
  }
226
+ .adn-item__heading {
227
+ display: flex;
228
+ align-items: baseline;
229
+ gap: var(--adn-space-2);
230
+ width: 100%;
231
+ }
167
232
  .adn-item__title {
233
+ flex: 1;
234
+ min-width: 0;
168
235
  font-weight: 600;
236
+ overflow: hidden;
237
+ text-overflow: ellipsis;
238
+ white-space: nowrap;
239
+ }
240
+ .adn-item__time {
241
+ flex-shrink: 0;
242
+ color: var(--adn-color-muted);
243
+ font-size: 0.75rem;
244
+ font-variant-numeric: tabular-nums;
169
245
  }
170
246
  .adn-item__body {
171
247
  color: var(--adn-color-muted);
172
248
  font-size: 0.875rem;
249
+ display: -webkit-box;
250
+ -webkit-box-orient: vertical;
251
+ -webkit-line-clamp: 2;
252
+ overflow: hidden;
173
253
  }
174
254
  .adn-item__unread-dot {
175
- position: absolute;
176
- top: var(--adn-space-3);
177
- right: var(--adn-space-2);
255
+ flex-shrink: 0;
256
+ align-self: center;
178
257
  width: 8px;
179
258
  height: 8px;
180
259
  background: var(--adn-color-accent);
@@ -311,6 +390,9 @@
311
390
  .adn-list__bulk-action {
312
391
  transition: none;
313
392
  }
393
+ .adn-list__skeleton-row {
394
+ animation: none;
395
+ }
314
396
  }
315
397
  .adn-workspace,
316
398
  .adn-settings {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/styles.css"],"sourcesContent":["/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n *\n * O pacote não tem cor própria (regra de módulos plugáveis §4.1): tudo sai de custom properties\n * com fallback neutro. O host sobrescreve `--adn-*` no escopo dele e a mesma tela ganha a cara do\n * produto — sem fork, sem prop de cor, sem valor hexadecimal aqui dentro que não seja fallback.\n */\n\n.adn-bell,\n.adn-list,\n.adn-preferences,\n.adn-workspace,\n.adn-settings {\n --adn-color-surface: var(--adn-surface, #ffffff);\n --adn-color-text: var(--adn-text, #111827);\n --adn-color-muted: var(--adn-muted, #6b7280);\n --adn-color-accent: var(--adn-accent, #3b82f6);\n --adn-color-danger: var(--adn-danger, #dc2626);\n --adn-color-border: var(--adn-border, #e5e7eb);\n --adn-radius: var(--adn-radius-md, 8px);\n /* Grade base de 4px (web.md §8) — múltiplos, nunca pixel mágico. */\n --adn-space-1: 4px;\n --adn-space-2: 8px;\n --adn-space-3: 12px;\n --adn-space-4: 16px;\n /* 44px: alvo mínimo de toque em mobile (web.md §10). */\n --adn-touch-target: 44px;\n\n color: var(--adn-color-text);\n font-family: inherit;\n}\n\n/* ── Sino ─────────────────────────────────────────────────────────────────────── */\n\n.adn-bell {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--adn-touch-target);\n min-height: var(--adn-touch-target);\n padding: var(--adn-space-2);\n background: transparent;\n border: none;\n border-radius: var(--adn-radius);\n cursor: pointer;\n color: inherit;\n}\n\n.adn-bell:hover {\n background: var(--adn-color-border);\n}\n\n/* Foco visível é requisito de teclado — remover outline sem repor indicador quebra navegação. */\n.adn-bell:focus-visible,\n.adn-list__bulk-action:focus-visible,\n.adn-list__retry:focus-visible,\n.adn-list__load-more:focus-visible,\n.adn-item__main:focus-visible,\n.adn-item__action:focus-visible,\n.adn-preferences__save:focus-visible {\n outline: 2px solid var(--adn-color-accent);\n outline-offset: 2px;\n}\n\n.adn-bell__icon {\n width: 20px;\n height: 20px;\n /* Herda a cor do botão, para acompanhar hover e estado desabilitado sem regra extra. */\n color: currentColor;\n}\n\n.adn-bell__badge {\n position: absolute;\n top: var(--adn-space-1);\n right: var(--adn-space-1);\n min-width: 18px;\n padding: 0 var(--adn-space-1);\n background: var(--adn-color-danger);\n color: #ffffff;\n border-radius: 9999px;\n font-size: 11px;\n line-height: 18px;\n text-align: center;\n}\n\n/* ── Lista ────────────────────────────────────────────────────────────────────── */\n\n.adn-list {\n display: flex;\n flex-direction: column;\n gap: var(--adn-space-2);\n background: var(--adn-color-surface);\n border-radius: var(--adn-radius);\n}\n\n.adn-list__header {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--adn-space-2);\n padding: var(--adn-space-3);\n border-bottom: 1px solid var(--adn-color-border);\n}\n\n.adn-list__title {\n margin: 0;\n font-size: 1rem;\n font-weight: 600;\n}\n\n.adn-list__bulk-action,\n.adn-list__retry,\n.adn-list__load-more {\n display: inline-flex;\n align-items: center;\n gap: var(--adn-space-2);\n min-height: var(--adn-touch-target);\n padding: var(--adn-space-2) var(--adn-space-3);\n background: transparent;\n border: 1px solid var(--adn-color-border);\n border-radius: var(--adn-radius);\n color: var(--adn-color-accent);\n cursor: pointer;\n font: inherit;\n}\n\n.adn-list__bulk-action:disabled,\n.adn-list__load-more:disabled,\n.adn-item__action:disabled,\n.adn-preferences__save:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.adn-list__bulk-icon,\n.adn-list__retry-icon,\n.adn-preferences__save-icon {\n width: 16px;\n height: 16px;\n color: currentColor;\n}\n\n.adn-list__items {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.adn-list__state {\n padding: var(--adn-space-4);\n color: var(--adn-color-muted);\n text-align: center;\n}\n\n.adn-list__state--error {\n color: var(--adn-color-danger);\n}\n\n.adn-list__state--empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--adn-space-2);\n}\n\n.adn-list__empty-icon {\n width: 32px;\n height: 32px;\n color: var(--adn-color-muted);\n}\n\n/* ── Item ─────────────────────────────────────────────────────────────────────── */\n\n.adn-item {\n display: flex;\n align-items: stretch;\n gap: var(--adn-space-2);\n border-bottom: 1px solid var(--adn-color-border);\n}\n\n.adn-item--unread {\n background: color-mix(in srgb, var(--adn-color-accent) 6%, transparent);\n}\n\n.adn-item__main {\n position: relative;\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--adn-space-1);\n min-height: var(--adn-touch-target);\n padding: var(--adn-space-3);\n background: transparent;\n border: none;\n cursor: pointer;\n font: inherit;\n color: inherit;\n text-align: left;\n}\n\n.adn-item__title {\n font-weight: 600;\n}\n\n.adn-item__body {\n color: var(--adn-color-muted);\n font-size: 0.875rem;\n}\n\n.adn-item__unread-dot {\n position: absolute;\n top: var(--adn-space-3);\n right: var(--adn-space-2);\n width: 8px;\n height: 8px;\n background: var(--adn-color-accent);\n border-radius: 9999px;\n}\n\n.adn-item__actions {\n display: flex;\n align-items: center;\n gap: var(--adn-space-1);\n padding-right: var(--adn-space-2);\n}\n\n.adn-item__action {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--adn-touch-target);\n min-height: var(--adn-touch-target);\n background: transparent;\n border: none;\n border-radius: var(--adn-radius);\n color: var(--adn-color-muted);\n cursor: pointer;\n}\n\n.adn-item__action:hover {\n background: var(--adn-color-border);\n color: var(--adn-color-text);\n}\n\n.adn-item__action--danger:hover {\n color: var(--adn-color-danger);\n}\n\n.adn-item__action-icon {\n width: 18px;\n height: 18px;\n color: currentColor;\n}\n\n/* ── Preferências ─────────────────────────────────────────────────────────────── */\n\n.adn-preferences {\n display: flex;\n flex-direction: column;\n gap: var(--adn-space-3);\n padding: var(--adn-space-3);\n background: var(--adn-color-surface);\n border-radius: var(--adn-radius);\n}\n\n.adn-preferences__title {\n margin: 0;\n font-size: 1rem;\n font-weight: 600;\n}\n\n.adn-preferences__description,\n.adn-preferences__state {\n margin: 0;\n color: var(--adn-color-muted);\n font-size: 0.875rem;\n}\n\n.adn-preferences__items {\n display: flex;\n flex-direction: column;\n gap: var(--adn-space-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.adn-preferences__item {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--adn-space-2);\n min-height: var(--adn-touch-target);\n}\n\n.adn-preferences__label {\n display: inline-flex;\n align-items: center;\n gap: var(--adn-space-2);\n cursor: pointer;\n}\n\n.adn-preferences__checkbox {\n width: 18px;\n height: 18px;\n accent-color: var(--adn-color-accent);\n}\n\n.adn-preferences__channel,\n.adn-preferences__quiet-hours {\n color: var(--adn-color-muted);\n font-size: 0.875rem;\n}\n\n.adn-preferences__footer {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--adn-space-3);\n}\n\n.adn-preferences__save {\n display: inline-flex;\n align-items: center;\n gap: var(--adn-space-2);\n min-height: var(--adn-touch-target);\n padding: var(--adn-space-2) var(--adn-space-4);\n background: var(--adn-color-accent);\n border: none;\n border-radius: var(--adn-radius);\n color: #ffffff;\n cursor: pointer;\n font: inherit;\n}\n\n.adn-preferences__feedback {\n font-size: 0.875rem;\n color: var(--adn-color-muted);\n}\n\n.adn-preferences__feedback--error {\n color: var(--adn-color-danger);\n}\n\n/* ── Responsivo ───────────────────────────────────────────────────────────────── */\n\n/* Mobile-first: `min-width` para adicionar, nunca `max-` para remover (web.md §10). */\n@media (min-width: 640px) {\n .adn-list__header {\n padding: var(--adn-space-3) var(--adn-space-4);\n }\n\n .adn-item__main {\n padding: var(--adn-space-3) var(--adn-space-4);\n }\n}\n\n@media (prefers-color-scheme: dark) {\n /* Só as peças herdam o palpite por sistema — as telas compostas herdam do host, que é quem sabe. */\n .adn-bell,\n .adn-list,\n .adn-preferences {\n --adn-color-surface: var(--adn-surface, #111827);\n --adn-color-text: var(--adn-text, #f9fafb);\n --adn-color-muted: var(--adn-muted, #9ca3af);\n --adn-color-border: var(--adn-border, #374151);\n }\n}\n\n/* Respeita quem pediu menos movimento no sistema. */\n@media (prefers-reduced-motion: reduce) {\n .adn-bell,\n .adn-item__action,\n .adn-list__bulk-action {\n transition: none;\n }\n}\n\n/* ---------------------------------------------------------------------------\n Telas compostas.\n\n SUPERFÍCIE TRANSPARENTE por padrão, e a borda é que delineia o card.\n\n As peças (`adn-list`, `adn-preferences`) pintavam fundo próprio, e passava porque o host as punha\n dentro de um `Card` dele — o fundo do host vencia. O workspace É o card, então o fundo do pacote\n aparece: com o sistema em modo escuro, o pacote pintava escuro enquanto o Tailwind do host\n renderizava claro. O pacote lê `prefers-color-scheme`, o host lê o tema DELE, e os dois discordam.\n Herdar é a única resposta que funciona nos dois casos; quem quiser fundo sólido define\n `--adn-surface`.\n\n Grid mobile-first: uma coluna, e a segunda entra a partir de 1024px. Área de toque de 44px nos\n controles (`web.md` §10) — checkbox nativo mede 13px, e configuração é tela que se ajusta no\n celular tanto quanto no monitor.\n --------------------------------------------------------------------------- */\n\n.adn-workspace,\n.adn-settings {\n display: flex;\n flex-direction: column;\n gap: var(--adn-space-4, 16px);\n /* `inherit` e não o token: a cor de texto do host já está no elemento pai. */\n color: inherit;\n}\n\n.adn-workspace__header,\n.adn-settings__header,\n.adn-settings__editor-header {\n display: flex;\n flex-wrap: wrap;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--adn-space-3, 12px);\n}\n\n.adn-workspace__title,\n.adn-settings__title {\n margin: 0;\n font-size: 1.25rem;\n font-weight: 600;\n}\n\n.adn-workspace__description,\n.adn-settings__description,\n.adn-settings__hint {\n /* `block` porque o hint é <span> dentro de <label>: inline, ele colava no rótulo e a tela mostrava\n \"TítuloO que aparece na lista\". */\n display: block;\n margin: 2px 0 0;\n font-size: 0.8125rem;\n color: var(--adn-color-muted);\n}\n\n.adn-workspace__actions {\n display: flex;\n align-items: center;\n gap: var(--adn-space-2, 8px);\n}\n\n.adn-button {\n display: inline-flex;\n align-items: center;\n gap: var(--adn-space-2, 8px);\n min-height: 44px;\n padding: 0 var(--adn-space-3, 12px);\n border-radius: var(--adn-radius);\n font-size: 0.875rem;\n font-weight: 500;\n text-decoration: none;\n color: inherit;\n background: transparent;\n border: 1px solid transparent;\n cursor: pointer;\n}\n\n.adn-button--outline {\n border-color: var(--adn-color-border);\n background: var(--adn-surface, transparent);\n}\n\n.adn-workspace__grid,\n.adn-settings__templates {\n display: grid;\n grid-template-columns: 1fr;\n gap: var(--adn-space-4, 16px);\n}\n\n.adn-workspace__grid--single {\n display: grid;\n grid-template-columns: 1fr;\n}\n\n@media (min-width: 1024px) {\n .adn-workspace__grid {\n grid-template-columns: 2fr 1fr;\n }\n\n .adn-settings__templates {\n grid-template-columns: 2fr 3fr;\n }\n\n /* Botão volta ao tamanho de mouse: 44px existe para o dedo. */\n .adn-button {\n min-height: 36px;\n }\n}\n\n.adn-settings__channels,\n.adn-settings__template-list,\n.adn-settings__editor {\n background: var(--adn-surface, transparent);\n border: 1px solid var(--adn-color-border);\n border-radius: var(--adn-radius);\n padding: var(--adn-space-4, 16px);\n}\n\n.adn-settings__section-title {\n margin: 0;\n font-size: 0.9375rem;\n font-weight: 600;\n}\n\n.adn-settings__category {\n margin-top: var(--adn-space-4, 16px);\n}\n\n.adn-settings__category-label {\n margin: 0;\n font-size: 0.875rem;\n font-weight: 500;\n}\n\n.adn-settings__channel-grid {\n display: grid;\n grid-template-columns: 1fr;\n gap: var(--adn-space-2, 8px);\n margin-top: var(--adn-space-2, 8px);\n}\n\n@media (min-width: 640px) {\n .adn-settings__channel-grid {\n grid-template-columns: 1fr 1fr;\n }\n}\n\n.adn-settings__channel {\n display: flex;\n align-items: flex-start;\n gap: var(--adn-space-2, 8px);\n padding: var(--adn-space-2, 8px);\n border: 1px solid var(--adn-color-border);\n border-radius: var(--adn-radius);\n cursor: pointer;\n /* Alvo de toque inteiro na linha, não só no quadradinho. */\n min-height: 44px;\n}\n\n.adn-settings__channel input {\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n margin-top: 2px;\n accent-color: var(--adn-color-accent);\n}\n\n.adn-settings__channel-label {\n display: block;\n font-size: 0.875rem;\n font-weight: 500;\n}\n\n.adn-settings__template-list ul {\n list-style: none;\n margin: var(--adn-space-3, 12px) 0 0;\n padding: 0;\n}\n\n.adn-settings__template-list li + li {\n border-top: 1px solid var(--adn-color-border);\n}\n\n.adn-settings__template-list button {\n display: flex;\n align-items: center;\n gap: var(--adn-space-2, 8px);\n width: 100%;\n min-height: 44px;\n padding: var(--adn-space-2, 8px);\n background: transparent;\n border: 0;\n text-align: left;\n color: inherit;\n cursor: pointer;\n}\n\n.adn-settings__row--active {\n background: color-mix(in srgb, var(--adn-color-accent) 8%, transparent);\n}\n\n.adn-settings__row-main {\n flex: 1;\n min-width: 0;\n}\n\n.adn-settings__row-title,\n.adn-settings__row-body {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.adn-settings__row-title {\n font-size: 0.875rem;\n font-weight: 500;\n}\n\n.adn-settings__row-body,\n.adn-settings__version {\n font-size: 0.75rem;\n color: var(--adn-color-muted);\n}\n\n.adn-settings__badge {\n flex-shrink: 0;\n padding: 2px var(--adn-space-2, 8px);\n border-radius: 999px;\n font-size: 0.75rem;\n font-weight: 500;\n background: color-mix(in srgb, var(--adn-color-accent) 12%, transparent);\n}\n\n.adn-settings__field {\n display: block;\n margin-top: var(--adn-space-3, 12px);\n}\n\n.adn-settings__field-label {\n font-size: 0.875rem;\n font-weight: 500;\n}\n\n.adn-settings__field input,\n.adn-settings__field textarea {\n display: block;\n width: 100%;\n margin-top: 4px;\n padding: var(--adn-space-2, 8px);\n font: inherit;\n font-size: 0.875rem;\n color: inherit;\n background: var(--adn-surface, transparent);\n border: 1px solid var(--adn-color-border);\n border-radius: var(--adn-radius);\n}\n\n.adn-settings__preview {\n margin-top: var(--adn-space-3, 12px);\n padding: var(--adn-space-3, 12px);\n border: 1px solid var(--adn-color-border);\n border-radius: var(--adn-radius);\n background: color-mix(in srgb, var(--adn-color-muted) 8%, transparent);\n}\n\n.adn-settings__preview-title {\n margin: 0;\n font-size: 0.6875rem;\n font-weight: 600;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--adn-color-muted);\n}\n\n.adn-settings__preview-subject {\n margin: 6px 0 0;\n font-size: 0.875rem;\n font-weight: 500;\n}\n\n.adn-settings__preview-body {\n margin: 0;\n font-size: 0.875rem;\n /* Quebra de linha do template é conteúdo: o destinatário vê os parágrafos. */\n white-space: pre-wrap;\n}\n\n.adn-settings__error {\n margin-top: var(--adn-space-2, 8px);\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--adn-color-danger);\n}\n\n.adn-settings__actions {\n display: flex;\n align-items: center;\n gap: var(--adn-space-2, 8px);\n margin-top: var(--adn-space-3, 12px);\n}\n\n.adn-settings__actions button {\n min-height: 44px;\n padding: 0 var(--adn-space-4, 16px);\n font: inherit;\n font-size: 0.875rem;\n font-weight: 500;\n color: #ffffff;\n background: var(--adn-color-accent);\n border: 0;\n border-radius: var(--adn-radius);\n cursor: pointer;\n}\n\n.adn-settings__actions button:disabled {\n opacity: 0.5;\n cursor: default;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/styles.css"],"sourcesContent":["/**\n * Copyright (c) 2026 Ada Technology. MIT License.\n *\n * O pacote não tem cor própria (regra de módulos plugáveis §4.1): tudo sai de custom properties\n * com fallback neutro. O host sobrescreve `--adn-*` no escopo dele e a mesma tela ganha a cara do\n * produto — sem fork, sem prop de cor, sem valor hexadecimal aqui dentro que não seja fallback.\n */\n\n.adn-bell,\n.adn-list,\n.adn-preferences,\n.adn-workspace,\n.adn-settings {\n --adn-color-surface: var(--adn-surface, #ffffff);\n --adn-color-text: var(--adn-text, #111827);\n --adn-color-muted: var(--adn-muted, #6b7280);\n --adn-color-accent: var(--adn-accent, #3b82f6);\n --adn-color-danger: var(--adn-danger, #dc2626);\n --adn-color-border: var(--adn-border, #e5e7eb);\n --adn-radius: var(--adn-radius-md, 8px);\n /* Grade base de 4px (web.md §8) — múltiplos, nunca pixel mágico. */\n --adn-space-1: 4px;\n --adn-space-2: 8px;\n --adn-space-3: 12px;\n --adn-space-4: 16px;\n /* 44px: alvo mínimo de toque em mobile (web.md §10). */\n --adn-touch-target: 44px;\n\n color: var(--adn-color-text);\n font-family: inherit;\n}\n\n/* ── Sino ─────────────────────────────────────────────────────────────────────── */\n\n.adn-bell {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--adn-touch-target);\n min-height: var(--adn-touch-target);\n padding: var(--adn-space-2);\n background: transparent;\n border: none;\n border-radius: var(--adn-radius);\n cursor: pointer;\n color: inherit;\n}\n\n.adn-bell:hover {\n background: var(--adn-color-border);\n}\n\n/* Foco visível é requisito de teclado — remover outline sem repor indicador quebra navegação. */\n.adn-bell:focus-visible,\n.adn-list__bulk-action:focus-visible,\n.adn-list__retry:focus-visible,\n.adn-list__load-more:focus-visible,\n.adn-item__main:focus-visible,\n.adn-item__action:focus-visible,\n.adn-preferences__save:focus-visible {\n outline: 2px solid var(--adn-color-accent);\n outline-offset: 2px;\n}\n\n.adn-bell__icon {\n width: 20px;\n height: 20px;\n /* Herda a cor do botão, para acompanhar hover e estado desabilitado sem regra extra. */\n color: currentColor;\n}\n\n.adn-bell__badge {\n position: absolute;\n top: var(--adn-space-1);\n right: var(--adn-space-1);\n min-width: 18px;\n padding: 0 var(--adn-space-1);\n background: var(--adn-color-danger);\n color: #ffffff;\n border-radius: 9999px;\n font-size: 11px;\n line-height: 18px;\n text-align: center;\n}\n\n/* ── Lista ────────────────────────────────────────────────────────────────────── */\n\n.adn-list {\n display: flex;\n flex-direction: column;\n background: var(--adn-color-surface);\n /* Mesma borda dos painéis de configuração: sem ela a lista flutuava sobre o fundo do host e o\n cabeçalho dela parecia ser da página. */\n border: 1px solid var(--adn-color-border);\n border-radius: var(--adn-radius);\n /* A linha e o cabeçalho vão até a borda; sem isto o canto reto vaza por cima do arredondado. */\n overflow: hidden;\n}\n\n.adn-list__header {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--adn-space-2);\n padding: var(--adn-space-3);\n border-bottom: 1px solid var(--adn-color-border);\n}\n\n.adn-list__heading {\n display: flex;\n align-items: center;\n gap: var(--adn-space-2);\n}\n\n.adn-list__title {\n margin: 0;\n font-size: 1rem;\n font-weight: 600;\n}\n\n/* O contador estava só no sino; quem abre a tela pelo menu não passa pelo sino. */\n.adn-list__count {\n min-width: 20px;\n padding: 0 var(--adn-space-1);\n border-radius: 9999px;\n background: color-mix(in srgb, var(--adn-color-accent) 16%, transparent);\n color: var(--adn-color-accent);\n font-size: 0.75rem;\n font-weight: 600;\n line-height: 20px;\n text-align: center;\n /* Dígito de largura fixa: sem isto o contador muda de largura ao passar de 9 para 10. */\n font-variant-numeric: tabular-nums;\n}\n\n.adn-list__bulk-action,\n.adn-list__retry,\n.adn-list__load-more {\n display: inline-flex;\n align-items: center;\n gap: var(--adn-space-2);\n min-height: var(--adn-touch-target);\n padding: var(--adn-space-2) var(--adn-space-3);\n background: transparent;\n border: 1px solid var(--adn-color-border);\n border-radius: var(--adn-radius);\n color: var(--adn-color-accent);\n cursor: pointer;\n font: inherit;\n}\n\n.adn-list__bulk-action:disabled,\n.adn-list__load-more:disabled,\n.adn-item__action:disabled,\n.adn-preferences__save:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.adn-list__bulk-icon,\n.adn-list__retry-icon,\n.adn-preferences__save-icon {\n width: 16px;\n height: 16px;\n color: currentColor;\n}\n\n.adn-list__skeleton {\n display: flex;\n flex-direction: column;\n gap: var(--adn-space-3);\n padding: var(--adn-space-3);\n}\n\n.adn-list__skeleton-row {\n height: var(--adn-touch-target);\n border-radius: var(--adn-radius);\n background: linear-gradient(\n 90deg,\n color-mix(in srgb, var(--adn-color-muted) 10%, transparent),\n color-mix(in srgb, var(--adn-color-muted) 22%, transparent),\n color-mix(in srgb, var(--adn-color-muted) 10%, transparent)\n );\n background-size: 200% 100%;\n animation: adn-skeleton 1.4s ease-in-out infinite;\n}\n\n@keyframes adn-skeleton {\n from {\n background-position: 200% 0;\n }\n\n to {\n background-position: -200% 0;\n }\n}\n\n.adn-list__footer {\n display: flex;\n justify-content: center;\n padding: var(--adn-space-3);\n border-top: 1px solid var(--adn-color-border);\n}\n\n.adn-list__items {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.adn-list__state {\n padding: var(--adn-space-4);\n color: var(--adn-color-muted);\n text-align: center;\n}\n\n.adn-list__state--error {\n color: var(--adn-color-danger);\n}\n\n.adn-list__state--empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--adn-space-2);\n}\n\n.adn-list__empty-icon {\n width: 32px;\n height: 32px;\n color: var(--adn-color-muted);\n}\n\n/* ── Item ─────────────────────────────────────────────────────────────────────── */\n\n.adn-item {\n display: flex;\n align-items: stretch;\n gap: var(--adn-space-2);\n border-bottom: 1px solid var(--adn-color-border);\n}\n\n.adn-item:last-child {\n border-bottom: none;\n}\n\n/* A linha inteira é clicável — o ponteiro sozinho não é aviso suficiente de que ela responde. */\n.adn-item:hover {\n background: color-mix(in srgb, var(--adn-color-muted) 8%, transparent);\n}\n\n.adn-item--unread {\n background: color-mix(in srgb, var(--adn-color-accent) 6%, transparent);\n}\n\n.adn-item--unread:hover {\n background: color-mix(in srgb, var(--adn-color-accent) 12%, transparent);\n}\n\n.adn-item__main {\n position: relative;\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--adn-space-1);\n min-height: var(--adn-touch-target);\n padding: var(--adn-space-3);\n background: transparent;\n border: none;\n cursor: pointer;\n font: inherit;\n color: inherit;\n text-align: left;\n}\n\n.adn-item__heading {\n display: flex;\n align-items: baseline;\n gap: var(--adn-space-2);\n width: 100%;\n}\n\n.adn-item__title {\n flex: 1;\n min-width: 0;\n font-weight: 600;\n /* Título longo empurrava o horário para fora da linha. */\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.adn-item__time {\n flex-shrink: 0;\n color: var(--adn-color-muted);\n font-size: 0.75rem;\n font-variant-numeric: tabular-nums;\n}\n\n.adn-item__body {\n color: var(--adn-color-muted);\n font-size: 0.875rem;\n /* Duas linhas e reticências: corpo de aviso longo esticava a linha e desalinhava a lista. */\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n overflow: hidden;\n}\n\n.adn-item__unread-dot {\n flex-shrink: 0;\n align-self: center;\n width: 8px;\n height: 8px;\n background: var(--adn-color-accent);\n border-radius: 9999px;\n}\n\n.adn-item__actions {\n display: flex;\n align-items: center;\n gap: var(--adn-space-1);\n padding-right: var(--adn-space-2);\n}\n\n.adn-item__action {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--adn-touch-target);\n min-height: var(--adn-touch-target);\n background: transparent;\n border: none;\n border-radius: var(--adn-radius);\n color: var(--adn-color-muted);\n cursor: pointer;\n}\n\n.adn-item__action:hover {\n background: var(--adn-color-border);\n color: var(--adn-color-text);\n}\n\n.adn-item__action--danger:hover {\n color: var(--adn-color-danger);\n}\n\n.adn-item__action-icon {\n width: 18px;\n height: 18px;\n color: currentColor;\n}\n\n/* ── Preferências ─────────────────────────────────────────────────────────────── */\n\n.adn-preferences {\n display: flex;\n flex-direction: column;\n gap: var(--adn-space-3);\n padding: var(--adn-space-3);\n background: var(--adn-color-surface);\n border-radius: var(--adn-radius);\n}\n\n.adn-preferences__title {\n margin: 0;\n font-size: 1rem;\n font-weight: 600;\n}\n\n.adn-preferences__description,\n.adn-preferences__state {\n margin: 0;\n color: var(--adn-color-muted);\n font-size: 0.875rem;\n}\n\n.adn-preferences__items {\n display: flex;\n flex-direction: column;\n gap: var(--adn-space-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.adn-preferences__item {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--adn-space-2);\n min-height: var(--adn-touch-target);\n}\n\n.adn-preferences__label {\n display: inline-flex;\n align-items: center;\n gap: var(--adn-space-2);\n cursor: pointer;\n}\n\n.adn-preferences__checkbox {\n width: 18px;\n height: 18px;\n accent-color: var(--adn-color-accent);\n}\n\n.adn-preferences__channel,\n.adn-preferences__quiet-hours {\n color: var(--adn-color-muted);\n font-size: 0.875rem;\n}\n\n.adn-preferences__footer {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--adn-space-3);\n}\n\n.adn-preferences__save {\n display: inline-flex;\n align-items: center;\n gap: var(--adn-space-2);\n min-height: var(--adn-touch-target);\n padding: var(--adn-space-2) var(--adn-space-4);\n background: var(--adn-color-accent);\n border: none;\n border-radius: var(--adn-radius);\n color: #ffffff;\n cursor: pointer;\n font: inherit;\n}\n\n.adn-preferences__feedback {\n font-size: 0.875rem;\n color: var(--adn-color-muted);\n}\n\n.adn-preferences__feedback--error {\n color: var(--adn-color-danger);\n}\n\n/* ── Responsivo ───────────────────────────────────────────────────────────────── */\n\n/* Mobile-first: `min-width` para adicionar, nunca `max-` para remover (web.md §10). */\n@media (min-width: 640px) {\n .adn-list__header {\n padding: var(--adn-space-3) var(--adn-space-4);\n }\n\n .adn-item__main {\n padding: var(--adn-space-3) var(--adn-space-4);\n }\n}\n\n@media (prefers-color-scheme: dark) {\n /* Só as peças herdam o palpite por sistema — as telas compostas herdam do host, que é quem sabe. */\n .adn-bell,\n .adn-list,\n .adn-preferences {\n --adn-color-surface: var(--adn-surface, #111827);\n --adn-color-text: var(--adn-text, #f9fafb);\n --adn-color-muted: var(--adn-muted, #9ca3af);\n --adn-color-border: var(--adn-border, #374151);\n }\n}\n\n/* Respeita quem pediu menos movimento no sistema. */\n@media (prefers-reduced-motion: reduce) {\n .adn-bell,\n .adn-item__action,\n .adn-list__bulk-action {\n transition: none;\n }\n\n .adn-list__skeleton-row {\n animation: none;\n }\n}\n\n/* ---------------------------------------------------------------------------\n Telas compostas.\n\n SUPERFÍCIE TRANSPARENTE por padrão, e a borda é que delineia o card.\n\n As peças (`adn-list`, `adn-preferences`) pintavam fundo próprio, e passava porque o host as punha\n dentro de um `Card` dele — o fundo do host vencia. O workspace É o card, então o fundo do pacote\n aparece: com o sistema em modo escuro, o pacote pintava escuro enquanto o Tailwind do host\n renderizava claro. O pacote lê `prefers-color-scheme`, o host lê o tema DELE, e os dois discordam.\n Herdar é a única resposta que funciona nos dois casos; quem quiser fundo sólido define\n `--adn-surface`.\n\n Grid mobile-first: uma coluna, e a segunda entra a partir de 1024px. Área de toque de 44px nos\n controles (`web.md` §10) — checkbox nativo mede 13px, e configuração é tela que se ajusta no\n celular tanto quanto no monitor.\n --------------------------------------------------------------------------- */\n\n.adn-workspace,\n.adn-settings {\n display: flex;\n flex-direction: column;\n gap: var(--adn-space-4, 16px);\n /* `inherit` e não o token: a cor de texto do host já está no elemento pai. */\n color: inherit;\n}\n\n.adn-workspace__header,\n.adn-settings__header,\n.adn-settings__editor-header {\n display: flex;\n flex-wrap: wrap;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--adn-space-3, 12px);\n}\n\n.adn-workspace__title,\n.adn-settings__title {\n margin: 0;\n font-size: 1.25rem;\n font-weight: 600;\n}\n\n.adn-workspace__description,\n.adn-settings__description,\n.adn-settings__hint {\n /* `block` porque o hint é <span> dentro de <label>: inline, ele colava no rótulo e a tela mostrava\n \"TítuloO que aparece na lista\". */\n display: block;\n margin: 2px 0 0;\n font-size: 0.8125rem;\n color: var(--adn-color-muted);\n}\n\n.adn-workspace__actions {\n display: flex;\n align-items: center;\n gap: var(--adn-space-2, 8px);\n}\n\n.adn-button {\n display: inline-flex;\n align-items: center;\n gap: var(--adn-space-2, 8px);\n min-height: 44px;\n padding: 0 var(--adn-space-3, 12px);\n border-radius: var(--adn-radius);\n font-size: 0.875rem;\n font-weight: 500;\n text-decoration: none;\n color: inherit;\n background: transparent;\n border: 1px solid transparent;\n cursor: pointer;\n}\n\n.adn-button--outline {\n border-color: var(--adn-color-border);\n background: var(--adn-surface, transparent);\n}\n\n.adn-workspace__grid,\n.adn-settings__templates {\n display: grid;\n grid-template-columns: 1fr;\n gap: var(--adn-space-4, 16px);\n}\n\n.adn-workspace__grid--single {\n display: grid;\n grid-template-columns: 1fr;\n}\n\n@media (min-width: 1024px) {\n .adn-workspace__grid {\n grid-template-columns: 2fr 1fr;\n }\n\n .adn-settings__templates {\n grid-template-columns: 2fr 3fr;\n }\n\n /* Botão volta ao tamanho de mouse: 44px existe para o dedo. */\n .adn-button {\n min-height: 36px;\n }\n}\n\n.adn-settings__channels,\n.adn-settings__template-list,\n.adn-settings__editor {\n background: var(--adn-surface, transparent);\n border: 1px solid var(--adn-color-border);\n border-radius: var(--adn-radius);\n padding: var(--adn-space-4, 16px);\n}\n\n.adn-settings__section-title {\n margin: 0;\n font-size: 0.9375rem;\n font-weight: 600;\n}\n\n.adn-settings__category {\n margin-top: var(--adn-space-4, 16px);\n}\n\n.adn-settings__category-label {\n margin: 0;\n font-size: 0.875rem;\n font-weight: 500;\n}\n\n.adn-settings__channel-grid {\n display: grid;\n grid-template-columns: 1fr;\n gap: var(--adn-space-2, 8px);\n margin-top: var(--adn-space-2, 8px);\n}\n\n@media (min-width: 640px) {\n .adn-settings__channel-grid {\n grid-template-columns: 1fr 1fr;\n }\n}\n\n.adn-settings__channel {\n display: flex;\n align-items: flex-start;\n gap: var(--adn-space-2, 8px);\n padding: var(--adn-space-2, 8px);\n border: 1px solid var(--adn-color-border);\n border-radius: var(--adn-radius);\n cursor: pointer;\n /* Alvo de toque inteiro na linha, não só no quadradinho. */\n min-height: 44px;\n}\n\n.adn-settings__channel input {\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n margin-top: 2px;\n accent-color: var(--adn-color-accent);\n}\n\n.adn-settings__channel-label {\n display: block;\n font-size: 0.875rem;\n font-weight: 500;\n}\n\n.adn-settings__template-list ul {\n list-style: none;\n margin: var(--adn-space-3, 12px) 0 0;\n padding: 0;\n}\n\n.adn-settings__template-list li + li {\n border-top: 1px solid var(--adn-color-border);\n}\n\n.adn-settings__template-list button {\n display: flex;\n align-items: center;\n gap: var(--adn-space-2, 8px);\n width: 100%;\n min-height: 44px;\n padding: var(--adn-space-2, 8px);\n background: transparent;\n border: 0;\n text-align: left;\n color: inherit;\n cursor: pointer;\n}\n\n.adn-settings__row--active {\n background: color-mix(in srgb, var(--adn-color-accent) 8%, transparent);\n}\n\n.adn-settings__row-main {\n flex: 1;\n min-width: 0;\n}\n\n.adn-settings__row-title,\n.adn-settings__row-body {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.adn-settings__row-title {\n font-size: 0.875rem;\n font-weight: 500;\n}\n\n.adn-settings__row-body,\n.adn-settings__version {\n font-size: 0.75rem;\n color: var(--adn-color-muted);\n}\n\n.adn-settings__badge {\n flex-shrink: 0;\n padding: 2px var(--adn-space-2, 8px);\n border-radius: 999px;\n font-size: 0.75rem;\n font-weight: 500;\n background: color-mix(in srgb, var(--adn-color-accent) 12%, transparent);\n}\n\n.adn-settings__field {\n display: block;\n margin-top: var(--adn-space-3, 12px);\n}\n\n.adn-settings__field-label {\n font-size: 0.875rem;\n font-weight: 500;\n}\n\n.adn-settings__field input,\n.adn-settings__field textarea {\n display: block;\n width: 100%;\n margin-top: 4px;\n padding: var(--adn-space-2, 8px);\n font: inherit;\n font-size: 0.875rem;\n color: inherit;\n background: var(--adn-surface, transparent);\n border: 1px solid var(--adn-color-border);\n border-radius: var(--adn-radius);\n}\n\n.adn-settings__preview {\n margin-top: var(--adn-space-3, 12px);\n padding: var(--adn-space-3, 12px);\n border: 1px solid var(--adn-color-border);\n border-radius: var(--adn-radius);\n background: color-mix(in srgb, var(--adn-color-muted) 8%, transparent);\n}\n\n.adn-settings__preview-title {\n margin: 0;\n font-size: 0.6875rem;\n font-weight: 600;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--adn-color-muted);\n}\n\n.adn-settings__preview-subject {\n margin: 6px 0 0;\n font-size: 0.875rem;\n font-weight: 500;\n}\n\n.adn-settings__preview-body {\n margin: 0;\n font-size: 0.875rem;\n /* Quebra de linha do template é conteúdo: o destinatário vê os parágrafos. */\n white-space: pre-wrap;\n}\n\n.adn-settings__error {\n margin-top: var(--adn-space-2, 8px);\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--adn-color-danger);\n}\n\n.adn-settings__actions {\n display: flex;\n align-items: center;\n gap: var(--adn-space-2, 8px);\n margin-top: var(--adn-space-3, 12px);\n}\n\n.adn-settings__actions button {\n min-height: 44px;\n padding: 0 var(--adn-space-4, 16px);\n font: inherit;\n font-size: 0.875rem;\n font-weight: 500;\n color: #ffffff;\n background: var(--adn-color-accent);\n border: 0;\n border-radius: var(--adn-radius);\n cursor: pointer;\n}\n\n.adn-settings__actions button:disabled {\n opacity: 0.5;\n cursor: default;\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adatechnology/notification-ui",
3
- "version": "0.1.0-rc.2",
3
+ "version": "0.1.0-rc.3",
4
4
  "license": "MIT",
5
5
  "description": "Notification UI — headless hooks + componentes de inbox, sino e preferências",
6
6
  "publishConfig": {
@@ -26,8 +26,8 @@
26
26
  "dependencies": {
27
27
  "clsx": "^2.1.1",
28
28
  "lucide-react": "^1.21.0",
29
- "@adatechnology/notification-client": "0.1.0-rc.2",
30
- "@adatechnology/notification-contracts": "0.1.0-rc.2"
29
+ "@adatechnology/notification-contracts": "0.1.0-rc.2",
30
+ "@adatechnology/notification-client": "0.1.0-rc.2"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@tanstack/react-query": "^5",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/NotificationProvider.tsx","../src/locales/pt-BR.json","../src/locales/en.json","../src/locales/index.ts","../src/hooks/useNotifications.ts","../src/hooks/queryKeys.ts","../src/hooks/useNotificationStream.ts","../src/hooks/usePreferences.ts","../src/hooks/useTemplateEditor.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 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 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","{\n \"bell.label\": \"Notificações\",\n \"bell.unreadSuffix\": \"não lidas\",\n \"list.title\": \"Notificações\",\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 \"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}\n","{\n \"bell.label\": \"Notifications\",\n \"bell.unreadSuffix\": \"unread\",\n \"list.title\": \"Notifications\",\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 \"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}\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 * 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} 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\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 * 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\nimport { useMemo, useState } from 'react'\nimport { renderTemplate } from '@adatechnology/notification-contracts'\nimport type {\n NotificationTemplate,\n RenderedTemplatePreview,\n UpsertTemplateBody,\n} from '@adatechnology/notification-contracts'\n\nimport { useTemplates, useUpsertTemplate } from './usePreferences'\n\nexport type TemplateDraft = {\n readonly key: string\n readonly channel: string\n readonly locale: string\n readonly subject: string\n readonly body: string\n readonly whatsappTemplateName: string\n}\n\nfunction toDraft(template: NotificationTemplate): TemplateDraft {\n return {\n key: template.key,\n channel: template.channel,\n locale: template.locale,\n subject: template.subject ?? '',\n body: template.body,\n whatsappTemplateName: template.whatsappTemplateName ?? '',\n }\n}\n\nexport type UseTemplateEditorParams = {\n /**\n * Valores de exemplo do preview. É do produto: só ele sabe que `{{shortCode}}` é \"QC-1042\".\n * Ausente, o preview mostra os campos vazios — que é o que o destinatário veria mesmo.\n */\n readonly previewPayload?: Readonly<Record<string, unknown>>\n}\n\nexport type UseTemplateEditorResult = {\n readonly templates: readonly NotificationTemplate[]\n readonly isLoading: boolean\n readonly isSaving: boolean\n readonly error: string | undefined\n readonly selected: NotificationTemplate | undefined\n readonly draft: TemplateDraft | undefined\n readonly preview: RenderedTemplatePreview | undefined\n readonly isDirty: boolean\n select: (template: NotificationTemplate) => void\n update: (patch: Partial<TemplateDraft>) => void\n save: () => void\n clear: () => void\n}\n\nexport function useTemplateEditor(params: UseTemplateEditorParams = {}): UseTemplateEditorResult {\n const templatesQuery = useTemplates()\n const upsert = useUpsertTemplate()\n\n const [selectedId, setSelectedId] = useState<string | undefined>(undefined)\n const [draft, setDraft] = useState<TemplateDraft | undefined>(undefined)\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 /** Decisão 2: derivado do id. */\n const selected = useMemo(() => templates.find((template) => template.id === selectedId), [templates, selectedId])\n\n /** Decisão 4: o mesmo render do envio. */\n const preview = useMemo(() => {\n if (!draft) return undefined\n return renderTemplate({\n channel: draft.channel,\n subject: draft.subject || undefined,\n body: draft.body,\n payload: params.previewPayload ?? {},\n })\n }, [draft, params.previewPayload])\n\n const isDirty = useMemo(() => {\n if (!draft || !selected) return false\n const original = toDraft(selected)\n return (Object.keys(original) as (keyof TemplateDraft)[]).some((field) => original[field] !== draft[field])\n }, [draft, selected])\n\n return {\n templates,\n isLoading: templatesQuery.isLoading,\n isSaving: upsert.isPending,\n error: upsert.error?.message,\n selected,\n draft,\n preview,\n isDirty,\n\n select(template) {\n setSelectedId(template.id)\n setDraft(toDraft(template))\n },\n\n update(patch) {\n setDraft((current) => (current ? { ...current, ...patch } : current))\n },\n\n save() {\n if (!draft) return\n const body: UpsertTemplateBody = {\n key: draft.key,\n channel: draft.channel as UpsertTemplateBody['channel'],\n locale: draft.locale,\n active: true,\n body: draft.body,\n // Decisão 3: chave omitida quando vazia.\n ...(draft.subject ? { subject: draft.subject } : {}),\n ...(draft.whatsappTemplateName ? { whatsappTemplateName: draft.whatsappTemplateName } : {}),\n }\n // A versão nova tem outro id: soltar a seleção evita o editor ficar preso na anterior.\n upsert.mutate(body, { onSuccess: () => setSelectedId(undefined) })\n },\n\n clear() {\n setSelectedId(undefined)\n setDraft(undefined)\n },\n }\n}\n"],"mappings":"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","names":["useQueryClient","useQueryClient","useMutation","useQuery","useQueryClient","useQuery","useQueryClient","useMutation","useMemo","useMemo"]}