@adatechnology/conversations-ui 0.1.0-rc.25 → 0.1.0-rc.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/chunk-DXPSPUWF.js +110 -0
  2. package/dist/{chunk-TV4OQRGH.js → chunk-UZJBYD5O.js} +123 -45
  3. package/dist/{chunk-2AYDBWNE.js → chunk-WCBDXZ3X.js} +13 -3
  4. package/dist/flows/index.d.ts +3 -1
  5. package/dist/flows/index.js +61 -18
  6. package/dist/index.d.ts +37 -10
  7. package/dist/index.js +694 -297
  8. package/dist/preview/index.js +3 -3
  9. package/dist/styles.css +119 -6
  10. package/package.json +1 -1
  11. package/src/AudioPlayer.tsx +8 -0
  12. package/src/AudioRecorderButton.test.tsx +3 -3
  13. package/src/AudioRecorderButton.tsx +3 -3
  14. package/src/AudioTranscription.tsx +4 -1
  15. package/src/Avatar.tsx +1 -1
  16. package/src/ConversationContextPanel.tsx +2 -2
  17. package/src/ConversationDocumentsPanel.tsx +11 -6
  18. package/src/ConversationHeader.test.tsx +66 -0
  19. package/src/ConversationHeader.tsx +147 -47
  20. package/src/ConversationListItem.tsx +3 -2
  21. package/src/ConversationRow.tsx +31 -7
  22. package/src/DocumentsLibrary.tsx +9 -4
  23. package/src/EmojiPicker.tsx +2 -1
  24. package/src/InteractiveMessage.tsx +3 -0
  25. package/src/Lightbox.tsx +1 -1
  26. package/src/MediaRenderer.tsx +2 -0
  27. package/src/MessageBubble.test.tsx +41 -0
  28. package/src/MessageBubble.tsx +27 -8
  29. package/src/MessageComposer.tsx +11 -8
  30. package/src/RichMessageComposer.test.tsx +42 -12
  31. package/src/RichMessageComposer.tsx +216 -45
  32. package/src/SimpleEmojiPicker.tsx +5 -3
  33. package/src/StatusTicks.tsx +1 -1
  34. package/src/Toast.tsx +4 -0
  35. package/src/Tooltip.test.ts +42 -0
  36. package/src/Tooltip.tsx +164 -0
  37. package/src/WhatsAppMessageEditor.tsx +4 -4
  38. package/src/WindowExpiredNotice.tsx +12 -4
  39. package/src/composer.constant.ts +33 -0
  40. package/src/conversationWindow.ts +7 -5
  41. package/src/documents/DocumentsWorkspace.tsx +10 -6
  42. package/src/flows/FlowGroupHeader.tsx +2 -2
  43. package/src/flows/FlowMapNode.tsx +2 -1
  44. package/src/flows/FlowNodeCard.tsx +3 -3
  45. package/src/flows/FlowNodePanel.tsx +6 -5
  46. package/src/flows/FlowPalette.tsx +7 -1
  47. package/src/flows/FlowPortalNode.tsx +1 -1
  48. package/src/flows/FlowsWorkspace.tsx +16 -5
  49. package/src/flows/labels.ts +4 -0
  50. package/src/hooks/useContainerWidth.ts +35 -0
  51. package/src/hooks/useConversationRealtime.ts +10 -8
  52. package/src/icon.constant.ts +12 -0
  53. package/src/index.ts +1 -0
  54. package/src/lib/composer-formatting.test.ts +78 -0
  55. package/src/lib/composer-formatting.ts +145 -0
  56. package/src/lib/whatsapp-formatting.test.tsx +37 -0
  57. package/src/lib/whatsapp-formatting.tsx +28 -3
  58. package/src/listing/index.tsx +5 -1
  59. package/src/pagination.constant.ts +10 -0
  60. package/src/preview/ConversationSimulatorPanel.tsx +1 -1
  61. package/src/providers/ConversationsProvider.tsx +8 -6
  62. package/src/settings/MessagesWorkspace.tsx +3 -0
  63. package/src/settings/TopicsForm.tsx +2 -0
  64. package/src/settings/TranscriptionSettingsForm.test.tsx +1 -1
  65. package/src/settings/TranscriptionSettingsForm.tsx +1 -0
  66. package/src/settings/WelcomeFarewellForm.tsx +1 -0
  67. package/src/settings/WhatsAppCreateTemplateForm.tsx +1 -0
  68. package/src/settings/WhatsAppTemplateSettingsForm.tsx +5 -2
  69. package/src/settings/WhatsAppTemplatesSettings.tsx +2 -0
  70. package/src/styles.css +100 -4
  71. package/src/workspace/BulkTemplateModal.tsx +4 -2
  72. package/src/workspace/ConversationPane.tsx +49 -19
  73. package/src/workspace/ConversationsInboxList.tsx +17 -8
  74. package/src/workspace/ConversationsWorkspace.tsx +24 -16
  75. package/src/workspace/useConversationsInbox.ts +5 -2
