refactor(editor): Move editor-ui and design-system to frontend dir (no-changelog) (#13564)

This commit is contained in:
Alex Grozav
2025-02-28 14:28:30 +02:00
committed by GitHub
parent 684353436d
commit f5743176e5
1635 changed files with 805 additions and 1079 deletions

View File

@@ -0,0 +1,123 @@
<script lang="ts" setup>
import Modal from '@/components/Modal.vue';
import { useSetupWorkflowCredentialsModalState } from '@/components/SetupWorkflowCredentialsModal/useSetupWorkflowCredentialsModalState';
import { useI18n } from '@/composables/useI18n';
import N8nHeading from '@n8n/design-system/components/N8nHeading';
import AppsRequiringCredsNotice from '@/views/SetupWorkflowFromTemplateView/AppsRequiringCredsNotice.vue';
import SetupTemplateFormStep from '@/views/SetupWorkflowFromTemplateView/SetupTemplateFormStep.vue';
import { onMounted, onUnmounted } from 'vue';
import { useTelemetry } from '@/composables/useTelemetry';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useUIStore } from '@/stores/ui.store';
const i18n = useI18n();
const telemetry = useTelemetry();
const workflowStore = useWorkflowsStore();
const uiStore = useUIStore();
const props = defineProps<{
modalName: string;
data: {};
}>();
const {
appCredentials,
credentialUsages,
numFilledCredentials,
selectedCredentialIdByKey,
setInitialCredentialSelection,
setCredential,
unsetCredential,
} = useSetupWorkflowCredentialsModalState();
onMounted(() => {
setInitialCredentialSelection();
telemetry.track('User opened cred setup', { source: 'canvas' }, { withPostHog: true });
});
onUnmounted(() => {
telemetry.track('User closed cred setup', {
completed: numFilledCredentials.value === credentialUsages.value.length,
creds_filled: numFilledCredentials.value,
creds_needed: credentialUsages.value.length,
workflow_id: workflowStore.workflowId,
});
});
</script>
<template>
<Modal width="700px" max-height="90%" :name="props.modalName">
<template #header>
<N8nHeading tag="h2" size="xlarge">
{{ i18n.baseText('setupCredentialsModal.title') }}
</N8nHeading>
</template>
<template #content>
<div :class="$style.grid">
<div :class="$style.notice" data-test-id="info-callout">
<AppsRequiringCredsNotice :app-credentials="appCredentials" />
</div>
<div>
<ol :class="$style.appCredentialsContainer">
<SetupTemplateFormStep
v-for="(credentials, index) in credentialUsages"
:key="credentials.key"
:class="$style.appCredential"
:order="index + 1"
:credentials="credentials"
:selected-credential-id="selectedCredentialIdByKey[credentials.key]"
@credential-selected="setCredential($event.credentialUsageKey, $event.credentialId)"
@credential-deselected="unsetCredential($event.credentialUsageKey)"
/>
</ol>
</div>
</div>
</template>
<template #footer>
<div :class="$style.footer">
<n8n-button
size="large"
:label="i18n.baseText('templateSetup.continue.button')"
:disabled="numFilledCredentials === 0"
data-test-id="continue-button"
@click="uiStore.closeModal(props.modalName)"
/>
</div>
</template>
</Modal>
</template>
<style lang="scss" module>
.grid {
margin: 0 auto;
margin-top: var(--spacing-l);
display: flex;
flex-direction: column;
justify-content: center;
}
.notice {
margin-bottom: var(--spacing-2xl);
}
.appCredentialsContainer {
display: flex;
flex-direction: column;
gap: var(--spacing-2xl);
margin-bottom: var(--spacing-2xl);
}
.appCredential:not(:last-of-type) {
padding-bottom: var(--spacing-2xl);
border-bottom: 1px solid var(--color-foreground-light);
}
.footer {
display: flex;
justify-content: flex-end;
}
</style>

View File

@@ -0,0 +1,129 @@
import { computed } from 'vue';
import type { INodeCredentialsDetails } from 'n8n-workflow';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
import { useCredentialsStore } from '@/stores/credentials.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
import type { TemplateCredentialKey } from '@/utils/templates/templateTransforms';
import { useCredentialSetupState } from '@/views/SetupWorkflowFromTemplateView/useCredentialSetupState';
export const useSetupWorkflowCredentialsModalState = () => {
const workflowsStore = useWorkflowsStore();
const credentialsStore = useCredentialsStore();
const nodeHelpers = useNodeHelpers();
const workflowNodes = computed(() => {
return workflowsStore.allNodes;
});
const {
appCredentials,
credentialOverrides,
credentialUsages,
credentialsByKey,
numFilledCredentials,
selectedCredentialIdByKey,
setSelectedCredentialId,
unsetSelectedCredential,
} = useCredentialSetupState(workflowNodes);
/**
* Selects initial credentials. For existing workflows this means using
* the credentials that are already set on the nodes.
*/
const setInitialCredentialSelection = () => {
selectedCredentialIdByKey.value = {};
for (const credUsage of credentialUsages.value) {
const typeCredentials = credentialsStore.getCredentialsByType(credUsage.credentialType);
// Make sure there is a credential for this type with the given name
const credential = typeCredentials.find((cred) => cred.name === credUsage.credentialName);
if (!credential) {
continue;
}
selectedCredentialIdByKey.value[credUsage.key] = credential.id;
}
};
/**
* Sets the given credential to all nodes that use it.
*/
const setCredential = (credentialKey: TemplateCredentialKey, credentialId: string) => {
setSelectedCredentialId(credentialKey, credentialId);
const usages = credentialsByKey.value.get(credentialKey);
if (!usages) {
return;
}
const credentialName = credentialsStore.getCredentialById(credentialId)?.name;
const credential: INodeCredentialsDetails = {
id: credentialId,
name: credentialName,
};
usages.usedBy.forEach((node) => {
workflowsStore.updateNodeProperties({
name: node.name,
properties: {
position: node.position,
credentials: {
...node.credentials,
[usages.credentialType]: credential,
},
},
});
// We can't use updateNodeCredentialIssues because the previous
// step creates a new instance of the node in the store and
// `node` no longer points to the correct node.
nodeHelpers.updateNodeCredentialIssuesByName(node.name);
});
setInitialCredentialSelection();
};
/**
* Removes given credential from all nodes that use it.
*/
const unsetCredential = (credentialKey: TemplateCredentialKey) => {
unsetSelectedCredential(credentialKey);
const usages = credentialsByKey.value.get(credentialKey);
if (!usages) {
return;
}
usages.usedBy.forEach((node) => {
const credentials = { ...node.credentials };
delete credentials[usages.credentialType];
workflowsStore.updateNodeProperties({
name: node.name,
properties: {
position: node.position,
credentials,
},
});
// We can't use updateNodeCredentialIssues because the previous
// step creates a new instance of the node in the store and
// `node` no longer points to the correct node.
nodeHelpers.updateNodeCredentialIssuesByName(node.name);
});
setInitialCredentialSelection();
};
return {
appCredentials,
credentialOverrides,
credentialUsages,
credentialsByKey,
numFilledCredentials,
selectedCredentialIdByKey,
setInitialCredentialSelection,
setCredential,
unsetCredential,
};
};