@adatechnology/conversations-ui 0.1.0-rc.26 → 0.1.0-rc.28
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-DXPSPUWF.js +110 -0
- package/dist/{chunk-TV4OQRGH.js → chunk-UZJBYD5O.js} +123 -45
- package/dist/{chunk-2AYDBWNE.js → chunk-WCBDXZ3X.js} +13 -3
- package/dist/flows/index.d.ts +75 -2
- package/dist/flows/index.js +176 -95
- package/dist/index.d.ts +37 -10
- package/dist/index.js +694 -297
- package/dist/preview/index.js +3 -3
- package/dist/styles.css +119 -6
- package/package.json +2 -2
- package/src/AudioPlayer.tsx +8 -0
- package/src/AudioRecorderButton.test.tsx +3 -3
- package/src/AudioRecorderButton.tsx +3 -3
- package/src/AudioTranscription.tsx +4 -1
- package/src/Avatar.tsx +1 -1
- package/src/ConversationContextPanel.tsx +2 -2
- package/src/ConversationDocumentsPanel.tsx +11 -6
- package/src/ConversationHeader.test.tsx +66 -0
- package/src/ConversationHeader.tsx +147 -47
- package/src/ConversationListItem.tsx +3 -2
- package/src/ConversationRow.tsx +31 -7
- package/src/DocumentsLibrary.tsx +9 -4
- package/src/EmojiPicker.tsx +2 -1
- package/src/InteractiveMessage.tsx +3 -0
- package/src/Lightbox.tsx +1 -1
- package/src/MediaRenderer.tsx +2 -0
- package/src/MessageBubble.test.tsx +41 -0
- package/src/MessageBubble.tsx +27 -8
- package/src/MessageComposer.tsx +11 -8
- package/src/RichMessageComposer.test.tsx +42 -12
- package/src/RichMessageComposer.tsx +216 -45
- 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/WhatsAppMessageEditor.tsx +4 -4
- package/src/WindowExpiredNotice.tsx +12 -4
- package/src/buildOutput.test.ts +79 -0
- package/src/composer.constant.ts +33 -0
- package/src/conversationWindow.ts +7 -5
- package/src/documents/DocumentsWorkspace.tsx +10 -6
- package/src/flows/FlowGroupHeader.tsx +2 -2
- package/src/flows/FlowMapNode.tsx +2 -1
- package/src/flows/FlowNodeCard.tsx +3 -3
- package/src/flows/FlowNodePanel.tsx +6 -5
- package/src/flows/FlowPalette.tsx +7 -1
- package/src/flows/FlowPortalNode.tsx +1 -1
- package/src/flows/FlowsWorkspace.tsx +69 -102
- package/src/flows/flowEditorOps.test.ts +241 -0
- package/src/flows/flowEditorOps.ts +177 -0
- package/src/flows/flowGraph.ts +1 -1
- package/src/flows/index.ts +16 -0
- package/src/flows/labels.ts +4 -0
- package/src/flows/workspaceContract.test.ts +95 -0
- package/src/hooks/useContainerWidth.ts +35 -0
- package/src/hooks/useConversationRealtime.ts +10 -8
- package/src/icon.constant.ts +12 -0
- package/src/index.ts +1 -0
- package/src/lib/composer-formatting.test.ts +78 -0
- package/src/lib/composer-formatting.ts +145 -0
- package/src/lib/whatsapp-formatting.test.tsx +37 -0
- package/src/lib/whatsapp-formatting.tsx +28 -3
- package/src/listing/index.tsx +5 -1
- package/src/pagination.constant.ts +10 -0
- package/src/preview/ConversationSimulatorPanel.tsx +1 -1
- package/src/providers/ConversationsProvider.tsx +8 -6
- package/src/settings/MessagesWorkspace.tsx +3 -0
- package/src/settings/TopicsForm.tsx +2 -0
- package/src/settings/TranscriptionSettingsForm.test.tsx +1 -1
- package/src/settings/TranscriptionSettingsForm.tsx +1 -0
- package/src/settings/WelcomeFarewellForm.tsx +1 -0
- package/src/settings/WhatsAppCreateTemplateForm.tsx +1 -0
- package/src/settings/WhatsAppTemplateSettingsForm.tsx +5 -2
- package/src/settings/WhatsAppTemplatesSettings.tsx +2 -0
- package/src/styles.css +100 -4
- package/src/workspace/BulkTemplateModal.tsx +4 -2
- package/src/workspace/ConversationPane.tsx +49 -19
- package/src/workspace/ConversationsInboxList.tsx +17 -8
- package/src/workspace/ConversationsWorkspace.tsx +24 -16
- package/src/workspace/useConversationsInbox.ts +5 -2
package/dist/flows/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TooltipLayer
|
|
3
|
+
} from "../chunk-DXPSPUWF.js";
|
|
1
4
|
import {
|
|
2
5
|
parseWhatsAppFormatting,
|
|
3
6
|
useIsDarkTheme
|
|
4
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-WCBDXZ3X.js";
|
|
5
8
|
|
|
6
9
|
// src/flows/FlowNodeCard.tsx
|
|
7
10
|
import { Handle, Position } from "@xyflow/react";
|
|
@@ -99,7 +102,7 @@ function FlowNodeCard({ data }) {
|
|
|
99
102
|
return /* @__PURE__ */ jsxs(
|
|
100
103
|
"div",
|
|
101
104
|
{
|
|
102
|
-
|
|
105
|
+
"data-cv-tooltip": isDetached ? labels.detachedNodeTooltip : label,
|
|
103
106
|
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" : ""}`,
|
|
104
107
|
onClick: () => onSelect(node.id),
|
|
105
108
|
children: [
|
|
@@ -107,7 +110,7 @@ function FlowNodeCard({ data }) {
|
|
|
107
110
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
108
111
|
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 uppercase tracking-wide font-semibold text-gray-500 dark:text-gray-400 text-xs", children: [
|
|
109
112
|
/* @__PURE__ */ jsx(Icon, { size: 12, strokeWidth: 2.5 }),
|
|
110
|
-
isStart && /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-emerald-500",
|
|
113
|
+
isStart && /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-emerald-500", "data-cv-tooltip": labels.startNodeTooltip }),
|
|
111
114
|
labels.legend[node.type]
|
|
112
115
|
] }),
|
|
113
116
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
@@ -116,7 +119,7 @@ function FlowNodeCard({ data }) {
|
|
|
116
119
|
liveCount > 0 && /* @__PURE__ */ jsx(
|
|
117
120
|
"span",
|
|
118
121
|
{
|
|
119
|
-
|
|
122
|
+
"data-cv-tooltip": labels.liveCountTooltip(liveCount),
|
|
120
123
|
className: "font-bold bg-blue-600 text-white rounded-full px-1.5 py-0.5 text-xs",
|
|
121
124
|
children: liveCount
|
|
122
125
|
}
|
|
@@ -181,6 +184,8 @@ var DEFAULT_FLOW_EDITOR_LABELS = {
|
|
|
181
184
|
question: "Texto da pergunta",
|
|
182
185
|
options: "Op\xE7\xF5es (choice)",
|
|
183
186
|
addOption: "Adicionar op\xE7\xE3o",
|
|
187
|
+
removeOption: "Remover op\xE7\xE3o",
|
|
188
|
+
close: "Fechar painel",
|
|
184
189
|
optionId: "Valor",
|
|
185
190
|
optionLabel: "Texto exibido",
|
|
186
191
|
next: "Pr\xF3ximo n\xF3",
|
|
@@ -323,12 +328,14 @@ function FlowMapNode({ data }) {
|
|
|
323
328
|
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1.5 text-emerald-700 dark:text-emerald-300", children: [
|
|
324
329
|
/* @__PURE__ */ jsx2(GitBranch2, { size: 14 }),
|
|
325
330
|
/* @__PURE__ */ jsx2("span", { className: "text-sm font-semibold truncate", children: label }),
|
|
326
|
-
isRoot && /* @__PURE__ */ jsx2("span", { className: "h-1.5 w-1.5 rounded-full bg-emerald-500 shrink-0",
|
|
331
|
+
isRoot && /* @__PURE__ */ jsx2("span", { className: "h-1.5 w-1.5 rounded-full bg-emerald-500 shrink-0", "data-cv-tooltip": labels.startNodeTooltip })
|
|
327
332
|
] }),
|
|
328
333
|
/* @__PURE__ */ jsx2("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: labels.flowMap.nodeCount(nodeCount) }),
|
|
329
334
|
/* @__PURE__ */ jsxs2(
|
|
330
335
|
"button",
|
|
331
336
|
{
|
|
337
|
+
"data-cv-tooltip": labels.flowMap.openFlow,
|
|
338
|
+
"aria-label": labels.flowMap.openFlow,
|
|
332
339
|
type: "button",
|
|
333
340
|
onClick: onOpen,
|
|
334
341
|
className: "mt-2 inline-flex items-center gap-1 text-xs font-medium text-blue-600 dark:text-blue-400 hover:underline",
|
|
@@ -678,7 +685,8 @@ function FlowGroupHeader({ data }) {
|
|
|
678
685
|
{
|
|
679
686
|
type: "button",
|
|
680
687
|
onClick: onFocus,
|
|
681
|
-
|
|
688
|
+
"data-cv-tooltip": labels.flowGroup.focus,
|
|
689
|
+
"aria-label": labels.flowGroup.focus,
|
|
682
690
|
className: "hover:text-blue-600 dark:hover:text-blue-400",
|
|
683
691
|
children: /* @__PURE__ */ jsx5(Maximize22, { size: 12 })
|
|
684
692
|
}
|
|
@@ -688,7 +696,8 @@ function FlowGroupHeader({ data }) {
|
|
|
688
696
|
{
|
|
689
697
|
type: "button",
|
|
690
698
|
onClick: onClose,
|
|
691
|
-
|
|
699
|
+
"data-cv-tooltip": labels.flowGroup.close,
|
|
700
|
+
"aria-label": labels.flowGroup.close,
|
|
692
701
|
className: "hover:text-red-600 dark:hover:text-red-400",
|
|
693
702
|
children: /* @__PURE__ */ jsx5(X, { size: 12 })
|
|
694
703
|
}
|
|
@@ -707,7 +716,8 @@ function FlowPortalNode({ data }) {
|
|
|
707
716
|
"button",
|
|
708
717
|
{
|
|
709
718
|
type: "button",
|
|
710
|
-
|
|
719
|
+
"data-cv-tooltip": labels.crossFlowPortal.tooltip,
|
|
720
|
+
"aria-label": labels.crossFlowPortal.tooltip,
|
|
711
721
|
onClick: onNavigate,
|
|
712
722
|
className: "relative flex items-center gap-1.5 rounded-full border-2 border-dashed border-cyan-400 dark:border-cyan-600 bg-cyan-50 dark:bg-cyan-950/40 px-3 py-1.5 text-xs font-medium text-cyan-700 dark:text-cyan-300 hover:bg-cyan-100 dark:hover:bg-cyan-950/70 transition-colors cursor-pointer",
|
|
713
723
|
children: [
|
|
@@ -752,6 +762,8 @@ function FlowPalette({ onAdd, labels: labelsOverride, actionOptions }) {
|
|
|
752
762
|
/* @__PURE__ */ jsxs6(
|
|
753
763
|
"button",
|
|
754
764
|
{
|
|
765
|
+
"data-cv-tooltip": labels.palette.title,
|
|
766
|
+
"aria-label": labels.palette.title,
|
|
755
767
|
onClick: () => setOpen((v) => !v),
|
|
756
768
|
className: "inline-flex items-center gap-1.5 rounded-lg bg-blue-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-blue-700",
|
|
757
769
|
children: [
|
|
@@ -766,6 +778,8 @@ function FlowPalette({ onAdd, labels: labelsOverride, actionOptions }) {
|
|
|
766
778
|
/* @__PURE__ */ jsxs6(
|
|
767
779
|
"button",
|
|
768
780
|
{
|
|
781
|
+
"data-cv-tooltip": labels.palette.question,
|
|
782
|
+
"aria-label": labels.palette.question,
|
|
769
783
|
onMouseEnter: () => setSubmenu("question"),
|
|
770
784
|
onClick: () => setSubmenu(submenu === "question" ? null : "question"),
|
|
771
785
|
className: "w-full flex items-center justify-between gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700",
|
|
@@ -782,6 +796,8 @@ function FlowPalette({ onAdd, labels: labelsOverride, actionOptions }) {
|
|
|
782
796
|
submenu === "question" && /* @__PURE__ */ jsx7("div", { className: "absolute left-full top-0 ml-1 w-56 rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 shadow-lg py-1", children: QUESTION_TYPES.map((qt) => /* @__PURE__ */ jsx7(
|
|
783
797
|
"button",
|
|
784
798
|
{
|
|
799
|
+
"data-cv-tooltip": labels.questionTypeLabels[qt],
|
|
800
|
+
"aria-label": labels.questionTypeLabels[qt],
|
|
785
801
|
onClick: () => select({ kind: "question", questionType: qt }),
|
|
786
802
|
className: "w-full text-left px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700",
|
|
787
803
|
children: labels.questionTypeLabels[qt]
|
|
@@ -792,6 +808,8 @@ function FlowPalette({ onAdd, labels: labelsOverride, actionOptions }) {
|
|
|
792
808
|
/* @__PURE__ */ jsxs6(
|
|
793
809
|
"button",
|
|
794
810
|
{
|
|
811
|
+
"data-cv-tooltip": labels.palette.decision,
|
|
812
|
+
"aria-label": labels.palette.decision,
|
|
795
813
|
onMouseEnter: () => setSubmenu(null),
|
|
796
814
|
onClick: () => select({ kind: "decision" }),
|
|
797
815
|
className: "w-full flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700",
|
|
@@ -808,7 +826,8 @@ function FlowPalette({ onAdd, labels: labelsOverride, actionOptions }) {
|
|
|
808
826
|
onMouseEnter: () => setSubmenu(null),
|
|
809
827
|
onClick: () => select({ kind: "condition" }),
|
|
810
828
|
className: "w-full flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700",
|
|
811
|
-
|
|
829
|
+
"data-cv-tooltip": labels.palette.conditionHint,
|
|
830
|
+
"aria-label": labels.palette.conditionHint,
|
|
812
831
|
children: [
|
|
813
832
|
/* @__PURE__ */ jsx7(Diamond2, { size: 15, className: "text-cyan-500" }),
|
|
814
833
|
" ",
|
|
@@ -820,6 +839,8 @@ function FlowPalette({ onAdd, labels: labelsOverride, actionOptions }) {
|
|
|
820
839
|
/* @__PURE__ */ jsxs6(
|
|
821
840
|
"button",
|
|
822
841
|
{
|
|
842
|
+
"data-cv-tooltip": labels.palette.action,
|
|
843
|
+
"aria-label": labels.palette.action,
|
|
823
844
|
onMouseEnter: () => setSubmenu("action"),
|
|
824
845
|
onClick: () => setSubmenu(submenu === "action" ? null : "action"),
|
|
825
846
|
className: "w-full flex items-center justify-between gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700",
|
|
@@ -836,6 +857,8 @@ function FlowPalette({ onAdd, labels: labelsOverride, actionOptions }) {
|
|
|
836
857
|
submenu === "action" && /* @__PURE__ */ jsx7("div", { className: "absolute left-full top-0 ml-1 w-64 rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 shadow-lg py-1", children: resolvedActionOptions.map((option) => /* @__PURE__ */ jsx7(
|
|
837
858
|
"button",
|
|
838
859
|
{
|
|
860
|
+
"data-cv-tooltip": option.label,
|
|
861
|
+
"aria-label": option.label,
|
|
839
862
|
onClick: () => select({ kind: "action", actionKind: option.actionKind }),
|
|
840
863
|
className: "w-full text-left px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700",
|
|
841
864
|
children: option.label
|
|
@@ -1003,7 +1026,7 @@ function FlowNodePanel({
|
|
|
1003
1026
|
return /* @__PURE__ */ jsxs8("div", { className: "fixed inset-y-0 right-0 w-96 bg-white dark:bg-gray-800 border-l border-gray-200 dark:border-gray-700 shadow-xl z-50 flex flex-col", children: [
|
|
1004
1027
|
/* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between px-4 py-3 border-b border-gray-100 dark:border-gray-700", children: [
|
|
1005
1028
|
/* @__PURE__ */ jsx9("h3", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: labels.nodePanel.title }),
|
|
1006
|
-
/* @__PURE__ */ jsx9("button", { onClick: onClose, className: "text-gray-400 hover:text-gray-600", children: /* @__PURE__ */ jsx9(X2, { size: 18 }) })
|
|
1029
|
+
/* @__PURE__ */ jsx9("button", { "data-cv-tooltip": labels.nodePanel.close, "aria-label": labels.nodePanel.close, onClick: onClose, className: "text-gray-400 hover:text-gray-600", children: /* @__PURE__ */ jsx9(X2, { size: 18 }) })
|
|
1007
1030
|
] }),
|
|
1008
1031
|
/* @__PURE__ */ jsxs8("div", { className: "flex-1 overflow-y-auto p-4 space-y-4", children: [
|
|
1009
1032
|
nodeIssues.length > 0 && /* @__PURE__ */ jsx9("div", { className: "rounded-lg border border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-900/40 p-2.5 space-y-1.5", children: nodeIssues.map((issue, i) => /* @__PURE__ */ jsx9(IssueRow, { issue }, i)) }),
|
|
@@ -1124,7 +1147,7 @@ function FlowNodePanel({
|
|
|
1124
1147
|
(draft.questionType === "choice" || draft.type === "menu") && /* @__PURE__ */ jsxs8("div", { children: [
|
|
1125
1148
|
/* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between mb-1.5", children: [
|
|
1126
1149
|
/* @__PURE__ */ jsx9("label", { className: "text-xs font-medium text-gray-500 dark:text-gray-400", children: labels.nodePanel.options }),
|
|
1127
|
-
/* @__PURE__ */ jsxs8("button", { onClick: addOption, className: "text-xs text-blue-600 hover:underline flex items-center gap-1", children: [
|
|
1150
|
+
/* @__PURE__ */ jsxs8("button", { "data-cv-tooltip": labels.nodePanel.addOption, "aria-label": labels.nodePanel.addOption, onClick: addOption, className: "text-xs text-blue-600 hover:underline flex items-center gap-1", children: [
|
|
1128
1151
|
/* @__PURE__ */ jsx9(Plus2, { size: 12 }),
|
|
1129
1152
|
" ",
|
|
1130
1153
|
labels.nodePanel.addOption
|
|
@@ -1149,7 +1172,7 @@ function FlowNodePanel({
|
|
|
1149
1172
|
className: `flex-1 ${INPUT_CLASSNAME}`
|
|
1150
1173
|
}
|
|
1151
1174
|
),
|
|
1152
|
-
/* @__PURE__ */ jsx9("button", { onClick: () => removeOption(i), className: "text-gray-400 hover:text-red-600", children: /* @__PURE__ */ jsx9(Trash2, { size: 14 }) })
|
|
1175
|
+
/* @__PURE__ */ jsx9("button", { "data-cv-tooltip": labels.nodePanel.removeOption, "aria-label": labels.nodePanel.removeOption, onClick: () => removeOption(i), className: "text-gray-400 hover:text-red-600", children: /* @__PURE__ */ jsx9(Trash2, { size: 14 }) })
|
|
1153
1176
|
] }, i)) })
|
|
1154
1177
|
] }),
|
|
1155
1178
|
!isAction && /* @__PURE__ */ jsxs8("div", { className: "pt-2 border-t border-gray-100 dark:border-gray-700", children: [
|
|
@@ -1204,6 +1227,8 @@ function FlowNodePanel({
|
|
|
1204
1227
|
/* @__PURE__ */ jsxs8(
|
|
1205
1228
|
"button",
|
|
1206
1229
|
{
|
|
1230
|
+
"data-cv-tooltip": labels.nodePanel.save,
|
|
1231
|
+
"aria-label": labels.nodePanel.save,
|
|
1207
1232
|
onClick: () => onChange(draft),
|
|
1208
1233
|
className: "flex-1 inline-flex items-center justify-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700",
|
|
1209
1234
|
children: [
|
|
@@ -1219,12 +1244,13 @@ function FlowNodePanel({
|
|
|
1219
1244
|
onClick: () => {
|
|
1220
1245
|
if (window.confirm(labels.nodePanel.deleteConfirm)) onDelete(node.id);
|
|
1221
1246
|
},
|
|
1222
|
-
|
|
1247
|
+
"data-cv-tooltip": labels.nodePanel.delete,
|
|
1248
|
+
"aria-label": labels.nodePanel.delete,
|
|
1223
1249
|
className: "px-3 py-2 text-sm text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-950/30 rounded-lg",
|
|
1224
1250
|
children: /* @__PURE__ */ jsx9(Trash2, { size: 14 })
|
|
1225
1251
|
}
|
|
1226
1252
|
),
|
|
1227
|
-
/* @__PURE__ */ jsx9("button", { onClick: onClose, className: "px-4 py-2 text-sm text-gray-600 dark:text-gray-300 hover:underline", children: labels.nodePanel.cancel })
|
|
1253
|
+
/* @__PURE__ */ jsx9("button", { "data-cv-tooltip": labels.nodePanel.cancel, "aria-label": labels.nodePanel.cancel, onClick: onClose, className: "px-4 py-2 text-sm text-gray-600 dark:text-gray-300 hover:underline", children: labels.nodePanel.cancel })
|
|
1228
1254
|
] })
|
|
1229
1255
|
] });
|
|
1230
1256
|
}
|
|
@@ -1241,6 +1267,87 @@ import {
|
|
|
1241
1267
|
} from "@xyflow/react";
|
|
1242
1268
|
import "@xyflow/react/dist/style.css";
|
|
1243
1269
|
import { Plus as Plus3, Trash2 as Trash22, LayoutGrid, AlertTriangle as AlertTriangle3, AlertCircle as AlertCircle3, Save as Save2, Undo2, Map as MapIcon, Workflow } from "lucide-react";
|
|
1270
|
+
|
|
1271
|
+
// src/flows/flowEditorOps.ts
|
|
1272
|
+
var NAMESPACE_SEPARATOR = "::";
|
|
1273
|
+
function namespaceNodeId(flowKey, nodeId) {
|
|
1274
|
+
return `${flowKey}${NAMESPACE_SEPARATOR}${nodeId}`;
|
|
1275
|
+
}
|
|
1276
|
+
function parseNamespacedId(value) {
|
|
1277
|
+
const index = value.indexOf(NAMESPACE_SEPARATOR);
|
|
1278
|
+
if (index === -1) return { flowKey: "", nodeId: value };
|
|
1279
|
+
return { flowKey: value.slice(0, index), nodeId: value.slice(index + NAMESPACE_SEPARATOR.length) };
|
|
1280
|
+
}
|
|
1281
|
+
function removeNodeAndCleanRefs(nodes, removedId) {
|
|
1282
|
+
const remaining = {};
|
|
1283
|
+
for (const [id, node] of Object.entries(nodes)) {
|
|
1284
|
+
if (id === removedId) continue;
|
|
1285
|
+
remaining[id] = { ...node, next: cleanNext(node.next, removedId) };
|
|
1286
|
+
}
|
|
1287
|
+
return remaining;
|
|
1288
|
+
}
|
|
1289
|
+
function cleanNext(next, removedId) {
|
|
1290
|
+
if (next === void 0) return void 0;
|
|
1291
|
+
if (typeof next === "string") return next === removedId ? "" : next;
|
|
1292
|
+
const byAnswer = {};
|
|
1293
|
+
for (const [answer, target] of Object.entries(next.byAnswer ?? {})) {
|
|
1294
|
+
byAnswer[answer] = target === removedId ? "" : target;
|
|
1295
|
+
}
|
|
1296
|
+
return { byAnswer, default: next.default === removedId ? "" : next.default ?? "" };
|
|
1297
|
+
}
|
|
1298
|
+
function mergedFlowKeysFrom(rootKey, graphs) {
|
|
1299
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1300
|
+
const queue = [rootKey];
|
|
1301
|
+
while (queue.length > 0) {
|
|
1302
|
+
const key = queue.shift();
|
|
1303
|
+
if (visited.has(key) || !graphs[key]) continue;
|
|
1304
|
+
visited.add(key);
|
|
1305
|
+
for (const target of crossFlowTargetsOf(graphs[key])) {
|
|
1306
|
+
if (!visited.has(target) && graphs[target]) queue.push(target);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
return [...visited];
|
|
1310
|
+
}
|
|
1311
|
+
function resolveConnection(params) {
|
|
1312
|
+
const { source, target, sourceHandle } = params.connection;
|
|
1313
|
+
if (!source || !target || source === target) return void 0;
|
|
1314
|
+
const sourceRef = parseNamespacedId(source);
|
|
1315
|
+
const targetRef = parseNamespacedId(target);
|
|
1316
|
+
let targetValue;
|
|
1317
|
+
if (targetRef.flowKey === sourceRef.flowKey) {
|
|
1318
|
+
targetValue = targetRef.nodeId;
|
|
1319
|
+
} else {
|
|
1320
|
+
const targetGraph = params.graphs[targetRef.flowKey];
|
|
1321
|
+
if (!targetGraph || targetGraph.startNodeId !== targetRef.nodeId) return void 0;
|
|
1322
|
+
targetValue = `${CROSS_FLOW_PREFIX}${targetRef.flowKey}`;
|
|
1323
|
+
}
|
|
1324
|
+
return {
|
|
1325
|
+
flowKey: sourceRef.flowKey,
|
|
1326
|
+
nodeId: sourceRef.nodeId,
|
|
1327
|
+
handle: sourceHandle ?? "next",
|
|
1328
|
+
targetValue
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
function applyConnection(node, resolved) {
|
|
1332
|
+
const currentNext = typeof node.next === "object" && node.next ? node.next : void 0;
|
|
1333
|
+
if (resolved.handle === "next") return { ...node, next: resolved.targetValue };
|
|
1334
|
+
if (resolved.handle === "__default") {
|
|
1335
|
+
return { ...node, next: { byAnswer: currentNext?.byAnswer ?? {}, default: resolved.targetValue } };
|
|
1336
|
+
}
|
|
1337
|
+
return {
|
|
1338
|
+
...node,
|
|
1339
|
+
next: {
|
|
1340
|
+
byAnswer: { ...currentNext?.byAnswer ?? {}, [resolved.handle]: resolved.targetValue },
|
|
1341
|
+
default: currentNext?.default ?? ""
|
|
1342
|
+
}
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
function isGraphDirty(working, published) {
|
|
1346
|
+
if (!working || !published) return false;
|
|
1347
|
+
return JSON.stringify(working) !== JSON.stringify(published);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
// src/flows/FlowsWorkspace.tsx
|
|
1244
1351
|
import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1245
1352
|
var RF_NODE_TYPES = {
|
|
1246
1353
|
...flowNodeTypes,
|
|
@@ -1251,7 +1358,6 @@ var RF_NODE_TYPES = {
|
|
|
1251
1358
|
var CHAIN_FRAME_PADDING = 36;
|
|
1252
1359
|
var COLUMN_GAP = 360;
|
|
1253
1360
|
var ROW_GAP = 40;
|
|
1254
|
-
var NS_SEP = "::";
|
|
1255
1361
|
var FLOW_KEY_PATTERN = /^[a-z0-9_]{2,40}$/;
|
|
1256
1362
|
var EDGE_COLOR_LINEAR = "#94a3b8";
|
|
1257
1363
|
var EDGE_COLOR_BRANCH = "#8b5cf6";
|
|
@@ -1263,25 +1369,8 @@ var BACKGROUND_COLOR_DARK2 = "#334155";
|
|
|
1263
1369
|
function portalNodeId(sourceId, target) {
|
|
1264
1370
|
return `__portal__${sourceId}__${target}`;
|
|
1265
1371
|
}
|
|
1266
|
-
function ns(flowKey, nodeId) {
|
|
1267
|
-
return `${flowKey}${NS_SEP}${nodeId}`;
|
|
1268
|
-
}
|
|
1269
|
-
function parseNs(id) {
|
|
1270
|
-
const index = id.indexOf(NS_SEP);
|
|
1271
|
-
return index === -1 ? { flowKey: "", nodeId: id } : { flowKey: id.slice(0, index), nodeId: id.slice(index + NS_SEP.length) };
|
|
1272
|
-
}
|
|
1273
1372
|
function autoMergeAll(rootKey, graphsSource) {
|
|
1274
|
-
|
|
1275
|
-
const queue = [rootKey];
|
|
1276
|
-
while (queue.length > 0) {
|
|
1277
|
-
const key = queue.shift();
|
|
1278
|
-
if (visited.has(key) || !graphsSource[key]) continue;
|
|
1279
|
-
visited.add(key);
|
|
1280
|
-
for (const target of crossFlowTargetsOf(graphsSource[key])) {
|
|
1281
|
-
if (!visited.has(target) && graphsSource[target]) queue.push(target);
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
return [...visited].map((key) => ({ key, offset: { x: 0, y: 0 } }));
|
|
1373
|
+
return mergedFlowKeysFrom(rootKey, graphsSource).map((key) => ({ key, offset: { x: 0, y: 0 } }));
|
|
1285
1374
|
}
|
|
1286
1375
|
function computeMergedLayout(openFlows, workingGraphs, primaryFlowKey) {
|
|
1287
1376
|
const openKeys = new Set(openFlows.map((flow) => flow.key));
|
|
@@ -1289,7 +1378,7 @@ function computeMergedLayout(openFlows, workingGraphs, primaryFlowKey) {
|
|
|
1289
1378
|
for (const { key } of openFlows) {
|
|
1290
1379
|
const graph = workingGraphs[key];
|
|
1291
1380
|
if (!graph) continue;
|
|
1292
|
-
for (const node of Object.values(graph.nodes)) nodeByNsId.set(
|
|
1381
|
+
for (const node of Object.values(graph.nodes)) nodeByNsId.set(namespaceNodeId(key, node.id), node);
|
|
1293
1382
|
}
|
|
1294
1383
|
function forwardEdges(flowKey, node) {
|
|
1295
1384
|
const result = [];
|
|
@@ -1297,16 +1386,16 @@ function computeMergedLayout(openFlows, workingGraphs, primaryFlowKey) {
|
|
|
1297
1386
|
if (isCrossFlowTarget(target)) {
|
|
1298
1387
|
const targetFlowKey = crossFlowKey(target);
|
|
1299
1388
|
const targetGraph = openKeys.has(targetFlowKey) ? workingGraphs[targetFlowKey] : void 0;
|
|
1300
|
-
if (targetGraph) result.push(
|
|
1389
|
+
if (targetGraph) result.push(namespaceNodeId(targetFlowKey, targetGraph.startNodeId));
|
|
1301
1390
|
} else if (workingGraphs[flowKey]?.nodes[target]) {
|
|
1302
|
-
result.push(
|
|
1391
|
+
result.push(namespaceNodeId(flowKey, target));
|
|
1303
1392
|
}
|
|
1304
1393
|
}
|
|
1305
1394
|
return result;
|
|
1306
1395
|
}
|
|
1307
1396
|
const rank = /* @__PURE__ */ new Map();
|
|
1308
1397
|
const primaryGraph = workingGraphs[primaryFlowKey];
|
|
1309
|
-
const rootId = primaryGraph ?
|
|
1398
|
+
const rootId = primaryGraph ? namespaceNodeId(primaryFlowKey, primaryGraph.startNodeId) : void 0;
|
|
1310
1399
|
if (rootId && nodeByNsId.has(rootId)) {
|
|
1311
1400
|
rank.set(rootId, 0);
|
|
1312
1401
|
const queue = [rootId];
|
|
@@ -1314,7 +1403,7 @@ function computeMergedLayout(openFlows, workingGraphs, primaryFlowKey) {
|
|
|
1314
1403
|
const id = queue.shift();
|
|
1315
1404
|
const node = nodeByNsId.get(id);
|
|
1316
1405
|
if (!node) continue;
|
|
1317
|
-
for (const nextId of forwardEdges(
|
|
1406
|
+
for (const nextId of forwardEdges(parseNamespacedId(id).flowKey, node)) {
|
|
1318
1407
|
if (!rank.has(nextId)) {
|
|
1319
1408
|
rank.set(nextId, rank.get(id) + 1);
|
|
1320
1409
|
queue.push(nextId);
|
|
@@ -1361,6 +1450,7 @@ function buildAllEdges(openFlows, workingGraphs, rootFlowKey, livePositions) {
|
|
|
1361
1450
|
for (const [id, node] of Object.entries(graph.nodes)) {
|
|
1362
1451
|
const isLive = (liveCounts[id] ?? 0) > 0;
|
|
1363
1452
|
for (const { target, optionId, isDefault } of targetsOf(node)) {
|
|
1453
|
+
if (!target) continue;
|
|
1364
1454
|
const crossFlow = isCrossFlowTarget(target);
|
|
1365
1455
|
let targetFlowKey = flowKey;
|
|
1366
1456
|
let rawTargetId = target;
|
|
@@ -1374,8 +1464,8 @@ function buildAllEdges(openFlows, workingGraphs, rootFlowKey, livePositions) {
|
|
|
1374
1464
|
rawTargetId = portalNodeId(id, target);
|
|
1375
1465
|
}
|
|
1376
1466
|
}
|
|
1377
|
-
const source =
|
|
1378
|
-
const edgeTarget =
|
|
1467
|
+
const source = namespaceNodeId(flowKey, id);
|
|
1468
|
+
const edgeTarget = namespaceNodeId(targetFlowKey, rawTargetId);
|
|
1379
1469
|
if (optionId === void 0 && !isDefault) {
|
|
1380
1470
|
const color = crossFlow ? EDGE_COLOR_CROSS_FLOW : isLive ? EDGE_COLOR_LIVE : EDGE_COLOR_LINEAR;
|
|
1381
1471
|
edges.push({
|
|
@@ -1442,29 +1532,6 @@ function newNodeFromSpec(spec, existingIds) {
|
|
|
1442
1532
|
const id = slugifyNodeId("nova_acao", existingIds);
|
|
1443
1533
|
return { id, type: "action", actionKind: spec.actionKind };
|
|
1444
1534
|
}
|
|
1445
|
-
function removeNodeAndCleanRefs(nodes, nodeId) {
|
|
1446
|
-
const { [nodeId]: _removed, ...rest } = nodes;
|
|
1447
|
-
return Object.fromEntries(
|
|
1448
|
-
Object.entries(rest).map(([id, node]) => {
|
|
1449
|
-
if (!node.next) return [id, node];
|
|
1450
|
-
if (typeof node.next === "string") {
|
|
1451
|
-
return [id, node.next === nodeId ? { ...node, next: void 0 } : node];
|
|
1452
|
-
}
|
|
1453
|
-
return [
|
|
1454
|
-
id,
|
|
1455
|
-
{
|
|
1456
|
-
...node,
|
|
1457
|
-
next: {
|
|
1458
|
-
byAnswer: Object.fromEntries(
|
|
1459
|
-
Object.entries(node.next.byAnswer).map(([key, value]) => [key, value === nodeId ? "" : value])
|
|
1460
|
-
),
|
|
1461
|
-
default: node.next.default === nodeId ? "" : node.next.default
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
];
|
|
1465
|
-
})
|
|
1466
|
-
);
|
|
1467
|
-
}
|
|
1468
1535
|
function extractErrorMessage(error) {
|
|
1469
1536
|
if (error instanceof Error) return error.message;
|
|
1470
1537
|
return void 0;
|
|
@@ -1641,7 +1708,7 @@ function FlowsWorkspace({
|
|
|
1641
1708
|
for (const { key: flowKey, offset } of openFlows) {
|
|
1642
1709
|
let resolvePosition2 = function(nodeId) {
|
|
1643
1710
|
if (mergedPositions) {
|
|
1644
|
-
const nsId =
|
|
1711
|
+
const nsId = namespaceNodeId(flowKey, nodeId);
|
|
1645
1712
|
return renderedPositionsRef.current.get(nsId) ?? mergedPositions.get(nsId) ?? { x: 0, y: 0 };
|
|
1646
1713
|
}
|
|
1647
1714
|
const local = graph.nodes[nodeId]?.position ?? fallbackPositions[nodeId] ?? { x: 0, y: 0 };
|
|
@@ -1663,7 +1730,7 @@ function FlowsWorkspace({
|
|
|
1663
1730
|
for (const node of Object.values(graph.nodes)) {
|
|
1664
1731
|
const position = resolvePosition2(node.id);
|
|
1665
1732
|
allNodes.push({
|
|
1666
|
-
id:
|
|
1733
|
+
id: namespaceNodeId(flowKey, node.id),
|
|
1667
1734
|
type: "flowNode",
|
|
1668
1735
|
position,
|
|
1669
1736
|
draggable: true,
|
|
@@ -1683,7 +1750,7 @@ function FlowsWorkspace({
|
|
|
1683
1750
|
const targetFlowKey = crossFlowKey(target);
|
|
1684
1751
|
if (openFlows.some((flow) => flow.key === targetFlowKey)) return;
|
|
1685
1752
|
allNodes.push({
|
|
1686
|
-
id:
|
|
1753
|
+
id: namespaceNodeId(flowKey, portalNodeId(node.id, target)),
|
|
1687
1754
|
type: "flowPortal",
|
|
1688
1755
|
draggable: false,
|
|
1689
1756
|
selectable: false,
|
|
@@ -1703,7 +1770,7 @@ function FlowsWorkspace({
|
|
|
1703
1770
|
const minY = Math.min(...positions.map((point) => point.y));
|
|
1704
1771
|
const maxY = Math.max(...positions.map((point) => point.y)) + estimateNodeHeight(graph.nodes[chain.actionNodeId]);
|
|
1705
1772
|
allNodes.push({
|
|
1706
|
-
id:
|
|
1773
|
+
id: namespaceNodeId(flowKey, `__chain__${chain.actionNodeId}`),
|
|
1707
1774
|
type: "flowGroupFrame",
|
|
1708
1775
|
draggable: false,
|
|
1709
1776
|
selectable: false,
|
|
@@ -1721,7 +1788,7 @@ function FlowsWorkspace({
|
|
|
1721
1788
|
if (!isPrimary) {
|
|
1722
1789
|
const startPosition = resolvePosition2(graph.startNodeId);
|
|
1723
1790
|
allNodes.push({
|
|
1724
|
-
id:
|
|
1791
|
+
id: namespaceNodeId(flowKey, "__group_header__"),
|
|
1725
1792
|
type: "flowGroupHeader",
|
|
1726
1793
|
draggable: false,
|
|
1727
1794
|
selectable: false,
|
|
@@ -1773,10 +1840,11 @@ function FlowsWorkspace({
|
|
|
1773
1840
|
const onNodeDragStop = useCallback(
|
|
1774
1841
|
(_event, node) => {
|
|
1775
1842
|
renderedPositionsRef.current.set(node.id, node.position);
|
|
1776
|
-
const { flowKey, nodeId } =
|
|
1843
|
+
const { flowKey, nodeId } = parseNamespacedId(node.id);
|
|
1777
1844
|
const openFlow = openFlows.find((flow) => flow.key === flowKey);
|
|
1778
1845
|
if (!openFlow) return;
|
|
1779
|
-
const
|
|
1846
|
+
const drawnWithOffset = openFlows.length === 1;
|
|
1847
|
+
const localPosition = drawnWithOffset ? { x: node.position.x - openFlow.offset.x, y: node.position.y - openFlow.offset.y } : node.position;
|
|
1780
1848
|
updateFlow(
|
|
1781
1849
|
flowKey,
|
|
1782
1850
|
(graph) => graph.nodes[nodeId] ? { ...graph, nodes: { ...graph.nodes, [nodeId]: { ...graph.nodes[nodeId], position: localPosition } } } : graph
|
|
@@ -1786,26 +1854,15 @@ function FlowsWorkspace({
|
|
|
1786
1854
|
);
|
|
1787
1855
|
const onConnect = useCallback(
|
|
1788
1856
|
(connection) => {
|
|
1789
|
-
const
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
} else {
|
|
1797
|
-
const targetGraph = workingGraphs[targetRef.flowKey];
|
|
1798
|
-
if (!targetGraph || targetGraph.startNodeId !== targetRef.nodeId) return;
|
|
1799
|
-
targetValue = `${CROSS_FLOW_PREFIX}${targetRef.flowKey}`;
|
|
1800
|
-
}
|
|
1801
|
-
updateFlow(sourceRef.flowKey, (graph) => {
|
|
1802
|
-
const node = graph.nodes[sourceRef.nodeId];
|
|
1857
|
+
const resolved = resolveConnection({
|
|
1858
|
+
connection: { source: connection.source, target: connection.target, sourceHandle: connection.sourceHandle },
|
|
1859
|
+
graphs: workingGraphs
|
|
1860
|
+
});
|
|
1861
|
+
if (!resolved) return;
|
|
1862
|
+
updateFlow(resolved.flowKey, (graph) => {
|
|
1863
|
+
const node = graph.nodes[resolved.nodeId];
|
|
1803
1864
|
if (!node) return graph;
|
|
1804
|
-
|
|
1805
|
-
const currentByAnswer = typeof node.next === "object" && node.next ? node.next.byAnswer : {};
|
|
1806
|
-
const currentDefault = typeof node.next === "object" && node.next ? node.next.default : "";
|
|
1807
|
-
const updatedNode = handle === "next" ? { ...node, next: targetValue } : handle === "__default" ? { ...node, next: { byAnswer: currentByAnswer, default: targetValue } } : { ...node, next: { byAnswer: { ...currentByAnswer, [handle]: targetValue }, default: currentDefault } };
|
|
1808
|
-
return { ...graph, nodes: { ...graph.nodes, [sourceRef.nodeId]: updatedNode } };
|
|
1865
|
+
return { ...graph, nodes: { ...graph.nodes, [resolved.nodeId]: applyConnection(node, resolved) } };
|
|
1809
1866
|
});
|
|
1810
1867
|
},
|
|
1811
1868
|
[workingGraphs, updateFlow]
|
|
@@ -1817,7 +1874,7 @@ function FlowsWorkspace({
|
|
|
1817
1874
|
newNode.position = { x: 0, y: maxY + 170 };
|
|
1818
1875
|
updateFlow(primaryFlowKey, (graph) => ({ ...graph, nodes: { ...graph.nodes, [newNode.id]: newNode } }));
|
|
1819
1876
|
setEditingRef({ flowKey: primaryFlowKey, nodeId: newNode.id });
|
|
1820
|
-
setPendingFocusNodeId(
|
|
1877
|
+
setPendingFocusNodeId(namespaceNodeId(primaryFlowKey, newNode.id));
|
|
1821
1878
|
}
|
|
1822
1879
|
function handleNodePanelChange(updated) {
|
|
1823
1880
|
if (!editingRef) return;
|
|
@@ -1839,7 +1896,7 @@ function FlowsWorkspace({
|
|
|
1839
1896
|
...graph,
|
|
1840
1897
|
nodes: Object.fromEntries(
|
|
1841
1898
|
Object.entries(graph.nodes).map(([id, node]) => {
|
|
1842
|
-
const position = positions2.get(
|
|
1899
|
+
const position = positions2.get(namespaceNodeId(key, id));
|
|
1843
1900
|
return [
|
|
1844
1901
|
id,
|
|
1845
1902
|
position ? { ...node, position: { x: position.x - offset.x, y: position.y - offset.y } } : node
|
|
@@ -1930,6 +1987,7 @@ function FlowsWorkspace({
|
|
|
1930
1987
|
const canCreateFlow = Boolean(api.createFlow);
|
|
1931
1988
|
const canDeleteFlow = Boolean(api.deleteFlow) && primaryFlowKey !== rootFlowKey;
|
|
1932
1989
|
return /* @__PURE__ */ jsxs9("div", { className: `space-y-4 h-full flex flex-col ${className ?? ""}`, children: [
|
|
1990
|
+
/* @__PURE__ */ jsx10(TooltipLayer, {}),
|
|
1933
1991
|
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between flex-wrap gap-3", children: [
|
|
1934
1992
|
/* @__PURE__ */ jsxs9("div", { children: [
|
|
1935
1993
|
/* @__PURE__ */ jsx10("h2", { className: "text-2xl font-bold text-gray-900 dark:text-gray-100", children: labels.workspace.title }),
|
|
@@ -1941,6 +1999,8 @@ function FlowsWorkspace({
|
|
|
1941
1999
|
/* @__PURE__ */ jsxs9(
|
|
1942
2000
|
"button",
|
|
1943
2001
|
{
|
|
2002
|
+
"data-cv-tooltip": viewMode === "map" ? labels.flowMap.toggleToDetail : labels.flowMap.toggleToMap,
|
|
2003
|
+
"aria-label": viewMode === "map" ? labels.flowMap.toggleToDetail : labels.flowMap.toggleToMap,
|
|
1944
2004
|
type: "button",
|
|
1945
2005
|
onClick: () => setViewMode((mode) => mode === "map" ? "detail" : "map"),
|
|
1946
2006
|
className: OUTLINE_BUTTON,
|
|
@@ -1957,7 +2017,8 @@ function FlowsWorkspace({
|
|
|
1957
2017
|
type: "button",
|
|
1958
2018
|
onClick: handleOrganize,
|
|
1959
2019
|
className: OUTLINE_BUTTON,
|
|
1960
|
-
|
|
2020
|
+
"data-cv-tooltip": labels.workspace.organizeTooltip,
|
|
2021
|
+
"aria-label": labels.workspace.organizeTooltip,
|
|
1961
2022
|
disabled: !primaryGraph,
|
|
1962
2023
|
children: [
|
|
1963
2024
|
/* @__PURE__ */ jsx10(LayoutGrid, { size: 14, "aria-hidden": "true" }),
|
|
@@ -1972,7 +2033,8 @@ function FlowsWorkspace({
|
|
|
1972
2033
|
type: "button",
|
|
1973
2034
|
onClick: handleDiscardChanges,
|
|
1974
2035
|
className: OUTLINE_BUTTON,
|
|
1975
|
-
|
|
2036
|
+
"data-cv-tooltip": labels.workspace.discardTooltip,
|
|
2037
|
+
"aria-label": labels.workspace.discardTooltip,
|
|
1976
2038
|
disabled: !isDirty || saveState === "saving",
|
|
1977
2039
|
children: [
|
|
1978
2040
|
/* @__PURE__ */ jsx10(Undo2, { size: 14, "aria-hidden": "true" }),
|
|
@@ -1984,6 +2046,8 @@ function FlowsWorkspace({
|
|
|
1984
2046
|
/* @__PURE__ */ jsxs9(
|
|
1985
2047
|
"button",
|
|
1986
2048
|
{
|
|
2049
|
+
"data-cv-tooltip": labels.workspace.saveGraph,
|
|
2050
|
+
"aria-label": labels.workspace.saveGraph,
|
|
1987
2051
|
type: "button",
|
|
1988
2052
|
onClick: () => void handlePublish(),
|
|
1989
2053
|
className: PRIMARY_BUTTON,
|
|
@@ -2002,6 +2066,8 @@ function FlowsWorkspace({
|
|
|
2002
2066
|
graphs && Object.values(graphs).map((graph) => /* @__PURE__ */ jsx10(
|
|
2003
2067
|
"button",
|
|
2004
2068
|
{
|
|
2069
|
+
"data-cv-tooltip": graph.label,
|
|
2070
|
+
"aria-label": graph.label,
|
|
2005
2071
|
type: "button",
|
|
2006
2072
|
onClick: () => focusFlow(graph.key),
|
|
2007
2073
|
className: `px-3 py-1.5 rounded-full text-xs font-medium border transition-colors ${primaryFlowKey === graph.key ? "bg-blue-600 text-white border-blue-600" : "bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-300 border-gray-200 dark:border-gray-700 hover:border-blue-300"}`,
|
|
@@ -2012,6 +2078,8 @@ function FlowsWorkspace({
|
|
|
2012
2078
|
canCreateFlow && /* @__PURE__ */ jsxs9(
|
|
2013
2079
|
"button",
|
|
2014
2080
|
{
|
|
2081
|
+
"data-cv-tooltip": labels.flowManager.newFlow,
|
|
2082
|
+
"aria-label": labels.flowManager.newFlow,
|
|
2015
2083
|
type: "button",
|
|
2016
2084
|
onClick: () => {
|
|
2017
2085
|
setFlowMutationState({ pending: false });
|
|
@@ -2038,6 +2106,8 @@ function FlowsWorkspace({
|
|
|
2038
2106
|
primaryGraph && canDeleteFlow && /* @__PURE__ */ jsxs9(
|
|
2039
2107
|
"button",
|
|
2040
2108
|
{
|
|
2109
|
+
"data-cv-tooltip": labels.flowManager.deleteFlow,
|
|
2110
|
+
"aria-label": labels.flowManager.deleteFlow,
|
|
2041
2111
|
type: "button",
|
|
2042
2112
|
onClick: () => {
|
|
2043
2113
|
setFlowMutationState({ pending: false });
|
|
@@ -2171,10 +2241,12 @@ function FlowsWorkspace({
|
|
|
2171
2241
|
flowMutationState.error && /* @__PURE__ */ jsx10("p", { className: "text-xs text-red-600", children: flowMutationState.error })
|
|
2172
2242
|
] }),
|
|
2173
2243
|
/* @__PURE__ */ jsxs9("div", { className: "flex justify-end gap-2 mt-4", children: [
|
|
2174
|
-
/* @__PURE__ */ jsx10("button", { type: "button", className: OUTLINE_BUTTON, onClick: () => setShowCreateDialog(false), children: labels.nodePanel.cancel }),
|
|
2244
|
+
/* @__PURE__ */ jsx10("button", { "data-cv-tooltip": labels.nodePanel.cancel, "aria-label": labels.nodePanel.cancel, type: "button", className: OUTLINE_BUTTON, onClick: () => setShowCreateDialog(false), children: labels.nodePanel.cancel }),
|
|
2175
2245
|
/* @__PURE__ */ jsx10(
|
|
2176
2246
|
"button",
|
|
2177
2247
|
{
|
|
2248
|
+
"data-cv-tooltip": labels.flowManager.create,
|
|
2249
|
+
"aria-label": labels.flowManager.create,
|
|
2178
2250
|
type: "button",
|
|
2179
2251
|
className: PRIMARY_BUTTON,
|
|
2180
2252
|
onClick: () => void handleCreateFlow(),
|
|
@@ -2188,10 +2260,12 @@ function FlowsWorkspace({
|
|
|
2188
2260
|
/* @__PURE__ */ jsx10("p", { className: "text-sm text-gray-600 dark:text-gray-300", children: primaryGraph ? labels.flowManager.deleteConfirm(primaryGraph.label) : "" }),
|
|
2189
2261
|
flowMutationState.error && /* @__PURE__ */ jsx10("p", { className: "text-xs text-red-600 mt-2", children: flowMutationState.error }),
|
|
2190
2262
|
/* @__PURE__ */ jsxs9("div", { className: "flex justify-end gap-2 mt-4", children: [
|
|
2191
|
-
/* @__PURE__ */ jsx10("button", { type: "button", className: OUTLINE_BUTTON, onClick: () => setShowDeleteDialog(false), children: labels.nodePanel.cancel }),
|
|
2263
|
+
/* @__PURE__ */ jsx10("button", { "data-cv-tooltip": labels.nodePanel.cancel, "aria-label": labels.nodePanel.cancel, type: "button", className: OUTLINE_BUTTON, onClick: () => setShowDeleteDialog(false), children: labels.nodePanel.cancel }),
|
|
2192
2264
|
/* @__PURE__ */ jsxs9(
|
|
2193
2265
|
"button",
|
|
2194
2266
|
{
|
|
2267
|
+
"data-cv-tooltip": labels.flowManager.deleteFlow,
|
|
2268
|
+
"aria-label": labels.flowManager.deleteFlow,
|
|
2195
2269
|
type: "button",
|
|
2196
2270
|
className: "inline-flex items-center gap-1.5 rounded-lg bg-red-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-red-700 disabled:opacity-40",
|
|
2197
2271
|
onClick: () => void handleDeleteFlow(),
|
|
@@ -2246,6 +2320,7 @@ export {
|
|
|
2246
2320
|
FlowsWorkspace,
|
|
2247
2321
|
NODE_CARD_WIDTH,
|
|
2248
2322
|
WHATSAPP_LIMITS,
|
|
2323
|
+
applyConnection,
|
|
2249
2324
|
computeAutoLayout,
|
|
2250
2325
|
computeFlowMapLayout,
|
|
2251
2326
|
crossFlowKey,
|
|
@@ -2258,9 +2333,15 @@ export {
|
|
|
2258
2333
|
flowNodeTypes,
|
|
2259
2334
|
flowPortalNodeTypes,
|
|
2260
2335
|
isCrossFlowTarget,
|
|
2336
|
+
isGraphDirty,
|
|
2261
2337
|
mergeFlowEditorLabels,
|
|
2338
|
+
mergedFlowKeysFrom,
|
|
2339
|
+
namespaceNodeId,
|
|
2262
2340
|
nodeLabel,
|
|
2341
|
+
parseNamespacedId,
|
|
2342
|
+
removeNodeAndCleanRefs,
|
|
2263
2343
|
rendersAsButtons,
|
|
2344
|
+
resolveConnection,
|
|
2264
2345
|
slugifyNodeId,
|
|
2265
2346
|
targetsOf,
|
|
2266
2347
|
validateGraph
|