mirror of
https://github.com/n8n-io/n8n.git
synced 2026-05-31 16:57:08 +02:00
177 lines
4.8 KiB
TypeScript
177 lines
4.8 KiB
TypeScript
import { h, nextTick } from 'vue';
|
|
import type { Router } from 'vue-router';
|
|
import { useI18n } from '@n8n/i18n';
|
|
import { type SourceControlledFile, SOURCE_CONTROL_FILE_STATUS } from '@n8n/api-types';
|
|
import type { BaseTextKey } from '@n8n/i18n';
|
|
import { VIEWS } from '@/constants';
|
|
import groupBy from 'lodash/groupBy';
|
|
import type { useToast } from '@/composables/useToast';
|
|
import { telemetry } from '@/plugins/telemetry';
|
|
|
|
type SourceControlledFileStatus = SourceControlledFile['status'];
|
|
|
|
const i18n = useI18n();
|
|
|
|
export const getStatusText = (status: SourceControlledFileStatus) =>
|
|
i18n.baseText(`settings.sourceControl.status.${status}` as BaseTextKey);
|
|
|
|
export const getStatusTheme = (status: SourceControlledFileStatus) => {
|
|
const statusToBadgeThemeMap: Partial<
|
|
Record<SourceControlledFileStatus, 'success' | 'danger' | 'warning'>
|
|
> = {
|
|
[SOURCE_CONTROL_FILE_STATUS.created]: 'success',
|
|
[SOURCE_CONTROL_FILE_STATUS.deleted]: 'danger',
|
|
[SOURCE_CONTROL_FILE_STATUS.modified]: 'warning',
|
|
} as const;
|
|
return statusToBadgeThemeMap[status];
|
|
};
|
|
|
|
type StatusPriority = Partial<Record<SourceControlledFileStatus, number>>;
|
|
const pullStatusPriority: StatusPriority = {
|
|
[SOURCE_CONTROL_FILE_STATUS.modified]: 2,
|
|
[SOURCE_CONTROL_FILE_STATUS.created]: 1,
|
|
[SOURCE_CONTROL_FILE_STATUS.deleted]: 3,
|
|
} as const;
|
|
|
|
export const getPullPriorityByStatus = (status: SourceControlledFileStatus) =>
|
|
pullStatusPriority[status] ?? 0;
|
|
|
|
const pushStatusPriority: StatusPriority = {
|
|
[SOURCE_CONTROL_FILE_STATUS.modified]: 1,
|
|
[SOURCE_CONTROL_FILE_STATUS.renamed]: 2,
|
|
[SOURCE_CONTROL_FILE_STATUS.created]: 3,
|
|
[SOURCE_CONTROL_FILE_STATUS.deleted]: 4,
|
|
} as const;
|
|
|
|
export const getPushPriorityByStatus = (status: SourceControlledFileStatus) =>
|
|
pushStatusPriority[status] ?? 0;
|
|
|
|
const createVariablesToast = (router: Router) => {
|
|
const route = { name: VIEWS.VARIABLES, query: { incomplete: 'true' } };
|
|
const { href } = router.resolve(route);
|
|
|
|
return {
|
|
title: i18n.baseText('settings.sourceControl.pull.upToDate.variables.title'),
|
|
message: h(
|
|
'a',
|
|
{
|
|
href,
|
|
onClick: (e: MouseEvent) => {
|
|
e.preventDefault();
|
|
telemetry.track('User clicked review variables');
|
|
void router.push(route);
|
|
},
|
|
},
|
|
i18n.baseText('settings.sourceControl.pull.upToDate.variables.description'),
|
|
),
|
|
type: 'info' as const,
|
|
duration: 0,
|
|
};
|
|
};
|
|
|
|
const createCredentialsToast = (router: Router) => {
|
|
const route = { name: VIEWS.CREDENTIALS, query: { setupNeeded: 'true' } };
|
|
const { href } = router.resolve(route);
|
|
|
|
return {
|
|
title: i18n.baseText('settings.sourceControl.pull.upToDate.credentials.title'),
|
|
message: h(
|
|
'a',
|
|
{
|
|
href,
|
|
onClick: (e: MouseEvent) => {
|
|
e.preventDefault();
|
|
telemetry.track('User clicked review credentials');
|
|
void router.push(route);
|
|
},
|
|
},
|
|
i18n.baseText('settings.sourceControl.pull.upToDate.credentials.description'),
|
|
),
|
|
type: 'info' as const,
|
|
duration: 0,
|
|
};
|
|
};
|
|
|
|
const pullMessage = ({
|
|
credential,
|
|
tags,
|
|
variables,
|
|
workflow,
|
|
folders,
|
|
}: Partial<Record<SourceControlledFile['type'], SourceControlledFile[]>>) => {
|
|
const messages: string[] = [];
|
|
|
|
if (workflow?.length) {
|
|
messages.push(
|
|
i18n.baseText('generic.workflow', {
|
|
adjustToNumber: workflow.length,
|
|
interpolate: { count: workflow.length },
|
|
}),
|
|
);
|
|
}
|
|
|
|
if (credential?.length) {
|
|
messages.push(
|
|
i18n.baseText('generic.credential', {
|
|
adjustToNumber: credential.length,
|
|
interpolate: { count: credential.length },
|
|
}),
|
|
);
|
|
}
|
|
|
|
if (variables?.length) {
|
|
messages.push(i18n.baseText('generic.variable_plural'));
|
|
}
|
|
|
|
if (tags?.length) {
|
|
messages.push(i18n.baseText('generic.tag_plural'));
|
|
}
|
|
|
|
if (folders?.length) {
|
|
messages.push(i18n.baseText('generic.folders_plural'));
|
|
}
|
|
|
|
return [
|
|
new Intl.ListFormat(i18n.locale, { style: 'long', type: 'conjunction' }).format(messages),
|
|
'were pulled',
|
|
].join(' ');
|
|
};
|
|
|
|
export const notifyUserAboutPullWorkFolderOutcome = async (
|
|
files: SourceControlledFile[],
|
|
toast: ReturnType<typeof useToast>,
|
|
router: Router,
|
|
) => {
|
|
if (!files?.length) {
|
|
toast.showMessage({
|
|
title: i18n.baseText('settings.sourceControl.pull.upToDate.title'),
|
|
message: i18n.baseText('settings.sourceControl.pull.upToDate.description'),
|
|
type: 'success',
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { credential, tags, variables, workflow, folders } = groupBy(files, 'type');
|
|
|
|
const toastMessages = [
|
|
...(variables?.length ? [createVariablesToast(router)] : []),
|
|
...(credential?.length ? [createCredentialsToast(router)] : []),
|
|
{
|
|
title: i18n.baseText('settings.sourceControl.pull.success.title'),
|
|
message: pullMessage({ credential, tags, variables, workflow, folders }),
|
|
type: 'success' as const,
|
|
},
|
|
];
|
|
|
|
for (const message of toastMessages) {
|
|
/**
|
|
* the toasts stack in a reversed way, resulting in
|
|
* Success
|
|
* Credentials
|
|
* Variables
|
|
*/
|
|
toast.showToast(message);
|
|
await nextTick();
|
|
}
|
|
};
|