diff --git a/packages/datalib/src/FormViewDisplay.ts b/packages/datalib/src/FormViewDisplay.ts new file mode 100644 index 00000000..76634057 --- /dev/null +++ b/packages/datalib/src/FormViewDisplay.ts @@ -0,0 +1,23 @@ +import _ from 'lodash'; +import { GridConfig, GridCache, GridConfigColumns, createGridCache, GroupFunc } from './GridConfig'; +import { ForeignKeyInfo, TableInfo, ColumnInfo, EngineDriver, NamedObjectInfo, DatabaseInfo } from 'dbgate-types'; +import { parseFilter, getFilterType } from 'dbgate-filterparser'; +import { filterName } from './filterName'; +import { ChangeSetFieldDefinition, ChangeSetRowDefinition } from './ChangeSet'; +import { Expression, Select, treeToSql, dumpSqlSelect, Condition } from 'dbgate-sqltree'; +import { isTypeLogical } from 'dbgate-tools'; +import { ChangeCacheFunc, ChangeConfigFunc, DisplayColumn } from './GridDisplay'; + +export class FormViewDisplay { + isLoadedCorrectly = true; + columns: DisplayColumn[]; + + constructor( + public config: GridConfig, + protected setConfig: ChangeConfigFunc, + public cache: GridCache, + protected setCache: ChangeCacheFunc, + public driver?: EngineDriver, + public dbinfo: DatabaseInfo = null + ) {} +} diff --git a/packages/datalib/src/GridConfig.ts b/packages/datalib/src/GridConfig.ts index 13d7a11b..80a37edd 100644 --- a/packages/datalib/src/GridConfig.ts +++ b/packages/datalib/src/GridConfig.ts @@ -29,6 +29,8 @@ export interface GridConfig extends GridConfigColumns { grouping: { [uniqueName: string]: GroupFunc }; childConfig?: GridConfig; reference?: GridReferenceDefinition; + isFormView?: boolean; + formViewKey?: { [uniqueName: string]: string }; } export interface GridCache { diff --git a/packages/datalib/src/GridDisplay.ts b/packages/datalib/src/GridDisplay.ts index 2c4d638d..66d86e16 100644 --- a/packages/datalib/src/GridDisplay.ts +++ b/packages/datalib/src/GridDisplay.ts @@ -518,4 +518,20 @@ export abstract class GridDisplay { conditions, }; } + + switchToFormView(rowData) { + if (!this.baseTable) return; + const { primaryKey } = this.baseTable; + if (!primaryKey) return; + const { columns } = primaryKey; + + this.setConfig((cfg) => ({ + ...cfg, + isFormView: true, + formViewKey: _.pick( + rowData, + columns.map((x) => x.columnName) + ), + })); + } } diff --git a/packages/datalib/src/TableFormViewDisplay.ts b/packages/datalib/src/TableFormViewDisplay.ts new file mode 100644 index 00000000..0554fab0 --- /dev/null +++ b/packages/datalib/src/TableFormViewDisplay.ts @@ -0,0 +1,62 @@ +import { FormViewDisplay } from './FormViewDisplay'; +import _ from 'lodash'; +import { GridDisplay, ChangeCacheFunc, DisplayColumn, DisplayedColumnInfo, ChangeConfigFunc } from './GridDisplay'; +import { TableInfo, EngineDriver, ViewInfo, ColumnInfo, NamedObjectInfo, DatabaseInfo } from 'dbgate-types'; +import { GridConfig, GridCache, createGridCache } from './GridConfig'; +import { Expression, Select, treeToSql, dumpSqlSelect, mergeConditions, Condition } from 'dbgate-sqltree'; +import { filterName } from './filterName'; +import { TableGridDisplay } from './TableGridDisplay'; + +export class TableFormViewDisplay extends FormViewDisplay { + public table: TableInfo; + // use utility functions from GridDisplay and publish result in FromViewDisplat interface + private gridDisplay: TableGridDisplay; + + constructor( + public tableName: NamedObjectInfo, + driver: EngineDriver, + config: GridConfig, + setConfig: ChangeConfigFunc, + cache: GridCache, + setCache: ChangeCacheFunc, + dbinfo: DatabaseInfo + ) { + super(config, setConfig, cache, setCache, driver, dbinfo); + this.gridDisplay = new TableGridDisplay(tableName, driver, config, setConfig, cache, setCache, dbinfo); + + this.isLoadedCorrectly = this.gridDisplay.isLoadedCorrectly; + this.columns = this.gridDisplay.columns; + } + + getPrimaryKeyCondition(): Condition { + if (!this.config.formViewKey) return null; + return { + conditionType: 'and', + conditions: _.keys(this.config.formViewKey).map((columnName) => ({ + conditionType: 'binary', + operator: '=', + left: { + exprType: 'column', + columnName, + source: { + alias: 'basetbl', + }, + }, + right: { + exprType: 'value', + value: this.config.formViewKey[columnName], + }, + })), + }; + } + + getCurrentRowQuery() { + if (!this.driver) return null; + const select = this.gridDisplay.createSelect(); + if (!select) return null; + select.topRecords = 1; + select.where = mergeConditions(select.where, this.getPrimaryKeyCondition()); + const sql = treeToSql(this.driver, select, dumpSqlSelect); + return sql; + } +} diff --git a/packages/datalib/src/index.ts b/packages/datalib/src/index.ts index 5185f22f..04472789 100644 --- a/packages/datalib/src/index.ts +++ b/packages/datalib/src/index.ts @@ -1,11 +1,13 @@ -export * from "./GridDisplay"; -export * from "./GridConfig"; -export * from "./TableGridDisplay"; -export * from "./ViewGridDisplay"; -export * from "./JslGridDisplay"; -export * from "./ChangeSet"; -export * from "./filterName"; -export * from "./FreeTableGridDisplay"; -export * from "./FreeTableModel"; -export * from "./MacroDefinition"; -export * from "./runMacro"; +export * from './GridDisplay'; +export * from './GridConfig'; +export * from './TableGridDisplay'; +export * from './ViewGridDisplay'; +export * from './JslGridDisplay'; +export * from './ChangeSet'; +export * from './filterName'; +export * from './FreeTableGridDisplay'; +export * from './FreeTableModel'; +export * from './MacroDefinition'; +export * from './runMacro'; +export * from './FormViewDisplay'; +export * from './TableFormViewDisplay'; diff --git a/packages/web/src/datagrid/DataGrid.js b/packages/web/src/datagrid/DataGrid.js index 3ccb1400..be578cc3 100644 --- a/packages/web/src/datagrid/DataGrid.js +++ b/packages/web/src/datagrid/DataGrid.js @@ -21,17 +21,13 @@ const DataGridContainer = styled.div` `; export default function DataGrid(props) { - const { GridCore, FormView } = props; + const { GridCore, FormView, config, formDisplay } = props; const theme = useTheme(); const [managerSize, setManagerSize] = React.useState(0); const [selection, setSelection] = React.useState([]); const [grider, setGrider] = React.useState(null); - const [formViewData, setFormViewData] = React.useState(null); - const isFormView = !!formViewData; - - const handleSetFormView = (rowData) => { - setFormViewData(rowData); - }; + // const [formViewData, setFormViewData] = React.useState(null); + const isFormView = !!(config && config.isFormView); return ( @@ -57,13 +53,13 @@ export default function DataGrid(props) { {isFormView ? ( - setFormViewData(null)} /> + ) : ( )} diff --git a/packages/web/src/datagrid/DataGridCore.js b/packages/web/src/datagrid/DataGridCore.js index a97ce1c0..011bb54f 100644 --- a/packages/web/src/datagrid/DataGridCore.js +++ b/packages/web/src/datagrid/DataGridCore.js @@ -116,7 +116,7 @@ export default function DataGridCore(props) { onSelectionChanged, frameSelection, onKeyDown, - onSetFormView, + formViewAvailable, } = props; // console.log('RENDER GRID', display.baseTable.pureName); const columns = React.useMemo(() => display.allColumns, [display]); @@ -943,6 +943,13 @@ export default function DataGridCore(props) { display.clearFilters(); }; + const handleSetFormView = + formViewAvailable && display.baseTable && display.baseTable.primaryKey + ? (rowData) => { + display.switchToFormView(rowData); + } + : null; + // console.log('visibleRealColumnIndexes', visibleRealColumnIndexes); // console.log( // 'gridScrollAreaWidth / columnSizes.getVisibleScrollSizeSum()', @@ -1048,7 +1055,7 @@ export default function DataGridCore(props) { display={display} focusedColumn={display.focusedColumn} frameSelection={frameSelection} - onSetFormView={onSetFormView} + onSetFormView={handleSetFormView} /> ) )} diff --git a/packages/web/src/datagrid/TableDataGrid.js b/packages/web/src/datagrid/TableDataGrid.js index 9ecce4f5..0c117faa 100644 --- a/packages/web/src/datagrid/TableDataGrid.js +++ b/packages/web/src/datagrid/TableDataGrid.js @@ -2,7 +2,7 @@ import React from 'react'; import _ from 'lodash'; import DataGrid from './DataGrid'; import styled from 'styled-components'; -import { TableGridDisplay, createGridConfig, createGridCache } from 'dbgate-datalib'; +import { TableGridDisplay, TableFormViewDisplay, createGridConfig, createGridCache } from 'dbgate-datalib'; import { getFilterValueExpression } from 'dbgate-filterparser'; import { findEngineDriver } from 'dbgate-tools'; import { useConnectionInfo, getTableInfo, useDatabaseInfo } from '../utility/metadataLoaders'; @@ -88,7 +88,22 @@ export default function TableDataGrid({ : null; } + function createFormDisplay() { + return connection + ? new TableFormViewDisplay( + { schemaName, pureName }, + findEngineDriver(connection, extensions), + config, + setConfig, + cache || myCache, + setCache || setMyCache, + dbinfo + ) + : null; + } + const [display, setDisplay] = React.useState(createDisplay()); + const [formDisplay, setFormDisplay] = React.useState(createFormDisplay()); React.useEffect(() => { setRefReloadToken((v) => v + 1); @@ -102,6 +117,13 @@ export default function TableDataGrid({ setDisplay(newDisplay); }, [connection, config, cache || myCache, conid, database, schemaName, pureName, dbinfo, extensions]); + React.useEffect(() => { + const newDisplay = createFormDisplay(); + if (!newDisplay) return; + if (formDisplay && formDisplay.isLoadedCorrectly && !newDisplay.isLoadedCorrectly) return; + setFormDisplay(newDisplay); + }, [connection, config, cache || myCache, conid, database, schemaName, pureName, dbinfo, extensions]); + const handleDatabaseStructureChanged = React.useCallback(() => { (setCache || setMyCache)(createGridCache()); }, []); @@ -159,9 +181,12 @@ export default function TableDataGrid({ x.pureName == pureName && x.schemaName == schemaName) - } + // tableInfo={ + // dbinfo && dbinfo.tables && dbinfo.tables.find((x) => x.pureName == pureName && x.schemaName == schemaName) + // } /> {reference && ( diff --git a/packages/web/src/formview/FormView.js b/packages/web/src/formview/FormView.js index 0457a13b..158e74d1 100644 --- a/packages/web/src/formview/FormView.js +++ b/packages/web/src/formview/FormView.js @@ -61,15 +61,34 @@ const NullSpan = styled.span` font-style: italic; `; -export default function FormView({ tableInfo, rowData, toolbarPortalRef, tabVisible, onSetTableView }) { +export default function FormView(props) { + const { rowData, toolbarPortalRef, tabVisible, config, setConfig } = props; + /** @type {import('dbgate-datalib').FormViewDisplay} */ + const formDisplay = props.formDisplay; const theme = useTheme(); const [headerRowRef, { height: rowHeight }] = useDimensions(); const [wrapperRef, { height: wrapperHeight }] = useDimensions(); - if (!tableInfo || !rowData) return null; + const handleSwitchToTable = () => { + setConfig((cfg) => ({ + ...cfg, + isFormView: false, + formViewKey: null, + })); + }; + + const toolbar = + toolbarPortalRef && + toolbarPortalRef.current && + tabVisible && + ReactDOM.createPortal(, toolbarPortalRef.current); + + // console.log('display', display); + + if (!formDisplay || !formDisplay.isLoadedCorrectly) return toolbar; const rowCount = Math.floor((wrapperHeight - 20) / rowHeight); - const columnChunks = _.chunk(tableInfo.columns, rowCount); + const columnChunks = _.chunk(formDisplay.columns, rowCount); return ( @@ -78,18 +97,15 @@ export default function FormView({ tableInfo, rowData, toolbarPortalRef, tabVisi {chunk.map((col) => ( - + - {rowData[col.columnName]} + {rowData && rowData[col.columnName]} ))} ))} - {toolbarPortalRef && - toolbarPortalRef.current && - tabVisible && - ReactDOM.createPortal(, toolbarPortalRef.current)} + {toolbar} ); } diff --git a/packages/web/src/formview/SqlFormView.js b/packages/web/src/formview/SqlFormView.js index 15528424..e8ca69b2 100644 --- a/packages/web/src/formview/SqlFormView.js +++ b/packages/web/src/formview/SqlFormView.js @@ -1,6 +1,71 @@ +import { TableFormViewDisplay } from 'dbgate-datalib'; +import { findEngineDriver } from 'dbgate-tools'; import React from 'react'; +import { useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders'; +import useExtensions from '../utility/useExtensions'; import FormView from './FormView'; +import axios from '../utility/axios'; + +async function loadCurrentRow(props) { + const { formDisplay, conid, database } = props; + /** @type {import('dbgate-datalib').TableFormViewDisplay} */ + + const sql = formDisplay.getCurrentRowQuery(); + + const response = await axios.request({ + url: 'database-connections/query-data', + method: 'post', + params: { + conid, + database, + }, + data: { sql }, + }); + + if (response.data.errorMessage) return response.data; + return response.data.rows[0]; +} export default function SqlFormView(props) { - return ; + const { formDisplay } = props; + const [rowData, setRowData] = React.useState(null); + + const handleLoadCurrentRow = async () => { + const row = await loadCurrentRow(props); + if (row) setRowData(row); + }; + + React.useEffect(() => { + handleLoadCurrentRow(); + }, [formDisplay]); + + // const { config, setConfig, cache, setCache, schemaName, pureName, conid, database } = props; + // const { formViewKey } = config; + + // const [display, setDisplay] = React.useState(null); + + // const connection = useConnectionInfo({ conid }); + // const dbinfo = useDatabaseInfo({ conid, database }); + // const extensions = useExtensions(); + + // console.log('SqlFormView.props', props); + + // React.useEffect(() => { + // const newDisplay = connection + // ? new TableFormViewDisplay( + // { schemaName, pureName }, + // findEngineDriver(connection, extensions), + // config, + // setConfig, + // cache, + // setCache, + // dbinfo + // ) + // : null; + // if (!newDisplay) return; + // if (display && display.isLoadedCorrectly && !newDisplay.isLoadedCorrectly) return; + // setDisplay(newDisplay); + // }, [config, cache, conid, database, schemaName, pureName, dbinfo, extensions]); + + return ; }