@@ -12,7 +12,9 @@
12
12
  */
13
13
 
14
14
  import { useEffect, useMemo, useState, type ReactNode } from 'react'
15
+ import { Check, CheckCheck, FlaskConical, Hourglass, Mail, MessagesSquare } from 'lucide-react'
15
16
 
17
+ import { ICON_SIZE_ACTION, ICON_SIZE_INLINE } from '../icon.constant'
16
18
  import type { ConversationContextEntry } from '../ConversationContextPanel'
17
19
  import type { ConversationHeaderUtility } from '../ConversationHeader'
18
20
  import type { QuickReply } from '../MessageComposer'
@@ -23,6 +25,7 @@ import { ConversationPane } from './ConversationPane'
23
25
  import { ConversationsInboxList } from './ConversationsInboxList'
24
26
  import { DEFAULT_CONVERSATIONS_WORKSPACE_LABELS, type ConversationsWorkspaceLabels } from './labels'
25
27
  import { useConversationsInbox, type UseConversationsInboxResult } from './useConversationsInbox'
28
+ import { TooltipLayer } from '../Tooltip'
26
29
 
27
30
  export interface ConversationsWorkspaceSimulator {
28
31
  /**
@@ -32,7 +35,8 @@ export interface ConversationsWorkspaceSimulator {
32
35
  render(params: { conversationId: string; close: () => void }): ReactNode
33
36
  /** Ausente = ligado. Serve para esconder fora de desenvolvimento sem condicionar o JSX. */
34
37
  readonly enabled?: boolean
35
- readonly icon?: string
38
+ /** Ícone da biblioteca (lucide) no utilitário do cabeçalho. Ausente, entra o frasco de teste. */
39
+ readonly icon?: ReactNode
36
40
  readonly label?: string
37
41
  }
38
42
 
