diff --git a/web/pgadmin/browser/static/js/toolbar.js b/web/pgadmin/browser/static/js/toolbar.js
index fc915026e..af3ad7d13 100644
--- a/web/pgadmin/browser/static/js/toolbar.js
+++ b/web/pgadmin/browser/static/js/toolbar.js
@@ -116,7 +116,7 @@ export function initializeToolbar(panel, wcDocker) {
else if ('name' in data && data.name === gettext('View Data'))
pgAdmin.Tools.SQLEditor.showViewData({mnuid: 3}, pgAdmin.Browser.tree.selected());
else if ('name' in data && data.name === gettext('Filtered Rows'))
- pgAdmin.Tools.SQLEditor.show_filtered_row({mnuid: 4}, pgAdmin.Browser.tree.selected());
+ pgAdmin.Tools.SQLEditor.showFilteredRow({mnuid: 4}, pgAdmin.Browser.tree.selected());
else if ('name' in data && data.name === gettext('Search objects'))
pgAdmin.SearchObjects.show_search_objects('', pgAdmin.Browser.tree.selected());
else if ('name' in data && data.name === gettext('PSQL Tool')){
diff --git a/web/pgadmin/static/js/Explain/Analysis.jsx b/web/pgadmin/static/js/Explain/Analysis.jsx
index fa822b156..bf4553dd2 100644
--- a/web/pgadmin/static/js/Explain/Analysis.jsx
+++ b/web/pgadmin/static/js/Explain/Analysis.jsx
@@ -24,16 +24,19 @@ const useStyles = makeStyles((theme)=>({
borderBottom: '2px dashed '+theme.palette.primary.main,
},
level2: {
- backgroundColor: '#fff',
- color: '#000',
+ backgroundColor: theme.otherVars.explain.sev2.bg,
+ color: theme.otherVars.explain.sev2.color,
},
level3: {
- backgroundColor: '#fff',
- color: '#000',
+ backgroundColor: theme.otherVars.explain.sev3.bg,
+ color: theme.otherVars.explain.sev3.color,
},
level4: {
- backgroundColor: '#fff',
- color: '#000',
+ backgroundColor: theme.otherVars.explain.sev4.bg,
+ color: theme.otherVars.explain.sev4.color,
+ },
+ textRight: {
+ textAlign: 'right',
},
}));
@@ -43,15 +46,6 @@ function getRowClassname(data, collapseParent) {
if(data['Plans']?.length > 0) {
className.push(classes.collapsible);
}
- if(!_.isEmpty(data['exclusive_flag'])) {
- className.push(classes['level'+data['exclusive_flag']]);
- }
- if(!_.isEmpty(data['inclusive_flag'])) {
- className.push(classes['level'+data['inclusive_flag']]);
- }
- if(!_.isEmpty(data['rowsx_flag'])) {
- className.push(classes['level'+data['rowsx_flag']]);
- }
if(collapseParent) {
className.push(classes.collapseParent);
}
@@ -61,10 +55,10 @@ function getRowClassname(data, collapseParent) {
function NodeText({displayText, extraInfo}) {
return (
<>
- {displayText}
- {extraInfo?.length > 0 &&
+ {displayText}
+ {extraInfo?.length > 0 &&
{extraInfo.map((item, i)=>{
- return - {HTMLReactParse(item)}
;
+ return - {HTMLReactParse(item)}
;
})}
}
>);
@@ -76,6 +70,7 @@ NodeText.propTypes = {
function ExplainRow({row, show, activeExId, setActiveExId, collapsedExId, toggleCollapseExId}) {
let data = row['data'];
+ const classes = useStyles();
const exId = `pga_ex_${data['level'].join('_')}`;
const parentExId = `pga_ex_${data['parent_node']}`;
const collapsed = collapsedExId.findIndex((v)=>parentExId.startsWith(v)) > -1;
@@ -94,29 +89,28 @@ function ExplainRow({row, show, activeExId, setActiveExId, collapsedExId, toggle
|
- {data['_serial']}. |
+ {data['_serial']}. |
|
-
+ |
{data['exclusive'] && (data['exclusive']+' ms')}
|
-
+ |
{data['inclusive'] && (data['inclusive']+' ms')}
|
- {!_.isUndefined(data['rowsx_flag'])
- && (data['rowsx_direction'] == 'positive' ? '↑' : '↓')
- } |
-
- {data['rowsx']}
+ |
+ {!_.isUndefined(data['rowsx_flag'])
+ && (data['rowsx_direction'] == 'positive' ? <>↑> : <>↓>)
+ } {data['rowsx']}
|
-
+ |
{data['Actual Rows']}
|
-
+ |
{data['Plan Rows']}
|
-
+ |
{data['Actual Loops']}
|
@@ -130,7 +124,9 @@ ExplainRow.propTypes = {
_serial: PropTypes.number,
parent_node: PropTypes.number,
exclusive: PropTypes.number,
+ exclusive_flag: PropTypes.string,
inclusive: PropTypes.number,
+ inclusive_flag: PropTypes.string,
rowsx_direction: PropTypes.string,
rowsx: PropTypes.number,
rowsx_flag: PropTypes.number,
@@ -178,7 +174,8 @@ export default function Analysis({explainTable}) {
|
-
+ |
|
diff --git a/web/pgadmin/static/js/Explain/Graphical.jsx b/web/pgadmin/static/js/Explain/Graphical.jsx
index 2c7ba2101..be722f4b6 100644
--- a/web/pgadmin/static/js/Explain/Graphical.jsx
+++ b/web/pgadmin/static/js/Explain/Graphical.jsx
@@ -250,6 +250,7 @@ function PlanContent({plan, pXpos, pYpos, ...props}) {
strokeWidth={1.2}
fill="gray"
fillOpacity={0.2}
+ pointerEvents="none"
/>
({
tabPanel: {
padding: 0,
backgroundColor: theme.palette.background.default,
- }
+ },
}));
// Some predefined constants used to calculate image location and its border
@@ -261,7 +261,11 @@ function parsePlan(data, ctx) {
totalCost = data['Total Cost'];
if (startCost != undefined && totalCost != undefined) {
arrowSize = Math.round(Math.log((startCost + totalCost) / 2 + startCost));
- arrowSize = arrowSize < 1 ? 1 : arrowSize > 10 ? 10 : arrowSize;
+ if (arrowSize < 1) {
+ arrowSize = 1;
+ } else if (arrowSize > 10) {
+ arrowSize = 10;
+ }
}
data['arr_id'] = _.uniqueId('arr');
ctx.arrows[data['arr_id']] = arrowSize;
@@ -292,15 +296,21 @@ function parsePlan(data, ctx) {
if ('Actual Total Time' in data && 'Actual Loops' in data) {
data['inclusive'] = Math.ceil10(
- data['Actual Total Time'] * data['Actual Loops'], -3
+ data['Actual Total Time'], -3
);
data['exclusive'] = data['inclusive'];
data['inclusive_factor'] = data['inclusive'] / (
data['total_time'] || data['Actual Total Time']
);
- data['inclusive_flag'] = data['inclusive_factor'] <= 0.1 ? '1' :
- data['inclusive_factor'] < 0.5 ? '2' :
- data['inclusive_factor'] <= 0.9 ? '3' : '4';
+ if (data['inclusive_factor'] <= 0.1) {
+ data['inclusive_flag'] = '1';
+ } else if (data['inclusive_factor'] < 0.5) {
+ data['inclusive_flag'] = '2';
+ } else if (data['inclusive_factor'] <= 0.9) {
+ data['inclusive_flag'] = '3';
+ } else {
+ data['inclusive_flag'] = '4';
+ }
}
if ('Actual Rows' in data && 'Plan Rows' in data) {
@@ -316,10 +326,20 @@ function parsePlan(data, ctx) {
);
data['rowsx_direction'] = 'positive';
}
- data['rowsx_flag'] = data['rowsx'] <= 10 ? '1' : (
- data['rowsx'] <= 100 ? '2' : (data['rowsx'] <= 1000 ? '3' : '4')
- );
- data['rowsx'] = Math.ceil10(data['rowsx'], -2);
+ if (data['rowsx'] <= 10) {
+ data['rowsx_flag'] = '1';
+ } else if (data['rowsx'] <= 100 ) {
+ data['rowsx_flag'] = '2';
+ } else if (data['rowsx'] <= 1000 ) {
+ data['rowsx_flag'] = '3';
+ } else {
+ data['rowsx_flag'] = '4';
+ }
+ if('loops' in data) {
+ data['rowsx'] = Math.ceil10(data['rowsx'] / data['loops'] || 1, -2);
+ } else {
+ data['rowsx'] = Math.ceil10(data['rowsx'], -2);
+ }
}
// Start calculating xpos, ypos, width and height for child plans if any
@@ -337,6 +357,7 @@ function parsePlan(data, ctx) {
ypos: ypos,
total_time: data['total_time'] || data['Actual Total Time'],
parent_node: lvl.join('_'),
+ loops: data['Actual Loops']
}, ctx);
if (maxChildWidth < plan.width) {
@@ -344,7 +365,7 @@ function parsePlan(data, ctx) {
}
if ('exclusive' in data) {
- if (plan.inclusive) {
+ if (plan.inclusive < data['exclusive']) {
data['exclusive'] -= plan.inclusive;
}
}
@@ -361,6 +382,11 @@ function parsePlan(data, ctx) {
plans.push(plan);
idx++;
});
+ } else{
+ if('loops' in data && 'exclusive' in data) {
+ data['inclusive'] = Math.ceil10(data['Actual Total Time'] / data['loops'] || 1, -3);
+ data['exclusive'] = data['inclusive'];
+ }
}
if ('exclusive' in data) {
@@ -368,9 +394,15 @@ function parsePlan(data, ctx) {
data['exclusive_factor'] = (
data['exclusive'] / (data['total_time'] || data['Actual Total Time'])
);
- data['exclusive_flag'] = data['exclusive_factor'] <= 0.1 ? '1' :
- data['exclusive_factor'] < 0.5 ? '2' :
- data['exclusive_factor'] <= 0.9 ? '3' : '4';
+ if (data['exclusive_factor'] <= 0.1) {
+ data['exclusive_flag'] = '1';
+ } else if (data['exclusive_factor'] < 0.5) {
+ data['exclusive_flag'] = '2';
+ } else if (data['exclusive_factor'] <= 0.9) {
+ data['exclusive_flag'] = '3';
+ } else {
+ data['exclusive_flag'] = '4';
+ }
}
// Final Width and Height of current node
@@ -389,6 +421,7 @@ function parsePlanData(data, ctx) {
...data['Plan'],
xpos: 0,
ypos: 0,
+ loops: 1,
}, ctx);
retPlan['Plan'] = plan;
retPlan['xpos'] = 0;
@@ -431,20 +464,23 @@ export default function Explain({plans=[]}) {
const classes = useStyles();
const [tabValue, setTabValue] = React.useState(0);
- let ctx = React.useRef({
- totalNodes: 0,
- totalDownloadedNodes: 0,
- isDownloaded: 0,
- explainTable: {
- rows: [],
- statistics: {
- tables: {},
- nodes: {},
+ let ctx = React.useRef({});
+ let planData = React.useMemo(()=>{
+ ctx.current = {
+ totalNodes: 0,
+ totalDownloadedNodes: 0,
+ isDownloaded: 0,
+ explainTable: {
+ rows: [],
+ statistics: {
+ tables: {},
+ nodes: {},
+ },
},
- },
- arrows: {},
- });
- let planData = React.useMemo(()=>(plans && parsePlanData(plans[0], ctx.current)), [plans]);
+ arrows: {},
+ };
+ return plans && parsePlanData(plans[0], ctx.current);
+ }, [plans]);
if(_.isEmpty(plans)) {
return
diff --git a/web/pgadmin/static/js/Theme/index.jsx b/web/pgadmin/static/js/Theme/index.jsx
index 9124743ec..402e82439 100644
--- a/web/pgadmin/static/js/Theme/index.jsx
+++ b/web/pgadmin/static/js/Theme/index.jsx
@@ -19,6 +19,7 @@ import CustomPropTypes from '../custom_prop_types';
import getStandardTheme from './standard';
import getDarkTheme from './dark';
import getHightContrastTheme from './high_contrast';
+import { CssBaseline } from '@material-ui/core';
/* Common settings across all themes */
let basicSettings = createMuiTheme();
@@ -278,6 +279,19 @@ function getFinalTheme(baseTheme) {
return createMuiTheme({
mixins: mixins,
overrides: {
+ MuiCssBaseline: {
+ '@global': {
+ ul: {
+ margin: 0,
+ padding: 0,
+ },
+ li: {
+ listStyle: 'none',
+ margin: 0,
+ padding: 0,
+ }
+ },
+ },
MuiOutlinedInput: {
root: {
'&.Mui-disabled .MuiOutlinedInput-notchedOutline': {
@@ -535,6 +549,7 @@ export default function Theme(props) {
}, []);
return (
+
{props.children}
);
@@ -561,6 +576,7 @@ export const commonTableStyles = makeStyles((theme)=>({
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
userSelect: 'text',
+ maxWidth: '250px',
'&:first-of-type':{
borderLeft: 'none',
},
diff --git a/web/pgadmin/static/js/Theme/standard.js b/web/pgadmin/static/js/Theme/standard.js
index 1efe55300..8b8b1fe41 100644
--- a/web/pgadmin/static/js/Theme/standard.js
+++ b/web/pgadmin/static/js/Theme/standard.js
@@ -105,6 +105,20 @@ export default function(basicSettings) {
qtDatagridSelectFg: '#222',
cardHeaderBg: '#fff',
emptySpaceBg: '#ebeef3',
+ explain: {
+ sev2: {
+ color: '#222222',
+ bg: '#FFEE88',
+ },
+ sev3: {
+ color: '#FFFFFF',
+ bg: '#EE8800'
+ },
+ sev4: {
+ color: '#FFFFFF',
+ bg: '#880000'
+ },
+ }
}
});
}
diff --git a/web/pgadmin/static/js/components/CodeMirror.jsx b/web/pgadmin/static/js/components/CodeMirror.jsx
index de891d62b..218efc8a3 100644
--- a/web/pgadmin/static/js/components/CodeMirror.jsx
+++ b/web/pgadmin/static/js/components/CodeMirror.jsx
@@ -405,6 +405,22 @@ export default function CodeMirror({currEditor, name, value, options, events, re
}
Object.keys(events||{}).forEach((eventName)=>{
+ if(eventName === 'change') {
+ let timeoutId;
+ const change = (...args)=>{
+ /* In case of indent, change is triggered for each line */
+ /* This can be avoided and taking only the latest */
+ if(timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ timeoutId = setTimeout(()=>{
+ events[eventName](...args);
+ timeoutId = null;
+ }, 0);
+ };
+ editor.current.on(eventName, change);
+ return;
+ }
editor.current.on(eventName, events[eventName]);
});
editor.current.on('drop', handleDrop);
diff --git a/web/pgadmin/static/js/components/ExternalIcon.jsx b/web/pgadmin/static/js/components/ExternalIcon.jsx
index 3fa91b895..249feee7d 100644
--- a/web/pgadmin/static/js/components/ExternalIcon.jsx
+++ b/web/pgadmin/static/js/components/ExternalIcon.jsx
@@ -1,5 +1,6 @@
import React from 'react';
import QueryToolSvg from '../../img/fonticon/query_tool.svg?svgr';
+import ViewDataSvg from '../../img/fonticon/view_data.svg?svgr';
import SaveDataSvg from '../../img/fonticon/save_data_changes.svg?svgr';
import PasteSvg from '../../img/content_paste.svg?svgr';
import FilterSvg from '../../img/filter_alt_black.svg?svgr';
@@ -22,16 +23,44 @@ ExternalIcon.propTypes = {
Icon: PropTypes.elementType.isRequired,
};
-export const QueryToolIcon = ()=>;
-export const SaveDataIcon = ()=>;
-export const PasteIcon = ()=>;
-export const FilterIcon = ()=>;
-export const CommitIcon = ()=>;
-export const RollbackIcon = ()=>;
-export const ClearIcon = ()=>;
-export const ConnectedIcon = ()=>;
-export const DisonnectedIcon = ()=>;
-export const RegexIcon = ()=>;
-export const FormatCaseIcon = ()=>;
-export const ExpandDialogIcon = ()=>;
-export const MinimizeDialogIcon = ()=>;
+export const QueryToolIcon = ({style})=>;
+QueryToolIcon.propTypes = {style: PropTypes.object};
+
+export const ViewDataIcon = ({style})=>;
+ViewDataIcon.propTypes = {style: PropTypes.object};
+
+export const SaveDataIcon = ({style})=>;
+SaveDataIcon.propTypes = {style: PropTypes.object};
+
+export const PasteIcon = ({style})=>;
+PasteIcon.propTypes = {style: PropTypes.object};
+
+export const FilterIcon = ({style})=>;
+FilterIcon.propTypes = {style: PropTypes.object};
+
+export const CommitIcon = ({style})=>;
+CommitIcon.propTypes = {style: PropTypes.object};
+
+export const RollbackIcon = ({style})=>;
+RollbackIcon.propTypes = {style: PropTypes.object};
+
+export const ClearIcon = ({style})=>;
+ClearIcon.propTypes = {style: PropTypes.object};
+
+export const ConnectedIcon = ({style})=>;
+ConnectedIcon.propTypes = {style: PropTypes.object};
+
+export const DisonnectedIcon = ({style})=>;
+DisonnectedIcon.propTypes = {style: PropTypes.object};
+
+export const RegexIcon = ({style})=>;
+RegexIcon.propTypes = {style: PropTypes.object};
+
+export const FormatCaseIcon = ({style})=>;
+FormatCaseIcon.propTypes = {style: PropTypes.object};
+
+export const ExpandDialogIcon = ({style})=>;QueryToolIcon.propTypes = {style: PropTypes.object};
+ExpandDialogIcon.propTypes = {style: PropTypes.object};
+
+export const MinimizeDialogIcon = ({style})=>;
+MinimizeDialogIcon.propTypes = {style: PropTypes.object};
diff --git a/web/pgadmin/static/js/components/Menu.jsx b/web/pgadmin/static/js/components/Menu.jsx
index 02b3e0b3c..84f51f65c 100644
--- a/web/pgadmin/static/js/components/Menu.jsx
+++ b/web/pgadmin/static/js/components/Menu.jsx
@@ -1,5 +1,5 @@
import { makeStyles } from '@material-ui/styles';
-import React from 'react';
+import React, { useRef } from 'react';
import CheckIcon from '@material-ui/icons/Check';
import PropTypes from 'prop-types';
@@ -34,6 +34,9 @@ const useStyles = makeStyles((theme)=>({
color: theme.palette.primary.contrastText,
}
},
+ checkIcon: {
+ width: '1.3rem',
+ },
hideCheck: {
visibility: 'hidden',
},
@@ -70,7 +73,7 @@ export const PgMenuItem = applyStatics(MenuItem)(({hasCheck=false, checked=false
};
}
return ;
@@ -84,3 +87,32 @@ PgMenuItem.propTypes = {
children: CustomPropTypes.children,
onClick: PropTypes.func,
};
+
+export function usePgMenuGroup() {
+ const [openMenuName, setOpenMenuName] = React.useState(null);
+ const prevMenuOpenIdRef = useRef(null);
+
+ const toggleMenu = React.useCallback((e)=>{
+ setOpenMenuName(()=>{
+ return prevMenuOpenIdRef.current == e.currentTarget?.name ? null : e.currentTarget?.name;
+ });
+ prevMenuOpenIdRef.current = null;
+ }, []);
+
+ const handleMenuClose = React.useCallback(()=>{
+ /* We have no way here to know if the menu was closed using menu button or not
+ We will keep the last menu name ref for sometime so that the menu does not
+ open again if menu button is clicked to close the menu */
+ prevMenuOpenIdRef.current = openMenuName;
+ setTimeout(()=>{
+ prevMenuOpenIdRef.current = null;
+ }, 300);
+ setOpenMenuName(null);
+ }, [openMenuName]);
+
+ return {
+ openMenuName: openMenuName,
+ toggleMenu: toggleMenu,
+ onMenuClose: handleMenuClose,
+ };
+}
diff --git a/web/pgadmin/static/js/helpers/EventBus.js b/web/pgadmin/static/js/helpers/EventBus.js
index 9a6906429..848d2822c 100644
--- a/web/pgadmin/static/js/helpers/EventBus.js
+++ b/web/pgadmin/static/js/helpers/EventBus.js
@@ -20,7 +20,7 @@ export default class EventBus {
if(e.event === event) {
return e.callback.toString()!=callback.toString();
}
- return e.event!=event && e.callback.toString()!=callback.toString();
+ return true;
});
} else {
this._eventListeners = this._eventListeners.filter((e)=>e.event!=event);
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx
index 6d2af6045..a2b000bed 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx
@@ -422,7 +422,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
eventBus.current.deregisterListener(e[0], e[1]);
});
};
- }, [qtState]);
+ }, [qtState.params, qtState.current_file]);
useEffect(()=>{
/* Fire query change so that title changes to latest */
@@ -602,11 +602,12 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
onResetLayout={onResetLayout}
docker={docker.current}
/>
-
+ {React.useMemo(()=>(
+ ), [containerRef.current, onManageMacros, onFilterClick])}
docker.current=obj}
defaultLayout={defaultLayout}
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/Editors.jsx b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/Editors.jsx
index b48152b11..48739c644 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/Editors.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/Editors.jsx
@@ -170,8 +170,8 @@ export function TextEditor({row, column, onRowChange, onClose}) {
}, []);
const onOK = ()=>{
- if(column.is_array && !isValidArray(value)) {
- console.error(gettext('Arrays must start with "{" and end with "}"'));
+ if(column.is_array && !isValidArray(localVal)) {
+ Notifier.error(gettext('Arrays must start with "{" and end with "}"'));
} else {
let columnVal = textColumnFinalVal(localVal, column);
onRowChange({ ...row, [column.key]: columnVal}, true);
@@ -205,14 +205,14 @@ TextEditor.propTypes = EditorPropTypes;
export function NumberEditor({row, column, onRowChange, onClose}) {
const classes = useStyles();
const value = row[column.key] ?? '';
- const onBlur = ()=>{
+ const isValidData = ()=>{
if(!column.is_array && isNaN(value)){
Notifier.error(gettext('Please enter a valid number'));
- return;
+ return false;
} else if(column.is_array) {
if(!isValidArray(value)) {
Notifier.error(gettext('Arrays must start with "{" and end with "}"'));
- return;
+ return false;
}
let checkVal = value.trim().slice(1, -1);
if(checkVal == '') {
@@ -223,16 +223,25 @@ export function NumberEditor({row, column, onRowChange, onClose}) {
for (const val of checkVal) {
if(isNaN(val)) {
Notifier.error(gettext('Arrays must start with "{" and end with "}"'));
- return;
+ return false;
}
}
}
- onClose(column.can_edit ? true : false);
+ return true;
+ };
+ const onBlur = ()=>{
+ if(isValidData()) {
+ onClose(column.can_edit ? true : false);
+ return true;
+ }
+ return false;
};
const onKeyDown = (e)=>{
if(e.code == 'Tab') {
e.preventDefault();
- onBlur();
+ if(!onBlur()) {
+ e.stopPropagation();
+ }
}
};
return (
@@ -245,7 +254,7 @@ export function NumberEditor({row, column, onRowChange, onClose}) {
onRowChange({ ...row, [column.key]: e.target.value });
}
}}
- // onBlur={onBlur}
+ onBlur={onBlur}
onKeyDown={onKeyDown}
/>
);
@@ -269,7 +278,7 @@ export function CheckboxEditor({row, column, onRowChange, onClose}) {
};
const onBlur = ()=>{onClose(true);};
let className = 'checked';
- if(!value) {
+ if(!value && value != null) {
className = 'unchecked';
} else if(value == null){
className = 'intermediate';
@@ -323,7 +332,7 @@ export function JsonTextEditor({row, column, onRowChange, onClose}) {
value={localVal}
options={{
onChange: onChange,
- onError: (error)=>console.error('Invalid Json: ' + error.message.split(':')[0]),
+ onError: (error)=>Notifier.error('Invalid Json: ' + error.message.split(':')[0]),
}}
className={'jsoneditor-div'}
/>
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/dialogs/NewConnectionDialog.jsx b/web/pgadmin/tools/sqleditor/static/js/components/dialogs/NewConnectionDialog.jsx
index fc35c0964..f0763d543 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/dialogs/NewConnectionDialog.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/dialogs/NewConnectionDialog.jsx
@@ -63,6 +63,9 @@ class NewConnectionSchema extends BaseUISchema {
.then(({data: respData})=>{
let groupedOptions = [];
_.forIn(respData.data.result.server_list, (v, k)=>{
+ if(v.length == 0) {
+ return;
+ }
/* initial selection */
_.find(v, (o)=>o.value==obj.params.sid).selected = true;
groupedOptions.push({
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/ConnectionBar.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/ConnectionBar.jsx
index da1493e69..cf9db036a 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/sections/ConnectionBar.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/ConnectionBar.jsx
@@ -106,15 +106,15 @@ export function ConnectionBar({connected, connecting, connectionStatus, connecti
>
- {connecting && '(Obtaining connection)'}{connTitle}
+ {connecting && gettext('(Obtaining connection)')}{connTitle}
{queryToolCtx.params.is_query_tool && }
- } onClick={onNewQueryToolClick}/>
+ } onClick={onNewQueryToolClick}/>
- } onClick={onResetLayout}/>
+ } onClick={onResetLayout}/>
0) {
infoList.push(gettext('3D geometries not rendered.'));
@@ -116,7 +116,7 @@ function parseData(rows, columns, column) {
'geoJSONs': [],
'selectedSRID': 0,
'getPopupContent': undefined,
- 'infoList': ['Empty row.'],
+ 'infoList': [gettext('Empty row.')],
};
}
@@ -294,19 +294,19 @@ function TheMap({data}) {
<>
{data.selectedSRID === 4326 &&
-
+
-
+
-
+
-
+
-
+
-
+
{
- setMenuOpenId(e.currentTarget.name);
- }, []);
-
- const handleMenuClose = useCallback(()=>{
- setMenuOpenId(null);
- }, []);
-
const checkMenuClick = useCallback((e)=>{
setCheckedMenuItems((prev)=>{
let newVal = !prev[e.value];
@@ -453,21 +445,21 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
accesskey={shortcut_key(queryToolPref.btn_save_file)} disabled={buttonsDisabled['save'] || !queryToolCtx.params.is_query_tool}
onClick={()=>{saveFile(false);}} />
} splitButton disabled={!queryToolCtx.params.is_query_tool}
- name="menu-saveas" ref={saveAsMenuRef} onClick={openMenu}
+ name="menu-saveas" ref={saveAsMenuRef} onClick={toggleMenu}
/>
>}
disabled={!queryToolCtx.params.is_query_tool}
- name="menu-edit" ref={editMenuRef} onClick={openMenu} />
+ name="menu-edit" ref={editMenuRef} onClick={toggleMenu} />
}
onClick={onFilterClick} disabled={buttonsDisabled['filter']} accesskey={shortcut_key(queryToolPref.btn_filter_dialog)}/>
} splitButton
disabled={buttonsDisabled['filter']} name="menu-filter" ref={filterMenuRef} accesskey={shortcut_key(queryToolPref.btn_filter_options)}
- onClick={openMenu} />
+ onClick={toggleMenu} />
} splitButton
name="menu-autocommit" ref={autoCommitMenuRef} accesskey={shortcut_key(queryToolPref.btn_delete_row)}
- onClick={openMenu} />
+ onClick={toggleMenu} disabled={!queryToolCtx.params.is_query_tool}/>
}
@@ -491,7 +483,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
onClick={()=>{explainAnalyse();}} disabled={buttonsDisabled['explain_analyse'] || !queryToolCtx.params.is_query_tool} shortcut={queryToolPref.explain_analyze_query}/>
} splitButton
disabled={!queryToolCtx.params.is_query_tool}
- name="menu-explain" ref={explainMenuRef} onClick={openMenu} />
+ name="menu-explain" ref={explainMenuRef} onClick={toggleMenu} />
}
@@ -502,7 +494,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
>}
- disabled={!queryToolCtx.params.is_query_tool} name="menu-macros" ref={macrosMenuRef} onClick={openMenu} />
+ disabled={!queryToolCtx.params.is_query_tool} name="menu-macros" ref={macrosMenuRef} onClick={toggleMenu} />
} onClick={onHelpClick} />
@@ -510,15 +502,15 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
{saveFile(true);}}>{gettext('Save as')}
{eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_FIND_REPLACE, false);}}>{gettext('Find')}
@@ -540,8 +532,8 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
{eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_INCLUDE_EXCLUDE_FILTER, true);}}>{gettext('Filter by Selection')}
{eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_INCLUDE_EXCLUDE_FILTER, false);}}>{gettext('Exclude by Selection')}
@@ -549,8 +541,8 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
{gettext('Auto commit?')}
@@ -559,8 +551,8 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
{gettext('Verbose')}
@@ -577,8 +569,8 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
{gettext('Manage macros')}
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/QueryHistory.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/QueryHistory.jsx
index cbbca3486..b3c4dc18a 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/sections/QueryHistory.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/QueryHistory.jsx
@@ -19,7 +19,7 @@ import moment from 'moment';
import PlayArrowRoundedIcon from '@material-ui/icons/PlayArrowRounded';
import AssessmentRoundedIcon from '@material-ui/icons/AssessmentRounded';
import ExplicitRoundedIcon from '@material-ui/icons/ExplicitRounded';
-import { SaveDataIcon, CommitIcon, RollbackIcon } from '../../../../../../static/js/components/ExternalIcon';
+import { SaveDataIcon, CommitIcon, RollbackIcon, ViewDataIcon } from '../../../../../../static/js/components/ExternalIcon';
import { InputSwitch } from '../../../../../../static/js/components/FormComponents';
import CodeMirror from '../../../../../../static/js/components/CodeMirror';
import { DefaultButton } from '../../../../../../static/js/components/Buttons';
@@ -30,6 +30,7 @@ import { LayoutEventsContext, LAYOUT_EVENTS } from '../../../../../../static/js/
import PropTypes from 'prop-types';
import { parseApiError } from '../../../../../../static/js/api_instance';
import * as clipboard from '../../../../../../static/js/clipboard';
+import EmptyPanelMessage from '../../../../../../static/js/components/EmptyPanelMessage';
const useStyles = makeStyles((theme)=>({
leftRoot: {
@@ -145,9 +146,9 @@ class QueryHistoryUtils {
getDatePrefix(date) {
let prefix = '';
if (this.isDaysBefore(date, 0)) {
- prefix = 'Today - ';
+ prefix = gettext('Today - ');
} else if (this.isDaysBefore(date, 1)) {
- prefix = 'Yesterday - ';
+ prefix = gettext('Yesterday - ');
}
return prefix;
}
@@ -247,7 +248,7 @@ function QuerySourceIcon({source}) {
case JSON.stringify(QuerySources.SAVE_DATA):
return ;
case JSON.stringify(QuerySources.VIEW_DATA):
- return ;
+ return ;
default:
return <>>;
}
@@ -312,7 +313,9 @@ function QueryHistoryDetails({entry}) {
}, [entry]);
if(!entry) {
- return <>>;
+ return
+
+ ;
}
return (
@@ -460,39 +463,45 @@ export function QueryHistory() {
{React.useMemo(()=>(
-
-
- {gettext('Show queries generated internally by pgAdmin?')}
- {
- setShowInternal(e.target.checked);
- qhu.current.showInternal = e.target.checked;
- setSelectedItemKey(qhu.current.getNextItemKey());
- }} />
-
- Remove
- Remove All
+ {qhu.current.size() == 0 ?
+ :
+ <>
+
+
+
+ {gettext('Show queries generated internally by pgAdmin?')}
+ {
+ setShowInternal(e.target.checked);
+ qhu.current.showInternal = e.target.checked;
+ setSelectedItemKey(qhu.current.getNextItemKey());
+ }} />
+
+
+ {gettext('Remove')}
+ {gettext('Remove All')}
+
+
+
+ } tabIndex="0" onKeyDown={onKeyPressed}>
+ {qhu.current.getGroups().map(([groupKey, groupHeader]) => (
+
+
+ {groupHeader}
+ {qhu.current.getGroupEntries(groupKey).map((entry) => (
+ {setSelectedItemKey(entry.itemKey);}}/>
+ ))}
+
+
+ ))}
+
+
+
+
+
-
-
- } tabIndex="0" onKeyDown={onKeyPressed}>
- {qhu.current.getGroups().map(([groupKey, groupHeader]) => (
-
-
- {groupHeader}
- {qhu.current.getGroupEntries(groupKey).map((entry) => (
- {setSelectedItemKey(entry.itemKey);}}/>
- ))}
-
-
- ))}
-
-
-
-
-
-
+ >}
), [selectedItemKey, showInternal, qhu.current.size()])}
>
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSet.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSet.jsx
index ee5b2245a..553f2b647 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSet.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSet.jsx
@@ -627,7 +627,7 @@ export class ResultSetUtils {
data.primary_keys = (_.isEmpty(data.primary_keys) && data.has_oids) ? data.oids : data.primary_keys;
data.can_edit = !_.isEmpty(data.primary_keys);
let procColumns = this.processColumns(data);
- onResultsAvailable(data, procColumns, this.processRows(result, procColumns, this.processRows(result, procColumns)));
+ onResultsAvailable(data, procColumns, this.processRows(result, procColumns));
this.setStartData(null);
let planJson = this.getPlanJson(result, data);
if(planJson) {
@@ -886,12 +886,14 @@ export function ResultSet() {
};
useEffect(()=>{
eventBus.registerListener(QUERY_TOOL_EVENTS.FETCH_MORE_ROWS, fetchMoreRows);
- return ()=>eventBus.deregisterListener(QUERY_TOOL_EVENTS.FETCH_MORE_ROWS, fetchMoreRows);
- }, [queryData, columns]);
+ return ()=>{
+ eventBus.deregisterListener(QUERY_TOOL_EVENTS.FETCH_MORE_ROWS, fetchMoreRows);
+ };
+ }, [queryData?.has_more_rows, columns]);
useEffect(()=>{
eventBus.fireEvent(QUERY_TOOL_EVENTS.ROWS_FETCHED, queryData?.rows_fetched_to, queryData?.rows_affected);
- }, [queryData]);
+ }, [queryData?.rows_fetched_to, queryData?.rows_affected]);
const warnSaveDataClose = ()=>{
// No changes.
@@ -975,7 +977,7 @@ export function ResultSet() {
}
eventBus.fireEvent(QUERY_TOOL_EVENTS.SAVE_DATA_DONE, true);
- if(!_.size(dataChangeStore.added)) {
+ if(_.size(dataChangeStore.added)) {
// Update the rows in a grid after addition
respData.data.query_results.forEach((qr)=>{
if(!_.isNull(qr.row_added)) {
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSetToolbar.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSetToolbar.jsx
index e51e2ad5a..3fd5cd034 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSetToolbar.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSetToolbar.jsx
@@ -158,7 +158,7 @@ export function ResultSetToolbar({containerRef, canEdit}) {
open={menuOpenId=='menu-copyheader'}
onClose={handleMenuClose}
>
- Copy with headers
+ {gettext('Copy with headers')}
>
);
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/StatusBar.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/StatusBar.jsx
index df562884e..446075cd9 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/sections/StatusBar.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/StatusBar.jsx
@@ -15,6 +15,7 @@ import _ from 'lodash';
import { QUERY_TOOL_EVENTS } from '../QueryToolConstants';
import { useStopwatch } from '../../../../../../static/js/custom_hooks';
import { QueryToolEventsContext } from '../QueryToolComponent';
+import gettext from 'sources/gettext';
const useStyles = makeStyles((theme)=>({
@@ -86,18 +87,18 @@ export function StatusBar() {
let stagedText = '';
if(dataRowChangeCounts.added > 0) {
- stagedText += ` Added: ${dataRowChangeCounts.added};`;
+ stagedText += ' ' + gettext('Added: %s', dataRowChangeCounts.added);
}
if(dataRowChangeCounts.updated > 0) {
- stagedText += ` Updated: ${dataRowChangeCounts.updated};`;
+ stagedText += ' ' + gettext('Updated: %s', dataRowChangeCounts.updated);
}
if(dataRowChangeCounts.deleted > 0) {
- stagedText += ` Deleted: ${dataRowChangeCounts.deleted};`;
+ stagedText += ' ' + gettext('Deleted: %s', dataRowChangeCounts.deleted);
}
return (
- Total rows: {rowsCount[0]} of {rowsCount[1]}
+ {gettext('Total rows: %s of %s', rowsCount[0], rowsCount[1])}
{lastTaskText &&
{lastTaskText} {hours.toString().padStart(2, '0')}:{minutes.toString().padStart(2, '0')}:{seconds.toString().padStart(2, '0')}.{msec.toString().padStart(3, '0')}
}
@@ -105,13 +106,13 @@ export function StatusBar() {
{lastTaskText} {hours.toString().padStart(2, '0')}:{minutes.toString().padStart(2, '0')}:{seconds.toString().padStart(2, '0')}.{msec.toString().padStart(3, '0')}
}
{Boolean(selectedRowsCount) &&
- Rows selected: {selectedRowsCount}}
+ {gettext('Rows selected: %s',selectedRowsCount)}}
{stagedText &&
- Changes staged: {stagedText}
+ {gettext('Changes staged: %s', stagedText)}
}
- Ln {position[0]}, Col {position[1]}
+ {gettext('Ln %s, Col %s', position[0], position[1])}
);
}
diff --git a/web/regression/javascript/components/Buttons.spec.js b/web/regression/javascript/components/Buttons.spec.js
index 9bedc95ef..7e2269c51 100644
--- a/web/regression/javascript/components/Buttons.spec.js
+++ b/web/regression/javascript/components/Buttons.spec.js
@@ -37,14 +37,14 @@ describe('components Buttons', ()=>{
it('PrimaryButton', ()=>{
let ThemedBtn = withTheme(PrimaryButton);
let btn = mount(Test);
- expect(btn.getDOMNode().classList.contains('MuiButton-containedPrimary')).toBe(true);
+ expect(btn.find('button').getDOMNode().classList.contains('MuiButton-containedPrimary')).toBe(true);
});
it('DefaultButton', ()=>{
let ThemedBtn = withTheme(DefaultButton);
let btn = mount(Test);
- expect(btn.getDOMNode().classList.contains('MuiButton-outlined')).toBe(true);
- expect(btn.getDOMNode().classList.contains('testClass')).toBe(true);
+ expect(btn.find('button').getDOMNode().classList.contains('MuiButton-outlined')).toBe(true);
+ expect(btn.find('button').getDOMNode().classList.contains('testClass')).toBe(true);
});
it('PgIconButton', ()=>{
diff --git a/web/regression/javascript/components/TabPanel.spec.js b/web/regression/javascript/components/TabPanel.spec.js
index e99a062b4..1f1067860 100644
--- a/web/regression/javascript/components/TabPanel.spec.js
+++ b/web/regression/javascript/components/TabPanel.spec.js
@@ -37,12 +37,12 @@ describe('TabPanel', ()=>{
});
it('init', ()=>{
- expect(panelInst.getDOMNode().hidden).toBeTrue();
+ expect(panelInst.find('div').at(0).getDOMNode().hidden).toBeTrue();
expect(panelInst.find('h1')).not.toBe(null);
});
it('tab select', ()=>{
panelInst.setProps({value: 0});
- expect(panelInst.getDOMNode().hidden).toBeFalse();
+ expect(panelInst.find('div').at(0).getDOMNode().hidden).toBeFalse();
});
});
|