165 lines
5.8 KiB
JavaScript
165 lines
5.8 KiB
JavaScript
/////////////////////////////////////////////////////////////
|
|
//
|
|
// pgAdmin 4 - PostgreSQL Tools
|
|
//
|
|
// Copyright (C) 2013 - 2026, The pgAdmin Development Team
|
|
// This software is released under the PostgreSQL Licence
|
|
//
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
import { useState, useContext, useCallback } from 'react';
|
|
import { Box } from '@mui/material';
|
|
import { styled } from '@mui/material/styles';
|
|
import { InputSelect, FormInput } from './FormComponents';
|
|
import PropTypes from 'prop-types';
|
|
import CustomPropTypes from '../custom_prop_types';
|
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
|
import { PgIconButton } from './Buttons';
|
|
import getApiInstance from '../api_instance';
|
|
import url_for from 'sources/url_for';
|
|
import gettext from 'sources/gettext';
|
|
import { SchemaStateContext } from '../SchemaView/SchemaState';
|
|
import { usePgAdmin } from '../PgAdminProvider';
|
|
import { clearOptionsCache } from '../../../preferences/static/js/components/PreferencesHelper';
|
|
|
|
const StyledBox = styled(Box)(() => ({
|
|
display: 'flex',
|
|
alignItems: 'flex-start',
|
|
'& .SelectRefresh-selectContainer': {
|
|
flexGrow: 1,
|
|
},
|
|
'& .SelectRefresh-buttonContainer': {
|
|
marginLeft: '4px',
|
|
'& button': {
|
|
height: '30px',
|
|
width: '30px',
|
|
},
|
|
},
|
|
}));
|
|
|
|
function ChildContent({ cid, helpid, onRefreshClick, isRefreshing, ...props }) {
|
|
return (
|
|
<StyledBox>
|
|
<Box className="SelectRefresh-selectContainer">
|
|
<InputSelect {...props} cid={cid} helpid={helpid} />
|
|
</Box>
|
|
<Box className="SelectRefresh-buttonContainer">
|
|
<PgIconButton
|
|
onClick={onRefreshClick}
|
|
icon={<RefreshIcon />}
|
|
title={gettext('Refresh models')}
|
|
disabled={isRefreshing}
|
|
/>
|
|
</Box>
|
|
</StyledBox>
|
|
);
|
|
}
|
|
|
|
ChildContent.propTypes = {
|
|
cid: PropTypes.string,
|
|
helpid: PropTypes.string,
|
|
onRefreshClick: PropTypes.func,
|
|
isRefreshing: PropTypes.bool,
|
|
};
|
|
|
|
export function SelectRefresh({ required, className, label, helpMessage, testcid, controlProps, ...props }) {
|
|
const [optionsState, setOptionsState] = useState({ options: [], reloadBasis: 0 });
|
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
|
const schemaState = useContext(SchemaStateContext);
|
|
const pgAdmin = usePgAdmin();
|
|
const {
|
|
getOptionsOnRefresh,
|
|
optionsRefreshUrl,
|
|
optionsUrl,
|
|
refreshDeps,
|
|
...selectControlProps
|
|
} = controlProps;
|
|
|
|
const onRefreshClick = useCallback(() => {
|
|
// If we have an optionsRefreshUrl, make a POST request with dependent field values
|
|
if (optionsRefreshUrl && refreshDeps && schemaState) {
|
|
setIsRefreshing(true);
|
|
|
|
// Build the request body from dependent field values
|
|
const requestBody = {};
|
|
for (const [paramName, fieldId] of Object.entries(refreshDeps)) {
|
|
// Find the field value from schema state
|
|
// fieldId is the preference ID, we need to look it up in state
|
|
const fieldValue = schemaState.data?.[fieldId];
|
|
// Only include non-empty values
|
|
if (fieldValue !== undefined && fieldValue !== null && fieldValue !== '') {
|
|
requestBody[paramName] = fieldValue;
|
|
}
|
|
}
|
|
|
|
const api = getApiInstance();
|
|
const refreshUrl = url_for(optionsRefreshUrl);
|
|
|
|
api.post(refreshUrl, requestBody)
|
|
.then((res) => {
|
|
if (res.data?.data?.error) {
|
|
// Server returned an error message - clear options and show error
|
|
setOptionsState((prev) => ({ options: [], reloadBasis: prev.reloadBasis + 1 }));
|
|
pgAdmin.Browser.notifier.error(res.data.data.error);
|
|
} else if (res.data?.data?.models) {
|
|
const models = res.data.data.models;
|
|
// Clear the cache so next time preferences opens, it uses the refreshed data
|
|
if (optionsUrl) {
|
|
clearOptionsCache(optionsUrl);
|
|
}
|
|
setOptionsState((prev) => ({ options: models, reloadBasis: prev.reloadBasis + 1 }));
|
|
} else {
|
|
// No models returned - clear the list
|
|
setOptionsState((prev) => ({ options: [], reloadBasis: prev.reloadBasis + 1 }));
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
// Network or other error - clear options and show error
|
|
setOptionsState((prev) => ({ options: [], reloadBasis: prev.reloadBasis + 1 }));
|
|
const errMsg = err.response?.data?.errormsg || err.message || gettext('Failed to refresh models');
|
|
pgAdmin.Browser.notifier.error(errMsg);
|
|
})
|
|
.finally(() => {
|
|
setIsRefreshing(false);
|
|
});
|
|
} else if (getOptionsOnRefresh) {
|
|
// Fall back to the original getOptionsOnRefresh callback
|
|
setIsRefreshing(true);
|
|
getOptionsOnRefresh()
|
|
.then((res) => {
|
|
setOptionsState((prev) => ({ options: res, reloadBasis: prev.reloadBasis + 1 }));
|
|
})
|
|
.catch((err) => {
|
|
setOptionsState((prev) => ({ options: [], reloadBasis: prev.reloadBasis + 1 }));
|
|
const errMsg = err.message || gettext('Failed to refresh options');
|
|
pgAdmin.Browser.notifier.error(errMsg);
|
|
})
|
|
.finally(() => {
|
|
setIsRefreshing(false);
|
|
});
|
|
}
|
|
}, [optionsRefreshUrl, optionsUrl, refreshDeps, schemaState, getOptionsOnRefresh, pgAdmin]);
|
|
|
|
return (
|
|
<FormInput required={required} label={label} className={className} helpMessage={helpMessage} testcid={testcid}>
|
|
<ChildContent
|
|
options={optionsState.options}
|
|
optionsReloadBasis={optionsState.reloadBasis}
|
|
onRefreshClick={onRefreshClick}
|
|
controlProps={selectControlProps}
|
|
isRefreshing={isRefreshing}
|
|
{...props}
|
|
/>
|
|
</FormInput>
|
|
);
|
|
}
|
|
|
|
SelectRefresh.propTypes = {
|
|
required: PropTypes.bool,
|
|
label: PropTypes.string,
|
|
className: CustomPropTypes.className,
|
|
helpMessage: PropTypes.string,
|
|
testcid: PropTypes.string,
|
|
controlProps: PropTypes.object,
|
|
};
|