@@ -166,7 +170,7 @@ export function ConversationsWorkspace({
166
170
  ...fromProduct,
167
171
  {
168
172
  key: 'simulator',
169
- icon: simulator?.icon ?? '🧪',
173
+ icon: simulator?.icon ?? <FlaskConical size={ICON_SIZE_ACTION} />,
170
174
  label: simulator?.label ?? 'Simular cliente',
171
175
  active: simulatorOpen,
172
176
  run: () => setSimulatorOpen((open) => !open),
@@ -176,22 +180,23 @@ export function ConversationsWorkspace({
176
180
 
177
181
  return (
178
182
  <div className={`cv-workspace${className ? ` ${className}` : ''}`}>
183
+ <TooltipLayer />
179
184
  {/* Cabeçalho denso em tela estreita: ícone + número. O rótulo escrito ocupava três linhas em
180
185
  375px e empurrava a lista para fora da tela. */}
181
186
  <header className="cv-workspace-header">
182
187
  <div className="cv-workspace-header__titles">
183
188
  <h1>{labels.title}</h1>
184
189
  <p>
185
- <span title={labels.conversations}>
186
- 💬 {inbox.totalCount}
190
+ <span data-cv-tooltip={labels.conversations} className="cv-stat">
191
+ <MessagesSquare size={ICON_SIZE_INLINE} aria-hidden="true" /> {inbox.totalCount}
187
192
  <span className="cv-only-wide"> {labels.conversations}</span>
188
193
  </span>
189
- <span title={labels.waiting} className="cv-workspace-header__waiting">
190
- ⏳ {inbox.waitingCount}
194
+ <span data-cv-tooltip={labels.waiting} className="cv-stat cv-workspace-header__waiting">
195
+ <Hourglass size={ICON_SIZE_INLINE} aria-hidden="true" /> {inbox.waitingCount}
191
196
  <span className="cv-only-wide"> {labels.waiting}</span>
192
197
  </span>
193
- <span title={labels.unread}>
194
- ✉️ {inbox.unreadCount}
198
+ <span data-cv-tooltip={labels.unread} className="cv-stat">
199
+ <Mail size={ICON_SIZE_INLINE} aria-hidden="true" /> {inbox.unreadCount}
195
200
  <span className="cv-only-wide"> {labels.unread}</span>
196
201
  </span>
197
202
  </p>
@@ -202,22 +207,24 @@ export function ConversationsWorkspace({
202
207
  type="button"
203
208
  onClick={() => inbox.setWaitingOnly(!inbox.waitingOnly)}
204
209
  aria-pressed={inbox.waitingOnly}
205
- title={labels.waitingOnly}
210
+ data-cv-tooltip={labels.waitingOnly} aria-label={labels.waitingOnly}
206
211
  className={inbox.waitingOnly ? 'cv-workspace-toggle cv-workspace-toggle--on' : 'cv-workspace-toggle'}
207
212
  >
208
- ⏳<span className="cv-only-wide"> {labels.waitingOnly}</span>
213
+ <Hourglass size={ICON_SIZE_ACTION} aria-hidden="true" />
214
+ <span className="cv-only-wide">{labels.waitingOnly}</span>
209
215
  </button>
210
216
  {/* O contador só aparece com seleção: " (0)" era texto morto ao lado do ícone. */}
211
217
  <button
212
218
  type="button"
213
219
  onClick={() => void inbox.markSelectedAsRead()}
214
220
  disabled={inbox.selectedIds.size === 0 || inbox.busy}
215
- title={labels.markSelectedAsRead}
221
+ data-cv-tooltip={labels.markSelectedAsRead}
216
222
  aria-label={labels.markSelectedAsRead}
217
223
  className="cv-workspace-toggle"
218
224
  >
219
- ✓<span className="cv-only-wide"> {labels.markSelectedAsRead}</span>
220
- {inbox.selectedIds.size > 0 ? ` (${inbox.selectedIds.size})` : ''}
225
+ <Check size={ICON_SIZE_ACTION} aria-hidden="true" />
226
+ <span className="cv-only-wide">{labels.markSelectedAsRead}</span>
227
+ {inbox.selectedIds.size > 0 ? <span>({inbox.selectedIds.size})</span> : null}
221
228
  </button>
222
229
  {/* Só com não lida na tela e só onde o host implementa a rota: sem isso o botão zerava
223
230
  nada e ainda assim ocupava o cabeçalho. */}
@@ -226,10 +233,11 @@ export function ConversationsWorkspace({
226
233
  type="button"
227
234
  onClick={() => void inbox.markAllAsRead()}
228
235
  disabled={inbox.busy}
229
- title={labels.markAllAsRead}
236
+ data-cv-tooltip={labels.markAllAsRead} aria-label={labels.markAllAsRead}
230
237
  className="cv-workspace-toggle"
231
238
  >
232
- ✓✓<span className="cv-only-wide"> {labels.markAllAsRead}</span>
239
+ <CheckCheck size={ICON_SIZE_ACTION} aria-hidden="true" />
240
+ <span className="cv-only-wide">{labels.markAllAsRead}</span>
233
241
  </button>
234
242
  ) : null}
235
243
  {renderHeaderActions?.(inbox)}
@@ -283,7 +291,7 @@ export function ConversationsWorkspace({
283
291
  now={inbox.now}
284
292
  busy={inbox.busy}
285
293
  labels={labels}
286
- {...(inbox.canTakeover ? { onTakeover: () => void inbox.takeover(selected.id) } : {})}
294
+ {...(inbox.canTakeover ? { onTakeover: () => inbox.takeover(selected.id) } : {})}
287
295
  {...(inbox.canTakeover ? { onReturnToBot: () => void inbox.releaseToBot(selected.id) } : {})}
288
296
  {...(inbox.canFinalize ? { onFinish: () => void inbox.finalize(selected.id) } : {})}
289
297
  {...(flowLabelOf ? { flowLabel: flowLabelOf(selected) } : {})}
@@ -265,8 +265,11 @@ export function useConversationsInbox(params: UseConversationsInboxParams = {}):
265
265
  selectedConversation: conversations.find((conversation) => conversation.id === selectedId),
266
266
  loading,
267
267
  now,
268
- totalCount: conversations.length,
269
- unreadCount: conversations.reduce((total, conversation) => total + conversation.unread, 0),
268
+ // Com paginação no servidor `conversations` é só a página, então contar o array mostrava o
269
+ // tamanho da página no lugar do tamanho da base. Não lidas e aguardando seguem sendo da página:
270
+ // somar a base inteira exige agregado do servidor, que a listagem não devolve.
271
+ totalCount: params.serverPaginated ? total : conversations.length,
272
+ unreadCount: conversations.reduce((sum, conversation) => sum + conversation.unread, 0),
270
273
  waitingCount: conversations.filter((conversation) => conversation.waitingHuman).length,
271
274
  filteredCount: totalForPaging,
272
275
  expiredSelectedCount: conversations.filter(