diff --git a/packages/frontend/@n8n/design-system/src/components/N8nActionDropdown/ActionDropdown.vue b/packages/frontend/@n8n/design-system/src/components/N8nActionDropdown/ActionDropdown.vue
index 3bb4f87dbf..1818ecb1b4 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nActionDropdown/ActionDropdown.vue
+++ b/packages/frontend/@n8n/design-system/src/components/N8nActionDropdown/ActionDropdown.vue
@@ -199,6 +199,7 @@ defineExpose({ open, close });
}
.icon {
+ display: flex;
text-align: center;
margin-right: var(--spacing-2xs);
diff --git a/packages/frontend/@n8n/design-system/src/css/_tokens.dark.scss b/packages/frontend/@n8n/design-system/src/css/_tokens.dark.scss
index 2aaed2df0c..6d1be46ef1 100644
--- a/packages/frontend/@n8n/design-system/src/css/_tokens.dark.scss
+++ b/packages/frontend/@n8n/design-system/src/css/_tokens.dark.scss
@@ -520,6 +520,9 @@
--color-menu-background: var(--p-gray-740);
--color-menu-hover-background: var(--p-gray-670);
--color-menu-active-background: var(--p-gray-670);
+
+ /* Ag Grid */
+ --grid-row-selected-background: var(--p-color-secondary-720);
}
body[data-theme='dark'] {
diff --git a/packages/frontend/@n8n/design-system/src/css/_tokens.scss b/packages/frontend/@n8n/design-system/src/css/_tokens.scss
index 4b6dd7502d..3d323fcfdc 100644
--- a/packages/frontend/@n8n/design-system/src/css/_tokens.scss
+++ b/packages/frontend/@n8n/design-system/src/css/_tokens.scss
@@ -686,6 +686,9 @@
// Params
--color-icon-base: var(--color-text-light);
--color-icon-hover: var(--p-color-primary-320);
+
+ /* Ag Grid */
+ --grid-row-selected-background: var(--p-color-secondary-070);
}
:root {
diff --git a/packages/frontend/@n8n/i18n/src/locales/en.json b/packages/frontend/@n8n/i18n/src/locales/en.json
index b1dab74f56..124ddf6bfa 100644
--- a/packages/frontend/@n8n/i18n/src/locales/en.json
+++ b/packages/frontend/@n8n/i18n/src/locales/en.json
@@ -2834,7 +2834,7 @@
"contextual.users.settings.unavailable.button.cloud": "Upgrade now",
"contextual.feature.unavailable.title": "Available on the Enterprise Plan",
"contextual.feature.unavailable.title.cloud": "Available on the Pro Plan",
- "dataStore.dataStores": "Data Tables",
+ "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}\"",
@@ -3004,8 +3004,10 @@
"settings.mfa.updateConfiguration": "MFA configuration updated",
"settings.mfa.invalidAuthenticatorCode": "Invalid authenticator code",
"projects.header.overview.subtitle": "All the workflows, credentials and executions you have access to",
+ "projects.header.overview.subtitleWithDataTables": "All the workflows, credentials and data tables you have access to",
"projects.header.shared.title": "Shared with you",
"projects.header.personal.subtitle": "Workflows and credentials owned by you",
+ "projects.header.personal.subtitleWithDataTables": "Workflows, credentials and data tables owned by you",
"projects.header.shared.subtitle": "Workflows and credentials other users have shared with you",
"projects.header.create.workflow": "Create Workflow",
"projects.header.create.credential": "Create Credential",
diff --git a/packages/frontend/editor-ui/src/components/Projects/ProjectHeader.test.ts b/packages/frontend/editor-ui/src/components/Projects/ProjectHeader.test.ts
index f5bea57e2f..16044d31af 100644
--- a/packages/frontend/editor-ui/src/components/Projects/ProjectHeader.test.ts
+++ b/packages/frontend/editor-ui/src/components/Projects/ProjectHeader.test.ts
@@ -123,6 +123,7 @@ describe('ProjectHeader', () => {
});
it('Overview: should render the correct title and subtitle', async () => {
+ settingsStore.isDataStoreFeatureEnabled = false;
vi.spyOn(projectPages, 'isOverviewSubPage', 'get').mockReturnValue(true);
const { getByTestId, rerender } = renderComponent();
const overviewSubtitle = 'All the workflows, credentials and executions you have access to';
@@ -146,6 +147,7 @@ describe('ProjectHeader', () => {
});
it('Personal: should render the correct title and subtitle', async () => {
+ settingsStore.isDataStoreFeatureEnabled = false;
vi.spyOn(projectPages, 'isOverviewSubPage', 'get').mockReturnValue(false);
vi.spyOn(projectPages, 'isSharedSubPage', 'get').mockReturnValue(false);
const { getByTestId, rerender } = renderComponent();
diff --git a/packages/frontend/editor-ui/src/components/Projects/ProjectHeader.vue b/packages/frontend/editor-ui/src/components/Projects/ProjectHeader.vue
index 13fa619b4e..f32c94d499 100644
--- a/packages/frontend/editor-ui/src/components/Projects/ProjectHeader.vue
+++ b/packages/frontend/editor-ui/src/components/Projects/ProjectHeader.vue
@@ -241,9 +241,17 @@ const sectionDescription = computed(() => {
if (projectPages.isSharedSubPage) {
return i18n.baseText('projects.header.shared.subtitle');
} else if (projectPages.isOverviewSubPage) {
- return i18n.baseText('projects.header.overview.subtitle');
+ return i18n.baseText(
+ settingsStore.isDataStoreFeatureEnabled
+ ? 'projects.header.overview.subtitleWithDataTables'
+ : 'projects.header.overview.subtitle',
+ );
} else if (isPersonalProject.value) {
- return i18n.baseText('projects.header.personal.subtitle');
+ return i18n.baseText(
+ settingsStore.isDataStoreFeatureEnabled
+ ? 'projects.header.personal.subtitleWithDataTables'
+ : 'projects.header.personal.subtitle',
+ );
}
return null;
diff --git a/packages/frontend/editor-ui/src/features/dataStore/DataStoreDetailsView.vue b/packages/frontend/editor-ui/src/features/dataStore/DataStoreDetailsView.vue
index 530e8ce7df..4c2140fe3e 100644
--- a/packages/frontend/editor-ui/src/features/dataStore/DataStoreDetailsView.vue
+++ b/packages/frontend/editor-ui/src/features/dataStore/DataStoreDetailsView.vue
@@ -1,6 +1,6 @@
+
+
+
+
+
+
diff --git a/packages/frontend/editor-ui/src/features/dataStore/components/dataGrid/DataStoreTable.test.ts b/packages/frontend/editor-ui/src/features/dataStore/components/dataGrid/DataStoreTable.test.ts
index a94299ab44..90456c6df7 100644
--- a/packages/frontend/editor-ui/src/features/dataStore/components/dataGrid/DataStoreTable.test.ts
+++ b/packages/frontend/editor-ui/src/features/dataStore/components/dataGrid/DataStoreTable.test.ts
@@ -1,6 +1,5 @@
import { createComponentRenderer } from '@/__tests__/render';
import DataStoreTable from '@/features/dataStore/components/dataGrid/DataStoreTable.vue';
-import { fireEvent, waitFor } from '@testing-library/vue';
import { createPinia, setActivePinia } from 'pinia';
import { useDataStoreStore } from '@/features/dataStore/dataStore.store';
import type { DataStore } from '@/features/dataStore/datastore.types';
@@ -44,6 +43,7 @@ vi.mock('ag-grid-community', () => ({
ClientSideRowModelApiModule: {},
ValidationModule: {},
UndoRedoEditModule: {},
+ CellStyleModule: {},
}));
// Mock the n8n theme
@@ -137,47 +137,16 @@ describe('DataStoreTable', () => {
});
describe('Component Initialization', () => {
- it('should render the component with AG Grid and AddColumnPopover', () => {
+ it('should render the component with AG Grid', () => {
const { getByTestId } = renderComponent();
expect(getByTestId('ag-grid-vue')).toBeInTheDocument();
- expect(getByTestId('add-column-popover')).toBeInTheDocument();
});
it('should render pagination controls', () => {
const { getByTestId } = renderComponent();
-
expect(getByTestId('data-store-content-pagination')).toBeInTheDocument();
});
-
- it('should render add row button', () => {
- const { getByTestId } = renderComponent();
-
- expect(getByTestId('data-store-add-row-button')).toBeInTheDocument();
- });
- });
-
- describe('Add Column Functionality', () => {
- it('should handle add column event from AddColumnPopover', async () => {
- const { getByTestId } = renderComponent();
-
- const addColumnPopover = getByTestId('add-column-popover');
- const addButton = addColumnPopover.querySelector(
- '[data-test-id="data-store-add-column-button"]',
- );
-
- expect(addButton).toBeInTheDocument();
-
- await fireEvent.click(addButton!);
-
- await waitFor(() => {
- expect(dataStoreStore.addDataStoreColumn).toHaveBeenCalledWith(
- mockDataStore.id,
- mockDataStore.projectId,
- { name: 'newColumn', type: 'string' },
- );
- });
- });
});
describe('Empty Data Store', () => {
diff --git a/packages/frontend/editor-ui/src/features/dataStore/components/dataGrid/DataStoreTable.vue b/packages/frontend/editor-ui/src/features/dataStore/components/dataGrid/DataStoreTable.vue
index 05ba1cc611..6245d1b997 100644
--- a/packages/frontend/editor-ui/src/features/dataStore/components/dataGrid/DataStoreTable.vue
+++ b/packages/frontend/editor-ui/src/features/dataStore/components/dataGrid/DataStoreTable.vue
@@ -38,18 +38,28 @@ import {
ClientSideRowModelApiModule,
ValidationModule,
UndoRedoEditModule,
+ CellStyleModule,
} from 'ag-grid-community';
import { n8nTheme } from '@/features/dataStore/components/dataGrid/n8nTheme';
-import AddColumnPopover from '@/features/dataStore/components/dataGrid/AddColumnPopover.vue';
import SelectedItemsInfo from '@/components/common/SelectedItemsInfo.vue';
import { useDataStoreStore } from '@/features/dataStore/dataStore.store';
import { useI18n } from '@n8n/i18n';
import { useToast } from '@/composables/useToast';
-import { DEFAULT_ID_COLUMN_NAME, EMPTY_VALUE, NULL_VALUE } from '@/features/dataStore/constants';
+import {
+ DEFAULT_ID_COLUMN_NAME,
+ EMPTY_VALUE,
+ NULL_VALUE,
+ DATA_STORE_ID_COLUMN_WIDTH,
+ DATA_STORE_HEADER_HEIGHT,
+ DATA_STORE_ROW_HEIGHT,
+ ADD_ROW_ROW_ID,
+} from '@/features/dataStore/constants';
import { useMessage } from '@/composables/useMessage';
import { MODAL_CONFIRM } from '@/constants';
import ColumnHeader from '@/features/dataStore/components/dataGrid/ColumnHeader.vue';
import { useDataStoreTypes } from '@/features/dataStore/composables/useDataStoreTypes';
+import AddColumnButton from '@/features/dataStore/components/dataGrid/AddColumnButton.vue';
+import AddRowButton from '@/features/dataStore/components/dataGrid/AddRowButton.vue';
import { isDataStoreValue } from '@/features/dataStore/typeGuards';
import NullEmptyCellRenderer from '@/features/dataStore/components/dataGrid/NullEmptyCellRenderer.vue';
import { onClickOutside } from '@vueuse/core';
@@ -68,6 +78,7 @@ ModuleRegistry.registerModules([
DateEditorModule,
ClientSideRowModelApiModule,
UndoRedoEditModule,
+ CellStyleModule,
]);
type Props = {
@@ -83,7 +94,7 @@ const emit = defineEmits<{
const i18n = useI18n();
const toast = useToast();
const message = useMessage();
-const { getDefaultValueForType, mapToAGCellType } = useDataStoreTypes();
+const { mapToAGCellType } = useDataStoreTypes();
const dataStoreStore = useDataStoreStore();
@@ -94,7 +105,8 @@ const rowData = ref([]);
const rowSelection: RowSelectionOptions | 'single' | 'multiple' = {
mode: 'multiRow',
enableClickSelection: false,
- checkboxes: true,
+ checkboxes: (params) => params.data?.id !== ADD_ROW_ROW_ID,
+ isRowSelectable: (params) => params.data?.id !== ADD_ROW_ROW_ID,
};
const contentLoading = ref(false);
@@ -106,13 +118,6 @@ const isTextEditorOpen = ref(false);
const gridContainer = useTemplateRef('gridContainer');
-// Shared config for all columns
-const defaultColumnDef: ColDef = {
- flex: 1,
- sortable: false,
- filter: false,
-};
-
// Pagination
const pageSizeOptions = [10, 20, 50];
const currentPage = ref(1);
@@ -132,7 +137,12 @@ const onGridReady = (params: GridReadyEvent) => {
const refreshGridData = () => {
if (gridApi.value) {
gridApi.value.setGridOption('columnDefs', colDefs.value);
- gridApi.value.setGridOption('rowData', rowData.value);
+ gridApi.value.setGridOption('rowData', [
+ ...rowData.value,
+ {
+ id: ADD_ROW_ROW_ID,
+ },
+ ]);
}
};
@@ -189,7 +199,7 @@ const onDeleteColumn = async (columnId: string) => {
}
};
-const onAddColumn = async ({ column }: { column: DataStoreColumnCreatePayload }) => {
+const onAddColumn = async (column: DataStoreColumnCreatePayload) => {
try {
const newColumn = await dataStoreStore.addDataStoreColumn(
props.dataStore.id,
@@ -199,9 +209,13 @@ const onAddColumn = async ({ column }: { column: DataStoreColumnCreatePayload })
if (!newColumn) {
throw new Error(i18n.baseText('generic.unknownError'));
}
- colDefs.value = [...colDefs.value, createColumnDef(newColumn)];
+ colDefs.value = [
+ ...colDefs.value.slice(0, -1),
+ createColumnDef(newColumn),
+ ...colDefs.value.slice(-1),
+ ];
rowData.value = rowData.value.map((row) => {
- return { ...row, [newColumn.name]: getDefaultValueForType(newColumn.type) };
+ return { ...row, [newColumn.name]: null };
});
refreshGridData();
} catch (error) {
@@ -214,13 +228,24 @@ const createColumnDef = (col: DataStoreColumn, extraProps: Partial = {})
colId: col.id,
field: col.name,
headerName: col.name,
- editable: true,
+ sortable: false,
+ flex: 1,
+ editable: (params) => params.data?.id !== ADD_ROW_ROW_ID,
resizable: true,
lockPinned: true,
headerComponent: ColumnHeader,
cellEditorPopup: false,
headerComponentParams: { onDelete: onDeleteColumn },
cellDataType: mapToAGCellType(col.type),
+ cellClass: (params) => {
+ if (params.data?.id === ADD_ROW_ROW_ID) {
+ return 'add-row-cell';
+ }
+ if (params.column.getUserProvidedColDef()?.cellDataType === 'boolean') {
+ return 'boolean-cell';
+ }
+ return '';
+ },
valueGetter: (params: ValueGetterParams) => {
// If the value is null, return null to show empty cell
if (params.data?.[col.name] === null || params.data?.[col.name] === undefined) {
@@ -236,6 +261,9 @@ const createColumnDef = (col: DataStoreColumn, extraProps: Partial = {})
return params.data?.[col.name];
},
cellRendererSelector: (params: ICellRendererParams) => {
+ if (params.data?.id === ADD_ROW_ROW_ID || col.id === 'add-column') {
+ return {};
+ }
let rowValue = params.data?.[col.name];
// When adding new column, rowValue is undefined (same below, in string cell editor)
if (rowValue === undefined) {
@@ -289,7 +317,7 @@ const createColumnDef = (col: DataStoreColumn, extraProps: Partial = {})
// Setup date editor
if (col.type === 'date') {
columnDef.cellEditor = 'agDateCellEditor';
- columnDef.cellEditorPopup = true;
+ columnDef.cellEditorPopup = false;
}
return {
...columnDef,
@@ -324,8 +352,8 @@ const onColumnMoved = async (moveEvent: ColumnMovedEvent) => {
const onAddRowClick = async () => {
try {
// Go to last page if we are not there already
- if (currentPage.value * pageSize.value < totalItems.value) {
- await setCurrentPage(Math.ceil(totalItems.value / pageSize.value));
+ if (currentPage.value * pageSize.value < totalItems.value + 1) {
+ await setCurrentPage(Math.ceil((totalItems.value + 1) / pageSize.value));
}
contentLoading.value = true;
emit('toggleSave', true);
@@ -335,7 +363,9 @@ const onAddRowClick = async () => {
props.dataStore.columns.forEach((col) => {
newRow[col.name] = null;
});
- rows.value.push(newRow);
+ rowData.value.push(newRow);
+ totalItems.value += 1;
+ refreshGridData();
} catch (error) {
toast.showError(error, i18n.baseText('dataStore.addRow.error'));
} finally {
@@ -360,10 +390,42 @@ const initColumnDefinitions = () => {
suppressMovable: true,
headerComponent: null,
lockPosition: true,
+ minWidth: DATA_STORE_ID_COLUMN_WIDTH,
+ maxWidth: DATA_STORE_ID_COLUMN_WIDTH,
+ resizable: false,
+ cellClass: (params) => (params.data?.id === ADD_ROW_ROW_ID ? 'add-row-cell' : 'id-column'),
+ cellRendererSelector: (params: ICellRendererParams) => {
+ if (params.value === ADD_ROW_ROW_ID) {
+ return {
+ component: AddRowButton,
+ params: {
+ onClick: onAddRowClick,
+ },
+ };
+ }
+ return undefined;
+ },
},
),
// Append other columns
...orderBy(props.dataStore.columns, 'index').map((col) => createColumnDef(col)),
+ createColumnDef(
+ {
+ index: props.dataStore.columns.length + 1,
+ id: 'add-column',
+ name: 'Add Column',
+ type: 'string',
+ },
+ {
+ editable: false,
+ suppressMovable: true,
+ lockPinned: true,
+ lockPosition: 'right',
+ resizable: false,
+ headerComponent: AddColumnButton,
+ headerComponentParams: { onAddColumn },
+ },
+ ),
];
};
@@ -404,8 +466,11 @@ const onCellClicked = (params: CellClickedEvent) => {
const clickedCellColumn = params.column.getColId();
const clickedCellRow = params.rowIndex;
- // Skip if rowIndex is null
- if (clickedCellRow === null) return;
+ if (
+ clickedCellRow === null ||
+ params.api.isEditing({ rowIndex: clickedCellRow, column: params.column, rowPinned: null })
+ )
+ return;
// Check if this is the same cell that was focused before this click
const wasAlreadyFocused =
@@ -437,10 +502,9 @@ const fetchDataStoreContent = async () => {
currentPage.value,
pageSize.value,
);
- rows.value = fetchedRows.data;
+ rowData.value = fetchedRows.data;
totalItems.value = fetchedRows.count;
- rowData.value = rows.value;
-
+ refreshGridData();
handleClearSelection();
} catch (error) {
toast.showError(error, i18n.baseText('dataStore.fetchContent.error'));
@@ -544,6 +608,11 @@ const handleClearSelection = () => {
gridApi.value.deselectAll();
}
};
+
+defineExpose({
+ addRow: onAddRowClick,
+ addColumn: onAddColumn,
+});
@@ -551,12 +620,9 @@ const handleClearSelection = () => {
{
@column-header-clicked="resetLastFocusedCell"
@selection-changed="onSelectionChanged"
/>
-
-
-
-
{
display: flex;
flex-direction: column;
gap: var(--spacing-m);
- width: calc(100% - var(--spacing-m) * 2);
align-items: center;
}
@@ -626,22 +677,35 @@ const handleClearSelection = () => {
// AG Grid style overrides
--ag-foreground-color: var(--color-text-base);
- --ag-accent-color: var(--color-primary);
+ --ag-cell-text-color: var(--color-text-dark);
+ --ag-accent-color: var(--p-color-secondary-470);
+ --ag-row-hover-color: var(--color-background-light-base);
--ag-background-color: var(--color-background-xlight);
--ag-border-color: var(--border-color-base);
--ag-border-radius: var(--border-radius-base);
--ag-wrapper-border-radius: 0;
--ag-font-family: var(--font-family);
--ag-font-size: var(--font-size-xs);
+ --ag-font-color: var(--color-text-base);
--ag-row-height: calc(var(--ag-grid-size) * 0.8 + 32px);
--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-font-weight: var(--font-weight-medium);
--ag-header-foreground-color: var(--color-text-dark);
--ag-cell-horizontal-padding: var(--spacing-2xs);
- --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);
+ --ag-header-column-border-height: 100%;
+ --ag-range-selection-border-color: var(--p-color-secondary-470);
+ --ag-input-padding-start: var(--spacing-2xs);
+ --ag-input-background-color: var(--color-text-xlight);
+ --ag-focus-shadow: none;
+
+ --cell-editing-border: 2px solid var(--color-secondary);
+
+ :global(.ag-cell) {
+ display: flex;
+ align-items: center;
+ }
:global(.ag-header-cell-resize) {
width: var(--spacing-xs);
@@ -649,26 +713,101 @@ const handleClearSelection = () => {
right: -7px;
}
- // Don't show borders for the checkbox cells
:global(.ag-cell[col-id='ag-Grid-SelectionColumn']) {
border: none;
+ padding-left: var(--spacing-l);
+ }
+
+ :global(.ag-header-cell[col-id='ag-Grid-SelectionColumn']) {
+ padding-left: var(--spacing-l);
+ &:after {
+ display: none;
+ }
}
:global(.ag-cell[col-id='ag-Grid-SelectionColumn'].ag-cell-focus) {
outline: none;
}
-}
-.add-column-popover {
- display: flex;
- position: absolute;
- right: -47px;
+ :global(.ag-root-wrapper) {
+ border-left: none;
+ }
+
+ :global(.id-column) {
+ color: var(--color-text-light);
+ justify-content: center;
+ }
+
+ :global(.ag-header-cell[col-id='id']) {
+ text-align: center;
+ }
+
+ :global(.add-row-cell) {
+ border: none !important;
+ background-color: transparent !important;
+ }
+
+ :global(.ag-header-cell[col-id='add-column']) {
+ &:after {
+ display: none;
+ }
+ }
+
+ :global(.ag-cell-value[col-id='add-column']),
+ :global(.ag-cell-value[col-id='id']),
+ :global(.ag-cell[col-id='ag-Grid-SelectionColumn']) {
+ border: none;
+ background-color: transparent;
+ }
+
+ :global(.ag-cell-value[col-id='id']) {
+ border-right: 1px solid var(--ag-border-color);
+ }
+
+ :global(.ag-large-text-input) {
+ position: fixed;
+ padding: 0;
+
+ textarea {
+ padding-top: var(--spacing-xs);
+
+ &:where(:focus-within, :active) {
+ border: var(--cell-editing-border);
+ }
+ }
+ }
+
+ :global(.ag-center-cols-viewport) {
+ min-height: auto;
+ }
+
+ :global(.ag-checkbox-input-wrapper) {
+ &:where(:focus-within, :active) {
+ box-shadow: none;
+ }
+ }
+
+ :global(.ag-cell-inline-editing) {
+ box-shadow: none;
+
+ &:global(.boolean-cell) {
+ border: var(--cell-editing-border) !important;
+
+ &:global(.ag-cell-focus) {
+ background-color: var(--grid-cell-active-background);
+ }
+ }
+ }
+
+ :global(.ag-cell-focus) {
+ background-color: var(--grid-row-selected-background);
+ }
}
.footer {
display: flex;
width: 100%;
- justify-content: space-between;
+ justify-content: flex-end;
margin-bottom: var(--spacing-l);
padding-right: var(--spacing-xl);
diff --git a/packages/frontend/editor-ui/src/features/dataStore/components/dataGrid/NullEmptyCellRenderer.vue b/packages/frontend/editor-ui/src/features/dataStore/components/dataGrid/NullEmptyCellRenderer.vue
index 4253acf86a..31ad699087 100644
--- a/packages/frontend/editor-ui/src/features/dataStore/components/dataGrid/NullEmptyCellRenderer.vue
+++ b/packages/frontend/editor-ui/src/features/dataStore/components/dataGrid/NullEmptyCellRenderer.vue
@@ -13,6 +13,6 @@ const props = defineProps<{
diff --git a/packages/frontend/editor-ui/src/features/dataStore/components/dataGrid/n8nTheme.ts b/packages/frontend/editor-ui/src/features/dataStore/components/dataGrid/n8nTheme.ts
index d0b90a779e..db730347bf 100644
--- a/packages/frontend/editor-ui/src/features/dataStore/components/dataGrid/n8nTheme.ts
+++ b/packages/frontend/editor-ui/src/features/dataStore/components/dataGrid/n8nTheme.ts
@@ -6,4 +6,8 @@ export const n8nTheme = themeQuartz.withPart(iconSetAlpine).withParams({
rowVerticalPaddingScale: 0.8,
sidePanelBorder: true,
wrapperBorder: true,
+ headerColumnBorder: { color: 'var(--color-foreground-base)' },
+ headerColumnBorderHeight: '100%',
+ checkboxUncheckedBackgroundColor: 'var(--color-background-light-base)',
+ checkboxCheckedBackgroundColor: 'var(--color-primary)',
});
diff --git a/packages/frontend/editor-ui/src/features/dataStore/constants.ts b/packages/frontend/editor-ui/src/features/dataStore/constants.ts
index 6efaaf5473..5460244d78 100644
--- a/packages/frontend/editor-ui/src/features/dataStore/constants.ts
+++ b/packages/frontend/editor-ui/src/features/dataStore/constants.ts
@@ -6,6 +6,13 @@ export const DATA_STORE_STORE = 'dataStoreStore';
export const DEFAULT_DATA_STORE_PAGE_SIZE = 10;
+export const DATA_STORE_ID_COLUMN_WIDTH = 46;
+
+export const DATA_STORE_HEADER_HEIGHT = 36;
+export const DATA_STORE_ROW_HEIGHT = 43;
+
+export const ADD_ROW_ROW_ID = '__n8n_add_row__';
+
export const DATA_STORE_CARD_ACTIONS = {
RENAME: 'rename',
DELETE: 'delete',
diff --git a/packages/frontend/editor-ui/src/features/dataStore/dataStore.api.ts b/packages/frontend/editor-ui/src/features/dataStore/dataStore.api.ts
index 44de0423b6..77c28f15cb 100644
--- a/packages/frontend/editor-ui/src/features/dataStore/dataStore.api.ts
+++ b/packages/frontend/editor-ui/src/features/dataStore/dataStore.api.ts
@@ -151,7 +151,7 @@ export const insertDataStoreRowApi = async (
row: DataStoreRow,
projectId: string,
) => {
- return await makeRestApiRequest(
+ return await makeRestApiRequest>(
context,
'POST',
`/projects/${projectId}/data-stores/${dataStoreId}/insert`,
diff --git a/packages/frontend/editor-ui/src/features/dataStore/dataStore.store.ts b/packages/frontend/editor-ui/src/features/dataStore/dataStore.store.ts
index 7b783ee83c..30d3736faa 100644
--- a/packages/frontend/editor-ui/src/features/dataStore/dataStore.store.ts
+++ b/packages/frontend/editor-ui/src/features/dataStore/dataStore.store.ts
@@ -191,7 +191,7 @@ export const useDataStoreStore = defineStore(DATA_STORE_STORE, () => {
emptyRow,
dataStore.projectId,
);
- return inserted[0];
+ return inserted[0].id;
};
const updateRow = async (
diff --git a/packages/frontend/editor-ui/src/stores/settings.store.ts b/packages/frontend/editor-ui/src/stores/settings.store.ts
index 600edf565e..ba19cf1005 100644
--- a/packages/frontend/editor-ui/src/stores/settings.store.ts
+++ b/packages/frontend/editor-ui/src/stores/settings.store.ts
@@ -141,7 +141,7 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
const isFoldersFeatureEnabled = computed(() => folders.value.enabled);
- const isDataStoreFeatureEnabled = computed(() => isModuleActive('data-store'));
+ const isDataStoreFeatureEnabled = computed(() => isModuleActive('data-table'));
const areTagsEnabled = computed(() =>
settings.value.workflowTagsDisabled !== undefined ? !settings.value.workflowTagsDisabled : true,