Compare commits

...

29 Commits

Author SHA1 Message Date
Dave Horton
40143ae79d update package-lock.json 2022-04-22 12:39:50 -04:00
Conner Luzier
536b183535 regions field added to aws, default to us-east-1 (#50)
* regions field added to aws, default to us-east-1

* added aws_region to post request

* no default setting on aws region

* aws_region required before saving

* no longer showing default on dropdown

Co-authored-by: Conner Luzier <connerluzier@outlook.com>
2022-04-21 13:33:04 -04:00
Dave Horton
bf88a27330 add data file for aws regions 2022-04-18 10:10:50 -04:00
Dave Horton
831450306d docs 2022-04-14 14:18:28 -04:00
Dave Horton
c8d1034dc9 testing instructions 2022-04-14 14:14:44 -04:00
dependabot[bot]
37af9522aa Bump node-forge from 1.2.1 to 1.3.1 (#46)
Bumps [node-forge](https://github.com/digitalbazaar/forge) from 1.2.1 to 1.3.1.
- [Release notes](https://github.com/digitalbazaar/forge/releases)
- [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/v1.2.1...v1.3.1)

---
updated-dependencies:
- dependency-name: node-forge
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-06 08:22:25 -04:00
Dave Horton
6bb81a499b bump version 2022-04-06 08:19:56 -04:00
Dave Horton
58f97dcfb2 show trace_id in recent calls detail display 2022-03-28 19:35:43 -04:00
Dave Horton
23a067b6dd bump version 2022-03-08 20:20:46 -05:00
Andrew
92db20965e moved building to Dockefile instead of entrypoint (#39)
* moved building to Dockefile instead of entrypoint

* moved enviroment variable to gloabl

* updated to use process env during build to allow use of window global var

* added new line

* updated constants for window.jambonz

* removed NODE_ENV in favor of just window.JAMBONZ

* removed unrequired and change const per pull request
2022-02-23 07:40:05 -05:00
akirilyuk
8f8d635bd3 fix all security vulnerabilities (#41)
Co-authored-by: akirilyuk <a.kirilyuk@cognigy.com>
2022-02-17 07:33:00 -05:00
dependabot[bot]
b075028b7b Bump follow-redirects from 1.14.7 to 1.14.8 (#40)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.7 to 1.14.8.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.7...v1.14.8)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-14 21:25:01 -05:00
Dave Horton
b5f2e5fc25 bump version 2022-02-09 15:43:14 -05:00
Dave Horton
6390cc6b81 add missing Azure regions 2022-02-03 08:59:47 -05:00
Dave Horton
d7db92f0c7 update version to 0.7.2 2022-01-31 07:32:10 -05:00
Dave Horton
f5201d2d69 Feature/wellsaid tts (#38)
* initial changes to support WellSaid TTS

* disable stt choice for WellSaid since they dont provide
2022-01-27 08:07:22 -05:00
akirilyuk
128ca045b0 update depenecies and fix security vulnerabilities (#37)
Co-authored-by: akirilyuk <a.kirilyuk@cognigy.com>
2022-01-14 08:00:35 -05:00
Dave Horton
3403996946 bump version 2021-12-21 09:43:00 -05:00
Dave Horton
87dbb461e0 added docker publish 2021-12-13 14:18:43 -05:00
Dave Horton
9bce9c5510 version bump 2021-12-13 09:55:03 -05:00
Brandon Lee Kitajchuk
2db5f26dbf Subspace (#35)
* pushing up what ive got from laptop

* beginnings of a UI for setting up subspace on a jambonz account

* enable the env flag and move content to right place

* changes to support subspace (thanks to nimbleape)

* fix column names

* Implement SIP realm selection for Subspace API calls

* Hook up Subspace disable method

* Finish up Subspace API handling

Co-authored-by: Dan Jenkins <dan@nimblea.pe>
Co-authored-by: Dave Horton <daveh@beachdognet.com>
2021-12-06 17:58:42 -05:00
Dave Horton
bfc7cc971c version bump 2021-12-02 19:34:28 -05:00
Dave Horton
35f353c905 bugfix: azure tts needs to be referenced by ShortName (#33) 2021-11-19 14:31:30 -05:00
Brandon Lee Kitajchuk
922d664bf8 Add Microsoft vendor to Jambonz Webapp (#32)
* Add Microsoft vendor to Speech Form

Add Microsoft vendor to Application Form

Clean up UI for Speech and Application forms

* Remove Sbcs from SpeechServiceAddEdit Form
2021-11-17 20:49:31 -05:00
Dave Horton
ff4d6b6e11 version bump 2021-11-03 13:53:11 -04:00
Dave Horton
70387ff4f1 bump version 2021-10-21 13:09:07 -04:00
Dave Horton
7a4c583345 bump version 2021-10-21 13:01:30 -04:00
Brandon Lee Kitajchuk
d54fbc4782 Update IP whitelist tooltips for carrier form (#29) 2021-10-21 11:41:19 -04:00
Brandon Lee Kitajchuk
14dd1319d9 SMS for Carrier Form (#28) 2021-10-17 12:29:26 -04:00
48 changed files with 28483 additions and 10878 deletions

3
.env
View File

@@ -1 +1,2 @@
REACT_APP_API_BASE_URL=http://[ip]:[port]/v1
REACT_APP_API_BASE_URL=http://127.0.0.1:3002/v1
GENERATE_SOURCEMAP=false

View File

@@ -2,15 +2,12 @@
"extends": "react-app",
"rules": {
"linebreak-style": [
"error",
"unix"
"error",
"unix"
],
"semi": [
"error",
"always"
],
"no-trailing-spaces": [
"error"
"error",
"always"
]
}
}
}

51
.github/workflows/docker-publish.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: Docker
on:
push:
# Publish `main` as Docker `latest` image.
branches:
- main
# Publish `v1.2.3` tags as releases.
tags:
- v*
env:
IMAGE_NAME: webapp
jobs:
push:
runs-on: ubuntu-latest
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v2
- name: Build image
run: docker build . --file Dockerfile --tag $IMAGE_NAME
- name: Log into registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Push image
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME
# Change all uppercase to lowercase
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
# Strip git ref prefix from version
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
# Strip "v" prefix from tag name
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
# Use Docker `latest` tag convention
[ "$VERSION" == "main" ] && VERSION=latest
echo IMAGE_ID=$IMAGE_ID
echo VERSION=$VERSION
docker tag $IMAGE_NAME $IMAGE_ID:$VERSION
docker push $IMAGE_ID:$VERSION

View File

@@ -1,8 +1,10 @@
FROM node:alpine as builder
RUN apk update && apk add --no-cache python3 make g++
COPY . /opt/app
WORKDIR /opt/app/
COPY package.json ./
RUN npm install
RUN npm run build
RUN npm prune
FROM node:alpine as webapp
@@ -10,6 +12,7 @@ RUN apk add curl
WORKDIR /opt/app
COPY . /opt/app
COPY --from=builder /opt/app/node_modules ./node_modules
COPY --from=builder /opt/app/build ./build
COPY ./entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -25,7 +25,11 @@ If there is an update to this code base, you can update the code without re-depl
## Development
Like production, you must specify the IP:port of the Jambonz API you will be hitting.
### Local server
See [howto-setup-test-environment](./howto-setup-test-environment.md) for details on how to set up a complete local test environment on your laptop.
### Remote server
If you want to test against a remote server, you must specify the IP:port of the Jambonz API you will be hitting.
1. Copy `.env` to `.env.local`
2. In `.env.local`, replace `[ip]:[port]` with the API's IP and port

View File

@@ -2,7 +2,9 @@
PUBLIC_IPV4="$(curl --fail -qs whatismyip.akamai.com)"
API_PORT="${API_PORT:-3000}"
API_VERSION="${API_VERSION:-v1}"
echo "REACT_APP_API_BASE_URL=${REACT_APP_API_BASE_URL:-http://$PUBLIC_IPV4:$API_PORT/$API_VERSION}" > /opt/app/.env
REACT_APP_API_BASE_URL=${REACT_APP_API_BASE_URL:-http://$PUBLIC_IPV4:$API_PORT/$API_VERSION}
echo "REACT_APP_API_BASE_URL=${REACT_APP_API_BASE_URL}" > /opt/app/.env
cd /opt/app/
npm run build
npm run serve
TAG="<script>window.JAMBONZ = { APP_API_BASE_URL: '${REACT_APP_API_BASE_URL}'};</script>"
sed -i -e "\@</head>@i\ $TAG" ./build/index.html
npm run serve

View File

@@ -0,0 +1,59 @@
# Setting up a local test environment
This document describes how to set up a local development and test environment on your laptop. Testing the jambonz-webapp requires a back-end system to run against, and we use docker-compose to run these back-end components, allowing you to develop and test the react UI locally.
## Prerequisites
- You will need to have docker and docker-compose installed on your laptop.
- You need to have cloned the [jambonz-api-server](https://github.com/jambonz/jambonz-api-server) repo to a folder on your laptop.
## Running the back-end services
Make sure the docker daemon is running on your laptop. Open a terminal window and cd into the project folder for jambonz-api-server, then run the following command to start the back-end processes.
```bash
cd jambonz-api-server
npm run integration-test
```
This will take a few minutes to start, but eventually a successfull startup will eventually look something like this:
```bash
$ npm run integration-test
> jambonz-api-server@v0.7.5 integration-test
> NODE_ENV=test JAMBONES_TIME_SERIES_HOST=127.0.0.1 AWS_REGION='us-east-1' JAMBONES_CURRENCY=USD JWT_SECRET=foobarbazzle JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_PORT=3360 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_LOGLEVEL=debug JAMBONES_CREATE_CALL_URL=http://localhost/v1/createCall node test/serve-integration.js
starting dockerized mysql and redis..
mysql is running
creating database..
creating schema..
seeding database..
creating admin user..
reset_admin_password, initial admin password is admin
sipp exited with non-zero code 1 signal null
1
ready for testing!
{"level":30, "time": "2022-04-14T18:07:49.318Z","pid":5292,"hostname":"MacBook-Pro-2.local","msg":"listening for HTTP traffic on port 3000","v":1}
{"level":20, "time": "2022-04-14T18:07:49.325Z","pid":5292,"hostname":"MacBook-Pro-2.local","args":[],"msg":"redis event connect","v":1}
{"level":20, "time": "2022-04-14T18:07:49.345Z","pid":5292,"hostname":"MacBook-Pro-2.local","args":[],"msg":"redis event ready","v":1}
```
This starts the a docker-compose network running the following containers:
- mysql
- redis
- influxdb
- heplify-server
- drachtio
- homer-webapp
Leaving the jambonz-api-server process running, open another terminal window, cd into the folder where you have checked out this project, and start it as shown below:
```
cd jambonz-webapp
npm start
```
This will start the react UI and open a browser page to http://localhost:3001.
You should now see the login page to the jambonz webapp and can log in with username admin and password admin. You will be forced to change the password, and then you should see the main page of the application.
From here you can make and test changes locally.

32718
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,9 @@
{
"name": "jambonz-cpaas-ui",
"version": "1.0.0",
"private": true,
"name": "jambonz-webapp",
"version": "v0.7.5",
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/jest-dom": "^5.16.1",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^7.2.1",
"antd": "^4.15.4",
"axios": "^0.21.1",
@@ -13,8 +12,8 @@
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.1",
"serve": "^11.3.0",
"react-scripts": "^5.0.0",
"serve": "^13.0.2",
"styled-components": "^5.0.1"
},
"scripts": {

View File

@@ -1,4 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable no-undef */
import React, { useContext, useEffect, useState, useRef } from 'react';
import axios from 'axios';
import { useHistory, useLocation } from 'react-router-dom';
@@ -16,6 +17,7 @@ import { Link as ReactRouterLink } from 'react-router-dom';
import { ServiceProviderValueContext, ServiceProviderMethodContext } from '../../contexts/ServiceProviderContext';
import LogoJambong from "../../images/LogoJambong.svg";
import AddModalButton from '../elements/AddModalButton';
import { APP_API_BASE_URL } from "../../constants";
const StyledNav = styled.nav`
position: relative;
@@ -106,7 +108,7 @@ const Nav = () => {
if (history.location.pathname !== '' && jwt) {
const serviceProvidersResponse = await axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/ServiceProviders',
headers: {
Authorization: `Bearer ${jwt}`,
@@ -137,7 +139,7 @@ const Nav = () => {
const serviceProviderResponse = await axios({
method: 'post',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders`,
headers: {
Authorization: `Bearer ${jwt}`,

View File

@@ -3,7 +3,8 @@ import axios from 'axios';
import styled from 'styled-components';
import { useHistory } from 'react-router-dom';
import { NotificationDispatchContext } from '../../contexts/NotificationContext';
import { ServiceProviderValueContext } from '../../contexts/ServiceProviderContext';
// import { ServiceProviderValueContext } from '../../contexts/ServiceProviderContext';
import { APP_API_BASE_URL } from "../../constants";
const Container = styled.div`
margin-top: 0.25rem;
@@ -21,7 +22,7 @@ const Container = styled.div`
const Sbcs = props => {
let history = useHistory();
const dispatch = useContext(NotificationDispatchContext);
const currentServiceProvider = useContext(ServiceProviderValueContext);
// const currentServiceProvider = useContext(ServiceProviderValueContext);
const [ sbcs, setSbcs ] = useState('');
useEffect(() => {
const getAPIData = async () => {
@@ -37,8 +38,9 @@ const Sbcs = props => {
}
const sbcResults = await axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
url: `/Sbcs?service_provider_sid=${currentServiceProvider}`,
baseURL: APP_API_BASE_URL,
// url: `/Sbcs?service_provider_sid=${currentServiceProvider}`,
url: '/Sbcs',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
},

View File

@@ -220,7 +220,8 @@ const TableContent = props => {
contentToDelete.name ||
contentToDelete.number ||
contentToDelete.tenant_fqdn ||
contentToDelete.token
contentToDelete.token ||
contentToDelete.vendor
) && (
<Modal
title={`Are you sure you want to delete the following ${props.name}?`}

View File

@@ -111,6 +111,7 @@ const Checkbox = (props, ref) => {
name={props.id}
type="checkbox"
checked={props.checked}
disabled={props.disabled}
onChange={props.onChange}
value={props.value}
ref={inputRef}

View File

@@ -2,19 +2,8 @@ import React, { useContext } from 'react';
import styled from 'styled-components/macro';
import { NotificationDispatchContext } from '../../contexts/NotificationContext';
import Button from './Button';
import Span from './Span';
const Span = styled.span`
text-align: left;
${props => props.hasBorder ? `
height: 2.25rem;
display: flex;
align-items: center;
width: 100%;
padding: 0 1rem;
border: 1px solid #B6B6B6;
border-radius: 0.125rem;
` : ''}
`;
const StyledButton = styled(Button)`
margin-left: 1rem;

View File

@@ -10,6 +10,7 @@ const Select = styled.select`
border-radius: 0.125rem;
background: #fff;
color: inherit;
max-width: 230px;
&:focus {
border-color: #565656;
outline: none;

View File

@@ -0,0 +1,16 @@
import styled from 'styled-components/macro';
const Span = styled.span`
text-align: left;
${props => props.hasBorder ? `
height: 2.25rem;
display: flex;
align-items: center;
width: 100%;
padding: 0 1rem;
border: 1px solid #B6B6B6;
border-radius: 0.125rem;
` : ''}
`;
export default Span;

View File

@@ -1,7 +1,11 @@
import styled from 'styled-components/macro';
import React, { useState, useRef, useEffect, useCallback } from 'react';
import Link from './Link';
const Tooltip = styled.span`
display: none;
label > span:hover > & {
display: inline;
position: absolute;
@@ -14,15 +18,105 @@ const Tooltip = styled.span`
box-shadow: 0 0.375rem 0.25rem rgba(0, 0, 0, 0.12),
0 0 0.25rem rgba(0, 0, 0, 0.18);
z-index: 80;
${props => !props.large ? `
white-space: nowrap;
` : `
text-align: left;
width: 22rem;
bottom: calc(100% + 0.5rem);
`}
white-space: nowrap;
}
`;
const StyledLinkWithTooltip = styled.span`
position: relative;
> span {
font-size: 14px;
position: absolute;
left: 50%;
transform: translate3d(-50%, calc(-100% - 5px), 0);
padding: 0.75rem 1rem;
border-radius: 0.25rem;
border: 1px solid #C6C6C6;
background: #FFF;
z-index: 80;
white-space: pre;
&:after {
content: "";
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid #FFF;
position: absolute;
left: 50%;
top: 100%;
transform: translateX(-50%);
z-index: 2;
}
&:before {
content: "";
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 10px solid #C6C6C6;
position: absolute;
left: 50%;
top: 100%;
transform: translateX(-50%);
z-index: 1;
}
}
`;
const LinkWithTooltip = props => {
const [isActive, setIsActive] = useState(false);
const tooltipRef = useRef();
const triggerRef = useRef();
const handleLinkClick = useCallback(() => {
setIsActive((oldActive) => {
const newActive = !oldActive;
return newActive;
});
}, [setIsActive]);
const handleOuterClick = useCallback((e) => {
if (!tooltipRef.current) {
return;
}
if (tooltipRef.current.contains(e.target)) {
return;
}
if (triggerRef.current.contains(e.target)) {
return;
}
handleLinkClick();
}, [tooltipRef, triggerRef, handleLinkClick]);
useEffect(() => {
document.addEventListener('click', handleOuterClick, false);
return () => document.removeEventListener('click', handleOuterClick, false);
}, [handleOuterClick]);
return (
<StyledLinkWithTooltip>
<Link to="#" onClick={handleLinkClick}>
<span ref={triggerRef}>{props.children}</span>
</Link>
{isActive ? (
<span ref={tooltipRef}>
{props.tipText}
</span>
) : null}
</StyledLinkWithTooltip>
);
};
export {
LinkWithTooltip,
};
export default Tooltip;

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-undef */
import React, { useState, useEffect, useContext, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import axios from 'axios';
@@ -6,6 +7,7 @@ import { ServiceProviderValueContext } from '../../contexts/ServiceProviderConte
import Form from '../elements/Form';
import Input from '../elements/Input';
import Label from '../elements/Label';
import Radio from '../elements/Radio';
import Select from '../elements/Select';
import InputGroup from '../elements/InputGroup';
import PasswordInput from '../elements/PasswordInput';
@@ -17,8 +19,10 @@ import Button from '../elements/Button';
import Link from '../elements/Link';
import Tooltip from '../elements/Tooltip';
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";
const StyledInputGroup = styled(InputGroup)`
position: relative;
@@ -61,6 +65,9 @@ const AccountForm = props => {
const refQueueWebhook = useRef(null);
const refQueueUser = useRef(null);
const refQueuePassword = useRef(null);
const refSubspaceId = useRef(null);
const refSubspaceSecret = useRef(null);
const refSubspaceOtherSip = useRef(null);
// Form inputs
const [ name, setName ] = useState('');
@@ -68,13 +75,24 @@ const AccountForm = props => {
const [ deviceCallingApplication, setDeviceCallingApplication ] = useState('');
const [ regWebhook, setRegWebhook ] = useState('');
const [ regMethod, setRegMethod ] = useState('POST');
const [ regUser, setRegUser ] = useState('' || '');
const [ regPassword, setRegPassword ] = useState('' || '');
const [ regUser, setRegUser ] = useState('');
const [ regPassword, setRegPassword ] = useState('');
const [ webhookSecret, setWebhookSecret ] = useState('');
const [ queueWebhook, setQueueWebhook ] = useState('');
const [ queueMethod, setQueueMethod ] = useState('POST');
const [ queueUser, setQueueUser ] = useState('' || '');
const [ queuePassword, setQueuePassword ] = useState('' || '');
const [ queueUser, setQueueUser ] = useState('');
const [ queuePassword, setQueuePassword ] = useState('');
const [ hasSubspace, setHasSubspace ] = useState(false);
const [ subspaceId, setSubspaceId ] = useState('');
const [ subspaceSecret, setSubspaceSecret ] = useState('');
const [ subspaceSipTeleportId, setSubspaceSipTeleportId ] = useState('');
const [ subspaceSipTeleportEntryPoints, setSubspaceSipTeleportEntryPoints ] = useState([]);
const [ showSubspaceModal, setShowSubspaceModal ] = useState(false);
const [ generatingSubspace, setGeneratingSubspace ] = useState(false);
const [ subspaceSipRealm, setSubspaceSipRealm ] = useState('');
const [ sbcs, setSbcs ] = useState([]);
const [ subspaceSipRealmOtherValue, setSubspaceSipRealmOtherValue ] = useState('');
const [ subspaceEnable, setSubspaceEnable ] = useState(false);
// Invalid form inputs
const [ invalidName, setInvalidName ] = useState(false);
@@ -85,6 +103,10 @@ const AccountForm = props => {
const [ invalidQueueWebhook, setInvalidQueueWebhook ] = useState(false);
const [ invalidQueueUser, setInvalidQueueUser ] = useState(false);
const [ invalidQueuePassword, setInvalidQueuePassword ] = useState(false);
// eslint-disable-next-line no-unused-vars
const [ invalidSubspaceId, setInvalidSubspaceId ] = useState(false);
// eslint-disable-next-line no-unused-vars
const [ invalidSubspaceClient, setInvalidSubspaceClient ] = useState(false);
const [ showLoader, setShowLoader ] = useState(true);
const [ errorMessage, setErrorMessage ] = useState('');
@@ -110,6 +132,14 @@ const AccountForm = props => {
}
};
const handleSubspaceMenuOpen = sid => {
if (menuOpen === sid) {
setMenuOpen(null);
} else {
setMenuOpen(sid);
}
};
const copyWebhookSecret = async e => {
e.preventDefault();
setMenuOpen(null);
@@ -141,7 +171,7 @@ const AccountForm = props => {
setGeneratingSecret(true);
const apiKeyResponse = await axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/Accounts/${accountSid}/WebhookSecret?regenerate=true`,
headers: {
Authorization: `Bearer ${jwt}`,
@@ -164,6 +194,96 @@ const AccountForm = props => {
}
};
const toggleSubspaceTeleport = (enable, e) => {
e.preventDefault();
setMenuOpen(null);
setSubspaceEnable(enable);
setShowSubspaceModal(true);
};
const resetSubspaceState = () => {
setMenuOpen(null);
setShowSubspaceModal(false);
setSubspaceSipRealmOtherValue('');
setGeneratingSubspace(false);
setSubspaceEnable(false);
setSubspaceSipRealm('');
};
const handleSubspaceEnable = async () => {
try {
setGeneratingSubspace(true);
const destination = subspaceSipRealm === 'other'
? subspaceSipRealmOtherValue
: subspaceSipRealm;
const response = await axios({
method: 'post',
baseURL: APP_API_BASE_URL,
url: `/Accounts/${accountSid}/SubspaceTeleport`,
headers: {
Authorization: `Bearer ${jwt}`,
},
data: { destination },
});
if (response.status === 200) {
setSubspaceSipTeleportId(response.data.subspace_sip_teleport_id || '');
setSubspaceSipTeleportEntryPoints(response.data.subspace_sip_teleport_destinations || []);
dispatch({
type: 'ADD',
level: 'success',
message: 'Successfully enabled subspace teleport.',
});
}
resetSubspaceState();
} catch (err) {
resetSubspaceState();
if (err.response.status === 500 && err.response.data.msg === 'Too Many Requests') {
dispatch({
type: 'ADD',
level: 'error',
message: 'You have already created the maximum number of SIP Teleports allowed for your Subspace account.',
});
} else {
handleErrors({ err, history, dispatch });
}
}
};
const handleSubspaceDisable = async () => {
try {
setGeneratingSubspace(true);
const response = await axios({
method: 'delete',
baseURL: APP_API_BASE_URL,
url: `/Accounts/${accountSid}/SubspaceTeleport`,
headers: {
Authorization: `Bearer ${jwt}`,
},
});
if (response.status === 204) {
setSubspaceSipTeleportId('');
setSubspaceSipTeleportEntryPoints([]);
dispatch({
type: 'ADD',
level: 'success',
message: `Successfully disabled subspace teleport.`,
});
}
resetSubspaceState();
} catch (err) {
resetSubspaceState();
handleErrors({ err, history, dispatch });
}
};
useEffect(() => {
const getAccounts = async () => {
try {
@@ -180,7 +300,7 @@ const AccountForm = props => {
const promiseList = [];
const accountsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/Accounts',
headers: {
Authorization: `Bearer ${jwt}`,
@@ -191,7 +311,7 @@ const AccountForm = props => {
if (props.type === 'edit') {
const applicationsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/Applications',
headers: {
Authorization: `Bearer ${jwt}`,
@@ -200,6 +320,16 @@ const AccountForm = props => {
promiseList.push(applicationsPromise);
}
const sbcsPromise = await axios({
method: 'get',
baseURL: APP_API_BASE_URL,
url: '/Sbcs',
headers: {
Authorization: `Bearer ${jwt}`,
},
});
promiseList.push(sbcsPromise);
const promiseAllValues = await Promise.all(promiseList);
const accountsData = (promiseAllValues[0] && promiseAllValues[0].data) || [];
@@ -212,6 +342,7 @@ const AccountForm = props => {
});
setAccountApplications(accountApplicationsData);
}
setSbcs(promiseAllValues[2].data);
if (props.type === 'setup' && accountsData.length > 1) {
history.push('/internal/accounts');
@@ -254,7 +385,11 @@ const AccountForm = props => {
setQueueUser((acc.queue_event_hook && acc.queue_event_hook.username) || '');
setQueuePassword((acc.queue_event_hook && acc.queue_event_hook.password) || '');
setWebhookSecret(acc.webhook_secret || '');
setSubspaceId(acc.subspace_client_id || '');
setSubspaceSecret(acc.subspace_client_secret || '');
setSubspaceSipTeleportId(acc.subspace_sip_teleport_id || '');
setSubspaceSipTeleportEntryPoints(acc.subspace_sip_teleport_destinations ? JSON.parse(acc.subspace_sip_teleport_destinations) : []);
setHasSubspace(acc.subspace_client_id ? true : false);
if (
(acc.registration_hook && acc.registration_hook.username) ||
(acc.registration_hook && acc.registration_hook.password)
@@ -404,6 +539,8 @@ const AccountForm = props => {
password: queuePassword || null,
},
webhook_secret: webhookSecret || null,
subspace_client_id: subspaceId || null,
subspace_client_secret: subspaceSecret || null,
};
if (props.type === 'add') {
@@ -420,7 +557,7 @@ const AccountForm = props => {
await axios({
method: props.type === 'add' ? 'post' : 'put',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url,
headers: {
Authorization: `Bearer ${jwt}`,
@@ -480,6 +617,19 @@ const AccountForm = props => {
},
];
const subspaceMenuItems = [
{
type: 'button',
name: 'Enable',
action: toggleSubspaceTeleport.bind(toggleSubspaceTeleport, true),
},
{
type: 'button',
name: 'Disable',
action: toggleSubspaceTeleport.bind(toggleSubspaceTeleport, false),
},
];
return (
showLoader
? <Loader
@@ -596,7 +746,7 @@ const AccountForm = props => {
<Select
large={props.type === 'setup'}
name="method"
id="method"
id="regMethod"
value={regMethod}
onChange={e => setRegMethod(e.target.value)}
>
@@ -665,7 +815,7 @@ const AccountForm = props => {
<Select
large={props.type === 'setup'}
name="method"
id="method"
id="queueMethod"
value={queueMethod}
onChange={e => setQueueMethod(e.target.value)}
>
@@ -711,6 +861,137 @@ const AccountForm = props => {
</Button>
)}
{ process.env.REACT_APP_ENABLE_SUBSPACE ? (
<>
<Label htmlFor="subspaceId">Subspace</Label>
<InputGroup>
<Input
large={props.type === 'setup'}
name="subspaceId"
id="subspaceId"
value={subspaceId}
onChange={e => setSubspaceId(e.target.value)}
placeholder="Client Id for Subspace"
ref={refSubspaceId}
style={{ margin: '0 4px' }}
/>
<PasswordInput
large={props.type === 'setup'}
allowShowPassword
name="subspaceSecret"
id="subspaceSecret"
password={subspaceSecret}
setPassword={setSubspaceSecret}
setErrorMessage={setErrorMessage}
placeholder="Client Secret for Subspace"
ref={refSubspaceSecret}
style={{ margin: '0 4px' }}
/>
<StyledInputGroup>
<TableMenu
disabled={!hasSubspace}
sid="subspace"
open={menuOpen === "subspace"}
handleMenuOpen={handleSubspaceMenuOpen}
menuItems={subspaceSipTeleportId ? [subspaceMenuItems[1]] : [subspaceMenuItems[0]]}
/>
</StyledInputGroup>
</InputGroup>
{subspaceSipTeleportId ? (
<div style={{ gridColumn: 2, textAlign: 'left' }}>
<div>Subspace is now enabled. To send your traffic through Subspace:</div>
{subspaceSipTeleportEntryPoints.map(entrypoint => (
<div key={entrypoint.transport_type}>
<Span>send {entrypoint.transport_type.split('_').join(' and ')} traffic to&nbsp;</Span>
<CopyableText text={entrypoint.address} textType="Address" />
</div>
))}
</div>
) : null}
{showSubspaceModal && (
<Modal
title={subspaceEnable ? 'Have Subspace send SIP to:' : 'Are you sure you want to delete your Subspace SIP Teleport?'}
loader={generatingSubspace}
hideButtons={generatingSubspace}
maskClosable={!generatingSubspace}
actionText={subspaceEnable ? 'Save' : 'Disable'}
content={
<ModalContainer>
{subspaceEnable ? (
<>
{sipRealm && (
<Radio
noLeftMargin
name="subspaceSipRealm"
id="sipRealmAccount"
label={sipRealm}
checked={subspaceSipRealm === sipRealm}
onChange={() => {
setSubspaceSipRealm(sipRealm);
setSubspaceSipRealmOtherValue('');
}}
/>
)}
{sbcs.map((sbc) => {
return (
<Radio
key={sbc.ipv4}
noLeftMargin
name="subspaceSipRealm"
id={sbc.sbc_address_sid}
label={`${sbc.ipv4}:${sbc.port}`}
checked={subspaceSipRealm === `${sbc.ipv4}:${sbc.port}`}
onChange={() => {
setSubspaceSipRealm(`${sbc.ipv4}:${sbc.port}`);
setSubspaceSipRealmOtherValue('');
}}
/>
);
})}
<Radio
noLeftMargin
name="subspaceSipRealm"
id="sipRealmOther"
label="Other"
checked={subspaceSipRealm === 'other'}
onChange={() => {
setSubspaceSipRealm('other');
setTimeout(() => refSubspaceOtherSip.current.focus(), 0);
}}
/>
{subspaceSipRealm === 'other' && (
<Input
ref={refSubspaceOtherSip}
name="subspaceSipRealm"
id="sipRealmOtherValue"
value={subspaceSipRealmOtherValue}
onChange={e => setSubspaceSipRealmOtherValue(e.target.value)}
placeholder="IP address or DNS name"
style={{ marginTop: '8px' }}
/>
)}
</>
) : null}
</ModalContainer>
}
handleCancel={() => {
setShowSubspaceModal(false);
resetSubspaceState();
}}
handleSubmit={() => {
if (subspaceEnable) {
handleSubspaceEnable();
} else {
handleSubspaceDisable();
}
}}
/>
)}
</>
) : null }
{errorMessage && (
<FormError grid message={errorMessage} />
)}

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-undef */
import React, { useState, useEffect, useContext, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import axios from 'axios';
@@ -15,8 +16,12 @@ import SpeechSynthesisLanguageGoogle from '../../data/SpeechSynthesisLanguageGoo
import SpeechSynthesisLanguageAws from '../../data/SpeechSynthesisLanguageAws';
import SpeechRecognizerLanguageGoogle from '../../data/SpeechRecognizerLanguageGoogle';
import SpeechRecognizerLanguageAws from '../../data/SpeechRecognizerLanguageAws';
import SpeechRecognizerLanguageMicrosoft from '../../data/SpeechRecognizerLanguageMicrosoft';
import SpeechSynthesisLanguageMicrosoft from '../../data/SpeechSynthesisLanguageMicrosoft';
import SpeechSynthesisLanguageWellSaid from '../../data/SpeechSynthesisLanguageWellSaid';
import Loader from '../blocks/Loader';
import CopyableText from '../elements/CopyableText';
import { APP_API_BASE_URL } from "../../constants";
const ApplicationForm = props => {
let history = useHistory();
@@ -100,7 +105,7 @@ const ApplicationForm = props => {
const accountsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/Accounts',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -108,7 +113,7 @@ const ApplicationForm = props => {
});
const applicationsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/Applications',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -391,7 +396,7 @@ const ApplicationForm = props => {
await axios({
method,
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -745,6 +750,14 @@ const ApplicationForm = props => {
? SpeechSynthesisLanguageGoogle.find(l => (
l.code === speechSynthesisLanguage
))
: e.target.value === 'microsoft'
? SpeechSynthesisLanguageMicrosoft.find(l => (
l.code === speechSynthesisLanguage
))
: e.target.value === 'wellsaid'
? SpeechSynthesisLanguageWellSaid.find(l => (
l.code === speechSynthesisLanguage
))
: SpeechSynthesisLanguageAws.find(l => (
l.code === speechSynthesisLanguage
));
@@ -758,9 +771,13 @@ const ApplicationForm = props => {
return;
}
newLang = SpeechSynthesisLanguageAws.find(l => (
l.code === 'en-US'
));
newLang = e.target.value === 'aws'
? SpeechSynthesisLanguageAws.find(l => (
l.code === 'en-US'
))
: SpeechSynthesisLanguageMicrosoft.find(l => (
l.code === 'en-US'
));
}
// Update state to reflect first voice option for language
@@ -769,6 +786,8 @@ const ApplicationForm = props => {
>
<option value="google">Google</option>
<option value="aws">AWS</option>
<option value="microsoft">Microsoft</option>
<option value="wellsaid">WellSaid</option>
</Select>
<Label middle htmlFor="speechSynthesisLanguage">Language</Label>
<Select
@@ -792,6 +811,14 @@ const ApplicationForm = props => {
? SpeechSynthesisLanguageGoogle.find(l => (
l.code === e.target.value
))
: speechSynthesisVendor === 'microsoft'
? SpeechSynthesisLanguageMicrosoft.find(l => (
l.code === e.target.value
))
: speechSynthesisVendor === 'wellsaid'
? SpeechSynthesisLanguageWellSaid.find(l => (
l.code === e.target.value
))
: SpeechSynthesisLanguageAws.find(l => (
l.code === e.target.value
));
@@ -804,6 +831,14 @@ const ApplicationForm = props => {
SpeechSynthesisLanguageGoogle.map(l => (
<option key={l.code} value={l.code}>{l.name}</option>
))
) : speechSynthesisVendor === 'microsoft' ? (
SpeechSynthesisLanguageMicrosoft.map(l => (
<option key={l.code} value={l.code}>{l.name}</option>
))
) : speechSynthesisVendor === 'wellsaid' ? (
SpeechSynthesisLanguageWellSaid.map(l => (
<option key={l.code} value={l.code}>{l.name}</option>
))
) : (
SpeechSynthesisLanguageAws.map(l => (
<option key={l.code} value={l.code}>{l.name}</option>
@@ -824,6 +859,18 @@ const ApplicationForm = props => {
.map(m => m.voices.map(v => (
<option key={v.value} value={v.value}>{v.name}</option>
)))
) : speechSynthesisVendor === 'microsoft' ? (
SpeechSynthesisLanguageMicrosoft
.filter(l => l.code === speechSynthesisLanguage)
.map(m => m.voices.map(v => (
<option key={v.value} value={v.value}>{v.name}</option>
)))
) : speechSynthesisVendor === 'wellsaid' ? (
SpeechSynthesisLanguageWellSaid
.filter(l => l.code === speechSynthesisLanguage)
.map(m => m.voices.map(v => (
<option key={v.value} value={v.value}>{v.name}</option>
)))
) : (
SpeechSynthesisLanguageAws
.filter(l => l.code === speechSynthesisLanguage)
@@ -862,6 +909,7 @@ const ApplicationForm = props => {
>
<option value="google">Google</option>
<option value="aws">AWS</option>
<option value="microsoft">Microsoft</option>
</Select>
<Label middle htmlFor="speechRecognizerLanguage">Language</Label>
<Select
@@ -875,6 +923,10 @@ const ApplicationForm = props => {
SpeechRecognizerLanguageGoogle.map(l => (
<option key={l.code} value={l.code}>{l.name}</option>
))
) : speechRecognizerVendor === 'microsoft' ? (
SpeechRecognizerLanguageMicrosoft.map(l => (
<option key={l.code} value={l.code}>{l.name}</option>
))
) : (
SpeechRecognizerLanguageAws.map(l => (
<option key={l.code} value={l.code}>{l.name}</option>

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-undef */
import React, { useState, useEffect, useContext, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import axios from 'axios';
@@ -10,6 +11,7 @@ import InputGroup from '../elements/InputGroup';
import FormError from '../blocks/FormError';
import Loader from '../blocks/Loader';
import Button from '../elements/Button';
import { APP_API_BASE_URL } from "../../constants";
const MsTeamsTenantForm = props => {
@@ -54,7 +56,7 @@ const MsTeamsTenantForm = props => {
const tenantsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/MicrosoftTeamsTenants',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -62,7 +64,7 @@ const MsTeamsTenantForm = props => {
});
const accountsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/Accounts',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -70,7 +72,7 @@ const MsTeamsTenantForm = props => {
});
const applicationsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/Applications',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -86,7 +88,7 @@ const MsTeamsTenantForm = props => {
if (props.type === 'add') {
promises.push(axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/ServiceProviders',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -247,7 +249,7 @@ const MsTeamsTenantForm = props => {
await axios({
method,
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-undef */
import React, { useState, useEffect, useContext, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import axios from 'axios';
@@ -12,6 +13,7 @@ import FormError from '../blocks/FormError';
import Loader from '../blocks/Loader';
import Button from '../elements/Button';
import phoneNumberFormat from '../../helpers/phoneNumberFormat';
import { APP_API_BASE_URL } from "../../constants";
const PhoneNumberForm = props => {
@@ -60,7 +62,7 @@ const PhoneNumberForm = props => {
const sipTrunksPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/VoipCarriers',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -68,7 +70,7 @@ const PhoneNumberForm = props => {
});
const accountsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/Accounts',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -76,7 +78,7 @@ const PhoneNumberForm = props => {
});
const applicationsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/Applications',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -84,7 +86,7 @@ const PhoneNumberForm = props => {
});
const phoneNumbersPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/PhoneNumbers',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -276,7 +278,7 @@ const PhoneNumberForm = props => {
await axios({
method,
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-undef */
import React, { useState, useEffect, useContext, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import axios from 'axios';
@@ -15,6 +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";
const Td = styled.td`
padding: 0.5rem 0;
@@ -74,7 +76,7 @@ const SettingsForm = () => {
const serviceProvidersResponse = await axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -119,7 +121,7 @@ const SettingsForm = () => {
axios({
method: 'delete',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${serviceProviderSid}`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -208,7 +210,7 @@ const SettingsForm = () => {
await axios({
method: 'put',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${serviceProviderSid}`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-undef */
import React, { useState, useEffect, useContext, useRef } from 'react';
import { useParams, useHistory } from 'react-router-dom';
import axios from 'axios';
@@ -18,8 +19,11 @@ import Code from '../elements/Code';
import FormError from '../blocks/FormError';
import Button from '../elements/Button';
import Loader from '../blocks/Loader';
import { ServiceProviderValueContext } from '../../contexts/ServiceProviderContext'
import { ServiceProviderValueContext } from '../../contexts/ServiceProviderContext';
import AwsRegions from '../../data/AwsRegions';
import MicrosoftAzureRegions from '../../data/MicrosoftAzureRegions';
import { APP_API_BASE_URL } from "../../constants";
const StyledButtonGroup = styled(InputGroup)`
@media (max-width: 576.98px) {
@@ -67,37 +71,50 @@ const SpeechServicesAddEdit = (props) => {
// Refs
const refVendorGoogle = useRef(null);
const refVendorAws = useRef(null);
const refVendorMs = useRef(null);
const refVendorWellSaid = useRef(null);
const refAccessKeyId = useRef(null);
const refSecretAccessKey = useRef(null);
const refUseForTts = useRef(null);
const refUseForStt = useRef(null);
const refApiKey = useRef(null);
const refRegion = useRef(null);
const refAwsRegion = useRef(null);
// Form inputs
const [ vendor, setVendor ] = useState('');
const [ serviceKey, setServiceKey ] = useState('');
const [ displayedServiceKey, setDisplayedServiceKey ] = useState('');
const [ accessKeyId, setAccessKeyId ] = useState('');
const [ secretAccessKey, setSecretAccessKey ] = useState('');
const [ useForTts, setUseForTts ] = useState(false);
const [ useForStt, setUseForStt ] = useState(false);
const [ accounts, setAccounts ] = useState([]);
const [ accountSid, setAccountSid ] = useState('');
const [vendor, setVendor] = useState('');
const [serviceKey, setServiceKey] = useState('');
const [displayedServiceKey, setDisplayedServiceKey] = useState('');
const [accessKeyId, setAccessKeyId] = useState('');
const [secretAccessKey, setSecretAccessKey] = useState('');
const [useForTts, setUseForTts] = useState(false);
const [useForStt, setUseForStt] = useState(false);
const [accounts, setAccounts] = useState([]);
const [accountSid, setAccountSid] = useState('');
const [apiKey, setApiKey] = useState('');
const [region, setRegion] = useState('');
const [awsregion, setAwsRegion] = useState('');
// Invalid form inputs
const [ invalidVendorGoogle, setInvalidVendorGoogle ] = useState(false);
const [ invalidVendorAws, setInvalidVendorAws ] = useState(false);
const [ invalidAccessKeyId, setInvalidAccessKeyId ] = useState(false);
const [ invalidSecretAccessKey, setInvalidSecretAccessKey ] = useState(false);
const [ invalidUseForTts, setInvalidUseForTts ] = useState(false);
const [ invalidUseForStt, setInvalidUseForStt ] = useState(false);
const [invalidVendorGoogle, setInvalidVendorGoogle] = useState(false);
const [invalidVendorAws, setInvalidVendorAws] = useState(false);
const [invalidVendorMs, setInvalidVendorMs] = useState(false);
const [invalidVendorWellSaid, setInvalidVendorWellSaid] = useState(false);
const [invalidAccessKeyId, setInvalidAccessKeyId] = useState(false);
const [invalidSecretAccessKey, setInvalidSecretAccessKey] = useState(false);
const [invalidUseForTts, setInvalidUseForTts] = useState(false);
const [invalidUseForStt, setInvalidUseForStt] = useState(false);
const [invalidApiKey, setInvalidApiKey] = useState(false);
const [invalidRegion, setInvalidRegion] = useState(false);
const [invalidAwsRegion, setInvalidAwsRegion] = useState(false);
const [ originalTtsValue, setOriginalTtsValue ] = useState(null);
const [ originalSttValue, setOriginalSttValue ] = useState(null);
const [originalTtsValue, setOriginalTtsValue] = useState(null);
const [originalSttValue, setOriginalSttValue] = useState(null);
const [ validServiceKey, setValidServiceKey ] = useState(false);
const [validServiceKey, setValidServiceKey] = useState(false);
const [ showLoader, setShowLoader ] = useState(true);
const [ errorMessage, setErrorMessage ] = useState('');
const [showLoader, setShowLoader] = useState(true);
const [errorMessage, setErrorMessage] = useState('');
useEffect(() => {
const getAPIData = async () => {
@@ -105,7 +122,7 @@ const SpeechServicesAddEdit = (props) => {
try {
const accountsResponse = await axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/Accounts',
headers: {
Authorization: `Bearer ${jwt}`,
@@ -117,7 +134,7 @@ const SpeechServicesAddEdit = (props) => {
if (type === 'edit') {
const speechCredential = await axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${currentServiceProvider}/SpeechCredentials/${speech_service_sid}`,
headers: {
Authorization: `Bearer ${jwt}`,
@@ -133,16 +150,19 @@ const SpeechServicesAddEdit = (props) => {
} catch (err) {
}
setAccountSid( speechCredential.data.account_sid || '');
setVendor( speechCredential.data.vendor || undefined);
setServiceKey( serviceKeyJson || '');
setDisplayedServiceKey( displayedServiceKeyJson || '');
setAccessKeyId( speechCredential.data.access_key_id || '');
setSecretAccessKey( speechCredential.data.secret_access_key || '');
setUseForTts( speechCredential.data.use_for_tts || false);
setUseForStt( speechCredential.data.use_for_stt || false);
setOriginalTtsValue( speechCredential.data.use_for_tts || false);
setOriginalSttValue( speechCredential.data.use_for_stt || false);
setAccountSid(speechCredential.data.account_sid || '');
setVendor(speechCredential.data.vendor || undefined);
setServiceKey(serviceKeyJson || '');
setDisplayedServiceKey(displayedServiceKeyJson || '');
setAccessKeyId(speechCredential.data.access_key_id || '');
setSecretAccessKey(speechCredential.data.secret_access_key || '');
setApiKey(speechCredential.data.api_key || '');
setRegion(speechCredential.data.region || '');
setAwsRegion(speechCredential.data.aws_region || '');
setUseForTts(speechCredential.data.use_for_tts || false);
setUseForStt(speechCredential.data.use_for_stt || false);
setOriginalTtsValue(speechCredential.data.use_for_tts || false);
setOriginalSttValue(speechCredential.data.use_for_stt || false);
}
setShowLoader(false);
} catch (err) {
@@ -206,10 +226,13 @@ const SpeechServicesAddEdit = (props) => {
setErrorMessage('');
setInvalidVendorGoogle(false);
setInvalidVendorAws(false);
setInvalidVendorMs(false);
setInvalidVendorWellSaid(false);
setInvalidAccessKeyId(false);
setInvalidSecretAccessKey(false);
setInvalidUseForTts(false);
setInvalidUseForStt(false);
setInvalidApiKey(false);
let errorMessages = [];
let focusHasBeenSet = false;
@@ -217,6 +240,8 @@ const SpeechServicesAddEdit = (props) => {
errorMessages.push('Please select a vendor.');
setInvalidVendorGoogle(true);
setInvalidVendorAws(true);
setInvalidVendorMs(true);
setInvalidVendorWellSaid(true);
if (!focusHasBeenSet) {
refVendorGoogle.current.focus();
focusHasBeenSet = true;
@@ -245,6 +270,42 @@ const SpeechServicesAddEdit = (props) => {
}
}
if (vendor === 'aws' && !awsregion) {
errorMessages.push('Please select a region.');
setInvalidAwsRegion(true);
if (!focusHasBeenSet) {
refAwsRegion.current.focus();
focusHasBeenSet = true;
}
}
if (vendor === 'microsoft' && !apiKey) {
errorMessages.push('Please provide an API key.');
setInvalidApiKey(true);
if (!focusHasBeenSet) {
refApiKey.current.focus();
focusHasBeenSet = true;
}
}
if (vendor === 'microsoft' && !region) {
errorMessages.push('Please select a region.');
setInvalidRegion(true);
if (!focusHasBeenSet) {
refRegion.current.focus();
focusHasBeenSet = true;
}
}
if (vendor === 'wellsaid' && !apiKey) {
errorMessages.push('Please provide an API key.');
setInvalidApiKey(true);
if (!focusHasBeenSet) {
refApiKey.current.focus();
focusHasBeenSet = true;
}
}
if (errorMessages.length > 1) {
setErrorMessage(errorMessages);
return;
@@ -266,7 +327,7 @@ const SpeechServicesAddEdit = (props) => {
const postResults = await axios({
method,
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url,
headers: {
Authorization: `Bearer ${jwt}`,
@@ -276,6 +337,9 @@ const SpeechServicesAddEdit = (props) => {
service_key: vendor === 'google' ? JSON.stringify(serviceKey) : null,
access_key_id: vendor === 'aws' ? accessKeyId : null,
secret_access_key: vendor === 'aws' ? secretAccessKey : null,
aws_region: vendor === 'aws' ? awsregion : null,
api_key: ['microsoft', 'wellsaid'].includes(vendor) ? apiKey : null,
region: vendor === 'microsoft' ? region : null,
use_for_tts: useForTts,
use_for_stt: useForStt,
service_provider_sid: accountSid ? null : currentServiceProvider,
@@ -297,7 +361,7 @@ const SpeechServicesAddEdit = (props) => {
if (useForTts || useForStt) {
const testResults = await axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${currentServiceProvider}/SpeechCredentials/${speech_service_sid}/test`,
headers: {
Authorization: `Bearer ${jwt}`,
@@ -342,7 +406,7 @@ const SpeechServicesAddEdit = (props) => {
if (type === 'add') {
await axios({
method: 'delete',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${currentServiceProvider}/SpeechCredentials/${speech_service_sid}`,
headers: {
Authorization: `Bearer ${jwt}`,
@@ -353,7 +417,7 @@ const SpeechServicesAddEdit = (props) => {
if (type === 'edit') {
await axios({
method,
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url,
headers: {
Authorization: `Bearer ${jwt}`,
@@ -414,7 +478,7 @@ const SpeechServicesAddEdit = (props) => {
return (
showLoader ? (
<Loader height={props.type === 'add' ? '424px' : '376px'}/>
<Loader height={props.type === 'add' ? '424px' : '376px'} />
) : (
<Form
large
@@ -444,6 +508,28 @@ const SpeechServicesAddEdit = (props) => {
ref={refVendorAws}
disabled={type === 'edit'}
/>
<Radio
name="vendor"
id="microsoft"
label="Microsoft"
checked={vendor === 'microsoft'}
onChange={() => setVendor('microsoft')}
invalid={invalidVendorMs}
ref={refVendorMs}
disabled={type === 'edit'}
/>
<Radio
name="vendor"
id="wellsaid"
label="WellSaid"
checked={vendor === 'wellsaid'}
onChange={() => setVendor('wellsaid')}
invalid={invalidVendorWellSaid}
ref={refVendorWellSaid}
disabled={type === 'edit'}
/>
</InputGroup>
<Label htmlFor="account">Used by</Label>
@@ -511,14 +597,86 @@ const SpeechServicesAddEdit = (props) => {
ref={refSecretAccessKey}
disabled={type === 'edit'}
/>
<Label htmlFor="regions">Region</Label>
<Select
name="regions"
id="regions"
value={awsregion}
onChange={e => setAwsRegion(e.target.value)}
ref={refAwsRegion}
invalid={invalidAwsRegion}
>
<option value="">
Select a region
</option>
{AwsRegions.map(r => (
<option
key={r.value}
value={r.value}
>
{r.name}
</option>
))}
</Select>
</>
) : vendor === 'microsoft' ? (
<>
<Label htmlFor="apiKey">API Key</Label>
<Input
name="apiKey"
id="apiKey"
value={apiKey}
onChange={e => setApiKey(e.target.value)}
placeholder=""
invalid={invalidApiKey}
ref={refApiKey}
disabled={type === 'edit'}
/>
<Label htmlFor="region">Region</Label>
<Select
name="region"
id="region"
value={region}
onChange={e => setRegion(e.target.value)}
ref={refRegion}
invalid={invalidRegion}
>
<option value="">
All regions
</option>
{MicrosoftAzureRegions.map(r => (
<option
key={r.value}
value={r.value}
>
{r.name}
</option>
))}
</Select>
</>
) : vendor === 'wellsaid' ? (
<>
<Label htmlFor="apiKey">API Key</Label>
<Input
name="apiKey"
id="apiKey"
value={apiKey}
onChange={e => setApiKey(e.target.value)}
placeholder=""
invalid={invalidApiKey}
ref={refApiKey}
disabled={type === 'edit'}
/>
</>
) : (
null
)}
{vendor === 'google' || vendor === 'aws' ? (
{['google', 'aws', 'microsoft', 'wellsaid'].includes(vendor) ? (
<>
<div/>
<div />
<Checkbox
noLeftMargin
name="useForTts"
@@ -529,21 +687,23 @@ const SpeechServicesAddEdit = (props) => {
invalid={invalidUseForTts}
ref={refUseForTts}
/>
<div/>
<div />
<Checkbox
noLeftMargin
name="useForStt"
id="useForStt"
label="Use for speech-to-text"
disabled={'wellsaid' === vendor}
checked={useForStt}
onChange={e => setUseForStt(e.target.checked)}
invalid={invalidUseForStt}
ref={refUseForStt}
/>
</>
) : (
null
)}
) :
(
null
)}
{errorMessage && (
<FormError grid message={errorMessage} />
@@ -559,7 +719,7 @@ const SpeechServicesAddEdit = (props) => {
dispatch({
type: 'ADD',
level: 'info',
message: type === 'add' ? 'New speech service canceled' :'Changes canceled',
message: type === 'add' ? 'New speech service canceled' : 'Changes canceled',
});
}}
>
@@ -578,4 +738,4 @@ const SpeechServicesAddEdit = (props) => {
);
};
export default SpeechServicesAddEdit;
export default SpeechServicesAddEdit;

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-undef */
import React, { useState, useEffect, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import axios from 'axios';
@@ -7,6 +8,7 @@ import Button from '../elements/Button';
import Input from '../elements/Input';
import PasswordInput from '../elements/PasswordInput';
import FormError from '../blocks/FormError';
import { APP_API_BASE_URL } from "../../constants";
const Login = props => {
let history = useHistory();
@@ -58,7 +60,7 @@ const Login = props => {
// Log in
const response = await axios({
method: 'post',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/login',
data: { username, password },
});
@@ -84,7 +86,7 @@ const Login = props => {
//-----------------------------------------------------------------------------
const serviceProvidersPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/serviceProviders',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -93,7 +95,7 @@ const Login = props => {
const accountsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/Accounts',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -102,7 +104,7 @@ const Login = props => {
const applicationsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/applications',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -111,7 +113,7 @@ const Login = props => {
const voipCarriersPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/voipCarriers',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-undef */
import React, { useEffect, useContext } from 'react';
import { useParams, useHistory } from 'react-router-dom';
import axios from 'axios';
@@ -5,6 +6,7 @@ import { NotificationDispatchContext } from '../../../contexts/NotificationConte
import InternalTemplate from '../../templates/InternalTemplate';
import AccountForm from '../../forms/AccountForm';
import TableContent from '../../blocks/TableContent.js';
import { APP_API_BASE_URL } from "../../../constants";
const AccountsAddEdit = () => {
let history = useHistory();
@@ -31,7 +33,7 @@ const AccountsAddEdit = () => {
}
const results = await axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/Accounts/${account_sid}/ApiKeys`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -112,7 +114,7 @@ const AccountsAddEdit = () => {
}
const result = await axios({
method: 'post',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/Apikeys',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -167,7 +169,7 @@ const AccountsAddEdit = () => {
}
await axios({
method: 'delete',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/Apikeys/${apiKeyToDelete.sid}`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-undef */
import React, { useEffect, useContext } from 'react';
import { useHistory } from 'react-router-dom';
import axios from 'axios';
@@ -5,6 +6,7 @@ import { NotificationDispatchContext } from '../../../contexts/NotificationConte
import InternalTemplate from '../../templates/InternalTemplate';
import TableContent from '../../blocks/TableContent.js';
import { ServiceProviderValueContext } from '../../../contexts/ServiceProviderContext';
import { APP_API_BASE_URL } from "../../../constants";
const AccountsList = () => {
let history = useHistory();
@@ -31,7 +33,7 @@ const AccountsList = () => {
if(!currentServiceProvider) return [];
const results = await axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${currentServiceProvider}/Accounts`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -43,6 +45,7 @@ const AccountsList = () => {
sip_realm: a.sip_realm,
url_reg: a.registration_hook && a.registration_hook.url,
url_queue: a.queue_event_hook && a.queue_event_hook.url,
subspace_enabled: a.subspace_sip_teleport_id ? 'Enabled' : ''
}));
return(simplifiedAccounts);
} catch (err) {
@@ -93,7 +96,7 @@ const AccountsList = () => {
// or if the account has any API keys
const applicationsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/Applications',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -101,7 +104,7 @@ const AccountsList = () => {
});
const phoneNumbersPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/PhoneNumbers',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -109,7 +112,7 @@ const AccountsList = () => {
});
const msTeamsTenantsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/MicrosoftTeamsTenants',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -117,7 +120,7 @@ const AccountsList = () => {
});
const apiKeysPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/Accounts/${accountToDelete.sid}/ApiKeys`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -178,7 +181,7 @@ const AccountsList = () => {
// Delete account
await axios({
method: 'delete',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/Accounts/${accountToDelete.sid}`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -220,7 +223,7 @@ const AccountsList = () => {
{ header: 'AccountSid', key: 'sid' },
{ header: 'SIP Realm', key: 'sip_realm' },
{ header: 'Registration Webhook', key: 'url_reg' },
{ header: 'Queue Event Webhook', key: 'url_queue' },
{ header: 'Queue Event Webhook', key: 'url_queue' }
]}
formatContentToDelete={formatAccountToDelete}
deleteContent={deleteAccount}

View File

@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable react/react-in-jsx-scope */
/* eslint-disable no-undef */
import React, { useContext, useState, useEffect } from "react";
import { useHistory } from "react-router-dom";
import axios from "axios";
@@ -14,6 +15,7 @@ import Select from "../../../components/elements/Select";
import AntdTable from "../../../components/blocks/AntdTable";
import handleErrors from "../../../helpers/handleErrors";
import { ServiceProviderValueContext } from '../../../contexts/ServiceProviderContext';
import { APP_API_BASE_URL } from "../../../constants";
const StyledButton = styled(Button)`
& > span {
@@ -115,7 +117,7 @@ const AlertsIndex = () => {
const alerts = await axios({
method: "get",
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/Accounts/${account}/Alerts`,
headers: {
Authorization: `Bearer ${jwt}`,
@@ -171,7 +173,7 @@ const AlertsIndex = () => {
setLoading(true);
const accountResponse = await axios({
method: "get",
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${currentServiceProvider}/Accounts`,
headers: {
Authorization: `Bearer ${jwt}`,

View File

@@ -1,4 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable no-undef */
import React, { useEffect, useContext, useState, useCallback } from 'react';
import { useHistory } from 'react-router-dom';
import axios from 'axios';
@@ -10,6 +11,7 @@ import Select from "../../../components/elements/Select";
import InputGroup from "../../../components/elements/InputGroup";
import { ServiceProviderValueContext } from '../../../contexts/ServiceProviderContext';
import handleErrors from "../../../helpers/handleErrors";
import { APP_API_BASE_URL } from "../../../constants";
const FilterLabel = styled.span`
color: #231f20;
@@ -57,7 +59,7 @@ const ApplicationsList = () => {
try {
const accountResponse = await axios({
method: "get",
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${currentServiceProvider}/Accounts`,
headers: {
Authorization: `Bearer ${jwt}`,
@@ -100,7 +102,7 @@ const ApplicationsList = () => {
}
const applicationsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/Accounts/${account}/Applications`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -173,7 +175,7 @@ const ApplicationsList = () => {
// check if any account or Microsoft Teams Tenant uses this application
const accountsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/Accounts',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -181,7 +183,7 @@ const ApplicationsList = () => {
});
const msTeamsTenantsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/MicrosoftTeamsTenants',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -226,7 +228,7 @@ const ApplicationsList = () => {
// Delete application
await axios({
method: 'delete',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/Applications/${applicationToDelete.sid}`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,

View File

@@ -2,7 +2,6 @@ import React, { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import InternalTemplate from '../../templates/InternalTemplate';
import CarrierForm from '../../forms/CarrierForm';
import Sbcs from '../../blocks/Sbcs';
const CarriersAddEdit = () => {
let { voip_carrier_sid } = useParams();
@@ -15,7 +14,6 @@ const CarriersAddEdit = () => {
<InternalTemplate
type="form"
title={pageTitle}
subtitle={<Sbcs />}
breadcrumbs={[
{ name: 'Carriers', url: '/internal/carriers' },
{ name: pageTitle },

View File

@@ -1,4 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable no-undef */
import React, { useEffect, useContext, useState } from 'react';
import axios from 'axios';
import { useHistory, useLocation } from 'react-router-dom';
@@ -7,12 +8,12 @@ import styled from 'styled-components/macro';
import { NotificationDispatchContext } from '../../../contexts/NotificationContext';
import InternalTemplate from '../../templates/InternalTemplate';
import TableContent from '../../blocks/TableContent.js';
import Sbcs from '../../blocks/Sbcs';
import sortSipGateways from '../../../helpers/sortSipGateways';
import { ServiceProviderValueContext } from '../../../contexts/ServiceProviderContext';
import InputGroup from '../../../components/elements/InputGroup';
import Select from '../../../components/elements/Select';
import handleErrors from '../../../helpers/handleErrors';
import { APP_API_BASE_URL } from "../../../constants";
const FilterLabel = styled.span`
color: #231f20;
@@ -65,7 +66,7 @@ const CarriersList = () => {
try {
const accountResponse = await axios({
method: "get",
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${currentServiceProvider}/Accounts`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -107,7 +108,7 @@ const CarriersList = () => {
// Get all SIP trunks
const trunkResults = await axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${currentServiceProvider}/VoipCarriers`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -123,7 +124,7 @@ const CarriersList = () => {
for (const t of trunkResultsFiltered) {
const gws = await axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/SipGateways?voip_carrier_sid=${t.voip_carrier_sid}`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -204,7 +205,7 @@ const CarriersList = () => {
for (const sid of carrierToDelete.gatewaysSid) {
await axios({
method: 'delete',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/SipGateways/${sid}`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -214,7 +215,7 @@ const CarriersList = () => {
// delete sip trunk
await axios({
method: 'delete',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/VoipCarriers/${carrierToDelete.sid}`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -246,7 +247,6 @@ const CarriersList = () => {
title="Carriers"
addButtonText="Add a Carrier"
addButtonLink="/internal/carriers/add"
subtitle={<Sbcs />}
>
<StyledInputGroup flexEnd space>
<FilterLabel htmlFor="account">Used By:</FilterLabel>

View File

@@ -1,9 +1,11 @@
/* eslint-disable no-undef */
import React, { useEffect, useContext } from 'react';
import axios from 'axios';
import { useHistory } from 'react-router-dom';
import { NotificationDispatchContext } from '../../../contexts/NotificationContext';
import InternalTemplate from '../../templates/InternalTemplate';
import TableContent from '../../blocks/TableContent.js';
import { APP_API_BASE_URL } from "../../../constants";
const MsTeamsTenantsList = () => {
let history = useHistory();
@@ -28,7 +30,7 @@ const MsTeamsTenantsList = () => {
}
const msTeamsTenantsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/MicrosoftTeamsTenants',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -36,7 +38,7 @@ const MsTeamsTenantsList = () => {
});
const accountsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/Accounts',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -44,7 +46,7 @@ const MsTeamsTenantsList = () => {
});
const applicationsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/Applications',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -114,7 +116,7 @@ const MsTeamsTenantsList = () => {
}
await axios({
method: 'delete',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/MicrosoftTeamsTenants/${tenant.sid}`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-undef */
import React, { useState, useEffect, useContext, useCallback } from 'react';
import axios from 'axios';
import { useHistory } from 'react-router-dom';
@@ -6,6 +7,7 @@ import InternalTemplate from '../../templates/InternalTemplate';
import TableContent from '../../blocks/TableContent.js';
import phoneNumberFormat from '../../../helpers/phoneNumberFormat';
import { ServiceProviderValueContext } from '../../../contexts/ServiceProviderContext';
import { APP_API_BASE_URL } from "../../../constants";
const PhoneNumbersList = () => {
let history = useHistory();
@@ -33,7 +35,7 @@ const PhoneNumbersList = () => {
if(!currentServiceProvider) return [];
const phoneNumbersPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${currentServiceProvider}/PhoneNumbers`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -41,7 +43,7 @@ const PhoneNumbersList = () => {
});
const accountsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${currentServiceProvider}/Accounts`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -49,7 +51,7 @@ const PhoneNumbersList = () => {
});
const applicationsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${currentServiceProvider}/Applications`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -57,7 +59,7 @@ const PhoneNumbersList = () => {
});
const sipTrunksPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${currentServiceProvider}/VoipCarriers`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -150,7 +152,7 @@ const PhoneNumbersList = () => {
}
await axios({
method: 'delete',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/PhoneNumbers/${phoneNumber.sid}`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -192,7 +194,7 @@ const PhoneNumbersList = () => {
for (const sid of phoneNumberSids) {
await axios({
method: 'put',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/PhoneNumbers/${sid}`,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,

View File

@@ -1,4 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable no-undef */
import React, { useContext, useEffect, useState } from "react";
import { useHistory } from "react-router-dom";
import axios from "axios";
@@ -15,6 +16,7 @@ import InputGroup from "../../../components/elements/InputGroup";
import Select from "../../../components/elements/Select";
import handleErrors from "../../../helpers/handleErrors";
import { ServiceProviderValueContext } from '../../../contexts/ServiceProviderContext';
import { APP_API_BASE_URL } from "../../../constants";
const FilterLabel = styled.span`
color: #231f20;
@@ -83,7 +85,7 @@ const PcapButton = ({call_data, account_sid, jwt_token}) => {
useEffect(() => {
axios({
method: "get",
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/Accounts/${account_sid}/RecentCalls/${call_data.sip_callid}`,
headers: {
Authorization: `Bearer ${jwt_token}`,
@@ -92,7 +94,7 @@ const PcapButton = ({call_data, account_sid, jwt_token}) => {
if (result_1.status === 200 && result_1.data.total > 0) {
axios({
method: "get",
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/Accounts/${account_sid}/RecentCalls/${call_data.sip_callid}/pcap`,
headers: {
Authorization: `Bearer ${jwt_token}`,
@@ -242,7 +244,7 @@ const RecentCallsIndex = () => {
setLoading(true);
const result = await axios({
method: "get",
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/Accounts/${account}/RecentCalls`,
headers: {
Authorization: `Bearer ${jwt}`,
@@ -267,6 +269,7 @@ const RecentCallsIndex = () => {
to: phoneNumberFormat(item.to),
status: item.answered ? "answered" : item.termination_reason,
duration: timeFormat(item.duration),
trace_id: item.trace_id
}));
setRecentCallsData(recentCalls);
@@ -299,6 +302,7 @@ const RecentCallsIndex = () => {
"remote_host",
"sip_status",
"trunk",
"trace_id"
];
return (
@@ -376,7 +380,7 @@ const RecentCallsIndex = () => {
setLoading(true);
const accountResponse = await axios({
method: "get",
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${currentServiceProvider}/Accounts`,
headers: {
Authorization: `Bearer ${jwt}`,

View File

@@ -2,7 +2,6 @@ import React, { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import InternalTemplate from '../../templates/InternalTemplate';
import SpeechForm from '../../forms/SpeechForm';
import Sbcs from '../../blocks/Sbcs';
const SpeechServicesAddEdit = () => {
let { speech_service_sid } = useParams();
@@ -14,7 +13,6 @@ const SpeechServicesAddEdit = () => {
<InternalTemplate
type="form"
title={pageTitle}
subtitle={<Sbcs />}
breadcrumbs={[
{ name: 'Speech Services', url: '/internal/speech-services' },
{ name: pageTitle },

View File

@@ -1,4 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable no-undef */
import React, { useContext, useState, useEffect } from 'react';
import { useHistory, useLocation } from 'react-router-dom';
import axios from 'axios';
@@ -9,9 +10,9 @@ import handleErrors from '../../../helpers/handleErrors';
import InternalTemplate from '../../templates/InternalTemplate';
import TableContent from '../../../components/blocks/TableContent';
import { ServiceProviderValueContext } from '../../../contexts/ServiceProviderContext';
import Sbcs from '../../blocks/Sbcs';
import InputGroup from '../../../components/elements/InputGroup';
import Select from '../../../components/elements/Select';
import { APP_API_BASE_URL } from "../../../constants";
const FilterLabel = styled.span`
color: #231f20;
@@ -61,7 +62,7 @@ const SpeechServicesList = () => {
try {
const accountResponse = await axios({
method: "get",
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${currentServiceProvider}/Accounts`,
headers: {
Authorization: `Bearer ${jwt}`,
@@ -106,7 +107,7 @@ const SpeechServicesList = () => {
`/ServiceProviders/${currentServiceProvider}/SpeechCredentials`;
const speechServices = await axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: speechApiUrl,
headers: {
Authorization: `Bearer ${jwt}`,
@@ -117,7 +118,7 @@ const SpeechServicesList = () => {
if (s.use_for_stt || s.use_for_tts) {
return axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${currentServiceProvider}/SpeechCredentials/${s.speech_credential_sid}/test`,
headers: {
Authorization: `Bearer ${jwt}`,
@@ -237,7 +238,7 @@ const SpeechServicesList = () => {
// Delete speech service
await axios({
method: 'delete',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/ServiceProviders/${currentServiceProvider}/SpeechCredentials/${speechServiceToDelete.sid}`,
headers: {
Authorization: `Bearer ${jwt}`,
@@ -268,7 +269,6 @@ const SpeechServicesList = () => {
<InternalTemplate
type="normalTable"
title="Speech Services"
subtitle={<Sbcs />}
addButtonText="Add Speech Service"
addButtonLink="/internal/speech-services/add"
>

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-undef */
import React, { useState, useEffect, useContext, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import axios from 'axios';
@@ -8,6 +9,7 @@ import Button from '../../elements/Button';
import Input from '../../elements/Input';
import FormError from '../../blocks/FormError';
import Loader from '../../blocks/Loader';
import { APP_API_BASE_URL } from "../../../constants";
const CreatePassword = () => {
let history = useHistory();
@@ -63,7 +65,7 @@ const CreatePassword = () => {
//-----------------------------------------------------------------------------
const serviceProvidersPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/serviceProviders',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -72,7 +74,7 @@ const CreatePassword = () => {
const accountsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/Accounts',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -81,7 +83,7 @@ const CreatePassword = () => {
const applicationsPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/applications',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -90,7 +92,7 @@ const CreatePassword = () => {
const voipCarriersPromise = axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/voipCarriers',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
@@ -235,7 +237,7 @@ const CreatePassword = () => {
const response = await axios({
method: 'put',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: `/Users/${user_sid}`,
data: {
old_password,

2
src/constants.js Normal file
View File

@@ -0,0 +1,2 @@
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;

View File

@@ -1,6 +1,7 @@
import React, { useState, createContext, useContext } from 'react';
import axios from 'axios';
import { NotificationDispatchContext } from './NotificationContext';
import { APP_API_BASE_URL } from "../constants";
export const ShowMsTeamsStateContext = createContext();
export const ShowMsTeamsDispatchContext = createContext();
@@ -13,7 +14,7 @@ export function ShowMsTeamsProvider(props) {
try {
const serviceProvidersResponse = await axios({
method: 'get',
baseURL: process.env.REACT_APP_API_BASE_URL,
baseURL: APP_API_BASE_URL,
url: '/ServiceProviders',
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,

60
src/data/AwsRegions.js Normal file
View File

@@ -0,0 +1,60 @@
const regions = [
{
name: 'US East (N. Virginia)',
value: 'us-east-1'
},
{
name: 'US East (Ohio)',
value: 'us-east-2'
},
{
name: 'US West (N. California)',
value: 'us-west-1'
},
{
name: 'US West (Oregon)',
value: 'us-west-2'
},
{
name: 'Africa (Cape Town)',
value: 'af-south-1'
},
{
name: 'Asia Pacific (Hong Kong)',
value: 'ap-east-1'
},
{
name: 'Asia Pacific (Jakarta)',
value: 'ap-southeast-3'
},
{
name: 'Asia Pacific (Mumbai)',
value: 'ap-south-1'
},
{
name: 'Asia Pacific (Osaka)',
value: 'ap-northeast-3'
},
{
name: 'Asia Pacific (Seoul)',
value: 'ap-northeast-2'
},
{
name: 'Asia Pacific (Singapore)',
value: 'ap-southeast-1'
},
{
name: 'Asia Pacific (Sydney)',
value: 'ap-southeast-2'
},
{
name: 'Asia Pacific (Tokyo)',
value: 'ap-northeast-1'
},
{
name: 'Canada (Central)',
value: 'ca-central-1'
}
];
export default regions;

View File

@@ -0,0 +1,96 @@
const regions = [
{
name: 'Asia (East)',
value: 'eastasia'
},
{
name: 'Asia (Southeast)',
value: 'southeastasia'
},
{
name: 'Australia (East)',
value: 'australiaeast'
},
{
name: 'Brazil (South)',
value: 'brazilsouth'
},
{
name: 'Canada (Central)',
value: 'canadacentral'
},
{
name: 'Europe (North)',
value: 'northeurope'
},
{
name: 'Europe (West)',
value: 'westeurope'
},
{
name: 'France (Central)',
value: 'francecentral'
},
{
name: 'Switzerland (North)',
value: 'switzerlandnorth'
},
{
name: 'India (Central)',
value: 'centralindia'
},
{
name: 'Japan (West)',
value: 'japanwest'
},
{
name: 'Japan (East)',
value: 'japaneast'
},
{
name: 'Korea (Central)',
value: 'koreacentral'
},
{
name: 'South Africa (North)',
value: 'southafricanorth'
},
{
name: 'UK (South)',
value: 'uksouth'
},
{
name: 'US (Cental)',
value: 'centralus'
},
{
name: 'US (West Central)',
value: 'westcentralus'
},
{
name: 'US (East)',
value: 'eastus'
},
{
name: 'US (East 2)',
value: 'eastus2'
},
{
name: 'US (North Central)',
value: 'northcentralus'
},
{
name: 'US (South Central)',
value: 'southcentralus'
},
{
name: 'US (West)',
value: 'westus'
},
{
name: 'US (West 2)',
value: 'westus2'
},
];
export default regions;

View File

@@ -1,4 +1,4 @@
export default [
const languages = [
{ name: 'Australian English', 'code': 'en-AU' },
{ name: 'British English', 'code': 'en-GB' },
{ name: 'US English', 'code': 'en-US' },
@@ -8,3 +8,5 @@ export default [
{ name: 'Italian', 'code': 'it-IT' },
{ name: 'US Spanish', 'code': 'es-US' },
];
export default languages;

View File

@@ -1,4 +1,4 @@
export default [
const languages = [
{ name: 'Afrikaans (South Africa)', code: 'af-ZA', },
{ name: 'Albanian (Albania)', code: 'sq-AL', },
{ name: 'Amharic (Ethiopia)', code: 'am-ET', },
@@ -128,3 +128,5 @@ export default [
{ name: 'Vietnamese (Vietnam)', code: 'vi-VN', },
{ name: 'Zulu (South Africa)', code: 'zu-ZA', },
];
export default languages;

View File

@@ -0,0 +1,424 @@
const languages = [
{
name: 'Arabic (Algeria)',
code: 'ar-DZ'
},
{
name: 'Arabic (Bahrain)',
code: 'ar-BH'
},
{
name: 'Arabic (Egypt)',
code: 'ar-EG'
},
{
name: 'Arabic (Iraq)',
code: 'ar-IQ'
},
{
name: 'Arabic (Israel)',
code: 'ar-IL'
},
{
name: 'Arabic (Jordan)',
code: 'ar-JO'
},
{
name: 'Arabic (Kuwait)',
code: 'ar-KW'
},
{
name: 'Arabic (Lebanon)',
code: 'ar-LB'
},
{
name: 'Arabic (Libya)',
code: 'ar-LY'
},
{
name: 'Arabic (Morocco)',
code: 'ar-MA'
},
{
name: 'Arabic (Oman)',
code: 'ar-OM'
},
{
name: 'Arabic (Qatar)',
code: 'ar-QA'
},
{
name: 'Arabic (Saudi Arabia)',
code: 'ar-SA'
},
{
name: 'Arabic (Palestinian Authority)',
code: 'ar-PS'
},
{
name: 'Arabic (Syria)',
code: 'ar-SY'
},
{
name: 'Arabic (Tunisia)',
code: 'ar-TN'
},
{
name: 'Arabic (United Arab Emirates)',
code: 'ar-AE'
},
{
name: 'Arabic (Yemen)',
code: 'ar-YE'
},
{
name: 'Bulgarian (Bulgaria)',
code: 'bg-BG'
},
{
name: 'Catalan (Spain)',
code: 'ca-ES'
},
{
name: 'Chinese (Cantonese, Traditional)',
code: 'zh-HK'
},
{
name: 'Chinese (Mandarin, Simplified)',
code: 'zh-CN'
},
{
name: 'Chinese (Taiwanese Mandarin)',
code: 'zh-TW'
},
{
name: 'Croatian (Croatia)',
code: 'hr-HR'
},
{
name: 'Czech (Czech)',
code: 'cs-CZ'
},
{
name: 'Danish (Denmark)',
code: 'da-DK'
},
{
name: 'Dutch (Netherlands)',
code: 'nl-NL'
},
{
name: 'English (Australia)',
code: 'en-AU'
},
{
name: 'English (Canada)',
code: 'en-CA'
},
{
name: 'English (Ghana)',
code: 'en-GH'
},
{
name: 'English (Hong Kong)',
code: 'en-HK'
},
{
name: 'English (India)',
code: 'en-IN'
},
{
name: 'English (Ireland)',
code: 'en-IE'
},
{
name: 'English (Kenya)',
code: 'en-KE'
},
{
name: 'English (New Zealand)',
code: 'en-NZ'
},
{
name: 'English (Nigeria)',
code: 'en-NG'
},
{
name: 'English (Philippines)',
code: 'en-PH'
},
{
name: 'English (Singapore)',
code: 'en-SG'
},
{
name: 'English (South Africa)',
code: 'en-ZA'
},
{
name: 'English (Tanzania)',
code: 'en-TZ'
},
{
name: 'English (United Kingdom)',
code: 'en-GB'
},
{
name: 'English (United States)',
code: 'en-US'
},
{
name: 'Estonian(Estonia)',
code: 'et-EE'
},
{
name: 'Filipino (Philippines)',
code: 'fil-PH'
},
{
name: 'Finnish (Finland)',
code: 'fi-FI'
},
{
name: 'French (Canada)',
code: 'fr-CA'
},
{
name: 'French (France)',
code: 'fr-FR'
},
{
name: 'French (Switzerland)',
code: 'fr-CH'
},
{
name: 'German (Austria)',
code: 'de-AT'
},
{
name: 'German (Switzerland)',
code: 'de-CH'
},
{
name: 'German (Germany)',
code: 'de-DE'
},
{
name: 'Greek (Greece)',
code: 'el-GR'
},
{
name: 'Gujarati (Indian)',
code: 'gu-IN'
},
{
name: 'Hebrew (Israel)',
code: 'he-IL'
},
{
name: 'Hindi (India)',
code: 'hi-IN'
},
{
name: 'Hungarian (Hungary)',
code: 'hu-HU'
},
{
name: 'Indonesian (Indonesia)',
code: 'id-ID'
},
{
name: 'Irish (Ireland)',
code: 'ga-IE'
},
{
name: 'Italian (Italy)',
code: 'it-IT'
},
{
name: 'Japanese (Japan)',
code: 'ja-JP'
},
{
name: 'Kannada (India)',
code: 'kn-IN'
},
{
name: 'Korean (Korea)',
code: 'ko-KR'
},
{
name: 'Latvian (Latvia)',
code: 'lv-LV'
},
{
name: 'Lithuanian (Lithuania)',
code: 'lt-LT'
},
{
name: 'Malay (Malaysia)',
code: 'ms-MY'
},
{
name: 'Maltese (Malta)',
code: 'mt-MT'
},
{
name: 'Marathi (India)',
code: 'mr-IN'
},
{
name: 'Norwegian (Bokmål, Norway)',
code: 'nb-NO'
},
{
name: 'Persian (Iran)',
code: 'fa-IR'
},
{
name: 'Polish (Poland)',
code: 'pl-PL'
},
{
name: 'Portuguese (Brazil)',
code: 'pt-BR'
},
{
name: 'Portuguese (Portugal)',
code: 'pt-PT'
},
{
name: 'Romanian (Romania)',
code: 'ro-RO'
},
{
name: 'Russian (Russia)',
code: 'ru-RU'
},
{
name: 'Slovak (Slovakia)',
code: 'sk-SK'
},
{
name: 'Slovenian (Slovenia)',
code: 'sl-SI'
},
{
name: 'Spanish (Argentina)',
code: 'es-AR'
},
{
name: 'Spanish (Bolivia)',
code: 'es-BO'
},
{
name: 'Spanish (Chile)',
code: 'es-CL'
},
{
name: 'Spanish (Colombia)',
code: 'es-CO'
},
{
name: 'Spanish (Costa Rica)',
code: 'es-CR'
},
{
name: 'Spanish (Cuba)',
code: 'es-CU'
},
{
name: 'Spanish (Dominican Republic)',
code: 'es-DO'
},
{
name: 'Spanish (Ecuador)',
code: 'es-EC'
},
{
name: 'Spanish (El Salvador)',
code: 'es-SV'
},
{
name: 'Spanish (Equatorial Guinea)',
code: 'es-GQ'
},
{
name: 'Spanish (Guatemala)',
code: 'es-GT'
},
{
name: 'Spanish (Honduras)',
code: 'es-HN'
},
{
name: 'Spanish (Mexico)',
code: 'es-MX'
},
{
name: 'Spanish (Nicaragua)',
code: 'es-NI'
},
{
name: 'Spanish (Panama)',
code: 'es-PA'
},
{
name: 'Spanish (Paraguay)',
code: 'es-PY'
},
{
name: 'Spanish (Peru)',
code: 'es-PE'
},
{
name: 'Spanish (Puerto Rico)',
code: 'es-PR'
},
{
name: 'Spanish (Spain)',
code: 'es-ES'
},
{
name: 'Spanish (Uruguay)',
code: 'es-UY'
},
{
name: 'Spanish (USA)',
code: 'es-US'
},
{
name: 'Spanish (Venezuela)',
code: 'es-VE'
},
{
name: 'Swahili (Kenya)',
code: 'sw-KE'
},
{
name: 'Swedish (Sweden)',
code: 'sv-SE'
},
{
name: 'Tamil (India)',
code: 'ta-IN'
},
{
name: 'Telugu (India)',
code: 'te-IN'
},
{
name: 'Thai (Thailand)',
code: 'th-TH'
},
{
name: 'Turkish (Turkey)',
code: 'tr-TR'
},
{
name: 'Vietnamese (Vietnam)',
code: 'vi-VN'
},
];
export default languages;

View File

@@ -1,4 +1,4 @@
export default [
const languages = [
{
code: 'arb',
name: 'Arabic',
@@ -235,3 +235,5 @@ export default [
],
},
];
export default languages;

View File

@@ -1,4 +1,4 @@
export default [
const languages = [
{
code: 'ar-XA',
name: 'Arabic',
@@ -408,3 +408,6 @@ export default [
],
},
];
export default languages;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
const languages = [
{
code: 'en-US',
name: 'English (US)',
voices: [
{ value: '3', name: 'Alana B.' },
{ value: '4', name: 'Ramona J.' },
{ value: '5', name: 'Ramona J. (promo)' },
{ value: '7', name: 'Wade C.' },
{ value: '8', name: 'Sofia H.' },
{ value: '9', name: 'David D.' },
{ value: '11', name: 'Isabel V.' },
{ value: '12', name: 'Ava H.' },
{ value: '13', name: 'Jeremy G.' },
{ value: '14', name: 'Nicole L.' },
{ value: '15', name: 'Paige L.' },
{ value: '16', name: 'Tobin A.' },
{ value: '17', name: 'Kai M.' },
{ value: '18', name: 'Tristan F.' },
{ value: '19', name: 'Patrick K.' },
{ value: '20', name: 'Soifia H. (promo)' },
{ value: '21', name: 'Damian P. (promo)' },
{ value: '22', name: 'Jodi P. (promo)' },
{ value: '23', name: 'Lee M. (promo)' },
{ value: '24', name: 'Selene R. (promo)' },
{ value: '26', name: 'Wade C. (promo)' },
{ value: '27', name: 'Joe F.' },
{ value: '28', name: 'Joe F. (promo)' },
{ value: '29', name: 'Garry J. (character)' },
{ value: '33', name: 'Jude D.' },
{ value: '34', name: 'Eric S. (promo)' },
{ value: '35', name: 'Chase J.' },
{ value: '37', name: 'Steve B. (promo)' },
{ value: '38', name: 'Bella B. (promo)' },
{ value: '39', name: 'Tilda C. (promo)' },
{ value: '41', name: 'Paul B. (promo)' }
],
}
];
export default languages;