From c1adcc9e2cd26f2fb16a885624d7fde700e86494 Mon Sep 17 00:00:00 2001 From: Sherfin Shamsudeen Date: Wed, 12 Oct 2022 22:26:10 +0530 Subject: [PATCH] Feature/automatic columns generation (Task ID: CU-2vjwn7r) (#4149) * Remove unnecessary setting of columnSizes in table inpector * Rough implementation of autogeneration of table columns * Use custom mapping for autogeneration of table columns * Use setTimeout to prevent race conditions while storing autogenerated columns * Autogenerate columns only on new tables * Do not generate columns that were deleted previously * Autogenerate columns only in edit mode * Re-render Inspector whenever app definition is updated * Refactor table column auto-generation * Remove unnecessary console.log * Add Number data type support for table column auto-generation * Remove unnecessary console.log * Ensure that column key is stored in deletion history if it is available --- frontend/src/Editor/Box.jsx | 4 ++ .../src/Editor/Components/Table/Table.jsx | 15 +++++- .../Table/columns/autogenerateColumns.js | 49 +++++++++++++++++++ frontend/src/Editor/Editor.jsx | 4 +- .../src/Editor/Inspector/Components/Table.jsx | 21 ++++---- frontend/src/Editor/Inspector/Inspector.jsx | 3 +- .../src/Editor/WidgetManager/widgetConfig.js | 4 ++ 7 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 frontend/src/Editor/Components/Table/columns/autogenerateColumns.js diff --git a/frontend/src/Editor/Box.jsx b/frontend/src/Editor/Box.jsx index c893e8aecb..16e67d4a07 100644 --- a/frontend/src/Editor/Box.jsx +++ b/frontend/src/Editor/Box.jsx @@ -309,6 +309,10 @@ export const Box = function Box({ dataQueries={dataQueries} variablesExposedForPreview={variablesExposedForPreview} exposeToCodeHinter={exposeToCodeHinter} + setProperty={(property, value) => { + paramUpdated(id, property, { value }); + }} + mode={mode} > ) : (
diff --git a/frontend/src/Editor/Components/Table/Table.jsx b/frontend/src/Editor/Components/Table/Table.jsx index 79ea902cdf..9b69d93a70 100644 --- a/frontend/src/Editor/Components/Table/Table.jsx +++ b/frontend/src/Editor/Components/Table/Table.jsx @@ -25,6 +25,7 @@ import { reducer, reducerActions, initialState } from './reducer'; import customFilter from './custom-filter'; import generateColumnsData from './columns'; import generateActionsData from './columns/actions'; +import autogenerateColumns from './columns/autogenerateColumns'; import IndeterminateCheckbox from './IndeterminateCheckbox'; import { useTranslation } from 'react-i18next'; import * as XLSX from 'xlsx/xlsx.mjs'; @@ -52,6 +53,8 @@ export function Table({ properties, variablesExposedForPreview, exposeToCodeHinter, + setProperty, + mode, exposedVariables, }) { const { @@ -214,7 +217,6 @@ export function Table({ if (currentState) { tableData = resolveReferences(component.definition.properties.data.value, currentState, []); if (!Array.isArray(tableData)) tableData = []; - console.log('resolved param', tableData); } tableData = tableData || []; @@ -286,6 +288,17 @@ export function Table({ ] ); + useEffect(() => { + if (tableData.length != 0 && component.definition.properties.autogenerateColumns.value && mode === 'edit') { + autogenerateColumns( + tableData, + component.definition.properties.columns.value, + component.definition.properties?.columnDeletionHistory?.value ?? [], + setProperty + ); + } + }, [JSON.stringify(tableData)]); + const computedStyles = { // width: `${width}px`, }; diff --git a/frontend/src/Editor/Components/Table/columns/autogenerateColumns.js b/frontend/src/Editor/Components/Table/columns/autogenerateColumns.js new file mode 100644 index 0000000000..f000598452 --- /dev/null +++ b/frontend/src/Editor/Components/Table/columns/autogenerateColumns.js @@ -0,0 +1,49 @@ +import _ from 'lodash'; +import { v4 as uuidv4 } from 'uuid'; + +export default function autogenerateColumns(tableData, existingColumns, columnDeletionHistory, setProperty) { + const firstRow = tableData?.[0] ?? {}; + + const keysOfTableData = Object.keys(firstRow); + + const keysOfExistingColumns = existingColumns.map((column) => column.key || column.name); + + const keysFromWhichNewColumnsShouldBeGenerated = _.difference(keysOfTableData, [ + ...keysOfExistingColumns, + ...columnDeletionHistory, + ]); + + const keysAndDataTypesToGenerateNewColumns = keysFromWhichNewColumnsShouldBeGenerated?.map((key) => [ + key, + typeof firstRow[key], + ]); + + const keysOfExistingColumnsThatNeedToPersist = existingColumns + .filter((column) => !column.autogenerated || keysOfTableData.includes(column.key || column.name)) + .map((column) => column.key || column.name); + + const generatedColumns = keysAndDataTypesToGenerateNewColumns.map(([key, dataType]) => ({ + id: uuidv4(), + name: key, + key: key, + columnType: convertDataTypeToColumnType(dataType), + autogenerated: true, + })); + + const finalKeys = [...keysFromWhichNewColumnsShouldBeGenerated, ...keysOfExistingColumnsThatNeedToPersist]; + const finalColumns = [...existingColumns, ...generatedColumns].filter((column) => + finalKeys.includes(column.key || column.name) + ); + + setTimeout(() => setProperty('columns', finalColumns), 10); +} + +const dataTypeToColumnTypeMapping = { + string: 'string', + number: 'number', +}; + +const convertDataTypeToColumnType = (dataType) => { + if (Object.keys(dataTypeToColumnTypeMapping).includes(dataType)) return dataTypeToColumnTypeMapping[dataType]; + else return 'default'; +}; diff --git a/frontend/src/Editor/Editor.jsx b/frontend/src/Editor/Editor.jsx index b451fd022a..7716c699c7 100644 --- a/frontend/src/Editor/Editor.jsx +++ b/frontend/src/Editor/Editor.jsx @@ -62,6 +62,7 @@ import { EditorContextWrapper } from './Context/EditorContextWrapper'; // eslint-disable-next-line import/no-unresolved import Selecto from 'react-selecto'; import { withTranslation } from 'react-i18next'; +import { v4 as uuid } from 'uuid'; setAutoFreeze(false); enablePatches(); @@ -551,7 +552,7 @@ class EditorComponent extends React.Component { }, this.handleAddPatch ); - this.setState({ isSaving: true, appDefinition: newDefinition }, () => { + this.setState({ isSaving: true, appDefinition: newDefinition, appDefinitionLocalVersion: uuid() }, () => { if (!opts.skipAutoSave) this.autoSave(); }); computeComponentState(this, newDefinition.components); @@ -1666,6 +1667,7 @@ class EditorComponent extends React.Component { apps={apps} darkMode={this.props.darkMode} handleEditorEscapeKeyPress={this.handleEditorEscapeKeyPress} + appDefinitionLocalVersion={this.state.appDefinitionLocalVersion} > ) : (
diff --git a/frontend/src/Editor/Inspector/Components/Table.jsx b/frontend/src/Editor/Inspector/Components/Table.jsx index 1b1413e1dd..9f91949434 100644 --- a/frontend/src/Editor/Inspector/Components/Table.jsx +++ b/frontend/src/Editor/Inspector/Components/Table.jsx @@ -684,16 +684,7 @@ class TableComponent extends React.Component { onColumnItemChange = (index, item, value) => { const columns = this.props.component.component.definition.properties.columns; const column = columns.value[index]; - if (item === 'name') { - const columnSizes = this.props.component.component.definition.properties.columnSizes; - if (columnSizes) { - const newColumnSizes = JSON.parse(JSON.stringify(columnSizes)); - if (newColumnSizes[column.name]) { - newColumnSizes[value] = newColumnSizes[column.name]; - this.props.paramUpdated({ name: 'columnSizes' }, null, newColumnSizes, 'properties'); - } - } - } + column[item] = value; const newColumns = columns.value; newColumns[index] = column; @@ -703,8 +694,16 @@ class TableComponent extends React.Component { removeColumn = (index) => { const columns = this.props.component.component.definition.properties.columns; const newValue = columns.value; - newValue.splice(index, 1); + const removedColumns = newValue.splice(index, 1); this.props.paramUpdated({ name: 'columns' }, 'value', newValue, 'properties'); + + const existingcolumnDeletionHistory = + this.props.component.component.definition.properties.columnDeletionHistory?.value ?? []; + const newcolumnDeletionHistory = [ + ...existingcolumnDeletionHistory, + ...removedColumns.map((column) => column.key || column.name), + ]; + this.props.paramUpdated({ name: 'columnDeletionHistory' }, 'value', newcolumnDeletionHistory, 'properties'); }; getPopoverFieldSource = (column, field) => diff --git a/frontend/src/Editor/Inspector/Inspector.jsx b/frontend/src/Editor/Inspector/Inspector.jsx index 161ac02784..6cc1d0c33f 100644 --- a/frontend/src/Editor/Inspector/Inspector.jsx +++ b/frontend/src/Editor/Inspector/Inspector.jsx @@ -29,6 +29,7 @@ export const Inspector = ({ switchSidebarTab, removeComponent, handleEditorEscapeKeyPress, + appDefinitionLocalVersion, }) => { const component = { id: selectedComponentId, @@ -389,7 +390,7 @@ export const Inspector = ({ }; return ( -
+