mirror of
https://github.com/n8n-io/n8n.git
synced 2024-09-11 13:15:28 +03:00
refactor(editor): Fix type errors (no-changelog) (#9584)
This commit is contained in:
parent
c1b1ee57b1
commit
4eb6edc73a
@ -81,13 +81,10 @@ export default defineComponent({
|
||||
return this.workflowsStore.workflow;
|
||||
},
|
||||
currentWorkflow(): string {
|
||||
return this.$route.params.name || this.workflowsStore.workflowId;
|
||||
return String(this.$route.params.name || this.workflowsStore.workflowId);
|
||||
},
|
||||
onWorkflowPage(): boolean {
|
||||
return (
|
||||
this.$route.meta &&
|
||||
(this.$route.meta.nodeView || this.$route.meta.keepWorkflowAlive === true)
|
||||
);
|
||||
return !!(this.$route.meta.nodeView || this.$route.meta.keepWorkflowAlive);
|
||||
},
|
||||
readOnly(): boolean {
|
||||
return this.sourceControlStore.preferences.branchReadOnly;
|
||||
@ -124,11 +121,15 @@ export default defineComponent({
|
||||
this.activeHeaderTab = MAIN_HEADER_TABS.WORKFLOW;
|
||||
}
|
||||
|
||||
if (to.params.name !== 'new') {
|
||||
if (to.params.name !== 'new' && typeof to.params.name === 'string') {
|
||||
this.workflowToReturnTo = to.params.name;
|
||||
}
|
||||
|
||||
if (from?.name === VIEWS.EXECUTION_PREVIEW && to.params.name === from.params.name) {
|
||||
if (
|
||||
from?.name === VIEWS.EXECUTION_PREVIEW &&
|
||||
to.params.name === from.params.name &&
|
||||
typeof from.params.executionId === 'string'
|
||||
) {
|
||||
this.executionToReturnTo = from.params.executionId;
|
||||
}
|
||||
},
|
||||
|
@ -3,7 +3,6 @@ import { createComponentRenderer } from '@/__tests__/render';
|
||||
import { STORES, WORKFLOW_SHARE_MODAL_KEY } from '@/constants';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
|
||||
vi.mock('vue-router', async () => {
|
||||
@ -48,12 +47,10 @@ const renderComponent = createComponentRenderer(WorkflowDetails, {
|
||||
pinia: createTestingPinia({ initialState }),
|
||||
});
|
||||
|
||||
let workflowsStore: ReturnType<typeof useWorkflowsStore>;
|
||||
let uiStore: ReturnType<typeof useUIStore>;
|
||||
|
||||
describe('WorkflowDetails', () => {
|
||||
beforeEach(() => {
|
||||
workflowsStore = useWorkflowsStore();
|
||||
uiStore = useUIStore();
|
||||
});
|
||||
it('renders workflow name and tags', async () => {
|
||||
@ -101,8 +98,6 @@ describe('WorkflowDetails', () => {
|
||||
});
|
||||
|
||||
it('opens share modal on share button click', async () => {
|
||||
vi.spyOn(workflowsStore, 'getWorkflowById', 'get').mockReturnValue(() => ({}));
|
||||
|
||||
const openModalSpy = vi.spyOn(uiStore, 'openModalWithData');
|
||||
|
||||
const { getByTestId } = renderComponent({
|
||||
|
@ -67,8 +67,8 @@
|
||||
>
|
||||
<div :class="{ [$style.avatar]: true, ['clickable']: isCollapsed }">
|
||||
<n8n-avatar
|
||||
:first-name="usersStore.currentUser.firstName"
|
||||
:last-name="usersStore.currentUser.lastName"
|
||||
:first-name="usersStore.currentUser?.firstName"
|
||||
:last-name="usersStore.currentUser?.lastName"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
@ -88,7 +88,7 @@
|
||||
:class="{ ['ml-2xs']: true, [$style.userName]: true, [$style.expanded]: fullyExpanded }"
|
||||
>
|
||||
<n8n-text size="small" :bold="true" color="text-dark">{{
|
||||
usersStore.currentUser.fullName
|
||||
usersStore.currentUser?.fullName
|
||||
}}</n8n-text>
|
||||
</div>
|
||||
<div :class="{ [$style.userActions]: true, [$style.expanded]: fullyExpanded }">
|
||||
@ -142,7 +142,7 @@ export default defineComponent({
|
||||
ProjectNavigation,
|
||||
},
|
||||
mixins: [userHelpers],
|
||||
setup(props, ctx) {
|
||||
setup() {
|
||||
const externalHooks = useExternalHooks();
|
||||
const { callDebounced } = useDebounce();
|
||||
|
||||
@ -435,11 +435,11 @@ export default defineComponent({
|
||||
findFirstAccessibleSettingsRoute() {
|
||||
const settingsRoutes = this.$router
|
||||
.getRoutes()
|
||||
.find((route) => route.path === '/settings')!
|
||||
.children.map((route) => route.name ?? '');
|
||||
.find((route) => route.path === '/settings')
|
||||
?.children.map((route) => route.name ?? '');
|
||||
|
||||
let defaultSettingsRoute = { name: VIEWS.USERS_SETTINGS };
|
||||
for (const route of settingsRoutes) {
|
||||
for (const route of settingsRoutes ?? []) {
|
||||
if (this.canUserAccessRouteByName(route.toString())) {
|
||||
defaultSettingsRoute = {
|
||||
name: route.toString() as VIEWS,
|
||||
|
@ -193,11 +193,6 @@ export default defineComponent({
|
||||
openUpdatesPanel() {
|
||||
this.uiStore.openModal(VERSIONS_MODAL_KEY);
|
||||
},
|
||||
async navigateTo(routeName: (typeof VIEWS)[keyof typeof VIEWS]) {
|
||||
if (this.$router.currentRoute.name !== routeName) {
|
||||
await this.$router.push({ name: routeName });
|
||||
}
|
||||
},
|
||||
async handleSelect(key: string) {
|
||||
switch (key) {
|
||||
case 'users': // Fakedoor feature added via hooks when user management is disabled on cloud
|
||||
|
@ -42,9 +42,9 @@ const files = ref<SourceControlAggregatedFile[]>(
|
||||
|
||||
const commitMessage = ref('');
|
||||
const loading = ref(true);
|
||||
const context = ref<'workflow' | 'workflows' | 'credentials' | string>('');
|
||||
const context = ref<'workflow' | 'workflows' | 'credentials' | ''>('');
|
||||
|
||||
const statusToBadgeThemeMap = {
|
||||
const statusToBadgeThemeMap: Record<string, string> = {
|
||||
created: 'success',
|
||||
deleted: 'danger',
|
||||
modified: 'warning',
|
||||
@ -64,7 +64,7 @@ const workflowId = computed(() => {
|
||||
});
|
||||
|
||||
const sortedFiles = computed(() => {
|
||||
const statusPriority = {
|
||||
const statusPriority: Record<string, number> = {
|
||||
modified: 1,
|
||||
renamed: 2,
|
||||
created: 3,
|
||||
@ -86,7 +86,11 @@ const sortedFiles = computed(() => {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0;
|
||||
return (a.updatedAt ?? 0) < (b.updatedAt ?? 0)
|
||||
? 1
|
||||
: (a.updatedAt ?? 0) > (b.updatedAt ?? 0)
|
||||
? -1
|
||||
: 0;
|
||||
});
|
||||
});
|
||||
|
||||
@ -151,13 +155,18 @@ function getContext() {
|
||||
return '';
|
||||
}
|
||||
|
||||
function getStagedFilesByContext(files: SourceControlAggregatedFile[]): Record<string, boolean> {
|
||||
const stagedFiles = files.reduce((acc, file) => {
|
||||
acc[file.file] = false;
|
||||
return acc;
|
||||
}, {});
|
||||
function getStagedFilesByContext(
|
||||
filesByContext: SourceControlAggregatedFile[],
|
||||
): Record<string, boolean> {
|
||||
const stagedFiles = filesByContext.reduce(
|
||||
(acc, file) => {
|
||||
acc[file.file] = false;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, boolean>,
|
||||
);
|
||||
|
||||
files.forEach((file) => {
|
||||
filesByContext.forEach((file) => {
|
||||
if (defaultStagedFileTypes.includes(file.type)) {
|
||||
stagedFiles[file.file] = true;
|
||||
}
|
||||
@ -184,13 +193,13 @@ function close() {
|
||||
}
|
||||
|
||||
function renderUpdatedAt(file: SourceControlAggregatedFile) {
|
||||
const currentYear = new Date().getFullYear();
|
||||
const currentYear = new Date().getFullYear().toString();
|
||||
|
||||
return i18n.baseText('settings.sourceControl.lastUpdated', {
|
||||
interpolate: {
|
||||
date: dateformat(
|
||||
file.updatedAt,
|
||||
`d mmm${file.updatedAt.startsWith(currentYear) ? '' : ', yyyy'}`,
|
||||
`d mmm${file.updatedAt?.startsWith(currentYear) ? '' : ', yyyy'}`,
|
||||
),
|
||||
time: dateformat(file.updatedAt, 'HH:MM'),
|
||||
},
|
||||
@ -227,6 +236,22 @@ async function commitAndPush() {
|
||||
loadingService.stopLoading();
|
||||
}
|
||||
}
|
||||
|
||||
function getStatusText(file: SourceControlAggregatedFile): string {
|
||||
if (file.status === 'deleted') {
|
||||
return i18n.baseText('settings.sourceControl.status.deleted');
|
||||
}
|
||||
|
||||
if (file.status === 'created') {
|
||||
return i18n.baseText('settings.sourceControl.status.created');
|
||||
}
|
||||
|
||||
if (file.status === 'modified') {
|
||||
return i18n.baseText('settings.sourceControl.status.modified');
|
||||
}
|
||||
|
||||
return i18n.baseText('settings.sourceControl.status.renamed');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -296,7 +321,7 @@ async function commitAndPush() {
|
||||
Current workflow
|
||||
</n8n-badge>
|
||||
<n8n-badge :theme="statusToBadgeThemeMap[file.status] || 'default'">
|
||||
{{ i18n.baseText(`settings.sourceControl.status.${file.status}`) }}
|
||||
{{ getStatusText(file) }}
|
||||
</n8n-badge>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,7 +8,8 @@
|
||||
<div :class="$style.cardDescription">
|
||||
<n8n-text color="text-light" size="small">
|
||||
<span v-show="data"
|
||||
>{{ $locale.baseText('workflows.item.updated') }} <TimeAgo :date="data.updatedAt" /> |
|
||||
>{{ $locale.baseText('workflows.item.updated') }}
|
||||
<TimeAgo :date="String(data.updatedAt)" /> |
|
||||
</span>
|
||||
<span v-show="data" class="mr-2xs"
|
||||
>{{ $locale.baseText('workflows.item.created') }} {{ formattedCreatedAtDate }}
|
||||
@ -52,7 +53,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import type { IWorkflowDb, IUser, ITag } from '@/Interface';
|
||||
import type { IWorkflowDb, IUser } from '@/Interface';
|
||||
import { DUPLICATE_MODAL_KEY, MODAL_CONFIRM, VIEWS, WORKFLOW_SHARE_MODAL_KEY } from '@/constants';
|
||||
import { useMessage } from '@/composables/useMessage';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
@ -149,11 +150,11 @@ export default defineComponent({
|
||||
return actions;
|
||||
},
|
||||
formattedCreatedAtDate(): string {
|
||||
const currentYear = new Date().getFullYear();
|
||||
const currentYear = new Date().getFullYear().toString();
|
||||
|
||||
return dateformat(
|
||||
this.data.createdAt,
|
||||
`d mmmm${this.data.createdAt.startsWith(currentYear) ? '' : ', yyyy'}`,
|
||||
`d mmmm${String(this.data.createdAt).startsWith(currentYear) ? '' : ', yyyy'}`,
|
||||
);
|
||||
},
|
||||
},
|
||||
@ -191,7 +192,9 @@ export default defineComponent({
|
||||
data: {
|
||||
id: this.data.id,
|
||||
name: this.data.name,
|
||||
tags: (this.data.tags || []).map((tag: ITag) => tag.id),
|
||||
tags: (this.data.tags ?? []).map((tag) =>
|
||||
typeof tag !== 'string' && 'id' in tag ? tag.id : tag,
|
||||
),
|
||||
},
|
||||
});
|
||||
} else if (action === WORKFLOW_LIST_ITEM_ACTIONS.SHARE) {
|
||||
|
@ -279,7 +279,7 @@
|
||||
<el-switch
|
||||
ref="inputField"
|
||||
:disabled="readOnlyEnv"
|
||||
:model-value="workflowSettings.executionTimeout > -1"
|
||||
:model-value="(workflowSettings.executionTimeout ?? -1) > -1"
|
||||
active-color="#13ce66"
|
||||
data-test-id="workflow-settings-timeout-workflow"
|
||||
@update:model-value="toggleTimeout"
|
||||
@ -288,7 +288,7 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div
|
||||
v-if="workflowSettings.executionTimeout > -1"
|
||||
v-if="(workflowSettings.executionTimeout ?? -1) > -1"
|
||||
data-test-id="workflow-settings-timeout-form"
|
||||
>
|
||||
<el-row>
|
||||
@ -306,7 +306,7 @@
|
||||
:disabled="readOnlyEnv"
|
||||
:model-value="timeoutHMS.hours"
|
||||
:min="0"
|
||||
@update:model-value="(value) => setTimeout('hours', value)"
|
||||
@update:model-value="(value: string) => setTimeout('hours', value)"
|
||||
>
|
||||
<template #append>{{ $locale.baseText('workflowSettings.hours') }}</template>
|
||||
</n8n-input>
|
||||
@ -317,7 +317,7 @@
|
||||
:model-value="timeoutHMS.minutes"
|
||||
:min="0"
|
||||
:max="60"
|
||||
@update:model-value="(value) => setTimeout('minutes', value)"
|
||||
@update:model-value="(value: string) => setTimeout('minutes', value)"
|
||||
>
|
||||
<template #append>{{ $locale.baseText('workflowSettings.minutes') }}</template>
|
||||
</n8n-input>
|
||||
@ -328,7 +328,7 @@
|
||||
:model-value="timeoutHMS.seconds"
|
||||
:min="0"
|
||||
:max="60"
|
||||
@update:model-value="(value) => setTimeout('seconds', value)"
|
||||
@update:model-value="(value: string) => setTimeout('seconds', value)"
|
||||
>
|
||||
<template #append>{{ $locale.baseText('workflowSettings.seconds') }}</template>
|
||||
</n8n-input>
|
||||
@ -828,7 +828,10 @@ export default defineComponent({
|
||||
data.versionId = this.workflowsStore.workflowVersionId;
|
||||
|
||||
try {
|
||||
const workflow = await this.workflowsStore.updateWorkflow(this.$route.params.name, data);
|
||||
const workflow = await this.workflowsStore.updateWorkflow(
|
||||
String(this.$route.params.name),
|
||||
data,
|
||||
);
|
||||
this.workflowsStore.setWorkflowVersionId(workflow.versionId);
|
||||
} catch (error) {
|
||||
this.showError(
|
||||
@ -840,12 +843,9 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
// Get the settings without the defaults set for local workflow settings
|
||||
const localWorkflowSettings: IWorkflowSettings = {};
|
||||
for (const key of Object.keys(this.workflowSettings)) {
|
||||
if (this.workflowSettings[key] !== 'DEFAULT') {
|
||||
localWorkflowSettings[key] = this.workflowSettings[key];
|
||||
}
|
||||
}
|
||||
const localWorkflowSettings = Object.fromEntries(
|
||||
Object.entries(this.workflowSettings).filter(([, value]) => value !== 'DEFAULT'),
|
||||
);
|
||||
|
||||
const oldSettings = deepCopy(this.workflowsStore.workflowSettings);
|
||||
|
||||
|
@ -31,7 +31,7 @@ const { debounce } = useDebounce();
|
||||
const telemetry = useTelemetry();
|
||||
|
||||
const props = withDefaults(defineProps<ExecutionFilterProps>(), {
|
||||
workflows: [] as Array<IWorkflowDb | IWorkflowShortResponse>,
|
||||
workflows: () => [] as Array<IWorkflowDb | IWorkflowShortResponse>,
|
||||
popoverPlacement: 'bottom' as Placement,
|
||||
teleported: true,
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { vi, describe, expect } from 'vitest';
|
||||
import { describe, expect } from 'vitest';
|
||||
import { render } from '@testing-library/vue';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { faker } from '@faker-js/faker';
|
||||
@ -7,7 +7,7 @@ import { createPinia, PiniaVuePlugin, setActivePinia } from 'pinia';
|
||||
import type { ExecutionSummary } from 'n8n-workflow';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import WorkflowExecutionsPreview from '@/components/executions/workflow/WorkflowExecutionsPreview.vue';
|
||||
import { VIEWS } from '@/constants';
|
||||
import { EnterpriseEditionFeature, VIEWS } from '@/constants';
|
||||
import { i18nInstance, I18nPlugin } from '@/plugins/i18n';
|
||||
import { FontAwesomePlugin } from '@/plugins/icons';
|
||||
import { GlobalComponentsPlugin } from '@/plugins/components';
|
||||
@ -78,9 +78,10 @@ describe('WorkflowExecutionsPreview.vue', () => {
|
||||
])(
|
||||
'when debug enterprise feature is %s it should handle debug link click accordingly',
|
||||
async (availability, path) => {
|
||||
vi.spyOn(settingsStore, 'isEnterpriseFeatureEnabled', 'get').mockReturnValue(
|
||||
() => availability,
|
||||
);
|
||||
settingsStore.settings.enterprise = {
|
||||
...(settingsStore.settings.enterprise ?? {}),
|
||||
[EnterpriseEditionFeature.DebugInEditor]: availability,
|
||||
};
|
||||
|
||||
// Not using createComponentRenderer helper here because this component should not stub `router-link`
|
||||
const { getByTestId } = render(WorkflowExecutionsPreview, {
|
||||
|
@ -72,7 +72,7 @@ import WorkflowExecutionsInfoAccordion from '@/components/executions/workflow/Wo
|
||||
import ExecutionsFilter from '@/components/executions/ExecutionsFilter.vue';
|
||||
import { VIEWS } from '@/constants';
|
||||
import type { ExecutionSummary } from 'n8n-workflow';
|
||||
import type { Route } from 'vue-router';
|
||||
import type { RouteRecord } from 'vue-router';
|
||||
import { defineComponent } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import { mapStores } from 'pinia';
|
||||
@ -131,7 +131,7 @@ export default defineComponent({
|
||||
...mapStores(useExecutionsStore, useWorkflowsStore),
|
||||
},
|
||||
watch: {
|
||||
$route(to: Route, from: Route) {
|
||||
$route(to: RouteRecord, from: RouteRecord) {
|
||||
if (from.name === VIEWS.EXECUTION_PREVIEW && to.name === VIEWS.EXECUTION_HOME) {
|
||||
// Skip parent route when navigating through executions with back button
|
||||
this.$router.go(-1);
|
||||
|
@ -1836,6 +1836,7 @@
|
||||
"settings.sourceControl.status.modified": "Modified",
|
||||
"settings.sourceControl.status.deleted": "Deleted",
|
||||
"settings.sourceControl.status.created": "New",
|
||||
"settings.sourceControl.status.renamed": "Renamed",
|
||||
"settings.sourceControl.pull.oneLastStep.title": "One last step",
|
||||
"settings.sourceControl.pull.oneLastStep.description": "You have new creds/vars. Fill them out to make sure your workflows function properly",
|
||||
"settings.sourceControl.pull.success.title": "Pulled successfully",
|
||||
|
Loading…
Reference in New Issue
Block a user