mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-09 17:50:52 +03:00
refactor(editor): Fix TypeScript issues in views (no-changelog) (#9573)
This commit is contained in:
parent
327794127e
commit
e23420d89d
@ -250,7 +250,7 @@ export interface IWorkflowData {
|
||||
export interface IWorkflowDataUpdate {
|
||||
id?: string;
|
||||
name?: string;
|
||||
nodes?: INode[];
|
||||
nodes?: Array<INode | IWorkflowTemplateNode>;
|
||||
connections?: IConnections;
|
||||
settings?: IWorkflowSettings;
|
||||
active?: boolean;
|
||||
@ -363,6 +363,7 @@ export interface ICredentialsResponse extends ICredentialsEncrypted {
|
||||
homeProject?: ProjectSharingData;
|
||||
currentUserHasAccess?: boolean;
|
||||
scopes?: Scope[];
|
||||
ownedBy?: Pick<IUserResponse, 'id' | 'firstName' | 'lastName' | 'email'>;
|
||||
}
|
||||
|
||||
export interface ICredentialsBase {
|
||||
|
@ -64,7 +64,7 @@ export default function useCanvasMouseSelect() {
|
||||
selectActive.value = false;
|
||||
}
|
||||
|
||||
function _getSelectionBox(event: MouseEvent) {
|
||||
function _getSelectionBox(event: MouseEvent | TouchEvent) {
|
||||
const [x, y] = getMousePositionWithinNodeView(event);
|
||||
return {
|
||||
x: Math.min(x, selectBox.value.x),
|
||||
@ -74,7 +74,7 @@ export default function useCanvasMouseSelect() {
|
||||
};
|
||||
}
|
||||
|
||||
function _getNodesInSelection(event: MouseEvent): INodeUi[] {
|
||||
function _getNodesInSelection(event: MouseEvent | TouchEvent): INodeUi[] {
|
||||
const returnNodes: INodeUi[] = [];
|
||||
const selectionBox = _getSelectionBox(event);
|
||||
|
||||
@ -128,9 +128,9 @@ export default function useCanvasMouseSelect() {
|
||||
_updateSelectBox(e);
|
||||
}
|
||||
|
||||
function mouseUpMouseSelect(e: MouseEvent) {
|
||||
function mouseUpMouseSelect(e: MouseEvent | TouchEvent) {
|
||||
// Ignore right-click
|
||||
if (e.button === 2 || isContextMenuOpen.value) return;
|
||||
if (('button' in e && e.button === 2) || isContextMenuOpen.value) return;
|
||||
|
||||
if (!selectActive.value) {
|
||||
if (isTouchDevice && e.target instanceof HTMLElement) {
|
||||
|
@ -212,7 +212,7 @@ export const useUIStore = defineStore(STORES.UI, {
|
||||
const settingsStore = useSettingsStore();
|
||||
const deploymentType = settingsStore.deploymentType;
|
||||
|
||||
let contextKey = '';
|
||||
let contextKey: '' | '.cloud' | '.desktop' = '';
|
||||
if (deploymentType === 'cloud') {
|
||||
contextKey = '.cloud';
|
||||
} else if (deploymentType === 'desktop_mac' || deploymentType === 'desktop_win') {
|
||||
@ -266,7 +266,7 @@ export const useUIStore = defineStore(STORES.UI, {
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
} as const;
|
||||
},
|
||||
getLastSelectedNode(): INodeUi | null {
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
|
@ -16,6 +16,7 @@ import { NodeConnectionType } from 'n8n-workflow';
|
||||
import type { BrowserJsPlumbInstance } from '@jsplumb/browser-ui';
|
||||
import { EVENT_CONNECTION_MOUSEOUT, EVENT_CONNECTION_MOUSEOVER } from '@jsplumb/browser-ui';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import type { StyleValue } from 'vue';
|
||||
|
||||
/*
|
||||
Canvas constants and functions.
|
||||
@ -609,7 +610,7 @@ export const getBackgroundStyles = (
|
||||
scale: number,
|
||||
offsetPosition: XYPosition,
|
||||
executionPreview: boolean,
|
||||
) => {
|
||||
): StyleValue => {
|
||||
const squareSize = GRID_SIZE * scale;
|
||||
const dotSize = 1 * scale;
|
||||
const dotPosition = (GRID_SIZE / 2) * scale;
|
||||
@ -623,7 +624,7 @@ export const getBackgroundStyles = (
|
||||
};
|
||||
}
|
||||
|
||||
const styles: object = {
|
||||
const styles: StyleValue = {
|
||||
'background-size': `${squareSize}px ${squareSize}px`,
|
||||
'background-position': `left ${offsetPosition[0]}px top ${offsetPosition[1]}px`,
|
||||
};
|
||||
|
@ -22,10 +22,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { type PropType, defineComponent } from 'vue';
|
||||
|
||||
import Logo from '@/components/Logo.vue';
|
||||
import SSOLogin from '@/components/SSOLogin.vue';
|
||||
import type { IFormBoxConfig } from '@/Interface';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AuthView',
|
||||
@ -34,7 +35,9 @@ export default defineComponent({
|
||||
SSOLogin,
|
||||
},
|
||||
props: {
|
||||
form: {},
|
||||
form: {
|
||||
type: Object as PropType<IFormBoxConfig>,
|
||||
},
|
||||
formLoading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
|
@ -18,20 +18,22 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import type { BaseTextKey } from '@/plugins/i18n';
|
||||
import { type PropType, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ErrorView',
|
||||
props: {
|
||||
messageKey: {
|
||||
type: String,
|
||||
type: String as PropType<BaseTextKey>,
|
||||
required: true,
|
||||
},
|
||||
errorCode: {
|
||||
type: Number,
|
||||
},
|
||||
redirectTextKey: {
|
||||
type: String,
|
||||
type: String as PropType<BaseTextKey>,
|
||||
required: true,
|
||||
},
|
||||
redirectPage: {
|
||||
type: String,
|
||||
|
@ -4,7 +4,7 @@
|
||||
<n8n-heading size="2xlarge">
|
||||
{{ $locale.baseText('settings.api') }}
|
||||
<span :style="{ fontSize: 'var(--font-size-s)', color: 'var(--color-text-light)' }">
|
||||
({{ $locale.baseText('beta') }})
|
||||
({{ $locale.baseText('generic.beta') }})
|
||||
</span>
|
||||
</n8n-heading>
|
||||
</div>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<n8n-heading size="2xlarge">{{
|
||||
i18n.baseText('settings.personal.personalSettings')
|
||||
}}</n8n-heading>
|
||||
<div :class="$style.user">
|
||||
<div v-if="currentUser" :class="$style.user">
|
||||
<span :class="$style.username" data-test-id="current-user-name">
|
||||
<n8n-text color="text-light">{{ currentUser.fullName }}</n8n-text>
|
||||
</span>
|
||||
|
@ -219,7 +219,7 @@ const onSelectSshKeyType = async (sshKeyType: TupleToUnion<SshKeyTypes>) => {
|
||||
:validation-rules="repoUrlValidationRules"
|
||||
:disabled="isConnected"
|
||||
:placeholder="locale.baseText('settings.sourceControl.repoUrlPlaceholder')"
|
||||
@validate="(value) => onValidate('repoUrl', value)"
|
||||
@validate="(value: boolean) => onValidate('repoUrl', value)"
|
||||
/>
|
||||
<n8n-button
|
||||
v-if="isConnected"
|
||||
@ -248,7 +248,7 @@ const onSelectSshKeyType = async (sshKeyType: TupleToUnion<SshKeyTypes>) => {
|
||||
:validation-rules="keyGeneratorTypeValidationRules"
|
||||
:options="sourceControlStore.sshKeyTypesWithLabel"
|
||||
:model-value="sourceControlStore.preferences.keyGeneratorType"
|
||||
@validate="(value) => onValidate('keyGeneratorType', value)"
|
||||
@validate="(value: boolean) => onValidate('keyGeneratorType', value)"
|
||||
@update:model-value="onSelectSshKeyType"
|
||||
/>
|
||||
<CopyInput
|
||||
@ -309,7 +309,7 @@ const onSelectSshKeyType = async (sshKeyType: TupleToUnion<SshKeyTypes>) => {
|
||||
:validation-rules="branchNameValidationRules"
|
||||
:options="branchNameOptions"
|
||||
:model-value="sourceControlStore.preferences.branchName"
|
||||
@validate="(value) => onValidate('branchName', value)"
|
||||
@validate="(value: boolean) => onValidate('branchName', value)"
|
||||
@update:model-value="onSelect"
|
||||
/>
|
||||
<n8n-tooltip placement="top">
|
||||
|
@ -84,7 +84,7 @@ import { defineComponent } from 'vue';
|
||||
import { mapStores } from 'pinia';
|
||||
import { EnterpriseEditionFeature, INVITE_USER_MODAL_KEY, VIEWS, ROLE } from '@/constants';
|
||||
|
||||
import type { IUser, IUserListAction, InvitableRoleName } from '@/Interface';
|
||||
import type { IRole, IUser, IUserListAction, InvitableRoleName } from '@/Interface';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import type { Route } from 'vue-router';
|
||||
import type { RouteLocationPathRaw } from 'vue-router';
|
||||
|
||||
import { VIEWS } from '@/constants';
|
||||
import SettingsSidebar from '@/components/SettingsSidebar.vue';
|
||||
@ -32,7 +32,7 @@ const SettingsView = defineComponent({
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
previousRoute: null as Route | null,
|
||||
previousRoute: null as RouteLocationPathRaw | null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
@ -101,9 +101,9 @@ export default defineComponent({
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUIStore, useUsersStore),
|
||||
inviteMessage(): null | string {
|
||||
inviteMessage(): string {
|
||||
if (!this.inviter) {
|
||||
return null;
|
||||
return '';
|
||||
}
|
||||
|
||||
return this.$locale.baseText('settings.signup.signUpInviterInfo', {
|
||||
|
@ -53,15 +53,18 @@ const canRender = ref(true);
|
||||
const isListLoading = ref(true);
|
||||
const requestNumberOfItems = ref(20);
|
||||
const lastReceivedItemsLength = ref(0);
|
||||
const editorRoute = computed(() => ({
|
||||
name: VIEWS.WORKFLOW,
|
||||
params: {
|
||||
name: route.params.workflowId,
|
||||
},
|
||||
}));
|
||||
const activeWorkflow = ref<IWorkflowDb | null>(null);
|
||||
const workflowHistory = ref<WorkflowHistory[]>([]);
|
||||
const activeWorkflowVersion = ref<WorkflowVersion | null>(null);
|
||||
|
||||
const workflowId = computed(() => normalizeSingleRouteParam('workflowId'));
|
||||
const versionId = computed(() => normalizeSingleRouteParam('versionId'));
|
||||
const editorRoute = computed(() => ({
|
||||
name: VIEWS.WORKFLOW,
|
||||
params: {
|
||||
name: workflowId.value,
|
||||
},
|
||||
}));
|
||||
const actions = computed<UserAction[]>(() =>
|
||||
workflowHistoryActionTypes.map((value) => ({
|
||||
label: i18n.baseText(`workflowHistory.item.actions.${value}`),
|
||||
@ -70,23 +73,18 @@ const actions = computed<UserAction[]>(() =>
|
||||
})),
|
||||
);
|
||||
|
||||
const isFirstItemShown = computed(
|
||||
() => workflowHistory.value[0]?.versionId === route.params.versionId,
|
||||
);
|
||||
const isFirstItemShown = computed(() => workflowHistory.value[0]?.versionId === versionId.value);
|
||||
const evaluatedPruneTime = computed(() => Math.floor(workflowHistoryStore.evaluatedPruneTime / 24));
|
||||
|
||||
const sendTelemetry = (event: string) => {
|
||||
telemetry.track(event, {
|
||||
instance_id: useRootStore().instanceId,
|
||||
workflow_id: route.params.workflowId,
|
||||
workflow_id: workflowId.value,
|
||||
});
|
||||
};
|
||||
|
||||
const loadMore = async (queryParams: WorkflowHistoryRequestParams) => {
|
||||
const history = await workflowHistoryStore.getWorkflowHistory(
|
||||
route.params.workflowId,
|
||||
queryParams,
|
||||
);
|
||||
const history = await workflowHistoryStore.getWorkflowHistory(workflowId.value, queryParams);
|
||||
lastReceivedItemsLength.value = history.length;
|
||||
workflowHistory.value = workflowHistory.value.concat(history);
|
||||
};
|
||||
@ -95,17 +93,17 @@ onBeforeMount(async () => {
|
||||
sendTelemetry('User opened workflow history');
|
||||
try {
|
||||
const [workflow] = await Promise.all([
|
||||
workflowsStore.fetchWorkflow(route.params.workflowId),
|
||||
workflowsStore.fetchWorkflow(workflowId.value),
|
||||
loadMore({ take: requestNumberOfItems.value }),
|
||||
]);
|
||||
activeWorkflow.value = workflow;
|
||||
isListLoading.value = false;
|
||||
|
||||
if (!route.params.versionId && workflowHistory.value.length) {
|
||||
if (!versionId.value && workflowHistory.value.length) {
|
||||
await router.replace({
|
||||
name: VIEWS.WORKFLOW_HISTORY,
|
||||
params: {
|
||||
workflowId: route.params.workflowId,
|
||||
workflowId: workflowId.value,
|
||||
versionId: workflowHistory.value[0].versionId,
|
||||
},
|
||||
});
|
||||
@ -116,11 +114,17 @@ onBeforeMount(async () => {
|
||||
}
|
||||
});
|
||||
|
||||
const normalizeSingleRouteParam = (name: string): string => {
|
||||
const param = route.params[name];
|
||||
if (typeof param === 'string') return param;
|
||||
return param?.[0] ?? '';
|
||||
};
|
||||
|
||||
const openInNewTab = (id: WorkflowVersionId) => {
|
||||
const { href } = router.resolve({
|
||||
name: VIEWS.WORKFLOW_HISTORY,
|
||||
params: {
|
||||
workflowId: route.params.workflowId,
|
||||
workflowId: workflowId.value,
|
||||
versionId: id,
|
||||
},
|
||||
});
|
||||
@ -183,7 +187,7 @@ const cloneWorkflowVersion = async (
|
||||
data: { formattedCreatedAt: string },
|
||||
) => {
|
||||
const clonedWorkflow = await workflowHistoryStore.cloneIntoNewWorkflow(
|
||||
route.params.workflowId,
|
||||
workflowId.value,
|
||||
id,
|
||||
data,
|
||||
);
|
||||
@ -210,17 +214,17 @@ const restoreWorkflowVersion = async (
|
||||
id: WorkflowVersionId,
|
||||
data: { formattedCreatedAt: string },
|
||||
) => {
|
||||
const workflow = await workflowsStore.fetchWorkflow(route.params.workflowId);
|
||||
const workflow = await workflowsStore.fetchWorkflow(workflowId.value);
|
||||
const modalAction = await openRestorationModal(workflow.active, data.formattedCreatedAt);
|
||||
if (modalAction === WorkflowHistoryVersionRestoreModalActions.cancel) {
|
||||
return;
|
||||
}
|
||||
activeWorkflow.value = await workflowHistoryStore.restoreWorkflow(
|
||||
route.params.workflowId,
|
||||
workflowId.value,
|
||||
id,
|
||||
modalAction === WorkflowHistoryVersionRestoreModalActions.deactivateAndRestore,
|
||||
);
|
||||
const history = await workflowHistoryStore.getWorkflowHistory(route.params.workflowId, {
|
||||
const history = await workflowHistoryStore.getWorkflowHistory(workflowId.value, {
|
||||
take: 1,
|
||||
});
|
||||
workflowHistory.value = history.concat(workflowHistory.value);
|
||||
@ -246,7 +250,7 @@ const onAction = async ({
|
||||
sendTelemetry('User opened version in new tab');
|
||||
break;
|
||||
case WORKFLOW_HISTORY_ACTIONS.DOWNLOAD:
|
||||
await workflowHistoryStore.downloadVersion(route.params.workflowId, id, data);
|
||||
await workflowHistoryStore.downloadVersion(workflowId.value, id, data);
|
||||
sendTelemetry('User downloaded version');
|
||||
break;
|
||||
case WORKFLOW_HISTORY_ACTIONS.CLONE:
|
||||
@ -278,7 +282,7 @@ const onPreview = async ({ event, id }: { event: MouseEvent; id: WorkflowVersion
|
||||
await router.push({
|
||||
name: VIEWS.WORKFLOW_HISTORY,
|
||||
params: {
|
||||
workflowId: route.params.workflowId,
|
||||
workflowId: workflowId.value,
|
||||
versionId: id,
|
||||
},
|
||||
});
|
||||
@ -290,24 +294,24 @@ const onUpgrade = () => {
|
||||
};
|
||||
|
||||
watchEffect(async () => {
|
||||
if (!route.params.versionId) {
|
||||
if (!versionId.value) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
activeWorkflowVersion.value = await workflowHistoryStore.getWorkflowVersion(
|
||||
route.params.workflowId,
|
||||
route.params.versionId,
|
||||
workflowId.value,
|
||||
versionId.value,
|
||||
);
|
||||
sendTelemetry('User selected version');
|
||||
} catch (error) {
|
||||
toast.showError(
|
||||
new Error(`${error.message} "${route.params.versionId}" `),
|
||||
new Error(`${error.message} "${versionId.value}" `),
|
||||
i18n.baseText('workflowHistory.title'),
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
activeWorkflow.value = await workflowsStore.fetchWorkflow(route.params.workflowId);
|
||||
activeWorkflow.value = await workflowsStore.fetchWorkflow(workflowId.value);
|
||||
} catch (error) {
|
||||
canRender.value = false;
|
||||
toast.showError(error, i18n.baseText('workflowHistory.title'));
|
||||
|
@ -9,7 +9,7 @@ import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
const loadingService = useLoadingService();
|
||||
const templateStore = useTemplatesStore();
|
||||
const workfowStore = useWorkflowsStore();
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const i18n = useI18n();
|
||||
@ -26,7 +26,7 @@ const openWorkflowTemplate = async (templateId: string) => {
|
||||
interpolate: { name: template.name },
|
||||
});
|
||||
|
||||
const workflow = await workfowStore.createNewWorkflow({
|
||||
const workflow = await workflowsStore.createNewWorkflow({
|
||||
name,
|
||||
connections: template.workflow.connections,
|
||||
nodes: template.workflow.nodes,
|
||||
|
@ -67,12 +67,11 @@
|
||||
<div class="text-center mt-s">
|
||||
<n8n-heading tag="h2" size="xlarge" class="mb-2xs">
|
||||
{{
|
||||
$locale.baseText(
|
||||
currentUser.firstName
|
||||
? 'workflows.empty.heading'
|
||||
: 'workflows.empty.heading.userNotSetup',
|
||||
{ interpolate: { name: currentUser.firstName } },
|
||||
)
|
||||
? $locale.baseText('workflows.empty.heading', {
|
||||
interpolate: { name: currentUser.firstName },
|
||||
})
|
||||
: $locale.baseText('workflows.empty.heading.userNotSetup')
|
||||
}}
|
||||
</n8n-heading>
|
||||
<n8n-text size="large" color="text-base">
|
||||
@ -178,13 +177,13 @@ import { useSourceControlStore } from '@/stores/sourceControl.store';
|
||||
import { useTagsStore } from '@/stores/tags.store';
|
||||
import { useProjectsStore } from '@/features/projects/projects.store';
|
||||
import ProjectTabs from '@/features/projects/components/ProjectTabs.vue';
|
||||
import { useTemplatesStore } from '@/stores/templates.store';
|
||||
|
||||
type IResourcesListLayoutInstance = InstanceType<typeof ResourcesListLayout>;
|
||||
|
||||
interface Filters {
|
||||
search: string;
|
||||
ownedBy: string;
|
||||
sharedWith: string;
|
||||
homeProject: string;
|
||||
status: string | boolean;
|
||||
tags: string[];
|
||||
}
|
||||
@ -210,9 +209,9 @@ const WorkflowsView = defineComponent({
|
||||
filters: {
|
||||
search: '',
|
||||
homeProject: '',
|
||||
status: StatusFilter.ALL as string | boolean,
|
||||
tags: [] as string[],
|
||||
},
|
||||
status: StatusFilter.ALL,
|
||||
tags: [],
|
||||
} as Filters,
|
||||
sourceControlStoreUnsubscribe: () => {},
|
||||
};
|
||||
},
|
||||
@ -225,6 +224,7 @@ const WorkflowsView = defineComponent({
|
||||
useSourceControlStore,
|
||||
useTagsStore,
|
||||
useProjectsStore,
|
||||
useTemplatesStore,
|
||||
),
|
||||
readOnlyEnv(): boolean {
|
||||
return this.sourceControlStore.preferences.branchReadOnly;
|
||||
@ -258,10 +258,18 @@ const WorkflowsView = defineComponent({
|
||||
return this.uiStore.suggestedTemplates;
|
||||
},
|
||||
userRole() {
|
||||
const userRole: string | undefined =
|
||||
this.usersStore.currentUserCloudInfo?.role ??
|
||||
this.usersStore.currentUser?.personalizationAnswers?.role;
|
||||
return userRole;
|
||||
const role = this.usersStore.currentUserCloudInfo?.role;
|
||||
|
||||
if (role) {
|
||||
return role;
|
||||
}
|
||||
|
||||
const answers = this.usersStore.currentUser?.personalizationAnswers;
|
||||
if (answers && 'role' in answers) {
|
||||
return answers.role;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
isSalesUser() {
|
||||
if (!this.userRole) {
|
||||
|
Loading…
Reference in New Issue
Block a user