mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
✨ Render base strings
This commit is contained in:
parent
61bb8de352
commit
2d8e158012
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -1,3 +1,3 @@
|
||||||
{
|
{
|
||||||
"vetur.experimental.templateInterpolationService": true,
|
// "vetur.experimental.templateInterpolationService": true,
|
||||||
}
|
}
|
|
@ -4,18 +4,18 @@
|
||||||
@click.stop="closeWindow"
|
@click.stop="closeWindow"
|
||||||
size="small"
|
size="small"
|
||||||
class="binary-data-window-back"
|
class="binary-data-window-back"
|
||||||
title="Back to overview page"
|
:title="$baseText('binaryDataDisplay.backToOverviewPage')"
|
||||||
icon="arrow-left"
|
icon="arrow-left"
|
||||||
label="Back to list"
|
:label="$baseText('binaryDataDisplay.backToList')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="binary-data-window-wrapper">
|
<div class="binary-data-window-wrapper">
|
||||||
<div v-if="!binaryData">
|
<div v-if="!binaryData">
|
||||||
Data to display did not get found
|
{{ $baseText('binaryDataDisplay.noDataFoundToDisplay') }}
|
||||||
</div>
|
</div>
|
||||||
<video v-else-if="binaryData.mimeType && binaryData.mimeType.startsWith('video/')" controls autoplay>
|
<video v-else-if="binaryData.mimeType && binaryData.mimeType.startsWith('video/')" controls autoplay>
|
||||||
<source :src="'data:' + binaryData.mimeType + ';base64,' + binaryData.data" :type="binaryData.mimeType">
|
<source :src="'data:' + binaryData.mimeType + ';base64,' + binaryData.data" :type="binaryData.mimeType">
|
||||||
Your browser does not support the video element. Kindly update it to latest version.
|
{{ $baseText('binaryDataDisplay.yourBrowserDoesNotSupport') }}
|
||||||
</video>
|
</video>
|
||||||
<embed v-else :src="'data:' + binaryData.mimeType + ';base64,' + binaryData.data" class="binary-data" :class="embedClass"/>
|
<embed v-else :src="'data:' + binaryData.mimeType + ';base64,' + binaryData.data" class="binary-data" :class="embedClass"/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,11 +31,13 @@ import {
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
||||||
|
import { renderText } from '@/components/mixins/renderText';
|
||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
export default mixins(
|
export default mixins(
|
||||||
nodeHelpers,
|
nodeHelpers,
|
||||||
|
renderText,
|
||||||
)
|
)
|
||||||
.extend({
|
.extend({
|
||||||
name: 'BinaryDataDisplay',
|
name: 'BinaryDataDisplay',
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div @keydown.stop class="collection-parameter">
|
<div @keydown.stop class="collection-parameter">
|
||||||
<div class="collection-parameter-wrapper">
|
<div class="collection-parameter-wrapper">
|
||||||
<div v-if="getProperties.length === 0" class="no-items-exist">
|
<div v-if="getProperties.length === 0" class="no-items-exist">
|
||||||
<n8n-text size="small">Currently no properties exist</n8n-text>
|
<n8n-text size="small">{{ $baseText('collectionParameter.noProperties') }}</n8n-text>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<parameter-input-list :parameters="getProperties" :nodeValues="nodeValues" :path="path" :hideDelete="hideDelete" :indent="true" @valueChanged="valueChanged" />
|
<parameter-input-list :parameters="getProperties" :nodeValues="nodeValues" :path="path" :hideDelete="hideDelete" :indent="true" @valueChanged="valueChanged" />
|
||||||
|
@ -67,7 +67,7 @@ export default mixins(
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
getPlaceholderText (): string {
|
getPlaceholderText (): string {
|
||||||
return this.parameter.placeholder ? this.parameter.placeholder : 'Choose Option To Add';
|
return this.parameter.placeholder ? this.parameter.placeholder : this.$baseText('collectionParameter.choose');
|
||||||
},
|
},
|
||||||
getProperties (): INodeProperties[] {
|
getProperties (): INodeProperties[] {
|
||||||
const returnProperties = [];
|
const returnProperties = [];
|
||||||
|
|
|
@ -13,9 +13,10 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
import { copyPaste } from './mixins/copyPaste';
|
import { copyPaste } from './mixins/copyPaste';
|
||||||
|
import { renderText } from './mixins/renderText';
|
||||||
import { showMessage } from './mixins/showMessage';
|
import { showMessage } from './mixins/showMessage';
|
||||||
|
|
||||||
export default mixins(copyPaste, showMessage).extend({
|
export default mixins(copyPaste, showMessage, renderText).extend({
|
||||||
props: {
|
props: {
|
||||||
label: {
|
label: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@ -38,7 +39,7 @@ export default mixins(copyPaste, showMessage).extend({
|
||||||
this.copyToClipboard(this.$props.copyContent);
|
this.copyToClipboard(this.$props.copyContent);
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Copied',
|
title: this.$baseText('credentialsEdit.showMessage.title'),
|
||||||
message: this.$props.successMessage,
|
message: this.$props.successMessage,
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,17 +3,17 @@
|
||||||
<banner
|
<banner
|
||||||
v-show="showValidationWarning"
|
v-show="showValidationWarning"
|
||||||
theme="danger"
|
theme="danger"
|
||||||
message="Please check the errors below"
|
:message="$baseText('credentialEdit.credentialConfig.pleaseCheckTheErrorsBelow')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<banner
|
<banner
|
||||||
v-if="authError && !showValidationWarning"
|
v-if="authError && !showValidationWarning"
|
||||||
theme="danger"
|
theme="danger"
|
||||||
message="Couldn’t connect with these settings"
|
:message="$baseText('credentialEdit.credentialConfig.couldntConnectWithTheseSettings')"
|
||||||
:details="authError"
|
:details="authError"
|
||||||
buttonLabel="Retry"
|
:buttonLabel="$baseText('credentialEdit.credentialConfig.retry')"
|
||||||
buttonLoadingLabel="Retrying"
|
buttonLoadingLabel="Retrying"
|
||||||
buttonTitle="Retry credentials test"
|
:buttonTitle="$baseText('credentialEdit.credentialConfig.retryCredentialTest')"
|
||||||
:buttonLoading="isRetesting"
|
:buttonLoading="isRetesting"
|
||||||
@click="$emit('retest')"
|
@click="$emit('retest')"
|
||||||
/>
|
/>
|
||||||
|
@ -21,35 +21,37 @@
|
||||||
<banner
|
<banner
|
||||||
v-show="showOAuthSuccessBanner && !showValidationWarning"
|
v-show="showOAuthSuccessBanner && !showValidationWarning"
|
||||||
theme="success"
|
theme="success"
|
||||||
message="Account connected"
|
:message="$baseText('credentialEdit.credentialConfig.accountConnected')"
|
||||||
buttonLabel="Reconnect"
|
:buttonLabel="$baseText('credentialEdit.credentialConfig.reconnect')"
|
||||||
buttonTitle="Reconnect OAuth Credentials"
|
:buttonTitle="$baseText('credentialEdit.credentialConfig.reconnectOAuth2Credential')"
|
||||||
@click="$emit('oauth')"
|
@click="$emit('oauth')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<banner
|
<banner
|
||||||
v-show="testedSuccessfully && !showValidationWarning"
|
v-show="testedSuccessfully && !showValidationWarning"
|
||||||
theme="success"
|
theme="success"
|
||||||
message="Connection tested successfully"
|
:message="$baseText('credentialEdit.credentialConfig.connectionTestedSuccessfully')"
|
||||||
buttonLabel="Retry"
|
:buttonLabel="$baseText('credentialEdit.credentialConfig.retry')"
|
||||||
buttonLoadingLabel="Retrying"
|
:buttonLoadingLabel="$baseText('credentialEdit.credentialConfig.retrying')"
|
||||||
buttonTitle="Retry credentials test"
|
:buttonTitle="$baseText('credentialEdit.credentialConfig.retryCredentialTest')"
|
||||||
:buttonLoading="isRetesting"
|
:buttonLoading="isRetesting"
|
||||||
@click="$emit('retest')"
|
@click="$emit('retest')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<n8n-info-tip v-if="documentationUrl && credentialProperties.length">
|
<n8n-info-tip v-if="documentationUrl && credentialProperties.length">
|
||||||
Need help filling out these fields?
|
{{ $baseText('credentialEdit.credentialConfig.needHelpFillingOutTheseFields') }}
|
||||||
<a :href="documentationUrl" target="_blank" @click="onDocumentationUrlClick">Open docs</a>
|
<a :href="documentationUrl" target="_blank" @click="onDocumentationUrlClick">
|
||||||
|
{{ $baseText('credentialEdit.credentialConfig.openDocs') }}
|
||||||
|
</a>
|
||||||
</n8n-info-tip>
|
</n8n-info-tip>
|
||||||
|
|
||||||
<CopyInput
|
<CopyInput
|
||||||
v-if="isOAuthType && credentialProperties.length"
|
v-if="isOAuthType && credentialProperties.length"
|
||||||
label="OAuth Redirect URL"
|
:label="$baseText('credentialEdit.credentialConfig.oAuthRedirectUrl')"
|
||||||
:copyContent="oAuthCallbackUrl"
|
:copyContent="oAuthCallbackUrl"
|
||||||
copyButtonText="Click to copy"
|
:copyButtonText="$baseText('credentialEdit.credentialConfig.clickToCopy')"
|
||||||
:subtitle="`In ${appName}, use the URL above when prompted to enter an OAuth callback or redirect URL`"
|
:subtitle="$baseText('credentialEdit.credentialConfig.subtitle', { interpolate: { appName } })"
|
||||||
successMessage="Redirect URL copied to clipboard"
|
:successMessage="$baseText('credentialEdit.credentialConfig.redirectUrlCopiedToClipboard')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CredentialInputs
|
<CredentialInputs
|
||||||
|
@ -78,8 +80,11 @@ import Banner from '../Banner.vue';
|
||||||
import CopyInput from '../CopyInput.vue';
|
import CopyInput from '../CopyInput.vue';
|
||||||
import CredentialInputs from './CredentialInputs.vue';
|
import CredentialInputs from './CredentialInputs.vue';
|
||||||
import OauthButton from './OauthButton.vue';
|
import OauthButton from './OauthButton.vue';
|
||||||
|
import { renderText } from '../mixins/renderText';
|
||||||
|
|
||||||
export default Vue.extend({
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
|
export default mixins(renderText).extend({
|
||||||
name: 'CredentialConfig',
|
name: 'CredentialConfig',
|
||||||
components: {
|
components: {
|
||||||
Banner,
|
Banner,
|
||||||
|
|
|
@ -53,10 +53,10 @@
|
||||||
:light="true"
|
:light="true"
|
||||||
>
|
>
|
||||||
<n8n-menu-item index="connection" :class="$style.credTab"
|
<n8n-menu-item index="connection" :class="$style.credTab"
|
||||||
><span slot="title">Connection</span></n8n-menu-item
|
><span slot="title">{{ $baseText('credentialEdit.credentialEdit.connection') }}</span></n8n-menu-item
|
||||||
>
|
>
|
||||||
<n8n-menu-item index="details" :class="$style.credTab"
|
<n8n-menu-item index="details" :class="$style.credTab"
|
||||||
><span slot="title">Details</span></n8n-menu-item
|
><span slot="title">{{ $baseText('credentialEdit.credentialEdit.details') }}</span></n8n-menu-item
|
||||||
>
|
>
|
||||||
</n8n-menu>
|
</n8n-menu>
|
||||||
</div>
|
</div>
|
||||||
|
@ -116,6 +116,7 @@ import CredentialIcon from '../CredentialIcon.vue';
|
||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
import { nodeHelpers } from '../mixins/nodeHelpers';
|
import { nodeHelpers } from '../mixins/nodeHelpers';
|
||||||
|
import { renderText } from '../mixins/renderText';
|
||||||
import { showMessage } from '../mixins/showMessage';
|
import { showMessage } from '../mixins/showMessage';
|
||||||
|
|
||||||
import CredentialConfig from './CredentialConfig.vue';
|
import CredentialConfig from './CredentialConfig.vue';
|
||||||
|
@ -128,7 +129,7 @@ interface NodeAccessMap {
|
||||||
[nodeType: string]: ICredentialNodeAccess | null;
|
[nodeType: string]: ICredentialNodeAccess | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default mixins(showMessage, nodeHelpers).extend({
|
export default mixins(renderText, showMessage, nodeHelpers).extend({
|
||||||
name: 'CredentialsDetail',
|
name: 'CredentialsDetail',
|
||||||
components: {
|
components: {
|
||||||
CredentialConfig,
|
CredentialConfig,
|
||||||
|
@ -349,20 +350,20 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
if (this.hasUnsavedChanges) {
|
if (this.hasUnsavedChanges) {
|
||||||
const displayName = this.credentialType ? this.credentialType.displayName : '';
|
const displayName = this.credentialType ? this.credentialType.displayName : '';
|
||||||
keepEditing = await this.confirmMessage(
|
keepEditing = await this.confirmMessage(
|
||||||
`Are you sure you want to throw away the changes you made to the ${displayName} credential?`,
|
this.$baseText('credentialEdit.credentialEdit.confirmMessage.beforeClose1.message', { interpolate: { credentialDisplayName: displayName } }),
|
||||||
'Close without saving?',
|
this.$baseText('credentialEdit.credentialEdit.confirmMessage.beforeClose1.headline'),
|
||||||
null,
|
null,
|
||||||
'Keep editing',
|
this.$baseText('credentialEdit.credentialEdit.confirmMessage.beforeClose1.cancelButtonText'),
|
||||||
'Close',
|
this.$baseText('credentialEdit.credentialEdit.confirmMessage.beforeClose1.confirmButtonText'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (this.isOAuthType && !this.isOAuthConnected) {
|
else if (this.isOAuthType && !this.isOAuthConnected) {
|
||||||
keepEditing = await this.confirmMessage(
|
keepEditing = await this.confirmMessage(
|
||||||
`You need to connect your credential for it to work`,
|
this.$baseText('credentialEdit.credentialEdit.confirmMessage.beforeClose2.message'),
|
||||||
'Close without connecting?',
|
this.$baseText('credentialEdit.credentialEdit.confirmMessage.beforeClose2.headline'),
|
||||||
null,
|
null,
|
||||||
'Keep editing',
|
this.$baseText('credentialEdit.credentialEdit.confirmMessage.beforeClose2.cancelButtonText'),
|
||||||
'Close',
|
this.$baseText('credentialEdit.credentialEdit.confirmMessage.beforeClose2.confirmButtonText'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,7 +401,9 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
this.$store.getters['credentials/getCredentialTypeByName'](name);
|
this.$store.getters['credentials/getCredentialTypeByName'](name);
|
||||||
|
|
||||||
if (!credentialsData) {
|
if (!credentialsData) {
|
||||||
throw new Error(`Could not find credentials of type: ${name}`);
|
throw new Error(
|
||||||
|
this.$baseText('credentialEdit.credentialEdit.couldNotFindCredentialOfType') + ':' + name,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (credentialsData.extends === undefined) {
|
if (credentialsData.extends === undefined) {
|
||||||
|
@ -436,7 +439,7 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
});
|
});
|
||||||
if (!currentCredentials) {
|
if (!currentCredentials) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Could not find the credentials with the id: ${this.credentialId}`,
|
this.$baseText('credentialEdit.credentialEdit.couldNotFindCredentialWithId') + ':' + this.credentialId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,11 +451,11 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
this.nodeAccess[access.nodeType] = access;
|
this.nodeAccess[access.nodeType] = access;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
this.$showError(
|
this.$showError(
|
||||||
e,
|
error,
|
||||||
'Problem loading credentials',
|
this.$baseText('credentialEdit.credentialEdit.showError.loadCredential.title'),
|
||||||
'There was a problem loading the credentials:',
|
this.$baseText('credentialEdit.credentialEdit.showError.loadCredential.message'),
|
||||||
);
|
);
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
|
|
||||||
|
@ -657,8 +660,8 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(
|
this.$showError(
|
||||||
error,
|
error,
|
||||||
'Problem creating credentials',
|
this.$baseText('credentialEdit.credentialEdit.showError.createCredential.title'),
|
||||||
'There was a problem creating the credentials:',
|
this.$baseText('credentialEdit.credentialEdit.showError.createCredential.message'),
|
||||||
);
|
);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -686,8 +689,8 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(
|
this.$showError(
|
||||||
error,
|
error,
|
||||||
'Problem updating credentials',
|
this.$baseText('credentialEdit.credentialEdit.showError.updateCredential.title'),
|
||||||
'There was a problem updating the credentials:',
|
this.$baseText('credentialEdit.credentialEdit.showError.updateCredential.message'),
|
||||||
);
|
);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -708,10 +711,10 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
const savedCredentialName = this.currentCredential.name;
|
const savedCredentialName = this.currentCredential.name;
|
||||||
|
|
||||||
const deleteConfirmed = await this.confirmMessage(
|
const deleteConfirmed = await this.confirmMessage(
|
||||||
`Are you sure you want to delete "${savedCredentialName}" credentials?`,
|
this.$baseText('credentialEdit.credentialEdit.confirmMessage.deleteCredential.message', { interpolate: { savedCredentialName } }),
|
||||||
'Delete Credentials?',
|
this.$baseText('credentialEdit.credentialEdit.confirmMessage.deleteCredential.headline'),
|
||||||
null,
|
null,
|
||||||
'Yes, delete!',
|
this.$baseText('credentialEdit.credentialEdit.confirmMessage.deleteCredential.confirmButtonText'),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (deleteConfirmed === false) {
|
if (deleteConfirmed === false) {
|
||||||
|
@ -727,8 +730,8 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(
|
this.$showError(
|
||||||
error,
|
error,
|
||||||
'Problem deleting credentials',
|
this.$baseText('credentialEdit.credentialEdit.showError.deleteCredential.title'),
|
||||||
'There was a problem deleting the credentials:',
|
this.$baseText('credentialEdit.credentialEdit.showError.deleteCredential.message'),
|
||||||
);
|
);
|
||||||
this.isDeleting = false;
|
this.isDeleting = false;
|
||||||
|
|
||||||
|
@ -740,8 +743,11 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
this.updateNodesCredentialsIssues();
|
this.updateNodesCredentialsIssues();
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Credentials deleted',
|
title: this.$baseText('credentialEdit.credentialEdit.showMessage.title'),
|
||||||
message: `The credential "${savedCredentialName}" was deleted!`,
|
message: this.$baseText(
|
||||||
|
'credentialEdit.credentialEdit.showMessage.message',
|
||||||
|
{ interpolate: { savedCredentialName } },
|
||||||
|
),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
|
@ -778,8 +784,8 @@ export default mixins(showMessage, nodeHelpers).extend({
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(
|
this.$showError(
|
||||||
error,
|
error,
|
||||||
'OAuth Authorization Error',
|
this.$baseText('credentialEdit.credentialEdit.showError.generateAuthorizationUrl.title'),
|
||||||
'Error generating authorization URL:',
|
this.$baseText('credentialEdit.credentialEdit.showError.generateAuthorizationUrl.message'),
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div :class="$style.container">
|
<div :class="$style.container">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="8" :class="$style.accessLabel">
|
<el-col :span="8" :class="$style.accessLabel">
|
||||||
<n8n-text :compact="true" :bold="true">Allow use by</n8n-text>
|
<n8n-text :compact="true" :bold="true">{{ $baseText('credentialEdit.credentialInfo.allowUseBy') }}</n8n-text>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="16">
|
<el-col :span="16">
|
||||||
<div
|
<div
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row v-if="currentCredential">
|
<el-row v-if="currentCredential">
|
||||||
<el-col :span="8" :class="$style.label">
|
<el-col :span="8" :class="$style.label">
|
||||||
<n8n-text :compact="true" :bold="true">Created</n8n-text>
|
<n8n-text :compact="true" :bold="true">{{ $baseText('credentialEdit.credentialInfo.created') }}</n8n-text>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="16" :class="$style.valueLabel">
|
<el-col :span="16" :class="$style.valueLabel">
|
||||||
<n8n-text :compact="true"><TimeAgo :date="currentCredential.createdAt" :capitalize="true" /></n8n-text>
|
<n8n-text :compact="true"><TimeAgo :date="currentCredential.createdAt" :capitalize="true" /></n8n-text>
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row v-if="currentCredential">
|
<el-row v-if="currentCredential">
|
||||||
<el-col :span="8" :class="$style.label">
|
<el-col :span="8" :class="$style.label">
|
||||||
<n8n-text :compact="true" :bold="true">Last modified</n8n-text>
|
<n8n-text :compact="true" :bold="true">{{ $baseText('credentialEdit.credentialInfo.lastModified') }}</n8n-text>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="16" :class="$style.valueLabel">
|
<el-col :span="16" :class="$style.valueLabel">
|
||||||
<n8n-text :compact="true"><TimeAgo :date="currentCredential.updatedAt" :capitalize="true" /></n8n-text>
|
<n8n-text :compact="true"><TimeAgo :date="currentCredential.updatedAt" :capitalize="true" /></n8n-text>
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row v-if="currentCredential">
|
<el-row v-if="currentCredential">
|
||||||
<el-col :span="8" :class="$style.label">
|
<el-col :span="8" :class="$style.label">
|
||||||
<n8n-text :compact="true" :bold="true">ID</n8n-text>
|
<n8n-text :compact="true" :bold="true">{{ $baseText('credentialEdit.credentialInfo.id') }}</n8n-text>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="16" :class="$style.valueLabel">
|
<el-col :span="16" :class="$style.valueLabel">
|
||||||
<n8n-text :compact="true">{{currentCredential.id}}</n8n-text>
|
<n8n-text :compact="true">{{currentCredential.id}}</n8n-text>
|
||||||
|
@ -47,10 +47,12 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
import { renderText } from '../mixins/renderText';
|
||||||
|
|
||||||
import TimeAgo from '../TimeAgo.vue';
|
import TimeAgo from '../TimeAgo.vue';
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default mixins(renderText).extend({
|
||||||
name: 'CredentialInfo',
|
name: 'CredentialInfo',
|
||||||
props: ['nodesWithAccess', 'nodeAccess', 'currentCredential'],
|
props: ['nodesWithAccess', 'nodeAccess', 'currentCredential'],
|
||||||
components: {
|
components: {
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
v-if="isGoogleOAuthType"
|
v-if="isGoogleOAuthType"
|
||||||
:src="basePath + 'google-signin-light.png'"
|
:src="basePath + 'google-signin-light.png'"
|
||||||
:class="$style.googleIcon"
|
:class="$style.googleIcon"
|
||||||
alt="Sign in with Google"
|
:alt="$baseText('credentialEdit.oAuthButton.signInWithGoogle')"
|
||||||
@click.stop="$emit('click')"
|
@click.stop="$emit('click')"
|
||||||
/>
|
/>
|
||||||
<n8n-button
|
<n8n-button
|
||||||
v-else
|
v-else
|
||||||
label="Connect my account"
|
:label="$baseText('credentialEdit.oAuthButton.connectMyAccount')"
|
||||||
size="large"
|
size="large"
|
||||||
@click.stop="$emit('click')"
|
@click.stop="$emit('click')"
|
||||||
/>
|
/>
|
||||||
|
@ -18,8 +18,10 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
import { renderText } from '../mixins/renderText';
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default mixins(renderText).extend({
|
||||||
props: {
|
props: {
|
||||||
isGoogleOAuthType: {
|
isGoogleOAuthType: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|
|
@ -2,32 +2,32 @@
|
||||||
<Modal
|
<Modal
|
||||||
:name="CREDENTIAL_LIST_MODAL_KEY"
|
:name="CREDENTIAL_LIST_MODAL_KEY"
|
||||||
width="80%"
|
width="80%"
|
||||||
title="Credentials"
|
:title="$baseText('credentialsList.credentials')"
|
||||||
>
|
>
|
||||||
<template v-slot:content>
|
<template v-slot:content>
|
||||||
<n8n-heading tag="h3" size="small" color="text-light">Your saved credentials:</n8n-heading>
|
<n8n-heading tag="h3" size="small" color="text-light">{{ $baseText('credentialsList.yourSavedCredentials') + ':' }}</n8n-heading>
|
||||||
<div class="new-credentials-button">
|
<div class="new-credentials-button">
|
||||||
<n8n-button
|
<n8n-button
|
||||||
title="Create New Credentials"
|
:title="$baseText('credentialsList.createNewCredential')"
|
||||||
icon="plus"
|
icon="plus"
|
||||||
label="Add New"
|
:label="$baseText('credentialsList.addNew')"
|
||||||
size="large"
|
size="large"
|
||||||
@click="createCredential()"
|
@click="createCredential()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table :data="credentialsToDisplay" :default-sort = "{prop: 'name', order: 'ascending'}" stripe max-height="450" @row-click="editCredential">
|
<el-table :data="credentialsToDisplay" :default-sort = "{prop: 'name', order: 'ascending'}" stripe max-height="450" @row-click="editCredential">
|
||||||
<el-table-column property="name" label="Name" class-name="clickable" sortable></el-table-column>
|
<el-table-column property="name" :label="$baseText('credentialsList.name')" class-name="clickable" sortable></el-table-column>
|
||||||
<el-table-column property="type" label="Type" class-name="clickable" sortable></el-table-column>
|
<el-table-column property="type" :label="$baseText('credentialsList.type')" class-name="clickable" sortable></el-table-column>
|
||||||
<el-table-column property="createdAt" label="Created" class-name="clickable" sortable></el-table-column>
|
<el-table-column property="createdAt" :label="$baseText('credentialsList.created')" class-name="clickable" sortable></el-table-column>
|
||||||
<el-table-column property="updatedAt" label="Updated" class-name="clickable" sortable></el-table-column>
|
<el-table-column property="updatedAt" :label="$baseText('credentialsList.updated')" class-name="clickable" sortable></el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="Operations"
|
:label="$baseText('credentialsList.operations')"
|
||||||
width="120">
|
width="120">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<div class="cred-operations">
|
<div class="cred-operations">
|
||||||
<n8n-icon-button title="Edit Credentials" @click.stop="editCredential(scope.row)" size="small" icon="pen" />
|
<n8n-icon-button :title="$baseText('credentialsList.editCredential')" @click.stop="editCredential(scope.row)" size="small" icon="pen" />
|
||||||
<n8n-icon-button title="Delete Credentials" @click.stop="deleteCredential(scope.row)" size="small" icon="trash" />
|
<n8n-icon-button :title="$baseText('credentialsList.deleteCredential')" @click.stop="deleteCredential(scope.row)" size="small" icon="trash" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
@ -103,7 +103,16 @@ export default mixins(
|
||||||
},
|
},
|
||||||
|
|
||||||
async deleteCredential (credential: ICredentialsResponse) {
|
async deleteCredential (credential: ICredentialsResponse) {
|
||||||
const deleteConfirmed = await this.confirmMessage(`Are you sure you want to delete "${credential.name}" credentials?`, 'Delete Credentials?', null, 'Yes, delete!');
|
const deleteConfirmed = await this.confirmMessage(
|
||||||
|
this.$baseText(
|
||||||
|
'credentialsList.confirmMessage.message',
|
||||||
|
{ interpolate: { credentialName: credential.name }},
|
||||||
|
),
|
||||||
|
this.$baseText('credentialsList.confirmMessage.headline'),
|
||||||
|
null,
|
||||||
|
this.$baseText('credentialsList.confirmMessage.confirmButtonText'),
|
||||||
|
this.$baseText('credentialsList.confirmMessage.cancelButtonText'),
|
||||||
|
);
|
||||||
|
|
||||||
if (deleteConfirmed === false) {
|
if (deleteConfirmed === false) {
|
||||||
return;
|
return;
|
||||||
|
@ -112,7 +121,11 @@ export default mixins(
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch('credentials/deleteCredential', {id: credential.id});
|
await this.$store.dispatch('credentials/deleteCredential', {id: credential.id});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(error, 'Problem deleting credentials', 'There was a problem deleting the credentials:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('credentialsList.showError.deleteCredential.title'),
|
||||||
|
this.$baseText('credentialsList.showError.deleteCredential.message'),
|
||||||
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -121,8 +134,11 @@ export default mixins(
|
||||||
this.updateNodesCredentialsIssues();
|
this.updateNodesCredentialsIssues();
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Credentials deleted',
|
title: this.$baseText('credentialsList.showMessage.title'),
|
||||||
message: `The credential "${credential.name}" was deleted!`,
|
message: this.$baseText(
|
||||||
|
'credentialsList.showMessage.message',
|
||||||
|
{ interpolate: { credentialName: credential.name }},
|
||||||
|
),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,15 +7,15 @@
|
||||||
maxWidth="460px"
|
maxWidth="460px"
|
||||||
>
|
>
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<h2 :class="$style.title">Add new credential</h2>
|
<h2 :class="$style.title">{{ $baseText('credentialSelectModal.addNewCredential') }}</h2>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<div>
|
<div>
|
||||||
<div :class="$style.subtitle">Select an app or service to connect to</div>
|
<div :class="$style.subtitle">{{ $baseText('credentialSelectModal.selectAnAppOrServiceToConnectTo') }}</div>
|
||||||
<n8n-select
|
<n8n-select
|
||||||
filterable
|
filterable
|
||||||
defaultFirstOption
|
defaultFirstOption
|
||||||
placeholder="Search for app..."
|
:placeholder="$baseText('credentialSelectModal.searchForApp')"
|
||||||
size="xlarge"
|
size="xlarge"
|
||||||
ref="select"
|
ref="select"
|
||||||
:value="selected"
|
:value="selected"
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<div :class="$style.footer">
|
<div :class="$style.footer">
|
||||||
<n8n-button
|
<n8n-button
|
||||||
label="Continue"
|
:label="$baseText('credentialSelectModal.continue')"
|
||||||
float="right"
|
float="right"
|
||||||
size="large"
|
size="large"
|
||||||
:disabled="!selected"
|
:disabled="!selected"
|
||||||
|
@ -52,8 +52,10 @@ import { mapGetters } from "vuex";
|
||||||
|
|
||||||
import Modal from './Modal.vue';
|
import Modal from './Modal.vue';
|
||||||
import { CREDENTIAL_SELECT_MODAL_KEY } from '../constants';
|
import { CREDENTIAL_SELECT_MODAL_KEY } from '../constants';
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
|
import { renderText } from './mixins/renderText';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default mixins(renderText).extend({
|
||||||
name: 'CredentialsSelectModal',
|
name: 'CredentialsSelectModal',
|
||||||
components: {
|
components: {
|
||||||
Modal,
|
Modal,
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div v-if="nodeType && showDocumentHelp" class="doc-help-wrapper">
|
<div v-if="nodeType && showDocumentHelp" class="doc-help-wrapper">
|
||||||
<svg id="help-logo" :href="documentationUrl" target="_blank" width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<svg id="help-logo" :href="documentationUrl" target="_blank" width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<title>Node Documentation</title>
|
<title>{{ $baseText('dataDisplay.nodeDocumentation') }}</title>
|
||||||
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
<g transform="translate(-1127.000000, -836.000000)" fill-rule="nonzero">
|
<g transform="translate(-1127.000000, -836.000000)" fill-rule="nonzero">
|
||||||
<g transform="translate(1117.000000, 825.000000)">
|
<g transform="translate(1117.000000, 825.000000)">
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<div class="text">
|
<div class="text">
|
||||||
Need help? <a id="doc-hyperlink" :href="documentationUrl" target="_blank" @click="onDocumentationUrlClick">Open {{nodeType.displayName}} documentation</a>
|
{{ $baseText('dataDisplay.needHelp') }} <a id="doc-hyperlink" :href="documentationUrl" target="_blank" @click="onDocumentationUrlClick">{{ $baseText('dataDisplay.openDocumentationFor', { interpolate: { nodeTypeDisplayName: nodeType.displayName } }) }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
|
@ -49,6 +49,7 @@ import {
|
||||||
|
|
||||||
import { externalHooks } from '@/components/mixins/externalHooks';
|
import { externalHooks } from '@/components/mixins/externalHooks';
|
||||||
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
||||||
|
import { renderText } from '@/components/mixins/renderText';
|
||||||
import { workflowHelpers } from '@/components/mixins/workflowHelpers';
|
import { workflowHelpers } from '@/components/mixins/workflowHelpers';
|
||||||
|
|
||||||
import NodeSettings from '@/components/NodeSettings.vue';
|
import NodeSettings from '@/components/NodeSettings.vue';
|
||||||
|
@ -56,7 +57,7 @@ import RunData from '@/components/RunData.vue';
|
||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
export default mixins(externalHooks, nodeHelpers, workflowHelpers).extend({
|
export default mixins(externalHooks, nodeHelpers, renderText, workflowHelpers).extend({
|
||||||
name: 'DataDisplay',
|
name: 'DataDisplay',
|
||||||
components: {
|
components: {
|
||||||
NodeSettings,
|
NodeSettings,
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<template >
|
<template >
|
||||||
<span class="static-text-wrapper">
|
<span class="static-text-wrapper">
|
||||||
<span v-show="!editActive" title="Click to change">
|
<span v-show="!editActive" :title="$baseText('displayWithChange.clickToChange')">
|
||||||
<span class="static-text" @mousedown="startEdit">{{currentValue}}</span>
|
<span class="static-text" @mousedown="startEdit">{{currentValue}}</span>
|
||||||
</span>
|
</span>
|
||||||
<span v-show="editActive">
|
<span v-show="editActive">
|
||||||
<input class="edit-field" ref="inputField" type="text" v-model="newValue" @keydown.enter.stop.prevent="setValue" @keydown.escape.stop.prevent="cancelEdit" @keydown.stop="noOp" @blur="cancelEdit" />
|
<input class="edit-field" ref="inputField" type="text" v-model="newValue" @keydown.enter.stop.prevent="setValue" @keydown.escape.stop.prevent="cancelEdit" @keydown.stop="noOp" @blur="cancelEdit" />
|
||||||
<font-awesome-icon icon="times" @mousedown="cancelEdit" class="icons clickable" title="Cancel Edit" />
|
<font-awesome-icon icon="times" @mousedown="cancelEdit" class="icons clickable" :title="$baseText('displayWithChange.cancelEdit')" />
|
||||||
<font-awesome-icon icon="check" @mousedown="setValue" class="icons clickable" title="Set Value" />
|
<font-awesome-icon icon="check" @mousedown="setValue" class="icons clickable" :title="$baseText('displayWithChange.setValue')" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
:name="modalName"
|
:name="modalName"
|
||||||
:eventBus="modalBus"
|
:eventBus="modalBus"
|
||||||
@enter="save"
|
@enter="save"
|
||||||
title="Duplicate Workflow"
|
:title="$baseText('duplicateWorkflowDialog.duplicateWorkflow')"
|
||||||
:center="true"
|
:center="true"
|
||||||
minWidth="420px"
|
minWidth="420px"
|
||||||
maxWidth="420px"
|
maxWidth="420px"
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
<n8n-input
|
<n8n-input
|
||||||
v-model="name"
|
v-model="name"
|
||||||
ref="nameInput"
|
ref="nameInput"
|
||||||
placeholder="Enter workflow name"
|
:placeholder="$baseText('duplicateWorkflowDialog.enterWorkflowName')"
|
||||||
:maxlength="MAX_WORKFLOW_NAME_LENGTH"
|
:maxlength="MAX_WORKFLOW_NAME_LENGTH"
|
||||||
/>
|
/>
|
||||||
<TagsDropdown
|
<TagsDropdown
|
||||||
|
@ -23,15 +23,15 @@
|
||||||
@blur="onTagsBlur"
|
@blur="onTagsBlur"
|
||||||
@esc="onTagsEsc"
|
@esc="onTagsEsc"
|
||||||
@update="onTagsUpdate"
|
@update="onTagsUpdate"
|
||||||
placeholder="Choose or create a tag"
|
:placeholder="$baseText('duplicateWorkflowDialog.chooseOrCreateATag')"
|
||||||
ref="dropdown"
|
ref="dropdown"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:footer="{ close }">
|
<template v-slot:footer="{ close }">
|
||||||
<div :class="$style.footer">
|
<div :class="$style.footer">
|
||||||
<n8n-button @click="save" :loading="isSaving" label="Save" float="right" />
|
<n8n-button @click="save" :loading="isSaving" :label="$baseText('duplicateWorkflowDialog.save')" float="right" />
|
||||||
<n8n-button type="outline" @click="close" :disabled="isSaving" label="Cancel" float="right" />
|
<n8n-button type="outline" @click="close" :disabled="isSaving" :label="$baseText('duplicateWorkflowDialog.cancel')" float="right" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -44,10 +44,11 @@ import mixins from "vue-typed-mixins";
|
||||||
import { MAX_WORKFLOW_NAME_LENGTH } from "@/constants";
|
import { MAX_WORKFLOW_NAME_LENGTH } from "@/constants";
|
||||||
import { workflowHelpers } from "@/components/mixins/workflowHelpers";
|
import { workflowHelpers } from "@/components/mixins/workflowHelpers";
|
||||||
import { showMessage } from "@/components/mixins/showMessage";
|
import { showMessage } from "@/components/mixins/showMessage";
|
||||||
|
import { renderText } from "@/components/mixins/renderText";
|
||||||
import TagsDropdown from "@/components/TagsDropdown.vue";
|
import TagsDropdown from "@/components/TagsDropdown.vue";
|
||||||
import Modal from "./Modal.vue";
|
import Modal from "./Modal.vue";
|
||||||
|
|
||||||
export default mixins(showMessage, workflowHelpers).extend({
|
export default mixins(showMessage, renderText, workflowHelpers).extend({
|
||||||
components: { TagsDropdown, Modal },
|
components: { TagsDropdown, Modal },
|
||||||
name: "DuplicateWorkflow",
|
name: "DuplicateWorkflow",
|
||||||
props: ["modalName", "isActive"],
|
props: ["modalName", "isActive"],
|
||||||
|
@ -101,8 +102,8 @@ export default mixins(showMessage, workflowHelpers).extend({
|
||||||
const name = this.name.trim();
|
const name = this.name.trim();
|
||||||
if (!name) {
|
if (!name) {
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: "Name missing",
|
title: this.$baseText('duplicateWorkflowDialog.showMessage.title'),
|
||||||
message: `Please enter a name.`,
|
message: this.$baseText('duplicateWorkflowDialog.showMessage.message'),
|
||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="error-header">
|
<div class="error-header">
|
||||||
<div class="error-message">ERROR: {{error.message}}</div>
|
<div class="error-message">{{ $baseText('nodeErrorView.error') + ':' + error.message }}</div>
|
||||||
<div class="error-description" v-if="error.description">{{error.description}}</div>
|
<div class="error-description" v-if="error.description">{{error.description}}</div>
|
||||||
</div>
|
</div>
|
||||||
<details>
|
<details>
|
||||||
<summary class="error-details__summary">
|
<summary class="error-details__summary">
|
||||||
<font-awesome-icon class="error-details__icon" icon="angle-right" /> Details
|
<font-awesome-icon class="error-details__icon" icon="angle-right" /> {{ $baseText('nodeErrorView.details') }}
|
||||||
</summary>
|
</summary>
|
||||||
<div class="error-details__content">
|
<div class="error-details__content">
|
||||||
<div v-if="error.timestamp">
|
<div v-if="error.timestamp">
|
||||||
<el-card class="box-card" shadow="never">
|
<el-card class="box-card" shadow="never">
|
||||||
<div slot="header" class="clearfix box-card__title">
|
<div slot="header" class="clearfix box-card__title">
|
||||||
<span>Time</span>
|
<span>{{ $baseText('nodeErrorView.time') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{{new Date(error.timestamp).toLocaleString()}}
|
{{new Date(error.timestamp).toLocaleString()}}
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
<div v-if="error.httpCode">
|
<div v-if="error.httpCode">
|
||||||
<el-card class="box-card" shadow="never">
|
<el-card class="box-card" shadow="never">
|
||||||
<div slot="header" class="clearfix box-card__title">
|
<div slot="header" class="clearfix box-card__title">
|
||||||
<span>HTTP-Code</span>
|
<span>{{ $baseText('nodeErrorView.httpCode') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{{error.httpCode}}
|
{{error.httpCode}}
|
||||||
|
@ -32,13 +32,13 @@
|
||||||
<div v-if="error.cause">
|
<div v-if="error.cause">
|
||||||
<el-card class="box-card" shadow="never">
|
<el-card class="box-card" shadow="never">
|
||||||
<div slot="header" class="clearfix box-card__title">
|
<div slot="header" class="clearfix box-card__title">
|
||||||
<span>Cause</span>
|
<span>{{ $baseText('nodeErrorView.cause') }}</span>
|
||||||
<br>
|
<br>
|
||||||
<span class="box-card__subtitle">Data below may contain sensitive information. Proceed with caution when sharing.</span>
|
<span class="box-card__subtitle">{{ $baseText('nodeErrorView.dataBelowMayContain') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="copy-button" v-if="displayCause">
|
<div class="copy-button" v-if="displayCause">
|
||||||
<n8n-icon-button @click="copyCause" title="Copy to Clipboard" icon="copy" />
|
<n8n-icon-button @click="copyCause" :title="$baseText('nodeErrorView.copyToClipboard')" icon="copy" />
|
||||||
</div>
|
</div>
|
||||||
<vue-json-pretty
|
<vue-json-pretty
|
||||||
v-if="displayCause"
|
v-if="displayCause"
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
class="json-data"
|
class="json-data"
|
||||||
/>
|
/>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
<font-awesome-icon icon="info-circle" /> The error cause is too large to be displayed.
|
<font-awesome-icon icon="info-circle" />{{ $baseText('nodeErrorView.theErrorCauseIsTooLargeToBeDisplayed') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
<div v-if="error.stack">
|
<div v-if="error.stack">
|
||||||
<el-card class="box-card" shadow="never">
|
<el-card class="box-card" shadow="never">
|
||||||
<div slot="header" class="clearfix box-card__title">
|
<div slot="header" class="clearfix box-card__title">
|
||||||
<span>Stack</span>
|
<span>{{ $baseText('nodeErrorView.stack') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<pre><code>{{error.stack}}</code></pre>
|
<pre><code>{{error.stack}}</code></pre>
|
||||||
|
@ -79,10 +79,12 @@ import mixins from 'vue-typed-mixins';
|
||||||
import {
|
import {
|
||||||
MAX_DISPLAY_DATA_SIZE,
|
MAX_DISPLAY_DATA_SIZE,
|
||||||
} from '@/constants';
|
} from '@/constants';
|
||||||
|
import { renderText } from '../mixins/renderText';
|
||||||
|
|
||||||
export default mixins(
|
export default mixins(
|
||||||
copyPaste,
|
copyPaste,
|
||||||
showMessage,
|
showMessage,
|
||||||
|
renderText,
|
||||||
).extend({
|
).extend({
|
||||||
name: 'NodeErrorView',
|
name: 'NodeErrorView',
|
||||||
props: [
|
props: [
|
||||||
|
@ -103,8 +105,8 @@ export default mixins(
|
||||||
},
|
},
|
||||||
copySuccess() {
|
copySuccess() {
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Copied to clipboard',
|
title: this.$baseText('nodeErrorView.showMessage.title'),
|
||||||
message: '',
|
message: this.$baseText('nodeErrorView.showMessage.message'),
|
||||||
type: 'info',
|
type: 'info',
|
||||||
});
|
});
|
||||||
},
|
},
|
|
@ -1,13 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<span>
|
<span>
|
||||||
<el-dialog :visible="dialogVisible" append-to-body width="80%" :title="`Workflow Executions ${combinedExecutions.length}/${finishedExecutionsCountEstimated === true ? '~' : ''}${combinedExecutionsCount}`" :before-close="closeDialog">
|
<el-dialog :visible="dialogVisible" append-to-body width="80%" :title="`${$baseText('executionsList.workflowExecutions')} ${combinedExecutions.length}/${finishedExecutionsCountEstimated === true ? '~' : ''}${combinedExecutionsCount}`" :before-close="closeDialog">
|
||||||
<div class="filters">
|
<div class="filters">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="2" class="filter-headline">
|
<el-col :span="2" class="filter-headline">
|
||||||
Filters:
|
{{ $baseText('executionsList.filters') }}:
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="7">
|
<el-col :span="7">
|
||||||
<n8n-select v-model="filter.workflowId" placeholder="Select Workflow" size="medium" filterable @change="handleFilterChanged">
|
<n8n-select v-model="filter.workflowId" :placeholder="$baseText('executionsList.selectWorkflow')" size="medium" filterable @change="handleFilterChanged">
|
||||||
<n8n-option
|
<n8n-option
|
||||||
v-for="item in workflows"
|
v-for="item in workflows"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
</n8n-select>
|
</n8n-select>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="5" :offset="1">
|
<el-col :span="5" :offset="1">
|
||||||
<n8n-select v-model="filter.status" placeholder="Select Status" size="medium" filterable @change="handleFilterChanged">
|
<n8n-select v-model="filter.status" :placeholder="$baseText('executionsList.selectStatus')" size="medium" filterable @change="handleFilterChanged">
|
||||||
<n8n-option
|
<n8n-option
|
||||||
v-for="item in statuses"
|
v-for="item in statuses"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
|
@ -27,15 +27,15 @@
|
||||||
</n8n-select>
|
</n8n-select>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="4" :offset="5" class="autorefresh">
|
<el-col :span="4" :offset="5" class="autorefresh">
|
||||||
<el-checkbox v-model="autoRefresh" @change="handleAutoRefreshToggle">Auto refresh</el-checkbox>
|
<el-checkbox v-model="autoRefresh" @change="handleAutoRefreshToggle">{{ $baseText('executionsList.autoRefresh') }}</el-checkbox>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="selection-options">
|
<div class="selection-options">
|
||||||
<span v-if="checkAll === true || isIndeterminate === true">
|
<span v-if="checkAll === true || isIndeterminate === true">
|
||||||
Selected: {{numSelected}} / <span v-if="finishedExecutionsCountEstimated === true">~</span>{{finishedExecutionsCount}}
|
{{ $baseText('executionsList.selected') }}: {{numSelected}} / <span v-if="finishedExecutionsCountEstimated === true">~</span>{{finishedExecutionsCount}}
|
||||||
<n8n-icon-button title="Delete Selected" icon="trash" size="mini" @click="handleDeleteSelected" />
|
<n8n-icon-button :title="$baseText('executionsList.deleteSelected')" icon="trash" size="mini" @click="handleDeleteSelected" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -49,49 +49,45 @@
|
||||||
<el-checkbox v-if="scope.row.stoppedAt !== undefined && scope.row.id" :value="selectedItems[scope.row.id.toString()] || checkAll" @change="handleCheckboxChanged(scope.row.id)" label=" "></el-checkbox>
|
<el-checkbox v-if="scope.row.stoppedAt !== undefined && scope.row.id" :value="selectedItems[scope.row.id.toString()] || checkAll" @change="handleCheckboxChanged(scope.row.id)" label=" "></el-checkbox>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column property="startedAt" label="Started At / ID" width="205">
|
<el-table-column property="startedAt" :label="$baseText('executionsList.startedAtId')" width="205">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
{{convertToDisplayDate(scope.row.startedAt)}}<br />
|
{{convertToDisplayDate(scope.row.startedAt)}}<br />
|
||||||
<small v-if="scope.row.id">ID: {{scope.row.id}}</small>
|
<small v-if="scope.row.id">ID: {{scope.row.id}}</small>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column property="workflowName" label="Name">
|
<el-table-column property="workflowName" :label="$baseText('executionsList.name')">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span class="workflow-name">
|
<span class="workflow-name">
|
||||||
{{scope.row.workflowName || '[UNSAVED WORKFLOW]'}}
|
{{ scope.row.workflowName || $baseText('executionsList.unsavedWorkflow') }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span v-if="scope.row.stoppedAt === undefined">
|
<span v-if="scope.row.stoppedAt === undefined">
|
||||||
(running)
|
({{ $baseText('executionsList.running') }})
|
||||||
</span>
|
</span>
|
||||||
<span v-if="scope.row.retryOf !== undefined">
|
<span v-if="scope.row.retryOf !== undefined">
|
||||||
<br /><small>Retry of "{{scope.row.retryOf}}"</small>
|
<br /><small>{{ $baseText('executionsList.retryOf') }} "{{scope.row.retryOf}}"</small>
|
||||||
</span>
|
</span>
|
||||||
<span v-else-if="scope.row.retrySuccessId !== undefined">
|
<span v-else-if="scope.row.retrySuccessId !== undefined">
|
||||||
<br /><small>Success retry "{{scope.row.retrySuccessId}}"</small>
|
<br /><small>{{ $baseText('executionsList.successRetry') }} "{{scope.row.retrySuccessId}}"</small>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="Status" width="122" align="center">
|
<el-table-column :label="$baseText('executionsList.status')" width="122" align="center">
|
||||||
<template slot-scope="scope" align="center">
|
<template slot-scope="scope" align="center">
|
||||||
|
|
||||||
<n8n-tooltip placement="top" >
|
<n8n-tooltip placement="top" >
|
||||||
<div slot="content" v-html="statusTooltipText(scope.row)"></div>
|
<div slot="content" v-html="statusTooltipText(scope.row)"></div>
|
||||||
|
<span class="status-badge running" v-if="scope.row.stoppedAt === undefined">
|
||||||
<span class="status-badge running" v-if="scope.row.waitTill">
|
{{ $baseText('executionsList.running') }}
|
||||||
Waiting
|
|
||||||
</span>
|
|
||||||
<span class="status-badge running" v-else-if="scope.row.stoppedAt === undefined">
|
|
||||||
Running
|
|
||||||
</span>
|
</span>
|
||||||
<span class="status-badge success" v-else-if="scope.row.finished">
|
<span class="status-badge success" v-else-if="scope.row.finished">
|
||||||
Success
|
{{ $baseText('executionsList.success') }}
|
||||||
</span>
|
</span>
|
||||||
<span class="status-badge error" v-else-if="scope.row.stoppedAt !== null">
|
<span class="status-badge error" v-else-if="scope.row.stoppedAt !== null">
|
||||||
Error
|
{{ $baseText('executionsList.error') }}
|
||||||
</span>
|
</span>
|
||||||
<span class="status-badge warning" v-else>
|
<span class="status-badge warning" v-else>
|
||||||
Unknown
|
{{ $baseText('executionsList.unknown') }}
|
||||||
</span>
|
</span>
|
||||||
</n8n-tooltip>
|
</n8n-tooltip>
|
||||||
|
|
||||||
|
@ -102,20 +98,24 @@
|
||||||
type="light"
|
type="light"
|
||||||
:theme="scope.row.stoppedAt === null ? 'warning': 'danger'"
|
:theme="scope.row.stoppedAt === null ? 'warning': 'danger'"
|
||||||
size="mini"
|
size="mini"
|
||||||
title="Retry execution"
|
:title="$baseText('executionsList.retryExecution')"
|
||||||
icon="redo"
|
icon="redo"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item :command="{command: 'currentlySaved', row: scope.row}">Retry with currently saved workflow</el-dropdown-item>
|
<el-dropdown-item :command="{command: 'currentlySaved', row: scope.row}">
|
||||||
<el-dropdown-item :command="{command: 'original', row: scope.row}">Retry with original workflow</el-dropdown-item>
|
{{ $baseText('executionsList.retryWithCurrentlySavedWorkflow') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item :command="{command: 'original', row: scope.row}">
|
||||||
|
{{ $baseText('executionsList.retryWithOriginalworkflow') }}
|
||||||
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column property="mode" label="Mode" width="100" align="center"></el-table-column>
|
<el-table-column property="mode" :label="$baseText('executionsList.mode')" width="100" align="center"></el-table-column>
|
||||||
<el-table-column label="Running Time" width="150" align="center">
|
<el-table-column :label="$baseText('executionsList.runningTime')" width="150" align="center">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span v-if="scope.row.stoppedAt === undefined">
|
<span v-if="scope.row.stoppedAt === undefined">
|
||||||
<font-awesome-icon icon="spinner" spin />
|
<font-awesome-icon icon="spinner" spin />
|
||||||
|
@ -134,10 +134,10 @@
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<div class="actions-container">
|
<div class="actions-container">
|
||||||
<span v-if="scope.row.stoppedAt === undefined || scope.row.waitTill">
|
<span v-if="scope.row.stoppedAt === undefined || scope.row.waitTill">
|
||||||
<n8n-icon-button icon="stop" size="small" title="Stop Execution" @click.stop="stopExecution(scope.row.id)" :loading="stoppingExecutions.includes(scope.row.id)" />
|
<n8n-icon-button icon="stop" size="small" :title="$baseText('executionsList.stopExecution')" @click.stop="stopExecution(scope.row.id)" :loading="stoppingExecutions.includes(scope.row.id)" />
|
||||||
</span>
|
</span>
|
||||||
<span v-if="scope.row.stoppedAt !== undefined && scope.row.id" >
|
<span v-if="scope.row.stoppedAt !== undefined && scope.row.id" >
|
||||||
<n8n-icon-button icon="folder-open" size="small" title="Open Past Execution" @click.stop="displayExecution(scope.row)" />
|
<n8n-icon-button icon="folder-open" size="small" :title="$baseText('executionsList.openPastExecution')" @click.stop="displayExecution(scope.row)" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -145,7 +145,7 @@
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<div class="load-more" v-if="finishedExecutionsCount > finishedExecutions.length || finishedExecutionsCountEstimated === true">
|
<div class="load-more" v-if="finishedExecutionsCount > finishedExecutions.length || finishedExecutionsCountEstimated === true">
|
||||||
<n8n-button icon="sync" title="Load More" label="Load More" @click="loadMore()" :loading="isDataLoading" />
|
<n8n-button icon="sync" :title="$baseText('executionsList.loadMore')" :label="$baseText('executionsList.loadMore')" @click="loadMore()" :loading="isDataLoading" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
@ -224,32 +224,41 @@ export default mixins(
|
||||||
|
|
||||||
stoppingExecutions: [] as string[],
|
stoppingExecutions: [] as string[],
|
||||||
workflows: [] as IWorkflowShortResponse[],
|
workflows: [] as IWorkflowShortResponse[],
|
||||||
statuses: [
|
|
||||||
{
|
|
||||||
id: 'ALL',
|
|
||||||
name: 'Any Status',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'error',
|
|
||||||
name: 'Error',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'running',
|
|
||||||
name: 'Running',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'success',
|
|
||||||
name: 'Success',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'waiting',
|
|
||||||
name: 'Waiting',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
statuses () {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 'ALL',
|
||||||
|
name: this.$baseText('executionsList.anyStatus'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'error',
|
||||||
|
name: this.$baseText('executionsList.error'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'running',
|
||||||
|
name: this.$baseText('executionsList.running'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'success',
|
||||||
|
name: this.$baseText('executionsList.success'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'waiting',
|
||||||
|
name: this.$baseText('executionsList.waiting'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
modes () {
|
||||||
|
return {
|
||||||
|
error: this.$baseText('executionsList.modes.error'),
|
||||||
|
retry: this.$baseText('executionsList.modes.retry'),
|
||||||
|
manual: this.$baseText('executionsList.modes.manual'),
|
||||||
|
trigger: this.$baseText('executionsList.modes.trigger'),
|
||||||
|
};
|
||||||
|
},
|
||||||
activeExecutions (): IExecutionsCurrentSummaryExtended[] {
|
activeExecutions (): IExecutionsCurrentSummaryExtended[] {
|
||||||
return this.$store.getters.getActiveExecutions;
|
return this.$store.getters.getActiveExecutions;
|
||||||
},
|
},
|
||||||
|
@ -356,7 +365,16 @@ export default mixins(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async handleDeleteSelected () {
|
async handleDeleteSelected () {
|
||||||
const deleteExecutions = await this.confirmMessage(`Are you sure that you want to delete the ${this.numSelected} selected executions?`, 'Delete Executions?', 'warning', 'Yes, delete!');
|
const deleteExecutions = await this.confirmMessage(
|
||||||
|
this.$baseText(
|
||||||
|
'executionsList.confirmMessage.message',
|
||||||
|
{ interpolate: { numSelected: this.numSelected.toString() }},
|
||||||
|
),
|
||||||
|
this.$baseText('executionsList.confirmMessage.headline'),
|
||||||
|
'warning',
|
||||||
|
this.$baseText('executionsList.confirmMessage.confirmButtonText'),
|
||||||
|
this.$baseText('executionsList.confirmMessage.cancelButtonText'),
|
||||||
|
);
|
||||||
|
|
||||||
if (deleteExecutions === false) {
|
if (deleteExecutions === false) {
|
||||||
return;
|
return;
|
||||||
|
@ -377,15 +395,19 @@ export default mixins(
|
||||||
await this.restApi().deleteExecutions(sendData);
|
await this.restApi().deleteExecutions(sendData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.isDataLoading = false;
|
this.isDataLoading = false;
|
||||||
this.$showError(error, 'Problem deleting executions', 'There was a problem deleting the executions:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('executionsList.showError.handleDeleteSelected.title'),
|
||||||
|
this.$baseText('executionsList.showError.handleDeleteSelected.message'),
|
||||||
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.isDataLoading = false;
|
this.isDataLoading = false;
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Execution deleted',
|
title: this.$baseText('executionsList.showMessage.handleDeleteSelected.title'),
|
||||||
message: 'The executions were deleted!',
|
message: this.$baseText('executionsList.showMessage.handleDeleteSelected.message'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -512,7 +534,11 @@ export default mixins(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const data = await this.restApi().getPastExecutions(this.workflowFilterPast, this.requestItemsPerRequest);
|
const data = await this.restApi().getPastExecutions(this.workflowFilterPast, this.requestItemsPerRequest);
|
||||||
this.finishedExecutions = data.results;
|
|
||||||
|
this.finishedExecutions = data.results.map((execution) => {
|
||||||
|
// @ts-ignore
|
||||||
|
return { ...execution, mode: this.modes[execution.mode] };
|
||||||
|
});
|
||||||
this.finishedExecutionsCount = data.count;
|
this.finishedExecutionsCount = data.count;
|
||||||
this.finishedExecutionsCountEstimated = data.estimated;
|
this.finishedExecutionsCountEstimated = data.estimated;
|
||||||
},
|
},
|
||||||
|
@ -536,10 +562,19 @@ export default mixins(
|
||||||
data = await this.restApi().getPastExecutions(filter, this.requestItemsPerRequest, lastId);
|
data = await this.restApi().getPastExecutions(filter, this.requestItemsPerRequest, lastId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.isDataLoading = false;
|
this.isDataLoading = false;
|
||||||
this.$showError(error, 'Problem loading workflows', 'There was a problem loading the workflows:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('executionsList.showError.loadMore.title'),
|
||||||
|
this.$baseText('executionsList.showError.loadMore.message') + ':',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data.results = data.results.map((execution) => {
|
||||||
|
// @ts-ignore
|
||||||
|
return { ...execution, mode: this.modes[execution.mode] };
|
||||||
|
});
|
||||||
|
|
||||||
this.finishedExecutions.push.apply(this.finishedExecutions, data.results);
|
this.finishedExecutions.push.apply(this.finishedExecutions, data.results);
|
||||||
this.finishedExecutionsCount = data.count;
|
this.finishedExecutionsCount = data.count;
|
||||||
this.finishedExecutionsCountEstimated = data.estimated;
|
this.finishedExecutionsCountEstimated = data.estimated;
|
||||||
|
@ -562,12 +597,16 @@ export default mixins(
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
workflows.unshift({
|
workflows.unshift({
|
||||||
id: 'ALL',
|
id: 'ALL',
|
||||||
name: 'All Workflows',
|
name: this.$baseText('executionsList.allWorkflows'),
|
||||||
});
|
});
|
||||||
|
|
||||||
Vue.set(this, 'workflows', workflows);
|
Vue.set(this, 'workflows', workflows);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(error, 'Problem loading workflows', 'There was a problem loading the workflows:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('executionsList.showError.loadWorkflows.title'),
|
||||||
|
this.$baseText('executionsList.showError.loadWorkflows.message') + ':',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async openDialog () {
|
async openDialog () {
|
||||||
|
@ -590,21 +629,25 @@ export default mixins(
|
||||||
|
|
||||||
if (retrySuccessful === true) {
|
if (retrySuccessful === true) {
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Retry successful',
|
title: this.$baseText('executionsList.showMessage.retrySuccessfulTrue.title'),
|
||||||
message: 'The retry was successful!',
|
message: this.$baseText('executionsList.showMessage.retrySuccessfulTrue.message'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Retry unsuccessful',
|
title: this.$baseText('executionsList.showMessage.retrySuccessfulFalse.title'),
|
||||||
message: 'The retry was not successful!',
|
message: this.$baseText('executionsList.showMessage.retrySuccessfulFalse.message'),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isDataLoading = false;
|
this.isDataLoading = false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(error, 'Problem with retry', 'There was a problem with the retry:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('executionsList.showError.retryExecution.title'),
|
||||||
|
this.$baseText('executionsList.showError.retryExecution.message') + ':',
|
||||||
|
);
|
||||||
|
|
||||||
this.isDataLoading = false;
|
this.isDataLoading = false;
|
||||||
}
|
}
|
||||||
|
@ -617,7 +660,11 @@ export default mixins(
|
||||||
const finishedExecutionsPromise = this.loadFinishedExecutions();
|
const finishedExecutionsPromise = this.loadFinishedExecutions();
|
||||||
await Promise.all([activeExecutionsPromise, finishedExecutionsPromise]);
|
await Promise.all([activeExecutionsPromise, finishedExecutionsPromise]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(error, 'Problem loading', 'There was a problem loading the data:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('executionsList.showError.refreshData.title'),
|
||||||
|
this.$baseText('executionsList.showError.refreshData.message') + ':',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isDataLoading = false;
|
this.isDataLoading = false;
|
||||||
|
@ -626,23 +673,41 @@ export default mixins(
|
||||||
if (entry.waitTill) {
|
if (entry.waitTill) {
|
||||||
const waitDate = new Date(entry.waitTill);
|
const waitDate = new Date(entry.waitTill);
|
||||||
if (waitDate.toISOString() === WAIT_TIME_UNLIMITED) {
|
if (waitDate.toISOString() === WAIT_TIME_UNLIMITED) {
|
||||||
return 'The workflow is waiting indefinitely for an incoming webhook call.';
|
return this.$baseText('executionsList.statusTooltipText.theWorkflowIsWaitingIndefinitely');
|
||||||
}
|
}
|
||||||
return `The worklow is waiting till ${waitDate.toLocaleDateString()} ${waitDate.toLocaleTimeString()}.`;
|
|
||||||
|
return this.$baseText(
|
||||||
|
'executionsList.statusTooltipText.theWorkflowIsWaitingTill',
|
||||||
|
{
|
||||||
|
interpolate: {
|
||||||
|
waitDateDate: waitDate.toLocaleDateString(),
|
||||||
|
waitDateTime: waitDate.toLocaleTimeString(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
} else if (entry.stoppedAt === undefined) {
|
} else if (entry.stoppedAt === undefined) {
|
||||||
return 'The worklow is currently executing.';
|
return this.$baseText('executionsList.statusTooltipText.theWorkflowIsCurrentlyExecuting');
|
||||||
} else if (entry.finished === true && entry.retryOf !== undefined) {
|
} else if (entry.finished === true && entry.retryOf !== undefined) {
|
||||||
return `The workflow execution was a retry of "${entry.retryOf}" and it was successful.`;
|
return this.$baseText(
|
||||||
|
'executionsList.statusTooltipText.theWorkflowExecutionWasARetryOfAndItWasSuccessful',
|
||||||
|
{ interpolate: { entryRetryOf: entry.retryOf }},
|
||||||
|
);
|
||||||
} else if (entry.finished === true) {
|
} else if (entry.finished === true) {
|
||||||
return 'The worklow execution was successful.';
|
return this.$baseText('executionsList.statusTooltipText.theWorkflowExecutionWasSuccessful');
|
||||||
} else if (entry.retryOf !== undefined) {
|
} else if (entry.retryOf !== undefined) {
|
||||||
return `The workflow execution was a retry of "${entry.retryOf}" and failed.<br />New retries have to be started from the original execution.`;
|
return this.$baseText(
|
||||||
|
'executionsList.statusTooltipText.theWorkflowExecutionWasARetryOfAndFailed',
|
||||||
|
{ interpolate: { entryRetryOf: entry.retryOf }},
|
||||||
|
);
|
||||||
} else if (entry.retrySuccessId !== undefined) {
|
} else if (entry.retrySuccessId !== undefined) {
|
||||||
return `The workflow execution failed but the retry "${entry.retrySuccessId}" was successful.`;
|
return this.$baseText(
|
||||||
|
'executionsList.statusTooltipText.theWorkflowExecutionFailedButTheRetryWasSuccessful',
|
||||||
|
{ interpolate: { entryRetrySuccessId: entry.retrySuccessId }},
|
||||||
|
);
|
||||||
} else if (entry.stoppedAt === null) {
|
} else if (entry.stoppedAt === null) {
|
||||||
return 'The workflow execution is probably still running but it may have crashed and n8n cannot safely tell. ';
|
return this.$baseText('executionsList.statusTooltipText.theWorkflowExecutionIsProbablyStillRunning');
|
||||||
} else {
|
} else {
|
||||||
return 'The workflow execution failed.';
|
return this.$baseText('executionsList.statusTooltipText.theWorkflowExecutionFailed');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async stopExecution (activeExecutionId: string) {
|
async stopExecution (activeExecutionId: string) {
|
||||||
|
@ -658,14 +723,21 @@ export default mixins(
|
||||||
this.stoppingExecutions.splice(index, 1);
|
this.stoppingExecutions.splice(index, 1);
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Execution stopped',
|
title: this.$baseText('executionsList.showMessage.stopExecution.title'),
|
||||||
message: `The execution with the id "${activeExecutionId}" got stopped!`,
|
message: this.$baseText(
|
||||||
|
'executionsList.showMessage.stopExecution.message',
|
||||||
|
{ interpolate: { activeExecutionId } },
|
||||||
|
),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
|
|
||||||
this.refreshData();
|
this.refreshData();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(error, 'Problem stopping execution', 'There was a problem stopping the execuction:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('executionsList.showError.stopExecution.title'),
|
||||||
|
this.$baseText('executionsList.showError.stopExecution.message'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="dialogVisible" @keydown.stop>
|
<div v-if="dialogVisible" @keydown.stop>
|
||||||
<el-dialog :visible="dialogVisible" custom-class="expression-dialog classic" append-to-body width="80%" title="Edit Expression" :before-close="closeDialog">
|
<el-dialog :visible="dialogVisible" custom-class="expression-dialog classic" append-to-body width="80%" :title="$baseText('expressionEdit.editExpression')" :before-close="closeDialog">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<div class="header-side-menu">
|
<div class="header-side-menu">
|
||||||
<div class="headline">
|
<div class="headline">
|
||||||
Edit Expression
|
{{ $baseText('expressionEdit.editExpression') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="sub-headline">
|
<div class="sub-headline">
|
||||||
Variable Selector
|
{{ $baseText('expressionEdit.variableSelector') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
<el-col :span="16" class="right-side">
|
<el-col :span="16" class="right-side">
|
||||||
<div class="expression-editor-wrapper">
|
<div class="expression-editor-wrapper">
|
||||||
<div class="editor-description">
|
<div class="editor-description">
|
||||||
Expression
|
{{ $baseText('expressionEdit.expression') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="expression-editor">
|
<div class="expression-editor">
|
||||||
<expression-input :parameter="parameter" ref="inputFieldExpression" rows="8" :value="value" :path="path" @change="valueChanged" @keydown.stop="noOp"></expression-input>
|
<expression-input :parameter="parameter" ref="inputFieldExpression" rows="8" :value="value" :path="path" @change="valueChanged" @keydown.stop="noOp"></expression-input>
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
<div class="expression-result-wrapper">
|
<div class="expression-result-wrapper">
|
||||||
<div class="editor-description">
|
<div class="editor-description">
|
||||||
Result
|
{{ $baseText('expressionEdit.result') }}
|
||||||
</div>
|
</div>
|
||||||
<expression-input :parameter="parameter" resolvedValue="true" ref="expressionResult" rows="8" :value="displayValue" :path="path"></expression-input>
|
<expression-input :parameter="parameter" resolvedValue="true" ref="expressionResult" rows="8" :value="displayValue" :path="path"></expression-input>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div @keydown.stop class="fixed-collection-parameter">
|
<div @keydown.stop class="fixed-collection-parameter">
|
||||||
<div v-if="getProperties.length === 0" class="no-items-exist">
|
<div v-if="getProperties.length === 0" class="no-items-exist">
|
||||||
<n8n-text size="small">Currently no items exist</n8n-text>
|
<n8n-text size="small">{{ $baseText('fixedCollectionParameter.currentlyNoItemsExist') }}</n8n-text>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-for="property in getProperties" :key="property.name" class="fixed-collection-parameter-property">
|
<div v-for="property in getProperties" :key="property.name" class="fixed-collection-parameter-property">
|
||||||
|
@ -85,7 +85,7 @@ export default mixins(genericHelpers)
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
getPlaceholderText (): string {
|
getPlaceholderText (): string {
|
||||||
return this.parameter.placeholder ? this.parameter.placeholder : 'Choose Option To Add';
|
return this.parameter.placeholder ? this.parameter.placeholder : this.$baseText('fixedCollectionParameter.choose');
|
||||||
},
|
},
|
||||||
getProperties (): INodePropertyCollection[] {
|
getProperties (): INodePropertyCollection[] {
|
||||||
const returnProperties = [];
|
const returnProperties = [];
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<span class="title">
|
<span class="title">
|
||||||
Execution Id:
|
{{ $baseText('executionDetails.executionId') + ':' }}
|
||||||
<span>
|
<span>
|
||||||
<strong>{{ executionId }}</strong
|
<strong>{{ executionId }}</strong
|
||||||
>
|
>
|
||||||
|
@ -9,23 +9,23 @@
|
||||||
icon="check"
|
icon="check"
|
||||||
class="execution-icon success"
|
class="execution-icon success"
|
||||||
v-if="executionFinished"
|
v-if="executionFinished"
|
||||||
title="Execution was successful"
|
:title="$baseText('executionDetails.executionWasSuccessful')"
|
||||||
/>
|
/>
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
icon="clock"
|
icon="clock"
|
||||||
class="execution-icon warning"
|
class="execution-icon warning"
|
||||||
v-else-if="executionWaiting"
|
v-else-if="executionWaiting"
|
||||||
title="Execution waiting"
|
:title="$baseText('executionDetails.executionWaiting')"
|
||||||
/>
|
/>
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
icon="times"
|
icon="times"
|
||||||
class="execution-icon error"
|
class="execution-icon error"
|
||||||
v-else
|
v-else
|
||||||
title="Execution failed"
|
:title="$baseText('executionDetails.executionFailed')"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
of
|
of
|
||||||
<span class="primary-color clickable" title="Open Workflow">
|
<span class="primary-color clickable" :title="$baseText('executionDetails.openWorkflow')">
|
||||||
<WorkflowNameShort :name="workflowName">
|
<WorkflowNameShort :name="workflowName">
|
||||||
<template v-slot="{ shortenedName }">
|
<template v-slot="{ shortenedName }">
|
||||||
<span @click="openWorkflow(workflowExecution.workflowId)">
|
<span @click="openWorkflow(workflowExecution.workflowId)">
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<n8n-tooltip class="primary-color" placement="bottom-end" >
|
<n8n-tooltip class="primary-color" placement="bottom-end" >
|
||||||
<div slot="content">
|
<div slot="content">
|
||||||
You're viewing the log of a previous execution. You cannot<br />
|
{{ $baseText('readOnly.youreViewingTheLogOf') }}
|
||||||
make changes since this execution already occured. Make changes<br />
|
|
||||||
to this workflow by clicking on its name on the left.
|
|
||||||
</div>
|
</div>
|
||||||
<span>
|
<span>
|
||||||
<font-awesome-icon icon="exclamation-triangle" />
|
<font-awesome-icon icon="exclamation-triangle" />
|
||||||
Read only
|
{{ $baseText('readOnly.readOnly') }}
|
||||||
</span>
|
</span>
|
||||||
</n8n-tooltip>
|
</n8n-tooltip>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
@blur="onTagsBlur"
|
@blur="onTagsBlur"
|
||||||
@update="onTagsUpdate"
|
@update="onTagsUpdate"
|
||||||
@esc="onTagsEditEsc"
|
@esc="onTagsEditEsc"
|
||||||
placeholder="Choose or create a tag"
|
:placeholder="$baseText('workflowDetails.chooseOrCreateATag')"
|
||||||
ref="dropdown"
|
ref="dropdown"
|
||||||
class="tags-edit"
|
class="tags-edit"
|
||||||
/>
|
/>
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
class="add-tag clickable"
|
class="add-tag clickable"
|
||||||
@click="onTagsEditEnable"
|
@click="onTagsEditEnable"
|
||||||
>
|
>
|
||||||
+ Add tag
|
+ {{ $baseText('workflowDetails.addTag') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<TagsContainer
|
<TagsContainer
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
<PushConnectionTracker class="actions">
|
<PushConnectionTracker class="actions">
|
||||||
<template>
|
<template>
|
||||||
<span class="activator">
|
<span class="activator">
|
||||||
<span>Active:</span>
|
<span>{{ $baseText('workflowDetails.active') + ':' }}</span>
|
||||||
<WorkflowActivator :workflow-active="isWorkflowActive" :workflow-id="currentWorkflowId" :disabled="!currentWorkflowId"/>
|
<WorkflowActivator :workflow-active="isWorkflowActive" :workflow-id="currentWorkflowId" :disabled="!currentWorkflowId"/>
|
||||||
</span>
|
</span>
|
||||||
<SaveButton
|
<SaveButton
|
||||||
|
@ -90,6 +90,7 @@ import SaveButton from "@/components/SaveButton.vue";
|
||||||
import TagsDropdown from "@/components/TagsDropdown.vue";
|
import TagsDropdown from "@/components/TagsDropdown.vue";
|
||||||
import InlineTextEdit from "@/components/InlineTextEdit.vue";
|
import InlineTextEdit from "@/components/InlineTextEdit.vue";
|
||||||
import BreakpointsObserver from "@/components/BreakpointsObserver.vue";
|
import BreakpointsObserver from "@/components/BreakpointsObserver.vue";
|
||||||
|
import { renderText } from "@/components/mixins/renderText";
|
||||||
|
|
||||||
const hasChanged = (prev: string[], curr: string[]) => {
|
const hasChanged = (prev: string[], curr: string[]) => {
|
||||||
if (prev.length !== curr.length) {
|
if (prev.length !== curr.length) {
|
||||||
|
@ -100,7 +101,7 @@ const hasChanged = (prev: string[], curr: string[]) => {
|
||||||
return curr.reduce((accu, val) => accu || !set.has(val), false);
|
return curr.reduce((accu, val) => accu || !set.has(val), false);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default mixins(workflowHelpers).extend({
|
export default mixins(renderText, workflowHelpers).extend({
|
||||||
name: "WorkflowDetails",
|
name: "WorkflowDetails",
|
||||||
components: {
|
components: {
|
||||||
TagsContainer,
|
TagsContainer,
|
||||||
|
@ -172,7 +173,7 @@ export default mixins(workflowHelpers).extend({
|
||||||
|
|
||||||
const saved = await this.saveCurrentWorkflow({ tags });
|
const saved = await this.saveCurrentWorkflow({ tags });
|
||||||
this.$telemetry.track('User edited workflow tags', { workflow_id: this.currentWorkflowId as string, new_tag_count: tags.length });
|
this.$telemetry.track('User edited workflow tags', { workflow_id: this.currentWorkflowId as string, new_tag_count: tags.length });
|
||||||
|
|
||||||
this.$data.tagsSaving = false;
|
this.$data.tagsSaving = false;
|
||||||
if (saved) {
|
if (saved) {
|
||||||
this.$data.isTagsEditEnabled = false;
|
this.$data.isTagsEditEnabled = false;
|
||||||
|
@ -196,8 +197,8 @@ export default mixins(workflowHelpers).extend({
|
||||||
const newName = name.trim();
|
const newName = name.trim();
|
||||||
if (!newName) {
|
if (!newName) {
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: "Name missing",
|
title: this.$baseText('workflowDetails.showMessage.title'),
|
||||||
message: `Please enter a name, or press 'esc' to go back to the old one.`,
|
message: this.$baseText('workflowDetails.showMessage.message'),
|
||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -22,94 +22,94 @@
|
||||||
<el-submenu index="workflow" title="Workflow" popperClass="sidebar-popper">
|
<el-submenu index="workflow" title="Workflow" popperClass="sidebar-popper">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<font-awesome-icon icon="network-wired"/>
|
<font-awesome-icon icon="network-wired"/>
|
||||||
<span slot="title" class="item-title-root">Workflows</span>
|
<span slot="title" class="item-title-root">{{ $baseText('mainSidebar.workflows') }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<n8n-menu-item index="workflow-new">
|
<n8n-menu-item index="workflow-new">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<font-awesome-icon icon="file"/>
|
<font-awesome-icon icon="file"/>
|
||||||
<span slot="title" class="item-title">New</span>
|
<span slot="title" class="item-title">{{ $baseText('mainSidebar.new') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</n8n-menu-item>
|
</n8n-menu-item>
|
||||||
<n8n-menu-item index="workflow-open">
|
<n8n-menu-item index="workflow-open">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<font-awesome-icon icon="folder-open"/>
|
<font-awesome-icon icon="folder-open"/>
|
||||||
<span slot="title" class="item-title">Open</span>
|
<span slot="title" class="item-title">{{ $baseText('mainSidebar.open') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</n8n-menu-item>
|
</n8n-menu-item>
|
||||||
<n8n-menu-item index="workflow-save">
|
<n8n-menu-item index="workflow-save">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<font-awesome-icon icon="save"/>
|
<font-awesome-icon icon="save"/>
|
||||||
<span slot="title" class="item-title">Save</span>
|
<span slot="title" class="item-title">{{ $baseText('mainSidebar.save') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</n8n-menu-item>
|
</n8n-menu-item>
|
||||||
<n8n-menu-item index="workflow-duplicate" :disabled="!currentWorkflow">
|
<n8n-menu-item index="workflow-duplicate" :disabled="!currentWorkflow">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<font-awesome-icon icon="copy"/>
|
<font-awesome-icon icon="copy"/>
|
||||||
<span slot="title" class="item-title">Duplicate</span>
|
<span slot="title" class="item-title">{{ $baseText('mainSidebar.duplicate') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</n8n-menu-item>
|
</n8n-menu-item>
|
||||||
<n8n-menu-item index="workflow-delete" :disabled="!currentWorkflow">
|
<n8n-menu-item index="workflow-delete" :disabled="!currentWorkflow">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<font-awesome-icon icon="trash"/>
|
<font-awesome-icon icon="trash"/>
|
||||||
<span slot="title" class="item-title">Delete</span>
|
<span slot="title" class="item-title">{{ $baseText('mainSidebar.delete') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</n8n-menu-item>
|
</n8n-menu-item>
|
||||||
<n8n-menu-item index="workflow-download">
|
<n8n-menu-item index="workflow-download">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<font-awesome-icon icon="file-download"/>
|
<font-awesome-icon icon="file-download"/>
|
||||||
<span slot="title" class="item-title">Download</span>
|
<span slot="title" class="item-title">{{ $baseText('mainSidebar.download') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</n8n-menu-item>
|
</n8n-menu-item>
|
||||||
<n8n-menu-item index="workflow-import-url">
|
<n8n-menu-item index="workflow-import-url">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<font-awesome-icon icon="cloud"/>
|
<font-awesome-icon icon="cloud"/>
|
||||||
<span slot="title" class="item-title">Import from URL</span>
|
<span slot="title" class="item-title">{{ $baseText('mainSidebar.importFromUrl') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</n8n-menu-item>
|
</n8n-menu-item>
|
||||||
<n8n-menu-item index="workflow-import-file">
|
<n8n-menu-item index="workflow-import-file">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<font-awesome-icon icon="hdd"/>
|
<font-awesome-icon icon="hdd"/>
|
||||||
<span slot="title" class="item-title">Import from File</span>
|
<span slot="title" class="item-title">{{ $baseText('mainSidebar.importFromFile') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</n8n-menu-item>
|
</n8n-menu-item>
|
||||||
<n8n-menu-item index="workflow-settings" :disabled="!currentWorkflow">
|
<n8n-menu-item index="workflow-settings" :disabled="!currentWorkflow">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<font-awesome-icon icon="cog"/>
|
<font-awesome-icon icon="cog"/>
|
||||||
<span slot="title" class="item-title">Settings</span>
|
<span slot="title" class="item-title">{{ $baseText('mainSidebar.settings') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</n8n-menu-item>
|
</n8n-menu-item>
|
||||||
</el-submenu>
|
</el-submenu>
|
||||||
|
|
||||||
<el-submenu index="credentials" title="Credentials" popperClass="sidebar-popper">
|
<el-submenu index="credentials" :title="$baseText('mainSidebar.credentials')" popperClass="sidebar-popper">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<font-awesome-icon icon="key"/>
|
<font-awesome-icon icon="key"/>
|
||||||
<span slot="title" class="item-title-root">Credentials</span>
|
<span slot="title" class="item-title-root">{{ $baseText('mainSidebar.credentials') }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<n8n-menu-item index="credentials-new">
|
<n8n-menu-item index="credentials-new">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<font-awesome-icon icon="file"/>
|
<font-awesome-icon icon="file"/>
|
||||||
<span slot="title" class="item-title">New</span>
|
<span slot="title" class="item-title">{{ $baseText('mainSidebar.new') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</n8n-menu-item>
|
</n8n-menu-item>
|
||||||
<n8n-menu-item index="credentials-open">
|
<n8n-menu-item index="credentials-open">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<font-awesome-icon icon="folder-open"/>
|
<font-awesome-icon icon="folder-open"/>
|
||||||
<span slot="title" class="item-title">Open</span>
|
<span slot="title" class="item-title">{{ $baseText('mainSidebar.open') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</n8n-menu-item>
|
</n8n-menu-item>
|
||||||
</el-submenu>
|
</el-submenu>
|
||||||
|
|
||||||
<n8n-menu-item index="executions">
|
<n8n-menu-item index="executions">
|
||||||
<font-awesome-icon icon="tasks"/>
|
<font-awesome-icon icon="tasks"/>
|
||||||
<span slot="title" class="item-title-root">Executions</span>
|
<span slot="title" class="item-title-root">{{ $baseText('mainSidebar.executions') }}</span>
|
||||||
</n8n-menu-item>
|
</n8n-menu-item>
|
||||||
|
|
||||||
<el-submenu index="help" class="help-menu" title="Help" popperClass="sidebar-popper">
|
<el-submenu index="help" class="help-menu" title="Help" popperClass="sidebar-popper">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<font-awesome-icon icon="question"/>
|
<font-awesome-icon icon="question"/>
|
||||||
<span slot="title" class="item-title-root">Help</span>
|
<span slot="title" class="item-title-root">{{ $baseText('mainSidebar.help') }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<MenuItemsIterator :items="helpMenuItems" :afterItemClick="trackHelpItemClick" />
|
<MenuItemsIterator :items="helpMenuItems" :afterItemClick="trackHelpItemClick" />
|
||||||
|
@ -117,7 +117,7 @@
|
||||||
<n8n-menu-item index="help-about">
|
<n8n-menu-item index="help-about">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<font-awesome-icon class="about-icon" icon="info"/>
|
<font-awesome-icon class="about-icon" icon="info"/>
|
||||||
<span slot="title" class="item-title">About n8n</span>
|
<span slot="title" class="item-title">{{ $baseText('mainSidebar.aboutN8n') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</n8n-menu-item>
|
</n8n-menu-item>
|
||||||
</el-submenu>
|
</el-submenu>
|
||||||
|
@ -155,6 +155,7 @@ import GiftNotificationIcon from './GiftNotificationIcon.vue';
|
||||||
import WorkflowSettings from '@/components/WorkflowSettings.vue';
|
import WorkflowSettings from '@/components/WorkflowSettings.vue';
|
||||||
|
|
||||||
import { genericHelpers } from '@/components/mixins/genericHelpers';
|
import { genericHelpers } from '@/components/mixins/genericHelpers';
|
||||||
|
import { renderText } from '@/components/mixins/renderText';
|
||||||
import { restApi } from '@/components/mixins/restApi';
|
import { restApi } from '@/components/mixins/restApi';
|
||||||
import { showMessage } from '@/components/mixins/showMessage';
|
import { showMessage } from '@/components/mixins/showMessage';
|
||||||
import { titleChange } from '@/components/mixins/titleChange';
|
import { titleChange } from '@/components/mixins/titleChange';
|
||||||
|
@ -168,42 +169,10 @@ import { mapGetters } from 'vuex';
|
||||||
import MenuItemsIterator from './MainSidebarMenuItemsIterator.vue';
|
import MenuItemsIterator from './MainSidebarMenuItemsIterator.vue';
|
||||||
import { CREDENTIAL_LIST_MODAL_KEY, CREDENTIAL_SELECT_MODAL_KEY, DUPLICATE_MODAL_KEY, TAGS_MANAGER_MODAL_KEY, VERSIONS_MODAL_KEY, WORKFLOW_SETTINGS_MODAL_KEY, WORKFLOW_OPEN_MODAL_KEY } from '@/constants';
|
import { CREDENTIAL_LIST_MODAL_KEY, CREDENTIAL_SELECT_MODAL_KEY, DUPLICATE_MODAL_KEY, TAGS_MANAGER_MODAL_KEY, VERSIONS_MODAL_KEY, WORKFLOW_SETTINGS_MODAL_KEY, WORKFLOW_OPEN_MODAL_KEY } from '@/constants';
|
||||||
|
|
||||||
const helpMenuItems: IMenuItem[] = [
|
|
||||||
{
|
|
||||||
id: 'docs',
|
|
||||||
type: 'link',
|
|
||||||
properties: {
|
|
||||||
href: 'https://docs.n8n.io',
|
|
||||||
title: 'Documentation',
|
|
||||||
icon: 'book',
|
|
||||||
newWindow: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'forum',
|
|
||||||
type: 'link',
|
|
||||||
properties: {
|
|
||||||
href: 'https://community.n8n.io',
|
|
||||||
title: 'Forum',
|
|
||||||
icon: 'users',
|
|
||||||
newWindow: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'examples',
|
|
||||||
type: 'link',
|
|
||||||
properties: {
|
|
||||||
href: 'https://n8n.io/workflows',
|
|
||||||
title: 'Workflows',
|
|
||||||
icon: 'network-wired',
|
|
||||||
newWindow: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default mixins(
|
export default mixins(
|
||||||
genericHelpers,
|
genericHelpers,
|
||||||
restApi,
|
restApi,
|
||||||
|
renderText,
|
||||||
showMessage,
|
showMessage,
|
||||||
titleChange,
|
titleChange,
|
||||||
workflowHelpers,
|
workflowHelpers,
|
||||||
|
@ -225,7 +194,6 @@ export default mixins(
|
||||||
basePath: this.$store.getters.getBaseUrl,
|
basePath: this.$store.getters.getBaseUrl,
|
||||||
executionsListDialogVisible: false,
|
executionsListDialogVisible: false,
|
||||||
stopExecutionInProgress: false,
|
stopExecutionInProgress: false,
|
||||||
helpMenuItems,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -236,6 +204,40 @@ export default mixins(
|
||||||
'hasVersionUpdates',
|
'hasVersionUpdates',
|
||||||
'nextVersions',
|
'nextVersions',
|
||||||
]),
|
]),
|
||||||
|
helpMenuItems (): object[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 'docs',
|
||||||
|
type: 'link',
|
||||||
|
properties: {
|
||||||
|
href: 'https://docs.n8n.io',
|
||||||
|
title: this.$baseText('mainSidebar.helpMenuItems.documentation'),
|
||||||
|
icon: 'book',
|
||||||
|
newWindow: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'forum',
|
||||||
|
type: 'link',
|
||||||
|
properties: {
|
||||||
|
href: 'https://community.n8n.io',
|
||||||
|
title: this.$baseText('mainSidebar.helpMenuItems.forum'),
|
||||||
|
icon: 'users',
|
||||||
|
newWindow: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'examples',
|
||||||
|
type: 'link',
|
||||||
|
properties: {
|
||||||
|
href: 'https://n8n.io/workflows',
|
||||||
|
title: this.$baseText('mainSidebar.helpMenuItems.workflows'),
|
||||||
|
icon: 'network-wired',
|
||||||
|
newWindow: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
exeuctionId (): string | undefined {
|
exeuctionId (): string | undefined {
|
||||||
return this.$route.params.id;
|
return this.$route.params.id;
|
||||||
},
|
},
|
||||||
|
@ -322,12 +324,19 @@ export default mixins(
|
||||||
this.stopExecutionInProgress = true;
|
this.stopExecutionInProgress = true;
|
||||||
await this.restApi().stopCurrentExecution(executionId);
|
await this.restApi().stopCurrentExecution(executionId);
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Execution stopped',
|
title: this.$baseText('mainSidebar.showMessage.stopExecution.title'),
|
||||||
message: `The execution with the id "${executionId}" got stopped!`,
|
message: this.$baseText(
|
||||||
|
'mainSidebar.showMessage.stopExecution.message',
|
||||||
|
{ interpolate: { executionId }},
|
||||||
|
),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(error, 'Problem stopping execution', 'There was a problem stopping the execuction:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('mainSidebar.showError.stopExecution.title'),
|
||||||
|
this.$baseText('mainSidebar.showError.stopExecution.message') + ':',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.stopExecutionInProgress = false;
|
this.stopExecutionInProgress = false;
|
||||||
},
|
},
|
||||||
|
@ -351,8 +360,8 @@ export default mixins(
|
||||||
worflowData = JSON.parse(data as string);
|
worflowData = JSON.parse(data as string);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Could not import file',
|
title: this.$baseText('mainSidebar.showMessage.handleFileImport.title'),
|
||||||
message: `The file does not contain valid JSON data.`,
|
message: this.$baseText('mainSidebar.showMessage.handleFileImport.message'),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
@ -374,17 +383,30 @@ export default mixins(
|
||||||
(this.$refs.importFile as HTMLInputElement).click();
|
(this.$refs.importFile as HTMLInputElement).click();
|
||||||
} else if (key === 'workflow-import-url') {
|
} else if (key === 'workflow-import-url') {
|
||||||
try {
|
try {
|
||||||
const promptResponse = await this.$prompt(`Workflow URL:`, 'Import Workflow from URL:', {
|
const promptResponse = await this.$prompt(
|
||||||
confirmButtonText: 'Import',
|
this.$baseText('mainSidebar.prompt.workflowUrl') + ':',
|
||||||
cancelButtonText: 'Cancel',
|
this.$baseText('mainSidebar.prompt.importWorkflowFromUrl') + ':',
|
||||||
inputErrorMessage: 'Invalid URL',
|
{
|
||||||
inputPattern: /^http[s]?:\/\/.*\.json$/i,
|
confirmButtonText: this.$baseText('mainSidebar.prompt.import'),
|
||||||
}) as MessageBoxInputData;
|
cancelButtonText: this.$baseText('mainSidebar.prompt.cancel'),
|
||||||
|
inputErrorMessage: this.$baseText('mainSidebar.prompt.invalidUrl'),
|
||||||
|
inputPattern: /^http[s]?:\/\/.*\.json$/i,
|
||||||
|
},
|
||||||
|
) as MessageBoxInputData;
|
||||||
|
|
||||||
this.$root.$emit('importWorkflowUrl', { url: promptResponse.value });
|
this.$root.$emit('importWorkflowUrl', { url: promptResponse.value });
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
} else if (key === 'workflow-delete') {
|
} else if (key === 'workflow-delete') {
|
||||||
const deleteConfirmed = await this.confirmMessage(`Are you sure that you want to delete the workflow "${this.workflowName}"?`, 'Delete Workflow?', 'warning', 'Yes, delete!');
|
const deleteConfirmed = await this.confirmMessage(
|
||||||
|
this.$baseText(
|
||||||
|
'mainSidebar.confirmMessage.workflowDelete.message',
|
||||||
|
{ interpolate: { workflowName: this.workflowName } },
|
||||||
|
),
|
||||||
|
this.$baseText('mainSidebar.confirmMessage.workflowDelete.headline'),
|
||||||
|
'warning',
|
||||||
|
this.$baseText('mainSidebar.confirmMessage.workflowDelete.confirmButtonText'),
|
||||||
|
this.$baseText('mainSidebar.confirmMessage.workflowDelete.cancelButtonText'),
|
||||||
|
);
|
||||||
|
|
||||||
if (deleteConfirmed === false) {
|
if (deleteConfirmed === false) {
|
||||||
return;
|
return;
|
||||||
|
@ -393,15 +415,22 @@ export default mixins(
|
||||||
try {
|
try {
|
||||||
await this.restApi().deleteWorkflow(this.currentWorkflow);
|
await this.restApi().deleteWorkflow(this.currentWorkflow);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(error, 'Problem deleting the workflow', 'There was a problem deleting the workflow:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('mainSidebar.showError.stopExecution.title'),
|
||||||
|
this.$baseText('mainSidebar.showError.stopExecution.message') + ':',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.$store.commit('setStateDirty', false);
|
this.$store.commit('setStateDirty', false);
|
||||||
// Reset tab title since workflow is deleted.
|
// Reset tab title since workflow is deleted.
|
||||||
this.$titleReset();
|
this.$titleReset();
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Workflow was deleted',
|
title: this.$baseText('mainSidebar.showMessage.handleSelect1.title'),
|
||||||
message: `The workflow "${this.workflowName}" was deleted!`,
|
message: this.$baseText(
|
||||||
|
'mainSidebar.showMessage.handleSelect1.message',
|
||||||
|
{ interpolate: { workflowName: this.workflowName }},
|
||||||
|
),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -436,7 +465,13 @@ export default mixins(
|
||||||
} else if (key === 'workflow-new') {
|
} else if (key === 'workflow-new') {
|
||||||
const result = this.$store.getters.getStateIsDirty;
|
const result = this.$store.getters.getStateIsDirty;
|
||||||
if(result) {
|
if(result) {
|
||||||
const importConfirm = await this.confirmMessage(`When you switch workflows your current workflow changes will be lost.`, 'Save your Changes?', 'warning', 'Yes, switch workflows and forget changes');
|
const importConfirm = await this.confirmMessage(
|
||||||
|
this.$baseText('mainSidebar.confirmMessage.workflowNew.message'),
|
||||||
|
this.$baseText('mainSidebar.confirmMessage.workflowNew.headline'),
|
||||||
|
'warning',
|
||||||
|
this.$baseText('mainSidebar.confirmMessage.workflowNew.confirmButton'),
|
||||||
|
this.$baseText('mainSidebar.confirmMessage.workflowNew.cancelButton'),
|
||||||
|
);
|
||||||
if (importConfirm === true) {
|
if (importConfirm === true) {
|
||||||
this.$store.commit('setStateDirty', false);
|
this.$store.commit('setStateDirty', false);
|
||||||
if (this.$router.currentRoute.name === 'NodeViewNew') {
|
if (this.$router.currentRoute.name === 'NodeViewNew') {
|
||||||
|
@ -446,8 +481,8 @@ export default mixins(
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Workflow created',
|
title: this.$baseText('mainSidebar.showMessage.handleSelect2.title'),
|
||||||
message: 'A new workflow got created!',
|
message: this.$baseText('mainSidebar.showMessage.handleSelect2.message'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -457,8 +492,8 @@ export default mixins(
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Workflow created',
|
title: this.$baseText('mainSidebar.showMessage.handleSelect3.title'),
|
||||||
message: 'A new workflow got created!',
|
message: this.$baseText('mainSidebar.showMessage.handleSelect3.message'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
|
|
||||||
<div v-for="(value, index) in values" :key="index" class="duplicate-parameter-item" :class="parameter.type">
|
<div v-for="(value, index) in values" :key="index" class="duplicate-parameter-item" :class="parameter.type">
|
||||||
<div class="delete-item clickable" v-if="!isReadOnly">
|
<div class="delete-item clickable" v-if="!isReadOnly">
|
||||||
<font-awesome-icon icon="trash" title="Delete Item" @click="deleteItem(index)" />
|
<font-awesome-icon icon="trash" :title="$baseText('multipleParameter.deleteItem')" @click="deleteItem(index)" />
|
||||||
<div v-if="sortable">
|
<div v-if="sortable">
|
||||||
<font-awesome-icon v-if="index !== 0" icon="angle-up" class="clickable" title="Move up" @click="moveOptionUp(index)" />
|
<font-awesome-icon v-if="index !== 0" icon="angle-up" class="clickable" :title="$baseText('multipleParameter.moveUp')" @click="moveOptionUp(index)" />
|
||||||
<font-awesome-icon v-if="index !== (values.length -1)" icon="angle-down" class="clickable" title="Move down" @click="moveOptionDown(index)" />
|
<font-awesome-icon v-if="index !== (values.length -1)" icon="angle-down" class="clickable" :title="$baseText('multipleParameter.moveDown')" @click="moveOptionDown(index)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="parameter.type === 'collection'">
|
<div v-if="parameter.type === 'collection'">
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
<div class="add-item-wrapper">
|
<div class="add-item-wrapper">
|
||||||
<div v-if="values && Object.keys(values).length === 0 || isReadOnly" class="no-items-exist">
|
<div v-if="values && Object.keys(values).length === 0 || isReadOnly" class="no-items-exist">
|
||||||
<n8n-text size="small">Currently no items exist</n8n-text>
|
<n8n-text size="small">{{ $baseText('multipleParameter.currentlyNoItemsExist') }}</n8n-text>
|
||||||
</div>
|
</div>
|
||||||
<n8n-button v-if="!isReadOnly" fullWidth @click="addItem()" :label="addButtonText" />
|
<n8n-button v-if="!isReadOnly" fullWidth @click="addItem()" :label="addButtonText" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,23 +16,23 @@
|
||||||
</n8n-tooltip>
|
</n8n-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="node-executing-info" title="Node is executing">
|
<div class="node-executing-info" :title="$baseText('node.nodeIsExecuting')">
|
||||||
<font-awesome-icon icon="sync-alt" spin />
|
<font-awesome-icon icon="sync-alt" spin />
|
||||||
</div>
|
</div>
|
||||||
<div class="node-options no-select-on-click" v-if="!isReadOnly">
|
<div class="node-options no-select-on-click" v-if="!isReadOnly">
|
||||||
<div v-touch:tap="deleteNode" class="option" title="Delete Node" >
|
<div v-touch:tap="deleteNode" class="option" :title="$baseText('node.deleteNode')">
|
||||||
<font-awesome-icon icon="trash" />
|
<font-awesome-icon icon="trash" />
|
||||||
</div>
|
</div>
|
||||||
<div v-touch:tap="disableNode" class="option" title="Activate/Deactivate Node" >
|
<div v-touch:tap="disableNode" class="option" :title="$baseText('node.activateDeactivateNode')">
|
||||||
<font-awesome-icon :icon="nodeDisabledIcon" />
|
<font-awesome-icon :icon="nodeDisabledIcon" />
|
||||||
</div>
|
</div>
|
||||||
<div v-touch:tap="duplicateNode" class="option" title="Duplicate Node" >
|
<div v-touch:tap="duplicateNode" class="option" :title="$baseText('node.duplicateNode')">
|
||||||
<font-awesome-icon icon="clone" />
|
<font-awesome-icon icon="clone" />
|
||||||
</div>
|
</div>
|
||||||
<div v-touch:tap="setNodeActive" class="option touch" title="Edit Node" v-if="!isReadOnly">
|
<div v-touch:tap="setNodeActive" class="option touch" :title="$baseText('node.editNode')" v-if="!isReadOnly">
|
||||||
<font-awesome-icon class="execute-icon" icon="cog" />
|
<font-awesome-icon class="execute-icon" icon="cog" />
|
||||||
</div>
|
</div>
|
||||||
<div v-touch:tap="executeNode" class="option" title="Execute Node" v-if="!isReadOnly && !workflowRunning">
|
<div v-touch:tap="executeNode" class="option" :title="$baseText('node.executeNode')" v-if="!isReadOnly && !workflowRunning">
|
||||||
<font-awesome-icon class="execute-icon" icon="play-circle" />
|
<font-awesome-icon class="execute-icon" icon="play-circle" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,6 +57,7 @@ import { WAIT_TIME_UNLIMITED } from '@/constants';
|
||||||
import { externalHooks } from '@/components/mixins/externalHooks';
|
import { externalHooks } from '@/components/mixins/externalHooks';
|
||||||
import { nodeBase } from '@/components/mixins/nodeBase';
|
import { nodeBase } from '@/components/mixins/nodeBase';
|
||||||
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
||||||
|
import { renderText } from '@/components/mixins/renderText';
|
||||||
import { workflowHelpers } from '@/components/mixins/workflowHelpers';
|
import { workflowHelpers } from '@/components/mixins/workflowHelpers';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -70,7 +71,7 @@ import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
|
|
||||||
export default mixins(externalHooks, nodeBase, nodeHelpers, workflowHelpers).extend({
|
export default mixins(externalHooks, nodeBase, nodeHelpers, renderText, workflowHelpers).extend({
|
||||||
name: 'Node',
|
name: 'Node',
|
||||||
components: {
|
components: {
|
||||||
NodeIcon,
|
NodeIcon,
|
||||||
|
|
|
@ -2,7 +2,14 @@
|
||||||
<div v-if="credentialTypesNodeDescriptionDisplayed.length" :class="$style.container">
|
<div v-if="credentialTypesNodeDescriptionDisplayed.length" :class="$style.container">
|
||||||
<div v-for="credentialTypeDescription in credentialTypesNodeDescriptionDisplayed" :key="credentialTypeDescription.name">
|
<div v-for="credentialTypeDescription in credentialTypesNodeDescriptionDisplayed" :key="credentialTypeDescription.name">
|
||||||
<n8n-input-label
|
<n8n-input-label
|
||||||
:label="`Credential for ${credentialTypeNames[credentialTypeDescription.name]}`"
|
:label="$baseText(
|
||||||
|
'nodeCredentials.credentialFor',
|
||||||
|
{
|
||||||
|
interpolate: {
|
||||||
|
credentialType: credentialTypeNames[credentialTypeDescription.name]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)"
|
||||||
:bold="false"
|
:bold="false"
|
||||||
size="small"
|
size="small"
|
||||||
|
|
||||||
|
@ -13,7 +20,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div :class="issues.length ? $style.hasIssues : $style.input" v-else >
|
<div :class="issues.length ? $style.hasIssues : $style.input" v-else >
|
||||||
<n8n-select :value="getSelectedId(credentialTypeDescription.name)" @change="(value) => onCredentialSelected(credentialTypeDescription.name, value)" placeholder="Select Credential" size="small">
|
<n8n-select :value="getSelectedId(credentialTypeDescription.name)" @change="(value) => onCredentialSelected(credentialTypeDescription.name, value)" :placeholder="$baseText('nodeCredentials.selectCredential')" size="small">
|
||||||
<n8n-option
|
<n8n-option
|
||||||
v-for="(item) in credentialOptions[credentialTypeDescription.name]"
|
v-for="(item) in credentialOptions[credentialTypeDescription.name]"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
|
@ -30,13 +37,13 @@
|
||||||
|
|
||||||
<div :class="$style.warning" v-if="issues.length">
|
<div :class="$style.warning" v-if="issues.length">
|
||||||
<n8n-tooltip placement="top" >
|
<n8n-tooltip placement="top" >
|
||||||
<div slot="content" v-html="'Issues:<br /> - ' + issues.join('<br /> - ')"></div>
|
<div slot="content" v-html="`${$baseText('nodeCredentials.issues')}:<br /> - ` + issues.join('<br /> - ')"></div>
|
||||||
<font-awesome-icon icon="exclamation-triangle" />
|
<font-awesome-icon icon="exclamation-triangle" />
|
||||||
</n8n-tooltip>
|
</n8n-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div :class="$style.edit" v-if="selected[credentialTypeDescription.name] && isCredentialExisting(credentialTypeDescription.name)">
|
<div :class="$style.edit" v-if="selected[credentialTypeDescription.name] && isCredentialExisting(credentialTypeDescription.name)">
|
||||||
<font-awesome-icon icon="pen" @click="editCredential(credentialTypeDescription.name)" class="clickable" title="Update Credentials" />
|
<font-awesome-icon icon="pen" @click="editCredential(credentialTypeDescription.name)" class="clickable" :title="$baseText('nodeCredentials.updateCredential')" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n8n-input-label>
|
</n8n-input-label>
|
||||||
|
@ -210,8 +217,16 @@ export default mixins(
|
||||||
});
|
});
|
||||||
this.updateNodesCredentialsIssues();
|
this.updateNodesCredentialsIssues();
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Node credentials updated',
|
title: this.$baseText('nodeCredentials.showMessage.title'),
|
||||||
message: `Nodes that used credentials "${oldCredentials.name}" have been updated to use "${selected.name}"`,
|
message: this.$baseText(
|
||||||
|
'nodeCredentials.showMessage.message',
|
||||||
|
{
|
||||||
|
interpolate: {
|
||||||
|
oldCredentialName: oldCredentials.name,
|
||||||
|
newCredentialName: selected.name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,26 +5,35 @@
|
||||||
<display-with-change :key-name="'name'" @valueChanged="valueChanged"></display-with-change>
|
<display-with-change :key-name="'name'" @valueChanged="valueChanged"></display-with-change>
|
||||||
<a v-if="nodeType" :href="'http://n8n.io/nodes/' + nodeType.name" target="_blank" class="node-info">
|
<a v-if="nodeType" :href="'http://n8n.io/nodes/' + nodeType.name" target="_blank" class="node-info">
|
||||||
<n8n-tooltip class="clickable" placement="top" >
|
<n8n-tooltip class="clickable" placement="top" >
|
||||||
<div slot="content" v-html="'<strong>Node Description:</strong><br />' + nodeTypeDescription + '<br /><br /><strong>Click the \'?\' icon to open this node on n8n.io </strong>'"></div>
|
<div slot="content" v-html="`<strong>${$baseText('nodeSettings.nodeDescription')}:</strong><br />` + nodeTypeDescription + `<br /><br /><strong>${$baseText('nodeSettings.clickOnTheQuestionMarkIcon')}</strong>`"></div>
|
||||||
<font-awesome-icon icon="question-circle" />
|
<font-awesome-icon icon="question-circle" />
|
||||||
</n8n-tooltip>
|
</n8n-tooltip>
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="node-is-not-valid" v-if="node && !nodeValid">
|
<div class="node-is-not-valid" v-if="node && !nodeValid">
|
||||||
<n8n-text>The node is not valid as its type "{{node.type}}" is unknown.</n8n-text>
|
<n8n-text>
|
||||||
|
{{
|
||||||
|
$baseText(
|
||||||
|
'nodeSettings.theNodeIsNotValidAsItsTypeIsUnknown',
|
||||||
|
{ interpolate: { nodeType: node.type } },
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</n8n-text>
|
||||||
</div>
|
</div>
|
||||||
<div class="node-parameters-wrapper" v-if="node && nodeValid">
|
<div class="node-parameters-wrapper" v-if="node && nodeValid">
|
||||||
<el-tabs stretch @tab-click="handleTabClick">
|
<el-tabs stretch @tab-click="handleTabClick">
|
||||||
<el-tab-pane label="Parameters">
|
<el-tab-pane :label="$baseText('nodeSettings.parameters')">
|
||||||
<node-credentials :node="node" @credentialSelected="credentialSelected"></node-credentials>
|
<node-credentials :node="node" @credentialSelected="credentialSelected"></node-credentials>
|
||||||
<node-webhooks :node="node" :nodeType="nodeType" />
|
<node-webhooks :node="node" :nodeType="nodeType" />
|
||||||
<parameter-input-list :parameters="parametersNoneSetting" :hideDelete="true" :nodeValues="nodeValues" path="parameters" @valueChanged="valueChanged" />
|
<parameter-input-list :parameters="parametersNoneSetting" :hideDelete="true" :nodeValues="nodeValues" path="parameters" @valueChanged="valueChanged" />
|
||||||
<div v-if="parametersNoneSetting.length === 0" class="no-parameters">
|
<div v-if="parametersNoneSetting.length === 0" class="no-parameters">
|
||||||
<n8n-text>This node does not have any parameters.</n8n-text>
|
<n8n-text>
|
||||||
|
{{ $baseText('nodeSettings.thisNodeDoesNotHaveAnyParameters') }}
|
||||||
|
</n8n-text>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="Settings">
|
<el-tab-pane :label="$baseText('nodeSettings.settings')">
|
||||||
<parameter-input-list :parameters="nodeSettings" :hideDelete="true" :nodeValues="nodeValues" path="" @valueChanged="valueChanged" />
|
<parameter-input-list :parameters="nodeSettings" :hideDelete="true" :nodeValues="nodeValues" path="" @valueChanged="valueChanged" />
|
||||||
<parameter-input-list :parameters="parametersSetting" :nodeValues="nodeValues" path="parameters" @valueChanged="valueChanged" />
|
<parameter-input-list :parameters="parametersSetting" :nodeValues="nodeValues" path="parameters" @valueChanged="valueChanged" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
@ -90,7 +99,7 @@ export default mixins(
|
||||||
if (this.nodeType && this.nodeType.description) {
|
if (this.nodeType && this.nodeType.description) {
|
||||||
return this.nodeType.description;
|
return this.nodeType.description;
|
||||||
} else {
|
} else {
|
||||||
return 'No description found';
|
return this.$baseText('nodeSettings.noDescriptionFound');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
headerStyle (): object {
|
headerStyle (): object {
|
||||||
|
@ -152,7 +161,7 @@ export default mixins(
|
||||||
|
|
||||||
nodeSettings: [
|
nodeSettings: [
|
||||||
{
|
{
|
||||||
displayName: 'Notes',
|
displayName: this.$baseText('nodeSettings.notes.displayName'),
|
||||||
name: 'notes',
|
name: 'notes',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
|
@ -160,50 +169,50 @@ export default mixins(
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
description: 'Optional note to save with the node.',
|
description: this.$baseText('nodeSettings.notes.description'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Display note in flow?',
|
displayName: this.$baseText('nodeSettings.notesInFlow.displayName'),
|
||||||
name: 'notesInFlow',
|
name: 'notesInFlow',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: false,
|
default: false,
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
description: 'If active, the note above will display in the flow as a subtitle.',
|
description: this.$baseText('nodeSettings.notesInFlow.description'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Node Color',
|
displayName: this.$baseText('nodeSettings.color.displayName'),
|
||||||
name: 'color',
|
name: 'color',
|
||||||
type: 'color',
|
type: 'color',
|
||||||
default: '#ff0000',
|
default: '#ff0000',
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
description: 'The color of the node in the flow.',
|
description: this.$baseText('nodeSettings.color.description'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Always Output Data',
|
displayName: this.$baseText('nodeSettings.alwaysOutputData.displayName'),
|
||||||
name: 'alwaysOutputData',
|
name: 'alwaysOutputData',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: false,
|
default: false,
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
description: 'If active, the node will return an empty item even if the <br />node returns no data during an initial execution. Be careful setting <br />this on IF-Nodes as it could cause an infinite loop.',
|
description: this.$baseText('nodeSettings.alwaysOutputData.description'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Execute Once',
|
displayName: this.$baseText('nodeSettings.executeOnce.displayName'),
|
||||||
name: 'executeOnce',
|
name: 'executeOnce',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: false,
|
default: false,
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
description: 'If active, the node executes only once, with data<br /> from the first item it recieves. ',
|
description: this.$baseText('nodeSettings.executeOnce.description'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Retry On Fail',
|
displayName: this.$baseText('nodeSettings.retryOnFail.displayName'),
|
||||||
name: 'retryOnFail',
|
name: 'retryOnFail',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: false,
|
default: false,
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
description: 'If active, the node tries to execute a failed attempt <br /> multiple times until it succeeds.',
|
description: this.$baseText('nodeSettings.retryOnFail.description'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Max. Tries',
|
displayName: this.$baseText('nodeSettings.maxTries.displayName'),
|
||||||
name: 'maxTries',
|
name: 'maxTries',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
|
@ -219,10 +228,10 @@ export default mixins(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
description: 'Number of times Retry On Fail should attempt to execute the node <br />before stopping and returning the execution as failed.',
|
description: this.$baseText('nodeSettings.maxTries.description'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Wait Between Tries',
|
displayName: this.$baseText('nodeSettings.waitBetweenTries.displayName'),
|
||||||
name: 'waitBetweenTries',
|
name: 'waitBetweenTries',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
|
@ -238,15 +247,15 @@ export default mixins(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
description: 'How long to wait between each attempt. Value in ms.',
|
description: this.$baseText('nodeSettings.waitBetweenTries.description'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Continue On Fail',
|
displayName: this.$baseText('nodeSettings.continueOnFail.displayName'),
|
||||||
name: 'continueOnFail',
|
name: 'continueOnFail',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: false,
|
default: false,
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
description: 'If active, the workflow continues even if this node\'s <br />execution fails. When this occurs, the node passes along input data from<br />previous nodes - so your workflow should account for unexpected output data.',
|
description: this.$baseText('nodeSettings.continueOnFail.description'),
|
||||||
},
|
},
|
||||||
] as INodeProperties[],
|
] as INodeProperties[],
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="webhooksNode.length" class="webhoooks">
|
<div v-if="webhooksNode.length" class="webhoooks">
|
||||||
<div class="clickable headline" :class="{expanded: !isMinimized}" @click="isMinimized=!isMinimized" :title="isMinimized ? 'Click to display Webhook URLs' : 'Click to hide Webhook URLs'">
|
<div class="clickable headline" :class="{expanded: !isMinimized}" @click="isMinimized=!isMinimized" :title="isMinimized ? $baseText('nodeWebhooks.clickToDisplayWebhookUrls') : $baseText('nodeWebhooks.clickToHideWebhookUrls')">
|
||||||
<font-awesome-icon icon="angle-down" class="minimize-button minimize-icon" />
|
<font-awesome-icon icon="angle-down" class="minimize-button minimize-icon" />
|
||||||
Webhook URLs
|
{{ $baseText('nodeWebhooks.webhookUrls') }}
|
||||||
</div>
|
</div>
|
||||||
<el-collapse-transition>
|
<el-collapse-transition>
|
||||||
<div class="node-webhooks" v-if="!isMinimized">
|
<div class="node-webhooks" v-if="!isMinimized">
|
||||||
|
@ -10,14 +10,14 @@
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-radio-group v-model="showUrlFor" size="mini">
|
<el-radio-group v-model="showUrlFor" size="mini">
|
||||||
<el-radio-button label="test">Test URL</el-radio-button>
|
<el-radio-button label="test">{{ $baseText('nodeWebhooks.testUrl') }}</el-radio-button>
|
||||||
<el-radio-button label="production">Production URL</el-radio-button>
|
<el-radio-button label="production">{{ $baseText('nodeWebhooks.productionUrl') }}</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<n8n-tooltip v-for="(webhook, index) in webhooksNode" :key="index" class="item" content="Click to copy Webhook URL" placement="left">
|
<n8n-tooltip v-for="(webhook, index) in webhooksNode" :key="index" class="item" :content="$baseText('nodeWebhooks.clickToCopyWebhookUrls')" placement="left">
|
||||||
<div class="webhook-wrapper">
|
<div class="webhook-wrapper">
|
||||||
<div class="http-field">
|
<div class="http-field">
|
||||||
<div class="http-method">
|
<div class="http-method">
|
||||||
|
@ -50,9 +50,11 @@ import { showMessage } from '@/components/mixins/showMessage';
|
||||||
import { workflowHelpers } from '@/components/mixins/workflowHelpers';
|
import { workflowHelpers } from '@/components/mixins/workflowHelpers';
|
||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
import { renderText } from './mixins/renderText';
|
||||||
|
|
||||||
export default mixins(
|
export default mixins(
|
||||||
copyPaste,
|
copyPaste,
|
||||||
|
renderText,
|
||||||
showMessage,
|
showMessage,
|
||||||
workflowHelpers,
|
workflowHelpers,
|
||||||
)
|
)
|
||||||
|
@ -83,8 +85,8 @@ export default mixins(
|
||||||
this.copyToClipboard(webhookUrl);
|
this.copyToClipboard(webhookUrl);
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Copied',
|
title: this.$baseText('nodeWebhooks.showMessage.title'),
|
||||||
message: `The webhook URL was successfully copied!`,
|
message: this.$baseText('nodeWebhooks.showMessage.message'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -95,7 +97,7 @@ export default mixins(
|
||||||
try {
|
try {
|
||||||
return this.resolveExpression(webhookData[key] as string) as string;
|
return this.resolveExpression(webhookData[key] as string) as string;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return '[INVALID EXPRESSION]';
|
return this.$baseText('nodeWebhooks.invalidExpression');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getWebhookUrl (webhookData: IWebhookDescription): string {
|
getWebhookUrl (webhookData: IWebhookDescription): string {
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
:placeholder="isValueExpression?'':parameter.placeholder"
|
:placeholder="isValueExpression?'':parameter.placeholder"
|
||||||
>
|
>
|
||||||
<div slot="suffix" class="expand-input-icon-container">
|
<div slot="suffix" class="expand-input-icon-container">
|
||||||
<font-awesome-icon v-if="!isValueExpression && !isReadOnly" icon="external-link-alt" class="edit-window-button clickable" title="Open Edit Window" @click="displayEditDialog()" />
|
<font-awesome-icon v-if="!isValueExpression && !isReadOnly" icon="external-link-alt" class="edit-window-button clickable" :title="$baseText('parameterInput.openEditWindow')" @click="displayEditDialog()" />
|
||||||
</div>
|
</div>
|
||||||
</n8n-input>
|
</n8n-input>
|
||||||
</div>
|
</div>
|
||||||
|
@ -78,7 +78,7 @@
|
||||||
:value="displayValue"
|
:value="displayValue"
|
||||||
:title="displayTitle"
|
:title="displayTitle"
|
||||||
:disabled="isReadOnly"
|
:disabled="isReadOnly"
|
||||||
:placeholder="parameter.placeholder?parameter.placeholder:'Select date and time'"
|
:placeholder="parameter.placeholder?parameter.placeholder:$baseText('parameterInput.selectDateAndTime')"
|
||||||
:picker-options="dateTimePickerOptions"
|
:picker-options="dateTimePickerOptions"
|
||||||
@change="valueChanged"
|
@change="valueChanged"
|
||||||
@focus="setFocus"
|
@focus="setFocus"
|
||||||
|
@ -169,7 +169,7 @@
|
||||||
|
|
||||||
<div class="parameter-issues" v-if="getIssues.length">
|
<div class="parameter-issues" v-if="getIssues.length">
|
||||||
<n8n-tooltip placement="top" >
|
<n8n-tooltip placement="top" >
|
||||||
<div slot="content" v-html="'Issues:<br /> - ' + getIssues.join('<br /> - ')"></div>
|
<div slot="content" v-html="`${$baseText('parameterInput.issues')}:<br /> - ` + getIssues.join('<br /> - ')"></div>
|
||||||
<font-awesome-icon icon="exclamation-triangle" />
|
<font-awesome-icon icon="exclamation-triangle" />
|
||||||
</n8n-tooltip>
|
</n8n-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -177,13 +177,13 @@
|
||||||
<div class="parameter-options" v-if="displayOptionsComputed">
|
<div class="parameter-options" v-if="displayOptionsComputed">
|
||||||
<el-dropdown trigger="click" @command="optionSelected" size="mini">
|
<el-dropdown trigger="click" @command="optionSelected" size="mini">
|
||||||
<span class="el-dropdown-link">
|
<span class="el-dropdown-link">
|
||||||
<font-awesome-icon icon="cogs" class="reset-icon clickable" title="Parameter Options"/>
|
<font-awesome-icon icon="cogs" class="reset-icon clickable" :title="$baseText('parameterInput.parameterOptions')"/>
|
||||||
</span>
|
</span>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item command="addExpression" v-if="parameter.noDataExpression !== true && !isValueExpression">Add Expression</el-dropdown-item>
|
<el-dropdown-item command="addExpression" v-if="parameter.noDataExpression !== true && !isValueExpression">{{ $baseText('parameterInput.addExpression') }}</el-dropdown-item>
|
||||||
<el-dropdown-item command="removeExpression" v-if="parameter.noDataExpression !== true && isValueExpression">Remove Expression</el-dropdown-item>
|
<el-dropdown-item command="removeExpression" v-if="parameter.noDataExpression !== true && isValueExpression">{{ $baseText('parameterInput.removeExpression') }}</el-dropdown-item>
|
||||||
<el-dropdown-item command="refreshOptions" v-if="Boolean(remoteMethod)">Refresh List</el-dropdown-item>
|
<el-dropdown-item command="refreshOptions" v-if="Boolean(remoteMethod)">{{ $baseText('parameterInput.refreshList') }}</el-dropdown-item>
|
||||||
<el-dropdown-item command="resetValue" :disabled="isDefault" divided>Reset Value</el-dropdown-item>
|
<el-dropdown-item command="resetValue" :disabled="isDefault" divided>{{ $baseText('parameterInput.resetValue') }}</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
inputSize="large"
|
inputSize="large"
|
||||||
/>
|
/>
|
||||||
<div class="errors" v-if="showRequiredErrors">
|
<div class="errors" v-if="showRequiredErrors">
|
||||||
This field is required. <a v-if="documentationUrl" :href="documentationUrl" target="_blank" @click="onDocumentationUrlClick">Open docs</a>
|
{{ $baseText('parameterInputExpanded.thisFieldIsRequired') }} <a v-if="documentationUrl" :href="documentationUrl" target="_blank" @click="onDocumentationUrlClick">Open docs</a>
|
||||||
</div>
|
</div>
|
||||||
</n8n-input-label>
|
</n8n-input-label>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -24,11 +24,11 @@
|
||||||
v-else-if="['collection', 'fixedCollection'].includes(parameter.type)"
|
v-else-if="['collection', 'fixedCollection'].includes(parameter.type)"
|
||||||
class="multi-parameter"
|
class="multi-parameter"
|
||||||
>
|
>
|
||||||
<div class="delete-option clickable" title="Delete" v-if="hideDelete !== true && !isReadOnly">
|
<div class="delete-option clickable" :title="$baseText('parameterInputList.delete')" v-if="hideDelete !== true && !isReadOnly">
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
icon="trash"
|
icon="trash"
|
||||||
class="reset-icon clickable"
|
class="reset-icon clickable"
|
||||||
title="Parameter Options"
|
:title="$baseText('parameterInputList.parameterOptions')"
|
||||||
@click="deleteOption(parameter.name)"
|
@click="deleteOption(parameter.name)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -59,11 +59,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="displayNodeParameter(parameter)" class="parameter-item">
|
<div v-else-if="displayNodeParameter(parameter)" class="parameter-item">
|
||||||
<div class="delete-option clickable" title="Delete" v-if="hideDelete !== true && !isReadOnly">
|
<div class="delete-option clickable" :title="$baseText('parameterInputList.delete')" v-if="hideDelete !== true && !isReadOnly">
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
icon="trash"
|
icon="trash"
|
||||||
class="reset-icon clickable"
|
class="reset-icon clickable"
|
||||||
title="Delete Parameter"
|
:title="$baseText('parameterInputList.deleteParameter')"
|
||||||
@click="deleteOption(parameter.name)"
|
@click="deleteOption(parameter.name)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<Modal
|
<Modal
|
||||||
:name="PERSONALIZATION_MODAL_KEY"
|
:name="PERSONALIZATION_MODAL_KEY"
|
||||||
:title="!submitted? 'Get started' : 'Thanks!'"
|
:title="!submitted? $baseText('personalizationModal.getStarted') : $baseText('personalizationModal.thanks')"
|
||||||
:subtitle="!submitted? 'These questions help us tailor n8n to you' : ''"
|
:subtitle="!submitted? $baseText('personalizationModal.theseQuestionsHelpUs') : ''"
|
||||||
:centerTitle="true"
|
:centerTitle="true"
|
||||||
:showClose="false"
|
:showClose="false"
|
||||||
:eventBus="modalBus"
|
:eventBus="modalBus"
|
||||||
|
@ -15,37 +15,37 @@
|
||||||
<template v-slot:content>
|
<template v-slot:content>
|
||||||
<div v-if="submitted" :class="$style.submittedContainer">
|
<div v-if="submitted" :class="$style.submittedContainer">
|
||||||
<img :class="$style.demoImage" :src="baseUrl + 'suggestednodes.png'" />
|
<img :class="$style.demoImage" :src="baseUrl + 'suggestednodes.png'" />
|
||||||
<n8n-text>Look out for things marked with a ✨. They are personalized to make n8n more relevant to you.</n8n-text>
|
<n8n-text>{{ $baseText('personalizationModal.lookOutForThingsMarked') }}</n8n-text>
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.container" v-else>
|
<div :class="$style.container" v-else>
|
||||||
<n8n-input-label label="Which of these areas do you mainly work in?">
|
<n8n-input-label :label="$baseText('personalizationModal.whichOfTheseAreasDoYouMainlyWorkIn')">
|
||||||
<n8n-select :value="values[WORK_AREA_KEY]" placeholder="Select..." @change="(value) => onInput(WORK_AREA_KEY, value)">
|
<n8n-select :value="values[WORK_AREA_KEY]" :placeholder="$baseText('personalizationModal.select')" @change="(value) => onInput(WORK_AREA_KEY, value)">
|
||||||
<n8n-option :value="AUTOMATION_CONSULTING_WORK_AREA" label="Automation consulting" />
|
<n8n-option :value="AUTOMATION_CONSULTING_WORK_AREA" :label="$baseText('personalizationModal.automationConsulting')" />
|
||||||
<n8n-option :value="FINANCE_WORK_AREA" label="Finance" />
|
<n8n-option :value="FINANCE_WORK_AREA" :label="$baseText('personalizationModal.finance')" />
|
||||||
<n8n-option :value="HR_WORK_AREA" label="HR" />
|
<n8n-option :value="HR_WORK_AREA" :label="$baseText('personalizationModal.hr')" />
|
||||||
<n8n-option :value="IT_ENGINEERING_WORK_AREA" label="IT / Engineering" />
|
<n8n-option :value="IT_ENGINEERING_WORK_AREA" :label="$baseText('personalizationModal.itEngineering')" />
|
||||||
<n8n-option :value="LEGAL_WORK_AREA" label="Legal" />
|
<n8n-option :value="LEGAL_WORK_AREA" :label="$baseText('personalizationModal.legal')" />
|
||||||
<n8n-option :value="MARKETING_WORK_AREA" label="Marketing / Growth" />
|
<n8n-option :value="MARKETING_WORK_AREA" :label="$baseText('personalizationModal.marketingGrowth')" />
|
||||||
<n8n-option :value="OPS_WORK_AREA" label="Operations" />
|
<n8n-option :value="OPS_WORK_AREA" :label="$baseText('personalizationModal.operations')" />
|
||||||
<n8n-option :value="PRODUCT_WORK_AREA" label="Product" />
|
<n8n-option :value="PRODUCT_WORK_AREA" :label="$baseText('personalizationModal.product')" />
|
||||||
<n8n-option :value="SALES_BUSINESSDEV_WORK_AREA" label="Sales / Business Development" />
|
<n8n-option :value="SALES_BUSINESSDEV_WORK_AREA" :label="$baseText('personalizationModal.salesBusinessDevelopment')" />
|
||||||
<n8n-option :value="SECURITY_WORK_AREA" label="Security" />
|
<n8n-option :value="SECURITY_WORK_AREA" :label="$baseText('personalizationModal.security')" />
|
||||||
<n8n-option :value="SUPPORT_WORK_AREA" label="Support" />
|
<n8n-option :value="SUPPORT_WORK_AREA" :label="$baseText('personalizationModal.support')" />
|
||||||
<n8n-option :value="OTHER_WORK_AREA_OPTION" label="Other (please specify)" />
|
<n8n-option :value="OTHER_WORK_AREA_OPTION" :label="$baseText('personalizationModal.otherPleaseSpecify')" />
|
||||||
</n8n-select>
|
</n8n-select>
|
||||||
</n8n-input-label>
|
</n8n-input-label>
|
||||||
|
|
||||||
<n8n-input
|
<n8n-input
|
||||||
v-if="otherWorkAreaFieldVisible"
|
v-if="otherWorkAreaFieldVisible"
|
||||||
:value="values[OTHER_WORK_AREA_KEY]"
|
:value="values[OTHER_WORK_AREA_KEY]"
|
||||||
placeholder="Specify your work area"
|
:placeholder="$baseText('personalizationModal.specifyYourWorkArea')"
|
||||||
@input="(value) => onInput(OTHER_WORK_AREA_KEY, value)"
|
@input="(value) => onInput(OTHER_WORK_AREA_KEY, value)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<n8n-input-label label="How are your coding skills?">
|
<n8n-input-label :label="$baseText('personalizationModal.howAreYourCodingSkills')">
|
||||||
<n8n-select :value="values[CODING_SKILL_KEY]" placeholder="Select..." @change="(value) => onInput(CODING_SKILL_KEY, value)">
|
<n8n-select :value="values[CODING_SKILL_KEY]" :placeholder="$baseText('personalizationModal.select')" @change="(value) => onInput(CODING_SKILL_KEY, value)">
|
||||||
<n8n-option
|
<n8n-option
|
||||||
label="0 (Never coded)"
|
:label="`0 (${baseText('personalizationModal.neverCoded')})`"
|
||||||
value="0"
|
value="0"
|
||||||
/>
|
/>
|
||||||
<n8n-option
|
<n8n-option
|
||||||
|
@ -65,36 +65,36 @@
|
||||||
value="4"
|
value="4"
|
||||||
/>
|
/>
|
||||||
<n8n-option
|
<n8n-option
|
||||||
label="5 (Pro coder)"
|
:label="`5 (${$baseText('personalizationModal.proCoder')})`"
|
||||||
value="5"
|
value="5"
|
||||||
/>
|
/>
|
||||||
</n8n-select>
|
</n8n-select>
|
||||||
</n8n-input-label>
|
</n8n-input-label>
|
||||||
|
|
||||||
<n8n-input-label label="How big is your company?">
|
<n8n-input-label :label="$baseText('personalizationModal.howBigIsYourCompany')">
|
||||||
<n8n-select :value="values[COMPANY_SIZE_KEY]" placeholder="Select..." @change="(value) => onInput(COMPANY_SIZE_KEY, value)">
|
<n8n-select :value="values[COMPANY_SIZE_KEY]" :placeholder="$baseText('personalizationModal.select')" @change="(value) => onInput(COMPANY_SIZE_KEY, value)">
|
||||||
<n8n-option
|
<n8n-option
|
||||||
label="Less than 20 people"
|
:label="$baseText('personalizationModal.lessThan20People')"
|
||||||
:value="COMPANY_SIZE_20_OR_LESS"
|
:value="COMPANY_SIZE_20_OR_LESS"
|
||||||
/>
|
/>
|
||||||
<n8n-option
|
<n8n-option
|
||||||
label="20-99 people"
|
:label="`20-99 ${$baseText('personalizationModal.people')}`"
|
||||||
:value="COMPANY_SIZE_20_99"
|
:value="COMPANY_SIZE_20_99"
|
||||||
/>
|
/>
|
||||||
<n8n-option
|
<n8n-option
|
||||||
label="100-499 people"
|
:label="`100-499 ${$baseText('personalizationModal.people')}`"
|
||||||
:value="COMPANY_SIZE_100_499"
|
:value="COMPANY_SIZE_100_499"
|
||||||
/>
|
/>
|
||||||
<n8n-option
|
<n8n-option
|
||||||
label="500-999 people"
|
:label="`500-999 ${$baseText('personalizationModal.people')}`"
|
||||||
:value="COMPANY_SIZE_500_999"
|
:value="COMPANY_SIZE_500_999"
|
||||||
/>
|
/>
|
||||||
<n8n-option
|
<n8n-option
|
||||||
label="1000+ people"
|
:label="`1000+ ${$baseText('personalizationModal.people')}`"
|
||||||
:value="COMPANY_SIZE_1000_OR_MORE"
|
:value="COMPANY_SIZE_1000_OR_MORE"
|
||||||
/>
|
/>
|
||||||
<n8n-option
|
<n8n-option
|
||||||
label="I'm not using n8n for work"
|
:label="$baseText('personalizationModal.imNotUsingN8nForWork')"
|
||||||
:value="COMPANY_SIZE_PERSONAL_USE"
|
:value="COMPANY_SIZE_PERSONAL_USE"
|
||||||
/>
|
/>
|
||||||
</n8n-select>
|
</n8n-select>
|
||||||
|
@ -103,8 +103,8 @@
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:footer>
|
<template v-slot:footer>
|
||||||
<div>
|
<div>
|
||||||
<n8n-button v-if="submitted" @click="closeDialog" label="Get started" float="right" />
|
<n8n-button v-if="submitted" @click="closeDialog" :label="$baseText('personalizationModal.getStarted')" float="right" />
|
||||||
<n8n-button v-else @click="save" :loading="isSaving" label="Continue" float="right" />
|
<n8n-button v-else @click="save" :loading="isSaving" :label="$baseText('personalizationModal.continue')" float="right" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -140,12 +140,13 @@ import {
|
||||||
} from "../constants";
|
} from "../constants";
|
||||||
import { workflowHelpers } from "@/components/mixins/workflowHelpers";
|
import { workflowHelpers } from "@/components/mixins/workflowHelpers";
|
||||||
import { showMessage } from "@/components/mixins/showMessage";
|
import { showMessage } from "@/components/mixins/showMessage";
|
||||||
|
import { renderText } from "@/components/mixins/renderText";
|
||||||
import Modal from "./Modal.vue";
|
import Modal from "./Modal.vue";
|
||||||
import { IPersonalizationSurveyAnswers, IPersonalizationSurveyKeys } from "@/Interface";
|
import { IPersonalizationSurveyAnswers, IPersonalizationSurveyKeys } from "@/Interface";
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import { mapGetters } from "vuex";
|
import { mapGetters } from "vuex";
|
||||||
|
|
||||||
export default mixins(showMessage, workflowHelpers).extend({
|
export default mixins(showMessage, renderText, workflowHelpers).extend({
|
||||||
components: { Modal },
|
components: { Modal },
|
||||||
name: "PersonalizationModal",
|
name: "PersonalizationModal",
|
||||||
data() {
|
data() {
|
||||||
|
@ -217,7 +218,7 @@ export default mixins(showMessage, workflowHelpers).extend({
|
||||||
|
|
||||||
this.submitted = true;
|
this.submitted = true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.$showError(e, 'Error while submitting results');
|
this.$showError(e, this.$baseText('personalizationModal.errorWhileSubmittingResults'));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$data.isSaving = false;
|
this.$data.isSaving = false;
|
||||||
|
|
|
@ -3,12 +3,10 @@
|
||||||
<div class="push-connection-lost primary-color" v-if="!pushConnectionActive">
|
<div class="push-connection-lost primary-color" v-if="!pushConnectionActive">
|
||||||
<n8n-tooltip placement="bottom-end" >
|
<n8n-tooltip placement="bottom-end" >
|
||||||
<div slot="content">
|
<div slot="content">
|
||||||
Cannot connect to server.<br />
|
{{ $baseText('pushConnectionTracker.cannotConnectToServer') }}
|
||||||
It is either down or you have a connection issue. <br />
|
|
||||||
It should reconnect automatically once the issue is resolved.
|
|
||||||
</div>
|
</div>
|
||||||
<span>
|
<span>
|
||||||
<font-awesome-icon icon="exclamation-triangle" /> Connection lost
|
<font-awesome-icon icon="exclamation-triangle" /> {{ $baseText('pushConnectionTracker.connectionLost') }}
|
||||||
</span>
|
</span>
|
||||||
</n8n-tooltip>
|
</n8n-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -20,7 +18,10 @@
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import { mapGetters } from "vuex";
|
import { mapGetters } from "vuex";
|
||||||
|
|
||||||
export default Vue.extend({
|
import mixins from 'vue-typed-mixins';
|
||||||
|
import { renderText } from "./mixins/renderText";
|
||||||
|
|
||||||
|
export default mixins(renderText).extend({
|
||||||
name: "PushConnectionTracker",
|
name: "PushConnectionTracker",
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(["pushConnectionActive"]),
|
...mapGetters(["pushConnectionActive"]),
|
||||||
|
|
|
@ -7,10 +7,10 @@
|
||||||
class="execute-node-button"
|
class="execute-node-button"
|
||||||
>
|
>
|
||||||
<n8n-button
|
<n8n-button
|
||||||
:title="`Executes this ${node.name} node after executing any previous nodes that have not yet returned data`"
|
:title="$baseText('runData.executesThisNodeAfterExecuting', { interpolate: { nodeName: node.name } })"
|
||||||
:loading="workflowRunning"
|
:loading="workflowRunning"
|
||||||
icon="play-circle"
|
icon="play-circle"
|
||||||
label="Execute Node"
|
:label="$baseText('runData.executeNode')"
|
||||||
@click.stop="runWorkflow(node.name, 'RunData.ExecuteNodeButton')"
|
@click.stop="runWorkflow(node.name, 'RunData.ExecuteNodeButton')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -18,10 +18,10 @@
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="title-text">
|
<div class="title-text">
|
||||||
<n8n-text :bold="true" v-if="dataCount < maxDisplayItems">
|
<n8n-text :bold="true" v-if="dataCount < maxDisplayItems">
|
||||||
Items: {{ dataCount }}
|
{{ $baseText('runData.items') }}: {{ dataCount }}
|
||||||
</n8n-text>
|
</n8n-text>
|
||||||
<div v-else class="title-text">
|
<div v-else class="title-text">
|
||||||
<n8n-text :bold="true">Items:</n8n-text>
|
<n8n-text :bold="true">{{ $baseText('runData.items') }}:</n8n-text>
|
||||||
<span class="opts">
|
<span class="opts">
|
||||||
<n8n-select size="mini" v-model="maxDisplayItems" @click.stop>
|
<n8n-select size="mini" v-model="maxDisplayItems" @click.stop>
|
||||||
<n8n-option v-for="option in maxDisplayItemsOptions" :label="option" :value="option" :key="option" />
|
<n8n-option v-for="option in maxDisplayItemsOptions" :label="option" :value="option" :key="option" />
|
||||||
|
@ -34,13 +34,13 @@
|
||||||
placement="right"
|
placement="right"
|
||||||
>
|
>
|
||||||
<div slot="content">
|
<div slot="content">
|
||||||
<n8n-text :bold="true" size="small">Start Time:</n8n-text> {{runMetadata.startTime}}<br/>
|
<n8n-text :bold="true" size="small">{{ $baseText('runData.startTime') + ':' }}</n8n-text> {{runMetadata.startTime}}<br/>
|
||||||
<n8n-text :bold="true" size="small">Execution Time:</n8n-text> {{runMetadata.executionTime}} ms
|
<n8n-text :bold="true" size="small">{{ $baseText('runData.executionTime') + ':' }}</n8n-text> {{runMetadata.executionTime}} {{ $baseText('runData.ms') }}
|
||||||
</div>
|
</div>
|
||||||
<font-awesome-icon icon="info-circle" class="primary-color" />
|
<font-awesome-icon icon="info-circle" class="primary-color" />
|
||||||
</n8n-tooltip>
|
</n8n-tooltip>
|
||||||
<n8n-text :bold="true" v-if="maxOutputIndex > 0">
|
<n8n-text :bold="true" v-if="maxOutputIndex > 0">
|
||||||
| Output:
|
| {{ $baseText('runData.output') }}:
|
||||||
</n8n-text>
|
</n8n-text>
|
||||||
<span class="opts" v-if="maxOutputIndex > 0" >
|
<span class="opts" v-if="maxOutputIndex > 0" >
|
||||||
<n8n-select size="mini" v-model="outputIndex" @click.stop>
|
<n8n-select size="mini" v-model="outputIndex" @click.stop>
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<n8n-text :bold="true" v-if="maxRunIndex > 0">
|
<n8n-text :bold="true" v-if="maxRunIndex > 0">
|
||||||
| Data of Execution:
|
| {{ $baseText('runData.dataOfExecution') }}:
|
||||||
</n8n-text>
|
</n8n-text>
|
||||||
<span class="opts">
|
<span class="opts">
|
||||||
<n8n-select v-if="maxRunIndex > 0" size="mini" v-model="runIndex" @click.stop>
|
<n8n-select v-if="maxRunIndex > 0" size="mini" v-model="runIndex" @click.stop>
|
||||||
|
@ -62,20 +62,26 @@
|
||||||
</div>
|
</div>
|
||||||
<div v-if="hasNodeRun && !hasRunError" class="title-data-display-selector" @click.stop>
|
<div v-if="hasNodeRun && !hasRunError" class="title-data-display-selector" @click.stop>
|
||||||
<el-radio-group v-model="displayMode" size="mini">
|
<el-radio-group v-model="displayMode" size="mini">
|
||||||
<el-radio-button label="JSON" :disabled="showData === false"></el-radio-button>
|
<el-radio-button :label="$baseText('runData.json')" :disabled="showData === false"></el-radio-button>
|
||||||
<el-radio-button label="Table"></el-radio-button>
|
<el-radio-button :label="$baseText('runData.table')"></el-radio-button>
|
||||||
<el-radio-button label="Binary" v-if="binaryData.length !== 0"></el-radio-button>
|
<el-radio-button :label="$baseText('runData.binary')" v-if="binaryData.length !== 0"></el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="hasNodeRun && !hasRunError && displayMode === 'JSON' && state.path !== deselectedPlaceholder" class="select-button">
|
<div v-if="hasNodeRun && !hasRunError && displayMode === 'JSON' && state.path !== deselectedPlaceholder" class="select-button">
|
||||||
<el-dropdown trigger="click" @command="handleCopyClick">
|
<el-dropdown trigger="click" @command="handleCopyClick">
|
||||||
<span class="el-dropdown-link">
|
<span class="el-dropdown-link">
|
||||||
<n8n-icon-button title="Copy to Clipboard" icon="copy" />
|
<n8n-icon-button :title="$baseText('runData.copyToClipboard')" icon="copy" />
|
||||||
</span>
|
</span>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item :command="{command: 'itemPath'}">Copy Item Path</el-dropdown-item>
|
<el-dropdown-item :command="{command: 'itemPath'}">
|
||||||
<el-dropdown-item :command="{command: 'parameterPath'}">Copy Parameter Path</el-dropdown-item>
|
{{ $baseText('runData.copyItemPath') }}
|
||||||
<el-dropdown-item :command="{command: 'value'}">Copy Value</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item :command="{command: 'parameterPath'}">
|
||||||
|
{{ $baseText('runData.copyParameterPath') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item :command="{command: 'value'}">
|
||||||
|
{{ $baseText('runData.copyValue') }}
|
||||||
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
@ -88,29 +94,33 @@
|
||||||
<span v-else>
|
<span v-else>
|
||||||
<div v-if="showData === false" class="too-much-data">
|
<div v-if="showData === false" class="too-much-data">
|
||||||
<h3>
|
<h3>
|
||||||
Node returned a large amount of data
|
{{ $baseText('runData.nodeReturnedALargeAmountOfData') }}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div class="text">
|
<div class="text">
|
||||||
The node contains {{parseInt(dataSize/1024).toLocaleString()}} KB of data.<br />
|
{{ $baseTeext(
|
||||||
Displaying it could cause problems!<br />
|
'runData.theNodeContains',
|
||||||
<br />
|
{
|
||||||
If you do decide to display it, avoid the JSON view!
|
interpolate: {
|
||||||
|
numberOfKb: parseInt(dataSize/1024).toLocaleString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<n8n-button
|
<n8n-button
|
||||||
icon="eye"
|
icon="eye"
|
||||||
label="Display Data Anyway"
|
:label="$baseText('runData.displayDataAnyway')"
|
||||||
@click="displayMode = 'Table';showData = true;"
|
@click="displayMode = 'Table';showData = true;"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="['JSON', 'Table'].includes(displayMode)">
|
<div v-else-if="['JSON', 'Table'].includes(displayMode)">
|
||||||
<div v-if="jsonData.length === 0" class="no-data">
|
<div v-if="jsonData.length === 0" class="no-data">
|
||||||
No text data found
|
{{ $baseText('runData.noTextDataFound') }}
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="displayMode === 'Table'">
|
<div v-else-if="displayMode === 'Table'">
|
||||||
<div v-if="tableData !== null && tableData.columns.length === 0" class="no-data">
|
<div v-if="tableData !== null && tableData.columns.length === 0" class="no-data">
|
||||||
Entries exist but they do not contain any JSON data.
|
{{ $baseText('runData.entriesExistButThey') }}
|
||||||
</div>
|
</div>
|
||||||
<table v-else-if="tableData !== null">
|
<table v-else-if="tableData !== null">
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -138,7 +148,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="displayMode === 'Binary'">
|
<div v-else-if="displayMode === 'Binary'">
|
||||||
<div v-if="binaryData.length === 0" class="no-data">
|
<div v-if="binaryData.length === 0" class="no-data">
|
||||||
No binary data found
|
{{ $baseText('runData.noBinaryDataFound') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
@ -156,24 +166,24 @@
|
||||||
{{key}}
|
{{key}}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="binaryData.fileName">
|
<div v-if="binaryData.fileName">
|
||||||
<div class="label">File Name: </div>
|
<div class="label">{{ $baseText('runData.fileName') }}: </div>
|
||||||
<div class="value">{{binaryData.fileName}}</div>
|
<div class="value">{{binaryData.fileName}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="binaryData.directory">
|
<div v-if="binaryData.directory">
|
||||||
<div class="label">Directory: </div>
|
<div class="label">{{ $baseText('runData.directory') }}: </div>
|
||||||
<div class="value">{{binaryData.directory}}</div>
|
<div class="value">{{binaryData.directory}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="binaryData.fileExtension">
|
<div v-if="binaryData.fileExtension">
|
||||||
<div class="label">File Extension:</div>
|
<div class="label">{{ $baseText('runData.fileExtension') }}:</div>
|
||||||
<div class="value">{{binaryData.fileExtension}}</div>
|
<div class="value">{{binaryData.fileExtension}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="binaryData.mimeType">
|
<div v-if="binaryData.mimeType">
|
||||||
<div class="label">Mime Type: </div>
|
<div class="label">{{ $baseText('runData.mimeType') }}: </div>
|
||||||
<div class="value">{{binaryData.mimeType}}</div>
|
<div class="value">{{binaryData.mimeType}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="binary-data-show-data-button-wrapper">
|
<div class="binary-data-show-data-button-wrapper">
|
||||||
<n8n-button size="small" label="Show Binary Data" class="binary-data-show-data-button" @click="displayBinaryData(index, key)" />
|
<n8n-button size="small" :label="$baseText('runData.showBinaryData')" class="binary-data-show-data-button" @click="displayBinaryData(index, key)" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -186,9 +196,9 @@
|
||||||
</span>
|
</span>
|
||||||
<div v-else class="message">
|
<div v-else class="message">
|
||||||
<div>
|
<div>
|
||||||
<n8n-text :bold="true">No data</n8n-text ><br />
|
<n8n-text :bold="true">{{ $baseText('runData.noData') }}</n8n-text ><br />
|
||||||
<br />
|
<br />
|
||||||
Data returned by this node will display here<br />
|
{{ $baseText('runData.dataReturnedByThisNodeWillDisplayHere') }}<br />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -221,7 +231,7 @@ import {
|
||||||
} from '@/constants';
|
} from '@/constants';
|
||||||
|
|
||||||
import BinaryDataDisplay from '@/components/BinaryDataDisplay.vue';
|
import BinaryDataDisplay from '@/components/BinaryDataDisplay.vue';
|
||||||
import NodeErrorView from '@/components/Error/NodeViewError.vue';
|
import NodeErrorView from '@/components/Error/NodeErrorView.vue';
|
||||||
|
|
||||||
import { copyPaste } from '@/components/mixins/copyPaste';
|
import { copyPaste } from '@/components/mixins/copyPaste';
|
||||||
import { externalHooks } from "@/components/mixins/externalHooks";
|
import { externalHooks } from "@/components/mixins/externalHooks";
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<span :class="$style.container">
|
<span :class="$style.container">
|
||||||
<span :class="$style.saved" v-if="saved">{{ savedLabel }}</span>
|
<span :class="$style.saved" v-if="saved">{{ $baseText('saveButton.saved') }}</span>
|
||||||
<n8n-button
|
<n8n-button
|
||||||
v-else
|
v-else
|
||||||
:label="isSaving ? savingLabel : saveLabel"
|
:label="saveButtonLabel"
|
||||||
:loading="isSaving"
|
:loading="isSaving"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
@click="$emit('click')"
|
@click="$emit('click')"
|
||||||
|
@ -13,8 +13,10 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
|
import { renderText } from './mixins/renderText';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default mixins(renderText).extend({
|
||||||
name: "SaveButton",
|
name: "SaveButton",
|
||||||
props: {
|
props: {
|
||||||
saved: {
|
saved: {
|
||||||
|
@ -28,15 +30,19 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
saveLabel: {
|
saveLabel: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'Save',
|
|
||||||
},
|
},
|
||||||
savingLabel: {
|
savingLabel: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'Saving',
|
|
||||||
},
|
},
|
||||||
savedLabel: {
|
savedLabel: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'Saved',
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
saveButtonLabel() {
|
||||||
|
return this.isSaving
|
||||||
|
? this.$baseText('saveButton.saving')
|
||||||
|
: this.$baseText('saveButton.save');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,12 +24,14 @@
|
||||||
ref="create"
|
ref="create"
|
||||||
>
|
>
|
||||||
<font-awesome-icon icon="plus-circle" />
|
<font-awesome-icon icon="plus-circle" />
|
||||||
<span>Create tag "{{ filter }}"</span>
|
<span>
|
||||||
|
{{ $baseText('tagsDropdown.createTag', { interpolate: { filter } }) }}
|
||||||
|
</span>
|
||||||
</n8n-option>
|
</n8n-option>
|
||||||
<n8n-option v-else-if="options.length === 0" value="message" disabled>
|
<n8n-option v-else-if="options.length === 0" value="message" disabled>
|
||||||
<span v-if="createEnabled">Type to create a tag</span>
|
<span v-if="createEnabled">{{ $baseText('tagsDropdown.typeToCreateATag') }}</span>
|
||||||
<span v-else-if="allTags.length > 0">No matching tags exist</span>
|
<span v-else-if="allTags.length > 0">{{ $baseText('tagsDropdown.noMatchingTagsExist') }}</span>
|
||||||
<span v-else>No tags exist</span>
|
<span v-else>{{ $baseText('tagsDropdown.noTagsExist') }}</span>
|
||||||
</n8n-option>
|
</n8n-option>
|
||||||
|
|
||||||
<!-- key is id+index for keyboard navigation to work well with filter -->
|
<!-- key is id+index for keyboard navigation to work well with filter -->
|
||||||
|
@ -44,7 +46,7 @@
|
||||||
|
|
||||||
<n8n-option :key="MANAGE_KEY" :value="MANAGE_KEY" class="ops manage-tags">
|
<n8n-option :key="MANAGE_KEY" :value="MANAGE_KEY" class="ops manage-tags">
|
||||||
<font-awesome-icon icon="cog" />
|
<font-awesome-icon icon="cog" />
|
||||||
<span>Manage tags</span>
|
<span>{{ $baseText('tagsDropdown.manageTags') }}</span>
|
||||||
</n8n-option>
|
</n8n-option>
|
||||||
</n8n-select>
|
</n8n-select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,11 +60,12 @@ import { ITag } from "@/Interface";
|
||||||
import { MAX_TAG_NAME_LENGTH, TAGS_MANAGER_MODAL_KEY } from "@/constants";
|
import { MAX_TAG_NAME_LENGTH, TAGS_MANAGER_MODAL_KEY } from "@/constants";
|
||||||
|
|
||||||
import { showMessage } from "@/components/mixins/showMessage";
|
import { showMessage } from "@/components/mixins/showMessage";
|
||||||
|
import { renderText } from "@/components/mixins/renderText";
|
||||||
|
|
||||||
const MANAGE_KEY = "__manage";
|
const MANAGE_KEY = "__manage";
|
||||||
const CREATE_KEY = "__create";
|
const CREATE_KEY = "__create";
|
||||||
|
|
||||||
export default mixins(showMessage).extend({
|
export default mixins(showMessage, renderText).extend({
|
||||||
name: "TagsDropdown",
|
name: "TagsDropdown",
|
||||||
props: ["placeholder", "currentTagIds", "createEnabled", "eventBus"],
|
props: ["placeholder", "currentTagIds", "createEnabled", "eventBus"],
|
||||||
data() {
|
data() {
|
||||||
|
@ -139,8 +142,11 @@ export default mixins(showMessage).extend({
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(
|
this.$showError(
|
||||||
error,
|
error,
|
||||||
"New tag was not created",
|
this.$baseText('tagsDropdown.showError.title'),
|
||||||
`A problem occurred when trying to create the "${name}" tag`,
|
this.$baseText(
|
||||||
|
'tagsDropdown.showError.message',
|
||||||
|
{ interpolate: { name } },
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,10 +3,9 @@
|
||||||
<el-col class="notags" :span="16">
|
<el-col class="notags" :span="16">
|
||||||
<div class="icon">🗄️</div>
|
<div class="icon">🗄️</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="headline">Ready to organize your workflows?</div>
|
<div class="headline">{{ $baseText('noTagsView.readyToOrganizeYourWorkflows') }}</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
With workflow tags, you're free to create the perfect tagging system for
|
{{ $baseText('noTagsView.withWorkflowTagsYouReFree') }}
|
||||||
your flows
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<n8n-button label="Create a tag" size="large" @click="$emit('enableCreate')" />
|
<n8n-button label="Create a tag" size="large" @click="$emit('enableCreate')" />
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<Modal
|
<Modal
|
||||||
title="Manage tags"
|
:title="$baseText('tagsManager.manageTags')"
|
||||||
:name="TAGS_MANAGER_MODAL_KEY"
|
:name="TAGS_MANAGER_MODAL_KEY"
|
||||||
:eventBus="modalBus"
|
:eventBus="modalBus"
|
||||||
@enter="onEnter"
|
@enter="onEnter"
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:footer="{ close }">
|
<template v-slot:footer="{ close }">
|
||||||
<n8n-button label="Done" @click="close" float="right" />
|
<n8n-button :label="$baseText('tagsManager.done')" @click="close" float="right" />
|
||||||
</template>
|
</template>
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
@ -38,12 +38,13 @@ import { mapGetters } from "vuex";
|
||||||
import { ITag } from "@/Interface";
|
import { ITag } from "@/Interface";
|
||||||
|
|
||||||
import { showMessage } from "@/components/mixins/showMessage";
|
import { showMessage } from "@/components/mixins/showMessage";
|
||||||
|
import { renderText } from "@/components/mixins/renderText";
|
||||||
import TagsView from "@/components/TagsManager/TagsView/TagsView.vue";
|
import TagsView from "@/components/TagsManager/TagsView/TagsView.vue";
|
||||||
import NoTagsView from "@/components/TagsManager/NoTagsView.vue";
|
import NoTagsView from "@/components/TagsManager/NoTagsView.vue";
|
||||||
import Modal from "@/components/Modal.vue";
|
import Modal from "@/components/Modal.vue";
|
||||||
import { TAGS_MANAGER_MODAL_KEY } from '../../constants';
|
import { TAGS_MANAGER_MODAL_KEY } from '../../constants';
|
||||||
|
|
||||||
export default mixins(showMessage).extend({
|
export default mixins(renderText, showMessage).extend({
|
||||||
name: "TagsManager",
|
name: "TagsManager",
|
||||||
created() {
|
created() {
|
||||||
this.$store.dispatch("tags/fetchAll", {force: true, withUsageCount: true});
|
this.$store.dispatch("tags/fetchAll", {force: true, withUsageCount: true});
|
||||||
|
@ -86,7 +87,9 @@ export default mixins(showMessage).extend({
|
||||||
async onCreate(name: string, cb: (tag: ITag | null, error?: Error) => void) {
|
async onCreate(name: string, cb: (tag: ITag | null, error?: Error) => void) {
|
||||||
try {
|
try {
|
||||||
if (!name) {
|
if (!name) {
|
||||||
throw new Error("Tag name cannot be empty");
|
throw new Error(
|
||||||
|
this.$baseText('tagsManager.tagNameCannotBeEmpty'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newTag = await this.$store.dispatch("tags/create", name);
|
const newTag = await this.$store.dispatch("tags/create", name);
|
||||||
|
@ -96,8 +99,11 @@ export default mixins(showMessage).extend({
|
||||||
const escapedName = escape(name);
|
const escapedName = escape(name);
|
||||||
this.$showError(
|
this.$showError(
|
||||||
error,
|
error,
|
||||||
"New tag was not created",
|
this.$baseText('tagsManager.showError.onCreate.title'),
|
||||||
`A problem occurred when trying to create the "${escapedName}" tag`,
|
this.$baseText(
|
||||||
|
'tagsManager.showError.onCreate.message',
|
||||||
|
{ interpolate: { escapedName } },
|
||||||
|
) + ':',
|
||||||
);
|
);
|
||||||
cb(null, error);
|
cb(null, error);
|
||||||
}
|
}
|
||||||
|
@ -109,7 +115,9 @@ export default mixins(showMessage).extend({
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!name) {
|
if (!name) {
|
||||||
throw new Error("Tag name cannot be empty");
|
throw new Error(
|
||||||
|
this.$baseText('tagsManager.tagNameCannotBeEmpty'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === oldName) {
|
if (name === oldName) {
|
||||||
|
@ -124,16 +132,22 @@ export default mixins(showMessage).extend({
|
||||||
const escapedOldName = escape(oldName);
|
const escapedOldName = escape(oldName);
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: "Tag was updated",
|
title: this.$baseText('tagsManager.showMessage.onUpdate.title'),
|
||||||
message: `The "${escapedOldName}" tag was successfully updated to "${escapedName}"`,
|
message: this.$baseText(
|
||||||
|
'tagsManager.showMessage.onUpdate.message',
|
||||||
|
{ interpolate: { escapedName, escapedOldName } },
|
||||||
|
),
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const escapedName = escape(oldName);
|
const escapedName = escape(oldName);
|
||||||
this.$showError(
|
this.$showError(
|
||||||
error,
|
error,
|
||||||
"Tag was not updated",
|
this.$baseText('tagsManager.showError.onUpdate.title'),
|
||||||
`A problem occurred when trying to update the "${escapedName}" tag`,
|
this.$baseText(
|
||||||
|
'tagsManager.showError.onUpdate.message',
|
||||||
|
{ interpolate: { escapedName } },
|
||||||
|
) + ':',
|
||||||
);
|
);
|
||||||
cb(false, error);
|
cb(false, error);
|
||||||
}
|
}
|
||||||
|
@ -146,7 +160,9 @@ export default mixins(showMessage).extend({
|
||||||
try {
|
try {
|
||||||
const deleted = await this.$store.dispatch("tags/delete", id);
|
const deleted = await this.$store.dispatch("tags/delete", id);
|
||||||
if (!deleted) {
|
if (!deleted) {
|
||||||
throw new Error('Could not delete tag');
|
throw new Error(
|
||||||
|
this.$baseText('tagsManager.couldNotDeleteTag'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$data.tagIds = this.$data.tagIds.filter((tagId: string) => tagId !== id);
|
this.$data.tagIds = this.$data.tagIds.filter((tagId: string) => tagId !== id);
|
||||||
|
@ -155,16 +171,22 @@ export default mixins(showMessage).extend({
|
||||||
|
|
||||||
const escapedName = escape(name);
|
const escapedName = escape(name);
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: "Tag was deleted",
|
title: this.$baseText('tagsManager.showMessage.onDelete.title'),
|
||||||
message: `The "${escapedName}" tag was successfully deleted from your tag collection`,
|
message: this.$baseText(
|
||||||
|
'tagsManager.showMessage.onDelete.message',
|
||||||
|
{ interpolate: { escapedName } },
|
||||||
|
),
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const escapedName = escape(name);
|
const escapedName = escape(name);
|
||||||
this.$showError(
|
this.$showError(
|
||||||
error,
|
error,
|
||||||
"Tag was not deleted",
|
this.$baseText('tagsManager.showError.onDelete.title'),
|
||||||
`A problem occurred when trying to delete the "${escapedName}" tag`,
|
this.$baseText(
|
||||||
|
'tagsManager.showError.onDelete.message',
|
||||||
|
{ interpolate: { escapedName } },
|
||||||
|
) + ':',
|
||||||
);
|
);
|
||||||
cb(false, error);
|
cb(false, error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
stripe
|
stripe
|
||||||
max-height="450"
|
max-height="450"
|
||||||
ref="table"
|
ref="table"
|
||||||
empty-text="No matching tags exist"
|
:empty-text="$baseText('tagsTable.noMatchingTagsExist')"
|
||||||
:data="rows"
|
:data="rows"
|
||||||
:span-method="getSpan"
|
:span-method="getSpan"
|
||||||
:row-class-name="getRowClasses"
|
:row-class-name="getRowClasses"
|
||||||
v-loading="isLoading"
|
v-loading="isLoading"
|
||||||
>
|
>
|
||||||
<el-table-column label="Name">
|
<el-table-column :label="$baseText('tagsTable.name')">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<div class="name" :key="scope.row.id" @keydown.stop>
|
<div class="name" :key="scope.row.id" @keydown.stop>
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
ref="nameInput"
|
ref="nameInput"
|
||||||
></n8n-input>
|
></n8n-input>
|
||||||
<span v-else-if="scope.row.delete">
|
<span v-else-if="scope.row.delete">
|
||||||
<span>Are you sure you want to delete this tag?</span>
|
<span>{{ $baseText('tagsTable.areYouSureYouWantToDeleteThisTag') }}</span>
|
||||||
<input ref="deleteHiddenInput" class="hidden" />
|
<input ref="deleteHiddenInput" class="hidden" />
|
||||||
</span>
|
</span>
|
||||||
<span v-else :class="{ disabled: scope.row.disable }">
|
<span v-else :class="{ disabled: scope.row.disable }">
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="Usage" width="150">
|
<el-table-column :label="$baseText('tagsTable.usage')" width="150">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
<div v-if="!scope.row.create && !scope.row.delete" :class="{ disabled: scope.row.disable }">
|
<div v-if="!scope.row.create && !scope.row.delete" :class="{ disabled: scope.row.disable }">
|
||||||
|
@ -44,20 +44,20 @@
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
<div class="ops" v-if="scope.row.create">
|
<div class="ops" v-if="scope.row.create">
|
||||||
<n8n-button label="Cancel" @click.stop="cancel" type="outline" :disabled="isSaving" />
|
<n8n-button :label="$baseText('tagsTable.cancel')" @click.stop="cancel" type="outline" :disabled="isSaving" />
|
||||||
<n8n-button label="Create tag" @click.stop="apply" :loading="isSaving" />
|
<n8n-button :label="$baseText('tagsTable.createTag')" @click.stop="apply" :loading="isSaving" />
|
||||||
</div>
|
</div>
|
||||||
<div class="ops" v-else-if="scope.row.update">
|
<div class="ops" v-else-if="scope.row.update">
|
||||||
<n8n-button label="Cancel" @click.stop="cancel" type="outline" :disabled="isSaving" />
|
<n8n-button :label="$baseText('tagsTable.cancel')" @click.stop="cancel" type="outline" :disabled="isSaving" />
|
||||||
<n8n-button label="Save changes" @click.stop="apply" :loading="isSaving" />
|
<n8n-button :label="$baseText('tagsTable.saveChanges')" @click.stop="apply" :loading="isSaving" />
|
||||||
</div>
|
</div>
|
||||||
<div class="ops" v-else-if="scope.row.delete">
|
<div class="ops" v-else-if="scope.row.delete">
|
||||||
<n8n-button label="Cancel" @click.stop="cancel" type="outline" :disabled="isSaving" />
|
<n8n-button :label="$baseText('tagsTable.cancel')" @click.stop="cancel" type="outline" :disabled="isSaving" />
|
||||||
<n8n-button label="Delete tag" @click.stop="apply" :loading="isSaving" />
|
<n8n-button :label="$baseText('tagsTable.deleteTag')" @click.stop="apply" :loading="isSaving" />
|
||||||
</div>
|
</div>
|
||||||
<div class="ops main" v-else-if="!scope.row.disable">
|
<div class="ops main" v-else-if="!scope.row.disable">
|
||||||
<n8n-icon-button title="Edit Tag" @click.stop="enableUpdate(scope.row)" icon="pen" />
|
<n8n-icon-button :title="$baseText('tagsTable.editTag')" @click.stop="enableUpdate(scope.row)" icon="pen" />
|
||||||
<n8n-icon-button title="Delete Tag" @click.stop="enableDelete(scope.row)" icon="trash" />
|
<n8n-icon-button :title="$baseText('tagsTable.deleteTag')" @click.stop="enableDelete(scope.row)" icon="trash" />
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
|
@ -66,14 +66,16 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { renderText } from "@/components/mixins/renderText";
|
||||||
import { MAX_TAG_NAME_LENGTH } from "@/constants";
|
import { MAX_TAG_NAME_LENGTH } from "@/constants";
|
||||||
import { ITagRow } from "@/Interface";
|
import { ITagRow } from "@/Interface";
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
const INPUT_TRANSITION_TIMEOUT = 350;
|
const INPUT_TRANSITION_TIMEOUT = 350;
|
||||||
const DELETE_TRANSITION_TIMEOUT = 100;
|
const DELETE_TRANSITION_TIMEOUT = 100;
|
||||||
|
|
||||||
export default Vue.extend({
|
export default mixins(renderText).extend({
|
||||||
name: "TagsTable",
|
name: "TagsTable",
|
||||||
props: ["rows", "isLoading", "newName", "isSaving"],
|
props: ["rows", "isLoading", "newName", "isSaving"],
|
||||||
data() {
|
data() {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<el-row class="tags-header">
|
<el-row class="tags-header">
|
||||||
<el-col :span="10">
|
<el-col :span="10">
|
||||||
<n8n-input
|
<n8n-input
|
||||||
placeholder="Search tags"
|
:placeholder="$baseText('tagsTableHeader.searchTags')"
|
||||||
:value="search"
|
:value="search"
|
||||||
@input="onSearchChange"
|
@input="onSearchChange"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
|
@ -13,15 +13,18 @@
|
||||||
</n8n-input>
|
</n8n-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="14">
|
<el-col :span="14">
|
||||||
<n8n-button @click="onAddNew" :disabled="disabled" icon="plus" label="Add new" size="large" float="right" />
|
<n8n-button @click="onAddNew" :disabled="disabled" icon="plus" :label="$baseText('tagsTableHeader.addNew')" size="large" float="right" />
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { renderText } from "@/components/mixins/renderText";
|
||||||
import { MAX_TAG_NAME_LENGTH } from "@/constants";
|
import { MAX_TAG_NAME_LENGTH } from "@/constants";
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
export default Vue.extend({
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
|
export default mixins(renderText).extend({
|
||||||
props: {
|
props: {
|
||||||
disabled: {
|
disabled: {
|
||||||
default: false,
|
default: false,
|
||||||
|
|
|
@ -31,11 +31,12 @@ import Vue from "vue";
|
||||||
import { ITag, ITagRow } from "@/Interface";
|
import { ITag, ITagRow } from "@/Interface";
|
||||||
import TagsTableHeader from "@/components/TagsManager/TagsView/TagsTableHeader.vue";
|
import TagsTableHeader from "@/components/TagsManager/TagsView/TagsTableHeader.vue";
|
||||||
import TagsTable from "@/components/TagsManager/TagsView/TagsTable.vue";
|
import TagsTable from "@/components/TagsManager/TagsView/TagsTable.vue";
|
||||||
|
import mixins from "vue-typed-mixins";
|
||||||
|
import { renderText } from "@/components/mixins/renderText";
|
||||||
|
|
||||||
const matches = (name: string, filter: string) => name.toLowerCase().trim().includes(filter.toLowerCase().trim());
|
const matches = (name: string, filter: string) => name.toLowerCase().trim().includes(filter.toLowerCase().trim());
|
||||||
const getUsage = (count: number | undefined) => count && count > 0 ? `${count} workflow${count > 1 ? "s" : ""}` : 'Not being used';
|
|
||||||
|
|
||||||
export default Vue.extend({
|
export default mixins(renderText).extend({
|
||||||
components: { TagsTableHeader, TagsTable },
|
components: { TagsTableHeader, TagsTable },
|
||||||
name: "TagsView",
|
name: "TagsView",
|
||||||
props: ["tags", "isLoading"],
|
props: ["tags", "isLoading"],
|
||||||
|
@ -55,6 +56,18 @@ export default Vue.extend({
|
||||||
return (this.$props.tags || []).length === 0 || this.$data.createEnabled;
|
return (this.$props.tags || []).length === 0 || this.$data.createEnabled;
|
||||||
},
|
},
|
||||||
rows(): ITagRow[] {
|
rows(): ITagRow[] {
|
||||||
|
const getUsage = (count: number | undefined) => count && count > 0
|
||||||
|
? this.$baseText(
|
||||||
|
count > 1 ?
|
||||||
|
'tagsView.inUse.plural' : 'tagsView.inUse.singular',
|
||||||
|
{
|
||||||
|
interpolate: {
|
||||||
|
count: count.toString(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: this.$baseText('tagsView.notBeingUsed');
|
||||||
|
|
||||||
const disabled = this.isCreateEnabled || this.$data.updateId || this.$data.deleteId;
|
const disabled = this.isCreateEnabled || this.$data.updateId || this.$data.deleteId;
|
||||||
const tagRows = (this.$props.tags || [])
|
const tagRows = (this.$props.tags || [])
|
||||||
.filter((tag: ITag) => this.stickyIds.has(tag.id) || matches(tag.name, this.$data.search))
|
.filter((tag: ITag) => this.stickyIds.has(tag.id) || matches(tag.name, this.$data.search))
|
||||||
|
@ -102,7 +115,7 @@ export default Vue.extend({
|
||||||
this.stickyIds.add(this.updateId);
|
this.stickyIds.add(this.updateId);
|
||||||
this.disableUpdate();
|
this.disableUpdate();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$emit("update", this.updateId, name, onUpdate);
|
this.$emit("update", this.updateId, name, onUpdate);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,39 +1,19 @@
|
||||||
<template functional>
|
<template>
|
||||||
<span :title="$options.methods.convertToHumanReadableDate($props)">
|
<span :title="convertDate">
|
||||||
{{$options.methods.format(props)}}
|
{{ format }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { format, LocaleFunc, register } from 'timeago.js';
|
import { format, LocaleFunc, register } from 'timeago.js';
|
||||||
import { convertToHumanReadableDate } from './helpers';
|
import { convertToHumanReadableDate } from './helpers';
|
||||||
|
import Vue from 'vue';
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
|
import { renderText } from './mixins/renderText';
|
||||||
|
|
||||||
const localeFunc = (num: number, index: number, totalSec: number): [string, string] => {
|
export default mixins(renderText).extend({
|
||||||
// number: the timeago / timein number;
|
name: 'TimeAgo',
|
||||||
// index: the index of array below;
|
|
||||||
// totalSec: total seconds between date to be formatted and today's date;
|
|
||||||
return [
|
|
||||||
['Just now', 'Right now'],
|
|
||||||
['Just now', 'Right now'], // ['%s seconds ago', 'in %s seconds'],
|
|
||||||
['1 minute ago', 'in 1 minute'],
|
|
||||||
['%s minutes ago', 'in %s minutes'],
|
|
||||||
['1 hour ago', 'in 1 hour'],
|
|
||||||
['%s hours ago', 'in %s hours'],
|
|
||||||
['1 day ago', 'in 1 day'],
|
|
||||||
['%s days ago', 'in %s days'],
|
|
||||||
['1 week ago', 'in 1 week'],
|
|
||||||
['%s weeks ago', 'in %s weeks'],
|
|
||||||
['1 month ago', 'in 1 month'],
|
|
||||||
['%s months ago', 'in %s months'],
|
|
||||||
['1 year ago', 'in 1 year'],
|
|
||||||
['%s years ago', 'in %s years'],
|
|
||||||
][index] as [string, string];
|
|
||||||
};
|
|
||||||
|
|
||||||
register('main', localeFunc as LocaleFunc);
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'UpdatesPanel',
|
|
||||||
props: {
|
props: {
|
||||||
date: {
|
date: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@ -43,17 +23,48 @@ export default {
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
beforeMount() {
|
||||||
|
register(this.defaultLocale, this.localeFunc as LocaleFunc);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
format(props: {date: string, capitalize: boolean}) {
|
localeFunc(num: number, index: number, totalSec: number): [string, string] {
|
||||||
const text = format(props.date, 'main');
|
// number: the timeago / timein number;
|
||||||
|
// index: the index of array below;
|
||||||
|
// totalSec: total seconds between date to be formatted and today's date;
|
||||||
|
return [
|
||||||
|
[this.$baseText('timeAgo.justNow'), this.$baseText('timeAgo.rightNow')],
|
||||||
|
[this.$baseText('timeAgo.justNow'), this.$baseText('timeAgo.rightNow')], // ['%s seconds ago', 'in %s seconds'],
|
||||||
|
[this.$baseText('timeAgo.oneMinuteAgo'), this.$baseText('timeAgo.inOneMinute')],
|
||||||
|
[this.$baseText('timeAgo.minutesAgo'), this.$baseText('timeAgo.inMinutes')],
|
||||||
|
[this.$baseText('timeAgo.oneHourAgo'), this.$baseText('timeAgo.inOneHour')],
|
||||||
|
[this.$baseText('timeAgo.hoursAgo'), this.$baseText('timeAgo.inHours')],
|
||||||
|
[this.$baseText('timeAgo.oneDayAgo'), this.$baseText('timeAgo.inOneDay')],
|
||||||
|
[this.$baseText('timeAgo.daysAgo'), this.$baseText('timeAgo.inDays')],
|
||||||
|
[this.$baseText('timeAgo.oneWeekAgo'), this.$baseText('timeAgo.inOneWeek')],
|
||||||
|
[this.$baseText('timeAgo.weeksAgo'), this.$baseText('timeAgo.inWeeks')],
|
||||||
|
[this.$baseText('timeAgo.oneMonthAgo'), this.$baseText('timeAgo.inOneMonth')],
|
||||||
|
[this.$baseText('timeAgo.monthsAgo'), this.$baseText('timeAgo.inMonths')],
|
||||||
|
[this.$baseText('timeAgo.oneYearAgo'), this.$baseText('timeAgo.inOneYear')],
|
||||||
|
[this.$baseText('timeAgo.yearsAgo'), this.$baseText('timeAgo.inYears')],
|
||||||
|
][index] as [string, string];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['defaultLocale']),
|
||||||
|
format(): string {
|
||||||
|
const text = format(this.date, this.defaultLocale);
|
||||||
|
|
||||||
if (!props.capitalize) {
|
if (!this.capitalize) {
|
||||||
return text.toLowerCase();
|
return text.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
},
|
},
|
||||||
convertToHumanReadableDate,
|
convertDate(): string {
|
||||||
|
const date = new Date(this.date);
|
||||||
|
const epoch = date.getTime() / 1000;
|
||||||
|
return convertToHumanReadableDate(epoch);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,16 +5,26 @@
|
||||||
width="520px"
|
width="520px"
|
||||||
>
|
>
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<span :class="$style.title">We’ve been busy ✨</span>
|
<span :class="$style.title">
|
||||||
|
{{ $baseText('updatesPanel.weVeBeenBusy') }}
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<section :class="$style['description']">
|
<section :class="$style['description']">
|
||||||
|
|
||||||
<p v-if="currentVersion">
|
<p v-if="currentVersion">
|
||||||
You’re on {{ currentVersion.name }}, which was released
|
{{ $baseText(
|
||||||
<strong><TimeAgo :date="currentVersion.createdAt" /></strong> and is
|
'updatesPanel.youReOnVersion',
|
||||||
<strong>{{ nextVersions.length }} version{{nextVersions.length > 1 ? "s" : ""}}</strong>
|
{ interpolate: { currentVersionName: currentVersion.name } }
|
||||||
behind the latest and greatest n8n
|
) }}
|
||||||
|
<strong><TimeAgo :date="currentVersion.createdAt" /></strong>{{ $baseText('updatesPanel.andIs') }} <strong>{{ $baseText(
|
||||||
|
'updatesPanel.version',
|
||||||
|
{
|
||||||
|
interpolate: {
|
||||||
|
numberOfVersions: nextVersions.length,
|
||||||
|
howManySuffix: nextVersions.length > 1 ? "s" : "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)}}</strong> {{ $baseText('updatesPanel.behindTheLatest') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
|
@ -24,7 +34,9 @@
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<font-awesome-icon icon="info-circle"></font-awesome-icon>
|
<font-awesome-icon icon="info-circle"></font-awesome-icon>
|
||||||
<span>How to update your n8n version</span>
|
<span>
|
||||||
|
{{ $baseText('updatesPanel.howToUpdateYourN8nVersion') }}
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
@ -49,8 +61,10 @@ import ModalDrawer from './ModalDrawer.vue';
|
||||||
import TimeAgo from './TimeAgo.vue';
|
import TimeAgo from './TimeAgo.vue';
|
||||||
import VersionCard from './VersionCard.vue';
|
import VersionCard from './VersionCard.vue';
|
||||||
import { VERSIONS_MODAL_KEY } from '../constants';
|
import { VERSIONS_MODAL_KEY } from '../constants';
|
||||||
|
import { renderText } from './mixins/renderText';
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default mixins(renderText).extend({
|
||||||
name: 'UpdatesPanel',
|
name: 'UpdatesPanel',
|
||||||
components: {
|
components: {
|
||||||
ModalDrawer,
|
ModalDrawer,
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
<el-dropdown trigger="click" @click.stop @command="optionSelected($event, item)" v-if="allowParentSelect === true">
|
<el-dropdown trigger="click" @click.stop @command="optionSelected($event, item)" v-if="allowParentSelect === true">
|
||||||
<span class="el-dropdown-link clickable" @click.stop>
|
<span class="el-dropdown-link clickable" @click.stop>
|
||||||
<font-awesome-icon icon="dot-circle" title="Select Item" />
|
<font-awesome-icon icon="dot-circle" :title="$baseText('variableSelectorItem.selectItem')" />
|
||||||
</span>
|
</span>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item :command="operation.command" v-for="operation in itemAddOperations" :key="operation.command">{{operation.displayName}}</el-dropdown-item>
|
<el-dropdown-item :command="operation.command" v-for="operation in itemAddOperations" :key="operation.command">{{operation.displayName}}</el-dropdown-item>
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
{{item.name}}:
|
{{item.name}}:
|
||||||
<font-awesome-icon icon="dot-circle" title="Select Item" />
|
<font-awesome-icon icon="dot-circle" title="Select Item" />
|
||||||
</div>
|
</div>
|
||||||
<div class="item-value">{{ item.value !== undefined?item.value:'--- EMPTY ---' }}</div>
|
<div class="item-value">{{ item.value !== undefined?item.value: $baseText('variableSelectorItem.empty') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -41,8 +41,10 @@ import {
|
||||||
IVariableSelectorOption,
|
IVariableSelectorOption,
|
||||||
IVariableItemSelected,
|
IVariableItemSelected,
|
||||||
} from '@/Interface';
|
} from '@/Interface';
|
||||||
|
import { renderText } from './mixins/renderText';
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default mixins(renderText).extend({
|
||||||
name: 'VariableSelectorItem',
|
name: 'VariableSelectorItem',
|
||||||
props: [
|
props: [
|
||||||
'allowParentSelect',
|
'allowParentSelect',
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
<template functional>
|
<template>
|
||||||
<!-- eslint-disable-next-line vue/no-mutating-props -->
|
<!-- eslint-disable-next-line vue/no-mutating-props -->
|
||||||
<a v-if="props.version" :set="version = props.version" :href="version.documentationUrl" target="_blank" :class="$style.card">
|
<a v-if="version" :set="version = version" :href="version.documentationUrl" target="_blank" :class="$style.card">
|
||||||
<div :class="$style.header">
|
<div :class="$style.header">
|
||||||
<div>
|
<div>
|
||||||
<div :class="$style.name">
|
<div :class="$style.name">
|
||||||
Version {{version.name}}
|
{{ `${$baseText('versionCard.version')} ${version.name}` }}
|
||||||
</div>
|
</div>
|
||||||
<WarningTooltip v-if="version.hasSecurityIssue">
|
<WarningTooltip v-if="version.hasSecurityIssue">
|
||||||
<template>
|
<template>
|
||||||
This version has a security issue.<br/>It is listed here for completeness.
|
{{ $baseText('versionCard.thisVersionHasASecurityIssue') }}
|
||||||
</template>
|
</template>
|
||||||
</WarningTooltip>
|
</WarningTooltip>
|
||||||
<Badge
|
<Badge
|
||||||
v-if="version.hasSecurityFix"
|
v-if="version.hasSecurityFix"
|
||||||
text="Security update"
|
:text="$baseText('versionCard.securityUpdate')"
|
||||||
type="danger"
|
type="danger"
|
||||||
/>
|
/>
|
||||||
<Badge
|
<Badge
|
||||||
v-if="version.hasBreakingChange"
|
v-if="version.hasBreakingChange"
|
||||||
text="Breaking changes"
|
:text="$baseText('versionCard.breakingChanges')"
|
||||||
type="warning"
|
type="warning"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style['release-date']">
|
<div :class="$style['release-date']">
|
||||||
Released <TimeAgo :date="version.createdAt" />
|
{{ $baseText('versionCard.released') }} <TimeAgo :date="version.createdAt" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.divider" v-if="version.description || (version.nodes && version.nodes.length)"></div>
|
<div :class="$style.divider" v-if="version.description || (version.nodes && version.nodes.length)"></div>
|
||||||
|
@ -48,19 +48,21 @@ import TimeAgo from './TimeAgo.vue';
|
||||||
import Badge from './Badge.vue';
|
import Badge from './Badge.vue';
|
||||||
import WarningTooltip from './WarningTooltip.vue';
|
import WarningTooltip from './WarningTooltip.vue';
|
||||||
import { IVersionNode } from '@/Interface';
|
import { IVersionNode } from '@/Interface';
|
||||||
|
import { renderText } from './mixins/renderText';
|
||||||
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
Vue.component('NodeIcon', NodeIcon);
|
Vue.component('NodeIcon', NodeIcon);
|
||||||
Vue.component('TimeAgo', TimeAgo);
|
Vue.component('TimeAgo', TimeAgo);
|
||||||
Vue.component('Badge', Badge);
|
Vue.component('Badge', Badge);
|
||||||
Vue.component('WarningTooltip', WarningTooltip);
|
Vue.component('WarningTooltip', WarningTooltip);
|
||||||
|
|
||||||
export default Vue.extend({
|
export default mixins(renderText).extend({
|
||||||
components: { NodeIcon, TimeAgo, Badge, WarningTooltip },
|
components: { NodeIcon, TimeAgo, Badge, WarningTooltip },
|
||||||
name: 'UpdatesPanel',
|
name: 'VersionCard',
|
||||||
props: ['version'],
|
props: ['version'],
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
nodeName (node: IVersionNode): string {
|
nodeName (node: IVersionNode): string {
|
||||||
return node !== null ? node.displayName : 'unknown';
|
return node !== null ? node.displayName : this.$baseText('versionCard.unknown');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
element-loading-spinner="el-icon-loading"
|
element-loading-spinner="el-icon-loading"
|
||||||
:value="workflowActive"
|
:value="workflowActive"
|
||||||
@change="activeChanged"
|
@change="activeChanged"
|
||||||
:title="workflowActive?'Deactivate Workflow':'Activate Workflow'"
|
:title="workflowActive ? $baseText('workflowActivator.deactivateWorkflow') : $baseText('workflowActivator.activateWorkflow')"
|
||||||
:disabled="disabled || loading"
|
:disabled="disabled || loading"
|
||||||
:active-color="getActiveColor"
|
:active-color="getActiveColor"
|
||||||
inactive-color="#8899AA">
|
inactive-color="#8899AA">
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
<div class="could-not-be-started" v-if="couldNotBeStarted">
|
<div class="could-not-be-started" v-if="couldNotBeStarted">
|
||||||
<n8n-tooltip placement="top">
|
<n8n-tooltip placement="top">
|
||||||
<div @click="displayActivationError" slot="content">The workflow is set to be active but could not be started.<br />Click to display error message.</div>
|
<div @click="displayActivationError" slot="content">{{ $baseText('workflowActivator.theWorkflowIsSetToBeActiveBut') }}</div>
|
||||||
<font-awesome-icon @click="displayActivationError" icon="exclamation-triangle" />
|
<font-awesome-icon @click="displayActivationError" icon="exclamation-triangle" />
|
||||||
</n8n-tooltip>
|
</n8n-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -79,8 +79,8 @@ export default mixins(
|
||||||
async activeChanged (newActiveState: boolean) {
|
async activeChanged (newActiveState: boolean) {
|
||||||
if (this.workflowId === undefined) {
|
if (this.workflowId === undefined) {
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Problem activating workflow',
|
title: this.$baseText('workflowActivator.showMessage.activeChangedWorkflowIdUndefined.title'),
|
||||||
message: 'The workflow did not get saved yet so can not be set active!',
|
message: this.$baseText('workflowActivator.showMessage.activeChangedWorkflowIdUndefined.message'),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
@ -88,8 +88,8 @@ export default mixins(
|
||||||
|
|
||||||
if (this.nodesIssuesExist === true) {
|
if (this.nodesIssuesExist === true) {
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Problem activating workflow',
|
title: this.$baseText('workflowActivator.showMessage.activeChangedNodesIssuesExistTrue.title'),
|
||||||
message: 'It is only possible to activate a workflow when all issues on all nodes got resolved!',
|
message: this.$baseText('workflowActivator.showMessage.activeChangedNodesIssuesExistTrue.message'),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
@ -105,7 +105,13 @@ export default mixins(
|
||||||
// for people because it would activate a different version of the workflow
|
// for people because it would activate a different version of the workflow
|
||||||
// than the one they can currently see.
|
// than the one they can currently see.
|
||||||
if (this.dirtyState) {
|
if (this.dirtyState) {
|
||||||
const importConfirm = await this.confirmMessage(`When you activate the workflow all currently unsaved changes of the workflow will be saved.`, 'Activate and save?', 'warning', 'Yes, activate and save!');
|
const importConfirm = await this.confirmMessage(
|
||||||
|
this.$baseText('workflowActivator.confirmMessage.message'),
|
||||||
|
this.$baseText('workflowActivator.confirmMessage.headline'),
|
||||||
|
'warning',
|
||||||
|
this.$baseText('workflowActivator.confirmMessage.confirmButtonText'),
|
||||||
|
this.$baseText('workflowActivator.confirmMessage.cancelButtonText'),
|
||||||
|
);
|
||||||
if (importConfirm === false) {
|
if (importConfirm === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -123,7 +129,14 @@ export default mixins(
|
||||||
await this.restApi().updateWorkflow(this.workflowId, data);
|
await this.restApi().updateWorkflow(this.workflowId, data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const newStateName = newActiveState === true ? 'activated' : 'deactivated';
|
const newStateName = newActiveState === true ? 'activated' : 'deactivated';
|
||||||
this.$showError(error, 'Problem', `There was a problem and the workflow could not be ${newStateName}:`);
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('workflowActivator.showError.title'),
|
||||||
|
this.$baseText(
|
||||||
|
'workflowActivator.showError.message',
|
||||||
|
{ interpolate: { newStateName } },
|
||||||
|
) + ':',
|
||||||
|
);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -155,16 +168,19 @@ export default mixins(
|
||||||
const errorData = await this.restApi().getActivationError(this.workflowId);
|
const errorData = await this.restApi().getActivationError(this.workflowId);
|
||||||
|
|
||||||
if (errorData === undefined) {
|
if (errorData === undefined) {
|
||||||
errorMessage = 'Sorry there was a problem. No error got found to display.';
|
errorMessage = this.$baseText('workflowActivator.showMessage.displayActivationError.message.errorDataUndefined');
|
||||||
} else {
|
} else {
|
||||||
errorMessage = `The following error occurred on workflow activation:<br /><i>${errorData.error.message}</i>`;
|
errorMessage = this.$baseText(
|
||||||
|
'workflowActivator.showMessage.displayActivationError.message.errorDataNotUndefined',
|
||||||
|
{ interpolate: { message: errorData.error.message } },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorMessage = 'Sorry there was a problem requesting the error';
|
errorMessage = this.$baseText('workflowActivator.showMessage.displayActivationError.message.catchBlock');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Problem activating workflow',
|
title: this.$baseText('workflowActivator.showMessage.displayActivationError.title'),
|
||||||
message: errorMessage,
|
message: errorMessage,
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
duration: 0,
|
duration: 0,
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
<template v-slot:header>
|
<template v-slot:header>
|
||||||
<div class="workflows-header">
|
<div class="workflows-header">
|
||||||
<n8n-heading tag="h1" size="xlarge" class="title">
|
<n8n-heading tag="h1" size="xlarge" class="title">
|
||||||
Open Workflow
|
{{ $baseText('workflowOpen.openWorkflow') }}
|
||||||
</n8n-heading>
|
</n8n-heading>
|
||||||
<div class="tags-filter">
|
<div class="tags-filter">
|
||||||
<TagsDropdown
|
<TagsDropdown
|
||||||
placeholder="Filter by tags..."
|
:placeholder="$baseText('workflowOpen.openWorkflow')"
|
||||||
:currentTagIds="filterTagIds"
|
:currentTagIds="filterTagIds"
|
||||||
:createEnabled="false"
|
:createEnabled="false"
|
||||||
@update="updateTagsFilter"
|
@update="updateTagsFilter"
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="search-filter">
|
<div class="search-filter">
|
||||||
<n8n-input placeholder="Search workflows..." ref="inputFieldFilter" v-model="filterText">
|
<n8n-input :placeholder="$baseText('workflowOpen.searchWorkflows')" ref="inputFieldFilter" v-model="filterText">
|
||||||
<font-awesome-icon slot="prefix" icon="search"></font-awesome-icon>
|
<font-awesome-icon slot="prefix" icon="search"></font-awesome-icon>
|
||||||
</n8n-input>
|
</n8n-input>
|
||||||
</div>
|
</div>
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
<template v-slot:content>
|
<template v-slot:content>
|
||||||
<el-table class="search-table" :data="filteredWorkflows" stripe @cell-click="openWorkflow" :default-sort = "{prop: 'updatedAt', order: 'descending'}" v-loading="isDataLoading">
|
<el-table class="search-table" :data="filteredWorkflows" stripe @cell-click="openWorkflow" :default-sort = "{prop: 'updatedAt', order: 'descending'}" v-loading="isDataLoading">
|
||||||
<el-table-column property="name" label="Name" class-name="clickable" sortable>
|
<el-table-column property="name" :label="$baseText('workflowOpen.name')" class-name="clickable" sortable>
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<div :key="scope.row.id">
|
<div :key="scope.row.id">
|
||||||
<span class="name">{{scope.row.name}}</span>
|
<span class="name">{{scope.row.name}}</span>
|
||||||
|
@ -38,9 +38,9 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column property="createdAt" label="Created" class-name="clickable" width="155" sortable></el-table-column>
|
<el-table-column property="createdAt" :label="$baseText('workflowOpen.created')" class-name="clickable" width="155" sortable></el-table-column>
|
||||||
<el-table-column property="updatedAt" label="Updated" class-name="clickable" width="155" sortable></el-table-column>
|
<el-table-column property="updatedAt" :label="$baseText('workflowOpen.updated')" class-name="clickable" width="155" sortable></el-table-column>
|
||||||
<el-table-column label="Active" width="75">
|
<el-table-column :label="$baseText('workflowOpen.active')" width="75">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<workflow-activator :workflow-active="scope.row.active" :workflow-id="scope.row.id" @workflowActiveChanged="workflowActiveChanged" />
|
<workflow-activator :workflow-active="scope.row.active" :workflow-id="scope.row.id" @workflowActiveChanged="workflowActiveChanged" />
|
||||||
</template>
|
</template>
|
||||||
|
@ -67,10 +67,12 @@ import TagsDropdown from '@/components/TagsDropdown.vue';
|
||||||
import WorkflowActivator from '@/components/WorkflowActivator.vue';
|
import WorkflowActivator from '@/components/WorkflowActivator.vue';
|
||||||
import { convertToDisplayDate } from './helpers';
|
import { convertToDisplayDate } from './helpers';
|
||||||
import { WORKFLOW_OPEN_MODAL_KEY } from '../constants';
|
import { WORKFLOW_OPEN_MODAL_KEY } from '../constants';
|
||||||
|
import { renderText } from '@/components/mixins/renderText';
|
||||||
|
|
||||||
export default mixins(
|
export default mixins(
|
||||||
genericHelpers,
|
genericHelpers,
|
||||||
restApi,
|
restApi,
|
||||||
|
renderText,
|
||||||
showMessage,
|
showMessage,
|
||||||
workflowHelpers,
|
workflowHelpers,
|
||||||
).extend({
|
).extend({
|
||||||
|
@ -141,8 +143,8 @@ export default mixins(
|
||||||
|
|
||||||
if (data.id === currentWorkflowId) {
|
if (data.id === currentWorkflowId) {
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Already open',
|
title: this.$baseText('workflowOpen.showMessage.title'),
|
||||||
message: 'This is the current workflow',
|
message: this.$baseText('workflowOpen.showMessage.message'),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
duration: 1500,
|
duration: 1500,
|
||||||
});
|
});
|
||||||
|
@ -152,7 +154,13 @@ export default mixins(
|
||||||
|
|
||||||
const result = this.$store.getters.getStateIsDirty;
|
const result = this.$store.getters.getStateIsDirty;
|
||||||
if(result) {
|
if(result) {
|
||||||
const importConfirm = await this.confirmMessage(`When you switch workflows your current workflow changes will be lost.`, 'Save your Changes?', 'warning', 'Yes, switch workflows and forget changes');
|
const importConfirm = await this.confirmMessage(
|
||||||
|
this.$baseText('workflowOpen.confirmMessage.message'),
|
||||||
|
this.$baseText('workflowOpen.confirmMessage.headline'),
|
||||||
|
'warning',
|
||||||
|
this.$baseText('workflowOpen.confirmMessage.confirmButtonText'),
|
||||||
|
this.$baseText('workflowOpen.confirmMessage.cancelButtonText'),
|
||||||
|
);
|
||||||
if (importConfirm === false) {
|
if (importConfirm === false) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
@ -189,7 +197,11 @@ export default mixins(
|
||||||
)
|
)
|
||||||
.catch(
|
.catch(
|
||||||
(error: Error) => {
|
(error: Error) => {
|
||||||
this.$showError(error, 'Problem loading workflows', 'There was a problem loading the workflows:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('workflowOpen.showError.title'),
|
||||||
|
this.$baseText('workflowOpen.showError.message') + ':',
|
||||||
|
);
|
||||||
this.isDataLoading = false;
|
this.isDataLoading = false;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
:name="WORKFLOW_SETTINGS_MODAL_KEY"
|
:name="WORKFLOW_SETTINGS_MODAL_KEY"
|
||||||
width="65%"
|
width="65%"
|
||||||
maxHeight="80%"
|
maxHeight="80%"
|
||||||
:title="`Settings for ${workflowName} (#${workflowId})`"
|
:title="$baseText('workflowSettings.settingsFor', { interpolate: { workflowName, workflowId } })"
|
||||||
:eventBus="modalBus"
|
:eventBus="modalBus"
|
||||||
:scrollable="true"
|
:scrollable="true"
|
||||||
>
|
>
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
<div v-loading="isLoading" class="workflow-settings">
|
<div v-loading="isLoading" class="workflow-settings">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="10" class="setting-name">
|
<el-col :span="10" class="setting-name">
|
||||||
Error Workflow:
|
{{ $baseText('workflowSettings.errorWorkflow') + ":" }}
|
||||||
<n8n-tooltip class="setting-info" placement="top" >
|
<n8n-tooltip class="setting-info" placement="top" >
|
||||||
<div slot="content" v-html="helpTexts.errorWorkflow"></div>
|
<div slot="content" v-html="helpTexts.errorWorkflow"></div>
|
||||||
<font-awesome-icon icon="question-circle" />
|
<font-awesome-icon icon="question-circle" />
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="10" class="setting-name">
|
<el-col :span="10" class="setting-name">
|
||||||
Timezone:
|
{{ $baseText('workflowSettings.timezone') + ":" }}
|
||||||
<n8n-tooltip class="setting-info" placement="top" >
|
<n8n-tooltip class="setting-info" placement="top" >
|
||||||
<div slot="content" v-html="helpTexts.timezone"></div>
|
<div slot="content" v-html="helpTexts.timezone"></div>
|
||||||
<font-awesome-icon icon="question-circle" />
|
<font-awesome-icon icon="question-circle" />
|
||||||
|
@ -49,14 +49,14 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="10" class="setting-name">
|
<el-col :span="10" class="setting-name">
|
||||||
Save Data Error Execution:
|
{{ $baseText('workflowSettings.saveDataErrorExecution') + ":" }}
|
||||||
<n8n-tooltip class="setting-info" placement="top" >
|
<n8n-tooltip class="setting-info" placement="top" >
|
||||||
<div slot="content" v-html="helpTexts.saveDataErrorExecution"></div>
|
<div slot="content" v-html="helpTexts.saveDataErrorExecution"></div>
|
||||||
<font-awesome-icon icon="question-circle" />
|
<font-awesome-icon icon="question-circle" />
|
||||||
</n8n-tooltip>
|
</n8n-tooltip>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="14" class="ignore-key-press">
|
<el-col :span="14" class="ignore-key-press">
|
||||||
<n8n-select v-model="workflowSettings.saveDataErrorExecution" placeholder="Select Option" size="medium" filterable :limit-popper-width="true">
|
<n8n-select v-model="workflowSettings.saveDataErrorExecution" :placeholder="$baseText('workflowSettings.selectOption')" size="medium" filterable :limit-popper-width="true">
|
||||||
<n8n-option
|
<n8n-option
|
||||||
v-for="option of saveDataErrorExecutionOptions"
|
v-for="option of saveDataErrorExecutionOptions"
|
||||||
:key="option.key"
|
:key="option.key"
|
||||||
|
@ -68,14 +68,14 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="10" class="setting-name">
|
<el-col :span="10" class="setting-name">
|
||||||
Save Data Success Execution:
|
{{ $baseText('workflowSettings.saveDataSuccessExecution') + ":" }}
|
||||||
<n8n-tooltip class="setting-info" placement="top" >
|
<n8n-tooltip class="setting-info" placement="top" >
|
||||||
<div slot="content" v-html="helpTexts.saveDataSuccessExecution"></div>
|
<div slot="content" v-html="helpTexts.saveDataSuccessExecution"></div>
|
||||||
<font-awesome-icon icon="question-circle" />
|
<font-awesome-icon icon="question-circle" />
|
||||||
</n8n-tooltip>
|
</n8n-tooltip>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="14" class="ignore-key-press">
|
<el-col :span="14" class="ignore-key-press">
|
||||||
<n8n-select v-model="workflowSettings.saveDataSuccessExecution" placeholder="Select Option" size="medium" filterable :limit-popper-width="true">
|
<n8n-select v-model="workflowSettings.saveDataSuccessExecution" :placeholder="$baseText('workflowSettings.selectOption')" size="medium" filterable :limit-popper-width="true">
|
||||||
<n8n-option
|
<n8n-option
|
||||||
v-for="option of saveDataSuccessExecutionOptions"
|
v-for="option of saveDataSuccessExecutionOptions"
|
||||||
:key="option.key"
|
:key="option.key"
|
||||||
|
@ -87,14 +87,14 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="10" class="setting-name">
|
<el-col :span="10" class="setting-name">
|
||||||
Save Manual Executions:
|
{{ $baseText('workflowSettings.saveManualExecutions') + ":" }}
|
||||||
<n8n-tooltip class="setting-info" placement="top" >
|
<n8n-tooltip class="setting-info" placement="top" >
|
||||||
<div slot="content" v-html="helpTexts.saveManualExecutions"></div>
|
<div slot="content" v-html="helpTexts.saveManualExecutions"></div>
|
||||||
<font-awesome-icon icon="question-circle" />
|
<font-awesome-icon icon="question-circle" />
|
||||||
</n8n-tooltip>
|
</n8n-tooltip>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="14" class="ignore-key-press">
|
<el-col :span="14" class="ignore-key-press">
|
||||||
<n8n-select v-model="workflowSettings.saveManualExecutions" placeholder="Select Option" size="medium" filterable :limit-popper-width="true">
|
<n8n-select v-model="workflowSettings.saveManualExecutions" :placeholder="$baseText('workflowSettings.selectOption')" size="medium" filterable :limit-popper-width="true">
|
||||||
<n8n-option
|
<n8n-option
|
||||||
v-for="option of saveManualOptions"
|
v-for="option of saveManualOptions"
|
||||||
:key="option.key"
|
:key="option.key"
|
||||||
|
@ -106,14 +106,14 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="10" class="setting-name">
|
<el-col :span="10" class="setting-name">
|
||||||
Save Execution Progress:
|
{{ $baseText('workflowSettings.saveExecutionProgress') + ":" }}
|
||||||
<n8n-tooltip class="setting-info" placement="top" >
|
<n8n-tooltip class="setting-info" placement="top" >
|
||||||
<div slot="content" v-html="helpTexts.saveExecutionProgress"></div>
|
<div slot="content" v-html="helpTexts.saveExecutionProgress"></div>
|
||||||
<font-awesome-icon icon="question-circle" />
|
<font-awesome-icon icon="question-circle" />
|
||||||
</n8n-tooltip>
|
</n8n-tooltip>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="14" class="ignore-key-press">
|
<el-col :span="14" class="ignore-key-press">
|
||||||
<n8n-select v-model="workflowSettings.saveExecutionProgress" placeholder="Select Option" size="medium" filterable :limit-popper-width="true">
|
<n8n-select v-model="workflowSettings.saveExecutionProgress" :placeholder="$baseText('workflowSettings.selectOption')" size="medium" filterable :limit-popper-width="true">
|
||||||
<n8n-option
|
<n8n-option
|
||||||
v-for="option of saveExecutionProgressOptions"
|
v-for="option of saveExecutionProgressOptions"
|
||||||
:key="option.key"
|
:key="option.key"
|
||||||
|
@ -125,7 +125,7 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="10" class="setting-name">
|
<el-col :span="10" class="setting-name">
|
||||||
Timeout Workflow:
|
{{ $baseText('workflowSettings.timeoutWorkflow') + ":" }}
|
||||||
<n8n-tooltip class="setting-info" placement="top" >
|
<n8n-tooltip class="setting-info" placement="top" >
|
||||||
<div slot="content" v-html="helpTexts.executionTimeoutToggle"></div>
|
<div slot="content" v-html="helpTexts.executionTimeoutToggle"></div>
|
||||||
<font-awesome-icon icon="question-circle" />
|
<font-awesome-icon icon="question-circle" />
|
||||||
|
@ -140,7 +140,7 @@
|
||||||
<div v-if="workflowSettings.executionTimeout > -1">
|
<div v-if="workflowSettings.executionTimeout > -1">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="10" class="setting-name">
|
<el-col :span="10" class="setting-name">
|
||||||
Timeout After:
|
{{ $baseText('workflowSettings.timeoutAfter') + ":" }}
|
||||||
<n8n-tooltip class="setting-info" placement="top" >
|
<n8n-tooltip class="setting-info" placement="top" >
|
||||||
<div slot="content" v-html="helpTexts.executionTimeout"></div>
|
<div slot="content" v-html="helpTexts.executionTimeout"></div>
|
||||||
<font-awesome-icon icon="question-circle" />
|
<font-awesome-icon icon="question-circle" />
|
||||||
|
@ -148,17 +148,17 @@
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<n8n-input size="medium" :value="timeoutHMS.hours" @input="(value) => setTimeout('hours', value)" :min="0">
|
<n8n-input size="medium" :value="timeoutHMS.hours" @input="(value) => setTimeout('hours', value)" :min="0">
|
||||||
<template slot="append">hours</template>
|
<template slot="append">{{ $baseText('workflowSettings.hours') }}</template>
|
||||||
</n8n-input>
|
</n8n-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="4" class="timeout-input">
|
<el-col :span="4" class="timeout-input">
|
||||||
<n8n-input size="medium" :value="timeoutHMS.minutes" @input="(value) => setTimeout('minutes', value)" :min="0" :max="60">
|
<n8n-input size="medium" :value="timeoutHMS.minutes" @input="(value) => setTimeout('minutes', value)" :min="0" :max="60">
|
||||||
<template slot="append">minutes</template>
|
<template slot="append">{{ $baseText('workflowSettings.minutes') }}</template>
|
||||||
</n8n-input>
|
</n8n-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="4" class="timeout-input">
|
<el-col :span="4" class="timeout-input">
|
||||||
<n8n-input size="medium" :value="timeoutHMS.seconds" @input="(value) => setTimeout('seconds', value)" :min="0" :max="60">
|
<n8n-input size="medium" :value="timeoutHMS.seconds" @input="(value) => setTimeout('seconds', value)" :min="0" :max="60">
|
||||||
<template slot="append">seconds</template>
|
<template slot="append">{{ $baseText('workflowSettings.seconds') }}</template>
|
||||||
</n8n-input>
|
</n8n-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
@ -207,14 +207,14 @@ export default mixins(
|
||||||
return {
|
return {
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
helpTexts: {
|
helpTexts: {
|
||||||
errorWorkflow: 'The workflow to run in case the current one fails.<br />To function correctly that workflow has to contain an "Error Trigger" node!',
|
errorWorkflow: this.$baseText('workflowSettings.helpTexts.errorWorkflow'),
|
||||||
timezone: 'The timezone in which the workflow should run. Gets for example used by "Cron" node.',
|
timezone: this.$baseText('workflowSettings.helpTexts.timezone'),
|
||||||
saveDataErrorExecution: 'If data data of executions should be saved in case they failed.',
|
saveDataErrorExecution: this.$baseText('workflowSettings.helpTexts.saveDataErrorExecution'),
|
||||||
saveDataSuccessExecution: 'If data data of executions should be saved in case they succeed.',
|
saveDataSuccessExecution: this.$baseText('workflowSettings.helpTexts.saveDataSuccessExecution'),
|
||||||
saveExecutionProgress: 'If data should be saved after each node, allowing you to resume in case of errors from where it stopped. May increase latency.',
|
saveExecutionProgress: this.$baseText('workflowSettings.helpTexts.saveExecutionProgress'),
|
||||||
saveManualExecutions: 'If data data of executions should be saved when started manually from the editor.',
|
saveManualExecutions: this.$baseText('workflowSettings.helpTexts.saveManualExecutions'),
|
||||||
executionTimeoutToggle: 'Cancel workflow execution after defined time',
|
executionTimeoutToggle: this.$baseText('workflowSettings.helpTexts.executionTimeoutToggle'),
|
||||||
executionTimeout: 'After what time the workflow should timeout.',
|
executionTimeout: this.$baseText('workflowSettings.helpTexts.executionTimeout'),
|
||||||
},
|
},
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
|
@ -324,15 +324,24 @@ export default mixins(
|
||||||
this.saveDataErrorExecutionOptions, [
|
this.saveDataErrorExecutionOptions, [
|
||||||
{
|
{
|
||||||
key: 'DEFAULT',
|
key: 'DEFAULT',
|
||||||
value: 'Default - ' + (this.defaultValues.saveDataErrorExecution === 'all' ? 'Save' : 'Do not save'),
|
value: this.$baseText(
|
||||||
|
'workflowSettings.saveDataErrorExecutionOptions.defaultSave',
|
||||||
|
{
|
||||||
|
interpolate: {
|
||||||
|
defaultValue: this.defaultValues.saveDataErrorExecution === 'all'
|
||||||
|
? this.$baseText('workflowSettings.saveDataErrorExecutionOptions.save')
|
||||||
|
: this.$baseText('workflowSettings.saveDataErrorExecutionOptions.doNotsave'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'all',
|
key: 'all',
|
||||||
value: 'Save',
|
value: this.$baseText('workflowSettings.saveDataErrorExecutionOptions.save'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'none',
|
key: 'none',
|
||||||
value: 'Do not save',
|
value: this.$baseText('workflowSettings.saveDataErrorExecutionOptions.doNotSave'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -343,15 +352,24 @@ export default mixins(
|
||||||
this.saveDataSuccessExecutionOptions, [
|
this.saveDataSuccessExecutionOptions, [
|
||||||
{
|
{
|
||||||
key: 'DEFAULT',
|
key: 'DEFAULT',
|
||||||
value: 'Default - ' + (this.defaultValues.saveDataSuccessExecution === 'all' ? 'Save' : 'Do not save'),
|
value: this.$baseText(
|
||||||
|
'workflowSettings.saveDataSuccessExecutionOptions.defaultSave',
|
||||||
|
{
|
||||||
|
interpolate: {
|
||||||
|
defaultValue: this.defaultValues.saveDataSuccessExecution === 'all'
|
||||||
|
? this.$baseText('workflowSettings.saveDataSuccessExecutionOptions.save')
|
||||||
|
: this.$baseText('workflowSettings.saveDataSuccessExecutionOptions.doNotSave'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'all',
|
key: 'all',
|
||||||
value: 'Save',
|
value: this.$baseText('workflowSettings.saveDataSuccessExecutionOptions.save'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'none',
|
key: 'none',
|
||||||
value: 'Do not save',
|
value: this.$baseText('workflowSettings.saveDataSuccessExecutionOptions.doNotSave'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -362,15 +380,22 @@ export default mixins(
|
||||||
this.saveExecutionProgressOptions, [
|
this.saveExecutionProgressOptions, [
|
||||||
{
|
{
|
||||||
key: 'DEFAULT',
|
key: 'DEFAULT',
|
||||||
value: 'Default - ' + (this.defaultValues.saveExecutionProgress === true ? 'Yes' : 'No'),
|
value: this.$baseText(
|
||||||
|
'workflowSettings.saveExecutionProgressOptions.defaultSave',
|
||||||
|
{
|
||||||
|
interpolate: {
|
||||||
|
defaultValue: this.defaultValues.saveExecutionProgress ? this.$baseText('workflowSettings.saveExecutionProgressOptions.yes') : this.$baseText('workflowSettings.saveExecutionProgressOptions.no'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: true,
|
key: true,
|
||||||
value: 'Yes',
|
value: this.$baseText('workflowSettings.saveExecutionProgressOptions.yes'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: false,
|
key: false,
|
||||||
value: 'No',
|
value: this.$baseText('workflowSettings.saveExecutionProgressOptions.no'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -379,15 +404,22 @@ export default mixins(
|
||||||
this.saveManualOptions.length = 0;
|
this.saveManualOptions.length = 0;
|
||||||
this.saveManualOptions.push({
|
this.saveManualOptions.push({
|
||||||
key: 'DEFAULT',
|
key: 'DEFAULT',
|
||||||
value: 'Default - ' + (this.defaultValues.saveManualExecutions === true ? 'Yes' : 'No'),
|
value: this.$baseText(
|
||||||
|
'workflowSettings.saveManualOptions.defaultSave',
|
||||||
|
{
|
||||||
|
interpolate: {
|
||||||
|
defaultValue: this.defaultValues.saveManualExecutions ? this.$baseText('workflowSettings.saveManualOptions.yes') : this.$baseText('workflowSettings.saveManualOptions.no'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
});
|
});
|
||||||
this.saveManualOptions.push({
|
this.saveManualOptions.push({
|
||||||
key: true,
|
key: true,
|
||||||
value: 'Yes',
|
value: this.$baseText('workflowSettings.saveManualOptions.yes'),
|
||||||
});
|
});
|
||||||
this.saveManualOptions.push({
|
this.saveManualOptions.push({
|
||||||
key: false,
|
key: false,
|
||||||
value: 'No',
|
value: this.$baseText('workflowSettings.saveManualOptions.no'),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -401,12 +433,15 @@ export default mixins(
|
||||||
|
|
||||||
let defaultTimezoneValue = timezones[this.defaultValues.timezone] as string | undefined;
|
let defaultTimezoneValue = timezones[this.defaultValues.timezone] as string | undefined;
|
||||||
if (defaultTimezoneValue === undefined) {
|
if (defaultTimezoneValue === undefined) {
|
||||||
defaultTimezoneValue = 'Default Timezone not valid!';
|
defaultTimezoneValue = this.$baseText('workflowSettings.defaultTimezoneNotValid');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.timezones.push({
|
this.timezones.push({
|
||||||
key: 'DEFAULT',
|
key: 'DEFAULT',
|
||||||
value: `Default - ${defaultTimezoneValue}`,
|
value: this.$baseText(
|
||||||
|
'workflowSettings.defaultTimezone',
|
||||||
|
{ interpolate: { defaultTimezoneValue } },
|
||||||
|
),
|
||||||
});
|
});
|
||||||
for (const timezone of Object.keys(timezones)) {
|
for (const timezone of Object.keys(timezones)) {
|
||||||
this.timezones.push({
|
this.timezones.push({
|
||||||
|
@ -430,7 +465,7 @@ export default mixins(
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
workflows.unshift({
|
workflows.unshift({
|
||||||
id: undefined as unknown as string,
|
id: undefined as unknown as string,
|
||||||
name: '- No Workflow -',
|
name: this.$baseText('workflowSettings.noWorkflow'),
|
||||||
});
|
});
|
||||||
|
|
||||||
Vue.set(this, 'workflows', workflows);
|
Vue.set(this, 'workflows', workflows);
|
||||||
|
@ -449,14 +484,33 @@ export default mixins(
|
||||||
: -1;
|
: -1;
|
||||||
|
|
||||||
if (data.settings!.executionTimeout === 0) {
|
if (data.settings!.executionTimeout === 0) {
|
||||||
this.$showError(new Error('timeout is activated but set to 0'), 'Problem saving settings', 'There was a problem saving the settings:');
|
this.$showError(
|
||||||
|
new Error(this.$baseText('workflowSettings.showError.saveSettings1.errorMessage')),
|
||||||
|
this.$baseText('workflowSettings.showError.saveSettings1.title'),
|
||||||
|
this.$baseText('workflowSettings.showError.saveSettings1.message') + ':',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (data.settings!.executionTimeout > this.workflowSettings.maxExecutionTimeout) {
|
if (data.settings!.executionTimeout > this.workflowSettings.maxExecutionTimeout) {
|
||||||
const { hours, minutes, seconds } = this.convertToHMS(this.workflowSettings.maxExecutionTimeout as number);
|
const { hours, minutes, seconds } = this.convertToHMS(this.workflowSettings.maxExecutionTimeout as number);
|
||||||
this.$showError(new Error(`Maximum Timeout is: ${hours} hours, ${minutes} minutes, ${seconds} seconds`), 'Problem saving settings', 'Set timeout is exceeding the maximum timeout!');
|
this.$showError(
|
||||||
|
new Error(
|
||||||
|
this.$baseText(
|
||||||
|
'workflowSettings.showError.saveSettings2.errorMessage',
|
||||||
|
{
|
||||||
|
interpolate: {
|
||||||
|
hours: hours.toString(),
|
||||||
|
minutes: minutes.toString(),
|
||||||
|
seconds: seconds.toString(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
this.$baseText('workflowSettings.showError.saveSettings2.title'),
|
||||||
|
this.$baseText('workflowSettings.showError.saveSettings2.message') + ':',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
delete data.settings!.maxExecutionTimeout;
|
delete data.settings!.maxExecutionTimeout;
|
||||||
|
@ -466,7 +520,11 @@ export default mixins(
|
||||||
try {
|
try {
|
||||||
await this.restApi().updateWorkflow(this.$route.params.name, data);
|
await this.restApi().updateWorkflow(this.$route.params.name, data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(error, 'Problem saving settings', 'There was a problem saving the settings:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('workflowSettings.showError.saveSettings3.title'),
|
||||||
|
this.$baseText('workflowSettings.showError.saveSettings3.message') + ':',
|
||||||
|
);
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -486,8 +544,8 @@ export default mixins(
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Settings saved',
|
title: this.$baseText('workflowSettings.showMessage.saveSettings.title'),
|
||||||
message: 'The workflow settings got saved!',
|
message: this.$baseText('workflowSettings.showMessage.saveSettings.message'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { showMessage } from '@/components/mixins/showMessage';
|
import { showMessage } from '@/components/mixins/showMessage';
|
||||||
import { translate } from '@/components/mixins/translate';
|
import { renderText } from '@/components/mixins/renderText';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
export const genericHelpers = mixins(showMessage, translate).extend({
|
export const genericHelpers = mixins(showMessage, renderText).extend({
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
loadingService: null as any | null, // tslint:disable-line:no-any
|
loadingService: null as any | null, // tslint:disable-line:no-any
|
||||||
|
@ -23,10 +23,10 @@ export const genericHelpers = mixins(showMessage, translate).extend({
|
||||||
displayTimer (msPassed: number, showMs = false): string {
|
displayTimer (msPassed: number, showMs = false): string {
|
||||||
if (msPassed < 60000) {
|
if (msPassed < 60000) {
|
||||||
if (showMs === false) {
|
if (showMs === false) {
|
||||||
return `${Math.floor(msPassed / 1000)} sec.`;
|
return `${Math.floor(msPassed / 1000)} ${this.$baseText('genericHelpers.sec')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${msPassed / 1000} sec.`;
|
return `${msPassed / 1000} ${this.$baseText('genericHelpers.sec')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const secondsPassed = Math.floor(msPassed / 1000);
|
const secondsPassed = Math.floor(msPassed / 1000);
|
||||||
|
@ -38,8 +38,9 @@ export const genericHelpers = mixins(showMessage, translate).extend({
|
||||||
editAllowedCheck (): boolean {
|
editAllowedCheck (): boolean {
|
||||||
if (this.isReadOnly) {
|
if (this.isReadOnly) {
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Workflow can not be changed!',
|
// title: 'Workflow can not be changed!',
|
||||||
message: `The workflow can not be edited as a past execution gets displayed. To make changed either open the original workflow of which the execution gets displayed or save it under a new name first.`,
|
title: this.$baseText('genericHelpers.showMessage.title'),
|
||||||
|
message: this.$baseText('genericHelpers.showMessage.message'),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
duration: 0,
|
duration: 0,
|
||||||
});
|
});
|
||||||
|
@ -58,7 +59,7 @@ export const genericHelpers = mixins(showMessage, translate).extend({
|
||||||
this.loadingService = this.$loading(
|
this.loadingService = this.$loading(
|
||||||
{
|
{
|
||||||
lock: true,
|
lock: true,
|
||||||
text: text || 'Loading',
|
text: text || this.$baseText('genericHelpers.loading'),
|
||||||
spinner: 'el-icon-loading',
|
spinner: 'el-icon-loading',
|
||||||
background: 'rgba(255, 255, 255, 0.8)',
|
background: 'rgba(255, 255, 255, 0.8)',
|
||||||
},
|
},
|
||||||
|
|
|
@ -263,8 +263,8 @@ export const pushConnection = mixins(
|
||||||
// Workflow did execute without a problem
|
// Workflow did execute without a problem
|
||||||
this.$titleSet(workflow.name as string, 'IDLE');
|
this.$titleSet(workflow.name as string, 'IDLE');
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Workflow was executed',
|
title: this.$baseText('pushConnection.showMessage.title'),
|
||||||
message: 'Workflow was executed successfully!',
|
message: this.$baseText('pushConnection.showMessage.message'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,7 @@
|
||||||
// import { TranslationPath } from '@/Interface';
|
// import { TranslationPath } from '@/Interface';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
/**
|
export const renderText = Vue.extend({
|
||||||
* Mixin to translate:
|
|
||||||
* - base strings, i.e. any string that is not node- or credentials-specific,
|
|
||||||
* - specific strings,
|
|
||||||
* - node-specific strings, i.e. those in `NodeView.vue`,
|
|
||||||
* - credentials-specific strings, i.e. those in `EditCredentials.vue`.
|
|
||||||
*/
|
|
||||||
export const translate = Vue.extend({
|
|
||||||
computed: {
|
computed: {
|
||||||
/**
|
/**
|
||||||
* Node type for the active node in `NodeView.vue`.
|
* Node type for the active node in `NodeView.vue`.
|
||||||
|
@ -19,23 +12,18 @@ export const translate = Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
// -----------------------------------------
|
|
||||||
// main methods
|
|
||||||
// -----------------------------------------
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate a base string. Called directly in Vue templates.
|
* Render a string of base text, i.e. a string with a **fixed path** to the value in the locale object. Allows for [interpolation](https://kazupon.github.io/vue-i18n/guide/formatting.html#named-formatting) when the localized value contains a string between curly braces.
|
||||||
* Optionally, [interpolate a variable](https://kazupon.github.io/vue-i18n/guide/formatting.html#named-formatting).
|
* ```js
|
||||||
|
* $baseText('fixed.path.to.localized.value');
|
||||||
|
* $baseText('fixed.path.to.localized.value', { interpolate: { var: arg } });
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
$baseText(
|
$baseText(
|
||||||
key: string,
|
key: string,
|
||||||
options?: { interpolate?: { [key: string]: string } },
|
options?: { interpolate: { [key: string]: string } },
|
||||||
): string {
|
): string {
|
||||||
const translatedBaseString = options && options.interpolate
|
return this.$t(key, options && options.interpolate).toString();
|
||||||
? this.$t(key, options.interpolate)
|
|
||||||
: this.$t(key);
|
|
||||||
|
|
||||||
return translatedBaseString.toString();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -7,10 +7,11 @@ import { ExecutionError } from 'n8n-workflow';
|
||||||
import { ElMessageBoxOptions } from 'element-ui/types/message-box';
|
import { ElMessageBoxOptions } from 'element-ui/types/message-box';
|
||||||
import { MessageType } from 'element-ui/types/message';
|
import { MessageType } from 'element-ui/types/message';
|
||||||
import { isChildOf } from './helpers';
|
import { isChildOf } from './helpers';
|
||||||
|
import { renderText } from '@/components/mixins/renderText';
|
||||||
|
|
||||||
let stickyNotificationQueue: ElNotificationComponent[] = [];
|
let stickyNotificationQueue: ElNotificationComponent[] = [];
|
||||||
|
|
||||||
export const showMessage = mixins(externalHooks).extend({
|
export const showMessage = mixins(externalHooks, renderText).extend({
|
||||||
methods: {
|
methods: {
|
||||||
$showMessage(messageData: ElNotificationOptions, track = true) {
|
$showMessage(messageData: ElNotificationOptions, track = true) {
|
||||||
messageData.dangerouslyUseHTMLString = true;
|
messageData.dangerouslyUseHTMLString = true;
|
||||||
|
@ -130,11 +131,11 @@ export const showMessage = mixins(externalHooks).extend({
|
||||||
this.$telemetry.track('Instance FE emitted error', { error_title: title, error_description: message, error_message: error.message, workflow_id: this.$store.getters.workflowId });
|
this.$telemetry.track('Instance FE emitted error', { error_title: title, error_description: message, error_message: error.message, workflow_id: this.$store.getters.workflowId });
|
||||||
},
|
},
|
||||||
|
|
||||||
async confirmMessage (message: string, headline: string, type: MessageType | null = 'warning', confirmButtonText = 'OK', cancelButtonText = 'Cancel'): Promise<boolean> {
|
async confirmMessage (message: string, headline: string, type: MessageType | null = 'warning', confirmButtonText?: string, cancelButtonText?: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const options: ElMessageBoxOptions = {
|
const options: ElMessageBoxOptions = {
|
||||||
confirmButtonText,
|
confirmButtonText: confirmButtonText || this.$baseText('showMessage.ok'),
|
||||||
cancelButtonText,
|
cancelButtonText: cancelButtonText || this.$baseText('showMessage.cancel'),
|
||||||
dangerouslyUseHTMLString: true,
|
dangerouslyUseHTMLString: true,
|
||||||
...(type && { type }),
|
...(type && { type }),
|
||||||
};
|
};
|
||||||
|
@ -172,7 +173,7 @@ export const showMessage = mixins(externalHooks).extend({
|
||||||
<summary
|
<summary
|
||||||
style="color: #ff6d5a; font-weight: bold; cursor: pointer;"
|
style="color: #ff6d5a; font-weight: bold; cursor: pointer;"
|
||||||
>
|
>
|
||||||
Show Details
|
${this.$baseText('showMessage.showDetails')}
|
||||||
</summary>
|
</summary>
|
||||||
<p>${node.name}: ${errorDescription}</p>
|
<p>${node.name}: ${errorDescription}</p>
|
||||||
</details>
|
</details>
|
||||||
|
|
|
@ -45,6 +45,7 @@ import { externalHooks } from '@/components/mixins/externalHooks';
|
||||||
import { restApi } from '@/components/mixins/restApi';
|
import { restApi } from '@/components/mixins/restApi';
|
||||||
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
||||||
import { showMessage } from '@/components/mixins/showMessage';
|
import { showMessage } from '@/components/mixins/showMessage';
|
||||||
|
import { renderText } from '@/components/mixins/renderText';
|
||||||
|
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
|
|
||||||
|
@ -54,6 +55,7 @@ import { v4 as uuidv4} from 'uuid';
|
||||||
export const workflowHelpers = mixins(
|
export const workflowHelpers = mixins(
|
||||||
externalHooks,
|
externalHooks,
|
||||||
nodeHelpers,
|
nodeHelpers,
|
||||||
|
renderText,
|
||||||
restApi,
|
restApi,
|
||||||
showMessage,
|
showMessage,
|
||||||
)
|
)
|
||||||
|
@ -483,8 +485,8 @@ export const workflowHelpers = mixins(
|
||||||
this.$store.commit('removeActiveAction', 'workflowSaving');
|
this.$store.commit('removeActiveAction', 'workflowSaving');
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Problem saving workflow',
|
title: this.$baseText('workflowHelpers.showMessage.title'),
|
||||||
message: `There was a problem saving the workflow: "${e.message}"`,
|
message: this.$baseText('workflowHelpers.showMessage.message') + `: "${e.message}"`,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -557,8 +559,8 @@ export const workflowHelpers = mixins(
|
||||||
this.$store.commit('removeActiveAction', 'workflowSaving');
|
this.$store.commit('removeActiveAction', 'workflowSaving');
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Problem saving workflow',
|
title: this.$baseText('workflowHelpers.showMessage.title'),
|
||||||
message: `There was a problem saving the workflow: "${e.message}"`,
|
message: this.$baseText('workflowHelpers.showMessage.message') + `: "${e.message}"`,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { externalHooks } from '@/components/mixins/externalHooks';
|
||||||
import { restApi } from '@/components/mixins/restApi';
|
import { restApi } from '@/components/mixins/restApi';
|
||||||
import { workflowHelpers } from '@/components/mixins/workflowHelpers';
|
import { workflowHelpers } from '@/components/mixins/workflowHelpers';
|
||||||
import { showMessage } from '@/components/mixins/showMessage';
|
import { showMessage } from '@/components/mixins/showMessage';
|
||||||
|
import { renderText } from '@/components/mixins/renderText';
|
||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
import { titleChange } from './titleChange';
|
import { titleChange } from './titleChange';
|
||||||
|
@ -21,6 +22,7 @@ import { titleChange } from './titleChange';
|
||||||
export const workflowRun = mixins(
|
export const workflowRun = mixins(
|
||||||
externalHooks,
|
externalHooks,
|
||||||
restApi,
|
restApi,
|
||||||
|
renderText,
|
||||||
workflowHelpers,
|
workflowHelpers,
|
||||||
showMessage,
|
showMessage,
|
||||||
titleChange,
|
titleChange,
|
||||||
|
@ -31,7 +33,9 @@ export const workflowRun = mixins(
|
||||||
if (this.$store.getters.pushConnectionActive === false) {
|
if (this.$store.getters.pushConnectionActive === false) {
|
||||||
// Do not start if the connection to server is not active
|
// Do not start if the connection to server is not active
|
||||||
// because then it can not receive the data as it executes.
|
// because then it can not receive the data as it executes.
|
||||||
throw new Error('No active connection to server. It is maybe down.');
|
throw new Error(
|
||||||
|
this.$baseText('workflowRun.noActiveConnectionToTheServer'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.commit('addActiveAction', 'workflowRunning');
|
this.$store.commit('addActiveAction', 'workflowRunning');
|
||||||
|
@ -89,8 +93,8 @@ export const workflowRun = mixins(
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Workflow can not be executed',
|
title: this.$baseText('workflowRun.showMessage.title'),
|
||||||
message: 'The workflow has issues. Please fix them first:<br /> - ' + errorMessages.join('<br /> - '),
|
message: this.$baseText('workflowRun.showMessage.message') + ':<br /> - ' + errorMessages.join('<br /> - '),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
duration: 0,
|
duration: 0,
|
||||||
});
|
});
|
||||||
|
@ -199,7 +203,11 @@ export const workflowRun = mixins(
|
||||||
return runWorkflowApiResponse;
|
return runWorkflowApiResponse;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$titleSet(workflow.name as string, 'ERROR');
|
this.$titleSet(workflow.name as string, 'ERROR');
|
||||||
this.$showError(error, 'Problem running workflow', 'There was a problem running the workflow:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('workflowRun.showError.title'),
|
||||||
|
this.$baseText('workflowRun.showError.message') + ':',
|
||||||
|
);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,7 @@ import axios from 'axios';
|
||||||
|
|
||||||
Vue.use(VueI18n);
|
Vue.use(VueI18n);
|
||||||
|
|
||||||
console.log('About to initialize i18n');
|
console.log('About to initialize i18n'); // eslint-disable-line no-console
|
||||||
|
|
||||||
export const i18n = new VueI18n({
|
export const i18n = new VueI18n({
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
|
@ -24,7 +24,7 @@ function setLanguage(language: string): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadLanguage(language?: string) {
|
export async function loadLanguage(language?: string) {
|
||||||
console.log(`loadLanguage called with ${language}`);
|
console.log(`loadLanguage called with ${language}`); // eslint-disable-line no-console
|
||||||
|
|
||||||
if (!language) return Promise.resolve();
|
if (!language) return Promise.resolve();
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,49 @@
|
||||||
export default {
|
export default {
|
||||||
en: {
|
en: {
|
||||||
|
versionCard: {
|
||||||
|
thisVersionHasASecurityIssue: 'This version has a security issue.<br/>It is listed here for completeness.',
|
||||||
|
released: 'Released',
|
||||||
|
securityUpdate: 'Security update',
|
||||||
|
breakingChanges: 'Breaking changes',
|
||||||
|
version: 'Version',
|
||||||
|
unknown: 'unknown',
|
||||||
|
},
|
||||||
|
updatesPanel: {
|
||||||
|
weVeBeenBusy: 'We’ve been busy ✨',
|
||||||
|
youReOnVersion: 'You’re on {currentVersionName}, which was released',
|
||||||
|
andIs: 'and is',
|
||||||
|
version: '{numberOfVersions} version{howManySuffix}',
|
||||||
|
behindTheLatest: 'behind the latest and greatest n8n',
|
||||||
|
howToUpdateYourN8nVersion: 'How to update your n8n version',
|
||||||
|
},
|
||||||
|
timeAgo: {
|
||||||
|
justNow: 'Just now',
|
||||||
|
rightNow: 'Right now',
|
||||||
|
oneMinuteAgo: '1 minute ago',
|
||||||
|
oneHourAgo: '1 hour ago',
|
||||||
|
oneDayAgo: '1 day ago',
|
||||||
|
oneWeekAgo: '1 week ago',
|
||||||
|
oneMonthAgo: '1 month ago',
|
||||||
|
oneYearAgo: '1 year ago',
|
||||||
|
minutesAgo: '%s minutes ago',
|
||||||
|
hoursAgo: '%s hours ago',
|
||||||
|
daysAgo: '%s days ago',
|
||||||
|
weeksAgo: '%s weeks ago',
|
||||||
|
monthsAgo: '%s months ago',
|
||||||
|
yearsAgo: '%s years ago',
|
||||||
|
inOneMinute: 'in 1 minute',
|
||||||
|
inOneHour: 'in 1 hour',
|
||||||
|
inOneDay: 'in 1 day',
|
||||||
|
inOneWeek: 'in 1 week',
|
||||||
|
inOneMonth: 'in 1 month',
|
||||||
|
inOneYear: 'in 1 year',
|
||||||
|
inMinutes: 'in %s minutes',
|
||||||
|
inHours: 'in %s hours',
|
||||||
|
inDays: 'in %s days',
|
||||||
|
inWeeks: 'in %s weeks',
|
||||||
|
inMonths: 'in %s months',
|
||||||
|
inYears: 'in %s years',
|
||||||
|
},
|
||||||
about: {
|
about: {
|
||||||
aboutN8n: 'About n8n',
|
aboutN8n: 'About n8n',
|
||||||
apacheWithCommons20Clause: 'Apache 2.0 with Commons Clause',
|
apacheWithCommons20Clause: 'Apache 2.0 with Commons Clause',
|
||||||
|
@ -10,133 +54,59 @@ export default {
|
||||||
},
|
},
|
||||||
binaryDataDisplay: {
|
binaryDataDisplay: {
|
||||||
backToList: 'Back to list',
|
backToList: 'Back to list',
|
||||||
dataToDisplayDidNotGetFound: 'Data to display did not get found',
|
backToOverviewPage: 'Back to overview page',
|
||||||
|
noDataFoundToDisplay: 'No data found to display',
|
||||||
|
yourBrowserDoesNotSupport: 'Your browser does not support the video element. Kindly update it to latest version.',
|
||||||
},
|
},
|
||||||
collectionParameter: {
|
collectionParameter: {
|
||||||
chooseOptionToAdd: 'Choose Option To Add',
|
choose: 'Choose...',
|
||||||
currentlyNoPropertiesExist: 'Currently no properties exist',
|
noProperties: 'No properties',
|
||||||
},
|
|
||||||
credentialsEdit: {
|
|
||||||
createNewCredentials: 'Create New Credentials',
|
|
||||||
credentialType: 'Credential type',
|
|
||||||
needHelp: 'Need help?',
|
|
||||||
nodeDocumentation: 'Node Documentation',
|
|
||||||
openCredentialDocs: 'Open credential docs',
|
|
||||||
showError: {
|
|
||||||
message: 'There was a problem loading the credentials',
|
|
||||||
title: 'Problem loading credentials',
|
|
||||||
},
|
|
||||||
showMessage: {
|
|
||||||
credentialsCreated: {
|
|
||||||
message: 'credentials were successfully created!',
|
|
||||||
title: 'Credentials created',
|
|
||||||
},
|
|
||||||
credentialsUpdated: {
|
|
||||||
message: 'credentials were successfully updated!',
|
|
||||||
title: 'Credentials updated',
|
|
||||||
},
|
|
||||||
credentialTypeNull1: {
|
|
||||||
message: 'Credentials of type {credentialsType} are not known.',
|
|
||||||
title: 'Credential type not known',
|
|
||||||
},
|
|
||||||
credentialTypeNull2: {
|
|
||||||
message: 'Credentials of type {credentialsType} are not known.',
|
|
||||||
title: 'Credential type not known',
|
|
||||||
},
|
|
||||||
currentCredentialsUndefined1: {
|
|
||||||
message: 'Could not find the credentials with the id',
|
|
||||||
title: 'Credentials not found',
|
|
||||||
},
|
|
||||||
currentCredentialsUndefined2: {
|
|
||||||
message: 'No credentials could be loaded!',
|
|
||||||
title: 'Problem loading credentials',
|
|
||||||
},
|
|
||||||
editCredentialsIdUndefined: {
|
|
||||||
message: 'The ID of the credentials which should be edited is missing!',
|
|
||||||
title: 'Credential ID missing',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
title: 'Edit Credentials',
|
|
||||||
},
|
|
||||||
credentialsInput: {
|
|
||||||
access: 'Access',
|
|
||||||
addAtLeastOneNodeWhichHasAccessToTheCredentials: 'Add at least one node which has access to the credentials!',
|
|
||||||
connected: 'Connected',
|
|
||||||
create: 'Create',
|
|
||||||
credentialData: 'Credential Data',
|
|
||||||
credentialsName: 'Credentials Name',
|
|
||||||
enterAllRequiredProperties: 'Enter all required properties',
|
|
||||||
important: 'Important',
|
|
||||||
noAccess: 'No Access',
|
|
||||||
nodesWithAccess: 'Nodes with access',
|
|
||||||
notConnected: 'Not connected',
|
|
||||||
oAuth2CallbackUrl: 'OAuth Callback URL',
|
|
||||||
save: 'Save',
|
|
||||||
showError: {
|
|
||||||
createCredentials: {
|
|
||||||
message: 'There was a problem creating the credentials',
|
|
||||||
title: 'Problem Creating Credentials',
|
|
||||||
},
|
|
||||||
oAuthCredentialAuthorize: {
|
|
||||||
message: 'Error generating authorization URL',
|
|
||||||
title: 'OAuth Authorization Error',
|
|
||||||
},
|
|
||||||
updateCredentials: {
|
|
||||||
message: 'There was a problem updating the credentials',
|
|
||||||
title: 'Problem Updating Credentials',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
showMessage: {
|
|
||||||
copyCallbackUrl: {
|
|
||||||
message: 'Callback URL was successfully copied!',
|
|
||||||
title: 'Copied',
|
|
||||||
},
|
|
||||||
receiveMessage: {
|
|
||||||
message: 'Connected successfully!',
|
|
||||||
title: 'Connected',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
credentialsList: {
|
credentialsList: {
|
||||||
|
name: 'Name',
|
||||||
|
type: 'Type',
|
||||||
|
credentials: 'Credentials',
|
||||||
addNew: 'Add New',
|
addNew: 'Add New',
|
||||||
confirmMessage: {
|
confirmMessage: {
|
||||||
cancelButtonText: '',
|
cancelButtonText: '',
|
||||||
confirmButtonText: 'Yes, delete!',
|
confirmButtonText: 'Yes, delete!',
|
||||||
headline: 'Delete Credentials?',
|
headline: 'Delete Credential?',
|
||||||
message: 'Are you sure you want to delete {credentialName} credentials?',
|
message: 'Are you sure you want to delete {credentialName} credential?',
|
||||||
},
|
},
|
||||||
createNewCredentials: 'Create New Credentials',
|
createNewCredential: 'Create New Credential',
|
||||||
credentials: 'Credentials',
|
editCredential: 'Edit Credential',
|
||||||
editCredentials: 'Edit Credentials',
|
deleteCredential: 'Delete Credential',
|
||||||
deleteCredentials: 'Delete Credentials',
|
|
||||||
showError: {
|
showError: {
|
||||||
deleteCredential: {
|
deleteCredential: {
|
||||||
message: 'There was a problem deleting the credentials',
|
message: '',
|
||||||
title: 'Problem deleting credentials',
|
title: 'Problem deleting credential',
|
||||||
},
|
|
||||||
loadCredentials: {
|
|
||||||
message: 'There was a problem loading the credentials',
|
|
||||||
title: 'Problem loading credentials',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
showMessage: {
|
showMessage: {
|
||||||
message: 'The credential {credentialsName} got deleted!',
|
message: 'The credential {credentialName} got deleted!',
|
||||||
title: 'Credentials deleted',
|
title: 'Credential deleted',
|
||||||
},
|
|
||||||
tableLabels: {
|
|
||||||
name: 'Name',
|
|
||||||
type: 'Type',
|
|
||||||
created: 'Created',
|
|
||||||
updated: 'Updated',
|
|
||||||
operations: 'Operations',
|
|
||||||
},
|
},
|
||||||
yourSavedCredentials: 'Your saved credentials',
|
yourSavedCredentials: 'Your saved credentials',
|
||||||
|
created: 'Created',
|
||||||
|
updated: 'Updated',
|
||||||
|
operations: 'Operations',
|
||||||
|
},
|
||||||
|
credentialSelectModal: {
|
||||||
|
searchForApp: 'Search for app...',
|
||||||
|
addNewCredential: 'Add new credential',
|
||||||
|
selectAnAppOrServiceToConnectTo: 'Select an app or service to connect to',
|
||||||
|
continue: 'Continue',
|
||||||
},
|
},
|
||||||
dataDisplay: {
|
dataDisplay: {
|
||||||
needHelp: 'Need help?',
|
needHelp: 'Need help?',
|
||||||
nodeDocumentation: 'Node Documentation',
|
nodeDocumentation: 'Node Documentation',
|
||||||
openDocumentationFor: 'Open {nodeTypeDisplayName} documentation',
|
openDocumentationFor: 'Open {nodeTypeDisplayName} documentation',
|
||||||
},
|
},
|
||||||
|
displayWithChange: {
|
||||||
|
clickToChange: 'Click to Change',
|
||||||
|
cancelEdit: 'Cancel Edit',
|
||||||
|
setValue: 'Set Value',
|
||||||
|
},
|
||||||
duplicateWorkflowDialog: {
|
duplicateWorkflowDialog: {
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
chooseOrCreateATag: 'Choose or create a tag',
|
chooseOrCreateATag: 'Choose or create a tag',
|
||||||
|
@ -148,11 +118,112 @@ export default {
|
||||||
title: 'Name missing',
|
title: 'Name missing',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
executionDetails: {
|
||||||
|
executionId: 'Execution ID',
|
||||||
|
executionWasSuccessful: 'Execution was successful',
|
||||||
|
executionWaiting: 'Execution waiting',
|
||||||
|
executionFailed: 'Execution failed',
|
||||||
|
openWorkflow: 'Open Workflow',
|
||||||
|
},
|
||||||
|
credentialEdit: {
|
||||||
|
credentialInfo: {
|
||||||
|
allowUseBy: 'Allow use by',
|
||||||
|
created: 'Created',
|
||||||
|
lastModified: 'Last modified',
|
||||||
|
id: 'ID',
|
||||||
|
},
|
||||||
|
credentialConfig: {
|
||||||
|
needHelpFillingOutTheseFields: 'Need help filling out these fields?',
|
||||||
|
openDocs: 'Open docs',
|
||||||
|
pleaseCheckTheErrorsBelow: 'Please check the errors below',
|
||||||
|
couldntConnectWithTheseSettings: "Couldn’t connect with these settings",
|
||||||
|
retry: 'Retry',
|
||||||
|
retrying: 'Retrying',
|
||||||
|
retryCredentialTest: 'Retry credential test',
|
||||||
|
accountConnected: 'Account connected',
|
||||||
|
reconnect: 'reconnect',
|
||||||
|
reconnectOAuth2Credential: 'Reconnect OAuth2 Credential',
|
||||||
|
connectionTestedSuccessfully: 'Connection tested successfully',
|
||||||
|
oAuthRedirectUrl: 'OAuth Redirect URL',
|
||||||
|
clickToCopy: 'ClickToCopy',
|
||||||
|
subtitle: 'In {appName}, use the URL above when prompted to enter an OAuth callback or redirect URL',
|
||||||
|
redirectUrlCopiedToClipboard: 'Redirect URL copied to clipboard',
|
||||||
|
},
|
||||||
|
oAuthButton: {
|
||||||
|
signInWithGoogle: 'Sign in with Google',
|
||||||
|
connectMyAccount: 'Connect my account',
|
||||||
|
},
|
||||||
|
credentialEdit: {
|
||||||
|
connection: 'Connection',
|
||||||
|
details: 'Details',
|
||||||
|
confirmMessage: {
|
||||||
|
beforeClose1: {
|
||||||
|
cancelButtonText: 'Keep Editing',
|
||||||
|
confirmButtonText: 'Close',
|
||||||
|
headline: 'Close without saving?',
|
||||||
|
message: 'Are you sure you want to throw away the changes you made to the {credentialDisplayName} credential?',
|
||||||
|
},
|
||||||
|
beforeClose2: {
|
||||||
|
cancelButtonText: 'Keep Editing',
|
||||||
|
confirmButtonText: 'Close',
|
||||||
|
headline: 'Close without connecting?',
|
||||||
|
message: 'You need to connect your credential for it to work',
|
||||||
|
},
|
||||||
|
deleteCredential: {
|
||||||
|
cancelButtonText: '',
|
||||||
|
confirmButtonText: 'Yes, delete!',
|
||||||
|
headline: 'Delete Credential?',
|
||||||
|
message: 'Are you sure you want to delete "{savedCredentialName}" credential?',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
couldNotFindCredentialOfType: 'Could not find credential of type',
|
||||||
|
couldNotFindCredentialWithId: 'Could not find credential with ID',
|
||||||
|
showMessage: {
|
||||||
|
title: 'Credential deleted',
|
||||||
|
message: 'The credential {savedCredentialName} was deleted!',
|
||||||
|
},
|
||||||
|
showError: {
|
||||||
|
loadCredential: {
|
||||||
|
message: '',
|
||||||
|
title: 'Problem loading credential',
|
||||||
|
},
|
||||||
|
createCredential: {
|
||||||
|
message: '',
|
||||||
|
title: 'Problem creating credential',
|
||||||
|
},
|
||||||
|
updateCredential: {
|
||||||
|
message: '',
|
||||||
|
title: 'Problem updating credential',
|
||||||
|
},
|
||||||
|
deleteCredential: {
|
||||||
|
message: '',
|
||||||
|
title: 'Problem deleting credential',
|
||||||
|
},
|
||||||
|
generateAuthorizationUrl: {
|
||||||
|
message: 'There was a problem generating the authorization URL',
|
||||||
|
title: 'OAuth Authorization Error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
readOnly: {
|
||||||
|
youreViewingTheLogOf: `You're viewing the log of a previous execution. You cannot<br />
|
||||||
|
make changes since this execution already occured. Make changes<br />
|
||||||
|
to this workflow by clicking on its name on the left.`,
|
||||||
|
readOnly: 'Read only',
|
||||||
|
},
|
||||||
executionsList: {
|
executionsList: {
|
||||||
|
modes: {
|
||||||
|
error: 'error',
|
||||||
|
retry: 'retry',
|
||||||
|
manual: 'manual',
|
||||||
|
trigger: 'trigger',
|
||||||
|
},
|
||||||
|
stopExecution: 'Stop Execution',
|
||||||
|
unsavedWorkflow: '[UNSAVED WORKFLOW]',
|
||||||
allWorkflows: 'All Workflows',
|
allWorkflows: 'All Workflows',
|
||||||
anyStatus: 'Any Status',
|
anyStatus: 'Any Status',
|
||||||
autoRefresh: 'Auto refresh',
|
autoRefresh: 'Auto refresh',
|
||||||
checkAll: 'Check all',
|
|
||||||
confirmMessage: {
|
confirmMessage: {
|
||||||
cancelButtonText: '',
|
cancelButtonText: '',
|
||||||
confirmButtonText: 'Yes, delete!',
|
confirmButtonText: 'Yes, delete!',
|
||||||
|
@ -170,32 +241,35 @@ export default {
|
||||||
retryWithCurrentlySavedWorkflow: 'Retry with currently saved workflow',
|
retryWithCurrentlySavedWorkflow: 'Retry with currently saved workflow',
|
||||||
retryWithOriginalworkflow: 'Retry with original workflow',
|
retryWithOriginalworkflow: 'Retry with original workflow',
|
||||||
running: 'Running',
|
running: 'Running',
|
||||||
runningParens: 'running',
|
|
||||||
runningTime: 'Running Time',
|
runningTime: 'Running Time',
|
||||||
|
waiting: 'Waiting',
|
||||||
selected: 'Selected',
|
selected: 'Selected',
|
||||||
|
deleteSelected: 'Delete Selected',
|
||||||
|
selectStatus: 'Select Status',
|
||||||
|
selectWorkflow: 'Select Workflow',
|
||||||
showError: {
|
showError: {
|
||||||
handleDeleteSelected: {
|
handleDeleteSelected: {
|
||||||
message: 'There was a problem deleting the executions',
|
message: '',
|
||||||
title: 'Problem deleting executions',
|
title: 'Problem deleting executions',
|
||||||
},
|
},
|
||||||
loadMore: {
|
loadMore: {
|
||||||
message: 'There was a problem loading the workflows',
|
message: '',
|
||||||
title: 'Problem loading workflows',
|
title: 'Problem loading workflows',
|
||||||
},
|
},
|
||||||
loadWorkflows: {
|
loadWorkflows: {
|
||||||
message: 'There was a problem loading the workflows',
|
message: '',
|
||||||
title: 'Problem loading workflows',
|
title: 'Problem loading workflows',
|
||||||
},
|
},
|
||||||
refreshData: {
|
refreshData: {
|
||||||
message: 'There was a problem loading the data',
|
message: '',
|
||||||
title: 'Problem loading',
|
title: 'Problem loading data',
|
||||||
},
|
},
|
||||||
retryExecution: {
|
retryExecution: {
|
||||||
message: 'There was a problem with the retry',
|
message: '',
|
||||||
title: 'Problem with retry',
|
title: 'Problem with retry',
|
||||||
},
|
},
|
||||||
stopExecution: {
|
stopExecution: {
|
||||||
message: 'There was a problem stopping the execuction',
|
message: '',
|
||||||
title: 'Problem stopping execution',
|
title: 'Problem stopping execution',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -209,17 +283,19 @@ export default {
|
||||||
title: 'Retry unsuccessful',
|
title: 'Retry unsuccessful',
|
||||||
},
|
},
|
||||||
retrySuccessfulTrue: {
|
retrySuccessfulTrue: {
|
||||||
message: 'The retry was successful!',
|
message: '',
|
||||||
title: 'Retry successful',
|
title: 'Retry successful',
|
||||||
},
|
},
|
||||||
stopExecution: {
|
stopExecution: {
|
||||||
message: 'The execution with the id {activeExecutionId} got stopped!',
|
message: 'The execution with the ID {activeExecutionId} got stopped!',
|
||||||
title: 'Execution stopped',
|
title: 'Execution stopped',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
startedAtId: 'Started At / ID',
|
startedAtId: 'Started At / ID',
|
||||||
status: 'Status',
|
status: 'Status',
|
||||||
statusTooltipText: {
|
statusTooltipText: {
|
||||||
|
theWorkflowIsWaitingIndefinitely: 'The workflow is waiting indefinitely for an incoming webhook call.',
|
||||||
|
theWorkflowIsWaitingTill: `The worklow is waiting till {waitDateDate} {waitDateTime}.`,
|
||||||
theWorkflowExecutionFailed: 'The workflow execution failed.',
|
theWorkflowExecutionFailed: 'The workflow execution failed.',
|
||||||
theWorkflowExecutionFailedButTheRetryWasSuccessful: 'The workflow execution failed but the retry {entryRetrySuccessId} was successful.',
|
theWorkflowExecutionFailedButTheRetryWasSuccessful: 'The workflow execution failed but the retry {entryRetrySuccessId} was successful.',
|
||||||
theWorkflowExecutionIsProbablyStillRunning: 'The workflow execution is probably still running but it may have crashed and n8n cannot safely tell. ',
|
theWorkflowExecutionIsProbablyStillRunning: 'The workflow execution is probably still running but it may have crashed and n8n cannot safely tell. ',
|
||||||
|
@ -240,22 +316,32 @@ export default {
|
||||||
variableSelector: 'Variable Selector',
|
variableSelector: 'Variable Selector',
|
||||||
},
|
},
|
||||||
fixedCollectionParameter: {
|
fixedCollectionParameter: {
|
||||||
chooseOptionToAdd: 'Choose Option To Add',
|
choose: 'Choose...',
|
||||||
currentlyNoItemsExist: 'Currently no items exist',
|
currentlyNoItemsExist: 'Currently no items exist',
|
||||||
},
|
},
|
||||||
genericHelpers: {
|
genericHelpers: {
|
||||||
showMessage: {
|
showMessage: {
|
||||||
message: 'The workflow can not be edited as a past execution gets displayed. To make changed either open the original workflow of which the execution gets displayed or save it under a new name first.',
|
message: 'The workflow cannot be edited as a past execution gets displayed. To make changed either open the original workflow of which the execution gets displayed or save it under a new name first.',
|
||||||
title: 'Workflow can not be changed!',
|
title: 'Workflow cannot be changed!',
|
||||||
},
|
},
|
||||||
|
loading: 'Loading',
|
||||||
|
sec: 'sec.',
|
||||||
},
|
},
|
||||||
mainSideBar: {
|
mainSidebar: {
|
||||||
aboutN8n: 'About n8n',
|
aboutN8n: 'About n8n',
|
||||||
confirmMessage: {
|
confirmMessage: {
|
||||||
cancelButtonText: '',
|
workflowDelete: {
|
||||||
confirmButtonText: 'Yes, delete!',
|
cancelButtonText: '',
|
||||||
headline: 'Delete Workflow?',
|
confirmButtonText: 'Yes, delete!',
|
||||||
message: 'Are you sure that you want to delete the workflow {workflowName}?',
|
headline: 'Delete Workflow?',
|
||||||
|
message: 'Are you sure that you want to delete the workflow {workflowName}?',
|
||||||
|
},
|
||||||
|
workflowNew: {
|
||||||
|
cancelButtonText: '',
|
||||||
|
confirmButtonText: 'Yes, switch workflows and forget changes',
|
||||||
|
headline: 'Save your Changes?',
|
||||||
|
message: 'When you switch workflows your current workflow changes will be lost.',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
credentials: 'Credentials',
|
credentials: 'Credentials',
|
||||||
delete: 'Delete',
|
delete: 'Delete',
|
||||||
|
@ -266,7 +352,7 @@ export default {
|
||||||
helpMenuItems: {
|
helpMenuItems: {
|
||||||
documentation: 'Documentation',
|
documentation: 'Documentation',
|
||||||
forum: 'Forum',
|
forum: 'Forum',
|
||||||
workflows: 'workflows',
|
workflows: 'Workflows',
|
||||||
},
|
},
|
||||||
importFromFile: 'Import from File',
|
importFromFile: 'Import from File',
|
||||||
importFromUrl: 'Import from URL',
|
importFromUrl: 'Import from URL',
|
||||||
|
@ -282,12 +368,8 @@ export default {
|
||||||
save: 'Save',
|
save: 'Save',
|
||||||
settings: 'Settings',
|
settings: 'Settings',
|
||||||
showError: {
|
showError: {
|
||||||
handleSelect: {
|
|
||||||
message: 'There was a problem deleting the workflow',
|
|
||||||
title: 'Problem deleting the workflow',
|
|
||||||
},
|
|
||||||
stopExecution: {
|
stopExecution: {
|
||||||
message: 'There was a problem stopping the execuction',
|
message: '',
|
||||||
title: 'Problem stopping execution',
|
title: 'Problem stopping execution',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -301,37 +383,52 @@ export default {
|
||||||
title: 'Workflow got deleted',
|
title: 'Workflow got deleted',
|
||||||
},
|
},
|
||||||
handleSelect2: {
|
handleSelect2: {
|
||||||
message: 'A new workflow got created',
|
message: '',
|
||||||
title: 'Workflow created',
|
title: 'Workflow created',
|
||||||
},
|
},
|
||||||
handleSelect3: {
|
handleSelect3: {
|
||||||
message: 'A new workflow got created',
|
message: '',
|
||||||
title: 'Workflow created',
|
title: 'Workflow created',
|
||||||
},
|
},
|
||||||
stopExecution: {
|
stopExecution: {
|
||||||
message: 'The execution with the id {executionId} got stopped!',
|
message: 'Execution with ID {executionId} got stopped!',
|
||||||
title: 'Execution stopped',
|
title: 'Execution stopped',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
workflow: 'Workflow',
|
|
||||||
workflows: 'Workflows',
|
workflows: 'Workflows',
|
||||||
},
|
},
|
||||||
multipleParameter: {
|
multipleParameter: {
|
||||||
currentlyNoItemsExist: 'Currently no items exist',
|
currentlyNoItemsExist: 'Currently no items exist',
|
||||||
|
moveUp: 'Move up',
|
||||||
|
moveDown: 'Move down',
|
||||||
|
deleteItem: 'Delete item',
|
||||||
|
},
|
||||||
|
node: {
|
||||||
|
nodeIsExecuting: 'Node is executing',
|
||||||
|
deleteNode: 'Delete Node',
|
||||||
|
activateDeactivateNode: 'Activate/Deactivate Node',
|
||||||
|
duplicateNode: 'Duplicate Node',
|
||||||
|
editNode: 'Edit Node',
|
||||||
|
executeNode: 'Execute Node',
|
||||||
},
|
},
|
||||||
nodeCredentials: {
|
nodeCredentials: {
|
||||||
credentials: 'Credentials',
|
credentialFor: 'Credential for {credentialType}',
|
||||||
|
selectCredential: 'Select Credential',
|
||||||
|
issues: 'Issues',
|
||||||
|
updateCredential: 'Update Credential',
|
||||||
showMessage: {
|
showMessage: {
|
||||||
message: 'The credentials named {name} of type {credentialType} could not be found!',
|
message: 'Nodes that used credential "{oldCredentialName}" have been updated to use "{newCredentialName}"',
|
||||||
title: 'Credentials not found',
|
title: 'Node credential updated',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
nodeErrorView: {
|
nodeErrorView: {
|
||||||
|
copyToClipboard: 'Copy to Clipboard',
|
||||||
cause: 'Cause',
|
cause: 'Cause',
|
||||||
dataBelowMayContain: 'Data below may contain sensitive information. Proceed with caution when sharing.',
|
dataBelowMayContain: 'Data below may contain sensitive information. Proceed with caution when sharing.',
|
||||||
|
theErrorCauseIsTooLargeToBeDisplayed: 'The error cause is too large to be displayed.',
|
||||||
details: 'Details',
|
details: 'Details',
|
||||||
error: 'ERROR',
|
error: 'ERROR',
|
||||||
httpCode: 'HTTP-Code',
|
httpCode: 'HTTP Code',
|
||||||
showMessage: {
|
showMessage: {
|
||||||
message: '',
|
message: '',
|
||||||
title: 'Copied to clipboard',
|
title: 'Copied to clipboard',
|
||||||
|
@ -340,48 +437,58 @@ export default {
|
||||||
time: 'Time',
|
time: 'Time',
|
||||||
},
|
},
|
||||||
nodeSettings: {
|
nodeSettings: {
|
||||||
settings: {
|
parameters: 'Parameters',
|
||||||
alwaysOutputData: {
|
settings: 'Settings',
|
||||||
description: 'If active, the node will return an empty item even if the <br />node returns no data during an initial execution. Be careful setting <br />this on IF-Nodes as it could cause an infinite loop.',
|
nodeDescription: 'Node Description',
|
||||||
displayName: 'Always Output Data',
|
clickOnTheQuestionMarkIcon: 'Click the \'?\' icon to open this node on n8n.io',
|
||||||
},
|
alwaysOutputData: {
|
||||||
color: {
|
description: 'If active, the node will return an empty item even if the <br />node returns no data during an initial execution. Be careful setting <br />this on IF-Nodes as it could cause an infinite loop.',
|
||||||
description: 'The color of the node in the flow.',
|
displayName: 'Always Output Data',
|
||||||
displayName: 'Node Color',
|
|
||||||
},
|
|
||||||
continueOnFail: {
|
|
||||||
description: 'If active, the workflow continues even if this node\'s <br />execution fails. When this occurs, the node passes along input data from<br />previous nodes - so your workflow should account for unexpected output data.',
|
|
||||||
displayName: 'Continue On Fail',
|
|
||||||
},
|
|
||||||
executeOnce: {
|
|
||||||
description: 'If active, the node executes only once, with data<br /> from the first item it recieves.',
|
|
||||||
displayName: 'Execute Once',
|
|
||||||
},
|
|
||||||
maxTries: {
|
|
||||||
description: 'Number of times Retry On Fail should attempt to execute the node <br />before stopping and returning the execution as failed.',
|
|
||||||
displayName: 'Max. Tries',
|
|
||||||
},
|
|
||||||
notes: {
|
|
||||||
description: 'Optional note to save with the node.',
|
|
||||||
displayName: 'Notes',
|
|
||||||
},
|
|
||||||
notesInFlow: {
|
|
||||||
description: 'If active, the note above will display in the flow as a subtitle.',
|
|
||||||
displayName: 'Display note in flow?',
|
|
||||||
},
|
|
||||||
retryOnFail: {
|
|
||||||
description: 'If active, the node tries to execute a failed attempt <br /> multiple times until it succeeds.',
|
|
||||||
displayName: 'Retry On Fail',
|
|
||||||
},
|
|
||||||
waitBetweenTries: {
|
|
||||||
description: 'How long to wait between each attempt. Value in ms.',
|
|
||||||
displayName: 'Wait Between Tries',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
color: {
|
||||||
|
description: 'The color of the node in the flow.',
|
||||||
|
displayName: 'Node Color',
|
||||||
|
},
|
||||||
|
continueOnFail: {
|
||||||
|
description: 'If active, the workflow continues even if this node\'s <br />execution fails. When this occurs, the node passes along input data from<br />previous nodes - so your workflow should account for unexpected output data.',
|
||||||
|
displayName: 'Continue On Fail',
|
||||||
|
},
|
||||||
|
executeOnce: {
|
||||||
|
description: 'If active, the node executes only once, with data<br /> from the first item it recieves.',
|
||||||
|
displayName: 'Execute Once',
|
||||||
|
},
|
||||||
|
maxTries: {
|
||||||
|
description: 'Number of times Retry On Fail should attempt to execute the node <br />before stopping and returning the execution as failed.',
|
||||||
|
displayName: 'Max. Tries',
|
||||||
|
},
|
||||||
|
notes: {
|
||||||
|
description: 'Optional note to save with the node.',
|
||||||
|
displayName: 'Notes',
|
||||||
|
},
|
||||||
|
notesInFlow: {
|
||||||
|
description: 'If active, the note above will display in the flow as a subtitle.',
|
||||||
|
displayName: 'Display note in flow?',
|
||||||
|
},
|
||||||
|
retryOnFail: {
|
||||||
|
description: 'If active, the node tries to execute a failed attempt <br /> multiple times until it succeeds.',
|
||||||
|
displayName: 'Retry On Fail',
|
||||||
|
},
|
||||||
|
waitBetweenTries: {
|
||||||
|
description: 'How long to wait between each attempt. Value in ms.',
|
||||||
|
displayName: 'Wait Between Tries',
|
||||||
|
},
|
||||||
|
noDescriptionFound: 'No description found',
|
||||||
theNodeIsNotValidAsItsTypeIsUnknown: 'The node is not valid as its type {nodeType} is unknown.',
|
theNodeIsNotValidAsItsTypeIsUnknown: 'The node is not valid as its type {nodeType} is unknown.',
|
||||||
thisNodeDoesNotHaveAnyParameters: 'This node does not have any parameters.',
|
thisNodeDoesNotHaveAnyParameters: 'This node does not have any parameters.',
|
||||||
},
|
},
|
||||||
nodeView: {
|
nodeView: {
|
||||||
|
addNode: 'Add node',
|
||||||
|
stopWaitingForWebhookCall: 'Stop waiting for Webhook call',
|
||||||
|
deletesTheCurrentExecutionData: 'Deletes the current Execution Data.',
|
||||||
|
resetZoom: 'Reset Zoom',
|
||||||
|
zoomToFit: 'Zoom to Fit',
|
||||||
|
zoomIn: 'Zoom In',
|
||||||
|
zoomOut: 'Zoom Out',
|
||||||
confirmMessage: {
|
confirmMessage: {
|
||||||
beforeRouteLeave: {
|
beforeRouteLeave: {
|
||||||
cancelButtonText: '',
|
cancelButtonText: '',
|
||||||
|
@ -402,6 +509,14 @@ export default {
|
||||||
message: 'Import workflow from this URL:<br /><i>{plainTextData}<i>',
|
message: 'Import workflow from this URL:<br /><i>{plainTextData}<i>',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
thisExecutionHasntFinishedYet: "This execution hasn't finished yet",
|
||||||
|
refresh: 'Refresh',
|
||||||
|
toSeeTheLatestStatus: 'to see the latest status',
|
||||||
|
moreInfo: 'More info',
|
||||||
|
loadingTemplate: 'Loading template',
|
||||||
|
couldntImportWorkflow: "Couldn't import workflow",
|
||||||
|
workflowTemplateWithIdCouldNotBeFound: 'Workflow template with id "{templateId}" could not be found!',
|
||||||
|
workflowWithIdCouldNotBeFound: 'Workflow with id "{workflowId}" could not be found!',
|
||||||
executesTheWorkflowFromTheStartOrWebhookNode: 'Executes the Workflow from the Start or Webhook Node.',
|
executesTheWorkflowFromTheStartOrWebhookNode: 'Executes the Workflow from the Start or Webhook Node.',
|
||||||
prompt: {
|
prompt: {
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
|
@ -415,6 +530,11 @@ export default {
|
||||||
executingWorkflow: 'Executing Workflow',
|
executingWorkflow: 'Executing Workflow',
|
||||||
waitingForWebhookCall: 'Waiting for Webhook-Call',
|
waitingForWebhookCall: 'Waiting for Webhook-Call',
|
||||||
},
|
},
|
||||||
|
dropConnectionToCreateNode: 'Drop connection<br />to create node',
|
||||||
|
deleteConnection: 'Delete Connection',
|
||||||
|
itLooksLikeYouHaveBeenEditingSomething: 'It looks like you have been editing something. If you leave before saving, your changes will be lost.',
|
||||||
|
thereWasAProblemLoadingTheNodeParametersOfNode: 'There was a problem loading the node-parameters of node',
|
||||||
|
noNodesGivenToAdd: 'No nodes given to add!',
|
||||||
showError: {
|
showError: {
|
||||||
getWorkflowDataFromUrl: {
|
getWorkflowDataFromUrl: {
|
||||||
message: 'There was a problem loading the workflow data from URL',
|
message: 'There was a problem loading the workflow data from URL',
|
||||||
|
@ -433,19 +553,19 @@ export default {
|
||||||
title: 'Init Problem',
|
title: 'Init Problem',
|
||||||
},
|
},
|
||||||
openExecution: {
|
openExecution: {
|
||||||
message: 'There was a problem opening the execution',
|
message: '',
|
||||||
title: 'Problem loading execution',
|
title: 'Problem loading execution',
|
||||||
},
|
},
|
||||||
openWorkflow: {
|
openWorkflow: {
|
||||||
message: 'There was a problem opening the workflow',
|
message: '',
|
||||||
title: 'Problem opening workflow',
|
title: 'Problem opening workflow',
|
||||||
},
|
},
|
||||||
stopExecution: {
|
stopExecution: {
|
||||||
message: 'There was a problem stopping the execuction',
|
message: '',
|
||||||
title: 'Problem stopping execution',
|
title: 'Problem stopping execution',
|
||||||
},
|
},
|
||||||
stopWaitingForWebhook: {
|
stopWaitingForWebhook: {
|
||||||
message: 'There was a problem deleting webhook',
|
message: '',
|
||||||
title: 'Problem deleting the test-webhook',
|
title: 'Problem deleting the test-webhook',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -455,13 +575,13 @@ export default {
|
||||||
title: 'Could not create node!',
|
title: 'Could not create node!',
|
||||||
},
|
},
|
||||||
keyDown: {
|
keyDown: {
|
||||||
message: 'A new workflow got created!',
|
message: '',
|
||||||
title: 'Created',
|
title: 'Workflow created',
|
||||||
},
|
},
|
||||||
showMaxNodeTypeError: {
|
showMaxNodeTypeError: {
|
||||||
message: {
|
message: {
|
||||||
singular: 'Node can not be created because in a workflow max. {maxNodes} node of type {nodeTypeDataDisplayName} is allowed!',
|
singular: 'Node cannot be created because in a workflow max. {maxNodes} node of type {nodeTypeDataDisplayName} is allowed!',
|
||||||
plural: 'Node can not be created because in a workflow max. {maxNodes} nodes of type {nodeTypeDataDisplayName} are allowed!',
|
plural: 'Node cannot be created because in a workflow max. {maxNodes} nodes of type {nodeTypeDataDisplayName} are allowed!',
|
||||||
},
|
},
|
||||||
title: 'Could not create node!',
|
title: 'Could not create node!',
|
||||||
},
|
},
|
||||||
|
@ -474,7 +594,7 @@ export default {
|
||||||
title: 'Execution stopped',
|
title: 'Execution stopped',
|
||||||
},
|
},
|
||||||
stopWaitingForWebhook: {
|
stopWaitingForWebhook: {
|
||||||
message: 'The webhook got deleted!',
|
message: '',
|
||||||
title: 'Webhook got deleted',
|
title: 'Webhook got deleted',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -482,7 +602,12 @@ export default {
|
||||||
stoppingCurrentExecution: 'Stopping current execution',
|
stoppingCurrentExecution: 'Stopping current execution',
|
||||||
},
|
},
|
||||||
nodeWebhooks: {
|
nodeWebhooks: {
|
||||||
displayUrlFor: 'Display URL for',
|
testUrl: 'Test URL',
|
||||||
|
productionUrl: 'Production URL',
|
||||||
|
clickToDisplayWebhookUrls: 'Click to display Webhook URLs',
|
||||||
|
clickToHideWebhookUrls: 'Click to hide Webhook URLs',
|
||||||
|
clickToCopyWebhookUrls: 'Click to copy Webhook URLs',
|
||||||
|
invalidExpression: '[INVALID EXPRESSION]',
|
||||||
showMessage: {
|
showMessage: {
|
||||||
message: 'The webhook URL was successfully copied!',
|
message: 'The webhook URL was successfully copied!',
|
||||||
title: 'Copied',
|
title: 'Copied',
|
||||||
|
@ -490,33 +615,66 @@ export default {
|
||||||
webhookUrls: 'Webhook URLs',
|
webhookUrls: 'Webhook URLs',
|
||||||
},
|
},
|
||||||
noTagsView: {
|
noTagsView: {
|
||||||
createATag: 'Create a tag',
|
|
||||||
readyToOrganizeYourWorkflows: 'Ready to organize your workflows?',
|
readyToOrganizeYourWorkflows: 'Ready to organize your workflows?',
|
||||||
withWorkflowTagsYouReFree: 'With workflow tags, you\'re free to create the perfect tagging system for your flows',
|
withWorkflowTagsYouReFree: 'With workflow tags, you\'re free to create the perfect tagging system for your flows',
|
||||||
},
|
},
|
||||||
oauth2: {
|
|
||||||
clientId: 'Client ID',
|
|
||||||
clientSecret: 'Client Secret',
|
|
||||||
},
|
|
||||||
parameterInput: {
|
parameterInput: {
|
||||||
addExpression: 'Add Expression',
|
addExpression: 'Add Expression',
|
||||||
removeExpression: 'Remove Expression',
|
removeExpression: 'Remove Expression',
|
||||||
|
refreshList: 'Refresh List',
|
||||||
resetValue: 'Reset Value',
|
resetValue: 'Reset Value',
|
||||||
selectDateAndTime: 'Select date and time',
|
selectDateAndTime: 'Select date and time',
|
||||||
|
openEditWindow: 'Open Edit Window',
|
||||||
|
issues: 'Issues',
|
||||||
|
parameterOptions: 'Parameter Options',
|
||||||
|
},
|
||||||
|
parameterInputExpanded: {
|
||||||
|
thisFieldIsRequired: 'This field is required.',
|
||||||
|
},
|
||||||
|
parameterInputList: {
|
||||||
|
delete: 'Delete',
|
||||||
|
parameterOptions: 'Parameter Options',
|
||||||
|
deleteParameter: 'Delete Parameter',
|
||||||
|
},
|
||||||
|
personalizationModal: {
|
||||||
|
lookOutForThingsMarked: 'Look out for things marked with a ✨. They are personalized to make n8n more relevant to you.',
|
||||||
|
getStarted: 'Get started',
|
||||||
|
thanks: 'Thanks!',
|
||||||
|
theseQuestionsHelpUs: 'These questions help us tailor n8n to you',
|
||||||
|
whichOfTheseAreasDoYouMainlyWorkIn: 'Which of these areas do you mainly work in?',
|
||||||
|
select: 'Select...',
|
||||||
|
automationConsulting: 'Automation consulting',
|
||||||
|
finance: 'finance',
|
||||||
|
legal: 'legal',
|
||||||
|
operations: 'operations',
|
||||||
|
hr: 'HR',
|
||||||
|
itEngineering: 'IT / Engineering',
|
||||||
|
marketingGrowth: 'Marketing / Growth',
|
||||||
|
product: 'Product',
|
||||||
|
salesBusinessDevelopment: 'Sales / Business Development',
|
||||||
|
security: 'Security',
|
||||||
|
support: 'Support',
|
||||||
|
otherPleaseSpecify: 'Other (please specify)',
|
||||||
|
specifyYourWorkArea: 'Specify your work area',
|
||||||
|
howAreYourCodingSkills: 'How are your coding skills',
|
||||||
|
neverCoded: 'Never coded',
|
||||||
|
proCoder: 'Pro coder',
|
||||||
|
howBigIsYourCompany: 'How big is your company',
|
||||||
|
lessThan20people: 'Less than 20 people',
|
||||||
|
people: 'people',
|
||||||
|
imNotUsingN8nForWork: "I'm not using n8n for work",
|
||||||
|
continue: 'Continue',
|
||||||
|
errorWhileSubmittingResults: 'Error while submitting results',
|
||||||
|
},
|
||||||
|
showMessage: {
|
||||||
|
showDetails: 'Show Details',
|
||||||
|
ok: 'OK',
|
||||||
|
cancel: 'Cancel',
|
||||||
},
|
},
|
||||||
pushConnection: {
|
pushConnection: {
|
||||||
showMessage: {
|
showMessage: {
|
||||||
runDataExecutedFinishedFalse: {
|
message: '',
|
||||||
message: {
|
title: 'Workflow executed successfully',
|
||||||
errorMessage1: 'There was a problem executing the workflow!',
|
|
||||||
errorMessage2: 'There was a problem executing the workflow:<br /><strong>{receivedError}</strong>',
|
|
||||||
},
|
|
||||||
title: 'Problem executing workflow',
|
|
||||||
},
|
|
||||||
runDataExecutedFinishedTrue: {
|
|
||||||
message: 'Workflow did get executed successfully!',
|
|
||||||
title: 'Workflow got executed',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pushConnectionTracker: {
|
pushConnectionTracker: {
|
||||||
|
@ -524,13 +682,18 @@ export default {
|
||||||
connectionLost: 'Connection lost',
|
connectionLost: 'Connection lost',
|
||||||
},
|
},
|
||||||
runData: {
|
runData: {
|
||||||
|
executesThisNodeAfterExecuting: 'Executes this {nodeName} node after executing any previous nodes that have not yet returned data',
|
||||||
copyItemPath: 'Copy Item Path',
|
copyItemPath: 'Copy Item Path',
|
||||||
copyParameterPath: 'Copy Parameter Path',
|
copyParameterPath: 'Copy Parameter Path',
|
||||||
|
copyToClipboard: 'Copy to Clipboard',
|
||||||
copyValue: 'Copy Value',
|
copyValue: 'Copy Value',
|
||||||
dataOfExecution: 'Data of Execution',
|
dataOfExecution: 'Data of Execution',
|
||||||
dataReturnedByTheNodeWillDisplayHere: 'Data returned by this node will display here.',
|
dataReturnedByThisNodeWillDisplayHere: 'Data returned by this node will display here.',
|
||||||
displayDataAnyway: 'Display Data Anyway',
|
displayDataAnyway: 'Display Data Anyway',
|
||||||
entriesExistButThey: 'Entries exist but they do not contain any JSON data.',
|
entriesExistButThey: 'Entries exist but they do not contain any JSON data.',
|
||||||
|
json: 'JSON',
|
||||||
|
table: 'Table',
|
||||||
|
binary: 'Binary',
|
||||||
executeNode: 'Execute Node',
|
executeNode: 'Execute Node',
|
||||||
executionTime: 'Execution Time',
|
executionTime: 'Execution Time',
|
||||||
fileExtension: 'File Extension',
|
fileExtension: 'File Extension',
|
||||||
|
@ -547,11 +710,13 @@ export default {
|
||||||
startTime: 'Start Time',
|
startTime: 'Start Time',
|
||||||
theNodeContains: 'The node contains {numberOfKb} KB of data.<br />Displaying it could cause problems!<br /><br />If you do decide to display it, avoid the JSON view!',
|
theNodeContains: 'The node contains {numberOfKb} KB of data.<br />Displaying it could cause problems!<br /><br />If you do decide to display it, avoid the JSON view!',
|
||||||
},
|
},
|
||||||
saveWorkflowButton: {
|
saveButton: {
|
||||||
save: 'Save',
|
save: 'Save',
|
||||||
|
saving: 'Saving',
|
||||||
saved: 'Saved',
|
saved: 'Saved',
|
||||||
},
|
},
|
||||||
tagsDropdown: {
|
tagsDropdown: {
|
||||||
|
createTag: 'Create tag "{filter}"',
|
||||||
manageTags: 'Manage tags',
|
manageTags: 'Manage tags',
|
||||||
noMatchingTagsExist: 'No matching tags exist',
|
noMatchingTagsExist: 'No matching tags exist',
|
||||||
noTagsExist: 'No tags exist',
|
noTagsExist: 'No tags exist',
|
||||||
|
@ -565,6 +730,7 @@ export default {
|
||||||
couldNotDeleteTag: 'Could not delete tag',
|
couldNotDeleteTag: 'Could not delete tag',
|
||||||
done: 'Done',
|
done: 'Done',
|
||||||
manageTags: 'Manage tags',
|
manageTags: 'Manage tags',
|
||||||
|
tagNameCannotBeEmpty: 'Tag name cannot be empty',
|
||||||
showError: {
|
showError: {
|
||||||
onCreate: {
|
onCreate: {
|
||||||
message: 'A problem occurred when trying to create the {escapedName} tag',
|
message: 'A problem occurred when trying to create the {escapedName} tag',
|
||||||
|
@ -591,13 +757,14 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tagsTable: {
|
tagsTable: {
|
||||||
|
noMatchingTagsExist: 'No matching tags exist',
|
||||||
|
saveChanges: 'Save changes?',
|
||||||
areYouSureYouWantToDeleteThisTag: 'Are you sure you want to delete this tag?',
|
areYouSureYouWantToDeleteThisTag: 'Are you sure you want to delete this tag?',
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
createTag: 'Create tag',
|
createTag: 'Create tag',
|
||||||
deleteTag: 'Delete tag',
|
deleteTag: 'Delete tag',
|
||||||
editTag: 'Edit Tag',
|
editTag: 'Edit Tag',
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
saveChanges: 'Save changes',
|
|
||||||
usage: 'Usage',
|
usage: 'Usage',
|
||||||
},
|
},
|
||||||
tagsTableHeader: {
|
tagsTableHeader: {
|
||||||
|
@ -634,7 +801,7 @@ export default {
|
||||||
title: 'Problem activating workflow',
|
title: 'Problem activating workflow',
|
||||||
},
|
},
|
||||||
activeChangedWorkflowIdUndefined: {
|
activeChangedWorkflowIdUndefined: {
|
||||||
message: 'The workflow did not get saved yet so can not be set active!',
|
message: 'The workflow did not get saved yet so cannot be set active!',
|
||||||
title: 'Problem activating workflow',
|
title: 'Problem activating workflow',
|
||||||
},
|
},
|
||||||
displayActivationError: {
|
displayActivationError: {
|
||||||
|
@ -658,14 +825,8 @@ export default {
|
||||||
},
|
},
|
||||||
workflowHelpers: {
|
workflowHelpers: {
|
||||||
showMessage: {
|
showMessage: {
|
||||||
saveAsNewWorkflow: {
|
message: '',
|
||||||
message: 'There was a problem saving the workflow',
|
title: 'Problem saving workflow',
|
||||||
title: 'Problem saving workflow',
|
|
||||||
},
|
|
||||||
saveCurrentWorkflow: {
|
|
||||||
message: 'There was a problem saving the workflow',
|
|
||||||
title: 'Problem saving workflow',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
workflowOpen: {
|
workflowOpen: {
|
||||||
|
@ -677,10 +838,9 @@ export default {
|
||||||
message: 'When you switch workflows your current workflow changes will be lost.',
|
message: 'When you switch workflows your current workflow changes will be lost.',
|
||||||
},
|
},
|
||||||
created: 'Created',
|
created: 'Created',
|
||||||
filterByTags: 'Filter by tags...',
|
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
openWorkflow: 'Open Workflow',
|
openWorkflow: 'Open Workflow',
|
||||||
searchWorkflows: 'Deutsch',
|
searchWorkflows: 'Search workflows...',
|
||||||
showError: {
|
showError: {
|
||||||
message: 'There was a problem loading the workflows',
|
message: 'There was a problem loading the workflows',
|
||||||
title: 'Problem loading workflows',
|
title: 'Problem loading workflows',
|
||||||
|
@ -692,17 +852,18 @@ export default {
|
||||||
updated: 'Updated',
|
updated: 'Updated',
|
||||||
},
|
},
|
||||||
workflowRun: {
|
workflowRun: {
|
||||||
|
noActiveConnectionToTheServer: 'No active connection to server. It is maybe down.',
|
||||||
showError: {
|
showError: {
|
||||||
message: 'There was a problem running the workflow',
|
message: '',
|
||||||
title: 'Problem running workflow',
|
title: 'Problem running workflow',
|
||||||
},
|
},
|
||||||
showMessage: {
|
showMessage: {
|
||||||
message: 'The workflow has issues. Please fix them first',
|
message: 'The workflow has issues. Please fix them first',
|
||||||
title: 'Workflow can not be executed',
|
title: 'Workflow cannot be executed',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
workflowSettings: {
|
workflowSettings: {
|
||||||
editExpression: 'Edit Expression',
|
settingsFor: 'Settings for {workflowName} (#{workflowId})',
|
||||||
errorWorkflow: 'Error Workflow',
|
errorWorkflow: 'Error Workflow',
|
||||||
helpTexts: {
|
helpTexts: {
|
||||||
errorWorkflow: 'The workflow to run in case the current one fails.<br />To function correctly that workflow has to contain an \'Error Trigger\' node!',
|
errorWorkflow: 'The workflow to run in case the current one fails.<br />To function correctly that workflow has to contain an \'Error Trigger\' node!',
|
||||||
|
@ -714,6 +875,8 @@ export default {
|
||||||
saveManualExecutions: 'If data data of executions should be saved when started manually from the editor.',
|
saveManualExecutions: 'If data data of executions should be saved when started manually from the editor.',
|
||||||
timezone: 'The timezone in which the workflow should run. Gets for example used by \'Cron\' node.',
|
timezone: 'The timezone in which the workflow should run. Gets for example used by \'Cron\' node.',
|
||||||
},
|
},
|
||||||
|
defaultTimezoneNotValid: 'Default Timezone not valid',
|
||||||
|
defaultTimezone: 'Default - {defaultTimezoneValue}',
|
||||||
hours: 'hours',
|
hours: 'hours',
|
||||||
minutes: 'minutes',
|
minutes: 'minutes',
|
||||||
noWorkflow: '- No Workflow -',
|
noWorkflow: '- No Workflow -',
|
||||||
|
@ -743,13 +906,10 @@ export default {
|
||||||
yes: 'Yes',
|
yes: 'Yes',
|
||||||
},
|
},
|
||||||
seconds: 'seconds',
|
seconds: 'seconds',
|
||||||
|
selectOption: 'selectOption',
|
||||||
showError: {
|
showError: {
|
||||||
openDialog: {
|
|
||||||
message: 'The following error occurred loading the data',
|
|
||||||
title: 'Problem loading settings',
|
|
||||||
},
|
|
||||||
saveSettings1: {
|
saveSettings1: {
|
||||||
errorMessage: 'timeout is activated but set to 0',
|
errorMessage: 'Timeout is activated but set to 0',
|
||||||
message: 'There was a problem saving the settings',
|
message: 'There was a problem saving the settings',
|
||||||
title: 'Problem saving settings',
|
title: 'Problem saving settings',
|
||||||
},
|
},
|
||||||
|
@ -764,10 +924,6 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
showMessage: {
|
showMessage: {
|
||||||
openDialog: {
|
|
||||||
message: 'No workflow active to display settings of.',
|
|
||||||
title: 'No workflow active',
|
|
||||||
},
|
|
||||||
saveSettings: {
|
saveSettings: {
|
||||||
message: 'The workflow settings got saved!',
|
message: 'The workflow settings got saved!',
|
||||||
title: 'Settings saved',
|
title: 'Settings saved',
|
||||||
|
@ -776,9 +932,8 @@ export default {
|
||||||
timeoutAfter: 'Timeout After',
|
timeoutAfter: 'Timeout After',
|
||||||
timeoutWorkflow: 'Timeout Workflow',
|
timeoutWorkflow: 'Timeout Workflow',
|
||||||
timezone: 'Timezone',
|
timezone: 'Timezone',
|
||||||
workflowSettings: 'Workflow Settings',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
'n8n-nodes-base': {}, // required for node translation
|
'n8n-nodes-base': {}, // required for node translation
|
||||||
},
|
},
|
||||||
};
|
};
|
|
@ -179,7 +179,7 @@ body {
|
||||||
|
|
||||||
// Notification
|
// Notification
|
||||||
.el-notification {
|
.el-notification {
|
||||||
border-radius: 0;
|
border-radius: 4px;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DataDisplay @valueChanged="valueChanged"/>
|
<DataDisplay @valueChanged="valueChanged"/>
|
||||||
<div v-if="!createNodeActive && !isReadOnly" class="node-creator-button" title="Add Node" @click="openNodeCreator">
|
<div v-if="!createNodeActive && !isReadOnly" class="node-creator-button" :title="$baseText('nodeView.addNode')" @click="openNodeCreator">
|
||||||
<n8n-icon-button size="xlarge" icon="plus" />
|
<n8n-icon-button size="xlarge" icon="plus" />
|
||||||
</div>
|
</div>
|
||||||
<node-creator
|
<node-creator
|
||||||
|
@ -40,22 +40,22 @@
|
||||||
@closeNodeCreator="closeNodeCreator"
|
@closeNodeCreator="closeNodeCreator"
|
||||||
></node-creator>
|
></node-creator>
|
||||||
<div :class="{ 'zoom-menu': true, expanded: !sidebarMenuCollapsed }">
|
<div :class="{ 'zoom-menu': true, expanded: !sidebarMenuCollapsed }">
|
||||||
<button @click="zoomToFit" class="button-white" title="Zoom to Fit">
|
<button @click="zoomToFit" class="button-white" :title="$baseText('nodeView.zoomToFit')">
|
||||||
<font-awesome-icon icon="expand"/>
|
<font-awesome-icon icon="expand"/>
|
||||||
</button>
|
</button>
|
||||||
<button @click="zoomIn()" class="button-white" title="Zoom In">
|
<button @click="zoomIn()" class="button-white" :title="$baseText('nodeView.zoomIn')">
|
||||||
<font-awesome-icon icon="search-plus"/>
|
<font-awesome-icon icon="search-plus"/>
|
||||||
</button>
|
</button>
|
||||||
<button @click="zoomOut()" class="button-white" title="Zoom Out">
|
<button @click="zoomOut()" class="button-white" :title="$baseText('nodeView.zoomOut')">
|
||||||
<font-awesome-icon icon="search-minus"/>
|
<font-awesome-icon icon="search-minus"/>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="nodeViewScale !== 1"
|
v-if="nodeViewScale !== 1"
|
||||||
@click="resetZoom()"
|
@click="resetZoom()"
|
||||||
class="button-white"
|
class="button-white"
|
||||||
title="Reset Zoom"
|
:title="$baseText('nodeView.resetZoom')"
|
||||||
>
|
>
|
||||||
<font-awesome-icon icon="undo" title="Reset Zoom"/>
|
<font-awesome-icon icon="undo" :title="$baseText('nodeView.resetZoom')"/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="workflow-execute-wrapper" v-if="!isReadOnly">
|
<div class="workflow-execute-wrapper" v-if="!isReadOnly">
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
:label="runButtonText"
|
:label="runButtonText"
|
||||||
size="large"
|
size="large"
|
||||||
icon="play-circle"
|
icon="play-circle"
|
||||||
title="Executes the Workflow from the Start or Webhook Node."
|
:title="$baseText('nodeView.executesTheWorkflowFromTheStartOrWebhookNode')"
|
||||||
:type="workflowRunning ? 'light' : 'primary'"
|
:type="workflowRunning ? 'light' : 'primary'"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -75,7 +75,10 @@
|
||||||
size="large"
|
size="large"
|
||||||
class="stop-execution"
|
class="stop-execution"
|
||||||
type="light"
|
type="light"
|
||||||
:title="stopExecutionInProgress ? 'Stopping current execution':'Stop current execution'"
|
:title="stopExecutionInProgress
|
||||||
|
? $baseText('nodeView.stoppingCurrentExecution')
|
||||||
|
: $baseText('nodeView.stopCurrentExecution')
|
||||||
|
"
|
||||||
:loading="stopExecutionInProgress"
|
:loading="stopExecutionInProgress"
|
||||||
@click.stop="stopExecution()"
|
@click.stop="stopExecution()"
|
||||||
/>
|
/>
|
||||||
|
@ -85,14 +88,14 @@
|
||||||
class="stop-execution"
|
class="stop-execution"
|
||||||
icon="stop"
|
icon="stop"
|
||||||
size="large"
|
size="large"
|
||||||
title="Stop waiting for Webhook call"
|
:title="$baseText('nodeView.stopWaitingForWebhookCall')"
|
||||||
type="light"
|
type="light"
|
||||||
@click.stop="stopWaitingForWebhook()"
|
@click.stop="stopWaitingForWebhook()"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<n8n-icon-button
|
<n8n-icon-button
|
||||||
v-if="!isReadOnly && workflowExecution && !workflowRunning"
|
v-if="!isReadOnly && workflowExecution && !workflowRunning"
|
||||||
title="Deletes the current Execution Data."
|
:title="$baseText('nodeView.deletesTheCurrentExecutionData')"
|
||||||
icon="trash"
|
icon="trash"
|
||||||
size="large"
|
size="large"
|
||||||
@click.stop="clearExecutionData()"
|
@click.stop="clearExecutionData()"
|
||||||
|
@ -122,6 +125,7 @@ import { newVersions } from '@/components/mixins/newVersions';
|
||||||
|
|
||||||
import { workflowHelpers } from '@/components/mixins/workflowHelpers';
|
import { workflowHelpers } from '@/components/mixins/workflowHelpers';
|
||||||
import { workflowRun } from '@/components/mixins/workflowRun';
|
import { workflowRun } from '@/components/mixins/workflowRun';
|
||||||
|
import { renderText } from '@/components/mixins/renderText';
|
||||||
|
|
||||||
import DataDisplay from '@/components/DataDisplay.vue';
|
import DataDisplay from '@/components/DataDisplay.vue';
|
||||||
import Modals from '@/components/Modals.vue';
|
import Modals from '@/components/Modals.vue';
|
||||||
|
@ -192,6 +196,7 @@ export default mixins(
|
||||||
mouseSelect,
|
mouseSelect,
|
||||||
moveNodeWorkflow,
|
moveNodeWorkflow,
|
||||||
restApi,
|
restApi,
|
||||||
|
renderText,
|
||||||
showMessage,
|
showMessage,
|
||||||
titleChange,
|
titleChange,
|
||||||
workflowHelpers,
|
workflowHelpers,
|
||||||
|
@ -241,14 +246,20 @@ export default mixins(
|
||||||
},
|
},
|
||||||
|
|
||||||
defaultLocale (newLocale, oldLocale) {
|
defaultLocale (newLocale, oldLocale) {
|
||||||
console.log(`Switching locale from ${oldLocale} to ${newLocale}`);
|
console.log(`Switching locale from ${oldLocale} to ${newLocale}`); // eslint-disable-line no-console
|
||||||
loadLanguage(newLocale);
|
loadLanguage(newLocale);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async beforeRouteLeave(to, from, next) {
|
async beforeRouteLeave(to, from, next) {
|
||||||
const result = this.$store.getters.getStateIsDirty;
|
const result = this.$store.getters.getStateIsDirty;
|
||||||
if(result) {
|
if(result) {
|
||||||
const importConfirm = await this.confirmMessage(`When you switch workflows your current workflow changes will be lost.`, 'Save your Changes?', 'warning', 'Yes, switch workflows and forget changes');
|
const importConfirm = await this.confirmMessage(
|
||||||
|
this.$baseText('nodeView.confirmMessage.beforeRouteLeave.message'),
|
||||||
|
this.$baseText('nodeView.confirmMessage.beforeRouteLeave.headline'),
|
||||||
|
'warning',
|
||||||
|
this.$baseText('nodeView.confirmMessage.beforeRouteLeave.confirmButtonText'),
|
||||||
|
this.$baseText('nodeView.confirmMessage.beforeRouteLeave.cancelButtonText'),
|
||||||
|
);
|
||||||
if (importConfirm === false) {
|
if (importConfirm === false) {
|
||||||
next(false);
|
next(false);
|
||||||
} else {
|
} else {
|
||||||
|
@ -284,14 +295,14 @@ export default mixins(
|
||||||
},
|
},
|
||||||
runButtonText (): string {
|
runButtonText (): string {
|
||||||
if (this.workflowRunning === false) {
|
if (this.workflowRunning === false) {
|
||||||
return 'Execute Workflow';
|
return this.$baseText('nodeView.runButtonText.executeWorkflow');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.executionWaitingForWebhook === true) {
|
if (this.executionWaitingForWebhook === true) {
|
||||||
return 'Waiting for Webhook-Call';
|
return this.$baseText('nodeView.runButtonText.waitingForWebhookCall');
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Executing Workflow';
|
return this.$baseText('nodeView.runButtonText.executingWorkflow');
|
||||||
},
|
},
|
||||||
workflowStyle (): object {
|
workflowStyle (): object {
|
||||||
const offsetPosition = this.$store.getters.getNodeViewOffsetPosition;
|
const offsetPosition = this.$store.getters.getNodeViewOffsetPosition;
|
||||||
|
@ -364,7 +375,11 @@ export default mixins(
|
||||||
try {
|
try {
|
||||||
data = await this.restApi().getExecution(executionId);
|
data = await this.restApi().getExecution(executionId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(error, 'Problem loading execution', 'There was a problem opening the execution:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('nodeView.showError.openExecution.title'),
|
||||||
|
this.$baseText('nodeView.showError.openExecution.message') + ':',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,15 +438,15 @@ export default mixins(
|
||||||
|
|
||||||
if ((data as IExecutionsSummary).waitTill) {
|
if ((data as IExecutionsSummary).waitTill) {
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: `This execution hasn't finished yet`,
|
title: this.$baseText('nodeView.thisExecutionHasntFinishedYet'),
|
||||||
message: `<a onclick="window.location.reload(false);">Refresh</a> to see the latest status.<br/> <a href="https://docs.n8n.io/nodes/n8n-nodes-base.wait/" target="_blank">More info</a>`,
|
message: `<a onclick="window.location.reload(false);">${this.$baseText('nodeView.refresh')}</a> ${this.$baseText('nodeView.toSeeTheLatestStatus')}.<br/> <a href="https://docs.n8n.io/nodes/n8n-nodes-base.wait/" target="_blank">${this.$baseText('nodeView.moreInfo')}</a>`,
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
duration: 0,
|
duration: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async openWorkflowTemplate (templateId: string) {
|
async openWorkflowTemplate (templateId: string) {
|
||||||
this.setLoadingText('Loading template');
|
this.setLoadingText(this.$baseText('nodeView.loadingTemplate'));
|
||||||
this.resetWorkspace();
|
this.resetWorkspace();
|
||||||
|
|
||||||
let data: IWorkflowTemplate | undefined;
|
let data: IWorkflowTemplate | undefined;
|
||||||
|
@ -440,7 +455,12 @@ export default mixins(
|
||||||
data = await this.$store.dispatch('workflows/getWorkflowTemplate', templateId);
|
data = await this.$store.dispatch('workflows/getWorkflowTemplate', templateId);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
throw new Error(`Workflow template with id "${templateId}" could not be found!`);
|
throw new Error(
|
||||||
|
this.$baseText(
|
||||||
|
'nodeView.workflowTemplateWithIdCouldNotBeFound',
|
||||||
|
{ interpolate: { templateId } },
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
data.workflow.nodes.forEach((node) => {
|
data.workflow.nodes.forEach((node) => {
|
||||||
|
@ -450,7 +470,7 @@ export default mixins(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(error, `Couldn't import workflow`);
|
this.$showError(error, this.$baseText('nodeView.couldntImportWorkflow'));
|
||||||
this.$router.push({ name: 'NodeViewNew' });
|
this.$router.push({ name: 'NodeViewNew' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -491,12 +511,21 @@ export default mixins(
|
||||||
try {
|
try {
|
||||||
data = await this.restApi().getWorkflow(workflowId);
|
data = await this.restApi().getWorkflow(workflowId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(error, 'Problem opening workflow', 'There was a problem opening the workflow:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('nodeView.showError.openWorkflow.title'),
|
||||||
|
this.$baseText('nodeView.showError.openWorkflow.message') + ':',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data === undefined) {
|
if (data === undefined) {
|
||||||
throw new Error(`Workflow with id "${workflowId}" could not be found!`);
|
throw new Error(
|
||||||
|
this.$baseText(
|
||||||
|
'nodeView.workflowWithIdCouldNotBeFound',
|
||||||
|
{ interpolate: { workflowId } },
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.commit('setActive', data.active || false);
|
this.$store.commit('setActive', data.active || false);
|
||||||
|
@ -656,8 +685,8 @@ export default mixins(
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Workflow created',
|
title: this.$baseText('nodeView.showMessage.keyDown.title'),
|
||||||
message: 'A new workflow was successfully created!',
|
message: this.$baseText('nodeView.showMessage.keyDown.message'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
} else if ((e.key === 's') && (this.isCtrlKeyPressed(e) === true)) {
|
} else if ((e.key === 's') && (this.isCtrlKeyPressed(e) === true)) {
|
||||||
|
@ -942,8 +971,11 @@ export default mixins(
|
||||||
this.stopExecutionInProgress = true;
|
this.stopExecutionInProgress = true;
|
||||||
await this.restApi().stopCurrentExecution(executionId);
|
await this.restApi().stopCurrentExecution(executionId);
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Execution stopped',
|
title: this.$baseText('nodeView.showMessage.stopExecutionTry.title'),
|
||||||
message: `The execution with the id "${executionId}" was stopped!`,
|
message: this.$baseText(
|
||||||
|
'nodeView.showMessage.stopExecutionTry.message',
|
||||||
|
{ interpolate: { executionId } },
|
||||||
|
),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -968,12 +1000,16 @@ export default mixins(
|
||||||
this.$store.commit('setWorkflowExecutionData', executedData);
|
this.$store.commit('setWorkflowExecutionData', executedData);
|
||||||
this.$store.commit('removeActiveAction', 'workflowRunning');
|
this.$store.commit('removeActiveAction', 'workflowRunning');
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Workflow finished executing',
|
title: this.$baseText('nodeView.showMessage.stopExecutionCatch.title'),
|
||||||
message: 'Unable to stop operation in time. Workflow finished executing already.',
|
message: this.$baseText('nodeView.showMessage.stopExecutionCatch.message'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.$showError(error, 'Problem stopping execution', 'There was a problem stopping the execuction:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('nodeView.showError.stopExecution.title'),
|
||||||
|
this.$baseText('nodeView.showError.stopExecution.message') + ':',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.stopExecutionInProgress = false;
|
this.stopExecutionInProgress = false;
|
||||||
|
@ -983,13 +1019,17 @@ export default mixins(
|
||||||
try {
|
try {
|
||||||
await this.restApi().removeTestWebhook(this.$store.getters.workflowId);
|
await this.restApi().removeTestWebhook(this.$store.getters.workflowId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(error, 'Problem deleting the test-webhook', 'There was a problem deleting webhook:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('nodeView.showError.stopWaitingForWebhook.title'),
|
||||||
|
this.$baseText('nodeView.showError.stopWaitingForWebhook.message') + ':',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Webhook deleted',
|
title: this.$baseText('nodeView.showMessage.stopWaitingForWebhook.title'),
|
||||||
message: `The webhook was deleted successfully`,
|
message: this.$baseText('nodeView.showMessage.stopWaitingForWebhook.message'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -1008,7 +1048,16 @@ export default mixins(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const importConfirm = await this.confirmMessage(`Import workflow from this URL:<br /><i>${plainTextData}<i>`, 'Import Workflow from URL?', 'warning', 'Yes, import!');
|
const importConfirm = await this.confirmMessage(
|
||||||
|
this.$baseText(
|
||||||
|
'nodeView.confirmMessage.receivedCopyPasteData.message',
|
||||||
|
{ interpolate: { plainTextData } },
|
||||||
|
),
|
||||||
|
this.$baseText('nodeView.confirmMessage.receivedCopyPasteData.headline'),
|
||||||
|
'warning',
|
||||||
|
this.$baseText('nodeView.confirmMessage.receivedCopyPasteData.confirmButtonText'),
|
||||||
|
this.$baseText('nodeView.confirmMessage.receivedCopyPasteData.cancelButtonText'),
|
||||||
|
);
|
||||||
|
|
||||||
if (importConfirm === false) {
|
if (importConfirm === false) {
|
||||||
return;
|
return;
|
||||||
|
@ -1050,7 +1099,11 @@ export default mixins(
|
||||||
workflowData = await this.restApi().getWorkflowFromUrl(url);
|
workflowData = await this.restApi().getWorkflowFromUrl(url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.stopLoading();
|
this.stopLoading();
|
||||||
this.$showError(error, 'Problem loading workflow', 'There was a problem loading the workflow data from URL:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('nodeView.showError.getWorkflowDataFromUrl.title'),
|
||||||
|
this.$baseText('nodeView.showError.getWorkflowDataFromUrl.message') + ':',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.stopLoading();
|
this.stopLoading();
|
||||||
|
@ -1088,7 +1141,11 @@ export default mixins(
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(error, 'Problem importing workflow', 'There was a problem importing workflow data:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('nodeView.showError.importWorkflowData.title'),
|
||||||
|
this.$baseText('nodeView.showError.importWorkflowData.message') + ':',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1213,8 +1270,18 @@ export default mixins(
|
||||||
showMaxNodeTypeError (nodeTypeData: INodeTypeDescription) {
|
showMaxNodeTypeError (nodeTypeData: INodeTypeDescription) {
|
||||||
const maxNodes = nodeTypeData.maxNodes;
|
const maxNodes = nodeTypeData.maxNodes;
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Could not create node!',
|
title: this.$baseText('nodeView.showMessage.showMaxNodeTypeError.title'),
|
||||||
message: `Node can not be created because in a workflow max. ${maxNodes} ${maxNodes === 1 ? 'node' : 'nodes'} of type "${nodeTypeData.displayName}" ${maxNodes === 1 ? 'is' : 'are'} allowed!`,
|
message: this.$baseText(
|
||||||
|
maxNodes === 1
|
||||||
|
? 'nodeView.showMessage.showMaxNodeTypeError.message.singular'
|
||||||
|
: 'nodeView.showMessage.showMaxNodeTypeError.message.plural',
|
||||||
|
{
|
||||||
|
interpolate: {
|
||||||
|
maxNodes: maxNodes!.toString(),
|
||||||
|
nodeTypeDataDisplayName: nodeTypeData.displayName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
duration: 0,
|
duration: 0,
|
||||||
});
|
});
|
||||||
|
@ -1228,8 +1295,11 @@ export default mixins(
|
||||||
|
|
||||||
if (nodeTypeData === null) {
|
if (nodeTypeData === null) {
|
||||||
this.$showMessage({
|
this.$showMessage({
|
||||||
title: 'Could not create node!',
|
title: this.$baseText('nodeView.showMessage.addNodeButton.title'),
|
||||||
message: `Node of type "${nodeTypeName}" could not be created as it is not known.`,
|
message: this.$baseText(
|
||||||
|
'nodeView.showMessage.addNodeButton.message',
|
||||||
|
{ interpolate: { nodeTypeName } },
|
||||||
|
),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
@ -1361,7 +1431,7 @@ export default mixins(
|
||||||
'Label',
|
'Label',
|
||||||
{
|
{
|
||||||
id: 'drop-add-node',
|
id: 'drop-add-node',
|
||||||
label: 'Drop connection<br />to create node',
|
label: this.$baseText('nodeView.dropConnectionToCreateNode'),
|
||||||
cssClass: 'drop-add-node-label',
|
cssClass: 'drop-add-node-label',
|
||||||
location: 0.5,
|
location: 0.5,
|
||||||
},
|
},
|
||||||
|
@ -1466,7 +1536,7 @@ export default mixins(
|
||||||
'Label',
|
'Label',
|
||||||
{
|
{
|
||||||
id: 'remove-connection',
|
id: 'remove-connection',
|
||||||
label: '<span class="delete-connection clickable" title="Delete Connection">x</span>',
|
label: `<span class="delete-connection clickable" title="${this.$baseText('nodeView.deleteConnection')}">x</span>`,
|
||||||
cssClass: 'remove-connection-label',
|
cssClass: 'remove-connection-label',
|
||||||
visible: false,
|
visible: false,
|
||||||
events: {
|
events: {
|
||||||
|
@ -1644,7 +1714,13 @@ export default mixins(
|
||||||
|
|
||||||
const result = this.$store.getters.getStateIsDirty;
|
const result = this.$store.getters.getStateIsDirty;
|
||||||
if(result) {
|
if(result) {
|
||||||
const importConfirm = await this.confirmMessage(`When you switch workflows your current workflow changes will be lost.`, 'Save your Changes?', 'warning', 'Yes, switch workflows and forget changes');
|
const importConfirm = await this.confirmMessage(
|
||||||
|
this.$baseText('nodeView.confirmMessage.initView.message'),
|
||||||
|
this.$baseText('nodeView.confirmMessage.initView.headline'),
|
||||||
|
'warning',
|
||||||
|
this.$baseText('nodeView.confirmMessage.initView.confirmButtonText'),
|
||||||
|
this.$baseText('nodeView.confirmMessage.initView.cancelButtonText'),
|
||||||
|
);
|
||||||
if (importConfirm === false) {
|
if (importConfirm === false) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
@ -1674,8 +1750,7 @@ export default mixins(
|
||||||
|
|
||||||
window.addEventListener("beforeunload", (e) => {
|
window.addEventListener("beforeunload", (e) => {
|
||||||
if(this.$store.getters.getStateIsDirty === true) {
|
if(this.$store.getters.getStateIsDirty === true) {
|
||||||
const confirmationMessage = 'It looks like you have been editing something. '
|
const confirmationMessage = this.$baseText('nodeView.itLooksLikeYouHaveBeenEditingSomething');
|
||||||
+ 'If you leave before saving, your changes will be lost.';
|
|
||||||
(e || window.event).returnValue = confirmationMessage; //Gecko + IE
|
(e || window.event).returnValue = confirmationMessage; //Gecko + IE
|
||||||
return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
|
return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
|
||||||
} else {
|
} else {
|
||||||
|
@ -1853,13 +1928,17 @@ export default mixins(
|
||||||
},
|
},
|
||||||
async renameNodePrompt (currentName: string) {
|
async renameNodePrompt (currentName: string) {
|
||||||
try {
|
try {
|
||||||
const promptResponsePromise = this.$prompt('New Name:', `Rename Node: "${currentName}"`, {
|
const promptResponsePromise = this.$prompt(
|
||||||
customClass: 'rename-prompt',
|
this.$baseText('nodeView.prompt.newName') + ':',
|
||||||
confirmButtonText: 'Rename',
|
this.$baseText('nodeView.prompt.renameNode') + `: ${currentName}`,
|
||||||
cancelButtonText: 'Cancel',
|
{
|
||||||
inputErrorMessage: 'Invalid Name',
|
customClass: 'rename-prompt',
|
||||||
inputValue: currentName,
|
confirmButtonText: this.$baseText('nodeView.prompt.rename'),
|
||||||
});
|
cancelButtonText: this.$baseText('nodeView.prompt.cancel'),
|
||||||
|
inputErrorMessage: this.$baseText('nodeView.prompt.invalidName'),
|
||||||
|
inputValue: currentName,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Wait till it had time to display
|
// Wait till it had time to display
|
||||||
await Vue.nextTick();
|
await Vue.nextTick();
|
||||||
|
@ -1990,7 +2069,7 @@ export default mixins(
|
||||||
try {
|
try {
|
||||||
nodeParameters = NodeHelpers.getNodeParameters(nodeType.properties, node.parameters, true, false);
|
nodeParameters = NodeHelpers.getNodeParameters(nodeType.properties, node.parameters, true, false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`There was a problem loading the node-parameters of node: "${node.name}"`); // eslint-disable-line no-console
|
console.error(this.$baseText('nodeView.thereWasAProblemLoadingTheNodeParametersOfNode') + `: "${node.name}"`); // eslint-disable-line no-console
|
||||||
console.error(e); // eslint-disable-line no-console
|
console.error(e); // eslint-disable-line no-console
|
||||||
}
|
}
|
||||||
node.parameters = nodeParameters !== null ? nodeParameters : {};
|
node.parameters = nodeParameters !== null ? nodeParameters : {};
|
||||||
|
@ -2065,7 +2144,9 @@ export default mixins(
|
||||||
|
|
||||||
if (!data.nodes) {
|
if (!data.nodes) {
|
||||||
// No nodes to add
|
// No nodes to add
|
||||||
throw new Error('No nodes given to add!');
|
throw new Error(
|
||||||
|
this.$baseText('nodeView.noNodesGivenToAdd'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get how many of the nodes of the types which have
|
// Get how many of the nodes of the types which have
|
||||||
|
@ -2338,7 +2419,11 @@ export default mixins(
|
||||||
try {
|
try {
|
||||||
await Promise.all(loadPromises);
|
await Promise.all(loadPromises);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(error, 'Init Problem', 'There was a problem loading init data:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('nodeView.showError.mounted1.title'),
|
||||||
|
this.$baseText('nodeView.showError.mounted1.message') + ':',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2347,7 +2432,11 @@ export default mixins(
|
||||||
this.initNodeView();
|
this.initNodeView();
|
||||||
await this.initView();
|
await this.initView();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(error, 'Init Problem', 'There was a problem initializing the workflow:');
|
this.$showError(
|
||||||
|
error,
|
||||||
|
this.$baseText('nodeView.showError.mounted2.title'),
|
||||||
|
this.$baseText('nodeView.showError.mounted2.message') + ':',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.stopLoading();
|
this.stopLoading();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue