mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
fix(editor): Improve WorkflowDiffModal UI (#17862)
This commit is contained in:
@@ -340,7 +340,8 @@ const modifiers = [
|
|||||||
<N8nIconButton
|
<N8nIconButton
|
||||||
icon="arrow-left"
|
icon="arrow-left"
|
||||||
type="secondary"
|
type="secondary"
|
||||||
class="mr-xs"
|
:class="[$style.backButton, 'mr-xs']"
|
||||||
|
icon-size="large"
|
||||||
@click="closeDialog"
|
@click="closeDialog"
|
||||||
></N8nIconButton>
|
></N8nIconButton>
|
||||||
<N8nHeading tag="h1" size="xlarge">
|
<N8nHeading tag="h1" size="xlarge">
|
||||||
@@ -359,7 +360,7 @@ const modifiers = [
|
|||||||
modifiers,
|
modifiers,
|
||||||
}"
|
}"
|
||||||
:popper-class="$style.popper"
|
:popper-class="$style.popper"
|
||||||
class="mr-xs"
|
class="mr-2xs"
|
||||||
@visible-change="setActiveTab"
|
@visible-change="setActiveTab"
|
||||||
>
|
>
|
||||||
<N8nButton type="secondary">
|
<N8nButton type="secondary">
|
||||||
@@ -397,7 +398,7 @@ const modifiers = [
|
|||||||
@click.prevent="setSelectedDetailId(change.node.id, activeTab)"
|
@click.prevent="setSelectedDetailId(change.node.id, activeTab)"
|
||||||
>
|
>
|
||||||
<DiffBadge :type="change.status" />
|
<DiffBadge :type="change.status" />
|
||||||
<NodeIcon :node-type="change.type" :size="16" />
|
<NodeIcon :node-type="change.type" :size="16" class="ml-2xs mr-4xs" />
|
||||||
{{ change.node.name }}
|
{{ change.node.name }}
|
||||||
</ElDropdownItem>
|
</ElDropdownItem>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -418,7 +419,11 @@ const modifiers = [
|
|||||||
setSelectedDetailId(change[1].connection.source?.id, activeTab)
|
setSelectedDetailId(change[1].connection.source?.id, activeTab)
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<NodeIcon :node-type="change[1].connection.sourceType" :size="16" />
|
<NodeIcon
|
||||||
|
:node-type="change[1].connection.sourceType"
|
||||||
|
:size="16"
|
||||||
|
class="ml-2xs mr-4xs"
|
||||||
|
/>
|
||||||
{{ change[1].connection.source?.name }}
|
{{ change[1].connection.source?.name }}
|
||||||
</ElDropdownItem>
|
</ElDropdownItem>
|
||||||
<div :class="$style.separator"></div>
|
<div :class="$style.separator"></div>
|
||||||
@@ -432,7 +437,11 @@ const modifiers = [
|
|||||||
setSelectedDetailId(change[1].connection.target?.id, activeTab)
|
setSelectedDetailId(change[1].connection.target?.id, activeTab)
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<NodeIcon :node-type="change[1].connection.targetType" :size="16" />
|
<NodeIcon
|
||||||
|
:node-type="change[1].connection.targetType"
|
||||||
|
:size="16"
|
||||||
|
class="ml-2xs mr-4xs"
|
||||||
|
/>
|
||||||
{{ change[1].connection.target?.name }}
|
{{ change[1].connection.target?.name }}
|
||||||
</ElDropdownItem>
|
</ElDropdownItem>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -459,6 +468,7 @@ const modifiers = [
|
|||||||
<N8nIconButton
|
<N8nIconButton
|
||||||
icon="chevron-left"
|
icon="chevron-left"
|
||||||
type="secondary"
|
type="secondary"
|
||||||
|
class="mr-2xs"
|
||||||
:class="$style.navigationButton"
|
:class="$style.navigationButton"
|
||||||
@click="previousNodeChange"
|
@click="previousNodeChange"
|
||||||
></N8nIconButton>
|
></N8nIconButton>
|
||||||
@@ -507,11 +517,11 @@ const modifiers = [
|
|||||||
<template v-else>
|
<template v-else>
|
||||||
<div :class="$style.emptyWorkflow">
|
<div :class="$style.emptyWorkflow">
|
||||||
<template v-if="targetWorkFlow.state.value?.remote">
|
<template v-if="targetWorkFlow.state.value?.remote">
|
||||||
<N8nText color="text-dark" size="large"> Deleted workflow </N8nText>
|
<N8nHeading size="large"> Deleted workflow </N8nHeading>
|
||||||
<N8nText color="text-base"> The workflow was deleted on the database </N8nText>
|
<N8nText color="text-base"> The workflow was deleted on the database </N8nText>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<N8nText color="text-dark" size="large"> Deleted workflow </N8nText>
|
<N8nHeading size="large"> Deleted workflow </N8nHeading>
|
||||||
<N8nText color="text-base"> The workflow was deleted on remote </N8nText>
|
<N8nText color="text-base"> The workflow was deleted on remote </N8nText>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -551,11 +561,11 @@ const modifiers = [
|
|||||||
<template v-else>
|
<template v-else>
|
||||||
<div :class="$style.emptyWorkflow">
|
<div :class="$style.emptyWorkflow">
|
||||||
<template v-if="targetWorkFlow.state.value?.remote">
|
<template v-if="targetWorkFlow.state.value?.remote">
|
||||||
<N8nText color="text-dark" size="large"> Deleted workflow </N8nText>
|
<N8nHeading size="large"> Deleted workflow </N8nHeading>
|
||||||
<N8nText color="text-base"> The workflow was deleted on remote </N8nText>
|
<N8nText color="text-base"> The workflow was deleted on remote </N8nText>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<N8nText color="text-dark" size="large"> Deleted workflow </N8nText>
|
<N8nHeading size="large"> Deleted workflow </N8nHeading>
|
||||||
<N8nText color="text-base"> The workflow was deleted on the data base </N8nText>
|
<N8nText color="text-base"> The workflow was deleted on the data base </N8nText>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -577,7 +587,34 @@ const modifiers = [
|
|||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style module>
|
<style module lang="scss">
|
||||||
|
/* Light theme diff colors */
|
||||||
|
:root,
|
||||||
|
[data-theme='light'] {
|
||||||
|
--diff-new: #0eab54;
|
||||||
|
--diff-new-light: #b4efc4;
|
||||||
|
--diff-new-faint: #ddfbe7;
|
||||||
|
--diff-modified: #bf941f;
|
||||||
|
--diff-modified-light: #f3dca1;
|
||||||
|
--diff-modified-faint: #fbf1d4;
|
||||||
|
--diff-del: #f51f32;
|
||||||
|
--diff-del-light: #fad3d0;
|
||||||
|
--diff-del-faint: #ffedec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark theme diff colors */
|
||||||
|
[data-theme='dark'] {
|
||||||
|
--diff-new: #38cb7a;
|
||||||
|
--diff-new-light: #43674f;
|
||||||
|
--diff-new-faint: #3a463e;
|
||||||
|
--diff-modified: #d6a625;
|
||||||
|
--diff-modified-light: #6a5c38;
|
||||||
|
--diff-modified-faint: #464236;
|
||||||
|
--diff-del: #fb887a;
|
||||||
|
--diff-del-light: #7a524e;
|
||||||
|
--diff-del-faint: #4d3e3d;
|
||||||
|
}
|
||||||
|
|
||||||
.workflowDiffModal {
|
.workflowDiffModal {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
@@ -587,7 +624,7 @@ const modifiers = [
|
|||||||
}
|
}
|
||||||
:global(.el-dialog__header) {
|
:global(.el-dialog__header) {
|
||||||
padding: 11px 16px;
|
padding: 11px 16px;
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-foreground-base);
|
||||||
}
|
}
|
||||||
:global(.el-dialog__headerbtn) {
|
:global(.el-dialog__headerbtn) {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -629,50 +666,58 @@ const modifiers = [
|
|||||||
}
|
}
|
||||||
|
|
||||||
.changes {
|
.changes {
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
> li {
|
> li {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 8px;
|
gap: var(--spacing-2xs);
|
||||||
padding: 8px;
|
padding: 10px 0 var(--spacing-3xs) var(--spacing-2xs);
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin-top: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickableChange {
|
||||||
|
padding: var(--spacing-3xs) var(--spacing-xs) var(--spacing-3xs) 0;
|
||||||
|
margin-left: -4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clickableChange {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: var(--spacing-2xs);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: var(--spacing-xs) var(--spacing-2xs);
|
||||||
|
line-height: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickableChangeActive {
|
||||||
|
background-color: var(--color-background-medium);
|
||||||
|
}
|
||||||
|
|
||||||
.separator {
|
.separator {
|
||||||
width: 1px;
|
width: 1px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
background-color: var(--color-foreground-xdark);
|
background-color: var(--color-foreground-xdark);
|
||||||
margin: -5px 23px;
|
margin: 0 0 -5px var(--spacing-xs);
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clickableChange {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clickableChangeActive {
|
|
||||||
background-color: var(--color-background-medium);
|
|
||||||
}
|
|
||||||
|
|
||||||
.deleted,
|
.deleted,
|
||||||
.added,
|
.added,
|
||||||
.modified {
|
.modified {
|
||||||
position: relative;
|
position: relative;
|
||||||
&::before {
|
&::before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 50%;
|
||||||
border-bottom-left-radius: 6px;
|
transform: translate(-50%, -50%);
|
||||||
border-top-right-radius: 2px;
|
border-radius: 4px;
|
||||||
color: var(--color-text-xlight);
|
color: var(--color-text-xlight);
|
||||||
font-family: var(--font-family-monospace);
|
font-family: Inter, var(--font-family);
|
||||||
font-size: 8px;
|
font-size: 10px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
@@ -681,51 +726,49 @@ const modifiers = [
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
&[data-node-type='n8n-nodes-base.stickyNote'],
|
|
||||||
&[data-node-type='n8n-nodes-base.manualTrigger'] {
|
|
||||||
&::before {
|
|
||||||
left: auto;
|
|
||||||
right: 0;
|
|
||||||
border-top-right-radius: 0;
|
|
||||||
border-top-left-radius: 2px;
|
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
border-bottom-right-radius: 6px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.deleted {
|
.deleted {
|
||||||
--canvas-node--background: rgba(234, 31, 48, 0.2);
|
--canvas-node--background: var(--diff-del-faint);
|
||||||
--canvas-node--border-color: var(--color-node-icon-red);
|
--canvas-node--border-color: var(--diff-del);
|
||||||
--color-sticky-background: rgba(234, 31, 48, 0.2);
|
--color-sticky-background: var(--diff-del-faint);
|
||||||
--color-sticky-border: var(--color-node-icon-red);
|
--color-sticky-border: var(--diff-del);
|
||||||
&::before {
|
&::before {
|
||||||
content: 'D';
|
content: 'D';
|
||||||
background-color: var(--color-node-icon-red);
|
background-color: var(--diff-del);
|
||||||
}
|
}
|
||||||
:global(.canvas-node-handle-main-output > div) {
|
:global(.canvas-node-handle-main-output > div:empty) {
|
||||||
background-color: var(--color-node-icon-red);
|
background-color: var(--diff-del);
|
||||||
}
|
}
|
||||||
:global(.canvas-node-handle-main-input .target) {
|
:global(.canvas-node-handle-main-input .target) {
|
||||||
background-color: var(--color-node-icon-red);
|
background-color: var(--diff-del);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure disabled nodes still show diff border color */
|
||||||
|
:global([class*='disabled']) {
|
||||||
|
--canvas-node--border-color: var(--diff-del) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.added {
|
.added {
|
||||||
--canvas-node--border-color: var(--color-node-icon-green);
|
--canvas-node--border-color: var(--diff-new);
|
||||||
--canvas-node--background: rgba(14, 171, 84, 0.2);
|
--canvas-node--background: var(--diff-new-faint);
|
||||||
--color-sticky-background: rgba(14, 171, 84, 0.2);
|
--color-sticky-background: var(--diff-new-faint);
|
||||||
--color-sticky-border: var(--color-node-icon-green);
|
--color-sticky-border: var(--diff-new);
|
||||||
position: relative;
|
position: relative;
|
||||||
&::before {
|
&::before {
|
||||||
content: 'N';
|
content: 'N';
|
||||||
background-color: var(--color-node-icon-green);
|
background-color: var(--diff-new);
|
||||||
}
|
}
|
||||||
:global(.canvas-node-handle-main-output > div) {
|
:global(.canvas-node-handle-main-output > div:empty) {
|
||||||
background-color: var(--color-node-icon-green);
|
background-color: var(--diff-new);
|
||||||
}
|
}
|
||||||
:global(.canvas-node-handle-main-input .target) {
|
:global(.canvas-node-handle-main-input .target) {
|
||||||
background-color: var(--color-node-icon-green);
|
background-color: var(--diff-new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure disabled nodes still show diff border color */
|
||||||
|
:global([class*='disabled']) {
|
||||||
|
--canvas-node--border-color: var(--diff-new) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.equal {
|
.equal {
|
||||||
@@ -741,30 +784,35 @@ const modifiers = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.modified {
|
.modified {
|
||||||
--canvas-node--border-color: var(--color-node-icon-orange);
|
--canvas-node--border-color: var(--diff-modified);
|
||||||
--canvas-node--background: rgba(255, 150, 90, 0.2);
|
--canvas-node--background: var(--diff-modified-faint);
|
||||||
--color-sticky-background: rgba(255, 150, 90, 0.2);
|
--color-sticky-background: var(--diff-modified-faint);
|
||||||
--color-sticky-border: var(--color-node-icon-orange);
|
--color-sticky-border: var(--diff-modified);
|
||||||
position: relative;
|
position: relative;
|
||||||
&::before {
|
&::before {
|
||||||
content: 'M';
|
content: 'M';
|
||||||
background-color: var(--color-node-icon-orange);
|
background-color: var(--diff-modified);
|
||||||
}
|
}
|
||||||
:global(.canvas-node-handle-main-output .source) {
|
:global(.canvas-node-handle-main-output > div:empty) {
|
||||||
--color-foreground-xdark: var(--color-node-icon-orange);
|
background-color: var(--diff-modified);
|
||||||
}
|
}
|
||||||
:global(.canvas-node-handle-main-input .target) {
|
:global(.canvas-node-handle-main-input .target) {
|
||||||
background-color: var(--color-node-icon-orange);
|
background-color: var(--diff-modified);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure disabled nodes still show diff border color */
|
||||||
|
:global([class*='disabled']) {
|
||||||
|
--canvas-node--border-color: var(--diff-modified) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.edge-deleted {
|
.edge-deleted {
|
||||||
--canvas-edge-color: var(--color-node-icon-red);
|
--canvas-edge-color: var(--diff-del);
|
||||||
--edge-highlight-color: rgba(234, 31, 48, 0.2);
|
--edge-highlight-color: var(--diff-del-light);
|
||||||
}
|
}
|
||||||
.edge-added {
|
.edge-added {
|
||||||
--canvas-edge-color: var(--color-node-icon-green);
|
--canvas-edge-color: var(--diff-new);
|
||||||
--edge-highlight-color: rgba(14, 171, 84, 0.2);
|
--edge-highlight-color: var(--diff-new-light);
|
||||||
}
|
}
|
||||||
.edge-equal {
|
.edge-equal {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
@@ -772,7 +820,7 @@ const modifiers = [
|
|||||||
|
|
||||||
.noNumberDiff {
|
.noNumberDiff {
|
||||||
min-height: 41px;
|
min-height: 41px;
|
||||||
margin-bottom: 10px !important;
|
margin-bottom: 10px;
|
||||||
:global(.blob-num) {
|
:global(.blob-num) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -785,8 +833,8 @@ const modifiers = [
|
|||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: var(--color-background-medium);
|
background-color: var(--color-primary);
|
||||||
color: var(--color-text-dark);
|
color: var(--color-text-xlight);
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
@@ -795,6 +843,12 @@ const modifiers = [
|
|||||||
.dropdownContent {
|
.dropdownContent {
|
||||||
min-width: 300px;
|
min-width: 300px;
|
||||||
padding: 2px 12px;
|
padding: 2px 12px;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflowDiffContent {
|
.workflowDiffContent {
|
||||||
@@ -812,7 +866,6 @@ const modifiers = [
|
|||||||
.workflowDiffPanel {
|
.workflowDiffPanel {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-top: 1px solid #ddd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.emptyWorkflow {
|
.emptyWorkflow {
|
||||||
@@ -827,15 +880,19 @@ const modifiers = [
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.navigationButton {
|
||||||
|
height: 34px;
|
||||||
|
width: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backButton {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.headerLeft {
|
.headerLeft {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigationButton {
|
|
||||||
height: 34px !important;
|
|
||||||
width: 34px !important;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -173,12 +173,17 @@ export const useWorkflowDiff = (
|
|||||||
targetRefs.workflowObjectRef,
|
targetRefs.workflowObjectRef,
|
||||||
);
|
);
|
||||||
|
|
||||||
const nodesDiff = computed(() =>
|
const nodesDiff = computed(() => {
|
||||||
compareWorkflowsNodes(
|
// Don't compute diff until both workflows are loaded to prevent initial flashing
|
||||||
|
if (!source.value?.workflow?.value || !target.value?.workflow?.value) {
|
||||||
|
return new Map<string, NodeDiff<INodeUi>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return compareWorkflowsNodes(
|
||||||
source.value.workflow?.value?.nodes ?? [],
|
source.value.workflow?.value?.nodes ?? [],
|
||||||
target.value.workflow?.value?.nodes ?? [],
|
target.value.workflow?.value?.nodes ?? [],
|
||||||
),
|
);
|
||||||
);
|
});
|
||||||
|
|
||||||
type Connection = {
|
type Connection = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -217,6 +222,11 @@ export const useWorkflowDiff = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const connectionsDiff = computed(() => {
|
const connectionsDiff = computed(() => {
|
||||||
|
// Don't compute diff until both workflows are loaded to prevent initial flashing
|
||||||
|
if (!source.value?.workflow?.value || !target.value?.workflow?.value) {
|
||||||
|
return new Map<string, { status: NodeDiffStatus; connection: Connection }>();
|
||||||
|
}
|
||||||
|
|
||||||
const sourceConnections = mapConnections(source.value?.connections ?? []);
|
const sourceConnections = mapConnections(source.value?.connections ?? []);
|
||||||
const targetConnections = mapConnections(target.value?.connections ?? []);
|
const targetConnections = mapConnections(target.value?.connections ?? []);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user