@adatechnology/conversations-ui 0.1.0-rc.8 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/ConversationSimulatorPanel--5fIzXWY.d.ts +804 -0
  2. package/dist/chunk-BJNRLLDO.js +2708 -0
  3. package/dist/chunk-DKPXKQGC.js +110 -0
  4. package/dist/{chunk-OGRRHQQW.js → chunk-WCBDXZ3X.js} +68 -4
  5. package/dist/flows/index.d.ts +422 -5
  6. package/dist/flows/index.js +2502 -678
  7. package/dist/index.d.ts +921 -17
  8. package/dist/index.js +3677 -678
  9. package/dist/preview/index.d.ts +62 -105
  10. package/dist/preview/index.js +162 -284
  11. package/dist/styles.css +893 -0
  12. package/package.json +9 -8
  13. package/src/AudioPlayer.tsx +8 -0
  14. package/src/AudioRecorderButton.test.tsx +30 -0
  15. package/src/AudioRecorderButton.tsx +248 -0
  16. package/src/AudioTranscription.test.tsx +115 -0
  17. package/src/AudioTranscription.tsx +252 -0
  18. package/src/Avatar.tsx +1 -1
  19. package/src/ConversationContextPanel.tsx +218 -44
  20. package/src/ConversationDocumentsPanel.tsx +11 -6
  21. package/src/ConversationHeader.test.tsx +66 -0
  22. package/src/ConversationHeader.tsx +147 -47
  23. package/src/ConversationListItem.tsx +8 -6
  24. package/src/ConversationLocalesProvider.tsx +28 -0
  25. package/src/ConversationRow.tsx +53 -7
  26. package/src/DarkModeToggle.test.tsx +76 -0
  27. package/src/DarkModeToggle.tsx +92 -0
  28. package/src/DocumentsLibrary.tsx +67 -7
  29. package/src/EmojiPicker.tsx +2 -1
  30. package/src/InteractiveMessage.tsx +3 -0
  31. package/src/Lightbox.tsx +1 -1
  32. package/src/MediaRenderer.tsx +88 -15
  33. package/src/MessageBubble.test.tsx +41 -0
  34. package/src/MessageBubble.tsx +47 -5
  35. package/src/MessageComposer.test.tsx +35 -0
  36. package/src/MessageComposer.tsx +122 -17
  37. package/src/MessageText.tsx +2 -1
  38. package/src/MessageTimestamp.tsx +2 -1
  39. package/src/RichMessageComposer.test.tsx +113 -0
  40. package/src/RichMessageComposer.tsx +551 -0
  41. package/src/SimpleEmojiPicker.tsx +5 -3
  42. package/src/StatusTicks.tsx +1 -1
  43. package/src/Toast.tsx +4 -0
  44. package/src/Tooltip.test.ts +42 -0
  45. package/src/Tooltip.tsx +167 -0
  46. package/src/Wallpaper.test.tsx +21 -0
  47. package/src/Wallpaper.tsx +67 -7
  48. package/src/WhatsAppMessageEditor.tsx +10 -7
  49. package/src/WindowExpiredNotice.tsx +12 -4
  50. package/src/{preview/audioRecorderFormat.test.ts → audioRecorderFormat.test.ts} +1 -1
  51. package/src/buildOutput.test.ts +79 -0
  52. package/src/composer.constant.ts +33 -0
  53. package/src/conversationTranscript.test.ts +57 -0
  54. package/src/conversationTranscript.ts +29 -4
  55. package/src/conversationWindow.ts +7 -5
  56. package/src/documentTypeLabel.test.ts +57 -0
  57. package/src/documents/DocumentsWorkspace.tsx +550 -0
  58. package/src/documents/index.ts +8 -0
  59. package/src/documents/labels.ts +92 -0
  60. package/src/flows/FlowConnectionEdge.tsx +104 -0
  61. package/src/flows/FlowGroupHeader.tsx +12 -2
  62. package/src/flows/FlowLegend.tsx +125 -0
  63. package/src/flows/FlowMapCanvas.tsx +15 -12
  64. package/src/flows/FlowMapNode.tsx +4 -1
  65. package/src/flows/FlowNodeCard.tsx +219 -34
  66. package/src/flows/FlowNodePanel.tsx +153 -39
  67. package/src/flows/FlowPalette.tsx +156 -70
  68. package/src/flows/FlowPortalNode.tsx +1 -1
  69. package/src/flows/FlowWhatsAppPreview.tsx +14 -3
  70. package/src/flows/FlowsWorkspace.tsx +1255 -0
  71. package/src/flows/flowCanvasModel.test.ts +456 -0
  72. package/src/flows/flowCanvasModel.ts +378 -0
  73. package/src/flows/flowEditorOps.test.ts +276 -0
  74. package/src/flows/flowEditorOps.ts +202 -0
  75. package/src/flows/flowGraph.ts +78 -53
  76. package/src/flows/flowMenuPlacement.test.ts +130 -0
  77. package/src/flows/flowMenuPlacement.ts +86 -0
  78. package/src/flows/index.ts +51 -2
  79. package/src/flows/labels.ts +180 -0
  80. package/src/flows/workspaceContract.test.ts +126 -0
  81. package/src/hooks/useContainerWidth.ts +35 -0
  82. package/src/hooks/useConversationRealtime.ts +10 -8
  83. package/src/hooks/useScrollToLatestMessage.ts +127 -0
  84. package/src/hooks/useUrlFilterState.ts +107 -0
  85. package/src/icon.constant.ts +12 -0
  86. package/src/index.ts +100 -0
  87. package/src/lib/composer-formatting.test.ts +78 -0
  88. package/src/lib/composer-formatting.ts +145 -0
  89. package/src/lib/whatsapp-formatting.test.tsx +37 -0
  90. package/src/lib/whatsapp-formatting.tsx +28 -3
  91. package/src/listing/index.tsx +202 -0
  92. package/src/pagination.constant.ts +10 -0
  93. package/src/preview/ConversationPreview.tsx +84 -45
  94. package/src/preview/ConversationSimulatorClient.ts +143 -0
  95. package/src/preview/ConversationSimulatorPanel.test.tsx +55 -0
  96. package/src/preview/ConversationSimulatorPanel.tsx +131 -0
  97. package/src/preview/createPreviewBridgeClient.test.ts +92 -0
  98. package/src/preview/createPreviewBridgeClient.ts +124 -0
  99. package/src/preview/createPreviewMediaUploader.ts +82 -0
  100. package/src/preview/createPreviewWebhookClient.test.ts +96 -0
  101. package/src/preview/createPreviewWebhookClient.ts +99 -3
  102. package/src/preview/index.ts +36 -2
  103. package/src/preview/previewMediaUploader.test.ts +61 -0
  104. package/src/providers/ConversationsProvider.tsx +8 -6
  105. package/src/providers/types.ts +59 -2
  106. package/src/quickReply.test.ts +58 -0
  107. package/src/replyLatency.test.ts +71 -0
  108. package/src/replyLatency.ts +57 -0
  109. package/src/settings/MessagesWorkspace.tsx +571 -0
  110. package/src/settings/TopicsForm.tsx +2 -0
  111. package/src/settings/TranscriptionSettingsForm.test.tsx +81 -0
  112. package/src/settings/TranscriptionSettingsForm.tsx +190 -0
  113. package/src/settings/WelcomeFarewellForm.tsx +1 -0
  114. package/src/settings/WhatsAppCreateTemplateForm.tsx +1 -0
  115. package/src/settings/WhatsAppTemplateSettingsForm.tsx +5 -2
  116. package/src/settings/WhatsAppTemplatesSettings.test.tsx +61 -0
  117. package/src/settings/WhatsAppTemplatesSettings.tsx +22 -2
  118. package/src/styles.css +858 -0
  119. package/src/theme.ts +13 -0
  120. package/src/types.ts +26 -0
  121. package/src/workspace/BulkTemplateModal.tsx +132 -0
  122. package/src/workspace/ConversationPane.tsx +432 -0
  123. package/src/workspace/ConversationsInboxList.tsx +194 -0
  124. package/src/workspace/ConversationsWorkspace.tsx +423 -0
  125. package/src/workspace/index.ts +17 -0
  126. package/src/workspace/labels.test.ts +17 -0
  127. package/src/workspace/labels.ts +85 -0
  128. package/src/workspace/useConversationsInbox.ts +332 -0
  129. package/dist/chunk-73MW5HNT.js +0 -1717
  130. package/dist/chunk-NV2RZ5KT.js +0 -56
  131. package/dist/types-B5C1DLu1.d.ts +0 -365
  132. package/src/preview/AudioRecorderButton.tsx +0 -117
