@dilipod/ui 0.4.14 → 0.4.16
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/components/workflow-flow.d.ts +29 -3
- package/dist/components/workflow-flow.d.ts.map +1 -1
- package/dist/components/workflow-viewer.d.ts +14 -0
- package/dist/components/workflow-viewer.d.ts.map +1 -1
- package/dist/index.js +382 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +383 -34
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/workflow-flow.tsx +163 -5
- package/src/components/workflow-viewer.tsx +381 -78
package/dist/index.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { clsx } from 'clsx';
|
|
|
10
10
|
import { twMerge } from 'tailwind-merge';
|
|
11
11
|
import * as SheetPrimitive from '@radix-ui/react-dialog';
|
|
12
12
|
import * as react_star from '@phosphor-icons/react';
|
|
13
|
-
import { X, CaretDown, Circle, CaretLeft, DotsThree, CaretRight, Check, House, Info, WarningCircle, Play, Download, Folder, ArrowSquareOut, CircleNotch, File, FileVideo, Lightning, Plus, CheckCircle, PaperPlaneTilt, CaretUp, Eye, TreeStructure, Code, PencilSimple, WebhooksLogo, Copy, CloudArrowUp, CloudArrowDown, ArrowsClockwise, DownloadSimple, ClockCounterClockwise, FileImage, FilePdf, FileDoc, Question, Warning, Trash, Robot, Globe, GitBranch, Package, Timer } from '@phosphor-icons/react';
|
|
13
|
+
import { X, CaretDown, Circle, CaretLeft, DotsThree, CaretRight, Check, House, Info, WarningCircle, Play, Download, Folder, ArrowSquareOut, CircleNotch, File, FileVideo, Lightning, Plus, CheckCircle, PaperPlaneTilt, CaretUp, Eye, TreeStructure, Code, PencilSimple, WebhooksLogo, Copy, CloudArrowUp, CloudArrowDown, ArrowsClockwise, DownloadSimple, ClockCounterClockwise, ArrowsLeftRight, Minus, Pencil, FileImage, FilePdf, FileDoc, Question, Warning, Trash, Robot, Globe, GitBranch, Package, Timer } from '@phosphor-icons/react';
|
|
14
14
|
import 'react-dom';
|
|
15
15
|
import * as SwitchPrimitive from '@radix-ui/react-switch';
|
|
16
16
|
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
|
|
@@ -53,6 +53,7 @@ __export(workflow_flow_exports, {
|
|
|
53
53
|
});
|
|
54
54
|
function getNodeTypeLabel(type) {
|
|
55
55
|
const labels = {
|
|
56
|
+
// n8n types
|
|
56
57
|
"n8n-nodes-base.webhook": "Webhook",
|
|
57
58
|
"n8n-nodes-base.scheduleTrigger": "Schedule",
|
|
58
59
|
"n8n-nodes-base.if": "Condition",
|
|
@@ -62,7 +63,22 @@ function getNodeTypeLabel(type) {
|
|
|
62
63
|
"n8n-nodes-base.respondToWebhook": "Response",
|
|
63
64
|
"@n8n/n8n-nodes-langchain.agent": "AI Agent",
|
|
64
65
|
"@n8n/n8n-nodes-langchain.lmChatOpenAi": "OpenAI",
|
|
65
|
-
"@n8n/n8n-nodes-langchain.lmChatAnthropic": "Anthropic"
|
|
66
|
+
"@n8n/n8n-nodes-langchain.lmChatAnthropic": "Anthropic",
|
|
67
|
+
// Sim Studio types
|
|
68
|
+
"starter": "Webhook",
|
|
69
|
+
"webhook": "Webhook",
|
|
70
|
+
"agent": "AI Agent",
|
|
71
|
+
"llm": "LLM",
|
|
72
|
+
"openai": "OpenAI",
|
|
73
|
+
"anthropic": "Anthropic",
|
|
74
|
+
"api": "API Request",
|
|
75
|
+
"http_request": "HTTP Request",
|
|
76
|
+
"condition": "Condition",
|
|
77
|
+
"code": "Code",
|
|
78
|
+
"response": "Response",
|
|
79
|
+
"function": "Function",
|
|
80
|
+
"evaluator": "Evaluator",
|
|
81
|
+
"router": "Router"
|
|
66
82
|
};
|
|
67
83
|
return labels[type] || type.split(".").pop()?.replace(/([A-Z])/g, " $1").trim() || type;
|
|
68
84
|
}
|
|
@@ -74,10 +90,98 @@ function CustomNode({ data }) {
|
|
|
74
90
|
/* @__PURE__ */ jsx(Handle, { type: "source", position: Position.Right, className: "!bg-slate-300 !w-1.5 !h-1.5 !border-0" })
|
|
75
91
|
] });
|
|
76
92
|
}
|
|
77
|
-
function WorkflowFlow({ workflow, height = 350, className = "" }) {
|
|
93
|
+
function WorkflowFlow({ workflow, height = 350, className = "", platform = "n8n" }) {
|
|
78
94
|
const { initialNodes, initialEdges } = useMemo(() => {
|
|
79
|
-
|
|
80
|
-
|
|
95
|
+
if (platform === "sim") {
|
|
96
|
+
const simWorkflow = workflow;
|
|
97
|
+
const blocks = simWorkflow.blocks || {};
|
|
98
|
+
const simEdges = simWorkflow.edges || [];
|
|
99
|
+
const blockList = Object.values(blocks);
|
|
100
|
+
if (blockList.length === 0) {
|
|
101
|
+
return { initialNodes: [], initialEdges: [] };
|
|
102
|
+
}
|
|
103
|
+
const forwardEdges2 = /* @__PURE__ */ new Map();
|
|
104
|
+
const backwardEdges2 = /* @__PURE__ */ new Map();
|
|
105
|
+
simEdges.forEach((edge) => {
|
|
106
|
+
const from = edge.source;
|
|
107
|
+
const to = edge.target;
|
|
108
|
+
if (from && to) {
|
|
109
|
+
if (!forwardEdges2.has(from)) forwardEdges2.set(from, []);
|
|
110
|
+
forwardEdges2.get(from).push(to);
|
|
111
|
+
if (!backwardEdges2.has(to)) backwardEdges2.set(to, []);
|
|
112
|
+
backwardEdges2.get(to).push(from);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
const triggerBlocks = blockList.filter(
|
|
116
|
+
(b) => b.type === "starter" || b.type === "webhook" || b.type === "api"
|
|
117
|
+
);
|
|
118
|
+
const roots2 = triggerBlocks.length > 0 ? triggerBlocks : blockList.filter((b) => !backwardEdges2.has(b.id) || backwardEdges2.get(b.id).length === 0);
|
|
119
|
+
const levels2 = /* @__PURE__ */ new Map();
|
|
120
|
+
const queue2 = [];
|
|
121
|
+
roots2.forEach((r) => {
|
|
122
|
+
levels2.set(r.id, 0);
|
|
123
|
+
queue2.push(r.id);
|
|
124
|
+
});
|
|
125
|
+
const visited2 = /* @__PURE__ */ new Set();
|
|
126
|
+
while (queue2.length > 0) {
|
|
127
|
+
const id = queue2.shift();
|
|
128
|
+
if (visited2.has(id)) continue;
|
|
129
|
+
visited2.add(id);
|
|
130
|
+
const children = forwardEdges2.get(id) || [];
|
|
131
|
+
const myLevel = levels2.get(id) || 0;
|
|
132
|
+
children.forEach((child) => {
|
|
133
|
+
const childLevel = levels2.get(child);
|
|
134
|
+
if (childLevel === void 0 || myLevel + 1 > childLevel) {
|
|
135
|
+
levels2.set(child, myLevel + 1);
|
|
136
|
+
}
|
|
137
|
+
if (!visited2.has(child)) {
|
|
138
|
+
queue2.push(child);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
blockList.forEach((block) => {
|
|
143
|
+
if (!levels2.has(block.id)) {
|
|
144
|
+
const maxLevel = Math.max(0, ...Array.from(levels2.values()));
|
|
145
|
+
levels2.set(block.id, maxLevel + 1);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
const nodesByLevel2 = /* @__PURE__ */ new Map();
|
|
149
|
+
levels2.forEach((level, id) => {
|
|
150
|
+
if (!nodesByLevel2.has(level)) nodesByLevel2.set(level, []);
|
|
151
|
+
nodesByLevel2.get(level).push(id);
|
|
152
|
+
});
|
|
153
|
+
const xGap2 = 170;
|
|
154
|
+
const yGap2 = 70;
|
|
155
|
+
const positions2 = /* @__PURE__ */ new Map();
|
|
156
|
+
const sortedLevels2 = Array.from(nodesByLevel2.keys()).sort((a, b) => a - b);
|
|
157
|
+
sortedLevels2.forEach((level) => {
|
|
158
|
+
const nodesInLevel = nodesByLevel2.get(level);
|
|
159
|
+
const totalHeight = (nodesInLevel.length - 1) * yGap2;
|
|
160
|
+
const startY = -totalHeight / 2;
|
|
161
|
+
nodesInLevel.forEach((id, i) => {
|
|
162
|
+
positions2.set(id, { x: level * xGap2, y: startY + i * yGap2 });
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
const nodes3 = blockList.map((block) => ({
|
|
166
|
+
id: block.id,
|
|
167
|
+
type: "custom",
|
|
168
|
+
position: positions2.get(block.id) || { x: 0, y: 0 },
|
|
169
|
+
data: { label: block.name || block.type, type: block.type }
|
|
170
|
+
}));
|
|
171
|
+
const edges3 = simEdges.map((edge, idx) => ({
|
|
172
|
+
id: edge.id || `edge-${idx}`,
|
|
173
|
+
source: edge.source || "",
|
|
174
|
+
target: edge.target || "",
|
|
175
|
+
type: "smoothstep",
|
|
176
|
+
pathOptions: { borderRadius: 20 },
|
|
177
|
+
style: { stroke: "#94a3b8", strokeWidth: 1.5 },
|
|
178
|
+
markerEnd: { type: MarkerType.ArrowClosed, color: "#94a3b8", width: 14, height: 14 }
|
|
179
|
+
})).filter((e) => e.source && e.target);
|
|
180
|
+
return { initialNodes: nodes3, initialEdges: edges3 };
|
|
181
|
+
}
|
|
182
|
+
const n8nWorkflow = workflow;
|
|
183
|
+
const n8nNodes = n8nWorkflow.nodes || [];
|
|
184
|
+
const connections = n8nWorkflow.connections || {};
|
|
81
185
|
const nodeIdMap = new Map(n8nNodes.map((n) => [n.name, n.id || n.name]));
|
|
82
186
|
const forwardEdges = /* @__PURE__ */ new Map();
|
|
83
187
|
const backwardEdges = /* @__PURE__ */ new Map();
|
|
@@ -4748,6 +4852,55 @@ function defaultFormatDistance(date, options) {
|
|
|
4748
4852
|
else result = `${diffDays} day${diffDays > 1 ? "s" : ""}`;
|
|
4749
4853
|
return options?.addSuffix ? `${result} ago` : result;
|
|
4750
4854
|
}
|
|
4855
|
+
function computeWorkflowDiff(stateA, stateB) {
|
|
4856
|
+
if (!stateA || !stateB) {
|
|
4857
|
+
return {
|
|
4858
|
+
blocksAdded: [],
|
|
4859
|
+
blocksRemoved: [],
|
|
4860
|
+
blocksModified: [],
|
|
4861
|
+
edgesAdded: 0,
|
|
4862
|
+
edgesRemoved: 0,
|
|
4863
|
+
summary: "Unable to compare - missing workflow data"
|
|
4864
|
+
};
|
|
4865
|
+
}
|
|
4866
|
+
const blocksA = stateA.blocks || {};
|
|
4867
|
+
const blocksB = stateB.blocks || {};
|
|
4868
|
+
const edgesA = stateA.edges || [];
|
|
4869
|
+
const edgesB = stateB.edges || [];
|
|
4870
|
+
const blockIdsA = new Set(Object.keys(blocksA));
|
|
4871
|
+
const blockIdsB = new Set(Object.keys(blocksB));
|
|
4872
|
+
const blocksAdded = [...blockIdsB].filter((id) => !blockIdsA.has(id));
|
|
4873
|
+
const blocksRemoved = [...blockIdsA].filter((id) => !blockIdsB.has(id));
|
|
4874
|
+
const blocksModified = [];
|
|
4875
|
+
for (const id of blockIdsA) {
|
|
4876
|
+
if (blockIdsB.has(id)) {
|
|
4877
|
+
const blockA = blocksA[id];
|
|
4878
|
+
const blockB = blocksB[id];
|
|
4879
|
+
if (JSON.stringify(blockA) !== JSON.stringify(blockB)) {
|
|
4880
|
+
blocksModified.push(id);
|
|
4881
|
+
}
|
|
4882
|
+
}
|
|
4883
|
+
}
|
|
4884
|
+
const edgeSignature = (e) => `${e.source || e.from}->${e.target || e.to}`;
|
|
4885
|
+
const edgeSigsA = new Set(edgesA.map(edgeSignature));
|
|
4886
|
+
const edgeSigsB = new Set(edgesB.map(edgeSignature));
|
|
4887
|
+
const edgesAdded = [...edgeSigsB].filter((sig) => !edgeSigsA.has(sig)).length;
|
|
4888
|
+
const edgesRemoved = [...edgeSigsA].filter((sig) => !edgeSigsB.has(sig)).length;
|
|
4889
|
+
const changes = [];
|
|
4890
|
+
if (blocksAdded.length > 0) changes.push(`+${blocksAdded.length} block${blocksAdded.length > 1 ? "s" : ""}`);
|
|
4891
|
+
if (blocksRemoved.length > 0) changes.push(`-${blocksRemoved.length} block${blocksRemoved.length > 1 ? "s" : ""}`);
|
|
4892
|
+
if (blocksModified.length > 0) changes.push(`~${blocksModified.length} modified`);
|
|
4893
|
+
if (edgesAdded > 0) changes.push(`+${edgesAdded} edge${edgesAdded > 1 ? "s" : ""}`);
|
|
4894
|
+
if (edgesRemoved > 0) changes.push(`-${edgesRemoved} edge${edgesRemoved > 1 ? "s" : ""}`);
|
|
4895
|
+
return {
|
|
4896
|
+
blocksAdded,
|
|
4897
|
+
blocksRemoved,
|
|
4898
|
+
blocksModified,
|
|
4899
|
+
edgesAdded,
|
|
4900
|
+
edgesRemoved,
|
|
4901
|
+
summary: changes.length > 0 ? changes.join(", ") : "No changes detected"
|
|
4902
|
+
};
|
|
4903
|
+
}
|
|
4751
4904
|
function N8nWorkflowSummary({ workflow, showFlow = false }) {
|
|
4752
4905
|
const nodes = workflow.nodes || [];
|
|
4753
4906
|
const triggerNode = nodes.find(
|
|
@@ -4969,6 +5122,12 @@ function WorkflowViewer({
|
|
|
4969
5122
|
const [pushingToSim, setPushingToSim] = useState(false);
|
|
4970
5123
|
const [pullingFromSim, setPullingFromSim] = useState(false);
|
|
4971
5124
|
const [switchingPlatform, setSwitchingPlatform] = useState(false);
|
|
5125
|
+
const [diffMode, setDiffMode] = useState(false);
|
|
5126
|
+
const [selectedBackupA, setSelectedBackupA] = useState(null);
|
|
5127
|
+
const [selectedBackupB, setSelectedBackupB] = useState(null);
|
|
5128
|
+
const [backupStateA, setBackupStateA] = useState(null);
|
|
5129
|
+
const [backupStateB, setBackupStateB] = useState(null);
|
|
5130
|
+
const [loadingDiff, setLoadingDiff] = useState(false);
|
|
4972
5131
|
const [localPlatform, setLocalPlatform] = useState(platform);
|
|
4973
5132
|
const [localIsActive, setLocalIsActive] = useState(isActive ?? true);
|
|
4974
5133
|
const hasUnsavedChanges = localPlatform !== platform || localIsActive !== (isActive ?? true);
|
|
@@ -5171,6 +5330,42 @@ function WorkflowViewer({
|
|
|
5171
5330
|
setViewMode("backups");
|
|
5172
5331
|
loadBackups();
|
|
5173
5332
|
}
|
|
5333
|
+
async function loadBackupForDiff(backupId, slot) {
|
|
5334
|
+
if (!workflowDefinitionId || !apiHandlers?.getBackupState) {
|
|
5335
|
+
return;
|
|
5336
|
+
}
|
|
5337
|
+
setLoadingDiff(true);
|
|
5338
|
+
try {
|
|
5339
|
+
const result = await apiHandlers.getBackupState(workflowDefinitionId, backupId);
|
|
5340
|
+
if (result.success && result.backup) {
|
|
5341
|
+
if (slot === "A") {
|
|
5342
|
+
setBackupStateA(result.backup.state);
|
|
5343
|
+
setSelectedBackupA(backupId);
|
|
5344
|
+
} else {
|
|
5345
|
+
setBackupStateB(result.backup.state);
|
|
5346
|
+
setSelectedBackupB(backupId);
|
|
5347
|
+
}
|
|
5348
|
+
}
|
|
5349
|
+
} catch {
|
|
5350
|
+
console.error("Failed to load backup for diff");
|
|
5351
|
+
} finally {
|
|
5352
|
+
setLoadingDiff(false);
|
|
5353
|
+
}
|
|
5354
|
+
}
|
|
5355
|
+
function startDiffMode() {
|
|
5356
|
+
setDiffMode(true);
|
|
5357
|
+
setSelectedBackupA(null);
|
|
5358
|
+
setSelectedBackupB(null);
|
|
5359
|
+
setBackupStateA(null);
|
|
5360
|
+
setBackupStateB(null);
|
|
5361
|
+
}
|
|
5362
|
+
function exitDiffMode() {
|
|
5363
|
+
setDiffMode(false);
|
|
5364
|
+
setSelectedBackupA(null);
|
|
5365
|
+
setSelectedBackupB(null);
|
|
5366
|
+
setBackupStateA(null);
|
|
5367
|
+
setBackupStateB(null);
|
|
5368
|
+
}
|
|
5174
5369
|
function openInSimStudio() {
|
|
5175
5370
|
if (simStudioUrl && simWorkflowId) {
|
|
5176
5371
|
window.open(`${simStudioUrl}/w/${simWorkflowId}`, "_blank");
|
|
@@ -5427,7 +5622,7 @@ function WorkflowViewer({
|
|
|
5427
5622
|
]
|
|
5428
5623
|
}
|
|
5429
5624
|
),
|
|
5430
|
-
|
|
5625
|
+
/* @__PURE__ */ jsxs(
|
|
5431
5626
|
"button",
|
|
5432
5627
|
{
|
|
5433
5628
|
onClick: () => setViewMode("flow"),
|
|
@@ -5551,14 +5746,15 @@ function WorkflowViewer({
|
|
|
5551
5746
|
children: pushingToSim ? "Pushing..." : "Push to Sim"
|
|
5552
5747
|
}
|
|
5553
5748
|
),
|
|
5554
|
-
|
|
5749
|
+
apiHandlers?.pullFromSim && /* @__PURE__ */ jsx(
|
|
5555
5750
|
Button,
|
|
5556
5751
|
{
|
|
5557
5752
|
onClick: pullFromSim,
|
|
5558
|
-
disabled: pullingFromSim,
|
|
5753
|
+
disabled: pullingFromSim || !simWorkflowId,
|
|
5559
5754
|
variant: "outline",
|
|
5560
5755
|
size: "sm",
|
|
5561
5756
|
icon: /* @__PURE__ */ jsx(CloudArrowDown, { size: 16 }),
|
|
5757
|
+
title: !simWorkflowId ? "No Sim workflow linked yet. Push to Sim first." : void 0,
|
|
5562
5758
|
children: pullingFromSim ? "Pulling..." : "Pull from Sim"
|
|
5563
5759
|
}
|
|
5564
5760
|
),
|
|
@@ -5624,43 +5820,196 @@ function WorkflowViewer({
|
|
|
5624
5820
|
)
|
|
5625
5821
|
] }),
|
|
5626
5822
|
/* @__PURE__ */ jsx("pre", { className: "p-4 bg-[var(--black)] text-gray-100 text-xs overflow-auto max-h-[500px] font-mono", children: JSON.stringify(workflow, null, 2) })
|
|
5627
|
-
] }) : viewMode === "flow" ?
|
|
5823
|
+
] }) : viewMode === "flow" ? /* @__PURE__ */ jsx(Suspense, { fallback: loadingComponent || DefaultLoading, children: /* @__PURE__ */ jsx(WorkflowFlow2, { workflow, height: 380, platform }) }) : viewMode === "backups" ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
5628
5824
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
5629
|
-
/* @__PURE__ */ jsx("h4", { className: "text-sm font-medium", children: "Backup History" }),
|
|
5630
|
-
/* @__PURE__ */
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5825
|
+
/* @__PURE__ */ jsx("h4", { className: "text-sm font-medium", children: diffMode ? "Compare Versions" : "Backup History" }),
|
|
5826
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5827
|
+
!diffMode && backups.length >= 2 && apiHandlers?.getBackupState && /* @__PURE__ */ jsx(
|
|
5828
|
+
Button,
|
|
5829
|
+
{
|
|
5830
|
+
onClick: startDiffMode,
|
|
5831
|
+
variant: "outline",
|
|
5832
|
+
size: "sm",
|
|
5833
|
+
icon: /* @__PURE__ */ jsx(ArrowsLeftRight, { size: 14 }),
|
|
5834
|
+
children: "Compare"
|
|
5835
|
+
}
|
|
5836
|
+
),
|
|
5837
|
+
diffMode && /* @__PURE__ */ jsx(
|
|
5838
|
+
Button,
|
|
5839
|
+
{
|
|
5840
|
+
onClick: exitDiffMode,
|
|
5841
|
+
variant: "outline",
|
|
5842
|
+
size: "sm",
|
|
5843
|
+
icon: /* @__PURE__ */ jsx(X, { size: 14 }),
|
|
5844
|
+
children: "Cancel"
|
|
5845
|
+
}
|
|
5846
|
+
),
|
|
5847
|
+
/* @__PURE__ */ jsx(
|
|
5848
|
+
Button,
|
|
5849
|
+
{
|
|
5850
|
+
onClick: () => {
|
|
5851
|
+
exitDiffMode();
|
|
5852
|
+
setViewMode("summary");
|
|
5853
|
+
},
|
|
5854
|
+
variant: "outline",
|
|
5855
|
+
size: "sm",
|
|
5856
|
+
children: "Back to Summary"
|
|
5857
|
+
}
|
|
5858
|
+
)
|
|
5859
|
+
] })
|
|
5639
5860
|
] }),
|
|
5640
|
-
|
|
5861
|
+
diffMode && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
5862
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
5863
|
+
/* @__PURE__ */ jsxs("div", { className: "p-3 bg-muted/50 rounded border border-border", children: [
|
|
5864
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-2", children: "From (older)" }),
|
|
5865
|
+
/* @__PURE__ */ jsxs(
|
|
5866
|
+
Select,
|
|
5867
|
+
{
|
|
5868
|
+
value: selectedBackupA || "",
|
|
5869
|
+
onChange: (e) => loadBackupForDiff(e.target.value, "A"),
|
|
5870
|
+
disabled: loadingDiff,
|
|
5871
|
+
className: "w-full",
|
|
5872
|
+
children: [
|
|
5873
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Select version..." }),
|
|
5874
|
+
backups.map((b) => /* @__PURE__ */ jsxs("option", { value: b.id, disabled: b.id === selectedBackupB, children: [
|
|
5875
|
+
"v",
|
|
5876
|
+
b.version,
|
|
5877
|
+
" - ",
|
|
5878
|
+
b.versionLabel || b.workflowName
|
|
5879
|
+
] }, b.id))
|
|
5880
|
+
]
|
|
5881
|
+
}
|
|
5882
|
+
)
|
|
5883
|
+
] }),
|
|
5884
|
+
/* @__PURE__ */ jsxs("div", { className: "p-3 bg-muted/50 rounded border border-border", children: [
|
|
5885
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-2", children: "To (newer)" }),
|
|
5886
|
+
/* @__PURE__ */ jsxs(
|
|
5887
|
+
Select,
|
|
5888
|
+
{
|
|
5889
|
+
value: selectedBackupB || "",
|
|
5890
|
+
onChange: (e) => loadBackupForDiff(e.target.value, "B"),
|
|
5891
|
+
disabled: loadingDiff,
|
|
5892
|
+
className: "w-full",
|
|
5893
|
+
children: [
|
|
5894
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Select version..." }),
|
|
5895
|
+
backups.map((b) => /* @__PURE__ */ jsxs("option", { value: b.id, disabled: b.id === selectedBackupA, children: [
|
|
5896
|
+
"v",
|
|
5897
|
+
b.version,
|
|
5898
|
+
" - ",
|
|
5899
|
+
b.versionLabel || b.workflowName
|
|
5900
|
+
] }, b.id))
|
|
5901
|
+
]
|
|
5902
|
+
}
|
|
5903
|
+
)
|
|
5904
|
+
] })
|
|
5905
|
+
] }),
|
|
5906
|
+
loadingDiff ? /* @__PURE__ */ jsx("div", { className: "py-4 text-center text-muted-foreground", children: "Loading versions..." }) : backupStateA && backupStateB ? (() => {
|
|
5907
|
+
const diff = computeWorkflowDiff(backupStateA, backupStateB);
|
|
5908
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
5909
|
+
/* @__PURE__ */ jsxs("div", { className: "p-3 bg-muted/30 rounded border border-border", children: [
|
|
5910
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium mb-2", children: "Changes Summary" }),
|
|
5911
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: diff.summary })
|
|
5912
|
+
] }),
|
|
5913
|
+
diff.blocksAdded.length > 0 && /* @__PURE__ */ jsxs("div", { className: "p-3 bg-green-500/10 rounded border border-green-500/30", children: [
|
|
5914
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-green-600 dark:text-green-400 mb-2", children: [
|
|
5915
|
+
/* @__PURE__ */ jsx(Plus, { size: 14 }),
|
|
5916
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium", children: [
|
|
5917
|
+
"Blocks Added (",
|
|
5918
|
+
diff.blocksAdded.length,
|
|
5919
|
+
")"
|
|
5920
|
+
] })
|
|
5921
|
+
] }),
|
|
5922
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: diff.blocksAdded.map((id) => {
|
|
5923
|
+
const block = backupStateB?.blocks?.[id];
|
|
5924
|
+
return /* @__PURE__ */ jsx(Badge, { variant: "success", size: "sm", children: block?.name || id }, id);
|
|
5925
|
+
}) })
|
|
5926
|
+
] }),
|
|
5927
|
+
diff.blocksRemoved.length > 0 && /* @__PURE__ */ jsxs("div", { className: "p-3 bg-red-500/10 rounded border border-red-500/30", children: [
|
|
5928
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-red-600 dark:text-red-400 mb-2", children: [
|
|
5929
|
+
/* @__PURE__ */ jsx(Minus, { size: 14 }),
|
|
5930
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium", children: [
|
|
5931
|
+
"Blocks Removed (",
|
|
5932
|
+
diff.blocksRemoved.length,
|
|
5933
|
+
")"
|
|
5934
|
+
] })
|
|
5935
|
+
] }),
|
|
5936
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: diff.blocksRemoved.map((id) => {
|
|
5937
|
+
const block = backupStateA?.blocks?.[id];
|
|
5938
|
+
return /* @__PURE__ */ jsx(Badge, { variant: "error", size: "sm", children: block?.name || id }, id);
|
|
5939
|
+
}) })
|
|
5940
|
+
] }),
|
|
5941
|
+
diff.blocksModified.length > 0 && /* @__PURE__ */ jsxs("div", { className: "p-3 bg-amber-500/10 rounded border border-amber-500/30", children: [
|
|
5942
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-amber-600 dark:text-amber-400 mb-2", children: [
|
|
5943
|
+
/* @__PURE__ */ jsx(Pencil, { size: 14 }),
|
|
5944
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium", children: [
|
|
5945
|
+
"Blocks Modified (",
|
|
5946
|
+
diff.blocksModified.length,
|
|
5947
|
+
")"
|
|
5948
|
+
] })
|
|
5949
|
+
] }),
|
|
5950
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: diff.blocksModified.map((id) => {
|
|
5951
|
+
const block = backupStateB?.blocks?.[id];
|
|
5952
|
+
return /* @__PURE__ */ jsx(Badge, { variant: "warning", size: "sm", children: block?.name || id }, id);
|
|
5953
|
+
}) })
|
|
5954
|
+
] }),
|
|
5955
|
+
(diff.edgesAdded > 0 || diff.edgesRemoved > 0) && /* @__PURE__ */ jsxs("div", { className: "p-3 bg-muted/30 rounded border border-border", children: [
|
|
5956
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Connection Changes" }),
|
|
5957
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-3 text-sm", children: [
|
|
5958
|
+
diff.edgesAdded > 0 && /* @__PURE__ */ jsxs("span", { className: "text-green-600 dark:text-green-400", children: [
|
|
5959
|
+
"+",
|
|
5960
|
+
diff.edgesAdded,
|
|
5961
|
+
" added"
|
|
5962
|
+
] }),
|
|
5963
|
+
diff.edgesRemoved > 0 && /* @__PURE__ */ jsxs("span", { className: "text-red-600 dark:text-red-400", children: [
|
|
5964
|
+
"-",
|
|
5965
|
+
diff.edgesRemoved,
|
|
5966
|
+
" removed"
|
|
5967
|
+
] })
|
|
5968
|
+
] })
|
|
5969
|
+
] })
|
|
5970
|
+
] });
|
|
5971
|
+
})() : selectedBackupA || selectedBackupB ? /* @__PURE__ */ jsx("div", { className: "py-4 text-center text-muted-foreground text-sm", children: "Select both versions to compare" }) : null
|
|
5972
|
+
] }),
|
|
5973
|
+
!diffMode && /* @__PURE__ */ jsx(Fragment, { children: loadingBackups ? /* @__PURE__ */ jsx("div", { className: "py-8 text-center text-muted-foreground", children: "Loading backups..." }) : backups.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "py-8 text-center text-muted-foreground", children: [
|
|
5641
5974
|
/* @__PURE__ */ jsx(ClockCounterClockwise, { size: 32, className: "mx-auto mb-2 opacity-50" }),
|
|
5642
5975
|
/* @__PURE__ */ jsx("p", { children: "No backups yet" }),
|
|
5643
5976
|
/* @__PURE__ */ jsx("p", { className: "text-xs mt-1", children: 'Click "Export from Sim" to create a backup' })
|
|
5644
|
-
] }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: backups.map((backup) => /* @__PURE__ */
|
|
5977
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: backups.map((backup, index) => /* @__PURE__ */ jsxs(
|
|
5645
5978
|
"div",
|
|
5646
5979
|
{
|
|
5647
5980
|
className: "flex items-center justify-between p-3 bg-muted/50 rounded border border-border",
|
|
5648
|
-
children:
|
|
5649
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center
|
|
5650
|
-
"
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
/* @__PURE__ */
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5981
|
+
children: [
|
|
5982
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
5983
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center w-8 h-8 rounded bg-primary/10 text-primary text-sm font-semibold", children: [
|
|
5984
|
+
"v",
|
|
5985
|
+
backup.version
|
|
5986
|
+
] }),
|
|
5987
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5988
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: backup.versionLabel || backup.workflowName }),
|
|
5989
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
5990
|
+
formatDistance(new Date(backup.exportedAt), { addSuffix: true }),
|
|
5991
|
+
backup.isDeployed && /* @__PURE__ */ jsx(Badge, { variant: "success", size: "sm", className: "ml-2", children: "Deployed" })
|
|
5992
|
+
] })
|
|
5658
5993
|
] })
|
|
5659
|
-
] })
|
|
5660
|
-
|
|
5994
|
+
] }),
|
|
5995
|
+
index < backups.length - 1 && apiHandlers?.getBackupState && /* @__PURE__ */ jsx(
|
|
5996
|
+
Button,
|
|
5997
|
+
{
|
|
5998
|
+
variant: "ghost",
|
|
5999
|
+
size: "sm",
|
|
6000
|
+
onClick: () => {
|
|
6001
|
+
startDiffMode();
|
|
6002
|
+
loadBackupForDiff(backups[index + 1].id, "A");
|
|
6003
|
+
loadBackupForDiff(backup.id, "B");
|
|
6004
|
+
},
|
|
6005
|
+
icon: /* @__PURE__ */ jsx(ArrowsLeftRight, { size: 14 }),
|
|
6006
|
+
children: "Diff"
|
|
6007
|
+
}
|
|
6008
|
+
)
|
|
6009
|
+
]
|
|
5661
6010
|
},
|
|
5662
6011
|
backup.id
|
|
5663
|
-
)) })
|
|
6012
|
+
)) }) })
|
|
5664
6013
|
] }) : platform === "n8n" ? /* @__PURE__ */ jsx(N8nWorkflowSummary, { workflow, showFlow: false }) : /* @__PURE__ */ jsx(SimWorkflowSummary, { workflow }) })
|
|
5665
6014
|
] })
|
|
5666
6015
|
] });
|