mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
fix(editor): Improve WorkflowDiffModal UI (#17862)
This commit is contained in:
@@ -340,7 +340,8 @@ const modifiers = [
|
||||
<N8nIconButton
|
||||
icon="arrow-left"
|
||||
type="secondary"
|
||||
class="mr-xs"
|
||||
:class="[$style.backButton, 'mr-xs']"
|
||||
icon-size="large"
|
||||
@click="closeDialog"
|
||||
></N8nIconButton>
|
||||
<N8nHeading tag="h1" size="xlarge">
|
||||
@@ -359,7 +360,7 @@ const modifiers = [
|
||||
modifiers,
|
||||
}"
|
||||
:popper-class="$style.popper"
|
||||
class="mr-xs"
|
||||
class="mr-2xs"
|
||||
@visible-change="setActiveTab"
|
||||
>
|
||||
<N8nButton type="secondary">
|
||||
@@ -397,7 +398,7 @@ const modifiers = [
|
||||
@click.prevent="setSelectedDetailId(change.node.id, activeTab)"
|
||||
>
|
||||
<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 }}
|
||||
</ElDropdownItem>
|
||||
</ul>
|
||||
@@ -418,7 +419,11 @@ const modifiers = [
|
||||
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 }}
|
||||
</ElDropdownItem>
|
||||
<div :class="$style.separator"></div>
|
||||
@@ -432,7 +437,11 @@ const modifiers = [
|
||||
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 }}
|
||||
</ElDropdownItem>
|
||||
</ul>
|
||||
@@ -459,6 +468,7 @@ const modifiers = [
|
||||
<N8nIconButton
|
||||
icon="chevron-left"
|
||||
type="secondary"
|
||||
class="mr-2xs"
|
||||
:class="$style.navigationButton"
|
||||
@click="previousNodeChange"
|
||||
></N8nIconButton>
|
||||
@@ -507,11 +517,11 @@ const modifiers = [
|
||||
<template v-else>
|
||||
<div :class="$style.emptyWorkflow">
|
||||
<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>
|
||||
</template>
|
||||
<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>
|
||||
</template>
|
||||
</div>
|
||||
@@ -551,11 +561,11 @@ const modifiers = [
|
||||
<template v-else>
|
||||
<div :class="$style.emptyWorkflow">
|
||||
<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>
|
||||
</template>
|
||||
<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>
|
||||
</template>
|
||||
</div>
|
||||
@@ -577,7 +587,34 @@ const modifiers = [
|
||||
</Modal>
|
||||
</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 {
|
||||
margin-bottom: 0;
|
||||
border-radius: 0;
|
||||
@@ -587,7 +624,7 @@ const modifiers = [
|
||||
}
|
||||
:global(.el-dialog__header) {
|
||||
padding: 11px 16px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
border-bottom: 1px solid var(--color-foreground-base);
|
||||
}
|
||||
:global(.el-dialog__headerbtn) {
|
||||
display: none;
|
||||
@@ -629,50 +666,58 @@ const modifiers = [
|
||||
}
|
||||
|
||||
.changes {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
> li {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
gap: var(--spacing-2xs);
|
||||
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 {
|
||||
width: 1px;
|
||||
height: 10px;
|
||||
background-color: var(--color-foreground-xdark);
|
||||
margin: -5px 23px;
|
||||
margin: 0 0 -5px var(--spacing-xs);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.clickableChange {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.clickableChangeActive {
|
||||
background-color: var(--color-background-medium);
|
||||
}
|
||||
|
||||
.deleted,
|
||||
.added,
|
||||
.modified {
|
||||
position: relative;
|
||||
&::before {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border-bottom-left-radius: 6px;
|
||||
border-top-right-radius: 2px;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 4px;
|
||||
color: var(--color-text-xlight);
|
||||
font-family: var(--font-family-monospace);
|
||||
font-size: 8px;
|
||||
font-family: Inter, var(--font-family);
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
z-index: 1;
|
||||
width: 16px;
|
||||
@@ -681,51 +726,49 @@ const modifiers = [
|
||||
justify-content: 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 {
|
||||
--canvas-node--background: rgba(234, 31, 48, 0.2);
|
||||
--canvas-node--border-color: var(--color-node-icon-red);
|
||||
--color-sticky-background: rgba(234, 31, 48, 0.2);
|
||||
--color-sticky-border: var(--color-node-icon-red);
|
||||
--canvas-node--background: var(--diff-del-faint);
|
||||
--canvas-node--border-color: var(--diff-del);
|
||||
--color-sticky-background: var(--diff-del-faint);
|
||||
--color-sticky-border: var(--diff-del);
|
||||
&::before {
|
||||
content: 'D';
|
||||
background-color: var(--color-node-icon-red);
|
||||
background-color: var(--diff-del);
|
||||
}
|
||||
:global(.canvas-node-handle-main-output > div) {
|
||||
background-color: var(--color-node-icon-red);
|
||||
:global(.canvas-node-handle-main-output > div:empty) {
|
||||
background-color: var(--diff-del);
|
||||
}
|
||||
: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 {
|
||||
--canvas-node--border-color: var(--color-node-icon-green);
|
||||
--canvas-node--background: rgba(14, 171, 84, 0.2);
|
||||
--color-sticky-background: rgba(14, 171, 84, 0.2);
|
||||
--color-sticky-border: var(--color-node-icon-green);
|
||||
--canvas-node--border-color: var(--diff-new);
|
||||
--canvas-node--background: var(--diff-new-faint);
|
||||
--color-sticky-background: var(--diff-new-faint);
|
||||
--color-sticky-border: var(--diff-new);
|
||||
position: relative;
|
||||
&::before {
|
||||
content: 'N';
|
||||
background-color: var(--color-node-icon-green);
|
||||
background-color: var(--diff-new);
|
||||
}
|
||||
:global(.canvas-node-handle-main-output > div) {
|
||||
background-color: var(--color-node-icon-green);
|
||||
:global(.canvas-node-handle-main-output > div:empty) {
|
||||
background-color: var(--diff-new);
|
||||
}
|
||||
: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 {
|
||||
@@ -741,30 +784,35 @@ const modifiers = [
|
||||
}
|
||||
}
|
||||
.modified {
|
||||
--canvas-node--border-color: var(--color-node-icon-orange);
|
||||
--canvas-node--background: rgba(255, 150, 90, 0.2);
|
||||
--color-sticky-background: rgba(255, 150, 90, 0.2);
|
||||
--color-sticky-border: var(--color-node-icon-orange);
|
||||
--canvas-node--border-color: var(--diff-modified);
|
||||
--canvas-node--background: var(--diff-modified-faint);
|
||||
--color-sticky-background: var(--diff-modified-faint);
|
||||
--color-sticky-border: var(--diff-modified);
|
||||
position: relative;
|
||||
&::before {
|
||||
content: 'M';
|
||||
background-color: var(--color-node-icon-orange);
|
||||
background-color: var(--diff-modified);
|
||||
}
|
||||
:global(.canvas-node-handle-main-output .source) {
|
||||
--color-foreground-xdark: var(--color-node-icon-orange);
|
||||
:global(.canvas-node-handle-main-output > div:empty) {
|
||||
background-color: var(--diff-modified);
|
||||
}
|
||||
: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 {
|
||||
--canvas-edge-color: var(--color-node-icon-red);
|
||||
--edge-highlight-color: rgba(234, 31, 48, 0.2);
|
||||
--canvas-edge-color: var(--diff-del);
|
||||
--edge-highlight-color: var(--diff-del-light);
|
||||
}
|
||||
.edge-added {
|
||||
--canvas-edge-color: var(--color-node-icon-green);
|
||||
--edge-highlight-color: rgba(14, 171, 84, 0.2);
|
||||
--canvas-edge-color: var(--diff-new);
|
||||
--edge-highlight-color: var(--diff-new-light);
|
||||
}
|
||||
.edge-equal {
|
||||
opacity: 0.5;
|
||||
@@ -772,7 +820,7 @@ const modifiers = [
|
||||
|
||||
.noNumberDiff {
|
||||
min-height: 41px;
|
||||
margin-bottom: 10px !important;
|
||||
margin-bottom: 10px;
|
||||
:global(.blob-num) {
|
||||
display: none;
|
||||
}
|
||||
@@ -785,8 +833,8 @@ const modifiers = [
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-background-medium);
|
||||
color: var(--color-text-dark);
|
||||
background-color: var(--color-primary);
|
||||
color: var(--color-text-xlight);
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
@@ -795,6 +843,12 @@ const modifiers = [
|
||||
.dropdownContent {
|
||||
min-width: 300px;
|
||||
padding: 2px 12px;
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.workflowDiffContent {
|
||||
@@ -812,7 +866,6 @@ const modifiers = [
|
||||
.workflowDiffPanel {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.emptyWorkflow {
|
||||
@@ -827,15 +880,19 @@ const modifiers = [
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.navigationButton {
|
||||
height: 34px;
|
||||
width: 34px;
|
||||
}
|
||||
|
||||
.backButton {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.headerLeft {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.navigationButton {
|
||||
height: 34px !important;
|
||||
width: 34px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -173,12 +173,17 @@ export const useWorkflowDiff = (
|
||||
targetRefs.workflowObjectRef,
|
||||
);
|
||||
|
||||
const nodesDiff = computed(() =>
|
||||
compareWorkflowsNodes(
|
||||
const nodesDiff = 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, NodeDiff<INodeUi>>();
|
||||
}
|
||||
|
||||
return compareWorkflowsNodes(
|
||||
source.value.workflow?.value?.nodes ?? [],
|
||||
target.value.workflow?.value?.nodes ?? [],
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
type Connection = {
|
||||
id: string;
|
||||
@@ -217,6 +222,11 @@ export const useWorkflowDiff = (
|
||||
}
|
||||
|
||||
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 targetConnections = mapConnections(target.value?.connections ?? []);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user