diff --git a/portals/devportal/src/main/webapp/source/src/app/components/Apis/Listing/SubscriptionPolicySelect.jsx b/portals/devportal/src/main/webapp/source/src/app/components/Apis/Listing/SubscriptionPolicySelect.jsx index 2c5d0f7ee8b..41dbe1f7136 100644 --- a/portals/devportal/src/main/webapp/source/src/app/components/Apis/Listing/SubscriptionPolicySelect.jsx +++ b/portals/devportal/src/main/webapp/source/src/app/components/Apis/Listing/SubscriptionPolicySelect.jsx @@ -18,9 +18,10 @@ import React from 'react'; import PropTypes from 'prop-types'; import { withStyles } from '@material-ui/core/styles'; -import Select from '@material-ui/core/Select'; +import Autocomplete from '@material-ui/lab/Autocomplete'; import MenuItem from '@material-ui/core/MenuItem'; import Button from '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; import { FormattedMessage } from 'react-intl'; import { ScopeValidation, resourceMethods, resourcePaths } from '../../Shared/ScopeValidation'; @@ -80,15 +81,17 @@ class SubscriptionPolicySelect extends React.Component { policies && (
- + )} + /> ) : (status === 'ON_HOLD') @@ -321,38 +321,40 @@ class SubscriptionTableData extends React.Component { ) : (
- - )} + option.label ?? option} + getOptionSelected={(option, value) => option.value === value} value={selectedTier} - name='throttlingPolicy' - onChange={(e) => this.setSelectedTier(e.target.value)} - helperText={( - this.setSelectedTier(newValue.value)} + renderInput={(params) => ( + + )} + helperText={( + + )} + margin='normal' + variant='outlined' /> )} - margin='normal' - variant='outlined' - > - {this.state.tiers.map((tier) => ( - - {tier.label} - - ))} - + /> { (status === 'TIER_UPDATE_PENDING') && (
diff --git a/portals/devportal/src/main/webapp/source/src/app/components/Shared/AppsAndKeys/SubscribeToApi.jsx b/portals/devportal/src/main/webapp/source/src/app/components/Shared/AppsAndKeys/SubscribeToApi.jsx index e5752854a63..0c8221393b5 100755 --- a/portals/devportal/src/main/webapp/source/src/app/components/Shared/AppsAndKeys/SubscribeToApi.jsx +++ b/portals/devportal/src/main/webapp/source/src/app/components/Shared/AppsAndKeys/SubscribeToApi.jsx @@ -112,12 +112,15 @@ const styles = theme => ({ }, appDropDown: { color: theme.palette.getContrastText(theme.palette.background.paper), + '&:hover': { + backgroundColor: 'unset', + }, }, }); const subscribeToApi = (props) => { const [appSelected, setAppSelected] = useState(''); - const [policySelected, setPolicySelected] = useState(''); + const [policySelected, setPolicySelected] = useState({tierName:''}); const [applicationsList, setApplicationsList] = useState([]); const { classes, @@ -128,9 +131,22 @@ const subscribeToApi = (props) => { renderSmall, } = props; + let sortedThrottlingPolicyList = throttlingPolicyList; + useEffect(() => { + sortedThrottlingPolicyList = throttlingPolicyList.sort((a, b) => { + // Sort by 'COMMERCIAL' tier plan first + if (a.tierPlan === 'COMMERCIAL' && b.tierPlan !== 'COMMERCIAL') { + return -1; + } else if (a.tierPlan !== 'COMMERCIAL' && b.tierPlan === 'COMMERCIAL') { + return 1; + } + + // For options within the same tier plan, sort alphabetically + return a.tierName.localeCompare(b.tierName); + }); if (throttlingPolicyList && throttlingPolicyList[0]) { - setPolicySelected(throttlingPolicyList[0].tierName); + setPolicySelected(sortedThrottlingPolicyList[0]); } }, [throttlingPolicyList]); @@ -158,8 +174,8 @@ const subscribeToApi = (props) => { setAppSelected(value); break; case 'throttlingPolicy': - newRequest.throttlingPolicy = target.value; - setPolicySelected(target.value); + newRequest.throttlingPolicy = value.tierName; + setPolicySelected(value); break; default: break; @@ -182,6 +198,7 @@ const subscribeToApi = (props) => { id="application-subscribe" aria-describedby='application-helper-text' options={applicationsList} + disableClearable value={(applicationsList.length !== 0 && appSelected === '') ? applicationsList[0] : appSelected} onChange={(e, value) => handleChange('application', e, value)} @@ -197,7 +214,7 @@ const subscribeToApi = (props) => { )} - {throttlingPolicyList && ( + {sortedThrottlingPolicyList && ( { defaultMessage='Business Plan' /> - } - displayEmpty - name='policySelected' - className={classes.selectEmpty} - > - {throttlingPolicyList.map(policy => ( - + options={sortedThrottlingPolicyList} + disableClearable + value={policySelected} + getOptionLabel={(option) => option.tierName} + getOptionSelected={(option, value) => option.tierName === value.tierName} + onChange={(e, value) => handleChange('throttlingPolicy', e, value)} + classes={{ root: classes.fullWidth }} + renderInput={(params) => } + groupBy={(option) => option.tierPlan === 'COMMERCIAL' ? 'Commercial' : 'Free'} + renderOption={(policy) => ( + {policy.tierPlan === 'COMMERCIAL' ? ( { )} - ))} - + )} + /> {' '} - {throttlingPolicyList.map((policy, index) => ( + {sortedThrottlingPolicyList.map((policy, index) => ( {policy.tierName} - {index !== throttlingPolicyList.length - 1 && ,} + {index !== sortedThrottlingPolicyList.length - 1 && ,} ))} diff --git a/portals/devportal/src/main/webapp/source/src/app/components/Shared/AppsAndKeys/Tokens.jsx b/portals/devportal/src/main/webapp/source/src/app/components/Shared/AppsAndKeys/Tokens.jsx index 7ff8363a566..347afe84aa4 100755 --- a/portals/devportal/src/main/webapp/source/src/app/components/Shared/AppsAndKeys/Tokens.jsx +++ b/portals/devportal/src/main/webapp/source/src/app/components/Shared/AppsAndKeys/Tokens.jsx @@ -19,13 +19,14 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import PropTypes from 'prop-types'; import { withStyles } from '@material-ui/core/styles'; -import Input from '@material-ui/core/Input'; -import InputLabel from '@material-ui/core/InputLabel'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import Checkbox from '@material-ui/core/Checkbox'; +import CheckBoxIcon from '@material-ui/icons/CheckBox'; +import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; import FormControl from '@material-ui/core/FormControl'; +import TextField from '@material-ui/core/TextField'; import Typography from '@material-ui/core/Typography'; -import Chip from '@material-ui/core/Chip'; -import MenuItem from '@material-ui/core/MenuItem'; -import Select from '@material-ui/core/Select'; + // Styles for Grid and Paper elements const styles = theme => ({ @@ -68,8 +69,11 @@ const MenuProps = { getContentAnchorEl: null, }; +const icon = ; +const checkedIcon = ; + /** - * Used to display generate acctoken UI + * Used to display generate access token UI */ const tokens = (props) => { /** @@ -107,37 +111,41 @@ const tokens = (props) => { className={classes.FormControlOdd} disabled={subscriptionScopes.length === 0} > - - - - - } - renderValue={selected => ( -
- {selected.map(value => ( - - ))} -
+ onChange={(e, newValue) => handleChange('scopesSelected', { target: { value: newValue } })} + renderOption={(option, { selected }) => ( + <> + + {option} + + )} + renderInput={(params) => ( + } + /> )} - MenuProps={MenuProps} - > - {subscriptionScopes.map(scope => ( - - {scope} - - ))} - + /> ({ textOverflow: 'ellipsis', }, })); + +const icon = ; +const checkedIcon = ; + /** * Render the categories drop down. * @param {JSON} props props passed from it's parents. @@ -60,104 +65,61 @@ function APICategories(props) { if (!categories.list) { return null; - } else if (categories.list.length === 0) { + } else { return ( - category.name)} + noOptionsText='No API categories defined' + disableCloseOnSelect + value={api.categories} + onChange={(e, newValue) => configDispatcher({ action: 'categories', value: newValue })} + renderOption={(category, { selected }) => ( <> - + {category} )} - margin='normal' - variant='outlined' - disabled - value='emptyMessage' - > - - - )} + renderInput={(params) => ( + + ) : ( + + ) + } + placeholder='Search API categories' + helperText='Select API Categories for the API' + margin='normal' + variant='outlined' + id='APICategories' /> - - - - ); - } else { - return ( - - - - )} - name='categories' - margin='normal' - variant='outlined' - disabled={isRestricted(['apim:api_create', 'apim:api_publish'], apiFromContext)} - value={api.categories} - SelectProps={{ - multiple: true, - renderValue: (selected) => (Array.isArray(selected) ? selected.join(', ') : selected), - MenuProps: { - anchorOrigin: { - vertical: 'bottom', - horizontal: 'left', - }, - getContentAnchorEl: null, - keepMounted: true, - PaperProps: { - style: { - maxHeight: 300, - maxWidth: 300, - }, - }, - }, - }} - onChange={(e) => configDispatcher({ action: 'categories', value: e.target.value })} - InputProps={{ - id: 'itest-id-categories-input', - }} - helperText='Select API Categories for the API' - > - { categories.list.map((category) => ( - - - - - ))} - + /> diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/operationComponents/OperationGovernance.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/operationComponents/OperationGovernance.jsx index 828e047f3f5..94a544eaa7e 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/operationComponents/OperationGovernance.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/operationComponents/OperationGovernance.jsx @@ -19,9 +19,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import Grid from '@material-ui/core/Grid'; +import Autocomplete from '@material-ui/lab/Autocomplete'; import Button from '@material-ui/core/Button'; import Box from '@material-ui/core/Box'; import Checkbox from '@material-ui/core/Checkbox'; +import CheckBoxIcon from '@material-ui/icons/CheckBox'; +import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; import Divider from '@material-ui/core/Divider'; import Typography from '@material-ui/core/Typography'; import Switch from '@material-ui/core/Switch'; @@ -32,13 +35,15 @@ import MenuItem from '@material-ui/core/MenuItem'; import Tooltip from '@material-ui/core/Tooltip'; import HelpOutline from '@material-ui/icons/HelpOutline'; import LaunchIcon from '@material-ui/icons/Launch'; -import ListSubheader from '@material-ui/core/ListSubheader'; import { Link } from 'react-router-dom'; import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; import ExpandLessIcon from '@material-ui/icons/ExpandLess'; import { useIntl, FormattedMessage } from 'react-intl'; import { getOperationScopes } from '../../operationUtils'; +const icon = ; +const checkedIcon = ; + /** * * Renders the security , throttling policies and scopes selection section in the operation collapsed page @@ -235,106 +240,68 @@ export default function OperationGovernance(props) { - - { operation['x-auth-type'] && operation['x-auth-type'].toLowerCase() !== 'none' ? ( - (Array.isArray(selected) ? selected.join(', ') : selected), + + {operation['x-auth-type'] && operation['x-auth-type'].toLowerCase() !== 'none' ? ( + option.shared ? 'Shared Scopes' : 'API Scopes'} + noOptionsText='No scopes available' + disableCloseOnSelect + value={operationScopes.map((scope) => ({ scope: { name: scope } }))} + getOptionLabel={(option) => option.scope.name} + getOptionSelected={(option, value) => option.scope.name === value.scope.name} + onChange={(event, newValue) => { + const selectedScopes = newValue.map((val) => val.scope.name); + operationsDispatcher({ + action: 'scopes', + data: { target, verb, value: selectedScopes ? [selectedScopes] : [] }, + }); }} - disabled={disableUpdate} - fullWidth - label={api.scopes.length !== 0 || sharedScopes ? intl.formatMessage({ - id: 'Apis.Details.Resources.components.operationComponents.' - + 'OperationGovernance.operation.scope.label.default', - defaultMessage: 'Operation scope', - }) : intl.formatMessage({ - id: 'Apis.Details.Resources.components.operationComponents.' - + 'OperationGovernance.operation.scope.label.notAvailable', - defaultMessage: 'No scope available', - })} - value={operationScopes} - onChange={({ target: { value } }) => operationsDispatcher({ - action: 'scopes', - data: { target, verb, value: value ? [value] : [] }, - })} - helperText={( - - )} - margin='dense' - variant='outlined' - id={verb + target + '-operation-scope-select'} - > - - - - {filteredApiScopes.length !== 0 ? filteredApiScopes.map((apiScope) => ( - - - {apiScope.scope.name} - - )) : ( - - - - - + renderOption={(option, { selected }) => ( + <> + + {option.scope.name} + )} - - - - {sharedScopes && sharedScopes.length !== 0 ? sharedScopes.map((sharedScope) => ( - - - {sharedScope.scope.name} - - )) : ( - - + style={{ width: 500 }} + renderInput={(params) => ( + - - + )} + margin='dense' + variant='outlined' + id={verb + target + '-operation-scope-select'} /> )} - - ) : null } + /> + ) : null} - + { operation['x-auth-type'] && operation['x-auth-type'].toLowerCase() !== 'none' ? !disableUpdate && ( diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/operationComponents/asyncapi/OperationGovernance.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/operationComponents/asyncapi/OperationGovernance.jsx index eba44dd017a..49fbdd0432d 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/operationComponents/asyncapi/OperationGovernance.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/operationComponents/asyncapi/OperationGovernance.jsx @@ -19,18 +19,19 @@ import React from 'react'; import PropTypes from 'prop-types'; import Grid from '@material-ui/core/Grid'; +import Autocomplete from '@material-ui/lab/Autocomplete'; import Checkbox from '@material-ui/core/Checkbox'; +import CheckBoxIcon from '@material-ui/icons/CheckBox'; +import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; import Divider from '@material-ui/core/Divider'; import Typography from '@material-ui/core/Typography'; import Switch from '@material-ui/core/Switch'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import FormControl from '@material-ui/core/FormControl'; import TextField from '@material-ui/core/TextField'; -import MenuItem from '@material-ui/core/MenuItem'; import Tooltip from '@material-ui/core/Tooltip'; import HelpOutline from '@material-ui/icons/HelpOutline'; import LaunchIcon from '@material-ui/icons/Launch'; -import ListSubheader from '@material-ui/core/ListSubheader'; import { Link } from 'react-router-dom'; import { useIntl, FormattedMessage } from 'react-intl'; import { isRestricted } from 'AppData/AuthManager'; @@ -50,6 +51,8 @@ export default function OperationGovernance(props) { const operationScopes = getAsyncAPIOperationScopes(operation[verb]); const filteredApiScopes = api.scopes.filter((sharedScope) => !sharedScope.shared); const intl = useIntl(); + const icon = ; + const checkedIcon = ; return ( <> @@ -108,110 +111,68 @@ export default function OperationGovernance(props) { - - { - operation['x-auth-type'] && operation['x-auth-type'].toLowerCase() !== 'none' ? ( - (Array.isArray(selected) ? selected.join(', ') : selected), - }} - disabled={disableUpdate} - fullWidth - label={api.scopes.length !== 0 || sharedScopes ? intl.formatMessage({ - id: 'Apis.Details.Topics.components.operationComponents.' - + 'OperationGovernance.operation.scope.label.default', - defaultMessage: 'Operation scope', - }) : intl.formatMessage({ - id: 'Apis.Details.Topics.components.operationComponents.' - + 'OperationGovernance.operation.scope.label.notAvailable', - defaultMessage: 'No scope available', - })} - value={operationScopes} - onChange={({ target: { value } }) => operationsDispatcher({ + + {operation['x-auth-type'] && operation['x-auth-type'].toLowerCase() !== 'none' ? ( + option.shared ? 'Shared Scopes' : 'API Scopes'} + noOptionsText='No scopes available' + disableCloseOnSelect + value={operationScopes.map((scope) => ({ scope: { name: scope } }))} + getOptionLabel={(option) => option.scope.name} + getOptionSelected={(option, value) => option.scope.name === value.scope.name} + onChange={(event, newValue) => { + const selectedScopes = newValue.map((val) => val.scope.name); + operationsDispatcher({ action: 'scopes', - data: { target, verb, value: value ? [value] : [] }, - })} - helperText={( - - )} - margin='dense' - variant='outlined' - > - - - - {filteredApiScopes.length !== 0 ? filteredApiScopes.map((apiScope) => ( - - - {apiScope.scope.name} - - )) : ( - - - - - - )} - - ( + <> + - - {sharedScopes && sharedScopes.length !== 0 ? sharedScopes.map((sharedScope) => ( - - + )} + style={{ width: 500 }} + renderInput={(params) => ( + - {sharedScope.scope.name} - - )) : ( - - - - - - )} - - ) : null - } + )} + margin='dense' + variant='outlined' + id={verb + target + '-operation-scope-select'} /> + )} + /> + ) : null} - + { operation['x-auth-type'] && operation['x-auth-type'].toLowerCase() !== 'none' ? !disableUpdate && ( ({ @@ -653,7 +655,6 @@ class SubscriptionsTable extends Component { ), options: { sort: false, - filterType: 'textField', customBodyRender: (value, tableMeta) => { if (tableMeta.rowData) { let claimsObject; @@ -695,6 +696,33 @@ class SubscriptionsTable extends Component { return null; }, filter: true, + display: true, + filterType: 'custom', + filterOptions: { + logic: (sub, filters) => { + if (filters.length) return !filters.includes(sub); + return false; + }, + display: (filterList, onChange, index, column) => { + return ( sub.subscriber)))} + value={filterList[index][0] ? filterList[index][0] : null} + onChange={(event, newValue) => { + const updatedFilterList = [...filterList]; + updatedFilterList[index] = newValue ? [newValue] : []; + onChange(updatedFilterList[index], index, column); + }} + renderInput={(params) => ( + + )} + />); + }, + }, }, }, { @@ -707,6 +735,34 @@ class SubscriptionsTable extends Component { ), options: { sort: false, + filter: true, + display: true, + filterType: 'custom', + filterOptions: { + logic: (app, filters) => { + if (filters.length) return !filters.includes(app); + return false; + }, + display: (filterList, onChange, index, column) => { + return ( sub.name)))} + value={filterList[index][0] ? filterList[index][0] : null} + onChange={(event, newValue) => { + const updatedFilterList = [...filterList]; + updatedFilterList[index] = newValue ? [newValue] : []; + onChange(updatedFilterList[index], index, column); + }} + renderInput={(params) => ( + + )} + />); + }, + }, }, }, { @@ -733,6 +789,34 @@ class SubscriptionsTable extends Component { ), options: { sort: false, + filter: true, + display: true, + filterType: 'custom', + filterOptions: { + logic: (tier, filters) => { + if (filters.length) return !filters.includes(tier); + return false; + }, + display: (filterList, onChange, index, column) => { + return ( sub.throttlingPolicy)))} + value={filterList[index][0] ? filterList[index][0] : null} + onChange={(event, newValue) => { + const updatedFilterList = [...filterList]; + updatedFilterList[index] = newValue ? [newValue] : []; + onChange(updatedFilterList[index], index, column); + }} + renderInput={(params) => ( + + )} + />); + }, + }, }, }, {