mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
fix(editor): Close Workflow URL Import Modal after import (#15177)
Co-authored-by: Milorad FIlipović <milorad@n8n.io>
This commit is contained in:
@@ -6,12 +6,10 @@ import {
|
|||||||
MANUAL_TRIGGER_NODE_NAME,
|
MANUAL_TRIGGER_NODE_NAME,
|
||||||
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
import { MessageBox as MessageBoxClass } from '../pages/modals/message-box';
|
|
||||||
import { NDV } from '../pages/ndv';
|
import { NDV } from '../pages/ndv';
|
||||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||||
|
|
||||||
const WorkflowPage = new WorkflowPageClass();
|
const WorkflowPage = new WorkflowPageClass();
|
||||||
const messageBox = new MessageBoxClass();
|
|
||||||
const ndv = new NDV();
|
const ndv = new NDV();
|
||||||
|
|
||||||
describe('Undo/Redo', () => {
|
describe('Undo/Redo', () => {
|
||||||
@@ -256,11 +254,11 @@ describe('Undo/Redo', () => {
|
|||||||
WorkflowPage.getters.workflowMenuItemImportFromURLItem().should('be.visible');
|
WorkflowPage.getters.workflowMenuItemImportFromURLItem().should('be.visible');
|
||||||
WorkflowPage.getters.workflowMenuItemImportFromURLItem().click();
|
WorkflowPage.getters.workflowMenuItemImportFromURLItem().click();
|
||||||
// Try while prompt is open
|
// Try while prompt is open
|
||||||
messageBox.getters.header().click();
|
WorkflowPage.getters.inputURLImportWorkflowFromURL().click();
|
||||||
WorkflowPage.actions.hitUndo();
|
WorkflowPage.actions.hitUndo();
|
||||||
WorkflowPage.getters.canvasNodes().should('have.have.length', 1);
|
WorkflowPage.getters.canvasNodes().should('have.have.length', 1);
|
||||||
// Close prompt and try again
|
// Close prompt and try again
|
||||||
messageBox.actions.cancel();
|
WorkflowPage.getters.cancelActionImportWorkflowFromURL().click();
|
||||||
WorkflowPage.actions.hitUndo();
|
WorkflowPage.actions.hitUndo();
|
||||||
WorkflowPage.getters.canvasNodes().should('have.have.length', 0);
|
WorkflowPage.getters.canvasNodes().should('have.have.length', 0);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import { WorkflowPage } from '../pages';
|
import { WorkflowPage } from '../pages';
|
||||||
import { MessageBox as MessageBoxClass } from '../pages/modals/message-box';
|
|
||||||
import { errorToast, successToast } from '../pages/notifications';
|
import { errorToast, successToast } from '../pages/notifications';
|
||||||
|
|
||||||
const workflowPage = new WorkflowPage();
|
const workflowPage = new WorkflowPage();
|
||||||
const messageBox = new MessageBoxClass();
|
|
||||||
|
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.fixture('Onboarding_workflow.json').then((data) => {
|
cy.fixture('Onboarding_workflow.json').then((data) => {
|
||||||
@@ -20,11 +18,13 @@ describe('Import workflow', () => {
|
|||||||
workflowPage.getters.workflowMenu().click();
|
workflowPage.getters.workflowMenu().click();
|
||||||
workflowPage.getters.workflowMenuItemImportFromURLItem().click();
|
workflowPage.getters.workflowMenuItemImportFromURLItem().click();
|
||||||
|
|
||||||
messageBox.getters.modal().should('be.visible');
|
workflowPage.getters.inputURLImportWorkflowFromURL().should('be.visible');
|
||||||
|
|
||||||
messageBox.getters.content().type('https://fakepage.com/workflow.json');
|
workflowPage.getters
|
||||||
|
.inputURLImportWorkflowFromURL()
|
||||||
|
.type('https://fakepage.com/workflow.json');
|
||||||
|
|
||||||
messageBox.getters.confirm().click();
|
workflowPage.getters.confirmActionImportWorkflowFromURL().click();
|
||||||
|
|
||||||
workflowPage.actions.zoomToFit();
|
workflowPage.actions.zoomToFit();
|
||||||
|
|
||||||
@@ -37,7 +37,6 @@ describe('Import workflow', () => {
|
|||||||
|
|
||||||
it('clicking outside modal should not show error toast', () => {
|
it('clicking outside modal should not show error toast', () => {
|
||||||
workflowPage.actions.visit(true);
|
workflowPage.actions.visit(true);
|
||||||
|
|
||||||
workflowPage.getters.workflowMenu().click();
|
workflowPage.getters.workflowMenu().click();
|
||||||
workflowPage.getters.workflowMenuItemImportFromURLItem().click();
|
workflowPage.getters.workflowMenuItemImportFromURLItem().click();
|
||||||
|
|
||||||
@@ -51,7 +50,7 @@ describe('Import workflow', () => {
|
|||||||
|
|
||||||
workflowPage.getters.workflowMenu().click();
|
workflowPage.getters.workflowMenu().click();
|
||||||
workflowPage.getters.workflowMenuItemImportFromURLItem().click();
|
workflowPage.getters.workflowMenuItemImportFromURLItem().click();
|
||||||
messageBox.getters.cancel().click();
|
workflowPage.getters.cancelActionImportWorkflowFromURL().click();
|
||||||
|
|
||||||
errorToast().should('not.exist');
|
errorToast().should('not.exist');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -219,6 +219,9 @@ export class WorkflowPage extends BasePage {
|
|||||||
}
|
}
|
||||||
return parseFloat(element.css('top'));
|
return parseFloat(element.css('top'));
|
||||||
},
|
},
|
||||||
|
inputURLImportWorkflowFromURL: () => cy.getByTestId('workflow-url-import-input'),
|
||||||
|
cancelActionImportWorkflowFromURL: () => cy.getByTestId('cancel-workflow-import-url-button'),
|
||||||
|
confirmActionImportWorkflowFromURL: () => cy.getByTestId('confirm-workflow-import-url-button'),
|
||||||
confirmModal: () => cy.get('div[role=dialog][aria-modal=true]'),
|
confirmModal: () => cy.get('div[role=dialog][aria-modal=true]'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import { createComponentRenderer } from '@/__tests__/render';
|
||||||
|
import ImportWorkflowUrlModal from './ImportWorkflowUrlModal.vue';
|
||||||
|
import { createTestingPinia } from '@pinia/testing';
|
||||||
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
|
import { nodeViewEventBus } from '@/event-bus';
|
||||||
|
import { IMPORT_WORKFLOW_URL_MODAL_KEY } from '@/constants';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
|
const ModalStub = {
|
||||||
|
template: `
|
||||||
|
<div>
|
||||||
|
<slot name="header" />
|
||||||
|
<slot name="title" />
|
||||||
|
<slot name="content" />
|
||||||
|
<slot name="footer" />
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
modalsById: {
|
||||||
|
[IMPORT_WORKFLOW_URL_MODAL_KEY]: {
|
||||||
|
open: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
modalStack: [IMPORT_WORKFLOW_URL_MODAL_KEY],
|
||||||
|
};
|
||||||
|
|
||||||
|
const global = {
|
||||||
|
stubs: {
|
||||||
|
Modal: ModalStub,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderModal = createComponentRenderer(ImportWorkflowUrlModal);
|
||||||
|
let pinia: ReturnType<typeof createTestingPinia>;
|
||||||
|
|
||||||
|
describe('ImportWorkflowUrlModal', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
pinia = createTestingPinia({ initialState });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should close the modal on cancel', async () => {
|
||||||
|
const { getByTestId } = renderModal({
|
||||||
|
global,
|
||||||
|
pinia,
|
||||||
|
});
|
||||||
|
|
||||||
|
const uiStore = useUIStore();
|
||||||
|
|
||||||
|
await userEvent.click(getByTestId('cancel-workflow-import-url-button'));
|
||||||
|
|
||||||
|
expect(uiStore.closeModal).toHaveBeenCalledWith(IMPORT_WORKFLOW_URL_MODAL_KEY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit importWorkflowUrl event on confirm', async () => {
|
||||||
|
const { getByTestId } = renderModal({
|
||||||
|
global,
|
||||||
|
pinia,
|
||||||
|
});
|
||||||
|
|
||||||
|
const urlInput = getByTestId('workflow-url-import-input');
|
||||||
|
const confirmButton = getByTestId('confirm-workflow-import-url-button');
|
||||||
|
|
||||||
|
await userEvent.type(urlInput, 'https://valid-url.com/workflow.json');
|
||||||
|
expect(confirmButton).toBeEnabled();
|
||||||
|
|
||||||
|
const emitSpy = vi.spyOn(nodeViewEventBus, 'emit');
|
||||||
|
await userEvent.click(confirmButton);
|
||||||
|
|
||||||
|
expect(emitSpy).toHaveBeenCalledWith('importWorkflowUrl', {
|
||||||
|
url: 'https://valid-url.com/workflow.json',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should disable confirm button for invalid URL', async () => {
|
||||||
|
const { getByTestId } = renderModal({
|
||||||
|
global,
|
||||||
|
pinia,
|
||||||
|
});
|
||||||
|
|
||||||
|
const urlInput = getByTestId('workflow-url-import-input');
|
||||||
|
const confirmButton = getByTestId('confirm-workflow-import-url-button');
|
||||||
|
|
||||||
|
await userEvent.type(urlInput, 'invalid-url');
|
||||||
|
expect(confirmButton).toBeDisabled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { useI18n } from '@/composables/useI18n';
|
||||||
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
|
import { nodeViewEventBus } from '@/event-bus';
|
||||||
|
import { VALID_WORKFLOW_IMPORT_URL_REGEX, IMPORT_WORKFLOW_URL_MODAL_KEY } from '@/constants';
|
||||||
|
|
||||||
|
const i18n = useI18n();
|
||||||
|
const uiStore = useUIStore();
|
||||||
|
|
||||||
|
const url = ref('');
|
||||||
|
const inputRef = ref<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
|
const isValid = computed(() => {
|
||||||
|
return url.value ? VALID_WORKFLOW_IMPORT_URL_REGEX.test(url.value) : true;
|
||||||
|
});
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
uiStore.closeModal(IMPORT_WORKFLOW_URL_MODAL_KEY);
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirm = () => {
|
||||||
|
nodeViewEventBus.emit('importWorkflowUrl', { url: url.value });
|
||||||
|
closeModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
const focusInput = async () => {
|
||||||
|
if (inputRef.value) {
|
||||||
|
inputRef.value.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
:name="IMPORT_WORKFLOW_URL_MODAL_KEY"
|
||||||
|
:title="i18n.baseText('mainSidebar.prompt.importWorkflowFromUrl')"
|
||||||
|
:show-close="true"
|
||||||
|
:center="true"
|
||||||
|
width="420px"
|
||||||
|
@opened="focusInput"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div :class="$style.noScrollbar">
|
||||||
|
<n8n-input
|
||||||
|
ref="inputRef"
|
||||||
|
v-model="url"
|
||||||
|
:placeholder="i18n.baseText('mainSidebar.prompt.workflowUrl')"
|
||||||
|
:state="isValid ? 'default' : 'error'"
|
||||||
|
data-test-id="workflow-url-import-input"
|
||||||
|
@keyup.enter="confirm"
|
||||||
|
/>
|
||||||
|
<p :class="$style['error-text']" :style="{ visibility: isValid ? 'hidden' : 'visible' }">
|
||||||
|
{{ i18n.baseText('mainSidebar.prompt.invalidUrl') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<div :class="$style.footer">
|
||||||
|
<n8n-button
|
||||||
|
type="primary"
|
||||||
|
float="right"
|
||||||
|
:disabled="!url || !isValid"
|
||||||
|
data-test-id="confirm-workflow-import-url-button"
|
||||||
|
@click="confirm"
|
||||||
|
>
|
||||||
|
{{ i18n.baseText('mainSidebar.prompt.import') }}
|
||||||
|
</n8n-button>
|
||||||
|
<n8n-button
|
||||||
|
type="secondary"
|
||||||
|
float="right"
|
||||||
|
data-test-id="cancel-workflow-import-url-button"
|
||||||
|
@click="closeModal"
|
||||||
|
>
|
||||||
|
{{ i18n.baseText('mainSidebar.prompt.cancel') }}
|
||||||
|
</n8n-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.error-text {
|
||||||
|
color: var(--color-danger);
|
||||||
|
font-size: var(--font-size-2xs);
|
||||||
|
margin-top: var(--spacing-2xs);
|
||||||
|
height: var(--spacing-s);
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
> * {
|
||||||
|
margin-left: var(--spacing-3xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.noScrollbar {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -6,11 +6,11 @@ import {
|
|||||||
MODAL_CONFIRM,
|
MODAL_CONFIRM,
|
||||||
PLACEHOLDER_EMPTY_WORKFLOW_ID,
|
PLACEHOLDER_EMPTY_WORKFLOW_ID,
|
||||||
SOURCE_CONTROL_PUSH_MODAL_KEY,
|
SOURCE_CONTROL_PUSH_MODAL_KEY,
|
||||||
VALID_WORKFLOW_IMPORT_URL_REGEX,
|
|
||||||
VIEWS,
|
VIEWS,
|
||||||
WORKFLOW_MENU_ACTIONS,
|
WORKFLOW_MENU_ACTIONS,
|
||||||
WORKFLOW_SETTINGS_MODAL_KEY,
|
WORKFLOW_SETTINGS_MODAL_KEY,
|
||||||
WORKFLOW_SHARE_MODAL_KEY,
|
WORKFLOW_SHARE_MODAL_KEY,
|
||||||
|
IMPORT_WORKFLOW_URL_MODAL_KEY,
|
||||||
} from '@/constants';
|
} from '@/constants';
|
||||||
import ShortenName from '@/components/ShortenName.vue';
|
import ShortenName from '@/components/ShortenName.vue';
|
||||||
import WorkflowTagsContainer from '@/components/WorkflowTagsContainer.vue';
|
import WorkflowTagsContainer from '@/components/WorkflowTagsContainer.vue';
|
||||||
@@ -476,24 +476,7 @@ async function onWorkflowMenuSelect(action: WORKFLOW_MENU_ACTIONS): Promise<void
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WORKFLOW_MENU_ACTIONS.IMPORT_FROM_URL: {
|
case WORKFLOW_MENU_ACTIONS.IMPORT_FROM_URL: {
|
||||||
try {
|
uiStore.openModal(IMPORT_WORKFLOW_URL_MODAL_KEY);
|
||||||
const promptResponse = await message.prompt(
|
|
||||||
locale.baseText('mainSidebar.prompt.workflowUrl') + ':',
|
|
||||||
locale.baseText('mainSidebar.prompt.importWorkflowFromUrl') + ':',
|
|
||||||
{
|
|
||||||
confirmButtonText: locale.baseText('mainSidebar.prompt.import'),
|
|
||||||
cancelButtonText: locale.baseText('mainSidebar.prompt.cancel'),
|
|
||||||
inputErrorMessage: locale.baseText('mainSidebar.prompt.invalidUrl'),
|
|
||||||
inputPattern: VALID_WORKFLOW_IMPORT_URL_REGEX,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (promptResponse.action === 'cancel') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeViewEventBus.emit('importWorkflowUrl', { url: promptResponse.value });
|
|
||||||
} catch (e) {}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WORKFLOW_MENU_ACTIONS.IMPORT_FROM_FILE: {
|
case WORKFLOW_MENU_ACTIONS.IMPORT_FROM_FILE: {
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import {
|
|||||||
MOVE_FOLDER_MODAL_KEY,
|
MOVE_FOLDER_MODAL_KEY,
|
||||||
WORKFLOW_ACTIVATION_CONFLICTING_WEBHOOK_MODAL_KEY,
|
WORKFLOW_ACTIVATION_CONFLICTING_WEBHOOK_MODAL_KEY,
|
||||||
FROM_AI_PARAMETERS_MODAL_KEY,
|
FROM_AI_PARAMETERS_MODAL_KEY,
|
||||||
|
IMPORT_WORKFLOW_URL_MODAL_KEY,
|
||||||
} from '@/constants';
|
} from '@/constants';
|
||||||
|
|
||||||
import AboutModal from '@/components/AboutModal.vue';
|
import AboutModal from '@/components/AboutModal.vue';
|
||||||
@@ -75,6 +76,7 @@ import PromptMfaCodeModal from './PromptMfaCodeModal/PromptMfaCodeModal.vue';
|
|||||||
import CommunityPlusEnrollmentModal from '@/components/CommunityPlusEnrollmentModal.vue';
|
import CommunityPlusEnrollmentModal from '@/components/CommunityPlusEnrollmentModal.vue';
|
||||||
import WorkflowActivationConflictingWebhookModal from '@/components/WorkflowActivationConflictingWebhookModal.vue';
|
import WorkflowActivationConflictingWebhookModal from '@/components/WorkflowActivationConflictingWebhookModal.vue';
|
||||||
import FromAiParametersModal from '@/components/FromAiParametersModal.vue';
|
import FromAiParametersModal from '@/components/FromAiParametersModal.vue';
|
||||||
|
import ImportWorkflowUrlModal from '@/components/ImportWorkflowUrlModal.vue';
|
||||||
import type { EventBus } from '@n8n/utils/event-bus';
|
import type { EventBus } from '@n8n/utils/event-bus';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -124,6 +126,10 @@ import type { EventBus } from '@n8n/utils/event-bus';
|
|||||||
</template>
|
</template>
|
||||||
</ModalRoot>
|
</ModalRoot>
|
||||||
|
|
||||||
|
<ModalRoot :name="IMPORT_WORKFLOW_URL_MODAL_KEY">
|
||||||
|
<ImportWorkflowUrlModal />
|
||||||
|
</ModalRoot>
|
||||||
|
|
||||||
<ModalRoot :name="PERSONALIZATION_MODAL_KEY">
|
<ModalRoot :name="PERSONALIZATION_MODAL_KEY">
|
||||||
<PersonalizationModal />
|
<PersonalizationModal />
|
||||||
</ModalRoot>
|
</ModalRoot>
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export const CREDENTIAL_SELECT_MODAL_KEY = 'selectCredential';
|
|||||||
export const DELETE_USER_MODAL_KEY = 'deleteUser';
|
export const DELETE_USER_MODAL_KEY = 'deleteUser';
|
||||||
export const INVITE_USER_MODAL_KEY = 'inviteUser';
|
export const INVITE_USER_MODAL_KEY = 'inviteUser';
|
||||||
export const DUPLICATE_MODAL_KEY = 'duplicate';
|
export const DUPLICATE_MODAL_KEY = 'duplicate';
|
||||||
|
export const IMPORT_WORKFLOW_URL_MODAL_KEY = 'importWorkflowUrl';
|
||||||
export const TAGS_MANAGER_MODAL_KEY = 'tagsManager';
|
export const TAGS_MANAGER_MODAL_KEY = 'tagsManager';
|
||||||
export const ANNOTATION_TAGS_MANAGER_MODAL_KEY = 'annotationTagsManager';
|
export const ANNOTATION_TAGS_MANAGER_MODAL_KEY = 'annotationTagsManager';
|
||||||
export const VERSIONS_MODAL_KEY = 'versions';
|
export const VERSIONS_MODAL_KEY = 'versions';
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import {
|
|||||||
MOVE_FOLDER_MODAL_KEY,
|
MOVE_FOLDER_MODAL_KEY,
|
||||||
WORKFLOW_ACTIVATION_CONFLICTING_WEBHOOK_MODAL_KEY,
|
WORKFLOW_ACTIVATION_CONFLICTING_WEBHOOK_MODAL_KEY,
|
||||||
FROM_AI_PARAMETERS_MODAL_KEY,
|
FROM_AI_PARAMETERS_MODAL_KEY,
|
||||||
|
IMPORT_WORKFLOW_URL_MODAL_KEY,
|
||||||
} from '@/constants';
|
} from '@/constants';
|
||||||
import type {
|
import type {
|
||||||
INodeUi,
|
INodeUi,
|
||||||
@@ -126,6 +127,7 @@ export const useUIStore = defineStore(STORES.UI, () => {
|
|||||||
SETUP_CREDENTIALS_MODAL_KEY,
|
SETUP_CREDENTIALS_MODAL_KEY,
|
||||||
PROJECT_MOVE_RESOURCE_MODAL,
|
PROJECT_MOVE_RESOURCE_MODAL,
|
||||||
NEW_ASSISTANT_SESSION_MODAL,
|
NEW_ASSISTANT_SESSION_MODAL,
|
||||||
|
IMPORT_WORKFLOW_URL_MODAL_KEY,
|
||||||
].map((modalKey) => [modalKey, { open: false }]),
|
].map((modalKey) => [modalKey, { open: false }]),
|
||||||
),
|
),
|
||||||
[DELETE_USER_MODAL_KEY]: {
|
[DELETE_USER_MODAL_KEY]: {
|
||||||
@@ -199,6 +201,12 @@ export const useUIStore = defineStore(STORES.UI, () => {
|
|||||||
nodeName: undefined,
|
nodeName: undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
[IMPORT_WORKFLOW_URL_MODAL_KEY]: {
|
||||||
|
open: false,
|
||||||
|
data: {
|
||||||
|
url: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const modalStack = ref<string[]>([]);
|
const modalStack = ref<string[]>([]);
|
||||||
|
|||||||
Reference in New Issue
Block a user