mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2024-07-07 04:53:25 -05:00
Remove the usage of MUI makeStyles as it doesn't support React 18. #7363
This commit is contained in:
parent
f66bd4bcfb
commit
cc999ae5a5
|
@ -81,7 +81,6 @@
|
|||
"@mui/icons-material": "^5.15.10",
|
||||
"@mui/lab": "^5.0.0-alpha.165",
|
||||
"@mui/material": "^5.15.10",
|
||||
"@mui/styles": "^5.15.10",
|
||||
"@mui/x-date-pickers": "^6.19.7",
|
||||
"@projectstorm/react-diagrams": "^6.6.1",
|
||||
"@simonwep/pickr": "^1.5.1",
|
||||
|
|
|
@ -8,35 +8,27 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import gettext from 'sources/gettext';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import url_for from 'sources/url_for';
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Box, Grid, InputLabel } from '@mui/material';
|
||||
import { DefaultButton } from '../../../static/js/components/Buttons';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { InputText } from '../../../static/js/components/FormComponents';
|
||||
import getApiInstance from '../../../static/js/api_instance';
|
||||
import { copyToClipboard } from '../../../static/js/clipboard';
|
||||
import { useDelayedCaller } from '../../../static/js/custom_hooks';
|
||||
import { usePgAdmin } from '../../../static/js/BrowserComponent';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
container: {
|
||||
padding: '16px',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
copyBtn: {
|
||||
const StyledDefaultButton = styled(DefaultButton)(({theme}) => ({
|
||||
'&.AboutComponent-copyBtn': {
|
||||
marginRight: '1px',
|
||||
float: 'right',
|
||||
borderColor: theme.otherVars.borderColor,
|
||||
fontSize: '13px',
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
export default function AboutComponent() {
|
||||
const classes = useStyles();
|
||||
const containerRef = useRef();
|
||||
const [aboutData, setAboutData] = useState([]);
|
||||
const [copyText, setCopyText] = useState(gettext('Copy'));
|
||||
|
@ -57,7 +49,7 @@ export default function AboutComponent() {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<Box className={classes.container} ref={containerRef}>
|
||||
<Box sx={{ padding: '16px', height: '100%', display: 'flex',flexDirection: 'column'}} ref={containerRef}>
|
||||
<Grid container spacing={0} style={{marginBottom: '8px'}}>
|
||||
<Grid item lg={3} md={3} sm={3} xs={12}>
|
||||
<InputLabel style={{fontWeight: 'bold'}}>{gettext('Version')}</InputLabel>
|
||||
|
@ -134,11 +126,11 @@ export default function AboutComponent() {
|
|||
<Box flexGrow="1" display="flex" flexDirection="column">
|
||||
<Box>
|
||||
<span style={{fontWeight: 'bold'}}>{gettext('Server Configuration')}</span>
|
||||
<DefaultButton className={classes.copyBtn} onClick={()=>{
|
||||
<StyledDefaultButton className='AboutComponent-copyBtn' onClick={()=>{
|
||||
copyToClipboard(aboutData.settings);
|
||||
setCopyText(gettext('Copied!'));
|
||||
revertCopiedText(1500);
|
||||
}}>{copyText}</DefaultButton>
|
||||
}}>{copyText}</StyledDefaultButton>
|
||||
</Box>
|
||||
<Box flexGrow="1" paddingTop="1px">
|
||||
<InputText style={{height: '100%'}} controlProps={{multiline: true, rows: 8}} inputStyle={{resize: 'none'}}
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import getApiInstance from 'sources/api_instance';
|
||||
import PgTable from 'sources/components/PgTable';
|
||||
import { InputCheckbox } from '../../../static/js/components/FormComponents';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import url_for from 'sources/url_for';
|
||||
import Graphs from './Graphs';
|
||||
import { Box, Tab, Tabs } from '@mui/material';
|
||||
|
@ -48,88 +48,39 @@ function parseData(data) {
|
|||
return res;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
emptyPanel: {
|
||||
const Root = styled('div')(({theme}) => ({
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
'& .Dashboard-dashboardPanel': {
|
||||
height: '100%',
|
||||
background: theme.palette.grey[400],
|
||||
'& .Dashboard-panelContent': {
|
||||
...theme.mixins.panelBorder.all,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden !important',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
minHeight: '400px',
|
||||
padding: '4px',
|
||||
'& .Dashboard-mainTabs': {
|
||||
...theme.mixins.panelBorder.all,
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
'& .Dashboard-terminateButton': {
|
||||
color: theme.palette.error.main
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'& .Dashboard-emptyPanel': {
|
||||
width: '100%',
|
||||
background: theme.otherVars.emptySpaceBg,
|
||||
overflow: 'auto',
|
||||
padding: '8px',
|
||||
display: 'flex',
|
||||
},
|
||||
dashboardPanel: {
|
||||
height: '100%',
|
||||
background: theme.palette.grey[400],
|
||||
},
|
||||
cardHeader: {
|
||||
padding: '0.25rem 0.5rem',
|
||||
fontWeight: 'bold !important',
|
||||
backgroundColor: theme.otherVars.tableBg,
|
||||
borderBottom: '1px solid',
|
||||
borderBottomColor: theme.otherVars.borderColor,
|
||||
},
|
||||
searchPadding: {
|
||||
display: 'flex',
|
||||
flex: 2.5,
|
||||
},
|
||||
component: {
|
||||
padding: '8px',
|
||||
},
|
||||
searchInput: {
|
||||
flex: 1,
|
||||
},
|
||||
panelIcon: {
|
||||
width: '80%',
|
||||
margin: '0 auto',
|
||||
marginTop: '25px !important',
|
||||
position: 'relative',
|
||||
textAlign: 'center',
|
||||
},
|
||||
panelMessage: {
|
||||
marginLeft: '0.5rem',
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
panelContent: {
|
||||
...theme.mixins.panelBorder.all,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden !important',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
minHeight: '400px',
|
||||
padding: '4px'
|
||||
},
|
||||
mainTabs: {
|
||||
...theme.mixins.panelBorder.all,
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
terminateButton: {
|
||||
color: theme.palette.error.main
|
||||
},
|
||||
chartCard: {
|
||||
border: '1px solid '+theme.otherVars.borderColor,
|
||||
},
|
||||
chartCardContent: {
|
||||
padding: '0.25rem 0.5rem',
|
||||
height: '165px',
|
||||
display: 'flex',
|
||||
},
|
||||
chartLegend: {
|
||||
marginLeft: 'auto',
|
||||
'& > div': {
|
||||
display: 'flex',
|
||||
fontWeight: 'normal',
|
||||
flexWrap: 'wrap',
|
||||
|
||||
'& .legend-value': {
|
||||
marginLeft: '4px',
|
||||
'& .legend-label': {
|
||||
marginLeft: '4px',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
let activeQSchemaObj = new ActiveQuery();
|
||||
|
@ -138,7 +89,7 @@ function Dashboard({
|
|||
nodeItem, nodeData, node, treeNodeInfo,
|
||||
...props
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
|
||||
let tabs = [gettext('Sessions'), gettext('Locks'), gettext('Prepared Transactions')];
|
||||
let mainTabs = [gettext('General'), gettext('System Statistics')];
|
||||
if(treeNodeInfo?.server?.replication_type) {
|
||||
|
@ -261,7 +212,7 @@ function Dashboard({
|
|||
size="xs"
|
||||
noBorder
|
||||
icon={<CancelIcon />}
|
||||
className={classes.terminateButton}
|
||||
className='Dashboard-terminateButton'
|
||||
onClick={() => {
|
||||
if (
|
||||
!canTakeAction(row, 'terminate')
|
||||
|
@ -796,8 +747,8 @@ function Dashboard({
|
|||
const showDefaultContents = () => {
|
||||
return (
|
||||
sid && !serverConnected ? (
|
||||
<Box className={classes.dashboardPanel}>
|
||||
<div className={classes.emptyPanel}>
|
||||
<Box className='Dashboard-dashboardPanel'>
|
||||
<div className='Dashboard-emptyPanel'>
|
||||
<EmptyPanelMessage text={msg}/>
|
||||
</div>
|
||||
</Box>
|
||||
|
@ -823,7 +774,7 @@ function Dashboard({
|
|||
<InputCheckbox
|
||||
label={gettext('Active sessions only')}
|
||||
labelPlacement="end"
|
||||
className={classes.searchInput}
|
||||
className='Dashboard-searchInput'
|
||||
onChange={(e) => {
|
||||
e.preventDefault();
|
||||
setActiveOnly(e.target.checked);
|
||||
|
@ -834,11 +785,11 @@ function Dashboard({
|
|||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
(<Root>
|
||||
{sid && serverConnected ? (
|
||||
<Box className={classes.dashboardPanel}>
|
||||
<Box className={classes.panelContent}>
|
||||
<Box className={classes.mainTabs}>
|
||||
<Box className='Dashboard-dashboardPanel'>
|
||||
<Box className='Dashboard-panelContent'>
|
||||
<Box className='Dashboard-mainTabs'>
|
||||
<Box>
|
||||
<Tabs
|
||||
value={mainTabVal}
|
||||
|
@ -850,7 +801,7 @@ function Dashboard({
|
|||
</Tabs>
|
||||
</Box>
|
||||
{/* General Statistics */}
|
||||
<TabPanel value={mainTabVal} index={0} classNameRoot={classes.tabPanel}>
|
||||
<TabPanel value={mainTabVal} index={0}>
|
||||
{!_.isUndefined(preferences) && preferences.show_graphs && (
|
||||
<Graphs
|
||||
key={sid + did}
|
||||
|
@ -876,7 +827,7 @@ function Dashboard({
|
|||
}}/>
|
||||
</Tabs>
|
||||
</Box>
|
||||
<TabPanel value={tabVal} index={0} classNameRoot={classes.tabPanel}>
|
||||
<TabPanel value={tabVal} index={0}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
tableNoBorder={false}
|
||||
|
@ -886,7 +837,7 @@ function Dashboard({
|
|||
schema={activeQSchemaObj}
|
||||
></PgTable>
|
||||
</TabPanel>
|
||||
<TabPanel value={tabVal} index={1} classNameRoot={classes.tabPanel}>
|
||||
<TabPanel value={tabVal} index={1}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
tableNoBorder={false}
|
||||
|
@ -894,7 +845,7 @@ function Dashboard({
|
|||
data={dashData}
|
||||
></PgTable>
|
||||
</TabPanel>
|
||||
<TabPanel value={tabVal} index={2} classNameRoot={classes.tabPanel}>
|
||||
<TabPanel value={tabVal} index={2}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
tableNoBorder={false}
|
||||
|
@ -902,7 +853,7 @@ function Dashboard({
|
|||
data={dashData}
|
||||
></PgTable>
|
||||
</TabPanel>
|
||||
<TabPanel value={tabVal} index={3} classNameRoot={classes.tabPanel}>
|
||||
<TabPanel value={tabVal} index={3}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
tableNoBorder={false}
|
||||
|
@ -914,7 +865,7 @@ function Dashboard({
|
|||
)}
|
||||
</TabPanel>
|
||||
{/* System Statistics */}
|
||||
<TabPanel value={mainTabVal} index={1} classNameRoot={classes.tabPanel}>
|
||||
<TabPanel value={mainTabVal} index={1} classNameRoot='Dashboard-tabPanel'>
|
||||
<Box height="100%" display="flex" flexDirection="column">
|
||||
{ssMsg === 'installed' && did === ldid ?
|
||||
<ErrorBoundary>
|
||||
|
@ -928,7 +879,7 @@ function Dashboard({
|
|||
})}
|
||||
</Tabs>
|
||||
</Box>
|
||||
<TabPanel value={systemStatsTabVal} index={0} classNameRoot={classes.tabPanel}>
|
||||
<TabPanel value={systemStatsTabVal} index={0} classNameRoot='Dashboard-tabPanel'>
|
||||
<Summary
|
||||
key={sid + did}
|
||||
preferences={preferences}
|
||||
|
@ -938,7 +889,7 @@ function Dashboard({
|
|||
serverConnected={serverConnected}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel value={systemStatsTabVal} index={1} classNameRoot={classes.tabPanel}>
|
||||
<TabPanel value={systemStatsTabVal} index={1} classNameRoot='Dashboard-tabPanel'>
|
||||
<CPU
|
||||
key={sid + did}
|
||||
preferences={preferences}
|
||||
|
@ -948,7 +899,7 @@ function Dashboard({
|
|||
serverConnected={serverConnected}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel value={systemStatsTabVal} index={2} classNameRoot={classes.tabPanel}>
|
||||
<TabPanel value={systemStatsTabVal} index={2} classNameRoot='Dashboard-tabPanel'>
|
||||
<Memory
|
||||
key={sid + did}
|
||||
preferences={preferences}
|
||||
|
@ -958,7 +909,7 @@ function Dashboard({
|
|||
serverConnected={serverConnected}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel value={systemStatsTabVal} index={3} classNameRoot={classes.tabPanel}>
|
||||
<TabPanel value={systemStatsTabVal} index={3} classNameRoot='Dashboard-tabPanel'>
|
||||
<Storage
|
||||
key={sid + did}
|
||||
preferences={preferences}
|
||||
|
@ -970,14 +921,14 @@ function Dashboard({
|
|||
/>
|
||||
</TabPanel>
|
||||
</ErrorBoundary> :
|
||||
<div className={classes.emptyPanel}>
|
||||
<div className='Dashboard-emptyPanel'>
|
||||
<EmptyPanelMessage text={ssMsg}/>
|
||||
</div>
|
||||
}
|
||||
</Box>
|
||||
</TabPanel>
|
||||
{/* Replication */}
|
||||
<TabPanel value={mainTabVal} index={2} classNameRoot={classes.tabPanel}>
|
||||
<TabPanel value={mainTabVal} index={2} classNameRoot='Dashboard-tabPanel'>
|
||||
<Replication key={sid} sid={sid} node={node}
|
||||
preferences={preferences} treeNodeInfo={treeNodeInfo} nodeData={nodeData} pageVisible={props.isActive} />
|
||||
</TabPanel>
|
||||
|
@ -985,7 +936,7 @@ function Dashboard({
|
|||
</Box>
|
||||
</Box>
|
||||
) : showDefaultContents() }
|
||||
</>
|
||||
</Root>)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ import React, { useState, useEffect, useRef, useReducer, useMemo } from 'react';
|
|||
import PgTable from 'sources/components/PgTable';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import {getGCD, getEpoch} from 'sources/utils';
|
||||
import ChartContainer from '../components/ChartContainer';
|
||||
import { Box, Grid } from '@mui/material';
|
||||
|
@ -23,40 +22,6 @@ import { getStatsUrl, transformData, statsReducer, X_AXIS_LENGTH } from './utili
|
|||
import { toPrettySize } from '../../../../static/js/utils';
|
||||
import SectionContainer from '../components/SectionContainer.jsx';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
autoResizer: {
|
||||
height: '100% !important',
|
||||
width: '100% !important',
|
||||
background: theme.palette.grey[400],
|
||||
padding: '8px',
|
||||
overflowX: 'auto !important',
|
||||
overflowY: 'hidden !important',
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
},
|
||||
container: {
|
||||
height: 'auto',
|
||||
padding: '0px !important',
|
||||
marginBottom: '4px',
|
||||
},
|
||||
fixedContainer: {
|
||||
flexGrow: 1,
|
||||
padding: '0px !important',
|
||||
marginBottom: '4px',
|
||||
},
|
||||
tableContainer: {
|
||||
padding: '6px',
|
||||
width: '100%',
|
||||
},
|
||||
containerHeader: {
|
||||
fontSize: '15px',
|
||||
fontWeight: 'bold',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
},
|
||||
}));
|
||||
|
||||
const chartsDefault = {
|
||||
'cpu_stats': {'User Normal': [], 'User Niced': [], 'Kernel': [], 'Idle': []},
|
||||
'la_stats': {'1 min': [], '5 mins': [], '10 mins': [], '15 mins': []},
|
||||
|
@ -246,15 +211,14 @@ CPU.propTypes = {
|
|||
};
|
||||
|
||||
export function CPUWrapper(props) {
|
||||
const classes = useStyles();
|
||||
const options = useMemo(()=>({
|
||||
showDataPoints: props.showDataPoints,
|
||||
showTooltip: props.showTooltip,
|
||||
lineBorderWidth: props.lineBorderWidth,
|
||||
}), [props.showTooltip, props.showDataPoints, props.lineBorderWidth]);
|
||||
return (
|
||||
<Box display="flex" flexDirection="column" height="100%">
|
||||
<Grid container spacing={0.5} className={classes.container}>
|
||||
(<Box display="flex" flexDirection="column" height="100%">
|
||||
<Grid container spacing={0.5} sx={{marginBottom: '4px'}}>
|
||||
<Grid item md={6}>
|
||||
<ChartContainer id='cu-graph' title={gettext('CPU usage')} datasets={props.cpuUsageInfo.datasets} errorMsg={props.errorMsg} isTest={props.isTest}>
|
||||
<StreamingChart data={props.cpuUsageInfo} dataPointSize={DATA_POINT_SIZE} xRange={X_AXIS_LENGTH} options={options} />
|
||||
|
@ -269,7 +233,6 @@ export function CPUWrapper(props) {
|
|||
<Box flexGrow={1} minHeight={0}>
|
||||
<SectionContainer title={gettext('Process CPU usage')}>
|
||||
<PgTable
|
||||
className={classes.autoResizer}
|
||||
columns={props.tableHeader}
|
||||
data={props.processCpuUsageStats}
|
||||
msg={props.errorMsg}
|
||||
|
@ -280,6 +243,7 @@ export function CPUWrapper(props) {
|
|||
</SectionContainer>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import React, { useState, useEffect, useRef, useReducer, useMemo } from 'react';
|
|||
import PgTable from 'sources/components/PgTable';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import {getGCD, getEpoch} from 'sources/utils';
|
||||
import ChartContainer from '../components/ChartContainer';
|
||||
import { Box, Grid } from '@mui/material';
|
||||
|
@ -22,39 +21,6 @@ import { getStatsUrl, transformData, statsReducer, X_AXIS_LENGTH } from './utili
|
|||
import { toPrettySize } from '../../../../static/js/utils';
|
||||
import SectionContainer from '../components/SectionContainer.jsx';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
autoResizer: {
|
||||
height: '100% !important',
|
||||
width: '100% !important',
|
||||
background: theme.palette.grey[400],
|
||||
padding: '8px',
|
||||
overflowX: 'auto !important',
|
||||
overflowY: 'hidden !important',
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
},
|
||||
container: {
|
||||
height: 'auto',
|
||||
padding: '0px !important',
|
||||
marginBottom: '4px',
|
||||
},
|
||||
fixedContainer: {
|
||||
flexGrow: 1,
|
||||
padding: '0px !important',
|
||||
marginBottom: '4px',
|
||||
},
|
||||
tableContainer: {
|
||||
padding: '6px',
|
||||
width: '100%',
|
||||
},
|
||||
containerHeader: {
|
||||
fontSize: '15px',
|
||||
fontWeight: 'bold',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
}
|
||||
}));
|
||||
|
||||
const chartsDefault = {
|
||||
'm_stats': {'Total': [], 'Used': [], 'Free': []},
|
||||
|
@ -248,7 +214,7 @@ Memory.propTypes = {
|
|||
};
|
||||
|
||||
export function MemoryWrapper(props) {
|
||||
const classes = useStyles();
|
||||
|
||||
const options = useMemo(()=>({
|
||||
showDataPoints: props.showDataPoints,
|
||||
showTooltip: props.showTooltip,
|
||||
|
@ -256,35 +222,36 @@ export function MemoryWrapper(props) {
|
|||
}), [props.showTooltip, props.showDataPoints, props.lineBorderWidth]);
|
||||
|
||||
return (
|
||||
<Box display="flex" flexDirection="column" height="100%">
|
||||
<Grid container spacing={0.5} className={classes.container}>
|
||||
<Grid item md={6}>
|
||||
<ChartContainer id='m-graph' title={gettext('Memory')} datasets={props.memoryUsageInfo.datasets} errorMsg={props.errorMsg} isTest={props.isTest}>
|
||||
<StreamingChart data={props.memoryUsageInfo} dataPointSize={DATA_POINT_SIZE} xRange={X_AXIS_LENGTH} options={options}
|
||||
valueFormatter={toPrettySize}/>
|
||||
</ChartContainer>
|
||||
(
|
||||
<Box display="flex" flexDirection="column" height="100%">
|
||||
<Grid container spacing={0.5} sx={{marginBottom: '4px'}}>
|
||||
<Grid item md={6}>
|
||||
<ChartContainer id='m-graph' title={gettext('Memory')} datasets={props.memoryUsageInfo.datasets} errorMsg={props.errorMsg} isTest={props.isTest}>
|
||||
<StreamingChart data={props.memoryUsageInfo} dataPointSize={DATA_POINT_SIZE} xRange={X_AXIS_LENGTH} options={options}
|
||||
valueFormatter={toPrettySize}/>
|
||||
</ChartContainer>
|
||||
</Grid>
|
||||
<Grid item md={6}>
|
||||
<ChartContainer id='sm-graph' title={gettext('Swap memory')} datasets={props.swapMemoryUsageInfo.datasets} errorMsg={props.errorMsg} isTest={props.isTest}>
|
||||
<StreamingChart data={props.swapMemoryUsageInfo} dataPointSize={DATA_POINT_SIZE} xRange={X_AXIS_LENGTH} options={options}
|
||||
valueFormatter={toPrettySize}/>
|
||||
</ChartContainer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item md={6}>
|
||||
<ChartContainer id='sm-graph' title={gettext('Swap memory')} datasets={props.swapMemoryUsageInfo.datasets} errorMsg={props.errorMsg} isTest={props.isTest}>
|
||||
<StreamingChart data={props.swapMemoryUsageInfo} dataPointSize={DATA_POINT_SIZE} xRange={X_AXIS_LENGTH} options={options}
|
||||
valueFormatter={toPrettySize}/>
|
||||
</ChartContainer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Box flexGrow={1} minHeight={0}>
|
||||
<SectionContainer title={gettext('Process memory usage')}>
|
||||
<PgTable
|
||||
className={classes.autoResizer}
|
||||
columns={props.tableHeader}
|
||||
data={props.processMemoryUsageStats}
|
||||
msg={props.errorMsg}
|
||||
type={'panel'}
|
||||
caveTable={false}
|
||||
tableNoBorder={false}
|
||||
></PgTable>
|
||||
</SectionContainer>
|
||||
<Box flexGrow={1} minHeight={0}>
|
||||
<SectionContainer title={gettext('Process memory usage')}>
|
||||
<PgTable
|
||||
columns={props.tableHeader}
|
||||
data={props.processMemoryUsageStats}
|
||||
msg={props.errorMsg}
|
||||
type={'panel'}
|
||||
caveTable={false}
|
||||
tableNoBorder={false}
|
||||
></PgTable>
|
||||
</SectionContainer>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import React, { useState, useEffect, useRef, useReducer, useMemo } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import url_for from 'sources/url_for';
|
||||
import {getGCD, getEpoch} from 'sources/utils';
|
||||
import ChartContainer from '../components/ChartContainer';
|
||||
|
@ -21,52 +21,29 @@ import axios from 'axios';
|
|||
import { BarChart, PieChart } from '../../../../static/js/chartjs';
|
||||
import { getStatsUrl, transformData, X_AXIS_LENGTH } from './utility.js';
|
||||
import { toPrettySize } from '../../../../static/js/utils';
|
||||
import clsx from 'clsx';
|
||||
import { commonTableStyles } from '../../../../static/js/Theme';
|
||||
import Table from '../../../../static/js/components/Table';
|
||||
import SectionContainer from '../components/SectionContainer.jsx';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
container: {
|
||||
height: 'auto',
|
||||
padding: '8px',
|
||||
marginBottom: '6px',
|
||||
},
|
||||
driveContainer: {
|
||||
width: '100%',
|
||||
},
|
||||
diskInfoCharts: {
|
||||
marginBottom: '4px',
|
||||
},
|
||||
containerHeaderText: {
|
||||
fontWeight: 'bold',
|
||||
padding: '4px 8px',
|
||||
},
|
||||
tableContainer: {
|
||||
|
||||
const Root = styled('div')(({theme}) => ({
|
||||
'& .Storage-tableContainer': {
|
||||
background: theme.otherVars.tableBg,
|
||||
padding: '0px',
|
||||
border: '1px solid '+theme.otherVars.borderColor,
|
||||
borderCollapse: 'collapse',
|
||||
borderRadius: '4px',
|
||||
overflow: 'auto',
|
||||
width: '100%',
|
||||
marginBottom: '4px',
|
||||
},
|
||||
tableWhiteSpace: {
|
||||
'& td, & th': {
|
||||
whiteSpace: 'break-spaces !important',
|
||||
margin: '4px 4px 4px 4px',
|
||||
'& .Storage-containerHeaderText': {
|
||||
fontWeight: 'bold',
|
||||
padding: '4px 8px',
|
||||
},
|
||||
'& .Storage-tableWhiteSpace': {
|
||||
'& td, & th': {
|
||||
whiteSpace: 'break-spaces !important',
|
||||
},
|
||||
},
|
||||
},
|
||||
driveContainerHeader: {
|
||||
height: 'auto',
|
||||
padding: '5px 0px 0px 0px',
|
||||
background: theme.otherVars.tableBg,
|
||||
marginBottom: '5px',
|
||||
borderRadius: '4px 4px 0px 0px',
|
||||
},
|
||||
driveContainerBody: {
|
||||
height: 'auto',
|
||||
padding: '0px',
|
||||
background: theme.otherVars.tableBg,
|
||||
borderRadius: '0px 0px 4px 4px',
|
||||
},
|
||||
}));
|
||||
|
||||
|
@ -122,12 +99,11 @@ const chartsDefault = {
|
|||
|
||||
|
||||
const DiskStatsTable = (props) => {
|
||||
const tableClasses = commonTableStyles();
|
||||
const classes = useStyles();
|
||||
|
||||
const tableHeader = props.tableHeader;
|
||||
const data = props.data;
|
||||
return (
|
||||
<table className={clsx(tableClasses.table, classes.tableWhiteSpace)}>
|
||||
<Table classNameRoot='Storage-tableWhiteSpace'>
|
||||
<thead>
|
||||
<tr>
|
||||
{tableHeader.map((item, index) => (
|
||||
|
@ -144,7 +120,7 @@ const DiskStatsTable = (props) => {
|
|||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -358,7 +334,7 @@ export default function Storage({preferences, sid, did, pageVisible, enablePoll=
|
|||
}, enablePoll ? pollDelay : -1);
|
||||
|
||||
return (
|
||||
<>
|
||||
(<Root>
|
||||
<div data-testid='graph-poll-delay' style={{display: 'none'}}>{pollDelay}</div>
|
||||
{chartDrawnOnce &&
|
||||
<StorageWrapper
|
||||
|
@ -373,7 +349,7 @@ export default function Storage({preferences, sid, did, pageVisible, enablePoll=
|
|||
isTest={false}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
</Root>)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -387,7 +363,7 @@ Storage.propTypes = {
|
|||
};
|
||||
|
||||
export function StorageWrapper(props) {
|
||||
const classes = useStyles();
|
||||
|
||||
const options = useMemo(()=>({
|
||||
showDataPoints: props.showDataPoints,
|
||||
showTooltip: props.showTooltip,
|
||||
|
@ -414,10 +390,10 @@ export function StorageWrapper(props) {
|
|||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={classes.tableContainer}>
|
||||
<div className={classes.containerHeaderText}>{gettext('Disk information')}</div>
|
||||
return (
|
||||
<Root>
|
||||
<div className='Storage-tableContainer'>
|
||||
<div className='Storage-containerHeaderText'>{gettext('Disk information')}</div>
|
||||
<DiskStatsTable tableHeader={props.tableHeader} data={props.diskStats} />
|
||||
</div>
|
||||
<Grid container spacing={0.5} sx={{marginBottom: '4px'}}>
|
||||
|
@ -511,7 +487,7 @@ export function StorageWrapper(props) {
|
|||
</Grid>
|
||||
</SectionContainer>
|
||||
))}
|
||||
</>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import React, { useState, useEffect, useRef, useReducer, useMemo } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import url_for from 'sources/url_for';
|
||||
import getApiInstance from 'sources/api_instance';
|
||||
import {getGCD, getEpoch} from 'sources/utils';
|
||||
|
@ -20,31 +20,22 @@ import StreamingChart from '../../../../static/js/components/PgChart/StreamingCh
|
|||
import {useInterval, usePrevious} from 'sources/custom_hooks';
|
||||
import axios from 'axios';
|
||||
import { getStatsUrl, transformData,statsReducer, X_AXIS_LENGTH } from './utility.js';
|
||||
import clsx from 'clsx';
|
||||
import { commonTableStyles } from '../../../../static/js/Theme';
|
||||
import Table from '../../../../static/js/components/Table';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
container: {
|
||||
height: 'auto',
|
||||
padding: '0px !important',
|
||||
marginBottom: '4px',
|
||||
},
|
||||
tableContainer: {
|
||||
const Root = styled('div')(({theme}) => ({
|
||||
'& .Summary-tableContainer': {
|
||||
background: theme.otherVars.tableBg,
|
||||
padding: '0px',
|
||||
border: '1px solid '+theme.otherVars.borderColor,
|
||||
borderCollapse: 'collapse',
|
||||
borderRadius: '4px',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
chartContainer: {
|
||||
padding: '4px',
|
||||
},
|
||||
containerHeader: {
|
||||
fontWeight: 'bold',
|
||||
marginBottom: '0px',
|
||||
borderBottom: '1px solid '+theme.otherVars.borderColor,
|
||||
padding: '4px 8px',
|
||||
'& .Summary-containerHeader': {
|
||||
fontWeight: 'bold',
|
||||
marginBottom: '0px',
|
||||
borderBottom: '1px solid '+theme.otherVars.borderColor,
|
||||
padding: '4px 8px',
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
|
@ -53,10 +44,9 @@ const chartsDefault = {
|
|||
};
|
||||
|
||||
const SummaryTable = (props) => {
|
||||
const tableClasses = commonTableStyles();
|
||||
const data = props.data;
|
||||
return (
|
||||
<table className={clsx(tableClasses.table)}>
|
||||
<Table >
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Property</th>
|
||||
|
@ -71,7 +61,7 @@ const SummaryTable = (props) => {
|
|||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -224,7 +214,7 @@ export default function Summary({preferences, sid, did, pageVisible, enablePoll=
|
|||
}, enablePoll ? pollDelay : -1);
|
||||
|
||||
return (
|
||||
<>
|
||||
(<Root>
|
||||
<div data-testid='graph-poll-delay' style={{display: 'none'}}>{pollDelay}</div>
|
||||
{chartDrawnOnce &&
|
||||
<SummaryWrapper
|
||||
|
@ -238,7 +228,7 @@ export default function Summary({preferences, sid, did, pageVisible, enablePoll=
|
|||
isTest={false}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
</Root>)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -251,7 +241,7 @@ Summary.propTypes = {
|
|||
};
|
||||
|
||||
function SummaryWrapper(props) {
|
||||
const classes = useStyles();
|
||||
|
||||
const options = useMemo(()=>({
|
||||
showDataPoints: props.showDataPoints,
|
||||
showTooltip: props.showTooltip,
|
||||
|
@ -259,23 +249,23 @@ function SummaryWrapper(props) {
|
|||
}), [props.showTooltip, props.showDataPoints, props.lineBorderWidth]);
|
||||
return (
|
||||
<>
|
||||
<Grid container spacing={0.5} className={classes.container}>
|
||||
<Grid container spacing={0.5} sx={{height: 'auto', padding: '0px !important', marginBottom: '4px'}}>
|
||||
<Grid item md={6}>
|
||||
<div className={classes.tableContainer}>
|
||||
<div className={classes.containerHeader}>{gettext('OS information')}</div>
|
||||
<div className='Summary-tableContainer'>
|
||||
<div className='Summary-containerHeader'>{gettext('OS information')}</div>
|
||||
<SummaryTable data={props.osStats} />
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item md={6}className={classes.chartContainer}>
|
||||
<Grid item md={6} sx={{padding: '4px'}}>
|
||||
<ChartContainer id='hpc-graph' title={gettext('Process & handle count')} datasets={props.processHandleCount.datasets} errorMsg={props.errorMsg} isTest={props.isTest}>
|
||||
<StreamingChart data={props.processHandleCount} dataPointSize={DATA_POINT_SIZE} xRange={X_AXIS_LENGTH} options={options} showSecondAxis={true} />
|
||||
</ChartContainer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container spacing={0.5} className={classes.container}>
|
||||
<Grid container spacing={0.5} sx={{height: 'auto', padding: '0px !important', marginBottom: '4px'}}>
|
||||
<Grid item md={6}>
|
||||
<div className={classes.tableContainer}>
|
||||
<div className={classes.containerHeader}>{gettext('CPU information')}</div>
|
||||
<div className='Summary-tableContainer'>
|
||||
<div className='Summary-containerHeader'>{gettext('CPU information')}</div>
|
||||
<SummaryTable data={props.cpuStats} />
|
||||
</div>
|
||||
</Grid>
|
||||
|
|
|
@ -8,96 +8,111 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import gettext from 'sources/gettext';
|
||||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import PgAdminLogo from './PgAdminLogo';
|
||||
import { Link } from '@mui/material';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
emptyPanel: {
|
||||
background: theme.palette.grey[400],
|
||||
overflow: 'hidden',
|
||||
padding: '8px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
height: '100%'
|
||||
|
||||
},
|
||||
dashboardContainer: {
|
||||
const Root = styled('div')(({theme}) => ({
|
||||
background: theme.palette.grey[400],
|
||||
overflow: 'hidden',
|
||||
padding: '8px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
height: '100%',
|
||||
'& .WelcomeDashboard-dashboardContainer': {
|
||||
paddingBottom: '8px',
|
||||
minHeight: '100%'
|
||||
},
|
||||
card: {
|
||||
position: 'relative',
|
||||
minWidth: 0,
|
||||
wordWrap: 'break-word',
|
||||
backgroundColor: theme.otherVars.tableBg,
|
||||
backgroundClip: 'border-box',
|
||||
border: '1px solid' + theme.otherVars.borderColor,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
marginTop: 8
|
||||
},
|
||||
row: {
|
||||
marginRight: '-8px',
|
||||
marginLeft: '-8px'
|
||||
},
|
||||
rowContent: {
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
marginRight: '-7.5px',
|
||||
marginLeft: '-7.5px'
|
||||
},
|
||||
cardHeader: {
|
||||
padding: '0.25rem 0.5rem',
|
||||
fontWeight: 'bold',
|
||||
backgroundColor: theme.otherVars.tableBg,
|
||||
borderBottom: '1px solid',
|
||||
borderBottomColor: theme.otherVars.borderColor,
|
||||
},
|
||||
dashboardLink: {
|
||||
color: theme.otherVars.colorFg + '!important',
|
||||
flex: '0 0 50%',
|
||||
maxWidth: '50%',
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer'
|
||||
},
|
||||
gettingStartedLink: {
|
||||
flex: '0 0 25%',
|
||||
maxWidth: '50%',
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer'
|
||||
},
|
||||
link: {
|
||||
color: theme.palette.text.primary + '!important',
|
||||
},
|
||||
cardColumn: {
|
||||
flex: '0 0 100%',
|
||||
maxWidth: '100%',
|
||||
margin: '8px'
|
||||
},
|
||||
cardBody: {
|
||||
flex: '1 1 auto',
|
||||
minHeight: '1px',
|
||||
padding: '0.5rem !important',
|
||||
},
|
||||
welcomeLogo: {
|
||||
width: '400px',
|
||||
'& .app-name': {
|
||||
fill: theme.otherVars.colorBrand
|
||||
minHeight: '100%',
|
||||
|
||||
'& .WelcomeDashboard-row': {
|
||||
marginRight: '-8px',
|
||||
marginLeft: '-8px'
|
||||
},
|
||||
'& .app-name-underline': {
|
||||
stroke: theme.palette.text.primary
|
||||
'& .WelcomeDashboard-cardColumn': {
|
||||
flex: '0 0 100%',
|
||||
maxWidth: '100%',
|
||||
margin: '8px',
|
||||
|
||||
'& .WelcomeDashboard-card': {
|
||||
position: 'relative',
|
||||
minWidth: 0,
|
||||
wordWrap: 'break-word',
|
||||
backgroundColor: theme.otherVars.tableBg,
|
||||
backgroundClip: 'border-box',
|
||||
border: '1px solid' + theme.otherVars.borderColor,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
marginTop: 8,
|
||||
|
||||
'& .WelcomeDashboard-cardHeader': {
|
||||
padding: '0.25rem 0.5rem',
|
||||
fontWeight: 'bold',
|
||||
backgroundColor: theme.otherVars.tableBg,
|
||||
borderBottom: '1px solid',
|
||||
borderBottomColor: theme.otherVars.borderColor,
|
||||
},
|
||||
'& .WelcomeDashboard-cardBody': {
|
||||
flex: '1 1 auto',
|
||||
minHeight: '1px',
|
||||
padding: '0.5rem !important',
|
||||
|
||||
'& .WelcomeDashboard-welcomeLogo': {
|
||||
width: '400px',
|
||||
'& .app-name': {
|
||||
fill: theme.otherVars.colorBrand
|
||||
},
|
||||
'& .app-name-underline': {
|
||||
stroke: theme.palette.text.primary
|
||||
},
|
||||
'& .app-tagline': {
|
||||
fill: theme.palette.text.primary
|
||||
}
|
||||
},
|
||||
|
||||
'& .WelcomeDashboard-rowContent': {
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
marginRight: '-7.5px',
|
||||
marginLeft: '-7.5px',
|
||||
|
||||
'& .WelcomeDashboard-dashboardLink': {
|
||||
color: theme.palette.text.primary + ' !important',
|
||||
flex: '0 0 50%',
|
||||
maxWidth: '50%',
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer',
|
||||
|
||||
'& .WelcomeDashboard-link': {
|
||||
color: theme.palette.text.primary + ' !important',
|
||||
|
||||
'& .WelcomeDashboard-dashboardIcon': {
|
||||
color: theme.otherVars.colorBrand
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
'& .WelcomeDashboard-gettingStartedLink': {
|
||||
flex: '0 0 25%',
|
||||
maxWidth: '50%',
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer',
|
||||
|
||||
'& .WelcomeDashboard-link': {
|
||||
color: theme.palette.text.primary + ' !important',
|
||||
|
||||
'& .WelcomeDashboard-dashboardIcon': {
|
||||
color: theme.otherVars.colorBrand
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'& .app-tagline': {
|
||||
fill: theme.palette.text.primary
|
||||
}
|
||||
},
|
||||
dashboardIcon: {
|
||||
color: theme.otherVars.colorBrand
|
||||
},
|
||||
}));
|
||||
|
||||
|
@ -130,18 +145,15 @@ function AddNewServer(pgBrowser) {
|
|||
}
|
||||
|
||||
export default function WelcomeDashboard({ pgBrowser }) {
|
||||
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<div className={classes.emptyPanel}>
|
||||
<div className={classes.dashboardContainer}>
|
||||
<div className={classes.row}>
|
||||
<div className={classes.cardColumn}>
|
||||
<div className={classes.card}>
|
||||
<div className={classes.cardHeader}>{gettext('Welcome')}</div>
|
||||
<div className={classes.cardBody}>
|
||||
<div className={classes.welcomeLogo}>
|
||||
<Root>
|
||||
<div className='WelcomeDashboard-dashboardContainer'>
|
||||
<div className='WelcomeDashboard-row'>
|
||||
<div className='WelcomeDashboard-cardColumn'>
|
||||
<div className='WelcomeDashboard-card'>
|
||||
<div className='WelcomeDashboard-cardHeader'>{gettext('Welcome')}</div>
|
||||
<div className='WelcomeDashboard-cardBody'>
|
||||
<div className='WelcomeDashboard-welcomeLogo'>
|
||||
<PgAdminLogo />
|
||||
</div>
|
||||
<h4>
|
||||
|
@ -157,15 +169,15 @@ export default function WelcomeDashboard({ pgBrowser }) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.row}>
|
||||
<div className={classes.cardColumn}>
|
||||
<div className={classes.card}>
|
||||
<div className={classes.cardHeader}>{gettext('Quick Links')}</div>
|
||||
<div className={classes.cardBody}>
|
||||
<div className={classes.rowContent}>
|
||||
<div className={classes.dashboardLink}>
|
||||
<Link onClick={() => { AddNewServer(pgBrowser); }} className={classes.link}>
|
||||
<div className={classes.dashboardIcon}>
|
||||
<div className='WelcomeDashboard-row'>
|
||||
<div className='WelcomeDashboard-cardColumn'>
|
||||
<div className='WelcomeDashboard-card'>
|
||||
<div className='WelcomeDashboard-cardHeader'>{gettext('Quick Links')}</div>
|
||||
<div className='WelcomeDashboard-cardBody'>
|
||||
<div className='WelcomeDashboard-rowContent'>
|
||||
<div className='WelcomeDashboard-dashboardLink'>
|
||||
<Link onClick={() => { AddNewServer(pgBrowser); }} className='WelcomeDashboard-link'>
|
||||
<div className='WelcomeDashboard-dashboardIcon'>
|
||||
<span
|
||||
className="fa fa-4x fa-server"
|
||||
aria-hidden="true"
|
||||
|
@ -174,9 +186,9 @@ export default function WelcomeDashboard({ pgBrowser }) {
|
|||
{gettext('Add New Server')}
|
||||
</Link>
|
||||
</div>
|
||||
<div className={classes.dashboardLink}>
|
||||
<Link onClick={() => pgAdmin.Preferences.show()} className={classes.link}>
|
||||
<div className={classes.dashboardIcon}>
|
||||
<div className='WelcomeDashboard-dashboardLink'>
|
||||
<Link onClick={() => pgAdmin.Preferences.show()} className='WelcomeDashboard-link'>
|
||||
<div className='WelcomeDashboard-dashboardIcon'>
|
||||
<span
|
||||
id="mnu_preferences"
|
||||
className="fa fa-4x fa-cogs"
|
||||
|
@ -191,19 +203,19 @@ export default function WelcomeDashboard({ pgBrowser }) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.row}>
|
||||
<div className={classes.cardColumn}>
|
||||
<div className={classes.card}>
|
||||
<div className={classes.cardHeader}>{gettext('Getting Started')}</div>
|
||||
<div className={classes.cardBody}>
|
||||
<div className={classes.rowContent}>
|
||||
<div className={classes.gettingStartedLink}>
|
||||
<div className='WelcomeDashboard-row'>
|
||||
<div className='WelcomeDashboard-cardColumn'>
|
||||
<div className='WelcomeDashboard-card'>
|
||||
<div className='WelcomeDashboard-cardHeader'>{gettext('Getting Started')}</div>
|
||||
<div className='WelcomeDashboard-cardBody'>
|
||||
<div className='WelcomeDashboard-rowContent'>
|
||||
<div className='WelcomeDashboard-gettingStartedLink'>
|
||||
<a
|
||||
href="https://www.postgresql.org/docs"
|
||||
target="postgres_help"
|
||||
className={classes.link}
|
||||
className='WelcomeDashboard-link'
|
||||
>
|
||||
<div className={classes.dashboardIcon}>
|
||||
<div className='WelcomeDashboard-dashboardIcon'>
|
||||
<span
|
||||
className="fa fa-4x dashboard-pg-doc"
|
||||
aria-hidden="true"
|
||||
|
@ -212,9 +224,9 @@ export default function WelcomeDashboard({ pgBrowser }) {
|
|||
{gettext('PostgreSQL Documentation')}
|
||||
</a>
|
||||
</div>
|
||||
<div className={classes.gettingStartedLink}>
|
||||
<a href="https://www.pgadmin.org" target="pgadmin_website" className={classes.link}>
|
||||
<div className={classes.dashboardIcon}>
|
||||
<div className='WelcomeDashboard-gettingStartedLink'>
|
||||
<a href="https://www.pgadmin.org" target="pgadmin_website" className='WelcomeDashboard-link'>
|
||||
<div className='WelcomeDashboard-dashboardIcon'>
|
||||
<span
|
||||
className="fa fa-4x fa-globe"
|
||||
aria-hidden="true"
|
||||
|
@ -223,13 +235,13 @@ export default function WelcomeDashboard({ pgBrowser }) {
|
|||
{gettext('pgAdmin Website')}
|
||||
</a>
|
||||
</div>
|
||||
<div className={classes.gettingStartedLink}>
|
||||
<div className='WelcomeDashboard-gettingStartedLink'>
|
||||
<a
|
||||
href="https://planet.postgresql.org"
|
||||
target="planet_website"
|
||||
className={classes.link}
|
||||
className='WelcomeDashboard-link'
|
||||
>
|
||||
<div className={classes.dashboardIcon}>
|
||||
<div className='WelcomeDashboard-dashboardIcon'>
|
||||
<span
|
||||
className="fa fa-4x fa-book"
|
||||
aria-hidden="true"
|
||||
|
@ -238,13 +250,13 @@ export default function WelcomeDashboard({ pgBrowser }) {
|
|||
{gettext('Planet PostgreSQL')}
|
||||
</a>
|
||||
</div>
|
||||
<div className={classes.gettingStartedLink}>
|
||||
<div className='WelcomeDashboard-gettingStartedLink'>
|
||||
<a
|
||||
href="https://www.postgresql.org/community"
|
||||
target="postgres_website"
|
||||
className={classes.link}
|
||||
className='WelcomeDashboard-link'
|
||||
>
|
||||
<div className={classes.dashboardIcon}>
|
||||
<div className='WelcomeDashboard-dashboardIcon'>
|
||||
<span
|
||||
className="fa fa-4x fa-users"
|
||||
aria-hidden="true"
|
||||
|
@ -259,7 +271,7 @@ export default function WelcomeDashboard({ pgBrowser }) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,29 +7,20 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import React from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Box, Card, CardContent, CardHeader } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
|
||||
import EmptyPanelMessage from '../../../../static/js/components/EmptyPanelMessage';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
chartCard: {
|
||||
border: '1px solid '+theme.otherVars.borderColor,
|
||||
height: '100%',
|
||||
},
|
||||
chartCardContent: {
|
||||
padding: '0.25rem 0.5rem',
|
||||
height: '165px',
|
||||
display: 'flex',
|
||||
},
|
||||
chartLegend: {
|
||||
const StyledCard = styled(Card)(({theme}) => ({
|
||||
border: '1px solid '+theme.otherVars.borderColor,
|
||||
height: '100%',
|
||||
'& .ChartContainer-chartLegend': {
|
||||
marginLeft: 'auto',
|
||||
'& > div': {
|
||||
display: 'flex',
|
||||
fontWeight: 'normal',
|
||||
|
||||
'& .legend-value': {
|
||||
marginLeft: '4px',
|
||||
'& .legend-label': {
|
||||
|
@ -37,17 +28,22 @@ const useStyles = makeStyles((theme) => ({
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'& .ChartContainer-cardContent': {
|
||||
padding: '0.25rem 0.5rem',
|
||||
height: '165px',
|
||||
display: 'flex',
|
||||
}
|
||||
}));
|
||||
|
||||
export default function ChartContainer(props) {
|
||||
const classes = useStyles();
|
||||
|
||||
|
||||
return (
|
||||
<Card className={classes.chartCard} elevation={0} data-testid="chart-container">
|
||||
<StyledCard elevation={0} data-testid="chart-container">
|
||||
<CardHeader title={<Box display="flex" justifyContent="space-between">
|
||||
<div id={props.id}>{props.title}</div>
|
||||
<div className={classes.chartLegend}>
|
||||
<div className='ChartContainer-chartLegend'>
|
||||
<div style={{display: 'flex', flexWrap: 'wrap'}}>
|
||||
{props.datasets?.map((datum)=>(
|
||||
<div className="legend-value" key={datum.label}>
|
||||
|
@ -58,11 +54,11 @@ export default function ChartContainer(props) {
|
|||
</div>
|
||||
</div>
|
||||
</Box>} />
|
||||
<CardContent className={classes.chartCardContent}>
|
||||
<CardContent className='ChartContainer-cardContent'>
|
||||
{!props.errorMsg && !props.isTest && props.children}
|
||||
{props.errorMsg && <EmptyPanelMessage text={props.errorMsg}/>}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</StyledCard>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,36 +8,33 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import gettext from 'sources/gettext';
|
||||
import CachedOutlinedIcon from '@mui/icons-material/CachedOutlined';
|
||||
import { PgIconButton } from '../../../../static/js/components/Buttons';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
refreshButton: {
|
||||
const StyledPgIconButton = styled(PgIconButton)(({theme}) => ({
|
||||
'&.RefreshButtons': {
|
||||
marginLeft: 'auto',
|
||||
height: '1.9rem',
|
||||
width: '2.2rem',
|
||||
height: '1.9rem !important',
|
||||
width: '2.2rem !important',
|
||||
...theme.mixins.panelBorder,
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
export default function RefreshButton({onClick}) {
|
||||
const classes = useStyles();
|
||||
|
||||
return(
|
||||
<PgIconButton
|
||||
return (
|
||||
<StyledPgIconButton
|
||||
size="xs"
|
||||
noBorder
|
||||
className={classes.refreshButton}
|
||||
className='RefreshButtons'
|
||||
icon={<CachedOutlinedIcon />}
|
||||
onClick={onClick}
|
||||
color="default"
|
||||
aria-label="Refresh"
|
||||
title={gettext('Refresh')}
|
||||
></PgIconButton>
|
||||
></StyledPgIconButton>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,42 +7,37 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import React from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
...theme.mixins.panelBorder.all,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden !important',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
minHeight: '400px',
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
},
|
||||
cardHeader: {
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
...theme.mixins.panelBorder.all,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden !important',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
minHeight: '400px',
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
'& .SectionContainer-cardHeader': {
|
||||
backgroundColor: theme.otherVars.tableBg,
|
||||
borderBottom: '1px solid',
|
||||
borderBottomColor: theme.otherVars.borderColor,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
'& .SectionContainer-cardTitle': {
|
||||
padding: '0.25rem 0.5rem',
|
||||
fontWeight: 'bold',
|
||||
}
|
||||
},
|
||||
cardTitle: {
|
||||
padding: '0.25rem 0.5rem',
|
||||
fontWeight: 'bold',
|
||||
}
|
||||
}));
|
||||
|
||||
export default function SectionContainer({title, titleExtras, children, style}) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Box className={classes.root} style={style}>
|
||||
<Box className={classes.cardHeader} title={title}>
|
||||
<div className={classes.cardTitle}>{title}</div>
|
||||
<StyledBox style={style}>
|
||||
<Box className='SectionContainer-cardHeader' title={title}>
|
||||
<div className='SectionContainer-cardTitle'>{title}</div>
|
||||
<div style={{marginLeft: 'auto'}}>
|
||||
{titleExtras}
|
||||
</div>
|
||||
|
@ -50,7 +45,7 @@ export default function SectionContainer({title, titleExtras, children, style})
|
|||
<Box height="100%" display="flex" flexDirection="column" minHeight={0}>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,24 +1,28 @@
|
|||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React from 'react';
|
||||
import CloseIcon from '@mui/icons-material/CloseRounded';
|
||||
import { DefaultButton, PgIconButton } from '../../../../static/js/components/Buttons';
|
||||
import clsx from 'clsx';
|
||||
import DescriptionOutlinedIcon from '@mui/icons-material/DescriptionOutlined';
|
||||
import { BgProcessManagerProcessState } from './BgProcessConstants';
|
||||
import PropTypes from 'prop-types';
|
||||
import gettext from 'sources/gettext';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
container: {
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
padding: '0.25rem 1rem 1rem',
|
||||
minWidth: '325px',
|
||||
...theme.mixins.panelBorder.all,
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
padding: '0.25rem 1rem 1rem',
|
||||
minWidth: '325px',
|
||||
...theme.mixins.panelBorder.all,
|
||||
'&.BgProcessNotify-containerSuccess': {
|
||||
borderColor: theme.palette.success.main,
|
||||
backgroundColor: theme.palette.success.light,
|
||||
},
|
||||
containerHeader: {
|
||||
'&.BgProcessNotify-containerError': {
|
||||
borderColor: theme.palette.error.main,
|
||||
backgroundColor: theme.palette.error.light,
|
||||
},
|
||||
'& .BgProcessNotify-containerHeader': {
|
||||
height: '32px',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
|
@ -26,36 +30,27 @@ const useStyles = makeStyles((theme)=>({
|
|||
alignItems: 'center',
|
||||
borderTopLeftRadius: 'inherit',
|
||||
borderTopRightRadius: 'inherit',
|
||||
'& .BgProcessNotify-iconSuccess': {
|
||||
color: theme.palette.success.main,
|
||||
},
|
||||
'& .BgProcessNotify-iconError': {
|
||||
color: theme.palette.error.main,
|
||||
}
|
||||
},
|
||||
containerBody: {
|
||||
'&.BgProcessNotify-containerBody': {
|
||||
marginTop: '1rem',
|
||||
overflowWrap: 'break-word',
|
||||
},
|
||||
containerSuccess: {
|
||||
borderColor: theme.palette.success.main,
|
||||
backgroundColor: theme.palette.success.light,
|
||||
},
|
||||
iconSuccess: {
|
||||
color: theme.palette.success.main,
|
||||
},
|
||||
containerError: {
|
||||
borderColor: theme.palette.error.main,
|
||||
backgroundColor: theme.palette.error.light,
|
||||
},
|
||||
iconError: {
|
||||
color: theme.palette.error.main,
|
||||
},
|
||||
}));
|
||||
|
||||
function ProcessNotifyMessage({title, desc, onClose, onViewProcess, success=true, dataTestSuffix=''}) {
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<Box className={clsx(classes.container, (success ? classes.containerSuccess : classes.containerError))} data-test={'process-popup-' + dataTestSuffix}>
|
||||
<Box display="flex" justifyContent="space-between" className={classes.containerHeader}>
|
||||
<StyledBox className={(success ? 'BgProcessNotify-containerSuccess' : 'BgProcessNotify-containerError')} data-test={'process-popup-' + dataTestSuffix}>
|
||||
<Box display="flex" justifyContent="space-between" className='BgProcessNotify-containerHeader'>
|
||||
<Box marginRight={'1rem'}>{title}</Box>
|
||||
<PgIconButton size="xs" noBorder icon={<CloseIcon />} onClick={onClose} title={'Close'} className={success ? classes.iconSuccess : classes.iconError} />
|
||||
<PgIconButton size="xs" noBorder icon={<CloseIcon />} onClick={onClose} title={'Close'} className={success ? 'BgProcessNotify-iconSuccess' : 'BgProcessNotify-iconError'} />
|
||||
</Box>
|
||||
<Box className={classes.containerBody}>
|
||||
<Box className='BgProcessNotify-containerBody'>
|
||||
<Box>{desc}</Box>
|
||||
<Box marginTop={'1rem'} display="flex">
|
||||
<DefaultButton startIcon={<DescriptionOutlinedIcon />} onClick={()=>{
|
||||
|
@ -64,7 +59,7 @@ function ProcessNotifyMessage({title, desc, onClose, onViewProcess, success=true
|
|||
}}>View Processes</DefaultButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
ProcessNotifyMessage.propTypes = {
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import gettext from 'sources/gettext';
|
||||
import url_for from 'sources/url_for';
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
import { MESSAGE_TYPE, NotifierMessage } from '../../../../static/js/components/FormComponents';
|
||||
import { BgProcessManagerProcessState } from './BgProcessConstants';
|
||||
|
@ -24,16 +24,14 @@ import pgAdmin from 'sources/pgadmin';
|
|||
import FolderSharedRoundedIcon from '@mui/icons-material/FolderSharedRounded';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
container: {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: '8px',
|
||||
userSelect: 'text',
|
||||
},
|
||||
cmd: {
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
backgroundColor: theme.palette.background.default,
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: '8px',
|
||||
userSelect: 'text',
|
||||
'& .ProcessDetails-cmd': {
|
||||
...theme.mixins.panelBorder.all,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: theme.otherVars.inputDisabledBg,
|
||||
|
@ -41,19 +39,7 @@ const useStyles = makeStyles((theme)=>({
|
|||
margin: '8px 0px',
|
||||
padding: '4px',
|
||||
},
|
||||
logs: {
|
||||
flexGrow: 1,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
padding: '4px',
|
||||
overflow: 'auto',
|
||||
textOverflow: 'wrap-text',
|
||||
margin: '8px 0px',
|
||||
...theme.mixins.panelBorder.all,
|
||||
},
|
||||
logErr: {
|
||||
color: theme.palette.error.main,
|
||||
},
|
||||
terminateBtn: {
|
||||
'& .ProcessDetails-terminateBtn': {
|
||||
backgroundColor: theme.palette.error.main,
|
||||
color: theme.palette.error.contrastText,
|
||||
border: 0,
|
||||
|
@ -65,7 +51,20 @@ const useStyles = makeStyles((theme)=>({
|
|||
color: theme.palette.error.contrastText + ' !important',
|
||||
border: 0,
|
||||
}
|
||||
}
|
||||
},
|
||||
'& .ProcessDetails-logs': {
|
||||
flexGrow: 1,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
padding: '4px',
|
||||
overflow: 'auto',
|
||||
textOverflow: 'wrap-text',
|
||||
margin: '8px 0px',
|
||||
...theme.mixins.panelBorder.all,
|
||||
|
||||
'& .ProcessDetails-logErr': {
|
||||
color: theme.palette.error.main,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
async function getDetailedStatus(api, jobId, out, err) {
|
||||
|
@ -80,7 +79,6 @@ async function getDetailedStatus(api, jobId, out, err) {
|
|||
}
|
||||
|
||||
export default function ProcessDetails({data}) {
|
||||
const classes = useStyles();
|
||||
const api = useMemo(()=>getApiInstance());
|
||||
const [logs, setLogs] = useState(null);
|
||||
const [completed, setCompleted] = useState(false);
|
||||
|
@ -149,15 +147,15 @@ export default function ProcessDetails({data}) {
|
|||
|
||||
const errRe = new RegExp(': (' + gettext('error') + '|' + gettext('fatal') + '):', 'i');
|
||||
return (
|
||||
<Box display="flex" flexDirection="column" className={classes.container} data-test="process-details">
|
||||
<StyledBox display="flex" flexDirection="column" data-test="process-details">
|
||||
<Box data-test="process-message">{data.details?.message}</Box>
|
||||
{data.details?.cmd && <>
|
||||
<Box>{gettext('Running command')}:</Box>
|
||||
<Box data-test="process-cmd" className={classes.cmd}>{data.details.cmd}</Box>
|
||||
<Box data-test="process-cmd" className='ProcessDetails-cmd'>{data.details.cmd}</Box>
|
||||
</>}
|
||||
{data.details?.query && <>
|
||||
<Box>{gettext('Running query')}:</Box>
|
||||
<Box data-test="process-cmd" className={classes.cmd}>{data.details.query}</Box>
|
||||
<Box data-test="process-cmd" className='ProcessDetails-cmd'>{data.details.query}</Box>
|
||||
</>}
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center" flexWrap="wrap">
|
||||
<Box><span><AccessTimeRoundedIcon /> {gettext('Start time')}: {new Date(data.stime).toString()}</span></Box>
|
||||
|
@ -167,12 +165,12 @@ export default function ProcessDetails({data}) {
|
|||
pgAdmin.Tools.FileManager.openStorageManager(data.current_storage_dir);
|
||||
}} style={{marginRight: '4px'}} />}
|
||||
<DefaultButton disabled={process_state != BgProcessManagerProcessState.PROCESS_STARTED || data.server_id != null}
|
||||
startIcon={<HighlightOffRoundedIcon />} className={classes.terminateBtn} onClick={onStopProcess}>
|
||||
startIcon={<HighlightOffRoundedIcon />} className='ProcessDetails-terminateBtn' onClick={onStopProcess}>
|
||||
Stop Process
|
||||
</DefaultButton>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box flexGrow={1} className={classes.logs}>
|
||||
<Box flexGrow={1} className='ProcessDetails-logs'>
|
||||
{logs == null && <span data-test="loading-logs">{gettext('Loading process logs...')}</span>}
|
||||
{logs?.length == 0 && gettext('No logs available.')}
|
||||
{logs?.map((log, i)=>{
|
||||
|
@ -181,14 +179,14 @@ export default function ProcessDetails({data}) {
|
|||
if(i==logs.length-1) {
|
||||
el?.scrollIntoView();
|
||||
}
|
||||
}} key={id} className={errRe.test(log) ? classes.logErr : ''}>{log}</div>;
|
||||
}} key={id} className={errRe.test(log) ? 'ProcessDetails-logErr' : ''}>{log}</div>;
|
||||
})}
|
||||
</Box>
|
||||
<Box display="flex" alignItems="center">
|
||||
<NotifierMessage type={notifyType} message={notifyText} closable={false} textCenter={true} style={{flexGrow: 1, marginRight: '8px'}} />
|
||||
<Box>{gettext('Execution time')}: {timeTaken} {gettext('seconds')}</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import PgTable from 'sources/components/PgTable';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { BgProcessManagerEvents, BgProcessManagerProcessState } from './BgProcessConstants';
|
||||
import { PgButtonGroup, PgIconButton } from '../../../../static/js/components/Buttons';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
|
@ -26,67 +26,36 @@ import ErrorBoundary from '../../../../static/js/helpers/ErrorBoundary';
|
|||
import ProcessDetails from './ProcessDetails';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
stopButton: {
|
||||
const Root = styled('div')(({theme}) => ({
|
||||
height: '100%',
|
||||
'& .Processes-stopButton': {
|
||||
color: theme.palette.error.main
|
||||
},
|
||||
buttonClick: {
|
||||
backgroundColor: theme.palette.grey[400]
|
||||
},
|
||||
emptyPanel: {
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
background: theme.otherVars.emptySpaceBg,
|
||||
overflow: 'auto',
|
||||
padding: '8px',
|
||||
display: 'flex',
|
||||
},
|
||||
panelIcon: {
|
||||
width: '80%',
|
||||
margin: '0 auto',
|
||||
marginTop: '25px !important',
|
||||
position: 'relative',
|
||||
textAlign: 'center',
|
||||
},
|
||||
panelMessage: {
|
||||
marginLeft: '0.5rem',
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
autoResizer: {
|
||||
height: '100% !important',
|
||||
width: '100% !important',
|
||||
background: theme.palette.grey[400],
|
||||
padding: '7.5px',
|
||||
overflow: 'auto !important',
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
},
|
||||
noPadding: {
|
||||
padding: 0,
|
||||
},
|
||||
bgSucess: {
|
||||
backgroundColor: theme.palette.success.light,
|
||||
height: '100%',
|
||||
padding: '4px',
|
||||
},
|
||||
bgFailed: {
|
||||
backgroundColor: theme.palette.error.light,
|
||||
height: '100%',
|
||||
padding: '4px',
|
||||
},
|
||||
bgTerm: {
|
||||
backgroundColor: theme.palette.warning.light,
|
||||
height: '100%',
|
||||
padding: '4px',
|
||||
},
|
||||
bgRunning: {
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
height: '100%',
|
||||
padding: '4px',
|
||||
'& .Processes-noPadding': {
|
||||
padding: '0 !important',
|
||||
'& .Processes-bgSucess': {
|
||||
backgroundColor: theme.palette.success.light,
|
||||
height: '100%',
|
||||
padding: '4px',
|
||||
},
|
||||
'& .Processes-bgFailed': {
|
||||
backgroundColor: theme.palette.error.light,
|
||||
height: '100%',
|
||||
padding: '4px',
|
||||
},
|
||||
'& .Processes-bgTerm': {
|
||||
backgroundColor: theme.palette.warning.light,
|
||||
height: '100%',
|
||||
padding: '4px',
|
||||
},
|
||||
'& .Processes-bgRunning': {
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
height: '100%',
|
||||
padding: '4px',
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
const ProcessStateTextAndColor = {
|
||||
[BgProcessManagerProcessState.PROCESS_NOT_STARTED]: [gettext('Not started'), 'bgRunning'],
|
||||
[BgProcessManagerProcessState.PROCESS_STARTED]: [gettext('Running'), 'bgRunning'],
|
||||
|
@ -96,7 +65,7 @@ const ProcessStateTextAndColor = {
|
|||
[BgProcessManagerProcessState.PROCESS_FAILED]: [gettext('Failed'), 'bgFailed'],
|
||||
};
|
||||
export default function Processes() {
|
||||
const classes = useStyles();
|
||||
|
||||
const pgAdmin = usePgAdmin();
|
||||
const [tableData, setTableData] = React.useState([]);
|
||||
const [selectedRows, setSelectedRows] = React.useState({});
|
||||
|
@ -130,7 +99,7 @@ export default function Processes() {
|
|||
size="xs"
|
||||
noBorder
|
||||
icon={<CancelIcon />}
|
||||
className={classes.stopButton}
|
||||
className='Processes-stopButton'
|
||||
disabled={row.original.process_state != BgProcessManagerProcessState.PROCESS_STARTED
|
||||
|| row.original.server_id != null}
|
||||
onClick={(e) => {
|
||||
|
@ -165,7 +134,7 @@ export default function Processes() {
|
|||
|
||||
const StatusCell = ({row})=>{
|
||||
const [text, bgcolor] = ProcessStateTextAndColor[row.original.process_state];
|
||||
return <Box className={classes[bgcolor]}>{text}</Box>;
|
||||
return <Box className={'Processes-'+bgcolor}>{text}</Box>;
|
||||
};
|
||||
StatusCell.displayName = 'StatusCell';
|
||||
StatusCell.propTypes = cellPropTypes;
|
||||
|
@ -247,7 +216,7 @@ export default function Processes() {
|
|||
width: 120,
|
||||
minWidth: 120,
|
||||
accessorFn: (row)=>ProcessStateTextAndColor[row.process_state][0],
|
||||
dataClassName: classes.noPadding,
|
||||
dataClassName: 'Processes-noPadding',
|
||||
cell: StatusCell,
|
||||
},
|
||||
{
|
||||
|
@ -274,48 +243,48 @@ export default function Processes() {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<PgTable
|
||||
data-test="processes"
|
||||
className={classes.autoResizer}
|
||||
columns={columns}
|
||||
data={tableData}
|
||||
sortOptions={[{id: 'stime', desc: true}]}
|
||||
selectedRows={selectedRows}
|
||||
setSelectedRows={setSelectedRows}
|
||||
hasSelectRow={true}
|
||||
tableProps={{
|
||||
getRowId: (row)=>{
|
||||
return row.id;
|
||||
}
|
||||
}}
|
||||
CustomHeader={()=>{
|
||||
return (
|
||||
<Box>
|
||||
<PgButtonGroup>
|
||||
<PgIconButton
|
||||
icon={<DeleteIcon style={{height: '1.4rem'}}/>}
|
||||
aria-label="Acknowledge and Remove"
|
||||
title={gettext('Acknowledge and Remove')}
|
||||
onClick={() => {
|
||||
pgAdmin.Browser.notifier.confirm(gettext('Remove Processes'), gettext('Are you sure you want to remove the selected processes?'), ()=>{
|
||||
pgAdmin.Browser.BgProcessManager.acknowledge(selectedRowIDs);
|
||||
setSelectedRows({});
|
||||
});
|
||||
}}
|
||||
disabled={selectedRowIDs.length <= 0}
|
||||
></PgIconButton>
|
||||
<PgIconButton
|
||||
icon={<HelpIcon style={{height: '1.4rem'}}/>}
|
||||
aria-label="Help"
|
||||
title={gettext('Help')}
|
||||
onClick={() => {
|
||||
window.open(url_for('help.static', {'filename': 'processes.html'}));
|
||||
}}
|
||||
></PgIconButton>
|
||||
</PgButtonGroup>
|
||||
</Box>
|
||||
);
|
||||
}}
|
||||
></PgTable>
|
||||
<Root>
|
||||
<PgTable
|
||||
data-test="processes"
|
||||
columns={columns}
|
||||
data={tableData}
|
||||
sortOptions={[{id: 'stime', desc: true}]}
|
||||
selectedRows={selectedRows}
|
||||
setSelectedRows={setSelectedRows}
|
||||
hasSelectRow={true}
|
||||
tableProps={{
|
||||
getRowId: (row)=>{
|
||||
return row.id;
|
||||
}
|
||||
}}
|
||||
CustomHeader={()=>{
|
||||
return (
|
||||
<Box>
|
||||
<PgButtonGroup>
|
||||
<PgIconButton
|
||||
icon={<DeleteIcon style={{height: '1.4rem'}}/>}
|
||||
aria-label="Acknowledge and Remove"
|
||||
title={gettext('Acknowledge and Remove')}
|
||||
onClick={() => {
|
||||
pgAdmin.Browser.notifier.confirm(gettext('Remove Processes'), gettext('Are you sure you want to remove the selected processes?'), ()=>{
|
||||
pgAdmin.Browser.BgProcessManager.acknowledge(selectedRowIDs);
|
||||
setSelectedRows({});
|
||||
});
|
||||
}}
|
||||
disabled={selectedRowIDs.length <= 0}
|
||||
></PgIconButton>
|
||||
<PgIconButton
|
||||
icon={<HelpIcon style={{height: '1.4rem'}}/>}
|
||||
aria-label="Help"
|
||||
title={gettext('Help')}
|
||||
onClick={() => {
|
||||
window.open(url_for('help.static', {'filename': 'processes.html'}));
|
||||
}}
|
||||
></PgIconButton>
|
||||
</PgButtonGroup>
|
||||
</Box>
|
||||
);
|
||||
}}
|
||||
></PgTable></Root>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import gettext from 'sources/gettext';
|
|||
import url_for from 'sources/url_for';
|
||||
import React from 'react';
|
||||
import { Box, Paper } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import Wizard from '../../../../static/js/helpers/wizard/Wizard';
|
||||
import WizardStep from '../../../../static/js/helpers/wizard/WizardStep';
|
||||
import {FormFooterMessage, MESSAGE_TYPE } from '../../../../static/js/components/FormComponents';
|
||||
|
@ -30,42 +29,9 @@ import EventBus from '../../../../static/js/helpers/EventBus';
|
|||
import { CLOUD_PROVIDERS, CLOUD_PROVIDERS_LABELS } from './cloud_constants';
|
||||
import { LAYOUT_EVENTS } from '../../../../static/js/helpers/Layout';
|
||||
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
({
|
||||
messageBox: {
|
||||
marginBottom: '1em',
|
||||
display: 'flex',
|
||||
},
|
||||
messagePadding: {
|
||||
paddingTop: '10px',
|
||||
flex: 2.5,
|
||||
},
|
||||
buttonMarginEDB: {
|
||||
position: 'relative',
|
||||
top: '20%',
|
||||
},
|
||||
toggleButton: {
|
||||
height: '100px',
|
||||
},
|
||||
summaryContainer: {
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
overflow: 'auto',
|
||||
},
|
||||
boxText: {
|
||||
paddingBottom: '5px'
|
||||
},
|
||||
authButton: {
|
||||
marginLeft: '12em'
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
export const CloudWizardEventsContext = React.createContext();
|
||||
|
||||
export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanelId}) {
|
||||
const classes = useStyles();
|
||||
const eventBus = React.useRef(new EventBus());
|
||||
|
||||
let steps = [gettext('Cloud Provider'), gettext('Credentials'), gettext('Cluster Type'),
|
||||
|
@ -416,10 +382,10 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanelId}
|
|||
});
|
||||
|
||||
let cloud_providers = [
|
||||
{label: gettext(CLOUD_PROVIDERS_LABELS.AWS), value: CLOUD_PROVIDERS.AWS, icon: <AWSIcon className={classes.icon} />},
|
||||
{label: gettext(CLOUD_PROVIDERS_LABELS.BIGANIMAL), value: CLOUD_PROVIDERS.BIGANIMAL, icon: <BigAnimalIcon className={classes.icon} />},
|
||||
{label: gettext(CLOUD_PROVIDERS_LABELS.AZURE), value: CLOUD_PROVIDERS.AZURE, icon: <AzureIcon className={classes.icon} /> },
|
||||
{label: gettext(CLOUD_PROVIDERS_LABELS.GOOGLE), value: CLOUD_PROVIDERS.GOOGLE, icon: <GoogleCloudIcon className={classes.icon} /> }];
|
||||
{label: gettext(CLOUD_PROVIDERS_LABELS.AWS), value: CLOUD_PROVIDERS.AWS, icon: <AWSIcon />},
|
||||
{label: gettext(CLOUD_PROVIDERS_LABELS.BIGANIMAL), value: CLOUD_PROVIDERS.BIGANIMAL, icon: <BigAnimalIcon />},
|
||||
{label: gettext(CLOUD_PROVIDERS_LABELS.AZURE), value: CLOUD_PROVIDERS.AZURE, icon: <AzureIcon /> },
|
||||
{label: gettext(CLOUD_PROVIDERS_LABELS.GOOGLE), value: CLOUD_PROVIDERS.GOOGLE, icon: <GoogleCloudIcon /> }];
|
||||
|
||||
return (
|
||||
<CloudWizardEventsContext.Provider value={eventBus.current}>
|
||||
|
@ -433,10 +399,10 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanelId}
|
|||
beforeNext={onBeforeNext}
|
||||
beforeBack={onBeforeBack}>
|
||||
<WizardStep stepId={0}>
|
||||
<Box className={classes.messageBox}>
|
||||
<Box className={classes.messagePadding}>{gettext('Select a cloud provider for PostgreSQL database.')}</Box>
|
||||
<Box sx={{ marginBottom: '1em', display: 'flex'}}>
|
||||
<Box sx={{paddingTop: '10px', flex: 2.5}}>{gettext('Select a cloud provider for PostgreSQL database.')}</Box>
|
||||
</Box>
|
||||
<Box className={classes.messageBox}>
|
||||
<Box sx={{ marginBottom: '1em', display: 'flex'}}>
|
||||
<ToggleButtons cloudProvider={cloudProvider} setCloudProvider={setCloudProvider}
|
||||
options={cloud_providers}
|
||||
></ToggleButtons>
|
||||
|
@ -444,8 +410,8 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanelId}
|
|||
<FormFooterMessage type={errMsg[0]} message={errMsg[1]} onClose={onErrClose} />
|
||||
</WizardStep>
|
||||
<WizardStep stepId={1} >
|
||||
<Box className={classes.buttonMarginEDB}>
|
||||
{cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && <Box className={classes.messageBox}>
|
||||
<Box sx={{ position: 'relative',top: '20%'}}>
|
||||
{cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && <Box sx={{ marginBottom: '1em', display: 'flex'}}>
|
||||
<Box>{gettext('The verification code to authenticate the pgAdmin to EDB BigAnimal is: ')} <strong>{verificationCode}</strong>
|
||||
<br/>{gettext('By clicking the below button, you will be redirected to the EDB BigAnimal authentication page in a new tab.')}
|
||||
</Box>
|
||||
|
@ -453,7 +419,7 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanelId}
|
|||
{cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && <PrimaryButton onClick={authenticateBigAnimal} disabled={verificationIntiated}>
|
||||
{gettext('Click here to authenticate yourself to EDB BigAnimal')}
|
||||
</PrimaryButton>}
|
||||
{cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && <Box className={classes.messageBox}>
|
||||
{cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && <Box sx={{ marginBottom: '1em', display: 'flex'}}>
|
||||
<Box ></Box>
|
||||
</Box>}
|
||||
</Box>
|
||||
|
@ -542,8 +508,8 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanelId}
|
|||
}
|
||||
</WizardStep>
|
||||
<WizardStep stepId={5} >
|
||||
<Box className={classes.boxText}>{gettext('Please review the details before creating the cloud instance.')}</Box>
|
||||
<Paper variant="outlined" elevation={0} className={classes.summaryContainer}>
|
||||
<Box sx={{ paddingBottom: '5px'}}>{gettext('Please review the details before creating the cloud instance.')}</Box>
|
||||
<Paper variant="outlined" elevation={0} sx={{ flexGrow: 1, minHeight: 0, overflow: 'auto'}}>
|
||||
{cloudProvider == CLOUD_PROVIDERS.AWS && callRDSAPI == 5 && <FinalSummary
|
||||
cloudProvider={cloudProvider}
|
||||
instanceData={cloudInstanceDetails}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import { getNodeAjaxOptions, getNodeListById } from 'pgbrowser/node_ajax';
|
||||
import {CloudInstanceDetailsSchema, CloudDBCredSchema, DatabaseSchema} from './aws_schema.ui';
|
||||
|
@ -16,16 +17,14 @@ import url_for from 'sources/url_for';
|
|||
import getApiInstance from '../../../../static/js/api_instance';
|
||||
import { isEmptyString } from 'sources/validators';
|
||||
import PropTypes from 'prop-types';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import gettext from 'sources/gettext';
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
const StyledSchemaView= styled(SchemaView)(() =>
|
||||
({
|
||||
formClass: {
|
||||
'& .aws-formClass': {
|
||||
overflow: 'auto',
|
||||
}
|
||||
}),
|
||||
);
|
||||
}));
|
||||
|
||||
// AWS credentials
|
||||
export function AwsCredentials(props) {
|
||||
|
@ -66,7 +65,7 @@ AwsCredentials.propTypes = {
|
|||
// AWS Instance Details
|
||||
export function AwsInstanceDetails(props) {
|
||||
const [cloudInstanceDetailsInstance, setCloudInstanceDetailsInstance] = React.useState();
|
||||
const classes = useStyles();
|
||||
|
||||
|
||||
React.useMemo(() => {
|
||||
const cloudDBInstanceSchema = new CloudInstanceDetailsSchema({
|
||||
|
@ -114,7 +113,7 @@ export function AwsInstanceDetails(props) {
|
|||
}, [props.cloudProvider]);
|
||||
|
||||
|
||||
return <SchemaView
|
||||
return <StyledSchemaView
|
||||
formType={'dialog'}
|
||||
getInitData={() => { /*This is intentional (SonarQube)*/ }}
|
||||
viewHelperProps={{ mode: 'create' }}
|
||||
|
@ -124,7 +123,7 @@ export function AwsInstanceDetails(props) {
|
|||
onDataChange={(isChanged, changedData) => {
|
||||
props.setCloudInstanceDetails(changedData);
|
||||
}}
|
||||
formClassName={classes.formClass}
|
||||
formClassName='aws-formClass'
|
||||
/>;
|
||||
}
|
||||
AwsInstanceDetails.propTypes = {
|
||||
|
|
|
@ -18,15 +18,6 @@ import getApiInstance from '../../../../static/js/api_instance';
|
|||
import { CloudWizardEventsContext } from './CloudWizard';
|
||||
import {MESSAGE_TYPE } from '../../../../static/js/components/FormComponents';
|
||||
import gettext from 'sources/gettext';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
({
|
||||
formClass: {
|
||||
overflow: 'auto',
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
// Azure credentials
|
||||
export function AzureCredentials(props) {
|
||||
|
@ -111,7 +102,6 @@ AzureCredentials.propTypes = {
|
|||
// Azure Instance
|
||||
export function AzureInstanceDetails(props) {
|
||||
const [azureInstanceSchema, setAzureInstanceSchema] = React.useState();
|
||||
const classes = useStyles();
|
||||
|
||||
React.useMemo(() => {
|
||||
const AzureSchema = new AzureClusterSchema({
|
||||
|
@ -195,7 +185,6 @@ export function AzureInstanceDetails(props) {
|
|||
onDataChange={(isChanged, changedData) => {
|
||||
props.setAzureInstanceData(changedData);
|
||||
}}
|
||||
formClassName={classes.formClass}
|
||||
/>;
|
||||
}
|
||||
AzureInstanceDetails.propTypes = {
|
||||
|
@ -211,7 +200,6 @@ AzureInstanceDetails.propTypes = {
|
|||
// Azure Database Details
|
||||
export function AzureDatabaseDetails(props) {
|
||||
const [azureDBInstance, setAzureDBInstance] = React.useState();
|
||||
const classes = useStyles();
|
||||
|
||||
React.useMemo(() => {
|
||||
const azureDBSchema = new AzureDatabaseSchema({
|
||||
|
@ -235,7 +223,6 @@ export function AzureDatabaseDetails(props) {
|
|||
onDataChange={(isChanged, changedData) => {
|
||||
props.setAzureDatabaseData(changedData);
|
||||
}}
|
||||
formClassName={classes.formClass}
|
||||
/>;
|
||||
}
|
||||
AzureDatabaseDetails.propTypes = {
|
||||
|
|
|
@ -11,38 +11,18 @@ import React from 'react';
|
|||
import { ToggleButton, ToggleButtonGroup } from '@mui/material';
|
||||
import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
|
||||
import { DefaultButton, PrimaryButton } from '../../../../static/js/components/Buttons';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getAWSSummary } from './aws';
|
||||
import {getAzureSummary} from './azure';
|
||||
import { getBigAnimalSummary } from './biganimal';
|
||||
import { commonTableStyles } from '../../../../static/js/Theme';
|
||||
import { Table, TableBody, TableCell, TableHead, TableRow } from '@mui/material';
|
||||
import clsx from 'clsx';
|
||||
import { TableBody, TableCell, TableHead, TableRow } from '@mui/material';
|
||||
import gettext from 'sources/gettext';
|
||||
import { getGoogleSummary } from './google';
|
||||
import { CLOUD_PROVIDERS_LABELS } from './cloud_constants';
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
({
|
||||
toggleButtonGroup: {
|
||||
height: '100px',
|
||||
flexGrow: '1'
|
||||
},
|
||||
toggleButtonMargin:{
|
||||
marginTop: '0px !important',
|
||||
padding: '12px'
|
||||
},
|
||||
gcpiconpadding:{
|
||||
paddingLeft: '1.5rem'
|
||||
}
|
||||
|
||||
}),
|
||||
);
|
||||
import Table from '../../../../static/js/components/Table';
|
||||
|
||||
|
||||
export function ToggleButtons(props) {
|
||||
const classes = useStyles();
|
||||
|
||||
const handleCloudProvider = (event, provider) => {
|
||||
if (provider) props.setCloudProvider(provider);
|
||||
|
@ -53,12 +33,12 @@ export function ToggleButtons(props) {
|
|||
color="primary"
|
||||
value={props.cloudProvider}
|
||||
onChange={handleCloudProvider}
|
||||
className={classes.toggleButtonGroup}
|
||||
sx={{ height: '100px', flexGrow: '1'}}
|
||||
orientation="vertical"
|
||||
exclusive>
|
||||
{
|
||||
(props.options||[]).map((option)=>{
|
||||
return (<ToggleButton value={option.value} key={option.label} aria-label={option.label} className={clsx(classes.toggleButtonMargin, option.label==gettext(CLOUD_PROVIDERS_LABELS.GOOGLE) ? classes.gcpiconpadding : null )} component={props.cloudProvider == option.value ? PrimaryButton : DefaultButton}>
|
||||
return (<ToggleButton value={option.value} key={option.label} aria-label={option.label} sx={{marginTop: '0px !important',padding: '12px'}} className={( option.label==gettext(CLOUD_PROVIDERS_LABELS.GOOGLE) ? 'paddingLeft: 1.5rem' : null )} component={props.cloudProvider == option.value ? PrimaryButton : DefaultButton}>
|
||||
<CheckRoundedIcon style={{visibility: props.cloudProvider == option.value ? 'visible': 'hidden'}}/>
|
||||
{option.icon} {option.label}
|
||||
</ToggleButton>);
|
||||
|
@ -75,7 +55,6 @@ ToggleButtons.propTypes = {
|
|||
|
||||
|
||||
export function FinalSummary(props) {
|
||||
const tableClasses = commonTableStyles();
|
||||
let summary = [],
|
||||
summaryHeader = ['Cloud Details', 'Version and Instance Details', 'Storage Details', 'Database Details'];
|
||||
|
||||
|
@ -104,7 +83,7 @@ export function FinalSummary(props) {
|
|||
|
||||
return summary.map((item, index) => {
|
||||
return (
|
||||
<Table key={summaryHeader[index]} className={clsx(tableClasses.table)}>
|
||||
<Table key={summaryHeader[index]}>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell colSpan={2}>{gettext(summaryHeader[index])}</TableCell>
|
||||
|
|
|
@ -18,15 +18,6 @@ import getApiInstance from '../../../../static/js/api_instance';
|
|||
import { CloudWizardEventsContext } from './CloudWizard';
|
||||
import {MESSAGE_TYPE } from '../../../../static/js/components/FormComponents';
|
||||
import gettext from 'sources/gettext';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
({
|
||||
formClass: {
|
||||
overflow: 'auto',
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
export function GoogleCredentials(props) {
|
||||
|
@ -126,7 +117,6 @@ GoogleCredentials.propTypes = {
|
|||
// Google Instance
|
||||
export function GoogleInstanceDetails(props) {
|
||||
const [googleInstanceSchema, setGoogleInstanceSchema] = React.useState();
|
||||
const classes = useStyles();
|
||||
|
||||
React.useMemo(() => {
|
||||
const GoogleClusterSchemaObj = new GoogleClusterSchema({
|
||||
|
@ -186,7 +176,6 @@ export function GoogleInstanceDetails(props) {
|
|||
onDataChange={(isChanged, changedData) => {
|
||||
props.setGoogleInstanceData(changedData);
|
||||
}}
|
||||
formClassName={classes.formClass}
|
||||
/>;
|
||||
}
|
||||
GoogleInstanceDetails.propTypes = {
|
||||
|
@ -202,7 +191,6 @@ GoogleInstanceDetails.propTypes = {
|
|||
// Google Database Details
|
||||
export function GoogleDatabaseDetails(props) {
|
||||
const [googleDBInstance, setGoogleDBInstance] = React.useState();
|
||||
const classes = useStyles();
|
||||
|
||||
React.useMemo(() => {
|
||||
const googleDBSchema = new GoogleDatabaseSchema({
|
||||
|
@ -226,7 +214,6 @@ export function GoogleDatabaseDetails(props) {
|
|||
onDataChange={(isChanged, changedData) => {
|
||||
props.setGoogleDatabaseData(changedData);
|
||||
}}
|
||||
formClassName={classes.formClass}
|
||||
/>;
|
||||
}
|
||||
GoogleDatabaseDetails.propTypes = {
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import _ from 'lodash';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React, { useEffect } from 'react';
|
||||
import PgTable from 'sources/components/PgTable';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import getApiInstance from 'sources/api_instance';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { getURL } from '../../../static/utils/utils';
|
||||
import Loader from 'sources/components/Loader';
|
||||
import EmptyPanelMessage from '../../../../static/js/components/EmptyPanelMessage';
|
||||
|
@ -22,8 +22,9 @@ import withStandardTabInfo from '../../../../static/js/helpers/withStandardTabIn
|
|||
import { BROWSER_PANELS } from '../../../../browser/static/js/constants';
|
||||
import { usePgAdmin } from '../../../../static/js/BrowserComponent';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
emptyPanel: {
|
||||
const Root = styled('div')(({theme}) => ({
|
||||
height : '100%',
|
||||
'& .Dependencies-emptyPanel': {
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
background: theme.otherVars.emptySpaceBg,
|
||||
|
@ -31,26 +32,6 @@ const useStyles = makeStyles((theme) => ({
|
|||
padding: '8px',
|
||||
display: 'flex',
|
||||
},
|
||||
panelIcon: {
|
||||
width: '80%',
|
||||
margin: '0 auto',
|
||||
marginTop: '25px !important',
|
||||
position: 'relative',
|
||||
textAlign: 'center',
|
||||
},
|
||||
panelMessage: {
|
||||
marginLeft: '0.5rem',
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
autoResizer: {
|
||||
height: '100% !important',
|
||||
width: '100% !important',
|
||||
background: theme.palette.grey[400],
|
||||
padding: '7.5px',
|
||||
overflow: 'auto !important',
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
},
|
||||
}));
|
||||
|
||||
function parseData(data, node) {
|
||||
|
@ -75,7 +56,7 @@ function parseData(data, node) {
|
|||
}
|
||||
|
||||
function Dependencies({ nodeData, nodeItem, node, treeNodeInfo, isActive, isStale, setIsStale }) {
|
||||
const classes = useStyles();
|
||||
|
||||
const [tableData, setTableData] = React.useState([]);
|
||||
const [loaderText, setLoaderText] = React.useState('');
|
||||
const [msg, setMsg] = React.useState('');
|
||||
|
@ -164,23 +145,22 @@ function Dependencies({ nodeData, nodeItem, node, treeNodeInfo, isActive, isStal
|
|||
}, [isActive, isStale]);
|
||||
|
||||
return (
|
||||
<>
|
||||
(<Root>
|
||||
{tableData.length > 0 ? (
|
||||
<PgTable
|
||||
className={classes.autoResizer}
|
||||
columns={columns}
|
||||
data={tableData}
|
||||
msg={msg}
|
||||
type={gettext('panel')}
|
||||
></PgTable>
|
||||
) : (
|
||||
<div className={classes.emptyPanel}>
|
||||
<div className='Dependencies-emptyPanel'>
|
||||
{loaderText ? (<Loader message={loaderText}/>) :
|
||||
<EmptyPanelMessage text={gettext(msg)}/>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</Root>)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import _ from 'lodash';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React, { useEffect } from 'react';
|
||||
import PgTable from 'sources/components/PgTable';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import getApiInstance from 'sources/api_instance';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { getURL } from '../../../static/utils/utils';
|
||||
import Loader from 'sources/components/Loader';
|
||||
import EmptyPanelMessage from '../../../../static/js/components/EmptyPanelMessage';
|
||||
|
@ -22,8 +22,9 @@ import withStandardTabInfo from '../../../../static/js/helpers/withStandardTabIn
|
|||
import { BROWSER_PANELS } from '../../../../browser/static/js/constants';
|
||||
import { usePgAdmin } from '../../../../static/js/BrowserComponent';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
emptyPanel: {
|
||||
const Root = styled('div')(({theme}) => ({
|
||||
height : '100%',
|
||||
'& .Dependents-emptyPanel': {
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
background: theme.otherVars.emptySpaceBg,
|
||||
|
@ -31,26 +32,6 @@ const useStyles = makeStyles((theme) => ({
|
|||
padding: '8px',
|
||||
display: 'flex',
|
||||
},
|
||||
panelIcon: {
|
||||
width: '80%',
|
||||
margin: '0 auto',
|
||||
marginTop: '25px !important',
|
||||
position: 'relative',
|
||||
textAlign: 'center',
|
||||
},
|
||||
panelMessage: {
|
||||
marginLeft: '0.5rem',
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
autoResizer: {
|
||||
height: '100% !important',
|
||||
width: '100% !important',
|
||||
background: theme.palette.grey[400],
|
||||
padding: '7.5px',
|
||||
overflow: 'auto !important',
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
},
|
||||
}));
|
||||
|
||||
function parseData(data, node) {
|
||||
|
@ -75,7 +56,7 @@ function parseData(data, node) {
|
|||
}
|
||||
|
||||
function Dependents({ nodeData, nodeItem, node, treeNodeInfo, isActive, isStale, setIsStale }) {
|
||||
const classes = useStyles();
|
||||
|
||||
const [tableData, setTableData] = React.useState([]);
|
||||
const [loaderText, setLoaderText] = React.useState('');
|
||||
const [msg, setMsg] = React.useState('');
|
||||
|
@ -164,24 +145,23 @@ function Dependents({ nodeData, nodeItem, node, treeNodeInfo, isActive, isStale,
|
|||
}, [isActive, isStale]);
|
||||
|
||||
return (
|
||||
<>
|
||||
(<Root>
|
||||
{tableData.length > 0 ? (
|
||||
<PgTable
|
||||
className={classes.autoResizer}
|
||||
columns={columns}
|
||||
data={tableData}
|
||||
msg={msg}
|
||||
type={gettext('panel')}
|
||||
></PgTable>
|
||||
) : (
|
||||
<div className={classes.emptyPanel}>
|
||||
<div className='Dependents-emptyPanel'>
|
||||
{loaderText ? (<Loader message={loaderText}/>) :
|
||||
<EmptyPanelMessage text={gettext(msg)}/>
|
||||
}
|
||||
</div>
|
||||
|
||||
)}
|
||||
</>
|
||||
</Root>)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,10 +7,9 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { DefaultButton, PgButtonGroup, PgIconButton, PrimaryButton } from '../../../../../static/js/components/Buttons';
|
||||
import { useModalStyles } from '../../../../../static/js/helpers/ModalProvider';
|
||||
import CloseIcon from '@mui/icons-material/CloseRounded';
|
||||
import FolderSharedIcon from '@mui/icons-material/FolderShared';
|
||||
import FolderIcon from '@mui/icons-material/Folder';
|
||||
|
@ -23,7 +22,6 @@ import SyncRoundedIcon from '@mui/icons-material/SyncRounded';
|
|||
import CreateNewFolderRoundedIcon from '@mui/icons-material/CreateNewFolderRounded';
|
||||
import GetAppRoundedIcon from '@mui/icons-material/GetAppRounded';
|
||||
import gettext from 'sources/gettext';
|
||||
import clsx from 'clsx';
|
||||
import { FormFooterMessage, InputSelectNonSearch, InputText, MESSAGE_TYPE } from '../../../../../static/js/components/FormComponents';
|
||||
import ListView from './ListView';
|
||||
import { PgMenu, PgMenuDivider, PgMenuItem, usePgMenuGroup } from '../../../../../static/js/components/Menu';
|
||||
|
@ -39,40 +37,59 @@ import ErrorBoundary from '../../../../../static/js/helpers/ErrorBoundary';
|
|||
import { MY_STORAGE } from './FileManagerConstants';
|
||||
import _ from 'lodash';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
footerSaveAs: {
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
backgroundColor: theme.palette.background.default,
|
||||
'& .FileManager-toolbar': {
|
||||
padding: '4px',
|
||||
display: 'flex',
|
||||
...theme.mixins.panelBorder?.bottom,
|
||||
'& .FileManager-sharedStorage': {
|
||||
width: '3rem !important',
|
||||
},
|
||||
'& .FileManager-inputFilename': {
|
||||
lineHeight: 1,
|
||||
width: '100%',
|
||||
},
|
||||
'& .FileManager-inputSearch': {
|
||||
marginLeft: '4px',
|
||||
lineHeight: 1,
|
||||
width: '130px',
|
||||
},
|
||||
'& .FileManager-storageName': {
|
||||
paddingLeft: '0.2rem'
|
||||
},
|
||||
'& .FileManager-sharedIcon': {
|
||||
width: '1.3rem'
|
||||
}
|
||||
},
|
||||
'& .FileManager-footer': {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
padding: '0.5rem',
|
||||
...theme.mixins.panelBorder?.top,
|
||||
'& .FileManager-margin': {
|
||||
marginLeft: '0.25rem',
|
||||
},
|
||||
},
|
||||
'& .FileManager-footer1': {
|
||||
justifyContent: 'space-between',
|
||||
padding: '4px 8px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
'& .FileManager-formatSelect': {
|
||||
'& .MuiSelect-select': {
|
||||
paddingTop: '4px',
|
||||
paddingBottom: '4px',
|
||||
}
|
||||
},
|
||||
},
|
||||
'& .FileManager-footerSaveAs': {
|
||||
justifyContent: 'initial',
|
||||
padding: '4px 8px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
footer1: {
|
||||
justifyContent: 'space-between',
|
||||
padding: '4px 8px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
toolbar: {
|
||||
padding: '4px',
|
||||
display: 'flex',
|
||||
...theme.mixins.panelBorder?.bottom,
|
||||
},
|
||||
inputFilename: {
|
||||
lineHeight: 1,
|
||||
width: '100%',
|
||||
},
|
||||
inputSearch: {
|
||||
marginLeft: '4px',
|
||||
lineHeight: 1,
|
||||
width: '130px',
|
||||
},
|
||||
formatSelect: {
|
||||
'& .MuiSelect-select': {
|
||||
paddingTop: '4px',
|
||||
paddingBottom: '4px',
|
||||
}
|
||||
},
|
||||
replaceOverlay: {
|
||||
'& .FileManager-replaceOverlay': {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
|
@ -82,7 +99,7 @@ const useStyles = makeStyles((theme)=>({
|
|||
zIndex: 2,
|
||||
display: 'flex',
|
||||
},
|
||||
replaceDialog: {
|
||||
'& .FileManager-replaceDialog': {
|
||||
margin: 'auto',
|
||||
marginLeft: '1rem',
|
||||
marginRight: '1rem',
|
||||
|
@ -91,15 +108,6 @@ const useStyles = makeStyles((theme)=>({
|
|||
width: '100%',
|
||||
...theme.mixins.panelBorder.all,
|
||||
},
|
||||
sharedStorage: {
|
||||
width: '3rem !important',
|
||||
},
|
||||
storageName: {
|
||||
paddingLeft: '0.2rem'
|
||||
},
|
||||
sharedIcon: {
|
||||
width: '1.3rem'
|
||||
}
|
||||
}));
|
||||
|
||||
export function getComparator(sortColumn) {
|
||||
|
@ -383,15 +391,13 @@ export class FileManagerUtils {
|
|||
}
|
||||
|
||||
function ConfirmFile({text, onYes, onNo}) {
|
||||
const classes = useStyles();
|
||||
const modalClasses = useModalStyles();
|
||||
return (
|
||||
<Box className={classes.replaceOverlay}>
|
||||
<Box margin={'8px'} className={classes.replaceDialog}>
|
||||
<Box className='FileManager-replaceOverlay'>
|
||||
<Box margin={'8px'} className='FileManager-replaceDialog'>
|
||||
<Box padding={'1rem'}>{text}{}</Box>
|
||||
<Box className={modalClasses.footer}>
|
||||
<Box className='FileManager-footer'>
|
||||
<DefaultButton data-test="no" startIcon={<CloseIcon />} onClick={onNo} >{gettext('No')}</DefaultButton>
|
||||
<PrimaryButton data-test="yes" className={modalClasses.margin} startIcon={<CheckRoundedIcon />}
|
||||
<PrimaryButton data-test="yes" className='FileManager-margin' startIcon={<CheckRoundedIcon />}
|
||||
onClick={onYes} autoFocus>{gettext('Yes')}</PrimaryButton>
|
||||
</Box>
|
||||
</Box>
|
||||
|
@ -405,8 +411,8 @@ ConfirmFile.propTypes = {
|
|||
};
|
||||
|
||||
export default function FileManager({params, closeModal, onOK, onCancel, sharedStorages=[], restrictedSharedStorage=[]}) {
|
||||
const classes = useStyles();
|
||||
const modalClasses = useModalStyles();
|
||||
|
||||
|
||||
const apiObj = useMemo(()=>getApiInstance(), []);
|
||||
const fmUtilsObj = useMemo(()=>new FileManagerUtils(apiObj, params), []);
|
||||
|
||||
|
@ -694,15 +700,15 @@ export default function FileManager({params, closeModal, onOK, onCancel, sharedS
|
|||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<Box display="flex" flexDirection="column" height="100%" className={modalClasses.container}>
|
||||
<StyledBox display="flex" flexDirection="column" height="100%">
|
||||
<Box flexGrow="1" display="flex" flexDirection="column" position="relative" overflow="hidden">
|
||||
<Loader message={loaderText} />
|
||||
{Boolean(confirmText) && <ConfirmFile text={confirmText} onNo={()=>setConfirmFile([null, null])} onYes={onConfirmYes}/>}
|
||||
<Box className={classes.toolbar}>
|
||||
<Box className='FileManager-toolbar'>
|
||||
<PgButtonGroup size="small" style={{flexGrow: 1}}>
|
||||
{ sharedStorages.length > 0 &&
|
||||
<PgIconButton title={ selectedSS == MY_STORAGE ? gettext('My Storage') :gettext(selectedSS)} icon={ selectedSS == MY_STORAGE ? <><FolderIcon/><KeyboardArrowDownIcon style={{marginLeft: '-10px'}} /></> : <><FolderSharedIcon /><KeyboardArrowDownIcon style={{marginLeft: '-10px'}} /></>} splitButton
|
||||
name="menu-shared-storage" ref={sharedSRef} onClick={toggleMenu} className={classes.sharedStorage}/>
|
||||
name="menu-shared-storage" ref={sharedSRef} onClick={toggleMenu} className='FileManager-sharedStorage'/>
|
||||
}
|
||||
<PgIconButton title={gettext('Home')} onClick={async ()=>{
|
||||
await openDir(fmUtilsObj.config?.options?.homedir, selectedSS);
|
||||
|
@ -710,7 +716,7 @@ export default function FileManager({params, closeModal, onOK, onCancel, sharedS
|
|||
<PgIconButton title={gettext('Go Back')} onClick={async ()=>{
|
||||
await openDir(fmUtilsObj.dirname(fmUtilsObj.currPath), selectedSS);
|
||||
}} icon={<ArrowUpwardRoundedIcon />} disabled={!fmUtilsObj.dirname(fmUtilsObj.currPath) || showUploader} />
|
||||
<InputText size="small" className={classes.inputFilename}
|
||||
<InputText size="small" className='FileManager-inputFilename'
|
||||
data-label="file-path"
|
||||
controlProps={{maxLength: null}}
|
||||
onKeyDown={async (e)=>{
|
||||
|
@ -724,7 +730,7 @@ export default function FileManager({params, closeModal, onOK, onCancel, sharedS
|
|||
await openDir(path, selectedSS);
|
||||
}} icon={<SyncRoundedIcon />} disabled={showUploader} />
|
||||
</PgButtonGroup>
|
||||
<InputText type="search" className={classes.inputSearch} data-label="search" placeholder={gettext('Search')} value={search} onChange={setSearch} />
|
||||
<InputText type="search" className='FileManager-inputSearch' data-label="search" placeholder={gettext('Search')} value={search} onChange={setSearch} />
|
||||
<PgButtonGroup size="small" style={{marginLeft: '4px'}}>
|
||||
{params.dialog_type == 'storage_dialog' &&
|
||||
<PgIconButton title={gettext('Download')} icon={<GetAppRoundedIcon />}
|
||||
|
@ -777,7 +783,7 @@ export default function FileManager({params, closeModal, onOK, onCancel, sharedS
|
|||
onClick={async (option)=> {
|
||||
option.keepOpen = false;
|
||||
await changeDir(option.value);
|
||||
}}><FolderIcon className={classes.sharedIcon}/><Box className={classes.storageName}>{gettext('My Storage')}</Box></PgMenuItem>
|
||||
}}><FolderIcon className='FileManager-sharedIcon'/><Box className='FileManager-storageName'>{gettext('My Storage')}</Box></PgMenuItem>
|
||||
|
||||
{
|
||||
sharedStorages.map((ss)=> {
|
||||
|
@ -786,7 +792,7 @@ export default function FileManager({params, closeModal, onOK, onCancel, sharedS
|
|||
onClick={async(option)=> {
|
||||
option.keepOpen = false;
|
||||
await changeDir(option.value);
|
||||
}}><FolderSharedIcon className={classes.sharedIcon}/><Box className={classes.storageName}>{gettext(ss)}</Box></PgMenuItem>);
|
||||
}}><FolderSharedIcon className='FileManager-sharedIcon'/><Box className='FileManager-storageName'>{gettext(ss)}</Box></PgMenuItem>);
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -810,16 +816,16 @@ export default function FileManager({params, closeModal, onOK, onCancel, sharedS
|
|||
onItemSelect={onItemSelect} />}
|
||||
<FormFooterMessage type={MESSAGE_TYPE.ERROR} message={_.escape(errorMsg)} closable onClose={()=>setErrorMsg('')} />
|
||||
{params.dialog_type == 'create_file' &&
|
||||
<Box className={clsx(modalClasses.footer, classes.footerSaveAs)}>
|
||||
<Box className={'FileManager-footer ' + 'FileManager-footerSaveAs'}>
|
||||
<span style={{whiteSpace: 'nowrap', marginRight: '4px'}}>Save As</span>
|
||||
<InputText inputRef={saveAsRef} autoFocus style={{height: '28px'}} value={saveAs} onChange={setSaveAs} />
|
||||
</Box>}
|
||||
{params.dialog_type != 'select_folder' &&
|
||||
<Box className={clsx(modalClasses.footer, classes.footer1)}>
|
||||
<Box className={'FileManager-footer ' + 'FileManager-footer1'}>
|
||||
<Box>{itemsText}</Box>
|
||||
<Box>
|
||||
<span style={{marginRight: '8px'}}>File Format</span>
|
||||
<InputSelectNonSearch value={fileType} className={classes.formatSelect}
|
||||
<InputSelectNonSearch value={fileType} className='FileManager-formatSelect'
|
||||
onChange={(e)=>{
|
||||
let val = e.target.value;
|
||||
fmUtilsObj.setFileType(val);
|
||||
|
@ -833,7 +839,7 @@ export default function FileManager({params, closeModal, onOK, onCancel, sharedS
|
|||
</Box>}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box className={modalClasses.footer}>
|
||||
<Box className='FileManager-footer'>
|
||||
<PgButtonGroup style={{flexGrow: 1}}>
|
||||
</PgButtonGroup>
|
||||
<DefaultButton data-test="close" startIcon={<CloseIcon />} onClick={()=>{
|
||||
|
@ -841,10 +847,10 @@ export default function FileManager({params, closeModal, onOK, onCancel, sharedS
|
|||
closeModal();
|
||||
}} >{gettext('Cancel')}</DefaultButton>
|
||||
{params.dialog_type != 'storage_dialog' &&
|
||||
<PrimaryButton data-test="save" className={modalClasses.margin} startIcon={<CheckRoundedIcon />}
|
||||
<PrimaryButton data-test="save" className='FileManager-margin' startIcon={<CheckRoundedIcon />}
|
||||
onClick={onOkClick} disabled={okBtnDisable || showUploader}>{okBtnText}</PrimaryButton>}
|
||||
</Box>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React, {useState, useEffect, useRef, useLayoutEffect} from 'react';
|
||||
import FolderIcon from '@mui/icons-material/Folder';
|
||||
import DescriptionIcon from '@mui/icons-material/Description';
|
||||
|
@ -16,49 +16,47 @@ import StorageRoundedIcon from '@mui/icons-material/StorageRounded';
|
|||
import PropTypes from 'prop-types';
|
||||
import gettext from 'sources/gettext';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
grid: {
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
'& .GridView-grid': {
|
||||
display: 'flex',
|
||||
fontSize: '13px',
|
||||
flexWrap: 'wrap',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
gridItem: {
|
||||
width: '100px',
|
||||
margin: '4px',
|
||||
textAlign: 'center',
|
||||
position: 'relative',
|
||||
border: '1px solid transparent',
|
||||
cursor: 'pointer',
|
||||
'&[aria-selected=true]': {
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
color: theme.otherVars.qtDatagridSelectFg,
|
||||
borderColor: theme.palette.primary.main,
|
||||
'& .GridView-gridItem': {
|
||||
width: '100px',
|
||||
margin: '4px',
|
||||
textAlign: 'center',
|
||||
position: 'relative',
|
||||
border: '1px solid transparent',
|
||||
cursor: 'pointer',
|
||||
'&[aria-selected=true]': {
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
color: theme.otherVars.qtDatagridSelectFg,
|
||||
borderColor: theme.palette.primary.main,
|
||||
},
|
||||
'& .GridView-gridItemContent': {
|
||||
padding: '4px',
|
||||
'& .GridView-gridFilename': {
|
||||
overflowWrap: 'break-word',
|
||||
},
|
||||
'& .GridView-gridItemEdit': {
|
||||
border: `1px solid ${theme.otherVars.inputBorderColor}`,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
},
|
||||
'& .GridView-protected': {
|
||||
height: '1.25rem',
|
||||
width: '1.25rem',
|
||||
position: 'absolute',
|
||||
left: '52px',
|
||||
color: theme.palette.error.main,
|
||||
backgroundColor: 'inherit',
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
gridItemContent: {
|
||||
padding: '4px',
|
||||
},
|
||||
gridFilename: {
|
||||
overflowWrap: 'break-word',
|
||||
},
|
||||
gridItemEdit: {
|
||||
border: `1px solid ${theme.otherVars.inputBorderColor}`,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
},
|
||||
protected: {
|
||||
height: '1.25rem',
|
||||
width: '1.25rem',
|
||||
position: 'absolute',
|
||||
left: '52px',
|
||||
color: theme.palette.error.main,
|
||||
backgroundColor: 'inherit',
|
||||
}
|
||||
}));
|
||||
|
||||
export function ItemView({idx, row, selected, onItemSelect, onItemEnter, onEditComplete}) {
|
||||
const classes = useStyles();
|
||||
const editMode = Boolean(onEditComplete);
|
||||
const fileNameRef = useRef();
|
||||
|
||||
|
@ -98,14 +96,14 @@ export function ItemView({idx, row, selected, onItemSelect, onItemEnter, onEditC
|
|||
}
|
||||
|
||||
return (
|
||||
<div tabIndex="-1" className={classes.gridItem} aria-selected={selected} onClick={()=>onItemSelect(idx)} onDoubleClick={()=>onItemEnter(row)} onKeyDown={handleItemKeyDown} role="gridcell">
|
||||
<div className={classes.gridItemContent}>
|
||||
<div tabIndex="-1" className='GridView-gridItem' aria-selected={selected} onClick={()=>onItemSelect(idx)} onDoubleClick={()=>onItemEnter(row)} onKeyDown={handleItemKeyDown} role="gridcell">
|
||||
<div className='GridView-gridItemContent'>
|
||||
<div>
|
||||
{icon}
|
||||
{Boolean(row.Protected) && <LockRoundedIcon className={classes.protected}/>}
|
||||
{Boolean(row.Protected) && <LockRoundedIcon className='GridView-protected'/>}
|
||||
</div>
|
||||
<div tabIndex="-1" ref={fileNameRef} onKeyDown={handleEditKeyDown} onBlur={()=>onEditComplete?.(row)}
|
||||
className={editMode ? classes.gridItemEdit : classes.gridFilename} suppressContentEditableWarning={true}
|
||||
className={editMode ? 'GridView-gridItemEdit' : 'GridView-gridFilename'} suppressContentEditableWarning={true}
|
||||
contentEditable={editMode} data-test="filename-div" role={editMode ? 'textbox' : 'none'}>{row['Filename']}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -121,7 +119,7 @@ ItemView.propTypes = {
|
|||
};
|
||||
|
||||
export default function GridView({items, operation, onItemSelect, onItemEnter}) {
|
||||
const classes = useStyles();
|
||||
|
||||
const [selectedIdx, setSelectedIdx] = useState(null);
|
||||
const gridRef = useRef();
|
||||
|
||||
|
@ -138,15 +136,15 @@ export default function GridView({items, operation, onItemSelect, onItemEnter})
|
|||
}
|
||||
|
||||
return (
|
||||
<Box flexGrow={1} overflow="hidden auto" id="grid">
|
||||
<div ref={gridRef} className={classes.grid}>
|
||||
<StyledBox flexGrow={1} overflow="hidden auto" id="grid">
|
||||
<div ref={gridRef} className='GridView-grid'>
|
||||
{items.map((item, i)=>(
|
||||
<ItemView key={item.Filename} idx={i} row={item} selected={selectedIdx==i} onItemSelect={setSelectedIdx}
|
||||
onItemEnter={onItemEnter} onEditComplete={operation.idx==i ? onEditComplete : null} />)
|
||||
)}
|
||||
</div>
|
||||
{items.length == 0 && <Box textAlign="center" p={1}>{gettext('No files/folders found')}</Box>}
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,4 @@
|
|||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import PgReactDataGrid from '../../../../../static/js/components/PgReactDataGrid';
|
||||
import FolderIcon from '@mui/icons-material/Folder';
|
||||
|
@ -16,8 +8,9 @@ import LockRoundedIcon from '@mui/icons-material/LockRounded';
|
|||
import PropTypes from 'prop-types';
|
||||
import gettext from 'sources/gettext';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
grid: {
|
||||
|
||||
const StyledPgReactDataGrid = styled(PgReactDataGrid)(({theme}) => ({
|
||||
'&.Grid-grid': {
|
||||
fontSize: '13px',
|
||||
'& .rdg-header-row': {
|
||||
'& .rdg-cell': {
|
||||
|
@ -31,39 +24,39 @@ const useStyles = makeStyles((theme)=>({
|
|||
'&.rdg-editor-container': {
|
||||
padding: '0px',
|
||||
},
|
||||
},
|
||||
'& .Grid-input': {
|
||||
appearance: 'none',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
verticalAlign: 'top',
|
||||
outline: 'none',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
border: 0,
|
||||
boxShadow: 'inset 0 0 0 1.5px '+theme.palette.primary.main,
|
||||
padding: '0 2px',
|
||||
'::selection': {
|
||||
background: theme.palette.primary.light,
|
||||
}
|
||||
},
|
||||
'& .Grid-protected': {
|
||||
height: '0.75rem',
|
||||
width: '0.75rem',
|
||||
position: 'absolute',
|
||||
left: '14px',
|
||||
top: '5px',
|
||||
color: theme.palette.error.main,
|
||||
backgroundColor: 'inherit',
|
||||
}
|
||||
}
|
||||
},
|
||||
input: {
|
||||
appearance: 'none',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
verticalAlign: 'top',
|
||||
outline: 'none',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
border: 0,
|
||||
boxShadow: 'inset 0 0 0 1.5px '+theme.palette.primary.main,
|
||||
padding: '0 2px',
|
||||
'::selection': {
|
||||
background: theme.palette.primary.light,
|
||||
}
|
||||
},
|
||||
protected: {
|
||||
height: '0.75rem',
|
||||
width: '0.75rem',
|
||||
position: 'absolute',
|
||||
left: '14px',
|
||||
top: '5px',
|
||||
color: theme.palette.error.main,
|
||||
backgroundColor: 'inherit',
|
||||
}
|
||||
}));
|
||||
|
||||
export const GridContextUtils = React.createContext();
|
||||
|
||||
export function FileNameEditor({row, column, onRowChange, onClose}) {
|
||||
const classes = useStyles();
|
||||
|
||||
const value = row[column.key] ?? '';
|
||||
const [localVal, setLocalVal] = React.useState(value);
|
||||
const localValRef = useRef(localVal);
|
||||
|
@ -84,7 +77,7 @@ export function FileNameEditor({row, column, onRowChange, onClose}) {
|
|||
};
|
||||
return (
|
||||
<input
|
||||
className={classes.input}
|
||||
className='Grid-input'
|
||||
value={localVal}
|
||||
onChange={(e)=>{
|
||||
setLocalVal(e.target.value);
|
||||
|
@ -102,7 +95,7 @@ FileNameEditor.propTypes = {
|
|||
onClose: PropTypes.func,
|
||||
};
|
||||
function FileNameFormatter({row}) {
|
||||
const classes = useStyles();
|
||||
|
||||
let icon = <DescriptionIcon style={{fontSize: '1.2rem'}} />;
|
||||
if(row.file_type == 'dir') {
|
||||
icon = <FolderIcon style={{fontSize: '1.2rem'}} />;
|
||||
|
@ -111,7 +104,7 @@ function FileNameFormatter({row}) {
|
|||
}
|
||||
return <>
|
||||
{icon}
|
||||
{Boolean(row.Protected) && <LockRoundedIcon className={classes.protected}/>}
|
||||
{Boolean(row.Protected) && <LockRoundedIcon className='Grid-protected'/>}
|
||||
<span style={{marginLeft: '4px'}}>{row['Filename']}</span>
|
||||
</>;
|
||||
}
|
||||
|
@ -142,7 +135,7 @@ const columns = [
|
|||
|
||||
|
||||
export default function ListView({items, operation, ...props}) {
|
||||
const classes = useStyles();
|
||||
|
||||
const gridRef = useRef();
|
||||
|
||||
useEffect(()=>{
|
||||
|
@ -157,10 +150,10 @@ export default function ListView({items, operation, ...props}) {
|
|||
}, [gridRef.current?.element]);
|
||||
|
||||
return (
|
||||
<PgReactDataGrid
|
||||
<StyledPgReactDataGrid
|
||||
gridRef={gridRef}
|
||||
id="list"
|
||||
className={classes.grid}
|
||||
className='Grid-grid'
|
||||
hasSelectColumn={false}
|
||||
columns={columns}
|
||||
rows={items}
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import React, { useCallback, useReducer, useEffect, useMemo } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { Box, List, ListItem } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import CloseIcon from '@mui/icons-material/CloseRounded';
|
||||
import { PgIconButton } from '../../../../../static/js/components/Buttons';
|
||||
import gettext from 'sources/gettext';
|
||||
|
@ -18,20 +18,19 @@ import convert from 'convert-units';
|
|||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 1,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: '4px',
|
||||
},
|
||||
uploadArea: {
|
||||
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 1,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: '4px',
|
||||
'& .Uploader-uploadArea': {
|
||||
border: `1px dashed ${theme.palette.grey[600]}`,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
|
@ -42,19 +41,12 @@ const useStyles = makeStyles((theme)=>({
|
|||
textAlign: 'center',
|
||||
padding: '4px',
|
||||
},
|
||||
uploadFilesRoot: {
|
||||
'& .Uploader-uploadFilesRoot': {
|
||||
width: '350px',
|
||||
border: `1px dashed ${theme.palette.grey[600]}`,
|
||||
borderLeft: 'none',
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto'
|
||||
},
|
||||
uploadProgress: {
|
||||
position: 'unset',
|
||||
padding: 0,
|
||||
},
|
||||
uploadPending: {
|
||||
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -131,7 +123,7 @@ UploadedFile.propTypes = {
|
|||
|
||||
|
||||
export default function Uploader({fmUtilsObj, onClose}) {
|
||||
const classes = useStyles();
|
||||
|
||||
const [files, dispatchFileAction] = useReducer(filesReducer, []);
|
||||
const onDrop = useCallback(acceptedFiles => {
|
||||
dispatchFileAction({
|
||||
|
@ -173,18 +165,18 @@ export default function Uploader({fmUtilsObj, onClose}) {
|
|||
}, [files.length]);
|
||||
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<StyledBox>
|
||||
<Box display="flex" justifyContent="flex-end">
|
||||
<PgIconButton title={gettext('Close')} icon={<CloseIcon />} size="xs" noBorder onClick={onClose} />
|
||||
</Box>
|
||||
<Box display="flex" flexGrow={1} overflow="hidden">
|
||||
<Box className={classes.uploadArea} {...getRootProps()}>
|
||||
<Box className='Uploader-uploadArea' {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
<Box>{gettext('Drop files here, or click to select files.')}</Box>
|
||||
<Box>{gettext('The file size limit (per file) is %s MB.', fmUtilsObj.config?.upload?.fileSizeLimit)}</Box>
|
||||
</Box>
|
||||
{files.length > 0 &&
|
||||
<Box className={classes.uploadFilesRoot}>
|
||||
<Box className='Uploader-uploadFilesRoot'>
|
||||
<List>
|
||||
{files.map((upfile)=>(
|
||||
<UploadedFile key={upfile.id} upfile={upfile} removeFile={async ()=>{
|
||||
|
@ -197,7 +189,7 @@ export default function Uploader({fmUtilsObj, onClose}) {
|
|||
</List>
|
||||
</Box>}
|
||||
</Box>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
Uploader.propTypes = {
|
||||
|
|
|
@ -7,13 +7,12 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import React from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import getApiInstance from 'sources/api_instance';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { Box } from '@mui/material';
|
||||
import { generateCollectionURL } from '../../browser/static/js/node_ajax';
|
||||
import gettext from 'sources/gettext';
|
||||
import PgTable from 'sources/components/PgTable';
|
||||
import Theme from 'sources/Theme';
|
||||
import PropTypes from 'prop-types';
|
||||
import { PgButtonGroup, PgIconButton } from '../../static/js/components/Buttons';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
|
@ -25,46 +24,16 @@ import { evalFunc } from '../../static/js/utils';
|
|||
import { usePgAdmin } from '../../static/js/BrowserComponent';
|
||||
import { getSwitchCell } from '../../static/js/components/PgReactTableStyled';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
emptyPanel: {
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
height: '100%',
|
||||
'&.CollectionNodeProperties-emptyPanel': {
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
background: theme.otherVars.emptySpaceBg,
|
||||
overflow: 'auto',
|
||||
padding: '8px',
|
||||
display: 'flex',
|
||||
},
|
||||
panelIcon: {
|
||||
width: '80%',
|
||||
margin: '0 auto',
|
||||
marginTop: '25px !important',
|
||||
position: 'relative',
|
||||
textAlign: 'center',
|
||||
},
|
||||
panelMessage: {
|
||||
marginLeft: '0.5rem',
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
searchPadding: {
|
||||
flex: 2.5
|
||||
},
|
||||
searchInput: {
|
||||
flex: 1,
|
||||
margin: '4 0 4 0',
|
||||
borderLeft: 'none',
|
||||
paddingLeft: 5
|
||||
},
|
||||
propertiesPanel: {
|
||||
height: '100%'
|
||||
},
|
||||
autoResizer: {
|
||||
height: '100% !important',
|
||||
width: '100% !important',
|
||||
background: theme.palette.grey[400],
|
||||
padding: '8px',
|
||||
overflow: 'hidden !important',
|
||||
overflowX: 'auto !important'
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
export default function CollectionNodeProperties({
|
||||
|
@ -76,9 +45,7 @@ export default function CollectionNodeProperties({
|
|||
isStale,
|
||||
setIsStale
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
const pgAdmin = usePgAdmin();
|
||||
|
||||
const [data, setData] = React.useState([]);
|
||||
const [infoMsg, setInfoMsg] = React.useState('Please select an object in the tree view.');
|
||||
const [selectedObject, setSelectedObject] = React.useState({});
|
||||
|
@ -193,7 +160,6 @@ export default function CollectionNodeProperties({
|
|||
}
|
||||
|
||||
setLoaderText(gettext('Loading...'));
|
||||
|
||||
if (!_.isUndefined(nodeObj.getSchema)) {
|
||||
schemaRef.current = nodeObj.getSchema?.(treeNodeInfo, nodeData);
|
||||
schemaRef.current?.fields.forEach((field) => {
|
||||
|
@ -232,7 +198,6 @@ export default function CollectionNodeProperties({
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
api({
|
||||
url: url,
|
||||
type: 'GET',
|
||||
|
@ -307,15 +272,14 @@ export default function CollectionNodeProperties({
|
|||
};
|
||||
|
||||
return (
|
||||
<Theme className='obj_properties'>
|
||||
<>
|
||||
<Loader message={loaderText}/>
|
||||
<Box className={classes.propertiesPanel}>
|
||||
<StyledBox>
|
||||
{data.length > 0 ?
|
||||
(
|
||||
<PgTable
|
||||
hasSelectRow={!('catalog' in treeNodeInfo) && (nodeData.label !== 'Catalogs') && _.isUndefined(node?.canSelect)}
|
||||
CustomHeader={CustomHeader}
|
||||
className={classes.autoResizer}
|
||||
columns={pgTableColumns}
|
||||
data={data}
|
||||
type={'panel'}
|
||||
|
@ -326,13 +290,13 @@ export default function CollectionNodeProperties({
|
|||
)
|
||||
:
|
||||
(
|
||||
<div className={classes.emptyPanel}>
|
||||
<div className='CollectionNodeProperties-emptyPanel'>
|
||||
<EmptyPanelMessage text={gettext(infoMsg)}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</Box>
|
||||
</Theme>
|
||||
</StyledBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import CollectionNodeProperties from './CollectionNodeProperties';
|
||||
import ErrorBoundary from '../../static/js/helpers/ErrorBoundary';
|
||||
import withStandardTabInfo from '../../static/js/helpers/withStandardTabInfo';
|
||||
|
@ -16,23 +17,19 @@ import ObjectNodeProperties from './ObjectNodeProperties';
|
|||
import EmptyPanelMessage from '../../static/js/components/EmptyPanelMessage';
|
||||
import gettext from 'sources/gettext';
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { usePgAdmin } from '../../static/js/BrowserComponent';
|
||||
import PropTypes from 'prop-types';
|
||||
import _ from 'lodash';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
height: '100%',
|
||||
background: theme.otherVars.emptySpaceBg,
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
height: '100%',
|
||||
background: theme.otherVars.emptySpaceBg,
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
}));
|
||||
|
||||
function Properties(props) {
|
||||
const isCollection = props.nodeData?._type?.startsWith('coll-') || props.nodeData?._type == 'dbms_job_scheduler';
|
||||
const classes = useStyles();
|
||||
const pgAdmin = usePgAdmin();
|
||||
let noPropertyMsg = '';
|
||||
|
||||
|
@ -44,27 +41,27 @@ function Properties(props) {
|
|||
|
||||
if(noPropertyMsg) {
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<StyledBox>
|
||||
<Box margin={'4px auto'}>
|
||||
<EmptyPanelMessage text={noPropertyMsg} />
|
||||
</Box>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
if(isCollection) {
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<StyledBox>
|
||||
<ErrorBoundary>
|
||||
<CollectionNodeProperties
|
||||
{...props}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<StyledBox>
|
||||
<ErrorBoundary>
|
||||
<ObjectNodeProperties
|
||||
{...props}
|
||||
|
@ -77,7 +74,7 @@ function Properties(props) {
|
|||
}}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,29 +8,29 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { generateNodeUrl } from '../../../../browser/static/js/node_ajax';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import getApiInstance from 'sources/api_instance';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import CodeMirror from '../../../../static/js/components/ReactCodeMirror';
|
||||
import Loader from 'sources/components/Loader';
|
||||
import withStandardTabInfo from '../../../../static/js/helpers/withStandardTabInfo';
|
||||
import { BROWSER_PANELS } from '../../../../browser/static/js/constants';
|
||||
import { usePgAdmin } from '../../../../static/js/BrowserComponent';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
textArea: {
|
||||
|
||||
const Root = styled('div')(({theme}) => ({
|
||||
'& .SQL-textArea': {
|
||||
height: '100% !important',
|
||||
width: '100% !important',
|
||||
background: theme.palette.grey[400],
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
function SQL({nodeData, node, treeNodeInfo, isActive, isStale, setIsStale}) {
|
||||
const classes = useStyles();
|
||||
const did = ((!_.isUndefined(treeNodeInfo)) && (!_.isUndefined(treeNodeInfo['database']))) ? treeNodeInfo['database']._id: 0;
|
||||
const dbConnected = !_.isUndefined(treeNodeInfo) && !_.isUndefined(treeNodeInfo['database']) ? treeNodeInfo.database.connected: false;
|
||||
const [nodeSQL, setNodeSQL] = React.useState('');
|
||||
|
@ -92,15 +92,15 @@ function SQL({nodeData, node, treeNodeInfo, isActive, isStale, setIsStale}) {
|
|||
}, [isStale, isActive, nodeData?.id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
(<Root style={{height: '100%'}} >
|
||||
<Loader message={loaderText}/>
|
||||
<CodeMirror
|
||||
className={classes.textArea}
|
||||
className='SQL-textArea'
|
||||
value={nodeSQL}
|
||||
readonly={true}
|
||||
showCopyBtn
|
||||
/>
|
||||
</>
|
||||
</Root>)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import _ from 'lodash';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React, { useEffect } from 'react';
|
||||
import PgTable from 'sources/components/PgTable';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import getApiInstance from 'sources/api_instance';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { getURL } from '../../../static/utils/utils';
|
||||
import Loader from 'sources/components/Loader';
|
||||
import EmptyPanelMessage from '../../../../static/js/components/EmptyPanelMessage';
|
||||
|
@ -22,36 +22,16 @@ import withStandardTabInfo from '../../../../static/js/helpers/withStandardTabIn
|
|||
import { BROWSER_PANELS } from '../../../../browser/static/js/constants';
|
||||
import { usePgAdmin } from '../../../../static/js/BrowserComponent';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
emptyPanel: {
|
||||
const Root = styled('div')(({theme}) => ({
|
||||
height : '100%',
|
||||
'& .Statistics-emptyPanel': {
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
background: theme.otherVars.emptySpaceBg,
|
||||
overflow: 'auto',
|
||||
padding: '8px',
|
||||
display: 'flex',
|
||||
},
|
||||
panelIcon: {
|
||||
width: '80%',
|
||||
margin: '0 auto',
|
||||
marginTop: '25px !important',
|
||||
position: 'relative',
|
||||
textAlign: 'center',
|
||||
},
|
||||
panelMessage: {
|
||||
marginLeft: '0.5rem',
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
autoResizer: {
|
||||
height: '100% !important',
|
||||
width: '100% !important',
|
||||
background: theme.palette.grey[400],
|
||||
padding: '7.5px',
|
||||
overflowX: 'auto !important',
|
||||
overflowY: 'hidden !important',
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
function getColumn(data, singleLineStatistics, prettifyFields=[]) {
|
||||
|
@ -146,9 +126,7 @@ function createSingleLineStatistics(data, prettifyFields) {
|
|||
}
|
||||
|
||||
function Statistics({ nodeData, nodeItem, node, treeNodeInfo, isActive, isStale, setIsStale }) {
|
||||
const classes = useStyles();
|
||||
const [tableData, setTableData] = React.useState([]);
|
||||
|
||||
const [msg, setMsg] = React.useState('');
|
||||
const [loaderText, setLoaderText] = React.useState('');
|
||||
const [columns, setColumns] = React.useState([
|
||||
|
@ -232,22 +210,21 @@ function Statistics({ nodeData, nodeItem, node, treeNodeInfo, isActive, isStale,
|
|||
}, [isStale, isActive, nodeData?.id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Root>
|
||||
{tableData.length > 0 ? (
|
||||
<PgTable
|
||||
className={classes.autoResizer}
|
||||
columns={columns}
|
||||
data={tableData}
|
||||
msg={msg}
|
||||
type={'panel'}
|
||||
></PgTable>
|
||||
) : (
|
||||
<div className={classes.emptyPanel}>
|
||||
<div className='Statistics-emptyPanel'>
|
||||
<Loader message={loaderText} />
|
||||
<EmptyPanelMessage text={gettext(msg)}/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,25 +8,81 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import gettext from 'sources/gettext';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import _ from 'lodash';
|
||||
import url_for from 'sources/url_for';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import { FileType } from 'react-aspen';
|
||||
import { Box } from '@mui/material';
|
||||
import PropTypes from 'prop-types';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import SchemaView from '../../../../static/js/SchemaView';
|
||||
import getApiInstance from '../../../../static/js/api_instance';
|
||||
import CloseSharpIcon from '@mui/icons-material/CloseSharp';
|
||||
import HelpIcon from '@mui/icons-material/HelpRounded';
|
||||
import SaveSharpIcon from '@mui/icons-material/SaveSharp';
|
||||
import clsx from 'clsx';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import { DefaultButton, PgIconButton, PrimaryButton } from '../../../../static/js/components/Buttons';
|
||||
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
|
||||
import { getBinaryPathSchema } from '../../../../browser/server_groups/servers/static/js/binary_path.ui';
|
||||
import usePreferences from '../store';
|
||||
|
||||
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
'& .PreferencesComponent-root': {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
height: '100%',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
overflow: 'hidden',
|
||||
'&$disabled': {
|
||||
color: '#ddd',
|
||||
},
|
||||
'& .PreferencesComponent-body': {
|
||||
borderColor: theme.otherVars.borderColor,
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
height: '100%',
|
||||
minHeight: 0,
|
||||
overflow: 'hidden',
|
||||
'& .PreferencesComponent-treeContainer': {
|
||||
flexBasis: '25%',
|
||||
alignItems: 'flex-start',
|
||||
paddingLeft: '5px',
|
||||
minHeight: 0,
|
||||
flexGrow: 1,
|
||||
'& .PreferencesComponent-tree': {
|
||||
height: '100%',
|
||||
flexGrow: 1
|
||||
},
|
||||
},
|
||||
'& .PreferencesComponent-preferencesContainer': {
|
||||
flexBasis: '75%',
|
||||
padding: '5px',
|
||||
borderColor: theme.otherVars.borderColor + '!important',
|
||||
borderLeft: '1px solid',
|
||||
position: 'relative',
|
||||
height: '100%',
|
||||
paddingTop: '5px',
|
||||
overflow: 'auto',
|
||||
},
|
||||
},
|
||||
'& .PreferencesComponent-footer': {
|
||||
borderTop: `1px solid ${theme.otherVars.inputBorderColor} !important`,
|
||||
padding: '0.5rem',
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
background: theme.otherVars.headerBg,
|
||||
'& .PreferencesComponent-actionBtn': {
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
'& .PreferencesComponent-buttonMargin': {
|
||||
marginLeft: '0.5em'
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
class PreferencesSchema extends BaseUISchema {
|
||||
constructor(initValues = {}, schemaFields = []) {
|
||||
super({
|
||||
|
@ -49,81 +105,6 @@ class PreferencesSchema extends BaseUISchema {
|
|||
}
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme) =>
|
||||
({
|
||||
root: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
height: '100%',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
overflow: 'hidden',
|
||||
'&$disabled': {
|
||||
color: '#ddd',
|
||||
}
|
||||
},
|
||||
body: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
minHeight: 0,
|
||||
},
|
||||
preferences: {
|
||||
borderColor: theme.otherVars.borderColor,
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
height: '100%',
|
||||
minHeight: 0,
|
||||
overflow: 'hidden'
|
||||
|
||||
},
|
||||
treeContainer: {
|
||||
flexBasis: '25%',
|
||||
alignItems: 'flex-start',
|
||||
paddingLeft: '5px',
|
||||
minHeight: 0,
|
||||
flexGrow: 1
|
||||
},
|
||||
tree: {
|
||||
height: '100%',
|
||||
flexGrow: 1
|
||||
},
|
||||
preferencesContainer: {
|
||||
flexBasis: '75%',
|
||||
padding: '5px',
|
||||
borderColor: theme.otherVars.borderColor + '!important',
|
||||
borderLeft: '1px solid',
|
||||
position: 'relative',
|
||||
height: '100%',
|
||||
paddingTop: '5px',
|
||||
overflow: 'auto'
|
||||
},
|
||||
actionBtn: {
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
buttonMargin: {
|
||||
marginLeft: '0.5em'
|
||||
},
|
||||
footer: {
|
||||
borderTop: `1px solid ${theme.otherVars.inputBorderColor} !important`,
|
||||
padding: '0.5rem',
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
background: theme.otherVars.headerBg,
|
||||
},
|
||||
customTreeClass: {
|
||||
'& .react-checkbox-tree': {
|
||||
height: '100% !important',
|
||||
border: 'none !important',
|
||||
},
|
||||
},
|
||||
preferencesTree: {
|
||||
height: 'calc(100% - 50px)',
|
||||
minHeight: 0
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
function RightPanel({ schema, ...props }) {
|
||||
let initData = () => new Promise((resolve, reject) => {
|
||||
|
@ -157,7 +138,7 @@ RightPanel.propTypes = {
|
|||
|
||||
|
||||
export default function PreferencesComponent({ ...props }) {
|
||||
const classes = useStyles();
|
||||
|
||||
const [disableSave, setDisableSave] = React.useState(true);
|
||||
const prefSchema = React.useRef(new PreferencesSchema({}, []));
|
||||
const prefChangedData = React.useRef({});
|
||||
|
@ -594,18 +575,17 @@ export default function PreferencesComponent({ ...props }) {
|
|||
};
|
||||
|
||||
return (
|
||||
<Box height={'100%'}>
|
||||
<Box className={classes.root}>
|
||||
<Box className={clsx(classes.preferences)}>
|
||||
<Box className={clsx(classes.treeContainer)} >
|
||||
|
||||
<Box className={clsx(classes.tree)} id={'treeContainer'} tabIndex={0}>
|
||||
<StyledBox height={'100%'}>
|
||||
<Box className='PreferencesComponent-root'>
|
||||
<Box className='PreferencesComponent-body'>
|
||||
<Box className='PreferencesComponent-treeContainer' >
|
||||
<Box className='PreferencesComponent-tree' id={'treeContainer'} tabIndex={0}>
|
||||
{
|
||||
useMemo(() => (prefTreeData && props.renderTree(prefTreeData)), [prefTreeData])
|
||||
}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box className={clsx(classes.preferencesContainer)}>
|
||||
<Box className='PreferencesComponent-preferencesContainer'>
|
||||
{
|
||||
prefSchema.current && loadTree > 0 &&
|
||||
<RightPanel schema={prefSchema.current} initValues={initValues} onDataChange={(changedData) => {
|
||||
|
@ -615,21 +595,21 @@ export default function PreferencesComponent({ ...props }) {
|
|||
}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box className={classes.footer}>
|
||||
<Box className='PreferencesComponent-footer'>
|
||||
<Box>
|
||||
<PgIconButton data-test="dialog-help" onClick={onDialogHelp} icon={<HelpIcon />} title={gettext('Help for this dialog.')} />
|
||||
</Box>
|
||||
<Box className={classes.actionBtn} marginLeft="auto">
|
||||
<DefaultButton className={classes.buttonMargin} onClick={() => { props.closeModal();}} startIcon={<CloseSharpIcon onClick={() => { props.closeModal();}} />}>
|
||||
<Box className='PreferencesComponent-actionBtn' marginLeft="auto">
|
||||
<DefaultButton className='PreferencesComponent-buttonMargin' onClick={() => { props.closeModal();}} startIcon={<CloseSharpIcon onClick={() => { props.closeModal();}} />}>
|
||||
{gettext('Cancel')}
|
||||
</DefaultButton>
|
||||
<PrimaryButton className={classes.buttonMargin} startIcon={<SaveSharpIcon />} disabled={disableSave} onClick={() => { savePreferences(prefChangedData, initValues); }}>
|
||||
<PrimaryButton className='PreferencesComponent-buttonMargin' startIcon={<SaveSharpIcon />} disabled={disableSave} onClick={() => { savePreferences(prefChangedData, initValues); }}>
|
||||
{gettext('Save')}
|
||||
</PrimaryButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box >
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { PrimaryButton } from './components/Buttons';
|
||||
import { PgMenu, PgMenuDivider, PgMenuItem, PgSubMenu } from './components/Menu';
|
||||
|
@ -15,51 +15,50 @@ import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
|||
import AccountCircleRoundedIcon from '@mui/icons-material/AccountCircleRounded';
|
||||
import { usePgAdmin } from '../../static/js/BrowserComponent';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
height: '30px',
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: theme.palette.primary.contrastText,
|
||||
padding: '0 0.5rem',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
logo: {
|
||||
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
height: '30px',
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: theme.palette.primary.contrastText,
|
||||
padding: '0 0.5rem',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
'& .AppMenuBar-logo': {
|
||||
width: '96px',
|
||||
height: '100%',
|
||||
/*
|
||||
* Using the SVG postgresql logo, modified to set the background color as #FFF
|
||||
* https://wiki.postgresql.org/images/9/90/PostgreSQL_logo.1color_blue.svg
|
||||
* background: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3C!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 42 42' style='enable-background:new 0 0 42 42;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bstroke:%23000000;stroke-width:3.3022;%7D .st1%7Bfill:%23336791;%7D .st2%7Bfill:none;stroke:%23FFFFFF;stroke-width:1.1007;stroke-linecap:round;stroke-linejoin:round;%7D .st3%7Bfill:none;stroke:%23FFFFFF;stroke-width:1.1007;stroke-linecap:round;stroke-linejoin:bevel;%7D .st4%7Bfill:%23FFFFFF;stroke:%23FFFFFF;stroke-width:0.3669;%7D .st5%7Bfill:%23FFFFFF;stroke:%23FFFFFF;stroke-width:0.1835;%7D .st6%7Bfill:none;stroke:%23FFFFFF;stroke-width:0.2649;stroke-linecap:round;stroke-linejoin:round;%7D%0A%3C/style%3E%3Cg id='orginal'%3E%3C/g%3E%3Cg id='Layer_x0020_3'%3E%3Cpath class='st0' d='M31.3,30c0.3-2.1,0.2-2.4,1.7-2.1l0.4,0c1.2,0.1,2.8-0.2,3.7-0.6c2-0.9,3.1-2.4,1.2-2 c-4.4,0.9-4.7-0.6-4.7-0.6c4.7-7,6.7-15.8,5-18c-4.6-5.9-12.6-3.1-12.7-3l0,0c-0.9-0.2-1.9-0.3-3-0.3c-2,0-3.5,0.5-4.7,1.4 c0,0-14.3-5.9-13.6,7.4c0.1,2.8,4,21.3,8.7,15.7c1.7-2,3.3-3.8,3.3-3.8c0.8,0.5,1.8,0.8,2.8,0.7l0.1-0.1c0,0.3,0,0.5,0,0.8 c-1.2,1.3-0.8,1.6-3.2,2.1c-2.4,0.5-1,1.4-0.1,1.6c1.1,0.3,3.7,0.7,5.5-1.8l-0.1,0.3c0.5,0.4,0.4,2.7,0.5,4.4 c0.1,1.7,0.2,3.2,0.5,4.1c0.3,0.9,0.7,3.3,3.9,2.6C29.1,38.3,31.1,37.5,31.3,30'/%3E%3Cpath class='st1' d='M38.3,25.3c-4.4,0.9-4.7-0.6-4.7-0.6c4.7-7,6.7-15.8,5-18c-4.6-5.9-12.6-3.1-12.7-3l0,0 c-0.9-0.2-1.9-0.3-3-0.3c-2,0-3.5,0.5-4.7,1.4c0,0-14.3-5.9-13.6,7.4c0.1,2.8,4,21.3,8.7,15.7c1.7-2,3.3-3.8,3.3-3.8 c0.8,0.5,1.8,0.8,2.8,0.7l0.1-0.1c0,0.3,0,0.5,0,0.8c-1.2,1.3-0.8,1.6-3.2,2.1c-2.4,0.5-1,1.4-0.1,1.6c1.1,0.3,3.7,0.7,5.5-1.8 l-0.1,0.3c0.5,0.4,0.8,2.4,0.7,4.3c-0.1,1.9-0.1,3.2,0.3,4.2c0.4,1,0.7,3.3,3.9,2.6c2.6-0.6,4-2,4.2-4.5c0.1-1.7,0.4-1.5,0.5-3 l0.2-0.7c0.3-2.3,0-3.1,1.7-2.8l0.4,0c1.2,0.1,2.8-0.2,3.7-0.6C39,26.4,40.2,24.9,38.3,25.3L38.3,25.3z'/%3E%3Cpath class='st2' d='M21.8,26.6c-0.1,4.4,0,8.8,0.5,9.8c0.4,1.1,1.3,3.2,4.5,2.5c2.6-0.6,3.6-1.7,4-4.1c0.3-1.8,0.9-6.7,1-7.7'/%3E%3Cpath class='st2' d='M18,4.7c0,0-14.3-5.8-13.6,7.4c0.1,2.8,4,21.3,8.7,15.7c1.7-2,3.2-3.7,3.2-3.7'/%3E%3Cpath class='st2' d='M25.7,3.6c-0.5,0.2,7.9-3.1,12.7,3c1.7,2.2-0.3,11-5,18'/%3E%3Cpath class='st3' d='M33.5,24.6c0,0,0.3,1.5,4.7,0.6c1.9-0.4,0.8,1.1-1.2,2c-1.6,0.8-5.3,0.9-5.3-0.1 C31.6,24.5,33.6,25.3,33.5,24.6c-0.1-0.6-1.1-1.2-1.7-2.7c-0.5-1.3-7.3-11.2,1.9-9.7c0.3-0.1-2.4-8.7-11-8.9 c-8.6-0.1-8.3,10.6-8.3,10.6'/%3E%3Cpath class='st2' d='M19.4,25.6c-1.2,1.3-0.8,1.6-3.2,2.1c-2.4,0.5-1,1.4-0.1,1.6c1.1,0.3,3.7,0.7,5.5-1.8c0.5-0.8,0-2-0.7-2.3 C20.5,25.1,20,24.9,19.4,25.6L19.4,25.6z'/%3E%3Cpath class='st2' d='M19.3,25.5c-0.1-0.8,0.3-1.7,0.7-2.8c0.6-1.6,2-3.3,0.9-8.5c-0.8-3.9-6.5-0.8-6.5-0.3c0,0.5,0.3,2.7-0.1,5.2 c-0.5,3.3,2.1,6,5,5.7'/%3E%3Cpath class='st4' d='M18,13.8c0,0.2,0.3,0.7,0.8,0.7c0.5,0.1,0.9-0.3,0.9-0.5c0-0.2-0.3-0.4-0.8-0.4C18.4,13.6,18,13.7,18,13.8 L18,13.8z'/%3E%3Cpath class='st5' d='M32,13.5c0,0.2-0.3,0.7-0.8,0.7c-0.5,0.1-0.9-0.3-0.9-0.5c0-0.2,0.3-0.4,0.8-0.4C31.6,13.2,32,13.3,32,13.5 L32,13.5z'/%3E%3Cpath class='st2' d='M33.7,12.2c0.1,1.4-0.3,2.4-0.4,3.9c-0.1,2.2,1,4.7-0.6,7.2'/%3E%3Cpath class='st6' d='M2.7,6.6'/%3E%3C/g%3E%3C/svg%3E%0A") 0 0 no-repeat;
|
||||
*/
|
||||
* Using the SVG postgresql logo, modified to set the background color as #FFF
|
||||
* https://wiki.postgresql.org/images/9/90/PostgreSQL_logo.1color_blue.svg
|
||||
* background: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3C!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 42 42' style='enable-background:new 0 0 42 42;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bstroke:%23000000;stroke-width:3.3022;%7D .st1%7Bfill:%23336791;%7D .st2%7Bfill:none;stroke:%23FFFFFF;stroke-width:1.1007;stroke-linecap:round;stroke-linejoin:round;%7D .st3%7Bfill:none;stroke:%23FFFFFF;stroke-width:1.1007;stroke-linecap:round;stroke-linejoin:bevel;%7D .st4%7Bfill:%23FFFFFF;stroke:%23FFFFFF;stroke-width:0.3669;%7D .st5%7Bfill:%23FFFFFF;stroke:%23FFFFFF;stroke-width:0.1835;%7D .st6%7Bfill:none;stroke:%23FFFFFF;stroke-width:0.2649;stroke-linecap:round;stroke-linejoin:round;%7D%0A%3C/style%3E%3Cg id='orginal'%3E%3C/g%3E%3Cg id='Layer_x0020_3'%3E%3Cpath class='st0' d='M31.3,30c0.3-2.1,0.2-2.4,1.7-2.1l0.4,0c1.2,0.1,2.8-0.2,3.7-0.6c2-0.9,3.1-2.4,1.2-2 c-4.4,0.9-4.7-0.6-4.7-0.6c4.7-7,6.7-15.8,5-18c-4.6-5.9-12.6-3.1-12.7-3l0,0c-0.9-0.2-1.9-0.3-3-0.3c-2,0-3.5,0.5-4.7,1.4 c0,0-14.3-5.9-13.6,7.4c0.1,2.8,4,21.3,8.7,15.7c1.7-2,3.3-3.8,3.3-3.8c0.8,0.5,1.8,0.8,2.8,0.7l0.1-0.1c0,0.3,0,0.5,0,0.8 c-1.2,1.3-0.8,1.6-3.2,2.1c-2.4,0.5-1,1.4-0.1,1.6c1.1,0.3,3.7,0.7,5.5-1.8l-0.1,0.3c0.5,0.4,0.4,2.7,0.5,4.4 c0.1,1.7,0.2,3.2,0.5,4.1c0.3,0.9,0.7,3.3,3.9,2.6C29.1,38.3,31.1,37.5,31.3,30'/%3E%3Cpath class='st1' d='M38.3,25.3c-4.4,0.9-4.7-0.6-4.7-0.6c4.7-7,6.7-15.8,5-18c-4.6-5.9-12.6-3.1-12.7-3l0,0 c-0.9-0.2-1.9-0.3-3-0.3c-2,0-3.5,0.5-4.7,1.4c0,0-14.3-5.9-13.6,7.4c0.1,2.8,4,21.3,8.7,15.7c1.7-2,3.3-3.8,3.3-3.8 c0.8,0.5,1.8,0.8,2.8,0.7l0.1-0.1c0,0.3,0,0.5,0,0.8c-1.2,1.3-0.8,1.6-3.2,2.1c-2.4,0.5-1,1.4-0.1,1.6c1.1,0.3,3.7,0.7,5.5-1.8 l-0.1,0.3c0.5,0.4,0.8,2.4,0.7,4.3c-0.1,1.9-0.1,3.2,0.3,4.2c0.4,1,0.7,3.3,3.9,2.6c2.6-0.6,4-2,4.2-4.5c0.1-1.7,0.4-1.5,0.5-3 l0.2-0.7c0.3-2.3,0-3.1,1.7-2.8l0.4,0c1.2,0.1,2.8-0.2,3.7-0.6C39,26.4,40.2,24.9,38.3,25.3L38.3,25.3z'/%3E%3Cpath class='st2' d='M21.8,26.6c-0.1,4.4,0,8.8,0.5,9.8c0.4,1.1,1.3,3.2,4.5,2.5c2.6-0.6,3.6-1.7,4-4.1c0.3-1.8,0.9-6.7,1-7.7'/%3E%3Cpath class='st2' d='M18,4.7c0,0-14.3-5.8-13.6,7.4c0.1,2.8,4,21.3,8.7,15.7c1.7-2,3.2-3.7,3.2-3.7'/%3E%3Cpath class='st2' d='M25.7,3.6c-0.5,0.2,7.9-3.1,12.7,3c1.7,2.2-0.3,11-5,18'/%3E%3Cpath class='st3' d='M33.5,24.6c0,0,0.3,1.5,4.7,0.6c1.9-0.4,0.8,1.1-1.2,2c-1.6,0.8-5.3,0.9-5.3-0.1 C31.6,24.5,33.6,25.3,33.5,24.6c-0.1-0.6-1.1-1.2-1.7-2.7c-0.5-1.3-7.3-11.2,1.9-9.7c0.3-0.1-2.4-8.7-11-8.9 c-8.6-0.1-8.3,10.6-8.3,10.6'/%3E%3Cpath class='st2' d='M19.4,25.6c-1.2,1.3-0.8,1.6-3.2,2.1c-2.4,0.5-1,1.4-0.1,1.6c1.1,0.3,3.7,0.7,5.5-1.8c0.5-0.8,0-2-0.7-2.3 C20.5,25.1,20,24.9,19.4,25.6L19.4,25.6z'/%3E%3Cpath class='st2' d='M19.3,25.5c-0.1-0.8,0.3-1.7,0.7-2.8c0.6-1.6,2-3.3,0.9-8.5c-0.8-3.9-6.5-0.8-6.5-0.3c0,0.5,0.3,2.7-0.1,5.2 c-0.5,3.3,2.1,6,5,5.7'/%3E%3Cpath class='st4' d='M18,13.8c0,0.2,0.3,0.7,0.8,0.7c0.5,0.1,0.9-0.3,0.9-0.5c0-0.2-0.3-0.4-0.8-0.4C18.4,13.6,18,13.7,18,13.8 L18,13.8z'/%3E%3Cpath class='st5' d='M32,13.5c0,0.2-0.3,0.7-0.8,0.7c-0.5,0.1-0.9-0.3-0.9-0.5c0-0.2,0.3-0.4,0.8-0.4C31.6,13.2,32,13.3,32,13.5 L32,13.5z'/%3E%3Cpath class='st2' d='M33.7,12.2c0.1,1.4-0.3,2.4-0.4,3.9c-0.1,2.2,1,4.7-0.6,7.2'/%3E%3Cpath class='st6' d='M2.7,6.6'/%3E%3C/g%3E%3C/svg%3E%0A") 0 0 no-repeat;
|
||||
*/
|
||||
background: 'url(data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDUgNTAiPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDojZmZmO30uY2xzLTJ7ZmlsbDojMzI2ODkzO308L3N0eWxlPjwvZGVmcz48dGl0bGU+cGdBZG1pbjwvdGl0bGU+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNNTguOTQsNDEuNGEyLjQ4LDIuNDgsMCwwLDEtMi4yNy0zLjQ5TDY0LDIxLjI5VjZhNiw2LDAsMCwwLTYtNkg2QTYsNiwwLDAsMCwwLDZWNDRhNiw2LDAsMCwwLDYsNkg1OGE2LDYsMCwwLDAsNi02VjQxLjRaIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNMjkuMjUsMzAuMTdhMTMuMTMsMTMuMTMsMCwwLDEtMS44Mi02LjkzLDEzLDEzLDAsMCwxLDEuODItNi44OCwxMi41LDEyLjUsMCwwLDEsMS40OC0xLjk1LDEwLjQ0LDEwLjQ0LDAsMCwwLTMuMjUtMi44OSwxMS4xNiwxMS4xNiwwLDAsMC01LjY1LTEuNDVxLTQuNDgsMC02LjcyLDIuNjRWMTAuNDRINy41MVY0MC4zNmExLDEsMCwwLDAsMSwxaDZhMSwxLDAsMCwwLDEtMVYzMS4xOWE4LjQ3LDguNDcsMCwwLDAsNi4zNCwyLjQsMTEuMjYsMTEuMjYsMCwwLDAsNS42NS0xLjQ1LDEwLjUzLDEwLjUzLDAsMCwwLDIuMDYtMS41NkMyOS40NCwzMC40NCwyOS4zNCwzMC4zMSwyOS4yNSwzMC4xN1pNMjMuNiwyNS44YTQuNTIsNC41MiwwLDAsMS0zLjQ1LDEuNDQsNC40OCw0LjQ4LDAsMCwxLTMuNDQtMS40NCw1LjYsNS42LDAsMCwxLTEuMzUtNCw1LjU5LDUuNTksMCwwLDEsMS4zNS00LDQuNDYsNC40NiwwLDAsMSwzLjQ0LTEuNDUsNC40OSw0LjQ5LDAsMCwxLDMuNDUsMS40NSw1LjYzLDUuNjMsMCwwLDEsMS4zNCw0QTUuNjQsNS42NCwwLDAsMSwyMy42LDI1LjhaIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNNTYuNDksMTIuNjNWMzEuMjRxMCw2LjM1LTMuNDQsOS41MXQtOS45MiwzLjE3YTI1LjQyLDI1LjQyLDAsMCwxLTYuMy0uNzUsMTUsMTUsMCwwLDEtNS0yLjIzbDIuODktNS41OWExMC4xNywxMC4xNywwLDAsMCwzLjUxLDEuNzksMTQuMzcsMTQuMzcsMCwwLDAsNC4xOC42NUE2LjUzLDYuNTMsMCwwLDAsNDcsMzYuNGE1LjM3LDUuMzcsMCwwLDAsMS40Ny00LjExdi0uNzZjLTEuNTQsMS44LTMuNzksMi42OS02Ljc2LDIuNjlhMTEuNywxMS43LDAsMCwxLTUuNTktMS4zNkExMC4zNywxMC4zNywwLDAsMSwzMi4wOSwyOWExMC44OSwxMC44OSwwLDAsMS0xLjUxLTUuNzcsMTAuODYsMTAuODYsMCwwLDEsMS41MS01Ljc0LDEwLjQyLDEwLjQyLDAsMCwxLDQuMDctMy44NiwxMS43MSwxMS43MSwwLDAsMSw1LjU5LTEuMzdjMy4yNSwwLDUuNjMsMS4wNiw3LjE0LDMuMTVWMTIuNjNabS05LjMsMTMuOTVhNC40LDQuNCwwLDAsMCwxLjQtMy4zNiw0LjM0LDQuMzQsMCwwLDAtMS4zOC0zLjM0LDUuNjUsNS42NSwwLDAsMC03LjE2LDAsNC4zLDQuMywwLDAsMC0xLjQxLDMuMzQsNC4zNSw0LjM1LDAsMCwwLDEuNDMsMy4zNiw1LjA4LDUuMDgsMCwwLDAsMy41NywxLjNBNSw1LDAsMCwwLDQ3LjE5LDI2LjU4WiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTgzLjQzLDMyLjg5SDcxbC0yLDUuMDlhMSwxLDAsMCwxLS45My42Mkg2MS43M2ExLDEsMCwwLDEtLjkxLTEuNEw3Mi45MSw5LjhhMSwxLDAsMCwxLC45Mi0uNmg2Ljg5YTEsMSwwLDAsMSwuOTEuNkw5My43NywzNy4yYTEsMSwwLDAsMS0uOTIsMS40SDg2LjQxYTEsMSwwLDAsMS0uOTMtLjYyWk04MSwyNi43NmwtMy43OC05LjQxLTMuNzgsOS40MVoiLz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0xMjAuNDQsOC40NFYzNy42YTEsMSwwLDAsMS0xLDFoLTUuNmExLDEsMCwwLDEtMS0xVjM2LjMzUTExMC42MiwzOSwxMDYuMTYsMzlhMTEuMjksMTEuMjksMCwwLDEtNS42Ny0xLjQ1LDEwLjU0LDEwLjU0LDAsMCwxLTQtNC4xNEExMi42MiwxMi42MiwwLDAsMSw5NSwyNy4xOCwxMi41MywxMi41MywwLDAsMSw5Ni40NCwyMWExMC4zNSwxMC4zNSwwLDAsMSw0LTQuMDksMTEuNDgsMTEuNDgsMCwwLDEsNS42Ny0xLjQzLDguMjQsOC4yNCwwLDAsMSw2LjMsMi4zNVY4LjQ0YTEsMSwwLDAsMSwxLTFoNkExLDEsMCwwLDEsMTIwLjQ0LDguNDRabS05LjE5LDIyLjc1YTUuNzEsNS43MSwwLDAsMCwxLjM0LTQsNS42LDUuNiwwLDAsMC0xLjMyLTMuOTUsNC40Nyw0LjQ3LDAsMCwwLTMuNDMtMS40Myw0LjUzLDQuNTMsMCwwLDAtMy40NCwxLjQzLDUuNTEsNS41MSwwLDAsMC0xLjM0LDMuOTUsNS42Nyw1LjY3LDAsMCwwLDEuMzQsNCw0Ljc3LDQuNzcsMCwwLDAsNi44NSwwWiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTE2MSwxOGMxLjY2LDEuNjgsMi41LDQuMjEsMi41LDcuNnYxMmExLDEsMCwwLDEtMSwxaC02YTEsMSwwLDAsMS0xLTFWMjYuODhhNS42Nyw1LjY3LDAsMCwwLS45LTMuNTMsMy4wOSwzLjA5LDAsMCwwLTIuNTUtMS4xMywzLjYyLDMuNjIsMCwwLDAtMi44OSwxLjI2LDUuNzEsNS43MSwwLDAsMC0xLjEsMy44MlYzNy42YTEsMSwwLDAsMS0xLDFoLTZhMSwxLDAsMCwxLTEtMVYyNi44OGMwLTMuMTEtMS4xNC00LjY2LTMuNDQtNC42NmEzLjcsMy43LDAsMCwwLTIuOTQsMS4yNiw1LjcxLDUuNzEsMCwwLDAtMS4wOSwzLjgyVjM3LjZhMSwxLDAsMCwxLTEsMWgtNmExLDEsMCwwLDEtMS0xVjE2Ljg0YTEsMSwwLDAsMSwxLTFoNS42YTEsMSwwLDAsMSwxLDF2MS4zOWE4LDgsMCwwLDEsMy0yLjA4LDEwLjIzLDEwLjIzLDAsMCwxLDMuOC0uNjksMTAsMTAsMCwwLDEsNC4yOS44OEE3LjI4LDcuMjgsMCwwLDEsMTQ2LjQyLDE5YTguODUsOC44NSwwLDAsMSwzLjQxLTIuNjUsMTAuOTMsMTAuOTMsMCwwLDEsNC40OS0uOTJBOSw5LDAsMCwxLDE2MSwxOFoiLz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0xNjguMTIsMTIuMWEzLjkxLDMuOTEsMCwwLDEtMS4zNC0yLjc5QTQuMTYsNC4xNiwwLDAsMSwxNjgsNi4xOWE1LDUsMCwwLDEsMy42Ny0xLjM2QTUuMjUsNS4yNSwwLDAsMSwxNzUuMTgsNmEzLjc1LDMuNzUsMCwwLDEsMS4zNCwzLDQuMSw0LjEsMCwwLDEtMS4zNCwzLjEzLDUuNjgsNS42OCwwLDAsMS03LjA2LDBabS41NCwzLjc0aDZhMSwxLDAsMCwxLDEsMVYzNy42YTEsMSwwLDAsMS0xLDFoLTZhMSwxLDAsMCwxLTEtMVYxNi44NEExLDEsMCwwLDEsMTY4LjY2LDE1Ljg0WiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTIwMS41NSwxOHEyLjU5LDIuNTIsMi41OSw3LjZ2MTJhMSwxLDAsMCwxLTEsMWgtNmExLDEsMCwwLDEtMS0xVjI2Ljg4cTAtNC42Ni0zLjc0LTQuNjZhNC4zLDQuMywwLDAsMC0zLjMsMS4zNCw1LjgzLDUuODMsMCwwLDAtMS4yNCw0djEwYTEsMSwwLDAsMS0xLDFoLTZhMSwxLDAsMCwxLTEtMVYxNi44NGExLDEsMCwwLDEsMS0xaDUuNjFhMSwxLDAsMCwxLDEsMXYxLjQ3YTkuMDUsOS4wNSwwLDAsMSwzLjE5LTIuMTIsMTAuNzgsMTAuNzgsMCwwLDEsNC0uNzNBOS4zNCw5LjM0LDAsMCwxLDIwMS41NSwxOFoiLz48L3N2Zz4=) 0 0 no-repeat',
|
||||
backgroundPositionY: 'center',
|
||||
},
|
||||
menus: {
|
||||
'& .AppMenuBar-menus': {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '2px',
|
||||
marginLeft: '16px',
|
||||
|
||||
|
||||
'& .MuiButton-containedPrimary': {
|
||||
padding: '1px 8px',
|
||||
}
|
||||
},
|
||||
userMenu: {
|
||||
'& .AppMenuBar-userMenu': {
|
||||
marginLeft: 'auto',
|
||||
'& .MuiButton-containedPrimary': {
|
||||
fontSize: '0.825rem',
|
||||
},
|
||||
'& .AppMenuBar-gravatar': {
|
||||
marginRight: '4px',
|
||||
}
|
||||
},
|
||||
gravatar: {
|
||||
marginRight: '4px',
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
|
||||
export default function AppMenuBar() {
|
||||
const classes = useStyles();
|
||||
|
||||
const [,setRefresh] = useState(false);
|
||||
const pgAdmin = usePgAdmin();
|
||||
|
||||
|
@ -97,10 +96,10 @@ export default function AppMenuBar() {
|
|||
|
||||
const userMenuInfo = pgAdmin.Browser.utils.userMenuInfo;
|
||||
|
||||
return(
|
||||
<Box className={classes.root} data-test="app-menu-bar">
|
||||
<div className={classes.logo} />
|
||||
<div className={classes.menus}>
|
||||
return (
|
||||
<StyledBox data-test="app-menu-bar">
|
||||
<div className='AppMenuBar-logo' />
|
||||
<div className='AppMenuBar-menus'>
|
||||
{pgAdmin.Browser.MainMenus?.map((menu)=>{
|
||||
return (
|
||||
<PgMenu
|
||||
|
@ -124,11 +123,11 @@ export default function AppMenuBar() {
|
|||
})}
|
||||
</div>
|
||||
{userMenuInfo &&
|
||||
<div className={classes.userMenu}>
|
||||
<div className='AppMenuBar-userMenu'>
|
||||
<PgMenu
|
||||
menuButton={
|
||||
<PrimaryButton data-test="loggedin-username">
|
||||
<div className={classes.gravatar}>
|
||||
<div className='AppMenuBar-gravatar'>
|
||||
{userMenuInfo.gravatar &&
|
||||
<img src={userMenuInfo.gravatar} width = "18" height = "18"
|
||||
alt ={`Gravatar for ${ userMenuInfo.username }`} />}
|
||||
|
@ -146,6 +145,6 @@ export default function AppMenuBar() {
|
|||
})}
|
||||
</PgMenu>
|
||||
</div>}
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,4 @@
|
|||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import gettext from 'sources/gettext';
|
||||
|
@ -15,6 +6,13 @@ import BaseUISchema from '../SchemaView/base_schema.ui';
|
|||
import SchemaView from '../SchemaView';
|
||||
import { isEmptyString } from 'sources/validators';
|
||||
|
||||
|
||||
const StyledSchemaView = styled(SchemaView)(({theme}) => ({
|
||||
'& .ChangeOwnershipContent-root': {
|
||||
...theme.mixins.tabPanel,
|
||||
}
|
||||
}));
|
||||
|
||||
class ChangeOwnershipSchema extends BaseUISchema {
|
||||
constructor(deletedUser, adminUserList, noOfSharedServers) {
|
||||
super({
|
||||
|
@ -50,17 +48,11 @@ class ChangeOwnershipSchema extends BaseUISchema {
|
|||
}
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
...theme.mixins.tabPanel,
|
||||
},
|
||||
}));
|
||||
|
||||
export default function ChangeOwnershipContent({onSave, onClose, deletedUser, userList, noOfSharedServers}) {
|
||||
const classes = useStyles();
|
||||
|
||||
const objChangeOwnership = new ChangeOwnershipSchema(deletedUser, userList, noOfSharedServers);
|
||||
|
||||
return<SchemaView
|
||||
return <StyledSchemaView
|
||||
formType={'dialog'}
|
||||
getInitData={() => { /*This is intentional (SonarQube)*/ }}
|
||||
schema={objChangeOwnership}
|
||||
|
@ -74,7 +66,7 @@ export default function ChangeOwnershipContent({onSave, onClose, deletedUser, us
|
|||
disableSqlHelp={true}
|
||||
disableDialogHelp={true}
|
||||
isTabView={false}
|
||||
formClassName={classes.root}
|
||||
formClassName='ChangeOwnershipContent-root'
|
||||
/>;
|
||||
}
|
||||
ChangeOwnershipContent.propTypes = {
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import gettext from 'sources/gettext';
|
||||
import BaseUISchema from '../SchemaView/base_schema.ui';
|
||||
import SchemaView from '../SchemaView';
|
||||
|
||||
const StyledSchemaView = styled(SchemaView )(({theme}) => ({
|
||||
'& .ChangePasswordContent-root': {
|
||||
...theme.mixins.tabPanel,
|
||||
}
|
||||
}));
|
||||
|
||||
class ChangePasswordSchema extends BaseUISchema {
|
||||
constructor(user, isPgpassFileUsed, hasCsrfToken=false, showUser=true) {
|
||||
super({
|
||||
|
@ -73,17 +70,9 @@ class ChangePasswordSchema extends BaseUISchema {
|
|||
}
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
...theme.mixins.tabPanel,
|
||||
},
|
||||
}));
|
||||
|
||||
export default function ChangePasswordContent({getInitData=() => { /*This is intentional (SonarQube)*/ },
|
||||
onSave, onClose, hasCsrfToken=false, showUser=true}) {
|
||||
const classes = useStyles();
|
||||
|
||||
return<SchemaView
|
||||
return <StyledSchemaView
|
||||
formType={'dialog'}
|
||||
getInitData={getInitData}
|
||||
schema={new ChangePasswordSchema('', false, hasCsrfToken, showUser)}
|
||||
|
@ -97,7 +86,7 @@ export default function ChangePasswordContent({getInitData=() => { /*This is int
|
|||
disableSqlHelp={true}
|
||||
disableDialogHelp={true}
|
||||
isTabView={false}
|
||||
formClassName={classes.root}
|
||||
formClassName='ChangePasswordContent-root'
|
||||
/>;
|
||||
}
|
||||
ChangePasswordContent.propTypes = {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { useModalStyles } from '../helpers/ModalProvider';
|
||||
import { ModalContent, ModalFooter } from '../../../static/js/components/ModalContent';
|
||||
import gettext from 'sources/gettext';
|
||||
import { Box } from '@mui/material';
|
||||
import { DefaultButton, PrimaryButton } from '../components/Buttons';
|
||||
|
@ -10,24 +10,23 @@ import HTMLReactParser from 'html-react-parser';
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function ConfirmSaveContent({closeModal, text, onDontSave, onSave}) {
|
||||
const classes = useModalStyles();
|
||||
return (
|
||||
<Box display="flex" flexDirection="column" height="100%">
|
||||
<ModalContent>
|
||||
<Box flexGrow="1" p={2}>{typeof(text) == 'string' ? HTMLReactParser(text) : text}</Box>
|
||||
<Box className={classes.footer}>
|
||||
<ModalFooter>
|
||||
<DefaultButton data-test="close" startIcon={<CloseIcon />} onClick={()=>{
|
||||
closeModal();
|
||||
}} >{gettext('Cancel')}</DefaultButton>
|
||||
<DefaultButton data-test="dont-save" className={classes.margin} startIcon={<DeleteRoundedIcon />} onClick={()=>{
|
||||
<DefaultButton data-test="dont-save" startIcon={<DeleteRoundedIcon />} onClick={()=>{
|
||||
onDontSave?.();
|
||||
closeModal();
|
||||
}} >{gettext('Don\'t save')}</DefaultButton>
|
||||
<PrimaryButton data-test="save" className={classes.margin} startIcon={<CheckRoundedIcon />} onClick={()=>{
|
||||
<PrimaryButton data-test="save" startIcon={<CheckRoundedIcon />} onClick={()=>{
|
||||
onSave?.();
|
||||
closeModal();
|
||||
}} autoFocus={true} >{gettext('Save')}</PrimaryButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,11 +14,11 @@ import { DefaultButton, PrimaryButton } from '../components/Buttons';
|
|||
import CloseIcon from '@mui/icons-material/CloseRounded';
|
||||
import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useModalStyles } from '../helpers/ModalProvider';
|
||||
import { FormFooterMessage, InputCheckbox, InputText, MESSAGE_TYPE } from '../components/FormComponents';
|
||||
import { ModalContent, ModalFooter } from '../../../static/js/components/ModalContent';
|
||||
|
||||
export default function ConnectServerContent({closeModal, data, onOK, setHeight}) {
|
||||
const classes = useModalStyles();
|
||||
|
||||
const containerRef = useRef();
|
||||
const firstEleRef = useRef();
|
||||
const okBtnRef = useRef();
|
||||
|
@ -58,7 +58,7 @@ export default function ConnectServerContent({closeModal, data, onOK, setHeight}
|
|||
}
|
||||
|
||||
return (
|
||||
<Box display="flex" flexDirection="column" className={classes.container} ref={containerRef}>
|
||||
<ModalContent ref={containerRef}>
|
||||
<Box flexGrow="1" p={2}>
|
||||
{data.prompt_tunnel_password && <>
|
||||
<Box>
|
||||
|
@ -105,11 +105,11 @@ export default function ConnectServerContent({closeModal, data, onOK, setHeight}
|
|||
position: 'unset', padding: '12px 0px 0px'
|
||||
}}/>
|
||||
</Box>
|
||||
<Box className={classes.footer}>
|
||||
<ModalFooter>
|
||||
<DefaultButton data-test="close" startIcon={<CloseIcon />} onClick={()=>{
|
||||
closeModal();
|
||||
}} >{gettext('Cancel')}</DefaultButton>
|
||||
<PrimaryButton ref={okBtnRef} data-test="save" className={classes.margin} startIcon={<CheckRoundedIcon />} onClick={()=>{
|
||||
<PrimaryButton ref={okBtnRef} data-test="save" startIcon={<CheckRoundedIcon />} onClick={()=>{
|
||||
let postFormData = new FormData();
|
||||
if(data.prompt_tunnel_password) {
|
||||
postFormData.append('tunnel_password', formData.tunnel_password);
|
||||
|
@ -124,8 +124,8 @@ export default function ConnectServerContent({closeModal, data, onOK, setHeight}
|
|||
onOK?.(postFormData);
|
||||
closeModal();
|
||||
}} >{gettext('OK')}</PrimaryButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,19 +11,16 @@ import React, { useState, useRef, useEffect } from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import gettext from 'sources/gettext';
|
||||
import url_for from 'sources/url_for';
|
||||
|
||||
import { Box } from '@mui/material';
|
||||
import CloseIcon from '@mui/icons-material/CloseRounded';
|
||||
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
|
||||
import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
|
||||
import HelpIcon from '@mui/icons-material/Help';
|
||||
|
||||
import { DefaultButton, PrimaryButton, PgIconButton } from '../components/Buttons';
|
||||
import { useModalStyles } from '../helpers/ModalProvider';
|
||||
import { FormFooterMessage, FormNote, InputText, MESSAGE_TYPE } from '../components/FormComponents';
|
||||
import { ModalContent, ModalFooter } from '../../../static/js/components/ModalContent';
|
||||
|
||||
export default function MasterPasswordContent({ closeModal, onResetPassowrd, onOK, onCancel, setHeight, isPWDPresent, data, keyringName}) {
|
||||
const classes = useModalStyles();
|
||||
const containerRef = useRef();
|
||||
const firstEleRef = useRef();
|
||||
const okBtnRef = useRef();
|
||||
|
@ -57,9 +54,8 @@ export default function MasterPasswordContent({ closeModal, onResetPassowrd, onO
|
|||
setHeight?.(containerRef.current?.offsetHeight);
|
||||
}, [containerRef.current]);
|
||||
|
||||
|
||||
return (
|
||||
<Box display="flex" flexDirection="column" className={classes.container} ref={containerRef}>
|
||||
<ModalContent ref={containerRef}>
|
||||
{isKeyring ?
|
||||
<Box flexGrow="1" p={2}>
|
||||
<Box>
|
||||
|
@ -98,7 +94,7 @@ export default function MasterPasswordContent({ closeModal, onResetPassowrd, onO
|
|||
}} />
|
||||
</Box>
|
||||
}
|
||||
<Box className={classes.footer}>
|
||||
<ModalFooter>
|
||||
<Box style={{ marginRight: 'auto' }}>
|
||||
<PgIconButton data-test="help-masterpassword" title={gettext('Help')} style={{ padding: '0.3rem', paddingLeft: '0.7rem' }} startIcon={<HelpIcon />} onClick={() => {
|
||||
let _url = url_for('help.static', {
|
||||
|
@ -120,7 +116,7 @@ export default function MasterPasswordContent({ closeModal, onResetPassowrd, onO
|
|||
closeModal();
|
||||
}} >{gettext('Cancel')}</DefaultButton>
|
||||
}
|
||||
<PrimaryButton ref={okBtnRef} data-test="save" className={classes.margin} startIcon={<CheckRoundedIcon />}
|
||||
<PrimaryButton ref={okBtnRef} data-test="save" startIcon={<CheckRoundedIcon />}
|
||||
disabled={formData.password.length == 0}
|
||||
onClick={() => {
|
||||
let postFormData = new FormData();
|
||||
|
@ -132,8 +128,8 @@ export default function MasterPasswordContent({ closeModal, onResetPassowrd, onO
|
|||
>
|
||||
{gettext('OK')}
|
||||
</PrimaryButton>
|
||||
</Box>
|
||||
</Box>);
|
||||
</ModalFooter>
|
||||
</ModalContent>);
|
||||
}
|
||||
|
||||
MasterPasswordContent.propTypes = {
|
||||
|
|
|
@ -14,12 +14,11 @@ import { DefaultButton, PrimaryButton } from '../components/Buttons';
|
|||
import CloseIcon from '@mui/icons-material/CloseRounded';
|
||||
import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useModalStyles } from '../helpers/ModalProvider';
|
||||
import { ModalContent, ModalFooter } from '../../../static/js/components/ModalContent';
|
||||
import { InputText } from '../components/FormComponents';
|
||||
import { isEmptyString } from '../../../static/js/validators';
|
||||
|
||||
export default function NamedRestoreContent({closeModal, onOK, setHeight}) {
|
||||
const classes = useModalStyles();
|
||||
const containerRef = useRef();
|
||||
const firstEleRef = useRef();
|
||||
const okBtnRef = useRef();
|
||||
|
@ -55,7 +54,7 @@ export default function NamedRestoreContent({closeModal, onOK, setHeight}) {
|
|||
const isOKDisabled = isEmptyString(formData.namedRestorePoint);
|
||||
|
||||
return (
|
||||
<Box display="flex" flexDirection="column" className={classes.container} ref={containerRef}>
|
||||
<ModalContent ref={containerRef}>
|
||||
<Box flexGrow="1" p={2}>
|
||||
<Box>
|
||||
<span style={{fontWeight: 'bold'}}>
|
||||
|
@ -67,18 +66,18 @@ export default function NamedRestoreContent({closeModal, onOK, setHeight}) {
|
|||
onChange={(e)=>onTextChange(e, 'namedRestorePoint')} onKeyDown={(e)=>onKeyDown(e)}/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box className={classes.footer}>
|
||||
<ModalFooter>
|
||||
<DefaultButton data-test="close" startIcon={<CloseIcon />} onClick={()=>{
|
||||
closeModal();
|
||||
}} >{gettext('Cancel')}</DefaultButton>
|
||||
<PrimaryButton ref={okBtnRef} data-test="save" disabled={isOKDisabled} className={classes.margin} startIcon={<CheckRoundedIcon />} onClick={()=>{
|
||||
<PrimaryButton ref={okBtnRef} data-test="save" disabled={isOKDisabled} startIcon={<CheckRoundedIcon />} onClick={()=>{
|
||||
let postFormData = new FormData();
|
||||
postFormData.append('value', formData.namedRestorePoint);
|
||||
onOK?.(postFormData);
|
||||
closeModal();
|
||||
}} >{gettext('OK')}</PrimaryButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,11 +18,10 @@ import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
|
|||
import gettext from 'sources/gettext';
|
||||
|
||||
import { DefaultButton, PrimaryButton } from '../components/Buttons';
|
||||
import { useModalStyles } from '../helpers/ModalProvider';
|
||||
import { ModalContent, ModalFooter } from '../../../static/js/components/ModalContent';
|
||||
import { FormFooterMessage, InputText, MESSAGE_TYPE } from '../components/FormComponents';
|
||||
|
||||
export default function RenameTabContent({ panelId, panelDocker, closeModal}) {
|
||||
const classes = useModalStyles();
|
||||
const containerRef = useRef();
|
||||
const okBtnRef = useRef();
|
||||
const panelData = useMemo(()=>panelDocker.find(panelId));
|
||||
|
@ -63,7 +62,7 @@ export default function RenameTabContent({ panelId, panelDocker, closeModal}) {
|
|||
const isValid = formData['title'].length != 0;
|
||||
|
||||
return (
|
||||
<Box display="flex" flexDirection="column" className={classes.container} ref={containerRef}>
|
||||
<ModalContent ref={containerRef}>
|
||||
<Box padding="8px">
|
||||
<Box marginBottom="4px">Current: {initialTitle}</Box>
|
||||
<InputText type="text" value={formData['title']} controlProps={{ maxLength: null }}
|
||||
|
@ -71,15 +70,15 @@ export default function RenameTabContent({ panelId, panelDocker, closeModal}) {
|
|||
</Box>
|
||||
<FormFooterMessage type={MESSAGE_TYPE.ERROR} message={!isValid ? gettext('Title cannot be empty') : ''}
|
||||
closable={false} style={{position: 'initial'}} />
|
||||
<Box className={classes.footer}>
|
||||
<ModalFooter>
|
||||
<DefaultButton data-test="close" startIcon={<CloseIcon />} onClick={() => {
|
||||
closeModal();
|
||||
}} >{gettext('Cancel')}</DefaultButton>
|
||||
<PrimaryButton data-test="save" startIcon={<CheckRoundedIcon />} onClick={onOkClick} className={classes.margin} disabled={!isValid}>
|
||||
<PrimaryButton data-test="save" startIcon={<CheckRoundedIcon />} onClick={onOkClick} disabled={!isValid}>
|
||||
{gettext('OK')}
|
||||
</PrimaryButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,19 +17,17 @@ import CloseIcon from '@mui/icons-material/CloseRounded';
|
|||
import HelpIcon from '@mui/icons-material/Help';
|
||||
|
||||
import { DefaultButton, PgIconButton } from '../components/Buttons';
|
||||
import { useModalStyles } from '../helpers/ModalProvider';
|
||||
import { ModalContent, ModalFooter }from '../../../static/js/components/ModalContent';
|
||||
|
||||
export default function UrlDialogContent({ url, helpFile, onClose }) {
|
||||
const classes = useModalStyles();
|
||||
|
||||
return (
|
||||
<Box display="flex" flexDirection="column" height="100%" className={classes.container}>
|
||||
<ModalContent>
|
||||
<Box flexGrow="1">
|
||||
<iframe src={url} width="100%" height="100%" onLoad={(e)=>{
|
||||
e.target?.contentWindow?.focus();
|
||||
}}/>
|
||||
</Box>
|
||||
<Box className={classes.footer}>
|
||||
<ModalFooter>
|
||||
<Box style={{ marginRight: 'auto' }}>
|
||||
<PgIconButton data-test={'help-'+helpFile} title={gettext('Help')} icon={<HelpIcon />} onClick={() => {
|
||||
let _url = url_for('help.static', {
|
||||
|
@ -42,8 +40,8 @@ export default function UrlDialogContent({ url, helpFile, onClose }) {
|
|||
<DefaultButton data-test="close" startIcon={<CloseIcon />} onClick={() => {
|
||||
onClose();
|
||||
}} >{gettext('Close')}</DefaultButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,53 +1,45 @@
|
|||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import _ from 'lodash';
|
||||
import ArrowRightAltIcon from '@mui/icons-material/ArrowRightAlt';
|
||||
import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord';
|
||||
import HTMLReactParse from 'html-react-parser';
|
||||
import { commonTableStyles } from '../Theme';
|
||||
import PropTypes from 'prop-types';
|
||||
import gettext from 'sources/gettext';
|
||||
import Table from '../components/Table';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
collapsible: {
|
||||
|
||||
const StyledTable = styled(Table)(({theme}) => ({
|
||||
'& .Analysis-collapsible': {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
collapseParent: {
|
||||
'& .Analysis-collapseParent': {
|
||||
borderBottom: '2px dashed '+theme.palette.primary.main,
|
||||
},
|
||||
level2: {
|
||||
'& .Analysis-textRight': {
|
||||
textAlign: 'right',
|
||||
},
|
||||
'& .Analysis-level2': {
|
||||
backgroundColor: theme.otherVars.explain.sev2.bg,
|
||||
color: theme.otherVars.explain.sev2.color,
|
||||
},
|
||||
level3: {
|
||||
'& .Analysis-level3': {
|
||||
backgroundColor: theme.otherVars.explain.sev3.bg,
|
||||
color: theme.otherVars.explain.sev3.color,
|
||||
},
|
||||
level4: {
|
||||
'& .Analysis-level4': {
|
||||
backgroundColor: theme.otherVars.explain.sev4.bg,
|
||||
color: theme.otherVars.explain.sev4.color,
|
||||
},
|
||||
textRight: {
|
||||
textAlign: 'right',
|
||||
},
|
||||
}));
|
||||
|
||||
function getRowClassname(classes, data, collapseParent) {
|
||||
function getRowClassname(data, collapseParent) {
|
||||
let className = [];
|
||||
if(data['Plans']?.length > 0) {
|
||||
className.push(classes.collapsible);
|
||||
className.push('Analysis-collapsible');
|
||||
}
|
||||
if(collapseParent) {
|
||||
className.push(classes.collapseParent);
|
||||
className.push('Analysis-collapseParent');
|
||||
}
|
||||
return className;
|
||||
}
|
||||
|
@ -70,11 +62,11 @@ 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;
|
||||
const className = getRowClassname(classes, data, collapsedExId.indexOf(exId) > -1);
|
||||
const className = getRowClassname(data, collapsedExId.indexOf(exId) > -1);
|
||||
let onRowClick = (e)=>{
|
||||
toggleCollapseExId(e.currentTarget.getAttribute('data-ex-id'), data['Plans']?.length);
|
||||
};
|
||||
|
@ -82,35 +74,35 @@ function ExplainRow({row, show, activeExId, setActiveExId, collapsedExId, toggle
|
|||
return (
|
||||
<tr onMouseEnter={(e)=>{setActiveExId(e.currentTarget.getAttribute('data-ex-id'));}}
|
||||
onMouseLeave={()=>{setActiveExId(null);}}
|
||||
className={clsx(className)} data-parent={parentExId}
|
||||
className={className} data-parent={parentExId}
|
||||
data-ex-id={`pga_ex_${data['level'].join('_')}`}
|
||||
style={collapsed ? {display: 'none'} : {}}
|
||||
onClick={onRowClick}>
|
||||
<td>
|
||||
<FiberManualRecordIcon fontSize="small" style={{visibility: activeExId==parentExId ? 'visible' : 'hidden'}} />
|
||||
</td>
|
||||
<td className={classes.textRight}>{data['_serial']}.</td>
|
||||
<td className='Analysis-textRight'>{data['_serial']}.</td>
|
||||
<td style={{paddingLeft: data['level'].length*30+'px'}} title={row['tooltip_text']}>
|
||||
<NodeText displayText={row['display_text']} extraInfo={row['node_extra_info']} />
|
||||
</td>
|
||||
<td className={clsx(classes.textRight, classes['level'+data['exclusive_flag']])} style={show.show_timings ? {} : {display: 'none'}}>
|
||||
<td className={'Analysis-textRight ' + 'Analysis-' +['level'+data['exclusive_flag']]} style={show.show_timings ? {} : {display: 'none'}}>
|
||||
{data['exclusive'] && (data['exclusive']+' ms')}
|
||||
</td>
|
||||
<td className={clsx(classes.textRight, classes['level'+data['inclusive_flag']])} style={show.show_timings ? {} : {display: 'none'}}>
|
||||
<td className={'Analysis-textRight ' + 'Analysis-' +['level'+data['inclusive_flag']]} style={show.show_timings ? {} : {display: 'none'}}>
|
||||
{data['inclusive'] && (data['inclusive']+' ms')}
|
||||
</td>
|
||||
<td className={clsx(classes.textRight, classes['level'+data['rowsx_flag']])} style={show.show_rowsx ? {} : {display: 'none'}}>
|
||||
<td className={'Analysis-textRight ' + 'Analysis-' +['level'+data['rowsx_flag']]} style={show.show_rowsx ? {} : {display: 'none'}}>
|
||||
{!_.isUndefined(data['rowsx_flag'])
|
||||
&& (data['rowsx_direction'] == 'positive' ? <>↑</> : <>↓</>)
|
||||
} {data['rowsx']}
|
||||
</td>
|
||||
<td className={classes.textRight} style={(show.show_rowsx || show.show_rows) ? {} : {display: 'none'}}>
|
||||
<td className='Analysis-textRight' style={(show.show_rowsx || show.show_rows) ? {} : {display: 'none'}}>
|
||||
{data['Actual Rows']}
|
||||
</td>
|
||||
<td className={classes.textRight} style={(show.show_rowsx || show.show_plan_rows) ? {} : {display: 'none'}}>
|
||||
<td className='Analysis-textRight' style={(show.show_rowsx || show.show_plan_rows) ? {} : {display: 'none'}}>
|
||||
{data['Plan Rows']}
|
||||
</td>
|
||||
<td className={classes.textRight} style={(show.show_rowsx || show.show_rows) ? {} : {display: 'none'}}>
|
||||
<td className='Analysis-textRight' style={(show.show_rowsx || show.show_rows) ? {} : {display: 'none'}}>
|
||||
{data['Actual Loops']}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -151,7 +143,6 @@ ExplainRow.propTypes = {
|
|||
};
|
||||
|
||||
export default function Analysis({explainTable}) {
|
||||
const tableClasses = commonTableStyles();
|
||||
const [activeExId, setActiveExId] = React.useState();
|
||||
const [collapsedExId, setCollapsedExId] = React.useState([]);
|
||||
|
||||
|
@ -165,46 +156,48 @@ export default function Analysis({explainTable}) {
|
|||
});
|
||||
}
|
||||
};
|
||||
return <table className={clsx(tableClasses.table, tableClasses.noBorder, tableClasses.borderBottom)}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan="2" style={{width: '30px'}}></th>
|
||||
<th rowSpan="2"><button disabled="">#</button></th>
|
||||
<th rowSpan="2"><button disabled="">Node</button></th>
|
||||
<th colSpan="2" style={explainTable.show_timings ? {} : {display: 'none'}}>
|
||||
<button disabled="">{gettext('Timings')}</button>
|
||||
</th>
|
||||
<th style={(explainTable.show_rowsx || explainTable.show_rows || explainTable.show_plan_rows) ? {} : {display: 'none'}}
|
||||
colSpan={(explainTable.show_rowsx) ? '3' : '1'}>
|
||||
<button disabled="">{gettext('Rows')}</button>
|
||||
</th>
|
||||
<th style={(explainTable.show_rowsx || explainTable.show_rows) ? {} : {display: 'none'}} rowSpan="2">
|
||||
<button disabled="">{gettext('Loops')}</button>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style={explainTable.show_timings ? {} : {display: 'none'}}>
|
||||
<button disabled="">{gettext('Exclusive')}</button>
|
||||
</th>
|
||||
<th style={explainTable.show_timings ? {} : {display: 'none'}}>
|
||||
<button disabled="">{gettext('Inclusive')}</button>
|
||||
</th>
|
||||
<th style={explainTable.show_rowsx ? {} : {display: 'none'}}><button disabled="">{gettext('Rows X')}</button></th>
|
||||
<th style={(explainTable.show_rowsx || explainTable.show_rows) ? {} : {display: 'none'}}><button disabled="">{gettext('Actual')}</button></th>
|
||||
<th style={(explainTable.show_rowsx || explainTable.show_plan_rows) ? {} : {display: 'none'}}><button disabled="">{gettext('Plan')}</button></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{_.sortBy(explainTable.rows,(r)=>r['data']['_serial']).map((row)=>{
|
||||
return <ExplainRow key={row?.data?.arr_id} row={row} show={{
|
||||
show_timings: explainTable.show_timings,
|
||||
show_rowsx: explainTable.show_rowsx,
|
||||
show_rows: explainTable.show_rows,
|
||||
show_plan_rows: explainTable.show_plan_rows,
|
||||
}} activeExId={activeExId} setActiveExId={setActiveExId} collapsedExId={collapsedExId} toggleCollapseExId={toggleCollapseExId} />;
|
||||
})}
|
||||
</tbody>
|
||||
</table>;
|
||||
return (
|
||||
<StyledTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan="2" style={{width: '30px'}}></th>
|
||||
<th rowSpan="2"><button disabled="">#</button></th>
|
||||
<th rowSpan="2"><button disabled="">Node</button></th>
|
||||
<th colSpan="2" style={explainTable.show_timings ? {} : {display: 'none'}}>
|
||||
<button disabled="">{gettext('Timings')}</button>
|
||||
</th>
|
||||
<th style={(explainTable.show_rowsx || explainTable.show_rows || explainTable.show_plan_rows) ? {} : {display: 'none'}}
|
||||
colSpan={(explainTable.show_rowsx) ? '3' : '1'}>
|
||||
<button disabled="">{gettext('Rows')}</button>
|
||||
</th>
|
||||
<th style={(explainTable.show_rowsx || explainTable.show_rows) ? {} : {display: 'none'}} rowSpan="2">
|
||||
<button disabled="">{gettext('Loops')}</button>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style={explainTable.show_timings ? {} : {display: 'none'}}>
|
||||
<button disabled="">{gettext('Exclusive')}</button>
|
||||
</th>
|
||||
<th style={explainTable.show_timings ? {} : {display: 'none'}}>
|
||||
<button disabled="">{gettext('Inclusive')}</button>
|
||||
</th>
|
||||
<th style={explainTable.show_rowsx ? {} : {display: 'none'}}><button disabled="">{gettext('Rows X')}</button></th>
|
||||
<th style={(explainTable.show_rowsx || explainTable.show_rows) ? {} : {display: 'none'}}><button disabled="">{gettext('Actual')}</button></th>
|
||||
<th style={(explainTable.show_rowsx || explainTable.show_plan_rows) ? {} : {display: 'none'}}><button disabled="">{gettext('Plan')}</button></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{_.sortBy(explainTable.rows,(r)=>r['data']['_serial']).map((row)=>{
|
||||
return <ExplainRow key={row?.data?.arr_id} row={row} show={{
|
||||
show_timings: explainTable.show_timings,
|
||||
show_rowsx: explainTable.show_rowsx,
|
||||
show_rows: explainTable.show_rows,
|
||||
show_plan_rows: explainTable.show_plan_rows,
|
||||
}} activeExId={activeExId} setActiveExId={setActiveExId} collapsedExId={collapsedExId} toggleCollapseExId={toggleCollapseExId} />;
|
||||
})}
|
||||
</tbody>
|
||||
</StyledTable>
|
||||
);
|
||||
}
|
||||
|
||||
Analysis.propTypes = {
|
||||
|
|
|
@ -7,43 +7,42 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import React from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { Box, Grid } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import gettext from 'sources/gettext';
|
||||
import { commonTableStyles } from '../Theme';
|
||||
import clsx from 'clsx';
|
||||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import Table from '../components/Table';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
title: {
|
||||
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
'& .ExplainStatistics-title': {
|
||||
fontWeight: 'bold',
|
||||
padding: '4px',
|
||||
backgroundColor: theme.otherVars.cardHeaderBg,
|
||||
borderTopLeftRadius: theme.shape.borderRadius,
|
||||
borderTopRightRadius: theme.shape.borderRadius,
|
||||
},
|
||||
tableRow: {
|
||||
backgroundColor: theme.palette.grey[200]
|
||||
},
|
||||
tableName:{
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
nodeName: {
|
||||
paddingLeft: '30px',
|
||||
'& .ExplainStatistics-tableRow': {
|
||||
backgroundColor: theme.palette.grey[200],
|
||||
'& .ExplainStatistics-tableName': {
|
||||
fontWeight: 'bold',
|
||||
'& .ExplainStatistics-nodeName': {
|
||||
paddingLeft: '30px',
|
||||
}
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export default function ExplainStatistics({explainTable}) {
|
||||
// _renderStatisticsTable
|
||||
const classes = useStyles();
|
||||
const tableClasses = commonTableStyles();
|
||||
|
||||
return (
|
||||
<Box p={1}>
|
||||
<StyledBox p={1}>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item lg={6} md={12}>
|
||||
<div className={classes.title}>{gettext('Statistics per Node Type')}</div>
|
||||
<table className={clsx(tableClasses.table)}>
|
||||
<div className='ExplainStatistics-title'>{gettext('Statistics per Node Type')}</div>
|
||||
<Table >
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{gettext('Node type')}</th>
|
||||
|
@ -67,11 +66,11 @@ export default function ExplainStatistics({explainTable}) {
|
|||
</tr>;
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</Table>
|
||||
</Grid>
|
||||
<Grid item lg={6} md={12}>
|
||||
<div className={classes.title}>{gettext('Statistics per Relation')}</div>
|
||||
<table className={clsx(tableClasses.table)}>
|
||||
<div className='ExplainStatistics-title'>{gettext('Statistics per Relation')}</div>
|
||||
<Table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{gettext('Relation name')}</th>
|
||||
|
@ -95,8 +94,8 @@ export default function ExplainStatistics({explainTable}) {
|
|||
let table = explainTable.statistics.tables[key];
|
||||
table.sum_of_times = _.sumBy(Object.values(table.nodes), 'sum_of_times');
|
||||
return <React.Fragment key={i}>
|
||||
<tr className={classes.tableRow}>
|
||||
<td className={classes.tableName}>{table.name}</td>
|
||||
<tr className='ExplainStatistics-tableRow'>
|
||||
<td className='ExplainStatistics-tableName'>{table.name}</td>
|
||||
<td>{table.count}</td>
|
||||
{explainTable.show_timings && <>
|
||||
<td>{Math.ceil10(table.sum_of_times, -3) + ' ms'}</td>
|
||||
|
@ -106,7 +105,7 @@ export default function ExplainStatistics({explainTable}) {
|
|||
{_.sortBy(Object.keys(table.nodes)).map((nodeKey, j)=>{
|
||||
let node = table.nodes[nodeKey];
|
||||
return <tr key={j}>
|
||||
<td><div className={classes.nodeName}>{node.name}</div></td>
|
||||
<td><div className='ExplainStatistics-nodeName'>{node.name}</div></td>
|
||||
<td>{node.count}</td>
|
||||
{explainTable.show_timings && <>
|
||||
<td>{Math.ceil10(node.sum_of_times, -3) + ' ms'}</td>
|
||||
|
@ -117,10 +116,10 @@ export default function ExplainStatistics({explainTable}) {
|
|||
</React.Fragment>;
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</Table>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import { Box, Card, CardContent, CardHeader, useTheme } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React, {useEffect} from 'react';
|
||||
import _ from 'lodash';
|
||||
import { PgButtonGroup, PgIconButton } from '../components/Buttons';
|
||||
|
@ -20,9 +20,40 @@ import ReactDOMServer from 'react-dom/server';
|
|||
import url_for from 'sources/url_for';
|
||||
import { downloadSvg } from './svg_download';
|
||||
import CloseIcon from '@mui/icons-material/CloseRounded';
|
||||
import { commonTableStyles } from '../Theme';
|
||||
import clsx from 'clsx';
|
||||
import PropTypes from 'prop-types';
|
||||
import Table from '../components/Table';
|
||||
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
'& .Graphical-explainDetails': {
|
||||
minWidth: '200px',
|
||||
maxWidth: '300px',
|
||||
position: 'absolute',
|
||||
top: '0.25rem',
|
||||
bottom: '0.25rem',
|
||||
right: '0.25rem',
|
||||
borderColor: theme.otherVars.borderColor,
|
||||
// box-shadow: 0 0.125rem 0.5rem rgb(132 142 160 / 28%);
|
||||
wordBreak: 'break-all',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
zIndex: 99,
|
||||
'& .Graphical-explainContent': {
|
||||
height: '100%',
|
||||
overflow: 'auto',
|
||||
'& .Graphical-tableBorderBottom':{
|
||||
'& tbody tr:last-of-type td': {
|
||||
borderBottom: '1px solid '+theme.otherVars.borderColor,
|
||||
},
|
||||
},
|
||||
'& .Graphical-tablewrapTd': {
|
||||
'& tbody td': {
|
||||
whiteSpace: 'pre-wrap',
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
));
|
||||
|
||||
// Some predefined constants used to calculate image location and its border
|
||||
let pWIDTH = 100;
|
||||
|
@ -338,30 +369,8 @@ PlanSVG.propTypes = {
|
|||
};
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
explainDetails: {
|
||||
minWidth: '200px',
|
||||
maxWidth: '300px',
|
||||
position: 'absolute',
|
||||
top: '0.25rem',
|
||||
bottom: '0.25rem',
|
||||
right: '0.25rem',
|
||||
borderColor: theme.otherVars.borderColor,
|
||||
// box-shadow: 0 0.125rem 0.5rem rgb(132 142 160 / 28%);
|
||||
wordBreak: 'break-all',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
zIndex: 99,
|
||||
},
|
||||
explainContent: {
|
||||
height: '100%',
|
||||
overflow: 'auto',
|
||||
}
|
||||
}));
|
||||
|
||||
export default function Graphical({planData, ctx}) {
|
||||
const tableStyles = commonTableStyles();
|
||||
const classes = useStyles();
|
||||
|
||||
const graphContainerRef = React.useRef();
|
||||
const [zoomFactor, setZoomFactor] = React.useState(INIT_ZOOM_FACTOR);
|
||||
const [[explainPlanTitle, explainPlanDetails], setExplainPlanDetails] = React.useState([null, null]);
|
||||
|
@ -406,7 +415,7 @@ export default function Graphical({planData, ctx}) {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<Box ref={graphContainerRef} height="100%" width="100%" overflow="auto">
|
||||
<StyledBox ref={graphContainerRef} height="100%" width="100%" overflow="auto">
|
||||
<Box position="absolute" top="4px" left="4px" gap="4px" display="flex">
|
||||
<PgButtonGroup size="small">
|
||||
<PgIconButton title={gettext('Zoom in')} icon={<ZoomInIcon />} onClick={()=>onCmdClick('in')}/>
|
||||
|
@ -421,22 +430,22 @@ export default function Graphical({planData, ctx}) {
|
|||
onNodeClick={onNodeClick}
|
||||
/>
|
||||
{Boolean(explainPlanDetails) &&
|
||||
<Card className={classes.explainDetails} data-label="explain-details">
|
||||
<Card className='Graphical-explainDetails' data-label="explain-details">
|
||||
<CardHeader title={<Box display="flex">
|
||||
{explainPlanTitle}
|
||||
<Box marginLeft="auto">
|
||||
<PgIconButton title={gettext('Close')} icon={<CloseIcon />} size="xs" noBorder onClick={()=>setExplainPlanDetails([null, null])}/>
|
||||
</Box>
|
||||
</Box>} />
|
||||
<CardContent className={classes.explainContent}>
|
||||
<table className={clsx(tableStyles.table, tableStyles.borderBottom, tableStyles.wrapTd)}>
|
||||
<CardContent className='Graphical-explainContent'>
|
||||
<Table classNameRoot={'Graphical-tableBorderBottom Graphical-tablewrapTd'}>
|
||||
<tbody>
|
||||
<NodeDetails download={false} plan={explainPlanDetails} />
|
||||
</tbody>
|
||||
</table>
|
||||
</Table>
|
||||
</CardContent>
|
||||
</Card>}
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,23 +7,23 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import { Box, Tab, Tabs } from '@mui/material';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import Graphical from './Graphical';
|
||||
import TabPanel from '../components/TabPanel';
|
||||
import gettext from 'sources/gettext';
|
||||
import ImageMapper from './ImageMapper';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import Analysis from './Analysis';
|
||||
import ExplainStatistics from './ExplainStatistics';
|
||||
import PropTypes from 'prop-types';
|
||||
import EmptyPanelMessage from '../components/EmptyPanelMessage';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
tabPanel: {
|
||||
padding: 0,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
},
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
'& .Explain-tabPanel': {
|
||||
padding: '0 !important',
|
||||
backgroundColor: theme.palette.background.default + ' !important',
|
||||
}
|
||||
}));
|
||||
|
||||
// Some predefined constants used to calculate image location and its border
|
||||
|
@ -466,7 +466,7 @@ function parsePlanData(data, ctx) {
|
|||
}
|
||||
|
||||
export default function Explain({plans=[]}) {
|
||||
const classes = useStyles();
|
||||
|
||||
const [tabValue, setTabValue] = React.useState(0);
|
||||
|
||||
let ctx = React.useRef({});
|
||||
|
@ -488,12 +488,14 @@ export default function Explain({plans=[]}) {
|
|||
}, [plans]);
|
||||
|
||||
if(_.isEmpty(plans)) {
|
||||
return <Box height="100%" display="flex" flexDirection="column">
|
||||
<EmptyPanelMessage text={gettext('Use Explain/Explain analyze button to generate the plan for a query. Alternatively, you can also execute "EXPLAIN (FORMAT JSON) [QUERY]".')} />
|
||||
</Box>;
|
||||
return (
|
||||
<StyledBox height="100%" display="flex" flexDirection="column">
|
||||
<EmptyPanelMessage text={gettext('Use Explain/Explain analyze button to generate the plan for a query. Alternatively, you can also execute "EXPLAIN (FORMAT JSON) [QUERY]".')} />
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Box height="100%" display="flex" flexDirection="column">
|
||||
<StyledBox height="100%" display="flex" flexDirection="column">
|
||||
<Box>
|
||||
<Tabs
|
||||
value={tabValue}
|
||||
|
@ -510,16 +512,16 @@ export default function Explain({plans=[]}) {
|
|||
<Tab label="Statistics" />
|
||||
</Tabs>
|
||||
</Box>
|
||||
<TabPanel value={tabValue} index={0} classNameRoot={classes.tabPanel}>
|
||||
<TabPanel value={tabValue} index={0} classNameRoot='Explain-tabPanel'>
|
||||
<Graphical planData={planData} ctx={ctx.current}/>
|
||||
</TabPanel>
|
||||
<TabPanel value={tabValue} index={1} classNameRoot={classes.tabPanel}>
|
||||
<TabPanel value={tabValue} index={1} classNameRoot='Explain-tabPanel'>
|
||||
<Analysis explainTable={ctx.current.explainTable} />
|
||||
</TabPanel>
|
||||
<TabPanel value={tabValue} index={2} classNameRoot={classes.tabPanel}>
|
||||
<TabPanel value={tabValue} index={2} classNameRoot='Explain-tabPanel'>
|
||||
<ExplainStatistics explainTable={ctx.current.explainTable} />
|
||||
</TabPanel>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Checkbox } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import clsx from 'clsx';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import gettext from 'sources/gettext';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Tree } from 'react-arborist';
|
||||
|
@ -13,35 +12,34 @@ import EmptyPanelMessage from '../components/EmptyPanelMessage';
|
|||
import CheckBoxIcon from '@mui/icons-material/CheckBox';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
node: {
|
||||
display: 'inline-block',
|
||||
paddingLeft: '1.5rem',
|
||||
height: '100%',
|
||||
},
|
||||
checkboxStyle: {
|
||||
fill: theme.palette.primary.main
|
||||
},
|
||||
tree: {
|
||||
|
||||
const Root = styled('div')(({theme}) => ({
|
||||
height: '100%',
|
||||
'& .PgTree-tree': {
|
||||
background: theme.palette.background.default,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flex: 1,
|
||||
},
|
||||
focusedNode: {
|
||||
background: theme.palette.primary.light,
|
||||
},
|
||||
leafNode: {
|
||||
marginLeft: '1.5rem'
|
||||
'& .PgTree-leafNode': {
|
||||
marginLeft: '1.5rem'
|
||||
},
|
||||
'& .PgTree-node': {
|
||||
display: 'inline-block',
|
||||
paddingLeft: '1.5rem',
|
||||
height: '100%',
|
||||
},
|
||||
'& .PgTree-focusedNode': {
|
||||
background: theme.palette.primary.light,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export const PgTreeSelectionContext = React.createContext();
|
||||
|
||||
export default function PgTreeView({ data = [], hasCheckbox = false, selectionChange = null}) {
|
||||
let classes = useStyles();
|
||||
|
||||
let treeData = data;
|
||||
const treeObj = useRef();
|
||||
const treeContainerRef = useRef();
|
||||
|
@ -69,10 +67,10 @@ export default function PgTreeView({ data = [], hasCheckbox = false, selectionCh
|
|||
selectionChange?.(selectedChNodes);
|
||||
};
|
||||
|
||||
return (<>
|
||||
return (<Root>
|
||||
{ treeData.length > 0 ?
|
||||
<PgTreeSelectionContext.Provider value={selectedCheckBoxNodes}>
|
||||
<div ref={(containerRef) => treeContainerRef.current = containerRef} className={clsx(classes.tree)}>
|
||||
<div ref={(containerRef) => treeContainerRef.current = containerRef} className={'PgTree-tree'}>
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
<Tree
|
||||
|
@ -97,7 +95,7 @@ export default function PgTreeView({ data = [], hasCheckbox = false, selectionCh
|
|||
:
|
||||
<EmptyPanelMessage text={gettext('No objects are found to display')}/>
|
||||
}
|
||||
</>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -108,7 +106,7 @@ PgTreeView.propTypes = {
|
|||
};
|
||||
|
||||
function Node({ node, style, tree, hasCheckbox, onNodeSelectionChange}) {
|
||||
const classes = useStyles();
|
||||
|
||||
const pgTreeSelCtx = React.useContext(PgTreeSelectionContext);
|
||||
const [isSelected, setIsSelected] = React.useState(pgTreeSelCtx.includes(node.id) || node.data?.isSelected);
|
||||
const [isIndeterminate, setIsIndeterminate] = React.useState(node?.parent.level==0);
|
||||
|
@ -173,16 +171,16 @@ function Node({ node, style, tree, hasCheckbox, onNodeSelectionChange}) {
|
|||
};
|
||||
|
||||
return (
|
||||
<div style={style} className={clsx(node.isFocused ? classes.focusedNode : '')} onClick={onSelect} onKeyDown={onKeyDown}>
|
||||
<div style={style} className={node.isFocused ? 'PgTree-focusedNode' : ''} onClick={onSelect} onKeyDown={onKeyDown}>
|
||||
<CollectionArrow node={node} tree={tree} selectedNodeIds={pgTreeSelCtx} />
|
||||
{
|
||||
hasCheckbox ? <Checkbox style={{ padding: 0 }} color="primary" className={clsx(!node.isInternal ? classes.leafNode: null)}
|
||||
hasCheckbox ? <Checkbox style={{ padding: 0 }} color="primary" className={!node.isInternal ? 'PgTree-leafNode': null}
|
||||
checked={isSelected}
|
||||
checkedIcon={isIndeterminate ? <IndeterminateCheckBoxIcon style={{height: '1.4rem'}} />: <CheckBoxIcon style={{height: '1.4rem'}} />}
|
||||
onChange={onCheckboxSelection}/> :
|
||||
<span className={clsx(node.data.icon)}></span>
|
||||
<span className={node.data.icon}></span>
|
||||
}
|
||||
<div className={clsx(node.data.icon, classes.node)}>{node.data.name}</div>
|
||||
<div className={node.data.icon + ' PgTree-node'}>{node.data.name}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,9 +7,8 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import React, {useRef,useState, useEffect} from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { CircularProgress, Typography } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import clsx from 'clsx';
|
||||
import {useDelayDebounce} from 'sources/custom_hooks';
|
||||
import {onlineHelpSearch} from './online_help';
|
||||
import {menuSearch} from './menuitems_help';
|
||||
|
@ -18,8 +17,19 @@ import PropTypes from 'prop-types';
|
|||
import { InputText } from '../components/FormComponents';
|
||||
import EmptyPanelMessage from '../components/EmptyPanelMessage';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
helpGroup: {
|
||||
const StyledDiv = styled('div')(({theme}) => ({
|
||||
'& .QuickSearch-loaderRoot': {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
padding: '8px',
|
||||
justifyContent: 'center',
|
||||
'& .QuickSearch-loader': {
|
||||
height: '25px !important',
|
||||
width: '25px !important',
|
||||
marginRight: '8px',
|
||||
}
|
||||
},
|
||||
'& .QuickSearch-helpGroup': {
|
||||
backgroundColor: theme.palette.grey[400],
|
||||
padding: '6px',
|
||||
fontSize: '0.85em',
|
||||
|
@ -27,7 +37,7 @@ const useStyles = makeStyles((theme)=>({
|
|||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
searchItem: {
|
||||
'& .QuickSearch-searchItem': {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: '4px 8px',
|
||||
|
@ -44,30 +54,18 @@ const useStyles = makeStyles((theme)=>({
|
|||
pointerEvents: 'none',
|
||||
}
|
||||
},
|
||||
showAll: {
|
||||
'& .QuickSearch-showAll': {
|
||||
marginLeft: 'auto',
|
||||
color: 'inherit',
|
||||
textDecoration: 'none'
|
||||
},
|
||||
loaderRoot: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
padding: '8px',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
loader: {
|
||||
height: '25px !important',
|
||||
width: '25px !important',
|
||||
marginRight: '8px',
|
||||
},
|
||||
}));
|
||||
|
||||
function SearchLoader({loading=false}) {
|
||||
const classes = useStyles();
|
||||
if(loading) {
|
||||
return (
|
||||
<div className={classes.loaderRoot}>
|
||||
<CircularProgress className={classes.loader} />
|
||||
<div className='QuickSearch-loaderRoot'>
|
||||
<CircularProgress className='QuickSearch-loader' />
|
||||
<Typography>{gettext('Searching...')}</Typography>
|
||||
</div>
|
||||
);
|
||||
|
@ -79,10 +77,10 @@ SearchLoader.propTypes = {
|
|||
};
|
||||
|
||||
function HelpArticleContents({isHelpLoading, isMenuLoading, helpSearchResult}) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (isHelpLoading && !(isMenuLoading??true)) ? (
|
||||
<div>
|
||||
<div className={classes.helpGroup}>
|
||||
<>
|
||||
<div className='QuickSearch-helpGroup'>
|
||||
<span className='fa fa-question-circle'></span>
|
||||
HELP ARTICLES
|
||||
{Object.keys(helpSearchResult.data).length > 10
|
||||
|
@ -95,7 +93,7 @@ function HelpArticleContents({isHelpLoading, isMenuLoading, helpSearchResult}) {
|
|||
}
|
||||
</div>
|
||||
<SearchLoader loading={true} />
|
||||
</div>) : <></>;
|
||||
</>) : <></>;
|
||||
}
|
||||
|
||||
HelpArticleContents.propTypes = {
|
||||
|
@ -105,7 +103,7 @@ HelpArticleContents.propTypes = {
|
|||
};
|
||||
|
||||
export default function QuickSearch({closeModal}) {
|
||||
const classes = useStyles();
|
||||
|
||||
const wrapperRef = useRef(null);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [isShowMinLengthMsg, setIsShowMinLengthMsg] = useState(false);
|
||||
|
@ -192,7 +190,7 @@ export default function QuickSearch({closeModal}) {
|
|||
let menuItemsHtmlElement = [];
|
||||
items.forEach((i) => {
|
||||
menuItemsHtmlElement.push(
|
||||
<div key={ 'li-menu-' + i.label }><a tabIndex={i.isDisabled ? '-1' : '0'} id={ 'li-menu-' + i.label } href={'#'} className={ (i.isDisabled ? clsx(classes.searchItem, 'disabled'):classes.searchItem)} onClick={
|
||||
<div key={ 'li-menu-' + i.label }><a tabIndex={i.isDisabled ? '-1' : '0'} id={ 'li-menu-' + i.label } href={'#'} className={ (i.isDisabled ? 'QuickSearch-searchItem disabled':'QuickSearch-searchItem')} onClick={
|
||||
() => {
|
||||
closeModal();
|
||||
i.callback();
|
||||
|
@ -261,7 +259,7 @@ export default function QuickSearch({closeModal}) {
|
|||
return (
|
||||
<div id='quick-search-container' onClick={setSearchTerm} onKeyDown={()=>{/* no need */}}></div>,
|
||||
<div id='quick-search-container' ref={wrapperRef} role="menu">
|
||||
<div>
|
||||
<StyledDiv>
|
||||
<div>
|
||||
<div style={{padding: '2px 2px 2px 2px'}}>
|
||||
<InputText value={searchTerm} autoComplete='off' autoFocus
|
||||
|
@ -276,7 +274,7 @@ export default function QuickSearch({closeModal}) {
|
|||
<div >
|
||||
{ (menuSearchResult.fetched && !(isMenuLoading??true) ) ?
|
||||
<div>
|
||||
<div className={classes.helpGroup}>
|
||||
<div className='QuickSearch-helpGroup'>
|
||||
<span className='fa fa-bars'></span> {gettext('MENU ITEMS')} ({menuSearchResult.data.length})
|
||||
</div>
|
||||
|
||||
|
@ -293,16 +291,16 @@ export default function QuickSearch({closeModal}) {
|
|||
|
||||
{ (helpSearchResult.fetched && !(isHelpLoading??true)) ?
|
||||
<div>
|
||||
<div className={classes.helpGroup}>
|
||||
<div className='QuickSearch-helpGroup'>
|
||||
<span className='fa fa-question-circle'></span> {gettext('HELP ARTICLES')} {Object.keys(helpSearchResult.data).length > 10 ?
|
||||
<span> (10 of {Object.keys(helpSearchResult.data).length})</span>:
|
||||
'(' + Object.keys(helpSearchResult.data).length + ')'}
|
||||
{ !helpSearchResult.clearedPooling ? <CircularProgress style={{height: '18px', width: '18px'}} /> :''}
|
||||
{ Object.keys(helpSearchResult.data).length > 10 ? <a href={helpSearchResult.url} className={classes.showAll} target='_blank' rel='noreferrer'>{gettext('Show all')} <span className='fas fa-external-link-alt' ></span></a> : ''}
|
||||
{ Object.keys(helpSearchResult.data).length > 10 ? <a href={helpSearchResult.url} className='QuickSearch-showAll' target='_blank' rel='noreferrer'>{gettext('Show all')} <span className='fas fa-external-link-alt' ></span></a> : ''}
|
||||
</div>
|
||||
|
||||
{Object.keys(helpSearchResult.data).map( (value, index) => {
|
||||
if(index <= 9) { return <div key={ 'li-help-' + value }><a tabIndex='0' href={helpSearchResult.data[value]} className={classes.searchItem} target='_blank' rel='noreferrer'>{value}</a></div>; }
|
||||
if(index <= 9) { return <div key={ 'li-help-' + value }><a tabIndex='0' href={helpSearchResult.data[value]} className='QuickSearch-searchItem' target='_blank' rel='noreferrer'>{value}</a></div>; }
|
||||
})}
|
||||
|
||||
{(Object.keys(helpSearchResult.data).length == 0) &&
|
||||
|
@ -315,7 +313,7 @@ export default function QuickSearch({closeModal}) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</StyledDiv>
|
||||
<div id='quick-search-iframe-container' />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
/* The DataGridView component is based on react-table component */
|
||||
|
||||
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { PgIconButton } from '../components/Buttons';
|
||||
import AddIcon from '@mui/icons-material/AddOutlined';
|
||||
import { MappedCellControl } from './MappedControl';
|
||||
|
@ -44,64 +44,58 @@ import { usePgAdmin } from '../BrowserComponent';
|
|||
import { requestAnimationAndFocus } from '../utils';
|
||||
import { PgReactTable, PgReactTableBody, PgReactTableCell, PgReactTableHeader, PgReactTableRow, PgReactTableRowContent, PgReactTableRowExpandContent } from '../components/PgReactTableStyled';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
grid: {
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
'& .DataGridView-grid': {
|
||||
...theme.mixins.panelBorder,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
},
|
||||
gridHeader: {
|
||||
display: 'flex',
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
backgroundColor: theme.otherVars.headerBg,
|
||||
},
|
||||
gridHeaderText: {
|
||||
padding: theme.spacing(0.5, 1),
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
},
|
||||
gridControls: {
|
||||
marginLeft: 'auto',
|
||||
},
|
||||
gridControlsButton: {
|
||||
border: 0,
|
||||
borderRadius: 0,
|
||||
...theme.mixins.panelBorder.left,
|
||||
},
|
||||
gridRowButton: {
|
||||
border: 0,
|
||||
borderRadius: 0,
|
||||
padding: 0,
|
||||
minWidth: 0,
|
||||
backgroundColor: 'inherit',
|
||||
'&.Mui-disabled': {
|
||||
border: 0,
|
||||
'& .DataGridView-gridHeader': {
|
||||
display: 'flex',
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
backgroundColor: theme.otherVars.headerBg,
|
||||
'& .DataGridView-gridHeaderText': {
|
||||
padding: theme.spacing(0.5, 1),
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
},
|
||||
'& .DataGridView-gridControls': {
|
||||
marginLeft: 'auto',
|
||||
'& .DataGridView-gridControlsButton': {
|
||||
border: 0,
|
||||
borderRadius: 0,
|
||||
...theme.mixins.panelBorder.left,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
gridTableContainer: {
|
||||
overflow: 'auto',
|
||||
width: '100%',
|
||||
},
|
||||
table: {
|
||||
'&.pgrt-table': {
|
||||
'& .pgrt-body':{
|
||||
'& .pgrt-row': {
|
||||
position: 'unset',
|
||||
backgroundColor: theme.otherVars.emptySpaceBg,
|
||||
|
||||
'& .pgrt-row-content':{
|
||||
'& .pgrd-row-cell': {
|
||||
height: 'auto',
|
||||
padding: theme.spacing(0.5),
|
||||
|
||||
'&.btn-cell, &.expanded-icon-cell': {
|
||||
padding: '2px 0px'
|
||||
'& .DataGridView-table': {
|
||||
'&.pgrt-table': {
|
||||
'& .pgrt-body':{
|
||||
'& .pgrt-row': {
|
||||
position: 'unset',
|
||||
backgroundColor: theme.otherVars.emptySpaceBg,
|
||||
'& .pgrt-row-content':{
|
||||
'& .pgrd-row-cell': {
|
||||
height: 'auto',
|
||||
padding: theme.spacing(0.5),
|
||||
'&.btn-cell, &.expanded-icon-cell': {
|
||||
padding: '2px 0px'
|
||||
},
|
||||
'& .DataGridView-gridRowButton': {
|
||||
border: 0,
|
||||
borderRadius: 0,
|
||||
padding: 0,
|
||||
minWidth: 0,
|
||||
backgroundColor: 'inherit',
|
||||
'&.Mui-disabled': {
|
||||
border: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
tableRowHovered: {
|
||||
'& .DataGridView-tableRowHovered': {
|
||||
position: 'relative',
|
||||
'& .hover-overlay': {
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
|
@ -110,23 +104,11 @@ const useStyles = makeStyles((theme)=>({
|
|||
opacity: 0.75,
|
||||
}
|
||||
},
|
||||
tableCellHeader: {
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
padding: theme.spacing(1, 0.5),
|
||||
textAlign: 'left',
|
||||
},
|
||||
tableContentWidth: {
|
||||
width: 'calc(100% - 3px)',
|
||||
},
|
||||
btnCell: {
|
||||
padding: theme.spacing(0.5, 0),
|
||||
textAlign: 'center',
|
||||
},
|
||||
btnReorder: {
|
||||
'& .DataGridView-btnReorder': {
|
||||
cursor: 'move',
|
||||
padding: '4px 2px',
|
||||
},
|
||||
resizer: {
|
||||
'& .DataGridView-resizer': {
|
||||
display: 'inline-block',
|
||||
width: '5px',
|
||||
height: '100%',
|
||||
|
@ -137,17 +119,17 @@ const useStyles = makeStyles((theme)=>({
|
|||
zIndex: 1,
|
||||
touchAction: 'none',
|
||||
},
|
||||
expandedForm: {
|
||||
'& .DataGridView-expandedForm': {
|
||||
border: '1px solid '+theme.palette.grey[400],
|
||||
},
|
||||
expandedIconCell: {
|
||||
'& .DataGridView-expandedIconCell': {
|
||||
backgroundColor: theme.palette.grey[400],
|
||||
borderBottom: 'none',
|
||||
}
|
||||
}));
|
||||
|
||||
function DataTableRow({index, row, totalRows, isResizing, isHovered, schema, schemaRef, accessPath, moveRow, setHoverIndex, viewHelperProps}) {
|
||||
const classes = useStyles();
|
||||
|
||||
const [key, setKey] = useState(false);
|
||||
const depListener = useContext(DepListenerContext);
|
||||
const rowRef = useRef(null);
|
||||
|
@ -259,7 +241,7 @@ function DataTableRow({index, row, totalRows, isResizing, isHovered, schema, sch
|
|||
drop(rowRef);
|
||||
|
||||
return useMemo(()=>
|
||||
<PgReactTableRowContent ref={rowRef} row={row} data-handler-id={handlerId} className={isHovered ? classes.tableRowHovered : null} data-test='data-table-row' style={{position: 'initial'}}>
|
||||
<PgReactTableRowContent ref={rowRef} row={row} data-handler-id={handlerId} className={isHovered ? 'DataGridView-tableRowHovered' : null} data-test='data-table-row' style={{position: 'initial'}}>
|
||||
{row.getVisibleCells().map((cell) => {
|
||||
let {modeSupported} = cell.column.field ? getFieldMetaData(cell.column.field, schemaRef.current, {}, viewHelperProps) : {modeSupported: true};
|
||||
|
||||
|
@ -280,16 +262,14 @@ function DataTableRow({index, row, totalRows, isResizing, isHovered, schema, sch
|
|||
}
|
||||
|
||||
export function DataGridHeader({label, canAdd, onAddClick, canSearch, onSearchTextChange}) {
|
||||
const classes = useStyles();
|
||||
const [searchText, setSearchText] = useState('');
|
||||
|
||||
return (
|
||||
<Box className={classes.gridHeader}>
|
||||
<Box className='DataGridView-gridHeader'>
|
||||
{ label &&
|
||||
<Box className={classes.gridHeaderText}>{label}</Box>
|
||||
<Box className='DataGridView-gridHeaderText'>{label}</Box>
|
||||
}
|
||||
{ canSearch &&
|
||||
<Box className={classes.gridHeaderText} width={'100%'}>
|
||||
<Box className='DataGridView-gridHeaderText' width={'100%'}>
|
||||
<InputText value={searchText}
|
||||
onChange={(value)=>{
|
||||
onSearchTextChange(value);
|
||||
|
@ -299,12 +279,12 @@ export function DataGridHeader({label, canAdd, onAddClick, canSearch, onSearchTe
|
|||
</InputText>
|
||||
</Box>
|
||||
}
|
||||
<Box className={classes.gridControls}>
|
||||
<Box className='DataGridView-gridControls'>
|
||||
{canAdd && <PgIconButton data-test="add-row" title={gettext('Add row')} onClick={()=>{
|
||||
setSearchText('');
|
||||
onSearchTextChange('');
|
||||
onAddClick();
|
||||
}} icon={<AddIcon />} className={classes.gridControlsButton} />}
|
||||
}} icon={<AddIcon />} className='DataGridView-gridControlsButton' />}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
@ -320,7 +300,7 @@ DataGridHeader.propTypes = {
|
|||
export default function DataGridView({
|
||||
value, viewHelperProps, schema, accessPath, dataDispatch, containerClassName,
|
||||
fixedRows, ...props}) {
|
||||
const classes = useStyles();
|
||||
|
||||
const stateUtils = useContext(StateUtilsContext);
|
||||
const checkIsMounted = useIsMounted();
|
||||
const [hoverIndex, setHoverIndex] = useState();
|
||||
|
@ -345,7 +325,7 @@ export default function DataGridView({
|
|||
maxSize: 26,
|
||||
minSize: 26,
|
||||
cell: ()=>{
|
||||
return <div className={classes.btnReorder}>
|
||||
return <div className='DataGridView-btnReorder'>
|
||||
<DragIndicatorRoundedIcon fontSize="small" />
|
||||
</div>;
|
||||
}
|
||||
|
@ -369,7 +349,7 @@ export default function DataGridView({
|
|||
if(props.canEditRow) {
|
||||
canEditRow = evalFunc(schemaRef.current, props.canEditRow, row || {});
|
||||
}
|
||||
return <PgIconButton data-test="expand-row" title={gettext('Edit row')} icon={<EditRoundedIcon fontSize="small" />} className={classes.gridRowButton}
|
||||
return <PgIconButton data-test="expand-row" title={gettext('Edit row')} icon={<EditRoundedIcon fontSize="small" />} className='DataGridView-gridRowButton'
|
||||
onClick={()=>{
|
||||
row.toggleExpanded();
|
||||
}} disabled={!canEditRow}
|
||||
|
@ -423,7 +403,7 @@ export default function DataGridView({
|
|||
}
|
||||
);
|
||||
}
|
||||
}} className={classes.gridRowButton} disabled={!canDeleteRow} />
|
||||
}} className='DataGridView-gridRowButton' disabled={!canDeleteRow} />
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -607,8 +587,8 @@ export default function DataGridView({
|
|||
}
|
||||
|
||||
return (
|
||||
<Box className={containerClassName}>
|
||||
<Box className={classes.grid}>
|
||||
<StyledBox className={containerClassName}>
|
||||
<Box className='DataGridView-grid'>
|
||||
{(props.label || props.canAdd) && <DataGridHeader label={props.label} canAdd={props.canAdd} onAddClick={onAddClick}
|
||||
canSearch={props.canSearch}
|
||||
onSearchTextChange={(value)=>{
|
||||
|
@ -616,7 +596,7 @@ export default function DataGridView({
|
|||
}}
|
||||
/>}
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<PgReactTable ref={tableRef} table={table} data-test="data-grid-view" tableClassName={classes.table}>
|
||||
<PgReactTable ref={tableRef} table={table} data-test="data-grid-view" tableClassName='DataGridView-table'>
|
||||
<PgReactTableHeader table={table} />
|
||||
<PgReactTableBody>
|
||||
{rows.map((row, i) => {
|
||||
|
@ -628,7 +608,7 @@ export default function DataGridView({
|
|||
{props.canEdit &&
|
||||
<PgReactTableRowExpandContent row={row}>
|
||||
<FormView value={row.original} viewHelperProps={viewHelperProps} dataDispatch={dataDispatch}
|
||||
schema={schemaRef.current} accessPath={accessPath.concat([row.index])} isNested={true} className={classes.expandedForm}
|
||||
schema={schemaRef.current} accessPath={accessPath.concat([row.index])} isNested={true} className='DataGridView-expandedForm'
|
||||
isDataGridForm={true} firstEleRef={(ele)=>{
|
||||
requestAnimationAndFocus(ele);
|
||||
}}/>
|
||||
|
@ -640,7 +620,7 @@ export default function DataGridView({
|
|||
</PgReactTable>
|
||||
</DndProvider>
|
||||
</Box>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,11 +8,10 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { Box, Tab, Tabs, Grid } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { MappedFormControl } from './MappedControl';
|
||||
import TabPanel from '../components/TabPanel';
|
||||
|
@ -26,44 +25,45 @@ import { useOnScreen } from '../custom_hooks';
|
|||
import { DepListenerContext } from './DepListener';
|
||||
import FieldSetView from './FieldSetView';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
fullSpace: {
|
||||
padding: 0,
|
||||
height: '100%',
|
||||
overflow: 'hidden',
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
'& .FormView-nestedControl': {
|
||||
height: 'unset !important',
|
||||
'& .FormView-controlRow': {
|
||||
marginBottom: theme.spacing(1),
|
||||
},
|
||||
'& .FormView-nestedTabPanel': {
|
||||
backgroundColor: theme.otherVars.headerBg,
|
||||
}
|
||||
},
|
||||
controlRow: {
|
||||
marginBottom: theme.spacing(1),
|
||||
},
|
||||
nestedTabPanel: {
|
||||
backgroundColor: theme.otherVars.headerBg,
|
||||
},
|
||||
nestedControl: {
|
||||
height: 'unset',
|
||||
},
|
||||
fullControl: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
errorMargin: {
|
||||
'& .FormView-errorMargin': {
|
||||
/* Error footer space */
|
||||
paddingBottom: '36px !important',
|
||||
},
|
||||
sqlTabInput: {
|
||||
border: 0,
|
||||
'& .FormView-fullSpace': {
|
||||
padding: '0 !important',
|
||||
height: '100%',
|
||||
overflow: 'hidden',
|
||||
'& .FormView-fullControl': {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
'& .FormView-sqlTabInput': {
|
||||
border: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
nonTabPanel: {
|
||||
padding: 0,
|
||||
background: 'inherit',
|
||||
'& .FormView-nonTabPanel': {
|
||||
backgroundColor: 'inherit',
|
||||
'& .FormView-nonTabPanelContent': {
|
||||
height: 'unset',
|
||||
'& .FormView-controlRow': {
|
||||
marginBottom: theme.spacing(1),
|
||||
},
|
||||
}
|
||||
},
|
||||
nonTabPanelContent: {
|
||||
height: 'unset'
|
||||
}
|
||||
}));
|
||||
|
||||
/* Optional SQL tab */
|
||||
function SQLTab({active, getSQLValue}) {
|
||||
const classes = useStyles();
|
||||
const [sql, setSql] = useState('Loading...');
|
||||
useEffect(()=>{
|
||||
let unmounted = false;
|
||||
|
@ -84,7 +84,7 @@ function SQLTab({active, getSQLValue}) {
|
|||
readOnly: true,
|
||||
}}
|
||||
readonly={true}
|
||||
className={classes.sqlTabInput}
|
||||
className='FormView-sqlTabInput'
|
||||
/>;
|
||||
}
|
||||
|
||||
|
@ -167,7 +167,7 @@ export default function FormView({
|
|||
let tabs = {};
|
||||
let tabsClassname = {};
|
||||
const [tabValue, setTabValue] = useState(0);
|
||||
const classes = useStyles();
|
||||
|
||||
const firstEleID = useRef();
|
||||
const formRef = useRef();
|
||||
const onScreenTracker = useRef(false);
|
||||
|
@ -272,7 +272,7 @@ export default function FormView({
|
|||
tabs[group].push(
|
||||
<FieldSetView key={`nested${tabs[group].length}`} value={value} viewHelperProps={viewHelperProps}
|
||||
schema={field.schema} accessPath={accessPath} dataDispatch={dataDispatch} isNested={true} isDataGridForm={isDataGridForm}
|
||||
controlClassName={classes.controlRow}
|
||||
controlClassName='FormView-controlRow'
|
||||
{...field} visible={visible}/>
|
||||
);
|
||||
} else if(field.type === 'collection') {
|
||||
|
@ -293,7 +293,7 @@ export default function FormView({
|
|||
key: field.id, ...field,
|
||||
value: value[field.id] || [], viewHelperProps: viewHelperProps,
|
||||
schema: field.schema, accessPath: accessPath.concat(field.id), dataDispatch: dataDispatch,
|
||||
containerClassName: classes.controlRow,
|
||||
containerClassName: 'FormView-controlRow',
|
||||
canAdd: canAdd, canReorder: canReorder,
|
||||
canEdit: canEdit, canDelete: canDelete,
|
||||
visible: visible, canAddRow: canAddRow, onDelete: field.onDelete, canSearch: field.canSearch,
|
||||
|
@ -315,7 +315,7 @@ export default function FormView({
|
|||
* from there as well.
|
||||
*/
|
||||
if(field.isFullTab) {
|
||||
tabsClassname[group] = classes.fullSpace;
|
||||
tabsClassname[group] ='FormView-fullSpace';
|
||||
fullTabs.push(group);
|
||||
}
|
||||
|
||||
|
@ -353,7 +353,7 @@ export default function FormView({
|
|||
});
|
||||
}}
|
||||
hasError={hasError}
|
||||
className={classes.controlRow}
|
||||
className='FormView-controlRow'
|
||||
noLabel={field.isFullTab}
|
||||
memoDeps={[
|
||||
value[id],
|
||||
|
@ -361,7 +361,7 @@ export default function FormView({
|
|||
disabled,
|
||||
visible,
|
||||
hasError,
|
||||
classes.controlRow,
|
||||
'FormView-controlRow',
|
||||
...(evalFunc(null, field.deps) || []).map((dep)=>value[dep]),
|
||||
]}
|
||||
/>;
|
||||
|
@ -383,7 +383,7 @@ export default function FormView({
|
|||
withContainer: false, controlGridBasis: 3
|
||||
}));
|
||||
tabs[group].push(
|
||||
<Grid container spacing={0} key={`ic-${inlineComponents[0].key}`} className={classes.controlRow} rowGap="8px">
|
||||
<Grid container spacing={0} key={`ic-${inlineComponents[0].key}`} className='FormView-controlRow' rowGap="8px">
|
||||
{inlineComponents}
|
||||
</Grid>
|
||||
);
|
||||
|
@ -398,7 +398,7 @@ export default function FormView({
|
|||
|
||||
if(inlineComponents?.length > 0) {
|
||||
tabs[inlineCompGroup].push(
|
||||
<Grid container spacing={0} key={`ic-${inlineComponents[0].key}`} className={classes.controlRow} rowGap="8px">
|
||||
<Grid container spacing={0} key={`ic-${inlineComponents[0].key}`} className='FormView-controlRow' rowGap="8px">
|
||||
{inlineComponents}
|
||||
</Grid>
|
||||
);
|
||||
|
@ -415,7 +415,7 @@ export default function FormView({
|
|||
finalTabs[sqlTabName] = [
|
||||
<SQLTab key="sqltab" active={sqlTabActive} getSQLValue={getSQLValue} />,
|
||||
];
|
||||
tabsClassname[sqlTabName] = classes.fullSpace;
|
||||
tabsClassname[sqlTabName] = 'FormView-fullSpace';
|
||||
fullTabs.push(sqlTabName);
|
||||
}
|
||||
|
||||
|
@ -430,7 +430,7 @@ export default function FormView({
|
|||
|
||||
if(isTabView) {
|
||||
return (
|
||||
<Box height="100%" display="flex" flexDirection="column" className={className} ref={formRef} data-test="form-view">
|
||||
<StyledBox height="100%" display="flex" flexDirection="column" className={className} ref={formRef} data-test="form-view">
|
||||
<Box>
|
||||
<Tabs
|
||||
value={tabValue}
|
||||
|
@ -447,33 +447,34 @@ export default function FormView({
|
|||
</Tabs>
|
||||
</Box>
|
||||
{Object.keys(finalTabs).map((tabName, i)=>{
|
||||
let contentClassName = [stateUtils.formErr.message ? classes.errorMargin : null];
|
||||
let contentClassName = [(stateUtils.formErr.message ? 'FormView-errorMargin': null)];
|
||||
if(fullTabs.indexOf(tabName) == -1) {
|
||||
contentClassName.push(classes.nestedControl);
|
||||
contentClassName.push('FormView-nestedControl');
|
||||
} else {
|
||||
contentClassName.push(classes.fullControl);
|
||||
contentClassName.push('FormView-fullControl');
|
||||
}
|
||||
return (
|
||||
<TabPanel key={tabName} value={tabValue} index={i} classNameRoot={clsx(tabsClassname[tabName], isNested ? classes.nestedTabPanel : null)}
|
||||
className={clsx(contentClassName)} data-testid={tabName}>
|
||||
<TabPanel key={tabName} value={tabValue} index={i} classNameRoot={[tabsClassname[tabName], (isNested ? 'FormView-nestedTabPanel' : null)].join(' ')}
|
||||
className={contentClassName.join(' ')} data-testid={tabName}>
|
||||
{finalTabs[tabName]}
|
||||
</TabPanel>
|
||||
);
|
||||
})}
|
||||
</Box>);
|
||||
</StyledBox>
|
||||
);
|
||||
} else {
|
||||
let contentClassName = [classes.nonTabPanelContent, stateUtils.formErr.message ? classes.errorMargin : null];
|
||||
let contentClassName = ['FormView-nonTabPanelContent', (stateUtils.formErr.message ? 'FormView-errorMargin' : null)];
|
||||
return (
|
||||
<Box height="100%" display="flex" flexDirection="column" className={clsx(className)} ref={formRef} data-test="form-view">
|
||||
<TabPanel value={tabValue} index={0} classNameRoot={classes.nonTabPanel}
|
||||
className={clsx(contentClassName)}>
|
||||
<StyledBox height="100%" display="flex" flexDirection="column" className={className} ref={formRef} data-test="form-view">
|
||||
<TabPanel value={tabValue} index={0} classNameRoot='FormView-nonTabPanel'
|
||||
className={contentClassName.join(' ')}>
|
||||
{Object.keys(finalTabs).map((tabName)=>{
|
||||
return (
|
||||
<React.Fragment key={tabName}>{finalTabs[tabName]}</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</TabPanel>
|
||||
</Box>);
|
||||
</StyledBox>);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';
|
||||
import { Box, Accordion, AccordionSummary, AccordionDetails} from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import SaveIcon from '@mui/icons-material/Save';
|
||||
import PublishIcon from '@mui/icons-material/Publish';
|
||||
|
@ -21,7 +20,6 @@ import HelpIcon from '@mui/icons-material/HelpRounded';
|
|||
import EditIcon from '@mui/icons-material/Edit';
|
||||
import diffArray from 'diff-arrays-of-objects';
|
||||
import _ from 'lodash';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import {FormFooterMessage, MESSAGE_TYPE } from 'sources/components/FormComponents';
|
||||
import { PrimaryButton, DefaultButton, PgIconButton } from 'sources/components/Buttons';
|
||||
|
@ -41,35 +39,45 @@ import { useIsMounted } from '../custom_hooks';
|
|||
import ErrorBoundary from '../helpers/ErrorBoundary';
|
||||
import { usePgAdmin } from '../BrowserComponent';
|
||||
import { PgButtonGroup } from '../components/Buttons';
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
const useDialogStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
},
|
||||
form: {
|
||||
const StyledBox = styled(Box)(({theme})=>({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
minHeight: 0,
|
||||
'& .Dialog-form': {
|
||||
flexGrow: 1,
|
||||
position: 'relative',
|
||||
minHeight: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
formProperties: {
|
||||
backgroundColor: theme.palette.grey[400],
|
||||
},
|
||||
footer: {
|
||||
'& .Dialog-footer': {
|
||||
padding: theme.spacing(1),
|
||||
background: theme.otherVars.headerBg,
|
||||
display: 'flex',
|
||||
zIndex: 1010,
|
||||
...theme.mixins.panelBorder.top,
|
||||
'& .Dialog-buttonMargin': {
|
||||
marginRight: '0.5rem',
|
||||
},
|
||||
},
|
||||
mappedControl: {
|
||||
paddingBottom: theme.spacing(1),
|
||||
'& .Properties-toolbar': {
|
||||
padding: theme.spacing(1),
|
||||
background: theme.palette.background.default,
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
},
|
||||
buttonMargin: {
|
||||
marginRight: '0.5rem',
|
||||
'& .Properties-form': {
|
||||
padding: theme.spacing(1),
|
||||
overflow: 'auto',
|
||||
flexGrow: 1,
|
||||
'& .Properties-controlRow': {
|
||||
marginBottom: theme.spacing(1),
|
||||
},
|
||||
},
|
||||
'& .Properties-noPadding': {
|
||||
padding: 0,
|
||||
},
|
||||
}));
|
||||
|
||||
|
@ -476,7 +484,6 @@ function prepareData(val, createMode=false) {
|
|||
/* If its the dialog */
|
||||
function SchemaDialogView({
|
||||
getInitData, viewHelperProps, loadingText, schema={}, showFooter=true, isTabView=true, checkDirtyOnEnableSave=false, ...props}) {
|
||||
const classes = useDialogStyles();
|
||||
/* Some useful states */
|
||||
const [dirty, setDirty] = useState(false);
|
||||
/* formErr has 2 keys - name and message.
|
||||
|
@ -784,10 +791,10 @@ function SchemaDialogView({
|
|||
|
||||
/* I am Groot */
|
||||
return (
|
||||
<StateUtilsContext.Provider value={stateUtils}>
|
||||
<DepListenerContext.Provider value={depListenerObj.current}>
|
||||
<Box className={classes.root}>
|
||||
<Box className={classes.form}>
|
||||
<StyledBox>
|
||||
<StateUtilsContext.Provider value={stateUtils}>
|
||||
<DepListenerContext.Provider value={depListenerObj.current}>
|
||||
<Box className='Dialog-form'>
|
||||
<Loader message={loaderText || loadingText}/>
|
||||
<FormView value={sessData} viewHelperProps={viewHelperProps}
|
||||
schema={schema} accessPath={[]} dataDispatch={sessDispatchWithListener}
|
||||
|
@ -795,18 +802,18 @@ function SchemaDialogView({
|
|||
<FormFooterMessage type={MESSAGE_TYPE.ERROR} message={formErr.message}
|
||||
onClose={onErrClose} />
|
||||
</Box>
|
||||
{showFooter && <Box className={classes.footer}>
|
||||
{showFooter && <Box className='Dialog-footer'>
|
||||
{(!props.disableSqlHelp || !props.disableDialogHelp) && <Box>
|
||||
<PgIconButton data-test="sql-help" onClick={()=>props.onHelp(true, isNew)} icon={<InfoIcon />}
|
||||
disabled={props.disableSqlHelp} className={classes.buttonMargin} title="SQL help for this object type."/>
|
||||
disabled={props.disableSqlHelp} className='Dialog-buttonMargin' title="SQL help for this object type."/>
|
||||
<PgIconButton data-test="dialog-help" onClick={()=>props.onHelp(false, isNew)} icon={<HelpIcon />} title="Help for this dialog."
|
||||
disabled={props.disableDialogHelp}/>
|
||||
</Box>}
|
||||
<Box marginLeft="auto">
|
||||
<DefaultButton data-test="Close" onClick={props.onClose} startIcon={<CloseIcon />} className={classes.buttonMargin}>
|
||||
<DefaultButton data-test="Close" onClick={props.onClose} startIcon={<CloseIcon />} className='Dialog-buttonMargin'>
|
||||
{gettext('Close')}
|
||||
</DefaultButton>
|
||||
<DefaultButton data-test="Reset" onClick={onResetClick} startIcon={<SettingsBackupRestoreIcon />} disabled={!dirty || saving} className={classes.buttonMargin}>
|
||||
<DefaultButton data-test="Reset" onClick={onResetClick} startIcon={<SettingsBackupRestoreIcon />} disabled={!dirty || saving} className='Dialog-buttonMargin'>
|
||||
{gettext('Reset')}
|
||||
</DefaultButton>
|
||||
<PrimaryButton data-test="Save" onClick={onSaveClick} startIcon={ButtonIcon} disabled={ !(viewHelperProps.mode === 'edit' || checkDirtyOnEnableSave ? dirty : true) || saving || Boolean(formErr.name && formErr.name !== 'apierror') || !formReady}>
|
||||
|
@ -814,9 +821,9 @@ function SchemaDialogView({
|
|||
</PrimaryButton>
|
||||
</Box>
|
||||
</Box>}
|
||||
</Box>
|
||||
</DepListenerContext.Provider>
|
||||
</StateUtilsContext.Provider>
|
||||
</DepListenerContext.Provider>
|
||||
</StateUtilsContext.Provider>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -851,38 +858,12 @@ SchemaDialogView.propTypes = {
|
|||
checkDirtyOnEnableSave: PropTypes.bool,
|
||||
};
|
||||
|
||||
const usePropsStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
height: '100%',
|
||||
minHeight: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
controlRow: {
|
||||
marginBottom: theme.spacing(1),
|
||||
},
|
||||
form: {
|
||||
padding: theme.spacing(1),
|
||||
overflow: 'auto',
|
||||
flexGrow: 1,
|
||||
},
|
||||
toolbar: {
|
||||
padding: theme.spacing(1),
|
||||
background: theme.palette.background.default,
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
},
|
||||
buttonMargin: {
|
||||
marginRight: '0.5rem',
|
||||
},
|
||||
noPadding: {
|
||||
padding: 0,
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
/* If its the properties tab */
|
||||
function SchemaPropertiesView({
|
||||
getInitData, viewHelperProps, schema={}, updatedData, ...props}) {
|
||||
const classes = usePropsStyles();
|
||||
|
||||
let defaultTab = 'General';
|
||||
let tabs = {};
|
||||
let tabsClassname = {};
|
||||
|
@ -926,7 +907,7 @@ function SchemaPropertiesView({
|
|||
group = group || defaultTab;
|
||||
|
||||
if(field.isFullTab) {
|
||||
tabsClassname[group] = classes.noPadding;
|
||||
tabsClassname[group] = 'Properties-noPadding';
|
||||
}
|
||||
|
||||
if(modeSupported) {
|
||||
|
@ -944,7 +925,7 @@ function SchemaPropertiesView({
|
|||
viewHelperProps={viewHelperProps}
|
||||
schema={field.schema}
|
||||
accessPath={[]}
|
||||
controlClassName={classes.controlRow}
|
||||
controlClassName='Properties-controlRow'
|
||||
{...field}
|
||||
visible={visible}
|
||||
/>
|
||||
|
@ -959,7 +940,7 @@ function SchemaPropertiesView({
|
|||
schema={field.schema}
|
||||
accessPath={[field.id]}
|
||||
formErr={{}}
|
||||
containerClassName={classes.controlRow}
|
||||
containerClassName='Properties-controlRow'
|
||||
canAdd={false}
|
||||
canEdit={false}
|
||||
canDelete={false}
|
||||
|
@ -983,11 +964,11 @@ function SchemaPropertiesView({
|
|||
readonly={readonly}
|
||||
disabled={disabled}
|
||||
visible={visible}
|
||||
className={field.isFullTab ? null : classes.controlRow}
|
||||
className={field.isFullTab ? null :'Properties-controlRow'}
|
||||
noLabel={field.isFullTab}
|
||||
memoDeps={[
|
||||
origData[field.id],
|
||||
classes.controlRow,
|
||||
'Properties-controlRow',
|
||||
field.isFullTab
|
||||
]}
|
||||
/>
|
||||
|
@ -998,9 +979,9 @@ function SchemaPropertiesView({
|
|||
|
||||
let finalTabs = _.pickBy(tabs, (v, tabName)=>schema.filterGroups.indexOf(tabName) <= -1);
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<StyledBox>
|
||||
<Loader message={loaderText}/>
|
||||
<Box className={classes.toolbar}>
|
||||
<Box className='Properties-toolbar'>
|
||||
<PgButtonGroup size="small">
|
||||
<PgIconButton
|
||||
data-test="help" onClick={()=>props.onHelp(true, false)} icon={<InfoIcon />} disabled={props.disableSqlHelp}
|
||||
|
@ -1009,7 +990,7 @@ function SchemaPropertiesView({
|
|||
onClick={props.onEdit} icon={<EditIcon />} title={gettext('Edit object...')} />
|
||||
</PgButtonGroup>
|
||||
</Box>
|
||||
<Box className={clsx(classes.form, classes.formProperties)}>
|
||||
<Box className={'Properties-form'}>
|
||||
<Box>
|
||||
{Object.keys(finalTabs).map((tabName)=>{
|
||||
let id = tabName.replace(' ', '');
|
||||
|
@ -1032,7 +1013,7 @@ function SchemaPropertiesView({
|
|||
})}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Box, Button, darken } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import React, { useEffect } from 'react';
|
||||
import { MESSAGE_TYPE, NotifierMessage } from '../components/FormComponents';
|
||||
|
@ -7,18 +7,13 @@ import { FinalNotifyContent } from '../helpers/Notifier';
|
|||
import PropTypes from 'prop-types';
|
||||
import CustomPropTypes from '../custom_prop_types';
|
||||
|
||||
const contentBg = '#2b709b';
|
||||
const loginBtnBg = '#038bba';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: theme.palette.primary.contrastText,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
height: '100%',
|
||||
},
|
||||
pageContent: {
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: theme.palette.primary.contrastText,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
height: '100%',
|
||||
'& .BasePage-pageContent': {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: '16px',
|
||||
|
@ -26,44 +21,45 @@ const useStyles = makeStyles((theme)=>({
|
|||
borderRadius: theme.shape.borderRadius,
|
||||
maxHeight: '100%',
|
||||
minWidth: '450px',
|
||||
maxWidth: '450px'
|
||||
},
|
||||
logo: {
|
||||
width: '96px',
|
||||
height: '40px',
|
||||
background: 'url(data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDUgNTAiPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDojZmZmO30uY2xzLTJ7ZmlsbDojMzI2ODkzO308L3N0eWxlPjwvZGVmcz48dGl0bGU+cGdBZG1pbjwvdGl0bGU+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNNTguOTQsNDEuNGEyLjQ4LDIuNDgsMCwwLDEtMi4yNy0zLjQ5TDY0LDIxLjI5VjZhNiw2LDAsMCwwLTYtNkg2QTYsNiwwLDAsMCwwLDZWNDRhNiw2LDAsMCwwLDYsNkg1OGE2LDYsMCwwLDAsNi02VjQxLjRaIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNMjkuMjUsMzAuMTdhMTMuMTMsMTMuMTMsMCwwLDEtMS44Mi02LjkzLDEzLDEzLDAsMCwxLDEuODItNi44OCwxMi41LDEyLjUsMCwwLDEsMS40OC0xLjk1LDEwLjQ0LDEwLjQ0LDAsMCwwLTMuMjUtMi44OSwxMS4xNiwxMS4xNiwwLDAsMC01LjY1LTEuNDVxLTQuNDgsMC02LjcyLDIuNjRWMTAuNDRINy41MVY0MC4zNmExLDEsMCwwLDAsMSwxaDZhMSwxLDAsMCwwLDEtMVYzMS4xOWE4LjQ3LDguNDcsMCwwLDAsNi4zNCwyLjQsMTEuMjYsMTEuMjYsMCwwLDAsNS42NS0xLjQ1LDEwLjUzLDEwLjUzLDAsMCwwLDIuMDYtMS41NkMyOS40NCwzMC40NCwyOS4zNCwzMC4zMSwyOS4yNSwzMC4xN1pNMjMuNiwyNS44YTQuNTIsNC41MiwwLDAsMS0zLjQ1LDEuNDQsNC40OCw0LjQ4LDAsMCwxLTMuNDQtMS40NCw1LjYsNS42LDAsMCwxLTEuMzUtNCw1LjU5LDUuNTksMCwwLDEsMS4zNS00LDQuNDYsNC40NiwwLDAsMSwzLjQ0LTEuNDUsNC40OSw0LjQ5LDAsMCwxLDMuNDUsMS40NSw1LjYzLDUuNjMsMCwwLDEsMS4zNCw0QTUuNjQsNS42NCwwLDAsMSwyMy42LDI1LjhaIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNNTYuNDksMTIuNjNWMzEuMjRxMCw2LjM1LTMuNDQsOS41MXQtOS45MiwzLjE3YTI1LjQyLDI1LjQyLDAsMCwxLTYuMy0uNzUsMTUsMTUsMCwwLDEtNS0yLjIzbDIuODktNS41OWExMC4xNywxMC4xNywwLDAsMCwzLjUxLDEuNzksMTQuMzcsMTQuMzcsMCwwLDAsNC4xOC42NUE2LjUzLDYuNTMsMCwwLDAsNDcsMzYuNGE1LjM3LDUuMzcsMCwwLDAsMS40Ny00LjExdi0uNzZjLTEuNTQsMS44LTMuNzksMi42OS02Ljc2LDIuNjlhMTEuNywxMS43LDAsMCwxLTUuNTktMS4zNkExMC4zNywxMC4zNywwLDAsMSwzMi4wOSwyOWExMC44OSwxMC44OSwwLDAsMS0xLjUxLTUuNzcsMTAuODYsMTAuODYsMCwwLDEsMS41MS01Ljc0LDEwLjQyLDEwLjQyLDAsMCwxLDQuMDctMy44NiwxMS43MSwxMS43MSwwLDAsMSw1LjU5LTEuMzdjMy4yNSwwLDUuNjMsMS4wNiw3LjE0LDMuMTVWMTIuNjNabS05LjMsMTMuOTVhNC40LDQuNCwwLDAsMCwxLjQtMy4zNiw0LjM0LDQuMzQsMCwwLDAtMS4zOC0zLjM0LDUuNjUsNS42NSwwLDAsMC03LjE2LDAsNC4zLDQuMywwLDAsMC0xLjQxLDMuMzQsNC4zNSw0LjM1LDAsMCwwLDEuNDMsMy4zNiw1LjA4LDUuMDgsMCwwLDAsMy41NywxLjNBNSw1LDAsMCwwLDQ3LjE5LDI2LjU4WiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTgzLjQzLDMyLjg5SDcxbC0yLDUuMDlhMSwxLDAsMCwxLS45My42Mkg2MS43M2ExLDEsMCwwLDEtLjkxLTEuNEw3Mi45MSw5LjhhMSwxLDAsMCwxLC45Mi0uNmg2Ljg5YTEsMSwwLDAsMSwuOTEuNkw5My43NywzNy4yYTEsMSwwLDAsMS0uOTIsMS40SDg2LjQxYTEsMSwwLDAsMS0uOTMtLjYyWk04MSwyNi43NmwtMy43OC05LjQxLTMuNzgsOS40MVoiLz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0xMjAuNDQsOC40NFYzNy42YTEsMSwwLDAsMS0xLDFoLTUuNmExLDEsMCwwLDEtMS0xVjM2LjMzUTExMC42MiwzOSwxMDYuMTYsMzlhMTEuMjksMTEuMjksMCwwLDEtNS42Ny0xLjQ1LDEwLjU0LDEwLjU0LDAsMCwxLTQtNC4xNEExMi42MiwxMi42MiwwLDAsMSw5NSwyNy4xOCwxMi41MywxMi41MywwLDAsMSw5Ni40NCwyMWExMC4zNSwxMC4zNSwwLDAsMSw0LTQuMDksMTEuNDgsMTEuNDgsMCwwLDEsNS42Ny0xLjQzLDguMjQsOC4yNCwwLDAsMSw2LjMsMi4zNVY4LjQ0YTEsMSwwLDAsMSwxLTFoNkExLDEsMCwwLDEsMTIwLjQ0LDguNDRabS05LjE5LDIyLjc1YTUuNzEsNS43MSwwLDAsMCwxLjM0LTQsNS42LDUuNiwwLDAsMC0xLjMyLTMuOTUsNC40Nyw0LjQ3LDAsMCwwLTMuNDMtMS40Myw0LjUzLDQuNTMsMCwwLDAtMy40NCwxLjQzLDUuNTEsNS41MSwwLDAsMC0xLjM0LDMuOTUsNS42Nyw1LjY3LDAsMCwwLDEuMzQsNCw0Ljc3LDQuNzcsMCwwLDAsNi44NSwwWiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTE2MSwxOGMxLjY2LDEuNjgsMi41LDQuMjEsMi41LDcuNnYxMmExLDEsMCwwLDEtMSwxaC02YTEsMSwwLDAsMS0xLTFWMjYuODhhNS42Nyw1LjY3LDAsMCwwLS45LTMuNTMsMy4wOSwzLjA5LDAsMCwwLTIuNTUtMS4xMywzLjYyLDMuNjIsMCwwLDAtMi44OSwxLjI2LDUuNzEsNS43MSwwLDAsMC0xLjEsMy44MlYzNy42YTEsMSwwLDAsMS0xLDFoLTZhMSwxLDAsMCwxLTEtMVYyNi44OGMwLTMuMTEtMS4xNC00LjY2LTMuNDQtNC42NmEzLjcsMy43LDAsMCwwLTIuOTQsMS4yNiw1LjcxLDUuNzEsMCwwLDAtMS4wOSwzLjgyVjM3LjZhMSwxLDAsMCwxLTEsMWgtNmExLDEsMCwwLDEtMS0xVjE2Ljg0YTEsMSwwLDAsMSwxLTFoNS42YTEsMSwwLDAsMSwxLDF2MS4zOWE4LDgsMCwwLDEsMy0yLjA4LDEwLjIzLDEwLjIzLDAsMCwxLDMuOC0uNjksMTAsMTAsMCwwLDEsNC4yOS44OEE3LjI4LDcuMjgsMCwwLDEsMTQ2LjQyLDE5YTguODUsOC44NSwwLDAsMSwzLjQxLTIuNjUsMTAuOTMsMTAuOTMsMCwwLDEsNC40OS0uOTJBOSw5LDAsMCwxLDE2MSwxOFoiLz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0xNjguMTIsMTIuMWEzLjkxLDMuOTEsMCwwLDEtMS4zNC0yLjc5QTQuMTYsNC4xNiwwLDAsMSwxNjgsNi4xOWE1LDUsMCwwLDEsMy42Ny0xLjM2QTUuMjUsNS4yNSwwLDAsMSwxNzUuMTgsNmEzLjc1LDMuNzUsMCwwLDEsMS4zNCwzLDQuMSw0LjEsMCwwLDEtMS4zNCwzLjEzLDUuNjgsNS42OCwwLDAsMS03LjA2LDBabS41NCwzLjc0aDZhMSwxLDAsMCwxLDEsMVYzNy42YTEsMSwwLDAsMS0xLDFoLTZhMSwxLDAsMCwxLTEtMVYxNi44NEExLDEsMCwwLDEsMTY4LjY2LDE1Ljg0WiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTIwMS41NSwxOHEyLjU5LDIuNTIsMi41OSw3LjZ2MTJhMSwxLDAsMCwxLTEsMWgtNmExLDEsMCwwLDEtMS0xVjI2Ljg4cTAtNC42Ni0zLjc0LTQuNjZhNC4zLDQuMywwLDAsMC0zLjMsMS4zNCw1LjgzLDUuODMsMCwwLDAtMS4yNCw0djEwYTEsMSwwLDAsMS0xLDFoLTZhMSwxLDAsMCwxLTEtMVYxNi44NGExLDEsMCwwLDEsMS0xaDUuNjFhMSwxLDAsMCwxLDEsMXYxLjQ3YTkuMDUsOS4wNSwwLDAsMSwzLjE5LTIuMTIsMTAuNzgsMTAuNzgsMCwwLDEsNC0uNzNBOS4zNCw5LjM0LDAsMCwxLDIwMS41NSwxOFoiLz48L3N2Zz4=) 0 0 no-repeat',
|
||||
backgroundPositionY: 'center',
|
||||
},
|
||||
item: {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
marginBottom: '15px',
|
||||
fontSize: '1.2rem'
|
||||
},
|
||||
button: {
|
||||
backgroundColor: loginBtnBg,
|
||||
color: '#fff',
|
||||
padding: '4px 8px',
|
||||
width: '100%',
|
||||
'&:hover': {
|
||||
backgroundColor: darken(loginBtnBg, 0.1),
|
||||
maxWidth: '450px',
|
||||
'& .BasePage-item': {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
marginBottom: '15px',
|
||||
fontSize: '1.2rem',
|
||||
'& .BasePage-logo': {
|
||||
width: '96px',
|
||||
height: '40px',
|
||||
background: 'url(data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDUgNTAiPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDojZmZmO30uY2xzLTJ7ZmlsbDojMzI2ODkzO308L3N0eWxlPjwvZGVmcz48dGl0bGU+cGdBZG1pbjwvdGl0bGU+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNNTguOTQsNDEuNGEyLjQ4LDIuNDgsMCwwLDEtMi4yNy0zLjQ5TDY0LDIxLjI5VjZhNiw2LDAsMCwwLTYtNkg2QTYsNiwwLDAsMCwwLDZWNDRhNiw2LDAsMCwwLDYsNkg1OGE2LDYsMCwwLDAsNi02VjQxLjRaIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNMjkuMjUsMzAuMTdhMTMuMTMsMTMuMTMsMCwwLDEtMS44Mi02LjkzLDEzLDEzLDAsMCwxLDEuODItNi44OCwxMi41LDEyLjUsMCwwLDEsMS40OC0xLjk1LDEwLjQ0LDEwLjQ0LDAsMCwwLTMuMjUtMi44OSwxMS4xNiwxMS4xNiwwLDAsMC01LjY1LTEuNDVxLTQuNDgsMC02LjcyLDIuNjRWMTAuNDRINy41MVY0MC4zNmExLDEsMCwwLDAsMSwxaDZhMSwxLDAsMCwwLDEtMVYzMS4xOWE4LjQ3LDguNDcsMCwwLDAsNi4zNCwyLjQsMTEuMjYsMTEuMjYsMCwwLDAsNS42NS0xLjQ1LDEwLjUzLDEwLjUzLDAsMCwwLDIuMDYtMS41NkMyOS40NCwzMC40NCwyOS4zNCwzMC4zMSwyOS4yNSwzMC4xN1pNMjMuNiwyNS44YTQuNTIsNC41MiwwLDAsMS0zLjQ1LDEuNDQsNC40OCw0LjQ4LDAsMCwxLTMuNDQtMS40NCw1LjYsNS42LDAsMCwxLTEuMzUtNCw1LjU5LDUuNTksMCwwLDEsMS4zNS00LDQuNDYsNC40NiwwLDAsMSwzLjQ0LTEuNDUsNC40OSw0LjQ5LDAsMCwxLDMuNDUsMS40NSw1LjYzLDUuNjMsMCwwLDEsMS4zNCw0QTUuNjQsNS42NCwwLDAsMSwyMy42LDI1LjhaIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNNTYuNDksMTIuNjNWMzEuMjRxMCw2LjM1LTMuNDQsOS41MXQtOS45MiwzLjE3YTI1LjQyLDI1LjQyLDAsMCwxLTYuMy0uNzUsMTUsMTUsMCwwLDEtNS0yLjIzbDIuODktNS41OWExMC4xNywxMC4xNywwLDAsMCwzLjUxLDEuNzksMTQuMzcsMTQuMzcsMCwwLDAsNC4xOC42NUE2LjUzLDYuNTMsMCwwLDAsNDcsMzYuNGE1LjM3LDUuMzcsMCwwLDAsMS40Ny00LjExdi0uNzZjLTEuNTQsMS44LTMuNzksMi42OS02Ljc2LDIuNjlhMTEuNywxMS43LDAsMCwxLTUuNTktMS4zNkExMC4zNywxMC4zNywwLDAsMSwzMi4wOSwyOWExMC44OSwxMC44OSwwLDAsMS0xLjUxLTUuNzcsMTAuODYsMTAuODYsMCwwLDEsMS41MS01Ljc0LDEwLjQyLDEwLjQyLDAsMCwxLDQuMDctMy44NiwxMS43MSwxMS43MSwwLDAsMSw1LjU5LTEuMzdjMy4yNSwwLDUuNjMsMS4wNiw3LjE0LDMuMTVWMTIuNjNabS05LjMsMTMuOTVhNC40LDQuNCwwLDAsMCwxLjQtMy4zNiw0LjM0LDQuMzQsMCwwLDAtMS4zOC0zLjM0LDUuNjUsNS42NSwwLDAsMC03LjE2LDAsNC4zLDQuMywwLDAsMC0xLjQxLDMuMzQsNC4zNSw0LjM1LDAsMCwwLDEuNDMsMy4zNiw1LjA4LDUuMDgsMCwwLDAsMy41NywxLjNBNSw1LDAsMCwwLDQ3LjE5LDI2LjU4WiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTgzLjQzLDMyLjg5SDcxbC0yLDUuMDlhMSwxLDAsMCwxLS45My42Mkg2MS43M2ExLDEsMCwwLDEtLjkxLTEuNEw3Mi45MSw5LjhhMSwxLDAsMCwxLC45Mi0uNmg2Ljg5YTEsMSwwLDAsMSwuOTEuNkw5My43NywzNy4yYTEsMSwwLDAsMS0uOTIsMS40SDg2LjQxYTEsMSwwLDAsMS0uOTMtLjYyWk04MSwyNi43NmwtMy43OC05LjQxLTMuNzgsOS40MVoiLz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0xMjAuNDQsOC40NFYzNy42YTEsMSwwLDAsMS0xLDFoLTUuNmExLDEsMCwwLDEtMS0xVjM2LjMzUTExMC42MiwzOSwxMDYuMTYsMzlhMTEuMjksMTEuMjksMCwwLDEtNS42Ny0xLjQ1LDEwLjU0LDEwLjU0LDAsMCwxLTQtNC4xNEExMi42MiwxMi42MiwwLDAsMSw5NSwyNy4xOCwxMi41MywxMi41MywwLDAsMSw5Ni40NCwyMWExMC4zNSwxMC4zNSwwLDAsMSw0LTQuMDksMTEuNDgsMTEuNDgsMCwwLDEsNS42Ny0xLjQzLDguMjQsOC4yNCwwLDAsMSw2LjMsMi4zNVY4LjQ0YTEsMSwwLDAsMSwxLTFoNkExLDEsMCwwLDEsMTIwLjQ0LDguNDRabS05LjE5LDIyLjc1YTUuNzEsNS43MSwwLDAsMCwxLjM0LTQsNS42LDUuNiwwLDAsMC0xLjMyLTMuOTUsNC40Nyw0LjQ3LDAsMCwwLTMuNDMtMS40Myw0LjUzLDQuNTMsMCwwLDAtMy40NCwxLjQzLDUuNTEsNS41MSwwLDAsMC0xLjM0LDMuOTUsNS42Nyw1LjY3LDAsMCwwLDEuMzQsNCw0Ljc3LDQuNzcsMCwwLDAsNi44NSwwWiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTE2MSwxOGMxLjY2LDEuNjgsMi41LDQuMjEsMi41LDcuNnYxMmExLDEsMCwwLDEtMSwxaC02YTEsMSwwLDAsMS0xLTFWMjYuODhhNS42Nyw1LjY3LDAsMCwwLS45LTMuNTMsMy4wOSwzLjA5LDAsMCwwLTIuNTUtMS4xMywzLjYyLDMuNjIsMCwwLDAtMi44OSwxLjI2LDUuNzEsNS43MSwwLDAsMC0xLjEsMy44MlYzNy42YTEsMSwwLDAsMS0xLDFoLTZhMSwxLDAsMCwxLTEtMVYyNi44OGMwLTMuMTEtMS4xNC00LjY2LTMuNDQtNC42NmEzLjcsMy43LDAsMCwwLTIuOTQsMS4yNiw1LjcxLDUuNzEsMCwwLDAtMS4wOSwzLjgyVjM3LjZhMSwxLDAsMCwxLTEsMWgtNmExLDEsMCwwLDEtMS0xVjE2Ljg0YTEsMSwwLDAsMSwxLTFoNS42YTEsMSwwLDAsMSwxLDF2MS4zOWE4LDgsMCwwLDEsMy0yLjA4LDEwLjIzLDEwLjIzLDAsMCwxLDMuOC0uNjksMTAsMTAsMCwwLDEsNC4yOS44OEE3LjI4LDcuMjgsMCwwLDEsMTQ2LjQyLDE5YTguODUsOC44NSwwLDAsMSwzLjQxLTIuNjUsMTAuOTMsMTAuOTMsMCwwLDEsNC40OS0uOTJBOSw5LDAsMCwxLDE2MSwxOFoiLz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0xNjguMTIsMTIuMWEzLjkxLDMuOTEsMCwwLDEtMS4zNC0yLjc5QTQuMTYsNC4xNiwwLDAsMSwxNjgsNi4xOWE1LDUsMCwwLDEsMy42Ny0xLjM2QTUuMjUsNS4yNSwwLDAsMSwxNzUuMTgsNmEzLjc1LDMuNzUsMCwwLDEsMS4zNCwzLDQuMSw0LjEsMCwwLDEtMS4zNCwzLjEzLDUuNjgsNS42OCwwLDAsMS03LjA2LDBabS41NCwzLjc0aDZhMSwxLDAsMCwxLDEsMVYzNy42YTEsMSwwLDAsMS0xLDFoLTZhMSwxLDAsMCwxLTEtMVYxNi44NEExLDEsMCwwLDEsMTY4LjY2LDE1Ljg0WiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTIwMS41NSwxOHEyLjU5LDIuNTIsMi41OSw3LjZ2MTJhMSwxLDAsMCwxLTEsMWgtNmExLDEsMCwwLDEtMS0xVjI2Ljg4cTAtNC42Ni0zLjc0LTQuNjZhNC4zLDQuMywwLDAsMC0zLjMsMS4zNCw1LjgzLDUuODMsMCwwLDAtMS4yNCw0djEwYTEsMSwwLDAsMS0xLDFoLTZhMSwxLDAsMCwxLTEtMVYxNi44NGExLDEsMCwwLDEsMS0xaDUuNjFhMSwxLDAsMCwxLDEsMXYxLjQ3YTkuMDUsOS4wNSwwLDAsMSwzLjE5LTIuMTIsMTAuNzgsMTAuNzgsMCwwLDEsNC0uNzNBOS4zNCw5LjM0LDAsMCwxLDIwMS41NSwxOFoiLz48L3N2Zz4=) 0 0 no-repeat',
|
||||
backgroundPositionY: 'center',
|
||||
},
|
||||
},
|
||||
'&.Mui-disabled': {
|
||||
opacity: 0.60,
|
||||
color: '#fff'
|
||||
},
|
||||
}
|
||||
'& .BasePage-button': {
|
||||
backgroundColor: loginBtnBg,
|
||||
color: '#fff',
|
||||
padding: '4px 8px',
|
||||
width: '100%',
|
||||
'&:hover': {
|
||||
backgroundColor: darken(loginBtnBg, 0.1),
|
||||
},
|
||||
'&.Mui-disabled': {
|
||||
opacity: 0.60,
|
||||
color: '#fff'
|
||||
},
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
const contentBg = '#2b709b';
|
||||
const loginBtnBg = '#038bba';
|
||||
|
||||
export function SecurityButton({...props}) {
|
||||
const classes = useStyles();
|
||||
return <Button type="submit" className={classes.button} {...props} />;
|
||||
|
||||
return <Button type="submit" className='BasePage-button' {...props} />;
|
||||
}
|
||||
|
||||
export default function BasePage({pageImage, title, children, messages}) {
|
||||
const classes = useStyles();
|
||||
const snackbar = useSnackbar();
|
||||
|
||||
useEffect(()=>{
|
||||
messages?.forEach((m)=>{
|
||||
snackbar.enqueueSnackbar(null, {
|
||||
|
@ -79,18 +75,18 @@ export default function BasePage({pageImage, title, children, messages}) {
|
|||
});
|
||||
}, [messages]);
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<StyledBox >
|
||||
<Box display="flex" minWidth="80%" gap="40px" alignItems="center" padding="20px 80px">
|
||||
<Box flexGrow={1} height="80%" textAlign="center">
|
||||
{pageImage}
|
||||
</Box>
|
||||
<Box className={classes.pageContent}>
|
||||
<Box className={classes.item}><div className={classes.logo} /></Box>
|
||||
<Box className={classes.item}>{title}</Box>
|
||||
<Box className='BasePage-pageContent'>
|
||||
<Box className='BasePage-item'><div className='BasePage-logo' /></Box>
|
||||
<Box className='BasePage-item'>{title}</Box>
|
||||
<Box display="flex" flexDirection="column" minHeight={0}>{children}</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
*/
|
||||
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { createTheme, ThemeProvider } from '@mui/material/styles';
|
||||
import { LocalizationProvider } from '@mui/x-date-pickers';
|
||||
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFnsV3';
|
||||
|
@ -30,6 +29,7 @@ import jsonEditorOverride from './overrides/jsoneditor.override';
|
|||
import pgadminOverride from './overrides/pgadmin.classes.override';
|
||||
import reactAspenOverride from './overrides/reactaspen.override';
|
||||
import usePreferences from '../../../preferences/static/js/store';
|
||||
import szhMenuOverride from './overrides/szhmenu.override';
|
||||
|
||||
/* Common settings across all themes */
|
||||
let basicSettings = createTheme();
|
||||
|
@ -423,7 +423,8 @@ function getFinalTheme(baseTheme) {
|
|||
...cmOverride(baseTheme),
|
||||
...jsonEditorOverride(baseTheme),
|
||||
...pgadminOverride(baseTheme),
|
||||
...reactAspenOverride(baseTheme)
|
||||
...reactAspenOverride(baseTheme),
|
||||
...szhMenuOverride(baseTheme)
|
||||
},
|
||||
},
|
||||
MuiOutlinedInput: {
|
||||
|
@ -805,83 +806,3 @@ export default function Theme({children}) {
|
|||
Theme.propTypes = {
|
||||
children: CustomPropTypes.children,
|
||||
};
|
||||
|
||||
export const commonTableStyles = makeStyles((theme)=>({
|
||||
table: {
|
||||
borderSpacing: 0,
|
||||
width: '100%',
|
||||
overflow: 'auto',
|
||||
backgroundColor: theme.otherVars.tableBg,
|
||||
border: '1px solid '+theme.otherVars.borderColor,
|
||||
'& tbody td, & thead th': {
|
||||
margin: 0,
|
||||
padding: theme.spacing(0.5),
|
||||
border: '1px solid '+theme.otherVars.borderColor,
|
||||
borderBottom: 'none',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
userSelect: 'text',
|
||||
maxWidth: '250px',
|
||||
'&:first-of-type':{
|
||||
borderLeft: 'none',
|
||||
},
|
||||
},
|
||||
'& thead tr:first-of-type th': {
|
||||
borderTop: 'none',
|
||||
},
|
||||
'& tbody tr:last-of-type': {
|
||||
'&:hover td': {
|
||||
borderBottomColor: theme.palette.primary.main,
|
||||
},
|
||||
'& td': {
|
||||
borderBottomColor: theme.otherVars.borderColor,
|
||||
}
|
||||
},
|
||||
'& th': {
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
padding: theme.spacing(1, 0.5),
|
||||
textAlign: 'left',
|
||||
},
|
||||
'& tbody > tr': {
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
'& td': {
|
||||
borderBottom: '1px solid '+theme.palette.primary.main,
|
||||
borderTop: '1px solid '+theme.palette.primary.main,
|
||||
},
|
||||
'&:last-of-type td': {
|
||||
borderBottomColor: theme.palette.primary.main,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
noBorder: {
|
||||
border: 0,
|
||||
},
|
||||
borderBottom: {
|
||||
'& tbody tr:last-of-type td': {
|
||||
borderBottom: '1px solid '+theme.otherVars.borderColor,
|
||||
},
|
||||
},
|
||||
wrapTd: {
|
||||
'& tbody td': {
|
||||
whiteSpace: 'pre-wrap',
|
||||
}
|
||||
},
|
||||
noHover: {
|
||||
'& tbody > tr': {
|
||||
'&:hover': {
|
||||
backgroundColor: theme.otherVars.tableBg,
|
||||
'& td': {
|
||||
borderBottomColor: theme.otherVars.borderColor,
|
||||
borderTopColor: theme.otherVars.borderColor,
|
||||
},
|
||||
'&:last-of-type td': {
|
||||
borderBottomColor: theme.otherVars.borderColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
|
38
web/pgadmin/static/js/Theme/overrides/szhmenu.override.js
Normal file
38
web/pgadmin/static/js/Theme/overrides/szhmenu.override.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
export default function szhMenuOverride(theme) {
|
||||
return {
|
||||
'& .szh-menu': {
|
||||
padding: '4px 0px',
|
||||
zIndex: 1005,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
border: `1px solid ${theme.otherVars.borderColor}`
|
||||
},
|
||||
'& .szh-menu__divider': {
|
||||
margin: 0,
|
||||
background: theme.otherVars.borderColor,
|
||||
},
|
||||
'& .szh-menu__item': {
|
||||
display: 'flex',
|
||||
padding: '3px 12px',
|
||||
'&:after': {
|
||||
right: '0.75rem',
|
||||
},
|
||||
'&.szh-menu__item--active, &.szh-menu__item--hover': {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: theme.palette.primary.contrastText,
|
||||
},
|
||||
'&.szh-menu__item--disabled':{
|
||||
color: theme.palette.text.muted,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -8,15 +8,15 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import { Button, ButtonGroup, Tooltip } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import React, { forwardRef } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import PropTypes from 'prop-types';
|
||||
import CustomPropTypes from '../custom_prop_types';
|
||||
import ShortcutTitle from './ShortcutTitle';
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
primaryButton: {
|
||||
|
||||
const StyledButton = styled(Button)(({theme}) => ({
|
||||
'&.Buttons-primaryButton': {
|
||||
border: '1px solid '+theme.palette.primary.main,
|
||||
'&.Mui-disabled': {
|
||||
color: [theme.palette.primary.contrastText,'!important'],
|
||||
|
@ -26,8 +26,17 @@ const useStyles = makeStyles((theme)=>({
|
|||
backgroundColor: theme.palette.primary.hoverMain,
|
||||
borderColor: theme.palette.primary.hoverBorderColor,
|
||||
},
|
||||
'&.Buttons-noBorderPrimary': {
|
||||
color: theme.palette.primary.contrastText,
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
'&:hover': {
|
||||
color: theme.palette.primary.contrastText,
|
||||
backgroundColor: theme.palette.primary.hoverMain,
|
||||
borderColor: theme.palette.primary.hoverBorderColor,
|
||||
},
|
||||
}
|
||||
},
|
||||
defaultButton: {
|
||||
'&.Buttons-defaultButton': {
|
||||
backgroundColor: theme.palette.default.main,
|
||||
color: theme.palette.default.contrastText,
|
||||
border: '1px solid '+theme.palette.default.borderColor,
|
||||
|
@ -40,15 +49,23 @@ const useStyles = makeStyles((theme)=>({
|
|||
backgroundColor: theme.palette.default.hoverMain,
|
||||
color: theme.palette.default.hoverContrastText,
|
||||
borderColor: theme.palette.default.hoverBorderColor,
|
||||
}
|
||||
},
|
||||
iconButton: {
|
||||
minWidth: 0,
|
||||
padding: '2px 4px',
|
||||
'&.MuiButton-sizeSmall, &.MuiButton-outlined.MuiButton-sizeSmall, &.MuiButton-contained.MuiButton-sizeSmall': {
|
||||
},
|
||||
'&.Buttons-noBorder': {
|
||||
border: 0,
|
||||
backgroundColor: 'transparent',
|
||||
color: theme.custom.icon.contrastText,
|
||||
'&:hover': {
|
||||
border: 0,
|
||||
color: theme.custom.icon.contrastText,
|
||||
backgroundColor: 'inherit',
|
||||
filter: 'brightness(85%)',
|
||||
},
|
||||
'&.Mui-disabled': {
|
||||
border: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
iconButtonDefault: {
|
||||
'&.Buttons-iconButtonDefault': {
|
||||
borderColor: theme.custom.icon.borderColor,
|
||||
color: theme.custom.icon.contrastText,
|
||||
backgroundColor: theme.custom.icon.main,
|
||||
|
@ -72,7 +89,13 @@ const useStyles = makeStyles((theme)=>({
|
|||
borderColor: theme.custom.icon.borderColor,
|
||||
}
|
||||
},
|
||||
splitButton: {
|
||||
'&.Buttons-iconButton': {
|
||||
minWidth: 0,
|
||||
padding: '2px 4px',
|
||||
'&.MuiButton-sizeSmall, &.MuiButton-outlined.MuiButton-sizeSmall, &.MuiButton-contained.MuiButton-sizeSmall': {
|
||||
},
|
||||
},
|
||||
'&.Buttons-splitButton': {
|
||||
'&.MuiButton-sizeSmall, &.MuiButton-outlined.MuiButton-sizeSmall, &.MuiButton-contained.MuiButton-sizeSmall': {
|
||||
width: '20px',
|
||||
minWidth: 0,
|
||||
|
@ -81,7 +104,7 @@ const useStyles = makeStyles((theme)=>({
|
|||
}
|
||||
}
|
||||
},
|
||||
xsButton: {
|
||||
'&.Buttons-xsButton': {
|
||||
padding: '2px 1px',
|
||||
height: '24px !important',
|
||||
minWidth: '24px',
|
||||
|
@ -92,44 +115,22 @@ const useStyles = makeStyles((theme)=>({
|
|||
minWidth: '30px',
|
||||
}
|
||||
},
|
||||
noBorder: {
|
||||
border: 0,
|
||||
backgroundColor: 'transparent',
|
||||
color: theme.custom.icon.contrastText,
|
||||
'&:hover': {
|
||||
border: 0,
|
||||
color: theme.custom.icon.contrastText,
|
||||
backgroundColor: 'inherit',
|
||||
filter: 'brightness(85%)',
|
||||
},
|
||||
'&.Mui-disabled': {
|
||||
border: 0,
|
||||
},
|
||||
},
|
||||
noBorderPrimary: {
|
||||
color: theme.palette.primary.contrastText,
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
'&:hover': {
|
||||
color: theme.palette.primary.contrastText,
|
||||
backgroundColor: theme.palette.primary.hoverMain,
|
||||
borderColor: theme.palette.primary.hoverBorderColor,
|
||||
},
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
|
||||
/* pgAdmin primary button */
|
||||
export const PrimaryButton = forwardRef((props, ref)=>{
|
||||
let {children, className, size, noBorder, ...otherProps} = props;
|
||||
const classes = useStyles();
|
||||
let allClassName = [classes.primaryButton, className];
|
||||
let allClassName = ['Buttons-primaryButton', className];
|
||||
if(size == 'xs') {
|
||||
size = undefined;
|
||||
allClassName.push(classes.xsButton);
|
||||
allClassName.push('Buttons-xsButton');
|
||||
}
|
||||
noBorder && allClassName.push(...[classes.noBorder, classes.noBorderPrimary]);
|
||||
noBorder && allClassName.push(...['Buttons-noBorder', 'Buttons-noBorderPrimary']);
|
||||
const dataLabel = typeof(children) == 'string' ? children : undefined;
|
||||
return (
|
||||
<Button ref={ref} size={size} className={clsx(allClassName)} data-label={dataLabel} {...otherProps} color="primary" variant="contained">{children}</Button>
|
||||
<StyledButton ref={ref} size={size} className={allClassName.join(' ')} data-label={dataLabel} {...otherProps} color="primary" variant="contained">{children}</StyledButton>
|
||||
);
|
||||
});
|
||||
PrimaryButton.displayName = 'PrimaryButton';
|
||||
|
@ -143,16 +144,15 @@ PrimaryButton.propTypes = {
|
|||
/* pgAdmin default button */
|
||||
export const DefaultButton = forwardRef((props, ref)=>{
|
||||
let {children, className, size, noBorder, ...otherProps} = props;
|
||||
const classes = useStyles();
|
||||
let allClassName = [classes.defaultButton, className];
|
||||
let allClassName = ['Buttons-defaultButton', className];
|
||||
if(size == 'xs') {
|
||||
size = undefined;
|
||||
allClassName.push(classes.xsButton);
|
||||
allClassName.push('Buttons-xsButton');
|
||||
}
|
||||
noBorder && allClassName.push(classes.noBorder);
|
||||
noBorder && allClassName.push('Buttons-noBorder');
|
||||
const dataLabel = typeof(children) == 'string' ? children : undefined;
|
||||
return (
|
||||
<Button variant="outlined" ref={ref} size={size} className={clsx(allClassName)} data-label={dataLabel} color="default" {...otherProps}>{children}</Button>
|
||||
<StyledButton variant="outlined" ref={ref} size={size} className={allClassName.join(' ')} data-label={dataLabel} color="default" {...otherProps}>{children}</StyledButton>
|
||||
);
|
||||
});
|
||||
DefaultButton.displayName = 'DefaultButton';
|
||||
|
@ -166,8 +166,6 @@ DefaultButton.propTypes = {
|
|||
|
||||
/* pgAdmin Icon button, takes Icon component as input */
|
||||
export const PgIconButton = forwardRef(({icon, title, shortcut, className, splitButton, style, color, accesskey, ...props}, ref)=>{
|
||||
const classes = useStyles();
|
||||
|
||||
let shortcutTitle = null;
|
||||
if(accesskey || shortcut) {
|
||||
shortcutTitle = <ShortcutTitle title={title} accesskey={accesskey} shortcut={shortcut}/>;
|
||||
|
@ -178,7 +176,7 @@ export const PgIconButton = forwardRef(({icon, title, shortcut, className, split
|
|||
if(color == 'primary') {
|
||||
return (
|
||||
<PrimaryButton ref={ref} style={style}
|
||||
className={clsx(classes.iconButton, (splitButton ? classes.splitButton : ''), className)}
|
||||
className={['Buttons-iconButton', (splitButton ? 'Buttons-splitButton' : ''), className].join(' ')}
|
||||
accessKey={accesskey} data-label={title || ''} {...props}>
|
||||
{icon}
|
||||
</PrimaryButton>
|
||||
|
@ -186,7 +184,7 @@ export const PgIconButton = forwardRef(({icon, title, shortcut, className, split
|
|||
} else {
|
||||
return (
|
||||
<DefaultButton ref={ref} style={style}
|
||||
className={clsx(classes.iconButton, classes.iconButtonDefault, (splitButton ? classes.splitButton : ''), className)}
|
||||
className={['Buttons-iconButton', 'Buttons-iconButtonDefault',(splitButton ? 'Buttons-splitButton' : ''), className].join(' ')}
|
||||
accessKey={accesskey} data-label={title || ''} {...props}>
|
||||
{icon}
|
||||
</DefaultButton>
|
||||
|
@ -196,17 +194,18 @@ export const PgIconButton = forwardRef(({icon, title, shortcut, className, split
|
|||
return (
|
||||
<Tooltip title={shortcutTitle || title || ''} aria-label={title || ''}>
|
||||
<PrimaryButton ref={ref} style={style}
|
||||
className={clsx(classes.iconButton, (splitButton ? classes.splitButton : ''), className)}
|
||||
className={['Buttons-iconButton', (splitButton ? 'Buttons-splitButton' : ''), className].join(' ')}
|
||||
accessKey={accesskey} data-label={title || ''} {...props}>
|
||||
{icon}
|
||||
</PrimaryButton>
|
||||
</Tooltip>
|
||||
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Tooltip title={shortcutTitle || title || ''} aria-label={title || ''}>
|
||||
<DefaultButton ref={ref} style={style}
|
||||
className={clsx(classes.iconButton, classes.iconButtonDefault, (splitButton ? classes.splitButton : ''), className)}
|
||||
className={['Buttons-iconButton', 'Buttons-iconButtonDefault',(splitButton ? 'Buttons-splitButton' : ''), className].join(' ')}
|
||||
accessKey={accesskey} data-label={title || ''} {...props}>
|
||||
{icon}
|
||||
</DefaultButton>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import CheckboxTree from 'react-checkbox-tree';
|
||||
import CheckBoxIcon from '@mui/icons-material/CheckBox';
|
||||
import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank';
|
||||
|
@ -8,37 +8,33 @@ import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
|||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const useStyles = makeStyles((theme) =>
|
||||
({
|
||||
treeRoot: {
|
||||
'& .rct-collapse, .rct-checkbox': {
|
||||
padding: 0
|
||||
},
|
||||
'& .rct-node-leaf':{
|
||||
padding: '0 0 0 10px'
|
||||
},
|
||||
'& .react-checkbox-tree': {
|
||||
height: '97%',
|
||||
fontSize: '0.815rem',
|
||||
overflow: 'auto',
|
||||
...theme.mixins.panelBorder
|
||||
},
|
||||
height: '100%'
|
||||
},
|
||||
unchecked: {
|
||||
fill: theme.otherVars.borderColor
|
||||
},
|
||||
checked: {
|
||||
fill: theme.palette.primary.main
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const StyledDiv = styled('div')(({theme}) => ({
|
||||
height: '100%',
|
||||
'& .rct-collapse, .rct-checkbox': {
|
||||
padding: 0
|
||||
},
|
||||
'& .rct-node-leaf':{
|
||||
padding: '0 0 0 10px'
|
||||
},
|
||||
'& .react-checkbox-tree': {
|
||||
height: '97%',
|
||||
fontSize: '0.815rem',
|
||||
overflow: 'auto',
|
||||
...theme.mixins.panelBorder
|
||||
},
|
||||
'& .CheckBoxTree-unchecked': {
|
||||
fill: theme.otherVars.borderColor
|
||||
},
|
||||
'& .CheckBoxTree-checked': {
|
||||
fill: theme.palette.primary.main
|
||||
}
|
||||
}));
|
||||
|
||||
export default function CheckBoxTree({treeData, ...props}) {
|
||||
const [checked, setChecked] = React.useState([]);
|
||||
const [expanded, setExpanded] = React.useState([]);
|
||||
|
||||
const classes = useStyles();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props.getSelectedServers) {
|
||||
props.getSelectedServers(checked);
|
||||
|
@ -46,7 +42,7 @@ export default function CheckBoxTree({treeData, ...props}) {
|
|||
}, [checked]);
|
||||
|
||||
return (
|
||||
<div className={classes.treeRoot}>
|
||||
<StyledDiv>
|
||||
<CheckboxTree
|
||||
nodes={treeData}
|
||||
checked={checked}
|
||||
|
@ -55,15 +51,15 @@ export default function CheckBoxTree({treeData, ...props}) {
|
|||
onExpand={expandedVal => setExpanded(expandedVal)}
|
||||
showNodeIcon={false}
|
||||
icons={{
|
||||
check: <CheckBoxIcon className={classes.checked}/>,
|
||||
uncheck: <CheckBoxOutlineBlankIcon className={classes.unchecked}/>,
|
||||
halfCheck: <IndeterminateCheckBoxIcon className={classes.checked}/>,
|
||||
check: <CheckBoxIcon className='CheckBoxTree-checked'/>,
|
||||
uncheck: <CheckBoxOutlineBlankIcon className='CheckBoxTree-unchecked'/>,
|
||||
halfCheck: <IndeterminateCheckBoxIcon className='CheckBoxTree-checked'/>,
|
||||
expandClose: <ChevronRightIcon />,
|
||||
expandOpen: <ExpandMoreIcon />,
|
||||
leaf: <ChevronRightIcon />
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</StyledDiv>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,28 +1,26 @@
|
|||
import React from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { Box } from '@mui/material';
|
||||
import InfoRoundedIcon from '@mui/icons-material/InfoRounded';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
color: theme.palette.text.primary,
|
||||
margin: '24px auto 12px',
|
||||
fontSize: '0.8rem',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: '100%',
|
||||
},
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
color: theme.palette.text.primary,
|
||||
margin: '24px auto 12px',
|
||||
fontSize: '0.8rem',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: '100%',
|
||||
}));
|
||||
|
||||
export default function EmptyPanelMessage({text, style}) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Box className={classes.root} style={style}>
|
||||
<StyledBox style={style}>
|
||||
<InfoRoundedIcon style={{height: '1.2rem'}}/>
|
||||
<span style={{marginLeft: '4px'}}>{text}</span>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
EmptyPanelMessage.propTypes = {
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import PropTypes from 'prop-types';
|
||||
import CustomPropTypes from '../custom_prop_types';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
fieldset: {
|
||||
padding: theme.spacing(0.5),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: 'inherit',
|
||||
border: '1px solid ' + theme.otherVars.borderColor,
|
||||
},
|
||||
legend: {
|
||||
const StyledFieldset = styled('fieldset')(({theme}) => ({
|
||||
padding: theme.spacing(0.5),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: 'inherit',
|
||||
border: '1px solid ' + theme.otherVars.borderColor,
|
||||
'& .FieldSet-legend': {
|
||||
width: 'unset',
|
||||
fontSize: 'inherit',
|
||||
fontWeight: 'bold',
|
||||
|
@ -19,12 +16,12 @@ const useStyles = makeStyles((theme)=>({
|
|||
}));
|
||||
|
||||
export default function FieldSet({title='', className, children}) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<fieldset className={clsx(classes.fieldset, className)}>
|
||||
<legend className={classes.legend}>{title}</legend>
|
||||
<StyledFieldset className={className}>
|
||||
<legend className='FieldSet-legend'>{title}</legend>
|
||||
{children}
|
||||
</fieldset>
|
||||
</StyledFieldset>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
/* Common form components used in pgAdmin */
|
||||
|
||||
import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import {
|
||||
Box, FormControl, OutlinedInput, FormHelperText, ToggleButton, ToggleButtonGroup,
|
||||
Grid, IconButton, FormControlLabel, Switch, Checkbox, useTheme, InputLabel, Paper, Select as MuiSelect, Radio, Tooltip,
|
||||
|
@ -24,7 +24,6 @@ import DescriptionIcon from '@mui/icons-material/Description';
|
|||
import AssignmentTurnedIn from '@mui/icons-material/AssignmentTurnedIn';
|
||||
import Select, { components as RSComponents } from 'react-select';
|
||||
import CreatableSelect from 'react-select/creatable';
|
||||
import clsx from 'clsx';
|
||||
import PropTypes from 'prop-types';
|
||||
import HTMLReactParse from 'html-react-parser';
|
||||
|
||||
|
@ -46,52 +45,40 @@ import PgTreeView from '../PgTreeView';
|
|||
import Loader from 'sources/components/Loader';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
formRoot: {
|
||||
padding: '1rem'
|
||||
const Root = styled('div')(({theme}) => ({
|
||||
'& .Form-optionIcon': {
|
||||
...theme.mixins.nodeIcon,
|
||||
},
|
||||
img: {
|
||||
maxWidth: '100%',
|
||||
height: 'auto'
|
||||
},
|
||||
info: {
|
||||
color: theme.palette.info.main,
|
||||
marginLeft: '0.25rem',
|
||||
fontSize: '1rem',
|
||||
},
|
||||
formLabel: {
|
||||
margin: theme.spacing(0.75, 0.75, 0.75, 0.75),
|
||||
display: 'flex',
|
||||
wordBreak: 'break-word'
|
||||
},
|
||||
formLabelError: {
|
||||
color: theme.palette.error.main,
|
||||
},
|
||||
sql: {
|
||||
// '& .Form-label': {
|
||||
// margin: theme.spacing(0.75, 0.75, 0.75, 0.75),
|
||||
// display: 'flex',
|
||||
// wordBreak: 'break-word'
|
||||
// },
|
||||
// '& .Form-labelError': {
|
||||
// color: theme.palette.error.main,
|
||||
// },
|
||||
'& .Form-sql': {
|
||||
border: '1px solid ' + theme.otherVars.inputBorderColor,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
height: '100%',
|
||||
},
|
||||
optionIcon: {
|
||||
...theme.mixins.nodeIcon,
|
||||
'& .Form-readOnlySwitch': {
|
||||
opacity: 0.75,
|
||||
'& .MuiSwitch-track': {
|
||||
opacity: theme.palette.action.disabledOpacity,
|
||||
}
|
||||
},
|
||||
colorBtn: {
|
||||
'& .Form-colorBtn': {
|
||||
height: theme.spacing(3.5),
|
||||
minHeight: theme.spacing(3.5),
|
||||
width: theme.spacing(3.5),
|
||||
minWidth: theme.spacing(3.5),
|
||||
},
|
||||
noteRoot: {
|
||||
'& .Form-noteRoot': {
|
||||
display: 'flex',
|
||||
backgroundColor: theme.otherVars.borderColor,
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
readOnlySwitch: {
|
||||
opacity: 0.75,
|
||||
'& .MuiSwitch-track': {
|
||||
opacity: theme.palette.action.disabledOpacity,
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
|
@ -125,36 +112,47 @@ FormIcon.propTypes = {
|
|||
close: PropTypes.bool,
|
||||
};
|
||||
|
||||
const StyledGrid = styled(Grid)(({theme}) => ({
|
||||
'& .Form-label': {
|
||||
margin: theme.spacing(0.75, 0.75, 0.75, 0.75),
|
||||
display: 'flex',
|
||||
wordBreak: 'break-word'
|
||||
},
|
||||
'& .Form-labelError': {
|
||||
color: theme.palette.error.main,
|
||||
},
|
||||
}));
|
||||
|
||||
/* Wrapper on any form component to add label, error indicator and help message */
|
||||
export function FormInput({ children, error, className, label, helpMessage, required, testcid, lid, withContainer=true, labelGridBasis=3, controlGridBasis=9, labelTooltip='' }) {
|
||||
const classes = useStyles();
|
||||
|
||||
const cid = testcid || _.uniqueId('c');
|
||||
const helpid = `h${cid}`;
|
||||
if(!withContainer) {
|
||||
return (
|
||||
<>
|
||||
<Grid item lg={labelGridBasis} md={labelGridBasis} sm={12} xs={12}>
|
||||
<InputLabel id={lid} htmlFor={lid ? undefined : cid} className={clsx(classes.formLabel, error ? classes.formLabelError : null)} required={required}>
|
||||
(<>
|
||||
<StyledGrid item lg={labelGridBasis} md={labelGridBasis} sm={12} xs={12}>
|
||||
<InputLabel id={lid} htmlFor={lid ? undefined : cid} className={'Form-label ' + (error ? 'Form-labelError' : null)} required={required}>
|
||||
{label}
|
||||
<FormIcon type={MESSAGE_TYPE.ERROR} style={{ marginLeft: 'auto', visibility: error ? 'unset' : 'hidden' }} />
|
||||
</InputLabel>
|
||||
</Grid>
|
||||
<Grid item lg={controlGridBasis} md={controlGridBasis} sm={12} xs={12}>
|
||||
</StyledGrid>
|
||||
<StyledGrid item lg={controlGridBasis} md={controlGridBasis} sm={12} xs={12}>
|
||||
<FormControl error={Boolean(error)} fullWidth>
|
||||
{React.cloneElement(children, { cid, helpid })}
|
||||
</FormControl>
|
||||
<FormHelperText id={helpid} variant="outlined">{HTMLReactParse(helpMessage || '')}</FormHelperText>
|
||||
</Grid>
|
||||
</>
|
||||
</StyledGrid>
|
||||
</>)
|
||||
);
|
||||
}
|
||||
|
||||
let labelComponent = <InputLabel id={lid} htmlFor={lid ? undefined : cid} className={clsx(classes.formLabel, error ? classes.formLabelError : null)} required={required}>
|
||||
let labelComponent = <InputLabel id={lid} htmlFor={lid ? undefined : cid} className={'Form-label ' + (error ? 'Form-labelError' : null)} required={required}>
|
||||
{label}
|
||||
<FormIcon type={MESSAGE_TYPE.ERROR} style={{ marginLeft: 'auto', visibility: error ? 'unset' : 'hidden' }} />
|
||||
</InputLabel>;
|
||||
return (
|
||||
<Grid container spacing={0} className={className} data-testid="form-input">
|
||||
<StyledGrid container spacing={0} className={className} data-testid="form-input">
|
||||
<Grid item lg={labelGridBasis} md={labelGridBasis} sm={12} xs={12}>
|
||||
{
|
||||
labelTooltip ?
|
||||
|
@ -169,7 +167,7 @@ export function FormInput({ children, error, className, label, helpMessage, requ
|
|||
</FormControl>
|
||||
<FormHelperText id={helpid} variant="outlined">{HTMLReactParse(helpMessage || '')}</FormHelperText>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</StyledGrid>
|
||||
);
|
||||
}
|
||||
FormInput.propTypes = {
|
||||
|
@ -188,24 +186,26 @@ FormInput.propTypes = {
|
|||
};
|
||||
|
||||
export function InputSQL({ value, options, onChange, className, controlProps, inputRef, ...props }) {
|
||||
const classes = useStyles();
|
||||
|
||||
const editor = useRef();
|
||||
|
||||
return (
|
||||
<CodeMirror
|
||||
currEditor={(obj) => {
|
||||
editor.current = obj;
|
||||
inputRef?.(obj);
|
||||
}}
|
||||
value={value || ''}
|
||||
options={{
|
||||
...options,
|
||||
}}
|
||||
className={clsx(classes.sql, className)}
|
||||
onChange={onChange}
|
||||
{...controlProps}
|
||||
{...props}
|
||||
/>
|
||||
<Root style={{height: '100%'}}>
|
||||
<CodeMirror
|
||||
currEditor={(obj) => {
|
||||
editor.current = obj;
|
||||
inputRef?.(obj);
|
||||
}}
|
||||
value={value || ''}
|
||||
options={{
|
||||
...options,
|
||||
}}
|
||||
className={'Form-sql ' + className}
|
||||
onChange={onChange}
|
||||
{...controlProps}
|
||||
{...props}
|
||||
/>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
InputSQL.propTypes = {
|
||||
|
@ -510,7 +510,7 @@ FormInputFileSelect.propTypes = {
|
|||
};
|
||||
|
||||
export function InputSwitch({ cid, helpid, value, onChange, readonly, controlProps, ...props }) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Switch color="primary"
|
||||
checked={Boolean(value)}
|
||||
|
@ -523,7 +523,7 @@ export function InputSwitch({ cid, helpid, value, onChange, readonly, controlPro
|
|||
}}
|
||||
{...controlProps}
|
||||
{...props}
|
||||
className={(readonly || props.disabled) ? classes.readOnlySwitch : null}
|
||||
className={(readonly || props.disabled) ? 'Form-readOnlySwitch' : null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -605,7 +605,6 @@ FormInputCheckbox.propTypes = {
|
|||
};
|
||||
|
||||
export function InputRadio({ helpid, value, onChange, controlProps, readonly, labelPlacement, ...props }) {
|
||||
const classes = useStyles();
|
||||
controlProps = controlProps || {};
|
||||
return (
|
||||
<FormControlLabel
|
||||
|
@ -624,11 +623,10 @@ export function InputRadio({ helpid, value, onChange, controlProps, readonly, la
|
|||
disableRipple
|
||||
{...props}
|
||||
/>
|
||||
|
||||
}
|
||||
label={controlProps.label}
|
||||
labelPlacement={labelPlacement}
|
||||
className={(readonly || props.disabled) ? classes.readOnlySwitch : null}
|
||||
className={(readonly || props.disabled) ? 'Form-readOnlySwitch' : null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -811,13 +809,13 @@ const customReactSelectStyles = (theme, readonly) => ({
|
|||
});
|
||||
|
||||
function OptionView({ image, imageUrl, label }) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<>
|
||||
{image && <span className={clsx(classes.optionIcon, image)}></span>}
|
||||
<Root>
|
||||
{image && <span className={'Form-optionIcon ' + image}></span>}
|
||||
{imageUrl && <img style={{height: '20px', marginRight: '4px'}} src={imageUrl} />}
|
||||
<span>{label}</span>
|
||||
</>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
OptionView.propTypes = {
|
||||
|
@ -1063,11 +1061,9 @@ FormInputSelect.propTypes = {
|
|||
|
||||
const ColorButton = withColorPicker(PgIconButton);
|
||||
export function InputColor({ value, controlProps, disabled, onChange, currObj }) {
|
||||
const classes = useStyles();
|
||||
|
||||
let btnStyles = { backgroundColor: value };
|
||||
return (
|
||||
<ColorButton title={gettext('Select the color')} className={classes.colorBtn} style={btnStyles} disabled={disabled}
|
||||
<ColorButton title={gettext('Select the color')} className='Form-colorBtn' style={btnStyles} disabled={disabled}
|
||||
icon={(_.isUndefined(value) || _.isNull(value) || value === '') && <CloseIcon data-label="CloseIcon" />} options={{
|
||||
...controlProps,
|
||||
disabled: disabled
|
||||
|
@ -1115,15 +1111,17 @@ PlainString.propTypes = {
|
|||
};
|
||||
|
||||
export function FormNote({ text, className, controlProps }) {
|
||||
const classes = useStyles();
|
||||
|
||||
/* If raw, then remove the styles and icon */
|
||||
return (
|
||||
<Box className={className}>
|
||||
<Paper elevation={0} className={controlProps?.raw ? '' : classes.noteRoot}>
|
||||
{!controlProps?.raw && <Box paddingRight="0.25rem"><DescriptionIcon fontSize="small" /></Box>}
|
||||
<Box>{HTMLReactParse(text || '')}</Box>
|
||||
</Paper>
|
||||
</Box>
|
||||
<Root>
|
||||
<Box className={className}>
|
||||
<Paper elevation={0} className={controlProps?.raw ? '' : 'Form-noteRoot'}>
|
||||
{!controlProps?.raw && <Box paddingRight="0.25rem"><DescriptionIcon fontSize="small" /></Box>}
|
||||
<Box>{HTMLReactParse(text || '')}</Box>
|
||||
</Paper>
|
||||
</Box>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
FormNote.propTypes = {
|
||||
|
@ -1132,76 +1130,25 @@ FormNote.propTypes = {
|
|||
controlProps: PropTypes.object,
|
||||
};
|
||||
|
||||
const useStylesFormFooter = makeStyles((theme) => ({
|
||||
root: {
|
||||
padding: theme.spacing(0.5),
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 10,
|
||||
},
|
||||
container: {
|
||||
borderWidth: '1px',
|
||||
borderStyle: 'solid',
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
padding: theme.spacing(0.5),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
minHeight: '36px',
|
||||
},
|
||||
containerSuccess: {
|
||||
borderColor: theme.palette.success.main,
|
||||
backgroundColor: theme.palette.success.light,
|
||||
},
|
||||
iconSuccess: {
|
||||
color: theme.palette.success.main,
|
||||
},
|
||||
containerError: {
|
||||
borderColor: theme.palette.error.main,
|
||||
backgroundColor: theme.palette.error.light,
|
||||
},
|
||||
iconError: {
|
||||
color: theme.palette.error.main,
|
||||
},
|
||||
containerInfo: {
|
||||
borderColor: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
},
|
||||
iconInfo: {
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
containerWarning: {
|
||||
borderColor: theme.palette.warning.main,
|
||||
backgroundColor: theme.palette.warning.light,
|
||||
},
|
||||
iconWarning: {
|
||||
color: theme.palette.warning.main,
|
||||
},
|
||||
message: {
|
||||
color: theme.palette.text.primary,
|
||||
marginLeft: theme.spacing(0.5),
|
||||
},
|
||||
messageCenter: {
|
||||
color: theme.palette.text.primary,
|
||||
margin: 'auto',
|
||||
},
|
||||
closeButton: {
|
||||
marginLeft: 'auto',
|
||||
},
|
||||
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
padding: theme.spacing(0.5),
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 10,
|
||||
}));
|
||||
|
||||
/* The form footer used mostly for showing error */
|
||||
export function FormFooterMessage({style, ...props}) {
|
||||
const classes = useStylesFormFooter();
|
||||
|
||||
if (!props.message) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<Box className={classes.root} style={style}>
|
||||
<StyledBox style={style}>
|
||||
<NotifierMessage {...props}></NotifierMessage>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1210,18 +1157,17 @@ FormFooterMessage.propTypes = {
|
|||
message: PropTypes.string,
|
||||
};
|
||||
|
||||
const useStylesKeyboardShortcut = makeStyles(() => ({
|
||||
customRow: {
|
||||
const StyledFormInput = styled(FormInput)(() => ({
|
||||
'&.FormInput-customRow': {
|
||||
paddingTop: 5
|
||||
}
|
||||
}));
|
||||
|
||||
export function FormInputKeyboardShortcut({ hasError, label, className, helpMessage, onChange, labelTooltip, ...props }) {
|
||||
const classes = useStylesKeyboardShortcut();
|
||||
return (
|
||||
<FormInput label={label} error={hasError} className={clsx(classes.customRow, className)} helpMessage={helpMessage} labelTooltip={labelTooltip}>
|
||||
<StyledFormInput label={label} error={hasError} className={'FormInput-customRow ' + className} helpMessage={helpMessage} labelTooltip={labelTooltip}>
|
||||
<KeyboardShortcuts onChange={onChange} {...props} />
|
||||
</FormInput>
|
||||
</StyledFormInput>
|
||||
|
||||
);
|
||||
}
|
||||
|
@ -1276,20 +1222,66 @@ FormInputSelectThemes.propTypes = {
|
|||
labelTooltip: PropTypes.string
|
||||
};
|
||||
|
||||
const StyledNotifierMessageBox = styled(Box)(({theme}) => ({
|
||||
borderWidth: '1px',
|
||||
borderStyle: 'solid',
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
padding: theme.spacing(0.5),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
minHeight: '36px',
|
||||
'&.FormFooter-containerError': {
|
||||
borderColor: theme.palette.error.main,
|
||||
backgroundColor: theme.palette.error.light,
|
||||
'& .FormFooter-iconError': {
|
||||
color: theme.palette.error.main,
|
||||
},
|
||||
},
|
||||
'&.FormFooter-containerSuccess': {
|
||||
borderColor: theme.palette.success.main,
|
||||
backgroundColor: theme.palette.success.light,
|
||||
'& .FormFooter-iconSuccess': {
|
||||
color: theme.palette.success.main,
|
||||
},
|
||||
},
|
||||
'&.FormFooter-containerInfo': {
|
||||
borderColor: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
'& .FormFooter-iconInfo': {
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
},
|
||||
'&.FormFooter-containerWarning': {
|
||||
borderColor: theme.palette.warning.main,
|
||||
backgroundColor: theme.palette.warning.light,
|
||||
'& .FormFooter-iconWarning': {
|
||||
color: theme.palette.warning.main,
|
||||
},
|
||||
},
|
||||
'& .FormFooter-message': {
|
||||
color: theme.palette.text.primary,
|
||||
marginLeft: theme.spacing(0.5),
|
||||
},
|
||||
'& .FormFooter-messageCenter': {
|
||||
color: theme.palette.text.primary,
|
||||
margin: 'auto',
|
||||
},
|
||||
'& .FormFooter-closeButton': {
|
||||
marginLeft: 'auto',
|
||||
},
|
||||
}));
|
||||
|
||||
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}`])} style={style} data-test="notifier-message">
|
||||
{showIcon && <FormIcon type={type} className={classes[`icon${type}`]} />}
|
||||
<Box className={textCenter ? classes.messageCenter : classes.message}>{HTMLReactParse(message || '')}</Box>
|
||||
{closable && <IconButton title={gettext('Close Message')} className={clsx(classes.closeButton, classes[`icon${type}`])} onClick={onClose}>
|
||||
<StyledNotifierMessageBox className={`FormFooter-container${type}`} style={style} data-test="notifier-message">
|
||||
{showIcon && <FormIcon type={type} className={`FormFooter-icon${type}`} />}
|
||||
<Box className={textCenter ? 'FormFooter-messageCenter' : 'FormFooter-message'}>{HTMLReactParse(message || '')}</Box>
|
||||
{closable && <IconButton title={gettext('Close Message')} className={'FormFooter-closeButton ' + `FormFooter-icon${type}`} onClick={onClose}>
|
||||
<FormIcon close={true} />
|
||||
</IconButton>}
|
||||
</Box>
|
||||
</StyledNotifierMessageBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -112,4 +112,4 @@ KeyboardShortcuts.propTypes = {
|
|||
onChange: PropTypes.func,
|
||||
fields: PropTypes.array,
|
||||
name: PropTypes.string,
|
||||
};
|
||||
};
|
|
@ -8,52 +8,50 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import { CircularProgress, Box, Typography } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
backgroundColor: theme.otherVars.loader.backgroundColor,
|
||||
color: theme.otherVars.loader.color,
|
||||
zIndex: 1000,
|
||||
display: 'flex',
|
||||
},
|
||||
loaderRoot: {
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
backgroundColor: theme.otherVars.loader.backgroundColor,
|
||||
color: theme.otherVars.loader.color,
|
||||
zIndex: 1000,
|
||||
display: 'flex',
|
||||
'& .Loader-loaderBody': {
|
||||
color: theme.otherVars.loader.color,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
margin: 'auto',
|
||||
'.MuiTypography-root': {
|
||||
marginLeft: theme.spacing(1),
|
||||
},
|
||||
'& .Loader-icon': {
|
||||
color: theme.otherVars.loader.color,
|
||||
},
|
||||
'& .Loader-message': {
|
||||
marginLeft: '0.5rem',
|
||||
fontSize: '16px',
|
||||
}
|
||||
},
|
||||
loader: {
|
||||
color: theme.otherVars.loader.color,
|
||||
},
|
||||
message: {
|
||||
marginLeft: '0.5rem',
|
||||
fontSize: '16px',
|
||||
}
|
||||
}));
|
||||
|
||||
export default function Loader({message, style, autoEllipsis=false, ...props}) {
|
||||
const classes = useStyles();
|
||||
|
||||
if(!message) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<Box className={classes.root} style={style} data-label="loader" {...props}>
|
||||
<Box className={classes.loaderRoot}>
|
||||
<CircularProgress className={classes.loader} />
|
||||
<Typography className={classes.message}>{message}{autoEllipsis ? '...':''}</Typography>
|
||||
<StyledBox style={style} data-label="loader" {...props}>
|
||||
<Box className='Loader-loaderBody'>
|
||||
<CircularProgress className='Loader-icon' />
|
||||
<Typography className='Loader-message'>{message}{autoEllipsis ? '...':''}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { makeStyles } from '@mui/styles';
|
||||
import React, { useRef } from 'react';
|
||||
import CheckIcon from '@mui/icons-material/Check';
|
||||
import PropTypes from 'prop-types';
|
||||
|
@ -12,52 +11,9 @@ import {
|
|||
} from '@szhsin/react-menu';
|
||||
export {MenuDivider as PgMenuDivider} from '@szhsin/react-menu';
|
||||
import { shortcutToString } from './ShortcutTitle';
|
||||
import clsx from 'clsx';
|
||||
import CustomPropTypes from '../custom_prop_types';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
menu: {
|
||||
'& .szh-menu': {
|
||||
padding: '4px 0px',
|
||||
zIndex: 1005,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
border: `1px solid ${theme.otherVars.borderColor}`
|
||||
},
|
||||
'& .szh-menu__divider': {
|
||||
margin: 0,
|
||||
background: theme.otherVars.borderColor,
|
||||
},
|
||||
'& .szh-menu__item': {
|
||||
display: 'flex',
|
||||
padding: '3px 12px',
|
||||
'&:after': {
|
||||
right: '0.75rem',
|
||||
},
|
||||
'&.szh-menu__item--active, &.szh-menu__item--hover': {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: theme.palette.primary.contrastText,
|
||||
},
|
||||
'&.szh-menu__item--disabled':{
|
||||
color: theme.palette.text.muted,
|
||||
}
|
||||
}
|
||||
},
|
||||
checkIcon: {
|
||||
width: '1.3rem',
|
||||
},
|
||||
hideCheck: {
|
||||
visibility: 'hidden',
|
||||
},
|
||||
shortcut: {
|
||||
marginLeft: 'auto',
|
||||
fontSize: '0.8em',
|
||||
paddingLeft: '12px',
|
||||
}
|
||||
}));
|
||||
|
||||
export function PgMenu({open, className='', label, menuButton=null, ...props}) {
|
||||
const classes = useStyles();
|
||||
const state = open ? 'open' : 'closed';
|
||||
props.anchorRef?.current?.setAttribute('data-state', state);
|
||||
|
||||
|
@ -65,7 +21,7 @@ export function PgMenu({open, className='', label, menuButton=null, ...props}) {
|
|||
return <Menu
|
||||
{...props}
|
||||
menuButton={menuButton}
|
||||
className={clsx(classes.menu, className)}
|
||||
className={className}
|
||||
aria-label={label || 'Menu'}
|
||||
onContextMenu={(e)=>e.preventDefault()}
|
||||
viewScroll='close'
|
||||
|
@ -75,7 +31,7 @@ export function PgMenu({open, className='', label, menuButton=null, ...props}) {
|
|||
<ControlledMenu
|
||||
state={state}
|
||||
{...props}
|
||||
className={clsx(classes.menu, className)}
|
||||
className={className}
|
||||
aria-label={label || 'Menu'}
|
||||
data-state={state}
|
||||
onContextMenu={(e)=>e.preventDefault()}
|
||||
|
@ -99,7 +55,7 @@ export const PgSubMenu = applyStatics(SubMenu)(({label, ...props})=>{
|
|||
});
|
||||
|
||||
export const PgMenuItem = applyStatics(MenuItem)(({hasCheck=false, checked=false, accesskey, shortcut, children, closeOnCheck=false, ...props})=>{
|
||||
const classes = useStyles();
|
||||
|
||||
let onClick = props.onClick;
|
||||
if(hasCheck) {
|
||||
onClick = (e)=>{
|
||||
|
@ -111,9 +67,9 @@ export const PgMenuItem = applyStatics(MenuItem)(({hasCheck=false, checked=false
|
|||
|
||||
const dataLabel = typeof(children) == 'string' ? children : props.datalabel;
|
||||
return <MenuItem {...props} onClick={onClick} data-label={dataLabel} data-checked={checked}>
|
||||
{hasCheck && <CheckIcon className={classes.checkIcon} style={checked ? {} : {visibility: 'hidden'}} data-label="CheckIcon"/>}
|
||||
{hasCheck && <CheckIcon style={checked ? {} : {visibility: 'hidden', width: '1.3rem'}} data-label="CheckIcon"/>}
|
||||
{children}
|
||||
<div className={classes.shortcut}>
|
||||
<div>
|
||||
{keyVal ? `(${keyVal})` : ''}
|
||||
</div>
|
||||
</MenuItem>;
|
||||
|
|
56
web/pgadmin/static/js/components/ModalContent.jsx
Normal file
56
web/pgadmin/static/js/components/ModalContent.jsx
Normal file
|
@ -0,0 +1,56 @@
|
|||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React, { forwardRef } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import CustomPropTypes from '../custom_prop_types';
|
||||
import { Box } from '@mui/material';
|
||||
|
||||
const StyledBox = styled(Box)(({theme})=>({
|
||||
backgroundColor: theme.palette.background.default,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
'& .ModalContent-footer': {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
padding: '0.5rem',
|
||||
...theme.mixins.panelBorder?.top,
|
||||
gap: '5px',
|
||||
'&.ModalContent-iconButtonStyle': {
|
||||
marginLeft: 'auto',
|
||||
marginRight: '4px'
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export const ModalContent = forwardRef(({children, ...props }, ref) => {
|
||||
return (
|
||||
<StyledBox ref={ref} {...props}>{children}</StyledBox>
|
||||
);
|
||||
});
|
||||
ModalContent.displayName = 'ModalContent';
|
||||
ModalContent.propTypes = {
|
||||
children: CustomPropTypes.children,
|
||||
};
|
||||
|
||||
|
||||
export function ModalFooter({children, classNameRoot, ...props}) {
|
||||
return (
|
||||
<StyledBox className={[classNameRoot]} {...props}>
|
||||
<Box className='ModalContent-footer'>
|
||||
{children}
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
ModalFooter.propTypes = {
|
||||
children: CustomPropTypes.children,
|
||||
classNameRoot: CustomPropTypes.className
|
||||
};
|
|
@ -7,7 +7,7 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import AccountTreeIcon from '@mui/icons-material/AccountTree';
|
||||
import CommentIcon from '@mui/icons-material/Comment';
|
||||
|
@ -15,33 +15,30 @@ import ArrowForwardIosRoundedIcon from '@mui/icons-material/ArrowForwardIosRound
|
|||
import { usePgAdmin } from '../../../static/js/BrowserComponent';
|
||||
import usePreferences from '../../../preferences/static/js/store';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
width: 'auto',
|
||||
maxWidth: '99%',
|
||||
zIndex: 1004,
|
||||
padding: '0.25rem 0.5rem',
|
||||
fontSize: '0.95em',
|
||||
color: theme.palette.background.default,
|
||||
backgroundColor: theme.palette.text.primary,
|
||||
borderTopRightRadius: theme.shape.borderRadius,
|
||||
},
|
||||
row: {
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
width: 'auto',
|
||||
maxWidth: '99%',
|
||||
zIndex: 1004,
|
||||
padding: '0.25rem 0.5rem',
|
||||
fontSize: '0.95em',
|
||||
color: theme.palette.background.default,
|
||||
backgroundColor: theme.palette.text.primary,
|
||||
borderTopRightRadius: theme.shape.borderRadius,
|
||||
'& .ObjectBreadcrumbs-row': {
|
||||
display: 'flex',
|
||||
alignItems: 'center'
|
||||
alignItems: 'center',
|
||||
'& .ObjectBreadcrumbs-overflow': {
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
}
|
||||
},
|
||||
overflow: {
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
export default function ObjectBreadcrumbs() {
|
||||
const classes = useStyles();
|
||||
const pgAdmin = usePgAdmin();
|
||||
const preferences = usePreferences().getPreferencesForModule('browser');
|
||||
const [objectData, setObjectData] = useState({
|
||||
|
@ -75,11 +72,11 @@ export default function ObjectBreadcrumbs() {
|
|||
return <></>;
|
||||
}
|
||||
|
||||
return(
|
||||
<Box className={classes.root} data-testid="object-breadcrumbs">
|
||||
<div className={classes.row}>
|
||||
return (
|
||||
<StyledBox data-testid="object-breadcrumbs">
|
||||
<div className='ObjectBreadcrumbs-row'>
|
||||
<AccountTreeIcon style={{height: '1rem', marginRight: '0.125rem'}} data-label="AccountTreeIcon"/>
|
||||
<div className={classes.overflow}>
|
||||
<div className='ObjectBreadcrumbs-overflow'>
|
||||
{
|
||||
objectData.path?.reduce((res, item, i)=>(
|
||||
res.concat(<span key={item+i}>{item}</span>, <ArrowForwardIosRoundedIcon key={item+i+'-arrow'} style={{height: '0.8rem', width: '1.25rem'}} />)
|
||||
|
@ -88,10 +85,10 @@ export default function ObjectBreadcrumbs() {
|
|||
</div>
|
||||
</div>
|
||||
{preferences.breadcrumbs_show_comment && objectData.description &&
|
||||
<div className={classes.row}>
|
||||
<div className='ObjectBreadcrumbs-row'>
|
||||
<CommentIcon style={{height: '1rem', marginRight: '0.125rem'}} data-label="CommentIcon"/>
|
||||
<div className={classes.overflow}>{objectData.description}</div>
|
||||
<div className='ObjectBreadcrumbs-overflow'>{objectData.description}</div>
|
||||
</div>}
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import UplotReact from 'uplot-react';
|
|||
import { useResizeDetector } from 'react-resize-detector';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTheme } from '@mui/styles';
|
||||
import { useTheme } from '@mui/material';
|
||||
|
||||
function tooltipPlugin(refreshRate) {
|
||||
let tooltipTopOffset = -20;
|
||||
|
|
|
@ -9,23 +9,22 @@
|
|||
import React, { useContext, useEffect } from 'react';
|
||||
import ReactDataGrid, { Row } from 'react-data-grid';
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import clsx from 'clsx';
|
||||
import PropTypes from 'prop-types';
|
||||
import CustomPropTypes from '../custom_prop_types';
|
||||
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
|
||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
||||
import gettext from 'sources/gettext';
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
const StyledReactDataGrid = styled(ReactDataGrid)(({theme})=>({
|
||||
'&.ReactGrid-root': {
|
||||
height: '100%',
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.otherVars.qtDatagridBg,
|
||||
fontSize: '12px',
|
||||
border: 'none',
|
||||
'--rdg-selection-color': theme.palette.primary.main,
|
||||
'& .rdg-cell': {
|
||||
'&.rdg-cell': {
|
||||
...theme.mixins.panelBorder.right,
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
fontWeight: 'abc',
|
||||
|
@ -44,7 +43,7 @@ const useStyles = makeStyles((theme)=>({
|
|||
'& .rdg-header-row': {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
},
|
||||
'& .rdg-row': {
|
||||
'&.rdg-row': {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
'&[aria-selected=true]': {
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
|
@ -52,7 +51,7 @@ const useStyles = makeStyles((theme)=>({
|
|||
},
|
||||
}
|
||||
},
|
||||
cellSelection: {
|
||||
'&.ReactGrid-cellSelection': {
|
||||
'& .rdg-cell': {
|
||||
'&[aria-selected=true]:not([role="columnheader"])': {
|
||||
outlineWidth: '1px',
|
||||
|
@ -62,7 +61,7 @@ const useStyles = makeStyles((theme)=>({
|
|||
}
|
||||
},
|
||||
},
|
||||
hasSelectColumn: {
|
||||
'&.ReactGrid-hasSelectColumn': {
|
||||
'& .rdg-cell': {
|
||||
'&[aria-selected=true][aria-colindex="1"]': {
|
||||
outlineWidth: '2px',
|
||||
|
@ -71,7 +70,7 @@ const useStyles = makeStyles((theme)=>({
|
|||
color: theme.palette.text.primary,
|
||||
}
|
||||
},
|
||||
'& .rdg-row[aria-selected=true] .rdg-cell:nth-child(1)': {
|
||||
'& .rdg-row[aria-selected=true] .rdg-cell:nth-of-type(1)': {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: theme.palette.primary.contrastText,
|
||||
}
|
||||
|
@ -131,16 +130,16 @@ CustomRow.propTypes = {
|
|||
|
||||
export default function PgReactDataGrid({gridRef, className, hasSelectColumn=true, onItemEnter, onItemSelect,
|
||||
onItemClick, noRowsText, noRowsIcon,...props}) {
|
||||
const classes = useStyles();
|
||||
let finalClassName = [classes.root];
|
||||
hasSelectColumn && finalClassName.push(classes.hasSelectColumn);
|
||||
props.enableCellSelect && finalClassName.push(classes.cellSelection);
|
||||
|
||||
let finalClassName = ['ReactGrid-root'];
|
||||
hasSelectColumn && finalClassName.push('ReactGrid-hasSelectColumn');
|
||||
props.enableCellSelect && finalClassName.push('ReactGrid-cellSelection');
|
||||
finalClassName.push(className);
|
||||
return (
|
||||
<GridContextUtils.Provider value={{onItemEnter, onItemSelect, onItemClick}}>
|
||||
<ReactDataGrid
|
||||
<StyledReactDataGrid
|
||||
ref={gridRef}
|
||||
className={clsx(finalClassName)}
|
||||
className={finalClassName.join(' ')}
|
||||
components={{
|
||||
sortIcon: CutomSortIcon,
|
||||
rowRenderer: CustomRow,
|
||||
|
|
|
@ -9,13 +9,12 @@
|
|||
|
||||
import React, { forwardRef, useEffect } from 'react';
|
||||
import { flexRender } from '@tanstack/react-table';
|
||||
import { styled } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
|
||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||
import { PgIconButton } from './Buttons';
|
||||
import clsx from 'clsx';
|
||||
import CustomPropTypes from '../custom_prop_types';
|
||||
import { InputSwitch } from './FormComponents';
|
||||
|
||||
|
@ -161,6 +160,9 @@ export const PgReactTableCell = forwardRef(({row, cell, children, className}, re
|
|||
if(row.original.icon && row.original.icon[cell.column.id]) {
|
||||
classNames.push(row.original.icon[cell.column.id], 'cell-with-icon');
|
||||
}
|
||||
if(cell.column.columnDef.dataClassName){
|
||||
classNames.push(cell.column.columnDef.dataClassName);
|
||||
}
|
||||
|
||||
classNames.push(className);
|
||||
|
||||
|
@ -170,7 +172,7 @@ export const PgReactTableCell = forwardRef(({row, cell, children, className}, re
|
|||
width: `calc(var(--col-${cell.column.id.replace(/\W/g, '_')}-size)*1px)`,
|
||||
...(cell.column.columnDef.maxSize ? { maxWidth: `${cell.column.columnDef.maxSize}px` } : {})
|
||||
}} role='cell'
|
||||
className={clsx(...classNames)}
|
||||
className={classNames.join(' ')}
|
||||
title={String(cell.getValue() ?? '')}>
|
||||
<div className='pgrd-row-cell-content'>{children}</div>
|
||||
</div>
|
||||
|
@ -187,7 +189,7 @@ PgReactTableCell.propTypes = {
|
|||
|
||||
export const PgReactTableRow = forwardRef(({ children, className, ...props }, ref)=>{
|
||||
return (
|
||||
<div className={clsx('pgrt-row', className)} ref={ref} role="row" {...props}>
|
||||
<div className={['pgrt-row', className].join(' ')} ref={ref} role="row" {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
@ -200,7 +202,7 @@ PgReactTableRow.propTypes = {
|
|||
|
||||
export const PgReactTableRowContent = forwardRef(({children, className, ...props}, ref)=>{
|
||||
return (
|
||||
<div className={clsx('pgrt-row-content', className)} ref={ref} {...props}>
|
||||
<div className={['pgrt-row-content', className].join(' ')} ref={ref} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
@ -311,8 +313,8 @@ export const PgReactTable = forwardRef(({children, table, rootClassName, tableCl
|
|||
}, [columns, table.getState().columnSizingInfo]);
|
||||
|
||||
return (
|
||||
<StyledDiv className={clsx('pgrt', rootClassName)} ref={ref} >
|
||||
<div className={clsx('pgrt-table', tableClassName)} style={{ ...columnSizeVars }} {...props}>
|
||||
<StyledDiv className={['pgrt', rootClassName].join(' ')} ref={ref} >
|
||||
<div className={['pgrt-table', tableClassName].join(' ')} style={{ ...columnSizeVars }} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
</StyledDiv>
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
flexRender,
|
||||
} from '@tanstack/react-table';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
import { styled } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Checkbox, Box } from '@mui/material';
|
||||
import { InputText } from './FormComponents';
|
||||
|
@ -28,7 +28,7 @@ import EmptyPanelMessage from './EmptyPanelMessage';
|
|||
import { PgReactTable, PgReactTableBody, PgReactTableCell, PgReactTableHeader, PgReactTableRow, PgReactTableRowContent, PgReactTableRowExpandContent } from './PgReactTableStyled';
|
||||
|
||||
const ROW_HEIGHT = 30;
|
||||
function TableRow({ index, style, schema, row, measureElement }) {
|
||||
function TableRow({ index, style, schema, row, measureElement}) {
|
||||
const [expandComplete, setExpandComplete] = React.useState(false);
|
||||
const rowRef = React.useRef();
|
||||
|
||||
|
@ -199,8 +199,6 @@ const StyledPgTableRoot = styled('div')(({theme})=>({
|
|||
overflow: 'hidden',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
|
||||
|
||||
'& .pgtable-header': {
|
||||
display: 'flex',
|
||||
background: theme.palette.background.default,
|
||||
|
@ -210,7 +208,6 @@ const StyledPgTableRoot = styled('div')(({theme})=>({
|
|||
minWidth: '300px'
|
||||
},
|
||||
},
|
||||
|
||||
'& .pgtable-body': {
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
|
@ -218,13 +215,11 @@ const StyledPgTableRoot = styled('div')(({theme})=>({
|
|||
flexDirection: 'column',
|
||||
backgroundColor: theme.otherVars.emptySpaceBg,
|
||||
},
|
||||
|
||||
'&.pgtable-pgrt-border': {
|
||||
'& .pgrt': {
|
||||
border: '1px solid ' + theme.otherVars.borderColor,
|
||||
}
|
||||
},
|
||||
|
||||
'&.pgtable-pgrt-cave': {
|
||||
'& .pgtable-body': {
|
||||
padding: '8px',
|
||||
|
@ -244,7 +239,7 @@ export default function PgTable({ caveTable = true, tableNoBorder = true, ...pro
|
|||
return (
|
||||
<StyledPgTableRoot className={[tableNoBorder ? '' : 'pgtable-pgrt-border', caveTable ? 'pgtable-pgrt-cave' : ''].join(' ')} data-test={props['data-test']}>
|
||||
<Box className='pgtable-header'>
|
||||
{props.CustomHeader && (<Box className='pgtable-custom-header-section'> <props.CustomHeader /></Box>)}
|
||||
{props.CustomHeader && (<Box className={['pgtable-custom-header-section', props['className']].join(' ')}> <props.CustomHeader /></Box>)}
|
||||
<Box marginLeft="auto">
|
||||
<InputText
|
||||
placeholder={gettext('Search')}
|
||||
|
@ -268,5 +263,6 @@ PgTable.propTypes = {
|
|||
CustomHeader: PropTypes.func,
|
||||
caveTable: PropTypes.bool,
|
||||
tableNoBorder: PropTypes.bool,
|
||||
'data-test': PropTypes.string
|
||||
'data-test': PropTypes.string,
|
||||
'className': PropTypes.string
|
||||
};
|
||||
|
|
|
@ -1,34 +1,23 @@
|
|||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import { InputCheckbox, InputText } from './FormComponents';
|
||||
import clsx from 'clsx';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const useStyles = makeStyles(()=>({
|
||||
|
||||
const Root = styled('div')(()=>({
|
||||
/* Display the privs table only when focussed */
|
||||
root: {
|
||||
'&:not(:focus-within) .priv-table': {
|
||||
display: 'none',
|
||||
}
|
||||
'&:not(:focus-within) .priv-table': {
|
||||
display: 'none',
|
||||
},
|
||||
table: {
|
||||
'& .Privilege-table': {
|
||||
borderSpacing: 0,
|
||||
width: '100%',
|
||||
fontSize: '0.8em',
|
||||
'& .Privilege-tableCell': {
|
||||
textAlign: 'left',
|
||||
}
|
||||
},
|
||||
tableCell: {
|
||||
textAlign: 'left',
|
||||
}
|
||||
}));
|
||||
|
||||
export default function Privilege({value, onChange, controlProps}) {
|
||||
|
@ -50,7 +39,7 @@ export default function Privilege({value, onChange, controlProps}) {
|
|||
};
|
||||
let all = false;
|
||||
let allWithGrant = false;
|
||||
const classes = useStyles();
|
||||
|
||||
let textValue = '';
|
||||
for(const v of value||[]) {
|
||||
if(v.privilege) {
|
||||
|
@ -129,16 +118,16 @@ export default function Privilege({value, onChange, controlProps}) {
|
|||
allWithGrant = (realVal.length === (value || []).length) && (value || []).every((d)=>d.with_grant);
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Root>
|
||||
<InputText value={textValue} readOnly/>
|
||||
<table className={clsx(classes.table, 'priv-table')} tabIndex="0">
|
||||
<table className={'Privilege-table priv-table'} tabIndex="0">
|
||||
{(realVal.length > 1) && <thead>
|
||||
<tr>
|
||||
<td className={classes.tableCell}>
|
||||
<td className='Privilege-tableCell'>
|
||||
<InputCheckbox name="all" controlProps={{label: 'ALL'}} id={checkboxId} size="small"
|
||||
onChange={(e)=>onCheckAll(e, false)} value={all}/>
|
||||
</td>
|
||||
<td className={classes.tableCell}>
|
||||
<td className='Privilege-tableCell'>
|
||||
<InputCheckbox name="all" controlProps={{label: 'WITH GRANT OPTION'}} id={checkboxId} size="small"
|
||||
disabled={!all} onChange={(e)=>onCheckAll(e, true)} value={allWithGrant}/>
|
||||
</td>
|
||||
|
@ -149,12 +138,12 @@ export default function Privilege({value, onChange, controlProps}) {
|
|||
realVal.map((d)=>{
|
||||
return (
|
||||
<tr key={d.privilege_type}>
|
||||
<td className={classes.tableCell}>
|
||||
<td className='Privilege-tableCell'>
|
||||
<InputCheckbox name={d.privilege_type} controlProps={{label: LABELS[d.privilege_type]}}
|
||||
id={checkboxId} value={Boolean(d.privilege)} size="small"
|
||||
onChange={(e)=>onCheck(e, false)}/>
|
||||
</td>
|
||||
<td className={classes.tableCell}>
|
||||
<td className='Privilege-tableCell'>
|
||||
<InputCheckbox name={d.privilege_type} controlProps={{label: 'WITH GRANT OPTION'}}
|
||||
id={checkboxId} value={Boolean(d.with_grant)} size="small" disabled={!d.privilege}
|
||||
onChange={(e)=>onCheck(e, true)}/>
|
||||
|
@ -165,7 +154,7 @@ export default function Privilege({value, onChange, controlProps}) {
|
|||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,36 +10,12 @@
|
|||
import gettext from 'sources/gettext';
|
||||
import _ from 'lodash';
|
||||
import { FormGroup, Grid, Typography } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import React from 'react';
|
||||
import { InputText } from './FormComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
formControlLabel: {
|
||||
padding: '3px',
|
||||
},
|
||||
formInput: {
|
||||
marginLeft: '5px'
|
||||
},
|
||||
formCheckboxControl: {
|
||||
padding: '3px',
|
||||
border: '1px solid',
|
||||
borderRadius: '0.25rem',
|
||||
},
|
||||
formGroup: {
|
||||
padding: '5px'
|
||||
},
|
||||
contentTextAlign: {
|
||||
textAlign: 'center'
|
||||
},
|
||||
contentStyle: {
|
||||
paddingLeft: 10,
|
||||
}
|
||||
}));
|
||||
|
||||
export default function QueryThresholds({ value, onChange }) {
|
||||
const classes = useStyles();
|
||||
const warningCid = _.uniqueId('c');
|
||||
const warninghelpid = `h${warningCid}`;
|
||||
const alertCid = _.uniqueId('c');
|
||||
|
@ -70,13 +46,13 @@ export default function QueryThresholds({ value, onChange }) {
|
|||
<Grid item lg={2} md={2} sm={2} xs={12}>
|
||||
<InputText cid={warningCid} helpid={warninghelpid} type='numeric' value={value?.warning} onChange={onWarningChange} />
|
||||
</Grid>
|
||||
<Grid item lg={2} md={2} sm={2} xs={12} className={classes.contentTextAlign}>
|
||||
<Grid item lg={2} md={2} sm={2} xs={12} sx={{ textAlign: 'center' }}>
|
||||
<Typography>{gettext('Alert')}</Typography>
|
||||
</Grid>
|
||||
<Grid item lg={2} md={2} sm={2} xs={12}>
|
||||
<InputText cid={alertCid} helpid={alerthelpid} type='numeric' value={value?.alert} onChange={onAlertChange} />
|
||||
</Grid>
|
||||
<Grid item lg={4} md={4} sm={4} xs={12} className={classes.contentStyle}>
|
||||
<Grid item lg={4} md={4} sm={4} xs={12} sx={{paddingLeft: 10 }}>
|
||||
<Typography>{gettext('(in minutes)')}</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
import gettext from 'sources/gettext';
|
||||
import { Box, InputAdornment } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { InputText } from '../../FormComponents';
|
||||
import { PgIconButton } from '../../Buttons';
|
||||
import CloseIcon from '@mui/icons-material/CloseRounded';
|
||||
|
@ -32,21 +32,19 @@ import {
|
|||
replaceAll,
|
||||
} from '@codemirror/search';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
position: 'absolute',
|
||||
zIndex: 99,
|
||||
right: '4px',
|
||||
top: '0px',
|
||||
...theme.mixins.panelBorder.all,
|
||||
borderTop: 'none',
|
||||
padding: '2px 4px',
|
||||
width: '250px',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
},
|
||||
marginTop: {
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
position: 'absolute',
|
||||
zIndex: 99,
|
||||
right: '4px',
|
||||
top: '0px',
|
||||
...theme.mixins.panelBorder.all,
|
||||
borderTop: 'none',
|
||||
padding: '2px 4px',
|
||||
width: '250px',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
'& .CodeMirror-marginTop': {
|
||||
marginTop: '0.25rem',
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
export default function FindDialog({editor, show, replace, onClose}) {
|
||||
|
@ -56,7 +54,7 @@ export default function FindDialog({editor, show, replace, onClose}) {
|
|||
const [matchCase, setMatchCase] = useState(false);
|
||||
const findInputRef = useRef();
|
||||
const searchQuery = useRef();
|
||||
const classes = useStyles();
|
||||
|
||||
|
||||
const search = ()=>{
|
||||
if(editor) {
|
||||
|
@ -148,7 +146,7 @@ export default function FindDialog({editor, show, replace, onClose}) {
|
|||
}
|
||||
|
||||
return (
|
||||
<Box className={classes.root} style={{visibility: show ? 'visible' : 'hidden'}} tabIndex="0" onKeyDown={onEscape}>
|
||||
<StyledBox style={{visibility: show ? 'visible' : 'hidden'}} tabIndex="0" onKeyDown={onEscape}>
|
||||
<InputText value={findVal}
|
||||
inputRef={(ele)=>{findInputRef.current = ele;}}
|
||||
onChange={(value)=>setFindVal(value)}
|
||||
|
@ -164,12 +162,12 @@ export default function FindDialog({editor, show, replace, onClose}) {
|
|||
/>
|
||||
{replace &&
|
||||
<InputText value={replaceVal}
|
||||
className={classes.marginTop}
|
||||
className='CodeMirror-marginTop'
|
||||
onChange={(value)=>setReplaceVal(value)}
|
||||
onKeyPress={onReplaceEnter}
|
||||
/>}
|
||||
|
||||
<Box display="flex" className={classes.marginTop}>
|
||||
<Box display="flex" className='CodeMirror-marginTop'>
|
||||
<PgIconButton title={gettext('Previous')} icon={<ArrowUpwardRoundedIcon />} size="xs" noBorder onClick={onFindPrev}
|
||||
style={{marginRight: '2px'}} />
|
||||
<PgIconButton title={gettext('Next')} icon={<ArrowDownwardRoundedIcon />} size="xs" noBorder onClick={onFindNext}
|
||||
|
@ -183,7 +181,7 @@ export default function FindDialog({editor, show, replace, onClose}) {
|
|||
<PgIconButton title={gettext('Close')} icon={<CloseIcon />} size="xs" noBorder onClick={clearAndClose}/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,35 +8,33 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
import gettext from 'sources/gettext';
|
||||
import { Box, FormControl } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { InputText } from '../../FormComponents';
|
||||
import { PgIconButton } from '../../Buttons';
|
||||
import CloseIcon from '@mui/icons-material/CloseRounded';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
position: 'absolute',
|
||||
zIndex: 99,
|
||||
right: '4px',
|
||||
top: '0px',
|
||||
...theme.mixins.panelBorder.all,
|
||||
borderTop: 'none',
|
||||
padding: '2px 4px',
|
||||
width: '250px',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
},
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
position: 'absolute',
|
||||
zIndex: 99,
|
||||
right: '4px',
|
||||
top: '0px',
|
||||
...theme.mixins.panelBorder.all,
|
||||
borderTop: 'none',
|
||||
padding: '2px 4px',
|
||||
width: '250px',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
}));
|
||||
|
||||
export default function GotoDialog({editor, show, onClose}) {
|
||||
const [gotoVal, setGotoVal] = useState('');
|
||||
const inputRef = useRef();
|
||||
const classes = useStyles();
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
if(show) {
|
||||
|
@ -72,8 +70,8 @@ export default function GotoDialog({editor, show, onClose}) {
|
|||
}
|
||||
|
||||
return (
|
||||
<Box className={classes.root} style={{visibility: show ? 'visible' : 'hidden'}} tabIndex="0" onKeyDown={onEscape}>
|
||||
<div style={{whiteSpace: 'nowrap'}}>Ln [,Col]</div>
|
||||
<StyledBox style={{visibility: show ? 'visible' : 'hidden'}} tabIndex="0" onKeyDown={onEscape}>
|
||||
<div style={{whiteSpace: 'nowrap'}}>Ln, [Col]</div>
|
||||
<FormControl>
|
||||
<InputText
|
||||
value={gotoVal}
|
||||
|
@ -83,7 +81,7 @@ export default function GotoDialog({editor, show, onClose}) {
|
|||
/>
|
||||
</FormControl>
|
||||
<PgIconButton title={gettext('Close')} icon={<CloseIcon />} size="xs" noBorder onClick={onClose}/>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,9 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React, { useCallback, useMemo, useRef, useState, useEffect } from 'react';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import FileCopyRoundedIcon from '@mui/icons-material/FileCopyRounded';
|
||||
import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
|
||||
import clsx from 'clsx';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import gettext from 'sources/gettext';
|
||||
|
@ -24,12 +23,10 @@ import CustomPropTypes from '../../custom_prop_types';
|
|||
import FindDialog from './components/FindDialog';
|
||||
import GotoDialog from './components/GotoDialog';
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
root: {
|
||||
position: 'relative',
|
||||
height: '100%'
|
||||
},
|
||||
copyButton: {
|
||||
const Root = styled('div')(() => ({
|
||||
position: 'relative',
|
||||
height: '100%',
|
||||
'& .CodeMirror-copyButton': {
|
||||
position: 'absolute',
|
||||
zIndex: 99,
|
||||
right: '4px',
|
||||
|
@ -39,14 +36,14 @@ const useStyles = makeStyles(() => ({
|
|||
|
||||
|
||||
function CopyButton({ editor }) {
|
||||
const classes = useStyles();
|
||||
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
const revertCopiedText = useDelayedCaller(() => {
|
||||
setIsCopied(false);
|
||||
});
|
||||
|
||||
return (
|
||||
<PgIconButton size="small" className={classes.copyButton} icon={isCopied ? <CheckRoundedIcon /> : <FileCopyRoundedIcon />}
|
||||
<PgIconButton size="small" className='CodeMirror-copyButton' icon={isCopied ? <CheckRoundedIcon /> : <FileCopyRoundedIcon />}
|
||||
title={isCopied ? gettext('Copied!') : gettext('Copy')}
|
||||
onClick={() => {
|
||||
copyToClipboard(editor?.getValue());
|
||||
|
@ -63,7 +60,6 @@ CopyButton.propTypes = {
|
|||
|
||||
|
||||
export default function CodeMirror({className, currEditor, showCopyBtn=false, customKeyMap=[], onTextSelect, ...props}) {
|
||||
const classes = useStyles();
|
||||
const editor = useRef();
|
||||
const [[showFind, isReplace], setShowFind] = useState([false, false]);
|
||||
const [showGoto, setShowGoto] = useState(false);
|
||||
|
@ -140,12 +136,12 @@ export default function CodeMirror({className, currEditor, showCopyBtn=false, cu
|
|||
|
||||
|
||||
return (
|
||||
<div className={clsx(className, classes.root)} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
||||
<Root className={[className].join(' ')} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} >
|
||||
<Editor currEditor={currEditorWrap} customKeyMap={finalCustomKeyMap} {...props} />
|
||||
{showCopy && <CopyButton editor={editor.current} />}
|
||||
<FindDialog editor={editor.current} show={showFind} replace={isReplace} onClose={closeFind} />
|
||||
<GotoDialog editor={editor.current} show={showGoto} onClose={closeGoto} />
|
||||
</div>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,20 +9,14 @@
|
|||
|
||||
import gettext from 'sources/gettext';
|
||||
import { Grid } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import React, { useMemo } from 'react';
|
||||
import {InputSelect } from './FormComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
import CustomPropTypes from '../custom_prop_types';
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
preview: {
|
||||
paddingTop: 10
|
||||
}
|
||||
}));
|
||||
|
||||
export default function SelectThemes({onChange, ...props}) {
|
||||
const classes = useStyles();
|
||||
|
||||
const previewSrc = useMemo(()=>(props.options?.find((o)=>o.value==props.value)?.preview_src), [props.value]);
|
||||
|
||||
return (
|
||||
|
@ -30,7 +24,7 @@ export default function SelectThemes({onChange, ...props}) {
|
|||
<Grid item lg={12} md={12} sm={12} xs={12}>
|
||||
<InputSelect ref={props.inputRef} onChange={onChange} {...props} />
|
||||
</Grid>
|
||||
<Grid item lg={12} md={12} sm={12} xs={12} className={classes.preview}>
|
||||
<Grid item lg={12} md={12} sm={12} xs={12} sx={{paddingTop: 10}}>
|
||||
<img className='img-fluid mx-auto d-block border' src={previewSrc} alt={gettext('Preview not available...')} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
|
|
@ -1,26 +1,27 @@
|
|||
import React from 'react';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
import { isMac } from '../keyboard_shortcuts';
|
||||
import _ from 'lodash';
|
||||
import CustomPropTypes from '../custom_prop_types';
|
||||
import gettext from 'sources/gettext';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
shortcutTitle: {
|
||||
|
||||
const Root = styled('div')(({theme}) => ({
|
||||
'& .ShortcutTitle-title': {
|
||||
width: '100%',
|
||||
textAlign: 'center',
|
||||
},
|
||||
shortcut: {
|
||||
'& .ShortcutTitle-shortcut': {
|
||||
justifyContent: 'center',
|
||||
marginTop: '0.125rem',
|
||||
display: 'flex',
|
||||
},
|
||||
key: {
|
||||
padding: '0 0.25rem',
|
||||
border: `1px solid ${theme.otherVars.borderColor}`,
|
||||
marginRight: '0.125rem',
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
'& .ShortcutTitle-key': {
|
||||
padding: '0 0.25rem',
|
||||
border: `1px solid ${theme.otherVars.borderColor}`,
|
||||
marginRight: '0.125rem',
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
|
@ -67,17 +68,17 @@ export function shortcutToString(shortcut, accesskey=null, asArray=false) {
|
|||
|
||||
/* The tooltip content to show shortcut details */
|
||||
export default function ShortcutTitle({title, shortcut, accesskey}) {
|
||||
const classes = useStyles();
|
||||
|
||||
let keys = shortcutToString(shortcut, accesskey, true);
|
||||
return (
|
||||
<>
|
||||
<div className={classes.shortcutTitle}>{title}</div>
|
||||
<div className={classes.shortcut}>
|
||||
(<Root>
|
||||
<div className='ShortcutTitle-title'>{title}</div>
|
||||
<div className='ShortcutTitle-shortcut'>
|
||||
{keys.map((key)=>{
|
||||
return <div key={key} className={classes.key}>{key}</div>;
|
||||
return <div key={key} className='ShortcutTitle-key'>{key}</div>;
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
</Root>)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,28 +9,25 @@
|
|||
|
||||
import React from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import clsx from 'clsx';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
import CustomPropTypes from '../custom_prop_types';
|
||||
|
||||
export const tabPanelStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
...theme.mixins.tabPanel,
|
||||
},
|
||||
content: {
|
||||
|
||||
const StyledBox = styled(Box)(({theme})=>({
|
||||
...theme.mixins.tabPanel,
|
||||
'& .TabPanel-content':{
|
||||
height: '100%',
|
||||
}
|
||||
}));
|
||||
|
||||
/* Material UI does not have any tabpanel component, we create one for us */
|
||||
export default function TabPanel({children, classNameRoot, className, value, index, ...props}) {
|
||||
const classes = tabPanelStyles();
|
||||
const active = value === index;
|
||||
return (
|
||||
<Box className={clsx(classes.root, classNameRoot)} component="div" hidden={!active} data-test="tabpanel" {...props}>
|
||||
<Box className={clsx(classes.content, className)}>{children}</Box>
|
||||
</Box>
|
||||
<StyledBox className={[classNameRoot].join(' ')} component="div" hidden={!active} data-test="tabpanel" {...props}>
|
||||
<Box className={['TabPanel-content', className].join(' ')}>{children}</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
76
web/pgadmin/static/js/components/Table.jsx
Normal file
76
web/pgadmin/static/js/components/Table.jsx
Normal file
|
@ -0,0 +1,76 @@
|
|||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import CustomPropTypes from '../custom_prop_types';
|
||||
|
||||
const StyledTable = styled('table')(({theme})=>({
|
||||
borderSpacing: 0,
|
||||
width: '100%',
|
||||
overflow: 'auto',
|
||||
backgroundColor: theme.otherVars.tableBg,
|
||||
border: '1px solid '+theme.otherVars.borderColor,
|
||||
'& tbody td, & thead th': {
|
||||
margin: 0,
|
||||
padding: theme.spacing(0.5),
|
||||
border: '1px solid '+theme.otherVars.borderColor,
|
||||
borderBottom: 'none',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
userSelect: 'text',
|
||||
maxWidth: '250px',
|
||||
'&:first-of-type':{
|
||||
borderLeft: 'none',
|
||||
},
|
||||
},
|
||||
'& thead tr:first-of-type th': {
|
||||
borderTop: 'none',
|
||||
},
|
||||
'& tbody tr:last-of-type': {
|
||||
'&:hover td': {
|
||||
borderBottomColor: theme.palette.primary.main,
|
||||
},
|
||||
'& td': {
|
||||
borderBottomColor: theme.otherVars.borderColor,
|
||||
}
|
||||
},
|
||||
'& th': {
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
padding: theme.spacing(1, 0.5),
|
||||
textAlign: 'left',
|
||||
},
|
||||
'& tbody > tr': {
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
'& td': {
|
||||
borderBottom: '1px solid '+theme.palette.primary.main,
|
||||
borderTop: '1px solid '+theme.palette.primary.main,
|
||||
},
|
||||
'&:last-of-type td': {
|
||||
borderBottomColor: theme.palette.primary.main,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
);
|
||||
|
||||
export default function Table({children, classNameRoot, ...props}) {
|
||||
return (
|
||||
<StyledTable className={[classNameRoot].join(' ')} {...props}>{children}</StyledTable>
|
||||
);
|
||||
}
|
||||
|
||||
Table.propTypes = {
|
||||
children: CustomPropTypes.children,
|
||||
classNameRoot: CustomPropTypes.className
|
||||
};
|
|
@ -8,8 +8,8 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import DataGridView, { DataGridHeader } from '../SchemaView/DataGridView';
|
||||
import SchemaView, { SCHEMA_STATE_ACTIONS } from '../SchemaView';
|
||||
import { DefaultButton } from '../components/Buttons';
|
||||
|
@ -18,22 +18,22 @@ import PropTypes from 'prop-types';
|
|||
import CustomPropTypes from '../custom_prop_types';
|
||||
import _ from 'lodash';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
formBorder: {
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
'& .DataGridViewWithHeaderForm-border': {
|
||||
...theme.mixins.panelBorder,
|
||||
borderBottom: 0,
|
||||
'& .DataGridViewWithHeaderForm-body': {
|
||||
padding: '0.25rem',
|
||||
'& .DataGridViewWithHeaderForm-addBtn': {
|
||||
marginLeft: 'auto',
|
||||
}
|
||||
},
|
||||
},
|
||||
form: {
|
||||
padding: '0.25rem',
|
||||
},
|
||||
addBtn: {
|
||||
marginLeft: 'auto',
|
||||
}
|
||||
}));
|
||||
|
||||
export default function DataGridViewWithHeaderForm(props) {
|
||||
let {containerClassName, headerSchema, headerVisible, ...otherProps} = props;
|
||||
const classes = useStyles();
|
||||
|
||||
const headerFormData = useRef({});
|
||||
const schemaRef = useRef(otherProps.schema);
|
||||
const [addDisabled, setAddDisabled] = useState(true);
|
||||
|
@ -61,10 +61,10 @@ export default function DataGridViewWithHeaderForm(props) {
|
|||
|
||||
headerVisible = headerVisible && evalFunc(null, headerVisible, state);
|
||||
return (
|
||||
<Box className={containerClassName}>
|
||||
<Box className={classes.formBorder}>
|
||||
<StyledBox className={containerClassName}>
|
||||
<Box className='DataGridViewWithHeaderForm-border'>
|
||||
{props.label && <DataGridHeader label={props.label} />}
|
||||
{headerVisible && <Box className={classes.form}>
|
||||
{headerVisible && <Box className='DataGridViewWithHeaderForm-body'>
|
||||
<SchemaView
|
||||
formType={'dialog'}
|
||||
getInitData={()=>Promise.resolve({})}
|
||||
|
@ -80,12 +80,12 @@ export default function DataGridViewWithHeaderForm(props) {
|
|||
resetKey={headerFormResetKey}
|
||||
/>
|
||||
<Box display="flex">
|
||||
<DefaultButton className={classes.addBtn} onClick={onAddClick} disabled={addDisabled}>Add</DefaultButton>
|
||||
<DefaultButton className='DataGridViewWithHeaderForm-addBtn' onClick={onAddClick} disabled={addDisabled}>Add</DefaultButton>
|
||||
</Box>
|
||||
</Box>}
|
||||
</Box>
|
||||
<DataGridView {...otherProps} label="" canAdd={false}/>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,7 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import { Box, Dialog, DialogContent, DialogTitle, Paper } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { getEpoch } from 'sources/utils';
|
||||
import { DefaultButton, PgIconButton, PrimaryButton } from '../components/Buttons';
|
||||
import Draggable from 'react-draggable';
|
||||
|
@ -22,38 +20,38 @@ import HTMLReactParser from 'html-react-parser';
|
|||
import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
|
||||
import { Rnd } from 'react-rnd';
|
||||
import { ExpandDialogIcon, MinimizeDialogIcon } from '../components/ExternalIcon';
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
export const ModalContext = React.createContext({});
|
||||
const MIN_HEIGHT = 190;
|
||||
const MIN_WIDTH = 500;
|
||||
|
||||
export function useModal() {
|
||||
return React.useContext(ModalContext);
|
||||
}
|
||||
const useAlertStyles = makeStyles((theme) => ({
|
||||
footer: {
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
'& .Alert-footer': {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
padding: '0.5rem',
|
||||
...theme.mixins.panelBorder.top,
|
||||
},
|
||||
margin: {
|
||||
'& .Alert-margin': {
|
||||
marginLeft: '0.25rem',
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
export function useModal() {
|
||||
return React.useContext(ModalContext);
|
||||
}
|
||||
|
||||
function AlertContent({ text, confirm, okLabel = gettext('OK'), cancelLabel = gettext('Cancel'), onOkClick, onCancelClick }) {
|
||||
const classes = useAlertStyles();
|
||||
return (
|
||||
<Box display="flex" flexDirection="column" height="100%">
|
||||
<StyledBox display="flex" flexDirection="column" height="100%">
|
||||
<Box flexGrow="1" p={2}>{typeof (text) == 'string' ? HTMLReactParser(text) : text}</Box>
|
||||
<Box className={classes.footer}>
|
||||
<Box className='Alert-footer'>
|
||||
{confirm &&
|
||||
<DefaultButton startIcon={<CloseIcon />} onClick={onCancelClick} >{cancelLabel}</DefaultButton>
|
||||
}
|
||||
<PrimaryButton className={classes.margin} startIcon={<CheckRoundedIcon />} onClick={onOkClick} autoFocus={true} >{okLabel}</PrimaryButton>
|
||||
<PrimaryButton className='Alert-margin' startIcon={<CheckRoundedIcon />} onClick={onOkClick} autoFocus={true} >{okLabel}</PrimaryButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
AlertContent.propTypes = {
|
||||
|
@ -143,17 +141,17 @@ ModalProvider.propTypes = {
|
|||
children: CustomPropTypes.children,
|
||||
};
|
||||
|
||||
const dialogStyle = makeStyles((theme) => ({
|
||||
dialog: {
|
||||
const StyledRnd = styled(Rnd)(({theme}) => ({
|
||||
'&.Dialog-content': {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
border: '1px solid ' + theme.otherVars.inputBorderColor,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
},
|
||||
fullScreen: {
|
||||
'&.Dialog-fullScreen': {
|
||||
transform: 'none !important'
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
|
@ -166,7 +164,6 @@ function setEnableResizing(props, resizeable) {
|
|||
}
|
||||
|
||||
function PaperComponent({minHeight, minWidth, ...props}) {
|
||||
let classes = dialogStyle();
|
||||
let [dialogPosition, setDialogPosition] = useState(null);
|
||||
let resizeable = checkIsResizable(props);
|
||||
|
||||
|
@ -179,9 +176,9 @@ function PaperComponent({minHeight, minWidth, ...props}) {
|
|||
|
||||
return (
|
||||
props.isresizeable == 'true' ?
|
||||
<Rnd
|
||||
<StyledRnd
|
||||
size={props.isfullscreen == 'true' && { width: '100%', height: '100%' }}
|
||||
className={clsx(classes.dialog, props.isfullscreen == 'true' ? classes.fullScreen : '')}
|
||||
className={'Dialog-content ' + ( props.isfullscreen == 'true' ? 'Dialog-fullScreen' : '')}
|
||||
default={{
|
||||
x: x_position,
|
||||
y: y_position,
|
||||
|
@ -210,7 +207,7 @@ function PaperComponent({minHeight, minWidth, ...props}) {
|
|||
dragHandleClassName="modal-drag-area"
|
||||
>
|
||||
<Paper {...props} style={{ width: '100%', height: '100%', maxHeight: '100%', maxWidth: '100%' }} />
|
||||
</Rnd>
|
||||
</StyledRnd>
|
||||
:
|
||||
<Draggable cancel={'[class*="MuiDialogContent-root"]'}>
|
||||
<Paper {...props} style={{ minWidth: '600px' }} />
|
||||
|
@ -227,19 +224,15 @@ PaperComponent.propTypes = {
|
|||
minHeight: PropTypes.number,
|
||||
};
|
||||
|
||||
export const useModalStyles = makeStyles((theme) => ({
|
||||
container: {
|
||||
const StyleDialog = styled(Dialog)(({theme}) => ({
|
||||
'& .Modal-container': {
|
||||
backgroundColor: theme.palette.background.default
|
||||
},
|
||||
|
||||
titleBar: {
|
||||
'& .Modal-titleBar': {
|
||||
display: 'flex',
|
||||
flexGrow: 1
|
||||
},
|
||||
title: {
|
||||
flexGrow: 1
|
||||
},
|
||||
icon: {
|
||||
'& .Modal-icon': {
|
||||
fill: 'currentColor',
|
||||
width: '1em',
|
||||
height: '1em',
|
||||
|
@ -249,16 +242,13 @@ export const useModalStyles = makeStyles((theme) => ({
|
|||
flexShrink: 0,
|
||||
userSelect: 'none',
|
||||
},
|
||||
footer: {
|
||||
'& .Modal-footer': {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
padding: '0.5rem',
|
||||
...theme.mixins.panelBorder?.top,
|
||||
},
|
||||
margin: {
|
||||
marginLeft: '0.25rem',
|
||||
},
|
||||
iconButtonStyle: {
|
||||
'& .Modal-iconButtonStyle': {
|
||||
marginLeft: 'auto',
|
||||
marginRight: '4px'
|
||||
},
|
||||
|
@ -266,7 +256,6 @@ export const useModalStyles = makeStyles((theme) => ({
|
|||
|
||||
function ModalContainer({ id, title, content, dialogHeight, dialogWidth, onClose, fullScreen = false, isFullWidth = false, showFullScreen = false, isResizeable = false, minHeight = MIN_HEIGHT, minWidth = MIN_WIDTH, showTitle=true }) {
|
||||
let useModalRef = useModal();
|
||||
const classes = useModalStyles();
|
||||
let closeModal = (_e, reason) => {
|
||||
if(reason == 'backdropClick' && showTitle) {
|
||||
return;
|
||||
|
@ -279,7 +268,7 @@ function ModalContainer({ id, title, content, dialogHeight, dialogWidth, onClose
|
|||
const [isFullScreen, setIsFullScreen] = useState(fullScreen);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
<StyleDialog
|
||||
open={true}
|
||||
onClose={closeModal}
|
||||
PaperComponent={PaperComponent}
|
||||
|
@ -290,15 +279,15 @@ function ModalContainer({ id, title, content, dialogHeight, dialogWidth, onClose
|
|||
>
|
||||
{ showTitle &&
|
||||
<DialogTitle className='modal-drag-area'>
|
||||
<Box className={classes.titleBar}>
|
||||
<Box className={classes.title} marginRight="0.25rem" >{title}</Box>
|
||||
<Box className='Modal-titleBar'>
|
||||
<Box sx={{ marginRight:'0.25rem', flexGrow: 1}}>{title}</Box>
|
||||
{
|
||||
showFullScreen && !isFullScreen &&
|
||||
<Box className={classes.iconButtonStyle}><PgIconButton title={gettext('Maximize')} icon={<ExpandDialogIcon className={classes.icon} />} size="xs" noBorder onClick={() => { setIsFullScreen(!isFullScreen); }} /></Box>
|
||||
<Box className='Modal-iconButtonStyle'><PgIconButton title={gettext('Maximize')} icon={<ExpandDialogIcon className='Modal-icon' />} size="xs" noBorder onClick={() => { setIsFullScreen(!isFullScreen); }} /></Box>
|
||||
}
|
||||
{
|
||||
showFullScreen && isFullScreen &&
|
||||
<Box className={classes.iconButtonStyle}><PgIconButton title={gettext('Minimize')} icon={<MinimizeDialogIcon className={classes.icon} />} size="xs" noBorder onClick={() => { setIsFullScreen(!isFullScreen); }} /></Box>
|
||||
<Box className='Modal-iconButtonStyle'><PgIconButton title={gettext('Minimize')} icon={<MinimizeDialogIcon className='Modal-icon' />} size="xs" noBorder onClick={() => { setIsFullScreen(!isFullScreen); }} /></Box>
|
||||
}
|
||||
|
||||
<Box marginLeft="auto"><PgIconButton title={gettext('Close')} icon={<CloseIcon />} size="xs" noBorder onClick={closeModal} /></Box>
|
||||
|
@ -308,7 +297,7 @@ function ModalContainer({ id, title, content, dialogHeight, dialogWidth, onClose
|
|||
<DialogContent height="100%">
|
||||
{useMemo(()=>{ return content(closeModal); }, [])}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</StyleDialog>
|
||||
);
|
||||
}
|
||||
ModalContainer.propTypes = {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import { SnackbarProvider, SnackbarContent } from 'notistack';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import {Box} from '@mui/material';
|
||||
import CloseIcon from '@mui/icons-material/CloseRounded';
|
||||
import { DefaultButton, PrimaryButton } from '../components/Buttons';
|
||||
|
@ -23,6 +23,19 @@ import _ from 'lodash';
|
|||
import { useModal } from './ModalProvider';
|
||||
import { parseApiError } from '../api_instance';
|
||||
|
||||
|
||||
const Root = styled('div')(({theme}) => ({
|
||||
'& .Notifier-footer': {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
padding: '0.5rem',
|
||||
...theme.mixins.panelBorder.top,
|
||||
'& .Notifier-margin': {
|
||||
marginLeft: '0.25rem',
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
const AUTO_HIDE_DURATION = 3000; // In milliseconds
|
||||
|
||||
export const FinalNotifyContent = React.forwardRef(({children}, ref) => {
|
||||
|
@ -33,27 +46,15 @@ FinalNotifyContent.propTypes = {
|
|||
children: CustomPropTypes.children,
|
||||
};
|
||||
|
||||
const useModalStyles = makeStyles((theme)=>({
|
||||
footer: {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
padding: '0.5rem',
|
||||
...theme.mixins.panelBorder.top,
|
||||
},
|
||||
margin: {
|
||||
marginLeft: '0.25rem',
|
||||
},
|
||||
}));
|
||||
function AlertContent({text, confirm, okLabel=gettext('OK'), cancelLabel=gettext('Cancel'), onOkClick, onCancelClick}) {
|
||||
const classes = useModalStyles();
|
||||
return (
|
||||
<Box display="flex" flexDirection="column" height="100%">
|
||||
<Box flexGrow="1" p={2}>{HTMLReactParser(text)}</Box>
|
||||
<Box className={classes.footer}>
|
||||
<Box className='Notifier-footer'>
|
||||
{confirm &&
|
||||
<DefaultButton startIcon={<CloseIcon />} onClick={onCancelClick} >{cancelLabel}</DefaultButton>
|
||||
}
|
||||
<PrimaryButton className={classes.margin} startIcon={<CheckRoundedIcon />} onClick={onOkClick} autoFocus={true} >{okLabel}</PrimaryButton>
|
||||
<PrimaryButton className='Notifier-margin' startIcon={<CheckRoundedIcon />} onClick={onOkClick} autoFocus={true} >{okLabel}</PrimaryButton>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
@ -197,23 +198,25 @@ export function NotifierProvider({ pgAdmin, pgWindow, getInstance, children, onR
|
|||
// if open in a window, then create your own Snackbar
|
||||
if(window.self == window.top) {
|
||||
return (
|
||||
<SnackbarProvider
|
||||
maxSnack={30}
|
||||
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
|
||||
ref={(obj)=>{
|
||||
pgAdmin.Browser.notifier = new Notifier(modal, new SnackbarNotifier(obj));
|
||||
getInstance?.(pgAdmin.Browser.notifier);
|
||||
onReady?.();
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SnackbarProvider>
|
||||
<Root>
|
||||
<SnackbarProvider
|
||||
maxSnack={30}
|
||||
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
|
||||
ref={(obj)=>{
|
||||
pgAdmin.Browser.notifier = new Notifier(modal, new SnackbarNotifier(obj));
|
||||
getInstance?.(pgAdmin.Browser.notifier);
|
||||
onReady?.();
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SnackbarProvider>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
(<Root>
|
||||
{children}
|
||||
</>
|
||||
</Root>)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import React from 'react';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import clsx from 'clsx';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import FastForwardIcon from '@mui/icons-material/FastForward';
|
||||
import FastRewindIcon from '@mui/icons-material/FastRewind';
|
||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||
|
@ -21,119 +20,110 @@ import { Box } from '@mui/material';
|
|||
import gettext from 'sources/gettext';
|
||||
import Loader from 'sources/components/Loader';
|
||||
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
backgroundColor: theme.palette.background.default + ' !important',
|
||||
'& .Wizard-root': {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
|
||||
const useStyles = makeStyles((theme) =>
|
||||
({
|
||||
wizardBase: {
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
backgroundColor: theme.palette.background.default
|
||||
},
|
||||
root: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
minHeight: 0
|
||||
},
|
||||
rightPanel: {
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
flexBasis: '75%',
|
||||
overflow: 'auto',
|
||||
height: '100%',
|
||||
minHeight: '100px'
|
||||
},
|
||||
leftPanel: {
|
||||
display: 'flex',
|
||||
flexBasis: '25%',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
borderRight: '1px solid',
|
||||
...theme.mixins.panelBorder.right,
|
||||
},
|
||||
label: {
|
||||
display: 'inline-block',
|
||||
position: 'relative',
|
||||
paddingLeft: '0.5rem',
|
||||
flexBasis: '70%'
|
||||
},
|
||||
labelArrow: {
|
||||
display: 'inline-block',
|
||||
position: 'relative',
|
||||
flexBasis: '30%'
|
||||
},
|
||||
labelDone: {
|
||||
display: 'inline-block',
|
||||
position: 'relative',
|
||||
flexBasis: '30%',
|
||||
color: theme.otherVars.activeStepBg + ' !important',
|
||||
padding: '4px'
|
||||
},
|
||||
stepLabel: {
|
||||
padding: '1em',
|
||||
paddingRight: 0
|
||||
},
|
||||
active: {
|
||||
fontWeight: 600
|
||||
},
|
||||
activeIndex: {
|
||||
backgroundColor: theme.otherVars.activeStepBg + ' !important',
|
||||
color: theme.otherVars.activeStepFg + ' !important'
|
||||
},
|
||||
stepIndex: {
|
||||
padding: '0.5em 1em ',
|
||||
height: '2.5em',
|
||||
borderRadius: '2em',
|
||||
backgroundColor: theme.otherVars.stepBg,
|
||||
color: theme.otherVars.stepFg,
|
||||
display: 'inline-block',
|
||||
flex: 0.5,
|
||||
|
||||
},
|
||||
wizard: {
|
||||
'& .Wizard-body': {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
minHeight: 100,
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
'& .Wizard-rightPanel': {
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
flexBasis: '75%',
|
||||
overflow: 'auto',
|
||||
height: '100%',
|
||||
minHeight: '100px',
|
||||
'& .Wizard-stepDefaultStyle': {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: '8px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
'& .Wizard-hidden': {
|
||||
display: 'none',
|
||||
}
|
||||
},
|
||||
'& .Wizard-active': {
|
||||
fontWeight: 600
|
||||
},
|
||||
'& .Wizard-leftPanel': {
|
||||
display: 'flex',
|
||||
flexBasis: '25%',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
borderRight: '1px solid',
|
||||
...theme.mixins.panelBorder.right,
|
||||
'& .Wizard-active': {
|
||||
fontWeight: 600
|
||||
},
|
||||
'& .Wizard-stepLabel': {
|
||||
padding: '1em',
|
||||
paddingRight: 0,
|
||||
'& .Wizard-stepIndex': {
|
||||
padding: '0.5em 1em ',
|
||||
height: '2.5em',
|
||||
borderRadius: '2em',
|
||||
backgroundColor: theme.otherVars.stepBg,
|
||||
color: theme.otherVars.stepFg,
|
||||
display: 'inline-block',
|
||||
flex: 0.5,
|
||||
'& .Wizard-activeIndex': {
|
||||
backgroundColor: theme.otherVars.activeStepBg + ' !important',
|
||||
color: theme.otherVars.activeStepFg + ' !important'
|
||||
},
|
||||
},
|
||||
'& .Wizard-label': {
|
||||
display: 'inline-block',
|
||||
position: 'relative',
|
||||
paddingLeft: '0.5rem',
|
||||
flexBasis: '70%'
|
||||
},
|
||||
'& .Wizard-labelArrow': {
|
||||
display: 'inline-block',
|
||||
position: 'relative',
|
||||
flexBasis: '30%'
|
||||
},
|
||||
'& .Wizard-labelDone': {
|
||||
display: 'inline-block',
|
||||
position: 'relative',
|
||||
flexBasis: '30%',
|
||||
color: theme.otherVars.activeStepBg + ' !important',
|
||||
padding: '4px'
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wizardFooter: {
|
||||
borderTop: `1px solid ${theme.otherVars.inputBorderColor} !important`,
|
||||
padding: '0.5rem',
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
background: theme.otherVars.headerBg,
|
||||
zIndex: 999,
|
||||
},
|
||||
backButton: {
|
||||
marginRight: theme.spacing(1),
|
||||
},
|
||||
instructions: {
|
||||
marginTop: theme.spacing(1),
|
||||
marginBottom: theme.spacing(1),
|
||||
},
|
||||
actionBtn: {
|
||||
},
|
||||
'& .Wizard-footer': {
|
||||
borderTop: `1px solid ${theme.otherVars.inputBorderColor} !important`,
|
||||
padding: '0.5rem',
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
background: theme.otherVars.headerBg,
|
||||
zIndex: 999,
|
||||
'& .Wizard-actionBtn': {
|
||||
alignItems: 'flex-start',
|
||||
'& .Wizard-buttonMargin': {
|
||||
marginLeft: '0.5em'
|
||||
},
|
||||
},
|
||||
buttonMargin: {
|
||||
marginLeft: '0.5em'
|
||||
},
|
||||
stepDefaultStyle: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: '8px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
hidden: {
|
||||
display: 'none',
|
||||
}
|
||||
}),
|
||||
);
|
||||
},
|
||||
}));
|
||||
|
||||
function Wizard({ stepList, onStepChange, onSave, className, ...props }) {
|
||||
const classes = useStyles();
|
||||
|
||||
const [activeStep, setActiveStep] = React.useState(0);
|
||||
const steps = stepList && stepList.length > 0 ? stepList : [];
|
||||
const [disableNext, setDisableNext] = React.useState(false);
|
||||
|
@ -183,25 +173,25 @@ function Wizard({ stepList, onStepChange, onSave, className, ...props }) {
|
|||
|
||||
|
||||
return (
|
||||
<Box className={classes.wizardBase}>
|
||||
<div className={clsx(classes.root, props?.rootClass)}>
|
||||
<div className={clsx(classes.wizard, className)}>
|
||||
<Box className={classes.leftPanel}>
|
||||
<StyledBox>
|
||||
<div className={'Wizard-root ' + (props?.rootClass ? props.rootClass : '') }>
|
||||
<div className={'Wizard-body ' + className}>
|
||||
<Box className='Wizard-leftPanel'>
|
||||
{steps.map((label, index) => (
|
||||
<Box key={label} className={clsx(classes.stepLabel, index === activeStep ? classes.active : '')}>
|
||||
<Box className={clsx(classes.stepIndex, index === activeStep ? classes.activeIndex : '')}>{index + 1}</Box>
|
||||
<Box className={classes.label}>{label} </Box>
|
||||
<Box className={classes.labelArrow}>{index === activeStep ? <ChevronRightIcon /> : null}</Box>
|
||||
<Box className={classes.labelDone}>{index < activeStep ? <DoneIcon />: null}</Box>
|
||||
<Box key={label} className={'Wizard-stepLabel ' + (index === activeStep ? 'Wizard-active' : '')}>
|
||||
<Box className={'Wizard-stepIndex ' + (index === activeStep ? 'Wizard-activeIndex' : '')}>{index + 1}</Box>
|
||||
<Box className='Wizard-label'>{label} </Box>
|
||||
<Box className='Wizard-labelArrow'>{index === activeStep ? <ChevronRightIcon /> : null}</Box>
|
||||
<Box className='Wizard-labelDone'>{index < activeStep ? <DoneIcon />: null}</Box>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
<div className={clsx(classes.rightPanel, props.stepPanelCss)}>
|
||||
<div className={'Wizard-rightPanel ' + props.stepPanelCss}>
|
||||
<Loader message={props?.loaderText} />
|
||||
{
|
||||
React.Children.map(props.children, (child) => {
|
||||
return (
|
||||
<div className={clsx(classes.stepDefaultStyle, child.props.className, (child.props.stepId !== activeStep ? classes.hidden : ''))}>
|
||||
<div className={'Wizard-stepDefaultStyle ' + child.props.className + ' ' +(child.props.stepId !== activeStep ? 'Wizard-hidden' : '')}>
|
||||
{child}
|
||||
</div>
|
||||
);
|
||||
|
@ -210,24 +200,24 @@ function Wizard({ stepList, onStepChange, onSave, className, ...props }) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.wizardFooter}>
|
||||
<div className='Wizard-footer'>
|
||||
<Box>
|
||||
<PgIconButton data-test="dialog-help" onClick={() => props.onHelp()} icon={<HelpIcon />} title="Help for this dialog."
|
||||
disabled={props.disableDialogHelp} />
|
||||
</Box>
|
||||
<Box className={classes.actionBtn} marginLeft="auto">
|
||||
<DefaultButton onClick={handleBack} disabled={activeStep === 0} className={classes.buttonMargin} startIcon={<FastRewindIcon />}>
|
||||
<Box className='Wizard-actionBtn' marginLeft="auto">
|
||||
<DefaultButton onClick={handleBack} disabled={activeStep === 0} className='Wizard-buttonMargin' startIcon={<FastRewindIcon />}>
|
||||
{gettext('Back')}
|
||||
</DefaultButton>
|
||||
<DefaultButton onClick={() => handleNext()} className={classes.buttonMargin} startIcon={<FastForwardIcon />} disabled={activeStep == steps.length - 1 || disableNext}>
|
||||
<DefaultButton onClick={() => handleNext()} className='Wizard-buttonMargin' startIcon={<FastForwardIcon />} disabled={activeStep == steps.length - 1 || disableNext}>
|
||||
{gettext('Next')}
|
||||
</DefaultButton>
|
||||
<PrimaryButton className={classes.buttonMargin} startIcon={<CheckIcon />} disabled={activeStep !== (steps.length - 1) } onClick={onSave}>
|
||||
<PrimaryButton className='Wizard-buttonMargin' startIcon={<CheckIcon />} disabled={activeStep !== (steps.length - 1) } onClick={onSave}>
|
||||
{gettext('Finish')}
|
||||
</PrimaryButton>
|
||||
</Box>
|
||||
</div>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,11 +9,12 @@
|
|||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
|
||||
import DeleteSweepIcon from '@mui/icons-material/DeleteSweep';
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import BugReportRoundedIcon from '@mui/icons-material/BugReportRounded';
|
||||
import CloseSharpIcon from '@mui/icons-material/CloseSharp';
|
||||
|
||||
|
@ -32,44 +33,40 @@ import { BROWSER_PANELS } from '../../../../../browser/static/js/constants';
|
|||
import usePreferences from '../../../../../preferences/static/js/store';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme) =>
|
||||
({
|
||||
root: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
height: '100%',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
body: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
minHeight: 0,
|
||||
},
|
||||
actionBtn: {
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
height: '100%',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
overflow: 'hidden',
|
||||
'& .DebuggerArgument-body': {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
minHeight: 0,
|
||||
},
|
||||
'& .DebuggerArgument-footer': {
|
||||
borderTop: `1px solid ${theme.otherVars.inputBorderColor} !important`,
|
||||
padding: '0.5rem',
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
background: theme.otherVars.headerBg,
|
||||
'& .DebuggerArgument-actionBtn': {
|
||||
alignItems: 'flex-start',
|
||||
'& .DebuggerArgument-buttonMargin': {
|
||||
marginLeft: '0.5em'
|
||||
},
|
||||
'& .DebuggerArgument-debugBtn': {
|
||||
fontSize: '1.12rem !important',
|
||||
},
|
||||
},
|
||||
buttonMargin: {
|
||||
marginLeft: '0.5em'
|
||||
},
|
||||
debugBtn: {
|
||||
fontSize: '1.12rem !important',
|
||||
},
|
||||
footer: {
|
||||
borderTop: `1px solid ${theme.otherVars.inputBorderColor} !important`,
|
||||
padding: '0.5rem',
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
background: theme.otherVars.headerBg,
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
export default function DebuggerArgumentComponent({ debuggerInfo, restartDebug, isEdbProc, transId, pgTreeInfo, pgData, ...props }) {
|
||||
const classes = useStyles();
|
||||
|
||||
const debuggerArgsSchema = useRef(new DebuggerArgumentSchema());
|
||||
const api = getApiInstance();
|
||||
const debuggerArgsData = useRef([]);
|
||||
|
@ -808,8 +805,8 @@ export default function DebuggerArgumentComponent({ debuggerInfo, restartDebug,
|
|||
}
|
||||
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<Box className={classes.body}>
|
||||
<StyledBox>
|
||||
<Box className='DebuggerArgument-body'>
|
||||
{
|
||||
loadArgs > 0 &&
|
||||
<>
|
||||
|
@ -852,25 +849,24 @@ export default function DebuggerArgumentComponent({ debuggerInfo, restartDebug,
|
|||
</>
|
||||
}
|
||||
</Box>
|
||||
<Box className={classes.footer}>
|
||||
<Box className='DebuggerArgument-footer'>
|
||||
<Box>
|
||||
<DefaultButton className={classes.buttonMargin} onClick={() => { clearArgs(); }} startIcon={<DeleteSweepIcon onClick={() => { clearArgs(); }} />}>
|
||||
<DefaultButton className='DebuggerArgument-buttonMargin' onClick={() => { clearArgs(); }} startIcon={<DeleteSweepIcon onClick={() => { clearArgs(); }} />}>
|
||||
{gettext('Clear All')}
|
||||
</DefaultButton>
|
||||
</Box>
|
||||
<Box className={classes.actionBtn} marginLeft="auto">
|
||||
<DefaultButton className={classes.buttonMargin} onClick={() => { props.closeModal(); }} startIcon={<CloseSharpIcon onClick={() => { props.closeModal(); }} />}>
|
||||
<Box className='DebuggerArgument-actionBtn' marginLeft="auto">
|
||||
<DefaultButton className='DebuggerArgument-buttonMargin' onClick={() => { props.closeModal(); }} startIcon={<CloseSharpIcon onClick={() => { props.closeModal(); }} />}>
|
||||
{gettext('Cancel')}
|
||||
</DefaultButton>
|
||||
<PrimaryButton className={classes.buttonMargin} startIcon={<BugReportRoundedIcon className={classes.debugBtn} />}
|
||||
<PrimaryButton className='DebuggerArgument-buttonMargin' startIcon={<BugReportRoundedIcon className='DebuggerArgument-debugBtn' />}
|
||||
disabled={isDisableDebug}
|
||||
onClick={() => { startDebugging(); }}>
|
||||
{gettext('Debug')}
|
||||
</PrimaryButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import React, { useContext, useEffect, useMemo } from 'react';
|
||||
|
@ -23,14 +23,13 @@ import { usePgAdmin } from '../../../../../static/js/BrowserComponent';
|
|||
import { isShortcutValue, toCodeMirrorKey } from '../../../../../static/js/utils';
|
||||
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
sql: {
|
||||
const StyledCodeMirror = styled(CodeMirror)(()=>({
|
||||
'&.Query-sql': {
|
||||
height: '100%',
|
||||
}
|
||||
}));
|
||||
|
||||
export default function DebuggerEditor({ getEditor, params }) {
|
||||
const classes = useStyles();
|
||||
const editor = React.useRef();
|
||||
const eventBus = useContext(DebuggerEventsContext);
|
||||
const pgAdmin = usePgAdmin();
|
||||
|
@ -97,7 +96,7 @@ export default function DebuggerEditor({ getEditor, params }) {
|
|||
);
|
||||
|
||||
return (
|
||||
<CodeMirror
|
||||
<StyledCodeMirror
|
||||
currEditor={(obj) => {
|
||||
editor.current = obj;
|
||||
}}
|
||||
|
@ -105,7 +104,7 @@ export default function DebuggerEditor({ getEditor, params }) {
|
|||
onBreakPointChange={(line, on)=>{
|
||||
setBreakpoint(line, on ? 1 : 0);
|
||||
}}
|
||||
className={classes.sql}
|
||||
className='Query-sql'
|
||||
readonly={true}
|
||||
customKeyMap={shortcutOverrideKeys}
|
||||
breakpoint
|
||||
|
|
|
@ -1,34 +1,24 @@
|
|||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React from 'react';
|
||||
import { DebuggerEventsContext } from './DebuggerComponent';
|
||||
import { DEBUGGER_EVENTS } from '../DebuggerConstants';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
whiteSpace: 'pre-wrap',
|
||||
fontFamily: '"Source Code Pro", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
|
||||
padding: '5px 10px',
|
||||
overflow: 'auto',
|
||||
height: '100%',
|
||||
fontSize: '12px',
|
||||
userSelect: 'text',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
...theme.mixins.fontSourceCode,
|
||||
}
|
||||
const StyledDiv = styled('div')(({theme}) => ({
|
||||
whiteSpace: 'pre-wrap',
|
||||
fontFamily: '"Source Code Pro", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
|
||||
padding: '5px 10px',
|
||||
overflow: 'auto',
|
||||
height: '100%',
|
||||
fontSize: '12px',
|
||||
userSelect: 'text',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
...theme.mixins.fontSourceCode,
|
||||
}));
|
||||
|
||||
export default function DebuggerMessages() {
|
||||
const classes = useStyles();
|
||||
|
||||
const [messageText, setMessageText] = React.useState('');
|
||||
const eventBus = React.useContext(DebuggerEventsContext);
|
||||
React.useEffect(()=>{
|
||||
|
@ -41,7 +31,5 @@ export default function DebuggerMessages() {
|
|||
});
|
||||
});
|
||||
}, []);
|
||||
return (
|
||||
<div className={classes.root} tabIndex="0" id='debugger-msg'>{messageText}</div>
|
||||
);
|
||||
return <StyledDiv tabIndex="0" id='debugger-msg'>{messageText}</StyledDiv>;
|
||||
}
|
||||
|
|
|
@ -8,12 +8,11 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import _ from 'lodash';
|
||||
import clsx from 'clsx';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import TableContainer from '@mui/material/TableContainer';
|
||||
|
||||
|
@ -21,32 +20,24 @@ import gettext from 'sources/gettext';
|
|||
|
||||
import { DebuggerEventsContext } from './DebuggerComponent';
|
||||
import { DEBUGGER_EVENTS } from '../DebuggerConstants';
|
||||
import { commonTableStyles } from '../../../../../static/js/Theme';
|
||||
import { InputText, InputDateTimePicker } from '../../../../../static/js/components/FormComponents';
|
||||
import Table from '../../../../../static/js/components/Table';
|
||||
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
table: {
|
||||
minWidth: 650,
|
||||
const StyledPaper = styled(Paper)(() => ({
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
overflow: 'auto',
|
||||
maxHeight: '100%',
|
||||
'& .LocalVariablesAndParams-container': {
|
||||
maxHeight: '100%',
|
||||
'& .LocalVariablesAndParams-cell': {
|
||||
textAlign: 'center'
|
||||
}
|
||||
},
|
||||
summaryContainer: {
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
overflow: 'auto',
|
||||
maxHeight: '100%'
|
||||
},
|
||||
container: {
|
||||
maxHeight: '100%'
|
||||
},
|
||||
cell: {
|
||||
textAlign: 'center'
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
export function LocalVariablesAndParams({ type }) {
|
||||
const classes = useStyles();
|
||||
const tableClasses = commonTableStyles();
|
||||
|
||||
const eventBus = React.useContext(DebuggerEventsContext);
|
||||
const [variablesData, setVariablesData] = useState([]);
|
||||
const preValue = React.useRef({});
|
||||
|
@ -104,9 +95,9 @@ export function LocalVariablesAndParams({ type }) {
|
|||
};
|
||||
|
||||
return (
|
||||
<Paper variant="outlined" elevation={0} className={classes.summaryContainer}>
|
||||
<TableContainer className={classes.container}>
|
||||
<table className={clsx(tableClasses.table)} aria-label="sticky table">
|
||||
<StyledPaper variant="outlined" elevation={0}>
|
||||
<TableContainer className='LocalVariablesAndParams-container'>
|
||||
<Table aria-label="sticky table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{gettext('Name')}</th>
|
||||
|
@ -151,15 +142,15 @@ export function LocalVariablesAndParams({ type }) {
|
|||
))}
|
||||
{
|
||||
variablesData.length == 0 &&
|
||||
<tr key={_.uniqueId('c')} className={classes.cell}>
|
||||
<tr key={_.uniqueId('c')} className='LocalVariablesAndParams-cell'>
|
||||
<td colSpan={3} >{gettext('No data found')}</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Paper>
|
||||
</StyledPaper>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,32 +7,23 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import _ from 'lodash';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import Paper from '@mui/material/Paper';
|
||||
|
||||
import { DebuggerEventsContext } from './DebuggerComponent';
|
||||
import { DEBUGGER_EVENTS } from '../DebuggerConstants';
|
||||
import { commonTableStyles } from '../../../../../static/js/Theme';
|
||||
import Table from '../../../../../static/js/components/Table';
|
||||
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
table: {
|
||||
minWidth: 650,
|
||||
},
|
||||
summaryContainer: {
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
overflow: 'auto',
|
||||
}
|
||||
const StyledPaper = styled(Paper)(() => ({
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
overflow: 'auto',
|
||||
}));
|
||||
|
||||
export function Results() {
|
||||
const classes = useStyles();
|
||||
const tableClasses = commonTableStyles();
|
||||
|
||||
const eventBus = React.useContext(DebuggerEventsContext);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
const [columns, setColumns] = useState([]);
|
||||
|
@ -43,8 +34,8 @@ export function Results() {
|
|||
});
|
||||
}, []);
|
||||
return (
|
||||
<Paper variant="outlined" elevation={0} className={classes.summaryContainer}>
|
||||
<table className={clsx(tableClasses.table)}>
|
||||
<StyledPaper variant="outlined" elevation={0}>
|
||||
<Table>
|
||||
<thead>
|
||||
<tr key={_.uniqueId('c')}>
|
||||
{
|
||||
|
@ -65,7 +56,7 @@ export function Results() {
|
|||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</Paper>
|
||||
</Table>
|
||||
</StyledPaper>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,39 +8,31 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import _ from 'lodash';
|
||||
import clsx from 'clsx';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import gettext from 'sources/gettext';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import TableContainer from '@mui/material/TableContainer';
|
||||
import Paper from '@mui/material/Paper';
|
||||
|
||||
import { DebuggerEventsContext } from './DebuggerComponent';
|
||||
import { DEBUGGER_EVENTS } from '../DebuggerConstants';
|
||||
import { commonTableStyles } from '../../../../../static/js/Theme';
|
||||
import { InputText } from '../../../../../static/js/components/FormComponents';
|
||||
import Table from '../../../../../static/js/components/Table';
|
||||
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
table: {
|
||||
minWidth: 650,
|
||||
},
|
||||
summaryContainer: {
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
overflow: 'auto',
|
||||
maxHeight: '100%'
|
||||
},
|
||||
container: {
|
||||
const StyledPaper = styled(Paper)(() => ({
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
overflow: 'auto',
|
||||
maxHeight: '100%',
|
||||
'& .Stack-container': {
|
||||
maxHeight: '100%'
|
||||
}
|
||||
}));
|
||||
|
||||
export function Stack() {
|
||||
const classes = useStyles();
|
||||
const tableClasses = commonTableStyles();
|
||||
|
||||
const eventBus = React.useContext(DebuggerEventsContext);
|
||||
const [stackData, setStackData] = useState([]);
|
||||
const [disableFrameSelection, setDisableFrameSelection] = useState(false);
|
||||
|
@ -54,9 +46,9 @@ export function Stack() {
|
|||
});
|
||||
}, []);
|
||||
return (
|
||||
<Paper variant="outlined" elevation={0} className={classes.summaryContainer}>
|
||||
<TableContainer className={classes.container}>
|
||||
<table className={clsx(tableClasses.table)} aria-label="sticky table">
|
||||
<StyledPaper variant="outlined" elevation={0}>
|
||||
<TableContainer className='Stack-container'>
|
||||
<Table aria-label="sticky table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{gettext('Name')}</th>
|
||||
|
@ -77,8 +69,8 @@ export function Stack() {
|
|||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Paper>
|
||||
</StyledPaper>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
|
||||
import React, { useCallback, useContext, useEffect, useState } from 'react';
|
||||
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import FormatIndentIncreaseIcon from '@mui/icons-material/FormatIndentIncrease';
|
||||
import FormatIndentDecreaseIcon from '@mui/icons-material/FormatIndentDecrease';
|
||||
import PlayCircleFilledWhiteIcon from '@mui/icons-material/PlayCircleFilledWhite';
|
||||
|
@ -28,20 +29,20 @@ import { DebuggerContext, DebuggerEventsContext } from './DebuggerComponent';
|
|||
import { DEBUGGER_EVENTS, MENUS } from '../DebuggerConstants';
|
||||
import { useKeyboardShortcuts } from '../../../../../static/js/custom_hooks';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
padding: '2px 4px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
backgroundColor: theme.otherVars.editorToolbarBg,
|
||||
flexWrap: 'wrap',
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
},
|
||||
|
||||
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
padding: '2px 4px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
backgroundColor: theme.otherVars.editorToolbarBg,
|
||||
flexWrap: 'wrap',
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
}));
|
||||
|
||||
export function ToolBar() {
|
||||
const classes = useStyles();
|
||||
|
||||
const debuggerCtx = useContext(DebuggerContext);
|
||||
const eventBus = useContext(DebuggerEventsContext);
|
||||
let preferences = debuggerCtx.preferences.debugger;
|
||||
|
@ -161,7 +162,7 @@ export function ToolBar() {
|
|||
], debuggerCtx.containerRef);
|
||||
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<StyledBox>
|
||||
<PgButtonGroup size="small">
|
||||
<PgIconButton data-test='step-in' title={gettext('Step into')} disabled={buttonsDisabled[MENUS.STEPINTO]} icon={<FormatIndentIncreaseIcon />} onClick={() => { stepInTODebugger(); }}
|
||||
shortcut={preferences?.btn_step_into} />
|
||||
|
@ -189,6 +190,6 @@ export function ToolBar() {
|
|||
<PgIconButton title={gettext('Reset layout')} icon={<RotateLeftRoundedIcon />}
|
||||
onClick={onResetLayout} />
|
||||
</PgButtonGroup>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
|
@ -8,13 +8,30 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import { DefaultButton, PgButtonGroup, PgIconButton } from '../../../../../../static/js/components/Buttons';
|
||||
import { Box, Tooltip, CircularProgress } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { ConnectedIcon, DisonnectedIcon } from '../../../../../../static/js/components/ExternalIcon';
|
||||
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
padding: '2px 4px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
backgroundColor: theme.otherVars.editorToolbarBg,
|
||||
flexWrap: 'wrap',
|
||||
'& .Status-connectionButton': {
|
||||
display: 'flex',
|
||||
width: '450px',
|
||||
backgroundColor: theme.palette.default.main,
|
||||
color: theme.palette.default.contrastText,
|
||||
border: '1px solid ' + theme.palette.default.borderColor,
|
||||
justifyContent: 'flex-start',
|
||||
}
|
||||
}));
|
||||
|
||||
export const STATUS = {
|
||||
CONNECTED: 1,
|
||||
DISCONNECTED: 2,
|
||||
|
@ -36,28 +53,9 @@ ConnectionStatusIcon.propTypes = {
|
|||
status: PropTypes.oneOf(Object.values(STATUS)).isRequired,
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
padding: '2px 4px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
backgroundColor: theme.otherVars.editorToolbarBg,
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
connectionButton: {
|
||||
display: 'flex',
|
||||
width: '450px',
|
||||
backgroundColor: theme.palette.default.main,
|
||||
color: theme.palette.default.contrastText,
|
||||
border: '1px solid ' + theme.palette.default.borderColor,
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
}));
|
||||
|
||||
/* The connection bar component */
|
||||
export default function ConnectionBar({status, bgcolor, fgcolor, title}) {
|
||||
const classes = useStyles();
|
||||
|
||||
const connTitle = useMemo(()=>{
|
||||
if(status == STATUS.CONNECTED) {
|
||||
return gettext('Connected');
|
||||
|
@ -70,14 +68,14 @@ export default function ConnectionBar({status, bgcolor, fgcolor, title}) {
|
|||
}
|
||||
}, [status]);
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<StyledBox>
|
||||
<PgButtonGroup size="small">
|
||||
<PgIconButton
|
||||
title={connTitle}
|
||||
icon={<ConnectionStatusIcon status={status} />}
|
||||
data-test="btn-conn-status"
|
||||
/>
|
||||
<DefaultButton className={classes.connectionButton} style={{backgroundColor: bgcolor, color: fgcolor}} data-test="btn-conn-title">
|
||||
<DefaultButton className='Status-connectionButton' style={{backgroundColor: bgcolor, color: fgcolor}} data-test="btn-conn-title">
|
||||
<Tooltip title={title}>
|
||||
<Box display="flex" width="100%">
|
||||
<Box textOverflow="ellipsis" overflow="hidden" marginRight="auto">
|
||||
|
@ -89,7 +87,7 @@ export default function ConnectionBar({status, bgcolor, fgcolor, title}) {
|
|||
</Tooltip>
|
||||
</DefaultButton>
|
||||
</PgButtonGroup>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ import ConfirmSaveContent from '../../../../../../static/js/Dialogs/ConfirmSaveC
|
|||
import Loader from '../../../../../../static/js/components/Loader';
|
||||
import { MainToolBar } from './MainToolBar';
|
||||
import { Box } from '@mui/material';
|
||||
import { withStyles } from '@mui/styles';
|
||||
import EventBus from '../../../../../../static/js/helpers/EventBus';
|
||||
import { ERD_EVENTS } from '../ERDConstants';
|
||||
import getApiInstance, { callFetch, parseApiError } from '../../../../../../static/js/api_instance';
|
||||
|
@ -35,6 +34,7 @@ import { openSocket, socketApiGet } from '../../../../../../static/js/socket_ins
|
|||
import { LAYOUT_EVENTS } from '../../../../../../static/js/helpers/Layout';
|
||||
import usePreferences from '../../../../../../preferences/static/js/store';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
/* Custom react-diagram action for keyboard events */
|
||||
export class KeyboardShortcutAction extends Action {
|
||||
|
@ -77,30 +77,30 @@ const getCanvasGrid = (theme)=>{
|
|||
return `url("data:image/svg+xml, %3Csvg width='100%25' viewBox='0 0 45 45' style='background-color:${erdCanvasBg}' height='100%25' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cpattern id='smallGrid' width='15' height='15' patternUnits='userSpaceOnUse'%3E%3Cpath d='M 15 0 L 0 0 0 15' fill='none' stroke='${erdGridColor}' stroke-width='0.5'/%3E%3C/pattern%3E%3Cpattern id='grid' width='45' height='45' patternUnits='userSpaceOnUse'%3E%3Crect width='100' height='100' fill='url(%23smallGrid)'/%3E%3Cpath d='M 100 0 L 0 0 0 100' fill='none' stroke='${erdGridColor}' stroke-width='1'/%3E%3C/pattern%3E%3C/defs%3E%3Crect width='100%25' height='100%25' fill='url(%23grid)' /%3E%3C/svg%3E%0A")`;
|
||||
};
|
||||
|
||||
const styles = ((theme)=>({
|
||||
diagramContainer: {
|
||||
const StyledBox = styled(Box)(({theme})=>({
|
||||
'& .ERDTool-diagramContainer': {
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
'& .ERDTool-diagramCanvas': {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.otherVars.erdCanvasBg,
|
||||
backgroundImage: getCanvasGrid(theme),
|
||||
cursor: 'unset',
|
||||
flexGrow: 1,
|
||||
},
|
||||
},
|
||||
diagramCanvas: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.otherVars.erdCanvasBg,
|
||||
backgroundImage: getCanvasGrid(theme),
|
||||
cursor: 'unset',
|
||||
flexGrow: 1,
|
||||
},
|
||||
html2canvasReset: {
|
||||
'& .ERDTool-html2canvasReset': {
|
||||
backgroundImage: 'none !important',
|
||||
overflow: 'auto !important',
|
||||
}
|
||||
}));
|
||||
|
||||
/* The main body container for the ERD */
|
||||
class ERDTool extends React.Component {
|
||||
export default class ERDTool extends React.Component {
|
||||
static contextType = ModalContext;
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -715,7 +715,7 @@ class ERDTool extends React.Component {
|
|||
* the canvas back to original state.
|
||||
* Code referred from - zoomToFitNodes function.
|
||||
*/
|
||||
this.diagramContainerRef.current?.classList.add(this.props.classes.html2canvasReset);
|
||||
this.diagramContainerRef.current?.classList.add('ERDTool-html2canvasReset');
|
||||
const margin = 10;
|
||||
let nodesRect = this.diagram.getEngine().getBoundingNodesRect(this.diagram.getModel().getNodes());
|
||||
let linksRect = this.diagram.getBoundingLinksRect();
|
||||
|
@ -788,7 +788,7 @@ class ERDTool extends React.Component {
|
|||
pgAdmin.Browser.notifier.alert(gettext('Error'), msg);
|
||||
}).then(()=>{
|
||||
/* Revert back to the original CSS styles */
|
||||
this.diagramContainerRef.current.classList.remove(this.props.classes.html2canvasReset);
|
||||
this.diagramContainerRef.current.classList.remove('ERDTool-html2canvasReset');
|
||||
this.canvasEle.style.width = '';
|
||||
this.canvasEle.style.height = '';
|
||||
this.canvasEle.childNodes.forEach((ele)=>{
|
||||
|
@ -923,7 +923,7 @@ class ERDTool extends React.Component {
|
|||
this.erdDialogs.modal = this.context;
|
||||
|
||||
return (
|
||||
<Box ref={this.containerRef} height="100%" display="flex" flexDirection="column">
|
||||
<StyledBox ref={this.containerRef} height="100%" display="flex" flexDirection="column">
|
||||
<ConnectionBar status={this.state.conn_status} bgcolor={this.props.params.bgcolor}
|
||||
fgcolor={this.props.params.fgcolor} title={_.unescape(this.props.params.title)}/>
|
||||
<MainToolBar preferences={this.state.preferences} eventBus={this.eventBus}
|
||||
|
@ -932,20 +932,20 @@ class ERDTool extends React.Component {
|
|||
/>
|
||||
<FloatingNote open={this.state.note_open} onClose={this.onNoteClose}
|
||||
anchorEl={this.noteRefEle} noteNode={this.state.note_node} appendTo={this.diagramContainerRef.current} rows={8}/>
|
||||
<div className={this.props.classes.diagramContainer} data-test="diagram-container" ref={this.diagramContainerRef} onDrop={this.onDropNode} onDragOver={e => {e.preventDefault();}}>
|
||||
<div className='ERDTool-diagramContainer' data-test="diagram-container" ref={this.diagramContainerRef} onDrop={this.onDropNode} onDragOver={e => {e.preventDefault();}}>
|
||||
<Loader message={this.state.loading_msg} autoEllipsis={true}/>
|
||||
<ERDCanvasSettings.Provider value={{
|
||||
cardinality_notation: this.state.cardinality_notation
|
||||
}}>
|
||||
{!this.props.isTest && <CanvasWidget className={this.props.classes.diagramCanvas} ref={(ele)=>{this.canvasEle = ele?.ref?.current;}} engine={this.diagram.getEngine()} />}
|
||||
{!this.props.isTest && <CanvasWidget className='ERDTool-diagramCanvas' ref={(ele)=>{this.canvasEle = ele?.ref?.current;}} engine={this.diagram.getEngine()} />}
|
||||
</ERDCanvasSettings.Provider>
|
||||
</div>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(ERDTool);
|
||||
//export default withStyles(styles)(ERDTool);
|
||||
|
||||
ERDTool.propTypes = {
|
||||
params:PropTypes.shape({
|
||||
|
|
|
@ -8,53 +8,59 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React, { useEffect, useState, useMemo } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import CustomPropTypes from 'sources/custom_prop_types';
|
||||
import { Box, Popper } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { DefaultButton } from '../../../../../../static/js/components/Buttons';
|
||||
import CheckIcon from '@mui/icons-material/Check';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
const StyledPopper = styled(Popper)(({theme}) => ({
|
||||
'& .FloatingNote-root': {
|
||||
width: '250px',
|
||||
marginLeft: '8px',
|
||||
...theme.mixins.panelBorder.all,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
'& .FloatingNote-note': {
|
||||
padding: '4px',
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: theme.palette.primary.contrastText,
|
||||
borderTopLeftRadius: 'inherit',
|
||||
borderTopRightRadius: 'inherit',
|
||||
},
|
||||
'& .FloatingNote-header': {
|
||||
padding: '4px',
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
},
|
||||
'& .FloatingNote-textarea': {
|
||||
width: '100%',
|
||||
border: 0,
|
||||
display: 'block',
|
||||
},
|
||||
'& .FloatingNote-buttons': {
|
||||
padding: '4px',
|
||||
...theme.mixins.panelBorder.top,
|
||||
textAlign: 'right',
|
||||
}
|
||||
},
|
||||
note: {
|
||||
padding: '4px',
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: theme.palette.primary.contrastText,
|
||||
borderTopLeftRadius: 'inherit',
|
||||
borderTopRightRadius: 'inherit',
|
||||
},
|
||||
header: {
|
||||
padding: '4px',
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
},
|
||||
textarea: {
|
||||
width: '100%',
|
||||
border: 0,
|
||||
display: 'block',
|
||||
},
|
||||
buttons: {
|
||||
padding: '4px',
|
||||
...theme.mixins.panelBorder.top,
|
||||
textAlign: 'right',
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}));
|
||||
|
||||
export default function FloatingNote({open, onClose, anchorEl, rows, noteNode}) {
|
||||
const [text, setText] = useState('');
|
||||
const classes = useStyles();
|
||||
|
||||
useEffect(()=>{
|
||||
if(noteNode) {
|
||||
setText(noteNode.getNote());
|
||||
|
@ -70,16 +76,16 @@ export default function FloatingNote({open, onClose, anchorEl, rows, noteNode})
|
|||
}, [open]);
|
||||
|
||||
return (
|
||||
<Popper
|
||||
<StyledPopper
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
placement="right-start"
|
||||
>
|
||||
<Box className={classes.root}>
|
||||
<Box className={classes.note}>{gettext('Note')}:</Box>
|
||||
<Box className={classes.header}>{header}</Box>
|
||||
<textarea className={classes.textarea} autoFocus value={text} rows={rows} onChange={(e)=>setText(e.target.value)}/>
|
||||
<Box className={classes.buttons}>
|
||||
<Box className='FloatingNote-root'>
|
||||
<Box className='FloatingNote-note'>{gettext('Note')}:</Box>
|
||||
<Box className='FloatingNote-header'>{header}</Box>
|
||||
<textarea className='FloatingNote-textarea' autoFocus value={text} rows={rows} onChange={(e)=>setText(e.target.value)}/>
|
||||
<Box className='FloatingNote-buttons'>
|
||||
<DefaultButton startIcon={<CheckIcon />} onClick={()=>{
|
||||
let updated = (noteNode.getNote() != text);
|
||||
noteNode.setNote(text);
|
||||
|
@ -87,7 +93,7 @@ export default function FloatingNote({open, onClose, anchorEl, rows, noteNode})
|
|||
}}>{gettext('OK')}</DefaultButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</Popper>
|
||||
</StyledPopper>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import React, {useCallback, useEffect, useState} from 'react';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { Box, useTheme } from '@mui/material';
|
||||
import { PgButtonGroup, PgIconButton } from '../../../../../../static/js/components/Buttons';
|
||||
import FolderRoundedIcon from '@mui/icons-material/FolderRounded';
|
||||
|
@ -38,40 +38,17 @@ import { MagicIcon, SQLFileIcon } from '../../../../../../static/js/components/E
|
|||
import { useModal } from '../../../../../../static/js/helpers/ModalProvider';
|
||||
import { withColorPicker } from '../../../../../../static/js/helpers/withColorPicker';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
root: {
|
||||
padding: '2px 4px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
backgroundColor: theme.otherVars.editorToolbarBg,
|
||||
flexWrap: 'wrap',
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
},
|
||||
connectionButton: {
|
||||
display: 'flex',
|
||||
width: '450px',
|
||||
backgroundColor: theme.palette.default.main,
|
||||
color: theme.palette.default.contrastText,
|
||||
border: '1px solid ' + theme.palette.default.borderColor,
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
fillColorIcon: (props)=>({
|
||||
'& path[fill-opacity]': {
|
||||
fillOpacity: 1,
|
||||
color: props.fillColor ?? theme.palette.background.default,
|
||||
}
|
||||
}),
|
||||
textColorIcon: (props)=>({
|
||||
'& path[fill-opacity]': {
|
||||
fillOpacity: 1,
|
||||
color: props.textColor ?? theme.palette.text.primary,
|
||||
}
|
||||
}),
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
padding: '2px 4px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
backgroundColor: theme.otherVars.editorToolbarBg,
|
||||
flexWrap: 'wrap',
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
}));
|
||||
|
||||
export function MainToolBar({preferences, eventBus, fillColor, textColor, notation, onNotationChange}) {
|
||||
const classes = useStyles({fillColor,textColor});
|
||||
const theme = useTheme();
|
||||
const [buttonsDisabled, setButtonsDisabled] = useState({
|
||||
'save': true,
|
||||
|
@ -177,8 +154,8 @@ export function MainToolBar({preferences, eventBus, fillColor, textColor, notati
|
|||
}, [checkedMenuItems['sql_with_drop']]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box className={classes.root}>
|
||||
(<>
|
||||
<StyledBox>
|
||||
<PgButtonGroup size="small">
|
||||
<PgIconButton title={gettext('Load Project')} icon={<FolderRoundedIcon />}
|
||||
shortcut={preferences.open_project} onClick={()=>{
|
||||
|
@ -245,7 +222,7 @@ export function MainToolBar({preferences, eventBus, fillColor, textColor, notati
|
|||
}} />
|
||||
</PgButtonGroup>
|
||||
<PgButtonGroup size="small">
|
||||
<ColorButton title={gettext('Fill Color')} icon={<FormatColorFillRoundedIcon className={classes.fillColorIcon} />}
|
||||
<ColorButton title={gettext('Fill Color')} icon={<FormatColorFillRoundedIcon />}
|
||||
value={fillColor ?? theme.palette.background.default} options={{
|
||||
allowSave: true,
|
||||
}}
|
||||
|
@ -256,7 +233,7 @@ export function MainToolBar({preferences, eventBus, fillColor, textColor, notati
|
|||
eventBus.fireEvent(ERD_EVENTS.CHANGE_COLORS, null, textColor);
|
||||
}
|
||||
}}/>
|
||||
<ColorButton title={gettext('Text Color')} icon={<FormatColorTextRoundedIcon className={classes.textColorIcon} />}
|
||||
<ColorButton title={gettext('Text Color')} icon={<FormatColorTextRoundedIcon />}
|
||||
value={textColor ?? theme.palette.text.primary} options={{
|
||||
allowSave: true,
|
||||
}}
|
||||
|
@ -309,7 +286,7 @@ export function MainToolBar({preferences, eventBus, fillColor, textColor, notati
|
|||
<PgButtonGroup size="small">
|
||||
<PgIconButton title={gettext('Help')} icon={<HelpIcon />} onClick={onHelpClick} />
|
||||
</PgButtonGroup>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
<PgMenu
|
||||
anchorRef={saveAsMenuRef}
|
||||
open={openMenuName=='menu-saveas'}
|
||||
|
@ -338,7 +315,7 @@ export function MainToolBar({preferences, eventBus, fillColor, textColor, notati
|
|||
<PgMenuItem hasCheck closeOnCheck value="crows" checked={notation == 'crows'} onClick={onNotationChange}>{gettext('Crow\'s Foot Notation')}</PgMenuItem>
|
||||
<PgMenuItem hasCheck closeOnCheck value="chen" checked={notation == 'chen'} onClick={onNotationChange}>{gettext('Chen Notation')}</PgMenuItem>
|
||||
</PgMenu>
|
||||
</>
|
||||
</>)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,9 +19,9 @@ import {
|
|||
import {Point} from '@projectstorm/geometry';
|
||||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import clsx from 'clsx';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { ERDCanvasSettings } from '../components/ERDTool';
|
||||
import { keyframes } from '@emotion/react';
|
||||
|
||||
export const POINTER_SIZE = 30;
|
||||
|
||||
|
@ -79,38 +79,46 @@ export class OneToManyLinkModel extends RightAngleLinkModel {
|
|||
}
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
svgLink: {
|
||||
const svgLinkSelected = keyframes`
|
||||
from { stroke-dashoffset: 24;}
|
||||
to { stroke-dashoffset: 0; }
|
||||
`;
|
||||
|
||||
const StyledG = styled('g')((
|
||||
{
|
||||
theme
|
||||
}
|
||||
) => ({
|
||||
|
||||
'& .OneToMany-svgLink': {
|
||||
stroke: theme.palette.text.primary,
|
||||
fontSize: '0.8em',
|
||||
},
|
||||
'@keyframes svgLinkSelected': {
|
||||
'from': { strokeDashoffset: 24},
|
||||
'to': { strokeDashoffset: 0 }
|
||||
},
|
||||
svgLinkSelected: {
|
||||
strokeDasharray: '10, 2',
|
||||
animation: '$svgLinkSelected 1s linear infinite'
|
||||
},
|
||||
svgLinkCircle: {
|
||||
'& .OneToMany-svgLinkCircle': {
|
||||
fill: theme.palette.text.primary,
|
||||
},
|
||||
svgLinkPath: {
|
||||
|
||||
'& .OneToMany-svgLinkSelected': {
|
||||
strokeDasharray: '10, 2',
|
||||
animation: `${svgLinkSelected} 1s linear infinite`
|
||||
},
|
||||
'& .OneToMany-svgLinkPath': {
|
||||
pointerEvents: 'all',
|
||||
cursor: 'move',
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
function ChenNotation({rotation, type}) {
|
||||
const classes = useStyles();
|
||||
|
||||
const textX = Math.sign(rotation) > 0 ? -14 : 8;
|
||||
const textY = -5;
|
||||
return (
|
||||
<>
|
||||
<text className={classes.svgLink} x={textX} y={textY} transform={'rotate(' + -rotation + ')' }>
|
||||
<text className='OneToMany-svgLink' x={textX} y={textY} transform={'rotate(' + -rotation + ')' }>
|
||||
{type == 'one' ? '1' : 'N'}
|
||||
</text>
|
||||
<line className={classes.svgLink} x1="0" y1="0" x2="0" y2="30"></line>
|
||||
<line className='OneToMany-svgLink' x1="0" y1="0" x2="0" y2="30"></line>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -121,7 +129,7 @@ ChenNotation.propTypes = {
|
|||
|
||||
function CustomLinkEndWidget(props) {
|
||||
const { point, rotation, tx, ty, type } = props;
|
||||
const classes = useStyles();
|
||||
|
||||
const settings = useContext(ERDCanvasSettings);
|
||||
|
||||
const svgForType = (itype) => {
|
||||
|
@ -131,13 +139,13 @@ function CustomLinkEndWidget(props) {
|
|||
if(itype == 'many') {
|
||||
return (
|
||||
<>
|
||||
<circle className={clsx(classes.svgLink, classes.svgLinkCircle)} cx="0" cy="16" r={props.width*2.5} strokeWidth={props.width} />
|
||||
<polyline className={classes.svgLink} points="-8,0 0,15 0,0 0,30 0,15 8,0" fill="none" strokeWidth={props.width} />
|
||||
<circle className={['OneToMany-svgLink','OneToMany-svgLinkCircle'].join(' ')} cx="0" cy="16" r={props.width*2.5} strokeWidth={props.width} />
|
||||
<polyline className='OneToMany-svgLink' points="-8,0 0,15 0,0 0,30 0,15 8,0" fill="none" strokeWidth={props.width} />
|
||||
</>
|
||||
);
|
||||
} else if (itype == 'one') {
|
||||
return (
|
||||
<polyline className={classes.svgLink} points="-8,15 0,15 0,0 0,30 0,15 8,15" fill="none" strokeWidth={props.width} />
|
||||
<polyline className='OneToMany-svgLink' points="-8,15 0,15 0,0 0,30 0,15 8,15" fill="none" strokeWidth={props.width} />
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -313,16 +321,16 @@ export class OneToManyLinkWidget extends RightAngleLinkWidget {
|
|||
|
||||
|
||||
this.refPaths = [];
|
||||
return <g data-default-link-test={this.props.link.getOptions().testName}>{paths}</g>;
|
||||
return <StyledG data-default-link-test={this.props.link.getOptions().testName}>{paths}</StyledG>;
|
||||
}
|
||||
}
|
||||
|
||||
const LinkSegment = forwardRef(({model, selected, path, ...props}, ref)=>{
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<path
|
||||
ref={ref}
|
||||
className={clsx(classes.svgLink, classes.svgLinkPath, (selected ? classes.svgLinkSelected : ''))}
|
||||
className={['OneToMany-svgLink','OneToMany-svgLinkPath', (selected ? 'OneToMany-svgLinkSelected' : '')].join(' ')}
|
||||
stroke={model.getOptions().color}
|
||||
strokeWidth={model.getOptions().width}
|
||||
selected={selected}
|
||||
|
|
|
@ -23,10 +23,8 @@ import { PgIconButton } from '../../../../../../static/js/components/Buttons';
|
|||
import NoteRoundedIcon from '@mui/icons-material/NoteRounded';
|
||||
import VisibilityRoundedIcon from '@mui/icons-material/VisibilityRounded';
|
||||
import VisibilityOffRoundedIcon from '@mui/icons-material/VisibilityOffRounded';
|
||||
import { withStyles } from '@mui/styles';
|
||||
import clsx from 'clsx';
|
||||
import { Box } from '@mui/material';
|
||||
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
const TYPE = 'table';
|
||||
const TABLE_WIDTH = 175;
|
||||
|
@ -180,8 +178,8 @@ RowIcon.propTypes = {
|
|||
icon: PropTypes.any.isRequired,
|
||||
};
|
||||
|
||||
const styles = (theme)=>({
|
||||
tableNode: {
|
||||
const StyledDiv = styled('div')(({theme})=>({
|
||||
'&.TableNode-tableNode': {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
...theme.mixins.panelBorder.all,
|
||||
|
@ -192,48 +190,48 @@ const styles = (theme)=>({
|
|||
'& div:last-child': {
|
||||
borderBottomLeftRadius: 'inherit',
|
||||
borderBottomRightRadius: 'inherit',
|
||||
}
|
||||
},
|
||||
'& .TableNode-tableSection': {
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
padding: '0.125rem 0.25rem',
|
||||
display: 'flex',
|
||||
'&.TableNode-tableNameText': {
|
||||
fontWeight: 'bold',
|
||||
wordBreak: 'break-all',
|
||||
margin: 'auto 0',
|
||||
'& .TableNode-error': {
|
||||
color: theme.palette.error.main,
|
||||
},
|
||||
},
|
||||
'&.TableNode-tableToolbar': {
|
||||
background: theme.otherVars.editorToolbarBg,
|
||||
borderTopLeftRadius: 'inherit',
|
||||
borderTopRightRadius: 'inherit',
|
||||
},
|
||||
'& .TableNode-noteBtn': {
|
||||
marginLeft: 'auto',
|
||||
backgroundColor: theme.palette.warning.main,
|
||||
color: theme.palette.warning.contrastText,
|
||||
},
|
||||
},
|
||||
'& .TableNode-columnSection': {
|
||||
display:'flex',
|
||||
width: '100%' ,
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
'& .TableNode-columnName': {
|
||||
display:'flex',
|
||||
width: '100%' ,
|
||||
padding: '0.125rem 0.25rem',
|
||||
wordBreak: 'break-all',
|
||||
},
|
||||
},
|
||||
},
|
||||
tableNodeSelected: {
|
||||
'&.TableNode-tableNodeSelected': {
|
||||
borderColor: theme.palette.primary.main,
|
||||
},
|
||||
tableSection: {
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
padding: '0.125rem 0.25rem',
|
||||
display: 'flex',
|
||||
},
|
||||
columnSection: {
|
||||
display:'flex',
|
||||
width: '100%' ,
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
},
|
||||
columnName: {
|
||||
display:'flex',
|
||||
width: '100%' ,
|
||||
padding: '0.125rem 0.25rem',
|
||||
wordBreak: 'break-all',
|
||||
},
|
||||
tableToolbar: {
|
||||
background: theme.otherVars.editorToolbarBg,
|
||||
borderTopLeftRadius: 'inherit',
|
||||
borderTopRightRadius: 'inherit',
|
||||
},
|
||||
tableNameText: {
|
||||
fontWeight: 'bold',
|
||||
wordBreak: 'break-all',
|
||||
margin: 'auto 0',
|
||||
},
|
||||
error: {
|
||||
color: theme.palette.error.main,
|
||||
},
|
||||
noteBtn: {
|
||||
marginLeft: 'auto',
|
||||
backgroundColor: theme.palette.warning.main,
|
||||
color: theme.palette.warning.contrastText,
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
class TableNodeWidgetRaw extends React.Component {
|
||||
export class TableNodeWidget extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
@ -277,14 +275,13 @@ class TableNodeWidgetRaw extends React.Component {
|
|||
if(col.attlen) {
|
||||
cltype += '('+ col.attlen + (col.attprecision ? ',' + col.attprecision : '') +')';
|
||||
}
|
||||
|
||||
const {classes} = this.props;
|
||||
|
||||
return (
|
||||
<Box className={classes.columnSection} key={col.attnum} data-test="column-row">
|
||||
<Box className='TableNode-columnSection' key={col.attnum} data-test="column-row">
|
||||
<Box marginRight="auto" padding="0" minHeight="0" display="flex" alignItems="center">
|
||||
{this.generatePort(leftPort)}
|
||||
</Box>
|
||||
<Box className={classes.columnName}>
|
||||
<Box className='TableNode-columnName'>
|
||||
<RowIcon icon={icon} />
|
||||
<Box margin="auto 0">
|
||||
<span data-test="column-name">{col.name}</span>
|
||||
|
@ -324,19 +321,18 @@ class TableNodeWidgetRaw extends React.Component {
|
|||
(tableData.unique_constraint||[]).forEach((uk)=>{
|
||||
localUkCols.push(...uk.columns.map((c)=>c.column));
|
||||
});
|
||||
const {classes} = this.props;
|
||||
const styles = {
|
||||
backgroundColor: tableMetaData.fillColor,
|
||||
color: tableMetaData.textColor,
|
||||
};
|
||||
return (
|
||||
<div className={clsx(classes.tableNode, (this.props.node.isSelected() ? classes.tableNodeSelected: ''))}
|
||||
<StyledDiv className={['TableNode-tableNode', (this.props.node.isSelected() ? 'TableNode-tableNodeSelected': '')].join(' ')}
|
||||
onDoubleClick={()=>{this.props.node.fireEvent({}, 'editTable');}} style={styles}>
|
||||
<div className={clsx(classes.tableSection, classes.tableToolbar)}>
|
||||
<div className={'TableNode-tableSection TableNode-tableToolbar'}>
|
||||
<PgIconButton size="xs" title={gettext('Show Details')} icon={this.state.show_details ? <VisibilityRoundedIcon /> : <VisibilityOffRoundedIcon />}
|
||||
onClick={this.toggleShowDetails} onDoubleClick={(e)=>{e.stopPropagation();}} />
|
||||
{this.props.node.getNote() &&
|
||||
<PgIconButton size="xs" className={classes.noteBtn}
|
||||
<PgIconButton size="xs" className='TableNode-noteBtn'
|
||||
title={gettext('Check Note')} icon={<NoteRoundedIcon />}
|
||||
onClick={()=>{
|
||||
this.props.node.fireEvent({}, 'showNote');
|
||||
|
@ -344,31 +340,29 @@ class TableNodeWidgetRaw extends React.Component {
|
|||
/>}
|
||||
</div>
|
||||
{tableMetaData.is_promise &&
|
||||
<div className={classes.tableSection}>
|
||||
{!tableMetaData.data_failed && <div className={classes.tableNameText}>{gettext('Fetching...')}</div>}
|
||||
{tableMetaData.data_failed && <div className={clsx(classes.tableNameText, classes.error)}>{gettext('Failed to get data. Please delete this table.')}</div>}
|
||||
<div className='TableNode-tableSection'>
|
||||
{!tableMetaData.data_failed && <div className='TableNode-tableNameText'>{gettext('Fetching...')}</div>}
|
||||
{tableMetaData.data_failed && <div className={'TableNode-tableNameText TableNode-error'}>{gettext('Failed to get data. Please delete this table.')}</div>}
|
||||
</div>}
|
||||
{!tableMetaData.is_promise && <>
|
||||
<div className={classes.tableSection}>
|
||||
<div className='TableNode-tableSection'>
|
||||
<RowIcon icon={SchemaIcon}/>
|
||||
<div className={classes.tableNameText} data-test="schema-name">{tableData.schema}</div>
|
||||
<div className='TableNode-tableNameText' data-test="schema-name">{tableData.schema}</div>
|
||||
</div>
|
||||
<div className={classes.tableSection}>
|
||||
<div className='TableNode-tableSection'>
|
||||
<RowIcon icon={TableIcon} />
|
||||
<div className={classes.tableNameText} data-test="table-name">{tableData.name}</div>
|
||||
<div className='TableNode-tableNameText' data-test="table-name">{tableData.name}</div>
|
||||
</div>
|
||||
{tableData.columns.length > 0 && <div>
|
||||
{_.map(tableData.columns, (col)=>this.generateColumn(col, localFkCols, localUkCols))}
|
||||
</div>}
|
||||
</>}
|
||||
</div>
|
||||
</StyledDiv>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const TableNodeWidget = withStyles(styles)(TableNodeWidgetRaw);
|
||||
|
||||
TableNodeWidgetRaw.propTypes = {
|
||||
TableNodeWidget.propTypes = {
|
||||
node: PropTypes.instanceOf(TableNodeModel),
|
||||
engine: PropTypes.instanceOf(DiagramEngine),
|
||||
classes: PropTypes.object,
|
||||
|
|
|
@ -12,7 +12,6 @@ import _ from 'lodash';
|
|||
import url_for from 'sources/url_for';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import Wizard from '../../../../static/js/helpers/wizard/Wizard';
|
||||
import WizardStep from '../../../../static/js/helpers/wizard/WizardStep';
|
||||
import PgTable from 'sources/components/PgTable';
|
||||
|
@ -20,46 +19,12 @@ import { getNodePrivilegeRoleSchema } from '../../../../../pgadmin/browser/serve
|
|||
import { InputSQL, FormFooterMessage, MESSAGE_TYPE } from '../../../../static/js/components/FormComponents';
|
||||
import getApiInstance from '../../../../static/js/api_instance';
|
||||
import SchemaView from '../../../../static/js/SchemaView';
|
||||
import clsx from 'clsx';
|
||||
import PropTypes from 'prop-types';
|
||||
import PrivilegeSchema from './privilege_schema.ui';
|
||||
import { usePgAdmin } from '../../../../static/js/BrowserComponent';
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
({
|
||||
root: {
|
||||
height: '100%'
|
||||
},
|
||||
searchBox: {
|
||||
marginBottom: '1em',
|
||||
display: 'flex',
|
||||
},
|
||||
searchPadding: {
|
||||
flex: 2.5
|
||||
},
|
||||
searchInput: {
|
||||
flex: 1,
|
||||
marginTop: 2,
|
||||
borderLeft: 'none',
|
||||
paddingLeft: 5
|
||||
},
|
||||
grantWizardSql: {
|
||||
height: '90% !important',
|
||||
width: '100%'
|
||||
},
|
||||
privilegeStep: {
|
||||
height: '100%',
|
||||
overflow: 'auto'
|
||||
},
|
||||
panelContent: {
|
||||
flexGrow: 1,
|
||||
minHeight: 0
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
export default function GrantWizard({ sid, did, nodeInfo, nodeData, onClose }) {
|
||||
const classes = useStyles();
|
||||
|
||||
let columns = [
|
||||
{
|
||||
|
||||
|
@ -310,11 +275,10 @@ export default function GrantWizard({ sid, did, nodeInfo, nodeData, onClose }) {
|
|||
loaderText={loaderText}
|
||||
>
|
||||
<WizardStep stepId={0}>
|
||||
<Box className={classes.panelContent}>
|
||||
<Box sx={{flexGrow: 1, minHeight: 0}}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
tableNoBorder={false}
|
||||
className={classes.table}
|
||||
height={window.innerHeight - 450}
|
||||
columns={columns}
|
||||
data={tableData}
|
||||
|
@ -327,7 +291,7 @@ export default function GrantWizard({ sid, did, nodeInfo, nodeData, onClose }) {
|
|||
</WizardStep>
|
||||
<WizardStep
|
||||
stepId={1}
|
||||
className={clsx(classes.privilegeStep)}>
|
||||
sx={{ height:'100%', overflow:'auto'}}>
|
||||
{privSchemaInstance &&
|
||||
<SchemaView
|
||||
formType={'dialog'}
|
||||
|
@ -347,7 +311,6 @@ export default function GrantWizard({ sid, did, nodeInfo, nodeData, onClose }) {
|
|||
<Box>{gettext('The SQL below will be executed on the database server to grant the selected privileges. Please click on Finish to complete the process.')}</Box>
|
||||
<InputSQL
|
||||
onLable={true}
|
||||
className={classes.grantWizardSql}
|
||||
readonly={true}
|
||||
value={msqlData.toString()} />
|
||||
</WizardStep>
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import gettext from 'sources/gettext';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import _ from 'lodash';
|
||||
import url_for from 'sources/url_for';
|
||||
import React from 'react';
|
||||
import { Box, Paper} from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import Wizard from '../../../../static/js/helpers/wizard/Wizard';
|
||||
import WizardStep from '../../../../static/js/helpers/wizard/WizardStep';
|
||||
import { FormFooterMessage, MESSAGE_TYPE, FormNote } from '../../../../static/js/components/FormComponents';
|
||||
|
@ -22,39 +22,33 @@ import ImportExportSelectionSchema from './import_export_selection.ui';
|
|||
import CheckBoxTree from '../../../../static/js/components/CheckBoxTree';
|
||||
import getApiInstance from '../../../../static/js/api_instance';
|
||||
import PropTypes from 'prop-types';
|
||||
import { commonTableStyles } from '../../../../static/js/Theme';
|
||||
import clsx from 'clsx';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import Table from '../../../../static/js/components/Table';
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
const StyledBox = styled(Box)(() =>
|
||||
({
|
||||
root: {
|
||||
height: '100%'
|
||||
},
|
||||
treeContainer: {
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
},
|
||||
boxText: {
|
||||
paddingBottom: '5px'
|
||||
},
|
||||
noOverflow: {
|
||||
overflow: 'hidden'
|
||||
},
|
||||
summaryContainer: {
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
overflow: 'auto',
|
||||
},
|
||||
noteContainer: {
|
||||
marginTop: '5px',
|
||||
height: '100%',
|
||||
'& .ImportExportServers-noOverflow': {
|
||||
overflow: 'hidden',
|
||||
'& .ImportExportServers-treeContainer': {
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
},
|
||||
'& .ImportExportServers-boxText': {
|
||||
paddingBottom: '5px'
|
||||
},
|
||||
'& .ImportExportServers-summaryContainer': {
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
overflow: 'auto',
|
||||
},
|
||||
'& .ImportExportServers-noteContainer': {
|
||||
marginTop: '5px',
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
}));
|
||||
|
||||
export default function ImportExportServers({onClose}) {
|
||||
const classes = useStyles();
|
||||
const tableClasses = commonTableStyles();
|
||||
|
||||
let steps = [gettext('Import/Export'), gettext('Database Servers'), gettext('Summary')];
|
||||
const [loaderText, setLoaderText] = React.useState('');
|
||||
|
@ -202,7 +196,7 @@ export default function ImportExportServers({onClose}) {
|
|||
};
|
||||
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<StyledBox>
|
||||
<Loader message={loaderText} />
|
||||
<Wizard
|
||||
title={gettext('Import/Export Servers')}
|
||||
|
@ -227,18 +221,18 @@ export default function ImportExportServers({onClose}) {
|
|||
/>
|
||||
<FormFooterMessage type={MESSAGE_TYPE.ERROR} message={errMsg} onClose={onErrClose} />
|
||||
</WizardStep>
|
||||
<WizardStep stepId={1} className={classes.noOverflow}>
|
||||
<Box className={classes.boxText}>{gettext('Select the Server Groups/Servers to import/export:')}</Box>
|
||||
<Box className={classes.treeContainer}>
|
||||
<WizardStep stepId={1} className='ImportExportServers-noOverflow'>
|
||||
<Box className='ImportExportServers-boxText'>{gettext('Select the Server Groups/Servers to import/export:')}</Box>
|
||||
<Box className='ImportExportServers-treeContainer'>
|
||||
<CheckBoxTree treeData={serverData} getSelectedServers={(selServers) => {
|
||||
setSelectedServers(selServers);
|
||||
}}/>
|
||||
</Box>
|
||||
</WizardStep>
|
||||
<WizardStep stepId={2} className={classes.noOverflow}>
|
||||
<Box className={classes.boxText}>{gettext(summaryText)}</Box>
|
||||
<Paper variant="outlined" elevation={0} className={classes.summaryContainer}>
|
||||
<table className={clsx(tableClasses.table)}>
|
||||
<WizardStep stepId={2} className='ImportExportServers-noOverflow'>
|
||||
<Box className='ImportExportServers-boxText'>{gettext(summaryText)}</Box>
|
||||
<Paper variant="outlined" elevation={0} className='ImportExportServers-summaryContainer'>
|
||||
<Table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Server Group</th>
|
||||
|
@ -255,13 +249,13 @@ export default function ImportExportServers({onClose}) {
|
|||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</Table>
|
||||
</Paper>
|
||||
{selectionFormData.imp_exp == 'i' &&
|
||||
<FormNote className={classes.noteContainer} text={noteText}/> }
|
||||
<FormNote className='ImportExportServers-noteContainer' text={noteText}/> }
|
||||
</WizardStep>
|
||||
</Wizard>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
ImportExportServers.propTypes = {
|
||||
|
|
|
@ -11,29 +11,14 @@ import PropTypes from 'prop-types';
|
|||
import React, { useContext, useState } from 'react';
|
||||
|
||||
import { Box, Grid, Typography } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
|
||||
import { InputSelect } from '../../../../../static/js/components/FormComponents';
|
||||
import { SchemaDiffEventsContext } from './SchemaDiffComponent';
|
||||
import { SCHEMA_DIFF_EVENT } from '../SchemaDiffConstants';
|
||||
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
root: {
|
||||
padding: '0rem'
|
||||
},
|
||||
spanLabel: {
|
||||
alignSelf: 'center',
|
||||
marginRight: '4px',
|
||||
},
|
||||
inputLabel: {
|
||||
padding: '0.3rem',
|
||||
},
|
||||
}));
|
||||
|
||||
export function InputComponent({ label, serverList, databaseList, schemaList, diff_type, selectedSid = null, selectedDid=null, selectedScid=null }) {
|
||||
|
||||
const classes = useStyles();
|
||||
const [selectedServer, setSelectedServer] = useState(selectedSid);
|
||||
const [selectedDatabase, setSelectedDatabase] = useState(selectedDid);
|
||||
const [selectedSchema, setSelectedSchema] = useState(selectedScid);
|
||||
|
@ -71,16 +56,16 @@ export function InputComponent({ label, serverList, databaseList, schemaList, di
|
|||
};
|
||||
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<Box sx={{padding: '0rem'}}>
|
||||
<Grid
|
||||
container
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
>
|
||||
<Grid item lg={2} md={2} sm={2} xs={2} className={classes.inputLabel}>
|
||||
<Grid item lg={2} md={2} sm={2} xs={2} sx={{padding: '0.3rem'}}>
|
||||
<Typography id={label}>{label}</Typography>
|
||||
</Grid>
|
||||
<Grid item lg={4} md={4} sm={4} xs={4} className={classes.inputLabel}>
|
||||
<Grid item lg={4} md={4} sm={4} xs={4} sx={{padding: '0.3rem'}}>
|
||||
<InputSelect
|
||||
options={serverList}
|
||||
optionsReloadBasis={serverList?.length}
|
||||
|
@ -95,7 +80,7 @@ export function InputComponent({ label, serverList, databaseList, schemaList, di
|
|||
></InputSelect>
|
||||
</Grid>
|
||||
|
||||
<Grid item lg={3} md={3} sm={3} xs={3} className={classes.inputLabel}>
|
||||
<Grid item lg={3} md={3} sm={3} xs={3} sx={{padding: '0.3rem'}}>
|
||||
<InputSelect
|
||||
options={databaseList}
|
||||
optionsReloadBasis={databaseList?.map ? _.join(databaseList.map((c)=>c.value), ',') : null}
|
||||
|
@ -111,7 +96,7 @@ export function InputComponent({ label, serverList, databaseList, schemaList, di
|
|||
></InputSelect>
|
||||
</Grid>
|
||||
|
||||
<Grid item lg={3} md={3} sm={3} xs={3} className={classes.inputLabel}>
|
||||
<Grid item lg={3} md={3} sm={3} xs={3} sx={{padding: '0.3rem'}}>
|
||||
<InputSelect
|
||||
options={schemaList}
|
||||
optionsReloadBasis={schemaList?.map ? _.join(schemaList.map((c)=>c.value), ',') : null}
|
||||
|
@ -127,7 +112,6 @@ export function InputComponent({ label, serverList, databaseList, schemaList, di
|
|||
></InputSelect>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</Box >
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,22 +7,16 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import _ from 'lodash';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { SelectColumn } from 'react-data-grid';
|
||||
import React, { useContext, useEffect, useLayoutEffect, useReducer, useRef, useState } from 'react';
|
||||
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import KeyboardArrowRightRoundedIcon from '@mui/icons-material/KeyboardArrowRightRounded';
|
||||
import KeyboardArrowDownRoundedIcon from '@mui/icons-material/KeyboardArrowDownRounded';
|
||||
import InfoIcon from '@mui/icons-material/InfoRounded';
|
||||
|
||||
import gettext from 'sources/gettext';
|
||||
import url_for from 'sources/url_for';
|
||||
|
||||
|
||||
import { FILTER_NAME, SCHEMA_DIFF_EVENT } from '../SchemaDiffConstants';
|
||||
import { SchemaDiffContext, SchemaDiffEventsContext } from './SchemaDiffComponent';
|
||||
import { InputCheckbox } from '../../../../../static/js/components/FormComponents';
|
||||
|
@ -30,152 +24,148 @@ import PgReactDataGrid from '../../../../../static/js/components/PgReactDataGrid
|
|||
import { usePgAdmin } from '../../../../../static/js/BrowserComponent';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
|
||||
paddingTop: '0.5rem',
|
||||
display: 'flex',
|
||||
height: '100%',
|
||||
flexDirection: 'column',
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.otherVars.qtDatagridBg,
|
||||
border: 'none',
|
||||
fontSize: '13px',
|
||||
'& .rdg': {
|
||||
flex: 1,
|
||||
borderTop: '1px solid' + theme.otherVars.borderColor,
|
||||
},
|
||||
'--rdg-background-color': theme.palette.default.main,
|
||||
'--rdg-selection-color': theme.palette.primary.main,
|
||||
'& .rdg-cell': {
|
||||
padding: 0,
|
||||
boxShadow: 'none',
|
||||
color: theme.otherVars.schemaDiff.diffColorFg + ' !important',
|
||||
...theme.mixins.panelBorder.right,
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
'&[aria-colindex="1"]': {
|
||||
padding: 0,
|
||||
},
|
||||
'&[aria-selected=true]:not([role="columnheader"]):not([aria-colindex="1"])': {
|
||||
outlineWidth: '0',
|
||||
outlineOffset: '-1px',
|
||||
color: theme.otherVars.qtDatagridSelectFg,
|
||||
},
|
||||
'&[aria-selected=true][aria-colindex="1"]': {
|
||||
outlineWidth: 0,
|
||||
}
|
||||
},
|
||||
'& .rdg-header-row .rdg-cell': {
|
||||
padding: 0,
|
||||
paddingLeft: '0.5rem',
|
||||
boxShadow: 'none',
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
paddingTop: '0.5rem',
|
||||
display: 'flex',
|
||||
height: '100%',
|
||||
flexDirection: 'column',
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.otherVars.qtDatagridBg,
|
||||
border: 'none',
|
||||
fontSize: '13px',
|
||||
'--rdg-background-color': theme.palette.default.main,
|
||||
'--rdg-selection-color': theme.palette.primary.main,
|
||||
'& .ResultGridComponent-gridPanel': {
|
||||
'--rdg-background-color': theme.palette.default.main + ' !important',
|
||||
'&.ResultGridComponent-grid': {
|
||||
fontSize: '13px',
|
||||
'--rdg-selection-color': 'none'
|
||||
},
|
||||
'& .rdg-header-row': {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
'& .rdg-cell': {
|
||||
padding: 0,
|
||||
paddingLeft: '0.5rem',
|
||||
boxShadow: 'none',
|
||||
'&[aria-colindex="1"]': {
|
||||
padding: 0,
|
||||
},
|
||||
'& .ResultGridComponent-headerSelectCell': {
|
||||
padding: '0rem 0.3rem 0 0.3rem'
|
||||
},
|
||||
},
|
||||
},
|
||||
'& .rdg-row': {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
'&[aria-selected=true]': {
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
color: theme.otherVars.qtDatagridSelectFg,
|
||||
'& .rdg-cell:nth-child(1)': {
|
||||
'& .rdg-cell:nth-of-type(1)': {
|
||||
backgroundColor: 'transparent',
|
||||
outlineColor: 'transparent',
|
||||
color: theme.palette.primary.contrastText,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
fontSize: '13px',
|
||||
'--rdg-selection-color': 'none'
|
||||
},
|
||||
subRow: {
|
||||
paddingLeft: '1rem'
|
||||
},
|
||||
recordRow: {
|
||||
marginLeft: '2.7rem',
|
||||
height: '1.3rem',
|
||||
width: '1.3rem',
|
||||
display: 'inline-block',
|
||||
marginRight: '0.3rem',
|
||||
paddingLeft: '0.5rem',
|
||||
},
|
||||
rowIcon: {
|
||||
display: 'inline-block !important',
|
||||
height: '1.3rem',
|
||||
width: '1.3rem'
|
||||
},
|
||||
cellExpand: {
|
||||
display: 'table',
|
||||
blockSize: '100%',
|
||||
|
||||
'& > span': {
|
||||
verticalAlign: 'middle',
|
||||
cursor: 'pointer',
|
||||
|
||||
'& > span': {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
'& .rdg-cell': {
|
||||
padding: 0,
|
||||
boxShadow: 'none',
|
||||
color: theme.otherVars.schemaDiff.diffColorFg + ' !important',
|
||||
...theme.mixins.panelBorder.right,
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
'&[aria-colindex="1"]': {
|
||||
padding: 0,
|
||||
},
|
||||
'&[aria-selected=true]:not([role="columnheader"]):not([aria-colindex="1"])': {
|
||||
outlineWidth: '0',
|
||||
outlineOffset: '-1px',
|
||||
color: theme.otherVars.qtDatagridSelectFg,
|
||||
},
|
||||
'&[aria-selected=true][aria-colindex="1"]': {
|
||||
outlineWidth: 0,
|
||||
},
|
||||
'& .ResultGridComponent-cellExpand': {
|
||||
display: 'table',
|
||||
blockSize: '100%',
|
||||
'& > span': {
|
||||
verticalAlign: 'middle',
|
||||
cursor: 'pointer',
|
||||
'& > span': {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}
|
||||
},
|
||||
'& .ResultGridComponent-subRow': {
|
||||
paddingLeft: '1rem',
|
||||
'& .ResultGridComponent-rowIcon': {
|
||||
display: 'inline-block !important',
|
||||
height: '1.3rem',
|
||||
width: '1.3rem'
|
||||
},
|
||||
'& .ResultGridComponent-count': {
|
||||
display: 'inline-block !important',
|
||||
'& .ResultGridComponent-countLabel': {
|
||||
paddingLeft: '1rem',
|
||||
},
|
||||
'& .ResultGridComponent-countStyle': {
|
||||
fontWeight: 900,
|
||||
fontSize: '0.8rem',
|
||||
paddingLeft: '0.3rem',
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
'& .ResultGridComponent-selectedRow': {
|
||||
paddingLeft: '0.5rem',
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
},
|
||||
'& .ResultGridComponent-source': {
|
||||
backgroundColor: theme.otherVars.schemaDiff.sourceRowColor,
|
||||
color: theme.otherVars.schemaDiff.diffSelectFG,
|
||||
paddingLeft: '0.5rem',
|
||||
},
|
||||
'& .ResultGridComponent-target': {
|
||||
backgroundColor: theme.otherVars.schemaDiff.targetRowColor,
|
||||
color: theme.otherVars.schemaDiff.diffSelectFG,
|
||||
paddingLeft: '0.5rem',
|
||||
},
|
||||
'& .ResultGridComponent-different': {
|
||||
backgroundColor: theme.otherVars.schemaDiff.diffRowColor,
|
||||
color: theme.otherVars.schemaDiff.diffSelectFG,
|
||||
paddingLeft: '0.5rem',
|
||||
},
|
||||
'& .ResultGridComponent-identical': {
|
||||
paddingLeft: '0.5rem',
|
||||
color: theme.otherVars.schemaDiff.diffColorFg,
|
||||
},
|
||||
'& .ResultGridComponent-recordRow': {
|
||||
marginLeft: '2.7rem',
|
||||
height: '1.3rem',
|
||||
width: '1.3rem',
|
||||
display: 'inline-block',
|
||||
marginRight: '0.3rem',
|
||||
paddingLeft: '0.5rem',
|
||||
},
|
||||
'& .ResultGridComponent-selectCell': {
|
||||
padding: '0 0.3rem'
|
||||
},
|
||||
'& .ResultGridComponent-selectedRowCheckBox': {
|
||||
backgroundColor: theme.otherVars.schemaDiff.diffSelCheckbox,
|
||||
},
|
||||
'& .ResultGridComponent-selChBox': {
|
||||
paddingLeft: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'& .ResultGridComponent-noRowsIcon': {
|
||||
width: '1.1rem',
|
||||
height: '1.1rem',
|
||||
marginRight: '0.5rem',
|
||||
},
|
||||
'&.rdg': {
|
||||
flex: 1,
|
||||
borderTop: '1px solid' + theme.otherVars.borderColor,
|
||||
},
|
||||
},
|
||||
gridPanel: {
|
||||
'--rdg-background-color': theme.palette.default.main + ' !important',
|
||||
},
|
||||
source: {
|
||||
backgroundColor: theme.otherVars.schemaDiff.sourceRowColor,
|
||||
color: theme.otherVars.schemaDiff.diffSelectFG,
|
||||
paddingLeft: '0.5rem',
|
||||
},
|
||||
target: {
|
||||
backgroundColor: theme.otherVars.schemaDiff.targetRowColor,
|
||||
color: theme.otherVars.schemaDiff.diffSelectFG,
|
||||
paddingLeft: '0.5rem',
|
||||
},
|
||||
different: {
|
||||
backgroundColor: theme.otherVars.schemaDiff.diffRowColor,
|
||||
color: theme.otherVars.schemaDiff.diffSelectFG,
|
||||
paddingLeft: '0.5rem',
|
||||
},
|
||||
identical: {
|
||||
paddingLeft: '0.5rem',
|
||||
color: theme.otherVars.schemaDiff.diffColorFg,
|
||||
},
|
||||
selectCell: {
|
||||
padding: '0 0.3rem'
|
||||
},
|
||||
headerSelectCell: {
|
||||
padding: '0rem 0.3rem 0 0.3rem'
|
||||
},
|
||||
count: {
|
||||
display: 'inline-block !important',
|
||||
},
|
||||
countStyle: {
|
||||
fontWeight: 900,
|
||||
fontSize: '0.8rem',
|
||||
paddingLeft: '0.3rem',
|
||||
},
|
||||
countLabel: {
|
||||
paddingLeft: '1rem',
|
||||
},
|
||||
selectedRow: {
|
||||
paddingLeft: '0.5rem',
|
||||
backgroundColor: theme.palette.primary.light
|
||||
},
|
||||
selectedRowCheckBox: {
|
||||
paddingLeft: '0.5rem',
|
||||
backgroundColor: theme.otherVars.schemaDiff.diffSelCheckbox,
|
||||
},
|
||||
selChBox: {
|
||||
paddingLeft: 0,
|
||||
},
|
||||
noRowsIcon:{
|
||||
width: '1.1rem',
|
||||
height: '1.1rem',
|
||||
marginRight: '0.5rem',
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
function useFocusRef(isSelected) {
|
||||
|
@ -219,8 +209,7 @@ function CellExpanderFormatter({
|
|||
isCellSelected,
|
||||
expanded,
|
||||
filterParams,
|
||||
onCellExpand,
|
||||
classes
|
||||
onCellExpand
|
||||
}) {
|
||||
const { ref, tabIndex } = useFocusRef(isCellSelected);
|
||||
'identicalCount' in row && setRecordCount(row, filterParams);
|
||||
|
@ -233,24 +222,24 @@ function CellExpanderFormatter({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={classes.cellExpand}>
|
||||
<div className='ResultGridComponent-cellExpand'>
|
||||
<span onClick={onCellExpand} onKeyDown={handleKeyDown}>
|
||||
<span ref={ref} tabIndex={tabIndex} className={'identicalCount' in row ? classes.subRow : null}>
|
||||
{expanded ? <KeyboardArrowDownRoundedIcon /> : <KeyboardArrowRightRoundedIcon />} <span className={clsx(row.icon, classes.rowIcon)}></span>{row.label}
|
||||
<span ref={ref} tabIndex={tabIndex} className={'identicalCount' in row ? 'ResultGridComponent-subRow' : null}>
|
||||
{expanded ? <KeyboardArrowDownRoundedIcon /> : <KeyboardArrowRightRoundedIcon />} <span className={row.icon + ' ResultGridComponent-rowIcon'}></span>{row.label}
|
||||
{
|
||||
'identicalCount' in row ?
|
||||
<span className={clsx(classes.count)}>
|
||||
<span className={'ResultGridComponent-count'}>
|
||||
{
|
||||
filterParams.includes(FILTER_NAME.IDENTICAL) && <><span className={classes.countLabel}>{FILTER_NAME.IDENTICAL}:</span> <span className={classes.countStyle}>{row.identicalCount} </span></>
|
||||
filterParams.includes(FILTER_NAME.IDENTICAL) && <><span className='ResultGridComponent-countLabel'>{FILTER_NAME.IDENTICAL}:</span> <span className='ResultGridComponent-countStyle'>{row.identicalCount} </span></>
|
||||
}
|
||||
{
|
||||
filterParams.includes(FILTER_NAME.DIFFERENT) && <><span className={classes.countLabel}>{FILTER_NAME.DIFFERENT}:</span> <span className={classes.countStyle}>{row.differentCount} </span></>
|
||||
filterParams.includes(FILTER_NAME.DIFFERENT) && <><span className='ResultGridComponent-countLabel'>{FILTER_NAME.DIFFERENT}:</span> <span className='ResultGridComponent-countStyle'>{row.differentCount} </span></>
|
||||
}
|
||||
{
|
||||
filterParams.includes(FILTER_NAME.SOURCE_ONLY) && <><span className={classes.countLabel}>{FILTER_NAME.SOURCE_ONLY}:</span> <span className={classes.countStyle}>{row.sourceOnlyCount} </span></>
|
||||
filterParams.includes(FILTER_NAME.SOURCE_ONLY) && <><span className='ResultGridComponent-countLabel'>{FILTER_NAME.SOURCE_ONLY}:</span> <span className='ResultGridComponent-countStyle'>{row.sourceOnlyCount} </span></>
|
||||
}
|
||||
{
|
||||
filterParams.includes(FILTER_NAME.TARGET_ONLY) && <><span className={classes.countLabel}>{FILTER_NAME.TARGET_ONLY}: </span><span className={classes.countStyle}>{row.targetOnlyCount}</span></>
|
||||
filterParams.includes(FILTER_NAME.TARGET_ONLY) && <><span className='ResultGridComponent-countLabel'>{FILTER_NAME.TARGET_ONLY}: </span><span className='ResultGridComponent-countStyle'>{row.targetOnlyCount}</span></>
|
||||
}
|
||||
</span>
|
||||
: null
|
||||
|
@ -444,7 +433,7 @@ function reducer(rows, { type, id, filterParams, gridData }) {
|
|||
}
|
||||
|
||||
export function ResultGridComponent({ gridData, allRowIds, filterParams, selectedRowIds, transId, sourceData, targetData }) {
|
||||
const classes = useStyles();
|
||||
|
||||
const [rows, dispatch] = useReducer(reducer, [...gridData]);
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
const [rootSelection, setRootSelection] = useState(false);
|
||||
|
@ -544,15 +533,15 @@ export function ResultGridComponent({ gridData, allRowIds, filterParams, selecte
|
|||
function getStyleClassName(row, selectedRowIds, isCellSelected, activeRowId, isCheckbox = false) {
|
||||
let clsName = null;
|
||||
if (selectedRowIds.includes(`${row.id}`) || isCellSelected || row.id == activeRowId) {
|
||||
clsName = isCheckbox ? classes.selectedRowCheckBox : classes.selectedRow;
|
||||
clsName = isCheckbox ? 'ResultGridComponent-selectedRowCheckBox' : 'ResultGridComponent-selectedRow';
|
||||
} else if (row.status == FILTER_NAME.DIFFERENT) {
|
||||
clsName = classes.different;
|
||||
clsName = 'ResultGridComponent-different';
|
||||
} else if (row.status == FILTER_NAME.SOURCE_ONLY) {
|
||||
clsName = classes.source;
|
||||
clsName = 'ResultGridComponent-source';
|
||||
} else if (row.status == FILTER_NAME.TARGET_ONLY) {
|
||||
clsName = classes.target;
|
||||
clsName = 'ResultGridComponent-target';
|
||||
} else if (row.status == FILTER_NAME.IDENTICAL) {
|
||||
clsName = classes.identical;
|
||||
clsName = 'ResultGridComponent-identical';
|
||||
}
|
||||
|
||||
return clsName;
|
||||
|
@ -568,7 +557,7 @@ export function ResultGridComponent({ gridData, allRowIds, filterParams, selecte
|
|||
return (
|
||||
<InputCheckbox
|
||||
cid={_.uniqueId('rgc')}
|
||||
className={classes.headerSelectCell}
|
||||
className='ResultGridComponent-headerSelectCell'
|
||||
value={selectedRows.length == allRowIds.length ? rootSelection : false}
|
||||
size='small'
|
||||
onChange={(e) => {
|
||||
|
@ -589,9 +578,9 @@ export function ResultGridComponent({ gridData, allRowIds, filterParams, selecte
|
|||
formatter({ row, isCellSelected }) {
|
||||
isCellSelected && setActiveRow(row.id);
|
||||
return (
|
||||
<Box className={!row?.children && clsx(getStyleClassName(row, selectedRows, isCellSelected, activeRow, true), classes.selChBox)}>
|
||||
<Box className={!row?.children && getStyleClassName(row, selectedRows, isCellSelected, activeRow, true) + ' ResultGridComponent-selChBox'}>
|
||||
<InputCheckbox
|
||||
className={classes.selectCell}
|
||||
className='ResultGridComponent-selectCell'
|
||||
cid={`${row.id}`}
|
||||
value={selectedRows.includes(`${row.id}`)}
|
||||
size='small'
|
||||
|
@ -639,14 +628,13 @@ export function ResultGridComponent({ gridData, allRowIds, filterParams, selecte
|
|||
expanded={row.isExpanded === true}
|
||||
filterParams={filterParams}
|
||||
onCellExpand={() => dispatch({ id: row.id, type: 'toggleSubRow', filterParams: filterParams, gridData: gridData, selectedRows: selectedRows })}
|
||||
classes={classes}
|
||||
/>
|
||||
)}
|
||||
<div className="rdg-cell-value">
|
||||
|
||||
{!hasChildren && (
|
||||
<Box className={clsx(getStyleClassName(row, selectedRows, isCellSelected, activeRow), classes.status)}>
|
||||
<span className={clsx(classes.recordRow, row.icon)}></span>
|
||||
<Box className={getStyleClassName(row, selectedRows, isCellSelected, activeRow)}>
|
||||
<span className={'ResultGridComponent-recordRow ' + row.icon}></span>
|
||||
{row.label}
|
||||
</Box>
|
||||
)}
|
||||
|
@ -728,13 +716,13 @@ export function ResultGridComponent({ gridData, allRowIds, filterParams, selecte
|
|||
}
|
||||
|
||||
return (
|
||||
<Box className={classes.root} flexGrow="1" minHeight="0" id="schema-diff-grid">
|
||||
<StyledBox flexGrow="1" minHeight="0" id="schema-diff-grid">
|
||||
{
|
||||
gridData ?
|
||||
<PgReactDataGrid
|
||||
id="schema-diff-result-grid"
|
||||
columns={columns} rows={rows}
|
||||
className={clsx('big-grid', classes.gridPanel, classes.grid)}
|
||||
className={'ResultGridComponent-gridPanel ResultGridComponent-grid'}
|
||||
treeDepth={2}
|
||||
enableRowSelect={true}
|
||||
defaultColumnOptions={{
|
||||
|
@ -747,14 +735,14 @@ export function ResultGridComponent({ gridData, allRowIds, filterParams, selecte
|
|||
rowKeyGetter={rowKeyGetter}
|
||||
direction={'vertical-lr'}
|
||||
noRowsText={gettext('No difference found')}
|
||||
noRowsIcon={<InfoIcon className={classes.noRowsIcon} />}
|
||||
noRowsIcon={<InfoIcon className='ResultGridComponent-noRowsIcon' />}
|
||||
/>
|
||||
:
|
||||
<>
|
||||
{gettext('Loading result grid...')}
|
||||
</>
|
||||
}
|
||||
</Box>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,52 +8,57 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
import gettext from 'sources/gettext';
|
||||
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
import React, { useContext, useState, useEffect } from 'react';
|
||||
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { InputSQL } from '../../../../../static/js/components/FormComponents';
|
||||
import { SchemaDiffEventsContext } from './SchemaDiffComponent';
|
||||
import { SCHEMA_DIFF_EVENT } from '../SchemaDiffConstants';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
header: {
|
||||
const Root = styled('div')(({theme}) => ({
|
||||
height: '100%',
|
||||
display:'flex',
|
||||
flexDirection:'column',
|
||||
'& .Results-header': {
|
||||
padding: '0.5rem',
|
||||
borderBottom: '1px solid ' + theme.otherVars.borderColor,
|
||||
},
|
||||
sqlContainer: {
|
||||
'& .Results-labelContainer': {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
'& .Results-label': {
|
||||
padding: '0.2rem 0.5rem',
|
||||
width: '33.33%'
|
||||
},
|
||||
},
|
||||
'& .Results-sqlContainer': {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
padding: '0rem 0rem 0.5rem',
|
||||
flexGrow: 1,
|
||||
overflow: 'hidden'
|
||||
},
|
||||
sqldata: {
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
flexDirection: 'column',
|
||||
padding: '0.2rem 0.5rem',
|
||||
width: '33.33%',
|
||||
},
|
||||
labelContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
label: {
|
||||
padding: '0.2rem 0.5rem',
|
||||
width: '33.33%'
|
||||
},
|
||||
sqlInput: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
overflow: 'hidden',
|
||||
height: '100%',
|
||||
}
|
||||
'& .Results-sqldata': {
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
flexDirection: 'column',
|
||||
padding: '0.2rem 0.5rem',
|
||||
width: '33.33%',
|
||||
height: '100%',
|
||||
'& .Results-sqlInput': {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
height: '100%',
|
||||
}
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export function Results() {
|
||||
const classes = useStyles();
|
||||
|
||||
const [sourceSQL, setSourceSQL] = useState(null);
|
||||
const [targetSQL, setTargetSQL] = useState(null);
|
||||
const [sqlDiff, setSqlDiff] = useState(null);
|
||||
|
@ -72,20 +77,19 @@ export function Results() {
|
|||
setSqlDiff(resultData.SQLdiff);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box className={classes.header}>
|
||||
<span>{gettext('DDL Comparison')}</span>
|
||||
(<Root>
|
||||
<Box className='Results-header'>
|
||||
<span>{gettext('DDL Comparision')}</span>
|
||||
</Box>
|
||||
<Box className={classes.labelContainer}>
|
||||
<Box className={classes.label}>{gettext('Source')}</Box>
|
||||
<Box className={classes.label}>{gettext('Target')}</Box>
|
||||
<Box className={classes.label}>{gettext('Difference')}</Box>
|
||||
<Box className='Results-labelContainer'>
|
||||
<Box className='Results-label'>{gettext('Source')}</Box>
|
||||
<Box className='Results-label'>{gettext('Target')}</Box>
|
||||
<Box className='Results-label'>{gettext('Difference')}</Box>
|
||||
</Box>
|
||||
<Box className={classes.sqlContainer}>
|
||||
<Box className={classes.sqldata}>
|
||||
<Box className={classes.sqlInput}>
|
||||
<Box className='Results-sqlContainer'>
|
||||
<Box className='Results-sqldata'>
|
||||
<Box className='Results-sqlInput'>
|
||||
<InputSQL
|
||||
onLable={true}
|
||||
value={sourceSQL}
|
||||
|
@ -97,8 +101,8 @@ export function Results() {
|
|||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box className={classes.sqldata}>
|
||||
<Box className={classes.sqlInput}>
|
||||
<Box className='Results-sqldata'>
|
||||
<Box className='Results-sqlInput'>
|
||||
<InputSQL
|
||||
onLable={true}
|
||||
value={targetSQL}
|
||||
|
@ -110,8 +114,8 @@ export function Results() {
|
|||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box className={classes.sqldata}>
|
||||
<Box className={classes.sqlInput}>
|
||||
<Box className='Results-sqldata'>
|
||||
<Box className='Results-sqlInput'>
|
||||
<InputSQL
|
||||
onLable={true}
|
||||
value={sqlDiff}
|
||||
|
@ -129,7 +133,7 @@ export function Results() {
|
|||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
</Root>)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import _ from 'lodash';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import React, { useState, useRef, useContext, useEffect } from 'react';
|
||||
|
@ -17,7 +18,6 @@ import { Box } from '@mui/material';
|
|||
import CompareArrowsRoundedIcon from '@mui/icons-material/CompareArrowsRounded';
|
||||
import FeaturedPlayListRoundedIcon from '@mui/icons-material/FeaturedPlayListRounded';
|
||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
|
||||
import { DefaultButton, PgButtonGroup, PgIconButton, PrimaryButton } from '../../../../../static/js/components/Buttons';
|
||||
import { FilterIcon } from '../../../../../static/js/components/ExternalIcon';
|
||||
|
@ -26,21 +26,19 @@ import { FILTER_NAME, MENUS, MENUS_COMPARE_CONSTANT, SCHEMA_DIFF_EVENT, IGNORE_O
|
|||
import { SchemaDiffContext, SchemaDiffEventsContext } from './SchemaDiffComponent';
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
emptyIcon: {
|
||||
width: '1.5rem'
|
||||
const Root = styled('div')(({theme}) => ({
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
'& .SchemaDiffButtons-compareBtn': {
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
justifyContent: 'flex-start',
|
||||
paddingLeft: '1.5rem',
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
paddingTop: '0.3rem',
|
||||
},
|
||||
},
|
||||
diff_btn: {
|
||||
marginRight: '1rem'
|
||||
},
|
||||
noactionBtn: {
|
||||
cursor: 'default',
|
||||
'&:hover': {
|
||||
backgroundColor: 'inherit',
|
||||
cursor: 'default'
|
||||
}
|
||||
},
|
||||
scriptBtn: {
|
||||
'& .SchemaDiffButtons-scriptBtn': {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
paddingRight: '0.3rem',
|
||||
|
@ -49,26 +47,22 @@ const useStyles = makeStyles((theme) => ({
|
|||
flexGrow: 1,
|
||||
},
|
||||
},
|
||||
filterBtn: {
|
||||
'&.SchemaDiffButtons-filterBtn': {
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
paddingTop: '0.3rem',
|
||||
flexGrow: 1,
|
||||
}
|
||||
},
|
||||
compareBtn: {
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
justifyContent: 'flex-start',
|
||||
paddingLeft: '1.5rem',
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
paddingTop: '0.3rem',
|
||||
},
|
||||
}
|
||||
'& .SchemaDiffButtons-noactionBtn': {
|
||||
cursor: 'default',
|
||||
'&:hover': {
|
||||
backgroundColor: 'inherit',
|
||||
cursor: 'default'
|
||||
}
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export function SchemaDiffButtonComponent({ sourceData, targetData, selectedRowIds, rows, compareParams, filterParams = [FILTER_NAME.DIFFERENT, FILTER_NAME.SOURCE_ONLY, FILTER_NAME.TARGET_ONLY] }) {
|
||||
const classes = useStyles();
|
||||
|
||||
const filterRef = useRef(null);
|
||||
const compareRef = useRef(null);
|
||||
|
||||
|
@ -164,8 +158,8 @@ export function SchemaDiffButtonComponent({ sourceData, targetData, selectedRowI
|
|||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box className={classes.compareBtn}>
|
||||
(<Root>
|
||||
<Box className='SchemaDiffButtons-compareBtn'>
|
||||
<PgButtonGroup size="small" disabled={isDisableCompare}>
|
||||
<PrimaryButton startIcon={<CompareArrowsRoundedIcon />}
|
||||
onClick={compareDiff}>{gettext('Compare')}</PrimaryButton>
|
||||
|
@ -173,14 +167,14 @@ export function SchemaDiffButtonComponent({ sourceData, targetData, selectedRowI
|
|||
name={MENUS.COMPARE} ref={compareRef} onClick={toggleMenu} ></PgIconButton>
|
||||
</PgButtonGroup>
|
||||
</Box>
|
||||
<Box className={classes.scriptBtn}>
|
||||
<Box className='SchemaDiffButtons-scriptBtn'>
|
||||
<PgButtonGroup size="small" disabled={selectedRowIds?.length <= 0}>
|
||||
<DefaultButton startIcon={<FeaturedPlayListRoundedIcon />} onClick={generateScript}>{gettext('Generate Script')}</DefaultButton>
|
||||
</PgButtonGroup>
|
||||
</Box>
|
||||
<Box className={classes.filterBtn}>
|
||||
<Box className='SchemaDiffButtons-filterBtn'>
|
||||
<PgButtonGroup size="small" disabled={isDisableCompare} style={{ paddingRight: '0.3rem' }}>
|
||||
<DefaultButton startIcon={<FilterIcon />} className={classes.noactionBtn}
|
||||
<DefaultButton startIcon={<FilterIcon />} className='SchemaDiffButtons-noactionBtn'
|
||||
>{gettext('Filter')}</DefaultButton>
|
||||
<PgIconButton title={gettext('Filter')} disabled={isDisableCompare} icon={<KeyboardArrowDownIcon />} splitButton
|
||||
name={MENUS.FILTER} ref={filterRef} onClick={toggleMenu} ></PgIconButton>
|
||||
|
@ -218,7 +212,7 @@ export function SchemaDiffButtonComponent({ sourceData, targetData, selectedRowI
|
|||
<PgMenuItem hasCheck checked={selectedFilters.includes(FILTER_NAME.TARGET_ONLY)}
|
||||
onClick={() => { selectFilterOption(FILTER_NAME.TARGET_ONLY); }}>{FILTER_NAME.TARGET_ONLY}</PgMenuItem>
|
||||
</PgMenu>
|
||||
</>
|
||||
</Root>)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import React, { useContext, useEffect, useState } from 'react';
|
|||
import { Box, Grid } from '@mui/material';
|
||||
import InfoRoundedIcon from '@mui/icons-material/InfoRounded';
|
||||
import HelpIcon from '@mui/icons-material/HelpRounded';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
|
||||
|
||||
import gettext from 'sources/gettext';
|
||||
import url_for from 'sources/url_for';
|
||||
|
@ -33,32 +33,6 @@ import { openSocket, socketApiGet } from '../../../../../static/js/socket_instan
|
|||
import { parseApiError } from '../../../../../static/js/api_instance';
|
||||
import { usePgAdmin } from '../../../../../static/js/BrowserComponent';
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
table: {
|
||||
minWidth: 650,
|
||||
},
|
||||
summaryContainer: {
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
overflow: 'auto',
|
||||
},
|
||||
note: {
|
||||
marginTop: '1.2rem',
|
||||
textAlign: 'center',
|
||||
},
|
||||
helpBtn: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row-reverse',
|
||||
paddingRight: '0.3rem'
|
||||
},
|
||||
compareComp: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
diffBtn: {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end'
|
||||
}
|
||||
}));
|
||||
|
||||
function generateFinalScript(script_array, scriptHeader, script_body) {
|
||||
_.each(Object.keys(script_array).reverse(), function (s) {
|
||||
|
@ -114,7 +88,6 @@ const onHelpClick=()=>{
|
|||
};
|
||||
|
||||
export function SchemaDiffCompare({ params }) {
|
||||
const classes = useStyles();
|
||||
const schemaDiffToolContext = useContext(SchemaDiffContext);
|
||||
const eventBus = useContext(SchemaDiffEventsContext);
|
||||
|
||||
|
@ -728,7 +701,7 @@ export function SchemaDiffCompare({ params }) {
|
|||
diff_type={TYPE.SOURCE}
|
||||
></InputComponent>
|
||||
</Grid>
|
||||
<Grid item lg={5} md={5} sm={2} xs={2} className={classes.helpBtn}>
|
||||
<Grid item lg={5} md={5} sm={2} xs={2} sx={{ display: 'flex',flexDirection: 'row-reverse',paddingRight: '0.3rem'}}>
|
||||
<PgButtonGroup size="small">
|
||||
<PgIconButton data-test='schema-diff-help' title={gettext('Help')} icon={<HelpIcon />} onClick={onHelpClick} />
|
||||
</PgButtonGroup>
|
||||
|
@ -752,7 +725,7 @@ export function SchemaDiffCompare({ params }) {
|
|||
></InputComponent>
|
||||
</Grid>
|
||||
|
||||
<Grid item lg={5} md={5} sm={12} xs={12} className={classes.diffBtn}>
|
||||
<Grid item lg={5} md={5} sm={12} xs={12}>
|
||||
<SchemaDiffButtonComponent
|
||||
sourceData={{
|
||||
'sid': selectedSourceSid,
|
||||
|
@ -792,7 +765,7 @@ export function SchemaDiffCompare({ params }) {
|
|||
}}
|
||||
></ResultGridComponent>
|
||||
:
|
||||
<Box className={classes.note}>
|
||||
<Box sx={{ marginTop: '1.2rem',textAlign: 'center'}}>
|
||||
<InfoRoundedIcon style={{ fontSize: '1.2rem' }} />
|
||||
{gettext(' Source and Target database server must be of the same major version.')}<br />
|
||||
<strong>{gettext(' Database Compare:')}</strong>
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
|
||||
|
||||
import React, { createContext, useMemo, useRef } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
import {DividerBox} from 'rc-dock';
|
||||
|
||||
import url_for from 'sources/url_for';
|
||||
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
|
||||
import { Results } from './Results';
|
||||
import { SchemaDiffCompare } from './SchemaDiffCompare';
|
||||
|
@ -24,21 +24,22 @@ import getApiInstance, { callFetch } from '../../../../../static/js/api_instance
|
|||
import { useModal } from '../../../../../static/js/helpers/ModalProvider';
|
||||
import usePreferences from '../../../../../preferences/static/js/store';
|
||||
|
||||
|
||||
export const SchemaDiffEventsContext = createContext();
|
||||
export const SchemaDiffContext = createContext();
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
resultPanle: {
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
'& .SchemaDiff-resultPanel': {
|
||||
backgroundColor: theme.palette.default.main,
|
||||
zIndex: 5,
|
||||
border: '1px solid ' + theme.otherVars.borderColor,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
height: 0,
|
||||
overflow: 'hidden',
|
||||
height: 0,
|
||||
},
|
||||
comparePanel:{
|
||||
'& .SchemaDiff-comparePanel': {
|
||||
overflow: 'hidden',
|
||||
border: '1px solid ' + theme.otherVars.borderColor,
|
||||
display: 'flex',
|
||||
|
@ -49,7 +50,6 @@ const useStyles = makeStyles((theme) => ({
|
|||
}));
|
||||
|
||||
export default function SchemaDiffComponent({params}) {
|
||||
const classes = useStyles();
|
||||
const eventBus = useRef(new EventBus());
|
||||
const containerRef = React.useRef(null);
|
||||
const api = getApiInstance();
|
||||
|
@ -85,14 +85,14 @@ export default function SchemaDiffComponent({params}) {
|
|||
return (
|
||||
<SchemaDiffContext.Provider value={schemaDiffContextValue}>
|
||||
<SchemaDiffEventsContext.Provider value={eventBus.current}>
|
||||
<Box display="flex" flexDirection="column" flexGrow="1" height="100%" tabIndex="0" style={{minHeight: 80}}>
|
||||
<StyledBox display="flex" flexDirection="column" flexGrow="1" height="100%" tabIndex="0" style={{minHeight: 80}}>
|
||||
<DividerBox mode='vertical' style={{flexGrow: 1}}>
|
||||
<div className={classes.comparePanel} id="schema-diff-compare-container" ref={containerRef}><SchemaDiffCompare params={params} /></div>
|
||||
<div className={classes.resultPanle} id="schema-diff-result-container">
|
||||
<div className='SchemaDiff-comparePanel' id="schema-diff-compare-container" ref={containerRef}><SchemaDiffCompare params={params} /></div>
|
||||
<div className='SchemaDiff-resultPanel' id="schema-diff-result-container">
|
||||
<Results />
|
||||
</div>
|
||||
</DividerBox>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
</SchemaDiffEventsContext.Provider>
|
||||
</SchemaDiffContext.Provider>
|
||||
);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React, { useState, useMemo, useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import HelpIcon from '@mui/icons-material/HelpRounded';
|
||||
|
@ -16,84 +16,87 @@ import pgAdmin from 'sources/pgadmin';
|
|||
import gettext from 'sources/gettext';
|
||||
import url_for from 'sources/url_for';
|
||||
import Loader from 'sources/components/Loader';
|
||||
import clsx from 'clsx';
|
||||
import getApiInstance, { parseApiError } from '../../../../static/js/api_instance';
|
||||
import { PrimaryButton, PgIconButton } from '../../../../static/js/components/Buttons';
|
||||
import { useModalStyles } from '../../../../static/js/helpers/ModalProvider';
|
||||
import { ModalContent } from '../../../../static/js/components/ModalContent';
|
||||
import { FormFooterMessage, InputSelect, InputText, MESSAGE_TYPE } from '../../../../static/js/components/FormComponents';
|
||||
import PgReactDataGrid from '../../../../static/js/components/PgReactDataGrid';
|
||||
|
||||
const pgBrowser = pgAdmin.Browser;
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
grid: {
|
||||
fontSize: '13px',
|
||||
'& .rdg-header-row': {
|
||||
'& .rdg-cell': {
|
||||
padding: '0px 4px',
|
||||
}
|
||||
},
|
||||
'& .rdg-cell': {
|
||||
padding: '0px 4px',
|
||||
'&[aria-colindex="1"]': {
|
||||
padding: '0px 4px',
|
||||
'&.rdg-editor-container': {
|
||||
padding: '0px',
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
toolbar: {
|
||||
const StyledBox = styled(Box)(({theme}) => ({
|
||||
'& .SearchObjects-toolbar': {
|
||||
padding: '4px',
|
||||
display: 'flex',
|
||||
...theme.mixins.panelBorder?.bottom,
|
||||
'& .SearchObjects-inputSearch': {
|
||||
lineHeight: 1,
|
||||
},
|
||||
'& .SearchObjects-Btnmargin': {
|
||||
marginLeft: '0.25rem',
|
||||
},
|
||||
},
|
||||
inputSearch: {
|
||||
lineHeight: 1,
|
||||
},
|
||||
footer1: {
|
||||
'& .SearchObjects-footer1': {
|
||||
justifyContent: 'space-between',
|
||||
padding: '4px 8px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
borderTop: `1px solid ${theme.otherVars.inputBorderColor}`,
|
||||
},
|
||||
footer: {
|
||||
'&.SearchObjects-footer': {
|
||||
borderTop: `1px solid ${theme.otherVars.inputBorderColor} !important`,
|
||||
padding: '0.5rem',
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
background: theme.otherVars.headerBg,
|
||||
},
|
||||
gridCell: {
|
||||
display: 'inline-block',
|
||||
height: '1.3rem',
|
||||
width: '1.3rem',
|
||||
'& .SearchObjects-grid': {
|
||||
fontSize: '13px !important',
|
||||
'& .rdg-header-row': {
|
||||
'& .rdg-cell': {
|
||||
padding: '0px 4px !important',
|
||||
}
|
||||
},
|
||||
'& .rdg-cell': {
|
||||
padding: '0px 4px',
|
||||
'&[aria-colindex="1"]': {
|
||||
padding: '0px 4px !important',
|
||||
'&.rdg-editor-container': {
|
||||
padding: '0px',
|
||||
},
|
||||
},
|
||||
'& .SearchObjects-textWrap': {
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden'
|
||||
},
|
||||
'& .SearchObjects-cellMuted': {
|
||||
color: `${theme.otherVars.textMuted} !important`,
|
||||
cursor: 'default !important',
|
||||
},
|
||||
'& .SearchObjects-gridCell': {
|
||||
display: 'inline-block',
|
||||
height: '1.3rem',
|
||||
width: '1.3rem',
|
||||
'& .SearchObjects-funcArgs': {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
|
||||
},
|
||||
}
|
||||
},
|
||||
funcArgs: {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
cellMuted: {
|
||||
color: `${theme.otherVars.textMuted} !important`,
|
||||
cursor: 'default !important',
|
||||
},
|
||||
textWrap: {
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden'
|
||||
}
|
||||
}));
|
||||
|
||||
const pgBrowser = pgAdmin.Browser;
|
||||
|
||||
function ObjectNameFormatter({row}) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<div className='rdg-cell-value'>
|
||||
<Box className={row.show_node ? '' : classes.cellMuted}>
|
||||
<span className={clsx(classes.gridCell, row.icon)}></span>
|
||||
<StyledBox className={row.show_node ? '' : 'SearchObjects-cellMuted'}>
|
||||
<span className={('SearchObjects-gridCell ' + row.icon)}></span>
|
||||
{row.name}
|
||||
{row.other_info != null && row.other_info != '' && (
|
||||
<span tabIndex="-1" className={classes.funcArgs} onClick={()=>{row.showArgs = true;}} onKeyDown={()=>{/* no need */}}> {row?.showArgs ? `(${row.other_info})` : '(...)'}</span>
|
||||
<span tabIndex="-1" className='SearchObjects-funcArgs' onClick={()=>{row.showArgs = true;}} onKeyDown={()=>{/* no need */}}> {row?.showArgs ? `(${row.other_info})` : '(...)'}</span>
|
||||
)}
|
||||
</Box>
|
||||
</StyledBox>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -102,7 +105,7 @@ ObjectNameFormatter.propTypes = {
|
|||
};
|
||||
|
||||
function TypePathFormatter({row, column}) {
|
||||
const classes = useStyles();
|
||||
|
||||
let val = '';
|
||||
|
||||
if(column.key == 'type') {
|
||||
|
@ -112,7 +115,7 @@ function TypePathFormatter({row, column}) {
|
|||
}
|
||||
|
||||
return (
|
||||
<Box className={clsx(classes.textWrap, row.show_node ? '' : classes.cellMuted)}>{val}</Box>
|
||||
<Box className={'SearchObjects-textWrap ' + (row.show_node ? '' : 'SearchObjects-cellMuted')}>{val}</Box>
|
||||
);
|
||||
}
|
||||
TypePathFormatter.propTypes = {
|
||||
|
@ -270,8 +273,6 @@ function getComparator(sortColumn) {
|
|||
};
|
||||
}
|
||||
export default function SearchObjects({nodeData}) {
|
||||
const classes = useStyles();
|
||||
const modalClasses = useModalStyles();
|
||||
const [type, setType] = React.useState('all');
|
||||
const [loaderText, setLoaderText] = useState('');
|
||||
const [search, setSearch] = useState('');
|
||||
|
@ -392,21 +393,21 @@ export default function SearchObjects({nodeData}) {
|
|||
};
|
||||
|
||||
return (
|
||||
<Box display="flex" flexDirection="column" height="100%" className={modalClasses.container}>
|
||||
<Box flexGrow="1" display="flex" flexDirection="column" position="relative" overflow="hidden">
|
||||
<ModalContent>
|
||||
<StyledBox>
|
||||
<Loader message={loaderText} />
|
||||
<Box className={classes.toolbar}>
|
||||
<InputText type="search" className={classes.inputSearch} data-label="search" placeholder={gettext('Type at least 3 characters')} value={search} onChange={setSearch} onKeyPress={onEnterPress}/>
|
||||
<Box style={{marginLeft: '4px', width: '50%'}}>
|
||||
<Box className='SearchObjects-toolbar'>
|
||||
<InputText type="search" className='SearchObjects-inputSearch' data-label="search" placeholder={gettext('Type at least 3 characters')} value={search} onChange={setSearch} onKeyPress={onEnterPress}/>
|
||||
<Box sx={{marginLeft: '4px', width: '50%'}}>
|
||||
<InputSelect value={type} controlProps={{allowClear: false}} options={typeOptions} onChange={(v)=>setType(v)}/>
|
||||
</Box>
|
||||
<PrimaryButton style={{width: '120px'}} data-test="search" className={modalClasses.margin} startIcon={<SearchRoundedIcon />}
|
||||
<PrimaryButton style={{width: '120px'}} data-test="search" className='SearchObjects-Btnmargin' startIcon={<SearchRoundedIcon />}
|
||||
onClick={onSearch} disabled={search.length < 3}>{gettext('Search')}</PrimaryButton>
|
||||
</Box>
|
||||
<Box flexGrow="1" display="flex" flexDirection="column" position="relative" overflow="hidden">
|
||||
<PgReactDataGrid
|
||||
id="searchobjects"
|
||||
className={classes.grid}
|
||||
className='SearchObjects-grid'
|
||||
hasSelectColumn={false}
|
||||
columns={columns}
|
||||
rows={sortedItems}
|
||||
|
@ -423,17 +424,15 @@ export default function SearchObjects({nodeData}) {
|
|||
onItemEnter={onItemEnter}
|
||||
/>
|
||||
</Box>
|
||||
<Box className={classes.footer1}>
|
||||
<Box className='SearchObjects-footer1'>
|
||||
<Box>{footerText}</Box>
|
||||
</Box>
|
||||
<FormFooterMessage type={MESSAGE_TYPE.ERROR} message={errorMsg} closable onClose={()=>setErrorMsg('')} />
|
||||
</Box>
|
||||
<Box className={classes.footer}>
|
||||
<Box>
|
||||
<PgIconButton data-test="dialog-help" onClick={onDialogHelp} icon={<HelpIcon />} title={gettext('Help for this dialog.')} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</StyledBox>
|
||||
<StyledBox className='SearchObjects-footer'>
|
||||
<PgIconButton data-test="dialog-help" onClick={onDialogHelp} icon={<HelpIcon />} title={gettext('Help for this dialog.')} />
|
||||
</StyledBox>
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user