mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
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:
committed by
Akshay Joshi
parent
4c2e309bdc
commit
64ffed0ddb
@@ -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,
|
||||
|
||||
@@ -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}/>
|
||||
});
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ export default function FilterDialog({onClose, onSave}) {
|
||||
getInitData={getInitData}
|
||||
schema={filterSchemaObj}
|
||||
viewHelperProps={{
|
||||
mode: 'edit',
|
||||
mode: 'create',
|
||||
}}
|
||||
onSave={onSaveClick}
|
||||
onClose={onClose}
|
||||
|
||||
@@ -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)=>{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -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)=>{
|
||||
|
||||
Reference in New Issue
Block a user