mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
feat: Replace all Vue.set usages with direct assignment and spread operator (no-changelog) (#6280)
* refactor: replace all Vue.set usages with direct assignment and spread operator * chore: fix linting issue * fix: fix updateNodeAtIndex function * fix: various post-refactoring fixes * fix: refactor recently added Vue.set directive
This commit is contained in:
@@ -29,7 +29,6 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
|||||||
WorkflowPage.actions.visit();
|
WorkflowPage.actions.visit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should add switch node and test connections', () => {
|
it('should add switch node and test connections', () => {
|
||||||
WorkflowPage.actions.addNodeToCanvas(SWITCH_NODE_NAME, true);
|
WorkflowPage.actions.addNodeToCanvas(SWITCH_NODE_NAME, true);
|
||||||
|
|
||||||
@@ -114,7 +113,7 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
|||||||
WorkflowPage.actions.zoomToFit();
|
WorkflowPage.actions.zoomToFit();
|
||||||
|
|
||||||
cy.get('.plus-draggable-endpoint').filter(':visible').should('not.have.class', 'ep-success');
|
cy.get('.plus-draggable-endpoint').filter(':visible').should('not.have.class', 'ep-success');
|
||||||
cy.get('.jtk-connector.success').should('have.length', 3);
|
cy.get('.jtk-connector.success').should('have.length', 4);
|
||||||
cy.get('.jtk-connector').should('have.length', 4);
|
cy.get('.jtk-connector').should('have.length', 4);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -16,12 +16,10 @@ describe('Data mapping', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
workflowPage.actions.visit();
|
workflowPage.actions.visit();
|
||||||
|
|
||||||
cy.window().then(
|
cy.window().then((win) => {
|
||||||
(win) => {
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
win.preventNodeViewBeforeUnload = true;
|
win.preventNodeViewBeforeUnload = true;
|
||||||
},
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('maps expressions from table header', () => {
|
it('maps expressions from table header', () => {
|
||||||
@@ -303,19 +301,28 @@ describe('Data mapping', () => {
|
|||||||
|
|
||||||
ndv.getters.parameterInput('keepOnlySet').find('input[type="checkbox"]').should('exist');
|
ndv.getters.parameterInput('keepOnlySet').find('input[type="checkbox"]').should('exist');
|
||||||
ndv.getters.parameterInput('keepOnlySet').find('input[type="text"]').should('not.exist');
|
ndv.getters.parameterInput('keepOnlySet').find('input[type="text"]').should('not.exist');
|
||||||
ndv.getters.inputDataContainer().should('exist').find('span').contains('count').realMouseDown().realMouseMove(100, 100);
|
ndv.getters
|
||||||
|
.inputDataContainer()
|
||||||
|
.should('exist')
|
||||||
|
.find('span')
|
||||||
|
.contains('count')
|
||||||
|
.realMouseDown()
|
||||||
|
.realMouseMove(100, 100);
|
||||||
cy.wait(50);
|
cy.wait(50);
|
||||||
|
|
||||||
ndv.getters.parameterInput('keepOnlySet').find('input[type="checkbox"]').should('not.exist');
|
ndv.getters.parameterInput('keepOnlySet').find('input[type="checkbox"]').should('not.exist');
|
||||||
ndv.getters.parameterInput('keepOnlySet').find('input[type="text"]')
|
ndv.getters
|
||||||
|
.parameterInput('keepOnlySet')
|
||||||
|
.find('input[type="text"]')
|
||||||
.should('exist')
|
.should('exist')
|
||||||
.invoke('css', 'border')
|
.invoke('css', 'border')
|
||||||
.then((border) => expect(border).to.include('dashed rgb(90, 76, 194)'));
|
.then((border) => expect(border).to.include('dashed rgb(90, 76, 194)'));
|
||||||
|
|
||||||
ndv.getters.parameterInput('value').find('input[type="text"]')
|
ndv.getters
|
||||||
|
.parameterInput('value')
|
||||||
|
.find('input[type="text"]')
|
||||||
.should('exist')
|
.should('exist')
|
||||||
.invoke('css', 'border')
|
.invoke('css', 'border')
|
||||||
.then((border) => expect(border).to.include('dashed rgb(90, 76, 194)'));
|
.then((border) => expect(border).to.include('dashed rgb(90, 76, 194)'));
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,14 +13,17 @@ export class NDV extends BasePage {
|
|||||||
outputPanel: () => cy.getByTestId('output-panel'),
|
outputPanel: () => cy.getByTestId('output-panel'),
|
||||||
executingLoader: () => cy.getByTestId('ndv-executing'),
|
executingLoader: () => cy.getByTestId('ndv-executing'),
|
||||||
inputDataContainer: () => this.getters.inputPanel().findChildByTestId('ndv-data-container'),
|
inputDataContainer: () => this.getters.inputPanel().findChildByTestId('ndv-data-container'),
|
||||||
inputDisplayMode: () => this.getters.inputPanel().findChildByTestId('ndv-run-data-display-mode').first(),
|
inputDisplayMode: () =>
|
||||||
|
this.getters.inputPanel().findChildByTestId('ndv-run-data-display-mode').first(),
|
||||||
outputDataContainer: () => this.getters.outputPanel().findChildByTestId('ndv-data-container'),
|
outputDataContainer: () => this.getters.outputPanel().findChildByTestId('ndv-data-container'),
|
||||||
outputDisplayMode: () => this.getters.outputPanel().findChildByTestId('ndv-run-data-display-mode').first(),
|
outputDisplayMode: () =>
|
||||||
|
this.getters.outputPanel().findChildByTestId('ndv-run-data-display-mode').first(),
|
||||||
pinDataButton: () => cy.getByTestId('ndv-pin-data'),
|
pinDataButton: () => cy.getByTestId('ndv-pin-data'),
|
||||||
editPinnedDataButton: () => cy.getByTestId('ndv-edit-pinned-data'),
|
editPinnedDataButton: () => cy.getByTestId('ndv-edit-pinned-data'),
|
||||||
pinnedDataEditor: () => this.getters.outputPanel().find('.cm-editor .cm-scroller'),
|
pinnedDataEditor: () => this.getters.outputPanel().find('.cm-editor .cm-scroller'),
|
||||||
runDataPaneHeader: () => cy.getByTestId('run-data-pane-header'),
|
runDataPaneHeader: () => cy.getByTestId('run-data-pane-header'),
|
||||||
savePinnedDataButton: () => this.getters.runDataPaneHeader().find('button').filter(':visible').contains('Save'),
|
savePinnedDataButton: () =>
|
||||||
|
this.getters.runDataPaneHeader().find('button').filter(':visible').contains('Save'),
|
||||||
outputTableRows: () => this.getters.outputDataContainer().find('table tr'),
|
outputTableRows: () => this.getters.outputDataContainer().find('table tr'),
|
||||||
outputTableHeaders: () => this.getters.outputDataContainer().find('table thead th'),
|
outputTableHeaders: () => this.getters.outputDataContainer().find('table thead th'),
|
||||||
outputTableRow: (row: number) => this.getters.outputTableRows().eq(row),
|
outputTableRow: (row: number) => this.getters.outputTableRows().eq(row),
|
||||||
@@ -52,10 +55,13 @@ export class NDV extends BasePage {
|
|||||||
outputBranches: () => this.getters.outputPanel().findChildByTestId('branches'),
|
outputBranches: () => this.getters.outputPanel().findChildByTestId('branches'),
|
||||||
inputBranches: () => this.getters.inputPanel().findChildByTestId('branches'),
|
inputBranches: () => this.getters.inputPanel().findChildByTestId('branches'),
|
||||||
resourceLocator: (paramName: string) => cy.getByTestId(`resource-locator-${paramName}`),
|
resourceLocator: (paramName: string) => cy.getByTestId(`resource-locator-${paramName}`),
|
||||||
resourceLocatorInput: (paramName: string) => this.getters.resourceLocator(paramName).find('[data-test-id="rlc-input-container"]'),
|
resourceLocatorInput: (paramName: string) =>
|
||||||
resourceLocatorDropdown: (paramName: string) => this.getters.resourceLocator(paramName).find('[data-test-id="resource-locator-dropdown"]'),
|
this.getters.resourceLocator(paramName).find('[data-test-id="rlc-input-container"]'),
|
||||||
|
resourceLocatorDropdown: (paramName: string) =>
|
||||||
|
this.getters.resourceLocator(paramName).find('[data-test-id="resource-locator-dropdown"]'),
|
||||||
resourceLocatorErrorMessage: () => cy.getByTestId('rlc-error-container'),
|
resourceLocatorErrorMessage: () => cy.getByTestId('rlc-error-container'),
|
||||||
resourceLocatorModeSelector: (paramName: string) => this.getters.resourceLocator(paramName).find('[data-test-id="rlc-mode-selector"]'),
|
resourceLocatorModeSelector: (paramName: string) =>
|
||||||
|
this.getters.resourceLocator(paramName).find('[data-test-id="rlc-mode-selector"]'),
|
||||||
};
|
};
|
||||||
|
|
||||||
actions = {
|
actions = {
|
||||||
@@ -82,7 +88,9 @@ export class NDV extends BasePage {
|
|||||||
this.getters.editPinnedDataButton().click();
|
this.getters.editPinnedDataButton().click();
|
||||||
|
|
||||||
this.getters.pinnedDataEditor().click();
|
this.getters.pinnedDataEditor().click();
|
||||||
this.getters.pinnedDataEditor().type(`{selectall}{backspace}${JSON.stringify(data).replace(new RegExp('{', 'g'), '{{}')}`);
|
this.getters
|
||||||
|
.pinnedDataEditor()
|
||||||
|
.type(`{selectall}{backspace}${JSON.stringify(data).replace(new RegExp('{', 'g'), '{{}')}`);
|
||||||
|
|
||||||
this.actions.savePinnedData();
|
this.actions.savePinnedData();
|
||||||
},
|
},
|
||||||
@@ -131,15 +139,11 @@ export class NDV extends BasePage {
|
|||||||
},
|
},
|
||||||
changeInputRunSelector: (runName: string) => {
|
changeInputRunSelector: (runName: string) => {
|
||||||
this.getters.inputRunSelector().click();
|
this.getters.inputRunSelector().click();
|
||||||
cy.get('.el-select-dropdown:visible .el-select-dropdown__item')
|
cy.get('.el-select-dropdown:visible .el-select-dropdown__item').contains(runName).click();
|
||||||
.contains(runName)
|
|
||||||
.click();
|
|
||||||
},
|
},
|
||||||
changeOutputRunSelector: (runName: string) => {
|
changeOutputRunSelector: (runName: string) => {
|
||||||
this.getters.outputRunSelector().click();
|
this.getters.outputRunSelector().click();
|
||||||
cy.get('.el-select-dropdown:visible .el-select-dropdown__item')
|
cy.get('.el-select-dropdown:visible .el-select-dropdown__item').contains(runName).click();
|
||||||
.contains(runName)
|
|
||||||
.click();
|
|
||||||
},
|
},
|
||||||
toggleOutputRunLinking: () => {
|
toggleOutputRunLinking: () => {
|
||||||
this.getters.outputRunSelector().find('button').click();
|
this.getters.outputRunSelector().find('button').click();
|
||||||
@@ -159,7 +163,10 @@ export class NDV extends BasePage {
|
|||||||
this.getters.resourceLocatorInput(paramName).type(value);
|
this.getters.resourceLocatorInput(paramName).type(value);
|
||||||
},
|
},
|
||||||
validateExpressionPreview: (paramName: string, value: string) => {
|
validateExpressionPreview: (paramName: string, value: string) => {
|
||||||
this.getters.parameterExpressionPreview(paramName).find('span').should('include.html', asEncodedHTML(value));
|
this.getters
|
||||||
|
.parameterExpressionPreview(paramName)
|
||||||
|
.find('span')
|
||||||
|
.should('include.html', asEncodedHTML(value));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -172,4 +179,3 @@ function asEncodedHTML(str: string): string {
|
|||||||
.replace(/"/g, '"')
|
.replace(/"/g, '"')
|
||||||
.replace(/ /g, ' ');
|
.replace(/ /g, ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -975,13 +975,10 @@ export interface ITagsState {
|
|||||||
fetchedUsageCount: boolean;
|
fetchedUsageCount: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Modals =
|
export type Modals = {
|
||||||
| {
|
|
||||||
[key: string]: ModalState;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
[CREDENTIAL_EDIT_MODAL_KEY]: NewCredentialsModal;
|
[CREDENTIAL_EDIT_MODAL_KEY]: NewCredentialsModal;
|
||||||
};
|
[key: string]: ModalState;
|
||||||
|
};
|
||||||
|
|
||||||
export type ModalState = {
|
export type ModalState = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
} from '@/utils';
|
} from '@/utils';
|
||||||
import type { INodeProperties, INodeTypeDescription, NodeParameterValue } from 'n8n-workflow';
|
import type { INodeProperties, INodeTypeDescription, NodeParameterValue } from 'n8n-workflow';
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
credentialType: Object;
|
credentialType: Object;
|
||||||
@@ -27,7 +26,7 @@ const ndvStore = useNDVStore();
|
|||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
const selected = ref('');
|
const selected = ref('');
|
||||||
const authRelatedFieldsValues = ref({} as { [key: string]: NodeParameterValue });
|
const authRelatedFieldsValues = ref<{ [key: string]: NodeParameterValue }>({});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (activeNodeType.value?.credentials) {
|
if (activeNodeType.value?.credentials) {
|
||||||
@@ -43,7 +42,10 @@ onMounted(() => {
|
|||||||
|
|
||||||
// Populate default values of related fields
|
// Populate default values of related fields
|
||||||
authRelatedFields.value.forEach((field) => {
|
authRelatedFields.value.forEach((field) => {
|
||||||
Vue.set(authRelatedFieldsValues.value, field.name, field.default);
|
authRelatedFieldsValues.value = {
|
||||||
|
...authRelatedFieldsValues.value,
|
||||||
|
[field.name]: field.default as NodeParameterValue,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -102,7 +104,10 @@ function onAuthTypeChange(newType: string): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function valueChanged(data: IUpdateInformation): void {
|
function valueChanged(data: IUpdateInformation): void {
|
||||||
Vue.set(authRelatedFieldsValues.value, data.name, data.value);
|
authRelatedFieldsValues.value = {
|
||||||
|
...authRelatedFieldsValues.value,
|
||||||
|
[data.name]: data.value as NodeParameterValue,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ export default defineComponent({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
credentialTypeName(): string {
|
credentialTypeName(): string {
|
||||||
return (this.credentialType as ICredentialType).name;
|
return (this.credentialType as ICredentialType)?.name;
|
||||||
},
|
},
|
||||||
credentialOwnerName(): string {
|
credentialOwnerName(): string {
|
||||||
return this.credentialsStore.getCredentialOwnerNameById(`${this.credentialId}`);
|
return this.credentialsStore.getCredentialOwnerNameById(`${this.credentialId}`);
|
||||||
|
|||||||
@@ -109,7 +109,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
|
|
||||||
@@ -234,12 +233,15 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (this.currentUser) {
|
if (this.currentUser) {
|
||||||
Vue.set(this.credentialData, 'ownedBy', {
|
this.credentialData = {
|
||||||
|
...this.credentialData,
|
||||||
|
ownedBy: {
|
||||||
id: this.currentUser.id,
|
id: this.currentUser.id,
|
||||||
firstName: this.currentUser.firstName,
|
firstName: this.currentUser.firstName,
|
||||||
lastName: this.currentUser.lastName,
|
lastName: this.currentUser.lastName,
|
||||||
email: this.currentUser.email,
|
email: this.currentUser.email,
|
||||||
});
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await this.loadCurrentCredential();
|
await this.loadCurrentCredential();
|
||||||
@@ -251,7 +253,10 @@ export default defineComponent({
|
|||||||
!this.credentialData.hasOwnProperty(property.name) &&
|
!this.credentialData.hasOwnProperty(property.name) &&
|
||||||
!this.credentialType.__overwrittenProperties?.includes(property.name)
|
!this.credentialType.__overwrittenProperties?.includes(property.name)
|
||||||
) {
|
) {
|
||||||
Vue.set(this.credentialData, property.name, property.default as CredentialInformation);
|
this.credentialData = {
|
||||||
|
...this.credentialData,
|
||||||
|
[property.name]: property.default as CredentialInformation,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -594,12 +599,18 @@ export default defineComponent({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.credentialData = currentCredentials.data || {};
|
this.credentialData = (currentCredentials.data as ICredentialDataDecryptedObject) || {};
|
||||||
if (currentCredentials.sharedWith) {
|
if (currentCredentials.sharedWith) {
|
||||||
Vue.set(this.credentialData, 'sharedWith', currentCredentials.sharedWith);
|
this.credentialData = {
|
||||||
|
...this.credentialData,
|
||||||
|
sharedWith: currentCredentials.sharedWith as IDataObject[],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (currentCredentials.ownedBy) {
|
if (currentCredentials.ownedBy) {
|
||||||
Vue.set(this.credentialData, 'ownedBy', currentCredentials.ownedBy);
|
this.credentialData = {
|
||||||
|
...this.credentialData,
|
||||||
|
ownedBy: currentCredentials.ownedBy as IDataObject[],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.credentialName = currentCredentials.name;
|
this.credentialName = currentCredentials.name;
|
||||||
@@ -650,7 +661,10 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onChangeSharedWith(sharees: IDataObject[]) {
|
onChangeSharedWith(sharees: IDataObject[]) {
|
||||||
Vue.set(this.credentialData, 'sharedWith', sharees);
|
this.credentialData = {
|
||||||
|
...this.credentialData,
|
||||||
|
sharedWith: sharees,
|
||||||
|
};
|
||||||
this.hasUnsavedChanges = true;
|
this.hasUnsavedChanges = true;
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@@ -997,7 +1011,11 @@ export default defineComponent({
|
|||||||
const params =
|
const params =
|
||||||
'scrollbars=no,resizable=yes,status=no,titlebar=noe,location=no,toolbar=no,menubar=no,width=500,height=700';
|
'scrollbars=no,resizable=yes,status=no,titlebar=noe,location=no,toolbar=no,menubar=no,width=500,height=700';
|
||||||
const oauthPopup = window.open(url, 'OAuth2 Authorization', params);
|
const oauthPopup = window.open(url, 'OAuth2 Authorization', params);
|
||||||
Vue.set(this.credentialData, 'oauthTokenData', null);
|
|
||||||
|
this.credentialData = {
|
||||||
|
...this.credentialData,
|
||||||
|
oauthTokenData: null as unknown as CredentialInformation,
|
||||||
|
};
|
||||||
|
|
||||||
const receiveMessage = (event: MessageEvent) => {
|
const receiveMessage = (event: MessageEvent) => {
|
||||||
// // TODO: Add check that it came from n8n
|
// // TODO: Add check that it came from n8n
|
||||||
@@ -1009,7 +1027,11 @@ export default defineComponent({
|
|||||||
|
|
||||||
// Set some kind of data that status changes.
|
// Set some kind of data that status changes.
|
||||||
// As data does not get displayed directly it does not matter what data.
|
// As data does not get displayed directly it does not matter what data.
|
||||||
Vue.set(this.credentialData, 'oauthTokenData', {});
|
this.credentialData = {
|
||||||
|
...this.credentialData,
|
||||||
|
oauthTokenData: {} as CredentialInformation,
|
||||||
|
};
|
||||||
|
|
||||||
this.credentialsStore.enableOAuthCredential(credential);
|
this.credentialsStore.enableOAuthCredential(credential);
|
||||||
|
|
||||||
// Close the window
|
// Close the window
|
||||||
@@ -1061,7 +1083,10 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
for (const property of this.credentialType.properties) {
|
for (const property of this.credentialType.properties) {
|
||||||
if (!this.credentialType.__overwrittenProperties?.includes(property.name)) {
|
if (!this.credentialType.__overwrittenProperties?.includes(property.name)) {
|
||||||
Vue.set(this.credentialData, property.name, property.default as CredentialInformation);
|
this.credentialData = {
|
||||||
|
...this.credentialData,
|
||||||
|
[property.name]: property.default as CredentialInformation,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ export default defineComponent({
|
|||||||
this.allVisibleSelected = !this.allVisibleSelected;
|
this.allVisibleSelected = !this.allVisibleSelected;
|
||||||
if (!this.allVisibleSelected) {
|
if (!this.allVisibleSelected) {
|
||||||
this.allExistingSelected = false;
|
this.allExistingSelected = false;
|
||||||
Vue.set(this, 'selectedItems', {});
|
this.selectedItems = {};
|
||||||
} else {
|
} else {
|
||||||
this.selectAllVisibleExecutions();
|
this.selectAllVisibleExecutions();
|
||||||
}
|
}
|
||||||
@@ -441,7 +441,10 @@ export default defineComponent({
|
|||||||
if (this.selectedItems[executionId]) {
|
if (this.selectedItems[executionId]) {
|
||||||
Vue.delete(this.selectedItems, executionId);
|
Vue.delete(this.selectedItems, executionId);
|
||||||
} else {
|
} else {
|
||||||
Vue.set(this.selectedItems, executionId, true);
|
this.selectedItems = {
|
||||||
|
...this.selectedItems,
|
||||||
|
[executionId]: true,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
this.allVisibleSelected =
|
this.allVisibleSelected =
|
||||||
Object.keys(this.selectedItems).length === this.combinedExecutions.length;
|
Object.keys(this.selectedItems).length === this.combinedExecutions.length;
|
||||||
@@ -502,7 +505,7 @@ export default defineComponent({
|
|||||||
handleClearSelection(): void {
|
handleClearSelection(): void {
|
||||||
this.allVisibleSelected = false;
|
this.allVisibleSelected = false;
|
||||||
this.allExistingSelected = false;
|
this.allExistingSelected = false;
|
||||||
Vue.set(this, 'selectedItems', {});
|
this.selectedItems = {};
|
||||||
},
|
},
|
||||||
async onFilterChanged(filter: ExecutionFilterType) {
|
async onFilterChanged(filter: ExecutionFilterType) {
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
@@ -635,7 +638,7 @@ export default defineComponent({
|
|||||||
this.finishedExecutionsCount = pastExecutions.count;
|
this.finishedExecutionsCount = pastExecutions.count;
|
||||||
this.finishedExecutionsCountEstimated = pastExecutions.estimated;
|
this.finishedExecutionsCountEstimated = pastExecutions.estimated;
|
||||||
|
|
||||||
Vue.set(this, 'finishedExecutions', alreadyPresentExecutionsFiltered);
|
this.finishedExecutions = alreadyPresentExecutionsFiltered;
|
||||||
this.workflowsStore.addToCurrentExecutions(alreadyPresentExecutionsFiltered);
|
this.workflowsStore.addToCurrentExecutions(alreadyPresentExecutionsFiltered);
|
||||||
|
|
||||||
this.adjustSelectionAfterMoreItemsLoaded();
|
this.adjustSelectionAfterMoreItemsLoaded();
|
||||||
@@ -706,7 +709,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
async loadWorkflows() {
|
async loadWorkflows() {
|
||||||
try {
|
try {
|
||||||
const workflows = await this.workflowsStore.fetchAllWorkflows();
|
const workflows =
|
||||||
|
(await this.workflowsStore.fetchAllWorkflows()) as IWorkflowShortResponse[];
|
||||||
workflows.sort((a, b) => {
|
workflows.sort((a, b) => {
|
||||||
if (a.name.toLowerCase() < b.name.toLowerCase()) {
|
if (a.name.toLowerCase() < b.name.toLowerCase()) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -717,13 +721,12 @@ export default defineComponent({
|
|||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
workflows.unshift({
|
workflows.unshift({
|
||||||
id: 'all',
|
id: 'all',
|
||||||
name: this.$locale.baseText('executionsList.allWorkflows'),
|
name: this.$locale.baseText('executionsList.allWorkflows'),
|
||||||
});
|
} as IWorkflowShortResponse);
|
||||||
|
|
||||||
Vue.set(this, 'workflows', workflows);
|
this.workflows = workflows;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.showError(
|
this.showError(
|
||||||
error,
|
error,
|
||||||
@@ -900,7 +903,7 @@ export default defineComponent({
|
|||||||
await this.refreshData();
|
await this.refreshData();
|
||||||
|
|
||||||
if (this.allVisibleSelected) {
|
if (this.allVisibleSelected) {
|
||||||
Vue.set(this, 'selectedItems', {});
|
this.selectedItems = {};
|
||||||
this.selectAllVisibleExecutions();
|
this.selectAllVisibleExecutions();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -922,7 +925,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
selectAllVisibleExecutions() {
|
selectAllVisibleExecutions() {
|
||||||
this.combinedExecutions.forEach((execution: IExecutionsSummary) => {
|
this.combinedExecutions.forEach((execution: IExecutionsSummary) => {
|
||||||
Vue.set(this.selectedItems, execution.id, true);
|
this.selectedItems = { ...this.selectedItems, [execution.id]: true };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
adjustSelectionAfterMoreItemsLoaded() {
|
adjustSelectionAfterMoreItemsLoaded() {
|
||||||
|
|||||||
@@ -409,8 +409,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
activeNode(node: INodeUi | null) {
|
activeNode(node: INodeUi | null, oldNode: INodeUi | null) {
|
||||||
if (node && !this.isActiveStickyNode) {
|
if (node && node.name !== oldNode?.name && !this.isActiveStickyNode) {
|
||||||
this.runInputIndex = -1;
|
this.runInputIndex = -1;
|
||||||
this.runOutputIndex = -1;
|
this.runOutputIndex = -1;
|
||||||
this.isLinkingEnabled = true;
|
this.isLinkingEnabled = true;
|
||||||
|
|||||||
@@ -533,8 +533,10 @@ export default defineComponent({
|
|||||||
Vue.delete(this.nodeValues, lastNamePart);
|
Vue.delete(this.nodeValues, lastNamePart);
|
||||||
} else {
|
} else {
|
||||||
// Value should be set
|
// Value should be set
|
||||||
// @ts-ignore
|
this.nodeValues = {
|
||||||
Vue.set(this.nodeValues, lastNamePart, value);
|
...this.nodeValues,
|
||||||
|
[lastNamePart as string]: value,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Data is on lower level
|
// Data is on lower level
|
||||||
@@ -556,14 +558,22 @@ export default defineComponent({
|
|||||||
} else {
|
} else {
|
||||||
// Value should be set
|
// Value should be set
|
||||||
if (typeof value === 'object') {
|
if (typeof value === 'object') {
|
||||||
// @ts-ignore
|
set(
|
||||||
Vue.set(get(this.nodeValues, nameParts.join('.')), lastNamePart, deepCopy(value));
|
get(this.nodeValues, nameParts.join('.')) as Record<string, unknown>,
|
||||||
|
lastNamePart as string,
|
||||||
|
deepCopy(value),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
set(
|
||||||
Vue.set(get(this.nodeValues, nameParts.join('.')), lastNamePart, value);
|
get(this.nodeValues, nameParts.join('.')) as Record<string, unknown>,
|
||||||
|
lastNamePart as string,
|
||||||
|
value,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.nodeValues = { ...this.nodeValues };
|
||||||
},
|
},
|
||||||
credentialSelected(updateInformation: INodeUpdatePropertiesInformation) {
|
credentialSelected(updateInformation: INodeUpdatePropertiesInformation) {
|
||||||
// Update the values on the node
|
// Update the values on the node
|
||||||
@@ -660,7 +670,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
data.splice(parseInt(index, 10), 1);
|
data.splice(parseInt(index, 10), 1);
|
||||||
Vue.set(nodeParameters as object, path, data);
|
set(nodeParameters as object, path, data);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (newValue === undefined) {
|
if (newValue === undefined) {
|
||||||
@@ -744,7 +754,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
data.splice(parseInt(index, 10), 1);
|
data.splice(parseInt(index, 10), 1);
|
||||||
Vue.set(nodeParameters as object, path, data);
|
set(nodeParameters as object, path, data);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (newValue === undefined) {
|
if (newValue === undefined) {
|
||||||
@@ -791,7 +801,10 @@ export default defineComponent({
|
|||||||
// A property on the node itself changed
|
// A property on the node itself changed
|
||||||
|
|
||||||
// Update data in settings
|
// Update data in settings
|
||||||
Vue.set(this.nodeValues, parameterData.name, newValue);
|
this.nodeValues = {
|
||||||
|
...this.nodeValues,
|
||||||
|
[parameterData.name]: newValue,
|
||||||
|
};
|
||||||
|
|
||||||
// Update data in vuex
|
// Update data in vuex
|
||||||
const updateInformation = {
|
const updateInformation = {
|
||||||
@@ -818,58 +831,91 @@ export default defineComponent({
|
|||||||
const foundNodeSettings = [];
|
const foundNodeSettings = [];
|
||||||
if (this.node.color) {
|
if (this.node.color) {
|
||||||
foundNodeSettings.push('color');
|
foundNodeSettings.push('color');
|
||||||
Vue.set(this.nodeValues, 'color', this.node.color);
|
this.nodeValues = {
|
||||||
|
...this.nodeValues,
|
||||||
|
color: this.node.color,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.node.notes) {
|
if (this.node.notes) {
|
||||||
foundNodeSettings.push('notes');
|
foundNodeSettings.push('notes');
|
||||||
Vue.set(this.nodeValues, 'notes', this.node.notes);
|
this.nodeValues = {
|
||||||
|
...this.nodeValues,
|
||||||
|
notes: this.node.notes,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.node.alwaysOutputData) {
|
if (this.node.alwaysOutputData) {
|
||||||
foundNodeSettings.push('alwaysOutputData');
|
foundNodeSettings.push('alwaysOutputData');
|
||||||
Vue.set(this.nodeValues, 'alwaysOutputData', this.node.alwaysOutputData);
|
this.nodeValues = {
|
||||||
|
...this.nodeValues,
|
||||||
|
alwaysOutputData: this.node.alwaysOutputData,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.node.executeOnce) {
|
if (this.node.executeOnce) {
|
||||||
foundNodeSettings.push('executeOnce');
|
foundNodeSettings.push('executeOnce');
|
||||||
Vue.set(this.nodeValues, 'executeOnce', this.node.executeOnce);
|
this.nodeValues = {
|
||||||
|
...this.nodeValues,
|
||||||
|
executeOnce: this.node.executeOnce,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.node.continueOnFail) {
|
if (this.node.continueOnFail) {
|
||||||
foundNodeSettings.push('continueOnFail');
|
foundNodeSettings.push('continueOnFail');
|
||||||
Vue.set(this.nodeValues, 'continueOnFail', this.node.continueOnFail);
|
this.nodeValues = {
|
||||||
|
...this.nodeValues,
|
||||||
|
continueOnFail: this.node.continueOnFail,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.node.notesInFlow) {
|
if (this.node.notesInFlow) {
|
||||||
foundNodeSettings.push('notesInFlow');
|
foundNodeSettings.push('notesInFlow');
|
||||||
Vue.set(this.nodeValues, 'notesInFlow', this.node.notesInFlow);
|
this.nodeValues = {
|
||||||
|
...this.nodeValues,
|
||||||
|
notesInFlow: this.node.notesInFlow,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.node.retryOnFail) {
|
if (this.node.retryOnFail) {
|
||||||
foundNodeSettings.push('retryOnFail');
|
foundNodeSettings.push('retryOnFail');
|
||||||
Vue.set(this.nodeValues, 'retryOnFail', this.node.retryOnFail);
|
this.nodeValues = {
|
||||||
|
...this.nodeValues,
|
||||||
|
retryOnFail: this.node.retryOnFail,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.node.maxTries) {
|
if (this.node.maxTries) {
|
||||||
foundNodeSettings.push('maxTries');
|
foundNodeSettings.push('maxTries');
|
||||||
Vue.set(this.nodeValues, 'maxTries', this.node.maxTries);
|
this.nodeValues = {
|
||||||
|
...this.nodeValues,
|
||||||
|
maxTries: this.node.maxTries,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.node.waitBetweenTries) {
|
if (this.node.waitBetweenTries) {
|
||||||
foundNodeSettings.push('waitBetweenTries');
|
foundNodeSettings.push('waitBetweenTries');
|
||||||
Vue.set(this.nodeValues, 'waitBetweenTries', this.node.waitBetweenTries);
|
this.nodeValues = {
|
||||||
|
...this.nodeValues,
|
||||||
|
waitBetweenTries: this.node.waitBetweenTries,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set default node settings
|
// Set default node settings
|
||||||
for (const nodeSetting of this.nodeSettings) {
|
for (const nodeSetting of this.nodeSettings) {
|
||||||
if (!foundNodeSettings.includes(nodeSetting.name)) {
|
if (!foundNodeSettings.includes(nodeSetting.name)) {
|
||||||
// Set default value
|
// Set default value
|
||||||
Vue.set(this.nodeValues, nodeSetting.name, nodeSetting.default);
|
this.nodeValues = {
|
||||||
|
...this.nodeValues,
|
||||||
|
[nodeSetting.name]: nodeSetting.default,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.set(this.nodeValues, 'parameters', deepCopy(this.node.parameters));
|
this.nodeValues = {
|
||||||
|
...this.nodeValues,
|
||||||
|
parameters: deepCopy(this.node.parameters),
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this.nodeValid = false;
|
this.nodeValid = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ import {
|
|||||||
defaultMessageEventBusDestinationSentryOptions,
|
defaultMessageEventBusDestinationSentryOptions,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import Vue, { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { LOG_STREAM_MODAL_KEY, MODAL_CONFIRM } from '@/constants';
|
import { LOG_STREAM_MODAL_KEY, MODAL_CONFIRM } from '@/constants';
|
||||||
import Modal from '@/components/Modal.vue';
|
import Modal from '@/components/Modal.vue';
|
||||||
import { useMessage } from '@/composables';
|
import { useMessage } from '@/composables';
|
||||||
@@ -249,7 +249,9 @@ export default defineComponent({
|
|||||||
showRemoveConfirm: false,
|
showRemoveConfirm: false,
|
||||||
typeSelectValue: '',
|
typeSelectValue: '',
|
||||||
typeSelectPlaceholder: 'Destination Type',
|
typeSelectPlaceholder: 'Destination Type',
|
||||||
nodeParameters: deepCopy(defaultMessageEventBusDestinationOptions),
|
nodeParameters: deepCopy(
|
||||||
|
defaultMessageEventBusDestinationOptions,
|
||||||
|
) as MessageEventBusDestinationOptions,
|
||||||
webhookDescription: webhookModalDescription,
|
webhookDescription: webhookModalDescription,
|
||||||
sentryDescription: sentryModalDescription,
|
sentryDescription: sentryModalDescription,
|
||||||
syslogDescription: syslogModalDescription,
|
syslogDescription: syslogModalDescription,
|
||||||
@@ -400,13 +402,13 @@ export default defineComponent({
|
|||||||
// Apply the new value
|
// Apply the new value
|
||||||
if (parameterData.value === undefined && parameterPathArray !== null) {
|
if (parameterData.value === undefined && parameterPathArray !== null) {
|
||||||
// Delete array item
|
// Delete array item
|
||||||
const path = parameterPathArray[1];
|
const path = parameterPathArray[1] as keyof MessageEventBusDestinationOptions;
|
||||||
const index = parameterPathArray[2];
|
const index = parameterPathArray[2];
|
||||||
const data = get(nodeParameters, path);
|
const data = get(nodeParameters, path);
|
||||||
|
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
data.splice(parseInt(index, 10), 1);
|
data.splice(parseInt(index, 10), 1);
|
||||||
Vue.set(nodeParameters, path, data);
|
nodeParameters[path] = data as never;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (newValue === undefined) {
|
if (newValue === undefined) {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue, { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
import type { ITag } from '@/Interface';
|
import type { ITag } from '@/Interface';
|
||||||
import IntersectionObserver from './IntersectionObserver.vue';
|
import IntersectionObserver from './IntersectionObserver.vue';
|
||||||
@@ -109,7 +109,7 @@ export default defineComponent({
|
|||||||
methods: {
|
methods: {
|
||||||
onObserved({ el, isIntersecting }: { el: HTMLElement; isIntersecting: boolean }) {
|
onObserved({ el, isIntersecting }: { el: HTMLElement; isIntersecting: boolean }) {
|
||||||
if (el.dataset.id) {
|
if (el.dataset.id) {
|
||||||
Vue.set(this.$data.visibility, el.dataset.id, isIntersecting);
|
this.$data.visibility = { ...this.$data.visibility, [el.dataset.id]: isIntersecting };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClick(e: MouseEvent, tag: TagEl) {
|
onClick(e: MouseEvent, tag: TagEl) {
|
||||||
|
|||||||
@@ -337,7 +337,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
|
|
||||||
@@ -537,7 +536,7 @@ export default defineComponent({
|
|||||||
workflowSettings.maxExecutionTimeout = this.rootStore.maxExecutionTimeout;
|
workflowSettings.maxExecutionTimeout = this.rootStore.maxExecutionTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.set(this, 'workflowSettings', workflowSettings);
|
this.workflowSettings = workflowSettings;
|
||||||
this.timeoutHMS = this.convertToHMS(workflowSettings.executionTimeout);
|
this.timeoutHMS = this.convertToHMS(workflowSettings.executionTimeout);
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
|
|
||||||
@@ -752,7 +751,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async loadWorkflows() {
|
async loadWorkflows() {
|
||||||
const workflows = await this.workflowsStore.fetchAllWorkflows();
|
const workflows = (await this.workflowsStore.fetchAllWorkflows()) as IWorkflowShortResponse[];
|
||||||
workflows.sort((a, b) => {
|
workflows.sort((a, b) => {
|
||||||
if (a.name.toLowerCase() < b.name.toLowerCase()) {
|
if (a.name.toLowerCase() < b.name.toLowerCase()) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -763,13 +762,12 @@ export default defineComponent({
|
|||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
workflows.unshift({
|
workflows.unshift({
|
||||||
id: undefined as unknown as string,
|
id: undefined as unknown as string,
|
||||||
name: this.$locale.baseText('workflowSettings.noWorkflow'),
|
name: this.$locale.baseText('workflowSettings.noWorkflow'),
|
||||||
});
|
} as IWorkflowShortResponse);
|
||||||
|
|
||||||
Vue.set(this, 'workflows', workflows);
|
this.workflows = workflows;
|
||||||
},
|
},
|
||||||
async saveSettings() {
|
async saveSettings() {
|
||||||
// Set that the active state should be changed
|
// Set that the active state should be changed
|
||||||
|
|||||||
@@ -220,10 +220,13 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
|
|||||||
},
|
},
|
||||||
upsertCredential(credential: ICredentialsResponse): void {
|
upsertCredential(credential: ICredentialsResponse): void {
|
||||||
if (credential.id) {
|
if (credential.id) {
|
||||||
Vue.set(this.credentials, credential.id, {
|
this.credentials = {
|
||||||
|
...this.credentials,
|
||||||
|
[credential.id]: {
|
||||||
...this.credentials[credential.id],
|
...this.credentials[credential.id],
|
||||||
...credential,
|
...credential,
|
||||||
});
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
enableOAuthCredential(credential: ICredentialsResponse): void {
|
enableOAuthCredential(credential: ICredentialsResponse): void {
|
||||||
@@ -351,31 +354,38 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
|
|||||||
|
|
||||||
// Enterprise edition actions
|
// Enterprise edition actions
|
||||||
setCredentialOwnedBy(payload: { credentialId: string; ownedBy: Partial<IUser> }) {
|
setCredentialOwnedBy(payload: { credentialId: string; ownedBy: Partial<IUser> }) {
|
||||||
Vue.set(this.credentials[payload.credentialId], 'ownedBy', payload.ownedBy);
|
this.credentials[payload.credentialId] = {
|
||||||
|
...this.credentials[payload.credentialId],
|
||||||
|
ownedBy: payload.ownedBy,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
async setCredentialSharedWith(payload: { sharedWith: IUser[]; credentialId: string }) {
|
async setCredentialSharedWith(payload: { sharedWith: IUser[]; credentialId: string }) {
|
||||||
if (useSettingsStore().isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)) {
|
if (useSettingsStore().isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)) {
|
||||||
await setCredentialSharedWith(useRootStore().getRestApiContext, payload.credentialId, {
|
await setCredentialSharedWith(useRootStore().getRestApiContext, payload.credentialId, {
|
||||||
shareWithIds: payload.sharedWith.map((sharee) => sharee.id),
|
shareWithIds: payload.sharedWith.map((sharee) => sharee.id),
|
||||||
});
|
});
|
||||||
Vue.set(this.credentials[payload.credentialId], 'sharedWith', payload.sharedWith);
|
|
||||||
|
this.credentials[payload.credentialId] = {
|
||||||
|
...this.credentials[payload.credentialId],
|
||||||
|
sharedWith: payload.sharedWith,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
addCredentialSharee(payload: { credentialId: string; sharee: Partial<IUser> }): void {
|
addCredentialSharee(payload: { credentialId: string; sharee: Partial<IUser> }): void {
|
||||||
Vue.set(
|
this.credentials[payload.credentialId] = {
|
||||||
this.credentials[payload.credentialId],
|
...this.credentials[payload.credentialId],
|
||||||
'sharedWith',
|
sharedWith: (this.credentials[payload.credentialId].sharedWith || []).concat([
|
||||||
(this.credentials[payload.credentialId].sharedWith || []).concat([payload.sharee]),
|
payload.sharee,
|
||||||
);
|
]),
|
||||||
|
};
|
||||||
},
|
},
|
||||||
removeCredentialSharee(payload: { credentialId: string; sharee: Partial<IUser> }): void {
|
removeCredentialSharee(payload: { credentialId: string; sharee: Partial<IUser> }): void {
|
||||||
Vue.set(
|
this.credentials[payload.credentialId] = {
|
||||||
this.credentials[payload.credentialId],
|
...this.credentials[payload.credentialId],
|
||||||
'sharedWith',
|
sharedWith: (this.credentials[payload.credentialId].sharedWith || []).filter(
|
||||||
(this.credentials[payload.credentialId].sharedWith || []).filter(
|
|
||||||
(sharee) => sharee.id !== payload.sharee.id,
|
(sharee) => sharee.id !== payload.sharee.id,
|
||||||
),
|
),
|
||||||
);
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
async getCredentialTranslation(credentialType: string): Promise<object> {
|
async getCredentialTranslation(credentialType: string): Promise<object> {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { CLOUD_BASE_URL_PRODUCTION, CLOUD_BASE_URL_STAGING, STORES } from '@/con
|
|||||||
import type { IRestApiContext, RootState } from '@/Interface';
|
import type { IRestApiContext, RootState } from '@/Interface';
|
||||||
import type { IDataObject } from 'n8n-workflow';
|
import type { IDataObject } from 'n8n-workflow';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import Vue from 'vue';
|
|
||||||
import { useNodeTypesStore } from './nodeTypes.store';
|
import { useNodeTypesStore } from './nodeTypes.store';
|
||||||
|
|
||||||
const { VUE_APP_URL_BASE_API } = import.meta.env;
|
const { VUE_APP_URL_BASE_API } = import.meta.env;
|
||||||
@@ -76,44 +75,44 @@ export const useRootStore = defineStore(STORES.ROOT, {
|
|||||||
actions: {
|
actions: {
|
||||||
setUrlBaseWebhook(urlBaseWebhook: string): void {
|
setUrlBaseWebhook(urlBaseWebhook: string): void {
|
||||||
const url = urlBaseWebhook.endsWith('/') ? urlBaseWebhook : `${urlBaseWebhook}/`;
|
const url = urlBaseWebhook.endsWith('/') ? urlBaseWebhook : `${urlBaseWebhook}/`;
|
||||||
Vue.set(this, 'urlBaseWebhook', url);
|
this.urlBaseWebhook = url;
|
||||||
},
|
},
|
||||||
setUrlBaseEditor(urlBaseEditor: string): void {
|
setUrlBaseEditor(urlBaseEditor: string): void {
|
||||||
const url = urlBaseEditor.endsWith('/') ? urlBaseEditor : `${urlBaseEditor}/`;
|
const url = urlBaseEditor.endsWith('/') ? urlBaseEditor : `${urlBaseEditor}/`;
|
||||||
Vue.set(this, 'urlBaseEditor', url);
|
this.urlBaseEditor = url;
|
||||||
},
|
},
|
||||||
setEndpointWebhook(endpointWebhook: string): void {
|
setEndpointWebhook(endpointWebhook: string): void {
|
||||||
Vue.set(this, 'endpointWebhook', endpointWebhook);
|
this.endpointWebhook = endpointWebhook;
|
||||||
},
|
},
|
||||||
setEndpointWebhookTest(endpointWebhookTest: string): void {
|
setEndpointWebhookTest(endpointWebhookTest: string): void {
|
||||||
Vue.set(this, 'endpointWebhookTest', endpointWebhookTest);
|
this.endpointWebhookTest = endpointWebhookTest;
|
||||||
},
|
},
|
||||||
setTimezone(timezone: string): void {
|
setTimezone(timezone: string): void {
|
||||||
Vue.set(this, 'timezone', timezone);
|
this.timezone = timezone;
|
||||||
},
|
},
|
||||||
setExecutionTimeout(executionTimeout: number): void {
|
setExecutionTimeout(executionTimeout: number): void {
|
||||||
Vue.set(this, 'executionTimeout', executionTimeout);
|
this.executionTimeout = executionTimeout;
|
||||||
},
|
},
|
||||||
setMaxExecutionTimeout(maxExecutionTimeout: number): void {
|
setMaxExecutionTimeout(maxExecutionTimeout: number): void {
|
||||||
Vue.set(this, 'maxExecutionTimeout', maxExecutionTimeout);
|
this.maxExecutionTimeout = maxExecutionTimeout;
|
||||||
},
|
},
|
||||||
setVersionCli(version: string): void {
|
setVersionCli(version: string): void {
|
||||||
Vue.set(this, 'versionCli', version);
|
this.versionCli = version;
|
||||||
},
|
},
|
||||||
setInstanceId(instanceId: string): void {
|
setInstanceId(instanceId: string): void {
|
||||||
Vue.set(this, 'instanceId', instanceId);
|
this.instanceId = instanceId;
|
||||||
},
|
},
|
||||||
setOauthCallbackUrls(urls: IDataObject): void {
|
setOauthCallbackUrls(urls: IDataObject): void {
|
||||||
Vue.set(this, 'oauthCallbackUrls', urls);
|
this.oauthCallbackUrls = urls;
|
||||||
},
|
},
|
||||||
setN8nMetadata(metadata: IDataObject): void {
|
setN8nMetadata(metadata: IDataObject): void {
|
||||||
Vue.set(this, 'n8nMetadata', metadata);
|
this.n8nMetadata = metadata as RootState['n8nMetadata'];
|
||||||
},
|
},
|
||||||
setDefaultLocale(locale: string): void {
|
setDefaultLocale(locale: string): void {
|
||||||
Vue.set(this, 'defaultLocale', locale);
|
this.defaultLocale = locale;
|
||||||
},
|
},
|
||||||
setIsNpmAvailable(isNpmAvailable: boolean): void {
|
setIsNpmAvailable(isNpmAvailable: boolean): void {
|
||||||
Vue.set(this, 'isNpmAvailable', isNpmAvailable);
|
this.isNpmAvailable = isNpmAvailable;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import type {
|
|||||||
} from '@/Interface';
|
} from '@/Interface';
|
||||||
import type { INodeIssues, IRunData } from 'n8n-workflow';
|
import type { INodeIssues, IRunData } from 'n8n-workflow';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import Vue from 'vue';
|
|
||||||
import { useWorkflowsStore } from './workflows.store';
|
import { useWorkflowsStore } from './workflows.store';
|
||||||
|
|
||||||
export const useNDVStore = defineStore(STORES.NDV, {
|
export const useNDVStore = defineStore(STORES.NDV, {
|
||||||
@@ -128,38 +127,47 @@ export const useNDVStore = defineStore(STORES.NDV, {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setInputNodeName(name: string | undefined): void {
|
setInputNodeName(nodeName: string | undefined): void {
|
||||||
Vue.set(this.input, 'nodeName', name);
|
this.input = {
|
||||||
|
...this.input,
|
||||||
|
nodeName,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
setInputRunIndex(run?: string): void {
|
setInputRunIndex(run?: number): void {
|
||||||
Vue.set(this.input, 'run', run);
|
this.input = {
|
||||||
|
...this.input,
|
||||||
|
run,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
setMainPanelDimensions(params: {
|
setMainPanelDimensions(params: {
|
||||||
panelType: string;
|
panelType: string;
|
||||||
dimensions: { relativeLeft?: number; relativeRight?: number; relativeWidth?: number };
|
dimensions: { relativeLeft?: number; relativeRight?: number; relativeWidth?: number };
|
||||||
}): void {
|
}): void {
|
||||||
Vue.set(this.mainPanelDimensions, params.panelType, {
|
this.mainPanelDimensions = {
|
||||||
|
...this.mainPanelDimensions,
|
||||||
|
[params.panelType]: {
|
||||||
...this.mainPanelDimensions[params.panelType],
|
...this.mainPanelDimensions[params.panelType],
|
||||||
...params.dimensions,
|
...params.dimensions,
|
||||||
});
|
},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
setNDVSessionId(): void {
|
setNDVSessionId(): void {
|
||||||
Vue.set(this, 'sessionId', `ndv-${Math.random().toString(36).slice(-8)}`);
|
this.sessionId = `ndv-${Math.random().toString(36).slice(-8)}`;
|
||||||
},
|
},
|
||||||
resetNDVSessionId(): void {
|
resetNDVSessionId(): void {
|
||||||
Vue.set(this, 'sessionId', '');
|
this.sessionId = '';
|
||||||
},
|
},
|
||||||
setPanelDisplayMode(params: { pane: NodePanelType; mode: IRunDataDisplayMode }): void {
|
setPanelDisplayMode(params: { pane: NodePanelType; mode: IRunDataDisplayMode }): void {
|
||||||
Vue.set(this[params.pane], 'displayMode', params.mode);
|
this[params.pane].displayMode = params.mode;
|
||||||
},
|
},
|
||||||
setOutputPanelEditModeEnabled(isEnabled: boolean): void {
|
setOutputPanelEditModeEnabled(isEnabled: boolean): void {
|
||||||
Vue.set(this.output.editMode, 'enabled', isEnabled);
|
this.output.editMode.enabled = isEnabled;
|
||||||
},
|
},
|
||||||
setOutputPanelEditModeValue(payload: string): void {
|
setOutputPanelEditModeValue(payload: string): void {
|
||||||
Vue.set(this.output.editMode, 'value', payload);
|
this.output.editMode.value = payload;
|
||||||
},
|
},
|
||||||
setMappableNDVInputFocus(paramName: string): void {
|
setMappableNDVInputFocus(paramName: string): void {
|
||||||
Vue.set(this, 'focusedMappableInput', paramName);
|
this.focusedMappableInput = paramName;
|
||||||
},
|
},
|
||||||
draggableStartDragging({ type, data }: { type: string; data: string }): void {
|
draggableStartDragging({ type, data }: { type: string; data: string }): void {
|
||||||
this.draggable = {
|
this.draggable = {
|
||||||
@@ -180,10 +188,10 @@ export const useNDVStore = defineStore(STORES.NDV, {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
setDraggableStickyPos(position: XYPosition | null): void {
|
setDraggableStickyPos(position: XYPosition | null): void {
|
||||||
Vue.set(this.draggable, 'stickyPosition', position);
|
this.draggable.stickyPosition = position;
|
||||||
},
|
},
|
||||||
setDraggableCanDrop(canDrop: boolean): void {
|
setDraggableCanDrop(canDrop: boolean): void {
|
||||||
Vue.set(this.draggable, 'canDrop', canDrop);
|
this.draggable.canDrop = canDrop;
|
||||||
},
|
},
|
||||||
setMappingTelemetry(telemetry: { [key: string]: string | number | boolean }): void {
|
setMappingTelemetry(telemetry: { [key: string]: string | number | boolean }): void {
|
||||||
this.mappingTelemetry = { ...this.mappingTelemetry, ...telemetry };
|
this.mappingTelemetry = { ...this.mappingTelemetry, ...telemetry };
|
||||||
@@ -192,13 +200,13 @@ export const useNDVStore = defineStore(STORES.NDV, {
|
|||||||
this.mappingTelemetry = {};
|
this.mappingTelemetry = {};
|
||||||
},
|
},
|
||||||
setHoveringItem(item: null | NDVState['hoveringItem']): void {
|
setHoveringItem(item: null | NDVState['hoveringItem']): void {
|
||||||
Vue.set(this, 'hoveringItem', item);
|
this.hoveringItem = item;
|
||||||
},
|
},
|
||||||
setNDVBranchIndex(e: { pane: 'input' | 'output'; branchIndex: number }): void {
|
setNDVBranchIndex(e: { pane: 'input' | 'output'; branchIndex: number }): void {
|
||||||
Vue.set(this[e.pane], 'branch', e.branchIndex);
|
this[e.pane].branch = e.branchIndex;
|
||||||
},
|
},
|
||||||
setNDVPanelDataIsEmpty(payload: { panel: 'input' | 'output'; isEmpty: boolean }): void {
|
setNDVPanelDataIsEmpty(payload: { panel: 'input' | 'output'; isEmpty: boolean }): void {
|
||||||
Vue.set(this[payload.panel].data, 'isEmpty', payload.isEmpty);
|
this[payload.panel].data.isEmpty = payload.isEmpty;
|
||||||
},
|
},
|
||||||
disableMappingHint(store = true) {
|
disableMappingHint(store = true) {
|
||||||
this.isMappingOnboarded = true;
|
this.isMappingOnboarded = true;
|
||||||
@@ -207,11 +215,19 @@ export const useNDVStore = defineStore(STORES.NDV, {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateNodeParameterIssues(issues: INodeIssues): void {
|
updateNodeParameterIssues(issues: INodeIssues): void {
|
||||||
const activeNode = this.activeNode;
|
const workflowsStore = useWorkflowsStore();
|
||||||
|
const activeNode = workflowsStore.getNodeByName(this.activeNodeName || '');
|
||||||
|
|
||||||
if (activeNode) {
|
if (activeNode) {
|
||||||
Vue.set(activeNode, 'issues', {
|
const nodeIndex = workflowsStore.workflow.nodes.findIndex((node) => {
|
||||||
|
return node.name === activeNode.name;
|
||||||
|
});
|
||||||
|
|
||||||
|
workflowsStore.updateNodeAtIndex(nodeIndex, {
|
||||||
|
issues: {
|
||||||
...activeNode.issues,
|
...activeNode.issues,
|
||||||
...issues,
|
...issues,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import type {
|
|||||||
ResourceMapperFields,
|
ResourceMapperFields,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import Vue from 'vue';
|
|
||||||
import { useCredentialsStore } from './credentials.store';
|
import { useCredentialsStore } from './credentials.store';
|
||||||
import { useRootStore } from './n8nRoot.store';
|
import { useRootStore } from './n8nRoot.store';
|
||||||
|
|
||||||
@@ -120,7 +119,7 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
|||||||
},
|
},
|
||||||
{ ...this.nodeTypes },
|
{ ...this.nodeTypes },
|
||||||
);
|
);
|
||||||
Vue.set(this, 'nodeTypes', nodeTypes);
|
this.nodeTypes = nodeTypes;
|
||||||
},
|
},
|
||||||
removeNodeTypes(nodeTypesToRemove: INodeTypeDescription[]): void {
|
removeNodeTypes(nodeTypesToRemove: INodeTypeDescription[]): void {
|
||||||
this.nodeTypes = nodeTypesToRemove.reduce(
|
this.nodeTypes = nodeTypesToRemove.reduce(
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import type {
|
|||||||
WorkflowSettings,
|
WorkflowSettings,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import Vue from 'vue';
|
|
||||||
import { useRootStore } from './n8nRoot.store';
|
import { useRootStore } from './n8nRoot.store';
|
||||||
import { useUIStore } from './ui.store';
|
import { useUIStore } from './ui.store';
|
||||||
import { useUsersStore } from './users.store';
|
import { useUsersStore } from './users.store';
|
||||||
@@ -220,13 +219,19 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
|||||||
useVersionsStore().setVersionNotificationSettings(settings.versionNotifications);
|
useVersionsStore().setVersionNotificationSettings(settings.versionNotifications);
|
||||||
},
|
},
|
||||||
stopShowingSetupPage(): void {
|
stopShowingSetupPage(): void {
|
||||||
Vue.set(this.userManagement, 'showSetupOnFirstLoad', false);
|
this.userManagement.showSetupOnFirstLoad = false;
|
||||||
},
|
},
|
||||||
disableTemplates(): void {
|
disableTemplates(): void {
|
||||||
Vue.set(this.settings.templates, 'enabled', false);
|
this.settings = {
|
||||||
|
...this.settings,
|
||||||
|
templates: {
|
||||||
|
...this.settings.templates,
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
setPromptsData(promptsData: IN8nPrompts): void {
|
setPromptsData(promptsData: IN8nPrompts): void {
|
||||||
Vue.set(this, 'promptsData', promptsData);
|
this.promptsData = promptsData;
|
||||||
},
|
},
|
||||||
setAllowedModules(allowedModules: { builtIn?: string[]; external?: string[] }): void {
|
setAllowedModules(allowedModules: { builtIn?: string[]; external?: string[] }): void {
|
||||||
this.settings.allowedModules = allowedModules;
|
this.settings.allowedModules = allowedModules;
|
||||||
@@ -315,13 +320,13 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
|||||||
return runLdapSync(rootStore.getRestApiContext, data);
|
return runLdapSync(rootStore.getRestApiContext, data);
|
||||||
},
|
},
|
||||||
setSaveDataErrorExecution(newValue: string) {
|
setSaveDataErrorExecution(newValue: string) {
|
||||||
Vue.set(this, 'saveDataErrorExecution', newValue);
|
this.saveDataErrorExecution = newValue;
|
||||||
},
|
},
|
||||||
setSaveDataSuccessExecution(newValue: string) {
|
setSaveDataSuccessExecution(newValue: string) {
|
||||||
Vue.set(this, 'saveDataSuccessExecution', newValue);
|
this.saveDataSuccessExecution = newValue;
|
||||||
},
|
},
|
||||||
setSaveManualExecutions(saveManualExecutions: boolean) {
|
setSaveManualExecutions(saveManualExecutions: boolean) {
|
||||||
Vue.set(this, 'saveManualExecutions', saveManualExecutions);
|
this.saveManualExecutions = saveManualExecutions;
|
||||||
},
|
},
|
||||||
async getTimezones(): Promise<IDataObject> {
|
async getTimezones(): Promise<IDataObject> {
|
||||||
const rootStore = useRootStore();
|
const rootStore = useRootStore();
|
||||||
|
|||||||
@@ -45,9 +45,15 @@ export const useTagsStore = defineStore(STORES.TAGS, {
|
|||||||
...currentTag,
|
...currentTag,
|
||||||
...tag,
|
...tag,
|
||||||
};
|
};
|
||||||
Vue.set(this.tags, tagId, newTag);
|
this.tags = {
|
||||||
|
...this.tags,
|
||||||
|
[tagId]: newTag,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
Vue.set(this.tags, tagId, tag);
|
this.tags = {
|
||||||
|
...this.tags,
|
||||||
|
[tagId]: tag,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import type {
|
|||||||
ITemplatesWorkflowFull,
|
ITemplatesWorkflowFull,
|
||||||
IWorkflowTemplate,
|
IWorkflowTemplate,
|
||||||
} from '@/Interface';
|
} from '@/Interface';
|
||||||
import Vue from 'vue';
|
|
||||||
import { useSettingsStore } from './settings.store';
|
import { useSettingsStore } from './settings.store';
|
||||||
import {
|
import {
|
||||||
getCategories,
|
getCategories,
|
||||||
@@ -104,27 +103,38 @@ export const useTemplatesStore = defineStore(STORES.TEMPLATES, {
|
|||||||
actions: {
|
actions: {
|
||||||
addCategories(categories: ITemplatesCategory[]): void {
|
addCategories(categories: ITemplatesCategory[]): void {
|
||||||
categories.forEach((category: ITemplatesCategory) => {
|
categories.forEach((category: ITemplatesCategory) => {
|
||||||
Vue.set(this.categories, category.id, category);
|
this.categories = {
|
||||||
|
...this.categories,
|
||||||
|
[category.id]: category,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
addCollections(collections: Array<ITemplatesCollection | ITemplatesCollectionFull>): void {
|
addCollections(collections: Array<ITemplatesCollection | ITemplatesCollectionFull>): void {
|
||||||
collections.forEach((collection) => {
|
collections.forEach((collection) => {
|
||||||
const workflows = (collection.workflows || []).map((workflow) => ({ id: workflow.id }));
|
const workflows = (collection.workflows || []).map((workflow) => ({ id: workflow.id }));
|
||||||
const cachedCollection = this.collections[collection.id] || {};
|
const cachedCollection = this.collections[collection.id] || {};
|
||||||
Vue.set(this.collections, collection.id, {
|
|
||||||
|
this.collections = {
|
||||||
|
...this.collections,
|
||||||
|
[collection.id]: {
|
||||||
...cachedCollection,
|
...cachedCollection,
|
||||||
...collection,
|
...collection,
|
||||||
workflows,
|
workflows,
|
||||||
});
|
},
|
||||||
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
addWorkflows(workflows: Array<ITemplatesWorkflow | ITemplatesWorkflowFull>): void {
|
addWorkflows(workflows: Array<ITemplatesWorkflow | ITemplatesWorkflowFull>): void {
|
||||||
workflows.forEach((workflow: ITemplatesWorkflow) => {
|
workflows.forEach((workflow: ITemplatesWorkflow) => {
|
||||||
const cachedWorkflow = this.workflows[workflow.id] || {};
|
const cachedWorkflow = this.workflows[workflow.id] || {};
|
||||||
Vue.set(this.workflows, workflow.id, {
|
|
||||||
|
this.workflows = {
|
||||||
|
...this.workflows,
|
||||||
|
[workflow.id]: {
|
||||||
...cachedWorkflow,
|
...cachedWorkflow,
|
||||||
...workflow,
|
...workflow,
|
||||||
});
|
},
|
||||||
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
addCollectionSearch(data: {
|
addCollectionSearch(data: {
|
||||||
@@ -133,9 +143,13 @@ export const useTemplatesStore = defineStore(STORES.TEMPLATES, {
|
|||||||
}): void {
|
}): void {
|
||||||
const collectionIds = data.collections.map((collection) => collection.id);
|
const collectionIds = data.collections.map((collection) => collection.id);
|
||||||
const searchKey = getSearchKey(data.query);
|
const searchKey = getSearchKey(data.query);
|
||||||
Vue.set(this.collectionSearches, searchKey, {
|
|
||||||
|
this.collectionSearches = {
|
||||||
|
...this.collectionSearches,
|
||||||
|
[searchKey]: {
|
||||||
collectionIds,
|
collectionIds,
|
||||||
});
|
},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
addWorkflowsSearch(data: {
|
addWorkflowsSearch(data: {
|
||||||
totalWorkflows: number;
|
totalWorkflows: number;
|
||||||
@@ -146,18 +160,24 @@ export const useTemplatesStore = defineStore(STORES.TEMPLATES, {
|
|||||||
const searchKey = getSearchKey(data.query);
|
const searchKey = getSearchKey(data.query);
|
||||||
const cachedResults = this.workflowSearches[searchKey];
|
const cachedResults = this.workflowSearches[searchKey];
|
||||||
if (!cachedResults) {
|
if (!cachedResults) {
|
||||||
Vue.set(this.workflowSearches, searchKey, {
|
this.workflowSearches = {
|
||||||
workflowIds,
|
...this.workflowSearches,
|
||||||
|
[searchKey]: {
|
||||||
|
workflowIds: workflowIds as unknown as string[],
|
||||||
totalWorkflows: data.totalWorkflows,
|
totalWorkflows: data.totalWorkflows,
|
||||||
});
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.set(this.workflowSearches, searchKey, {
|
this.workflowSearches = {
|
||||||
workflowIds: [...cachedResults.workflowIds, ...workflowIds],
|
...this.workflowSearches,
|
||||||
|
[searchKey]: {
|
||||||
|
workflowIds: [...cachedResults.workflowIds, ...workflowIds] as string[],
|
||||||
totalWorkflows: data.totalWorkflows,
|
totalWorkflows: data.totalWorkflows,
|
||||||
});
|
},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
setWorkflowSearchLoading(query: ITemplatesQuery): void {
|
setWorkflowSearchLoading(query: ITemplatesQuery): void {
|
||||||
const searchKey = getSearchKey(query);
|
const searchKey = getSearchKey(query);
|
||||||
@@ -166,7 +186,10 @@ export const useTemplatesStore = defineStore(STORES.TEMPLATES, {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.set(this.workflowSearches[searchKey], 'loadingMore', true);
|
this.workflowSearches[searchKey] = {
|
||||||
|
...this.workflowSearches[searchKey],
|
||||||
|
loadingMore: true,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
setWorkflowSearchLoaded(query: ITemplatesQuery): void {
|
setWorkflowSearchLoaded(query: ITemplatesQuery): void {
|
||||||
const searchKey = getSearchKey(query);
|
const searchKey = getSearchKey(query);
|
||||||
@@ -175,7 +198,10 @@ export const useTemplatesStore = defineStore(STORES.TEMPLATES, {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.set(this.workflowSearches[searchKey], 'loadingMore', false);
|
this.workflowSearches[searchKey] = {
|
||||||
|
...this.workflowSearches[searchKey],
|
||||||
|
loadingMore: false,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
resetSessionId(): void {
|
resetSessionId(): void {
|
||||||
this.previousSessionId = this.currentSessionId;
|
this.previousSessionId = this.currentSessionId;
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ import type {
|
|||||||
UIState,
|
UIState,
|
||||||
XYPosition,
|
XYPosition,
|
||||||
} from '@/Interface';
|
} from '@/Interface';
|
||||||
import Vue from 'vue';
|
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { useRootStore } from './n8nRoot.store';
|
import { useRootStore } from './n8nRoot.store';
|
||||||
import { getCurlToJson } from '@/api/curlHelper';
|
import { getCurlToJson } from '@/api/curlHelper';
|
||||||
@@ -51,6 +50,7 @@ import { useSettingsStore } from './settings.store';
|
|||||||
import { useCloudPlanStore } from './cloudPlan.store';
|
import { useCloudPlanStore } from './cloudPlan.store';
|
||||||
import type { BaseTextKey } from '@/plugins/i18n';
|
import type { BaseTextKey } from '@/plugins/i18n';
|
||||||
import { i18n as locale } from '@/plugins/i18n';
|
import { i18n as locale } from '@/plugins/i18n';
|
||||||
|
import type { Modals, NewCredentialsModal } from '@/Interface';
|
||||||
import { useTelemetryStore } from '@/stores/telemetry.store';
|
import { useTelemetryStore } from '@/stores/telemetry.store';
|
||||||
|
|
||||||
export const useUIStore = defineStore(STORES.UI, {
|
export const useUIStore = defineStore(STORES.UI, {
|
||||||
@@ -332,36 +332,57 @@ export const useUIStore = defineStore(STORES.UI, {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setMode(name: string, mode: string): void {
|
setMode(name: keyof Modals, mode: string): void {
|
||||||
Vue.set(this.modals[name], 'mode', mode);
|
this.modals[name] = {
|
||||||
|
...this.modals[name],
|
||||||
|
mode,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
setActiveId(name: string, id: string): void {
|
setActiveId(name: keyof Modals, activeId: string): void {
|
||||||
Vue.set(this.modals[name], 'activeId', id);
|
this.modals[name] = {
|
||||||
|
...this.modals[name],
|
||||||
|
activeId,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
setShowAuthSelector(name: string, show: boolean) {
|
setShowAuthSelector(name: keyof Modals, showAuthSelector: boolean) {
|
||||||
Vue.set(this.modals[name], 'showAuthSelector', show);
|
this.modals[name] = {
|
||||||
|
...this.modals[name],
|
||||||
|
showAuthSelector,
|
||||||
|
} as NewCredentialsModal;
|
||||||
},
|
},
|
||||||
setModalData(payload: { name: string; data: Record<string, unknown> }) {
|
setModalData(payload: { name: keyof Modals; data: Record<string, unknown> }) {
|
||||||
Vue.set(this.modals[payload.name], 'data', payload.data);
|
this.modals[payload.name] = {
|
||||||
|
...this.modals[payload.name],
|
||||||
|
data: payload.data,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
openModal(name: string): void {
|
openModal(name: keyof Modals): void {
|
||||||
Vue.set(this.modals[name], 'open', true);
|
this.modals[name] = {
|
||||||
this.modalStack = [name].concat(this.modalStack);
|
...this.modals[name],
|
||||||
|
open: true,
|
||||||
|
};
|
||||||
|
this.modalStack = [name].concat(this.modalStack) as string[];
|
||||||
},
|
},
|
||||||
openModalWithData(payload: { name: string; data: Record<string, unknown> }): void {
|
openModalWithData(payload: { name: keyof Modals; data: Record<string, unknown> }): void {
|
||||||
this.setModalData(payload);
|
this.setModalData(payload);
|
||||||
this.openModal(payload.name);
|
this.openModal(payload.name);
|
||||||
},
|
},
|
||||||
closeModal(name: string): void {
|
closeModal(name: keyof Modals): void {
|
||||||
Vue.set(this.modals[name], 'open', false);
|
this.modals[name] = {
|
||||||
|
...this.modals[name],
|
||||||
|
open: false,
|
||||||
|
};
|
||||||
this.modalStack = this.modalStack.filter((openModalName: string) => {
|
this.modalStack = this.modalStack.filter((openModalName: string) => {
|
||||||
return name !== openModalName;
|
return name !== openModalName;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
closeAllModals(): void {
|
closeAllModals(): void {
|
||||||
Object.keys(this.modals).forEach((name: string) => {
|
Object.keys(this.modals).forEach((name) => {
|
||||||
if (this.modals[name].open) {
|
if (this.modals[name].open) {
|
||||||
Vue.set(this.modals[name], 'open', false);
|
this.modals[name] = {
|
||||||
|
...this.modals[name],
|
||||||
|
open: false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.modalStack = [];
|
this.modalStack = [];
|
||||||
@@ -385,10 +406,16 @@ export const useUIStore = defineStore(STORES.UI, {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
setDraggableStickyPos(position: XYPosition): void {
|
setDraggableStickyPos(position: XYPosition): void {
|
||||||
Vue.set(this.draggable, 'stickyPosition', position);
|
this.draggable = {
|
||||||
|
...this.draggable,
|
||||||
|
stickyPosition: position,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
setDraggableCanDrop(canDrop: boolean): void {
|
setDraggableCanDrop(canDrop: boolean): void {
|
||||||
Vue.set(this.draggable, 'canDrop', canDrop);
|
this.draggable = {
|
||||||
|
...this.draggable,
|
||||||
|
canDrop,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
openDeleteUserModal(id: string): void {
|
openDeleteUserModal(id: string): void {
|
||||||
this.setActiveId(DELETE_USER_MODAL_KEY, id);
|
this.setActiveId(DELETE_USER_MODAL_KEY, id);
|
||||||
@@ -460,17 +487,23 @@ export const useUIStore = defineStore(STORES.UI, {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
resetSelectedNodes(): void {
|
resetSelectedNodes(): void {
|
||||||
Vue.set(this, 'selectedNodes', []);
|
this.selectedNodes = [];
|
||||||
},
|
},
|
||||||
addSidebarMenuItems(menuItems: IMenuItem[]) {
|
addSidebarMenuItems(menuItems: IMenuItem[]) {
|
||||||
const updated = this.sidebarMenuItems.concat(menuItems);
|
const updated = this.sidebarMenuItems.concat(menuItems);
|
||||||
Vue.set(this, 'sidebarMenuItems', updated);
|
this.sidebarMenuItems = updated;
|
||||||
},
|
},
|
||||||
setCurlCommand(payload: { name: string; command: string }): void {
|
setCurlCommand(payload: { name: string; command: string }): void {
|
||||||
Vue.set(this.modals[payload.name], 'curlCommand', payload.command);
|
this.modals[payload.name] = {
|
||||||
|
...this.modals[payload.name],
|
||||||
|
curlCommand: payload.command,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
setHttpNodeParameters(payload: { name: string; parameters: string }): void {
|
setHttpNodeParameters(payload: { name: string; parameters: string }): void {
|
||||||
Vue.set(this.modals[payload.name], 'httpNodeParameters', payload.parameters);
|
this.modals[payload.name] = {
|
||||||
|
...this.modals[payload.name],
|
||||||
|
httpNodeParameters: payload.parameters,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
toggleSidebarMenuCollapse(): void {
|
toggleSidebarMenuCollapse(): void {
|
||||||
this.sidebarMenuCollapsed = !this.sidebarMenuCollapsed;
|
this.sidebarMenuCollapsed = !this.sidebarMenuCollapsed;
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ import type {
|
|||||||
import { getCredentialPermissions } from '@/permissions';
|
import { getCredentialPermissions } from '@/permissions';
|
||||||
import { getPersonalizedNodeTypes, isAuthorized, PERMISSIONS, ROLE } from '@/utils';
|
import { getPersonalizedNodeTypes, isAuthorized, PERMISSIONS, ROLE } from '@/utils';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import Vue from 'vue';
|
|
||||||
import { useRootStore } from './n8nRoot.store';
|
import { useRootStore } from './n8nRoot.store';
|
||||||
import { usePostHog } from './posthog.store';
|
import { usePostHog } from './posthog.store';
|
||||||
import { useSettingsStore } from './settings.store';
|
import { useSettingsStore } from './settings.store';
|
||||||
@@ -133,17 +132,29 @@ export const useUsersStore = defineStore(STORES.USERS, {
|
|||||||
isPendingUser: isPendingUser(updatedUser),
|
isPendingUser: isPendingUser(updatedUser),
|
||||||
isOwner: updatedUser.globalRole?.name === ROLE.Owner,
|
isOwner: updatedUser.globalRole?.name === ROLE.Owner,
|
||||||
};
|
};
|
||||||
Vue.set(this.users, user.id, user);
|
|
||||||
|
this.users = {
|
||||||
|
...this.users,
|
||||||
|
[user.id]: user,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
deleteUserById(userId: string): void {
|
deleteUserById(userId: string): void {
|
||||||
Vue.delete(this.users, userId);
|
const { [userId]: _, ...users } = this.users;
|
||||||
|
this.users = users;
|
||||||
},
|
},
|
||||||
setPersonalizationAnswers(answers: IPersonalizationLatestVersion): void {
|
setPersonalizationAnswers(answers: IPersonalizationLatestVersion): void {
|
||||||
if (!this.currentUser) {
|
if (!this.currentUser) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Vue.set(this.currentUser, 'personalizationAnswers', answers);
|
|
||||||
|
this.users = {
|
||||||
|
...this.users,
|
||||||
|
[this.currentUser.id]: {
|
||||||
|
...this.currentUser,
|
||||||
|
personalizationAnswers: answers,
|
||||||
|
},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
async loginWithCookie(): Promise<void> {
|
async loginWithCookie(): Promise<void> {
|
||||||
const rootStore = useRootStore();
|
const rootStore = useRootStore();
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import Vue from 'vue';
|
|
||||||
import type { IUser } from '../Interface';
|
import type { IUser } from '../Interface';
|
||||||
import { setWorkflowSharedWith } from '@/api/workflows.ee';
|
import { setWorkflowSharedWith } from '@/api/workflows.ee';
|
||||||
import { EnterpriseEditionFeature, STORES } from '@/constants';
|
import { EnterpriseEditionFeature, STORES } from '@/constants';
|
||||||
@@ -29,8 +28,14 @@ export const useWorkflowsEEStore = defineStore(STORES.WORKFLOWS_EE, {
|
|||||||
setWorkflowOwnedBy(payload: { workflowId: string; ownedBy: Partial<IUser> }): void {
|
setWorkflowOwnedBy(payload: { workflowId: string; ownedBy: Partial<IUser> }): void {
|
||||||
const workflowsStore = useWorkflowsStore();
|
const workflowsStore = useWorkflowsStore();
|
||||||
|
|
||||||
Vue.set(workflowsStore.workflowsById[payload.workflowId], 'ownedBy', payload.ownedBy);
|
workflowsStore.workflowsById[payload.workflowId] = {
|
||||||
Vue.set(workflowsStore.workflow, 'ownedBy', payload.ownedBy);
|
...workflowsStore.workflowsById[payload.workflowId],
|
||||||
|
ownedBy: payload.ownedBy,
|
||||||
|
};
|
||||||
|
workflowsStore.workflow = {
|
||||||
|
...workflowsStore.workflow,
|
||||||
|
ownedBy: payload.ownedBy,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
setWorkflowSharedWith(payload: {
|
setWorkflowSharedWith(payload: {
|
||||||
workflowId: string;
|
workflowId: string;
|
||||||
@@ -38,30 +43,34 @@ export const useWorkflowsEEStore = defineStore(STORES.WORKFLOWS_EE, {
|
|||||||
}): void {
|
}): void {
|
||||||
const workflowsStore = useWorkflowsStore();
|
const workflowsStore = useWorkflowsStore();
|
||||||
|
|
||||||
Vue.set(workflowsStore.workflowsById[payload.workflowId], 'sharedWith', payload.sharedWith);
|
workflowsStore.workflowsById[payload.workflowId] = {
|
||||||
Vue.set(workflowsStore.workflow, 'sharedWith', payload.sharedWith);
|
...workflowsStore.workflowsById[payload.workflowId],
|
||||||
|
sharedWith: payload.sharedWith,
|
||||||
|
};
|
||||||
|
workflowsStore.workflow = {
|
||||||
|
...workflowsStore.workflow,
|
||||||
|
sharedWith: payload.sharedWith,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
addWorkflowSharee(payload: { workflowId: string; sharee: Partial<IUser> }): void {
|
addWorkflowSharee(payload: { workflowId: string; sharee: Partial<IUser> }): void {
|
||||||
const workflowsStore = useWorkflowsStore();
|
const workflowsStore = useWorkflowsStore();
|
||||||
|
|
||||||
Vue.set(
|
workflowsStore.workflowsById[payload.workflowId] = {
|
||||||
workflowsStore.workflowsById[payload.workflowId],
|
...workflowsStore.workflowsById[payload.workflowId],
|
||||||
'sharedWith',
|
sharedWith: (workflowsStore.workflowsById[payload.workflowId].sharedWith || []).concat([
|
||||||
(workflowsStore.workflowsById[payload.workflowId].sharedWith || []).concat([
|
|
||||||
payload.sharee,
|
payload.sharee,
|
||||||
]),
|
]),
|
||||||
);
|
};
|
||||||
},
|
},
|
||||||
removeWorkflowSharee(payload: { workflowId: string; sharee: Partial<IUser> }): void {
|
removeWorkflowSharee(payload: { workflowId: string; sharee: Partial<IUser> }): void {
|
||||||
const workflowsStore = useWorkflowsStore();
|
const workflowsStore = useWorkflowsStore();
|
||||||
|
|
||||||
Vue.set(
|
workflowsStore.workflowsById[payload.workflowId] = {
|
||||||
workflowsStore.workflowsById[payload.workflowId],
|
...workflowsStore.workflowsById[payload.workflowId],
|
||||||
'sharedWith',
|
sharedWith: (workflowsStore.workflowsById[payload.workflowId].sharedWith || []).filter(
|
||||||
(workflowsStore.workflowsById[payload.workflowId].sharedWith || []).filter(
|
|
||||||
(sharee) => sharee.id !== payload.sharee.id,
|
(sharee) => sharee.id !== payload.sharee.id,
|
||||||
),
|
),
|
||||||
);
|
};
|
||||||
},
|
},
|
||||||
async saveWorkflowSharedWith(payload: {
|
async saveWorkflowSharedWith(payload: {
|
||||||
sharedWith: Array<Partial<IUser>>;
|
sharedWith: Array<Partial<IUser>>;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import type {
|
|||||||
IExecutionsListResponse,
|
IExecutionsListResponse,
|
||||||
IExecutionsStopData,
|
IExecutionsStopData,
|
||||||
INewWorkflowData,
|
INewWorkflowData,
|
||||||
|
INodeMetadata,
|
||||||
INodeUi,
|
INodeUi,
|
||||||
INodeUpdatePropertiesInformation,
|
INodeUpdatePropertiesInformation,
|
||||||
IPushDataExecutionFinished,
|
IPushDataExecutionFinished,
|
||||||
@@ -26,6 +27,7 @@ import type {
|
|||||||
IStartRunData,
|
IStartRunData,
|
||||||
IUpdateInformation,
|
IUpdateInformation,
|
||||||
IUsedCredential,
|
IUsedCredential,
|
||||||
|
IUser,
|
||||||
IWorkflowDataUpdate,
|
IWorkflowDataUpdate,
|
||||||
IWorkflowDb,
|
IWorkflowDb,
|
||||||
IWorkflowsMap,
|
IWorkflowsMap,
|
||||||
@@ -44,6 +46,7 @@ import type {
|
|||||||
INodeCredentialsDetails,
|
INodeCredentialsDetails,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
INodeIssueData,
|
INodeIssueData,
|
||||||
|
INodeIssueObjectProperty,
|
||||||
INodeParameters,
|
INodeParameters,
|
||||||
INodeTypeData,
|
INodeTypeData,
|
||||||
INodeTypes,
|
INodeTypes,
|
||||||
@@ -83,6 +86,7 @@ import { useNodeTypesStore } from './nodeTypes.store';
|
|||||||
import { useWorkflowsEEStore } from '@/stores/workflows.ee.store';
|
import { useWorkflowsEEStore } from '@/stores/workflows.ee.store';
|
||||||
import { useUsersStore } from '@/stores/users.store';
|
import { useUsersStore } from '@/stores/users.store';
|
||||||
import { useSettingsStore } from '@/stores/settings.store';
|
import { useSettingsStore } from '@/stores/settings.store';
|
||||||
|
import type { NodeMetadataMap } from '@/Interface';
|
||||||
|
|
||||||
const createEmptyWorkflow = (): IWorkflowDb => ({
|
const createEmptyWorkflow = (): IWorkflowDb => ({
|
||||||
id: PLACEHOLDER_EMPTY_WORKFLOW_ID,
|
id: PLACEHOLDER_EMPTY_WORKFLOW_ID,
|
||||||
@@ -416,7 +420,10 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
this.workflow = createEmptyWorkflow();
|
this.workflow = createEmptyWorkflow();
|
||||||
|
|
||||||
if (settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)) {
|
if (settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)) {
|
||||||
Vue.set(this.workflow, 'ownedBy', usersStore.currentUser);
|
this.workflow = {
|
||||||
|
...this.workflow,
|
||||||
|
ownedBy: usersStore.currentUser as IUser,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -509,10 +516,13 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
addWorkflow(workflow: IWorkflowDb): void {
|
addWorkflow(workflow: IWorkflowDb): void {
|
||||||
Vue.set(this.workflowsById, workflow.id, {
|
this.workflowsById = {
|
||||||
|
...this.workflowsById,
|
||||||
|
[workflow.id]: {
|
||||||
...this.workflowsById[workflow.id],
|
...this.workflowsById[workflow.id],
|
||||||
...deepCopy(workflow),
|
...deepCopy(workflow),
|
||||||
});
|
},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
setWorkflowActive(workflowId: string): void {
|
setWorkflowActive(workflowId: string): void {
|
||||||
@@ -576,57 +586,60 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
setWorkflowSettings(workflowSettings: IWorkflowSettings): void {
|
setWorkflowSettings(workflowSettings: IWorkflowSettings): void {
|
||||||
Vue.set(this.workflow, 'settings', workflowSettings);
|
this.workflow = {
|
||||||
|
...this.workflow,
|
||||||
|
settings: workflowSettings as IWorkflowDb['settings'],
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
setWorkflowPinData(pinData: IPinData): void {
|
setWorkflowPinData(pinData: IPinData): void {
|
||||||
Vue.set(this.workflow, 'pinData', pinData || {});
|
this.workflow = {
|
||||||
|
...this.workflow,
|
||||||
|
pinData: pinData || {},
|
||||||
|
};
|
||||||
dataPinningEventBus.emit('pin-data', pinData || {});
|
dataPinningEventBus.emit('pin-data', pinData || {});
|
||||||
},
|
},
|
||||||
|
|
||||||
setWorkflowTagIds(tags: string[]): void {
|
setWorkflowTagIds(tags: string[]): void {
|
||||||
Vue.set(this.workflow, 'tags', tags);
|
this.workflow = {
|
||||||
|
...this.workflow,
|
||||||
|
tags,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
addWorkflowTagIds(tags: string[]): void {
|
addWorkflowTagIds(tags: string[]): void {
|
||||||
Vue.set(this.workflow, 'tags', [...new Set([...(this.workflow.tags || []), ...tags])]);
|
this.workflow = {
|
||||||
|
...this.workflow,
|
||||||
|
tags: [...new Set([...(this.workflow.tags || []), ...tags])] as IWorkflowDb['tags'],
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
removeWorkflowTagId(tagId: string): void {
|
removeWorkflowTagId(tagId: string): void {
|
||||||
const tags = this.workflow.tags as string[];
|
const tags = this.workflow.tags as string[];
|
||||||
const updated = tags.filter((id: string) => id !== tagId);
|
const updated = tags.filter((id: string) => id !== tagId);
|
||||||
Vue.set(this.workflow, 'tags', updated);
|
this.workflow = {
|
||||||
|
...this.workflow,
|
||||||
|
tags: updated as IWorkflowDb['tags'],
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
setWorkflow(workflow: IWorkflowDb): void {
|
setWorkflow(workflow: IWorkflowDb): void {
|
||||||
Vue.set(this, 'workflow', workflow);
|
this.workflow = workflow;
|
||||||
|
this.workflow = {
|
||||||
if (!this.workflow.hasOwnProperty('active')) {
|
...this.workflow,
|
||||||
Vue.set(this.workflow, 'active', false);
|
...(!this.workflow.hasOwnProperty('active') ? { active: false } : {}),
|
||||||
}
|
...(!this.workflow.hasOwnProperty('connections') ? { connections: {} } : {}),
|
||||||
if (!this.workflow.hasOwnProperty('connections')) {
|
...(!this.workflow.hasOwnProperty('createdAt') ? { createdAt: -1 } : {}),
|
||||||
Vue.set(this.workflow, 'connections', {});
|
...(!this.workflow.hasOwnProperty('updatedAt') ? { updatedAt: -1 } : {}),
|
||||||
}
|
...(!this.workflow.hasOwnProperty('id') ? { id: PLACEHOLDER_EMPTY_WORKFLOW_ID } : {}),
|
||||||
if (!this.workflow.hasOwnProperty('createdAt')) {
|
...(!this.workflow.hasOwnProperty('nodes') ? { nodes: [] } : {}),
|
||||||
Vue.set(this.workflow, 'createdAt', -1);
|
...(!this.workflow.hasOwnProperty('settings') ? { settings: {} } : {}),
|
||||||
}
|
};
|
||||||
if (!this.workflow.hasOwnProperty('updatedAt')) {
|
|
||||||
Vue.set(this.workflow, 'updatedAt', -1);
|
|
||||||
}
|
|
||||||
if (!this.workflow.hasOwnProperty('id')) {
|
|
||||||
Vue.set(this.workflow, 'id', PLACEHOLDER_EMPTY_WORKFLOW_ID);
|
|
||||||
}
|
|
||||||
if (!this.workflow.hasOwnProperty('nodes')) {
|
|
||||||
Vue.set(this.workflow, 'nodes', []);
|
|
||||||
}
|
|
||||||
if (!this.workflow.hasOwnProperty('settings')) {
|
|
||||||
Vue.set(this.workflow, 'settings', {});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
pinData(payload: { node: INodeUi; data: INodeExecutionData[] }): void {
|
pinData(payload: { node: INodeUi; data: INodeExecutionData[] }): void {
|
||||||
if (!this.workflow.pinData) {
|
if (!this.workflow.pinData) {
|
||||||
Vue.set(this.workflow, 'pinData', {});
|
this.workflow = { ...this.workflow, pinData: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Array.isArray(payload.data)) {
|
if (!Array.isArray(payload.data)) {
|
||||||
@@ -637,7 +650,13 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
isJsonKeyObject(item) ? item : { json: item },
|
isJsonKeyObject(item) ? item : { json: item },
|
||||||
);
|
);
|
||||||
|
|
||||||
Vue.set(this.workflow.pinData!, payload.node.name, storedPinData);
|
this.workflow = {
|
||||||
|
...this.workflow,
|
||||||
|
pinData: {
|
||||||
|
...this.workflow.pinData,
|
||||||
|
[payload.node.name]: storedPinData,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const uiStore = useUIStore();
|
const uiStore = useUIStore();
|
||||||
uiStore.stateIsDirty = true;
|
uiStore.stateIsDirty = true;
|
||||||
@@ -647,11 +666,14 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
|
|
||||||
unpinData(payload: { node: INodeUi }): void {
|
unpinData(payload: { node: INodeUi }): void {
|
||||||
if (!this.workflow.pinData) {
|
if (!this.workflow.pinData) {
|
||||||
Vue.set(this.workflow, 'pinData', {});
|
this.workflow = { ...this.workflow, pinData: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.set(this.workflow.pinData!, payload.node.name, undefined);
|
const { [payload.node.name]: _, ...pinData } = this.workflow.pinData!;
|
||||||
delete this.workflow.pinData![payload.node.name];
|
this.workflow = {
|
||||||
|
...this.workflow,
|
||||||
|
pinData,
|
||||||
|
};
|
||||||
|
|
||||||
const uiStore = useUIStore();
|
const uiStore = useUIStore();
|
||||||
uiStore.stateIsDirty = true;
|
uiStore.stateIsDirty = true;
|
||||||
@@ -670,10 +692,25 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
|
|
||||||
// Check if source node and type exist already and if not add them
|
// Check if source node and type exist already and if not add them
|
||||||
if (!this.workflow.connections.hasOwnProperty(sourceData.node)) {
|
if (!this.workflow.connections.hasOwnProperty(sourceData.node)) {
|
||||||
Vue.set(this.workflow.connections, sourceData.node, {});
|
this.workflow = {
|
||||||
|
...this.workflow,
|
||||||
|
connections: {
|
||||||
|
...this.workflow.connections,
|
||||||
|
[sourceData.node]: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (!this.workflow.connections[sourceData.node].hasOwnProperty(sourceData.type)) {
|
if (!this.workflow.connections[sourceData.node].hasOwnProperty(sourceData.type)) {
|
||||||
Vue.set(this.workflow.connections[sourceData.node], sourceData.type, []);
|
this.workflow = {
|
||||||
|
...this.workflow,
|
||||||
|
connections: {
|
||||||
|
...this.workflow.connections,
|
||||||
|
[sourceData.node]: {
|
||||||
|
...this.workflow.connections[sourceData.node],
|
||||||
|
[sourceData.type]: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
this.workflow.connections[sourceData.node][sourceData.type].length <
|
this.workflow.connections[sourceData.node][sourceData.type].length <
|
||||||
@@ -817,12 +854,19 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
uiStore.lastSelectedNode = nameData.new;
|
uiStore.lastSelectedNode = nameData.new;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.set(this.nodeMetadata, nameData.new, this.nodeMetadata[nameData.old]);
|
this.nodeMetadata = { ...this.nodeMetadata, [nameData.new]: this.nodeMetadata[nameData.old] };
|
||||||
Vue.delete(this.nodeMetadata, nameData.old);
|
Vue.delete(this.nodeMetadata, nameData.old);
|
||||||
|
|
||||||
if (this.workflow.pinData && this.workflow.pinData.hasOwnProperty(nameData.old)) {
|
if (this.workflow.pinData && this.workflow.pinData.hasOwnProperty(nameData.old)) {
|
||||||
Vue.set(this.workflow.pinData, nameData.new, this.workflow.pinData[nameData.old]);
|
this.workflow = {
|
||||||
Vue.delete(this.workflow.pinData, nameData.old);
|
...this.workflow,
|
||||||
|
pinData: {
|
||||||
|
...this.workflow.pinData,
|
||||||
|
[nameData.new]: this.workflow.pinData[nameData.old],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Vue.delete(this.workflow.pinData!, nameData.old);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.workflowExecutionPairedItemMappings = getPairedItemsMapping(this.workflowExecutionData);
|
this.workflowExecutionPairedItemMappings = getPairedItemsMapping(this.workflowExecutionData);
|
||||||
@@ -835,13 +879,31 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateNodeAtIndex(nodeIndex: number, nodeData: Partial<INodeUi>): void {
|
||||||
|
if (nodeIndex !== -1) {
|
||||||
|
const node = this.workflow.nodes[nodeIndex];
|
||||||
|
this.workflow = {
|
||||||
|
...this.workflow,
|
||||||
|
nodes: [
|
||||||
|
...this.workflow.nodes.slice(0, nodeIndex),
|
||||||
|
{ ...node, ...nodeData },
|
||||||
|
...this.workflow.nodes.slice(nodeIndex + 1),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
setNodeIssue(nodeIssueData: INodeIssueData): boolean {
|
setNodeIssue(nodeIssueData: INodeIssueData): boolean {
|
||||||
const node = this.workflow.nodes.find((node) => {
|
const nodeIndex = this.workflow.nodes.findIndex((node) => {
|
||||||
return node.name === nodeIssueData.node;
|
return node.name === nodeIssueData.node;
|
||||||
});
|
});
|
||||||
if (!node) {
|
|
||||||
|
if (nodeIndex === -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const node = this.workflow.nodes[nodeIndex!];
|
||||||
|
|
||||||
if (nodeIssueData.value === null) {
|
if (nodeIssueData.value === null) {
|
||||||
// Remove the value if one exists
|
// Remove the value if one exists
|
||||||
if (node.issues === undefined || node.issues[nodeIssueData.type] === undefined) {
|
if (node.issues === undefined || node.issues[nodeIssueData.type] === undefined) {
|
||||||
@@ -853,10 +915,17 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
Vue.delete(node.issues, nodeIssueData.type);
|
Vue.delete(node.issues, nodeIssueData.type);
|
||||||
} else {
|
} else {
|
||||||
if (node.issues === undefined) {
|
if (node.issues === undefined) {
|
||||||
Vue.set(node, 'issues', {});
|
this.updateNodeAtIndex(nodeIndex, {
|
||||||
|
issues: {},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// Set/Overwrite the value
|
|
||||||
Vue.set(node.issues!, nodeIssueData.type, nodeIssueData.value);
|
this.updateNodeAtIndex(nodeIndex, {
|
||||||
|
issues: {
|
||||||
|
...node.issues,
|
||||||
|
[nodeIssueData.type]: nodeIssueData.value as INodeIssueObjectProperty,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
@@ -871,7 +940,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
this.workflow.nodes.push(nodeData);
|
this.workflow.nodes.push(nodeData);
|
||||||
// Init node metadata
|
// Init node metadata
|
||||||
if (!this.nodeMetadata[nodeData.name]) {
|
if (!this.nodeMetadata[nodeData.name]) {
|
||||||
Vue.set(this.nodeMetadata, nodeData.name, {});
|
this.nodeMetadata = { ...this.nodeMetadata, [nodeData.name]: {} as INodeMetadata };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -899,7 +968,10 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data.removePinData) {
|
if (data.removePinData) {
|
||||||
Vue.set(this.workflow, 'pinData', {});
|
this.workflow = {
|
||||||
|
...this.workflow,
|
||||||
|
pinData: {},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.workflow.nodes.splice(0, this.workflow.nodes.length);
|
this.workflow.nodes.splice(0, this.workflow.nodes.length);
|
||||||
@@ -908,26 +980,29 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
|
|
||||||
updateNodeProperties(updateInformation: INodeUpdatePropertiesInformation): void {
|
updateNodeProperties(updateInformation: INodeUpdatePropertiesInformation): void {
|
||||||
// Find the node that should be updated
|
// Find the node that should be updated
|
||||||
const node = this.workflow.nodes.find((node) => {
|
const nodeIndex = this.workflow.nodes.findIndex((node) => {
|
||||||
return node.name === updateInformation.name;
|
return node.name === updateInformation.name;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (node) {
|
if (nodeIndex !== -1) {
|
||||||
for (const key of Object.keys(updateInformation.properties)) {
|
for (const key of Object.keys(updateInformation.properties)) {
|
||||||
const uiStore = useUIStore();
|
const uiStore = useUIStore();
|
||||||
uiStore.stateIsDirty = true;
|
uiStore.stateIsDirty = true;
|
||||||
Vue.set(node, key, updateInformation.properties[key]);
|
|
||||||
|
this.updateNodeAtIndex(nodeIndex, {
|
||||||
|
[key]: updateInformation.properties[key],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setNodeValue(updateInformation: IUpdateInformation): void {
|
setNodeValue(updateInformation: IUpdateInformation): void {
|
||||||
// Find the node that should be updated
|
// Find the node that should be updated
|
||||||
const node = this.workflow.nodes.find((node) => {
|
const nodeIndex = this.workflow.nodes.findIndex((node) => {
|
||||||
return node.name === updateInformation.name;
|
return node.name === updateInformation.name;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (node === undefined || node === null || !updateInformation.key) {
|
if (nodeIndex === -1 || !updateInformation.key) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Node with the name "${updateInformation.name}" could not be found to set parameter.`,
|
`Node with the name "${updateInformation.name}" could not be found to set parameter.`,
|
||||||
);
|
);
|
||||||
@@ -935,21 +1010,26 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
|
|
||||||
const uiStore = useUIStore();
|
const uiStore = useUIStore();
|
||||||
uiStore.stateIsDirty = true;
|
uiStore.stateIsDirty = true;
|
||||||
Vue.set(node, updateInformation.key, updateInformation.value);
|
|
||||||
|
this.updateNodeAtIndex(nodeIndex, {
|
||||||
|
[updateInformation.key]: updateInformation.value,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setNodeParameters(updateInformation: IUpdateInformation, append?: boolean): void {
|
setNodeParameters(updateInformation: IUpdateInformation, append?: boolean): void {
|
||||||
// Find the node that should be updated
|
// Find the node that should be updated
|
||||||
const node = this.workflow.nodes.find((node) => {
|
const nodeIndex = this.workflow.nodes.findIndex((node) => {
|
||||||
return node.name === updateInformation.name;
|
return node.name === updateInformation.name;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (node === undefined || node === null) {
|
if (nodeIndex === -1) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Node with the name "${updateInformation.name}" could not be found to set parameter.`,
|
`Node with the name "${updateInformation.name}" could not be found to set parameter.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const node = this.workflow.nodes[nodeIndex];
|
||||||
|
|
||||||
const uiStore = useUIStore();
|
const uiStore = useUIStore();
|
||||||
uiStore.stateIsDirty = true;
|
uiStore.stateIsDirty = true;
|
||||||
const newParameters =
|
const newParameters =
|
||||||
@@ -957,13 +1037,17 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
? { ...node.parameters, ...updateInformation.value }
|
? { ...node.parameters, ...updateInformation.value }
|
||||||
: updateInformation.value;
|
: updateInformation.value;
|
||||||
|
|
||||||
Vue.set(node, 'parameters', newParameters);
|
this.updateNodeAtIndex(nodeIndex, {
|
||||||
|
parameters: newParameters as INodeParameters,
|
||||||
|
});
|
||||||
|
|
||||||
if (!this.nodeMetadata[node.name]) {
|
this.nodeMetadata = {
|
||||||
Vue.set(this.nodeMetadata, node.name, {});
|
...this.nodeMetadata,
|
||||||
}
|
[node.name]: {
|
||||||
|
...this.nodeMetadata[node.name],
|
||||||
Vue.set(this.nodeMetadata[node.name], 'parametersLastUpdatedAt', Date.now());
|
parametersLastUpdatedAt: Date.now(),
|
||||||
|
},
|
||||||
|
} as NodeMetadataMap;
|
||||||
},
|
},
|
||||||
|
|
||||||
setLastNodeParameters(updateInformation: IUpdateInformation) {
|
setLastNodeParameters(updateInformation: IUpdateInformation) {
|
||||||
@@ -989,9 +1073,21 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
throw new Error('The "workflowExecutionData" is not initialized!');
|
throw new Error('The "workflowExecutionData" is not initialized!');
|
||||||
}
|
}
|
||||||
if (this.workflowExecutionData.data.resultData.runData[pushData.nodeName] === undefined) {
|
if (this.workflowExecutionData.data.resultData.runData[pushData.nodeName] === undefined) {
|
||||||
Vue.set(this.workflowExecutionData.data.resultData.runData, pushData.nodeName, []);
|
this.workflowExecutionData = {
|
||||||
|
...this.workflowExecutionData,
|
||||||
|
data: {
|
||||||
|
...this.workflowExecutionData.data,
|
||||||
|
resultData: {
|
||||||
|
...this.workflowExecutionData.data.resultData,
|
||||||
|
runData: {
|
||||||
|
...this.workflowExecutionData.data.resultData.runData,
|
||||||
|
[pushData.nodeName]: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
this.workflowExecutionData.data.resultData.runData[pushData.nodeName].push(pushData.data);
|
this.workflowExecutionData.data!.resultData.runData[pushData.nodeName].push(pushData.data);
|
||||||
this.workflowExecutionPairedItemMappings = getPairedItemsMapping(this.workflowExecutionData);
|
this.workflowExecutionPairedItemMappings = getPairedItemsMapping(this.workflowExecutionData);
|
||||||
},
|
},
|
||||||
clearNodeExecutionData(nodeName: string): void {
|
clearNodeExecutionData(nodeName: string): void {
|
||||||
@@ -1033,28 +1129,37 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
finishedActiveExecution: IPushDataExecutionFinished | IPushDataUnsavedExecutionFinished,
|
finishedActiveExecution: IPushDataExecutionFinished | IPushDataUnsavedExecutionFinished,
|
||||||
): void {
|
): void {
|
||||||
// Find the execution to set to finished
|
// Find the execution to set to finished
|
||||||
const activeExecution = this.activeExecutions.find((execution) => {
|
const activeExecutionIndex = this.activeExecutions.findIndex((execution) => {
|
||||||
return execution.id === finishedActiveExecution.executionId;
|
return execution.id === finishedActiveExecution.executionId;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (activeExecution === undefined) {
|
if (activeExecutionIndex === -1) {
|
||||||
// The execution could not be found
|
// The execution could not be found
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finishedActiveExecution.executionId !== undefined) {
|
const activeExecution = this.activeExecutions[activeExecutionIndex];
|
||||||
Vue.set(activeExecution, 'id', finishedActiveExecution.executionId);
|
|
||||||
}
|
this.activeExecutions = [
|
||||||
|
...this.activeExecutions.slice(0, activeExecutionIndex),
|
||||||
|
{
|
||||||
|
...activeExecution,
|
||||||
|
...(finishedActiveExecution.executionId !== undefined
|
||||||
|
? { id: finishedActiveExecution.executionId }
|
||||||
|
: {}),
|
||||||
|
finished: finishedActiveExecution.data.finished,
|
||||||
|
stoppedAt: finishedActiveExecution.data.stoppedAt,
|
||||||
|
},
|
||||||
|
...this.activeExecutions.slice(activeExecutionIndex + 1),
|
||||||
|
];
|
||||||
|
|
||||||
Vue.set(activeExecution, 'finished', finishedActiveExecution.data.finished);
|
|
||||||
Vue.set(activeExecution, 'stoppedAt', finishedActiveExecution.data.stoppedAt);
|
|
||||||
if (finishedActiveExecution.data && (finishedActiveExecution.data as IRun).data) {
|
if (finishedActiveExecution.data && (finishedActiveExecution.data as IRun).data) {
|
||||||
this.setWorkflowExecutionRunData((finishedActiveExecution.data as IRun).data);
|
this.setWorkflowExecutionRunData((finishedActiveExecution.data as IRun).data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setActiveExecutions(newActiveExecutions: IExecutionsCurrentSummaryExtended[]): void {
|
setActiveExecutions(newActiveExecutions: IExecutionsCurrentSummaryExtended[]): void {
|
||||||
Vue.set(this, 'activeExecutions', newActiveExecutions);
|
this.activeExecutions = newActiveExecutions;
|
||||||
},
|
},
|
||||||
|
|
||||||
async retryExecution(id: string, loadWorkflow?: boolean): Promise<boolean> {
|
async retryExecution(id: string, loadWorkflow?: boolean): Promise<boolean> {
|
||||||
@@ -1247,7 +1352,13 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
setNodePristine(nodeName: string, isPristine: boolean): void {
|
setNodePristine(nodeName: string, isPristine: boolean): void {
|
||||||
Vue.set(this.nodeMetadata[nodeName], 'pristine', isPristine);
|
this.nodeMetadata = {
|
||||||
|
...this.nodeMetadata,
|
||||||
|
[nodeName]: {
|
||||||
|
...this.nodeMetadata[nodeName],
|
||||||
|
pristine: isPristine,
|
||||||
|
},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user