form view - loading, navigation

This commit is contained in:
Jan Prochazka
2021-01-09 21:27:34 +01:00
parent b71b58c93f
commit 5ab2ed9646
5 changed files with 167 additions and 14 deletions

View File

@@ -3,9 +3,18 @@ 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 {
Expression,
Select,
treeToSql,
dumpSqlSelect,
mergeConditions,
Condition,
OrderByExpression,
} from 'dbgate-sqltree';
import { filterName } from './filterName';
import { TableGridDisplay } from './TableGridDisplay';
import stableStringify from 'json-stable-stringify';
export class TableFormViewDisplay extends FormViewDisplay {
public table: TableInfo;
@@ -28,7 +37,7 @@ export class TableFormViewDisplay extends FormViewDisplay {
this.columns = this.gridDisplay.columns;
}
getPrimaryKeyCondition(): Condition {
getPrimaryKeyEqualCondition(): Condition {
if (!this.config.formViewKey) return null;
return {
conditionType: 'and',
@@ -50,12 +59,130 @@ export class TableFormViewDisplay extends FormViewDisplay {
};
}
getCurrentRowQuery() {
getPrimaryKeyOperatorCondition(operator): Condition {
if (!this.config.formViewKey) return null;
const conditions = [];
const { primaryKey } = this.gridDisplay.baseTable;
for (let index = 0; index < primaryKey.columns.length; index++) {
conditions.push({
conditionType: 'and',
conditions: [
...primaryKey.columns.slice(0, index).map(({ columnName }) => ({
conditionType: 'binary',
operator: '=',
left: {
exprType: 'column',
columnName,
source: {
alias: 'basetbl',
},
},
right: {
exprType: 'value',
value: this.config.formViewKey[columnName],
},
})),
...primaryKey.columns.slice(index).map(({ columnName }) => ({
conditionType: 'binary',
operator: operator,
left: {
exprType: 'column',
columnName,
source: {
alias: 'basetbl',
},
},
right: {
exprType: 'value',
value: this.config.formViewKey[columnName],
},
})),
],
});
}
if (conditions.length == 1) {
return conditions[0];
}
return {
conditionType: 'or',
conditions,
};
}
getSelect() {
if (!this.driver) return null;
const select = this.gridDisplay.createSelect();
if (!select) return null;
select.topRecords = 1;
select.where = mergeConditions(select.where, this.getPrimaryKeyCondition());
return select;
}
getCurrentRowQuery() {
const select = this.getSelect();
if (!select) return null;
select.where = mergeConditions(select.where, this.getPrimaryKeyEqualCondition());
const sql = treeToSql(this.driver, select, dumpSqlSelect);
return sql;
}
extractKey(row) {
const formViewKey = _.pick(
row,
this.gridDisplay.baseTable.primaryKey.columns.map((x) => x.columnName)
);
return formViewKey;
}
navigate(row) {
const formViewKey = this.extractKey(row);
this.setConfig((cfg) => ({
...cfg,
formViewKey,
}));
}
isLoadedCurrentRow(row) {
if (!row) return false;
const formViewKey = this.extractKey(row);
return stableStringify(formViewKey) == stableStringify(this.config.formViewKey);
}
navigateRowQuery(commmand: 'begin' | 'previous' | 'next' | 'end') {
if (!this.driver) return null;
const select = this.gridDisplay.createSelect();
if (!select) return null;
const { primaryKey } = this.gridDisplay.baseTable;
function getOrderBy(direction): OrderByExpression[] {
return primaryKey.columns.map(({ columnName }) => ({
exprType: 'column',
columnName,
direction,
}));
}
select.topRecords = 1;
switch (commmand) {
case 'begin':
select.orderBy = getOrderBy('ASC');
break;
case 'end':
select.orderBy = getOrderBy('DESC');
break;
case 'previous':
select.orderBy = getOrderBy('DESC');
select.where = mergeConditions(select.where, this.getPrimaryKeyOperatorCondition('<'));
break;
case 'next':
select.orderBy = getOrderBy('ASC');
select.where = mergeConditions(select.where, this.getPrimaryKeyOperatorCondition('>'));
break;
}
const sql = treeToSql(this.driver, select, dumpSqlSelect);
return sql;
}

View File

@@ -62,7 +62,7 @@ const NullSpan = styled.span`
`;
export default function FormView(props) {
const { rowData, toolbarPortalRef, tabVisible, config, setConfig } = props;
const { rowData, toolbarPortalRef, tabVisible, config, setConfig, onNavigate } = props;
/** @type {import('dbgate-datalib').FormViewDisplay} */
const formDisplay = props.formDisplay;
const theme = useTheme();
@@ -81,7 +81,10 @@ export default function FormView(props) {
toolbarPortalRef &&
toolbarPortalRef.current &&
tabVisible &&
ReactDOM.createPortal(<FormViewToolbar switchToTable={handleSwitchToTable} />, toolbarPortalRef.current);
ReactDOM.createPortal(
<FormViewToolbar switchToTable={handleSwitchToTable} onNavigate={onNavigate} />,
toolbarPortalRef.current
);
// console.log('display', display);

View File

@@ -1,12 +1,24 @@
import React from 'react';
import ToolbarButton from '../widgets/ToolbarButton';
export default function FormViewToolbar({ switchToTable }) {
export default function FormViewToolbar({ switchToTable, onNavigate }) {
return (
<>
<ToolbarButton onClick={switchToTable} icon="icon table">
Table view
</ToolbarButton>
<ToolbarButton onClick={() => onNavigate('begin')} icon="icon arrow-begin">
First
</ToolbarButton>
<ToolbarButton onClick={() => onNavigate('previous')} icon="icon arrow-left">
Previous
</ToolbarButton>
<ToolbarButton onClick={() => onNavigate('next')} icon="icon arrow-right">
Next
</ToolbarButton>
<ToolbarButton onClick={() => onNavigate('end')} icon="icon arrow-end">
Last
</ToolbarButton>
</>
);
}

View File

@@ -6,11 +6,10 @@ import useExtensions from '../utility/useExtensions';
import FormView from './FormView';
import axios from '../utility/axios';
async function loadCurrentRow(props) {
const { formDisplay, conid, database } = props;
async function loadRow(props, sql) {
const { conid, database } = props;
/** @type {import('dbgate-datalib').TableFormViewDisplay} */
const sql = formDisplay.getCurrentRowQuery();
const formDisplay = props.formDisplay;
const response = await axios.request({
url: 'database-connections/query-data',
@@ -31,12 +30,22 @@ export default function SqlFormView(props) {
const [rowData, setRowData] = React.useState(null);
const handleLoadCurrentRow = async () => {
const row = await loadCurrentRow(props);
const row = await loadRow(props, formDisplay.getCurrentRowQuery());
if (row) setRowData(row);
};
const handleNavigate = async (command) => {
const row = await loadRow(props, formDisplay.navigateRowQuery(command));
if (row) {
setRowData(row);
formDisplay.navigate(row);
}
};
React.useEffect(() => {
handleLoadCurrentRow();
if (formDisplay && !formDisplay.isLoadedCurrentRow(rowData)) {
handleLoadCurrentRow();
}
}, [formDisplay]);
// const { config, setConfig, cache, setCache, schemaName, pureName, conid, database } = props;
@@ -67,5 +76,5 @@ export default function SqlFormView(props) {
// setDisplay(newDisplay);
// }, [config, cache, conid, database, schemaName, pureName, dbinfo, extensions]);
return <FormView {...props} rowData={rowData} />;
return <FormView {...props} rowData={rowData} onNavigate={handleNavigate} />;
}

View File

@@ -39,6 +39,8 @@ const iconNames = {
'icon arrow-up': 'mdi mdi-arrow-up',
'icon arrow-down': 'mdi mdi-arrow-down',
'icon arrow-left': 'mdi mdi-arrow-left',
'icon arrow-begin': 'mdi mdi-arrow-collapse-left',
'icon arrow-end': 'mdi mdi-arrow-collapse-right',
'icon arrow-right': 'mdi mdi-arrow-right',
'icon format-code': 'mdi mdi-code-tags-check',
'icon show-wizard': 'mdi mdi-comment-edit',