TT#28058 Add New Call Forward Destination

What has been done:
- TT#29602, CallForwarding: Create UI input form
- TT#29603, CallForwarding: Implement API method for add new destination
- TT#29604, CallForwarding: Implement store and bindings for add new
  destination feature

Change-Id: I723488e684990c21ee74700cf6e849a5d16cda9c
changes/26/18126/20
raxelsen 7 years ago committed by Hans-Peter Herzog
parent d600aa4a8e
commit 9567594bd1

@ -1,6 +1,7 @@
import _ from 'lodash';
import Vue from 'vue';
import { getJsonBody } from './utils'
import { getJsonBody } from './utils';
let rowCountAssumption = 1000;
@ -87,7 +88,7 @@ export function loadAlwaysEverybodyDestinations(subscriberId) {
return new Promise((resolve, reject)=>{
Promise.resolve().then(()=>{
return getMappings(subscriberId);
}).then((mappings)=>{
}).then((mappings) => {
let cfuPromises = [];
let cfnaPromises = [];
let cfbPromises = [];
@ -118,6 +119,9 @@ export function loadAlwaysEverybodyDestinations(subscriberId) {
Promise.all(cfbPromises)
]);
}).then((res)=>{
computeLowestPriorityAndAddGroupName(res[0], 'cfu');
computeLowestPriorityAndAddGroupName(res[1], 'cfna');
computeLowestPriorityAndAddGroupName(res[2], 'cfb');
resolve({
online: res[0],
offline: res[1],
@ -129,6 +133,15 @@ export function loadAlwaysEverybodyDestinations(subscriberId) {
});
}
export function computeLowestPriorityAndAddGroupName(group, groupName) {
group.forEach(destinationset => {
let lowest = _.maxBy(destinationset.destinations, 'priority') || { priority: 1 };
destinationset.lowestPriority = lowest.priority;
destinationset.groupName = groupName;
});
return group;
}
export function getDestinationsetById(id) {
return new Promise((resolve, reject)=>{
Vue.http.get('/api/cfdestinationsets/' + id).then((res)=>{
@ -175,3 +188,102 @@ export function deleteDestinationsetById(id) {
});
});
}
export function addDestinationToDestinationset(options) {
let headers = {
'Content-Type': 'application/json-patch+json'
};
return new Promise((resolve, reject) => {
Vue.http.patch('/api/cfdestinationsets/' + options.id, [{
op: 'replace',
path: '/destinations',
value: options.data
}], { headers: headers }).then(result => {
resolve(result);
}).catch(err => {
reject(err);
});
});
}
export function addNewDestinationset() {
let destinationsetName = `csc-${Date.now()}`;
return new Promise((resolve, reject) => {
Vue.http.post('/api/cfdestinationsets/', { name: destinationsetName })
.then(response => {
resolve(_.last(_.split(response.headers.get('Location'), '/')));
}).catch(err => {
reject(err);
});
});
}
export function addDestinationToExistingGroup(options) {
return new Promise((resolve, reject)=> {
Promise.resolve().then(() => {
return getDestinationsetById(options.id);
}).then((destinationset) => {
let data = destinationset.destinations;
data.push(options.data);
return addDestinationToDestinationset({
id: options.id, data: data
});
}).then(() => {
resolve();
}).catch((err) => {
reject(err);
});
});
}
export function addDestinationToEmptyGroup(options) {
return new Promise((resolve, reject)=> {
let destinationsetId;
Promise.resolve().then(() => {
return addNewDestinationset();
}).then((id) => {
destinationsetId = id;
return addDestinationToDestinationset({
id: id, data: [options.data]
});
//}).then(() => {
// return getMappings(subscriberId);
//}).then((mappings) => {
// return addNewMapping({
// destinationsetId: destinationsetId,
// group: options.groupName,
// subscriberId: options.subscriberId,
// mappings: mappings
// });
}).then(() => {
return addNewMapping({
destinationsetId: destinationsetId,
group: options.groupName,
subscriberId: options.subscriberId
});
}).then(() => {
resolve();
}).catch((err) => {
reject(err);
});
});
}
export function addNewMapping(options) {
let headers = {
'Content-Type': 'application/json-patch+json'
};
return new Promise((resolve, reject) => {
let mappingsToSend = [{ destinationset_id: options.destinationsetId,
sourceset_id: null, timeset_id: null }];
Vue.http.patch('/api/cfmappings/' + options.subscriberId, [{
op: 'replace',
path: '/' + options.group,
value: mappingsToSend
}], { headers: headers }).then(result => {
resolve(result);
}).catch(err => {
reject(err);
});
});
}

@ -283,4 +283,9 @@
z-index: 1001;
}
.q-if-control.q-if-control-before.q-icon,
.q-if-control.q-if-control-before.q-icon:before {
font-size:24px;
}
</style>

@ -4,7 +4,7 @@
</template>
<script>
import CscPage from '../../CscPage'
import CscPage from '../../CscPage'
export default {
data () {
return {}

@ -3,14 +3,17 @@
<q-card class="dest-card">
<csc-destinations :title="$t('pages.callForward.whenOnline')"
:group="destinations.online"
group-name="cfu"
icon="signal_wifi_4_bar">
</csc-destinations>
<csc-destinations :title="$t('pages.callForward.whenBusy')"
:group="destinations.busy"
group-name="cfb"
icon="record_voice_over">
</csc-destinations>
<csc-destinations :title="$t('pages.callForward.whenOffline')"
:group="destinations.offline"
group-name="cfna"
icon="signal_wifi_off">
</csc-destinations>
</q-card>
@ -34,8 +37,6 @@
CscPage,
CscDestinations
},
methods: {
},
computed: {
destinations() {
return this.$store.state.callForward.alwaysEverybodyDestinations;

@ -4,7 +4,7 @@
</template>
<script>
import CscPage from '../../CscPage'
import CscPage from '../../CscPage'
export default {
data () {
return {}

@ -0,0 +1,170 @@
<template>
<div class="add-destination-form">
<q-btn v-if="!isFormEnabled" flat color="primary" icon="fa-plus">
{{ $t('pages.callForward.addDestinationButton') }}
<q-popover ref="popover">
<q-list separator link>
<q-item @click="enableForm('number'),
$refs.popover.close()">
{{ $t('pages.callForward.buttons.addNumber') }}
</q-item>
<q-item @click="enableForm('voicebox'),
$refs.popover.close()">
{{ $t('pages.callForward.buttons.addVoicemail') }}
</q-item>
<q-item @click="enableForm('fax2mail'),
$refs.popover.close()"
v-if="hasFaxCapability">
{{ $t('pages.callForward.buttons.addFax2Mail') }}
</q-item>
</q-list>
</q-popover>
</q-btn>
<div v-if="isFormEnabled">
<q-field :error="addFormError" :error-label="$t('pages.callForward.addInputError')">
<q-input :before="beforeIconDestination" :float-label="$t('pages.callForward.destination')" type="text"
v-model="destinationForm.destination" @keyup.enter="addDestination()"
:clearable="isFormTypeNumber" :autofocus="isFormTypeNumber"
:disable="!isFormTypeNumber || addDestinationIsRequesting" />
</q-field>
<q-field :error="addFormError"
:error-label="$t('pages.callForward.addInputError')">
<q-input :before="beforeIconTimeout" :float-label="$t('pages.callForward.timeout')"
type="number" v-if="isFormTypeNumber" v-model="destinationForm.timeout"
:min="0" :max="600" suffix="seconds" />
</q-field>
<q-btn flat dark @click="disableForm()">{{ $t('buttons.cancel') }}</q-btn>
<q-btn flat color="primary" icon-right="fa-save" @click="addDestination()">{{ $t('buttons.save') }}</q-btn>
</div>
</div>
</template>
<script>
import { startLoading, stopLoading,
showGlobalError, showToast } from '../../../helpers/ui'
import { normalizeTerminationInput } from '../../../filters/number-format'
import { mapGetters, mapState } from 'vuex'
import { QItem, Toast, QBtn, QSelect, QPopover, QList,
QField, QInput, QSlider } from 'quasar-framework'
export default {
name: 'csc-add-destination-form',
props: [
'destinations',
'id',
'groupName',
'priority'
],
data () {
return {
addFormError: false,
formEnabled: false,
destinationForm: {
destination: '',
timeout: 300
}
}
},
components: {
QSelect,
QPopover,
QField,
QInput,
QSlider,
QList,
QItem,
Toast,
QBtn
},
computed: {
...mapState('callForward', [
'activeForm',
'formType',
'addDestinationState',
'addDestinationError',
'lastAddedDestination'
]),
...mapGetters('callForward', [
'hasFaxCapability'
]),
isFormTypeNumber() {
return this.formType === 'number';
},
isFormEnabled() {
return this.activeForm === this.groupName && this.formEnabled;
},
addDestinationIsRequesting() {
return this.addDestinationState === 'requesting';
},
addDestinationError() {
return this.$store.state.callForward.addDestinationError || this.$t('pages.callForward.addErrorMessage');
},
beforeIconTimeout() {
return [{
icon: 'schedule'
}];
},
beforeIconDestination() {
return [{
icon: 'fa-angle-double-right'
}];
}
},
watch: {
addDestinationState(state) {
if (state === 'failed') {
stopLoading();
showGlobalError(this.addDestinationError);
} else if (state === 'succeeded') {
stopLoading();
showToast(this.$t('pages.callForward.addDestinationSuccessMessage', {
destination: this.lastAddedDestination
}));
this.disableForm();
} else if (state === 'button') {
stopLoading();
}
}
},
methods: {
enableForm(type) {
this.formEnabled = true;
this.$store.dispatch('callForward/setFormType', type);
this.$store.dispatch('callForward/setActiveForm', this.groupName);
this.$store.dispatch('callForward/setDestinationsetId', this.id);
this.$store.dispatch('callForward/setGroupName', this.groupName);
this.$store.dispatch('callForward/setPriority', this.priority);
if (type === 'voicebox') {
this.destinationForm.destination = 'Voicemail';
} else if (type === 'fax2mail') {
this.destinationForm.destination = 'Fax2Mail';
} else {
this.destinationForm.destination = '';
}
},
disableForm() {
this.destinationForm.timeout = 300;
this.destinationForm.destination = '';
this.formEnabled = false;
this.$store.dispatch('callForward/resetFormState');
this.$store.dispatch('callForward/resetDestinationState');
},
addDestination() {
startLoading();
this.$store.dispatch('callForward/addDestination', {
form: this.destinationForm,
destinations: this.destinations
});
}
}
}
</script>
<style lang="stylus">
@import '~variables'
.add-destination-form
margin 0 15px
.q-slider.label-always
padding 15px 0 5px
height 50px
</style>

@ -1,20 +1,35 @@
<template>
<div>
<q-item highlight v-for="(destination, index) in destinations">
<q-item v-for="(destination, index) in destinations">
<q-item-main>
<div class="dest-row">
<span v-if="index == 0"> {{ $t('pages.callForward.firstRing') }} </span>
<span v-else-if="index > 0"> {{ $t('pages.callForward.thenRing') }} </span>
<span class="dest-values"> {{ destination.destination | numberFormat }} </span>
<span v-if="index == 0">
{{ $t('pages.callForward.firstRing') }}
</span>
<span v-else-if="index > 0">
{{ $t('pages.callForward.thenRing') }}
</span>
<span class="dest-values">
{{ destination.destination | destinationFormat }}
</span>
<span v-if="isNumber(destination.destination)">
<span> {{ $t('pages.callForward.for') }} </span>
<span class="dest-values">{{ destination.timeout }}</span>
<span> {{ $t('pages.callForward.secs') }} </span>
<span>
{{ $t('pages.callForward.for') }}
</span>
<span class="dest-values">
{{ destination.timeout }}
</span>
<span>
{{ $t('pages.callForward.secs') }}
</span>
</span>
</div>
</q-item-main>
<q-item-side right>
<q-btn color="negative" flat icon="delete" @click="deleteDestination(index)">{{ $t('buttons.remove') }}</q-btn>
<q-btn color="negative" flat icon="delete"
@click="deleteDestination(index)">
{{ $t('buttons.remove') }}
</q-btn>
</q-item-side>
</q-item>
</div>
@ -24,12 +39,13 @@
import numberFormat from '../../../filters/number-format'
import _ from 'lodash'
import { showToast } from '../../../helpers/ui'
import { QItem, QItemSide, Dialog, Toast, QBtn, QItemMain } from 'quasar-framework'
import { QItem, QItemMain, QItemSide, Toast,
Dialog, QBtn } from 'quasar-framework'
export default {
name: 'csc-destination',
props: [
'destinations',
'destinationsetId'
'id'
],
components: {
QItem,
@ -44,7 +60,11 @@
methods: {
isNumber(destination) {
let dest = destination.split(/:|@/);
return !isNaN(dest[1]);
if (dest[2] === 'fax2mail.local') {
return false;
} else {
return !isNaN(dest[1]);
};
},
deleteDestination(index) {
let clonedDestinations = _.cloneDeep(this.destinations);
@ -66,7 +86,7 @@
color: 'negative',
handler () {
store.dispatch('callForward/deleteDestinationFromDestinationset', {
id: self.destinationsetId,
id: self.id,
data: clonedDestinations,
deleteDestinationset: isLastDestination }).then((result) => {
store.dispatch('callForward/loadAlwaysEverybodyDestinations');
@ -88,8 +108,6 @@
<style lang="stylus">
@import '~variables'
.dest-row
display inline-block
width 90%
.dest-values
font-weight 500
</style>

@ -1,5 +1,5 @@
<template>
<div>
<div class="dest-section">
<q-card-title class="dest-title">
<q-icon :name="icon" class="dest-icon" />
{{ title }}
@ -14,38 +14,62 @@
</q-item>
</div>
<div v-else v-for="destinationset in group">
<csc-destination :destinations="destinationset.destinations" :destinationset-id="destinationset.id">
<csc-destination v-bind="destinationset">
</csc-destination>
</div>
</div>
</q-list>
<csc-add-destination-form v-bind="lastDestinationset">
</csc-add-destination-form>
</q-card-main>
</div>
</template>
<script>
import _ from 'lodash'
import CscDestination from './CscDestination'
import { QCardTitle, QCardMain, QList,
QItem, QIcon } from 'quasar-framework'
import CscAddDestinationForm from './CscAddDestinationForm'
import { showToast } from '../../../helpers/ui'
import { QCardTitle, QCardMain, QCardSeparator,
QItem, QList } from 'quasar-framework'
export default {
name: 'csc-destinations',
props: [
'title',
'icon',
'group'
'group',
'groupName'
],
components: {
QCardTitle,
QCardMain,
QList,
QItem,
QIcon,
CscDestination
QCardSeparator,
CscDestination,
CscAddDestinationForm
},
computed: {
lastDestinationset() {
let destinationset = _.findLast(this.group) || {};
destinationset.groupName = this.groupName;
destinationset.priority = destinationset.lowestPriority || 1;
return destinationset;
}
}
}
</script>
<style lang="stylus">
@import '~variables'
.dest-section
.dest-title
padding 0 15px
.dest-title:first-child
padding 20px 15px 0 15px
.q-item
padding 0 15px
.q-list
margin-bottom 0
.dest-row
inline-block
.dest-title

@ -2,8 +2,10 @@
import Vue from 'vue';
import NumberFilter from './number'
import NumberFormatFilter from './number-format'
import { normalizeDestination } from './number-format'
import DateFilter from './date'
Vue.filter('number', NumberFilter);
Vue.filter('readableDate', DateFilter);
Vue.filter('numberFormat', NumberFormatFilter);
Vue.filter('destinationFormat', normalizeDestination);

@ -1,32 +1,40 @@
import _ from 'lodash';
import url from 'url';
import { PhoneNumberUtil, PhoneNumberFormat } from 'google-libphonenumber';
import { format } from 'quasar-framework'
const { capitalize } = format
const { capitalize } = format;
var phoneUtil = PhoneNumberUtil.getInstance();
export default function(number) {
export default function numberFormat(number) {
try {
let phoneNumber = url.parse(number, true).auth.split(':')[0];
if (isNaN(phoneNumber)) {
phoneNumber = normalizeDestination(url.parse(number, true));
let destination = url.parse(number, true);
let extractedNumber = destination.auth.split(':')[0];
let normalizedNumber = normalizeNumber(extractedNumber);
if(normalizedNumber !== extractedNumber) {
return normalizedNumber;
} else {
return number;
}
return normalizeNumber(phoneNumber);
} catch(err1) {
} catch(err) {
return normalizeNumber(number);
}
}
export function normalizeNumber(number) {
if(_.isString(number) && number.match(/^\+?[0-9]+$/)) {
if(_.isString(number)) {
let normalizedNumber = number.replace(/\s*/g, '');
if(normalizedNumber.match(/^\+/) === null) {
normalizedNumber = '+' + normalizedNumber;
}
try {
return phoneUtil.format(phoneUtil.parse(normalizedNumber, 'DE'), PhoneNumberFormat.INTERNATIONAL);
} catch(err) {
return normalizedNumber;
if(normalizedNumber.match(/^\+?[0-9]+$/)) {
if(normalizedNumber.match(/^\+/) === null) {
normalizedNumber = '+' + normalizedNumber;
}
try {
return phoneUtil.format(phoneUtil.parse(normalizedNumber, 'DE'), PhoneNumberFormat.INTERNATIONAL);
} catch(err) {
return normalizedNumber;
}
} else {
return number;
}
} else {
return number;
@ -41,13 +49,38 @@ export function rawNumber(number) {
}
export function normalizeDestination(destination) {
let normalizedDestination;
if (destination.host == 'app.local') {
normalizedDestination = destination.auth;
} else if (destination.host == 'voicebox.local') {
normalizedDestination = 'Voicemail';
try {
let parsedDestination = url.parse(destination, true);
let authParts = parsedDestination.auth.split(':');
let host = parsedDestination.host;
let normalizedNumber = normalizeNumber(authParts[0]);
let isNumber = normalizedNumber !== authParts[0];
if (host === 'voicebox.local') {
return 'Voicemail';
} else if (host === 'fax2mail.local') {
return 'Fax2Mail';
} else if (host === 'managersecretary.local') {
return 'Manager Secretary';
} else if (authParts[0] === 'custom-hours') {
return 'Custom Announcement';
} else if (host === 'app.local') {
return _.capitalize(authParts[0]);
} else if (!isNumber) {
return _.capitalize(host.split('.')[0]);
} else {
return normalizedNumber;
}
} catch(err) {
return normalizeNumber(destination);
}
}
export function normalizeTerminationInput(destination) {
if (destination === 'Voicemail') {
return 'voicebox';
} else if (destination = 'Fax2Mail') {
return 'fax2mail';
} else {
normalizedDestination = capitalize(destination.host.split('.')[0]);
return destination;
}
return normalizedDestination;
}

@ -9,7 +9,8 @@
"cancel": "Cancel",
"save": "Save",
"remove": "Remove",
"edit": "Edit"
"edit": "Edit",
"add": "Add"
},
"toasts": {
"callAvailable": "You are now able to start and receive calls",
@ -121,6 +122,11 @@
"companyHours": "Company Hours",
"afterHours": "After Hours"
},
"buttons": {
"addNumber": "Add Number",
"addVoicemail": "Add Voicemail",
"addFax2Mail": "Add Fax2Mail"
},
"whenOnline": "When I am online ...",
"whenBusy": "When I am busy ...",
"whenOffline": "When I am offline ...",
@ -133,7 +139,13 @@
"removeDialogTitle": "Remove call forward destination",
"removeDialogText": "You are about to remove the destination {destination}",
"removeSuccessMessage": "Removed destination {destination}",
"removeErrorMessage": "An error occured while trying to delete the destination. Please try again."
"removeErrorMessage": "An error occured while trying to delete the destination. Please try again.",
"addDestinationButton": "Add destination",
"addDestinationSuccessMessage": "Added destination {destination}",
"addErrorMessage": "An error occured while trying to add the destination. Please try again.",
"addInputError": "Input a valid number or subscriber name",
"timeout": "Timeout",
"destination": "Destination"
}
},
"call": {

@ -2,10 +2,22 @@
'use strict';
import _ from 'lodash';
import { getSourcesets, getDestinationsets, getTimesets,
getMappings, loadAlwaysEverybodyDestinations,
import { getSourcesets,
getDestinationsets,
getTimesets,
getMappings,
loadAlwaysEverybodyDestinations,
deleteDestinationFromDestinationset,
deleteDestinationsetById } from '../api/call-forward';
addDestinationToDestinationset,
addDestinationToEmptyGroup,
addDestinationToExistingGroup } from '../api/call-forward';
const AddDestinationState = {
button: 'button',
requesting: 'requesting',
succeeded: 'succeeded',
failed: 'failed'
};
export default {
namespaced: true,
@ -15,9 +27,42 @@ export default {
timesets: null,
destinationsets: null,
alwaysEverybodyDestinations: {
online: [],
busy: [],
offline: []
online: [{}],
busy: [{}],
offline: [{}]
},
addDestinationState: AddDestinationState.button,
addDestinationError: null,
activeForm: '',
formType: '',
destinationsetId: '',
groupName: '',
form: {
announcement_id: null,
destination: '',
priority: 1,
timeout: ''
},
lastAddedDestination: null
},
getters: {
hasFaxCapability(state, getters, rootState, rootGetters) {
return rootGetters['user/hasFaxCapability'];
},
getSubscriberId(state, getters, rootState, rootGetters) {
return rootGetters['user/getSubscriberId'];
},
getForm(state) {
return state.form;
},
getFormType(state) {
return state.formType;
},
getGroupName(state) {
return state.groupName;
},
getDestinationsetId(state) {
return state.destinationsetId;
}
},
mutations: {
@ -35,6 +80,51 @@ export default {
},
loadAlwaysEverybodyDestinations(state, result) {
state.alwaysEverybodyDestinations = result;
},
setActiveForm(state, value) {
state.activeForm = value;
},
setFormType(state, value) {
state.formType = value;
},
setDestinationsetId(state, value) {
state.destinationsetId = value;
},
setGroupName(state, value) {
state.groupName = value;
},
setPriority(state, value) {
state.form.priority = value;
},
setLastAddedDestination(state, value) {
state.lastAddedDestination = value;
},
resetFormState(state) {
state.form = {
announcement_id: null,
destination: '',
priority: 1,
timeout: ''
}
},
resetDestinationState(state) {
state.activeForm = '';
state.formType = '';
state.destinationsetId = '';
state.groupName = '';
state.addDestinationState = AddDestinationState.button;
},
addDestinationRequesting(state) {
state.addDestinationState = AddDestinationState.requesting;
state.addDestinationError = null;
},
addDestinationSucceeded(state) {
state.addDestinationState = AddDestinationState.succeeded;
state.addDestinationError = null;
},
addDestinationFailed(state, error) {
state.addDestinationState = AddDestinationState.failed;
state.addDestinationError = error;
}
},
actions: {
@ -95,15 +185,75 @@ export default {
});
});
},
deleteDestinationsetById(context, id) {
addDestinationToDestinationset(context, options) {
return new Promise((resolve, reject) => {
deleteDestinationsetById(id)
addDestinationToDestinationset(options)
.then((result) => {
resolve(result);
}).catch((err) => {
reject(err);
});
});
},
addDestination(context, options) {
let form = _.clone(context.getters.getForm);
let updatedOptions;
let type = context.getters.getFormType;
context.commit('addDestinationRequesting');
if (type !== 'number') {
delete form.timeout;
form.destination = type;
} else {
form.timeout = options.form.timeout;
form.destination = options.form.destination;
};
updatedOptions = {
subscriberId: context.getters.getSubscriberId,
data: form,
groupName: context.getters.getGroupName,
id: context.getters.getDestinationsetId
};
if (options.destinations) {
return new Promise((resolve, reject) => {
addDestinationToExistingGroup(updatedOptions).then(() => {
context.commit('setLastAddedDestination', options.form.destination);
context.commit('addDestinationSucceeded');
context.dispatch('loadAlwaysEverybodyDestinations');
}).catch((err) => {
context.commit('addDestinationFailed', err.message);
});
});
} else {
return new Promise((resolve, reject) => {
addDestinationToEmptyGroup(updatedOptions).then((result) => {
context.commit('addDestinationSucceeded');
context.dispatch('loadAlwaysEverybodyDestinations');
}).catch((err) => {
context.commit('addDestinationFailed', err.message);
});
});
}
},
setActiveForm(context, value) {
context.commit('setActiveForm', value);
},
setFormType(context, value) {
context.commit('setFormType', value);
},
setDestinationsetId(context, value) {
context.commit('setDestinationsetId', value);
},
setGroupName(context, value) {
context.commit('setGroupName', value);
},
setPriority(context, value) {
context.commit('setPriority', value);
},
resetFormState(context) {
context.commit('resetFormState');
},
resetDestinationState(context) {
context.commit('resetDestinationState');
}
}
};

@ -44,6 +44,9 @@ export default {
},
hasRtcEngineCapabilityEnabled(state, getters) {
return getters.hasRtcEngineCapability && state.capabilities.rtcengine === true;
},
getSubscriberId(state, getters) {
return state.subscriberId;
}
},
mutations: {

@ -5,7 +5,8 @@ import Vue from 'vue';
import VueResource from 'vue-resource';
import { getMappings, getSourcesets, getTimesets,
getDestinationsets, getDestinationsetById,
deleteDestinationFromDestinationset } from '../../src/api/call-forward';
deleteDestinationFromDestinationset,
addDestinationToDestinationset } from '../../src/api/call-forward';
import { assert } from 'chai';
Vue.use(VueResource);
@ -359,4 +360,30 @@ describe('CallForward', function(){
});
});
it('should add destination to call forward destinationset', function(done){
let options = {
id: 3,
data: {
"announcement_id": null,
"destination": "112233",
"priority": 1,
"timeout": 60
}
};
Vue.http.interceptors = [];
Vue.http.interceptors.unshift((request, next)=>{
next(request.respondWith(JSON.stringify({}), {
status: 204
}));
});
addDestinationToDestinationset(options).then((result)=>{
assert.isOk(result);
done();
}).catch((err)=>{
done(err);
});
});
});

@ -37,4 +37,19 @@ describe('CallForward', function(){
assert.deepEqual(state.alwaysEverybodyDestinations, data);
});
it(' should reset destination form', function() {
let state = {
conversations: [
]
};
let data = {
announcement_id: null,
destination: '',
priority: 1,
timeout: ''
};
CallForwardModule.mutations.resetFormState(state);
assert.deepEqual(state.form, data);
});
});

@ -0,0 +1,68 @@
'use strict';
import { assert } from 'chai';
import numberFormat from '../../src/filters/number-format';
import { normalizeNumber, normalizeDestination } from '../../src/filters/number-format';
const numbers = {
valid1: '+43 993 004',
valid2: '43993004',
valid3: ' 43993004 ',
valid4: '+43993004',
valid5: '43 993 004',
valid6: '4 3 9 9 3 0 0 4',
valid7: '+4 3 9 9 3 0 0 4',
valid8: ' +43993004 ',
invalid1: '43993004+',
invalid2: '$43993004',
invalid3: 'a43993004',
invalid4: 'abcdefghi'
};
const sipUris = {
valid1: 'sip:43993004@sipwise.com',
invalid1: 'sip:a43993004@sipwise.com'
};
const destinations = {
voiceMail: 'sip:vmu@voicebox.local',
fax2Mail: 'sip:@fax2mail.local',
managerSecretary: 'sip:@managersecretary.local',
app: 'sip:app@app.local',
customHours: 'sip:custom-hours@app.local',
conference: 'sip:@conference.local',
number: 'sip:43993004@sipwise.com'
};
describe('NumberFormatFilter', function() {
it('should normalize phone numbers', function(){
assert.equal(normalizeNumber(numbers.valid1), numbers.valid1);
assert.equal(normalizeNumber(numbers.valid2), numbers.valid1);
assert.equal(normalizeNumber(numbers.valid3), numbers.valid1);
assert.equal(normalizeNumber(numbers.valid4), numbers.valid1);
assert.equal(normalizeNumber(numbers.valid5), numbers.valid1);
assert.equal(normalizeNumber(numbers.valid6), numbers.valid1);
assert.equal(normalizeNumber(numbers.valid7), numbers.valid1);
assert.equal(normalizeNumber(numbers.valid8), numbers.valid1);
assert.equal(normalizeNumber(numbers.invalid1), numbers.invalid1);
assert.equal(normalizeNumber(numbers.invalid2), numbers.invalid2);
assert.equal(normalizeNumber(numbers.invalid3), numbers.invalid3);
assert.equal(normalizeNumber(numbers.invalid4), numbers.invalid4);
});
it('should format a number or sip uri', function(){
assert.equal(numberFormat(sipUris.valid1), numbers.valid1);
assert.equal(numberFormat(sipUris.invalid1), sipUris.invalid1);
});
it('should format a call forward destination', function(){
assert.equal(normalizeDestination(destinations.voiceMail), 'Voicemail');
assert.equal(normalizeDestination(destinations.fax2Mail), 'Fax2Mail');
assert.equal(normalizeDestination(destinations.managerSecretary), 'Manager Secretary');
assert.equal(normalizeDestination(destinations.app), 'App');
assert.equal(normalizeDestination(destinations.customHours), 'Custom Announcement');
assert.equal(normalizeDestination(destinations.conference), 'Conference');
assert.equal(normalizeDestination(destinations.number), numbers.valid1);
});
});
Loading…
Cancel
Save