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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/dist/ConversationSimulatorPanel--5fIzXWY.d.ts +804 -0
  2. package/dist/{chunk-G5BM3VBP.js → chunk-BJNRLLDO.js} +1248 -282
  3. package/dist/chunk-DKPXKQGC.js +110 -0
  4. package/dist/{chunk-2AYDBWNE.js → chunk-WCBDXZ3X.js} +13 -3
  5. package/dist/flows/index.d.ts +422 -5
  6. package/dist/flows/index.js +2502 -676
  7. package/dist/index.d.ts +919 -17
  8. package/dist/index.js +3676 -675
  9. package/dist/preview/index.d.ts +62 -105
  10. package/dist/preview/index.js +162 -284
  11. package/dist/styles.css +893 -0
  12. package/package.json +9 -8
  13. package/src/AudioPlayer.tsx +8 -0
  14. package/src/AudioRecorderButton.test.tsx +30 -0
  15. package/src/AudioRecorderButton.tsx +248 -0
  16. package/src/AudioTranscription.test.tsx +115 -0
  17. package/src/AudioTranscription.tsx +252 -0
  18. package/src/Avatar.tsx +1 -1
  19. package/src/ConversationContextPanel.tsx +218 -44
  20. package/src/ConversationDocumentsPanel.tsx +11 -6
  21. package/src/ConversationHeader.test.tsx +66 -0
  22. package/src/ConversationHeader.tsx +147 -47
  23. package/src/ConversationListItem.tsx +8 -6
  24. package/src/ConversationLocalesProvider.tsx +28 -0
  25. package/src/ConversationRow.tsx +53 -7
  26. package/src/DarkModeToggle.test.tsx +76 -0
  27. package/src/DarkModeToggle.tsx +92 -0
  28. package/src/DocumentsLibrary.tsx +67 -7
  29. package/src/EmojiPicker.tsx +2 -1
  30. package/src/InteractiveMessage.tsx +3 -0
  31. package/src/Lightbox.tsx +1 -1
  32. package/src/MediaRenderer.tsx +88 -15
  33. package/src/MessageBubble.test.tsx +41 -0
  34. package/src/MessageBubble.tsx +47 -5
  35. package/src/MessageComposer.test.tsx +35 -0
  36. package/src/MessageComposer.tsx +122 -17
  37. package/src/MessageText.tsx +2 -1
  38. package/src/MessageTimestamp.tsx +2 -1
  39. package/src/RichMessageComposer.test.tsx +113 -0
  40. package/src/RichMessageComposer.tsx +551 -0
  41. package/src/SimpleEmojiPicker.tsx +5 -3
  42. package/src/StatusTicks.tsx +1 -1
  43. package/src/Toast.tsx +4 -0
  44. package/src/Tooltip.test.ts +42 -0
  45. package/src/Tooltip.tsx +167 -0
  46. package/src/Wallpaper.tsx +27 -13
  47. package/src/WhatsAppMessageEditor.tsx +10 -7
  48. package/src/WindowExpiredNotice.tsx +12 -4
  49. package/src/{preview/audioRecorderFormat.test.ts → audioRecorderFormat.test.ts} +1 -1
  50. package/src/buildOutput.test.ts +79 -0
  51. package/src/composer.constant.ts +33 -0
  52. package/src/conversationTranscript.test.ts +57 -0
  53. package/src/conversationTranscript.ts +29 -4
  54. package/src/conversationWindow.ts +7 -5
  55. package/src/documentTypeLabel.test.ts +57 -0
  56. package/src/documents/DocumentsWorkspace.tsx +550 -0
  57. package/src/documents/index.ts +8 -0
  58. package/src/documents/labels.ts +92 -0
  59. package/src/flows/FlowConnectionEdge.tsx +104 -0
  60. package/src/flows/FlowGroupHeader.tsx +12 -2
  61. package/src/flows/FlowLegend.tsx +125 -0
  62. package/src/flows/FlowMapCanvas.tsx +15 -12
  63. package/src/flows/FlowMapNode.tsx +4 -1
  64. package/src/flows/FlowNodeCard.tsx +219 -34
  65. package/src/flows/FlowNodePanel.tsx +153 -39
  66. package/src/flows/FlowPalette.tsx +156 -70
  67. package/src/flows/FlowPortalNode.tsx +1 -1
  68. package/src/flows/FlowWhatsAppPreview.tsx +14 -3
  69. package/src/flows/FlowsWorkspace.tsx +1255 -0
  70. package/src/flows/flowCanvasModel.test.ts +456 -0
  71. package/src/flows/flowCanvasModel.ts +378 -0
  72. package/src/flows/flowEditorOps.test.ts +276 -0
  73. package/src/flows/flowEditorOps.ts +202 -0
  74. package/src/flows/flowGraph.ts +78 -53
  75. package/src/flows/flowMenuPlacement.test.ts +130 -0
  76. package/src/flows/flowMenuPlacement.ts +86 -0
  77. package/src/flows/index.ts +51 -2
  78. package/src/flows/labels.ts +180 -0
  79. package/src/flows/workspaceContract.test.ts +126 -0
  80. package/src/hooks/useContainerWidth.ts +35 -0
  81. package/src/hooks/useConversationRealtime.ts +10 -8
  82. package/src/hooks/useScrollToLatestMessage.ts +127 -0
  83. package/src/hooks/useUrlFilterState.ts +107 -0
  84. package/src/icon.constant.ts +12 -0
  85. package/src/index.ts +100 -0
  86. package/src/lib/composer-formatting.test.ts +78 -0
  87. package/src/lib/composer-formatting.ts +145 -0
  88. package/src/lib/whatsapp-formatting.test.tsx +37 -0
  89. package/src/lib/whatsapp-formatting.tsx +28 -3
  90. package/src/listing/index.tsx +202 -0
  91. package/src/pagination.constant.ts +10 -0
  92. package/src/preview/ConversationPreview.tsx +84 -45
  93. package/src/preview/ConversationSimulatorClient.ts +143 -0
  94. package/src/preview/ConversationSimulatorPanel.test.tsx +55 -0
  95. package/src/preview/ConversationSimulatorPanel.tsx +131 -0
  96. package/src/preview/createPreviewBridgeClient.test.ts +92 -0
  97. package/src/preview/createPreviewBridgeClient.ts +124 -0
  98. package/src/preview/createPreviewMediaUploader.ts +82 -0
  99. package/src/preview/createPreviewWebhookClient.test.ts +96 -0
  100. package/src/preview/createPreviewWebhookClient.ts +99 -3
  101. package/src/preview/index.ts +36 -2
  102. package/src/preview/previewMediaUploader.test.ts +61 -0
  103. package/src/providers/ConversationsProvider.tsx +8 -6
  104. package/src/providers/types.ts +59 -2
  105. package/src/quickReply.test.ts +58 -0
  106. package/src/replyLatency.test.ts +71 -0
  107. package/src/replyLatency.ts +57 -0
  108. package/src/settings/MessagesWorkspace.tsx +571 -0
  109. package/src/settings/TopicsForm.tsx +2 -0
  110. package/src/settings/TranscriptionSettingsForm.test.tsx +81 -0
  111. package/src/settings/TranscriptionSettingsForm.tsx +190 -0
  112. package/src/settings/WelcomeFarewellForm.tsx +1 -0
  113. package/src/settings/WhatsAppCreateTemplateForm.tsx +1 -0
  114. package/src/settings/WhatsAppTemplateSettingsForm.tsx +5 -2
  115. package/src/settings/WhatsAppTemplatesSettings.test.tsx +61 -0
  116. package/src/settings/WhatsAppTemplatesSettings.tsx +22 -2
  117. package/src/styles.css +858 -0
  118. package/src/theme.ts +13 -0
  119. package/src/types.ts +26 -0
  120. package/src/workspace/BulkTemplateModal.tsx +132 -0
  121. package/src/workspace/ConversationPane.tsx +432 -0
  122. package/src/workspace/ConversationsInboxList.tsx +194 -0
  123. package/src/workspace/ConversationsWorkspace.tsx +423 -0
  124. package/src/workspace/index.ts +17 -0
  125. package/src/workspace/labels.test.ts +17 -0
  126. package/src/workspace/labels.ts +85 -0
  127. package/src/workspace/useConversationsInbox.ts +332 -0
  128. package/dist/types-B5C1DLu1.d.ts +0 -365
  129. package/src/preview/AudioRecorderButton.tsx +0 -117
