1. Filter dialog doesn't work in edit mode (getting back end errors), reverting it back to create mode of SchemaView.

2. Enable the copy button when a cell is selected.
3. Exclude, Include related fixes.
This commit is contained in:
Aditya Toshniwal
2022-04-29 16:29:46 +05:30
committed by Akshay Joshi
parent 4c2e309bdc
commit 64ffed0ddb
9 changed files with 170 additions and 126 deletions

View File

@@ -372,6 +372,14 @@ function getFinalTheme(baseTheme) {
}
},
},
MuiSelect: {
icon: {
color: baseTheme.palette.text.primary,
'&.Mui-disabled': {
color: baseTheme.palette.text.muted,
}
},
},
MuiIconButton: {
root: {
color: baseTheme.palette.text.primary,

View File

@@ -525,7 +525,6 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
}, isNew)
);
}, ()=>{
// selectConn(currSelectedConn, currConnected, false);
});
} else {
selectConn(currSelectedConn, currConnected, false);
@@ -541,31 +540,37 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
id: 'new-conn',
title: gettext('Add New Connection'),
content: <NewConnectionDialog onSave={(_isNew, data)=>{
let connectionData = {
sgid: 0,
sid: data.sid,
did: data.did,
user: data.user,
role: data.role ?? null,
password: data.password,
title: getTitle(pgAdmin, qtState.preferences.browser, null, false, data.server_name, data.database_name, data.user, true),
conn_title: getTitle(pgAdmin, null, null, true, data.server_name, data.database_name, data.user, true),
server_name: data.server_name,
database_name: data.database_name,
is_selected: true,
};
return new Promise((resolve, reject)=>{
let connectionData = {
sgid: 0,
sid: data.sid,
did: data.did,
user: data.user,
role: data.role ?? null,
password: data.password,
title: getTitle(pgAdmin, qtState.preferences.browser, null, false, data.server_name, data.database_name, data.role || data.user, true),
conn_title: getTitle(pgAdmin, null, null, true, data.server_name, data.database_name, data.role || data.user, true),
server_name: data.server_name,
database_name: data.database_name,
is_selected: true,
};
let existIdx = _.findIndex(qtState.connection_list, (conn)=>(
conn.sid == connectionData.sid && conn.did == connectionData.did
&& conn.user == connectionData.user && conn.role == connectionData.role
));
if(existIdx > -1) {
return Promise.reject(gettext('Connection with this configuration already present.'));
}
updateQueryToolConnection(connectionData, true).then(()=>{
onClose();
let existIdx = _.findIndex(qtState.connection_list, (conn)=>(
conn.sid == connectionData.sid && conn.did == connectionData.did
&& conn.user == connectionData.user && conn.role == connectionData.role
));
if(existIdx > -1) {
reject(gettext('Connection with this configuration already present.'));
return;
}
updateQueryToolConnection(connectionData, true)
.catch((err)=>{
reject(err);
}).then(()=>{
resolve();
onClose();
});
});
return Promise.resolve();
}}
onClose={onClose}/>
});

View File

@@ -36,7 +36,7 @@ export const QUERY_TOOL_EVENTS = {
CURSOR_ACTIVITY: 'CURSOR_ACTIVITY',
SET_MESSAGE: 'SET_MESSAGE',
ROWS_FETCHED: 'ROWS_FETCHED',
SELECTED_ROWS_COLS_CHANGED: 'SELECTED_ROWS_COLS_CHANGED',
SELECTED_ROWS_COLS_CELL_CHANGED: 'SELECTED_ROWS_COLS_CELL_CHANGED',
DATAGRID_CHANGED: 'DATAGRID_CHANGED',
HIGHLIGHT_ERROR: 'HIGHLIGHT_ERROR',
FOCUS_PANEL: 'FOCUS_PANEL',

View File

@@ -8,7 +8,7 @@
//////////////////////////////////////////////////////////////
import { Box, makeStyles } from '@material-ui/core';
import _ from 'lodash';
import React, {useState, useEffect, useCallback, useContext, useRef} from 'react';
import React, {useState, useEffect, useMemo, useContext, useRef} from 'react';
import ReactDataGrid, {Row, useRowSelection} from 'react-data-grid';
import LockIcon from '@material-ui/icons/Lock';
import EditIcon from '@material-ui/icons/Edit';
@@ -19,7 +19,7 @@ import clsx from 'clsx';
import { PgIconButton } from '../../../../../../static/js/components/Buttons';
import MapIcon from '@material-ui/icons/Map';
import { QueryToolEventsContext } from '../QueryToolComponent';
import PropTypes, { number } from 'prop-types';
import PropTypes from 'prop-types';
import gettext from 'sources/gettext';
export const ROWNUM_KEY = '$_pgadmin_rownum_key_$';
@@ -99,15 +99,22 @@ const useStyles = makeStyles((theme)=>({
}));
export const RowInfoContext = React.createContext();
export const DataGridExtrasContext = React.createContext();
function CustomRow(props) {
const rowRef = useRef();
const dataGridExtras = useContext(DataGridExtrasContext);
const rowInfoValue = {
rowIdx: props.rowIdx,
getCellElement: (colIdx)=>{
return rowRef.current?.querySelector(`.rdg-cell[aria-colindex="${colIdx+1}"]`);
}
};
if(!props.isRowSelected && props.selectedCellIdx > 0) {
dataGridExtras.onSelectedCellChange?.([props.row, props.viewportColumns?.[props.selectedCellIdx]]);
} else if(props.selectedCellIdx == 0) {
dataGridExtras.onSelectedCellChange?.(null);
}
return (
<RowInfoContext.Provider value={rowInfoValue}>
<Row ref={rowRef} {...props} />
@@ -116,7 +123,11 @@ function CustomRow(props) {
}
CustomRow.propTypes = {
rowIdx: number,
rowIdx: PropTypes.number,
isRowSelected: PropTypes.bool,
selectedCellIdx: PropTypes.number,
row: PropTypes.object,
viewportColumns: PropTypes.array,
};
function SelectAllHeaderRenderer(props) {
@@ -134,9 +145,14 @@ SelectAllHeaderRenderer.propTypes = {
onAllRowsSelectionChange: PropTypes.func,
};
function SelectableHeaderRenderer({column, selectedColumns, onSelectedColumnsChange}) {
function SelectableHeaderRenderer({column, selectedColumns, onSelectedColumnsChange, isCellSelected}) {
const classes = useStyles();
const eventBus = useContext(QueryToolEventsContext);
const dataGridExtras = useContext(DataGridExtrasContext);
if(isCellSelected) {
dataGridExtras.onSelectedCellChange?.(null);
}
const onClick = ()=>{
eventBus.fireEvent(QUERY_TOOL_EVENTS.FETCH_MORE_ROWS, true, ()=>{
@@ -176,6 +192,7 @@ SelectableHeaderRenderer.propTypes = {
column: PropTypes.object,
selectedColumns: PropTypes.objectOf(Set),
onSelectedColumnsChange: PropTypes.func,
isCellSelected: PropTypes.bool,
};
function setEditorFormatter(col) {
@@ -345,14 +362,6 @@ export default function QueryToolDataGrid({columns, rows, totalRowCount, dataCha
});
}, [dataChangeStore, selectedColumns]);
const onRowClick = useCallback((row, column)=>{
if(column.key === ROWNUM_KEY) {
onSelectedCellChange && onSelectedCellChange(null);
} else {
onSelectedCellChange && onSelectedCellChange([row, column]);
}
}, []);
function handleCopy() {
if (window.isSecureContext) {
eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_COPY_DATA);
@@ -360,22 +369,23 @@ export default function QueryToolDataGrid({columns, rows, totalRowCount, dataCha
}
return (
<ReactDataGrid
id="datagrid"
columns={readyColumns}
rows={rows}
className={classes.root}
headerRowHeight={40}
rowHeight={25}
mincolumnWidthBy={50}
enableCellSelect={true}
onRowClick={onRowClick}
onCopy={handleCopy}
components={{
rowRenderer: CustomRow,
}}
{...props}
/>
<DataGridExtrasContext.Provider value={useMemo(()=>({onSelectedCellChange}), [])}>
<ReactDataGrid
id="datagrid"
columns={readyColumns}
rows={rows}
className={classes.root}
headerRowHeight={40}
rowHeight={25}
mincolumnWidthBy={50}
enableCellSelect={true}
onCopy={handleCopy}
components={{
rowRenderer: CustomRow,
}}
{...props}
/>
</DataGridExtrasContext.Provider>
);
}

View File

@@ -136,7 +136,7 @@ export default function FilterDialog({onClose, onSave}) {
getInitData={getInitData}
schema={filterSchemaObj}
viewHelperProps={{
mode: 'edit',
mode: 'create',
}}
onSave={onSaveClick}
onClose={onClose}

View File

@@ -253,7 +253,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
eventBus.registerListener(QUERY_TOOL_EVENTS.DATAGRID_CHANGED, (isDirty)=>{
setDisableButton('save-data', !isDirty);
});
eventBus.registerListener(QUERY_TOOL_EVENTS.SELECTED_ROWS_COLS_CHANGED, (rows)=>{
eventBus.registerListener(QUERY_TOOL_EVENTS.SELECTED_ROWS_COLS_CELL_CHANGED, (rows)=>{
setDisableButton('delete-rows', !rows);
});
eventBus.registerListener(QUERY_TOOL_EVENTS.SET_FILTER_INFO, (canFilter, filterApplied)=>{

View File

@@ -730,51 +730,101 @@ export function ResultSet() {
const [selectedRows, setSelectedRows] = useState(new Set());
const [selectedColumns, setSelectedColumns] = useState(new Set());
const selectedCell = useRef([]);
const setSelectedCell = (val)=>selectedCell.current=val;
const setSelectedCell = (val)=>{
selectedCell.current=val;
fireRowsColsCellChanged();
};
const [rowsResetKey, setRowsResetKey] = useState(true);
rsu.current.setEventBus(eventBus);
const isDataChanged = ()=>{
return Boolean(_.size(dataChangeStore.updated) || _.size(dataChangeStore.added) || _.size(dataChangeStore.deleted));
};
const fireRowsColsCellChanged = ()=>{
eventBus.fireEvent(QUERY_TOOL_EVENTS.SELECTED_ROWS_COLS_CELL_CHANGED, selectedRows.size, selectedColumns.size, selectedCell.current?.length);
};
const executionStartCallback = async (query, explainObject, external=false, reconnect=false)=>{
/* Reset */
eventBus.fireEvent(QUERY_TOOL_EVENTS.HIGHLIGHT_ERROR, null);
dispatchDataChange({type: 'reset'});
setSelectedRows(new Set());
setSelectedColumns(new Set());
setLoaderText(gettext('Waiting for the query to complete...'));
let goForPoll = await rsu.current.startExecution(
query, explainObject,
()=>{
setColumns([]);
setRows([]);
},
{isQueryTool: queryToolCtx.params.is_query_tool, external: external, reconnect: reconnect}
);
if(goForPoll) {
rsu.current.pollForResult(
(procQueryData, procColumns, procRows)=>{
setQueryData(procQueryData);
setColumns(procColumns);
setRows(procRows);
setRowsResetKey(!rowsResetKey);
},
(planJson)=>{
/* No need to open if plan is empty */
if(!LayoutHelper.isTabOpen(queryToolCtx.docker, PANELS.EXPLAIN) && !planJson) {
return;
}
LayoutHelper.openTab(queryToolCtx.docker, {
id: PANELS.EXPLAIN,
title:gettext('Explain'),
content: <Explain plans={planJson} />,
closable: true,
}, PANELS.MESSAGES, 'after-tab', true);
},
const yesCallback = async ()=>{
/* Reset */
eventBus.fireEvent(QUERY_TOOL_EVENTS.HIGHLIGHT_ERROR, null);
dispatchDataChange({type: 'reset'});
setSelectedRows(new Set());
setSelectedColumns(new Set());
setLoaderText(gettext('Waiting for the query to complete...'));
let goForPoll = await rsu.current.startExecution(
query, explainObject,
()=>{
setColumns([]);
setRows([]);
},
{isQueryTool: queryToolCtx.params.is_query_tool, external: external, reconnect: reconnect}
);
if(goForPoll) {
rsu.current.pollForResult(
(procQueryData, procColumns, procRows)=>{
setQueryData(procQueryData);
setColumns(procColumns);
setRows(procRows);
setRowsResetKey(!rowsResetKey);
},
(planJson)=>{
/* No need to open if plan is empty */
if(!LayoutHelper.isTabOpen(queryToolCtx.docker, PANELS.EXPLAIN) && !planJson) {
return;
}
LayoutHelper.openTab(queryToolCtx.docker, {
id: PANELS.EXPLAIN,
title: gettext('Explain'),
content: <Explain plans={planJson} />,
closable: true,
}, PANELS.MESSAGES, 'after-tab', true);
},
()=>{
setColumns([]);
setRows([]);
}
);
}
};
if(isDataChanged()) {
queryToolCtx.modal.confirm(
gettext('Unsaved changes'),
gettext('The data has been modified, but not saved. Are you sure you wish to discard the changes?'),
yesCallback,
function() {
eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_END);
}
);
} else {
yesCallback();
}
};
const triggerFilter = async (include)=>{
if(_.isEmpty(selectedCell.current)) {
return;
}
setLoaderText(gettext('Applying the new filter...'));
try {
let data = {
[selectedCell.current[1].key]: selectedCell.current[0][selectedCell.current[1].key],
};
if(include) {
await rsu.current.includeFilter(data);
} else {
await rsu.current.excludeFilter(data);
}
setLoaderText('');
eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION);
} catch(err) {
eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, err, {
checkTransaction: true,
});
setLoaderText('');
}
};
@@ -831,6 +881,7 @@ export function ResultSet() {
setLoaderText('');
}
});
eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_INCLUDE_EXCLUDE_FILTER, triggerFilter);
}, []);
useEffect(()=>{
@@ -838,39 +889,10 @@ export function ResultSet() {
return ()=>{
eventBus.deregisterListener(QUERY_TOOL_EVENTS.EXECUTION_START, executionStartCallback);
};
}, [queryToolCtx.docker]);
}, [queryToolCtx.docker, dataChangeStore]);
useEffect(()=>{
const triggerFilter = async (include)=>{
if(_.isEmpty(selectedCell.current)) {
return;
}
setLoaderText(gettext('Applying the new filter...'));
try {
let data = {
[selectedCell.current[1].key]: selectedCell.current[0][selectedCell.current[1].key],
};
if(include) {
await rsu.current.includeFilter(data);
} else {
await rsu.current.excludeFilter(data);
}
setSelectedCell([]);
setLoaderText('');
eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION);
} catch(err) {
eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, err, {
checkTransaction: true,
});
setLoaderText('');
}
};
eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_INCLUDE_EXCLUDE_FILTER, triggerFilter);
return ()=>eventBus.deregisterListener(QUERY_TOOL_EVENTS.TRIGGER_INCLUDE_EXCLUDE_FILTER, triggerFilter);
}, [selectedCell.current]);
useEffect(()=>{
eventBus.fireEvent(QUERY_TOOL_EVENTS.SELECTED_ROWS_COLS_CHANGED, selectedRows.size, selectedColumns.size);
fireRowsColsCellChanged();
}, [selectedRows.size, selectedColumns.size]);
useEffect(()=>{
@@ -905,8 +927,7 @@ export function ResultSet() {
const warnSaveDataClose = ()=>{
// No changes.
if(!_.size(dataChangeStore.updated) && !_.size(dataChangeStore.added) && !_.size(dataChangeStore.deleted)
|| !queryToolCtx.preferences?.sqleditor.prompt_save_data_changes) {
if(!isDataChanged() || !queryToolCtx.preferences?.sqleditor.prompt_save_data_changes) {
eventBus.fireEvent(QUERY_TOOL_EVENTS.WARN_SAVE_TEXT_CLOSE);
return;
}

View File

@@ -104,9 +104,9 @@ export function ResultSetToolbar({containerRef, canEdit, totalRowCount}) {
eventBus.registerListener(QUERY_TOOL_EVENTS.DATAGRID_CHANGED, (isDirty)=>{
setDisableButton('save-data', !isDirty);
});
eventBus.registerListener(QUERY_TOOL_EVENTS.SELECTED_ROWS_COLS_CHANGED, (rows, cols)=>{
eventBus.registerListener(QUERY_TOOL_EVENTS.SELECTED_ROWS_COLS_CELL_CHANGED, (rows, cols, cells)=>{
setDisableButton('delete-rows', !rows);
setDisableButton('copy-rows', (!rows && !cols));
setDisableButton('copy-rows', (!rows && !cols && !cells));
});
}, []);

View File

@@ -73,7 +73,7 @@ export function StatusBar() {
eventBus.registerListener(QUERY_TOOL_EVENTS.ROWS_FETCHED, (fetched, total)=>{
setRowsCount([fetched||0, total||0]);
});
eventBus.registerListener(QUERY_TOOL_EVENTS.SELECTED_ROWS_COLS_CHANGED, (rows)=>{
eventBus.registerListener(QUERY_TOOL_EVENTS.SELECTED_ROWS_COLS_CELL_CHANGED, (rows)=>{
setSelectedRowsCount(rows);
});
eventBus.registerListener(QUERY_TOOL_EVENTS.DATAGRID_CHANGED, (_isDirty, dataChangeStore)=>{