@@ -1,6 +1,8 @@
1
- import { y as MessagePayload, m as ConversationSummary, k as ConversationEventSource, o as ConversationsApi, L as ListConversationsParams, l as ConversationPage, S as SSEProvider, i as ConversationDocument, A as ResolveMediaUrl } from '../types-B5C1DLu1.js';
1
+ import { U as MessagePayload, u as ConversationSummary, m as ConversationEventSource, w as ConversationsApi, Q as ListConversationsParams, n as ConversationPage, a5 as SSEProvider, k as ConversationDocument, a6 as SendPreviewMediaParams, Z as PreviewUploadedMedia, $ as PreviewWebhookClient, a3 as ResolveMediaUrl } from '../ConversationSimulatorPanel--5fIzXWY.js';
2
+ export { A as AudioRecorderButton, a as AudioRecorderButtonLabels, b as AudioRecorderButtonProps, o as ConversationPreview, p as ConversationPreviewProps, q as ConversationSimulatorClient, r as ConversationSimulatorPanel, s as ConversationSimulatorPanelLabels, t as ConversationSimulatorPanelProps, B as CreatePreviewMediaUploaderParams, D as CreatePreviewWebhookClientParams, E as DEFAULT_AUDIO_RECORDER_BUTTON_LABELS, G as DEFAULT_CONVERSATION_SIMULATOR_PANEL_LABELS, I as DEFAULT_MEDIA_UPLOAD_PATH, W as PreviewInProductionError, X as PreviewMediaUploadRejectedError, Y as PreviewMediaUploadRequest, _ as PreviewUploadedMedia, a0 as PreviewWebhookRejectedError, a4 as SIMULATOR_FILE_MEDIA_KINDS, a7 as SendSimulatorMediaParams, a8 as SimulatorMediaKind, a9 as ToSimulatorClientParams, ac as acceptsMediaKind, ad as assertPreviewEnvironment, ah as createPreviewMediaPoster, ai as createPreviewMediaUploader, aj as createPreviewWebhookClient, al as isConversationSimulatorClient, am as mediaKindOf, an as mediaTypeOf, ao as signPreviewPayload, ap as simulatorPanelLabelsOf, aq as toConversationSimulatorClient } from '../ConversationSimulatorPanel--5fIzXWY.js';
3
+ import { InteractiveReplyOption } from '@adatechnology/meta-whatsapp-contracts/testing';
2
4
  import * as react from 'react';