@@ -0,0 +1,190 @@
1
+ import type { FormEvent } from 'react'
2
+ import { AudioLines, Save } from 'lucide-react'
3
+ import type { TranscriptionMode } from '../types'
4
+
5
+ export interface TranscriptionSettingsFormLabels {
6
+ sectionTitle: string
7
+ enabled: string
8
+ enabledHint: string
9
+ modeTitle: string
10
+ modeAuto: string
11
+ modeAutoHint: string
12
+ modeOnDemand: string
13
+ modeOnDemandHint: string
14
+ unavailable: string
15
+ save: string
16
+ saving: string
17
+ saveSuccess: string
18
+ }
19
+
20
+ const DEFAULT_LABELS: TranscriptionSettingsFormLabels = {
21
+ sectionTitle: 'Transcrição de áudio',
22
+ enabled: 'Transcrever notas de voz',
23
+ enabledHint: 'Converte o áudio do cliente em texto, que o atendente lê e copia direto da conversa.',
24
+ modeTitle: 'Quando transcrever',
25
+ modeAuto: 'Automaticamente',
26
+ modeAutoHint: 'Toda nota de voz recebida é transcrita na hora. Mais cômodo, consome mais cota.',
27
+ modeOnDemand: 'Quando o atendente pedir',
28
+ modeOnDemandHint: 'Transcreve só ao clicar no botão da conversa. Consome cota apenas com áudio que alguém vai ler.',
29
+ unavailable: 'Transcrição não está disponível neste ambiente — fale com quem administra a instalação.',
30
+ save: 'Salvar',
31
+ saving: 'Salvando...',
32
+ saveSuccess: 'Configuração de transcrição salva.',
33
+ }
34
+
35
+ export interface TranscriptionSettingsFormProps {
36
+ enabled: boolean
37
+ onEnabledChange: (value: boolean) => void
38
+ mode: TranscriptionMode
39
+ onModeChange: (value: TranscriptionMode) => void
40
+ onSave: (event: FormEvent) => void
41
+ /**
42
+ * A capacidade existe no servidor (engine e credencial configurados)?
43
+ *
44
+ * Distinto de `enabled`: isto é "o ambiente consegue", aquilo é "esta empresa quer". Sem a
45
+ * distinção, um lojista ligaria o interruptor num ambiente sem engine e concluiria que o produto
46
+ * está quebrado — o texto nunca apareceria e nada explicaria por quê.
47
+ */
48
+ isAvailable?: boolean
49
+ saving?: boolean
50
+ saveSuccess?: boolean
51
+ labels?: Partial<TranscriptionSettingsFormLabels>
52
+ }
53
+
54
+ /**
55
+ * Interruptor de transcrição por empresa, para a tela de configurações.
56
+ *
57
+ * Puramente apresentacional, como os outros forms daqui: quem persiste é o host. O pacote não sabe a
58
+ * rota, e a política vive nas configurações do módulo (`settings.transcriptionEnabled`), não em
59
+ * variável de ambiente — mudar de ideia sobre transcrever não deveria exigir deploy.
60
+ */
61
+ export function TranscriptionSettingsForm({
62
+ enabled,
63
+ onEnabledChange,
64
+ mode,
65
+ onModeChange,
66
+ onSave,
67
+ isAvailable = true,
68
+ saving = false,
69
+ saveSuccess = false,
70
+ labels: labelsOverride,
71
+ }: TranscriptionSettingsFormProps) {
72
+ const labels = { ...DEFAULT_LABELS, ...labelsOverride }
73
+
74
+ return (
75
+ <section className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden">
76
+ <div className="px-6 py-4 border-b border-gray-100 dark:border-gray-700">
77
+ <h2 className="text-sm font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-1.5">
78
+ <AudioLines size={14} className="text-blue-600 dark:text-blue-400" />
79
+ {labels.sectionTitle}
80
+ </h2>
81
+ </div>
82
+
83
+ <form onSubmit={onSave} className="p-6 space-y-5">
84
+ {!isAvailable && (
85
+ <div className="bg-amber-50 dark:bg-amber-950/40 border border-amber-200 dark:border-amber-800 rounded-xl px-4 py-3 text-sm text-amber-800 dark:text-amber-300">
86
+ {labels.unavailable}
87
+ </div>
88
+ )}
89
+
90
+ <label className="flex items-start gap-3 cursor-pointer">
91
+ <input
92
+ type="checkbox"
93
+ checked={enabled}
94
+ onChange={(event) => onEnabledChange(event.target.checked)}
95
+ disabled={!isAvailable}
96
+ className="mt-0.5 h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500 disabled:opacity-50"
97
+ />
98
+ <span>
99
+ <span className="block text-sm font-medium text-gray-700 dark:text-gray-300">{labels.enabled}</span>
100
+ <span className="block text-xs text-gray-500 dark:text-gray-400">{labels.enabledHint}</span>
101
+ </span>
102
+ </label>
103
+
104
+ {/* O modo só aparece quando ligado: escolher "quando transcrever" sem transcrever nada é uma
105
+ pergunta sem consequência, e ler as duas opções gasta atenção do operador à toa. */}
106
+ {enabled && (
107
+ <fieldset disabled={!isAvailable} className="space-y-2.5">
108
+ <legend className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{labels.modeTitle}</legend>
109
+
110
+ <ModeOption
111
+ value="onDemand"
112
+ current={mode}
113
+ onSelect={onModeChange}
114
+ title={labels.modeOnDemand}
115
+ hint={labels.modeOnDemandHint}
116
+ />
117
+ <ModeOption
118
+ value="auto"
119
+ current={mode}
120
+ onSelect={onModeChange}
121
+ title={labels.modeAuto}
122
+ hint={labels.modeAutoHint}
123
+ />
124
+ </fieldset>
125
+ )}
126
+
127
+ {saveSuccess && (
128
+ <div className="bg-green-50 dark:bg-green-950/40 border border-green-200 dark:border-green-800 rounded-xl px-4 py-3 text-sm text-green-700 dark:text-green-400">
129
+ {labels.saveSuccess}
130
+ </div>
131
+ )}
132
+
133
+ <div className="flex justify-end">
134
+ <button
135
+ data-cv-tooltip={saving ? labels.saving : labels.save} aria-label={saving ? labels.saving : labels.save}
136
+ type="submit"
137
+ disabled={saving || !isAvailable}
138
+ className="flex items-center gap-2 bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white font-semibold px-5 py-2.5 rounded-xl transition-colors text-sm"
139
+ >
140
+ {saving ? (
141
+ <span className="w-4 h-4 border-2 border-white/40 border-t-white rounded-full animate-spin" />
142
+ ) : (
143
+ <Save size={14} />
144
+ )}
145
+ {saving ? labels.saving : labels.save}
146
+ </button>
147
+ </div>
148
+ </form>
149
+ </section>
150
+ )
151
+ }
152
+
153
+ function ModeOption({
154
+ value,
155
+ current,
156
+ onSelect,
157
+ title,
158
+ hint,
159
+ }: {
160
+ value: TranscriptionMode
161
+ current: TranscriptionMode
162
+ onSelect: (value: TranscriptionMode) => void
163
+ title: string
164
+ hint: string
165
+ }) {
166
+ const isSelected = current === value
167
+
168
+ return (
169
+ <label
170
+ className={`flex items-start gap-3 cursor-pointer rounded-xl border px-4 py-3 transition-colors ${
171
+ isSelected
172
+ ? 'border-blue-500 bg-blue-50 dark:border-blue-500 dark:bg-blue-950/30'
173
+ : 'border-gray-200 hover:border-gray-300 dark:border-gray-700 dark:hover:border-gray-600'
174
+ }`}
175
+ >
176
+ <input
177
+ type="radio"
178
+ name="transcription-mode"
179
+ value={value}
180
+ checked={isSelected}
181
+ onChange={() => onSelect(value)}
182
+ className="mt-0.5 h-4 w-4 border-gray-300 text-blue-600 focus:ring-blue-500"
183
+ />
184
+ <span>
185
+ <span className="block text-sm font-medium text-gray-700 dark:text-gray-300">{title}</span>
186
+ <span className="block text-xs text-gray-500 dark:text-gray-400">{hint}</span>
187
+ </span>
188
+ </label>
189
+ )
190
+ }
@@ -104,6 +104,7 @@ export function WelcomeFarewellForm({
104
104
 
105
105
  <div className="flex justify-end">
106
106
  <button
107
+ data-cv-tooltip={saving ? labels.saving : labels.save} aria-label={saving ? labels.saving : labels.save}
107
108
  type="submit"
108
109
  disabled={saving}
109
110
  className="flex items-center gap-2 bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white font-semibold px-5 py-2.5 rounded-xl transition-colors text-sm"
@@ -297,6 +297,7 @@ export function WhatsAppCreateTemplateForm({
297
297
 
298
298
  <div className="flex justify-end">
299
299
  <button
300
+ data-cv-tooltip={submitting ? labels.sending : labels.sendForApproval} aria-label={submitting ? labels.sending : labels.sendForApproval}
300
301
  type="submit"
301
302
  disabled={submitting || !value.name || !value.bodyText}
302
303
  className="flex items-center gap-2 bg-emerald-600 hover:bg-emerald-700 disabled:bg-emerald-400 text-white font-semibold px-5 py-2.5 rounded-xl transition-colors text-sm"
@@ -132,6 +132,7 @@ export function WhatsAppTemplateSettingsForm({
132
132
  <div className="flex items-center justify-between mb-1.5">
133
133
  <label className="text-sm font-medium text-gray-700 dark:text-gray-300">{labels.templateLabel}</label>
134
134
  <button
135
+ data-cv-tooltip={labels.refreshList} aria-label={labels.refreshList}
135
136
  type="button"
136
137
  onClick={onRefreshTemplates}
137
138
  disabled={loadingTemplates}
@@ -180,6 +181,7 @@ export function WhatsAppTemplateSettingsForm({
180
181
  <div className="flex items-center justify-between mb-1.5">
181
182
  <label className="text-sm font-medium text-gray-700 dark:text-gray-300">{labels.variablesLabel}</label>
182
183
  <button
184
+ data-cv-tooltip={labels.addVariable} aria-label={labels.addVariable}
183
185
  type="button"
184
186
  onClick={() => onVariablesChange([...variables, ''])}
185
187
  className="flex items-center gap-1 text-xs text-blue-600 dark:text-blue-400 hover:underline"
@@ -198,7 +200,7 @@ export function WhatsAppTemplateSettingsForm({
198
200
  key={available.token}
199
201
  type="button"
200
202
  onClick={() => insertVariable(available.token)}
201
- title={`Inserir ${available.token}`}
203
+ data-cv-tooltip={`Inserir ${available.token}`} aria-label={`Inserir ${available.token}`}
202
204
  className="inline-flex items-center gap-1 rounded-md border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-800 px-2 py-1 text-xs hover:border-blue-400 dark:hover:border-blue-500 transition-colors"
203
205
  >
204
206
  <code className="text-blue-600 dark:text-blue-300">{available.token}</code>
@@ -233,7 +235,7 @@ export function WhatsAppTemplateSettingsForm({
233
235
  type="button"
234
236
  onClick={() => removeVariable(index)}
235
237
  className="text-gray-400 hover:text-red-500 dark:hover:text-red-400 transition-colors flex-shrink-0"
236
- title={labels.removeVariable}
238
+ data-cv-tooltip={labels.removeVariable} aria-label={labels.removeVariable}
237
239
  >
238
240
  <Trash2 size={14} />
239
241
  </button>
@@ -250,6 +252,7 @@ export function WhatsAppTemplateSettingsForm({
250
252
 
251
253
  <div className="flex justify-end">
252
254
  <button
255
+ data-cv-tooltip={saving ? labels.saving : labels.save} aria-label={saving ? labels.saving : labels.save}
253
256
  type="submit"
254
257
  disabled={saving || !selectedTemplateName.trim()}
255
258
  className="flex items-center gap-2 bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white font-semibold px-5 py-2.5 rounded-xl transition-colors text-sm"
@@ -0,0 +1,61 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { renderToStaticMarkup } from 'react-dom/server'
3
+
4
+ import { WhatsAppTemplatesSettings } from './WhatsAppTemplatesSettings'
5
+ import type { WhatsAppTemplatesSettingsProps } from './WhatsAppTemplatesSettings'
6
+
7
+ function render(overrides: Partial<WhatsAppTemplatesSettingsProps> = {}): string {
8
+ return renderToStaticMarkup(
9
+ <WhatsAppTemplatesSettings
10
+ templates={[]}
11
+ selectedTemplateName=""
12
+ onSelectTemplate={() => {}}
13
+ variables={[]}
14
+ onVariablesChange={() => {}}
15
+ onSave={() => {}}
16
+ {...overrides}
17
+ />,
18
+ )
19
+ }
20
+
21
+ describe('WhatsAppTemplatesSettings', () => {
22
+ it('renderiza os formulários extras de papel na aba de seleção', () => {
23
+ const markup = render({ extraRoleForms: <div>MARCADOR_PAPEL_EXTRA</div> })
24
+
25
+ expect(markup).toContain('MARCADOR_PAPEL_EXTRA')
26
+ })
27
+
28
+ it('sem extraRoleForms, o comportamento é idêntico ao de antes', () => {
29
+ const markup = render()
30
+
31
+ expect(markup).not.toContain('MARCADOR_PAPEL_EXTRA')
32
+ })
33
+
34
+ it('aceita previewCompanyName e variableExamples em create sem quebrar a tipagem', () => {
35
+ const markup = render({
36
+ create: {
37
+ value: {
38
+ name: '',
39
+ category: 'UTILITY',
40
+ language: 'pt_BR',
41
+ headerType: 'NONE',
42
+ headerText: '',
43
+ bodyText: '',
44
+ footerText: '',
45
+ },
46
+ onChange: () => {},
47
+ onSubmit: () => {},
48
+ previewCompanyName: 'Empresa Teste',
49
+ variableExamples: ['João', '123'],
50
+ },
51
+ })
52
+
53
+ expect(markup).toContain('Criar template')
54
+ })
55
+
56
+ it('sem create, a aba de criação não aparece', () => {
57
+ const markup = render()
58
+
59
+ expect(markup).not.toContain('Criar template')
60
+ })
61
+ })
@@ -8,14 +8,16 @@
8
8
  * Segue presentacional: nenhuma chamada de rede aqui. Templates, estado e handlers vêm por props.
9
9
  */
10
10
 
11
- import { useState, type FormEvent } from 'react'
11
+ import { useState, type FormEvent, type ReactNode } from 'react'
12
12
  import {
13
13
  WhatsAppTemplateSettingsForm,
14
14
  type WhatsAppTemplateSummary,
15
15
  type WhatsAppTemplateVariableSuggestion,
16
+ type WhatsAppTemplateSettingsFormLabels,
16
17
  } from './WhatsAppTemplateSettingsForm'
17
18
  import {
18
19
  WhatsAppCreateTemplateForm,
20
+ type WhatsAppCreateTemplateFormLabels,
19
21
  type WhatsAppCreateTemplateResult,
20
22
  type WhatsAppCreateTemplateState,
21
23
  } from './WhatsAppCreateTemplateForm'
@@ -56,13 +58,26 @@ export interface WhatsAppTemplatesSettingsProps {
56
58
  onSubmit: (event: FormEvent) => void
57
59
  submitting?: boolean
58
60
  result?: WhatsAppCreateTemplateResult | null
61
+ labels?: Partial<WhatsAppCreateTemplateFormLabels>
62
+ /** Nome exibido na prévia do template criado; sem isto o form usa seu próprio fallback. */
63
+ previewCompanyName?: string
64
+ variableExamples?: readonly string[]
59
65
  }
60
66
  labels?: Partial<WhatsAppTemplatesSettingsLabels>
67
+ /** Vocabulário do formulário de seleção — separado de `labels`, que nomeia só as abas daqui. */
68
+ settingsLabels?: Partial<WhatsAppTemplateSettingsFormLabels>
69
+ /**
70
+ * Formulários extras de seleção (um por papel adicional de template, ex.: despedida), empilhados
71
+ * abaixo do principal na mesma sub-aba. Ausente = só o papel principal, comportamento de sempre.
72
+ */
73
+ extraRoleForms?: ReactNode
61
74
  }
62
75
 
63
76
  export function WhatsAppTemplatesSettings({
64
77
  labels: labelsOverride,
78
+ settingsLabels,
65
79
  create,
80
+ extraRoleForms,
66
81
  ...settingsProps
67
82
  }: WhatsAppTemplatesSettingsProps) {
68
83
  const labels = { ...DEFAULT_TEMPLATES_SETTINGS_LABELS, ...labelsOverride }
@@ -72,6 +87,7 @@ export function WhatsAppTemplatesSettings({
72
87
  <div className="space-y-4">
73
88
  <nav className="flex gap-1 border-b" role="tablist">
74
89
  <button
90
+ data-cv-tooltip={labels.selectTab} aria-label={labels.selectTab}
75
91
  type="button"
76
92
  role="tab"
77
93
  aria-selected={tab === TEMPLATE_SETTINGS_TAB.SELECT}
@@ -85,6 +101,7 @@ export function WhatsAppTemplatesSettings({
85
101
  que não leva a nada. */}
86
102
  {create ? (
87
103
  <button
104
+ data-cv-tooltip={labels.createTab} aria-label={labels.createTab}
88
105
  type="button"
89
106
  role="tab"
90
107
  aria-selected={tab === TEMPLATE_SETTINGS_TAB.CREATE}
@@ -97,7 +114,10 @@ export function WhatsAppTemplatesSettings({
97
114
  </nav>
98
115
 
99
116
  {tab === TEMPLATE_SETTINGS_TAB.SELECT ? (
100
- <WhatsAppTemplateSettingsForm {...settingsProps} />
117
+ <div className="space-y-6">
118
+ <WhatsAppTemplateSettingsForm {...settingsProps} {...(settingsLabels ? { labels: settingsLabels } : {})} />
119
+ {extraRoleForms}
120
+ </div>
101
121
  ) : create ? (
102
122
  <WhatsAppCreateTemplateForm {...create} />
103
123
  ) : null}