@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.
@@ -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>