mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-14 08:34:07 -08:00
refactor: Clean up hooks and fake doors (no-changelog) (#11498)
This commit is contained in:
parent
c5191e697a
commit
02b77367bd
|
@ -52,7 +52,6 @@ import type {
|
|||
AI_NODE_CREATOR_VIEW,
|
||||
CREDENTIAL_EDIT_MODAL_KEY,
|
||||
SignInType,
|
||||
FAKE_DOOR_FEATURES,
|
||||
TRIGGER_NODE_CREATOR_VIEW,
|
||||
REGULAR_NODE_CREATOR_VIEW,
|
||||
AI_OTHERS_NODE_CREATOR_VIEW,
|
||||
|
@ -62,7 +61,6 @@ import type { BulkCommand, Undoable } from '@/models/history';
|
|||
import type { PartialBy, TupleToUnion } from '@/utils/typeHelpers';
|
||||
|
||||
import type { ProjectSharingData } from '@/types/projects.types';
|
||||
import type { BaseTextKey } from './plugins/i18n';
|
||||
|
||||
export * from 'n8n-design-system/types';
|
||||
|
||||
|
@ -1036,24 +1034,6 @@ export interface NotificationOptions extends Partial<ElementNotificationOptions>
|
|||
message: string | ElementNotificationOptions['message'];
|
||||
}
|
||||
|
||||
export type IFakeDoor = {
|
||||
id: FAKE_DOOR_FEATURES;
|
||||
featureName: BaseTextKey;
|
||||
icon?: string;
|
||||
infoText?: BaseTextKey;
|
||||
actionBoxTitle: BaseTextKey;
|
||||
actionBoxDescription: BaseTextKey;
|
||||
actionBoxButtonLabel?: BaseTextKey;
|
||||
linkURL: string;
|
||||
uiLocations: IFakeDoorLocation[];
|
||||
};
|
||||
|
||||
export type IFakeDoorLocation =
|
||||
| 'settings'
|
||||
| 'settings/users'
|
||||
| 'credentialsModal'
|
||||
| 'workflowShareModal';
|
||||
|
||||
export type NodeFilterType =
|
||||
| typeof REGULAR_NODE_CREATOR_VIEW
|
||||
| typeof TRIGGER_NODE_CREATOR_VIEW
|
||||
|
|
|
@ -66,9 +66,6 @@ describe('Init', () => {
|
|||
vi.mocked(useRootStore).mockReturnValue({ defaultLocale: 'es' } as ReturnType<
|
||||
typeof useRootStore
|
||||
>);
|
||||
vi.mock('@/hooks/register', () => ({
|
||||
initializeCloudHooks: vi.fn(),
|
||||
}));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
|
@ -22,7 +22,6 @@ import { NodeHelpers } from 'n8n-workflow';
|
|||
import CredentialConfig from '@/components/CredentialEdit/CredentialConfig.vue';
|
||||
import CredentialInfo from '@/components/CredentialEdit/CredentialInfo.vue';
|
||||
import CredentialSharing from '@/components/CredentialEdit/CredentialSharing.ee.vue';
|
||||
import FeatureComingSoon from '@/components/FeatureComingSoon.vue';
|
||||
import InlineNameEdit from '@/components/InlineNameEdit.vue';
|
||||
import Modal from '@/components/Modal.vue';
|
||||
import SaveButton from '@/components/SaveButton.vue';
|
||||
|
@ -518,14 +517,13 @@ async function loadCurrentCredential() {
|
|||
|
||||
function onTabSelect(tab: string) {
|
||||
activeTab.value = tab;
|
||||
const tabName: string = tab.replaceAll('coming-soon/', '');
|
||||
const credType: string = credentialType.value ? credentialType.value.name : '';
|
||||
const activeNode: INode | null = ndvStore.activeNode;
|
||||
|
||||
telemetry.track('User viewed credential tab', {
|
||||
credential_type: credType,
|
||||
node_type: activeNode ? activeNode.type : null,
|
||||
tab: tabName,
|
||||
tab,
|
||||
workflow_id: workflowsStore.workflowId,
|
||||
credential_id: credentialId.value,
|
||||
sharing_enabled: EnterpriseEditionFeature.Sharing,
|
||||
|
@ -1130,9 +1128,6 @@ function resetCredentialData(): void {
|
|||
<div v-else-if="activeTab === 'details' && credentialType" :class="$style.mainContent">
|
||||
<CredentialInfo :current-credential="currentCredential" />
|
||||
</div>
|
||||
<div v-else-if="activeTab.startsWith('coming-soon')" :class="$style.mainContent">
|
||||
<FeatureComingSoon :feature-id="activeTab.split('/')[1]"></FeatureComingSoon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { mapStores } from 'pinia';
|
||||
import type { IFakeDoor } from '@/Interface';
|
||||
import { useRootStore } from '@/stores/root.store';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FeatureComingSoon',
|
||||
props: {
|
||||
featureId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
showTitle: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useRootStore, useSettingsStore, useUIStore, useUsersStore),
|
||||
userId(): string {
|
||||
return this.usersStore.currentUserId || '';
|
||||
},
|
||||
instanceId(): string {
|
||||
return this.rootStore.instanceId;
|
||||
},
|
||||
featureInfo(): IFakeDoor | undefined {
|
||||
return this.uiStore.fakeDoorsById[this.featureId];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openLinkPage() {
|
||||
if (this.featureInfo) {
|
||||
window.open(
|
||||
`${this.featureInfo.linkURL}&u=${this.instanceId}#${this.userId}&v=${this.rootStore.versionCli}`,
|
||||
'_blank',
|
||||
);
|
||||
this.$telemetry.track('user clicked feature waiting list button', {
|
||||
feature: this.featureId,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="featureInfo" :class="[$style.container]">
|
||||
<div v-if="showTitle" class="mb-2xl">
|
||||
<n8n-heading size="2xlarge">
|
||||
{{ $locale.baseText(featureInfo.featureName) }}
|
||||
</n8n-heading>
|
||||
</div>
|
||||
<div v-if="featureInfo.infoText" class="mb-l">
|
||||
<n8n-info-tip theme="info" type="note">
|
||||
<span v-n8n-html="$locale.baseText(featureInfo.infoText)"></span>
|
||||
</n8n-info-tip>
|
||||
</div>
|
||||
<div :class="$style.actionBoxContainer">
|
||||
<n8n-action-box
|
||||
:description="$locale.baseText(featureInfo.actionBoxDescription)"
|
||||
:button-text="
|
||||
$locale.baseText(featureInfo.actionBoxButtonLabel || 'fakeDoor.actionBox.button.label')
|
||||
"
|
||||
@click:button="openLinkPage"
|
||||
>
|
||||
<template #heading>
|
||||
<span v-n8n-html="$locale.baseText(featureInfo.actionBoxTitle)" />
|
||||
</template>
|
||||
</n8n-action-box>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.actionBoxContainer {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
|
@ -2,9 +2,7 @@
|
|||
import { computed } from 'vue';
|
||||
import { ABOUT_MODAL_KEY, VIEWS } from '@/constants';
|
||||
import { useUserHelpers } from '@/composables/useUserHelpers';
|
||||
import type { IFakeDoor } from '@/Interface';
|
||||
import type { IMenuItem } from 'n8n-design-system';
|
||||
import type { BaseTextKey } from '@/plugins/i18n';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { useRootStore } from '@/stores/root.store';
|
||||
|
@ -26,23 +24,6 @@ const rootStore = useRootStore();
|
|||
const settingsStore = useSettingsStore();
|
||||
const uiStore = useUIStore();
|
||||
|
||||
const settingsFakeDoorFeatures = computed<IFakeDoor[]>(() =>
|
||||
Object.keys(uiStore.fakeDoorsByLocation)
|
||||
.filter((location: string) => location.includes('settings'))
|
||||
.map((location) => uiStore.fakeDoorsByLocation[location]),
|
||||
);
|
||||
|
||||
const handleSelect = (key: string) => {
|
||||
switch (key) {
|
||||
case 'users': // Fakedoor feature added via hooks when user management is disabled on cloud
|
||||
case 'logging':
|
||||
router.push({ name: VIEWS.FAKE_DOOR, params: { featureId: key } }).catch(() => {});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const sidebarMenuItems = computed<IMenuItem[]>(() => {
|
||||
const menuItems: IMenuItem[] = [
|
||||
{
|
||||
|
@ -122,19 +103,6 @@ const sidebarMenuItems = computed<IMenuItem[]>(() => {
|
|||
},
|
||||
];
|
||||
|
||||
for (const item of settingsFakeDoorFeatures.value) {
|
||||
if (item.uiLocations.includes('settings')) {
|
||||
menuItems.push({
|
||||
id: item.id,
|
||||
icon: item.icon ?? 'question',
|
||||
label: i18n.baseText(item.featureName as BaseTextKey),
|
||||
position: 'top',
|
||||
available: true,
|
||||
activateOnRoutePaths: [`/settings/coming-soon/${item.id}`],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
menuItems.push({
|
||||
id: 'settings-log-streaming',
|
||||
icon: 'sign-in-alt',
|
||||
|
@ -159,7 +127,7 @@ const sidebarMenuItems = computed<IMenuItem[]>(() => {
|
|||
|
||||
<template>
|
||||
<div :class="$style.container">
|
||||
<n8n-menu :items="sidebarMenuItems" @select="handleSelect">
|
||||
<n8n-menu :items="sidebarMenuItems">
|
||||
<template #header>
|
||||
<div :class="$style.returnButton" data-test-id="settings-back" @click="emit('return')">
|
||||
<i class="mr-xs">
|
||||
|
|
|
@ -479,7 +479,6 @@ export const enum VIEWS {
|
|||
PERSONAL_SETTINGS = 'PersonalSettings',
|
||||
API_SETTINGS = 'APISettings',
|
||||
NOT_FOUND = 'NotFoundView',
|
||||
FAKE_DOOR = 'ComingSoon',
|
||||
COMMUNITY_NODES = 'CommunityNodes',
|
||||
WORKFLOWS = 'WorkflowsView',
|
||||
WORKFLOW_EXECUTIONS = 'WorkflowExecutions',
|
||||
|
@ -500,12 +499,6 @@ export const enum VIEWS {
|
|||
|
||||
export const EDITABLE_CANVAS_VIEWS = [VIEWS.WORKFLOW, VIEWS.NEW_WORKFLOW, VIEWS.EXECUTION_DEBUG];
|
||||
|
||||
export const enum FAKE_DOOR_FEATURES {
|
||||
ENVIRONMENTS = 'environments',
|
||||
LOGGING = 'logging',
|
||||
SSO = 'sso',
|
||||
}
|
||||
|
||||
export const TEST_PIN_DATA = [
|
||||
{
|
||||
name: 'First item',
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
import { hooksAddFakeDoorFeatures } from '@/hooks/utils';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import type { ExternalHooks } from '@/types';
|
||||
|
||||
export const n8nCloudHooks: PartialDeep<ExternalHooks> = {
|
||||
app: {
|
||||
mount: [
|
||||
() => {
|
||||
hooksAddFakeDoorFeatures();
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
|
@ -1,40 +0,0 @@
|
|||
import type { PartialDeep } from 'type-fest';
|
||||
import type { ExternalHooks, ExternalHooksGenericContext } from '@/types';
|
||||
|
||||
export function extendExternalHooks(hooksExtension: PartialDeep<ExternalHooks>) {
|
||||
if (typeof window.n8nExternalHooks === 'undefined') {
|
||||
window.n8nExternalHooks = {};
|
||||
}
|
||||
|
||||
for (const resource of Object.keys(hooksExtension) as Array<keyof ExternalHooks>) {
|
||||
if (typeof window.n8nExternalHooks[resource] === 'undefined') {
|
||||
window.n8nExternalHooks[resource] = {};
|
||||
}
|
||||
|
||||
const extensionContext = hooksExtension[resource] as ExternalHooksGenericContext;
|
||||
const context = window.n8nExternalHooks[resource] as ExternalHooksGenericContext;
|
||||
for (const operator of Object.keys(extensionContext)) {
|
||||
if (typeof context[operator] === 'undefined') {
|
||||
context[operator] = [];
|
||||
}
|
||||
|
||||
context[operator].push(...extensionContext[operator]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let cloudHooksInitialized = false;
|
||||
export async function initializeCloudHooks() {
|
||||
if (cloudHooksInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { n8nCloudHooks } = await import('@/hooks/cloud');
|
||||
extendExternalHooks(n8nCloudHooks);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to extend external hooks: ${error.message}`);
|
||||
} finally {
|
||||
cloudHooksInitialized = true;
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
import type { ITelemetryTrackProperties } from 'n8n-workflow';
|
||||
|
||||
export interface TelemetryEventData {
|
||||
eventName: string;
|
||||
properties?: ITelemetryTrackProperties;
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
import { useUIStore } from '@/stores/ui.store';
|
||||
import type { IFakeDoor } from '@/Interface';
|
||||
import { FAKE_DOOR_FEATURES } from '@/constants';
|
||||
import type { BaseTextKey } from '@/plugins/i18n';
|
||||
|
||||
export function compileFakeDoorFeatures(): IFakeDoor[] {
|
||||
const uiStore = useUIStore();
|
||||
const fakeDoorFeatures: IFakeDoor[] = uiStore.fakeDoorFeatures.map((feature) => ({ ...feature }));
|
||||
|
||||
const environmentsFeature = fakeDoorFeatures.find(
|
||||
(feature) => feature.id === FAKE_DOOR_FEATURES.ENVIRONMENTS,
|
||||
);
|
||||
if (environmentsFeature) {
|
||||
environmentsFeature.actionBoxTitle += '.cloud';
|
||||
environmentsFeature.linkURL += '&edition=cloud';
|
||||
}
|
||||
|
||||
const loggingFeature = fakeDoorFeatures.find(
|
||||
(feature) => feature.id === FAKE_DOOR_FEATURES.LOGGING,
|
||||
);
|
||||
if (loggingFeature) {
|
||||
loggingFeature.actionBoxTitle += '.cloud';
|
||||
loggingFeature.linkURL += '&edition=cloud';
|
||||
loggingFeature.infoText = '' as BaseTextKey;
|
||||
}
|
||||
|
||||
return fakeDoorFeatures;
|
||||
}
|
||||
|
||||
export const hooksAddFakeDoorFeatures = () => {
|
||||
const uiStore = useUIStore();
|
||||
|
||||
uiStore.fakeDoorFeatures = compileFakeDoorFeatures();
|
||||
};
|
|
@ -1 +0,0 @@
|
|||
export * from './hooksAddFakeDoorFeatures';
|
|
@ -5,7 +5,6 @@ import { useSettingsStore } from '@/stores/settings.store';
|
|||
import { useSourceControlStore } from '@/stores/sourceControl.store';
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
import { useExternalHooks } from '@/composables/useExternalHooks';
|
||||
import { initializeCloudHooks } from '@/hooks/register';
|
||||
import { useVersionsStore } from '@/stores/versions.store';
|
||||
import { useProjectsStore } from '@/stores/projects.store';
|
||||
import { useRolesStore } from './stores/roles.store';
|
||||
|
@ -34,14 +33,6 @@ export async function initializeCore() {
|
|||
await usersStore.initialize();
|
||||
|
||||
void versionsStore.checkForNewVersions();
|
||||
|
||||
if (settingsStore.isCloudDeployment) {
|
||||
try {
|
||||
await initializeCloudHooks();
|
||||
} catch (e) {
|
||||
console.error('Failed to initialize cloud hooks:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
coreInitialized = true;
|
||||
|
|
|
@ -825,12 +825,6 @@
|
|||
"expressionModalInput.pairedItemConnectionError": "No path back to node",
|
||||
"expressionModalInput.pairedItemInvalidPinnedError": "Unpin node ‘{node}’ and execute",
|
||||
"expressionModalInput.pairedItemError": "Can’t determine which item to use",
|
||||
"fakeDoor.settings.environments.name": "Environments",
|
||||
"fakeDoor.settings.sso.name": "Single Sign-On",
|
||||
"fakeDoor.settings.sso.actionBox.title": "We’re working on this (as a paid feature)",
|
||||
"fakeDoor.settings.sso.actionBox.title.cloud": "We’re working on this",
|
||||
"fakeDoor.settings.sso.actionBox.description": "SSO will offer a secured and convenient way to access n8n using your existing credentials (Google, Github, Keycloak…)",
|
||||
"fakeDoor.actionBox.button.label": "Join the list",
|
||||
"fixedCollectionParameter.choose": "Choose...",
|
||||
"fixedCollectionParameter.currentlyNoItemsExist": "Currently no items exist",
|
||||
"fixedCollectionParameter.deleteItem": "Delete item",
|
||||
|
|
|
@ -38,7 +38,6 @@ const SettingsCommunityNodesView = async () =>
|
|||
await import('./views/SettingsCommunityNodesView.vue');
|
||||
const SettingsApiView = async () => await import('./views/SettingsApiView.vue');
|
||||
const SettingsLogStreamingView = async () => await import('./views/SettingsLogStreamingView.vue');
|
||||
const SettingsFakeDoorView = async () => await import('./views/SettingsFakeDoorView.vue');
|
||||
const SetupView = async () => await import('./views/SetupView.vue');
|
||||
const SigninView = async () => await import('./views/SigninView.vue');
|
||||
const SignupView = async () => await import('./views/SignupView.vue');
|
||||
|
@ -642,24 +641,6 @@ export const routes: RouteRecordRaw[] = [
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'coming-soon/:featureId',
|
||||
name: VIEWS.FAKE_DOOR,
|
||||
components: {
|
||||
settingsView: SettingsFakeDoorView,
|
||||
},
|
||||
meta: {
|
||||
middleware: ['authenticated'],
|
||||
telemetry: {
|
||||
pageCategory: 'settings',
|
||||
getProperties(route: RouteLocation) {
|
||||
return {
|
||||
feature: route.params.featureId,
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'ldap',
|
||||
name: VIEWS.LDAP_SETTINGS,
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
CREDENTIAL_SELECT_MODAL_KEY,
|
||||
DELETE_USER_MODAL_KEY,
|
||||
DUPLICATE_MODAL_KEY,
|
||||
FAKE_DOOR_FEATURES,
|
||||
IMPORT_CURL_MODAL_KEY,
|
||||
INVITE_USER_MODAL_KEY,
|
||||
LOG_STREAM_MODAL_KEY,
|
||||
|
@ -41,7 +40,6 @@ import {
|
|||
} from '@/constants';
|
||||
import type {
|
||||
CloudUpdateLinkSourceType,
|
||||
IFakeDoorLocation,
|
||||
INodeUi,
|
||||
UTMCampaign,
|
||||
XYPosition,
|
||||
|
@ -51,7 +49,6 @@ import type {
|
|||
NotificationOptions,
|
||||
ModalState,
|
||||
ModalKey,
|
||||
IFakeDoor,
|
||||
} from '@/Interface';
|
||||
import { defineStore } from 'pinia';
|
||||
import { useRootStore } from '@/stores/root.store';
|
||||
|
@ -160,18 +157,6 @@ export const useUIStore = defineStore(STORES.UI, () => {
|
|||
const modalStack = ref<string[]>([]);
|
||||
const sidebarMenuCollapsed = ref<boolean>(true);
|
||||
const currentView = ref<string>('');
|
||||
const fakeDoorFeatures = ref<IFakeDoor[]>([
|
||||
{
|
||||
id: FAKE_DOOR_FEATURES.SSO,
|
||||
featureName: 'fakeDoor.settings.sso.name',
|
||||
icon: 'key',
|
||||
actionBoxTitle: 'fakeDoor.settings.sso.actionBox.title',
|
||||
actionBoxDescription: 'fakeDoor.settings.sso.actionBox.description',
|
||||
linkURL: 'https://n8n-community.typeform.com/to/l7QOrERN#f=sso',
|
||||
uiLocations: ['settings/users'],
|
||||
},
|
||||
]);
|
||||
|
||||
const draggable = ref<Draggable>({
|
||||
isDragging: false,
|
||||
type: '',
|
||||
|
@ -306,22 +291,6 @@ export const useUIStore = defineStore(STORES.UI, () => {
|
|||
|
||||
const activeModals = computed(() => modalStack.value.map((modalName) => modalName));
|
||||
|
||||
const fakeDoorsByLocation = computed(() =>
|
||||
fakeDoorFeatures.value.reduce((acc: { [uiLocation: string]: IFakeDoor }, fakeDoor) => {
|
||||
fakeDoor.uiLocations.forEach((uiLocation: IFakeDoorLocation) => {
|
||||
acc[uiLocation] = fakeDoor;
|
||||
});
|
||||
return acc;
|
||||
}, {}),
|
||||
);
|
||||
|
||||
const fakeDoorsById = computed(() =>
|
||||
fakeDoorFeatures.value.reduce((acc: { [id: string]: IFakeDoor }, fakeDoor) => {
|
||||
acc[fakeDoor.id.toString()] = fakeDoor;
|
||||
return acc;
|
||||
}, {}),
|
||||
);
|
||||
|
||||
const isReadOnlyView = computed(() => {
|
||||
return ![
|
||||
VIEWS.WORKFLOW.toString(),
|
||||
|
@ -635,7 +604,6 @@ export const useUIStore = defineStore(STORES.UI, () => {
|
|||
getLastSelectedNode,
|
||||
isVersionsOpen,
|
||||
isModalActiveById,
|
||||
fakeDoorsByLocation,
|
||||
isReadOnlyView,
|
||||
isActionActive,
|
||||
activeActions,
|
||||
|
@ -659,13 +627,11 @@ export const useUIStore = defineStore(STORES.UI, () => {
|
|||
nodeViewInitialized,
|
||||
addFirstStepOnLoad,
|
||||
sidebarMenuCollapsed,
|
||||
fakeDoorFeatures,
|
||||
bannerStack,
|
||||
theme,
|
||||
modalsById,
|
||||
currentView,
|
||||
isAnyModalOpen,
|
||||
fakeDoorsById,
|
||||
pendingNotificationsForViews,
|
||||
setTheme,
|
||||
setMode,
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
<script lang="ts">
|
||||
import type { IFakeDoor } from '@/Interface';
|
||||
import { defineComponent } from 'vue';
|
||||
import FeatureComingSoon from '@/components/FeatureComingSoon.vue';
|
||||
import { mapStores } from 'pinia';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SettingsFakeDoorView',
|
||||
components: {
|
||||
FeatureComingSoon,
|
||||
},
|
||||
props: {
|
||||
featureId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUIStore),
|
||||
featureInfo(): IFakeDoor | undefined {
|
||||
return this.uiStore.fakeDoorsById[this.featureId];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openLinkPage() {
|
||||
if (this.featureInfo) {
|
||||
window.open(this.featureInfo.linkURL, '_blank');
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FeatureComingSoon :feature-id="featureId" show-title />
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
|
||||
*:first-child {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
Reference in a new issue