diff --git a/package.json b/package.json index fb557562a7..1aade9cb28 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "redux-thunk": "^2.1.0", "ringcentral": "^3.1.2", "ringcentral-client": "^1.0.0-rc1", - "ringcentral-integration": "^0.7.1", + "ringcentral-integration": "^0.7.4", "sass-loader": "^6.0.5", "source-map-loader": "^0.2.1", "style-loader": "^0.18.2", diff --git a/src/components/ActiveCallDialPad/styles.scss b/src/components/ActiveCallDialPad/styles.scss index ee0a0d9420..22d5cdc576 100644 --- a/src/components/ActiveCallDialPad/styles.scss +++ b/src/components/ActiveCallDialPad/styles.scss @@ -49,7 +49,9 @@ $input-height: 30px; } .stopButton { - background: #ff4646; + circle { + fill: #ff4646; + } g, path { fill: #ffffff; } diff --git a/src/components/ActiveCallPad/index.js b/src/components/ActiveCallPad/index.js index d09d31b806..17bb55dce3 100644 --- a/src/components/ActiveCallPad/index.js +++ b/src/components/ActiveCallPad/index.js @@ -95,7 +95,7 @@ export default function ActiveCallPad(props) {
null} + onClick={props.onToggleTransferPanel} title={i18n.getString('transfer', props.currentLocale)} icon={TransferIcon} className={styles.callButton} @@ -139,6 +139,7 @@ ActiveCallPad.propTypes = { onShowKeyPad: PropTypes.func.isRequired, onAdd: PropTypes.func.isRequired, onShowFlipPanel: PropTypes.func.isRequired, + onToggleTransferPanel: PropTypes.func.isRequired, flipNumbers: PropTypes.array.isRequired, }; diff --git a/src/components/ActiveCallPanel/index.js b/src/components/ActiveCallPanel/index.js index 0ad09695b4..bce1c0c48b 100644 --- a/src/components/ActiveCallPanel/index.js +++ b/src/components/ActiveCallPanel/index.js @@ -111,6 +111,7 @@ function ActiveCallPanel({ hangup, onAdd, onShowFlipPanel, + onToggleTransferPanel, children, showContactDisplayPlaceholder, brand, @@ -160,6 +161,7 @@ function ActiveCallPanel({ hangup={hangup} onAdd={onAdd} onShowFlipPanel={onShowFlipPanel} + onToggleTransferPanel={onToggleTransferPanel} flipNumbers={flipNumbers} /> {children} @@ -199,6 +201,7 @@ ActiveCallPanel.propTypes = { showContactDisplayPlaceholder: PropTypes.bool, onShowFlipPanel: PropTypes.func, flipNumbers: PropTypes.array, + onToggleTransferPanel: PropTypes.func, }; ActiveCallPanel.defaultProps = { @@ -213,6 +216,7 @@ ActiveCallPanel.defaultProps = { showContactDisplayPlaceholder: true, flipNumbers: [], onShowFlipPanel: () => null, + onToggleTransferPanel: () => null, }; export default ActiveCallPanel; diff --git a/src/components/CallCtrlPanel/index.js b/src/components/CallCtrlPanel/index.js index 476a5d534e..7cdb62a27c 100644 --- a/src/components/CallCtrlPanel/index.js +++ b/src/components/CallCtrlPanel/index.js @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import ActiveCallDialPad from '../ActiveCallDialPad'; import ActiveCallPanel from '../ActiveCallPanel'; import FlipPanel from '../FlipPanel'; +import TransferPanel from '../TransferPanel'; class CallCtrlPanel extends Component { constructor(props) { @@ -36,6 +37,12 @@ class CallCtrlPanel extends Component { isShowFlipPanel: false }); }; + + this.toggleTransferPanel = () => { + this.setState(prevState => ({ + isShowTransferPanel: !prevState.isShowTransferPanel + })); + }; } render() { @@ -62,6 +69,16 @@ class CallCtrlPanel extends Component { /> ); } + if (this.state.isShowTransferPanel) { + return ( + + ); + } return ( {this.props.children} @@ -113,6 +131,7 @@ CallCtrlPanel.propTypes = { isOnMute: PropTypes.bool, isOnHold: PropTypes.bool, isOnFlip: PropTypes.bool, + isOnTransfer: PropTypes.bool, flipNumbers: PropTypes.array, recordStatus: PropTypes.string.isRequired, onMute: PropTypes.func.isRequired, @@ -124,6 +143,7 @@ CallCtrlPanel.propTypes = { onAdd: PropTypes.func.isRequired, hangup: PropTypes.func.isRequired, flip: PropTypes.func.isRequired, + transfer: PropTypes.func.isRequired, onBackButtonClick: PropTypes.func.isRequired, onKeyPadChange: PropTypes.func.isRequired, formatPhone: PropTypes.func.isRequired, @@ -142,7 +162,7 @@ CallCtrlPanel.defaultProps = { startTime: null, isOnMute: false, isOnHold: false, - isOnRecord: false, + isOnTransfer: false, isOnFlip: false, flipNumbers: [], phoneNumber: null, diff --git a/src/components/TransferPanel/i18n/en-US.js b/src/components/TransferPanel/i18n/en-US.js new file mode 100644 index 0000000000..659677362c --- /dev/null +++ b/src/components/TransferPanel/i18n/en-US.js @@ -0,0 +1,5 @@ +export default { + to: 'To:', + transferTo: 'Transfer to', + blindTransfer: 'Transfer' +}; diff --git a/src/components/TransferPanel/i18n/index.js b/src/components/TransferPanel/i18n/index.js new file mode 100644 index 0000000000..25458f23b1 --- /dev/null +++ b/src/components/TransferPanel/i18n/index.js @@ -0,0 +1,4 @@ +import I18n from 'ringcentral-integration/lib/I18n'; +import loadLocale from './loadLocale'; + +export default new I18n(loadLocale); diff --git a/src/components/TransferPanel/i18n/loadLocale.js b/src/components/TransferPanel/i18n/loadLocale.js new file mode 100644 index 0000000000..12b11cfa2e --- /dev/null +++ b/src/components/TransferPanel/i18n/loadLocale.js @@ -0,0 +1 @@ +/* loadLocale */ diff --git a/src/components/TransferPanel/index.js b/src/components/TransferPanel/index.js new file mode 100644 index 0000000000..da996b9372 --- /dev/null +++ b/src/components/TransferPanel/index.js @@ -0,0 +1,98 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; +import DialPad from '../DialPad'; +import BackHeader from '../BackHeader'; +import ActiveCallButton from '../ActiveCallButton'; +import dynamicsFont from '../../assets/DynamicsFont/DynamicsFont.scss'; +import TransferIcon from '../../assets/images/Transfer.svg'; +import styles from './styles.scss'; +import i18n from './i18n'; + +export default class TransferPanel extends PureComponent { + static propTypes = { + transfer: PropTypes.func.isRequired, + currentLocale: PropTypes.string.isRequired, + toggleTransferPanel: PropTypes.func.isRequired, + isOnTransfer: PropTypes.bool.isRequired + }; + + constructor(props) { + super(props); + this.state = { + value: '', + }; + } + + onButtonOutput = (key) => { + this.setState((preState) => { + const value = preState.value + key; + return { value }; + }); + } + + handleChange = (event) => { + this.setState({ value: event.target.value }); + } + + clearText = () => { + this.setState({ + value: '' + }); + } + + transfer = () => { + this.props.transfer(this.state.value); + } + + render() { + const showClearButton = this.state.value === '' ? { display: 'none' } : { display: 'block' }; + const isButtonDisabled = this.state.value === '' || this.props.isOnTransfer; + return ( +
+ + + + )} + buttons={[]} + > + {i18n.getString('transferTo', this.props.currentLocale)} + +
+ + + +
+
+ +
+ +
+
+
+ ); + } +} diff --git a/src/components/TransferPanel/styles.scss b/src/components/TransferPanel/styles.scss new file mode 100644 index 0000000000..3dc95691c2 --- /dev/null +++ b/src/components/TransferPanel/styles.scss @@ -0,0 +1,90 @@ +@import '../../lib/commonStyles/colors'; +@import '../../lib/commonStyles/full-size'; +@import '../../lib/commonStyles/fonts'; + +$input-height: 30px; + +.root { + @include full-size; + box-sizing: border-box; +} + +.dialInput { + height: $input-height; + box-sizing: content-box; + width: calc(100% - 30px); + border: 0px; + margin-bottom: 5px; + padding: 5px 0; + margin: 5px 20px; + min-width: 100px; + label { + color: $darkergray; + } + input { + @include secondary-font; + box-sizing: content-box; + display: inline-block; + text-align: left; + padding: 0; + outline: 0; + height: 100%; + width: calc(100% - 55px); + margin-left: 5%; + background-color: transparent; + border: none; + font-size:15px; + color: $lightblack; + } + .input::-ms-clear { + display: none; + } +} + +.padContainer { + height: calc(100% - #{$input-height}); + padding: 0 10px; +} + +.dialPad { + height: 65%; +} + +.button { + width: 30%; + text-align: center; + padding: 2px; +} + +.buttonRow { + text-align: center; + height: 20%; +} + +.backButton { + color: $lightblack; + + .backIcon { + display: inline-block; + position: absolute; + transform: rotate(90deg); + top: 5px; + left: 10px; + } + .backLabel { + display: inline-block; + width: 79px; + font-size: 14px; + } +} +.clear { + position: absolute; + right: 25px; + margin-top: -24px; + cursor: pointer; + font-size: 18px; + color: $grey-light; + &:hover { + color: $rc-blue; + } +} diff --git a/src/components/WebphoneAlert/i18n/en-US.js b/src/components/WebphoneAlert/i18n/en-US.js index d33352a6ac..0e46f34e57 100644 --- a/src/components/WebphoneAlert/i18n/en-US.js +++ b/src/components/WebphoneAlert/i18n/en-US.js @@ -12,5 +12,6 @@ export default { [webphoneErrors.holdError]: 'Call cannot be hold at the moment.', [webphoneErrors.flipError]: 'Cannot flip the call. Please try again later.', [webphoneErrors.recordError]: 'You cannot record the call at the moment. Error code: {errorCode}', - [webphoneErrors.recordDisabled]: 'Sorry, your account does not have the feature to record a call. Please contact your account administrator.' + [webphoneErrors.recordDisabled]: 'Sorry, your account does not have the feature to record a call. Please contact your account administrator.', + [webphoneErrors.transferError]: 'Cannot transfer the call. Please try again later.' }; diff --git a/src/components/WebphoneAlert/index.js b/src/components/WebphoneAlert/index.js index f26a34144c..4cdacaaac4 100644 --- a/src/components/WebphoneAlert/index.js +++ b/src/components/WebphoneAlert/index.js @@ -37,5 +37,6 @@ WebphoneAlert.handleMessage = ({ message }) => ( (message === webphoneErrors.holdError) || (message === webphoneErrors.flipError) || (message === webphoneErrors.recordError) || - (message === webphoneErrors.recordDisabled) + (message === webphoneErrors.recordDisabled) || + (message === webphoneErrors.transferError) ); diff --git a/src/containers/CallCtrlPage/index.js b/src/containers/CallCtrlPage/index.js index 48a855479e..c7dc7a7359 100644 --- a/src/containers/CallCtrlPage/index.js +++ b/src/containers/CallCtrlPage/index.js @@ -60,6 +60,8 @@ class CallCtrlPage extends Component { this.props.sendDTMF(value, this.props.session.id); this.flip = value => this.props.flip(value, this.props.session.id); + this.transfer = value => + this.props.transfer(value, this.props.session.id); } componentDidMount() { @@ -119,6 +121,7 @@ class CallCtrlPage extends Component { isOnMute={session.isOnMute} isOnHold={session.isOnHold} isOnFlip={session.isOnFlip} + isOnTransfer={session.isOnTransfer} recordStatus={session.recordStatus} onBackButtonClick={this.props.onBackButtonClick} onMute={this.onMute} @@ -131,6 +134,7 @@ class CallCtrlPage extends Component { hangup={this.hangup} onAdd={this.props.onAdd} flip={this.flip} + transfer={this.transfer} nameMatches={this.props.nameMatches} fallBackName={fallbackUserName} areaCode={this.props.areaCode} @@ -155,8 +159,9 @@ CallCtrlPage.propTypes = { startTime: PropTypes.number, isOnMute: PropTypes.bool, isOnHold: PropTypes.bool, - isOnRecord: PropTypes.bool, isOnFlip: PropTypes.bool, + isOnTransfer: PropTypes.bool, + recordStatus: PropTypes.string.isRequired, to: PropTypes.string, from: PropTypes.string, contactMatch: PropTypes.object, @@ -173,6 +178,7 @@ CallCtrlPage.propTypes = { formatPhone: PropTypes.func.isRequired, onAdd: PropTypes.func.isRequired, flip: PropTypes.func.isRequired, + transfer: PropTypes.func.isRequired, children: PropTypes.node, nameMatches: PropTypes.array.isRequired, areaCode: PropTypes.string.isRequired, @@ -241,6 +247,7 @@ function mapToFunctions(_, { onBackButtonClick, onAdd, flip: (flipNumber, sessionId) => webphone.flip(flipNumber, sessionId), + transfer: (transferNumber, sessionId) => webphone.transfer(transferNumber, sessionId) }; } diff --git a/yarn.lock b/yarn.lock index 2d03ea5494..6da9e6042d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2763,12 +2763,6 @@ file-entry-cache@^2.0.0: flat-cache "^1.2.1" object-assign "^4.0.1" -file-loader@^0.10.1: - version "0.10.1" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.10.1.tgz#815034119891fc6441fb5a64c11bc93c22ddd842" - dependencies: - loader-utils "^1.0.2" - file-loader@^0.11.2: version "0.11.2" resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.11.2.tgz#4ff1df28af38719a6098093b88c82c71d1794a34" @@ -6206,7 +6200,7 @@ requires-port@1.0.x, requires-port@1.x.x: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" -reselect@^2.5.4: +reselect@2.5.4: version "2.5.4" resolved "https://registry.yarnpkg.com/reselect/-/reselect-2.5.4.tgz#b7d23fdf00b83fa7ad0279546f8dbbbd765c7047" @@ -6269,23 +6263,23 @@ ringcentral-client@^1.0.0-rc1: form-data "^2.1.2" isomorphic-fetch "^2.2.1" -ringcentral-integration@^0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/ringcentral-integration/-/ringcentral-integration-0.7.1.tgz#eead2056a8ddc6ad23df6b18c117d16e163cb566" +ringcentral-integration@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/ringcentral-integration/-/ringcentral-integration-0.7.4.tgz#d089cc99c7265bf76ca3f0543b01682aea3be52b" dependencies: - file-loader "^0.10.1" + file-loader "^0.11.2" json-mask "^0.3.8" loganberry "^0.9.1" phoneformat.js "^1.0.3" ramda "^0.24.1" redux "^3.5.2" - reselect "^2.5.4" - ringcentral-web-phone "^0.4.1" + reselect "2.5.4" + ringcentral-web-phone "^0.4.2" url-loader "^0.5.8" uuid "^3.0.1" yards "^0.1.4" -ringcentral-web-phone@^0.4.1: +ringcentral-web-phone@^0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/ringcentral-web-phone/-/ringcentral-web-phone-0.4.2.tgz#6a854f4691472715fbbcb4f94b8ee1dc3a835c33" dependencies: