Fix Dashboard minor UI issues.

Fix the issue where PG logs doesn't display in CSV or JSON format even if it gets selected through the UI.
pull/7685/head
Khushboo Vashi 2024-07-11 10:26:42 +05:30
parent 6b012193e3
commit b303693ae8
5 changed files with 69 additions and 64 deletions

View File

@ -536,16 +536,18 @@ def logs(log_format=None, disp_format=None, sid=None, page=0):
status, _format = g.conn.execute_scalar(sql)
# Check the requested format is available or not
log_format = ''
if log_format == 'C' and 'csvlog' in _format:
log_format = 'csvlog'
elif log_format == 'J' and 'jsonlog' in _format:
log_format = 'jsonlog'
else:
log_format = ''
sql = render_template(
"/".join([g.template_path, 'log_stat.sql']),
log_format=log_format, conn=g.conn
)
status, res = g.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=res)
@ -557,6 +559,12 @@ def logs(log_format=None, disp_format=None, sid=None, page=0):
file_stat = json.loads(res[0])
if file_stat <= 0:
return ajax_response(
response={'logs_disabled': True},
status=200
)
_start = 0
_end = ON_DEMAND_LOG_COUNT
page = int(page)
@ -573,13 +581,12 @@ def logs(log_format=None, disp_format=None, sid=None, page=0):
log_format=log_format, conn=g.conn
)
status, res = g.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=res)
final_res = res['rows'][0]['pg_read_file'].split('\n')
# Json format
if log_format == 'J':
if log_format == 'jsonlog':
for f in final_res:
try:
_tmp_log = json.loads(f)
@ -591,7 +598,7 @@ def logs(log_format=None, disp_format=None, sid=None, page=0):
pass
# CSV format
elif log_format == 'C':
elif log_format == 'csvlog':
for f in final_res:
try:
_tmp_log = f.split(',')

View File