3
- import { InteractiveReplyOption, InboundMediaType } from '@adatechnology/meta-whatsapp-contracts/testing';
5
+ export { PREVIEW_MEDIA_ID_PREFIX } from '@adatechnology/meta-whatsapp-contracts';
4
6
 
5
7
  /**
6
8
  * Estado em memória que alimenta o preview de atendimento humano. Mock de API e mock de SSE
@@ -112,122 +114,77 @@ declare const PREVIEW_MESSAGES: Readonly<Record<string, readonly MessagePayload[
112
114
  declare const PREVIEW_DOCUMENTS: Readonly<Record<string, readonly ConversationDocument[]>>;
113
115
 
114
116
  /**
115
- * Cliente que entrega mensagens do preview no webhook real, assinadas com HMAC a mesma validação
116
- * de staging e produção, sem rota alternativa e sem bypass. Do ponto de vista da API, este cliente
117
- * é indistinguível da Meta; o que muda é apenas quem assina.
117
+ * Cliente do preview que NÃO carrega segredo: em vez de montar e assinar o payload da Meta no
118
+ * navegador, manda um comando semântico (`{ kind: 'text', text }`) para uma rota do próprio host,
119
+ * autenticada pela sessão que o painel tem. Quem monta o payload e assina é o servidor, com o
120
+ * app secret que nunca sai de lá.
118
121
  *
119
- * Assina com WebCrypto porque `node:crypto` não existe no navegador. Os builders vêm dos contratos
120
- * (isomórficos) justamente para que o mesmo payload seja montado nos dois runtimes.
122
+ * Por que esta fábrica existe ao lado de `createPreviewWebhookClient`: assinar no navegador exige o
123
+ * app secret dentro do bundle, e bundle é público por definição — em qualquer ambiente com URL
124
+ * acessível isso é o mesmo que publicar o segredo. Com o segredo vazado, qualquer um forja webhooks
125
+ * válidos daquele app: injeta mensagens de qualquer número e dispara os fluxos. `createPreviewWebhook
126
+ * Client` continua servindo para execução puramente local (docker de dev, onde o bundle não é
127
+ * servido para ninguém); para qualquer ambiente publicado, a ponte é o caminho.
121
128
  *
122
- * ⚠️ Isto carrega o app secret de DESENVOLVIMENTO no bundle. existe para o docker local, e
123
- * `assertPreviewEnvironment` recusa rodar em produção um segredo de dev vazado é irrelevante,
124
- * mas o hábito de embarcar segredo em frontend não é.
129
+ * O pacote não decide autenticação: o host injeta `sendCommand` (ou `headers` + `fetchImplementation`),
130
+ * porque token, cookie e cabeçalho de sessão são do produto, não da biblioteca.
125
131
  */
