mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
1) Added support to show all background processes in separate panel. Fixes #3709
2) Port process watcher to React. Fixes #7404
This commit is contained in:
committed by
Akshay Joshi
parent
271b6d91fc
commit
c2b23465cc
@@ -242,7 +242,7 @@ export function DataGridHeader({label, canAdd, onAddClick, canSearch, onSearchTe
|
||||
onSearchTextChange(value);
|
||||
setSearchText(value);
|
||||
}}
|
||||
placeholder={'Search'}>
|
||||
placeholder={gettext('Search')}>
|
||||
</InputText>
|
||||
</Box>
|
||||
}
|
||||
|
||||
@@ -14,14 +14,13 @@ import axios from 'axios';
|
||||
/* Get the axios instance to call back end APIs.
|
||||
Do not import axios directly, instead use this */
|
||||
export default function getApiInstance(headers={}) {
|
||||
const api = axios.create({
|
||||
return axios.create({
|
||||
headers: {
|
||||
'Content-type': 'application/json',
|
||||
[pgAdmin.csrf_token_header]: pgAdmin.csrf_token,
|
||||
...headers,
|
||||
}
|
||||
});
|
||||
return api;
|
||||
}
|
||||
|
||||
export function parseApiError(error) {
|
||||
|
||||
@@ -81,6 +81,17 @@ const useStyles = makeStyles((theme)=>({
|
||||
},
|
||||
noBorder: {
|
||||
border: 0,
|
||||
color: 'inherit',
|
||||
backgroundColor: 'transparent',
|
||||
'&:hover': {
|
||||
border: 0,
|
||||
color: 'inherit',
|
||||
backgroundColor: 'inherit',
|
||||
filter: 'brightness(85%)',
|
||||
},
|
||||
'&.Mui-disabled': {
|
||||
border: 0,
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
@@ -371,7 +371,8 @@ export const InputText = forwardRef(({
|
||||
maxLength: controlProps?.multiline ? null : maxlength,
|
||||
'aria-describedby': helpid,
|
||||
...(type ? { pattern: !_.isUndefined(controlProps) && !_.isUndefined(controlProps.pattern) ? controlProps.pattern : patterns[type] } : {}),
|
||||
style: inputStyle || {}
|
||||
style: inputStyle || {},
|
||||
autoComplete: 'new-password',
|
||||
}}
|
||||
readOnly={Boolean(readonly)}
|
||||
disabled={Boolean(disabled)}
|
||||
@@ -1167,6 +1168,7 @@ const useStylesFormFooter = makeStyles((theme) => ({
|
||||
padding: theme.spacing(0.5),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
minHeight: '36px',
|
||||
},
|
||||
containerSuccess: {
|
||||
borderColor: theme.palette.success.main,
|
||||
@@ -1290,11 +1292,13 @@ FormInputSelectThemes.propTypes = {
|
||||
};
|
||||
|
||||
|
||||
export function NotifierMessage({ type = MESSAGE_TYPE.SUCCESS, message, closable = true, showIcon=true, textCenter=false, onClose = () => {/*This is intentional (SonarQube)*/ } }) {
|
||||
export function NotifierMessage({
|
||||
type = MESSAGE_TYPE.SUCCESS, message, style, closable = true, showIcon=true, textCenter=false,
|
||||
onClose = () => {/*This is intentional (SonarQube)*/ }}) {
|
||||
const classes = useStylesFormFooter();
|
||||
|
||||
return (
|
||||
<Box className={clsx(classes.container, classes[`container${type}`])}>
|
||||
<Box className={clsx(classes.container, classes[`container${type}`])} style={style}>
|
||||
{showIcon && <FormIcon type={type} className={classes[`icon${type}`]} />}
|
||||
<Box className={textCenter ? classes.messageCenter : classes.message}>{HTMLReactParse(message || '')}</Box>
|
||||
{closable && <IconButton className={clsx(classes.closeButton, classes[`icon${type}`])} onClick={onClose}>
|
||||
@@ -1311,6 +1315,7 @@ NotifierMessage.propTypes = {
|
||||
showIcon: PropTypes.bool,
|
||||
textCenter: PropTypes.bool,
|
||||
onClose: PropTypes.func,
|
||||
style: PropTypes.object,
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ import _ from 'lodash';
|
||||
import gettext from 'sources/gettext';
|
||||
import SchemaView from '../SchemaView';
|
||||
import EmptyPanelMessage from './EmptyPanelMessage';
|
||||
import KeyboardArrowUpIcon from '@material-ui/icons/KeyboardArrowUp';
|
||||
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
|
||||
|
||||
/* eslint-disable react/display-name */
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
@@ -50,31 +52,18 @@ const useStyles = makeStyles((theme) => ({
|
||||
overflowX: 'hidden !important',
|
||||
overflow: 'overlay !important',
|
||||
},
|
||||
customHeader:{
|
||||
CustomHeader:{
|
||||
marginTop: '8px',
|
||||
marginLeft: '4px'
|
||||
},
|
||||
searchBox: {
|
||||
display: 'flex',
|
||||
background: theme.palette.background.default
|
||||
},
|
||||
warning: {
|
||||
backgroundColor: theme.palette.warning.main + '!important'
|
||||
},
|
||||
alert: {
|
||||
backgroundColor: theme.palette.error.main + '!important'
|
||||
},
|
||||
searchPadding: {
|
||||
flex: 2.5
|
||||
},
|
||||
searchInput: {
|
||||
flex: 1,
|
||||
marginTop: 8,
|
||||
borderLeft: 'none',
|
||||
paddingLeft: 5,
|
||||
marginRight: 8,
|
||||
marginBottom: 8,
|
||||
|
||||
minWidth: '300px'
|
||||
},
|
||||
tableContainer: {
|
||||
overflowX: 'auto',
|
||||
@@ -93,14 +82,18 @@ const useStyles = makeStyles((theme) => ({
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
},
|
||||
pgTableHeadar: {
|
||||
pgTableContainer: {
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
overflow: 'hidden !important',
|
||||
height: '100% !important',
|
||||
flexDirection: 'column'
|
||||
overflow: 'hidden',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
},
|
||||
pgTableHeader: {
|
||||
display: 'flex',
|
||||
background: theme.palette.background.default,
|
||||
padding: '8px',
|
||||
},
|
||||
|
||||
tableRowContent:{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
@@ -134,10 +127,9 @@ const useStyles = makeStyles((theme) => ({
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
padding: theme.spacing(1, 0.5),
|
||||
textAlign: 'left',
|
||||
overflowY: 'auto',
|
||||
overflowX: 'hidden',
|
||||
alignContent: 'center',
|
||||
backgroundColor: theme.otherVars.tableBg,
|
||||
overflow: 'hidden',
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
...theme.mixins.panelBorder.right,
|
||||
...theme.mixins.panelBorder.top,
|
||||
@@ -221,7 +213,7 @@ IndeterminateCheckbox.propTypes = {
|
||||
};
|
||||
|
||||
const ROW_HEIGHT = 35;
|
||||
export default function PgTable({ columns, data, isSelectRow, caveTable=true, ...props }) {
|
||||
export default function PgTable({ columns, data, isSelectRow, caveTable=true, schema, ExpandedComponent, sortOptions, ...props }) {
|
||||
// Use the state and functions returned from useTable to build your UI
|
||||
const classes = useStyles();
|
||||
const [searchVal, setSearchVal] = React.useState('');
|
||||
@@ -277,6 +269,9 @@ export default function PgTable({ columns, data, isSelectRow, caveTable=true, ..
|
||||
defaultColumn,
|
||||
isSelectRow,
|
||||
autoResetSortBy: false,
|
||||
initialState: {
|
||||
sortBy: sortOptions || [],
|
||||
}
|
||||
},
|
||||
useGlobalFilter,
|
||||
useSortBy,
|
||||
@@ -333,7 +328,7 @@ export default function PgTable({ columns, data, isSelectRow, caveTable=true, ..
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
sortble: false,
|
||||
sortable: false,
|
||||
width: 35,
|
||||
maxWidth: 35,
|
||||
minWidth: 0
|
||||
@@ -403,7 +398,7 @@ export default function PgTable({ columns, data, isSelectRow, caveTable=true, ..
|
||||
}, [expandComplete]);
|
||||
|
||||
return (
|
||||
<div style={style} key={row.id} ref={rowRef}>
|
||||
<div style={style} key={row.id} ref={rowRef} data-test="row-container">
|
||||
<div className={classes.tableRowContent}>
|
||||
<div {...row.getRowProps()} className={classes.tr}>
|
||||
{row.cells.map((cell) => {
|
||||
@@ -421,7 +416,7 @@ export default function PgTable({ columns, data, isSelectRow, caveTable=true, ..
|
||||
classNames.push(classes.alert);
|
||||
}
|
||||
return (
|
||||
<div key={cell.column.id} {...cell.getCellProps()} className={clsx(classNames, row.original.icon && row.original.icon[cell.column.id], row.original.icon[cell.column.id] && classes.cellIcon)}
|
||||
<div key={cell.column.id} {...cell.getCellProps()} className={clsx(classNames, cell.column?.dataClassName, row.original.icon?.[cell.column.id], row.original.icon?.[cell.column.id] && classes.cellIcon)}
|
||||
title={_.isUndefined(cell.value) || _.isNull(cell.value) ? '': String(cell.value)}>
|
||||
{cell.render('Cell')}
|
||||
</div>
|
||||
@@ -430,13 +425,14 @@ export default function PgTable({ columns, data, isSelectRow, caveTable=true, ..
|
||||
</div>
|
||||
{!_.isUndefined(row) && row.isExpanded && (
|
||||
<Box key={row.id} className={classes.expandedForm}>
|
||||
<SchemaView
|
||||
{schema && <SchemaView
|
||||
getInitData={()=>Promise.resolve({})}
|
||||
viewHelperProps={{ mode: 'properties' }}
|
||||
schema={props.schema[row.id]}
|
||||
schema={schema[row.id]}
|
||||
showFooter={false}
|
||||
onDataChange={()=>{setExpandComplete(true);}}
|
||||
/>
|
||||
/>}
|
||||
{ExpandedComponent && <ExpandedComponent row={row} onExpandComplete={()=>setExpandComplete(true)}/>}
|
||||
</Box>
|
||||
)}
|
||||
</div>
|
||||
@@ -447,24 +443,30 @@ export default function PgTable({ columns, data, isSelectRow, caveTable=true, ..
|
||||
);
|
||||
// Render the UI for your table
|
||||
return (
|
||||
<Box className={classes.pgTableHeadar}>
|
||||
<Box className={classes.searchBox}>
|
||||
{props.customHeader && (<Box className={classes.customHeader}> <props.customHeader /></Box>)}
|
||||
<Box className={classes.searchPadding}></Box>
|
||||
<InputText
|
||||
placeholder={'Search'}
|
||||
className={classes.searchInput}
|
||||
value={searchVal}
|
||||
onChange={(val) => {
|
||||
setSearchVal(val);
|
||||
}}
|
||||
/>
|
||||
<Box className={classes.pgTableContainer} data-test={props['data-test']}>
|
||||
<Box className={classes.pgTableHeader}>
|
||||
{props.CustomHeader && (<Box className={classes.customHeader}> <props.CustomHeader /></Box>)}
|
||||
<Box marginLeft="auto">
|
||||
<InputText
|
||||
placeholder={'Search'}
|
||||
className={classes.searchInput}
|
||||
value={searchVal}
|
||||
onChange={(val) => {
|
||||
setSearchVal(val);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<div className={classes.tableContainer}>
|
||||
<div {...getTableProps({style:{minWidth: totalColumnsWidth}})} className={clsx(classes.table, caveTable ? classes.caveTable : '')}>
|
||||
<div>
|
||||
{headerGroups.map((headerGroup) => (
|
||||
<div key={''} {...headerGroup.getHeaderGroupProps()}>
|
||||
<div key={''} {...headerGroup.getHeaderGroupProps((column)=>({
|
||||
style: {
|
||||
...column.style,
|
||||
height: '40px',
|
||||
}
|
||||
}))}>
|
||||
{headerGroup.headers.map((column) => (
|
||||
<div
|
||||
key={column.id}
|
||||
@@ -472,14 +474,14 @@ export default function PgTable({ columns, data, isSelectRow, caveTable=true, ..
|
||||
className={clsx(classes.tableCellHeader, column.className)}
|
||||
>
|
||||
<div
|
||||
{...(column.sortble ? column.getSortByToggleProps() : {})}
|
||||
{...(column.sortable ? column.getSortByToggleProps() : {})}
|
||||
>
|
||||
{column.render('Header')}
|
||||
<span>
|
||||
{column.isSorted
|
||||
? column.isSortedDesc
|
||||
? ' 🔽'
|
||||
: ' 🔼'
|
||||
? <KeyboardArrowDownIcon style={{fontSize: '1.2rem'}} />
|
||||
: <KeyboardArrowUpIcon style={{fontSize: '1.2rem'}} />
|
||||
: ''}
|
||||
</span>
|
||||
</div>
|
||||
@@ -507,14 +509,13 @@ export default function PgTable({ columns, data, isSelectRow, caveTable=true, ..
|
||||
height={height}
|
||||
itemCount={rows.length}
|
||||
itemSize={getRowHeight}
|
||||
sorted={props?.sortOptions}
|
||||
>
|
||||
{RenderRow}
|
||||
</VariableSizeList>)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
) : (
|
||||
<EmptyPanelMessage text={gettext('No record found')}/>
|
||||
<EmptyPanelMessage text={gettext('No rows found')}/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
@@ -526,7 +527,7 @@ export default function PgTable({ columns, data, isSelectRow, caveTable=true, ..
|
||||
PgTable.propTypes = {
|
||||
stepId: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
customHeader: PropTypes.func,
|
||||
CustomHeader: PropTypes.func,
|
||||
className: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
caveTable: PropTypes.bool,
|
||||
fixedSizeList: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
@@ -544,8 +545,9 @@ PgTable.propTypes = {
|
||||
setSelectedRows: PropTypes.func,
|
||||
getSelectedRows: PropTypes.func,
|
||||
searchText: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
sortOptions: PropTypes.array,
|
||||
schema: PropTypes.object,
|
||||
rows: PropTypes.object
|
||||
rows: PropTypes.object,
|
||||
ExpandedComponent: PropTypes.node,
|
||||
'data-test': PropTypes.string
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ export function useInterval(callback, delay) {
|
||||
}
|
||||
|
||||
if(delay > -1) {
|
||||
tick();
|
||||
let id = setInterval(tick, delay);
|
||||
return () => clearInterval(id);
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ const useModalStyles = makeStyles((theme)=>({
|
||||
},
|
||||
margin: {
|
||||
marginLeft: '0.25rem',
|
||||
}
|
||||
},
|
||||
}));
|
||||
function AlertContent({text, confirm, okLabel=gettext('OK'), cancelLabel=gettext('Cancel'), onOkClick, onCancelClick}) {
|
||||
const classes = useModalStyles();
|
||||
@@ -144,11 +144,10 @@ var Notifier = {
|
||||
}
|
||||
},
|
||||
_callNotify(msg, type, autoHideDuration) {
|
||||
if (!_.isNull(autoHideDuration)) {
|
||||
this.notify(<NotifierMessage type={type} message={msg} closable={false} />, autoHideDuration);
|
||||
} else {
|
||||
this.notify(<NotifierMessage type={type} message={msg}/>, null);
|
||||
}
|
||||
this.notify(
|
||||
<NotifierMessage style={{maxWidth: '50vw'}} type={type} message={msg} closable={_.isNull(autoHideDuration) ? true : false} />,
|
||||
autoHideDuration
|
||||
);
|
||||
},
|
||||
|
||||
pgRespErrorNotify(xhr, error, prefixMsg='') {
|
||||
|
||||
Reference in New Issue
Block a user