mirror of
https://github.com/jambonz/jambonz-webapp.git
synced 2026-01-25 02:08:19 +00:00
Compare commits
19 Commits
fix/filter
...
v0.8.5-5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa7889a0d8 | ||
|
|
a892550b06 | ||
|
|
053f8e509f | ||
|
|
fc40695828 | ||
|
|
42af4f6243 | ||
|
|
7ec8065977 | ||
|
|
d8f05da6fd | ||
|
|
15c2b955ca | ||
|
|
87b3ca7e94 | ||
|
|
adafff7ec3 | ||
|
|
bc9a2464fd | ||
|
|
2a6f8c272c | ||
|
|
f031c47228 | ||
|
|
2e9b86c0c4 | ||
|
|
dd93bedd0e | ||
|
|
e2157ce50e | ||
|
|
a382f21f86 | ||
|
|
a20e1513bc | ||
|
|
af8c09587c |
513
package-lock.json
generated
513
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "jambonz-webapp",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "jambonz-webapp",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -77,17 +77,89 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz",
|
||||
"integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==",
|
||||
"version": "7.22.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
|
||||
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/highlight": "^7.22.5"
|
||||
"@babel/highlight": "^7.22.13",
|
||||
"chalk": "^2.4.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/compat-data": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz",
|
||||
@@ -128,12 +200,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz",
|
||||
"integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==",
|
||||
"version": "7.23.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz",
|
||||
"integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.22.5",
|
||||
"@babel/types": "^7.23.3",
|
||||
"@jridgewell/gen-mapping": "^0.3.2",
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"jsesc": "^2.5.1"
|
||||
@@ -202,22 +274,22 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/helper-environment-visitor": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz",
|
||||
"integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-function-name": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz",
|
||||
"integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
|
||||
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.22.5",
|
||||
"@babel/types": "^7.22.5"
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/types": "^7.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -287,9 +359,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-split-export-declaration": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz",
|
||||
"integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==",
|
||||
"version": "7.22.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
|
||||
"integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.22.5"
|
||||
@@ -308,9 +380,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz",
|
||||
"integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -340,13 +412,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz",
|
||||
"integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
|
||||
"integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.22.5",
|
||||
"chalk": "^2.0.0",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -425,9 +497,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz",
|
||||
"integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==",
|
||||
"version": "7.23.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz",
|
||||
"integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -522,33 +594,33 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz",
|
||||
"integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==",
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.22.5",
|
||||
"@babel/parser": "^7.22.5",
|
||||
"@babel/types": "^7.22.5"
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/parser": "^7.22.15",
|
||||
"@babel/types": "^7.22.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz",
|
||||
"integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==",
|
||||
"version": "7.23.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz",
|
||||
"integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.22.5",
|
||||
"@babel/generator": "^7.22.5",
|
||||
"@babel/helper-environment-visitor": "^7.22.5",
|
||||
"@babel/helper-function-name": "^7.22.5",
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/generator": "^7.23.3",
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-function-name": "^7.23.0",
|
||||
"@babel/helper-hoist-variables": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.5",
|
||||
"@babel/parser": "^7.22.5",
|
||||
"@babel/types": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||
"@babel/parser": "^7.23.3",
|
||||
"@babel/types": "^7.23.3",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
@@ -566,13 +638,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz",
|
||||
"integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==",
|
||||
"version": "7.23.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz",
|
||||
"integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -612,9 +684,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cypress/request": {
|
||||
"version": "2.88.10",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz",
|
||||
"integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==",
|
||||
"version": "2.88.12",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.12.tgz",
|
||||
"integrity": "sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"aws-sign2": "~0.7.0",
|
||||
@@ -630,9 +702,9 @@
|
||||
"json-stringify-safe": "~5.0.1",
|
||||
"mime-types": "~2.1.19",
|
||||
"performance-now": "^2.1.0",
|
||||
"qs": "~6.5.2",
|
||||
"qs": "~6.10.3",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"tough-cookie": "~2.5.0",
|
||||
"tough-cookie": "^4.1.3",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
@@ -652,15 +724,6 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@cypress/request/node_modules/qs": {
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
|
||||
"integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@cypress/xvfb": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz",
|
||||
@@ -2433,9 +2496,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cypress": {
|
||||
"version": "10.8.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-10.8.0.tgz",
|
||||
"integrity": "sha512-QVse0dnLm018hgti2enKMVZR9qbIO488YGX06nH5j3Dg1isL38DwrBtyrax02CANU6y8F4EJUuyW6HJKw1jsFA==",
|
||||
"version": "10.11.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-10.11.0.tgz",
|
||||
"integrity": "sha512-lsaE7dprw5DoXM00skni6W5ElVVLGAdRUUdZjX2dYsGjbY/QnpzWZ95Zom1mkGg0hAaO/QVTZoFVS7Jgr/GUPA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
@@ -5437,7 +5500,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.14",
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -5447,11 +5512,14 @@
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.4",
|
||||
"nanoid": "^3.3.6",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
@@ -5460,10 +5528,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss/node_modules/nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
@@ -5572,6 +5646,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/querystringify": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"dev": true,
|
||||
@@ -5856,6 +5936,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.1",
|
||||
"dev": true,
|
||||
@@ -6018,9 +6104,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@@ -6498,16 +6585,27 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tough-cookie": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
|
||||
"integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"psl": "^1.1.28",
|
||||
"punycode": "^2.1.1"
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
"universalify": "^0.2.0",
|
||||
"url-parse": "^1.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/tough-cookie/node_modules/universalify": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
@@ -6758,6 +6856,16 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url-parse": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
@@ -7031,12 +7139,71 @@
|
||||
}
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz",
|
||||
"integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==",
|
||||
"version": "7.22.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
|
||||
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/highlight": "^7.22.5"
|
||||
"@babel/highlight": "^7.22.13",
|
||||
"chalk": "^2.4.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/compat-data": {
|
||||
@@ -7069,12 +7236,12 @@
|
||||
}
|
||||
},
|
||||
"@babel/generator": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz",
|
||||
"integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==",
|
||||
"version": "7.23.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz",
|
||||
"integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.22.5",
|
||||
"@babel/types": "^7.23.3",
|
||||
"@jridgewell/gen-mapping": "^0.3.2",
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"jsesc": "^2.5.1"
|
||||
@@ -7131,19 +7298,19 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-environment-visitor": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz",
|
||||
"integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-function-name": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz",
|
||||
"integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
|
||||
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/template": "^7.22.5",
|
||||
"@babel/types": "^7.22.5"
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/types": "^7.23.0"
|
||||
}
|
||||
},
|
||||
"@babel/helper-hoist-variables": {
|
||||
@@ -7194,9 +7361,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-split-export-declaration": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz",
|
||||
"integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==",
|
||||
"version": "7.22.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
|
||||
"integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.22.5"
|
||||
@@ -7209,9 +7376,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-validator-identifier": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz",
|
||||
"integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-validator-option": {
|
||||
@@ -7232,13 +7399,13 @@
|
||||
}
|
||||
},
|
||||
"@babel/highlight": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz",
|
||||
"integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
|
||||
"integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.22.5",
|
||||
"chalk": "^2.0.0",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -7301,9 +7468,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz",
|
||||
"integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==",
|
||||
"version": "7.23.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz",
|
||||
"integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/plugin-syntax-jsx": {
|
||||
@@ -7354,30 +7521,30 @@
|
||||
}
|
||||
},
|
||||
"@babel/template": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz",
|
||||
"integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==",
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.22.5",
|
||||
"@babel/parser": "^7.22.5",
|
||||
"@babel/types": "^7.22.5"
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/parser": "^7.22.15",
|
||||
"@babel/types": "^7.22.15"
|
||||
}
|
||||
},
|
||||
"@babel/traverse": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz",
|
||||
"integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==",
|
||||
"version": "7.23.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz",
|
||||
"integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.22.5",
|
||||
"@babel/generator": "^7.22.5",
|
||||
"@babel/helper-environment-visitor": "^7.22.5",
|
||||
"@babel/helper-function-name": "^7.22.5",
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/generator": "^7.23.3",
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-function-name": "^7.23.0",
|
||||
"@babel/helper-hoist-variables": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.5",
|
||||
"@babel/parser": "^7.22.5",
|
||||
"@babel/types": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||
"@babel/parser": "^7.23.3",
|
||||
"@babel/types": "^7.23.3",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
@@ -7391,13 +7558,13 @@
|
||||
}
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz",
|
||||
"integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==",
|
||||
"version": "7.23.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz",
|
||||
"integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-string-parser": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
@@ -7430,9 +7597,9 @@
|
||||
}
|
||||
},
|
||||
"@cypress/request": {
|
||||
"version": "2.88.10",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz",
|
||||
"integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==",
|
||||
"version": "2.88.12",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.12.tgz",
|
||||
"integrity": "sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"aws-sign2": "~0.7.0",
|
||||
@@ -7448,9 +7615,9 @@
|
||||
"json-stringify-safe": "~5.0.1",
|
||||
"mime-types": "~2.1.19",
|
||||
"performance-now": "^2.1.0",
|
||||
"qs": "~6.5.2",
|
||||
"qs": "~6.10.3",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"tough-cookie": "~2.5.0",
|
||||
"tough-cookie": "^4.1.3",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
@@ -7463,12 +7630,6 @@
|
||||
"requires": {
|
||||
"mime-db": "1.52.0"
|
||||
}
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
|
||||
"integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8653,9 +8814,9 @@
|
||||
"devOptional": true
|
||||
},
|
||||
"cypress": {
|
||||
"version": "10.8.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-10.8.0.tgz",
|
||||
"integrity": "sha512-QVse0dnLm018hgti2enKMVZR9qbIO488YGX06nH5j3Dg1isL38DwrBtyrax02CANU6y8F4EJUuyW6HJKw1jsFA==",
|
||||
"version": "10.11.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-10.11.0.tgz",
|
||||
"integrity": "sha512-lsaE7dprw5DoXM00skni6W5ElVVLGAdRUUdZjX2dYsGjbY/QnpzWZ95Zom1mkGg0hAaO/QVTZoFVS7Jgr/GUPA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@cypress/request": "^2.88.10",
|
||||
@@ -10716,18 +10877,20 @@
|
||||
"dev": true
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.14",
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nanoid": "^3.3.4",
|
||||
"nanoid": "^3.3.6",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -10799,6 +10962,12 @@
|
||||
"side-channel": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"querystringify": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||
"dev": true
|
||||
},
|
||||
"queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"dev": true
|
||||
@@ -10973,6 +11142,12 @@
|
||||
"version": "2.0.2",
|
||||
"dev": true
|
||||
},
|
||||
"requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.22.1",
|
||||
"dev": true,
|
||||
@@ -11073,7 +11248,9 @@
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true
|
||||
},
|
||||
"send": {
|
||||
@@ -11409,13 +11586,23 @@
|
||||
"dev": true
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
|
||||
"integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"psl": "^1.1.28",
|
||||
"punycode": "^2.1.1"
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
"universalify": "^0.2.0",
|
||||
"url-parse": "^1.5.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"universalify": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ts-node": {
|
||||
@@ -11575,6 +11762,16 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"url-parse": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jambonz-webapp",
|
||||
"description": "A simple provisioning web app for jambonz",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type {
|
||||
Currency,
|
||||
ElevenLabsOptions,
|
||||
LimitField,
|
||||
LimitUnitOption,
|
||||
PasswordSettings,
|
||||
@@ -200,11 +201,25 @@ export const AUDIO_FORMAT_OPTIONS = [
|
||||
|
||||
export const DEFAULT_ELEVENLABS_MODEL = "eleven_multilingual_v2";
|
||||
|
||||
export const ELEVENLABS_MODEL_OPTIONS = [
|
||||
{ name: "Multilingual v2", value: "eleven_multilingual_v2" },
|
||||
{ name: "Multilingual v1", value: "eleven_multilingual_v1" },
|
||||
{ name: "English v1", value: "eleven_monolingual_v1" },
|
||||
export const DEFAULT_WHISPER_MODEL = "tts-1";
|
||||
|
||||
// Google Custom Voice reported usage options
|
||||
|
||||
export const DEFAULT_GOOGLE_CUSTOM_VOICES_REPORTED_USAGE = "REALTIME";
|
||||
export const GOOGLE_CUSTOM_VOICES_REPORTED_USAGE = [
|
||||
{ name: "REPORTED_USAGE_UNSPECIFIED", value: "REPORTED_USAGE_UNSPECIFIED" },
|
||||
{ name: "REALTIME", value: "REALTIME" },
|
||||
{ name: "OFFLINE", value: "OFFLINE" },
|
||||
];
|
||||
// Eleven Labs options
|
||||
export const DEFAULT_ELEVENLABS_OPTIONS: Partial<ElevenLabsOptions> = {
|
||||
optimize_streaming_latency: 100,
|
||||
voice_settings: {
|
||||
stability: 0.5,
|
||||
similarity_boost: 0.5,
|
||||
use_speaker_boost: true,
|
||||
},
|
||||
};
|
||||
/** Password Length options */
|
||||
|
||||
export const PASSWORD_MIN = 8;
|
||||
@@ -352,3 +367,4 @@ export const API_PRICE = `${API_BASE_URL}/Prices`;
|
||||
export const API_SUBSCRIPTIONS = `${API_BASE_URL}/Subscriptions`;
|
||||
export const API_CHANGE_PASSWORD = `${API_BASE_URL}/change-password`;
|
||||
export const API_SIGNIN = `${API_BASE_URL}/signin`;
|
||||
export const API_GOOGLE_CUSTOM_VOICES = `${API_BASE_URL}/GoogleCustomVoices`;
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
API_SUBSCRIPTIONS,
|
||||
API_CHANGE_PASSWORD,
|
||||
API_SIGNIN,
|
||||
API_GOOGLE_CUSTOM_VOICES,
|
||||
} from "./constants";
|
||||
import { ROUTE_LOGIN } from "src/router/routes";
|
||||
import {
|
||||
@@ -94,6 +95,8 @@ import type {
|
||||
LanguageOption,
|
||||
VoiceOption,
|
||||
GetLanguages,
|
||||
GoogleCustomVoice,
|
||||
GoogleCustomVoicesQuery,
|
||||
} from "./types";
|
||||
import { Availability, StatusCodes } from "./types";
|
||||
import { JaegerRoot } from "./jaeger-types";
|
||||
@@ -454,6 +457,16 @@ export const postLcr = (payload: Partial<Lcr>) => {
|
||||
return postFetch<SidResponse, Partial<Lcr>>(API_LCRS, payload);
|
||||
};
|
||||
|
||||
export const postLcrCreateRoutes = (
|
||||
sid: string,
|
||||
payload: Partial<LcrRoute[]>
|
||||
) => {
|
||||
return postFetch<EmptyResponse, Partial<LcrRoute[]>>(
|
||||
`${API_LCRS}/${sid}/Routes`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const postLcrRoute = (payload: Partial<LcrRoute>) => {
|
||||
return postFetch<SidResponse, Partial<LcrRoute>>(API_LCR_ROUTES, payload);
|
||||
};
|
||||
@@ -501,6 +514,13 @@ export const postChangepassword = (payload: Partial<ChangePassword>) => {
|
||||
export const postSignIn = (payload: Partial<SignIn>) => {
|
||||
return postFetch<SignIn, Partial<SignIn>>(API_SIGNIN, payload);
|
||||
};
|
||||
|
||||
export const postGoogleCustomVoice = (payload: Partial<GoogleCustomVoice>) => {
|
||||
return postFetch<SidResponse, Partial<GoogleCustomVoice>>(
|
||||
API_GOOGLE_CUSTOM_VOICES,
|
||||
payload
|
||||
);
|
||||
};
|
||||
/** Named wrappers for `putFetch` */
|
||||
|
||||
export const putUser = (sid: string, payload: Partial<UserUpdatePayload>) => {
|
||||
@@ -597,6 +617,16 @@ export const putLcr = (sid: string, payload: Partial<Lcr>) => {
|
||||
return putFetch<EmptyResponse, Partial<Lcr>>(`${API_LCRS}/${sid}`, payload);
|
||||
};
|
||||
|
||||
export const putLcrUpdateRoutes = (
|
||||
sid: string,
|
||||
payload: Partial<LcrRoute[]>
|
||||
) => {
|
||||
return putFetch<EmptyResponse, Partial<LcrRoute[]>>(
|
||||
`${API_LCRS}/${sid}/Routes`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const putLcrRoutes = (sid: string, payload: Partial<LcrRoute>) => {
|
||||
return putFetch<EmptyResponse, Partial<LcrRoute>>(
|
||||
`${API_LCR_ROUTES}/${sid}`,
|
||||
@@ -630,6 +660,17 @@ export const putActivationCode = (
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const putGoogleCustomVoice = (
|
||||
sid: string,
|
||||
payload: Partial<GoogleCustomVoice>
|
||||
) => {
|
||||
return putFetch<EmptyResponse, Partial<GoogleCustomVoice>>(
|
||||
`${API_GOOGLE_CUSTOM_VOICES}/${sid}`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
/** Named wrappers for `deleteFetch` */
|
||||
|
||||
export const deleteUser = (sid: string) => {
|
||||
@@ -719,6 +760,10 @@ export const deleteClient = (sid: string) => {
|
||||
export const deleteRecord = (url: string) => {
|
||||
return deleteFetch<EmptyResponse>(url);
|
||||
};
|
||||
|
||||
export const deleteGoogleCustomVoice = (sid: string) => {
|
||||
return deleteFetch<EmptyResponse>(`${API_GOOGLE_CUSTOM_VOICES}/${sid}`);
|
||||
};
|
||||
/** Named wrappers for `getFetch` */
|
||||
|
||||
export const getUser = (sid: string) => {
|
||||
@@ -771,6 +816,13 @@ export const getAvailability = (domain: string) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const getGoogleCustomVoices = (
|
||||
query: Partial<GoogleCustomVoicesQuery>
|
||||
) => {
|
||||
const qryStr = getQuery<Partial<GoogleCustomVoicesQuery>>(query);
|
||||
return getFetch<GoogleCustomVoice[]>(`${API_GOOGLE_CUSTOM_VOICES}?${qryStr}`);
|
||||
};
|
||||
|
||||
/** Wrappers for APIs that can have a mock dev server response */
|
||||
|
||||
export const getMe = () => {
|
||||
|
||||
@@ -42,6 +42,13 @@ export interface WaveSurferSttResult {
|
||||
transcript: string;
|
||||
confidence: number;
|
||||
language_code: string;
|
||||
latency?: number;
|
||||
}
|
||||
|
||||
export interface WaveSurferTtsLatencyResult {
|
||||
vendor: string;
|
||||
latency: string;
|
||||
isCached: string;
|
||||
}
|
||||
|
||||
export interface WaveSurferDtmfResult {
|
||||
|
||||
@@ -372,6 +372,14 @@ export interface RecentCall {
|
||||
recording_url?: string;
|
||||
}
|
||||
|
||||
export interface GoogleCustomVoice {
|
||||
google_custom_voice_sid?: string;
|
||||
speech_credential_sid?: string;
|
||||
name: string;
|
||||
reported_usage: string;
|
||||
model: string;
|
||||
}
|
||||
|
||||
export interface SpeechCredential {
|
||||
speech_credential_sid: string;
|
||||
service_provider_sid: null | string;
|
||||
@@ -408,6 +416,8 @@ export interface SpeechCredential {
|
||||
label: null | string;
|
||||
cobalt_server_uri: null | string;
|
||||
model_id: null | string;
|
||||
model: null | string;
|
||||
options: null | string;
|
||||
}
|
||||
|
||||
export interface Alert {
|
||||
@@ -495,7 +505,7 @@ export interface LcrRoute {
|
||||
lcr_route_sid?: null | string;
|
||||
lcr_sid: null | string;
|
||||
regex: null | string;
|
||||
desciption?: null | string;
|
||||
description?: null | string;
|
||||
priority: number;
|
||||
lcr_carrier_set_entries?: LcrCarrierSetEntry[];
|
||||
}
|
||||
@@ -514,6 +524,9 @@ export interface Client {
|
||||
username: null | string;
|
||||
password?: null | string;
|
||||
is_active: boolean;
|
||||
allow_direct_app_calling: boolean;
|
||||
allow_direct_queue_calling: boolean;
|
||||
allow_direct_user_calling: boolean;
|
||||
}
|
||||
|
||||
export interface PageQuery {
|
||||
@@ -528,6 +541,13 @@ export interface CallQuery extends PageQuery {
|
||||
answered?: string;
|
||||
}
|
||||
|
||||
export interface GoogleCustomVoicesQuery {
|
||||
speech_credential_sid?: string;
|
||||
label?: string;
|
||||
account_sid?: string;
|
||||
service_provider_sid: string;
|
||||
}
|
||||
|
||||
export interface PagedResponse<Type> {
|
||||
page_size: number;
|
||||
total: number;
|
||||
@@ -693,3 +713,13 @@ export interface GetLanguages extends GetVoices {
|
||||
export interface LanguageOption extends SelectorOptions {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface ElevenLabsOptions {
|
||||
optimize_streaming_latency: number;
|
||||
voice_settings: Partial<{
|
||||
similarity_boost: number;
|
||||
stability: number;
|
||||
style: number;
|
||||
use_speaker_boost: boolean;
|
||||
}>;
|
||||
}
|
||||
|
||||
@@ -24,4 +24,3 @@ export const MSG_WEBHOOK_FIELDS = (
|
||||
<span>password</span> fields are required.
|
||||
</>
|
||||
);
|
||||
export const NOT_AVAILABLE_PREFIX = "NotAvalable";
|
||||
|
||||
@@ -693,7 +693,9 @@ export const AccountForm = ({
|
||||
{isDeleteAccount && (
|
||||
<Section slim>
|
||||
<form
|
||||
className="form form--internal"
|
||||
className={`form form--internal ${
|
||||
!account?.data && account?.refetch ? "form--blur" : ""
|
||||
}`}
|
||||
onSubmit={handleDeleteAccount}
|
||||
>
|
||||
<fieldset>
|
||||
|
||||
@@ -71,7 +71,7 @@ export const Accounts = () => {
|
||||
</Icon>
|
||||
</Link>
|
||||
</section>
|
||||
<section className="filters filters--spaced">
|
||||
<section className="filters filters--multi">
|
||||
<SearchFilter
|
||||
placeholder="Filter accounts"
|
||||
filter={[filter, setFilter]}
|
||||
|
||||
@@ -63,6 +63,7 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
|
||||
const navigate = useNavigate();
|
||||
const { synthesis, recognizers } = useSpeechVendors();
|
||||
const user = useSelectState("user");
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
const [accounts] = useServiceProviderData<Account[]>("Accounts");
|
||||
const [applications] = useApiData<Application[]>("Applications");
|
||||
const [applicationName, setApplicationName] = useState("");
|
||||
@@ -549,7 +550,12 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
|
||||
|
||||
return (
|
||||
<Section slim>
|
||||
<form className="form form--internal" onSubmit={handleSubmit}>
|
||||
<form
|
||||
className={`form form--internal ${
|
||||
!application?.data && application?.refetch ? "form--blur" : ""
|
||||
}`}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<fieldset>
|
||||
<MS>{MSG_REQUIRED_FIELDS}</MS>
|
||||
</fieldset>
|
||||
@@ -674,6 +680,10 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
|
||||
);
|
||||
})}
|
||||
<SpeechProviderSelection
|
||||
serviceProviderSid={
|
||||
currentServiceProvider?.service_provider_sid || ""
|
||||
}
|
||||
accountSid={accountSid}
|
||||
credentials={credentials}
|
||||
synthesis={synthesis}
|
||||
ttsVendor={[synthVendor, setSynthVendor]}
|
||||
@@ -701,6 +711,10 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
|
||||
}}
|
||||
>
|
||||
<SpeechProviderSelection
|
||||
serviceProviderSid={
|
||||
currentServiceProvider?.service_provider_sid || ""
|
||||
}
|
||||
accountSid={accountSid}
|
||||
credentials={credentials}
|
||||
synthesis={synthesis}
|
||||
ttsVendor={[
|
||||
|
||||
@@ -96,7 +96,7 @@ export const Applications = () => {
|
||||
</Link>
|
||||
)}
|
||||
</section>
|
||||
<section className="filters filters--spaced">
|
||||
<section className="filters filters--multi">
|
||||
<SearchFilter
|
||||
placeholder="Filter applications"
|
||||
filter={[filter, setFilter]}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { postSpeechServiceLanguages, postSpeechServiceVoices } from "src/api";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
getGoogleCustomVoices,
|
||||
postSpeechServiceLanguages,
|
||||
postSpeechServiceVoices,
|
||||
} from "src/api";
|
||||
import { SpeechCredential } from "src/api/types";
|
||||
import { Selector } from "src/components/forms";
|
||||
import { SelectorOption } from "src/components/forms/selector";
|
||||
@@ -14,11 +18,13 @@ import {
|
||||
VENDOR_COBALT,
|
||||
VENDOR_CUSTOM,
|
||||
VENDOR_DEEPGRAM,
|
||||
VENDOR_ASSEMBLYAI,
|
||||
VENDOR_ELEVENLABS,
|
||||
VENDOR_GOOGLE,
|
||||
VENDOR_MICROSOFT,
|
||||
VENDOR_SONIOX,
|
||||
VENDOR_WELLSAID,
|
||||
VENDOR_WHISPER,
|
||||
} from "src/vendor";
|
||||
import {
|
||||
LabelOptions,
|
||||
@@ -30,6 +36,8 @@ import {
|
||||
VoiceLanguage,
|
||||
} from "src/vendor/types";
|
||||
type SpeechProviderSelectionProbs = {
|
||||
accountSid: string;
|
||||
serviceProviderSid: string;
|
||||
credentials: SpeechCredential[] | undefined;
|
||||
synthesis: SynthesisVendors | undefined;
|
||||
ttsVendor: [
|
||||
@@ -53,6 +61,8 @@ type SpeechProviderSelectionProbs = {
|
||||
};
|
||||
|
||||
export const SpeechProviderSelection = ({
|
||||
accountSid,
|
||||
serviceProviderSid,
|
||||
credentials,
|
||||
synthesis,
|
||||
ttsVendor: [synthVendor, setSynthVendor],
|
||||
@@ -80,14 +90,18 @@ export const SpeechProviderSelection = ({
|
||||
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
|
||||
const currentVendor = useRef(synthVendor);
|
||||
|
||||
useEffect(() => {
|
||||
currentVendor.current = synthVendor;
|
||||
if (!synthesis) {
|
||||
return;
|
||||
}
|
||||
let options = synthesis[synthVendor as keyof SynthesisVendors]
|
||||
const voiceOpts = synthesis[synthVendor as keyof SynthesisVendors]
|
||||
.filter((lang: VoiceLanguage) => {
|
||||
// ELEVENLABS has same voice for all lange, take voices from the 1st language
|
||||
if (synthVendor === VENDOR_ELEVENLABS) {
|
||||
// Only first language has voices, the rest has empty voices
|
||||
if (synthVendor === VENDOR_ELEVENLABS && lang.voices.length > 0) {
|
||||
return true;
|
||||
}
|
||||
return lang.code === synthLang;
|
||||
@@ -98,15 +112,15 @@ export const SpeechProviderSelection = ({
|
||||
value: voice.value,
|
||||
}))
|
||||
) as Voice[];
|
||||
setSynthesisVoiceOptions(options);
|
||||
setSynthesisVoiceOptions(voiceOpts);
|
||||
|
||||
options = synthesis[synthVendor as keyof SynthesisVendors].map(
|
||||
const langOpts = synthesis[synthVendor as keyof SynthesisVendors].map(
|
||||
(lang: VoiceLanguage) => ({
|
||||
name: lang.name,
|
||||
value: lang.code,
|
||||
})
|
||||
);
|
||||
setSynthesisLanguageOptions(options);
|
||||
setSynthesisLanguageOptions(langOpts);
|
||||
|
||||
if (synthVendor === VENDOR_ELEVENLABS) {
|
||||
postSpeechServiceVoices(
|
||||
@@ -118,6 +132,10 @@ export const SpeechProviderSelection = ({
|
||||
label: synthLabel,
|
||||
}
|
||||
).then(({ json }) => {
|
||||
// If after successfully fetching data, vendor is still good, then apply value
|
||||
if (currentVendor.current !== VENDOR_ELEVENLABS) {
|
||||
return;
|
||||
}
|
||||
if (json.length > 0) {
|
||||
setSynthesisVoiceOptions(json);
|
||||
}
|
||||
@@ -136,8 +154,37 @@ export const SpeechProviderSelection = ({
|
||||
setSynthesisLanguageOptions(json);
|
||||
}
|
||||
});
|
||||
} else if (synthVendor === VENDOR_GOOGLE) {
|
||||
getGoogleCustomVoices({
|
||||
...(synthLabel && { label: synthLabel }),
|
||||
account_sid: accountSid,
|
||||
service_provider_sid: serviceProviderSid,
|
||||
}).then(({ json }) => {
|
||||
// If after successfully fetching data, vendor is still good, then apply value
|
||||
if (currentVendor.current !== VENDOR_GOOGLE) {
|
||||
return;
|
||||
}
|
||||
const customVOices = json.map((v) => ({
|
||||
name: `${v.name} (Custom)`,
|
||||
value: `custom_${v.google_custom_voice_sid}`,
|
||||
}));
|
||||
const options = synthesis[synthVendor as keyof SynthesisVendors]
|
||||
.filter((lang: VoiceLanguage) => {
|
||||
return lang.code === synthLang;
|
||||
})
|
||||
.flatMap((lang: VoiceLanguage) =>
|
||||
lang.voices.map((voice: Voice) => ({
|
||||
name: voice.name,
|
||||
value: voice.value,
|
||||
}))
|
||||
) as Voice[];
|
||||
setSynthesisVoiceOptions([...customVOices, ...options]);
|
||||
if (customVOices.length > 0) {
|
||||
setSynthVoice(customVOices[0].value);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [synthVendor, synthesis, synthLabel]);
|
||||
}, [synthVendor, synthesis, synthLabel, accountSid, serviceProviderSid]);
|
||||
|
||||
useEffect(() => {
|
||||
if (credentials) {
|
||||
@@ -160,6 +207,7 @@ export const SpeechProviderSelection = ({
|
||||
options={ttsVendorOptions.filter(
|
||||
(vendor) =>
|
||||
vendor.value != VENDOR_DEEPGRAM &&
|
||||
vendor.value != VENDOR_ASSEMBLYAI &&
|
||||
vendor.value != VENDOR_SONIOX &&
|
||||
vendor.value !== VENDOR_CUSTOM &&
|
||||
vendor.value !== VENDOR_COBALT
|
||||
@@ -185,10 +233,18 @@ export const SpeechProviderSelection = ({
|
||||
}
|
||||
|
||||
if (vendor === VENDOR_ELEVENLABS) {
|
||||
const newLang = synthesis[vendor].find(
|
||||
(lang) => lang.code === ELEVENLABS_LANG_EN
|
||||
);
|
||||
// Samve Voices applied to all languages
|
||||
// Voices are only available for the 1st language.
|
||||
setSynthLang(ELEVENLABS_LANG_EN);
|
||||
setSynthVoice(synthesis[vendor][0].voices[0].value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vendor === VENDOR_WHISPER) {
|
||||
const newLang = synthesis[vendor].find(
|
||||
(lang) => lang.code === LANG_EN_US
|
||||
);
|
||||
setSynthLang(LANG_EN_US);
|
||||
setSynthVoice(newLang!.voices[0].value);
|
||||
return;
|
||||
}
|
||||
@@ -323,6 +379,8 @@ export const SpeechProviderSelection = ({
|
||||
options={sttVendorOptions.filter(
|
||||
(vendor) =>
|
||||
vendor.value != VENDOR_WELLSAID &&
|
||||
vendor.value != VENDOR_ELEVENLABS &&
|
||||
vendor.value != VENDOR_WHISPER &&
|
||||
vendor.value !== VENDOR_CUSTOM
|
||||
)}
|
||||
onChange={(e) => {
|
||||
|
||||
@@ -336,10 +336,13 @@ export const CarrierForm = ({
|
||||
const gateway = sipGateways[i];
|
||||
const type = getIpValidationType(gateway.ipv4);
|
||||
|
||||
/** DH: unclear why we had this restriction, removing for now
|
||||
if (type === FQDN_TOP_LEVEL) {
|
||||
refSipIp.current[i].focus();
|
||||
return "When using an FQDN, you must use a subdomain (e.g. sip.example.com).";
|
||||
} else if (type === FQDN && (!gateway.outbound || gateway.inbound)) {
|
||||
*/
|
||||
if (type === FQDN && (!gateway.outbound || gateway.inbound)) {
|
||||
refSipIp.current[i].focus();
|
||||
return "A fully qualified domain name may only be used for outbound calls.";
|
||||
} else if (type === INVALID) {
|
||||
@@ -636,7 +639,12 @@ export const CarrierForm = ({
|
||||
|
||||
return (
|
||||
<Section slim>
|
||||
<form className="form form--internal" onSubmit={handleSubmit}>
|
||||
<form
|
||||
className={`form form--internal ${
|
||||
!carrier?.data && carrier?.refetch ? "form--blur" : ""
|
||||
}`}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<fieldset>
|
||||
<MS>{MSG_REQUIRED_FIELDS}</MS>
|
||||
</fieldset>
|
||||
|
||||
@@ -155,7 +155,7 @@ export const Carriers = () => {
|
||||
</Icon>
|
||||
</Link>
|
||||
</section>
|
||||
<section className="filters filters--spaced">
|
||||
<section className="filters filters--multi">
|
||||
<SearchFilter
|
||||
placeholder="Filter carriers"
|
||||
filter={[filter, setFilter]}
|
||||
|
||||
@@ -10,6 +10,13 @@ type CarrierProps = {
|
||||
};
|
||||
|
||||
export const RegisterStatus = ({ carrier }: CarrierProps) => {
|
||||
const getReason = () => {
|
||||
return carrier.register_status.reason
|
||||
? typeof carrier.register_status.reason === "string"
|
||||
? carrier.register_status.reason
|
||||
: "Not Started"
|
||||
: "Not Started";
|
||||
};
|
||||
const renderStatus = () => {
|
||||
return (
|
||||
<div
|
||||
@@ -20,7 +27,7 @@ export const RegisterStatus = ({ carrier }: CarrierProps) => {
|
||||
: "jam"
|
||||
: "jean"
|
||||
}`}
|
||||
title={carrier.register_status.reason || "Not Started"}
|
||||
title={getReason()}
|
||||
>
|
||||
{carrier.register_status.status === CARRIER_REG_OK ? (
|
||||
<Icons.CheckCircle />
|
||||
@@ -40,8 +47,7 @@ export const RegisterStatus = ({ carrier }: CarrierProps) => {
|
||||
<details className={carrier.register_status.status || "not-tested"}>
|
||||
<summary>{renderStatus()}</summary>
|
||||
<MS>
|
||||
<strong>Reason:</strong>{" "}
|
||||
{carrier.register_status.reason || "Not Started"}
|
||||
<strong>Reason:</strong> {getReason()}
|
||||
</MS>
|
||||
<PcapButton
|
||||
accountSid={carrier.account_sid || ""}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { H1 } from "@jambonz/ui-kit";
|
||||
import React, { useEffect } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useApiData } from "src/api";
|
||||
import { Client } from "src/api/types";
|
||||
import { toastError } from "src/store";
|
||||
import ClientsForm from "./form";
|
||||
import { ROUTE_INTERNAL_CLIENTS } from "src/router/routes";
|
||||
|
||||
export const ClientsEdit = () => {
|
||||
const params = useParams();
|
||||
const navigate = useNavigate();
|
||||
const [data, refetch, error] = useApiData<Client>(
|
||||
`Clients/${params.client_sid}`
|
||||
);
|
||||
@@ -16,6 +18,7 @@ export const ClientsEdit = () => {
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
toastError(error.msg);
|
||||
navigate(ROUTE_INTERNAL_CLIENTS);
|
||||
}
|
||||
}, [error]);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from "src/api";
|
||||
import { USER_ACCOUNT } from "src/api/constants";
|
||||
import { Account, Client, UseApiDataMap } from "src/api/types";
|
||||
import { Section } from "src/components";
|
||||
import { Section, Tooltip } from "src/components";
|
||||
import { AccountSelect, Message, Passwd } from "src/components/forms";
|
||||
import { MSG_REQUIRED_FIELDS } from "src/constants";
|
||||
import { ROUTE_INTERNAL_CLIENTS } from "src/router/routes";
|
||||
@@ -30,7 +30,12 @@ export const ClientsForm = ({ client }: ClientsFormProps) => {
|
||||
const [accountSid, setAccountSid] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [username, setUsername] = useState("");
|
||||
const [isActive, setIsActive] = useState(true);
|
||||
const [isActive, setIsActive] = useState(
|
||||
client ? client.data?.is_active : true
|
||||
);
|
||||
const [allowDirectAppCalling, setAllowDirectAppCalling] = useState(true);
|
||||
const [allowDirectQueueCalling, setAllowDirectQueueCalling] = useState(true);
|
||||
const [allowDirectUserCalling, setAllowDirectUserCalling] = useState(true);
|
||||
const [modal, setModal] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
@@ -42,6 +47,9 @@ export const ClientsForm = ({ client }: ClientsFormProps) => {
|
||||
username: username,
|
||||
password: password,
|
||||
is_active: isActive,
|
||||
allow_direct_app_calling: allowDirectAppCalling,
|
||||
allow_direct_queue_calling: allowDirectQueueCalling,
|
||||
allow_direct_user_calling: allowDirectUserCalling,
|
||||
})
|
||||
.then(() => {
|
||||
toastSuccess("Client created successfully");
|
||||
@@ -52,10 +60,10 @@ export const ClientsForm = ({ client }: ClientsFormProps) => {
|
||||
});
|
||||
} else {
|
||||
putClient(client.data?.client_sid || "", {
|
||||
account_sid: accountSid,
|
||||
username: username,
|
||||
...(password && { password: password }),
|
||||
is_active: isActive,
|
||||
allow_direct_app_calling: allowDirectAppCalling,
|
||||
allow_direct_queue_calling: allowDirectQueueCalling,
|
||||
allow_direct_user_calling: allowDirectUserCalling,
|
||||
})
|
||||
.then(() => {
|
||||
toastSuccess("Client updated successfully");
|
||||
@@ -99,6 +107,9 @@ export const ClientsForm = ({ client }: ClientsFormProps) => {
|
||||
}
|
||||
|
||||
setIsActive(client.data.is_active);
|
||||
setAllowDirectAppCalling(client.data.allow_direct_app_calling);
|
||||
setAllowDirectQueueCalling(client.data.allow_direct_queue_calling);
|
||||
setAllowDirectUserCalling(client.data.allow_direct_user_calling);
|
||||
}
|
||||
}, [client]);
|
||||
|
||||
@@ -107,8 +118,6 @@ export const ClientsForm = ({ client }: ClientsFormProps) => {
|
||||
if (!accountSid || !accounts || !acc) return;
|
||||
if (!acc?.sip_realm) {
|
||||
setErrorMessage(`Sip realm is not set for the account.`);
|
||||
} else if (!acc?.device_calling_application_sid) {
|
||||
setErrorMessage(`Device calling application is not set for the account.`);
|
||||
} else {
|
||||
setErrorMessage("");
|
||||
}
|
||||
@@ -116,7 +125,12 @@ export const ClientsForm = ({ client }: ClientsFormProps) => {
|
||||
return (
|
||||
<>
|
||||
<Section slim>
|
||||
<form className="form form--internal" onSubmit={handleSubmit}>
|
||||
<form
|
||||
className={`form form--internal ${
|
||||
!client?.data && client?.refetch ? "form--blur" : ""
|
||||
}`}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<fieldset>
|
||||
<MS>{MSG_REQUIRED_FIELDS}</MS>
|
||||
{errorMessage && <Message message={errorMessage} />}
|
||||
@@ -134,22 +148,12 @@ export const ClientsForm = ({ client }: ClientsFormProps) => {
|
||||
placeholder="user name"
|
||||
value={username}
|
||||
required={true}
|
||||
disabled={hasValue(client)}
|
||||
autoComplete="off"
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<label htmlFor="is_active" className="chk">
|
||||
<input
|
||||
id="is_active"
|
||||
name="is_active"
|
||||
type="checkbox"
|
||||
checked={isActive}
|
||||
onChange={(e) => setIsActive(e.target.checked)}
|
||||
/>
|
||||
<div>Active</div>
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<label htmlFor="password">
|
||||
Password{!hasValue(client) && <span>*</span>}
|
||||
</label>
|
||||
@@ -160,14 +164,67 @@ export const ClientsForm = ({ client }: ClientsFormProps) => {
|
||||
value={password}
|
||||
placeholder="Password"
|
||||
setValue={setPassword}
|
||||
disabled={hasValue(client)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<label htmlFor="is_active" className="chk">
|
||||
<input
|
||||
id="is_active"
|
||||
name="is_active"
|
||||
type="checkbox"
|
||||
checked={isActive}
|
||||
onChange={(e) => setIsActive(e.target.checked)}
|
||||
/>
|
||||
<div>Active</div>
|
||||
</label>
|
||||
<label htmlFor="allow_direct_app_calling" className="chk">
|
||||
<input
|
||||
id="allow_direct_app_calling"
|
||||
name="allow_direct_app_calling"
|
||||
type="checkbox"
|
||||
checked={allowDirectAppCalling}
|
||||
onChange={(e) => setAllowDirectAppCalling(e.target.checked)}
|
||||
/>
|
||||
<div>Allow direct calling to applications</div>
|
||||
<Tooltip text="Allow user to call applications without configuring an application for sip device calls.">
|
||||
{" "}
|
||||
</Tooltip>
|
||||
</label>
|
||||
<label htmlFor="allow_direct_queue_calling" className="chk">
|
||||
<input
|
||||
id="allow_direct_queue_calling"
|
||||
name="allow_direct_queue_calling"
|
||||
type="checkbox"
|
||||
checked={allowDirectQueueCalling}
|
||||
onChange={(e) => setAllowDirectQueueCalling(e.target.checked)}
|
||||
/>
|
||||
<div>Allow direct calling to queues</div>
|
||||
<Tooltip text="Allow user to take calls from queues without configuring an application for sip device calls.">
|
||||
{" "}
|
||||
</Tooltip>
|
||||
</label>
|
||||
<label htmlFor="allow_direct_user_calling" className="chk">
|
||||
<input
|
||||
id="allow_direct_user_calling"
|
||||
name="allow_direct_user_calling"
|
||||
type="checkbox"
|
||||
checked={allowDirectUserCalling}
|
||||
onChange={(e) => setAllowDirectUserCalling(e.target.checked)}
|
||||
/>
|
||||
<div>Allow direct calling to other users</div>
|
||||
<Tooltip text="Allow user to call other users without configuring an application for sip device calls.">
|
||||
{" "}
|
||||
</Tooltip>
|
||||
</label>
|
||||
</fieldset>
|
||||
{user?.scope !== USER_ACCOUNT && (
|
||||
<fieldset>
|
||||
<AccountSelect
|
||||
accounts={accounts}
|
||||
account={[accountSid, setAccountSid]}
|
||||
label="Used by"
|
||||
label="Belongs to"
|
||||
required={true}
|
||||
defaultOption={false}
|
||||
disabled={hasValue(client)}
|
||||
|
||||
@@ -126,7 +126,7 @@ export const Clients = () => {
|
||||
</Link>
|
||||
</section>
|
||||
|
||||
<section className="filters filters--spaced">
|
||||
<section className="filters filters--multi">
|
||||
<SearchFilter
|
||||
placeholder="Filter clients"
|
||||
filter={[filter, setFilter]}
|
||||
|
||||
@@ -6,7 +6,6 @@ import update from "immutability-helper";
|
||||
import { deleteLcrRoute } from "src/api";
|
||||
import { toastError, toastSuccess } from "src/store";
|
||||
import { SelectorOption } from "src/components/forms/selector";
|
||||
import { NOT_AVAILABLE_PREFIX } from "src/constants";
|
||||
|
||||
type ContainerProps = {
|
||||
lcrRoute: [LcrRoute[], React.Dispatch<React.SetStateAction<LcrRoute[]>>];
|
||||
@@ -61,11 +60,7 @@ export const Container = ({
|
||||
};
|
||||
|
||||
const handleRouteDelete = (r: LcrRoute | undefined, index: number) => {
|
||||
if (
|
||||
r &&
|
||||
r.lcr_route_sid &&
|
||||
!r.lcr_route_sid.startsWith(NOT_AVAILABLE_PREFIX)
|
||||
) {
|
||||
if (r && r.lcr_route_sid) {
|
||||
deleteLcrRoute(r.lcr_route_sid)
|
||||
.then(() => {
|
||||
toastSuccess("Least cost routing rule successfully deleted");
|
||||
@@ -82,7 +77,7 @@ export const Container = ({
|
||||
{hasLength(lcrRoutes) &&
|
||||
lcrRoutes.map((lr, i) => (
|
||||
<Card
|
||||
key={lr.lcr_route_sid}
|
||||
key={lr.lcr_route_sid || i}
|
||||
lr={lr}
|
||||
index={i}
|
||||
moveCard={moveCard}
|
||||
|
||||
@@ -8,37 +8,33 @@ import {
|
||||
useDispatch,
|
||||
useSelectState,
|
||||
} from "src/store";
|
||||
import { MSG_REQUIRED_FIELDS, NOT_AVAILABLE_PREFIX } from "src/constants";
|
||||
import { MSG_REQUIRED_FIELDS } from "src/constants";
|
||||
import { setLocation } from "src/store/localStore";
|
||||
import { AccountSelect, Message, Selector } from "src/components/forms";
|
||||
import type {
|
||||
Account,
|
||||
Carrier,
|
||||
Lcr,
|
||||
LcrCarrierSetEntry,
|
||||
LcrRoute,
|
||||
UseApiDataMap,
|
||||
} from "src/api/types";
|
||||
import { ROUTE_INTERNAL_LEST_COST_ROUTING } from "src/router/routes";
|
||||
import {
|
||||
deleteLcr,
|
||||
postLcrCarrierSetEntry,
|
||||
putLcrCarrierSetEntries,
|
||||
putLcrRoutes,
|
||||
putLcr,
|
||||
postLcrCreateRoutes,
|
||||
putLcrUpdateRoutes,
|
||||
useApiData,
|
||||
useServiceProviderData,
|
||||
getLcrRoute,
|
||||
} from "src/api";
|
||||
import { USER_ACCOUNT, USER_ADMIN } from "src/api/constants";
|
||||
import { postLcr } from "src/api";
|
||||
import { postLcrRoute } from "src/api";
|
||||
import DeleteLcr from "./delete";
|
||||
import { Scope } from "src/store/types";
|
||||
import { DndProvider } from "react-dnd";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import Container from "./container";
|
||||
import { v4 } from "uuid";
|
||||
import { hasValue } from "src/utils";
|
||||
|
||||
type LcrFormProps = {
|
||||
lcrDataMap?: UseApiDataMap<Lcr>;
|
||||
@@ -47,7 +43,7 @@ type LcrFormProps = {
|
||||
|
||||
export const LcrForm = ({ lcrDataMap, lcrRouteDataMap }: LcrFormProps) => {
|
||||
const LCR_ROUTE_TEMPLATE: LcrRoute = {
|
||||
lcr_route_sid: `${NOT_AVAILABLE_PREFIX}${v4()}`,
|
||||
lcr_route_sid: "",
|
||||
regex: "",
|
||||
lcr_sid: "",
|
||||
priority: 0,
|
||||
@@ -68,7 +64,7 @@ export const LcrForm = ({ lcrDataMap, lcrRouteDataMap }: LcrFormProps) => {
|
||||
const [defaultLcrCarrier, setDefaultLcrCarrier] = useState("");
|
||||
const [defaultLcrCarrierSetEntrySid, setDefaultLcrCarrierSetEntrySid] =
|
||||
useState<string | null>();
|
||||
const [defaultLcrRouteSid, setDefaultLcrRouteSid] = useState("");
|
||||
const [defaultLcrRoute, setDefaultLcrRoute] = useState<LcrRoute | null>(null);
|
||||
const [defaultCarrier, setDefaultCarrier] = useState("");
|
||||
const [apiUrl, setApiUrl] = useState("");
|
||||
const [accountSid, setAccountSid] = useState("");
|
||||
@@ -136,44 +132,53 @@ export const LcrForm = ({ lcrDataMap, lcrRouteDataMap }: LcrFormProps) => {
|
||||
setPreviousLcr(lcrDataMap.data);
|
||||
}
|
||||
|
||||
if (
|
||||
lcrRouteDataMap &&
|
||||
lcrRouteDataMap.data &&
|
||||
lcrRouteDataMap.data !== previousLcrRoutes
|
||||
) {
|
||||
setPreviousLcrRoutes(lcrRouteDataMap.data);
|
||||
// Find default carrier
|
||||
lcrRouteDataMap.data.forEach((lr) => {
|
||||
lr.lcr_carrier_set_entries?.forEach((entry) => {
|
||||
if (
|
||||
entry.lcr_carrier_set_entry_sid ===
|
||||
lcrDataMap?.data?.default_carrier_set_entry_sid
|
||||
) {
|
||||
setDefaultLcrCarrier(entry.voip_carrier_sid || defaultCarrier);
|
||||
setDefaultLcrCarrierSetEntrySid(
|
||||
entry.lcr_carrier_set_entry_sid || null
|
||||
);
|
||||
setDefaultLcrRouteSid(entry.lcr_route_sid || "");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
useMemo(() => {
|
||||
let default_lcr_route_sid = "";
|
||||
if (
|
||||
lcrRouteDataMap &&
|
||||
lcrRouteDataMap.data &&
|
||||
lcrRouteDataMap.data !== previousLcrRoutes
|
||||
) {
|
||||
setPreviousLcrRoutes(lcrRouteDataMap.data);
|
||||
// Find default carrier
|
||||
lcrRouteDataMap.data.forEach((lr) => {
|
||||
lr.lcr_carrier_set_entries?.forEach((entry) => {
|
||||
if (
|
||||
entry.lcr_carrier_set_entry_sid ===
|
||||
lcrDataMap?.data?.default_carrier_set_entry_sid
|
||||
) {
|
||||
setDefaultLcrCarrier(entry.voip_carrier_sid || defaultCarrier);
|
||||
setDefaultLcrCarrierSetEntrySid(
|
||||
entry.lcr_carrier_set_entry_sid || null
|
||||
);
|
||||
default_lcr_route_sid = entry.lcr_route_sid || "";
|
||||
setDefaultLcrRoute(lr);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (lcrRouteDataMap && lcrRouteDataMap.data)
|
||||
setLcrRoutes(
|
||||
lcrRouteDataMap.data.filter(
|
||||
(route) => route.lcr_route_sid !== defaultLcrRouteSid
|
||||
(route) => route.lcr_route_sid !== default_lcr_route_sid
|
||||
)
|
||||
);
|
||||
}, [defaultLcrRouteSid]);
|
||||
}, [lcrRouteDataMap?.data]);
|
||||
|
||||
const addLcrRoutes = () => {
|
||||
const newLcrRoute = LCR_ROUTE_TEMPLATE;
|
||||
const ls = [
|
||||
...lcrRoutes,
|
||||
{
|
||||
...LCR_ROUTE_TEMPLATE,
|
||||
...newLcrRoute,
|
||||
priority: lcrRoutes.length,
|
||||
lcr_carrier_set_entries: newLcrRoute.lcr_carrier_set_entries?.map(
|
||||
(r) => ({
|
||||
...r,
|
||||
voip_carrier_sid: defaultCarrier || carrierSelectorOptions[0].value,
|
||||
})
|
||||
),
|
||||
},
|
||||
];
|
||||
setLcrRoutes(ls);
|
||||
@@ -194,11 +199,48 @@ export const LcrForm = ({ lcrDataMap, lcrRouteDataMap }: LcrFormProps) => {
|
||||
const lcrPayload: Lcr = getLcrPayload();
|
||||
postLcr(lcrPayload)
|
||||
.then(({ json }) => {
|
||||
Promise.all(
|
||||
lcrRoutes.map((route, i) => handleLcrRoutePost(json.sid, route, i))
|
||||
)
|
||||
const lcrsPayload = lcrRoutes.map((l, i) => ({
|
||||
...l,
|
||||
lcr_carrier_set_entries: l.lcr_carrier_set_entries?.map((e) => ({
|
||||
...e,
|
||||
voip_carrier_sid:
|
||||
e.voip_carrier_sid ||
|
||||
defaultCarrier ||
|
||||
carrierSelectorOptions[0]?.value,
|
||||
})),
|
||||
lcr_sid: json.sid,
|
||||
priority: i,
|
||||
}));
|
||||
lcrsPayload.push({
|
||||
lcr_sid: json.sid,
|
||||
regex: ".*",
|
||||
description: "System Default Route",
|
||||
priority: 9999,
|
||||
lcr_carrier_set_entries: [
|
||||
{
|
||||
lcr_route_sid: "",
|
||||
voip_carrier_sid:
|
||||
defaultLcrCarrier || carrierSelectorOptions[0]?.value,
|
||||
priority: 0,
|
||||
},
|
||||
],
|
||||
});
|
||||
postLcrCreateRoutes(json.sid, lcrsPayload)
|
||||
.then(() => {
|
||||
handleLcrDefaultCarrierPost(json.sid);
|
||||
if (lcrDataMap) {
|
||||
toastSuccess("Least cost routing successfully updated");
|
||||
} else {
|
||||
toastSuccess("Least cost routing successfully created");
|
||||
if (user?.access === Scope.admin) {
|
||||
navigate(ROUTE_INTERNAL_LEST_COST_ROUTING);
|
||||
} else {
|
||||
navigate(
|
||||
`${ROUTE_INTERNAL_LEST_COST_ROUTING}/${json.sid}/edit`
|
||||
);
|
||||
}
|
||||
// Update global state
|
||||
dispatch({ type: "lcr" });
|
||||
}
|
||||
})
|
||||
.catch(({ msg }) => {
|
||||
toastError(msg);
|
||||
@@ -209,209 +251,38 @@ export const LcrForm = ({ lcrDataMap, lcrRouteDataMap }: LcrFormProps) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleLcrDefaultCarrierPost = (lcr_sid: string) => {
|
||||
const defaultRoute = {
|
||||
lcr_sid: lcr_sid,
|
||||
regex: ".*",
|
||||
desciption: "System Default Route",
|
||||
priority: 9999,
|
||||
lcr_carrier_set_entries: [
|
||||
{
|
||||
lcr_route_sid: "",
|
||||
voip_carrier_sid: defaultLcrCarrier,
|
||||
priority: 0,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
handleLcrRoutePost(lcr_sid, defaultRoute, 9999).then((lcr_route_sid) => {
|
||||
// There is small hack here to wait the data commited in bd for lcr entries
|
||||
new Promise(async (r) => setTimeout(() => r(0), 300)).then(() => {
|
||||
getLcrRoute(lcr_route_sid).then(({ json }) => {
|
||||
if (json.lcr_carrier_set_entries?.length) {
|
||||
const lcr_carrier_set_entry_sid =
|
||||
json.lcr_carrier_set_entries[0].lcr_carrier_set_entry_sid;
|
||||
putLcr(lcr_sid, {
|
||||
default_carrier_set_entry_sid: lcr_carrier_set_entry_sid,
|
||||
})
|
||||
.then(() => {
|
||||
if (lcrDataMap) {
|
||||
toastSuccess("Least cost routing successfully updated");
|
||||
} else {
|
||||
toastSuccess("Least cost routing successfully created");
|
||||
if (user?.access === Scope.admin) {
|
||||
navigate(ROUTE_INTERNAL_LEST_COST_ROUTING);
|
||||
} else {
|
||||
navigate(
|
||||
`${ROUTE_INTERNAL_LEST_COST_ROUTING}/${lcr_sid}/edit`
|
||||
);
|
||||
}
|
||||
// Update global state
|
||||
dispatch({ type: "lcr" });
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toastError(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleLcrRoutePost = (
|
||||
lcr_sid: string,
|
||||
route: LcrRoute,
|
||||
priority: number
|
||||
): Promise<string> => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const lcrRoutePayload: LcrRoute = {
|
||||
lcr_sid,
|
||||
regex: route.regex,
|
||||
priority,
|
||||
};
|
||||
postLcrRoute(lcrRoutePayload)
|
||||
.then(({ json }) => {
|
||||
if (route.lcr_carrier_set_entries) {
|
||||
Promise.all(
|
||||
route.lcr_carrier_set_entries.map((entry) => {
|
||||
handleLcrCarrierSetEntryPost(json.sid, entry);
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
resolve(json.sid);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleLcrCarrierSetEntryPost = (
|
||||
lcr_route_sid: string,
|
||||
entry: LcrCarrierSetEntry
|
||||
): Promise<string> => {
|
||||
const lcrCarrierSetEntryPayload: LcrCarrierSetEntry = {
|
||||
...entry,
|
||||
voip_carrier_sid: entry.voip_carrier_sid || defaultCarrier,
|
||||
lcr_route_sid,
|
||||
};
|
||||
return new Promise<string>(async (r, e) => {
|
||||
postLcrCarrierSetEntry(lcrCarrierSetEntryPayload)
|
||||
.then(({ json }) => r(json.sid))
|
||||
.catch((error) => e(error));
|
||||
});
|
||||
};
|
||||
|
||||
const handleLcrPut = () => {
|
||||
if (lcrDataMap && lcrDataMap.data && lcrDataMap.data.lcr_sid) {
|
||||
// update LCR
|
||||
const lcrPayload: Lcr = getLcrPayload();
|
||||
putLcr(lcrDataMap.data.lcr_sid, lcrPayload).then(() => {
|
||||
Promise.all(
|
||||
lcrRoutes.map((route, i) => {
|
||||
if (
|
||||
route.lcr_route_sid &&
|
||||
!route.lcr_route_sid?.startsWith(NOT_AVAILABLE_PREFIX)
|
||||
) {
|
||||
handleLcrRoutePut(
|
||||
lcrDataMap.data?.lcr_sid || "",
|
||||
route.lcr_route_sid,
|
||||
route,
|
||||
i
|
||||
);
|
||||
} else {
|
||||
handleLcrRoutePost(lcrDataMap.data?.lcr_sid || "", route, i);
|
||||
}
|
||||
})
|
||||
)
|
||||
putLcrUpdateRoutes(lcrDataMap.data?.lcr_sid || "", [
|
||||
...lcrRoutes.map((r, i) => ({
|
||||
...r,
|
||||
priority: i,
|
||||
})),
|
||||
...(hasValue(defaultLcrRoute)
|
||||
? [
|
||||
{
|
||||
...defaultLcrRoute,
|
||||
lcr_carrier_set_entries:
|
||||
defaultLcrRoute.lcr_carrier_set_entries?.map((r) => ({
|
||||
...r,
|
||||
voip_carrier_sid:
|
||||
defaultLcrCarrier ||
|
||||
r.voip_carrier_sid ||
|
||||
carrierSelectorOptions[0].value,
|
||||
})),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
])
|
||||
.then(() => {
|
||||
if (defaultLcrCarrierSetEntrySid) {
|
||||
const defaultEntry: LcrCarrierSetEntry = {
|
||||
lcr_route_sid: defaultLcrRouteSid,
|
||||
voip_carrier_sid: defaultLcrCarrier,
|
||||
priority: 0,
|
||||
};
|
||||
|
||||
handleLcrCarrierEntryPut(
|
||||
defaultLcrRouteSid,
|
||||
defaultLcrCarrierSetEntrySid,
|
||||
defaultEntry
|
||||
).then(() => {
|
||||
toastSuccess("Least cost routing rule successfully updated");
|
||||
});
|
||||
}
|
||||
toastSuccess("Least cost routing rule successfully updated");
|
||||
})
|
||||
.catch((error) => toastError(error));
|
||||
});
|
||||
}
|
||||
|
||||
const handleLcrRoutePut = (
|
||||
lcr_sid: string,
|
||||
lcr_route_sid: string,
|
||||
route: LcrRoute,
|
||||
priority: number
|
||||
): Promise<string> => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const lcrRoutePayload: LcrRoute = {
|
||||
lcr_sid,
|
||||
regex: route.regex,
|
||||
priority,
|
||||
};
|
||||
putLcrRoutes(lcr_route_sid, lcrRoutePayload).then(() => {
|
||||
if (
|
||||
route.lcr_carrier_set_entries &&
|
||||
route.lcr_carrier_set_entries.length > 0
|
||||
) {
|
||||
Promise.all(
|
||||
route.lcr_carrier_set_entries.map((entry) => {
|
||||
if (entry.lcr_carrier_set_entry_sid) {
|
||||
return handleLcrCarrierEntryPut(
|
||||
entry.lcr_route_sid || lcr_route_sid,
|
||||
entry.lcr_carrier_set_entry_sid,
|
||||
entry
|
||||
);
|
||||
} else {
|
||||
return handleLcrCarrierSetEntryPost(lcr_route_sid, entry);
|
||||
}
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
resolve("Least cost routing rule successfully updated");
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleLcrCarrierEntryPut = (
|
||||
lcr_route_sid: string,
|
||||
lcr_carrier_set_entry_sid: string,
|
||||
entry: LcrCarrierSetEntry
|
||||
): Promise<string> => {
|
||||
const lcrCarrierSetEntryPayload: LcrCarrierSetEntry = {
|
||||
lcr_route_sid,
|
||||
workload: entry.workload,
|
||||
voip_carrier_sid: entry.voip_carrier_sid || defaultCarrier,
|
||||
priority: entry.priority,
|
||||
};
|
||||
return new Promise<string>(async (r, e) => {
|
||||
putLcrCarrierSetEntries(
|
||||
lcr_carrier_set_entry_sid,
|
||||
lcrCarrierSetEntryPayload
|
||||
)
|
||||
.then(() => r("success"))
|
||||
.catch((error) => e(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
@@ -449,7 +320,12 @@ export const LcrForm = ({ lcrDataMap, lcrRouteDataMap }: LcrFormProps) => {
|
||||
return (
|
||||
<>
|
||||
<Section slim>
|
||||
<form className="form form--internal" onSubmit={handleSubmit}>
|
||||
<form
|
||||
className={`form form--internal ${
|
||||
!lcrDataMap?.data && lcrDataMap?.refetch ? "form--blur" : ""
|
||||
}`}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<fieldset>
|
||||
<MS>{MSG_REQUIRED_FIELDS}</MS>
|
||||
{errorMessage && <Message message={errorMessage} />}
|
||||
|
||||
@@ -99,7 +99,7 @@ export const Lcrs = () => {
|
||||
multiple carriers available.
|
||||
</M>
|
||||
</section>
|
||||
<section className="filters filters--spaced">
|
||||
<section className="filters filters--multi">
|
||||
<SearchFilter placeholder="Filter lcrs" filter={[filter, setFilter]} />
|
||||
<ScopedAccess user={user} scope={Scope.admin}>
|
||||
<AccountFilter
|
||||
|
||||
@@ -120,7 +120,12 @@ export const MsTeamsTenantForm = ({
|
||||
|
||||
return (
|
||||
<Section slim>
|
||||
<form className="form form--internal" onSubmit={handleSubmit}>
|
||||
<form
|
||||
className={`form form--internal ${
|
||||
!msTeamsTenant?.data && msTeamsTenant?.refetch ? "form--blur" : ""
|
||||
}`}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<fieldset>
|
||||
<MS>{MSG_REQUIRED_FIELDS}</MS>
|
||||
</fieldset>
|
||||
|
||||
@@ -89,7 +89,7 @@ export const MSTeamsTenants = () => {
|
||||
</Link>
|
||||
)}
|
||||
</section>
|
||||
<section className="filters filters--spaced">
|
||||
<section className="filters filters--multi">
|
||||
<SearchFilter
|
||||
placeholder="Filter ms teams tenants"
|
||||
filter={[filter, setFilter]}
|
||||
|
||||
@@ -141,7 +141,12 @@ export const PhoneNumberForm = ({ phoneNumber }: PhoneNumberFormProps) => {
|
||||
return (
|
||||
<>
|
||||
<Section slim>
|
||||
<form className="form form--internal" onSubmit={handleSubmit}>
|
||||
<form
|
||||
className={`form form--internal ${
|
||||
!phoneNumber?.data && phoneNumber?.refetch ? "form--blur" : ""
|
||||
}`}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<fieldset>
|
||||
<MS>{MSG_REQUIRED_FIELDS}</MS>
|
||||
</fieldset>
|
||||
|
||||
@@ -129,7 +129,7 @@ export const PhoneNumbers = () => {
|
||||
</Link>
|
||||
)}
|
||||
</section>
|
||||
<section className="filters filters--spaced">
|
||||
<section className="filters filters--multi">
|
||||
<SearchFilter
|
||||
placeholder="Filter phone numbers"
|
||||
filter={[filter, setFilter]}
|
||||
|
||||
@@ -14,10 +14,10 @@ import {
|
||||
JaegerSpan,
|
||||
WaveSurferDtmfResult,
|
||||
WaveSurferSttResult,
|
||||
WaveSurferTtsLatencyResult,
|
||||
} from "src/api/jaeger-types";
|
||||
import {
|
||||
getSpanAttributeByName,
|
||||
getSpansByName,
|
||||
getSpansByNameRegex,
|
||||
getSpansFromJaegerRoot,
|
||||
} from "./utils";
|
||||
@@ -42,6 +42,9 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
useState<WaveSurferSttResult | null>();
|
||||
const [waveSurferDtmfData, setWaveSurferDtmfData] =
|
||||
useState<WaveSurferDtmfResult | null>();
|
||||
|
||||
const [waveSurferTtsLatencyData, setWaveSurferTtsLatencyData] =
|
||||
useState<WaveSurferTtsLatencyResult | null>();
|
||||
const [regionChecked, setRegionChecked] = useState(false);
|
||||
|
||||
const wavesurferId = `wavesurfer--${call_sid}`;
|
||||
@@ -112,6 +115,43 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
});
|
||||
};
|
||||
|
||||
const PEAKS_WINDOW = 5; // require 30 ms of speech energy over threshold to trigger
|
||||
const PEAK_THRESHOLD = 0.03;
|
||||
|
||||
const getSilenceStartTime = (
|
||||
start: number,
|
||||
end: number,
|
||||
channel: number
|
||||
): number => {
|
||||
if (waveSurferRef.current) {
|
||||
const duration = waveSurferRef.current.getDecodedData()?.duration;
|
||||
if (duration && duration > 0) {
|
||||
const maxLength = Math.round(duration * 8000) / 10; // evaluate speech energy every 10 ms
|
||||
const peaks = waveSurferRef.current.exportPeaks({ maxLength });
|
||||
if (peaks && peaks.length > channel) {
|
||||
if (duration && duration > 0) {
|
||||
const data = peaks[channel];
|
||||
const startPeak = Math.ceil((start * data.length) / duration);
|
||||
const endPeak = Math.ceil((end * data.length) / duration);
|
||||
let count = 0;
|
||||
for (let i = endPeak; i > startPeak; i--)
|
||||
if (Math.abs(data[i]) > PEAK_THRESHOLD) {
|
||||
count++;
|
||||
if (count === PEAKS_WINDOW) {
|
||||
return (
|
||||
((i + PEAKS_WINDOW) * duration) / data.length + 0.02 // add 20 ms adjustment
|
||||
);
|
||||
}
|
||||
} else {
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
const drawSttRegionForSpan = (
|
||||
s: JaegerSpan,
|
||||
startPoint: JaegerSpan,
|
||||
@@ -128,26 +168,42 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
const end =
|
||||
(s.endTimeUnixNano - startPoint.startTimeUnixNano) / 1_000_000_000;
|
||||
|
||||
const region = waveSurferRegionsPluginRef.current.addRegion({
|
||||
id: s.spanId,
|
||||
start,
|
||||
end,
|
||||
color: "rgba(255, 0, 0, 0.15)",
|
||||
drag: false,
|
||||
resize: false,
|
||||
});
|
||||
const endSpeechTime = getSilenceStartTime(start, end, channel);
|
||||
|
||||
changeRegionMouseStyle(region, channel);
|
||||
const [sttResult] = getSpanAttributeByName(s.attributes, "stt.result");
|
||||
let att: WaveSurferSttResult;
|
||||
if (sttResult) {
|
||||
const data = JSON.parse(sttResult.value.stringValue);
|
||||
|
||||
att = {
|
||||
vendor: data.vendor.name,
|
||||
transcript: data.alternatives[0].transcript,
|
||||
confidence: data.alternatives[0].confidence,
|
||||
language_code: data.language_code,
|
||||
...(endSpeechTime > 0 && { latency: end - endSpeechTime }),
|
||||
};
|
||||
|
||||
const [sttResolve] = getSpanAttributeByName(
|
||||
s.attributes,
|
||||
"stt.resolve"
|
||||
);
|
||||
if (
|
||||
endSpeechTime > 0 &&
|
||||
sttResolve &&
|
||||
sttResolve.value.stringValue === "speech"
|
||||
) {
|
||||
const latencyRegion = waveSurferRegionsPluginRef.current.addRegion({
|
||||
id: s.spanId + "latency",
|
||||
start: endSpeechTime,
|
||||
end,
|
||||
color: "rgba(255, 255, 0, 0.55)",
|
||||
drag: false,
|
||||
resize: false,
|
||||
content: `${(end - endSpeechTime).toFixed(2)} sec`,
|
||||
});
|
||||
|
||||
changeRegionMouseStyle(latencyRegion, channel);
|
||||
}
|
||||
} else {
|
||||
const [sttResolve] = getSpanAttributeByName(
|
||||
s.attributes,
|
||||
@@ -171,6 +227,17 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
}
|
||||
}
|
||||
|
||||
const region = waveSurferRegionsPluginRef.current.addRegion({
|
||||
id: s.spanId,
|
||||
start,
|
||||
end,
|
||||
color: "rgba(255, 0, 0, 0.15)",
|
||||
drag: false,
|
||||
resize: false,
|
||||
});
|
||||
|
||||
changeRegionMouseStyle(region, channel);
|
||||
|
||||
region.on("click", () => {
|
||||
setWaveSurferRegionData(att);
|
||||
});
|
||||
@@ -178,10 +245,49 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
const drawTtsLatencyRegion = (s: JaegerSpan, startPoint: JaegerSpan) => {
|
||||
if (waveSurferRegionsPluginRef.current) {
|
||||
const r = waveSurferRegionsPluginRef.current
|
||||
.getRegions()
|
||||
.find((r) => r.id === s.spanId);
|
||||
if (!r) {
|
||||
const start =
|
||||
(s.startTimeUnixNano - startPoint.startTimeUnixNano) / 1_000_000_000;
|
||||
const end =
|
||||
(s.endTimeUnixNano - startPoint.startTimeUnixNano) / 1_000_000_000;
|
||||
|
||||
const [ttsVendor] = getSpanAttributeByName(s.attributes, "tts.vendor");
|
||||
const [ttsCache] = getSpanAttributeByName(s.attributes, "tts.cached");
|
||||
if (ttsVendor && ttsCache && !Boolean(ttsCache.value.boolValue)) {
|
||||
const latencyRegion = waveSurferRegionsPluginRef.current.addRegion({
|
||||
id: s.spanId,
|
||||
start: start,
|
||||
end,
|
||||
color: "rgba(255, 155, 0, 0.55)",
|
||||
drag: false,
|
||||
resize: false,
|
||||
content: `${(end - start).toFixed(2)} sec`,
|
||||
});
|
||||
|
||||
changeRegionMouseStyle(latencyRegion, 2);
|
||||
console.log(ttsCache.value.boolValue);
|
||||
|
||||
latencyRegion.on("click", () => {
|
||||
setWaveSurferTtsLatencyData({
|
||||
vendor: ttsVendor.value.stringValue,
|
||||
latency: `${(end - start).toFixed(2)} sec`,
|
||||
isCached: String(ttsCache.value.boolValue),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const buildWavesurferRegion = () => {
|
||||
if (jaegerRoot) {
|
||||
const spans = getSpansFromJaegerRoot(jaegerRoot);
|
||||
const [startPoint] = getSpansByName(spans, "background-listen:listen");
|
||||
const startPoint = spans[0];
|
||||
// there should be only one startPoint for background listen
|
||||
if (startPoint) {
|
||||
const gatherSpans = getSpansByNameRegex(spans, /:gather{/);
|
||||
@@ -204,6 +310,11 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
dtmfSpans.forEach((ds) => {
|
||||
drawDtmfRegionForSpan(ds, startPoint);
|
||||
});
|
||||
|
||||
const ttsSpans = getSpansByNameRegex(spans, /tts-generation/);
|
||||
ttsSpans.forEach((tts) => {
|
||||
drawTtsLatencyRegion(tts, startPoint);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -423,7 +534,7 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div>Overlay STT and DTMF events</div>
|
||||
<div>Overlay STT, TTS latency and DTMF events</div>
|
||||
</label>
|
||||
</div>
|
||||
{waveSurferRegionData && (
|
||||
@@ -475,6 +586,16 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{waveSurferRegionData.latency && (
|
||||
<div className="spanDetailsWrapper__details">
|
||||
<div className="spanDetailsWrapper__details_header">
|
||||
<strong>Latency:</strong>
|
||||
</div>
|
||||
<div className="spanDetailsWrapper__details_body">
|
||||
{waveSurferRegionData.latency.toFixed(2)} seconds
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ModalClose>
|
||||
@@ -509,6 +630,45 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
</div>
|
||||
</ModalClose>
|
||||
)}
|
||||
{waveSurferTtsLatencyData && (
|
||||
<ModalClose handleClose={() => setWaveSurferTtsLatencyData(null)}>
|
||||
<div className="spanDetailsWrapper__header">
|
||||
<P>
|
||||
<strong>Tts Latency</strong>
|
||||
</P>
|
||||
</div>
|
||||
<div className="spanDetailsWrapper">
|
||||
<div className="spanDetailsWrapper__detailsWrapper">
|
||||
<div className="spanDetailsWrapper__details">
|
||||
<div className="spanDetailsWrapper__details_header">
|
||||
<strong>Vendor:</strong>
|
||||
</div>
|
||||
<div className="spanDetailsWrapper__details_body">
|
||||
{waveSurferTtsLatencyData.vendor}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="spanDetailsWrapper__details">
|
||||
<div className="spanDetailsWrapper__details_header">
|
||||
<strong>Latency:</strong>
|
||||
</div>
|
||||
<div className="spanDetailsWrapper__details_body">
|
||||
{waveSurferTtsLatencyData.latency}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="spanDetailsWrapper__details">
|
||||
<div className="spanDetailsWrapper__details_header">
|
||||
<strong>From Cache:</strong>
|
||||
</div>
|
||||
<div className="spanDetailsWrapper__details_body">
|
||||
{waveSurferTtsLatencyData.isCached}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalClose>
|
||||
)}
|
||||
{deleteRecordUrl && (
|
||||
<Modal
|
||||
handleCancel={() => setDeleteRecordUrl("")}
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import React, { Fragment, useEffect, useState } from "react";
|
||||
import { Button, ButtonGroup, MS } from "@jambonz/ui-kit";
|
||||
import { Button, ButtonGroup, Icon, MS, MXS } from "@jambonz/ui-kit";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
|
||||
import { ROUTE_INTERNAL_SPEECH } from "src/router/routes";
|
||||
import { Section, Tooltip } from "src/components";
|
||||
import { Icons, Section, Tooltip } from "src/components";
|
||||
import {
|
||||
FileUpload,
|
||||
Selector,
|
||||
Passwd,
|
||||
AccountSelect,
|
||||
Checkzone,
|
||||
Message,
|
||||
} from "src/components/forms";
|
||||
import { toastError, toastSuccess, useSelectState } from "src/store";
|
||||
import {
|
||||
deleteGoogleCustomVoice,
|
||||
getGoogleCustomVoices,
|
||||
postGoogleCustomVoice,
|
||||
postSpeechService,
|
||||
putGoogleCustomVoice,
|
||||
putSpeechService,
|
||||
useServiceProviderData,
|
||||
} from "src/api";
|
||||
@@ -32,6 +37,9 @@ import {
|
||||
VENDOR_CUSTOM,
|
||||
VENDOR_COBALT,
|
||||
VENDOR_ELEVENLABS,
|
||||
VENDOR_ASSEMBLYAI,
|
||||
VENDOR_WHISPER,
|
||||
useTtsModels,
|
||||
} from "src/vendor";
|
||||
import { MSG_REQUIRED_FIELDS } from "src/constants";
|
||||
import {
|
||||
@@ -39,17 +47,29 @@ import {
|
||||
getObscuredSecret,
|
||||
isUserAccountScope,
|
||||
isNotBlank,
|
||||
hasLength,
|
||||
} from "src/utils";
|
||||
import { getObscuredGoogleServiceKey } from "./utils";
|
||||
import { CredentialStatus } from "./status";
|
||||
|
||||
import type { RegionVendors, GoogleServiceKey, Vendor } from "src/vendor/types";
|
||||
import type { Account, SpeechCredential, UseApiDataMap } from "src/api/types";
|
||||
import type {
|
||||
RegionVendors,
|
||||
GoogleServiceKey,
|
||||
Vendor,
|
||||
TtsModels,
|
||||
} from "src/vendor/types";
|
||||
import type {
|
||||
Account,
|
||||
GoogleCustomVoice,
|
||||
SpeechCredential,
|
||||
UseApiDataMap,
|
||||
} from "src/api/types";
|
||||
import { setAccountFilter, setLocation } from "src/store/localStore";
|
||||
import {
|
||||
DEFAULT_ELEVENLABS_MODEL,
|
||||
DEFAULT_ELEVENLABS_OPTIONS,
|
||||
DEFAULT_GOOGLE_CUSTOM_VOICES_REPORTED_USAGE,
|
||||
DISABLE_CUSTOM_SPEECH,
|
||||
ELEVENLABS_MODEL_OPTIONS,
|
||||
GOOGLE_CUSTOM_VOICES_REPORTED_USAGE,
|
||||
} from "src/api/constants";
|
||||
|
||||
type SpeechServiceFormProps = {
|
||||
@@ -82,7 +102,7 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
const [sttApiKey, setSttApiKey] = useState("");
|
||||
const [ttsRegion, setTtsRegion] = useState("");
|
||||
const [ttsApiKey, setTtsApiKey] = useState("");
|
||||
const [ttsModelId, setTtsModelId] = useState(DEFAULT_ELEVENLABS_MODEL);
|
||||
const [ttsModelId, setTtsModelId] = useState("");
|
||||
const [instanceId, setInstanceId] = useState("");
|
||||
const [initialCheckCustomTts, setInitialCheckCustomTts] = useState(false);
|
||||
const [initialCheckCustomStt, setInitialCheckCustomStt] = useState(false);
|
||||
@@ -117,6 +137,13 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
const [onPremNuanceSttUrl, setOnPremNuanceSttUrl] = useState("");
|
||||
const [cobaltServerUri, setCobaltServerUri] = useState("");
|
||||
const [label, setLabel] = useState("");
|
||||
const [useCustomVoicesCheck, setUseCustomVoicesCheck] = useState(false);
|
||||
const [customVoices, setCustomVoices] = useState<GoogleCustomVoice[]>([]);
|
||||
const [customVoicesMessage, setCustomVoicesMessage] = useState("");
|
||||
const ttsModels = useTtsModels();
|
||||
const [optionsInitialChecked, setOptionsInitialChecked] = useState(false);
|
||||
const [options, setOptions] = useState("");
|
||||
const [tmpOptions, setTmpOptions] = useState("");
|
||||
|
||||
const handleFile = (file: File) => {
|
||||
const handleError = () => {
|
||||
@@ -144,6 +171,62 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handlePutGoogleCustomVoices = () => {
|
||||
if (!credential || !credential.data) {
|
||||
return;
|
||||
}
|
||||
if (useCustomVoicesCheck) {
|
||||
Promise.all(
|
||||
customVoices.map((v) => {
|
||||
if (v.google_custom_voice_sid) {
|
||||
const sid = v.google_custom_voice_sid;
|
||||
delete v.google_custom_voice_sid;
|
||||
return putGoogleCustomVoice(sid, v);
|
||||
} else {
|
||||
return postGoogleCustomVoice({
|
||||
...v,
|
||||
speech_credential_sid: credential.data?.speech_credential_sid,
|
||||
});
|
||||
}
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
toastSuccess("Speech credential updated successfully");
|
||||
credential.refetch();
|
||||
navigate(
|
||||
`${ROUTE_INTERNAL_SPEECH}/${credential?.data?.speech_credential_sid}/edit`
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
toastError(error.msg);
|
||||
});
|
||||
} else if (useCustomVoicesCheck && customVoices.length > 0) {
|
||||
Promise.all(
|
||||
customVoices.map((v) => {
|
||||
if (v.google_custom_voice_sid) {
|
||||
return deleteGoogleCustomVoice(v.google_custom_voice_sid);
|
||||
}
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
toastSuccess("Speech credential updated successfully");
|
||||
credential.refetch();
|
||||
navigate(
|
||||
`${ROUTE_INTERNAL_SPEECH}/${credential?.data?.speech_credential_sid}/edit`
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
toastError(error.msg);
|
||||
});
|
||||
} else {
|
||||
toastSuccess("Speech credential updated successfully");
|
||||
credential.refetch();
|
||||
navigate(
|
||||
`${ROUTE_INTERNAL_SPEECH}/${credential.data.speech_credential_sid}/edit`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -203,9 +286,12 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
...(vendor === VENDOR_COBALT && {
|
||||
cobalt_server_uri: cobaltServerUri || null,
|
||||
}),
|
||||
...(vendor === VENDOR_ELEVENLABS && {
|
||||
...((vendor === VENDOR_ELEVENLABS || vendor === VENDOR_WHISPER) && {
|
||||
model_id: ttsModelId || null,
|
||||
}),
|
||||
...(vendor === VENDOR_ELEVENLABS && {
|
||||
options: options || null,
|
||||
}),
|
||||
};
|
||||
|
||||
if (credential && credential.data) {
|
||||
@@ -218,11 +304,15 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
)
|
||||
.then(() => {
|
||||
if (credential && credential.data) {
|
||||
toastSuccess("Speech credential updated successfully");
|
||||
credential.refetch();
|
||||
navigate(
|
||||
`${ROUTE_INTERNAL_SPEECH}/${credential.data.speech_credential_sid}/edit`
|
||||
);
|
||||
if (credential.data.vendor === VENDOR_GOOGLE) {
|
||||
handlePutGoogleCustomVoices();
|
||||
} else {
|
||||
toastSuccess("Speech credential updated successfully");
|
||||
credential.refetch();
|
||||
navigate(
|
||||
`${ROUTE_INTERNAL_SPEECH}/${credential.data.speech_credential_sid}/edit`
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -240,17 +330,34 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
vendor === VENDOR_MICROSOFT ||
|
||||
vendor === VENDOR_WELLSAID ||
|
||||
vendor === VENDOR_DEEPGRAM ||
|
||||
vendor === VENDOR_ASSEMBLYAI ||
|
||||
vendor === VENDOR_SONIOX ||
|
||||
vendor === VENDOR_ELEVENLABS
|
||||
vendor === VENDOR_ELEVENLABS ||
|
||||
vendor === VENDOR_WHISPER
|
||||
? apiKey
|
||||
: null,
|
||||
}),
|
||||
riva_server_uri: vendor == VENDOR_NVIDIA ? rivaServerUri : null,
|
||||
})
|
||||
.then(() => {
|
||||
toastSuccess("Speech credential created successfully");
|
||||
navigate(ROUTE_INTERNAL_SPEECH);
|
||||
setAccountFilter(accountSid);
|
||||
.then(({ json }) => {
|
||||
if (vendor === VENDOR_GOOGLE && useCustomVoicesCheck) {
|
||||
Promise.all(
|
||||
customVoices.map((v) =>
|
||||
postGoogleCustomVoice({
|
||||
...v,
|
||||
speech_credential_sid: json.sid,
|
||||
})
|
||||
)
|
||||
).then(() => {
|
||||
toastSuccess("Speech credential created successfully");
|
||||
navigate(ROUTE_INTERNAL_SPEECH);
|
||||
setAccountFilter(accountSid);
|
||||
});
|
||||
} else {
|
||||
toastSuccess("Speech credential created successfully");
|
||||
navigate(ROUTE_INTERNAL_SPEECH);
|
||||
setAccountFilter(accountSid);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toastError(error.msg);
|
||||
@@ -402,11 +509,46 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
setTtsModelId(credential.data.model_id);
|
||||
}
|
||||
}
|
||||
if (credential?.data?.options) {
|
||||
setOptions(credential.data.options);
|
||||
setOptionsInitialChecked(true);
|
||||
}
|
||||
if (credential?.data?.vendor === VENDOR_GOOGLE) {
|
||||
// let try to check if there is custom voices
|
||||
getGoogleCustomVoices({
|
||||
speech_credential_sid: credential.data.speech_credential_sid,
|
||||
}).then(({ json }) => {
|
||||
setCustomVoices(json);
|
||||
setUseCustomVoicesCheck(json.length > 0);
|
||||
});
|
||||
}
|
||||
}, [credential]);
|
||||
|
||||
const updateCustomVoices = (
|
||||
index: number,
|
||||
key: string,
|
||||
value: typeof customVoices[number][keyof GoogleCustomVoice]
|
||||
) => {
|
||||
setCustomVoices((prev) =>
|
||||
prev.map((g, i) =>
|
||||
i === index
|
||||
? {
|
||||
...g,
|
||||
[key]: value,
|
||||
}
|
||||
: g
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Section slim>
|
||||
<form className="form form--internal" onSubmit={handleSubmit}>
|
||||
<form
|
||||
className={`form form--internal ${
|
||||
!credential?.data && credential?.refetch ? "form--blur" : ""
|
||||
}`}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<fieldset>
|
||||
<MS>{MSG_REQUIRED_FIELDS}</MS>
|
||||
</fieldset>
|
||||
@@ -439,6 +581,15 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
setRegion("");
|
||||
setApiKey("");
|
||||
setGoogleServiceKey(null);
|
||||
if (
|
||||
ttsModels &&
|
||||
(e.target.value === VENDOR_ELEVENLABS ||
|
||||
e.target.value === VENDOR_WHISPER)
|
||||
) {
|
||||
setTtsModelId(
|
||||
ttsModels[e.target.value as keyof TtsModels][0].value
|
||||
);
|
||||
}
|
||||
}}
|
||||
disabled={credential ? true : false}
|
||||
required
|
||||
@@ -489,6 +640,7 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
{vendor && (
|
||||
<fieldset>
|
||||
{vendor !== VENDOR_DEEPGRAM &&
|
||||
vendor !== VENDOR_ASSEMBLYAI &&
|
||||
vendor !== VENDOR_COBALT &&
|
||||
vendor !== VENDOR_SONIOX &&
|
||||
vendor != VENDOR_CUSTOM && (
|
||||
@@ -505,6 +657,7 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
)}
|
||||
{vendor !== VENDOR_WELLSAID &&
|
||||
vendor !== VENDOR_CUSTOM &&
|
||||
vendor !== VENDOR_WHISPER &&
|
||||
vendor !== VENDOR_ELEVENLABS && (
|
||||
<label htmlFor="use_for_stt" className="chk">
|
||||
<input
|
||||
@@ -647,6 +800,166 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
</pre>
|
||||
</fieldset>
|
||||
)}
|
||||
{ttsCheck && vendor === VENDOR_GOOGLE && (
|
||||
<fieldset>
|
||||
<label htmlFor="use_custom_voice" className="chk">
|
||||
<input
|
||||
id="use_custom_voice"
|
||||
name="use_custom_voice"
|
||||
type="checkbox"
|
||||
onChange={(e) => {
|
||||
if (customVoices.length === 0) {
|
||||
setCustomVoices([
|
||||
{
|
||||
name: "",
|
||||
reported_usage:
|
||||
DEFAULT_GOOGLE_CUSTOM_VOICES_REPORTED_USAGE,
|
||||
model: "",
|
||||
},
|
||||
]);
|
||||
}
|
||||
setUseCustomVoicesCheck(e.target.checked);
|
||||
}}
|
||||
checked={useCustomVoicesCheck}
|
||||
/>
|
||||
<div>Use custom voices</div>
|
||||
</label>
|
||||
{useCustomVoicesCheck && (
|
||||
<fieldset>
|
||||
<label htmlFor="sip_gateways">Custom Voices</label>
|
||||
<MXS>
|
||||
<em>At least one Custom voice is required.</em>
|
||||
</MXS>
|
||||
{customVoicesMessage && (
|
||||
<Message message={customVoicesMessage} />
|
||||
)}
|
||||
{hasLength(customVoices) &&
|
||||
customVoices.map((v, i) => (
|
||||
<div key={`custom_voice_${i}`} className="customVoice">
|
||||
<div>
|
||||
<div>
|
||||
<label htmlFor="custom_voice_name">
|
||||
Name / Reported Usage
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<input
|
||||
id={`sip_ip_${i}`}
|
||||
name={`sip_ip_${i}`}
|
||||
type="text"
|
||||
placeholder="Assigned Name"
|
||||
required
|
||||
value={v.name}
|
||||
onChange={(e) => {
|
||||
updateCustomVoices(i, "name", e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Selector
|
||||
id={"google_custom_voices_reported_usage"}
|
||||
name={"google_custom_voices_reported_usage"}
|
||||
value={v.reported_usage}
|
||||
options={GOOGLE_CUSTOM_VOICES_REPORTED_USAGE}
|
||||
onChange={(e) => {
|
||||
updateCustomVoices(
|
||||
i,
|
||||
"reported_usage",
|
||||
e.target.value
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<label htmlFor="custom_voice_name">Model</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<input
|
||||
id={`sip_ip_${i}`}
|
||||
name={`sip_ip_${i}`}
|
||||
type="text"
|
||||
placeholder="Model"
|
||||
required
|
||||
value={v.model}
|
||||
style={{ maxWidth: "100%" }}
|
||||
onChange={(e) => {
|
||||
updateCustomVoices(
|
||||
i,
|
||||
"model",
|
||||
e.target.value
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
className="btnty"
|
||||
title="Delete custom voice"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setCustomVoicesMessage("");
|
||||
if (customVoices.length === 1) {
|
||||
setCustomVoicesMessage(
|
||||
"You must provide at least one custom voice."
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (v.google_custom_voice_sid) {
|
||||
deleteGoogleCustomVoice(
|
||||
v.google_custom_voice_sid
|
||||
).finally(() => {
|
||||
credential?.refetch();
|
||||
});
|
||||
}
|
||||
setCustomVoices((prev) =>
|
||||
prev.filter((_, idx) => idx !== i)
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Icon>
|
||||
<Icons.Trash2 />
|
||||
</Icon>
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<ButtonGroup left>
|
||||
<button
|
||||
className="btnty"
|
||||
type="button"
|
||||
title="Add Voice"
|
||||
onClick={() => {
|
||||
setCustomVoicesMessage("");
|
||||
setCustomVoices((prev) => [
|
||||
...prev,
|
||||
{
|
||||
name: "",
|
||||
reported_usage:
|
||||
DEFAULT_GOOGLE_CUSTOM_VOICES_REPORTED_USAGE,
|
||||
model: "",
|
||||
},
|
||||
]);
|
||||
}}
|
||||
>
|
||||
<Icon subStyle="teal">
|
||||
<Icons.Plus />
|
||||
</Icon>
|
||||
</button>
|
||||
</ButtonGroup>
|
||||
</fieldset>
|
||||
)}
|
||||
</fieldset>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{vendor === VENDOR_NUANCE && (
|
||||
@@ -788,7 +1101,9 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
)}
|
||||
{(vendor === VENDOR_WELLSAID ||
|
||||
vendor === VENDOR_DEEPGRAM ||
|
||||
vendor === VENDOR_ASSEMBLYAI ||
|
||||
vendor == VENDOR_ELEVENLABS ||
|
||||
vendor === VENDOR_WHISPER ||
|
||||
vendor === VENDOR_SONIOX) && (
|
||||
<fieldset>
|
||||
<label htmlFor={`${vendor}_apikey`}>
|
||||
@@ -805,18 +1120,99 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
/>
|
||||
</fieldset>
|
||||
)}
|
||||
{vendor == VENDOR_ELEVENLABS && (
|
||||
{(vendor == VENDOR_ELEVENLABS || vendor == VENDOR_WHISPER) &&
|
||||
ttsModels && (
|
||||
<fieldset>
|
||||
<label htmlFor={`${vendor}_tts_model_id`}>Model</label>
|
||||
<Selector
|
||||
id={"tts_model_id"}
|
||||
name={"tts_model_id"}
|
||||
value={ttsModelId}
|
||||
options={ttsModels[vendor as keyof TtsModels]}
|
||||
onChange={(e) => {
|
||||
setTtsModelId(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</fieldset>
|
||||
)}
|
||||
{vendor === VENDOR_ELEVENLABS && (
|
||||
<fieldset>
|
||||
<label htmlFor={`${vendor}_apikey`}>Model</label>
|
||||
<Selector
|
||||
id={"audio_format"}
|
||||
name={"audio_format"}
|
||||
value={ttsModelId}
|
||||
options={ELEVENLABS_MODEL_OPTIONS}
|
||||
onChange={(e) => {
|
||||
setTtsModelId(e.target.value);
|
||||
<Checkzone
|
||||
hidden
|
||||
name="cz_speech_credential_options"
|
||||
label="Extra Options"
|
||||
initialCheck={optionsInitialChecked}
|
||||
handleChecked={(e) => {
|
||||
if (e.target.checked) {
|
||||
setOptions(
|
||||
tmpOptions
|
||||
? tmpOptions
|
||||
: JSON.stringify(DEFAULT_ELEVENLABS_OPTIONS, null, 2)
|
||||
);
|
||||
}
|
||||
if (!e.target.checked) {
|
||||
setTmpOptions(options);
|
||||
setOptions("");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-end",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center", // Align items vertically in the center
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href="https://elevenlabs.io/docs/api-reference/streaming"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ marginRight: "10px", fontSize: "16px" }}
|
||||
>
|
||||
Docs
|
||||
</a>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
if (options) {
|
||||
setOptions((prev) => {
|
||||
try {
|
||||
return JSON.stringify(JSON.parse(options), null, 2);
|
||||
} catch (err) {
|
||||
return prev;
|
||||
}
|
||||
});
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
background: "none",
|
||||
border: "none",
|
||||
cursor: "pointer",
|
||||
color: "green",
|
||||
fontSize: "16px",
|
||||
}}
|
||||
>
|
||||
Beautify
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<textarea
|
||||
id="input_speech_credential_options"
|
||||
name="speech_credential_options"
|
||||
rows={6}
|
||||
cols={55}
|
||||
placeholder="extra options in Json"
|
||||
value={options}
|
||||
onChange={(e) => setOptions(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</Checkzone>
|
||||
</fieldset>
|
||||
)}
|
||||
{regions &&
|
||||
|
||||
@@ -112,7 +112,7 @@ export const SpeechServices = () => {
|
||||
</Icon>
|
||||
</Link>
|
||||
</section>
|
||||
<section className="filters filters--ender">
|
||||
<section className="filters filters--multi">
|
||||
<ScopedAccess user={user} scope={Scope.service_provider}>
|
||||
<AccountFilter
|
||||
account={[accountSid, setAccountSid]}
|
||||
|
||||
@@ -183,7 +183,12 @@ export const UserForm = ({ user }: UserFormProps) => {
|
||||
return (
|
||||
<>
|
||||
<Section slim>
|
||||
<form className="form form--internal" onSubmit={handleSubmit}>
|
||||
<form
|
||||
className={`form form--internal ${
|
||||
!user?.data && user?.refetch ? "form--blur" : ""
|
||||
}`}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<fieldset>
|
||||
<MS>{MSG_REQUIRED_FIELDS}</MS>
|
||||
</fieldset>
|
||||
|
||||
@@ -88,7 +88,7 @@ export const Users = () => {
|
||||
</Icon>
|
||||
</Link>
|
||||
</section>
|
||||
<section className="filters filters--mix">
|
||||
<section className="filters filters--multi">
|
||||
<section>
|
||||
<SearchFilter
|
||||
placeholder="Filter users"
|
||||
|
||||
@@ -105,6 +105,10 @@ fieldset {
|
||||
}
|
||||
}
|
||||
|
||||
&--blur {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
label {
|
||||
@include ui-mixins.m();
|
||||
@include ui-mixins.font-medium();
|
||||
@@ -294,3 +298,53 @@ fieldset {
|
||||
.bucket_tag {
|
||||
@extend .lcr;
|
||||
}
|
||||
|
||||
.customVoice {
|
||||
padding: ui-vars.$px02;
|
||||
border-radius: ui-vars.$px01;
|
||||
border: 2px solid ui-vars.$grey;
|
||||
max-width: ui-vars.$width-mobile;
|
||||
position: relative;
|
||||
|
||||
> div {
|
||||
display: grid;
|
||||
grid-gap: ui-vars.$px02;
|
||||
align-items: center;
|
||||
|
||||
&:nth-child(1) {
|
||||
grid-template-columns: [col] 100%;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
grid-template-columns: [col] calc(40% - #{ui-vars.$px02 * 2}) [col] 60%;
|
||||
margin-top: ui-vars.$px02;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
grid-template-columns: [col] 100%;
|
||||
margin-top: ui-vars.$px02;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
grid-template-columns: [col] 100%;
|
||||
margin-top: ui-vars.$px02;
|
||||
}
|
||||
}
|
||||
|
||||
> button {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 50%;
|
||||
transform: translate3d(50%, 50%, 0);
|
||||
|
||||
@include mixins.small() {
|
||||
top: auto;
|
||||
bottom: auto;
|
||||
transform: none;
|
||||
position: relative;
|
||||
margin-top: ui-vars.$px02;
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
41
src/vendor/index.tsx
vendored
41
src/vendor/index.tsx
vendored
@@ -5,6 +5,7 @@ import type {
|
||||
SynthesisVendors,
|
||||
RecognizerVendors,
|
||||
RegionVendors,
|
||||
TtsModels,
|
||||
} from "./types";
|
||||
|
||||
export const LANG_EN_US = "en-US";
|
||||
@@ -23,6 +24,8 @@ export const VENDOR_SONIOX = "soniox";
|
||||
export const VENDOR_CUSTOM = "custom";
|
||||
export const VENDOR_COBALT = "cobalt";
|
||||
export const VENDOR_ELEVENLABS = "elevenlabs";
|
||||
export const VENDOR_ASSEMBLYAI = "assemblyai";
|
||||
export const VENDOR_WHISPER = "whisper";
|
||||
|
||||
export const vendors: VendorOptions[] = [
|
||||
{
|
||||
@@ -73,8 +76,40 @@ export const vendors: VendorOptions[] = [
|
||||
name: "ElevenLabs",
|
||||
value: VENDOR_ELEVENLABS,
|
||||
},
|
||||
{
|
||||
name: "AssemblyAI",
|
||||
value: VENDOR_ASSEMBLYAI,
|
||||
},
|
||||
{
|
||||
name: "Whisper",
|
||||
value: VENDOR_WHISPER,
|
||||
},
|
||||
].sort((a, b) => a.name.localeCompare(b.name)) as VendorOptions[];
|
||||
|
||||
export const useTtsModels = () => {
|
||||
const [models, setModels] = useState<TtsModels>();
|
||||
|
||||
useEffect(() => {
|
||||
let ignore = false;
|
||||
Promise.all([
|
||||
import("./speech-synthsis-models/elevenlabs-models"),
|
||||
import("./speech-synthsis-models/whisper-models"),
|
||||
]).then(([{ default: elevenlabs }, { default: whisper }]) => {
|
||||
if (!ignore) {
|
||||
setModels({
|
||||
elevenlabs,
|
||||
whisper,
|
||||
});
|
||||
}
|
||||
});
|
||||
return function cleanup() {
|
||||
ignore = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return models;
|
||||
};
|
||||
|
||||
export const useRegionVendors = () => {
|
||||
const [regions, setRegions] = useState<RegionVendors>();
|
||||
|
||||
@@ -128,6 +163,7 @@ export const useSpeechVendors = () => {
|
||||
import("./speech-recognizer/nvidia-speech-recognizer-lang"),
|
||||
import("./speech-recognizer/soniox-speech-recognizer-lang"),
|
||||
import("./speech-recognizer/cobalt-speech-recognizer-lang"),
|
||||
import("./speech-recognizer/assemblyai-speech-recognizer-lang"),
|
||||
import("./speech-synthesis/aws-speech-synthesis-lang"),
|
||||
import("./speech-synthesis/google-speech-synthesis-lang"),
|
||||
import("./speech-synthesis/ms-speech-synthesis-lang"),
|
||||
@@ -136,6 +172,7 @@ export const useSpeechVendors = () => {
|
||||
import("./speech-synthesis/ibm-speech-synthesis-lang"),
|
||||
import("./speech-synthesis/nvidia-speech-synthesis-lang"),
|
||||
import("./speech-synthesis/elevellabs-speech-synthesis-lang"),
|
||||
import("./speech-synthesis/whisper-speech-synthesis-lang"),
|
||||
]).then(
|
||||
([
|
||||
{ default: awsRecognizer },
|
||||
@@ -147,6 +184,7 @@ export const useSpeechVendors = () => {
|
||||
{ default: nvidiaRecognizer },
|
||||
{ default: sonioxRecognizer },
|
||||
{ default: cobaltRecognizer },
|
||||
{ default: assemblyaiRecognizer },
|
||||
{ default: awsSynthesis },
|
||||
{ default: googleSynthesis },
|
||||
{ default: msSynthesis },
|
||||
@@ -155,6 +193,7 @@ export const useSpeechVendors = () => {
|
||||
{ default: ibmSynthesis },
|
||||
{ default: nvidiaynthesis },
|
||||
{ default: elevenLabsSynthesis },
|
||||
{ default: whisperSynthesis },
|
||||
]) => {
|
||||
if (!ignore) {
|
||||
setSpeech({
|
||||
@@ -167,6 +206,7 @@ export const useSpeechVendors = () => {
|
||||
ibm: ibmSynthesis,
|
||||
nvidia: nvidiaynthesis,
|
||||
elevenlabs: elevenLabsSynthesis,
|
||||
whisper: whisperSynthesis,
|
||||
},
|
||||
recognizers: {
|
||||
aws: awsRecognizer,
|
||||
@@ -178,6 +218,7 @@ export const useSpeechVendors = () => {
|
||||
nvidia: nvidiaRecognizer,
|
||||
soniox: sonioxRecognizer,
|
||||
cobalt: cobaltRecognizer,
|
||||
assemblyai: assemblyaiRecognizer,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
26
src/vendor/speech-recognizer/assemblyai-speech-recognizer-lang.ts
vendored
Normal file
26
src/vendor/speech-recognizer/assemblyai-speech-recognizer-lang.ts
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { Language } from "../types";
|
||||
|
||||
export const languages: Language[] = [
|
||||
{ name: "Global English", code: "en" },
|
||||
{ name: "Australian English", code: "en_au" },
|
||||
{ name: "British English", code: "en_uk" },
|
||||
{ name: "US English", code: "en_us" },
|
||||
{ name: "Spanish", code: "es" },
|
||||
{ name: "French", code: "fr" },
|
||||
{ name: "German", code: "de" },
|
||||
{ name: "Italian", code: "it" },
|
||||
{ name: "Portuguese", code: "pt" },
|
||||
{ name: "Dutch", code: "nl" },
|
||||
{ name: "Hindi", code: "hi" },
|
||||
{ name: "Japanese", code: "ja" },
|
||||
{ name: "Chinese", code: "zh" },
|
||||
{ name: "Finnish", code: "fi" },
|
||||
{ name: "Korean", code: "ko" },
|
||||
{ name: "Polish", code: "pl" },
|
||||
{ name: "Russian", code: "ru" },
|
||||
{ name: "Turkish", code: "tr" },
|
||||
{ name: "Ukrainian", code: "uk" },
|
||||
{ name: "Vietnamese", code: "vi" },
|
||||
];
|
||||
|
||||
export default languages;
|
||||
5853
src/vendor/speech-synthesis/ms-speech-synthesis-raw.ts
vendored
5853
src/vendor/speech-synthesis/ms-speech-synthesis-raw.ts
vendored
File diff suppressed because it is too large
Load Diff
18
src/vendor/speech-synthesis/whisper-speech-synthesis-lang.ts
vendored
Normal file
18
src/vendor/speech-synthesis/whisper-speech-synthesis-lang.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { VoiceLanguage } from "../types";
|
||||
|
||||
export const languages: VoiceLanguage[] = [
|
||||
{
|
||||
code: "en-US",
|
||||
name: "English",
|
||||
voices: [
|
||||
{ value: "alloy", name: "Alloy" },
|
||||
{ value: "echo", name: "Echo" },
|
||||
{ value: "fable", name: "Fable" },
|
||||
{ value: "onyx", name: "Onyx" },
|
||||
{ value: "nova", name: "Nova" },
|
||||
{ value: "shimmer", name: "Shimmer" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default languages;
|
||||
11
src/vendor/speech-synthsis-models/elevenlabs-models.ts
vendored
Normal file
11
src/vendor/speech-synthsis-models/elevenlabs-models.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { Model } from "../types";
|
||||
|
||||
export const models: Model[] = [
|
||||
{ name: "Turbo v2", value: "eleven_turbo_v2" },
|
||||
{ name: "Multilingual v2", value: "eleven_multilingual_v2" },
|
||||
{ name: "Multilingual v1", value: "eleven_multilingual_v1" },
|
||||
{ name: "English v1", value: "eleven_monolingual_v1" },
|
||||
{ name: "English v2", value: "eleven_english_sts_v2" },
|
||||
];
|
||||
|
||||
export default models;
|
||||
8
src/vendor/speech-synthsis-models/whisper-models.ts
vendored
Normal file
8
src/vendor/speech-synthsis-models/whisper-models.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import type { Model } from "../types";
|
||||
|
||||
export const models: Model[] = [
|
||||
{ name: "TTS-1", value: "tts-1" },
|
||||
{ name: "TTS-1-HD", value: "tts-1-hd" },
|
||||
];
|
||||
|
||||
export default models;
|
||||
17
src/vendor/types.ts
vendored
17
src/vendor/types.ts
vendored
@@ -10,7 +10,9 @@ export type Vendor =
|
||||
| "Soniox"
|
||||
| "Cobalt"
|
||||
| "Custom"
|
||||
| "ElevenLabs";
|
||||
| "ElevenLabs"
|
||||
| "assemblyai"
|
||||
| "whisper";
|
||||
|
||||
export interface VendorOptions {
|
||||
name: Vendor;
|
||||
@@ -27,6 +29,11 @@ export interface Region {
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface Model {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface Voice {
|
||||
name: string;
|
||||
value: string;
|
||||
@@ -63,6 +70,11 @@ export interface RegionVendors {
|
||||
ibm: Region[];
|
||||
}
|
||||
|
||||
export interface TtsModels {
|
||||
elevenlabs: Model[];
|
||||
whisper: Model[];
|
||||
}
|
||||
|
||||
export interface RecognizerVendors {
|
||||
aws: Language[];
|
||||
google: Language[];
|
||||
@@ -73,6 +85,7 @@ export interface RecognizerVendors {
|
||||
nvidia: Language[];
|
||||
soniox: Language[];
|
||||
cobalt: Language[];
|
||||
assemblyai: Language[];
|
||||
}
|
||||
|
||||
export interface SynthesisVendors {
|
||||
@@ -84,6 +97,7 @@ export interface SynthesisVendors {
|
||||
ibm: VoiceLanguage[];
|
||||
nvidia: VoiceLanguage[];
|
||||
elevenlabs: VoiceLanguage[];
|
||||
whisper: VoiceLanguage[];
|
||||
}
|
||||
|
||||
export interface MSRawSpeech {
|
||||
@@ -100,5 +114,6 @@ export interface MSRawSpeech {
|
||||
StyleList?: string[];
|
||||
RolePlayList?: string[];
|
||||
WordPerMinute?: Record<string, unknown>;
|
||||
WordsPerMinute?: string;
|
||||
SecondaryLocaleList?: string[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user