@ -83,15 +83,19 @@ const Root = styled('div')(({theme}) => ({
},
'& .Dashboard-textArea': {
height: '88%',
}
},
'& .RefreshButtons': {
display: 'flex',
},
'& .Mui-disabled': {
pointerEvents: 'auto',
},
},
},
},
'& .Dashboard-emptyPanel': {
width: '100%',
height: '100%',
background: theme.otherVars.emptySpaceBg,
overflow: 'auto',
padding: '8px',
display: 'flex',
},
@ -234,7 +238,7 @@ function getCancelCell(pgAdmin, sid, did, canTakeAction, onSuccess) {
function CustomRefresh({refresh, setRefresh}) {
return (
<RefreshButton onClick={(e) => {
<RefreshButton noBorder={false} onClick={(e) => {
e.preventDefault();
setRefresh(!refresh);
}}/>
@ -245,12 +249,8 @@ CustomRefresh.propTypes = {
setRefresh: PropTypes.func,
};
function ActiveOnlyHeader({activeOnly, setActiveOnly, refresh, setRefresh}) {
return (<Fragment>
<RefreshButton onClick={(e) => {
e.preventDefault();
setRefresh(!refresh);
}}/>
function ActiveOnlyHeader({activeOnly, setActiveOnly}) {
return (
<InputCheckbox
label={gettext('Active sessions only')}
labelPlacement="end"
@ -263,7 +263,7 @@ function ActiveOnlyHeader({activeOnly, setActiveOnly, refresh, setRefresh}) {
controlProps={{
label: gettext('Active sessions only'),
}}
/></Fragment>
/>
);
}
ActiveOnlyHeader.propTypes = {
@ -768,7 +768,6 @@ function Dashboard({
enableFilters: true,
minSize: 50,
size: 80,
cell: ({ value }) => String(value)
},
];
@ -824,8 +823,10 @@ function Dashboard({
let _format = res.data;
let _frm = [
{'label': gettext('Text'), 'value': 'T', 'disabled': !_format.includes('stderr')},
{'label': gettext('JSON'), 'value': 'J', 'disabled': !_format.includes('jsonlog')},
{'label': gettext('CSV'), 'value': 'C', 'disabled': !_format.includes('csvlog')}
{'label': gettext('JSON'), 'value': 'J', 'disabled': !_format.includes('jsonlog'),
tooltip: gettext('Enable JSON logging from postgresql.conf.')},
{'label': gettext('CSV'), 'value': 'C', 'disabled': !_format.includes('csvlog'),
tooltip: gettext('Enable CSV logging from postgres.conf.')}
];
setLogConfigFormat(_frm);
})
@ -840,7 +841,6 @@ function Dashboard({
useEffect(() => {
if (mainTabVal == 0) return;
// disable replication tab
if(!treeNodeInfo?.server?.replication_type && mainTabVal == 5) {
setMainTabVal(0);
@ -879,7 +879,7 @@ function Dashboard({
if (node) {
setSsMsg(gettext('Loading logs...'));
setDashData([]);
if (mainTabVal != 4 && mainTabVal != 5) {
if (mainTabVal == 1 || mainTabVal == 2 || mainTabVal == 3) {
api({
url: url,
type: 'GET',
@ -1081,6 +1081,7 @@ function Dashboard({
<TabPanel value={mainTabVal} index={1} classNameRoot='Dashboard-tabPanel'>
{!_.isUndefined(preferences) && preferences.show_activity && (
<Fragment>
<CustomRefresh refresh={refresh} setRefresh={setRefresh}/>
<SectionContainer title={gettext('Sessions')} style={{height: 'auto', minHeight: '200px', paddingBottom: '20px'}}
>
<PgTable
@ -1094,7 +1095,6 @@ function Dashboard({
</SectionContainer>
<SectionContainer title={gettext('Locks')} style={{height: 'auto', minHeight: '200px', paddingBottom: '20px'}}>
<PgTable
customHeader={<CustomRefresh refresh={refresh} setRefresh={setRefresh}/>}
caveTable={false}
tableNoBorder={false}
columns={databaseLocksColumns}
@ -1103,7 +1103,6 @@ function Dashboard({
</SectionContainer>
<SectionContainer title={gettext('Prepared Transactions')} style={{height: 'auto', minHeight: '200px', paddingBottom: '20px'}}>
<PgTable
customHeader={<CustomRefresh refresh={refresh} setRefresh={setRefresh}/>}
caveTable={false}
tableNoBorder={false}
columns={databasePreparedColumns}

View File

@ -23,11 +23,11 @@ const StyledPgIconButton = styled(PgIconButton)(({theme}) => ({
}
}));
export default function RefreshButton({onClick}) {
export default function RefreshButton({onClick, noBorder=true}) {
return (
<StyledPgIconButton
size="xs"
noBorder
noBorder={noBorder}
className='RefreshButtons'
icon={<CachedOutlinedIcon />}
onClick={onClick}
@ -39,5 +39,6 @@ export default function RefreshButton({onClick}) {
}
RefreshButton.propTypes = {
onClick: PropTypes.func
onClick: PropTypes.func,
noBorder: PropTypes.bool
};

View File

@ -676,7 +676,7 @@ export const InputToggle = forwardRef(({ cid, value, onChange, options, disabled
const isDisabled = disabled || option.disabled || (readonly && !isSelected);
return <ToggleCheckButton ref={i == 0 ? ref : null} key={option.label} label={option.label}
selected={isSelected} value={option.value} disabled={isDisabled}
selected={isSelected} value={option.value} disabled={isDisabled} title={option.tooltip}
/>;
})
}

View File

@ -118,46 +118,44 @@ export function Table({ columns, data, hasSelectRow, schema, sortOptions, tableP
let totalFetched = 0;
let totalDBRowCount = 0;
if (loadNextPage) {
//Infinite scrolling
const { _data, fetchNextPage, isFetching } =
useInfiniteQuery({
queryKey: ['logs'],
queryFn: async () => {
const fetchedData = await loadNextPage();
return fetchedData;
},
initialPageParam: 0,
getNextPageParam: (_lastGroup, groups) => groups.length,
refetchOnWindowFocus: false,
placeholderData: keepPreviousData,
});
flatData = _data || [];
totalFetched = flatData.length;
//called on scroll and possibly on mount to fetch more data as the user scrolls and reaches bottom of table
fetchMoreOnBottomReached = React.useCallback(
(containerRefElement = HTMLDivElement | null) => {
if (containerRefElement) {
const { scrollHeight, scrollTop, clientHeight } = containerRefElement;
//once the user has scrolled within 500px of the bottom of the table, fetch more data if we can
if (
scrollHeight - scrollTop - clientHeight < 500 &&
!isFetching
) {
fetchNextPage();
}
}
//Infinite scrolling
const { _data, fetchNextPage, isFetching } =
useInfiniteQuery({
queryKey: ['logs'],
queryFn: async () => {
const fetchedData = await loadNextPage();
return fetchedData;
},
[fetchNextPage, isFetching, totalFetched, totalDBRowCount]
);
initialPageParam: 0,
getNextPageParam: (_lastGroup, groups) => groups.length,
refetchOnWindowFocus: false,
placeholderData: keepPreviousData,
});
//a check on mount and after a fetch to see if the table is already scrolled to the bottom and immediately needs to fetch more data
React.useEffect(() => {
fetchMoreOnBottomReached(tableRef.current);
}, [fetchMoreOnBottomReached]);
}
flatData = _data || [];
totalFetched = flatData.length;
//called on scroll and possibly on mount to fetch more data as the user scrolls and reaches bottom of table
fetchMoreOnBottomReached = React.useCallback(
(containerRefElement = HTMLDivElement | null) => {
if (containerRefElement) {
const { scrollHeight, scrollTop, clientHeight } = containerRefElement;
//once the user has scrolled within 500px of the bottom of the table, fetch more data if we can
if (
scrollHeight - scrollTop - clientHeight < 500 &&
!isFetching
) {
fetchNextPage();
}
}
},
[fetchNextPage, isFetching, totalFetched, totalDBRowCount]
);
//a check on mount and after a fetch to see if the table is already scrolled to the bottom and immediately needs to fetch more data
React.useEffect(() => {
fetchMoreOnBottomReached(tableRef.current);
}, [fetchMoreOnBottomReached]);
const table = useReactTable({
columns: finalColumns,