mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Port data filter dialog to React. Fixes #7340
This commit is contained in:
parent
a86a8c6a34
commit
6e2ee9a21f
Binary file not shown.
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 45 KiB |
@ -22,6 +22,7 @@ Housekeeping
|
|||||||
| `Issue #6131 <https://redmine.postgresql.org/issues/6131>`_ - Port query tool to React.
|
| `Issue #6131 <https://redmine.postgresql.org/issues/6131>`_ - Port query tool to React.
|
||||||
| `Issue #6746 <https://redmine.postgresql.org/issues/6746>`_ - Improve the Kerberos Documentation.
|
| `Issue #6746 <https://redmine.postgresql.org/issues/6746>`_ - Improve the Kerberos Documentation.
|
||||||
| `Issue #7255 <https://redmine.postgresql.org/issues/7255>`_ - Ensure the database and schema restriction controls are not shown as a drop-down.
|
| `Issue #7255 <https://redmine.postgresql.org/issues/7255>`_ - Ensure the database and schema restriction controls are not shown as a drop-down.
|
||||||
|
| `Issue #7340 <https://redmine.postgresql.org/issues/7340>`_ - Port data filter dialog to React.
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
*********
|
*********
|
||||||
|
@ -15,5 +15,3 @@ in the edit grid window:
|
|||||||
.. image:: images/viewdata_filter_dialog.png
|
.. image:: images/viewdata_filter_dialog.png
|
||||||
:alt: View Data filter dialog window
|
:alt: View Data filter dialog window
|
||||||
:align: center
|
:align: center
|
||||||
|
|
||||||
.. note:: Use SHIFT + ENTER keys to apply filter.
|
|
@ -16,7 +16,9 @@ import SchemaView from 'sources/SchemaView';
|
|||||||
import 'wcdocker';
|
import 'wcdocker';
|
||||||
|
|
||||||
/* The entry point for rendering React based view in properties, called in node.js */
|
/* The entry point for rendering React based view in properties, called in node.js */
|
||||||
export function getUtilityView(schema, treeNodeInfo, actionType, formType, container, containerPanel, onSave, extraData, saveBtnName, urlBase, sqlHelpUrl, helpUrl) {
|
export function getUtilityView(schema, treeNodeInfo, actionType, formType, container, containerPanel,
|
||||||
|
onSave, extraData, saveBtnName, urlBase, sqlHelpUrl, helpUrl, isTabView=true) {
|
||||||
|
|
||||||
let serverInfo = treeNodeInfo && ('server' in treeNodeInfo) &&
|
let serverInfo = treeNodeInfo && ('server' in treeNodeInfo) &&
|
||||||
pgAdmin.Browser.serverInfo && pgAdmin.Browser.serverInfo[treeNodeInfo.server._id];
|
pgAdmin.Browser.serverInfo && pgAdmin.Browser.serverInfo[treeNodeInfo.server._id];
|
||||||
let inCatalog = treeNodeInfo && ('catalog' in treeNodeInfo);
|
let inCatalog = treeNodeInfo && ('catalog' in treeNodeInfo);
|
||||||
@ -90,6 +92,7 @@ export function getUtilityView(schema, treeNodeInfo, actionType, formType, conta
|
|||||||
onDataChange={()=>{/*This is intentional (SonarQube)*/}}
|
onDataChange={()=>{/*This is intentional (SonarQube)*/}}
|
||||||
confirmOnCloseReset={confirmOnReset}
|
confirmOnCloseReset={confirmOnReset}
|
||||||
hasSQL={false}
|
hasSQL={false}
|
||||||
|
isTabView={isTabView}
|
||||||
disableSqlHelp={sqlHelpUrl == undefined || sqlHelpUrl == ''}
|
disableSqlHelp={sqlHelpUrl == undefined || sqlHelpUrl == ''}
|
||||||
disableDialogHelp={helpUrl == undefined || helpUrl == ''}
|
disableDialogHelp={helpUrl == undefined || helpUrl == ''}
|
||||||
/>, container);
|
/>, container);
|
||||||
|
@ -594,16 +594,18 @@ def validate_filter(sid, did, obj_id):
|
|||||||
obj_id: Id of currently selected object
|
obj_id: Id of currently selected object
|
||||||
"""
|
"""
|
||||||
if request.data:
|
if request.data:
|
||||||
filter_sql = json.loads(request.data, encoding='utf-8')
|
filter_data = json.loads(request.data, encoding='utf-8')
|
||||||
else:
|
else:
|
||||||
filter_sql = request.args or request.form
|
filter_data = request.args or request.form
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Create object of SQLFilter class
|
# Create object of SQLFilter class
|
||||||
sql_filter_obj = SQLFilter(sid=sid, did=did, obj_id=obj_id)
|
sql_filter_obj = SQLFilter(sid=sid, did=did, obj_id=obj_id)
|
||||||
|
|
||||||
# Call validate_filter method to validate the SQL.
|
# Call validate_filter method to validate the SQL.
|
||||||
status, res = sql_filter_obj.validate_filter(filter_sql)
|
status, res = sql_filter_obj.validate_filter(filter_data['filter_sql'])
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=str(res))
|
||||||
except ObjectGone:
|
except ObjectGone:
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -207,13 +207,13 @@ export default class SQLEditor {
|
|||||||
// This is a callback function to show data when user click on menu item.
|
// This is a callback function to show data when user click on menu item.
|
||||||
showViewData(data, i) {
|
showViewData(data, i) {
|
||||||
const transId = commonUtils.getRandomInt(1, 9999999);
|
const transId = commonUtils.getRandomInt(1, 9999999);
|
||||||
showViewData.showViewData(this, pgBrowser, alertify, data, i, transId);
|
showViewData.showViewData(this, pgBrowser, data, i, transId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a callback function to show filtered data when user click on menu item.
|
// This is a callback function to show filtered data when user click on menu item.
|
||||||
showFilteredRow(data, i) {
|
showFilteredRow(data, i) {
|
||||||
const transId = commonUtils.getRandomInt(1, 9999999);
|
const transId = commonUtils.getRandomInt(1, 9999999);
|
||||||
showViewData.showViewData(this, pgBrowser, alertify, data, i, transId, true, this.preferences);
|
showViewData.showViewData(this, pgBrowser, data, i, transId, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a callback function to show query tool when user click on menu item.
|
// This is a callback function to show query tool when user click on menu item.
|
||||||
|
@ -9,21 +9,51 @@
|
|||||||
import gettext from '../../../../static/js/gettext';
|
import gettext from '../../../../static/js/gettext';
|
||||||
import url_for from '../../../../static/js/url_for';
|
import url_for from '../../../../static/js/url_for';
|
||||||
import {getDatabaseLabel, generateTitle} from './sqleditor_title';
|
import {getDatabaseLabel, generateTitle} from './sqleditor_title';
|
||||||
import CodeMirror from 'bundled_codemirror';
|
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
|
||||||
import * as SqlEditorUtils from 'sources/sqleditor_utils';
|
|
||||||
import $ from 'jquery';
|
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
import Notify from '../../../../static/js/helpers/Notifier';
|
import Notify from '../../../../static/js/helpers/Notifier';
|
||||||
|
import { isEmptyString } from 'sources/validators';
|
||||||
|
import { getUtilityView } from '../../../../browser/static/js/utility_view';
|
||||||
|
|
||||||
|
export default class DataFilterSchema extends BaseUISchema {
|
||||||
|
constructor(fieldOptions = {}) {
|
||||||
|
super({
|
||||||
|
filter_sql: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
this.fieldOptions = {
|
||||||
|
...fieldOptions,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get baseFields() {
|
||||||
|
return [{
|
||||||
|
id: 'filter_sql',
|
||||||
|
label: gettext('Data Filter'),
|
||||||
|
type: 'sql', isFullTab: true, cell: 'text',
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(state, setError) {
|
||||||
|
let errmsg = null;
|
||||||
|
|
||||||
|
if (isEmptyString(state.filter_sql)) {
|
||||||
|
errmsg = gettext('Data filter can not be empty.');
|
||||||
|
setError('filter_sql', errmsg);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
setError('filter_sql', null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function showViewData(
|
export function showViewData(
|
||||||
queryToolMod,
|
queryToolMod,
|
||||||
pgBrowser,
|
pgBrowser,
|
||||||
alertify,
|
|
||||||
connectionData,
|
connectionData,
|
||||||
treeIdentifier,
|
treeIdentifier,
|
||||||
transId,
|
transId,
|
||||||
filter=false,
|
filter=false
|
||||||
preferences=null
|
|
||||||
) {
|
) {
|
||||||
const node = pgBrowser.tree.findNodeByDomElement(treeIdentifier);
|
const node = pgBrowser.tree.findNodeByDomElement(treeIdentifier);
|
||||||
if (node === undefined || !node.getData()) {
|
if (node === undefined || !node.getData()) {
|
||||||
@ -49,27 +79,17 @@ export function showViewData(
|
|||||||
|
|
||||||
const gridUrl = generateUrl(transId, connectionData, node.getData(), parentData);
|
const gridUrl = generateUrl(transId, connectionData, node.getData(), parentData);
|
||||||
const queryToolTitle = generateViewDataTitle(pgBrowser, treeIdentifier);
|
const queryToolTitle = generateViewDataTitle(pgBrowser, treeIdentifier);
|
||||||
|
|
||||||
if(filter) {
|
if(filter) {
|
||||||
initFilterDialog(alertify, pgBrowser);
|
|
||||||
|
|
||||||
const validateUrl = generateFilterValidateUrl(node.getData(), parentData);
|
const validateUrl = generateFilterValidateUrl(node.getData(), parentData);
|
||||||
|
// Show Data Filter Dialog
|
||||||
let okCallback = function(sql) {
|
showFilterDialog(pgBrowser, node, queryToolMod, transId, gridUrl,
|
||||||
queryToolMod.launch(transId, gridUrl, false, queryToolTitle, null, sql);
|
queryToolTitle, validateUrl);
|
||||||
};
|
|
||||||
|
|
||||||
$.get(url_for('sqleditor.filter'),
|
|
||||||
function(data) {
|
|
||||||
alertify.filterDialog(gettext('Data Filter - %s', queryToolTitle), data, validateUrl, preferences, okCallback)
|
|
||||||
.resizeTo(pgBrowser.stdW.sm,pgBrowser.stdH.sm);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
queryToolMod.launch(transId, gridUrl, false, queryToolTitle);
|
queryToolMod.launch(transId, gridUrl, false, queryToolTitle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function retrieveNameSpaceName(parentData) {
|
export function retrieveNameSpaceName(parentData) {
|
||||||
if(!parentData) {
|
if(!parentData) {
|
||||||
return null;
|
return null;
|
||||||
@ -130,170 +150,26 @@ function generateFilterValidateUrl(nodeData, parentData) {
|
|||||||
return url_for('sqleditor.filter_validate', url_params);
|
return url_for('sqleditor.filter_validate', url_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
function initFilterDialog(alertify, pgBrowser) {
|
function showFilterDialog(pgBrowser, treeNodeInfo, queryToolMod, transId,
|
||||||
// Create filter dialog using alertify
|
gridUrl, queryToolTitle, validateUrl) {
|
||||||
let filter_editor = null;
|
|
||||||
if (!alertify.filterDialog) {
|
|
||||||
alertify.dialog('filterDialog', function factory() {
|
|
||||||
return {
|
|
||||||
main: function(title, message, validateUrl, preferences, okCallback) {
|
|
||||||
this.set('title', title);
|
|
||||||
this.message = message;
|
|
||||||
this.validateUrl = validateUrl;
|
|
||||||
this.okCallback = okCallback;
|
|
||||||
this.preferences = preferences;
|
|
||||||
},
|
|
||||||
|
|
||||||
setup:function() {
|
let schema = new DataFilterSchema();
|
||||||
return {
|
|
||||||
buttons:[{
|
|
||||||
text: '',
|
|
||||||
key: 112,
|
|
||||||
className: 'btn btn-primary-icon pull-left fa fa-question pg-alertify-icon-button',
|
|
||||||
attrs: {
|
|
||||||
name: 'dialog_help',
|
|
||||||
type: 'button',
|
|
||||||
label: gettext('Data Filter'),
|
|
||||||
'aria-label': gettext('Help'),
|
|
||||||
url: url_for('help.static', {
|
|
||||||
'filename': 'viewdata_filter.html',
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},{
|
|
||||||
text: gettext('Cancel'),
|
|
||||||
key: 27,
|
|
||||||
className: 'btn btn-secondary fa fa-times pg-alertify-button',
|
|
||||||
},{
|
|
||||||
text: gettext('OK'),
|
|
||||||
key: null,
|
|
||||||
className: 'btn btn-primary fa fa-check pg-alertify-button',
|
|
||||||
}],
|
|
||||||
options: {
|
|
||||||
modal: 0,
|
|
||||||
resizable: true,
|
|
||||||
maximizable: false,
|
|
||||||
pinnable: false,
|
|
||||||
autoReset: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
build: function() {
|
|
||||||
var that = this;
|
|
||||||
alertify.pgDialogBuild.apply(that);
|
|
||||||
|
|
||||||
// Set the tooltip of OK
|
// Register dialog panel
|
||||||
$(that.__internal.buttons[2].element).attr('title', gettext('Use SHIFT + ENTER to apply filter...'));
|
pgBrowser.Node.registerUtilityPanel();
|
||||||
|
var panel = pgBrowser.Node.addUtilityPanel(pgBrowser.stdW.md),
|
||||||
|
j = panel.$container.find('.obj_properties').first();
|
||||||
|
panel.title(gettext('Data Filter - %s', queryToolTitle));
|
||||||
|
panel.focus();
|
||||||
|
|
||||||
// For sort/filter dialog we capture the keypress event
|
var helpUrl = url_for('help.static', {'filename': 'viewdata_filter.html'});
|
||||||
// and on "shift + enter" we clicked on "OK" button.
|
|
||||||
$(that.elements.body).on('keypress', function(evt) {
|
|
||||||
if (evt.shiftKey && evt.keyCode == 13) {
|
|
||||||
that.__internal.buttons[2].element.click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
prepare:function() {
|
|
||||||
var that = this,
|
|
||||||
$content = $(this.message),
|
|
||||||
$sql_filter = $content.find('#sql_filter');
|
|
||||||
|
|
||||||
$(this.elements.header).attr('data-title', this.get('title'));
|
let okCallback = function() {
|
||||||
$(this.elements.body.childNodes[0]).addClass(
|
queryToolMod.launch(transId, gridUrl, false, queryToolTitle, null, schema._sessData.filter_sql);
|
||||||
'dataview_filter_dialog'
|
};
|
||||||
);
|
|
||||||
|
|
||||||
this.setContent($content.get(0));
|
getUtilityView(schema, treeNodeInfo, 'create', 'dialog', j[0], panel,
|
||||||
// Disable OK button
|
okCallback, [], 'OK', validateUrl, undefined, helpUrl, false);
|
||||||
that.__internal.buttons[2].element.disabled = true;
|
|
||||||
|
|
||||||
// Apply CodeMirror to filter text area.
|
|
||||||
filter_editor = this.filter_obj = CodeMirror.fromTextArea($sql_filter.get(0), {
|
|
||||||
lineNumbers: true,
|
|
||||||
mode: 'text/x-pgsql',
|
|
||||||
extraKeys: pgBrowser.editor_shortcut_keys,
|
|
||||||
indentWithTabs: !that.preferences.use_spaces,
|
|
||||||
indentUnit: that.preferences.tab_size,
|
|
||||||
tabSize: that.preferences.tab_size,
|
|
||||||
lineWrapping: that.preferences.wrap_code,
|
|
||||||
autoCloseBrackets: that.preferences.insert_pair_brackets,
|
|
||||||
matchBrackets: that.preferences.brace_matching,
|
|
||||||
screenReaderLabel: gettext('Filter SQL'),
|
|
||||||
});
|
|
||||||
|
|
||||||
let sql_font_size = SqlEditorUtils.calcFontSize(that.preferences.sql_font_size);
|
|
||||||
$(this.filter_obj.getWrapperElement()).css('font-size', sql_font_size);
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
// Set focus on editor
|
|
||||||
that.filter_obj.refresh();
|
|
||||||
that.filter_obj.focus();
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
that.filter_obj.on('change', function() {
|
|
||||||
if (that.filter_obj.getValue() !== '') {
|
|
||||||
that.__internal.buttons[2].element.disabled = false;
|
|
||||||
} else {
|
|
||||||
that.__internal.buttons[2].element.disabled = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
callback: function(closeEvent) {
|
|
||||||
|
|
||||||
if (closeEvent.button.text == gettext('OK')) {
|
|
||||||
var sql = this.filter_obj.getValue();
|
|
||||||
var that = this;
|
|
||||||
closeEvent.cancel = true; // Do not close dialog
|
|
||||||
|
|
||||||
// Make ajax call to include the filter by selection
|
|
||||||
$.ajax({
|
|
||||||
url: that.validateUrl,
|
|
||||||
method: 'POST',
|
|
||||||
async: false,
|
|
||||||
contentType: 'application/json',
|
|
||||||
data: JSON.stringify(sql),
|
|
||||||
})
|
|
||||||
.done(function(res) {
|
|
||||||
if (res.data.status) {
|
|
||||||
that.okCallback(sql);
|
|
||||||
that.close(); // Close the dialog
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Notify.alert(
|
|
||||||
gettext('Validation Error'),
|
|
||||||
gettext(res.data.result),
|
|
||||||
function(){
|
|
||||||
filter_editor.focus();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.fail(function(e) {
|
|
||||||
if (e.status === 410){
|
|
||||||
pgBrowser.report_error(gettext('Error filtering rows - %s.', e.statusText), e.responseJSON.errormsg);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Notify.alert(
|
|
||||||
gettext('Validation Error'),
|
|
||||||
e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
} else if(closeEvent.index == 0) {
|
|
||||||
/* help Button */
|
|
||||||
closeEvent.cancel = true;
|
|
||||||
pgBrowser.showHelp(
|
|
||||||
closeEvent.button.element.name,
|
|
||||||
closeEvent.button.element.getAttribute('url'),
|
|
||||||
null, null
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasServerOrDatabaseConfiguration(parentData) {
|
function hasServerOrDatabaseConfiguration(parentData) {
|
||||||
|
@ -217,6 +217,7 @@ module.exports = {
|
|||||||
'tools': path.join(__dirname, './pgadmin/tools/'),
|
'tools': path.join(__dirname, './pgadmin/tools/'),
|
||||||
'pgadmin.user_management.current_user': regressionDir + '/javascript/fake_current_user',
|
'pgadmin.user_management.current_user': regressionDir + '/javascript/fake_current_user',
|
||||||
'pgadmin.browser.constants': regressionDir + '/javascript/fake_constants',
|
'pgadmin.browser.constants': regressionDir + '/javascript/fake_constants',
|
||||||
|
'pgadmin.help': path.join(__dirname, './pgadmin/help/static/js/help'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user