126
132
 
127
- type PreviewWebhookClient = {
128
- sendText(text: string): Promise<void>;
129
- sendButtonReply(reply: InteractiveReplyOption): Promise<void>;
130
- sendListReply(reply: InteractiveReplyOption): Promise<void>;
131
- sendAudio(mediaId: string): Promise<void>;
132
- sendMedia(params: SendPreviewMediaParams): Promise<void>;
133
- };
134
- type SendPreviewMediaParams = {
135
- readonly mediaType: InboundMediaType;
136
- /**
137
- * Id que o host já usa para buscar o arquivo. Não é bytes: o webhook da Meta entrega mídia por
138
- * referência, e o consumidor baixa depois — mandar base64 aqui simularia um payload que a Meta
139
- * nunca produz, e o caminho testado deixaria de ser o de produção.
140
- */
133
+ /**
134
+ * Comando semântico entregue ao host. É deliberadamente o QUE o cliente fez, não o payload da Meta:
135
+ * se o navegador mandasse o payload pronto, a rota viraria um injetor de webhook arbitrário para
136
+ * quem tivesse sessão. Mandando a intenção, o servidor é quem escolhe a forma.
137
+ */
138
+ type PreviewInboundCommand = {
139
+ readonly kind: 'text';
140
+ readonly from: string;
141
+ readonly text: string;
142
+ } | {
143
+ readonly kind: 'buttonReply';
144
+ readonly from: string;
145
+ readonly reply: InteractiveReplyOption;
146
+ } | {
147
+ readonly kind: 'listReply';
148
+ readonly from: string;
149
+ readonly reply: InteractiveReplyOption;
150
+ } | {
151
+ readonly kind: 'audio';
152
+ readonly from: string;
141
153
  readonly mediaId: string;
142
- readonly mimeType?: string;
143
- readonly filename?: string;
144
- readonly caption?: string;
145
- };
146
- type CreatePreviewWebhookClientParams = {
147
- readonly webhookUrl: string;
148
- readonly appSecret: string;
154
+ } | ({
155
+ readonly kind: 'media';
149
156
  readonly from: string;
150
- readonly phoneNumberId?: string;
151
- readonly fetchImplementation?: typeof fetch;
152
- };
153
- declare class PreviewInProductionError extends Error {
154
- constructor();
155
- }
156
- declare class PreviewWebhookRejectedError extends Error {
157
+ } & SendPreviewMediaParams);
158
+ type SendPreviewInboundCommand = (command: PreviewInboundCommand) => Promise<void>;
159
+ declare class PreviewBridgeRejectedError extends Error {
157
160
  readonly status: number;
158
161
  constructor(status: number);
159
162
  }
160
- /**
161
- * Falha alto em vez de degradar em silêncio: um preview que "quase funciona" em produção é pior
162
- * que um que se recusa a montar.
163
- */
164
- declare function assertPreviewEnvironment(isProduction: boolean): void;
165
- /**
166
- * Assina um texto qualquer com o app secret, no mesmo formato do header da Meta.
167
- *
168
- * Exportada porque o preview precisa provar identidade em MAIS de um lugar: além de entregar a
169
- * mensagem no webhook, ele lê o transcript de volta — e ler pela API de admin exigia uma sessão que
170
- * a aba do simulador não tem. Assinar a leitura com o segredo que ele já carrega resolve sem token
171
- * de admin e sem rota aberta.
172
- */
173
- declare function signPreviewPayload(params: {
174
- rawBody: string;
175
- appSecret: string;
176
- }): Promise<string>;
177
- declare function createPreviewWebhookClient(params: CreatePreviewWebhookClientParams): PreviewWebhookClient;
178
-
179
- type ConversationPreviewProps = {
180
- client: PreviewWebhookClient;
181
- sse: SSEProvider;
182
- conversationId: string;
183
- loadMessages: (conversationId: string) => Promise<MessagePayload[]>;
184
- placeholder?: string;
163
+ type CreatePreviewBridgeClientParams = {
164
+ readonly from: string;
185
165
  /**
186
- * Recarrega o transcript a cada N ms. Serve a host SEM stream: a resposta do bot é assíncrona, e
187
- * sem SSE nem polling elaapareceria no próximo envio — o sintoma é "às vezes ele não
188
- * responde". Ausente, não faz polling (host com SSE não precisa).
166
+ * Entrega o comando. Use quando o host tem um cliente HTTP com sessão, interceptors e refresh
167
+ * de token reimplementar isso aqui duplicaria a autenticação do produto.
189
168
  */
190
- pollIntervalMs?: number;
169
+ readonly sendCommand?: SendPreviewInboundCommand;
170
+ /** Alternativa a `sendCommand` para hosts sem cliente HTTP próprio. */
171
+ readonly endpointUrl?: string;
172
+ readonly headers?: Readonly<Record<string, string>>;
173
+ readonly fetchImplementation?: typeof fetch;
191
174
  /**
192
- * Como transformar um arquivo do disco (ou o áudio gravado) na referência que o webhook carrega.
193
- * O caminho da Meta entrega mídia por `id`, e quem sabe hospedar o arquivo é o host — a SDK não
194
- * inventa um endpoint de upload. Ausente, o compositor não oferece anexo nem gravação: melhor um
195
- * botão que não existe do que um que falha ao ser tocado.
175
+ * Rota que guarda o áudio gravado. Por padrão, `/v1/preview/media` na origem do `endpointUrl`.
176
+ *
177
+ * Aqui não assinatura a calcular: a ponte existe justamente para não ter segredo no navegador,
178
+ * e a rota é protegida pela sessão do painel os mesmos `headers` do comando valem para o upload.
196
179
  */
197
- uploadMedia?: (file: File) => Promise<PreviewUploadedMedia>;
198
- };
199
- type PreviewUploadedMedia = {
200
- readonly mediaId: string;
201
- readonly mimeType?: string;
202
- readonly filename?: string;
180
+ readonly mediaUploadUrl?: string;
181
+ /**
182
+ * Substitui o upload embutido. Necessário para host que só passa `sendCommand`: sem `endpointUrl`
183
+ * não há origem a derivar, e sem destino o gravador não é desenhado.
184
+ */
185
+ readonly uploadMedia?: (file: File) => Promise<PreviewUploadedMedia>;
203
186
  };
204
- /** Deriva o tipo de mídia do WhatsApp a partir do MIME do arquivo escolhido. */
205
- declare function mediaTypeOf(mimeType: string): SendPreviewMediaParams['mediaType'];
206
- declare function ConversationPreview({ client, sse, conversationId, loadMessages, placeholder, pollIntervalMs, uploadMedia, }: ConversationPreviewProps): react.JSX.Element;
207
-
208
- /**
209
- * Gravação de áudio no simulador, pelo microfone do próprio navegador.
210
- *
211
- * Existe porque áudio é o formato que mais chega de cliente real e o que mais quebra fluxo: sem
212
- * poder gravar aqui, testar o caminho de transcrição exigia mandar mensagem do celular de alguém.
213
- *
214
- * O arquivo gravado sai daqui como `File` e segue exatamente o mesmo caminho de um anexo — quem
215
- * hospeda e devolve o `mediaId` é o host, via `uploadMedia`.
216
- */
217
- interface AudioRecorderButtonLabels {
218
- start: string;
219
- stop: string;
220
- unsupported: string;
221
- denied: string;
222
- }
223
- declare const DEFAULT_AUDIO_RECORDER_BUTTON_LABELS: AudioRecorderButtonLabels;
224
- interface AudioRecorderButtonProps {
225
- onRecorded: (file: File) => void | Promise<void>;
226
- onFailure?: (message: string) => void;
227
- labels?: Partial<AudioRecorderButtonLabels>;
228
- disabled?: boolean;
229
- }
230
- declare function AudioRecorderButton({ onRecorded, onFailure, labels, disabled }: AudioRecorderButtonProps): react.JSX.Element;
187
+ declare function createPreviewBridgeClient(params: CreatePreviewBridgeClientParams): PreviewWebhookClient;
231
188
 
232
189
  /**
233
190
  * Roteiro que mantém o preview vivo: sem tráfego chegando, a inbox é uma tela estática e as
@@ -327,4 +284,4 @@ type MediaTypesPreviewProps = {
327
284
  };
328
285
  declare function MediaTypesPreview({ conversationId, className, }: MediaTypesPreviewProps): react.JSX.Element;
329
286
 
330
- export { type AppendMessageParams, AudioRecorderButton, type AudioRecorderButtonLabels, type AudioRecorderButtonProps, ConversationPreview, type ConversationPreviewProps, type CreateMockConversationsApiParams, type CreateMockSSEProviderParams, type CreatePreviewStoreParams, type CreatePreviewWebhookClientParams, DEFAULT_AUDIO_RECORDER_BUTTON_LABELS, DEFAULT_PREVIEW_SCRIPT, GLOBAL_CHANNEL, type ListConversationsFilters, MEDIA_TYPES_CONVERSATION_ID, MediaTypesPreview, type MediaTypesPreviewProps, type MockEventSource, PREVIEW_CONVERSATIONS, PREVIEW_DOCUMENTS, PREVIEW_FILE_SAMPLES, PREVIEW_MESSAGES, type PreviewEmission, PreviewInProductionError, type PreviewScriptStep, type PreviewStore, type PreviewStoreListener, type PreviewUploadedMedia, type PreviewWebhookClient, PreviewWebhookRejectedError, type SendPreviewMediaParams, type SetModeParams, type StartPreviewScriptParams, assertPreviewEnvironment, conversationChannel, createMockConversationsApi, createMockEventSource, createMockSSEProvider, createPreviewMediaResolver, createPreviewStore, createPreviewWebhookClient, mediaTypeOf, previewFileBase64, previewFileUrl, resolvePreviewFileSample, signPreviewPayload, startPreviewScript };
287
+ export { type AppendMessageParams, type CreateMockConversationsApiParams, type CreateMockSSEProviderParams, type CreatePreviewBridgeClientParams, type CreatePreviewStoreParams, DEFAULT_PREVIEW_SCRIPT, GLOBAL_CHANNEL, type ListConversationsFilters, MEDIA_TYPES_CONVERSATION_ID, MediaTypesPreview, type MediaTypesPreviewProps, type MockEventSource, PREVIEW_CONVERSATIONS, PREVIEW_DOCUMENTS, PREVIEW_FILE_SAMPLES, PREVIEW_MESSAGES, PreviewBridgeRejectedError, type PreviewEmission, type PreviewInboundCommand, type PreviewScriptStep, type PreviewStore, type PreviewStoreListener, PreviewWebhookClient, type SendPreviewInboundCommand, SendPreviewMediaParams, type SetModeParams, type StartPreviewScriptParams, conversationChannel, createMockConversationsApi, createMockEventSource, createMockSSEProvider, createPreviewBridgeClient, createPreviewMediaResolver, createPreviewStore, previewFileBase64, previewFileUrl, resolvePreviewFileSample, startPreviewScript };