form view - row count

This commit is contained in:
Jan Prochazka 2021-01-11 17:25:05 +01:00
parent 7c8c7467e1
commit 9d50ac0093
4 changed files with 183 additions and 8 deletions

View File

@ -42,7 +42,7 @@ export class TableFormViewDisplay extends FormViewDisplay {
if (!row) row = this.config.formViewKey;
if (!row) return null;
const { primaryKey } = this.gridDisplay.baseTable;
if (primaryKey) return null;
if (!primaryKey) return null;
return {
conditionType: 'and',
conditions: primaryKey.columns.map(({ columnName }) => ({
@ -68,6 +68,7 @@ export class TableFormViewDisplay extends FormViewDisplay {
const conditions = [];
const { primaryKey } = this.gridDisplay.baseTable;
if (!primaryKey) return null;
for (let index = 0; index < primaryKey.columns.length; index++) {
conditions.push({
conditionType: 'and',
@ -133,7 +134,40 @@ export class TableFormViewDisplay extends FormViewDisplay {
return sql;
}
getCountSelect() {
const select = this.getSelect();
if (!select) return null;
select.orderBy = null;
select.columns = [
{
exprType: 'raw',
sql: 'COUNT(*)',
alias: 'count',
},
];
select.topRecords = null;
return select;
}
getCountQuery() {
if (!this.driver) return null;
const select = this.getCountSelect();
const sql = treeToSql(this.driver, select, dumpSqlSelect);
return sql;
}
getBeforeCountQuery() {
if (!this.driver) return null;
const select = this.getCountSelect();
select.where = mergeConditions(select.where, this.getPrimaryKeyOperatorCondition('<'));
const sql = treeToSql(this.driver, select, dumpSqlSelect);
return sql;
}
extractKey(row) {
if (!row || !this.gridDisplay.baseTable || !this.gridDisplay.baseTable.primaryKey) {
return null;
}
const formViewKey = _.pick(
row,
this.gridDisplay.baseTable.primaryKey.columns.map((x) => x.columnName)
@ -150,6 +184,7 @@ export class TableFormViewDisplay extends FormViewDisplay {
}
isLoadedCurrentRow(row) {
console.log('isLoadedCurrentRow', row, this.config.formViewKey);
if (!row) return false;
const formViewKey = this.extractKey(row);
return stableStringify(formViewKey) == stableStringify(this.config.formViewKey);

View File

@ -15,6 +15,7 @@ import keycodes from '../utility/keycodes';
import { CellFormattedValue } from '../datagrid/DataGridRow';
import { cellFromEvent } from '../datagrid/selection';
import InplaceEditor from '../datagrid/InplaceEditor';
import { copyTextToClipboard } from '../utility/clipboard';
const Table = styled.table`
border-collapse: collapse;
@ -87,12 +88,33 @@ const FocusField = styled.input`
top: -1000px;
`;
const RowCountLabel = styled.div`
position: absolute;
background-color: ${(props) => props.theme.gridbody_background_yellow[1]};
right: 40px;
bottom: 20px;
`;
function isDataCell(cell) {
return cell[1] % 2 == 1;
}
export default function FormView(props) {
const { toolbarPortalRef, tabVisible, config, setConfig, onNavigate, former, onSave } = props;
const {
toolbarPortalRef,
tabVisible,
config,
setConfig,
onNavigate,
former,
onSave,
conid,
database,
onReload,
onReconnect,
allRowCount,
rowCountBefore,
} = props;
/** @type {import('dbgate-datalib').FormViewDisplay} */
const formDisplay = props.formDisplay;
const theme = useTheme();
@ -197,6 +219,25 @@ export default function FormView(props) {
if (onSave) onSave();
}
function getCellColumn(cell) {
const chunk = columnChunks[Math.floor(cell[1] / 2)];
if (!chunk) return;
const column = chunk[cell[0]];
return column;
}
function setCellValue(cell, value) {
const column = getCellColumn(cell);
if (!column) return;
former.setCellValue(column.uniqueName, value);
}
function setNull() {
if (isDataCell(currentCell)) {
setCellValue(currentCell, null);
}
}
const scrollIntoView = (cell) => {
const element = cellRefs.current[`${cell[0]},${cell[1]}`];
if (element) element.scrollIntoView();
@ -212,6 +253,13 @@ export default function FormView(props) {
scrollIntoView(moved);
};
function copyToClipboard() {
const column = getCellColumn(currentCell);
if (!column) return;
const text = currentCell[1] % 2 == 1 ? rowData[column.uniqueName] : column.columnName;
copyTextToClipboard(text);
}
const handleKeyDown = (event) => {
const navigation = handleKeyNavigation(event);
if (navigation) {
@ -232,6 +280,36 @@ export default function FormView(props) {
// this.saveAndFocus();
}
if (event.keyCode == keycodes.n0 && event.ctrlKey) {
event.preventDefault();
setNull();
}
if (event.keyCode == keycodes.r && event.ctrlKey) {
event.preventDefault();
former.revertRowChanges();
}
// if (event.keyCode == keycodes.f && event.ctrlKey) {
// event.preventDefault();
// filterSelectedValue();
// }
if (event.keyCode == keycodes.z && event.ctrlKey) {
event.preventDefault();
former.undo();
}
if (event.keyCode == keycodes.y && event.ctrlKey) {
event.preventDefault();
former.redo();
}
if (event.keyCode == keycodes.c && event.ctrlKey) {
event.preventDefault();
copyToClipboard();
}
if (
!event.ctrlKey &&
!event.altKey &&
@ -281,6 +359,11 @@ export default function FormView(props) {
return 100;
};
const rowCountInfo = React.useMemo(() => {
if (allRowCount == null || rowCountBefore == null) return 'Loading row count...';
return `Row: ${(rowCountBefore + 1).toLocaleString()} / ${allRowCount.toLocaleString()}`;
}, [rowCountBefore, allRowCount]);
const [inplaceEditorState, dispatchInsplaceEditor] = React.useReducer((state, action) => {
switch (action.type) {
case 'show':
@ -313,7 +396,14 @@ export default function FormView(props) {
toolbarPortalRef.current &&
tabVisible &&
ReactDOM.createPortal(
<FormViewToolbar switchToTable={handleSwitchToTable} onNavigate={onNavigate} />,
<FormViewToolbar
switchToTable={handleSwitchToTable}
onNavigate={onNavigate}
reload={onReload}
reconnect={onReconnect}
save={handleSave}
former={former}
/>,
toolbarPortalRef.current
);
@ -371,6 +461,7 @@ export default function FormView(props) {
))}
<FocusField type="text" ref={focusFieldRef} onKeyDown={handleKeyDown} />
{rowCountInfo && <RowCountLabel theme={theme}>{rowCountInfo}</RowCountLabel>}
{toolbar}
</Wrapper>

View File

@ -1,7 +1,7 @@
import React from 'react';
import ToolbarButton from '../widgets/ToolbarButton';
export default function FormViewToolbar({ switchToTable, onNavigate }) {
export default function FormViewToolbar({ switchToTable, onNavigate, reload, reconnect, former, save }) {
return (
<>
<ToolbarButton onClick={switchToTable} icon="icon table">
@ -19,6 +19,24 @@ export default function FormViewToolbar({ switchToTable, onNavigate }) {
<ToolbarButton onClick={() => onNavigate('end')} icon="icon arrow-end">
Last
</ToolbarButton>
<ToolbarButton onClick={reload} icon="icon reload">
Refresh
</ToolbarButton>
<ToolbarButton onClick={reconnect} icon="icon connection">
Reconnect
</ToolbarButton>
<ToolbarButton disabled={!former.canUndo} onClick={() => former.undo()} icon="icon undo">
Undo
</ToolbarButton>
<ToolbarButton disabled={!former.canRedo} onClick={() => former.redo()} icon="icon redo">
Redo
</ToolbarButton>
<ToolbarButton disabled={!former.allowSave} onClick={save} icon="icon save">
Save
</ToolbarButton>
<ToolbarButton disabled={!former.containsChanges} onClick={() => former.revertAllChanges()} icon="icon close">
Revert
</ToolbarButton>
</>
);
}

View File

@ -14,8 +14,8 @@ import useShowModal from '../modals/showModal';
async function loadRow(props, sql) {
const { conid, database } = props;
/** @type {import('dbgate-datalib').TableFormViewDisplay} */
const formDisplay = props.formDisplay;
if (!sql) return null;
const response = await axios.request({
url: 'database-connections/query-data',
@ -35,6 +35,7 @@ export default function SqlFormView(props) {
const { formDisplay, changeSetState, dispatchChangeSet, conid, database } = props;
const [rowData, setRowData] = React.useState(null);
const [reloadToken, setReloadToken] = React.useState(0);
const [rowCountInfo, setRowCountInfo] = React.useState(null);
const confirmSqlModalState = useModalState();
const [confirmSql, setConfirmSql] = React.useState('');
@ -49,6 +50,18 @@ export default function SqlFormView(props) {
if (row) setRowData(row);
};
const handleLoadRowCount = async () => {
const countRow = await loadRow(props, formDisplay.getCountQuery());
const countBeforeRow = await loadRow(props, formDisplay.getBeforeCountQuery());
if (countRow && countBeforeRow) {
setRowCountInfo({
allRowCount: parseInt(countRow.count),
rowCountBefore: parseInt(countBeforeRow.count),
});
}
};
const handleNavigate = async (command) => {
const row = await loadRow(props, formDisplay.navigateRowQuery(command));
if (row) {
@ -59,13 +72,19 @@ export default function SqlFormView(props) {
React.useEffect(() => {
if (formDisplay) handleLoadCurrentRow();
setRowCountInfo(null);
handleLoadRowCount();
}, [reloadToken]);
React.useEffect(() => {
if (!formDisplay.isLoadedCorrectly) return;
if (formDisplay && !formDisplay.isLoadedCurrentRow(rowData)) {
handleLoadCurrentRow();
}
}, [formDisplay, rowData]);
setRowCountInfo(null);
handleLoadRowCount();
}, [formDisplay]);
const former = React.useMemo(() => new ChangeSetFormer(rowData, changeSetState, dispatchChangeSet, formDisplay), [
rowData,
@ -133,7 +152,19 @@ export default function SqlFormView(props) {
return (
<>
<FormView {...props} rowData={rowData} onNavigate={handleNavigate} former={former} onSave={handleSave} />
<FormView
{...props}
rowData={rowData}
onNavigate={handleNavigate}
former={former}
onSave={handleSave}
onReload={() => setReloadToken((x) => x + 1)}
onReconnect={async () => {
await axios.post('database-connections/refresh', { conid, database });
formDisplay.reload();
}}
{...rowCountInfo}
/>
<ConfirmSqlModal
modalState={confirmSqlModalState}
sql={confirmSql}