mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
feat(editor): Address data tables UI feedback (no-changelog) (#18566)
Co-authored-by: Milorad FIlipović <milorad@n8n.io>
This commit is contained in:
@@ -2837,7 +2837,7 @@
|
||||
"dataStore.dataStores": "Data Tables",
|
||||
"dataStore.empty.label": "You don't have any data tables yet",
|
||||
"dataStore.empty.description": "Once you create data tables for your projects, they will appear here",
|
||||
"dataStore.empty.button.label": "Create data table in \"{projectName}\"",
|
||||
"dataStore.empty.button.label": "Create Data Table in \"{projectName}\"",
|
||||
"dataStore.card.size": "{size}MB",
|
||||
"dataStore.card.column.count": "{count} column | {count} columns",
|
||||
"dataStore.card.row.count": "{count} record | {count} records",
|
||||
@@ -2847,9 +2847,9 @@
|
||||
"dataStore.sort.nameDesc": "Sort by name (Z-A)",
|
||||
"dataStore.search.placeholder": "Search",
|
||||
"dataStore.error.fetching": "Error loading data tables",
|
||||
"dataStore.add.title": "Create data table",
|
||||
"dataStore.add.title": "Create new data table",
|
||||
"dataStore.add.description": "Set up a new data table to organize and manage your data.",
|
||||
"dataStore.add.button.label": "Create data table",
|
||||
"dataStore.add.button.label": "Create Data Table",
|
||||
"dataStore.add.input.name.label": "Data Table Name",
|
||||
"dataStore.add.input.name.placeholder": "Enter data table name",
|
||||
"dataStore.add.error": "Error creating data table",
|
||||
|
||||
@@ -44,10 +44,27 @@ const projectTabsSpy = vi.fn().mockReturnValue({
|
||||
render: vi.fn(),
|
||||
});
|
||||
|
||||
const ProjectCreateResourceStub = {
|
||||
props: {
|
||||
actions: Array,
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<div data-test-id="add-resource"><button role="button"></button></div>
|
||||
<button data-test-id="add-resource-workflow" @click="$emit('action', 'workflow')">Workflow</button>
|
||||
<button data-test-id="action-credential" @click="$emit('action', 'credential')">Credentials</button>
|
||||
<div data-test-id="add-resource-actions" >
|
||||
<button v-for="action in $props.actions" :key="action.value"></button>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
|
||||
const renderComponent = createComponentRenderer(ProjectHeader, {
|
||||
global: {
|
||||
stubs: {
|
||||
ProjectTabs: projectTabsSpy,
|
||||
ProjectCreateResource: ProjectCreateResourceStub,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -69,6 +86,7 @@ describe('ProjectHeader', () => {
|
||||
|
||||
projectsStore.teamProjectsLimit = -1;
|
||||
settingsStore.settings.folders = { enabled: false };
|
||||
settingsStore.isDataStoreFeatureEnabled = true;
|
||||
|
||||
// Setup default moduleTabs structure
|
||||
uiStore.moduleTabs = {
|
||||
@@ -436,4 +454,21 @@ describe('ProjectHeader', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ProjectCreateResource', () => {
|
||||
it('should render menu items', () => {
|
||||
const { getByTestId } = renderComponent();
|
||||
const actionsContainer = getByTestId('add-resource-actions');
|
||||
expect(actionsContainer).toBeInTheDocument();
|
||||
expect(actionsContainer.children).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('should not render datastore menu item if data store feature is disabled', () => {
|
||||
settingsStore.isDataStoreFeatureEnabled = false;
|
||||
const { getByTestId } = renderComponent();
|
||||
const actionsContainer = getByTestId('add-resource-actions');
|
||||
expect(actionsContainer).toBeInTheDocument();
|
||||
expect(actionsContainer.children).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,12 +20,7 @@ import { type IconName } from '@n8n/design-system/components/N8nIcon/icons';
|
||||
import type { IUser } from 'n8n-workflow';
|
||||
import { type IconOrEmoji, isIconOrEmoji } from '@n8n/design-system/components/N8nIconPicker/types';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
|
||||
export type CustomAction = {
|
||||
id: string;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
import { PROJECT_DATA_STORES } from '@/features/dataStore/constants';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -37,17 +32,8 @@ const uiStore = useUIStore();
|
||||
|
||||
const projectPages = useProjectPages();
|
||||
|
||||
type Props = {
|
||||
customActions?: CustomAction[];
|
||||
};
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
customActions: () => [],
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
createFolder: [];
|
||||
customActionSelected: [actionId: string, projectId: string];
|
||||
}>();
|
||||
|
||||
const headerIcon = computed((): IconOrEmoji => {
|
||||
@@ -122,6 +108,7 @@ const ACTION_TYPES = {
|
||||
WORKFLOW: 'workflow',
|
||||
CREDENTIAL: 'credential',
|
||||
FOLDER: 'folder',
|
||||
DATA_STORE: 'dataStore',
|
||||
} as const;
|
||||
type ActionTypes = (typeof ACTION_TYPES)[keyof typeof ACTION_TYPES];
|
||||
|
||||
@@ -155,16 +142,17 @@ const menu = computed(() => {
|
||||
});
|
||||
}
|
||||
|
||||
// Append custom actions
|
||||
if (props.customActions?.length) {
|
||||
props.customActions.forEach((customAction) => {
|
||||
items.push({
|
||||
value: customAction.id,
|
||||
label: customAction.label,
|
||||
disabled: customAction.disabled ?? false,
|
||||
});
|
||||
if (settingsStore.isDataStoreFeatureEnabled) {
|
||||
// TODO: this should probably be moved to the module descriptor as a setting
|
||||
items.push({
|
||||
value: ACTION_TYPES.DATA_STORE,
|
||||
label: i18n.baseText('dataStore.add.button.label'),
|
||||
disabled:
|
||||
sourceControlStore.preferences.branchReadOnly ||
|
||||
!getResourcePermissions(homeProject.value?.scopes)?.dataStore?.create,
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
});
|
||||
|
||||
@@ -196,6 +184,12 @@ const actions: Record<ActionTypes, (projectId: string) => void> = {
|
||||
[ACTION_TYPES.FOLDER]: () => {
|
||||
emit('createFolder');
|
||||
},
|
||||
[ACTION_TYPES.DATA_STORE]: (projectId: string) => {
|
||||
void router.push({
|
||||
name: PROJECT_DATA_STORES,
|
||||
params: { projectId, new: 'new' },
|
||||
});
|
||||
},
|
||||
} as const;
|
||||
|
||||
const pageType = computed(() => {
|
||||
@@ -267,12 +261,6 @@ const onSelect = (action: string) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a custom action
|
||||
if (!executableAction) {
|
||||
emit('customActionSelected', action, homeProject.value.id);
|
||||
return;
|
||||
}
|
||||
|
||||
executableAction(homeProject.value.id);
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import ProjectHeader, { type CustomAction } from '@/components/Projects/ProjectHeader.vue';
|
||||
import ResourcesListLayout from '@/components/layouts/ResourcesListLayout.vue';
|
||||
import ProjectHeader from '@/components/Projects/ProjectHeader.vue';
|
||||
import InsightsSummary from '@/features/insights/components/InsightsSummary.vue';
|
||||
import { useProjectPages } from '@/composables/useProjectPages';
|
||||
import { useInsightsStore } from '@/features/insights/insights.store';
|
||||
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { ProjectTypes } from '@/types/projects.types';
|
||||
import { useProjectsStore } from '@/stores/projects.store';
|
||||
import type { SortingAndPaginationUpdates } from '@/Interface';
|
||||
@@ -17,6 +16,7 @@ import { useSourceControlStore } from '@/stores/sourceControl.store';
|
||||
import {
|
||||
ADD_DATA_STORE_MODAL_KEY,
|
||||
DEFAULT_DATA_STORE_PAGE_SIZE,
|
||||
PROJECT_DATA_STORES,
|
||||
} from '@/features/dataStore/constants';
|
||||
import { useDebounce } from '@/composables/useDebounce';
|
||||
import { useDocumentTitle } from '@/composables/useDocumentTitle';
|
||||
@@ -26,6 +26,7 @@ import { useDataStoreStore } from '@/features/dataStore/dataStore.store';
|
||||
|
||||
const i18n = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const projectPages = useProjectPages();
|
||||
const { callDebounced } = useDebounce();
|
||||
const documentTitle = useDocumentTitle();
|
||||
@@ -35,20 +36,13 @@ const dataStoreStore = useDataStoreStore();
|
||||
const insightsStore = useInsightsStore();
|
||||
const projectsStore = useProjectsStore();
|
||||
const sourceControlStore = useSourceControlStore();
|
||||
const uiStore = useUIStore();
|
||||
|
||||
const loading = ref(true);
|
||||
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(DEFAULT_DATA_STORE_PAGE_SIZE);
|
||||
|
||||
const customProjectActions = computed<CustomAction[]>(() => [
|
||||
{
|
||||
id: 'add-data-store',
|
||||
label: i18n.baseText('dataStore.add.button.label'),
|
||||
disabled: loading.value || projectPages.isOverviewSubPage,
|
||||
},
|
||||
]);
|
||||
|
||||
const dataStoreResources = computed<DataStoreResource[]>(() =>
|
||||
dataStoreStore.dataStores.map((ds) => {
|
||||
return {
|
||||
@@ -58,6 +52,10 @@ const dataStoreResources = computed<DataStoreResource[]>(() =>
|
||||
}),
|
||||
);
|
||||
|
||||
const projectId = computed(() => {
|
||||
return Array.isArray(route.params.projectId) ? route.params.projectId[0] : route.params.projectId;
|
||||
});
|
||||
|
||||
const totalCount = computed(() => dataStoreStore.totalCount);
|
||||
|
||||
const currentProject = computed(() => projectsStore.currentProject);
|
||||
@@ -86,11 +84,8 @@ const readOnlyEnv = computed(() => sourceControlStore.preferences.branchReadOnly
|
||||
|
||||
const initialize = async () => {
|
||||
loading.value = true;
|
||||
const projectId = Array.isArray(route.params.projectId)
|
||||
? route.params.projectId[0]
|
||||
: route.params.projectId;
|
||||
try {
|
||||
await dataStoreStore.fetchDataStores(projectId, currentPage.value, pageSize.value);
|
||||
await dataStoreStore.fetchDataStores(projectId.value, currentPage.value, pageSize.value);
|
||||
} catch (error) {
|
||||
toast.showError(error, 'Error loading data stores');
|
||||
} finally {
|
||||
@@ -111,18 +106,27 @@ const onPaginationUpdate = async (payload: SortingAndPaginationUpdates) => {
|
||||
};
|
||||
|
||||
const onAddModalClick = () => {
|
||||
useUIStore().openModal(ADD_DATA_STORE_MODAL_KEY);
|
||||
};
|
||||
|
||||
const onProjectHeaderAction = (action: string) => {
|
||||
if (action === 'add-data-store') {
|
||||
useUIStore().openModal(ADD_DATA_STORE_MODAL_KEY);
|
||||
}
|
||||
void router.push({
|
||||
name: PROJECT_DATA_STORES,
|
||||
params: { projectId: projectId.value, new: 'new' },
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
documentTitle.set(i18n.baseText('dataStore.dataStores'));
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.params.new,
|
||||
() => {
|
||||
if (route.params.new === 'new') {
|
||||
uiStore.openModal(ADD_DATA_STORE_MODAL_KEY);
|
||||
} else {
|
||||
uiStore.closeModal(ADD_DATA_STORE_MODAL_KEY);
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<ResourcesListLayout
|
||||
@@ -144,10 +148,7 @@ onMounted(() => {
|
||||
@update:pagination-and-sort="onPaginationUpdate"
|
||||
>
|
||||
<template #header>
|
||||
<ProjectHeader
|
||||
:custom-actions="customProjectActions"
|
||||
@custom-action-selected="onProjectHeaderAction"
|
||||
>
|
||||
<ProjectHeader>
|
||||
<InsightsSummary
|
||||
v-if="projectPages.isOverviewSubPage && insightsStore.isSummaryEnabled"
|
||||
:loading="insightsStore.weeklySummary.isLoading"
|
||||
|
||||
@@ -4,7 +4,8 @@ import { onMounted, ref } from 'vue';
|
||||
import { useDataStoreStore } from '@/features/dataStore/dataStore.store';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { DATA_STORE_DETAILS, PROJECT_DATA_STORES } from '@/features/dataStore/constants';
|
||||
|
||||
type Props = {
|
||||
modalName: string;
|
||||
@@ -16,6 +17,7 @@ const dataStoreStore = useDataStoreStore();
|
||||
const uiStore = useUIStore();
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const i18n = useI18n();
|
||||
const toast = useToast();
|
||||
|
||||
@@ -31,18 +33,35 @@ onMounted(() => {
|
||||
|
||||
const onSubmit = async () => {
|
||||
try {
|
||||
await dataStoreStore.createDataStore(dataStoreName.value, route.params.projectId as string);
|
||||
} catch (error) {
|
||||
toast.showError(error, i18n.baseText('dataStore.add.error'));
|
||||
} finally {
|
||||
const newDataStore = await dataStoreStore.createDataStore(
|
||||
dataStoreName.value,
|
||||
route.params.projectId as string,
|
||||
);
|
||||
void router.push({
|
||||
name: DATA_STORE_DETAILS,
|
||||
params: {
|
||||
id: newDataStore.id,
|
||||
},
|
||||
});
|
||||
dataStoreName.value = '';
|
||||
uiStore.closeModal(props.modalName);
|
||||
} catch (error) {
|
||||
toast.showError(error, i18n.baseText('dataStore.add.error'));
|
||||
}
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
uiStore.closeModal(props.modalName);
|
||||
redirectToDataStores();
|
||||
};
|
||||
|
||||
const redirectToDataStores = () => {
|
||||
void router.replace({ name: PROJECT_DATA_STORES });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :name="props.modalName" :center="true" width="540px">
|
||||
<Modal :name="props.modalName" :center="true" width="540px" :before-close="redirectToDataStores">
|
||||
<template #header>
|
||||
<h2>{{ i18n.baseText('dataStore.add.title') }}</h2>
|
||||
</template>
|
||||
@@ -66,7 +85,7 @@ const onSubmit = async () => {
|
||||
</n8n-input-label>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer="{ close }">
|
||||
<template #footer>
|
||||
<div :class="$style.footer">
|
||||
<n8n-button
|
||||
:disabled="!dataStoreName"
|
||||
@@ -78,7 +97,7 @@ const onSubmit = async () => {
|
||||
type="secondary"
|
||||
:label="i18n.baseText('generic.cancel')"
|
||||
data-test-id="cancel-add-data-store-button"
|
||||
@click="close"
|
||||
@click="onCancel"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -96,7 +96,7 @@ describe('DataStoreBreadcrumbs', () => {
|
||||
});
|
||||
|
||||
expect(getByText('Data Stores')).toBeInTheDocument();
|
||||
const separators = getAllByText('›');
|
||||
const separators = getAllByText('/');
|
||||
expect(separators.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
@@ -224,7 +224,7 @@ describe('DataStoreBreadcrumbs', () => {
|
||||
}),
|
||||
});
|
||||
|
||||
const separators = getAllByText('›');
|
||||
const separators = getAllByText('/');
|
||||
expect(separators.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ import { PROJECT_DATA_STORES } from '@/features/dataStore/constants';
|
||||
import { useDataStoreStore } from '@/features/dataStore/dataStore.store';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
|
||||
const BREADCRUMBS_SEPARATOR = '›';
|
||||
const BREADCRUMBS_SEPARATOR = '/';
|
||||
|
||||
type Props = {
|
||||
dataStore: DataStore;
|
||||
|
||||
@@ -82,7 +82,7 @@ const emit = defineEmits<{
|
||||
const i18n = useI18n();
|
||||
const toast = useToast();
|
||||
const message = useMessage();
|
||||
const dataStoreTypes = useDataStoreTypes();
|
||||
const { getDefaultValueForType, mapToAGCellType } = useDataStoreTypes();
|
||||
|
||||
const dataStoreStore = useDataStoreStore();
|
||||
|
||||
@@ -142,22 +142,6 @@ const setPageSize = async (size: number) => {
|
||||
await fetchDataStoreContent();
|
||||
};
|
||||
|
||||
const onAddColumn = async ({ column }: { column: DataStoreColumnCreatePayload }) => {
|
||||
try {
|
||||
const newColumn = await dataStoreStore.addDataStoreColumn(
|
||||
props.dataStore.id,
|
||||
props.dataStore.projectId,
|
||||
column,
|
||||
);
|
||||
if (!newColumn) {
|
||||
throw new Error(i18n.baseText('generic.unknownError'));
|
||||
}
|
||||
colDefs.value = [...colDefs.value, createColumnDef(newColumn)];
|
||||
} catch (error) {
|
||||
toast.showError(error, i18n.baseText('dataStore.addColumn.error'));
|
||||
}
|
||||
};
|
||||
|
||||
const onDeleteColumn = async (columnId: string) => {
|
||||
if (!gridApi.value) return;
|
||||
|
||||
@@ -183,7 +167,7 @@ const onDeleteColumn = async (columnId: string) => {
|
||||
colDefs.value = colDefs.value.filter((def) => def.colId !== columnId);
|
||||
const rowDataOldValue = [...rowData.value];
|
||||
rowData.value = rowData.value.map((row) => {
|
||||
const { [columnToDelete.field ?? '']: _, ...rest } = row;
|
||||
const { [columnToDelete.field!]: _, ...rest } = row;
|
||||
return rest;
|
||||
});
|
||||
refreshGridData();
|
||||
@@ -201,7 +185,26 @@ const onDeleteColumn = async (columnId: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Split this up to create column def based on type
|
||||
const onAddColumn = async ({ column }: { column: DataStoreColumnCreatePayload }) => {
|
||||
try {
|
||||
const newColumn = await dataStoreStore.addDataStoreColumn(
|
||||
props.dataStore.id,
|
||||
props.dataStore.projectId,
|
||||
column,
|
||||
);
|
||||
if (!newColumn) {
|
||||
throw new Error(i18n.baseText('generic.unknownError'));
|
||||
}
|
||||
colDefs.value = [...colDefs.value, createColumnDef(newColumn)];
|
||||
rowData.value = rowData.value.map((row) => {
|
||||
return { ...row, [newColumn.name]: getDefaultValueForType(newColumn.type) };
|
||||
});
|
||||
refreshGridData();
|
||||
} catch (error) {
|
||||
toast.showError(error, i18n.baseText('dataStore.addColumn.error'));
|
||||
}
|
||||
};
|
||||
|
||||
const createColumnDef = (col: DataStoreColumn, extraProps: Partial<ColDef> = {}) => {
|
||||
const columnDef: ColDef = {
|
||||
colId: col.id,
|
||||
@@ -209,11 +212,11 @@ const createColumnDef = (col: DataStoreColumn, extraProps: Partial<ColDef> = {})
|
||||
headerName: col.name,
|
||||
editable: true,
|
||||
resizable: true,
|
||||
lockPinned: true,
|
||||
headerComponent: ColumnHeader,
|
||||
cellEditorPopup: false,
|
||||
headerComponentParams: { onDelete: onDeleteColumn },
|
||||
...extraProps,
|
||||
cellDataType: dataStoreTypes.mapToAGCellType(col.type),
|
||||
cellDataType: mapToAGCellType(col.type),
|
||||
valueGetter: (params: ValueGetterParams<DataStoreRow>) => {
|
||||
// If the value is null, return null to show empty cell
|
||||
if (params.data?.[col.name] === null || params.data?.[col.name] === undefined) {
|
||||
@@ -284,7 +287,10 @@ const createColumnDef = (col: DataStoreColumn, extraProps: Partial<ColDef> = {})
|
||||
columnDef.cellEditor = 'agDateCellEditor';
|
||||
columnDef.cellEditorPopup = true;
|
||||
}
|
||||
return columnDef;
|
||||
return {
|
||||
...columnDef,
|
||||
...extraProps,
|
||||
};
|
||||
};
|
||||
|
||||
const onColumnMoved = async (moveEvent: ColumnMovedEvent) => {
|
||||
@@ -555,17 +561,19 @@ const onCellEditingStopped = (params: CellEditingStoppedEvent<DataStoreRow>) =>
|
||||
--ag-font-family: var(--font-family);
|
||||
--ag-font-size: var(--font-size-xs);
|
||||
--ag-row-height: calc(var(--ag-grid-size) * 0.8 + 32px);
|
||||
--ag-header-background-color: var(--color-background-base);
|
||||
--ag-header-background-color: var(--color-background-light-base);
|
||||
--ag-header-font-size: var(--font-size-xs);
|
||||
--ag-header-font-weight: var(--font-weight-bold);
|
||||
--ag-header-foreground-color: var(--color-text-dark);
|
||||
--ag-cell-horizontal-padding: var(--spacing-2xs);
|
||||
--ag-header-column-resize-handle-color: var(--border-color-base);
|
||||
--ag-header-column-resize-handle-color: var(--color-foreground-base);
|
||||
--ag-header-column-resize-handle-height: 100%;
|
||||
--ag-header-height: calc(var(--ag-grid-size) * 0.8 + 32px);
|
||||
|
||||
:global(.ag-header-cell-resize) {
|
||||
width: var(--spacing-4xs);
|
||||
width: var(--spacing-xs);
|
||||
// this is needed so that we compensate for the width
|
||||
right: -7px;
|
||||
}
|
||||
|
||||
// Don't show borders for the checkbox cells
|
||||
|
||||
@@ -40,7 +40,7 @@ export const DataStoreModule: FrontendModuleDescription = {
|
||||
},
|
||||
{
|
||||
name: PROJECT_DATA_STORES,
|
||||
path: 'datatables',
|
||||
path: 'datatables/:new(new)?',
|
||||
props: true,
|
||||
components: {
|
||||
default: DataStoreView,
|
||||
|
||||
@@ -98,7 +98,7 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||
const activeModules = computed(() => settings.value.activeModules);
|
||||
|
||||
const isModuleActive = (moduleName: string) => {
|
||||
return activeModules.value.includes(moduleName);
|
||||
return activeModules.value?.includes(moduleName);
|
||||
};
|
||||
|
||||
const partialExecutionVersion = computed<1 | 2>(() => {
|
||||
@@ -144,6 +144,8 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||
|
||||
const isFoldersFeatureEnabled = computed(() => folders.value.enabled);
|
||||
|
||||
const isDataStoreFeatureEnabled = computed(() => isModuleActive('data-store'));
|
||||
|
||||
const areTagsEnabled = computed(() =>
|
||||
settings.value.workflowTagsDisabled !== undefined ? !settings.value.workflowTagsDisabled : true,
|
||||
);
|
||||
@@ -396,5 +398,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||
isMFAEnforced,
|
||||
activeModules,
|
||||
isModuleActive,
|
||||
isDataStoreFeatureEnabled,
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user