@adatechnology/conversations-ui 0.1.0-rc.3 → 0.1.0-rc.31
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/chunk-CUYYYZWD.js +2317 -0
- package/dist/chunk-DXPSPUWF.js +110 -0
- package/dist/{chunk-OGRRHQQW.js → chunk-WCBDXZ3X.js} +68 -4
- package/dist/flows/index.d.ts +338 -4
- package/dist/flows/index.js +1331 -55
- package/dist/index.d.ts +1074 -42
- package/dist/index.js +3572 -742
- package/dist/preview/index.d.ts +328 -8
- package/dist/preview/index.js +902 -115
- package/dist/styles.css +899 -0
- package/dist/types-De5aN-E_.d.ts +502 -0
- package/package.json +3 -3
- 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 +14 -3
- package/src/ConversationContextPanel.tsx +218 -44
- package/src/ConversationDocumentsPanel.tsx +347 -24
- package/src/ConversationHeader.test.tsx +66 -0
- package/src/ConversationHeader.tsx +163 -45
- package/src/ConversationListItem.tsx +19 -2
- package/src/ConversationLocalesProvider.tsx +42 -0
- package/src/ConversationRow.tsx +31 -7
- package/src/DocumentsLibrary.tsx +382 -0
- package/src/EmojiPicker.tsx +70 -55
- package/src/FileIcon.test.ts +83 -0
- package/src/FileIcon.tsx +88 -11
- package/src/InteractiveMessage.test.tsx +41 -0
- package/src/InteractiveMessage.tsx +146 -0
- package/src/Lightbox.tsx +18 -3
- package/src/MediaRenderer.tsx +98 -22
- package/src/MessageBubble.test.tsx +41 -0
- package/src/MessageBubble.tsx +75 -6
- package/src/MessageComposer.test.tsx +35 -0
- package/src/MessageComposer.tsx +155 -19
- 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 +164 -0
- package/src/Wallpaper.test.tsx +21 -0
- package/src/Wallpaper.tsx +67 -7
- package/src/WhatsAppMessageEditor.tsx +28 -4
- package/src/WindowExpiredNotice.tsx +12 -4
- package/src/audioRecorderFormat.test.ts +67 -0
- 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/emojiCatalog.test.ts +35 -0
- package/src/emojiCatalog.ts +189 -0
- package/src/flows/FlowGroupHeader.tsx +12 -2
- package/src/flows/FlowMapCanvas.tsx +15 -12
- package/src/flows/FlowMapNode.tsx +4 -1
- package/src/flows/FlowNodeCard.tsx +35 -8
- package/src/flows/FlowNodePanel.tsx +149 -38
- package/src/flows/FlowPalette.tsx +13 -3
- package/src/flows/FlowPortalNode.tsx +1 -1
- package/src/flows/FlowWhatsAppPreview.tsx +14 -3
- package/src/flows/FlowsWorkspace.tsx +1023 -0
- package/src/flows/flowCanvasModel.test.ts +312 -0
- package/src/flows/flowCanvasModel.ts +368 -0
- package/src/flows/flowEditorOps.test.ts +241 -0
- package/src/flows/flowEditorOps.ts +177 -0
- package/src/flows/flowGraph.ts +6 -6
- package/src/flows/index.ts +47 -1
- package/src/flows/labels.ts +141 -0
- package/src/flows/workspaceContract.test.ts +95 -0
- package/src/hooks/useContainerWidth.ts +35 -0
- package/src/hooks/useConversationActions.ts +56 -0
- package/src/hooks/useConversationDocuments.ts +11 -7
- package/src/hooks/useConversationList.ts +15 -9
- package/src/hooks/useConversationMessages.ts +2 -2
- 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 +114 -13
- package/src/lib/cn.test.ts +29 -0
- package/src/lib/composer-formatting.test.ts +78 -0
- package/src/lib/composer-formatting.ts +145 -0
- package/src/lib/createMediaUrlResolver.ts +33 -0
- package/src/lib/paginated.test.ts +33 -0
- package/src/lib/paginated.ts +26 -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 +199 -13
- package/src/preview/ConversationSimulatorPanel.test.tsx +55 -0
- package/src/preview/ConversationSimulatorPanel.tsx +89 -0
- package/src/preview/MediaTypesPreview.tsx +87 -0
- package/src/preview/conversationPreviewFailures.test.ts +64 -0
- package/src/preview/createMockConversationsApi.ts +175 -15
- 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 +127 -4
- package/src/preview/index.ts +33 -3
- package/src/preview/mediaTypeOf.test.ts +15 -0
- package/src/preview/mockDocumentsSearch.test.ts +57 -0
- package/src/preview/preview.test.ts +5 -3
- package/src/preview/previewFileSamples.test.ts +151 -0
- package/src/preview/previewFileSamples.ts +74 -0
- package/src/preview/previewFixtures.ts +288 -1
- package/src/preview/previewMediaSource.test.ts +62 -0
- package/src/preview/previewMediaSource.ts +91 -0
- package/src/preview/previewMediaUploader.test.ts +61 -0
- package/src/providers/ConversationsProvider.tsx +8 -6
- package/src/providers/types.ts +185 -10
- package/src/quickReply.test.ts +58 -0
- package/src/settings/MessagesWorkspace.tsx +468 -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 +4 -1
- package/src/settings/WhatsAppTemplateSettingsForm.tsx +5 -2
- package/src/settings/WhatsAppTemplatesSettings.tsx +9 -1
- package/src/styles.css +862 -0
- package/src/types.ts +64 -1
- package/src/useWaitingNotifications.ts +74 -29
- 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 +346 -0
- package/src/workspace/index.ts +12 -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-N7B24WYD.js +0 -719
- package/dist/chunk-NV2RZ5KT.js +0 -56
- package/dist/types-C0PtaO7S.d.ts +0 -207
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import react__default, { ReactNode, FormEvent } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
export {
|
|
5
|
-
|
|
6
|
-
type ResolveMediaUrl = (message: MessagePayload) => Promise<string | null>;
|
|
7
|
-
interface MediaRendererProps {
|
|
8
|
-
message: MessagePayload;
|
|
9
|
-
onLightbox: (src: string) => void;
|
|
10
|
-
onResolveUrl?: ResolveMediaUrl;
|
|
11
|
-
}
|
|
12
|
-
declare function MediaRenderer({ message, onLightbox, onResolveUrl }: MediaRendererProps): react.JSX.Element | null;
|
|
2
|
+
import react__default, { ReactNode, CSSProperties, UIEvent, FormEvent, RefObject } from 'react';
|
|
3
|
+
import { G as MessagePayload, N as ResolveMediaUrl, z as InteractiveSelection, J as MessageTranscription, x as InteractivePayload, r as ConversationsFeatures, o as ConversationSummary, j as ConversationChannel, L as ListConversationsParams, q as ConversationsApi, S as SSEProvider, T as TranscriptionMode, B as ListDocumentsParams, k as ConversationDocument, p as ConversationTemplate, f as ChannelFilter, g as ChannelFilterOption } from './types-De5aN-E_.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, s as ConversationsTheme, t as ConversationsUIConfig, D as DEFAULT_AUDIO_RECORDER_BUTTON_LABELS, u as DEFAULT_CONVERSATION_CHANNEL, v as DEFAULT_MAX_RECORDING_MILLISECONDS, F as FormatContactHandleParams, H as HANDLE_KIND, w as HandleKind, I as InteractiveOption, y as InteractiveSection, M as MediaRenderer, E as MediaRendererProps, R as REOPEN_MECHANISM, K as ReopenMechanism, O as TranscriptionStatus, P as capabilitiesOf, Q as channelFiltersFor, U as contactFlag, V as formatContactHandle } from './types-De5aN-E_.js';
|
|
13
5
|
|
|
14
6
|
interface MessageBubbleProps {
|
|
15
7
|
message: MessagePayload;
|
|
@@ -19,16 +11,56 @@ interface MessageBubbleProps {
|
|
|
19
11
|
isSelecting?: boolean;
|
|
20
12
|
isSelected?: boolean;
|
|
21
13
|
onToggleSelect?: () => void;
|
|
14
|
+
/**
|
|
15
|
+
* Como buscar a mídia da mensagem. Ausente, o balão usa o `ConversationsApi` do
|
|
16
|
+
* `ConversationsProvider` — passe apenas para sobrescrever (cache próprio, CDN, proxy do host).
|
|
17
|
+
*/
|
|
22
18
|
onResolveMediaUrl?: ResolveMediaUrl;
|
|
19
|
+
/**
|
|
20
|
+
* Toque numa opção do menu interativo. Ausente, as opções aparecem desabilitadas — que é o certo
|
|
21
|
+
* no histórico da inbox: o operador vê o que foi oferecido, sem responder no lugar do cliente.
|
|
22
|
+
*/
|
|
23
|
+
onInteractiveSelect?: (selection: InteractiveSelection) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Pede a transcrição do áudio. Ausente, o balão usa `transcribeAudio` do `ConversationsApi` do
|
|
26
|
+
* contexto — passe apenas para sobrescrever. Sem nenhum dos dois, o bloco de transcrição só exibe
|
|
27
|
+
* o que já veio pronto do backend.
|
|
28
|
+
*/
|
|
29
|
+
onTranscribeAudio?: (messageId: string) => Promise<MessageTranscription | void>;
|
|
30
|
+
className?: string;
|
|
31
|
+
}
|
|
32
|
+
declare function MessageBubble({ message, isMine, senderName, isFirstInGroup, isSelecting, isSelected, onToggleSelect, onResolveMediaUrl, onInteractiveSelect, onTranscribeAudio, className, }: MessageBubbleProps): react.JSX.Element;
|
|
33
|
+
|
|
34
|
+
interface InteractiveMessageLabels {
|
|
35
|
+
/** Fallback do rótulo do botão que abre a lista, quando o payload não traz um. */
|
|
36
|
+
openList: string;
|
|
37
|
+
}
|
|
38
|
+
declare const DEFAULT_INTERACTIVE_MESSAGE_LABELS: InteractiveMessageLabels;
|
|
39
|
+
interface InteractiveMessageProps {
|
|
40
|
+
payload: InteractivePayload;
|
|
41
|
+
onSelect?: (selection: InteractiveSelection) => void;
|
|
42
|
+
labels?: Partial<InteractiveMessageLabels>;
|
|
23
43
|
className?: string;
|
|
24
44
|
}
|
|
25
|
-
declare function
|
|
45
|
+
declare function InteractiveMessage({ payload, onSelect, labels, className }: InteractiveMessageProps): react.JSX.Element;
|
|
26
46
|
|
|
27
47
|
interface ConversationWallpaperProps {
|
|
28
48
|
children?: ReactNode;
|
|
29
49
|
className?: string;
|
|
50
|
+
/** Ajusta ou substitui o fundo padrão — para produto com identidade visual própria. */
|
|
51
|
+
style?: CSSProperties;
|
|
52
|
+
/**
|
|
53
|
+
* Rolagem da área de mensagens. Par do `ref`: é este elemento que rola, então é aqui que
|
|
54
|
+
* `useScrollToLatestMessage` observa a posição para saber se o operador está acompanhando o fim.
|
|
55
|
+
*/
|
|
56
|
+
onScroll?: (event: UIEvent<HTMLDivElement>) => void;
|
|
30
57
|
}
|
|
31
|
-
|
|
58
|
+
/**
|
|
59
|
+
* `forwardRef` porque quem controla a rolagem é de fora — o hook precisa do elemento para saltar
|
|
60
|
+
* até a última mensagem. `forwardRef` e não `ref` como prop: o pacote suporta React 18, onde
|
|
61
|
+
* ref-como-prop ainda não existe.
|
|
62
|
+
*/
|
|
63
|
+
declare const ConversationWallpaper: react.ForwardRefExoticComponent<ConversationWallpaperProps & react.RefAttributes<HTMLDivElement>>;
|
|
32
64
|
|
|
33
65
|
interface ConversationLocales {
|
|
34
66
|
bubble: {
|
|
@@ -44,6 +76,27 @@ interface ConversationLocales {
|
|
|
44
76
|
listenAudio: string;
|
|
45
77
|
viewVideo: string;
|
|
46
78
|
moderationFlagged: string;
|
|
79
|
+
mediaLoading: string;
|
|
80
|
+
mediaRetry: string;
|
|
81
|
+
mediaError: string;
|
|
82
|
+
mediaUnavailable: string;
|
|
83
|
+
imageAlt: string;
|
|
84
|
+
untitledDocument: string;
|
|
85
|
+
downloadFile: string;
|
|
86
|
+
};
|
|
87
|
+
transcription: {
|
|
88
|
+
label: string;
|
|
89
|
+
copy: string;
|
|
90
|
+
copied: string;
|
|
91
|
+
transcribe: string;
|
|
92
|
+
transcribing: string;
|
|
93
|
+
retry: string;
|
|
94
|
+
failed: string;
|
|
95
|
+
/** Áudio processado sem fala detectada — distinto de "não transcrito". */
|
|
96
|
+
empty: string;
|
|
97
|
+
unsupported: string;
|
|
98
|
+
showMore: string;
|
|
99
|
+
showLess: string;
|
|
47
100
|
};
|
|
48
101
|
selection: {
|
|
49
102
|
select: string;
|
|
@@ -69,13 +122,97 @@ interface AudioPlayerProps {
|
|
|
69
122
|
}
|
|
70
123
|
declare function AudioPlayer({ src, isMine }: AudioPlayerProps): react.JSX.Element;
|
|
71
124
|
|
|
125
|
+
interface AudioTranscriptionProps {
|
|
126
|
+
transcription?: MessageTranscription | null;
|
|
127
|
+
/**
|
|
128
|
+
* Pede a transcrição ao backend. Ausente, o bloco nunca oferece o botão — um host em modo
|
|
129
|
+
* automático não tem rota para isso, e desenhar um botão que estoura no clique é pior do que não
|
|
130
|
+
* desenhar nada.
|
|
131
|
+
*
|
|
132
|
+
* O que for devolvido é exibido na hora. Sem isso o texto só apareceria no próximo refetch da
|
|
133
|
+
* lista, e transcrição não emite evento de tempo real: o operador clicaria, veria o spinner
|
|
134
|
+
* terminar e continuaria sem o texto na tela.
|
|
135
|
+
*/
|
|
136
|
+
onTranscribe?: () => Promise<MessageTranscription | void>;
|
|
137
|
+
isMine?: boolean;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Transcrição sob a nota de voz, com botão de copiar.
|
|
141
|
+
*
|
|
142
|
+
* Copiar é o motivo de o bloco existir: o operador cola o pedido do cliente no sistema interno, no
|
|
143
|
+
* orçamento, na busca. Seleção manual de texto dentro de um balão de chat é exatamente onde o
|
|
144
|
+
* arrasto do mouse falha — pega o balão vizinho, o horário, o nome do remetente.
|
|
145
|
+
*
|
|
146
|
+
* `navigator.clipboard.writeText` com try/catch silencioso, mesmo padrão de `MessageText.tsx`: a API
|
|
147
|
+
* exige contexto seguro e permissão, e um host servindo em HTTP simples não a tem. Falhar sem alarme
|
|
148
|
+
* é o certo — o texto continua na tela para seleção manual.
|
|
149
|
+
*/
|
|
150
|
+
declare function AudioTranscription({ transcription, onTranscribe, isMine }: AudioTranscriptionProps): react.JSX.Element | null;
|
|
151
|
+
|
|
152
|
+
interface EmojiPickerLabels {
|
|
153
|
+
search: string;
|
|
154
|
+
noResults: string;
|
|
155
|
+
}
|
|
156
|
+
declare const DEFAULT_EMOJI_PICKER_LABELS: EmojiPickerLabels;
|
|
72
157
|
interface EmojiPickerProps {
|
|
73
158
|
onSelect: (emoji: string) => void;
|
|
159
|
+
labels?: Partial<EmojiPickerLabels>;
|
|
74
160
|
className?: string;
|
|
75
161
|
}
|
|
76
|
-
declare const EmojiPicker: ({ onSelect, className }: EmojiPickerProps) => react.JSX.Element;
|
|
162
|
+
declare const EmojiPicker: ({ onSelect, labels, className }: EmojiPickerProps) => react.JSX.Element;
|
|
77
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Catálogo de emojis com palavras-chave em português, usado pela busca do seletor.
|
|
166
|
+
*
|
|
167
|
+
* Fica em módulo próprio (e não dentro do `EmojiPicker`) porque a busca precisa varrer TODAS as
|
|
168
|
+
* categorias, não só a aberta — o índice é montado uma vez, no carregamento, e não a cada tecla.
|
|
169
|
+
*
|
|
170
|
+
* As palavras-chave são de busca, não legendas: incluem sinônimos e formas sem acento, porque quem
|
|
171
|
+
* digita rápido escreve "coracao" e "polegar" com a mesma frequência que a forma correta.
|
|
172
|
+
*/
|
|
173
|
+
type EmojiEntry = {
|
|
174
|
+
readonly emoji: string;
|
|
175
|
+
readonly keywords: readonly string[];
|
|
176
|
+
};
|
|
177
|
+
type EmojiCategory = {
|
|
178
|
+
readonly name: string;
|
|
179
|
+
readonly entries: readonly EmojiEntry[];
|
|
180
|
+
};
|
|
181
|
+
declare const EMOJI_CATEGORIES: readonly EmojiCategory[];
|
|
182
|
+
/**
|
|
183
|
+
* Busca por prefixo de palavra-chave, não por substring: "ca" traz "casa" e "cartão", mas "sa" não
|
|
184
|
+
* traz "casa" — casar no meio da palavra devolvia resultado que ninguém consegue explicar.
|
|
185
|
+
*/
|
|
186
|
+
declare function searchEmojis(query: string): readonly EmojiEntry[];
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Mensagem pronta que o atendente cola no campo com um clique.
|
|
190
|
+
*
|
|
191
|
+
* `text` aceita string com `{{variavel}}` ou função: a string cobre o caso comum (copy fixa com o
|
|
192
|
+
* nome do cliente no meio) sem o host escrever código, e a função cobre o que precisa de lógica —
|
|
193
|
+
* escolher texto por produto, pluralizar, formatar moeda.
|
|
194
|
+
*/
|
|
195
|
+
interface QuickReply {
|
|
196
|
+
key: string;
|
|
197
|
+
/** O que aparece no chip, emoji incluso: `👋 Saudação`. */
|
|
198
|
+
label: string;
|
|
199
|
+
text: string | ((variables: Readonly<Record<string, string>>) => string);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Troca `{{nome}}` pelos valores passados. Variável ausente vira string vazia, e não o literal
|
|
203
|
+
* `{{nome}}`: mandar "Olá {{nome}}!" para o cliente é pior que mandar "Olá !".
|
|
204
|
+
*/
|
|
205
|
+
declare function applyQuickReplyVariables(template: string, variables?: Readonly<Record<string, string>>): string;
|
|
206
|
+
declare function resolveQuickReply(quickReply: QuickReply, variables?: Readonly<Record<string, string>>): string;
|
|
207
|
+
interface MessageComposerLabels {
|
|
208
|
+
emoji: string;
|
|
209
|
+
attach: string;
|
|
210
|
+
send: string;
|
|
211
|
+
removeAttachment: string;
|
|
212
|
+
}
|
|
213
|
+
declare const DEFAULT_MESSAGE_COMPOSER_LABELS: MessageComposerLabels;
|
|
78
214
|
interface MessageComposerProps {
|
|
215
|
+
labels?: Partial<MessageComposerLabels>;
|
|
79
216
|
onSend: (text: string) => void;
|
|
80
217
|
onAttach?: (file: File) => void;
|
|
81
218
|
value?: string;
|
|
@@ -85,16 +222,130 @@ interface MessageComposerProps {
|
|
|
85
222
|
maxLength?: number;
|
|
86
223
|
disabled?: boolean;
|
|
87
224
|
acceptedFileTypes?: string;
|
|
225
|
+
/**
|
|
226
|
+
* Ocupa o lugar do botão de enviar enquanto não há nada para enviar — é onde o WhatsApp põe o
|
|
227
|
+
* microfone. Fora do campo, o botão vira um bloco solto ao lado do pill e quebra a barra.
|
|
228
|
+
*/
|
|
229
|
+
idleAction?: ReactNode;
|
|
230
|
+
/** Mensagens prontas exibidas acima do campo. Vazio ou ausente, a faixa não é renderizada. */
|
|
231
|
+
quickReplies?: readonly QuickReply[];
|
|
232
|
+
/** Valores para `{{variavel}}` — tipicamente nome do cliente, produto, protocolo. */
|
|
233
|
+
quickReplyVariables?: Readonly<Record<string, string>>;
|
|
88
234
|
className?: string;
|
|
89
235
|
classNames?: Partial<MessageComposerClassNames>;
|
|
90
236
|
}
|
|
91
237
|
interface MessageComposerClassNames {
|
|
92
238
|
root: string;
|
|
239
|
+
quickReplies: string;
|
|
240
|
+
quickReply: string;
|
|
93
241
|
field: string;
|
|
94
242
|
}
|
|
95
|
-
|
|
243
|
+
/**
|
|
244
|
+
* Exatamente o que a Meta aceita em mensagem de mídia — imagem, sticker, áudio, vídeo e a lista
|
|
245
|
+
* fechada de documentos. Oferecer no seletor um formato que o WhatsApp recusa (`.zip`, `.rtf`)
|
|
246
|
+
* empurra a falha para depois do envio, quando já não dá para explicar ao operador o que houve.
|
|
247
|
+
* Produto com regra própria passa `acceptedFileTypes`.
|
|
248
|
+
*/
|
|
249
|
+
declare const DEFAULT_ACCEPTED_FILE_TYPES: string;
|
|
250
|
+
declare const MessageComposer: ({ onSend, onAttach, value: externalValue, onChange: externalOnChange, quickReplies, quickReplyVariables, features, placeholder, maxLength, disabled, acceptedFileTypes, idleAction, className, classNames, labels, }: MessageComposerProps) => react.JSX.Element;
|
|
96
251
|
|
|
252
|
+
/** Cada ação que a barra sabe oferecer. `toolbar` decide quais delas aparecem. */
|
|
253
|
+
declare const RICH_COMPOSER_ACTION: {
|
|
254
|
+
readonly BOLD: "bold";
|
|
255
|
+
readonly ITALIC: "italic";
|
|
256
|
+
readonly STRIKETHROUGH: "strikethrough";
|
|
257
|
+
readonly MONOSPACE: "monospace";
|
|
258
|
+
readonly EMOJI: "emoji";
|
|
259
|
+
readonly ATTACH: "attach";
|
|
260
|
+
readonly VARIABLES: "variables";
|
|
261
|
+
};
|
|
262
|
+
type RichComposerAction = (typeof RICH_COMPOSER_ACTION)[keyof typeof RICH_COMPOSER_ACTION];
|
|
263
|
+
/** Texto das dicas. O host troca só o que quiser; o resto fica no padrão. */
|
|
264
|
+
interface RichComposerTooltips {
|
|
265
|
+
bold: string;
|
|
266
|
+
italic: string;
|
|
267
|
+
strikethrough: string;
|
|
268
|
+
monospace: string;
|
|
269
|
+
emoji: string;
|
|
270
|
+
attach: string;
|
|
271
|
+
send: string;
|
|
272
|
+
variables: string;
|
|
273
|
+
/** Botão que abre a formatação recolhida, quando a barra está estreita demais para os quatro. */
|
|
274
|
+
formatting: string;
|
|
275
|
+
}
|
|
276
|
+
declare const DEFAULT_RICH_COMPOSER_TOOLTIPS: RichComposerTooltips;
|
|
277
|
+
/**
|
|
278
|
+
* Um valor que o operador pode inserir no texto sem digitar. `value` é o que entra no campo — pode
|
|
279
|
+
* ser o dado já resolvido ("Anderson") ou um marcador a resolver depois ("{{nome}}"): quem decide é
|
|
280
|
+
* o host, porque só ele sabe se a substituição acontece aqui ou no envio.
|
|
281
|
+
*/
|
|
282
|
+
interface RichComposerVariable {
|
|
283
|
+
id: string;
|
|
284
|
+
label: string;
|
|
285
|
+
value: string;
|
|
286
|
+
/** Prévia do que será inserido, para o operador conferir antes de tocar. */
|
|
287
|
+
tooltip?: string;
|
|
288
|
+
}
|
|
289
|
+
/** Uma resposta rápida: o que o operador lê no chip e o que cai no campo ao tocar nele. */
|
|
290
|
+
interface RichComposerQuickReply {
|
|
291
|
+
id: string;
|
|
292
|
+
label: string;
|
|
293
|
+
text: string;
|
|
294
|
+
/** Dica ao passar o mouse. Sem isto, o chip não tem `title` — rótulo curto já se explica. */
|
|
295
|
+
tooltip?: string;
|
|
296
|
+
}
|
|
297
|
+
/** Handle imperativo: `contentEditable` controlado pelo React perde o cursor a cada tecla. */
|
|
298
|
+
interface RichMessageComposerHandle {
|
|
299
|
+
setContent: (text: string) => void;
|
|
300
|
+
clear: () => void;
|
|
301
|
+
focus: () => void;
|
|
302
|
+
}
|
|
303
|
+
interface RichMessageComposerProps {
|
|
304
|
+
/** Texto na notação do WhatsApp (`*negrito*`), não HTML. */
|
|
305
|
+
value: string;
|
|
306
|
+
onChange: (value: string) => void;
|
|
307
|
+
onSend: () => void;
|
|
308
|
+
onAttachFiles?: (files: FileList) => void;
|
|
309
|
+
quickReplies?: RichComposerQuickReply[];
|
|
310
|
+
/** Variáveis que o operador pode inserir no texto. Vazio ou ausente, o botão não aparece. */
|
|
311
|
+
variables?: RichComposerVariable[];
|
|
312
|
+
/**
|
|
313
|
+
* Quais ações aparecem. Ausente, todas aparecem — menos as que não têm como funcionar (anexo sem
|
|
314
|
+
* `onAttachFiles` some sozinho, porque botão que não faz nada é pior que botão nenhum).
|
|
315
|
+
*/
|
|
316
|
+
toolbar?: Partial<Record<RichComposerAction, boolean>>;
|
|
317
|
+
tooltips?: Partial<RichComposerTooltips>;
|
|
318
|
+
placeholder?: string;
|
|
319
|
+
disabled?: boolean;
|
|
320
|
+
isSending?: boolean;
|
|
321
|
+
/** Ocupa o lugar do enviar enquanto não há nada para enviar — onde o WhatsApp põe o microfone. */
|
|
322
|
+
idleAction?: ReactNode;
|
|
323
|
+
/** Prévia dos anexos já escolhidos, desenhada pelo host acima da barra. */
|
|
324
|
+
attachmentsPreview?: ReactNode;
|
|
325
|
+
/**
|
|
326
|
+
* Há anexo na fila esperando envio. O campo não conhece a fila — é do host — e sem essa dica ele
|
|
327
|
+
* concluía que não havia nada a enviar: com uma imagem anexada e nenhum texto, o botão de enviar
|
|
328
|
+
* dava lugar ao `idleAction` e a imagem ficava presa.
|
|
329
|
+
*/
|
|
330
|
+
hasQueuedAttachments?: boolean;
|
|
331
|
+
acceptedFileTypes?: string;
|
|
332
|
+
className?: string;
|
|
333
|
+
}
|
|
334
|
+
declare const RichMessageComposer: react.ForwardRefExoticComponent<RichMessageComposerProps & react.RefAttributes<RichMessageComposerHandle>>;
|
|
335
|
+
|
|
336
|
+
interface WhatsAppMessageEditorLabels {
|
|
337
|
+
bold: string;
|
|
338
|
+
/** Tooltip da negrito — traz a sintaxe do WhatsApp junto, por isso é separado do `aria-label`. */
|
|
339
|
+
boldHint: string;
|
|
340
|
+
italic: string;
|
|
341
|
+
italicHint: string;
|
|
342
|
+
strikethrough: string;
|
|
343
|
+
strikethroughHint: string;
|
|
344
|
+
insertPlaceholder: (token: string) => string;
|
|
345
|
+
}
|
|
346
|
+
declare const DEFAULT_WHATSAPP_MESSAGE_EDITOR_LABELS: WhatsAppMessageEditorLabels;
|
|
97
347
|
interface WhatsAppMessageEditorProps {
|
|
348
|
+
labels?: Partial<WhatsAppMessageEditorLabels>;
|
|
98
349
|
value: string;
|
|
99
350
|
onChange: (value: string) => void;
|
|
100
351
|
placeholder?: string;
|
|
@@ -103,7 +354,7 @@ interface WhatsAppMessageEditorProps {
|
|
|
103
354
|
previewLabel?: string;
|
|
104
355
|
emptyPreviewText?: string;
|
|
105
356
|
}
|
|
106
|
-
declare function WhatsAppMessageEditor({ value, onChange, placeholder, placeholders, rows, previewLabel, emptyPreviewText, }: WhatsAppMessageEditorProps): react.JSX.Element;
|
|
357
|
+
declare function WhatsAppMessageEditor({ value, onChange, placeholder, placeholders, rows, previewLabel, emptyPreviewText, labels, }: WhatsAppMessageEditorProps): react.JSX.Element;
|
|
107
358
|
|
|
108
359
|
interface SimpleEmojiPickerProps {
|
|
109
360
|
onSelect: (emoji: string) => void;
|
|
@@ -124,16 +375,30 @@ interface DateDividerProps {
|
|
|
124
375
|
}
|
|
125
376
|
declare function DateDivider({ iso, className, classNames }: DateDividerProps): react.JSX.Element;
|
|
126
377
|
|
|
378
|
+
interface AvatarLabels {
|
|
379
|
+
/** Lido por leitor de tela quando não há nome nem imagem — a silhueta genérica. */
|
|
380
|
+
unnamedContact: string;
|
|
381
|
+
}
|
|
127
382
|
interface AvatarProps {
|
|
128
383
|
name?: string | null;
|
|
129
384
|
avatarUrl?: string;
|
|
130
385
|
size?: 'sm' | 'md' | 'lg';
|
|
131
386
|
className?: string;
|
|
387
|
+
labels?: Partial<AvatarLabels>;
|
|
132
388
|
}
|
|
133
|
-
declare
|
|
389
|
+
declare const DEFAULT_AVATAR_LABELS: AvatarLabels;
|
|
390
|
+
declare function Avatar({ name, avatarUrl, size, className, labels }: AvatarProps): react.JSX.Element;
|
|
134
391
|
|
|
392
|
+
interface ConversationListItemLabels {
|
|
393
|
+
/** Tooltip do ponto vermelho: a janela de atendimento de 24h já fechou. */
|
|
394
|
+
expiredWindow: string;
|
|
395
|
+
/** Tooltip do ponto laranja: a janela de atendimento está perto de fechar. */
|
|
396
|
+
warningWindow: string;
|
|
397
|
+
}
|
|
398
|
+
declare const DEFAULT_CONVERSATION_LIST_ITEM_LABELS: ConversationListItemLabels;
|
|
135
399
|
interface ConversationListItemProps {
|
|
136
400
|
conversation: ConversationSummary;
|
|
401
|
+
labels?: Partial<ConversationListItemLabels>;
|
|
137
402
|
active?: boolean;
|
|
138
403
|
selected?: boolean;
|
|
139
404
|
onClick?: () => void;
|
|
@@ -150,7 +415,7 @@ interface ConversationListItemProps {
|
|
|
150
415
|
*/
|
|
151
416
|
highlightActive?: boolean;
|
|
152
417
|
}
|
|
153
|
-
declare const ConversationListItem: ({ conversation, active, selected, onClick, onSelect, showDivider, highlightActive, }: ConversationListItemProps) => react.JSX.Element;
|
|
418
|
+
declare const ConversationListItem: ({ conversation, active, selected, onClick, onSelect, showDivider, highlightActive, labels, }: ConversationListItemProps) => react.JSX.Element;
|
|
154
419
|
|
|
155
420
|
type ToastType = 'success' | 'error' | 'info';
|
|
156
421
|
interface ToastContextValue {
|
|
@@ -164,18 +429,41 @@ declare function ToastProvider({ children }: {
|
|
|
164
429
|
}): react.JSX.Element;
|
|
165
430
|
declare function useToast(): ToastContextValue;
|
|
166
431
|
|
|
432
|
+
/**
|
|
433
|
+
* Camada única de dicas do pacote.
|
|
434
|
+
*
|
|
435
|
+
* O `title` nativo existia em todos os botões e mesmo assim a dica "não aparecia": o navegador
|
|
436
|
+
* espera de 1 a 2 segundos antes de desenhar, o balão sai fora do tema e some ao menor movimento do
|
|
437
|
+
* mouse. Aqui a dica é do produto — aparece em 120ms, com o mesmo contraste do resto da interface.
|
|
438
|
+
*
|
|
439
|
+
* Um só ouvinte no documento atende a interface inteira: cada botão declara `data-cv-tooltip` e não
|
|
440
|
+
* precisa de estado, ref ou wrapper próprio. O balão é impresso em `document.body` via portal
|
|
441
|
+
* porque `overflow` de container (a régua de respostas rápidas rola na horizontal) recortaria um
|
|
442
|
+
* balão desenhado dentro do próprio botão.
|
|
443
|
+
*/
|
|
444
|
+
/** Atributo que marca um elemento como portador de dica. */
|
|
445
|
+
declare const TOOLTIP_ATTRIBUTE = "data-cv-tooltip";
|
|
446
|
+
declare function TooltipLayer(): react.ReactPortal | null;
|
|
447
|
+
|
|
167
448
|
interface StatusTicksProps {
|
|
168
449
|
status: string;
|
|
169
450
|
title?: string;
|
|
170
451
|
}
|
|
171
452
|
declare function StatusTicks({ status, title }: StatusTicksProps): react.JSX.Element;
|
|
172
453
|
|
|
454
|
+
interface LightboxLabels {
|
|
455
|
+
/** Texto alternativo quando a imagem não tem legenda — sem ele o leitor de tela anuncia a URL. */
|
|
456
|
+
imageAlt: string;
|
|
457
|
+
close: string;
|
|
458
|
+
}
|
|
173
459
|
interface LightboxProps {
|
|
174
460
|
imageUrl: string;
|
|
175
461
|
caption?: string;
|
|
176
462
|
onClose: () => void;
|
|
463
|
+
labels?: Partial<LightboxLabels>;
|
|
177
464
|
}
|
|
178
|
-
declare
|
|
465
|
+
declare const DEFAULT_LIGHTBOX_LABELS: LightboxLabels;
|
|
466
|
+
declare function Lightbox({ imageUrl, caption, onClose, labels }: LightboxProps): react.JSX.Element;
|
|
179
467
|
|
|
180
468
|
interface FileIconProps {
|
|
181
469
|
filename?: string;
|
|
@@ -222,23 +510,23 @@ type ConversationWindow = (typeof CONVERSATION_WINDOW)[keyof typeof CONVERSATION
|
|
|
222
510
|
declare const WINDOW_FILTERS: readonly [{
|
|
223
511
|
readonly value: "all";
|
|
224
512
|
readonly label: "Todas";
|
|
225
|
-
readonly
|
|
513
|
+
readonly tone: "";
|
|
226
514
|
}, {
|
|
227
515
|
readonly value: "fresh";
|
|
228
516
|
readonly label: "<12h";
|
|
229
|
-
readonly
|
|
517
|
+
readonly tone: "fresh";
|
|
230
518
|
}, {
|
|
231
519
|
readonly value: "warning";
|
|
232
520
|
readonly label: "12-21h";
|
|
233
|
-
readonly
|
|
521
|
+
readonly tone: "warning";
|
|
234
522
|
}, {
|
|
235
523
|
readonly value: "critical";
|
|
236
524
|
readonly label: "21-24h";
|
|
237
|
-
readonly
|
|
525
|
+
readonly tone: "critical";
|
|
238
526
|
}, {
|
|
239
527
|
readonly value: "expired";
|
|
240
528
|
readonly label: ">24h";
|
|
241
|
-
readonly
|
|
529
|
+
readonly tone: "expired";
|
|
242
530
|
}];
|
|
243
531
|
type WindowOfParams = {
|
|
244
532
|
readonly lastInboundAt: string | null;
|
|
@@ -307,6 +595,19 @@ interface ConversationHeaderClassNames {
|
|
|
307
595
|
desktopActions: string;
|
|
308
596
|
mobileMenu: string;
|
|
309
597
|
}
|
|
598
|
+
/**
|
|
599
|
+
* Utilitário extra que o host pendura no cabeçalho — ícone no desktop, item de menu no celular,
|
|
600
|
+
* como os nativos. Entra por aqui, e não por um slot de ReactNode, porque é isso que preserva o
|
|
601
|
+
* comportamento responsivo: um nó solto viraria um quarto ícone em 375px, sem área de toque.
|
|
602
|
+
*/
|
|
603
|
+
interface ConversationHeaderUtility {
|
|
604
|
+
key: string;
|
|
605
|
+
/** Ícone da biblioteca (lucide), no tamanho dos utilitários nativos: `<Play size={16} />`. */
|
|
606
|
+
icon: ReactNode;
|
|
607
|
+
label: string;
|
|
608
|
+
run: () => void;
|
|
609
|
+
active?: boolean;
|
|
610
|
+
}
|
|
310
611
|
interface ConversationHeaderProps {
|
|
311
612
|
conversation: ConversationSummary;
|
|
312
613
|
busy?: boolean;
|
|
@@ -316,29 +617,46 @@ interface ConversationHeaderProps {
|
|
|
316
617
|
onDownload?: () => void;
|
|
317
618
|
onOpenDocuments?: () => void;
|
|
318
619
|
documentsOpen?: boolean;
|
|
620
|
+
/** Ações do produto que não existem no contrato do pacote (ex.: ferramentas de dev). */
|
|
621
|
+
extraUtilities?: readonly ConversationHeaderUtility[];
|
|
319
622
|
onBack?: () => void;
|
|
320
623
|
labels?: Partial<ConversationHeaderLabels>;
|
|
321
624
|
className?: string;
|
|
322
625
|
classNames?: Partial<ConversationHeaderClassNames>;
|
|
323
626
|
}
|
|
324
|
-
declare function ConversationHeader({ conversation, busy, onTakeover, onReturnToBot, onFinish, onDownload, onOpenDocuments, documentsOpen, onBack, labels: labelsOverride, className, classNames, }: ConversationHeaderProps): react.JSX.Element;
|
|
627
|
+
declare function ConversationHeader({ conversation, busy, onTakeover, onReturnToBot, onFinish, onDownload, onOpenDocuments, documentsOpen, extraUtilities, onBack, labels: labelsOverride, className, classNames, }: ConversationHeaderProps): react.JSX.Element;
|
|
325
628
|
|
|
326
629
|
/**
|
|
327
630
|
* "Suas Seleções": o que o bot já coletou na conversa. Para o atendente que assume no meio, é a
|
|
328
|
-
* diferença entre ler o transcript inteiro e ver o estado
|
|
631
|
+
* diferença entre ler o transcript inteiro e ver o estado de relance.
|
|
329
632
|
*
|
|
330
633
|
* O pacote não interpreta o contexto — ele é `Record<string, unknown>` e cada produto nomeia as
|
|
331
634
|
* próprias chaves. O host traduz para `entries`; aqui só se decide como mostrar.
|
|
635
|
+
*
|
|
636
|
+
* Desenho em paridade com financiamento-imobiliario-bot/apps/web/src/components/SelectionsSummary.tsx:
|
|
637
|
+
* card com gradiente, badge de contagem, pills de status no cabeçalho e grid de cards com borda
|
|
638
|
+
* colorida por estado. O que ficou diferente de lá, e por quê, está comentado no ponto.
|
|
332
639
|
*/
|
|
640
|
+
/**
|
|
641
|
+
* `completed` tem valor, `pending` ainda não foi coletado, `editing` é o cliente refazendo a
|
|
642
|
+
* resposta. O host só precisa mandar `status` para o terceiro caso — os dois primeiros saem do
|
|
643
|
+
* próprio `value`, e exigir o campo quebraria quem já usa o painel.
|
|
644
|
+
*/
|
|
645
|
+
type ConversationContextStatus = 'completed' | 'pending' | 'editing';
|
|
333
646
|
interface ConversationContextEntry {
|
|
334
647
|
key: string;
|
|
335
648
|
label: string;
|
|
336
649
|
value?: string | undefined;
|
|
337
650
|
icon?: string;
|
|
651
|
+
status?: ConversationContextStatus;
|
|
338
652
|
}
|
|
339
653
|
interface ConversationContextPanelLabels {
|
|
340
654
|
title: string;
|
|
341
655
|
empty: string;
|
|
656
|
+
collapse: string;
|
|
657
|
+
expand: string;
|
|
658
|
+
/** Placeholder do card ainda não coletado. */
|
|
659
|
+
notCollected: string;
|
|
342
660
|
}
|
|
343
661
|
declare const DEFAULT_CONVERSATION_CONTEXT_LABELS: ConversationContextPanelLabels;
|
|
344
662
|
interface ConversationContextPanelClassNames {
|
|
@@ -349,11 +667,23 @@ interface ConversationContextPanelClassNames {
|
|
|
349
667
|
}
|
|
350
668
|
interface ConversationContextPanelProps {
|
|
351
669
|
entries: readonly ConversationContextEntry[];
|
|
670
|
+
/**
|
|
671
|
+
* Estado inicial. Ausente, abre sozinho no desktop quando há algum dado preenchido.
|
|
672
|
+
*
|
|
673
|
+
* Existe porque "abre sozinho" nem sempre é o que o produto quer: com 1 de 6 campos preenchidos o
|
|
674
|
+
* painel ocupa altura mostrando quase só pendências, e empurra a conversa — que é o que se veio ver.
|
|
675
|
+
*/
|
|
676
|
+
defaultOpen?: boolean;
|
|
677
|
+
/**
|
|
678
|
+
* Rótulo do fluxo em curso, exibido em azul ao lado do título — no financiamento é o produto
|
|
679
|
+
* escolhido ("Consórcio", "MCMV"). Ausente, o cabeçalho fica só com título e contagens.
|
|
680
|
+
*/
|
|
681
|
+
flowLabel?: string;
|
|
352
682
|
labels?: Partial<ConversationContextPanelLabels>;
|
|
353
683
|
className?: string;
|
|
354
684
|
classNames?: Partial<ConversationContextPanelClassNames>;
|
|
355
685
|
}
|
|
356
|
-
declare function ConversationContextPanel({ entries, labels: labelsOverride, className, classNames, }: ConversationContextPanelProps): react.JSX.Element;
|
|
686
|
+
declare function ConversationContextPanel({ entries, defaultOpen, flowLabel, labels: labelsOverride, className, classNames, }: ConversationContextPanelProps): react.JSX.Element;
|
|
357
687
|
|
|
358
688
|
interface WindowExpiredNoticeLabels {
|
|
359
689
|
title: string;
|
|
@@ -373,6 +703,217 @@ declare function WindowExpiredNotice({ onSendTemplate, disabled, labels: labelsO
|
|
|
373
703
|
*/
|
|
374
704
|
declare function isWindowBlocking(window: ConversationWindow): boolean;
|
|
375
705
|
|
|
706
|
+
/**
|
|
707
|
+
* Biblioteca de arquivos de TODAS as conversas — a tela de Documentos do painel, fora do
|
|
708
|
+
* atendimento.
|
|
709
|
+
*
|
|
710
|
+
* Distinta do `ConversationDocumentsPanel`: aquele parte de uma conversa aberta e vive dentro dela;
|
|
711
|
+
* esta varre a empresa e por isso mostra de qual conversa cada arquivo veio, com o número
|
|
712
|
+
* clicável. Sem essa referência, uma lista global de anexos não responde nenhuma pergunta.
|
|
713
|
+
*/
|
|
714
|
+
interface DocumentsLibraryLabels {
|
|
715
|
+
title: string;
|
|
716
|
+
searchPlaceholder: string;
|
|
717
|
+
empty: string;
|
|
718
|
+
noResults: string;
|
|
719
|
+
loading: string;
|
|
720
|
+
failure: string;
|
|
721
|
+
view: string;
|
|
722
|
+
download: string;
|
|
723
|
+
openConversation: string;
|
|
724
|
+
sourceFilterAll: string;
|
|
725
|
+
sourceFilterCustomer: string;
|
|
726
|
+
sourceFilterTeam: string;
|
|
727
|
+
sortMostRecent: string;
|
|
728
|
+
sortOldest: string;
|
|
729
|
+
clearFilters: string;
|
|
730
|
+
upload: string;
|
|
731
|
+
uploadError: string;
|
|
732
|
+
total: (count: number) => string;
|
|
733
|
+
page: (current: number, last: number) => string;
|
|
734
|
+
}
|
|
735
|
+
declare const DEFAULT_DOCUMENTS_LIBRARY_LABELS: DocumentsLibraryLabels;
|
|
736
|
+
interface DocumentsLibraryClassNames {
|
|
737
|
+
root: string;
|
|
738
|
+
title: string;
|
|
739
|
+
filters: string;
|
|
740
|
+
search: string;
|
|
741
|
+
sourceSelect: string;
|
|
742
|
+
sortButton: string;
|
|
743
|
+
clearButton: string;
|
|
744
|
+
status: string;
|
|
745
|
+
list: string;
|
|
746
|
+
item: string;
|
|
747
|
+
conversationLink: string;
|
|
748
|
+
filename: string;
|
|
749
|
+
meta: string;
|
|
750
|
+
pagination: string;
|
|
751
|
+
}
|
|
752
|
+
interface DocumentsLibraryProps {
|
|
753
|
+
/** Itens por página. O total vem do servidor. */
|
|
754
|
+
perPage?: number;
|
|
755
|
+
/** Abrir a conversa de origem. Ausente, o número aparece como texto e não como link. */
|
|
756
|
+
onOpenConversation?: (conversationId: string) => void;
|
|
757
|
+
labels?: Partial<DocumentsLibraryLabels>;
|
|
758
|
+
className?: string;
|
|
759
|
+
classNames?: Partial<DocumentsLibraryClassNames>;
|
|
760
|
+
}
|
|
761
|
+
declare function DocumentsLibrary({ perPage, onOpenConversation, labels: labelsOverride, className, classNames, }: DocumentsLibraryProps): react.JSX.Element | null;
|
|
762
|
+
|
|
763
|
+
type SortDirection = 'asc' | 'desc';
|
|
764
|
+
interface SortableHeadProps {
|
|
765
|
+
readonly label: string;
|
|
766
|
+
readonly field: string;
|
|
767
|
+
readonly activeField: string;
|
|
768
|
+
readonly direction: SortDirection;
|
|
769
|
+
readonly onSort: (field: string) => void;
|
|
770
|
+
readonly className?: string;
|
|
771
|
+
}
|
|
772
|
+
declare function SortableHead({ label, field, activeField, direction, onSort, className }: SortableHeadProps): react.JSX.Element;
|
|
773
|
+
interface FilterOption {
|
|
774
|
+
readonly value: string;
|
|
775
|
+
readonly label: string;
|
|
776
|
+
}
|
|
777
|
+
interface MultiSelectFilterProps {
|
|
778
|
+
readonly label: string;
|
|
779
|
+
readonly options: readonly FilterOption[];
|
|
780
|
+
readonly selected: readonly string[];
|
|
781
|
+
readonly onChange: (selected: readonly string[]) => void;
|
|
782
|
+
readonly className?: string;
|
|
783
|
+
}
|
|
784
|
+
declare function MultiSelectFilter({ label, options, selected, onChange, className }: MultiSelectFilterProps): react.JSX.Element;
|
|
785
|
+
interface BulkActionBarProps {
|
|
786
|
+
readonly selectedCount: number;
|
|
787
|
+
readonly selectedLabel: (count: number) => string;
|
|
788
|
+
readonly clearLabel: string;
|
|
789
|
+
readonly onClear: () => void;
|
|
790
|
+
readonly children?: ReactNode;
|
|
791
|
+
}
|
|
792
|
+
declare function BulkActionBar({ selectedCount, selectedLabel, clearLabel, onClear, children }: BulkActionBarProps): react.JSX.Element | null;
|
|
793
|
+
interface ListingPaginationProps {
|
|
794
|
+
readonly page: number;
|
|
795
|
+
readonly total: number;
|
|
796
|
+
readonly perPage: number;
|
|
797
|
+
readonly perPageOptions?: readonly number[];
|
|
798
|
+
readonly onPageChange: (page: number) => void;
|
|
799
|
+
readonly onPerPageChange?: (perPage: number) => void;
|
|
800
|
+
readonly labels: {
|
|
801
|
+
readonly show: string;
|
|
802
|
+
readonly perPage: string;
|
|
803
|
+
readonly total: (count: number) => string;
|
|
804
|
+
readonly page: (current: number, last: number) => string;
|
|
805
|
+
readonly previous: string;
|
|
806
|
+
readonly next: string;
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
declare function ListingPagination({ page, total, perPage, perPageOptions, onPageChange, onPerPageChange, labels, }: ListingPaginationProps): react.JSX.Element;
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Vocabulário da biblioteca de arquivos. Sobrescrevível campo a campo: o produto troca "cliente"
|
|
813
|
+
* por "lead" ou o idioma inteiro sem manter uma cópia da tela para isso.
|
|
814
|
+
*/
|
|
815
|
+
interface DocumentsWorkspaceLabels {
|
|
816
|
+
readonly title: string;
|
|
817
|
+
readonly subtitle: (total: number) => string;
|
|
818
|
+
readonly searchPlaceholder: string;
|
|
819
|
+
readonly empty: string;
|
|
820
|
+
readonly noResults: string;
|
|
821
|
+
readonly loading: string;
|
|
822
|
+
readonly failure: string;
|
|
823
|
+
readonly view: string;
|
|
824
|
+
readonly download: string;
|
|
825
|
+
readonly remove: string;
|
|
826
|
+
readonly removeConfirm: (filename: string) => string;
|
|
827
|
+
readonly openConversation: string;
|
|
828
|
+
readonly sourceFilter: string;
|
|
829
|
+
readonly categoryFilter: string;
|
|
830
|
+
readonly startDate: string;
|
|
831
|
+
readonly endDate: string;
|
|
832
|
+
readonly clearFilters: string;
|
|
833
|
+
readonly selectAllPage: string;
|
|
834
|
+
readonly selectRow: string;
|
|
835
|
+
readonly bulkSelected: (count: number) => string;
|
|
836
|
+
readonly bulkClear: string;
|
|
837
|
+
readonly bulkDownloadZip: string;
|
|
838
|
+
readonly bulkRemove: (count: number) => string;
|
|
839
|
+
readonly bulkRemoveConfirm: (count: number) => string;
|
|
840
|
+
readonly upload: string;
|
|
841
|
+
readonly uploadError: string;
|
|
842
|
+
readonly columnFilename: string;
|
|
843
|
+
readonly columnContact: string;
|
|
844
|
+
readonly columnType: string;
|
|
845
|
+
readonly columnSize: string;
|
|
846
|
+
readonly columnSource: string;
|
|
847
|
+
readonly columnDate: string;
|
|
848
|
+
readonly columnActions: string;
|
|
849
|
+
readonly sourceLabels: Readonly<Record<string, string>>;
|
|
850
|
+
readonly categoryLabels: Readonly<Record<string, string>>;
|
|
851
|
+
readonly show: string;
|
|
852
|
+
readonly perPage: string;
|
|
853
|
+
readonly total: (count: number) => string;
|
|
854
|
+
readonly page: (current: number, last: number) => string;
|
|
855
|
+
readonly previousPage: string;
|
|
856
|
+
readonly nextPage: string;
|
|
857
|
+
}
|
|
858
|
+
declare const DEFAULT_DOCUMENTS_WORKSPACE_LABELS: DocumentsWorkspaceLabels;
|
|
859
|
+
|
|
860
|
+
interface DocumentsWorkspaceClassNames {
|
|
861
|
+
root: string;
|
|
862
|
+
header: string;
|
|
863
|
+
filters: string;
|
|
864
|
+
table: string;
|
|
865
|
+
row: string;
|
|
866
|
+
status: string;
|
|
867
|
+
}
|
|
868
|
+
interface DocumentsWorkspaceProps {
|
|
869
|
+
readonly perPage?: number;
|
|
870
|
+
readonly perPageOptions?: readonly number[];
|
|
871
|
+
/** Abrir a conversa de origem. Ausente, o número aparece como texto e não como link. */
|
|
872
|
+
readonly onOpenConversation?: (conversationId: string) => void;
|
|
873
|
+
/** Origens filtráveis. Ausente, usa o vocabulário padrão (`customer`, `agent`, `bot`). */
|
|
874
|
+
readonly sources?: readonly FilterOption[];
|
|
875
|
+
/** Categorias de arquivo. Lista vazia esconde o filtro — nem todo host classifica anexo. */
|
|
876
|
+
readonly categories?: readonly FilterOption[];
|
|
877
|
+
/** Recorte por data. Desligado quando o backend não sabe filtrar por período. */
|
|
878
|
+
readonly dateFilter?: boolean;
|
|
879
|
+
/**
|
|
880
|
+
* Filtros do produto (cliente, unidade, campanha). Recebe os parâmetros extras atuais e devolve
|
|
881
|
+
* os controles; o que o produto guardar aqui viaja em `extra` para o `getAllDocuments`.
|
|
882
|
+
*/
|
|
883
|
+
readonly renderFilters?: (context: DocumentsFiltersContext) => ReactNode;
|
|
884
|
+
readonly labels?: Partial<DocumentsWorkspaceLabels>;
|
|
885
|
+
readonly className?: string;
|
|
886
|
+
readonly classNames?: Partial<DocumentsWorkspaceClassNames>;
|
|
887
|
+
/** Espelhar filtros e paginação na URL. Desligado em preview, onde não há rota de verdade. */
|
|
888
|
+
readonly syncUrl?: boolean;
|
|
889
|
+
}
|
|
890
|
+
interface DocumentsFiltersContext {
|
|
891
|
+
readonly extra: Readonly<Record<string, string | number>>;
|
|
892
|
+
readonly setExtra: (next: Readonly<Record<string, string | number>>) => void;
|
|
893
|
+
}
|
|
894
|
+
declare function DocumentsWorkspace({ perPage: initialPerPage, perPageOptions, onOpenConversation, sources, categories, dateFilter, renderFilters, labels: labelsOverride, className, classNames, syncUrl, }: DocumentsWorkspaceProps): react.JSX.Element | null;
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Estado de listagem espelhado na URL — ordenação, filtros e paginação (regra `web.md` §7).
|
|
898
|
+
*
|
|
899
|
+
* Escrito sobre `history.replaceState` e não sobre um router: o pacote roda em três produtos com
|
|
900
|
+
* routers diferentes, e exigir um deles arrastaria dependência de framework para dentro do módulo.
|
|
901
|
+
* `replaceState` também é o comportamento certo aqui — filtrar não é navegar, e cada tecla digitada
|
|
902
|
+
* na busca não deve virar uma entrada no botão "voltar".
|
|
903
|
+
*/
|
|
904
|
+
/** `enabled: false` mantém o mesmo contrato de estado sem tocar na URL — para uso em preview e teste. */
|
|
905
|
+
interface UrlStateOptions {
|
|
906
|
+
readonly enabled?: boolean;
|
|
907
|
+
}
|
|
908
|
+
declare function useUrlStringState(key: string, initial: string, { enabled }?: UrlStateOptions): [string, (next: string) => void];
|
|
909
|
+
declare function useUrlNumberState(key: string, initial: number, { enabled }?: UrlStateOptions): [number, (next: number) => void];
|
|
910
|
+
declare function useUrlArrayState(key: string, { enabled }?: UrlStateOptions): [readonly string[], (next: readonly string[]) => void];
|
|
911
|
+
/**
|
|
912
|
+
* Espera o usuário parar de digitar antes de deixar o valor chegar à query. Sem isto, cada tecla
|
|
913
|
+
* na busca vira uma chamada de rede e uma reescrita de URL.
|
|
914
|
+
*/
|
|
915
|
+
declare function useDebouncedValue<TValue>(value: TValue, delayMs?: number): TValue;
|
|
916
|
+
|
|
376
917
|
/**
|
|
377
918
|
* Arquivos trocados na conversa. Sai do transcript e vira lista própria porque anexo é o que o
|
|
378
919
|
* atendente mais precisa reencontrar depois — rolar meses de mensagens para achar um comprovante é
|
|
@@ -381,29 +922,76 @@ declare function isWindowBlocking(window: ConversationWindow): boolean;
|
|
|
381
922
|
* Usa `useConversationDocuments`, então funciona com qualquer `ConversationsApi`. Host sem
|
|
382
923
|
* biblioteca de documentos cai no estado vazio, sem quebrar.
|
|
383
924
|
*/
|
|
925
|
+
/** Origens que o filtro oferece. `all` não é origem — é a ausência de filtro. */
|
|
926
|
+
declare const DOCUMENT_SOURCE_FILTER: {
|
|
927
|
+
readonly ALL: "all";
|
|
928
|
+
readonly CUSTOMER: "customer";
|
|
929
|
+
readonly TEAM: "team";
|
|
930
|
+
};
|
|
931
|
+
type DocumentSourceFilter = (typeof DOCUMENT_SOURCE_FILTER)[keyof typeof DOCUMENT_SOURCE_FILTER];
|
|
384
932
|
interface ConversationDocumentsPanelLabels {
|
|
385
933
|
toggle: string;
|
|
386
934
|
title: string;
|
|
387
935
|
searchPlaceholder: string;
|
|
388
936
|
empty: string;
|
|
937
|
+
/** Distinto de `empty`: sem resultado POR CAUSA do filtro, e não conversa sem anexo nenhum. */
|
|
938
|
+
noResults: string;
|
|
389
939
|
loading: string;
|
|
390
940
|
failure: string;
|
|
391
941
|
download: string;
|
|
942
|
+
view: string;
|
|
943
|
+
sourceFilterAll: string;
|
|
944
|
+
sourceFilterCustomer: string;
|
|
945
|
+
sourceFilterTeam: string;
|
|
946
|
+
sortMostRecent: string;
|
|
947
|
+
sortOldest: string;
|
|
948
|
+
clearFilters: string;
|
|
949
|
+
selectAll: string;
|
|
950
|
+
downloadSelected: (count: number) => string;
|
|
951
|
+
archiveFailed: string;
|
|
952
|
+
total: (count: number) => string;
|
|
953
|
+
page: (current: number, last: number) => string;
|
|
392
954
|
}
|
|
393
955
|
declare const DEFAULT_CONVERSATION_DOCUMENTS_LABELS: ConversationDocumentsPanelLabels;
|
|
956
|
+
/**
|
|
957
|
+
* Partes estilizáveis do painel, no mesmo contrato do `ConversationHeader`: `cn` funde por cima da
|
|
958
|
+
* base e conflito de utilitário (padding, tamanho de fonte, borda) fica com o valor do produto.
|
|
959
|
+
*
|
|
960
|
+
* `status` cobre carregando/erro/vazio de uma vez — são a mesma linha de texto auxiliar, e slots
|
|
961
|
+
* separados só multiplicariam chave para quem quer mudar a cor de aviso.
|
|
962
|
+
*/
|
|
394
963
|
interface ConversationDocumentsPanelClassNames {
|
|
395
964
|
root: string;
|
|
396
965
|
body: string;
|
|
966
|
+
title: string;
|
|
967
|
+
filters: string;
|
|
968
|
+
search: string;
|
|
969
|
+
sourceSelect: string;
|
|
970
|
+
sortButton: string;
|
|
971
|
+
clearButton: string;
|
|
972
|
+
status: string;
|
|
973
|
+
list: string;
|
|
974
|
+
item: string;
|
|
975
|
+
sourceBadge: string;
|
|
976
|
+
filename: string;
|
|
977
|
+
meta: string;
|
|
978
|
+
viewButton: string;
|
|
979
|
+
downloadButton: string;
|
|
980
|
+
pagination: string;
|
|
981
|
+
selectionBar: string;
|
|
982
|
+
checkbox: string;
|
|
397
983
|
}
|
|
398
984
|
interface ConversationDocumentsPanelProps {
|
|
399
985
|
conversationId: string;
|
|
400
986
|
/** Controlado de fora porque o gatilho vive no cabeçalho, junto das outras ações da conversa. */
|
|
401
987
|
open: boolean;
|
|
988
|
+
/** Itens por página. O total vem do servidor; sem paginação no host, a barra não aparece. */
|
|
989
|
+
perPage?: number;
|
|
402
990
|
labels?: Partial<ConversationDocumentsPanelLabels>;
|
|
403
991
|
className?: string;
|
|
404
992
|
classNames?: Partial<ConversationDocumentsPanelClassNames>;
|
|
405
993
|
}
|
|
406
|
-
declare function ConversationDocumentsPanel({ conversationId, open, labels: labelsOverride, className, classNames, }: ConversationDocumentsPanelProps): react.JSX.Element | null;
|
|
994
|
+
declare function ConversationDocumentsPanel({ conversationId, open, perPage, labels: labelsOverride, className, classNames, }: ConversationDocumentsPanelProps): react.JSX.Element | null;
|
|
407
995
|
|
|
408
996
|
/**
|
|
409
997
|
* Serialização do transcript para download. Fica no pacote porque o formato de um histórico de
|
|
@@ -454,11 +1042,35 @@ declare function useDarkMode(): {
|
|
|
454
1042
|
declare const NARROW_MAX_WIDTH_PX = 1023;
|
|
455
1043
|
declare function useIsNarrow(): boolean;
|
|
456
1044
|
|
|
1045
|
+
interface UseWaitingNotificationsLabels {
|
|
1046
|
+
/** Título da notificação do sistema. Recebe a conversa para o host escolher nome × número. */
|
|
1047
|
+
title: (conversation: ConversationSummary) => string;
|
|
1048
|
+
body: (conversation: ConversationSummary) => string;
|
|
1049
|
+
}
|
|
1050
|
+
interface UseWaitingNotificationsParams {
|
|
1051
|
+
/**
|
|
1052
|
+
* Repassado cru ao `fetchConversations`. É o que permite filtrar não lidas **no servidor** em
|
|
1053
|
+
* vez de baixar a lista inteira e contar no cliente: um painel com milhares de conversas não
|
|
1054
|
+
* pode paginar 50 por vez atrás de quem tem `unread > 0`.
|
|
1055
|
+
*/
|
|
1056
|
+
readonly params?: ListConversationsParams;
|
|
1057
|
+
readonly intervalMs?: number;
|
|
1058
|
+
/** Desliga o polling sem desmontar quem chama — útil com a aba em segundo plano. */
|
|
1059
|
+
readonly enabled?: boolean;
|
|
1060
|
+
readonly icon?: string;
|
|
1061
|
+
readonly labels?: Partial<UseWaitingNotificationsLabels>;
|
|
1062
|
+
}
|
|
457
1063
|
interface UseWaitingNotificationsResult {
|
|
458
1064
|
unreadCount: number;
|
|
459
1065
|
conversations: ConversationSummary[];
|
|
1066
|
+
/**
|
|
1067
|
+
* Releitura sob demanda. Existe porque o polling é o piso, não o mecanismo: quem já recebe SSE
|
|
1068
|
+
* ou acabou de marcar tudo como lido sabe da mudança antes do próximo tick, e esperar 10s para
|
|
1069
|
+
* o contador acompanhar faz a interface parecer travada.
|
|
1070
|
+
*/
|
|
1071
|
+
refresh: () => Promise<void>;
|
|
460
1072
|
}
|
|
461
|
-
declare function useWaitingNotifications(): UseWaitingNotificationsResult;
|
|
1073
|
+
declare function useWaitingNotifications(params?: UseWaitingNotificationsParams): UseWaitingNotificationsResult;
|
|
462
1074
|
|
|
463
1075
|
interface ConversationsContextValue {
|
|
464
1076
|
api: ConversationsApi;
|
|
@@ -540,6 +1152,7 @@ interface WhatsAppCreateTemplateFormLabels {
|
|
|
540
1152
|
sectionDescription: string;
|
|
541
1153
|
nameLabel: string;
|
|
542
1154
|
nameHint: string;
|
|
1155
|
+
namePlaceholder: string;
|
|
543
1156
|
categoryLabel: string;
|
|
544
1157
|
languageLabel: string;
|
|
545
1158
|
headerLabel: string;
|
|
@@ -605,6 +1218,47 @@ interface WelcomeFarewellFormProps {
|
|
|
605
1218
|
}
|
|
606
1219
|
declare function WelcomeFarewellForm({ welcomeMessage, onWelcomeMessageChange, farewellMessage, onFarewellMessageChange, onSave, saving, saveSuccess, welcomePlaceholders, farewellPlaceholders, labels: labelsOverride, }: WelcomeFarewellFormProps): react.JSX.Element;
|
|
607
1220
|
|
|
1221
|
+
interface TranscriptionSettingsFormLabels {
|
|
1222
|
+
sectionTitle: string;
|
|
1223
|
+
enabled: string;
|
|
1224
|
+
enabledHint: string;
|
|
1225
|
+
modeTitle: string;
|
|
1226
|
+
modeAuto: string;
|
|
1227
|
+
modeAutoHint: string;
|
|
1228
|
+
modeOnDemand: string;
|
|
1229
|
+
modeOnDemandHint: string;
|
|
1230
|
+
unavailable: string;
|
|
1231
|
+
save: string;
|
|
1232
|
+
saving: string;
|
|
1233
|
+
saveSuccess: string;
|
|
1234
|
+
}
|
|
1235
|
+
interface TranscriptionSettingsFormProps {
|
|
1236
|
+
enabled: boolean;
|
|
1237
|
+
onEnabledChange: (value: boolean) => void;
|
|
1238
|
+
mode: TranscriptionMode;
|
|
1239
|
+
onModeChange: (value: TranscriptionMode) => void;
|
|
1240
|
+
onSave: (event: FormEvent) => void;
|
|
1241
|
+
/**
|
|
1242
|
+
* A capacidade existe no servidor (engine e credencial configurados)?
|
|
1243
|
+
*
|
|
1244
|
+
* Distinto de `enabled`: isto é "o ambiente consegue", aquilo é "esta empresa quer". Sem a
|
|
1245
|
+
* distinção, um lojista ligaria o interruptor num ambiente sem engine e concluiria que o produto
|
|
1246
|
+
* está quebrado — o texto nunca apareceria e nada explicaria por quê.
|
|
1247
|
+
*/
|
|
1248
|
+
isAvailable?: boolean;
|
|
1249
|
+
saving?: boolean;
|
|
1250
|
+
saveSuccess?: boolean;
|
|
1251
|
+
labels?: Partial<TranscriptionSettingsFormLabels>;
|
|
1252
|
+
}
|
|
1253
|
+
/**
|
|
1254
|
+
* Interruptor de transcrição por empresa, para a tela de configurações.
|
|
1255
|
+
*
|
|
1256
|
+
* Puramente apresentacional, como os outros forms daqui: quem persiste é o host. O pacote não sabe a
|
|
1257
|
+
* rota, e a política vive nas configurações do módulo (`settings.transcriptionEnabled`), não em
|
|
1258
|
+
* variável de ambiente — mudar de ideia sobre transcrever não deveria exigir deploy.
|
|
1259
|
+
*/
|
|
1260
|
+
declare function TranscriptionSettingsForm({ enabled, onEnabledChange, mode, onModeChange, onSave, isAvailable, saving, saveSuccess, labels: labelsOverride, }: TranscriptionSettingsFormProps): react.JSX.Element;
|
|
1261
|
+
|
|
608
1262
|
declare const TEMPLATE_SETTINGS_TAB: {
|
|
609
1263
|
readonly SELECT: "select";
|
|
610
1264
|
readonly CREATE: "create";
|
|
@@ -635,10 +1289,13 @@ interface WhatsAppTemplatesSettingsProps {
|
|
|
635
1289
|
onSubmit: (event: FormEvent) => void;
|
|
636
1290
|
submitting?: boolean;
|
|
637
1291
|
result?: WhatsAppCreateTemplateResult | null;
|
|
1292
|
+
labels?: Partial<WhatsAppCreateTemplateFormLabels>;
|
|
638
1293
|
};
|
|
639
1294
|
labels?: Partial<WhatsAppTemplatesSettingsLabels>;
|
|
1295
|
+
/** Vocabulário do formulário de seleção — separado de `labels`, que nomeia só as abas daqui. */
|
|
1296
|
+
settingsLabels?: Partial<WhatsAppTemplateSettingsFormLabels>;
|
|
640
1297
|
}
|
|
641
|
-
declare function WhatsAppTemplatesSettings({ labels: labelsOverride, create, ...settingsProps }: WhatsAppTemplatesSettingsProps): react.JSX.Element;
|
|
1298
|
+
declare function WhatsAppTemplatesSettings({ labels: labelsOverride, settingsLabels, create, ...settingsProps }: WhatsAppTemplatesSettingsProps): react.JSX.Element;
|
|
642
1299
|
|
|
643
1300
|
interface TopicItem {
|
|
644
1301
|
key: string;
|
|
@@ -664,6 +1321,67 @@ interface TopicsFormProps {
|
|
|
664
1321
|
}
|
|
665
1322
|
declare function TopicsForm({ topics, onToggle, onMessageChange, onSave, saving, saveSuccess, labels: labelsOverride, }: TopicsFormProps): react.JSX.Element;
|
|
666
1323
|
|
|
1324
|
+
interface BotMessages {
|
|
1325
|
+
welcomeMessage: string;
|
|
1326
|
+
farewellMessage: string;
|
|
1327
|
+
}
|
|
1328
|
+
interface TemplateSettings {
|
|
1329
|
+
templateName: string;
|
|
1330
|
+
templateLanguage: string;
|
|
1331
|
+
variables: string[];
|
|
1332
|
+
}
|
|
1333
|
+
interface TranscriptionSettings {
|
|
1334
|
+
enabled: boolean;
|
|
1335
|
+
mode: TranscriptionMode;
|
|
1336
|
+
available: boolean;
|
|
1337
|
+
}
|
|
1338
|
+
interface MessagesWorkspaceApi {
|
|
1339
|
+
getMessages(): Promise<BotMessages>;
|
|
1340
|
+
saveMessages(messages: BotMessages): Promise<void>;
|
|
1341
|
+
/** Sem este par, a seção de tópicos não é desenhada. */
|
|
1342
|
+
getTopics?(): Promise<TopicItem[]>;
|
|
1343
|
+
saveTopics?(topics: TopicItem[]): Promise<void>;
|
|
1344
|
+
/** Sem este par, a aba de templates não é desenhada. */
|
|
1345
|
+
getTemplateSettings?(): Promise<TemplateSettings>;
|
|
1346
|
+
saveTemplateSettings?(settings: TemplateSettings): Promise<void>;
|
|
1347
|
+
/**
|
|
1348
|
+
* Lista de templates aprovados na Meta. Ausente, a aba ainda existe (dá para salvar o nome
|
|
1349
|
+
* escolhido), mas nasce sem opções e sem botão de recarregar.
|
|
1350
|
+
*/
|
|
1351
|
+
listTemplates?(): Promise<WhatsAppTemplateSummary[]>;
|
|
1352
|
+
/** Sem isto, o submenu "Criar template" some. */
|
|
1353
|
+
createTemplate?(input: WhatsAppCreateTemplateState): Promise<WhatsAppCreateTemplateResult>;
|
|
1354
|
+
/** Sem este par, a aba de transcrição não é desenhada. */
|
|
1355
|
+
getTranscription?(): Promise<TranscriptionSettings>;
|
|
1356
|
+
saveTranscription?(settings: Omit<TranscriptionSettings, 'available'>): Promise<void>;
|
|
1357
|
+
}
|
|
1358
|
+
interface MessagesWorkspaceLabels {
|
|
1359
|
+
title: string;
|
|
1360
|
+
subtitle: string;
|
|
1361
|
+
loading: string;
|
|
1362
|
+
loadError: string;
|
|
1363
|
+
tabBot: string;
|
|
1364
|
+
tabTemplates: string;
|
|
1365
|
+
tabTranscription: string;
|
|
1366
|
+
welcomeFarewell: Partial<WelcomeFarewellFormLabels>;
|
|
1367
|
+
topics: Partial<TopicsFormLabels>;
|
|
1368
|
+
templates: Partial<WhatsAppTemplatesSettingsLabels>;
|
|
1369
|
+
templateSettings: Partial<WhatsAppTemplateSettingsFormLabels>;
|
|
1370
|
+
createTemplate: Partial<WhatsAppCreateTemplateFormLabels>;
|
|
1371
|
+
transcription: Partial<TranscriptionSettingsFormLabels>;
|
|
1372
|
+
}
|
|
1373
|
+
interface MessagesWorkspaceProps {
|
|
1374
|
+
readonly api: MessagesWorkspaceApi;
|
|
1375
|
+
readonly labels?: Partial<MessagesWorkspaceLabels>;
|
|
1376
|
+
readonly welcomePlaceholders?: readonly string[];
|
|
1377
|
+
readonly farewellPlaceholders?: readonly string[];
|
|
1378
|
+
readonly availableVariables?: WhatsAppTemplateVariableSuggestion[];
|
|
1379
|
+
/** Aviso do produto acima da aba de templates (ex.: rota da Graph API ainda não implementada). */
|
|
1380
|
+
readonly renderTemplatesNotice?: () => ReactNode;
|
|
1381
|
+
readonly className?: string;
|
|
1382
|
+
}
|
|
1383
|
+
declare function MessagesWorkspace({ api, labels: labelsOverride, welcomePlaceholders, farewellPlaceholders, availableVariables, renderTemplatesNotice, className, }: MessagesWorkspaceProps): react.JSX.Element;
|
|
1384
|
+
|
|
667
1385
|
interface UseConversationMessagesResult {
|
|
668
1386
|
messages: MessagePayload[];
|
|
669
1387
|
loading: boolean;
|
|
@@ -677,7 +1395,7 @@ interface UseConversationMessagesResult {
|
|
|
677
1395
|
caption?: string;
|
|
678
1396
|
}) => Promise<MessagePayload>;
|
|
679
1397
|
sendTemplate: (data: {
|
|
680
|
-
templateName
|
|
1398
|
+
templateName?: string;
|
|
681
1399
|
languageCode?: string;
|
|
682
1400
|
bodyParams?: string[];
|
|
683
1401
|
}) => Promise<void>;
|
|
@@ -688,14 +1406,28 @@ declare function useConversationMessages(conversationId: string, params?: {
|
|
|
688
1406
|
before?: string;
|
|
689
1407
|
}): UseConversationMessagesResult;
|
|
690
1408
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
}
|
|
1409
|
+
type UseScrollToLatestMessageParams = {
|
|
1410
|
+
/** Troca de conversa. Muda ⇒ salto instantâneo para o fim. */
|
|
1411
|
+
readonly conversationId: string | undefined;
|
|
1412
|
+
/** Quantidade de mensagens carregadas. Cresce ⇒ acompanha o fim, se o operador estiver lá. */
|
|
1413
|
+
readonly messageCount: number;
|
|
1414
|
+
};
|
|
1415
|
+
type UseScrollToLatestMessageResult = {
|
|
1416
|
+
/** Vai no elemento que rola — tipicamente o `ConversationWallpaper`. */
|
|
1417
|
+
readonly containerRef: RefObject<HTMLDivElement | null>;
|
|
1418
|
+
/** Ligue no `onScroll` do mesmo elemento: é o que detecta o operador lendo o histórico. */
|
|
1419
|
+
readonly handleScroll: (event: UIEvent<HTMLDivElement>) => void;
|
|
1420
|
+
/** `true` quando o operador rolou para trás — serve a um botão "ir para a última". */
|
|
1421
|
+
readonly isAwayFromBottom: boolean;
|
|
1422
|
+
readonly scrollToBottom: (behavior?: ScrollBehavior) => void;
|
|
1423
|
+
};
|
|
1424
|
+
declare function useScrollToLatestMessage({ conversationId, messageCount, }: UseScrollToLatestMessageParams): UseScrollToLatestMessageResult;
|
|
1425
|
+
|
|
1426
|
+
type UseConversationListParams = ListConversationsParams;
|
|
697
1427
|
interface UseConversationListResult {
|
|
698
1428
|
conversations: ConversationSummary[];
|
|
1429
|
+
/** Total no servidor. Cai para o tamanho da página quando a API devolve só o array. */
|
|
1430
|
+
total: number;
|
|
699
1431
|
loading: boolean;
|
|
700
1432
|
error: Error | undefined;
|
|
701
1433
|
refetch: () => Promise<void>;
|
|
@@ -710,12 +1442,11 @@ interface UseConversationContextResult {
|
|
|
710
1442
|
}
|
|
711
1443
|
declare function useConversationContext(conversationId: string): UseConversationContextResult;
|
|
712
1444
|
|
|
713
|
-
|
|
714
|
-
search?: string;
|
|
715
|
-
page?: number;
|
|
716
|
-
}
|
|
1445
|
+
type UseConversationDocumentsParams = ListDocumentsParams;
|
|
717
1446
|
interface UseConversationDocumentsResult {
|
|
718
1447
|
documents: ConversationDocument[];
|
|
1448
|
+
/** Total no servidor. Cai para o tamanho da página quando a API devolve só o array. */
|
|
1449
|
+
total: number;
|
|
719
1450
|
loading: boolean;
|
|
720
1451
|
error: Error | undefined;
|
|
721
1452
|
refetch: () => Promise<void>;
|
|
@@ -726,6 +1457,27 @@ type ConversationRealtimeHandler = (event: MessageEvent) => void;
|
|
|
726
1457
|
declare function useConversationRealtime(conversationId: string | undefined, onEvent: ConversationRealtimeHandler): void;
|
|
727
1458
|
declare function useGlobalRealtime(onEvent: ConversationRealtimeHandler): void;
|
|
728
1459
|
|
|
1460
|
+
interface UseConversationActionsResult {
|
|
1461
|
+
/** `undefined` quando a API do host não implementa a operação — a UI esconde a afordância. */
|
|
1462
|
+
takeover: (() => Promise<void>) | undefined;
|
|
1463
|
+
release: (() => Promise<void>) | undefined;
|
|
1464
|
+
finalize: (() => Promise<void>) | undefined;
|
|
1465
|
+
}
|
|
1466
|
+
/**
|
|
1467
|
+
* Ações de atendimento de UMA conversa, já ligadas ao id.
|
|
1468
|
+
*
|
|
1469
|
+
* Separado de `useConversationMessages` porque assumir e devolver conversa também acontece a
|
|
1470
|
+
* partir da lista, onde nenhuma thread está aberta — embutir nas mensagens obrigaria a carregar
|
|
1471
|
+
* a thread inteira só para desenhar um botão na linha.
|
|
1472
|
+
*/
|
|
1473
|
+
declare function useConversationActions(conversationId: string): UseConversationActionsResult;
|
|
1474
|
+
interface UseInboxActionsResult {
|
|
1475
|
+
markAllRead: (() => Promise<void>) | undefined;
|
|
1476
|
+
listTemplates: (() => Promise<ConversationTemplate[]>) | undefined;
|
|
1477
|
+
}
|
|
1478
|
+
/** Ações que valem para a caixa inteira, sem conversa selecionada. */
|
|
1479
|
+
declare function useInboxActions(): UseInboxActionsResult;
|
|
1480
|
+
|
|
729
1481
|
declare function parseWhatsAppFormatting(text: string): ReactNode[];
|
|
730
1482
|
declare function waToHTML(text: string): string;
|
|
731
1483
|
declare function htmlToWA(html: string): string;
|
|
@@ -735,6 +1487,8 @@ declare function formatPhone(number: string): string;
|
|
|
735
1487
|
declare function phoneInitials(number: string): string;
|
|
736
1488
|
|
|
737
1489
|
declare function formatTimestamp(timestamp: string): string;
|
|
1490
|
+
declare function formatDateTime(iso: string): string;
|
|
1491
|
+
declare function isSameDay(a: Date, b: Date): boolean;
|
|
738
1492
|
declare function formatFileSize(bytes: number): string;
|
|
739
1493
|
|
|
740
1494
|
interface AsyncResourceState<T> {
|
|
@@ -744,4 +1498,282 @@ interface AsyncResourceState<T> {
|
|
|
744
1498
|
refetch: () => Promise<void>;
|
|
745
1499
|
}
|
|
746
1500
|
|
|
747
|
-
|
|
1501
|
+
/**
|
|
1502
|
+
* Resolve a mídia de uma mensagem numa URL exibível, usando só o `ConversationsApi`.
|
|
1503
|
+
*
|
|
1504
|
+
* Mora no pacote, e não em cada host, porque a regra não tem nada de específico de produto: é a
|
|
1505
|
+
* tradução de `uploadId`/`mediaId` pelos dois métodos que o próprio contrato já declara. Deixá-la no
|
|
1506
|
+
* host significava que todo projeto que adotasse o SDK reescreveria as mesmas oito linhas — e, na
|
|
1507
|
+
* prática, ninguém escrevia: o `MediaRenderer` só busca mídia pela porta `onResolveMediaUrl`, então
|
|
1508
|
+
* onde nada era injetado foto, vídeo e áudio ficavam no placeholder para sempre.
|
|
1509
|
+
*/
|
|
1510
|
+
|
|
1511
|
+
declare function createMediaUrlResolver(api: Pick<ConversationsApi, 'getDocumentUrl' | 'getMediaProxyUrl'>): ResolveMediaUrl;
|
|
1512
|
+
|
|
1513
|
+
/**
|
|
1514
|
+
* Textos da inbox. Todos sobrescrevíveis: o produto troca vocabulário ("cliente" vira "lead") ou
|
|
1515
|
+
* o idioma inteiro sem tocar no layout — e sem manter uma cópia da tela para isso.
|
|
1516
|
+
*/
|
|
1517
|
+
interface ConversationsWorkspaceLabels {
|
|
1518
|
+
readonly title: string;
|
|
1519
|
+
readonly conversations: string;
|
|
1520
|
+
readonly waiting: string;
|
|
1521
|
+
readonly unread: string;
|
|
1522
|
+
readonly waitingOnly: string;
|
|
1523
|
+
readonly markSelectedAsRead: string;
|
|
1524
|
+
readonly search: string;
|
|
1525
|
+
readonly windowLegend: string;
|
|
1526
|
+
readonly channelLegend: string;
|
|
1527
|
+
readonly selectAll: string;
|
|
1528
|
+
readonly emptyList: string;
|
|
1529
|
+
readonly emptyDetail: string;
|
|
1530
|
+
readonly bulkSelected: (count: number) => string;
|
|
1531
|
+
readonly bulkClear: string;
|
|
1532
|
+
readonly bulkFinalize: string;
|
|
1533
|
+
readonly bulkFinalizeConfirm: (count: number) => string;
|
|
1534
|
+
readonly bulkTemplate: string;
|
|
1535
|
+
readonly markAllAsRead: string;
|
|
1536
|
+
readonly templateModalTitle: string;
|
|
1537
|
+
readonly templateModalSearch: string;
|
|
1538
|
+
readonly templateModalEmpty: string;
|
|
1539
|
+
readonly templateModalCancel: string;
|
|
1540
|
+
readonly templateModalSending: string;
|
|
1541
|
+
readonly templateModalNoneExpired: string;
|
|
1542
|
+
readonly templateModalAvailable: (count: number) => string;
|
|
1543
|
+
readonly templateModalSend: (count: number) => string;
|
|
1544
|
+
readonly messagesSelected: (count: number) => string;
|
|
1545
|
+
readonly copySelected: string;
|
|
1546
|
+
readonly composerPlaceholder: string;
|
|
1547
|
+
readonly attachFailure: string;
|
|
1548
|
+
/** Tira um arquivo da fila antes de enviar. */
|
|
1549
|
+
readonly attachmentRemove: string;
|
|
1550
|
+
readonly recordFailure: string;
|
|
1551
|
+
readonly sendFailure: string;
|
|
1552
|
+
readonly takeoverToReply: string;
|
|
1553
|
+
readonly signIn: string;
|
|
1554
|
+
readonly pageOf: (page: number, pageCount: number) => string;
|
|
1555
|
+
readonly rangeOf: (first: number, last: number, total: number) => string;
|
|
1556
|
+
}
|
|
1557
|
+
declare const DEFAULT_CONVERSATIONS_WORKSPACE_LABELS: ConversationsWorkspaceLabels;
|
|
1558
|
+
|
|
1559
|
+
/**
|
|
1560
|
+
* Estado da inbox: filtros, paginação, seleção em massa e ações de atendimento.
|
|
1561
|
+
*
|
|
1562
|
+
* Nasceu igual em três produtos e divergiu em três — contadores diferentes, um marcava lida ao
|
|
1563
|
+
* abrir e outro não, um perdia a página ao trocar de filtro. É a lógica de operar uma inbox, não
|
|
1564
|
+
* regra de nenhum negócio, e por isso mora aqui.
|
|
1565
|
+
*
|
|
1566
|
+
* A paginação é do cliente porque nem todo backend pagina a listagem. Fatiar aqui mantém a tela
|
|
1567
|
+
* utilizável com centenas de conversas; com dezenas de milhares o gargalo volta e a resposta é
|
|
1568
|
+
* paginar no servidor.
|
|
1569
|
+
*/
|
|
1570
|
+
|
|
1571
|
+
declare const CONVERSATIONS_PER_PAGE = 50;
|
|
1572
|
+
interface UseConversationsInboxParams {
|
|
1573
|
+
/** Recortes do produto repassados crus ao backend dele (carteira, campanha, tipo). */
|
|
1574
|
+
readonly filters?: Record<string, string | undefined>;
|
|
1575
|
+
readonly perPage?: number;
|
|
1576
|
+
/**
|
|
1577
|
+
* Marca como lida a conversa aberta. Ligado por padrão: badge subindo na conversa que o atendente
|
|
1578
|
+
* tem na frente é ruído que ele não tem como resolver.
|
|
1579
|
+
*/
|
|
1580
|
+
readonly markReadOnOpen?: boolean;
|
|
1581
|
+
/**
|
|
1582
|
+
* Pede a página ao servidor em vez de fatiar o que veio. Necessário quando a base é grande — o
|
|
1583
|
+
* padrão traz tudo e pagina no cliente porque nem todo backend pagina a listagem.
|
|
1584
|
+
*/
|
|
1585
|
+
readonly serverPaginated?: boolean;
|
|
1586
|
+
readonly describeFailure?: (error: unknown) => string;
|
|
1587
|
+
}
|
|
1588
|
+
interface UseConversationsInboxResult {
|
|
1589
|
+
readonly conversations: readonly ConversationSummary[];
|
|
1590
|
+
readonly pageConversations: readonly ConversationSummary[];
|
|
1591
|
+
readonly selectedConversation: ConversationSummary | undefined;
|
|
1592
|
+
readonly loading: boolean;
|
|
1593
|
+
readonly now: number;
|
|
1594
|
+
readonly totalCount: number;
|
|
1595
|
+
readonly unreadCount: number;
|
|
1596
|
+
readonly waitingCount: number;
|
|
1597
|
+
readonly filteredCount: number;
|
|
1598
|
+
/** Selecionadas fora da janela de 24h — as únicas que um template alcança. */
|
|
1599
|
+
readonly expiredSelectedCount: number;
|
|
1600
|
+
readonly page: number;
|
|
1601
|
+
readonly pageCount: number;
|
|
1602
|
+
readonly firstOnPage: number;
|
|
1603
|
+
readonly lastOnPage: number;
|
|
1604
|
+
readonly selectedId: string | undefined;
|
|
1605
|
+
readonly selectedIds: ReadonlySet<string>;
|
|
1606
|
+
readonly allOnPageSelected: boolean;
|
|
1607
|
+
readonly waitingOnly: boolean;
|
|
1608
|
+
readonly windowFilter: ConversationWindow;
|
|
1609
|
+
readonly channelFilter: ChannelFilter;
|
|
1610
|
+
readonly channelFilters: readonly ChannelFilterOption[];
|
|
1611
|
+
readonly search: string;
|
|
1612
|
+
readonly busy: boolean;
|
|
1613
|
+
/** Por que a lista está vazia, quando não é por não haver conversa. */
|
|
1614
|
+
readonly loadFailure: string | undefined;
|
|
1615
|
+
/** Ausentes quando o host não implementa a capacidade — a UI não desenha a afordância. */
|
|
1616
|
+
readonly canTakeover: boolean;
|
|
1617
|
+
readonly canFinalize: boolean;
|
|
1618
|
+
readonly canListTemplates: boolean;
|
|
1619
|
+
readonly canMarkAllRead: boolean;
|
|
1620
|
+
refetch(): Promise<void> | void;
|
|
1621
|
+
selectConversation(conversationId: string): void;
|
|
1622
|
+
clearSelection(): void;
|
|
1623
|
+
toggleSelected(conversationId: string): void;
|
|
1624
|
+
toggleSelectAllOnPage(): void;
|
|
1625
|
+
clearBulkSelection(): void;
|
|
1626
|
+
setWaitingOnly(value: boolean): void;
|
|
1627
|
+
setWindowFilter(value: ConversationWindow): void;
|
|
1628
|
+
setChannelFilter(value: ChannelFilter): void;
|
|
1629
|
+
setSearch(value: string): void;
|
|
1630
|
+
goToPage(value: number): void;
|
|
1631
|
+
markSelectedAsRead(): Promise<void>;
|
|
1632
|
+
markAllAsRead(): Promise<void>;
|
|
1633
|
+
takeover(conversationId: string): Promise<void>;
|
|
1634
|
+
releaseToBot(conversationId: string): Promise<void>;
|
|
1635
|
+
finalize(conversationId: string): Promise<void>;
|
|
1636
|
+
finalizeSelected(): Promise<void>;
|
|
1637
|
+
sendTemplateToSelected(templateName?: string): Promise<void>;
|
|
1638
|
+
}
|
|
1639
|
+
declare function useConversationsInbox(params?: UseConversationsInboxParams): UseConversationsInboxResult;
|
|
1640
|
+
|
|
1641
|
+
interface ConversationsWorkspaceSimulator {
|
|
1642
|
+
/**
|
|
1643
|
+
* Desenha o painel do cliente. Fica com o host porque o simulador precisa da rota assinada no
|
|
1644
|
+
* servidor DELE — e é o que mantém o `preview/` fora do bundle de quem não usa.
|
|
1645
|
+
*/
|
|
1646
|
+
render(params: {
|
|
1647
|
+
conversationId: string;
|
|
1648
|
+
close: () => void;
|
|
1649
|
+
}): ReactNode;
|
|
1650
|
+
/** Ausente = ligado. Serve para esconder fora de desenvolvimento sem condicionar o JSX. */
|
|
1651
|
+
readonly enabled?: boolean;
|
|
1652
|
+
/** Ícone da biblioteca (lucide) no utilitário do cabeçalho. Ausente, entra o frasco de teste. */
|
|
1653
|
+
readonly icon?: ReactNode;
|
|
1654
|
+
readonly label?: string;
|
|
1655
|
+
}
|
|
1656
|
+
interface ConversationsWorkspaceProps {
|
|
1657
|
+
readonly labels?: Partial<ConversationsWorkspaceLabels>;
|
|
1658
|
+
readonly filters?: Record<string, string | undefined>;
|
|
1659
|
+
readonly perPage?: number;
|
|
1660
|
+
readonly markReadOnOpen?: boolean;
|
|
1661
|
+
/** Pede a página ao servidor em vez de fatiar no cliente. */
|
|
1662
|
+
readonly serverPaginated?: boolean;
|
|
1663
|
+
/** Conversa a abrir na montagem (deep link `?id=`). */
|
|
1664
|
+
readonly initialConversationId?: string | undefined;
|
|
1665
|
+
/** Idem, pelo telefone — é o que costuma vir no link de um alerta ou de um pedido. */
|
|
1666
|
+
readonly initialWhatsappNumber?: string | undefined;
|
|
1667
|
+
readonly simulator?: ConversationsWorkspaceSimulator;
|
|
1668
|
+
readonly quickReplies?: readonly QuickReply[];
|
|
1669
|
+
readonly quickReplyVariablesFor?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => Record<string, string>;
|
|
1670
|
+
/** Etapa do fluxo mostrada no painel de contexto. */
|
|
1671
|
+
readonly flowLabelOf?: (conversation: ConversationSummary) => string | undefined;
|
|
1672
|
+
/** Substitui o download local do transcript (ex.: exportação completa pela rota do servidor). */
|
|
1673
|
+
readonly onDownload?: (conversation: ConversationSummary) => void;
|
|
1674
|
+
/** Bloqueia o composer enquanto a conversa estiver com o bot. */
|
|
1675
|
+
readonly requireTakeoverToReply?: boolean;
|
|
1676
|
+
/** Deixa marcar mensagens no transcript e copiar o trecho. */
|
|
1677
|
+
readonly messageSelection?: boolean;
|
|
1678
|
+
/** Texto já no campo ao abrir a conversa (deep link que sugere a resposta). */
|
|
1679
|
+
readonly initialComposerText?: string | undefined;
|
|
1680
|
+
/** `rich` troca o campo simples pelo texto com a formatação do WhatsApp desenhada ao escrever. */
|
|
1681
|
+
readonly composer?: 'simple' | 'rich';
|
|
1682
|
+
/** Valores que o operador insere sem digitar. Só o composer `rich` os oferece. */
|
|
1683
|
+
readonly composerVariablesFor?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => readonly RichComposerVariable[];
|
|
1684
|
+
/** Fila de anexos com legenda, como no WhatsApp. Ausente, o clipe manda cada arquivo na hora. */
|
|
1685
|
+
readonly onSendAttachments?: (conversation: ConversationSummary, files: readonly File[], caption: string) => Promise<void>;
|
|
1686
|
+
/** Nota de voz. Ausente, o microfone não aparece. */
|
|
1687
|
+
readonly onRecordAudio?: (conversation: ConversationSummary, file: File) => Promise<void>;
|
|
1688
|
+
readonly contextEntriesOf?: (context: Record<string, unknown> | undefined) => readonly ConversationContextEntry[];
|
|
1689
|
+
readonly onAttach?: (conversation: ConversationSummary, file: File) => Promise<void>;
|
|
1690
|
+
readonly extraUtilitiesFor?: (conversation: ConversationSummary) => readonly ConversationHeaderUtility[];
|
|
1691
|
+
readonly renderFilters?: (inbox: UseConversationsInboxResult) => ReactNode;
|
|
1692
|
+
readonly renderBulkActions?: (inbox: UseConversationsInboxResult) => ReactNode;
|
|
1693
|
+
readonly renderRow?: (conversation: ConversationSummary) => ReactNode;
|
|
1694
|
+
readonly renderAboveTranscript?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => ReactNode;
|
|
1695
|
+
readonly renderHeaderActions?: (inbox: UseConversationsInboxResult) => ReactNode;
|
|
1696
|
+
readonly onSendTemplateToSelected?: (inbox: UseConversationsInboxResult) => void;
|
|
1697
|
+
/** Destino do link de reentrar no painel, mostrado junto do aviso de sessão expirada. */
|
|
1698
|
+
readonly signInHref?: string;
|
|
1699
|
+
readonly className?: string;
|
|
1700
|
+
}
|
|
1701
|
+
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;
|
|
1702
|
+
|
|
1703
|
+
interface ConversationPaneProps {
|
|
1704
|
+
readonly conversation: ConversationSummary;
|
|
1705
|
+
readonly now: number;
|
|
1706
|
+
readonly busy: boolean;
|
|
1707
|
+
readonly labels: ConversationsWorkspaceLabels;
|
|
1708
|
+
/** Devolve a promessa quando o host a tem: o envio de template espera a tomada antes de sair. */
|
|
1709
|
+
readonly onTakeover?: (() => void | Promise<void>) | undefined;
|
|
1710
|
+
readonly onReturnToBot?: (() => void) | undefined;
|
|
1711
|
+
readonly onFinish?: (() => void) | undefined;
|
|
1712
|
+
readonly onBack: () => void;
|
|
1713
|
+
readonly extraUtilities?: readonly ConversationHeaderUtility[];
|
|
1714
|
+
/** Traduz o contexto cru do produto nas linhas do painel. Ausente, o painel não aparece. */
|
|
1715
|
+
readonly contextEntriesOf?: (context: Record<string, unknown> | undefined) => readonly ConversationContextEntry[];
|
|
1716
|
+
readonly quickReplies?: readonly QuickReply[];
|
|
1717
|
+
/**
|
|
1718
|
+
* Recebe o contexto junto porque o dado que interessa à variável (o nome que o bot perguntou,
|
|
1719
|
+
* por exemplo) vive no contexto do fluxo, não no resumo da listagem.
|
|
1720
|
+
*/
|
|
1721
|
+
readonly quickReplyVariablesFor?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => Record<string, string>;
|
|
1722
|
+
/** Etapa do fluxo ao lado do contexto (ex.: "Anotando o pedido"). */
|
|
1723
|
+
readonly flowLabel?: string | undefined;
|
|
1724
|
+
/**
|
|
1725
|
+
* Substitui o download local do transcript. Existe para o produto que exporta pela rota do
|
|
1726
|
+
* servidor, onde o arquivo sai completo em vez de só com o que a tela carregou.
|
|
1727
|
+
*/
|
|
1728
|
+
readonly onDownload?: (() => void) | undefined;
|
|
1729
|
+
/**
|
|
1730
|
+
* Bloqueia o composer enquanto a conversa estiver com o bot. Ligado, responder sem assumir
|
|
1731
|
+
* atropelaria o fluxo automático no meio de uma pergunta.
|
|
1732
|
+
*/
|
|
1733
|
+
readonly requireTakeoverToReply?: boolean;
|
|
1734
|
+
/**
|
|
1735
|
+
* Deixa marcar mensagens e copiar o trecho. É o que se faz para levar um pedaço da conversa a um
|
|
1736
|
+
* e-mail ou a um chamado, sem baixar o transcript inteiro.
|
|
1737
|
+
*/
|
|
1738
|
+
readonly messageSelection?: boolean;
|
|
1739
|
+
/** Texto já no campo ao abrir (deep link que sugere a resposta). */
|
|
1740
|
+
readonly initialComposerText?: string | undefined;
|
|
1741
|
+
/**
|
|
1742
|
+
* Peça do produto entre o contexto e o transcript (ex.: ficha do lead, resumo do pedido). Recebe o
|
|
1743
|
+
* contexto do fluxo junto: é dele que sai o resumo, e sem isso o host teria de buscá-lo de novo.
|
|
1744
|
+
*/
|
|
1745
|
+
readonly renderAboveTranscript?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => ReactNode;
|
|
1746
|
+
readonly onAttach?: ((file: File) => Promise<void>) | undefined;
|
|
1747
|
+
/**
|
|
1748
|
+
* `rich` troca o `textarea` pelo campo com a formatação do WhatsApp desenhada enquanto se escreve.
|
|
1749
|
+
* Vale onde o atendente manda texto longo e formatado; para responder "já vou ver" o campo simples
|
|
1750
|
+
* é menos coisa na tela.
|
|
1751
|
+
*/
|
|
1752
|
+
readonly composer?: 'simple' | 'rich';
|
|
1753
|
+
/** Valores que o operador insere sem digitar. Só o composer `rich` os oferece. */
|
|
1754
|
+
readonly composerVariablesFor?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => readonly RichComposerVariable[];
|
|
1755
|
+
/**
|
|
1756
|
+
* Fila de anexos com legenda, como no WhatsApp: os arquivos escolhidos ficam visíveis acima da
|
|
1757
|
+
* barra e saem junto com o texto escrito. Ausente, o clipe manda cada arquivo na hora — o que
|
|
1758
|
+
* perde a legenda, e era a diferença entre as telas.
|
|
1759
|
+
*/
|
|
1760
|
+
readonly onSendAttachments?: (files: readonly File[], caption: string) => Promise<void>;
|
|
1761
|
+
/** Grava e envia nota de voz. Ausente, o microfone não aparece. */
|
|
1762
|
+
readonly onRecordAudio?: (file: File) => Promise<void>;
|
|
1763
|
+
}
|
|
1764
|
+
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;
|
|
1765
|
+
|
|
1766
|
+
interface ConversationsInboxListProps {
|
|
1767
|
+
readonly inbox: UseConversationsInboxResult;
|
|
1768
|
+
readonly labels: ConversationsWorkspaceLabels;
|
|
1769
|
+
readonly className?: string;
|
|
1770
|
+
/** Barra extra do produto acima da lista (ex.: filtro de carteira, seletor de campanha). */
|
|
1771
|
+
readonly renderFilters?: (inbox: UseConversationsInboxResult) => ReactNode;
|
|
1772
|
+
/** Ações em lote do produto, ao lado de finalizar e template. */
|
|
1773
|
+
readonly renderBulkActions?: (inbox: UseConversationsInboxResult) => ReactNode;
|
|
1774
|
+
readonly renderRow?: (conversation: ConversationSummary) => ReactNode;
|
|
1775
|
+
readonly onSendTemplateToSelected?: () => void;
|
|
1776
|
+
}
|
|
1777
|
+
declare function ConversationsInboxList({ inbox, labels, className, renderFilters, renderBulkActions, renderRow, onSendTemplateToSelected, }: ConversationsInboxListProps): react.JSX.Element;
|
|
1778
|
+
|
|
1779
|
+
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, 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_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, 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, MultiSelectFilter, type MultiSelectFilterProps, NARROW_MAX_WIDTH_PX, type QuickReply, RICH_COMPOSER_ACTION, ResolveMediaUrl, type RichComposerAction, type RichComposerQuickReply, type RichComposerTooltips, type RichComposerVariable, RichMessageComposer, type RichMessageComposerHandle, type RichMessageComposerProps, SSEProvider, SimpleEmojiPicker, type SimpleEmojiPickerProps, 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, isSameDay, isWindowBlocking, parseWhatsAppFormatting, phoneInitials, 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 };
|