@adatechnology/conversations-ui 0.1.0-rc.24 → 0.1.0-rc.26
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/flows/index.d.ts +128 -2
- package/dist/flows/index.js +1085 -6
- package/dist/index.d.ts +220 -4
- package/dist/index.js +1237 -329
- package/dist/preview/index.d.ts +2 -2
- package/dist/styles.css +82 -0
- package/dist/{types-C6A_9edv.d.ts → types-BfINicc-.d.ts} +36 -1
- package/package.json +2 -2
- package/src/documents/DocumentsWorkspace.tsx +489 -0
- package/src/documents/index.ts +8 -0
- package/src/documents/labels.ts +88 -0
- package/src/flows/FlowNodeCard.tsx +12 -3
- package/src/flows/FlowNodePanel.tsx +15 -2
- package/src/flows/FlowsWorkspace.tsx +1254 -0
- package/src/flows/index.ts +3 -1
- package/src/flows/labels.ts +132 -0
- package/src/hooks/useUrlFilterState.ts +107 -0
- package/src/index.ts +30 -0
- package/src/listing/index.tsx +198 -0
- package/src/providers/types.ts +36 -1
- package/src/settings/MessagesWorkspace.tsx +465 -0
- package/src/settings/WhatsAppTemplatesSettings.tsx +7 -1
- package/src/styles.css +70 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vocabulário da biblioteca de arquivos. Sobrescrevível campo a campo: o produto troca "cliente"
|
|
3
|
+
* por "lead" ou o idioma inteiro sem manter uma cópia da tela para isso.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface DocumentsWorkspaceLabels {
|
|
7
|
+
readonly title: string
|
|
8
|
+
readonly subtitle: (total: number) => string
|
|
9
|
+
readonly searchPlaceholder: string
|
|
10
|
+
readonly empty: string
|
|
11
|
+
readonly noResults: string
|
|
12
|
+
readonly loading: string
|
|
13
|
+
readonly failure: string
|
|
14
|
+
readonly view: string
|
|
15
|
+
readonly download: string
|
|
16
|
+
readonly remove: string
|
|
17
|
+
readonly removeConfirm: (filename: string) => string
|
|
18
|
+
readonly openConversation: string
|
|
19
|
+
readonly sourceFilter: string
|
|
20
|
+
readonly categoryFilter: string
|
|
21
|
+
readonly startDate: string
|
|
22
|
+
readonly endDate: string
|
|
23
|
+
readonly clearFilters: string
|
|
24
|
+
readonly selectAllPage: string
|
|
25
|
+
readonly selectRow: string
|
|
26
|
+
readonly bulkSelected: (count: number) => string
|
|
27
|
+
readonly bulkClear: string
|
|
28
|
+
readonly bulkDownloadZip: string
|
|
29
|
+
readonly bulkRemove: (count: number) => string
|
|
30
|
+
readonly bulkRemoveConfirm: (count: number) => string
|
|
31
|
+
readonly columnFilename: string
|
|
32
|
+
readonly columnContact: string
|
|
33
|
+
readonly columnType: string
|
|
34
|
+
readonly columnSize: string
|
|
35
|
+
readonly columnSource: string
|
|
36
|
+
readonly columnDate: string
|
|
37
|
+
readonly columnActions: string
|
|
38
|
+
readonly sourceLabels: Readonly<Record<string, string>>
|
|
39
|
+
readonly categoryLabels: Readonly<Record<string, string>>
|
|
40
|
+
readonly show: string
|
|
41
|
+
readonly perPage: string
|
|
42
|
+
readonly total: (count: number) => string
|
|
43
|
+
readonly page: (current: number, last: number) => string
|
|
44
|
+
readonly previousPage: string
|
|
45
|
+
readonly nextPage: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const DEFAULT_DOCUMENTS_WORKSPACE_LABELS: DocumentsWorkspaceLabels = {
|
|
49
|
+
title: 'Documentos',
|
|
50
|
+
subtitle: (total) => `${total} arquivo${total === 1 ? '' : 's'} trocado${total === 1 ? '' : 's'}`,
|
|
51
|
+
searchPlaceholder: 'Buscar por nome do arquivo ou telefone',
|
|
52
|
+
empty: 'Nenhum arquivo trocado ainda.',
|
|
53
|
+
noResults: 'Nenhum arquivo encontrado para os filtros aplicados.',
|
|
54
|
+
loading: 'Carregando arquivos…',
|
|
55
|
+
failure: 'Não foi possível carregar os arquivos.',
|
|
56
|
+
view: 'Visualizar',
|
|
57
|
+
download: 'Baixar',
|
|
58
|
+
remove: 'Excluir',
|
|
59
|
+
removeConfirm: (filename) => `Excluir "${filename}"?`,
|
|
60
|
+
openConversation: 'Abrir conversa',
|
|
61
|
+
sourceFilter: 'Origem',
|
|
62
|
+
categoryFilter: 'Tipo',
|
|
63
|
+
startDate: 'De',
|
|
64
|
+
endDate: 'Até',
|
|
65
|
+
clearFilters: 'Limpar filtros',
|
|
66
|
+
selectAllPage: 'Selecionar todos desta página',
|
|
67
|
+
selectRow: 'Selecionar arquivo',
|
|
68
|
+
bulkSelected: (count) => `${count} selecionado${count === 1 ? '' : 's'}`,
|
|
69
|
+
bulkClear: 'Limpar seleção',
|
|
70
|
+
bulkDownloadZip: 'Baixar em zip',
|
|
71
|
+
bulkRemove: (count) => `Excluir ${count}`,
|
|
72
|
+
bulkRemoveConfirm: (count) => `Excluir ${count} arquivo${count === 1 ? '' : 's'}?`,
|
|
73
|
+
columnFilename: 'Arquivo',
|
|
74
|
+
columnContact: 'Contato',
|
|
75
|
+
columnType: 'Tipo',
|
|
76
|
+
columnSize: 'Tamanho',
|
|
77
|
+
columnSource: 'Origem',
|
|
78
|
+
columnDate: 'Recebido em',
|
|
79
|
+
columnActions: 'Ações',
|
|
80
|
+
sourceLabels: { customer: 'Cliente', agent: 'Atendente', bot: 'Bot' },
|
|
81
|
+
categoryLabels: { document: 'Documento', image: 'Imagem', audio: 'Áudio', video: 'Vídeo' },
|
|
82
|
+
show: 'Mostrar',
|
|
83
|
+
perPage: 'por página',
|
|
84
|
+
total: (count) => `${count} no total`,
|
|
85
|
+
page: (current, last) => `${current} / ${last}`,
|
|
86
|
+
previousPage: 'Página anterior',
|
|
87
|
+
nextPage: 'Próxima página',
|
|
88
|
+
}
|
|
@@ -43,6 +43,9 @@ const DEFAULT_ACTION_KIND_ICON: Record<string, LucideIcon> = {
|
|
|
43
43
|
// que só faz sentido para quem escreveu o interpretador do fluxo, não para quem está editando.
|
|
44
44
|
export function nodeLabel(node: FlowNodeData | undefined, labels: FlowEditorLabels): string {
|
|
45
45
|
if (!node) return '—'
|
|
46
|
+
// Apelido dado no editor manda sobre qualquer texto derivado: dois nós de mesma ação ficam
|
|
47
|
+
// idênticos no card sem ele, e é exatamente para desempatá-los que o campo existe.
|
|
48
|
+
if (node.label) return node.label
|
|
46
49
|
if (node.type === 'action') {
|
|
47
50
|
const actionKindLabel = node.actionKind ? labels.actionKindLabels[node.actionKind] : undefined
|
|
48
51
|
// Sempre retorna string: um actionKind futuro sem label mapeado cai no próprio valor bruto
|
|
@@ -62,6 +65,12 @@ export type FlowNodeCardData = {
|
|
|
62
65
|
liveCount: number
|
|
63
66
|
isStart: boolean
|
|
64
67
|
isSelected: boolean
|
|
68
|
+
/**
|
|
69
|
+
* Nó sem nenhuma ligação de entrada — o bot nunca chega nele. Contorno tracejado e pulso: sem
|
|
70
|
+
* isso, desconectar um fio ou criar um card solto passa despercebido até o fluxo quebrar em
|
|
71
|
+
* produção.
|
|
72
|
+
*/
|
|
73
|
+
isDetached?: boolean
|
|
65
74
|
issues: GraphIssue[]
|
|
66
75
|
labels: FlowEditorLabels
|
|
67
76
|
actionKindIcons?: Record<string, LucideIcon>
|
|
@@ -112,7 +121,7 @@ function SourceRow({ label, isDefault, handleId }: { label: string; isDefault: b
|
|
|
112
121
|
}
|
|
113
122
|
|
|
114
123
|
export function FlowNodeCard({ data }: NodeProps) {
|
|
115
|
-
const { node, liveCount, isStart, isSelected, issues, labels, actionKindIcons, onSelect } =
|
|
124
|
+
const { node, liveCount, isStart, isSelected, isDetached, issues, labels, actionKindIcons, onSelect } =
|
|
116
125
|
data as unknown as FlowNodeCardData
|
|
117
126
|
const label = nodeLabel(node, labels)
|
|
118
127
|
const iconMap = { ...DEFAULT_ACTION_KIND_ICON, ...actionKindIcons }
|
|
@@ -126,8 +135,8 @@ export function FlowNodeCard({ data }: NodeProps) {
|
|
|
126
135
|
|
|
127
136
|
return (
|
|
128
137
|
<div
|
|
129
|
-
title={label}
|
|
130
|
-
className={`relative rounded-lg border-2 px-3 py-2 w-60 cursor-pointer shadow-sm hover:shadow-md transition-shadow ${NODE_TYPE_COLOR[node.type]} ${isSelected ? 'ring-2 ring-blue-500 ring-offset-2 dark:ring-offset-gray-900' : ''}`}
|
|
138
|
+
title={isDetached ? labels.detachedNodeTooltip : label}
|
|
139
|
+
className={`relative rounded-lg border-2 px-3 py-2 w-60 cursor-pointer shadow-sm hover:shadow-md transition-shadow ${NODE_TYPE_COLOR[node.type]} ${isSelected ? 'ring-2 ring-blue-500 ring-offset-2 dark:ring-offset-gray-900' : ''} ${isDetached ? 'border-dashed !border-amber-400 animate-pulse' : ''}`}
|
|
131
140
|
onClick={() => onSelect(node.id)}
|
|
132
141
|
>
|
|
133
142
|
<Handle type="target" position={Position.Top} id="target" className="!bg-gray-400 dark:!bg-gray-500" />
|
|
@@ -80,7 +80,7 @@ export interface FlowNodePanelProps {
|
|
|
80
80
|
* URL assinada são dele, e o painel não tem como buscar nada. Ausente, o nó continua editável —
|
|
81
81
|
* só não dá para anexar por aqui.
|
|
82
82
|
*/
|
|
83
|
-
renderMediaPicker?: (node: FlowNodeData) => ReactNode
|
|
83
|
+
renderMediaPicker?: (node: FlowNodeData, graph: FlowGraphData) => ReactNode
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
// Paridade com financiamento-imobiliario-bot/apps/web/src/components/flows/FlowNodePanel.tsx —
|
|
@@ -211,6 +211,19 @@ export function FlowNodePanel({
|
|
|
211
211
|
</p>
|
|
212
212
|
)}
|
|
213
213
|
|
|
214
|
+
<div>
|
|
215
|
+
<label className="text-xs font-medium text-gray-500 dark:text-gray-400">
|
|
216
|
+
{labels.nodePanel.nodeName}
|
|
217
|
+
</label>
|
|
218
|
+
<input
|
|
219
|
+
value={draft.label ?? ''}
|
|
220
|
+
placeholder={labels.nodePanel.nodeNamePlaceholder}
|
|
221
|
+
onChange={(event) => setDraft((prev) => ({ ...prev, label: event.target.value || undefined }))}
|
|
222
|
+
className={`w-full mt-1 ${INPUT_CLASSNAME}`}
|
|
223
|
+
/>
|
|
224
|
+
<p className="text-[11px] text-gray-400 dark:text-gray-500 mt-1">{labels.nodePanel.nodeNameHint}</p>
|
|
225
|
+
</div>
|
|
226
|
+
|
|
214
227
|
{draft.contextKey && (
|
|
215
228
|
<div>
|
|
216
229
|
<label className="text-xs font-medium text-gray-500 dark:text-gray-400">
|
|
@@ -322,7 +335,7 @@ export function FlowNodePanel({
|
|
|
322
335
|
<div>
|
|
323
336
|
<label className="text-xs font-medium text-gray-500 dark:text-gray-400">{labels.nodePanel.media}</label>
|
|
324
337
|
<div className="mt-1">
|
|
325
|
-
{renderMediaPicker?.(node) ?? (
|
|
338
|
+
{renderMediaPicker?.(node, graph) ?? (
|
|
326
339
|
<p className="text-xs text-gray-400">{labels.nodePanel.mediaUnavailable}</p>
|
|
327
340
|
)}
|
|
328
341
|
</div>
|