diff --git a/src/components/forms/AccountForm.js b/src/components/forms/AccountForm.js index 9f8a0d0..41c53d4 100644 --- a/src/components/forms/AccountForm.js +++ b/src/components/forms/AccountForm.js @@ -22,7 +22,7 @@ import CopyableText from '../elements/CopyableText'; import Span from '../elements/Span'; import handleErrors from "../../helpers/handleErrors"; import styled from 'styled-components/macro'; -import { APP_API_BASE_URL } from "../../constants"; +import { APP_API_BASE_URL, LIMITS } from "../../constants"; const StyledInputGroup = styled(InputGroup)` position: relative; @@ -94,6 +94,7 @@ const AccountForm = props => { const [ sbcs, setSbcs ] = useState([]); const [ subspaceSipRealmOtherValue, setSubspaceSipRealmOtherValue ] = useState(''); const [ subspaceEnable, setSubspaceEnable ] = useState(false); + const [localLimits, setLocalLimits] = useState([]); // Invalid form inputs const [ invalidName, setInvalidName ] = useState(false); @@ -319,6 +320,16 @@ const AccountForm = props => { }, }); promiseList.push(applicationsPromise); + + const limitsPromise = axios({ + method: 'get', + baseURL: APP_API_BASE_URL, + url: `/Accounts/${props.account_sid}/Limits`, + headers: { + Authorization: `Bearer ${jwt}`, + }, + }); + promiseList.push(limitsPromise); } const sbcsPromise = await axios({ @@ -337,13 +348,16 @@ const AccountForm = props => { setAccounts(accountsData); if (props.type === 'edit') { + // Application Data const allApplications = (promiseAllValues[1] && promiseAllValues[1].data) || []; const accountApplicationsData = allApplications.filter(app => { return app.account_sid === props.account_sid; }); setAccountApplications(accountApplicationsData); + // Limits Data + setLocalLimits(promiseAllValues[2]?.data); } - setSbcs(promiseAllValues[2]?.data); + setSbcs(promiseAllValues[3]?.data); if (props.type === 'setup' && accountsData.length > 1) { history.push('/internal/accounts'); @@ -558,7 +572,7 @@ const AccountForm = props => { ? `/Accounts` : `/Accounts/${accountSid}`; - await axios({ + const accountResp = await axios({ method: props.type === 'add' ? 'post' : 'put', baseURL: APP_API_BASE_URL, url, @@ -567,6 +581,23 @@ const AccountForm = props => { }, data: axiosData, }); + // Update Limits + const acc_sid = accountSid ? accountSid : accountResp.data.sid; + await Promise.all( + localLimits.map(l => { + const method = l.quantity === "" ? 'delete' : 'post'; + const limitUrl = l.quantity === "" ? `/Accounts/${props.account_sid}/Limits?category=${l.category}` : `/Accounts/${acc_sid}/Limits`; + return axios({ + method: method, + baseURL: APP_API_BASE_URL, + url: limitUrl, + headers: { + Authorization: `Bearer ${jwt}`, + }, + ...(method === 'post' && {data: l}) + }); + }) + ); if (props.type === 'setup') { isMounted = false; @@ -892,6 +923,31 @@ const AccountForm = props => { )} + {LIMITS.map(({ label, category }) => { + const quantity = localLimits?.find(l => l.category === category)?.quantity; + return + + = 0 ? quantity : ""} + onChange={e => { + const limit = localLimits.find(l => l.category === category); + const value = e.target.value ? Number(e.target.value) : ""; + if (limit) { + setLocalLimits(localLimits.map(l => l.category === category ? {...l, quantity: value} : l)); + } else { + setLocalLimits([...localLimits, {category, quantity: value}]); + } + }} + > + + + ; + })} { process.env.REACT_APP_ENABLE_SUBSPACE ? ( <> @@ -1022,7 +1078,6 @@ const AccountForm = props => { )} ) : null } - {errorMessage && ( )} diff --git a/src/components/forms/SettingsForm.js b/src/components/forms/SettingsForm.js index 8f8ec3c..c428608 100644 --- a/src/components/forms/SettingsForm.js +++ b/src/components/forms/SettingsForm.js @@ -16,7 +16,7 @@ import Loader from '../blocks/Loader'; import Modal from '../blocks/Modal'; import { ServiceProviderValueContext } from '../../contexts/ServiceProviderContext'; import handleErrors from "../../helpers/handleErrors"; -import { APP_API_BASE_URL } from "../../constants"; +import { APP_API_BASE_URL, LIMITS } from "../../constants"; const Td = styled.td` padding: 0.5rem 0; @@ -60,6 +60,19 @@ const SettingsForm = () => { const [serviceProviderSid, setServiceProviderSid] = useState(''); const [serviceProviders, setServiceProviders] = useState([]); const [confirmDelete, setConfirmDelete] = useState(false); + const [localLimits, setLocalLimits] = useState([]); + + const callApi = async (path, method, data) => { + return await axios({ + method: method, + baseURL: APP_API_BASE_URL, + url: path, + headers: { + Authorization: `Bearer ${localStorage.getItem('token')}`, + }, + ...(data && {data}) + }); + }; useEffect(() => { const getSettingsData = async () => { @@ -74,14 +87,7 @@ const SettingsForm = () => { return; } - const serviceProvidersResponse = await axios({ - method: 'get', - baseURL: APP_API_BASE_URL, - url: `/ServiceProviders`, - headers: { - Authorization: `Bearer ${localStorage.getItem('token')}`, - }, - }); + const serviceProvidersResponse = await callApi(`/ServiceProviders`, 'get'); const sps = serviceProvidersResponse.data; const sp = sps.find(s => s.service_provider_sid === currentServiceProvider); @@ -91,6 +97,12 @@ const SettingsForm = () => { setServiceProviderSid(sp.service_provider_sid || ''); setEnableMsTeams(sp.ms_teams_fqdn ? true : false); setSbcDomainName(sp.ms_teams_fqdn || ''); + + // Fetch Service provider Limits + if (sp.service_provider_sid) { + const serviceProvidersLimitsResponse = await callApi(`/ServiceProviders/${sp.service_provider_sid}/Limits`, 'get'); + setLocalLimits(serviceProvidersLimitsResponse.data); + } } catch (err) { handleErrors({ err, history, dispatch }); } finally { @@ -208,15 +220,12 @@ const SettingsForm = () => { name: serviceProviderName.trim(), }; - await axios({ - method: 'put', - baseURL: APP_API_BASE_URL, - url: `/ServiceProviders/${serviceProviderSid}`, - headers: { - Authorization: `Bearer ${localStorage.getItem('token')}`, - }, - data, - }); + await callApi(`/ServiceProviders/${serviceProviderSid}`, 'put', data); + await Promise.all( + localLimits.map(l => l.quantity === "" ? + callApi(`/ServiceProviders/${serviceProviderSid}/Limits?category=${l.category}`, 'delete') : + callApi(`/ServiceProviders/${serviceProviderSid}/Limits`, 'post', l)) + ); refreshMsTeamsData(); @@ -272,6 +281,7 @@ const SettingsForm = () => { invalid={invalidServiceProviderName} ref={refServiceProviderName} /> +
{/* needed for CSS grid layout */}
{ title={(!enableMsTeams && "You must enable Microsoft Teams Direct Routing in order to provide an SBC Domain Name") || ""} /> + {LIMITS.map(({ label, category }) => { + const quantity = localLimits?.find(l => l.category === category)?.quantity; + return + + = 0 ? quantity : ""} + onChange={e => { + const limit = localLimits.find(l => l.category === category); + const value = e.target.value ? Number(e.target.value) : ""; + if (limit) { + setLocalLimits(localLimits.map(l => l.category === category ? {...l, quantity: value} : l)); + } else { + setLocalLimits([...localLimits, {category, quantity: value}]); + } + }} + /> + ; + })} + {errorMessage && !confirmDelete && ( )} diff --git a/src/constants.js b/src/constants.js index a625ba2..9adfa68 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,2 +1,16 @@ const { REACT_APP_API_BASE_URL } = process.env; export const APP_API_BASE_URL = (window.JAMBONZ) ? window.JAMBONZ.APP_API_BASE_URL : REACT_APP_API_BASE_URL; +export const LIMITS = [ + // { + // label: "Max registered devices (0=unlimited)", + // category: "device", + // }, + // { + // label: "Max api calls per minute (0=unlimited)", + // category: "api_rate", + // }, + { + label: "Max concurrent calls (0=unlimited)", + category: "voice_call_session", + }, + ]; \ No newline at end of file