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
This commit is contained in:
Sherfin Shamsudeen 2022-10-12 22:26:10 +05:30 committed by GitHub
parent 0e7a4e6b38
commit c1adcc9e2c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 86 additions and 14 deletions

View file

@ -309,6 +309,10 @@ export const Box = function Box({
dataQueries={dataQueries}
variablesExposedForPreview={variablesExposedForPreview}
exposeToCodeHinter={exposeToCodeHinter}
setProperty={(property, value) => {
paramUpdated(id, property, { value });
}}
mode={mode}
></ComponentToRender>
) : (
<div className="m-1" style={{ height: '76px', width: '76px', marginLeft: '18px' }}>

View file

@ -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`,
};

View file

@ -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';
};

View file

@ -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}
></Inspector>
) : (
<center className="mt-5 p-2">

View file

@ -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) =>

View file

@ -29,6 +29,7 @@ export const Inspector = ({
switchSidebarTab,
removeComponent,
handleEditorEscapeKeyPress,
appDefinitionLocalVersion,
}) => {
const component = {
id: selectedComponentId,
@ -389,7 +390,7 @@ export const Inspector = ({
};
return (
<div className="inspector">
<div className="inspector" key={appDefinitionLocalVersion}>
<ConfirmDialog
show={showWidgetDeleteConfirmation}
message={'Widget will be deleted, do you want to continue?'}

View file

@ -346,19 +346,23 @@ export const widgets = [
displaySearchBox: { value: '{{true}}' },
showDownloadButton: { value: '{{true}}' },
showFilterButton: { value: '{{true}}' },
autogenerateColumns: { value: true },
columns: {
value: [
{
name: 'id',
id: 'e3ecbf7fa52c4d7210a93edb8f43776267a489bad52bd108be9588f790126737',
autogenerated: true,
},
{
name: 'name',
id: '5d2a3744a006388aadd012fcc15cc0dbcb5f9130e0fbb64c558561c97118754a',
autogenerated: true,
},
{
name: 'email',
id: 'afc9a5091750a1bd4760e38760de3b4be11a43452ae8ae07ce2eebc569fe9a7f',
autogenerated: true,
},
],
},