@adatechnology/conversations-ui 0.1.0-rc.9 → 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.
- package/dist/ConversationSimulatorPanel--5fIzXWY.d.ts +804 -0
- package/dist/{chunk-G5BM3VBP.js → chunk-BJNRLLDO.js} +1248 -282
- package/dist/chunk-DKPXKQGC.js +110 -0
- package/dist/{chunk-2AYDBWNE.js → chunk-WCBDXZ3X.js} +13 -3
- package/dist/flows/index.d.ts +422 -5
- package/dist/flows/index.js +2502 -676
- package/dist/index.d.ts +919 -17
- package/dist/index.js +3676 -675
- package/dist/preview/index.d.ts +62 -105
- package/dist/preview/index.js +162 -284
- package/dist/styles.css +893 -0
- package/package.json +9 -8
- package/src/AudioPlayer.tsx +8 -0
- package/src/AudioRecorderButton.test.tsx +30 -0
- package/src/AudioRecorderButton.tsx +248 -0
- package/src/AudioTranscription.test.tsx +115 -0
- package/src/AudioTranscription.tsx +252 -0
- package/src/Avatar.tsx +1 -1
- package/src/ConversationContextPanel.tsx +218 -44
- package/src/ConversationDocumentsPanel.tsx +11 -6
- package/src/ConversationHeader.test.tsx +66 -0
- package/src/ConversationHeader.tsx +147 -47
- package/src/ConversationListItem.tsx +8 -6
- package/src/ConversationLocalesProvider.tsx +28 -0
- package/src/ConversationRow.tsx +53 -7
- package/src/DarkModeToggle.test.tsx +76 -0
- package/src/DarkModeToggle.tsx +92 -0
- package/src/DocumentsLibrary.tsx +67 -7
- package/src/EmojiPicker.tsx +2 -1
- package/src/InteractiveMessage.tsx +3 -0
- package/src/Lightbox.tsx +1 -1
- package/src/MediaRenderer.tsx +88 -15
- package/src/MessageBubble.test.tsx +41 -0
- package/src/MessageBubble.tsx +47 -5
- package/src/MessageComposer.test.tsx +35 -0
- package/src/MessageComposer.tsx +122 -17
- package/src/MessageText.tsx +2 -1
- package/src/MessageTimestamp.tsx +2 -1
- package/src/RichMessageComposer.test.tsx +113 -0
- package/src/RichMessageComposer.tsx +551 -0
- package/src/SimpleEmojiPicker.tsx +5 -3
- package/src/StatusTicks.tsx +1 -1
- package/src/Toast.tsx +4 -0
- package/src/Tooltip.test.ts +42 -0
- package/src/Tooltip.tsx +167 -0
- package/src/Wallpaper.tsx +27 -13
- package/src/WhatsAppMessageEditor.tsx +10 -7
- package/src/WindowExpiredNotice.tsx +12 -4
- package/src/{preview/audioRecorderFormat.test.ts → audioRecorderFormat.test.ts} +1 -1
- package/src/buildOutput.test.ts +79 -0
- package/src/composer.constant.ts +33 -0
- package/src/conversationTranscript.test.ts +57 -0
- package/src/conversationTranscript.ts +29 -4
- package/src/conversationWindow.ts +7 -5
- package/src/documentTypeLabel.test.ts +57 -0
- package/src/documents/DocumentsWorkspace.tsx +550 -0
- package/src/documents/index.ts +8 -0
- package/src/documents/labels.ts +92 -0
- package/src/flows/FlowConnectionEdge.tsx +104 -0
- package/src/flows/FlowGroupHeader.tsx +12 -2
- package/src/flows/FlowLegend.tsx +125 -0
- package/src/flows/FlowMapCanvas.tsx +15 -12
- package/src/flows/FlowMapNode.tsx +4 -1
- package/src/flows/FlowNodeCard.tsx +219 -34
- package/src/flows/FlowNodePanel.tsx +153 -39
- package/src/flows/FlowPalette.tsx +156 -70
- package/src/flows/FlowPortalNode.tsx +1 -1
- package/src/flows/FlowWhatsAppPreview.tsx +14 -3
- package/src/flows/FlowsWorkspace.tsx +1255 -0
- package/src/flows/flowCanvasModel.test.ts +456 -0
- package/src/flows/flowCanvasModel.ts +378 -0
- package/src/flows/flowEditorOps.test.ts +276 -0
- package/src/flows/flowEditorOps.ts +202 -0
- package/src/flows/flowGraph.ts +78 -53
- package/src/flows/flowMenuPlacement.test.ts +130 -0
- package/src/flows/flowMenuPlacement.ts +86 -0
- package/src/flows/index.ts +51 -2
- package/src/flows/labels.ts +180 -0
- package/src/flows/workspaceContract.test.ts +126 -0
- package/src/hooks/useContainerWidth.ts +35 -0
- package/src/hooks/useConversationRealtime.ts +10 -8
- package/src/hooks/useScrollToLatestMessage.ts +127 -0
- package/src/hooks/useUrlFilterState.ts +107 -0
- package/src/icon.constant.ts +12 -0
- package/src/index.ts +100 -0
- package/src/lib/composer-formatting.test.ts +78 -0
- package/src/lib/composer-formatting.ts +145 -0
- package/src/lib/whatsapp-formatting.test.tsx +37 -0
- package/src/lib/whatsapp-formatting.tsx +28 -3
- package/src/listing/index.tsx +202 -0
- package/src/pagination.constant.ts +10 -0
- package/src/preview/ConversationPreview.tsx +84 -45
- package/src/preview/ConversationSimulatorClient.ts +143 -0
- package/src/preview/ConversationSimulatorPanel.test.tsx +55 -0
- package/src/preview/ConversationSimulatorPanel.tsx +131 -0
- package/src/preview/createPreviewBridgeClient.test.ts +92 -0
- package/src/preview/createPreviewBridgeClient.ts +124 -0
- package/src/preview/createPreviewMediaUploader.ts +82 -0
- package/src/preview/createPreviewWebhookClient.test.ts +96 -0
- package/src/preview/createPreviewWebhookClient.ts +99 -3
- package/src/preview/index.ts +36 -2
- package/src/preview/previewMediaUploader.test.ts +61 -0
- package/src/providers/ConversationsProvider.tsx +8 -6
- package/src/providers/types.ts +59 -2
- package/src/quickReply.test.ts +58 -0
- package/src/replyLatency.test.ts +71 -0
- package/src/replyLatency.ts +57 -0
- package/src/settings/MessagesWorkspace.tsx +571 -0
- package/src/settings/TopicsForm.tsx +2 -0
- package/src/settings/TranscriptionSettingsForm.test.tsx +81 -0
- package/src/settings/TranscriptionSettingsForm.tsx +190 -0
- package/src/settings/WelcomeFarewellForm.tsx +1 -0
- package/src/settings/WhatsAppCreateTemplateForm.tsx +1 -0
- package/src/settings/WhatsAppTemplateSettingsForm.tsx +5 -2
- package/src/settings/WhatsAppTemplatesSettings.test.tsx +61 -0
- package/src/settings/WhatsAppTemplatesSettings.tsx +22 -2
- package/src/styles.css +858 -0
- package/src/theme.ts +13 -0
- package/src/types.ts +26 -0
- package/src/workspace/BulkTemplateModal.tsx +132 -0
- package/src/workspace/ConversationPane.tsx +432 -0
- package/src/workspace/ConversationsInboxList.tsx +194 -0
- package/src/workspace/ConversationsWorkspace.tsx +423 -0
- package/src/workspace/index.ts +17 -0
- package/src/workspace/labels.test.ts +17 -0
- package/src/workspace/labels.ts +85 -0
- package/src/workspace/useConversationsInbox.ts +332 -0
- package/dist/types-B5C1DLu1.d.ts +0 -365
- package/src/preview/AudioRecorderButton.tsx +0 -117
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import react__default, { ReactNode, CSSProperties, FormEvent } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
export {
|
|
2
|
+
import react__default, { ReactNode, CSSProperties, UIEvent, FormEvent, RefObject } from 'react';
|
|
3
|
+
import { U as MessagePayload, a3 as ResolveMediaUrl, P as InteractiveSelection, V as MessageTranscription, N as InteractivePayload, x as ConversationsFeatures, u as ConversationSummary, j as ConversationChannel, Q as ListConversationsParams, w as ConversationsApi, a5 as SSEProvider, aa as TranscriptionMode, R as ListDocumentsParams, k as ConversationDocument, v as ConversationTemplate, f as ChannelFilter, g as ChannelFilterOption, q as ConversationSimulatorClient, s as ConversationSimulatorPanelLabels, Z as PreviewUploadedMedia } from './ConversationSimulatorPanel--5fIzXWY.js';
|
|
4
|
+
export { A as AudioRecorderButton, a as AudioRecorderButtonLabels, b as AudioRecorderButtonProps, C as CHANNEL_CAPABILITIES, c as CHANNEL_FILTER_ALL, d as CONVERSATION_CHANNEL, e as ChannelCapabilities, h as CompanyDocument, i as CompanyDocumentPage, l as ConversationDocumentPage, m as ConversationEventSource, n as ConversationPage, y as ConversationsTheme, z as ConversationsUIConfig, E as DEFAULT_AUDIO_RECORDER_BUTTON_LABELS, F as DEFAULT_CONVERSATION_CHANNEL, H as DEFAULT_MAX_RECORDING_MILLISECONDS, J as FormatContactHandleParams, K as HANDLE_KIND, L as HandleKind, M as InteractiveOption, O as InteractiveSection, S as MediaRenderer, T as MediaRendererProps, a1 as REOPEN_MECHANISM, a2 as ReopenMechanism, a7 as SendSimulatorMediaParams, a8 as SimulatorMediaKind, ab as TranscriptionStatus, ae as capabilitiesOf, af as channelFiltersFor, ag as contactFlag, ak as formatContactHandle } from './ConversationSimulatorPanel--5fIzXWY.js';
|
|
5
|
+
import '@adatechnology/meta-whatsapp-contracts/testing';
|
|
5
6
|
|
|
6
7
|
interface MessageBubbleProps {
|
|
7
8
|
message: MessagePayload;
|
|
@@ -21,9 +22,15 @@ interface MessageBubbleProps {
|
|
|
21
22
|
* no histórico da inbox: o operador vê o que foi oferecido, sem responder no lugar do cliente.
|
|
22
23
|
*/
|
|
23
24
|
onInteractiveSelect?: (selection: InteractiveSelection) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Pede a transcrição do áudio. Ausente, o balão usa `transcribeAudio` do `ConversationsApi` do
|
|
27
|
+
* contexto — passe apenas para sobrescrever. Sem nenhum dos dois, o bloco de transcrição só exibe
|
|
28
|
+
* o que já veio pronto do backend.
|
|
29
|
+
*/
|
|
30
|
+
onTranscribeAudio?: (messageId: string) => Promise<MessageTranscription | void>;
|
|
24
31
|
className?: string;
|
|
25
32
|
}
|
|
26
|
-
declare function MessageBubble({ message, isMine, senderName, isFirstInGroup, isSelecting, isSelected, onToggleSelect, onResolveMediaUrl, onInteractiveSelect, className, }: MessageBubbleProps): react.JSX.Element;
|
|
33
|
+
declare function MessageBubble({ message, isMine, senderName, isFirstInGroup, isSelecting, isSelected, onToggleSelect, onResolveMediaUrl, onInteractiveSelect, onTranscribeAudio, className, }: MessageBubbleProps): react.JSX.Element;
|
|
27
34
|
|
|
28
35
|
interface InteractiveMessageLabels {
|
|
29
36
|
/** Fallback do rótulo do botão que abre a lista, quando o payload não traz um. */
|
|
@@ -43,8 +50,18 @@ interface ConversationWallpaperProps {
|
|
|
43
50
|
className?: string;
|
|
44
51
|
/** Ajusta ou substitui o fundo padrão — para produto com identidade visual própria. */
|
|
45
52
|
style?: CSSProperties;
|
|
53
|
+
/**
|
|
54
|
+
* Rolagem da área de mensagens. Par do `ref`: é este elemento que rola, então é aqui que
|
|
55
|
+
* `useScrollToLatestMessage` observa a posição para saber se o operador está acompanhando o fim.
|
|
56
|
+
*/
|
|
57
|
+
onScroll?: (event: UIEvent<HTMLDivElement>) => void;
|
|
46
58
|
}
|
|
47
|
-
|
|
59
|
+
/**
|
|
60
|
+
* `forwardRef` porque quem controla a rolagem é de fora — o hook precisa do elemento para saltar
|
|
61
|
+
* até a última mensagem. `forwardRef` e não `ref` como prop: o pacote suporta React 18, onde
|
|
62
|
+
* ref-como-prop ainda não existe.
|
|
63
|
+
*/
|
|
64
|
+
declare const ConversationWallpaper: react.ForwardRefExoticComponent<ConversationWallpaperProps & react.RefAttributes<HTMLDivElement>>;
|
|
48
65
|
|
|
49
66
|
interface ConversationLocales {
|
|
50
67
|
bubble: {
|
|
@@ -68,6 +85,20 @@ interface ConversationLocales {
|
|
|
68
85
|
untitledDocument: string;
|
|
69
86
|
downloadFile: string;
|
|
70
87
|
};
|
|
88
|
+
transcription: {
|
|
89
|
+
label: string;
|
|
90
|
+
copy: string;
|
|
91
|
+
copied: string;
|
|
92
|
+
transcribe: string;
|
|
93
|
+
transcribing: string;
|
|
94
|
+
retry: string;
|
|
95
|
+
failed: string;
|
|
96
|
+
/** Áudio processado sem fala detectada — distinto de "não transcrito". */
|
|
97
|
+
empty: string;
|
|
98
|
+
unsupported: string;
|
|
99
|
+
showMore: string;
|
|
100
|
+
showLess: string;
|
|
101
|
+
};
|
|
71
102
|
selection: {
|
|
72
103
|
select: string;
|
|
73
104
|
};
|
|
@@ -92,6 +123,33 @@ interface AudioPlayerProps {
|
|
|
92
123
|
}
|
|
93
124
|
declare function AudioPlayer({ src, isMine }: AudioPlayerProps): react.JSX.Element;
|
|
94
125
|
|
|
126
|
+
interface AudioTranscriptionProps {
|
|
127
|
+
transcription?: MessageTranscription | null;
|
|
128
|
+
/**
|
|
129
|
+
* Pede a transcrição ao backend. Ausente, o bloco nunca oferece o botão — um host em modo
|
|
130
|
+
* automático não tem rota para isso, e desenhar um botão que estoura no clique é pior do que não
|
|
131
|
+
* desenhar nada.
|
|
132
|
+
*
|
|
133
|
+
* O que for devolvido é exibido na hora. Sem isso o texto só apareceria no próximo refetch da
|
|
134
|
+
* lista, e transcrição não emite evento de tempo real: o operador clicaria, veria o spinner
|
|
135
|
+
* terminar e continuaria sem o texto na tela.
|
|
136
|
+
*/
|
|
137
|
+
onTranscribe?: () => Promise<MessageTranscription | void>;
|
|
138
|
+
isMine?: boolean;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Transcrição sob a nota de voz, com botão de copiar.
|
|
142
|
+
*
|
|
143
|
+
* Copiar é o motivo de o bloco existir: o operador cola o pedido do cliente no sistema interno, no
|
|
144
|
+
* orçamento, na busca. Seleção manual de texto dentro de um balão de chat é exatamente onde o
|
|
145
|
+
* arrasto do mouse falha — pega o balão vizinho, o horário, o nome do remetente.
|
|
146
|
+
*
|
|
147
|
+
* `navigator.clipboard.writeText` com try/catch silencioso, mesmo padrão de `MessageText.tsx`: a API
|
|
148
|
+
* exige contexto seguro e permissão, e um host servindo em HTTP simples não a tem. Falhar sem alarme
|
|
149
|
+
* é o certo — o texto continua na tela para seleção manual.
|
|
150
|
+
*/
|
|
151
|
+
declare function AudioTranscription({ transcription, onTranscribe, isMine }: AudioTranscriptionProps): react.JSX.Element | null;
|
|
152
|
+
|
|
95
153
|
interface EmojiPickerLabels {
|
|
96
154
|
search: string;
|
|
97
155
|
noResults: string;
|
|
@@ -128,10 +186,30 @@ declare const EMOJI_CATEGORIES: readonly EmojiCategory[];
|
|
|
128
186
|
*/
|
|
129
187
|
declare function searchEmojis(query: string): readonly EmojiEntry[];
|
|
130
188
|
|
|
189
|
+
/**
|
|
190
|
+
* Mensagem pronta que o atendente cola no campo com um clique.
|
|
191
|
+
*
|
|
192
|
+
* `text` aceita string com `{{variavel}}` ou função: a string cobre o caso comum (copy fixa com o
|
|
193
|
+
* nome do cliente no meio) sem o host escrever código, e a função cobre o que precisa de lógica —
|
|
194
|
+
* escolher texto por produto, pluralizar, formatar moeda.
|
|
195
|
+
*/
|
|
196
|
+
interface QuickReply {
|
|
197
|
+
key: string;
|
|
198
|
+
/** O que aparece no chip, emoji incluso: `👋 Saudação`. */
|
|
199
|
+
label: string;
|
|
200
|
+
text: string | ((variables: Readonly<Record<string, string>>) => string);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Troca `{{nome}}` pelos valores passados. Variável ausente vira string vazia, e não o literal
|
|
204
|
+
* `{{nome}}`: mandar "Olá {{nome}}!" para o cliente é pior que mandar "Olá !".
|
|
205
|
+
*/
|
|
206
|
+
declare function applyQuickReplyVariables(template: string, variables?: Readonly<Record<string, string>>): string;
|
|
207
|
+
declare function resolveQuickReply(quickReply: QuickReply, variables?: Readonly<Record<string, string>>): string;
|
|
131
208
|
interface MessageComposerLabels {
|
|
132
209
|
emoji: string;
|
|
133
210
|
attach: string;
|
|
134
211
|
send: string;
|
|
212
|
+
removeAttachment: string;
|
|
135
213
|
}
|
|
136
214
|
declare const DEFAULT_MESSAGE_COMPOSER_LABELS: MessageComposerLabels;
|
|
137
215
|
interface MessageComposerProps {
|
|
@@ -145,11 +223,22 @@ interface MessageComposerProps {
|
|
|
145
223
|
maxLength?: number;
|
|
146
224
|
disabled?: boolean;
|
|
147
225
|
acceptedFileTypes?: string;
|
|
226
|
+
/**
|
|
227
|
+
* Ocupa o lugar do botão de enviar enquanto não há nada para enviar — é onde o WhatsApp põe o
|
|
228
|
+
* microfone. Fora do campo, o botão vira um bloco solto ao lado do pill e quebra a barra.
|
|
229
|
+
*/
|
|
230
|
+
idleAction?: ReactNode;
|
|
231
|
+
/** Mensagens prontas exibidas acima do campo. Vazio ou ausente, a faixa não é renderizada. */
|
|
232
|
+
quickReplies?: readonly QuickReply[];
|
|
233
|
+
/** Valores para `{{variavel}}` — tipicamente nome do cliente, produto, protocolo. */
|
|
234
|
+
quickReplyVariables?: Readonly<Record<string, string>>;
|
|
148
235
|
className?: string;
|
|
149
236
|
classNames?: Partial<MessageComposerClassNames>;
|
|
150
237
|
}
|
|
151
238
|
interface MessageComposerClassNames {
|
|
152
239
|
root: string;
|
|
240
|
+
quickReplies: string;
|
|
241
|
+
quickReply: string;
|
|
153
242
|
field: string;
|
|
154
243
|
}
|
|
155
244
|
/**
|
|
@@ -159,7 +248,91 @@ interface MessageComposerClassNames {
|
|
|
159
248
|
* Produto com regra própria passa `acceptedFileTypes`.
|
|
160
249
|
*/
|
|
161
250
|
declare const DEFAULT_ACCEPTED_FILE_TYPES: string;
|
|
162
|
-
declare const MessageComposer: ({ onSend, onAttach, value: externalValue, onChange: externalOnChange, features, placeholder, maxLength, disabled, acceptedFileTypes, className, classNames, labels, }: MessageComposerProps) => react.JSX.Element;
|
|
251
|
+
declare const MessageComposer: ({ onSend, onAttach, value: externalValue, onChange: externalOnChange, quickReplies, quickReplyVariables, features, placeholder, maxLength, disabled, acceptedFileTypes, idleAction, className, classNames, labels, }: MessageComposerProps) => react.JSX.Element;
|
|
252
|
+
|
|
253
|
+
/** Cada ação que a barra sabe oferecer. `toolbar` decide quais delas aparecem. */
|
|
254
|
+
declare const RICH_COMPOSER_ACTION: {
|
|
255
|
+
readonly BOLD: "bold";
|
|
256
|
+
readonly ITALIC: "italic";
|
|
257
|
+
readonly STRIKETHROUGH: "strikethrough";
|
|
258
|
+
readonly MONOSPACE: "monospace";
|
|
259
|
+
readonly EMOJI: "emoji";
|
|
260
|
+
readonly ATTACH: "attach";
|
|
261
|
+
readonly VARIABLES: "variables";
|
|
262
|
+
};
|
|
263
|
+
type RichComposerAction = (typeof RICH_COMPOSER_ACTION)[keyof typeof RICH_COMPOSER_ACTION];
|
|
264
|
+
/** Texto das dicas. O host troca só o que quiser; o resto fica no padrão. */
|
|
265
|
+
interface RichComposerTooltips {
|
|
266
|
+
bold: string;
|
|
267
|
+
italic: string;
|
|
268
|
+
strikethrough: string;
|
|
269
|
+
monospace: string;
|
|
270
|
+
emoji: string;
|
|
271
|
+
attach: string;
|
|
272
|
+
send: string;
|
|
273
|
+
variables: string;
|
|
274
|
+
/** Botão que abre a formatação recolhida, quando a barra está estreita demais para os quatro. */
|
|
275
|
+
formatting: string;
|
|
276
|
+
}
|
|
277
|
+
declare const DEFAULT_RICH_COMPOSER_TOOLTIPS: RichComposerTooltips;
|
|
278
|
+
/**
|
|
279
|
+
* Um valor que o operador pode inserir no texto sem digitar. `value` é o que entra no campo — pode
|
|
280
|
+
* ser o dado já resolvido ("Anderson") ou um marcador a resolver depois ("{{nome}}"): quem decide é
|
|
281
|
+
* o host, porque só ele sabe se a substituição acontece aqui ou no envio.
|
|
282
|
+
*/
|
|
283
|
+
interface RichComposerVariable {
|
|
284
|
+
id: string;
|
|
285
|
+
label: string;
|
|
286
|
+
value: string;
|
|
287
|
+
/** Prévia do que será inserido, para o operador conferir antes de tocar. */
|
|
288
|
+
tooltip?: string;
|
|
289
|
+
}
|
|
290
|
+
/** Uma resposta rápida: o que o operador lê no chip e o que cai no campo ao tocar nele. */
|
|
291
|
+
interface RichComposerQuickReply {
|
|
292
|
+
id: string;
|
|
293
|
+
label: string;
|
|
294
|
+
text: string;
|
|
295
|
+
/** Dica ao passar o mouse. Sem isto, o chip não tem `title` — rótulo curto já se explica. */
|
|
296
|
+
tooltip?: string;
|
|
297
|
+
}
|
|
298
|
+
/** Handle imperativo: `contentEditable` controlado pelo React perde o cursor a cada tecla. */
|
|
299
|
+
interface RichMessageComposerHandle {
|
|
300
|
+
setContent: (text: string) => void;
|
|
301
|
+
clear: () => void;
|
|
302
|
+
focus: () => void;
|
|
303
|
+
}
|
|
304
|
+
interface RichMessageComposerProps {
|
|
305
|
+
/** Texto na notação do WhatsApp (`*negrito*`), não HTML. */
|
|
306
|
+
value: string;
|
|
307
|
+
onChange: (value: string) => void;
|
|
308
|
+
onSend: () => void;
|
|
309
|
+
onAttachFiles?: (files: FileList) => void;
|
|
310
|
+
quickReplies?: RichComposerQuickReply[];
|
|
311
|
+
/** Variáveis que o operador pode inserir no texto. Vazio ou ausente, o botão não aparece. */
|
|
312
|
+
variables?: RichComposerVariable[];
|
|
313
|
+
/**
|
|
314
|
+
* Quais ações aparecem. Ausente, todas aparecem — menos as que não têm como funcionar (anexo sem
|
|
315
|
+
* `onAttachFiles` some sozinho, porque botão que não faz nada é pior que botão nenhum).
|
|
316
|
+
*/
|
|
317
|
+
toolbar?: Partial<Record<RichComposerAction, boolean>>;
|
|
318
|
+
tooltips?: Partial<RichComposerTooltips>;
|
|
319
|
+
placeholder?: string;
|
|
320
|
+
disabled?: boolean;
|
|
321
|
+
isSending?: boolean;
|
|
322
|
+
/** Ocupa o lugar do enviar enquanto não há nada para enviar — onde o WhatsApp põe o microfone. */
|
|
323
|
+
idleAction?: ReactNode;
|
|
324
|
+
/** Prévia dos anexos já escolhidos, desenhada pelo host acima da barra. */
|
|
325
|
+
attachmentsPreview?: ReactNode;
|
|
326
|
+
/**
|
|
327
|
+
* Há anexo na fila esperando envio. O campo não conhece a fila — é do host — e sem essa dica ele
|
|
328
|
+
* concluía que não havia nada a enviar: com uma imagem anexada e nenhum texto, o botão de enviar
|
|
329
|
+
* dava lugar ao `idleAction` e a imagem ficava presa.
|
|
330
|
+
*/
|
|
331
|
+
hasQueuedAttachments?: boolean;
|
|
332
|
+
acceptedFileTypes?: string;
|
|
333
|
+
className?: string;
|
|
334
|
+
}
|
|
335
|
+
declare const RichMessageComposer: react.ForwardRefExoticComponent<RichMessageComposerProps & react.RefAttributes<RichMessageComposerHandle>>;
|
|
163
336
|
|
|
164
337
|
interface WhatsAppMessageEditorLabels {
|
|
165
338
|
bold: string;
|
|
@@ -257,6 +430,22 @@ declare function ToastProvider({ children }: {
|
|
|
257
430
|
}): react.JSX.Element;
|
|
258
431
|
declare function useToast(): ToastContextValue;
|
|
259
432
|
|
|
433
|
+
/**
|
|
434
|
+
* Camada única de dicas do pacote.
|
|
435
|
+
*
|
|
436
|
+
* O `title` nativo existia em todos os botões e mesmo assim a dica "não aparecia": o navegador
|
|
437
|
+
* espera de 1 a 2 segundos antes de desenhar, o balão sai fora do tema e some ao menor movimento do
|
|
438
|
+
* mouse. Aqui a dica é do produto — aparece em 120ms, com o mesmo contraste do resto da interface.
|
|
439
|
+
*
|
|
440
|
+
* Um só ouvinte no documento atende a interface inteira: cada botão declara `data-cv-tooltip` e não
|
|
441
|
+
* precisa de estado, ref ou wrapper próprio. O balão é impresso em `document.body` via portal
|
|
442
|
+
* porque `overflow` de container (a régua de respostas rápidas rola na horizontal) recortaria um
|
|
443
|
+
* balão desenhado dentro do próprio botão.
|
|
444
|
+
*/
|
|
445
|
+
/** Atributo que marca um elemento como portador de dica. */
|
|
446
|
+
declare const TOOLTIP_ATTRIBUTE = "data-cv-tooltip";
|
|
447
|
+
declare function TooltipLayer(): react.ReactPortal | null;
|
|
448
|
+
|
|
260
449
|
interface StatusTicksProps {
|
|
261
450
|
status: string;
|
|
262
451
|
title?: string;
|
|
@@ -322,23 +511,23 @@ type ConversationWindow = (typeof CONVERSATION_WINDOW)[keyof typeof CONVERSATION
|
|
|
322
511
|
declare const WINDOW_FILTERS: readonly [{
|
|
323
512
|
readonly value: "all";
|
|
324
513
|
readonly label: "Todas";
|
|
325
|
-
readonly
|
|
514
|
+
readonly tone: "";
|
|
326
515
|
}, {
|
|
327
516
|
readonly value: "fresh";
|
|
328
517
|
readonly label: "<12h";
|
|
329
|
-
readonly
|
|
518
|
+
readonly tone: "fresh";
|
|
330
519
|
}, {
|
|
331
520
|
readonly value: "warning";
|
|
332
521
|
readonly label: "12-21h";
|
|
333
|
-
readonly
|
|
522
|
+
readonly tone: "warning";
|
|
334
523
|
}, {
|
|
335
524
|
readonly value: "critical";
|
|
336
525
|
readonly label: "21-24h";
|
|
337
|
-
readonly
|
|
526
|
+
readonly tone: "critical";
|
|
338
527
|
}, {
|
|
339
528
|
readonly value: "expired";
|
|
340
529
|
readonly label: ">24h";
|
|
341
|
-
readonly
|
|
530
|
+
readonly tone: "expired";
|
|
342
531
|
}];
|
|
343
532
|
type WindowOfParams = {
|
|
344
533
|
readonly lastInboundAt: string | null;
|
|
@@ -356,6 +545,45 @@ type WindowOfParams = {
|
|
|
356
545
|
declare function windowOf(params: WindowOfParams): ConversationWindow;
|
|
357
546
|
declare function formatStalledFor(lastAt: string, now: number): string;
|
|
358
547
|
|
|
548
|
+
/**
|
|
549
|
+
* Copyright (c) 2026 Ada Technology. MIT License.
|
|
550
|
+
*
|
|
551
|
+
* Há quanto tempo o cliente espera resposta.
|
|
552
|
+
*
|
|
553
|
+
* Não confundir com `conversationWindow`: aquilo é a janela de sessão da plataforma (o que o canal
|
|
554
|
+
* ainda deixa enviar), isto é serviço (quanto o cliente esperou). Os dois divergem justamente no
|
|
555
|
+
* caso que interessa — respondida a conversa, o relógio do SLA para e o da janela continua correndo.
|
|
556
|
+
*
|
|
557
|
+
* A conta sai de dados que a listagem já traz: se a última mensagem foi do cliente, ninguém
|
|
558
|
+
* respondeu ainda e a espera conta desde ela. Se a última foi nossa, não há espera pendente.
|
|
559
|
+
*/
|
|
560
|
+
declare const REPLY_LATENCY: {
|
|
561
|
+
/** Até 6h — dentro do combinado. */
|
|
562
|
+
readonly WITHIN: "within";
|
|
563
|
+
/** 6h a 12h — passou do combinado. */
|
|
564
|
+
readonly LATE: "late";
|
|
565
|
+
/** Acima de 12h. */
|
|
566
|
+
readonly CRITICAL: "critical";
|
|
567
|
+
};
|
|
568
|
+
type ReplyLatency = (typeof REPLY_LATENCY)[keyof typeof REPLY_LATENCY];
|
|
569
|
+
declare const REPLY_LATENCY_LATE_HOURS = 6;
|
|
570
|
+
declare const REPLY_LATENCY_CRITICAL_HOURS = 12;
|
|
571
|
+
type ReplyLatencyParams = {
|
|
572
|
+
/** Direção da última mensagem. Ausente = desconhecida, e aí não se afirma espera. */
|
|
573
|
+
readonly lastDirection?: 'inbound' | 'outbound' | undefined;
|
|
574
|
+
readonly lastInboundAt: string | null;
|
|
575
|
+
readonly now: number;
|
|
576
|
+
};
|
|
577
|
+
/**
|
|
578
|
+
* `null` quando não há espera a mostrar — conversa já respondida, ou cliente que nunca escreveu.
|
|
579
|
+
*
|
|
580
|
+
* Devolver uma faixa nesses casos encheria a lista de selos em conversa que não deve nada, e o
|
|
581
|
+
* alerta que aponta para todo lado não aponta para lugar nenhum.
|
|
582
|
+
*/
|
|
583
|
+
declare function replyLatencyOf(params: ReplyLatencyParams): ReplyLatency | null;
|
|
584
|
+
/** Só o que passou do combinado merece alerta — é o corte que a lista usa para destacar. */
|
|
585
|
+
declare function isReplyOverdue(latency: ReplyLatency | null): boolean;
|
|
586
|
+
|
|
359
587
|
type ConversationRowClassNames = {
|
|
360
588
|
root: string;
|
|
361
589
|
windowBar: string;
|
|
@@ -414,8 +642,8 @@ interface ConversationHeaderClassNames {
|
|
|
414
642
|
*/
|
|
415
643
|
interface ConversationHeaderUtility {
|
|
416
644
|
key: string;
|
|
417
|
-
/**
|
|
418
|
-
icon:
|
|
645
|
+
/** Ícone da biblioteca (lucide), no tamanho dos utilitários nativos: `<Play size={16} />`. */
|
|
646
|
+
icon: ReactNode;
|
|
419
647
|
label: string;
|
|
420
648
|
run: () => void;
|
|
421
649
|
active?: boolean;
|
|
@@ -440,20 +668,35 @@ declare function ConversationHeader({ conversation, busy, onTakeover, onReturnTo
|
|
|
440
668
|
|
|
441
669
|
/**
|
|
442
670
|
* "Suas Seleções": o que o bot já coletou na conversa. Para o atendente que assume no meio, é a
|
|
443
|
-
* diferença entre ler o transcript inteiro e ver o estado
|
|
671
|
+
* diferença entre ler o transcript inteiro e ver o estado de relance.
|
|
444
672
|
*
|
|
445
673
|
* O pacote não interpreta o contexto — ele é `Record<string, unknown>` e cada produto nomeia as
|
|
446
674
|
* próprias chaves. O host traduz para `entries`; aqui só se decide como mostrar.
|
|
675
|
+
*
|
|
676
|
+
* Desenho em paridade com financiamento-imobiliario-bot/apps/web/src/components/SelectionsSummary.tsx:
|
|
677
|
+
* card com gradiente, badge de contagem, pills de status no cabeçalho e grid de cards com borda
|
|
678
|
+
* colorida por estado. O que ficou diferente de lá, e por quê, está comentado no ponto.
|
|
447
679
|
*/
|
|
680
|
+
/**
|
|
681
|
+
* `completed` tem valor, `pending` ainda não foi coletado, `editing` é o cliente refazendo a
|
|
682
|
+
* resposta. O host só precisa mandar `status` para o terceiro caso — os dois primeiros saem do
|
|
683
|
+
* próprio `value`, e exigir o campo quebraria quem já usa o painel.
|
|
684
|
+
*/
|
|
685
|
+
type ConversationContextStatus = 'completed' | 'pending' | 'editing';
|
|
448
686
|
interface ConversationContextEntry {
|
|
449
687
|
key: string;
|
|
450
688
|
label: string;
|
|
451
689
|
value?: string | undefined;
|
|
452
690
|
icon?: string;
|
|
691
|
+
status?: ConversationContextStatus;
|
|
453
692
|
}
|
|
454
693
|
interface ConversationContextPanelLabels {
|
|
455
694
|
title: string;
|
|
456
695
|
empty: string;
|
|
696
|
+
collapse: string;
|
|
697
|
+
expand: string;
|
|
698
|
+
/** Placeholder do card ainda não coletado. */
|
|
699
|
+
notCollected: string;
|
|
457
700
|
}
|
|
458
701
|
declare const DEFAULT_CONVERSATION_CONTEXT_LABELS: ConversationContextPanelLabels;
|
|
459
702
|
interface ConversationContextPanelClassNames {
|
|
@@ -464,11 +707,23 @@ interface ConversationContextPanelClassNames {
|
|
|
464
707
|
}
|
|
465
708
|
interface ConversationContextPanelProps {
|
|
466
709
|
entries: readonly ConversationContextEntry[];
|
|
710
|
+
/**
|
|
711
|
+
* Estado inicial. Ausente, abre sozinho no desktop quando há algum dado preenchido.
|
|
712
|
+
*
|
|
713
|
+
* Existe porque "abre sozinho" nem sempre é o que o produto quer: com 1 de 6 campos preenchidos o
|
|
714
|
+
* painel ocupa altura mostrando quase só pendências, e empurra a conversa — que é o que se veio ver.
|
|
715
|
+
*/
|
|
716
|
+
defaultOpen?: boolean;
|
|
717
|
+
/**
|
|
718
|
+
* Rótulo do fluxo em curso, exibido em azul ao lado do título — no financiamento é o produto
|
|
719
|
+
* escolhido ("Consórcio", "MCMV"). Ausente, o cabeçalho fica só com título e contagens.
|
|
720
|
+
*/
|
|
721
|
+
flowLabel?: string;
|
|
467
722
|
labels?: Partial<ConversationContextPanelLabels>;
|
|
468
723
|
className?: string;
|
|
469
724
|
classNames?: Partial<ConversationContextPanelClassNames>;
|
|
470
725
|
}
|
|
471
|
-
declare function ConversationContextPanel({ entries, labels: labelsOverride, className, classNames, }: ConversationContextPanelProps): react.JSX.Element;
|
|
726
|
+
declare function ConversationContextPanel({ entries, defaultOpen, flowLabel, labels: labelsOverride, className, classNames, }: ConversationContextPanelProps): react.JSX.Element;
|
|
472
727
|
|
|
473
728
|
interface WindowExpiredNoticeLabels {
|
|
474
729
|
title: string;
|
|
@@ -512,6 +767,8 @@ interface DocumentsLibraryLabels {
|
|
|
512
767
|
sortMostRecent: string;
|
|
513
768
|
sortOldest: string;
|
|
514
769
|
clearFilters: string;
|
|
770
|
+
upload: string;
|
|
771
|
+
uploadError: string;
|
|
515
772
|
total: (count: number) => string;
|
|
516
773
|
page: (current: number, last: number) => string;
|
|
517
774
|
}
|
|
@@ -543,6 +800,160 @@ interface DocumentsLibraryProps {
|
|
|
543
800
|
}
|
|
544
801
|
declare function DocumentsLibrary({ perPage, onOpenConversation, labels: labelsOverride, className, classNames, }: DocumentsLibraryProps): react.JSX.Element | null;
|
|
545
802
|
|
|
803
|
+
type SortDirection = 'asc' | 'desc';
|
|
804
|
+
interface SortableHeadProps {
|
|
805
|
+
readonly label: string;
|
|
806
|
+
readonly field: string;
|
|
807
|
+
readonly activeField: string;
|
|
808
|
+
readonly direction: SortDirection;
|
|
809
|
+
readonly onSort: (field: string) => void;
|
|
810
|
+
readonly className?: string;
|
|
811
|
+
}
|
|
812
|
+
declare function SortableHead({ label, field, activeField, direction, onSort, className }: SortableHeadProps): react.JSX.Element;
|
|
813
|
+
interface FilterOption {
|
|
814
|
+
readonly value: string;
|
|
815
|
+
readonly label: string;
|
|
816
|
+
}
|
|
817
|
+
interface MultiSelectFilterProps {
|
|
818
|
+
readonly label: string;
|
|
819
|
+
readonly options: readonly FilterOption[];
|
|
820
|
+
readonly selected: readonly string[];
|
|
821
|
+
readonly onChange: (selected: readonly string[]) => void;
|
|
822
|
+
readonly className?: string;
|
|
823
|
+
}
|
|
824
|
+
declare function MultiSelectFilter({ label, options, selected, onChange, className }: MultiSelectFilterProps): react.JSX.Element;
|
|
825
|
+
interface BulkActionBarProps {
|
|
826
|
+
readonly selectedCount: number;
|
|
827
|
+
readonly selectedLabel: (count: number) => string;
|
|
828
|
+
readonly clearLabel: string;
|
|
829
|
+
readonly onClear: () => void;
|
|
830
|
+
readonly children?: ReactNode;
|
|
831
|
+
}
|
|
832
|
+
declare function BulkActionBar({ selectedCount, selectedLabel, clearLabel, onClear, children }: BulkActionBarProps): react.JSX.Element | null;
|
|
833
|
+
interface ListingPaginationProps {
|
|
834
|
+
readonly page: number;
|
|
835
|
+
readonly total: number;
|
|
836
|
+
readonly perPage: number;
|
|
837
|
+
readonly perPageOptions?: readonly number[];
|
|
838
|
+
readonly onPageChange: (page: number) => void;
|
|
839
|
+
readonly onPerPageChange?: (perPage: number) => void;
|
|
840
|
+
readonly labels: {
|
|
841
|
+
readonly show: string;
|
|
842
|
+
readonly perPage: string;
|
|
843
|
+
readonly total: (count: number) => string;
|
|
844
|
+
readonly page: (current: number, last: number) => string;
|
|
845
|
+
readonly previous: string;
|
|
846
|
+
readonly next: string;
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
declare function ListingPagination({ page, total, perPage, perPageOptions, onPageChange, onPerPageChange, labels, }: ListingPaginationProps): react.JSX.Element;
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Vocabulário da biblioteca de arquivos. Sobrescrevível campo a campo: o produto troca "cliente"
|
|
853
|
+
* por "lead" ou o idioma inteiro sem manter uma cópia da tela para isso.
|
|
854
|
+
*/
|
|
855
|
+
interface DocumentsWorkspaceLabels {
|
|
856
|
+
readonly title: string;
|
|
857
|
+
readonly subtitle: (total: number) => string;
|
|
858
|
+
readonly searchPlaceholder: string;
|
|
859
|
+
readonly empty: string;
|
|
860
|
+
readonly noResults: string;
|
|
861
|
+
readonly loading: string;
|
|
862
|
+
readonly failure: string;
|
|
863
|
+
readonly view: string;
|
|
864
|
+
readonly download: string;
|
|
865
|
+
readonly remove: string;
|
|
866
|
+
readonly removeConfirm: (filename: string) => string;
|
|
867
|
+
readonly openConversation: string;
|
|
868
|
+
readonly sourceFilter: string;
|
|
869
|
+
readonly categoryFilter: string;
|
|
870
|
+
readonly startDate: string;
|
|
871
|
+
readonly endDate: string;
|
|
872
|
+
readonly clearFilters: string;
|
|
873
|
+
readonly selectAllPage: string;
|
|
874
|
+
readonly selectRow: string;
|
|
875
|
+
readonly bulkSelected: (count: number) => string;
|
|
876
|
+
readonly bulkClear: string;
|
|
877
|
+
readonly bulkDownloadZip: string;
|
|
878
|
+
readonly bulkRemove: (count: number) => string;
|
|
879
|
+
readonly bulkRemoveConfirm: (count: number) => string;
|
|
880
|
+
readonly upload: string;
|
|
881
|
+
readonly uploadError: string;
|
|
882
|
+
readonly columnFilename: string;
|
|
883
|
+
readonly columnContact: string;
|
|
884
|
+
readonly columnType: string;
|
|
885
|
+
readonly columnSize: string;
|
|
886
|
+
readonly columnSource: string;
|
|
887
|
+
readonly columnDate: string;
|
|
888
|
+
readonly columnActions: string;
|
|
889
|
+
readonly sourceLabels: Readonly<Record<string, string>>;
|
|
890
|
+
readonly categoryLabels: Readonly<Record<string, string>>;
|
|
891
|
+
readonly show: string;
|
|
892
|
+
readonly perPage: string;
|
|
893
|
+
readonly total: (count: number) => string;
|
|
894
|
+
readonly page: (current: number, last: number) => string;
|
|
895
|
+
readonly previousPage: string;
|
|
896
|
+
readonly nextPage: string;
|
|
897
|
+
}
|
|
898
|
+
declare const DEFAULT_DOCUMENTS_WORKSPACE_LABELS: DocumentsWorkspaceLabels;
|
|
899
|
+
|
|
900
|
+
interface DocumentsWorkspaceClassNames {
|
|
901
|
+
root: string;
|
|
902
|
+
header: string;
|
|
903
|
+
filters: string;
|
|
904
|
+
table: string;
|
|
905
|
+
row: string;
|
|
906
|
+
status: string;
|
|
907
|
+
}
|
|
908
|
+
interface DocumentsWorkspaceProps {
|
|
909
|
+
readonly perPage?: number;
|
|
910
|
+
readonly perPageOptions?: readonly number[];
|
|
911
|
+
/** Abrir a conversa de origem. Ausente, o número aparece como texto e não como link. */
|
|
912
|
+
readonly onOpenConversation?: (conversationId: string) => void;
|
|
913
|
+
/** Origens filtráveis. Ausente, usa o vocabulário padrão (`customer`, `agent`, `bot`). */
|
|
914
|
+
readonly sources?: readonly FilterOption[];
|
|
915
|
+
/** Categorias de arquivo. Lista vazia esconde o filtro — nem todo host classifica anexo. */
|
|
916
|
+
readonly categories?: readonly FilterOption[];
|
|
917
|
+
/** Recorte por data. Desligado quando o backend não sabe filtrar por período. */
|
|
918
|
+
readonly dateFilter?: boolean;
|
|
919
|
+
/**
|
|
920
|
+
* Filtros do produto (cliente, unidade, campanha). Recebe os parâmetros extras atuais e devolve
|
|
921
|
+
* os controles; o que o produto guardar aqui viaja em `extra` para o `getAllDocuments`.
|
|
922
|
+
*/
|
|
923
|
+
readonly renderFilters?: (context: DocumentsFiltersContext) => ReactNode;
|
|
924
|
+
readonly labels?: Partial<DocumentsWorkspaceLabels>;
|
|
925
|
+
readonly className?: string;
|
|
926
|
+
readonly classNames?: Partial<DocumentsWorkspaceClassNames>;
|
|
927
|
+
/** Espelhar filtros e paginação na URL. Desligado em preview, onde não há rota de verdade. */
|
|
928
|
+
readonly syncUrl?: boolean;
|
|
929
|
+
}
|
|
930
|
+
interface DocumentsFiltersContext {
|
|
931
|
+
readonly extra: Readonly<Record<string, string | number>>;
|
|
932
|
+
readonly setExtra: (next: Readonly<Record<string, string | number>>) => void;
|
|
933
|
+
}
|
|
934
|
+
declare function DocumentsWorkspace({ perPage: initialPerPage, perPageOptions, onOpenConversation, sources, categories, dateFilter, renderFilters, labels: labelsOverride, className, classNames, syncUrl, }: DocumentsWorkspaceProps): react.JSX.Element | null;
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* Estado de listagem espelhado na URL — ordenação, filtros e paginação (regra `web.md` §7).
|
|
938
|
+
*
|
|
939
|
+
* Escrito sobre `history.replaceState` e não sobre um router: o pacote roda em três produtos com
|
|
940
|
+
* routers diferentes, e exigir um deles arrastaria dependência de framework para dentro do módulo.
|
|
941
|
+
* `replaceState` também é o comportamento certo aqui — filtrar não é navegar, e cada tecla digitada
|
|
942
|
+
* na busca não deve virar uma entrada no botão "voltar".
|
|
943
|
+
*/
|
|
944
|
+
/** `enabled: false` mantém o mesmo contrato de estado sem tocar na URL — para uso em preview e teste. */
|
|
945
|
+
interface UrlStateOptions {
|
|
946
|
+
readonly enabled?: boolean;
|
|
947
|
+
}
|
|
948
|
+
declare function useUrlStringState(key: string, initial: string, { enabled }?: UrlStateOptions): [string, (next: string) => void];
|
|
949
|
+
declare function useUrlNumberState(key: string, initial: number, { enabled }?: UrlStateOptions): [number, (next: number) => void];
|
|
950
|
+
declare function useUrlArrayState(key: string, { enabled }?: UrlStateOptions): [readonly string[], (next: readonly string[]) => void];
|
|
951
|
+
/**
|
|
952
|
+
* Espera o usuário parar de digitar antes de deixar o valor chegar à query. Sem isto, cada tecla
|
|
953
|
+
* na busca vira uma chamada de rede e uma reescrita de URL.
|
|
954
|
+
*/
|
|
955
|
+
declare function useDebouncedValue<TValue>(value: TValue, delayMs?: number): TValue;
|
|
956
|
+
|
|
546
957
|
/**
|
|
547
958
|
* Arquivos trocados na conversa. Sai do transcript e vira lista própria porque anexo é o que o
|
|
548
959
|
* atendente mais precisa reencontrar depois — rolar meses de mensagens para achar um comprovante é
|
|
@@ -661,6 +1072,49 @@ declare function useDarkMode(): {
|
|
|
661
1072
|
toggle: () => void;
|
|
662
1073
|
};
|
|
663
1074
|
|
|
1075
|
+
interface DarkModeToggleLabels {
|
|
1076
|
+
/** Ação de ir para o tema escuro — é o rótulo lido enquanto o tema claro está ativo. */
|
|
1077
|
+
toDark: string;
|
|
1078
|
+
toLight: string;
|
|
1079
|
+
}
|
|
1080
|
+
interface DarkModeToggleClassNames {
|
|
1081
|
+
/** O tamanho do ícone vem daqui quando o host usa a própria escala (`size-4`, `size-5`). */
|
|
1082
|
+
icon?: string;
|
|
1083
|
+
label?: string;
|
|
1084
|
+
}
|
|
1085
|
+
interface DarkModeToggleProps {
|
|
1086
|
+
isDark: boolean;
|
|
1087
|
+
onToggle: () => void;
|
|
1088
|
+
/** Rótulo ao lado do ícone. Só-ícone exige a área de toque mínima, que a classe `cv-touch` dá. */
|
|
1089
|
+
showLabel?: boolean;
|
|
1090
|
+
labels?: Partial<DarkModeToggleLabels>;
|
|
1091
|
+
/**
|
|
1092
|
+
* Substitui a aparência do botão. As classes passam por `tailwind-merge`, então utilitário do
|
|
1093
|
+
* host vence o do pacote em vez de somar: `px-4` descarta o `px-3` daqui.
|
|
1094
|
+
*
|
|
1095
|
+
* A exceção é utilitário de token do host (`rounded-panel`), que o merge não reconhece como
|
|
1096
|
+
* família e deixa passar junto com o `rounded-lg` do pacote — aí quem decide é a ordem no CSS
|
|
1097
|
+
* gerado, não a do atributo. Nesse caso use a forma arbitrária (`rounded-[var(--radius-panel)]`),
|
|
1098
|
+
* que o merge entende e substitui.
|
|
1099
|
+
*/
|
|
1100
|
+
className?: string;
|
|
1101
|
+
classNames?: DarkModeToggleClassNames;
|
|
1102
|
+
}
|
|
1103
|
+
declare const DEFAULT_DARK_MODE_TOGGLE_LABELS: DarkModeToggleLabels;
|
|
1104
|
+
/**
|
|
1105
|
+
* Interruptor de tema, apresentacional: o estado vem do host, junto com `useDarkMode`.
|
|
1106
|
+
*
|
|
1107
|
+
* Ele não monta o hook de propósito. `useDarkMode` é o controlador — escreve a classe `dark` no
|
|
1108
|
+
* `<html>` e persiste a escolha —, e uma barra lateral que também existe como gaveta fica montada
|
|
1109
|
+
* duas vezes na mesma tela. Dois controladores guardam estados iniciais separados, e o segundo
|
|
1110
|
+
* botão passa a mostrar o ícone contrário ao tema que está no ar. Com o estado no host, o hook é
|
|
1111
|
+
* chamado uma vez acima dos dois e as duas cópias concordam.
|
|
1112
|
+
*
|
|
1113
|
+
* O rótulo nomeia o destino, não o estado atual: no claro se lê "Tema escuro", que é o que o clique
|
|
1114
|
+
* faz. `aria-pressed` diria o oposto na mesma frase, então fica de fora.
|
|
1115
|
+
*/
|
|
1116
|
+
declare function DarkModeToggle({ isDark, onToggle, showLabel, labels, className, classNames, }: DarkModeToggleProps): react.JSX.Element;
|
|
1117
|
+
|
|
664
1118
|
/**
|
|
665
1119
|
* Detecta tela estreita para decisões que CSS não resolve — como abrir ou não um painel por padrão.
|
|
666
1120
|
*
|
|
@@ -847,6 +1301,47 @@ interface WelcomeFarewellFormProps {
|
|
|
847
1301
|
}
|
|
848
1302
|
declare function WelcomeFarewellForm({ welcomeMessage, onWelcomeMessageChange, farewellMessage, onFarewellMessageChange, onSave, saving, saveSuccess, welcomePlaceholders, farewellPlaceholders, labels: labelsOverride, }: WelcomeFarewellFormProps): react.JSX.Element;
|
|
849
1303
|
|
|
1304
|
+
interface TranscriptionSettingsFormLabels {
|
|
1305
|
+
sectionTitle: string;
|
|
1306
|
+
enabled: string;
|
|
1307
|
+
enabledHint: string;
|
|
1308
|
+
modeTitle: string;
|
|
1309
|
+
modeAuto: string;
|
|
1310
|
+
modeAutoHint: string;
|
|
1311
|
+
modeOnDemand: string;
|
|
1312
|
+
modeOnDemandHint: string;
|
|
1313
|
+
unavailable: string;
|
|
1314
|
+
save: string;
|
|
1315
|
+
saving: string;
|
|
1316
|
+
saveSuccess: string;
|
|
1317
|
+
}
|
|
1318
|
+
interface TranscriptionSettingsFormProps {
|
|
1319
|
+
enabled: boolean;
|
|
1320
|
+
onEnabledChange: (value: boolean) => void;
|
|
1321
|
+
mode: TranscriptionMode;
|
|
1322
|
+
onModeChange: (value: TranscriptionMode) => void;
|
|
1323
|
+
onSave: (event: FormEvent) => void;
|
|
1324
|
+
/**
|
|
1325
|
+
* A capacidade existe no servidor (engine e credencial configurados)?
|
|
1326
|
+
*
|
|
1327
|
+
* Distinto de `enabled`: isto é "o ambiente consegue", aquilo é "esta empresa quer". Sem a
|
|
1328
|
+
* distinção, um lojista ligaria o interruptor num ambiente sem engine e concluiria que o produto
|
|
1329
|
+
* está quebrado — o texto nunca apareceria e nada explicaria por quê.
|
|
1330
|
+
*/
|
|
1331
|
+
isAvailable?: boolean;
|
|
1332
|
+
saving?: boolean;
|
|
1333
|
+
saveSuccess?: boolean;
|
|
1334
|
+
labels?: Partial<TranscriptionSettingsFormLabels>;
|
|
1335
|
+
}
|
|
1336
|
+
/**
|
|
1337
|
+
* Interruptor de transcrição por empresa, para a tela de configurações.
|
|
1338
|
+
*
|
|
1339
|
+
* Puramente apresentacional, como os outros forms daqui: quem persiste é o host. O pacote não sabe a
|
|
1340
|
+
* rota, e a política vive nas configurações do módulo (`settings.transcriptionEnabled`), não em
|
|
1341
|
+
* variável de ambiente — mudar de ideia sobre transcrever não deveria exigir deploy.
|
|
1342
|
+
*/
|
|
1343
|
+
declare function TranscriptionSettingsForm({ enabled, onEnabledChange, mode, onModeChange, onSave, isAvailable, saving, saveSuccess, labels: labelsOverride, }: TranscriptionSettingsFormProps): react.JSX.Element;
|
|
1344
|
+
|
|
850
1345
|
declare const TEMPLATE_SETTINGS_TAB: {
|
|
851
1346
|
readonly SELECT: "select";
|
|
852
1347
|
readonly CREATE: "create";
|
|
@@ -877,10 +1372,21 @@ interface WhatsAppTemplatesSettingsProps {
|
|
|
877
1372
|
onSubmit: (event: FormEvent) => void;
|
|
878
1373
|
submitting?: boolean;
|
|
879
1374
|
result?: WhatsAppCreateTemplateResult | null;
|
|
1375
|
+
labels?: Partial<WhatsAppCreateTemplateFormLabels>;
|
|
1376
|
+
/** Nome exibido na prévia do template criado; sem isto o form usa seu próprio fallback. */
|
|
1377
|
+
previewCompanyName?: string;
|
|
1378
|
+
variableExamples?: readonly string[];
|
|
880
1379
|
};
|
|
881
1380
|
labels?: Partial<WhatsAppTemplatesSettingsLabels>;
|
|
1381
|
+
/** Vocabulário do formulário de seleção — separado de `labels`, que nomeia só as abas daqui. */
|
|
1382
|
+
settingsLabels?: Partial<WhatsAppTemplateSettingsFormLabels>;
|
|
1383
|
+
/**
|
|
1384
|
+
* Formulários extras de seleção (um por papel adicional de template, ex.: despedida), empilhados
|
|
1385
|
+
* abaixo do principal na mesma sub-aba. Ausente = só o papel principal, comportamento de sempre.
|
|
1386
|
+
*/
|
|
1387
|
+
extraRoleForms?: ReactNode;
|
|
882
1388
|
}
|
|
883
|
-
declare function WhatsAppTemplatesSettings({ labels: labelsOverride, create, ...settingsProps }: WhatsAppTemplatesSettingsProps): react.JSX.Element;
|
|
1389
|
+
declare function WhatsAppTemplatesSettings({ labels: labelsOverride, settingsLabels, create, extraRoleForms, ...settingsProps }: WhatsAppTemplatesSettingsProps): react.JSX.Element;
|
|
884
1390
|
|
|
885
1391
|
interface TopicItem {
|
|
886
1392
|
key: string;
|
|
@@ -906,6 +1412,88 @@ interface TopicsFormProps {
|
|
|
906
1412
|
}
|
|
907
1413
|
declare function TopicsForm({ topics, onToggle, onMessageChange, onSave, saving, saveSuccess, labels: labelsOverride, }: TopicsFormProps): react.JSX.Element;
|
|
908
1414
|
|
|
1415
|
+
interface BotMessages {
|
|
1416
|
+
welcomeMessage: string;
|
|
1417
|
+
farewellMessage: string;
|
|
1418
|
+
}
|
|
1419
|
+
interface TemplateSettings {
|
|
1420
|
+
templateName: string;
|
|
1421
|
+
templateLanguage: string;
|
|
1422
|
+
variables: string[];
|
|
1423
|
+
}
|
|
1424
|
+
/**
|
|
1425
|
+
* Papel adicional de template (ex.: despedida) além do principal (`getTemplateSettings`/
|
|
1426
|
+
* `saveTemplateSettings`). Cada papel ganha seu próprio formulário empilhado na mesma aba, com
|
|
1427
|
+
* carregamento e salvamento independentes — é o que permite hosts com mais de um papel (o
|
|
1428
|
+
* financiamento tem boas-vindas + despedida) convergirem para este componente em vez de remontar
|
|
1429
|
+
* a tela à parte.
|
|
1430
|
+
*/
|
|
1431
|
+
interface MessagesWorkspaceTemplateRole {
|
|
1432
|
+
readonly key: string;
|
|
1433
|
+
readonly labels: {
|
|
1434
|
+
sectionTitle: string;
|
|
1435
|
+
sectionDescription: string;
|
|
1436
|
+
};
|
|
1437
|
+
getSettings(): Promise<TemplateSettings>;
|
|
1438
|
+
saveSettings(settings: TemplateSettings): Promise<void>;
|
|
1439
|
+
}
|
|
1440
|
+
interface TranscriptionSettings {
|
|
1441
|
+
enabled: boolean;
|
|
1442
|
+
mode: TranscriptionMode;
|
|
1443
|
+
available: boolean;
|
|
1444
|
+
}
|
|
1445
|
+
interface MessagesWorkspaceApi {
|
|
1446
|
+
getMessages(): Promise<BotMessages>;
|
|
1447
|
+
saveMessages(messages: BotMessages): Promise<void>;
|
|
1448
|
+
/** Sem este par, a seção de tópicos não é desenhada. */
|
|
1449
|
+
getTopics?(): Promise<TopicItem[]>;
|
|
1450
|
+
saveTopics?(topics: TopicItem[]): Promise<void>;
|
|
1451
|
+
/** Sem este par, a aba de templates não é desenhada. */
|
|
1452
|
+
getTemplateSettings?(): Promise<TemplateSettings>;
|
|
1453
|
+
saveTemplateSettings?(settings: TemplateSettings): Promise<void>;
|
|
1454
|
+
/** Papéis adicionais de template (ex.: despedida) além do principal acima. */
|
|
1455
|
+
templateRoles?: MessagesWorkspaceTemplateRole[];
|
|
1456
|
+
/**
|
|
1457
|
+
* Lista de templates aprovados na Meta. Ausente, a aba ainda existe (dá para salvar o nome
|
|
1458
|
+
* escolhido), mas nasce sem opções e sem botão de recarregar.
|
|
1459
|
+
*/
|
|
1460
|
+
listTemplates?(): Promise<WhatsAppTemplateSummary[]>;
|
|
1461
|
+
/** Sem isto, o submenu "Criar template" some. */
|
|
1462
|
+
createTemplate?(input: WhatsAppCreateTemplateState): Promise<WhatsAppCreateTemplateResult>;
|
|
1463
|
+
/** Sem este par, a aba de transcrição não é desenhada. */
|
|
1464
|
+
getTranscription?(): Promise<TranscriptionSettings>;
|
|
1465
|
+
saveTranscription?(settings: Omit<TranscriptionSettings, 'available'>): Promise<void>;
|
|
1466
|
+
}
|
|
1467
|
+
interface MessagesWorkspaceLabels {
|
|
1468
|
+
title: string;
|
|
1469
|
+
subtitle: string;
|
|
1470
|
+
loading: string;
|
|
1471
|
+
loadError: string;
|
|
1472
|
+
tabBot: string;
|
|
1473
|
+
tabTemplates: string;
|
|
1474
|
+
tabTranscription: string;
|
|
1475
|
+
welcomeFarewell: Partial<WelcomeFarewellFormLabels>;
|
|
1476
|
+
topics: Partial<TopicsFormLabels>;
|
|
1477
|
+
templates: Partial<WhatsAppTemplatesSettingsLabels>;
|
|
1478
|
+
templateSettings: Partial<WhatsAppTemplateSettingsFormLabels>;
|
|
1479
|
+
createTemplate: Partial<WhatsAppCreateTemplateFormLabels>;
|
|
1480
|
+
transcription: Partial<TranscriptionSettingsFormLabels>;
|
|
1481
|
+
}
|
|
1482
|
+
interface MessagesWorkspaceProps {
|
|
1483
|
+
readonly api: MessagesWorkspaceApi;
|
|
1484
|
+
readonly labels?: Partial<MessagesWorkspaceLabels>;
|
|
1485
|
+
readonly welcomePlaceholders?: readonly string[];
|
|
1486
|
+
readonly farewellPlaceholders?: readonly string[];
|
|
1487
|
+
readonly availableVariables?: WhatsAppTemplateVariableSuggestion[];
|
|
1488
|
+
/** Aviso do produto acima da aba de templates (ex.: rota da Graph API ainda não implementada). */
|
|
1489
|
+
readonly renderTemplatesNotice?: () => ReactNode;
|
|
1490
|
+
/** Repassados ao formulário de criação — mesma prévia que `WhatsAppCreateTemplateForm` já suporta. */
|
|
1491
|
+
readonly createTemplatePreviewCompanyName?: string;
|
|
1492
|
+
readonly createTemplateVariableExamples?: readonly string[];
|
|
1493
|
+
readonly className?: string;
|
|
1494
|
+
}
|
|
1495
|
+
declare function MessagesWorkspace({ api, labels: labelsOverride, welcomePlaceholders, farewellPlaceholders, availableVariables, renderTemplatesNotice, createTemplatePreviewCompanyName, createTemplateVariableExamples, className, }: MessagesWorkspaceProps): react.JSX.Element;
|
|
1496
|
+
|
|
909
1497
|
interface UseConversationMessagesResult {
|
|
910
1498
|
messages: MessagePayload[];
|
|
911
1499
|
loading: boolean;
|
|
@@ -930,6 +1518,23 @@ declare function useConversationMessages(conversationId: string, params?: {
|
|
|
930
1518
|
before?: string;
|
|
931
1519
|
}): UseConversationMessagesResult;
|
|
932
1520
|
|
|
1521
|
+
type UseScrollToLatestMessageParams = {
|
|
1522
|
+
/** Troca de conversa. Muda ⇒ salto instantâneo para o fim. */
|
|
1523
|
+
readonly conversationId: string | undefined;
|
|
1524
|
+
/** Quantidade de mensagens carregadas. Cresce ⇒ acompanha o fim, se o operador estiver lá. */
|
|
1525
|
+
readonly messageCount: number;
|
|
1526
|
+
};
|
|
1527
|
+
type UseScrollToLatestMessageResult = {
|
|
1528
|
+
/** Vai no elemento que rola — tipicamente o `ConversationWallpaper`. */
|
|
1529
|
+
readonly containerRef: RefObject<HTMLDivElement | null>;
|
|
1530
|
+
/** Ligue no `onScroll` do mesmo elemento: é o que detecta o operador lendo o histórico. */
|
|
1531
|
+
readonly handleScroll: (event: UIEvent<HTMLDivElement>) => void;
|
|
1532
|
+
/** `true` quando o operador rolou para trás — serve a um botão "ir para a última". */
|
|
1533
|
+
readonly isAwayFromBottom: boolean;
|
|
1534
|
+
readonly scrollToBottom: (behavior?: ScrollBehavior) => void;
|
|
1535
|
+
};
|
|
1536
|
+
declare function useScrollToLatestMessage({ conversationId, messageCount, }: UseScrollToLatestMessageParams): UseScrollToLatestMessageResult;
|
|
1537
|
+
|
|
933
1538
|
type UseConversationListParams = ListConversationsParams;
|
|
934
1539
|
interface UseConversationListResult {
|
|
935
1540
|
conversations: ConversationSummary[];
|
|
@@ -1017,4 +1622,301 @@ interface AsyncResourceState<T> {
|
|
|
1017
1622
|
|
|
1018
1623
|
declare function createMediaUrlResolver(api: Pick<ConversationsApi, 'getDocumentUrl' | 'getMediaProxyUrl'>): ResolveMediaUrl;
|
|
1019
1624
|
|
|
1020
|
-
|
|
1625
|
+
/**
|
|
1626
|
+
* Textos da inbox. Todos sobrescrevíveis: o produto troca vocabulário ("cliente" vira "lead") ou
|
|
1627
|
+
* o idioma inteiro sem tocar no layout — e sem manter uma cópia da tela para isso.
|
|
1628
|
+
*/
|
|
1629
|
+
interface ConversationsWorkspaceLabels {
|
|
1630
|
+
readonly title: string;
|
|
1631
|
+
readonly conversations: string;
|
|
1632
|
+
readonly waiting: string;
|
|
1633
|
+
readonly unread: string;
|
|
1634
|
+
readonly waitingOnly: string;
|
|
1635
|
+
readonly markSelectedAsRead: string;
|
|
1636
|
+
readonly search: string;
|
|
1637
|
+
readonly windowLegend: string;
|
|
1638
|
+
readonly channelLegend: string;
|
|
1639
|
+
readonly selectAll: string;
|
|
1640
|
+
readonly emptyList: string;
|
|
1641
|
+
readonly emptyDetail: string;
|
|
1642
|
+
readonly bulkSelected: (count: number) => string;
|
|
1643
|
+
readonly bulkClear: string;
|
|
1644
|
+
readonly bulkFinalize: string;
|
|
1645
|
+
readonly bulkFinalizeConfirm: (count: number) => string;
|
|
1646
|
+
readonly bulkTemplate: string;
|
|
1647
|
+
readonly markAllAsRead: string;
|
|
1648
|
+
readonly templateModalTitle: string;
|
|
1649
|
+
readonly templateModalSearch: string;
|
|
1650
|
+
readonly templateModalEmpty: string;
|
|
1651
|
+
readonly templateModalCancel: string;
|
|
1652
|
+
readonly templateModalSending: string;
|
|
1653
|
+
readonly templateModalNoneExpired: string;
|
|
1654
|
+
readonly templateModalAvailable: (count: number) => string;
|
|
1655
|
+
readonly templateModalSend: (count: number) => string;
|
|
1656
|
+
readonly messagesSelected: (count: number) => string;
|
|
1657
|
+
readonly copySelected: string;
|
|
1658
|
+
readonly composerPlaceholder: string;
|
|
1659
|
+
readonly attachFailure: string;
|
|
1660
|
+
/** Tira um arquivo da fila antes de enviar. */
|
|
1661
|
+
readonly attachmentRemove: string;
|
|
1662
|
+
readonly recordFailure: string;
|
|
1663
|
+
readonly sendFailure: string;
|
|
1664
|
+
readonly takeoverToReply: string;
|
|
1665
|
+
readonly signIn: string;
|
|
1666
|
+
readonly pageOf: (page: number, pageCount: number) => string;
|
|
1667
|
+
readonly rangeOf: (first: number, last: number, total: number) => string;
|
|
1668
|
+
}
|
|
1669
|
+
declare const DEFAULT_CONVERSATIONS_WORKSPACE_LABELS: ConversationsWorkspaceLabels;
|
|
1670
|
+
|
|
1671
|
+
/**
|
|
1672
|
+
* Estado da inbox: filtros, paginação, seleção em massa e ações de atendimento.
|
|
1673
|
+
*
|
|
1674
|
+
* Nasceu igual em três produtos e divergiu em três — contadores diferentes, um marcava lida ao
|
|
1675
|
+
* abrir e outro não, um perdia a página ao trocar de filtro. É a lógica de operar uma inbox, não
|
|
1676
|
+
* regra de nenhum negócio, e por isso mora aqui.
|
|
1677
|
+
*
|
|
1678
|
+
* A paginação é do cliente porque nem todo backend pagina a listagem. Fatiar aqui mantém a tela
|
|
1679
|
+
* utilizável com centenas de conversas; com dezenas de milhares o gargalo volta e a resposta é
|
|
1680
|
+
* paginar no servidor.
|
|
1681
|
+
*/
|
|
1682
|
+
|
|
1683
|
+
declare const CONVERSATIONS_PER_PAGE = 50;
|
|
1684
|
+
interface UseConversationsInboxParams {
|
|
1685
|
+
/** Recortes do produto repassados crus ao backend dele (carteira, campanha, tipo). */
|
|
1686
|
+
readonly filters?: Record<string, string | undefined>;
|
|
1687
|
+
readonly perPage?: number;
|
|
1688
|
+
/**
|
|
1689
|
+
* Marca como lida a conversa aberta. Ligado por padrão: badge subindo na conversa que o atendente
|
|
1690
|
+
* tem na frente é ruído que ele não tem como resolver.
|
|
1691
|
+
*/
|
|
1692
|
+
readonly markReadOnOpen?: boolean;
|
|
1693
|
+
/**
|
|
1694
|
+
* Pede a página ao servidor em vez de fatiar o que veio. Necessário quando a base é grande — o
|
|
1695
|
+
* padrão traz tudo e pagina no cliente porque nem todo backend pagina a listagem.
|
|
1696
|
+
*/
|
|
1697
|
+
readonly serverPaginated?: boolean;
|
|
1698
|
+
readonly describeFailure?: (error: unknown) => string;
|
|
1699
|
+
}
|
|
1700
|
+
interface UseConversationsInboxResult {
|
|
1701
|
+
readonly conversations: readonly ConversationSummary[];
|
|
1702
|
+
readonly pageConversations: readonly ConversationSummary[];
|
|
1703
|
+
readonly selectedConversation: ConversationSummary | undefined;
|
|
1704
|
+
readonly loading: boolean;
|
|
1705
|
+
readonly now: number;
|
|
1706
|
+
readonly totalCount: number;
|
|
1707
|
+
readonly unreadCount: number;
|
|
1708
|
+
readonly waitingCount: number;
|
|
1709
|
+
readonly filteredCount: number;
|
|
1710
|
+
/** Selecionadas fora da janela de 24h — as únicas que um template alcança. */
|
|
1711
|
+
readonly expiredSelectedCount: number;
|
|
1712
|
+
readonly page: number;
|
|
1713
|
+
readonly pageCount: number;
|
|
1714
|
+
readonly firstOnPage: number;
|
|
1715
|
+
readonly lastOnPage: number;
|
|
1716
|
+
readonly selectedId: string | undefined;
|
|
1717
|
+
readonly selectedIds: ReadonlySet<string>;
|
|
1718
|
+
readonly allOnPageSelected: boolean;
|
|
1719
|
+
readonly waitingOnly: boolean;
|
|
1720
|
+
readonly windowFilter: ConversationWindow;
|
|
1721
|
+
readonly channelFilter: ChannelFilter;
|
|
1722
|
+
readonly channelFilters: readonly ChannelFilterOption[];
|
|
1723
|
+
readonly search: string;
|
|
1724
|
+
readonly busy: boolean;
|
|
1725
|
+
/** Por que a lista está vazia, quando não é por não haver conversa. */
|
|
1726
|
+
readonly loadFailure: string | undefined;
|
|
1727
|
+
/** Ausentes quando o host não implementa a capacidade — a UI não desenha a afordância. */
|
|
1728
|
+
readonly canTakeover: boolean;
|
|
1729
|
+
readonly canFinalize: boolean;
|
|
1730
|
+
readonly canListTemplates: boolean;
|
|
1731
|
+
readonly canMarkAllRead: boolean;
|
|
1732
|
+
refetch(): Promise<void> | void;
|
|
1733
|
+
selectConversation(conversationId: string): void;
|
|
1734
|
+
clearSelection(): void;
|
|
1735
|
+
toggleSelected(conversationId: string): void;
|
|
1736
|
+
toggleSelectAllOnPage(): void;
|
|
1737
|
+
clearBulkSelection(): void;
|
|
1738
|
+
setWaitingOnly(value: boolean): void;
|
|
1739
|
+
setWindowFilter(value: ConversationWindow): void;
|
|
1740
|
+
setChannelFilter(value: ChannelFilter): void;
|
|
1741
|
+
setSearch(value: string): void;
|
|
1742
|
+
goToPage(value: number): void;
|
|
1743
|
+
markSelectedAsRead(): Promise<void>;
|
|
1744
|
+
markAllAsRead(): Promise<void>;
|
|
1745
|
+
takeover(conversationId: string): Promise<void>;
|
|
1746
|
+
releaseToBot(conversationId: string): Promise<void>;
|
|
1747
|
+
finalize(conversationId: string): Promise<void>;
|
|
1748
|
+
finalizeSelected(): Promise<void>;
|
|
1749
|
+
sendTemplateToSelected(templateName?: string): Promise<void>;
|
|
1750
|
+
}
|
|
1751
|
+
declare function useConversationsInbox(params?: UseConversationsInboxParams): UseConversationsInboxResult;
|
|
1752
|
+
|
|
1753
|
+
type SimulatorTransportParams = {
|
|
1754
|
+
readonly conversationId: string;
|
|
1755
|
+
readonly channel: ConversationChannel;
|
|
1756
|
+
/** Identificador do contato no canal: telefone no WhatsApp, id de sessão no chat do site. */
|
|
1757
|
+
readonly handle: string;
|
|
1758
|
+
};
|
|
1759
|
+
/**
|
|
1760
|
+
* Fábrica do transporte daquele canal.
|
|
1761
|
+
*
|
|
1762
|
+
* É o único ponto onde o host precisa saber de canal: o painel, a moldura e o comportamento são os
|
|
1763
|
+
* mesmos em todos. No WhatsApp devolve o cliente-ponte (assinatura no servidor do host); no chat do
|
|
1764
|
+
* site, o cliente das rotas do widget.
|
|
1765
|
+
*/
|
|
1766
|
+
type SimulatorTransportFactory = (params: SimulatorTransportParams) => ConversationSimulatorClient;
|
|
1767
|
+
interface ConversationsWorkspaceSimulator {
|
|
1768
|
+
/**
|
|
1769
|
+
* Um transporte por canal — o workspace monta o painel com o da conversa selecionada.
|
|
1770
|
+
*
|
|
1771
|
+
* Canal sem transporte não desenha o botão: capacidade é opcional por ausência, e oferecer
|
|
1772
|
+
* "simular" numa conversa que não tem como receber a mensagem é um botão que falha ao ser tocado.
|
|
1773
|
+
*/
|
|
1774
|
+
readonly transports?: Partial<Record<ConversationChannel, SimulatorTransportFactory>>;
|
|
1775
|
+
/**
|
|
1776
|
+
* Válvula de escape: o host desenha o painel inteiro. Tem precedência sobre `transports`.
|
|
1777
|
+
*
|
|
1778
|
+
* Era a única porta antes de `transports`, quando o simulador só falava WhatsApp e cada produto
|
|
1779
|
+
* remontava a moldura — o que fez duas telas da mesma casa divergirem. Continua aceito para não
|
|
1780
|
+
* quebrar quem já a usa.
|
|
1781
|
+
*/
|
|
1782
|
+
render?(params: {
|
|
1783
|
+
conversationId: string;
|
|
1784
|
+
channel: ConversationChannel;
|
|
1785
|
+
close: () => void;
|
|
1786
|
+
}): ReactNode;
|
|
1787
|
+
/** Ausente = ligado. Serve para esconder fora de desenvolvimento sem condicionar o JSX. */
|
|
1788
|
+
readonly enabled?: boolean;
|
|
1789
|
+
/** Ícone da biblioteca (lucide) no utilitário do cabeçalho. Ausente, entra o frasco de teste. */
|
|
1790
|
+
readonly icon?: ReactNode;
|
|
1791
|
+
readonly label?: string;
|
|
1792
|
+
/** Vocabulário do painel. O que muda de canal (destino, placeholder) já vem resolvido. */
|
|
1793
|
+
readonly labels?: Partial<ConversationSimulatorPanelLabels>;
|
|
1794
|
+
/** Destino do upload no canal que entrega mídia por referência (o caminho da Meta). */
|
|
1795
|
+
readonly uploadMedia?: (file: File) => Promise<PreviewUploadedMedia>;
|
|
1796
|
+
/** Recarrega o transcript a cada N ms. Serve a host sem stream. */
|
|
1797
|
+
readonly pollIntervalMs?: number;
|
|
1798
|
+
}
|
|
1799
|
+
interface ConversationsWorkspaceProps {
|
|
1800
|
+
readonly labels?: Partial<ConversationsWorkspaceLabels>;
|
|
1801
|
+
readonly filters?: Record<string, string | undefined>;
|
|
1802
|
+
readonly perPage?: number;
|
|
1803
|
+
readonly markReadOnOpen?: boolean;
|
|
1804
|
+
/** Pede a página ao servidor em vez de fatiar no cliente. */
|
|
1805
|
+
readonly serverPaginated?: boolean;
|
|
1806
|
+
/** Conversa a abrir na montagem (deep link `?id=`). */
|
|
1807
|
+
readonly initialConversationId?: string | undefined;
|
|
1808
|
+
/** Idem, pelo telefone — é o que costuma vir no link de um alerta ou de um pedido. */
|
|
1809
|
+
readonly initialWhatsappNumber?: string | undefined;
|
|
1810
|
+
readonly simulator?: ConversationsWorkspaceSimulator;
|
|
1811
|
+
readonly quickReplies?: readonly QuickReply[];
|
|
1812
|
+
readonly quickReplyVariablesFor?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => Record<string, string>;
|
|
1813
|
+
/** Etapa do fluxo mostrada no painel de contexto. */
|
|
1814
|
+
readonly flowLabelOf?: (conversation: ConversationSummary) => string | undefined;
|
|
1815
|
+
/** Substitui o download local do transcript (ex.: exportação completa pela rota do servidor). */
|
|
1816
|
+
readonly onDownload?: (conversation: ConversationSummary) => void;
|
|
1817
|
+
/** Bloqueia o composer enquanto a conversa estiver com o bot. */
|
|
1818
|
+
readonly requireTakeoverToReply?: boolean;
|
|
1819
|
+
/** Deixa marcar mensagens no transcript e copiar o trecho. */
|
|
1820
|
+
readonly messageSelection?: boolean;
|
|
1821
|
+
/** Texto já no campo ao abrir a conversa (deep link que sugere a resposta). */
|
|
1822
|
+
readonly initialComposerText?: string | undefined;
|
|
1823
|
+
/** `rich` troca o campo simples pelo texto com a formatação do WhatsApp desenhada ao escrever. */
|
|
1824
|
+
readonly composer?: 'simple' | 'rich';
|
|
1825
|
+
/** Valores que o operador insere sem digitar. Só o composer `rich` os oferece. */
|
|
1826
|
+
readonly composerVariablesFor?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => readonly RichComposerVariable[];
|
|
1827
|
+
/** Fila de anexos com legenda, como no WhatsApp. Ausente, o clipe manda cada arquivo na hora. */
|
|
1828
|
+
readonly onSendAttachments?: (conversation: ConversationSummary, files: readonly File[], caption: string) => Promise<void>;
|
|
1829
|
+
/** Nota de voz. Ausente, o microfone não aparece. */
|
|
1830
|
+
readonly onRecordAudio?: (conversation: ConversationSummary, file: File) => Promise<void>;
|
|
1831
|
+
readonly contextEntriesOf?: (context: Record<string, unknown> | undefined) => readonly ConversationContextEntry[];
|
|
1832
|
+
readonly onAttach?: (conversation: ConversationSummary, file: File) => Promise<void>;
|
|
1833
|
+
readonly extraUtilitiesFor?: (conversation: ConversationSummary) => readonly ConversationHeaderUtility[];
|
|
1834
|
+
readonly renderFilters?: (inbox: UseConversationsInboxResult) => ReactNode;
|
|
1835
|
+
readonly renderBulkActions?: (inbox: UseConversationsInboxResult) => ReactNode;
|
|
1836
|
+
readonly renderRow?: (conversation: ConversationSummary) => ReactNode;
|
|
1837
|
+
readonly renderAboveTranscript?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => ReactNode;
|
|
1838
|
+
readonly renderHeaderActions?: (inbox: UseConversationsInboxResult) => ReactNode;
|
|
1839
|
+
readonly onSendTemplateToSelected?: (inbox: UseConversationsInboxResult) => void;
|
|
1840
|
+
/** Destino do link de reentrar no painel, mostrado junto do aviso de sessão expirada. */
|
|
1841
|
+
readonly signInHref?: string;
|
|
1842
|
+
readonly className?: string;
|
|
1843
|
+
}
|
|
1844
|
+
declare function ConversationsWorkspace({ labels: labelsOverride, filters, perPage, markReadOnOpen, serverPaginated, initialConversationId, initialWhatsappNumber, simulator, quickReplies, quickReplyVariablesFor, flowLabelOf, onDownload, requireTakeoverToReply, messageSelection, initialComposerText, composer, composerVariablesFor, onSendAttachments, onRecordAudio, contextEntriesOf, onAttach, extraUtilitiesFor, renderFilters, renderBulkActions, renderRow, renderAboveTranscript, renderHeaderActions, onSendTemplateToSelected, signInHref, className, }: ConversationsWorkspaceProps): react.JSX.Element;
|
|
1845
|
+
|
|
1846
|
+
interface ConversationPaneProps {
|
|
1847
|
+
readonly conversation: ConversationSummary;
|
|
1848
|
+
readonly now: number;
|
|
1849
|
+
readonly busy: boolean;
|
|
1850
|
+
readonly labels: ConversationsWorkspaceLabels;
|
|
1851
|
+
/** Devolve a promessa quando o host a tem: o envio de template espera a tomada antes de sair. */
|
|
1852
|
+
readonly onTakeover?: (() => void | Promise<void>) | undefined;
|
|
1853
|
+
readonly onReturnToBot?: (() => void) | undefined;
|
|
1854
|
+
readonly onFinish?: (() => void) | undefined;
|
|
1855
|
+
readonly onBack: () => void;
|
|
1856
|
+
readonly extraUtilities?: readonly ConversationHeaderUtility[];
|
|
1857
|
+
/** Traduz o contexto cru do produto nas linhas do painel. Ausente, o painel não aparece. */
|
|
1858
|
+
readonly contextEntriesOf?: (context: Record<string, unknown> | undefined) => readonly ConversationContextEntry[];
|
|
1859
|
+
readonly quickReplies?: readonly QuickReply[];
|
|
1860
|
+
/**
|
|
1861
|
+
* Recebe o contexto junto porque o dado que interessa à variável (o nome que o bot perguntou,
|
|
1862
|
+
* por exemplo) vive no contexto do fluxo, não no resumo da listagem.
|
|
1863
|
+
*/
|
|
1864
|
+
readonly quickReplyVariablesFor?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => Record<string, string>;
|
|
1865
|
+
/** Etapa do fluxo ao lado do contexto (ex.: "Anotando o pedido"). */
|
|
1866
|
+
readonly flowLabel?: string | undefined;
|
|
1867
|
+
/**
|
|
1868
|
+
* Substitui o download local do transcript. Existe para o produto que exporta pela rota do
|
|
1869
|
+
* servidor, onde o arquivo sai completo em vez de só com o que a tela carregou.
|
|
1870
|
+
*/
|
|
1871
|
+
readonly onDownload?: (() => void) | undefined;
|
|
1872
|
+
/**
|
|
1873
|
+
* Bloqueia o composer enquanto a conversa estiver com o bot. Ligado, responder sem assumir
|
|
1874
|
+
* atropelaria o fluxo automático no meio de uma pergunta.
|
|
1875
|
+
*/
|
|
1876
|
+
readonly requireTakeoverToReply?: boolean;
|
|
1877
|
+
/**
|
|
1878
|
+
* Deixa marcar mensagens e copiar o trecho. É o que se faz para levar um pedaço da conversa a um
|
|
1879
|
+
* e-mail ou a um chamado, sem baixar o transcript inteiro.
|
|
1880
|
+
*/
|
|
1881
|
+
readonly messageSelection?: boolean;
|
|
1882
|
+
/** Texto já no campo ao abrir (deep link que sugere a resposta). */
|
|
1883
|
+
readonly initialComposerText?: string | undefined;
|
|
1884
|
+
/**
|
|
1885
|
+
* Peça do produto entre o contexto e o transcript (ex.: ficha do lead, resumo do pedido). Recebe o
|
|
1886
|
+
* contexto do fluxo junto: é dele que sai o resumo, e sem isso o host teria de buscá-lo de novo.
|
|
1887
|
+
*/
|
|
1888
|
+
readonly renderAboveTranscript?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => ReactNode;
|
|
1889
|
+
readonly onAttach?: ((file: File) => Promise<void>) | undefined;
|
|
1890
|
+
/**
|
|
1891
|
+
* `rich` troca o `textarea` pelo campo com a formatação do WhatsApp desenhada enquanto se escreve.
|
|
1892
|
+
* Vale onde o atendente manda texto longo e formatado; para responder "já vou ver" o campo simples
|
|
1893
|
+
* é menos coisa na tela.
|
|
1894
|
+
*/
|
|
1895
|
+
readonly composer?: 'simple' | 'rich';
|
|
1896
|
+
/** Valores que o operador insere sem digitar. Só o composer `rich` os oferece. */
|
|
1897
|
+
readonly composerVariablesFor?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => readonly RichComposerVariable[];
|
|
1898
|
+
/**
|
|
1899
|
+
* Fila de anexos com legenda, como no WhatsApp: os arquivos escolhidos ficam visíveis acima da
|
|
1900
|
+
* barra e saem junto com o texto escrito. Ausente, o clipe manda cada arquivo na hora — o que
|
|
1901
|
+
* perde a legenda, e era a diferença entre as telas.
|
|
1902
|
+
*/
|
|
1903
|
+
readonly onSendAttachments?: (files: readonly File[], caption: string) => Promise<void>;
|
|
1904
|
+
/** Grava e envia nota de voz. Ausente, o microfone não aparece. */
|
|
1905
|
+
readonly onRecordAudio?: (file: File) => Promise<void>;
|
|
1906
|
+
}
|
|
1907
|
+
declare function ConversationPane({ conversation, now, busy, labels, onTakeover, onReturnToBot, onFinish, onBack, extraUtilities, contextEntriesOf, quickReplies, quickReplyVariablesFor, flowLabel, onDownload, requireTakeoverToReply, messageSelection, initialComposerText, renderAboveTranscript, onAttach, composer, composerVariablesFor, onSendAttachments, onRecordAudio, }: ConversationPaneProps): react.JSX.Element;
|
|
1908
|
+
|
|
1909
|
+
interface ConversationsInboxListProps {
|
|
1910
|
+
readonly inbox: UseConversationsInboxResult;
|
|
1911
|
+
readonly labels: ConversationsWorkspaceLabels;
|
|
1912
|
+
readonly className?: string;
|
|
1913
|
+
/** Barra extra do produto acima da lista (ex.: filtro de carteira, seletor de campanha). */
|
|
1914
|
+
readonly renderFilters?: (inbox: UseConversationsInboxResult) => ReactNode;
|
|
1915
|
+
/** Ações em lote do produto, ao lado de finalizar e template. */
|
|
1916
|
+
readonly renderBulkActions?: (inbox: UseConversationsInboxResult) => ReactNode;
|
|
1917
|
+
readonly renderRow?: (conversation: ConversationSummary) => ReactNode;
|
|
1918
|
+
readonly onSendTemplateToSelected?: () => void;
|
|
1919
|
+
}
|
|
1920
|
+
declare function ConversationsInboxList({ inbox, labels, className, renderFilters, renderBulkActions, renderRow, onSendTemplateToSelected, }: ConversationsInboxListProps): react.JSX.Element;
|
|
1921
|
+
|
|
1922
|
+
export { type AsyncResourceState, AudioPlayer, type AudioPlayerProps, AudioTranscription, type AudioTranscriptionProps, Avatar, type AvatarLabels, type AvatarProps, type BotMessages, type BuildTranscriptTextParams, BulkActionBar, type BulkActionBarProps, CHANNEL_BRAND_COLOR, CONVERSATIONS_PER_PAGE, CONVERSATION_WINDOW, ChannelFilter, ChannelFilterOption, ChannelIcon, type ChannelIconProps, ConversationChannel, type ConversationContextEntry, ConversationContextPanel, type ConversationContextPanelClassNames, type ConversationContextPanelLabels, type ConversationContextPanelProps, type ConversationContextStatus, ConversationDocument, ConversationDocumentsPanel, type ConversationDocumentsPanelClassNames, type ConversationDocumentsPanelLabels, type ConversationDocumentsPanelProps, ConversationHeader, type ConversationHeaderClassNames, type ConversationHeaderLabels, type ConversationHeaderProps, type ConversationHeaderUtility, ConversationListItem, type ConversationListItemLabels, type ConversationListItemProps, type ConversationLocales, ConversationLocalesProvider, type ConversationLocalesProviderProps, ConversationPane, type ConversationPaneProps, type ConversationRealtimeHandler, ConversationRow, type ConversationRowClassNames, type ConversationRowProps, ConversationSimulatorClient, ConversationSummary, ConversationTemplate, ConversationWallpaper, type ConversationWallpaperProps, type ConversationWindow, ConversationsApi, ConversationsFeatures, ConversationsInboxList, type ConversationsInboxListProps, ConversationsProvider, ConversationsWorkspace, type ConversationsWorkspaceLabels, type ConversationsWorkspaceProps, type ConversationsWorkspaceSimulator, DEFAULT_ACCEPTED_FILE_TYPES, DEFAULT_AVATAR_LABELS, DEFAULT_CONVERSATIONS_WORKSPACE_LABELS, DEFAULT_CONVERSATION_CONTEXT_LABELS, DEFAULT_CONVERSATION_DOCUMENTS_LABELS, DEFAULT_CONVERSATION_HEADER_LABELS, DEFAULT_CONVERSATION_LIST_ITEM_LABELS, DEFAULT_DARK_MODE_TOGGLE_LABELS, DEFAULT_DOCUMENTS_LIBRARY_LABELS, DEFAULT_DOCUMENTS_WORKSPACE_LABELS, DEFAULT_EMOJI_PICKER_LABELS, DEFAULT_INTERACTIVE_MESSAGE_LABELS, DEFAULT_LIGHTBOX_LABELS, DEFAULT_MESSAGE_COMPOSER_LABELS, DEFAULT_RICH_COMPOSER_TOOLTIPS, DEFAULT_TEMPLATES_SETTINGS_LABELS, DEFAULT_WHATSAPP_MESSAGE_EDITOR_LABELS, DEFAULT_WINDOW_EXPIRED_LABELS, DOCUMENT_SOURCE_FILTER, DarkModeToggle, type DarkModeToggleLabels, type DarkModeToggleProps, DateDivider, type DateDividerClassNames, type DateDividerProps, type DocumentSourceFilter, type DocumentsFiltersContext, DocumentsLibrary, type DocumentsLibraryClassNames, type DocumentsLibraryLabels, type DocumentsLibraryProps, DocumentsWorkspace, type DocumentsWorkspaceClassNames, type DocumentsWorkspaceLabels, type DocumentsWorkspaceProps, EMOJI_CATEGORIES, type EmojiCategory, type EmojiEntry, EmojiPicker, type EmojiPickerLabels, type EmojiPickerProps, FileIcon, type FileIconProps, type FilterOption, InteractiveMessage, type InteractiveMessageLabels, type InteractiveMessageProps, InteractivePayload, InteractiveSelection, Lightbox, type LightboxLabels, type LightboxProps, ListConversationsParams, ListDocumentsParams, ListingPagination, type ListingPaginationProps, MessageBubble, type MessageBubbleProps, MessageComposer, type MessageComposerClassNames, type MessageComposerLabels, type MessageComposerProps, MessagePayload, MessageTail, MessageText, type MessageTextProps, MessageTimestamp, MessageTranscription, MessagesWorkspace, type MessagesWorkspaceApi, type MessagesWorkspaceLabels, type MessagesWorkspaceProps, type MessagesWorkspaceTemplateRole, MultiSelectFilter, type MultiSelectFilterProps, NARROW_MAX_WIDTH_PX, type QuickReply, REPLY_LATENCY, REPLY_LATENCY_CRITICAL_HOURS, REPLY_LATENCY_LATE_HOURS, RICH_COMPOSER_ACTION, type ReplyLatency, type ReplyLatencyParams, ResolveMediaUrl, type RichComposerAction, type RichComposerQuickReply, type RichComposerTooltips, type RichComposerVariable, RichMessageComposer, type RichMessageComposerHandle, type RichMessageComposerProps, SSEProvider, SimpleEmojiPicker, type SimpleEmojiPickerProps, type SimulatorTransportFactory, type SimulatorTransportParams, type SortDirection, SortableHead, type SortableHeadProps, StatusTicks, type StatusTicksProps, TEMPLATE_SETTINGS_TAB, TOOLTIP_ATTRIBUTE, type TemplateSettings, type TemplateSettingsTab, ToastProvider, TooltipLayer, type TopicItem, TopicsForm, type TopicsFormLabels, type TopicsFormProps, TranscriptionMode, type TranscriptionSettings, TranscriptionSettingsForm, type TranscriptionSettingsFormLabels, type TranscriptionSettingsFormProps, type UrlStateOptions, type UseConversationActionsResult, type UseConversationContextResult, type UseConversationDocumentsParams, type UseConversationDocumentsResult, type UseConversationListParams, type UseConversationListResult, type UseConversationMessagesResult, type UseConversationsInboxParams, type UseConversationsInboxResult, type UseInboxActionsResult, type UseScrollToLatestMessageParams, type UseScrollToLatestMessageResult, type UseWaitingNotificationsLabels, type UseWaitingNotificationsParams, type UseWaitingNotificationsResult, WINDOW_FILTERS, WelcomeFarewellForm, type WelcomeFarewellFormLabels, type WelcomeFarewellFormProps, WhatsAppCreateTemplateForm, type WhatsAppCreateTemplateFormLabels, type WhatsAppCreateTemplateFormProps, type WhatsAppCreateTemplateResult, type WhatsAppCreateTemplateState, WhatsAppMessageEditor, type WhatsAppMessageEditorLabels, type WhatsAppMessageEditorProps, type WhatsAppTemplateHeaderType, WhatsAppTemplateSettingsForm, type WhatsAppTemplateSettingsFormLabels, type WhatsAppTemplateSettingsFormProps, type WhatsAppTemplateSummary, type WhatsAppTemplateVariableSuggestion, WhatsAppTemplatesSettings, type WhatsAppTemplatesSettingsLabels, type WhatsAppTemplatesSettingsProps, WindowExpiredNotice, type WindowExpiredNoticeLabels, type WindowExpiredNoticeProps, type WindowOfParams, applyQuickReplyVariables, buildTranscriptFilename, buildTranscriptText, createMediaUrlResolver, downloadTextFile, formatDateTime, formatFileSize, formatPhone, formatStalledFor, formatTimestamp, htmlToWA, isReplyOverdue, isSameDay, isWindowBlocking, parseWhatsAppFormatting, phoneInitials, replyLatencyOf, resolveQuickReply, searchEmojis, toast, useConversationActions, useConversationContext, useConversationDocuments, useConversationList, useConversationLocales, useConversationMessages, useConversationRealtime, useConversations, useConversationsInbox, useDarkMode, useDebouncedValue, useGlobalRealtime, useInboxActions, useIsDarkTheme, useIsNarrow, useScrollToLatestMessage, useToast, useUrlArrayState, useUrlNumberState, useUrlStringState, useWaitingNotifications, waToHTML, waToHTMLInline, windowOf };
|