@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.
- package/dist/ConversationSimulatorPanel--5fIzXWY.d.ts +804 -0
- package/dist/chunk-BJNRLLDO.js +2708 -0
- package/dist/chunk-DKPXKQGC.js +110 -0
- package/dist/{chunk-OGRRHQQW.js → chunk-WCBDXZ3X.js} +68 -4
- package/dist/flows/index.d.ts +422 -5
- package/dist/flows/index.js +2502 -678
- package/dist/index.d.ts +921 -17
- package/dist/index.js +3677 -678
- 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.test.tsx +21 -0
- package/src/Wallpaper.tsx +67 -7
- 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/chunk-73MW5HNT.js +0 -1717
- package/dist/chunk-NV2RZ5KT.js +0 -56
- 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, 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. */
|
|
@@ -41,8 +48,20 @@ declare function InteractiveMessage({ payload, onSelect, labels, className }: In
|
|
|
41
48
|
interface ConversationWallpaperProps {
|
|
42
49
|
children?: ReactNode;
|
|
43
50
|
className?: string;
|
|
51
|
+
/** Ajusta ou substitui o fundo padrão — para produto com identidade visual própria. */
|
|
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;
|
|
44
58
|
}
|
|
45
|
-
|
|
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>>;
|
|
46
65
|
|
|
47
66
|
interface ConversationLocales {
|
|
48
67
|
bubble: {
|
|
@@ -66,6 +85,20 @@ interface ConversationLocales {
|
|
|
66
85
|
untitledDocument: string;
|
|
67
86
|
downloadFile: string;
|
|
68
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
|
+
};
|
|
69
102
|
selection: {
|
|
70
103
|
select: string;
|
|
71
104
|
};
|
|
@@ -90,6 +123,33 @@ interface AudioPlayerProps {
|
|
|
90
123
|
}
|
|
91
124
|
declare function AudioPlayer({ src, isMine }: AudioPlayerProps): react.JSX.Element;
|
|
92
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
|
+
|
|
93
153
|
interface EmojiPickerLabels {
|
|
94
154
|
search: string;
|
|
95
155
|
noResults: string;
|
|
@@ -126,10 +186,30 @@ declare const EMOJI_CATEGORIES: readonly EmojiCategory[];
|
|
|
126
186
|
*/
|
|
127
187
|
declare function searchEmojis(query: string): readonly EmojiEntry[];
|
|
128
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;
|
|
129
208
|
interface MessageComposerLabels {
|
|
130
209
|
emoji: string;
|
|
131
210
|
attach: string;
|
|
132
211
|
send: string;
|
|
212
|
+
removeAttachment: string;
|
|
133
213
|
}
|
|
134
214
|
declare const DEFAULT_MESSAGE_COMPOSER_LABELS: MessageComposerLabels;
|
|
135
215
|
interface MessageComposerProps {
|
|
@@ -143,11 +223,22 @@ interface MessageComposerProps {
|
|
|
143
223
|
maxLength?: number;
|
|
144
224
|
disabled?: boolean;
|
|
145
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>>;
|
|
146
235
|
className?: string;
|
|
147
236
|
classNames?: Partial<MessageComposerClassNames>;
|
|
148
237
|
}
|
|
149
238
|
interface MessageComposerClassNames {
|
|
150
239
|
root: string;
|
|
240
|
+
quickReplies: string;
|
|
241
|
+
quickReply: string;
|
|
151
242
|
field: string;
|
|
152
243
|
}
|
|
153
244
|
/**
|
|
@@ -157,7 +248,91 @@ interface MessageComposerClassNames {
|
|
|
157
248
|
* Produto com regra própria passa `acceptedFileTypes`.
|
|
158
249
|
*/
|
|
159
250
|
declare const DEFAULT_ACCEPTED_FILE_TYPES: string;
|
|
160
|
-
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>>;
|
|
161
336
|
|
|
162
337
|
interface WhatsAppMessageEditorLabels {
|
|
163
338
|
bold: string;
|
|
@@ -255,6 +430,22 @@ declare function ToastProvider({ children }: {
|
|
|
255
430
|
}): react.JSX.Element;
|
|
256
431
|
declare function useToast(): ToastContextValue;
|
|
257
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
|
+
|
|
258
449
|
interface StatusTicksProps {
|
|
259
450
|
status: string;
|
|
260
451
|
title?: string;
|
|
@@ -320,23 +511,23 @@ type ConversationWindow = (typeof CONVERSATION_WINDOW)[keyof typeof CONVERSATION
|
|
|
320
511
|
declare const WINDOW_FILTERS: readonly [{
|
|
321
512
|
readonly value: "all";
|
|
322
513
|
readonly label: "Todas";
|
|
323
|
-
readonly
|
|
514
|
+
readonly tone: "";
|
|
324
515
|
}, {
|
|
325
516
|
readonly value: "fresh";
|
|
326
517
|
readonly label: "<12h";
|
|
327
|
-
readonly
|
|
518
|
+
readonly tone: "fresh";
|
|
328
519
|
}, {
|
|
329
520
|
readonly value: "warning";
|
|
330
521
|
readonly label: "12-21h";
|
|
331
|
-
readonly
|
|
522
|
+
readonly tone: "warning";
|
|
332
523
|
}, {
|
|
333
524
|
readonly value: "critical";
|
|
334
525
|
readonly label: "21-24h";
|
|
335
|
-
readonly
|
|
526
|
+
readonly tone: "critical";
|
|
336
527
|
}, {
|
|
337
528
|
readonly value: "expired";
|
|
338
529
|
readonly label: ">24h";
|
|
339
|
-
readonly
|
|
530
|
+
readonly tone: "expired";
|
|
340
531
|
}];
|
|
341
532
|
type WindowOfParams = {
|
|
342
533
|
readonly lastInboundAt: string | null;
|
|
@@ -354,6 +545,45 @@ type WindowOfParams = {
|
|
|
354
545
|
declare function windowOf(params: WindowOfParams): ConversationWindow;
|
|
355
546
|
declare function formatStalledFor(lastAt: string, now: number): string;
|
|
356
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
|
+
|
|
357
587
|
type ConversationRowClassNames = {
|
|
358
588
|
root: string;
|
|
359
589
|
windowBar: string;
|
|
@@ -412,8 +642,8 @@ interface ConversationHeaderClassNames {
|
|
|
412
642
|
*/
|
|
413
643
|
interface ConversationHeaderUtility {
|
|
414
644
|
key: string;
|
|
415
|
-
/**
|
|
416
|
-
icon:
|
|
645
|
+
/** Ícone da biblioteca (lucide), no tamanho dos utilitários nativos: `<Play size={16} />`. */
|
|
646
|
+
icon: ReactNode;
|
|
417
647
|
label: string;
|
|
418
648
|
run: () => void;
|
|
419
649
|
active?: boolean;
|
|
@@ -438,20 +668,35 @@ declare function ConversationHeader({ conversation, busy, onTakeover, onReturnTo
|
|
|
438
668
|
|
|
439
669
|
/**
|
|
440
670
|
* "Suas Seleções": o que o bot já coletou na conversa. Para o atendente que assume no meio, é a
|
|
441
|
-
* 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.
|
|
442
672
|
*
|
|
443
673
|
* O pacote não interpreta o contexto — ele é `Record<string, unknown>` e cada produto nomeia as
|
|
444
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.
|
|
445
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';
|
|
446
686
|
interface ConversationContextEntry {
|
|
447
687
|
key: string;
|
|
448
688
|
label: string;
|
|
449
689
|
value?: string | undefined;
|
|
450
690
|
icon?: string;
|
|
691
|
+
status?: ConversationContextStatus;
|
|
451
692
|
}
|
|
452
693
|
interface ConversationContextPanelLabels {
|
|
453
694
|
title: string;
|
|
454
695
|
empty: string;
|
|
696
|
+
collapse: string;
|
|
697
|
+
expand: string;
|
|
698
|
+
/** Placeholder do card ainda não coletado. */
|
|
699
|
+
notCollected: string;
|
|
455
700
|
}
|
|
456
701
|
declare const DEFAULT_CONVERSATION_CONTEXT_LABELS: ConversationContextPanelLabels;
|
|
457
702
|
interface ConversationContextPanelClassNames {
|
|
@@ -462,11 +707,23 @@ interface ConversationContextPanelClassNames {
|
|
|
462
707
|
}
|
|
463
708
|
interface ConversationContextPanelProps {
|
|
464
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;
|
|
465
722
|
labels?: Partial<ConversationContextPanelLabels>;
|
|
466
723
|
className?: string;
|
|
467
724
|
classNames?: Partial<ConversationContextPanelClassNames>;
|
|
468
725
|
}
|
|
469
|
-
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;
|
|
470
727
|
|
|
471
728
|
interface WindowExpiredNoticeLabels {
|
|
472
729
|
title: string;
|
|
@@ -510,6 +767,8 @@ interface DocumentsLibraryLabels {
|
|
|
510
767
|
sortMostRecent: string;
|
|
511
768
|
sortOldest: string;
|
|
512
769
|
clearFilters: string;
|
|
770
|
+
upload: string;
|
|
771
|
+
uploadError: string;
|
|
513
772
|
total: (count: number) => string;
|
|
514
773
|
page: (current: number, last: number) => string;
|
|
515
774
|
}
|
|
@@ -541,6 +800,160 @@ interface DocumentsLibraryProps {
|
|
|
541
800
|
}
|
|
542
801
|
declare function DocumentsLibrary({ perPage, onOpenConversation, labels: labelsOverride, className, classNames, }: DocumentsLibraryProps): react.JSX.Element | null;
|
|
543
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
|
+
|
|
544
957
|
/**
|
|
545
958
|
* Arquivos trocados na conversa. Sai do transcript e vira lista própria porque anexo é o que o
|
|
546
959
|
* atendente mais precisa reencontrar depois — rolar meses de mensagens para achar um comprovante é
|
|
@@ -659,6 +1072,49 @@ declare function useDarkMode(): {
|
|
|
659
1072
|
toggle: () => void;
|
|
660
1073
|
};
|
|
661
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
|
+
|
|
662
1118
|
/**
|
|
663
1119
|
* Detecta tela estreita para decisões que CSS não resolve — como abrir ou não um painel por padrão.
|
|
664
1120
|
*
|
|
@@ -845,6 +1301,47 @@ interface WelcomeFarewellFormProps {
|
|
|
845
1301
|
}
|
|
846
1302
|
declare function WelcomeFarewellForm({ welcomeMessage, onWelcomeMessageChange, farewellMessage, onFarewellMessageChange, onSave, saving, saveSuccess, welcomePlaceholders, farewellPlaceholders, labels: labelsOverride, }: WelcomeFarewellFormProps): react.JSX.Element;
|
|
847
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
|
+
|
|
848
1345
|
declare const TEMPLATE_SETTINGS_TAB: {
|
|
849
1346
|
readonly SELECT: "select";
|
|
850
1347
|
readonly CREATE: "create";
|
|
@@ -875,10 +1372,21 @@ interface WhatsAppTemplatesSettingsProps {
|
|
|
875
1372
|
onSubmit: (event: FormEvent) => void;
|
|
876
1373
|
submitting?: boolean;
|
|
877
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[];
|
|
878
1379
|
};
|
|
879
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;
|
|
880
1388
|
}
|
|
881
|
-
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;
|
|
882
1390
|
|
|
883
1391
|
interface TopicItem {
|
|
884
1392
|
key: string;
|
|
@@ -904,6 +1412,88 @@ interface TopicsFormProps {
|
|
|
904
1412
|
}
|
|
905
1413
|
declare function TopicsForm({ topics, onToggle, onMessageChange, onSave, saving, saveSuccess, labels: labelsOverride, }: TopicsFormProps): react.JSX.Element;
|
|
906
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
|
+
|
|
907
1497
|
interface UseConversationMessagesResult {
|
|
908
1498
|
messages: MessagePayload[];
|
|
909
1499
|
loading: boolean;
|
|
@@ -928,6 +1518,23 @@ declare function useConversationMessages(conversationId: string, params?: {
|
|
|
928
1518
|
before?: string;
|
|
929
1519
|
}): UseConversationMessagesResult;
|
|
930
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
|
+
|
|
931
1538
|
type UseConversationListParams = ListConversationsParams;
|
|
932
1539
|
interface UseConversationListResult {
|
|
933
1540
|
conversations: ConversationSummary[];
|
|
@@ -1015,4 +1622,301 @@ interface AsyncResourceState<T> {
|
|
|
1015
1622
|
|
|
1016
1623
|
declare function createMediaUrlResolver(api: Pick<ConversationsApi, 'getDocumentUrl' | 'getMediaProxyUrl'>): ResolveMediaUrl;
|
|
1017
1624
|
|
|
1018
|
-
|
|
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 };
|