TT#37304 CallForwarding: As a Customer, I want to enable/disable calls to my own number

What has been done:
- TT#42759, Implement API method for toggling of enable/disable own number
- TT#42763, Implement state handling for toggle button and timeout
- TT#42766, Extend unit tests to also account for cft destinations
- TT#42755, Extend API method to also render cft destinations
- TT#42761, Implement UI for toggle button
- TT#42760, Implement API methods for fetching, updating and resetting
  cft_timeout
- TT#43163, Implement UI for timeout input field with reset/save
- TT#42764, Implement success toast and error handling for toggle action
- TT#42765, Implement success toast and error handling for timeout input

Change-Id: I0ce37bbe14caacadb822f14d01782009633c4354
changes/37/23037/12
raxelsen 7 years ago committed by Hans-Peter Herzog
parent 3a8999649d
commit 91e33e2516

@ -2,3 +2,4 @@
host=gerrit.mgm.sipwise.com
port=29418
project=ngcp-csc-ui

@ -12,6 +12,7 @@ export function getMappings(id) {
let jsonBody = getJsonBody(result.body);
delete jsonBody._links;
delete jsonBody.cfs;
delete jsonBody.cfr;
resolve(getJsonBody(result.body));
}).catch((err) => {
reject(err);
@ -131,15 +132,25 @@ export function loadDestinations(options) {
export function getDestinationsBySourcesetId(options) {
return new Promise((resolve, reject) => {
let cftTimeset = null;
let cfuTimeset = null;
let cfnaTimeset = null;
let cfbTimeset = null;
Promise.resolve().then(() => {
return getMappings(options.subscriberId);
}).then((mappings) => {
let cftPromises = [];
let cfuPromises = [];
let cfnaPromises = [];
let cfbPromises = [];
if(_.has(mappings, 'cft') && _.isArray(mappings.cft) && mappings.cft.length > 0) {
mappings.cft.forEach((cftMapping) => {
if (cftMapping.timeset === options.timeset && cftMapping.sourceset_id === options.sourceset_id) {
cftTimeset = cftMapping.timeset_id;
cftPromises.push(getDestinationsetById(cftMapping.destinationset_id));
}
});
}
if(_.has(mappings, 'cfu') && _.isArray(mappings.cfu) && mappings.cfu.length > 0) {
mappings.cfu.forEach((cfuMapping) => {
if (cfuMapping.timeset === options.timeset && cfuMapping.sourceset_id === options.sourceset_id) {
@ -165,22 +176,27 @@ export function getDestinationsBySourcesetId(options) {
});
}
return Promise.all([
Promise.all(cftPromises),
Promise.all(cfuPromises),
Promise.all(cfnaPromises),
Promise.all(cfbPromises)
]);
}).then((result) => {
addNameIdAndTerminating({ group: result[0], groupName: 'cfu', timesetId: cfuTimeset });
addNameIdAndTerminating({ group: result[1], groupName: 'cfna', timesetId: cfnaTimeset });
addNameIdAndTerminating({ group: result[2], groupName: 'cfb', timesetId: cfbTimeset });
let ownPhone = result[0].length > 0 && result[1].length === 0;
let cftDestinations = addNameIdOwnPhoneAndTerminating({ group: _.cloneDeep(result[0]), groupName: 'cft', timesetId: cftTimeset, ownPhone: ownPhone });
let cfuDestinations = addNameIdOwnPhoneAndTerminating({ group: _.cloneDeep(result[1]), groupName: 'cfu', timesetId: cfuTimeset, ownPhone: ownPhone });
let offlineDestinations = addNameIdOwnPhoneAndTerminating({ group: _.cloneDeep(result[2]), groupName: 'cfna', timesetId: cfnaTimeset, ownPhone: ownPhone });
let busyDestinations = addNameIdOwnPhoneAndTerminating({ group: _.cloneDeep(result[3]), groupName: 'cfb', timesetId: cfbTimeset, ownPhone: ownPhone });
let onlineDestinations = getOnlineDestinations({ cftDestinations: cftDestinations, cfuDestinations: cfuDestinations });
resolve({
sourcesetId: options.sourceset_id,
sourcesetName: options.sourceset_name,
sourcesetMode: options.sourceset_mode,
ownPhone: ownPhone,
destinationGroups: {
online: result[0],
offline: result[1],
busy: result[2]
online: onlineDestinations,
offline: offlineDestinations,
busy: busyDestinations
}
})
}).catch((err)=>{
@ -189,13 +205,24 @@ export function getDestinationsBySourcesetId(options) {
});
}
export function addNameIdAndTerminating(options) {
export function getOnlineDestinations(options) {
if (options.cftDestinations.length > 0 && options.cfuDestinations.length === 0) {
return options.cftDestinations;
}
else {
return options.cfuDestinations;
}
}
export function addNameIdOwnPhoneAndTerminating(options) {
let terminatingFlag = false;
options.group.forEach(destinationset => {
destinationset.groupName = options.groupName;
destinationset.timesetId = options.timesetId;
destinationset.ownPhone = options.ownPhone;
destinationset.destinations.forEach(destination => {
let normalized = normalizeDestination(destination.destination);
if (!terminatingFlag && _.includes(['Voicebox', 'Fax2Mail', 'Manager Secretary',
'Custom Announcement', 'Conference'], normalized)) {
terminatingFlag = true;
@ -841,3 +868,81 @@ export function deleteSourceFromSourcesetByIndex(options) {
});
});
}
export function flipCfuAndCft(options) {
return new Promise((resolve, reject) => {
Promise.resolve().then(() => {
return getMappings(options.subscriberId);
}).then((mappings) => {
let flipValues = mappings[options.fromType].filter((destinationset) => {
return destinationset.sourceset_id === options.sourcesetId && destinationset.timeset_id === options.timesetId;
});
let fromValues = mappings[options.fromType].filter((destinationset) => {
return !(destinationset.sourceset_id === options.sourcesetId && destinationset.timeset_id === options.timesetId);
})
let toValues = mappings[options.toType].concat(flipValues);
let patchOptions = [
{
op: 'replace',
path: '/' + options.fromType,
value: fromValues
}, {
op: 'replace',
path: '/' + options.toType,
value: toValues
}
];
let timeoutOption = {
op: 'replace',
path: '/cft_ringtimeout',
value: 15
};
if (!mappings.cft_ringtimeout) {
patchOptions.push(timeoutOption);
}
return new Promise((resolve, reject) => {
let headers = {
'Content-Type': 'application/json-patch+json'
};
Vue.http.patch('api/cfmappings/' + options.subscriberId,
patchOptions, { headers: headers }).then((result) => {
resolve(result);
}).catch((err) => {
reject(err);
});
});
}).then(() => {
resolve();
}).catch((err) => {
reject(err);
});
});
}
export function getOwnPhoneTimeout(id) {
return new Promise((resolve, reject)=>{
Vue.http.get('api/cfmappings/' + id).then((res) => {
let timeout = getJsonBody(res.body).cft_ringtimeout;
resolve(timeout);
}).catch((err) => {
reject(err);
});
});
}
export function updateOwnPhoneTimeout(options) {
return new Promise((resolve, reject)=>{
let headers = {
'Content-Type': 'application/json-patch+json'
};
Vue.http.patch('api/cfmappings/' + options.subscriberId, [{
op: 'replace',
path: '/cft_ringtimeout',
value: options.timeout
}], { headers: headers }).then(() => {
resolve();
}).catch((err) => {
reject(err);
});
});
}

@ -53,8 +53,6 @@
:float-label="$t('pages.callForward.timeout')"
type="number"
v-model="destinationForm.timeout"
:min="0"
:max="600"
suffix="seconds"
/>
</q-field>

@ -1,26 +1,33 @@
<template>
<div class="dest-card">
<csc-destinations :title="$t('pages.callForward.whenOnline')"
ref="online"
class="csc-destinations"
:group="destinations.online"
group-name="cfu"
:timeset="timeset"
:sourceset="sourceset"
icon="smartphone" />
:show-own-phone="true"
:own-phone-timeout="ownPhoneTimeout"
:loading="isUpdating"
icon="smartphone"
/>
<csc-destinations :title="$t('pages.callForward.whenBusy')"
class="csc-destinations"
:group="destinations.busy"
group-name="cfb"
:timeset="timeset"
:sourceset="sourceset"
icon="phonelink_ring" />
icon="phonelink_ring"
/>
<csc-destinations :title="$t('pages.callForward.whenOffline')"
class="csc-destinations"
:group="destinations.offline"
group-name="cfna"
:timeset="timeset"
:sourceset="sourceset"
icon="phonelink_erase" />
icon="phonelink_erase"
/>
</div>
</template>
@ -36,13 +43,12 @@
'sourceset',
'destinations'
],
data () {
return {
}
},
components: {
CscDestinations
},
created() {
this.$store.dispatch('callForward/loadOwnPhoneTimeout');
},
computed: {
...mapState('callForward', {
removeDestinationState: 'removeDestinationState',
@ -62,7 +68,15 @@
}
}),
...mapGetters('callForward', {
timesLength: 'getTimesetTimesLength'
timesLength: 'getTimesetTimesLength',
isUpdating: 'isUpdating',
updateOwnPhoneToggleState: 'updateOwnPhoneToggleState',
updateOwnPhoneToggleError: 'updateOwnPhoneToggleError',
ownPhoneTimeout: 'ownPhoneTimeout',
lastOwnPhoneToggle: 'lastOwnPhoneToggle',
updateOwnPhoneTimeoutState: 'updateOwnPhoneTimeoutState',
updateOwnPhoneTimeoutError: 'updateOwnPhoneTimeoutError',
lastOwnPhoneTimeout: 'lastOwnPhoneTimeout'
})
},
methods: {
@ -124,25 +138,35 @@
}
},
removeTimeState(state) {
if (state === 'requesting') {
startLoading();
}
else if (state === 'failed') {
stopLoading();
showGlobalError(this.removeTimeError);
if (state === 'failed') {
showGlobalError(this.changeDestinationError);
}
else if (state === 'succeeded') {
stopLoading();
if (this.timesLength <= 1) {
showToast(this.$t('pages.callForward.times.removeTimesetSuccessMessage'));
}
else {
showToast(this.$t('pages.callForward.times.removeSuccessMessage', {
day: this.lastRemovedDay
}));
}
this.reloadTimes(this.timeset);
}
},
updateOwnPhoneToggleState(state) {
if (state === 'failed') {
showGlobalError(this.updateOwnPhoneToggleError);
}
else if (state === 'succeeded') {
this.reloadDestinations(this.timeset);
showToast(this.$t('pages.callForward.updateOwnPhoneToggleSuccessMessage', {
toggle: this.lastOwnPhoneToggle
}));
}
},
updateOwnPhoneTimeoutState(state) {
if (state === 'failed') {
showGlobalError(this.updateOwnPhoneToggleError);
}
else if (state === 'succeeded') {
this.$refs.online.hideModal();
this.reloadDestinations(this.timeset);
showToast(this.$t('pages.callForward.updateOwnPhoneTimeoutSuccessMessage', {
timeout: this.lastOwnPhoneTimeout
}));
}
}
}
}

@ -1,12 +1,22 @@
<template>
<div>
<q-item highlight separator class="csc-destination" :key="index" v-for="(destination, index) in destinations">
<q-item
highlight
separator
class="csc-destination"
:key="index"
v-for="(destination, index) in destinations"
>
<q-item-main>
<div v-if="$q.platform.is.desktop" class="dest-row" :class="{ terminated: destination.terminated }">
<span v-if="index == 0">
<div
v-if="$q.platform.is.desktop"
class="dest-row"
:class="{ terminated: destination.terminated }"
>
<span v-if="index == 0 && !ownPhoneEnabled">
{{ $t('pages.callForward.firstRing') }}
</span>
<span v-else-if="index > 0">
<span v-else>
{{ $t('pages.callForward.thenRing') }}
</span>
<span class="dest-values">
@ -27,23 +37,33 @@
{{ $t('pages.callForward.terminatedTooltip') }}
</q-tooltip>
</div>
<div v-if="$q.platform.is.mobile" class="dest-row" :class="{ terminated: destination.terminated, mobile: mobileClasses }">
<q-item-tile class="dest-values" label>
<div
v-if="$q.platform.is.mobile"
class="dest-row mobile"
:class="{ terminated: destination.terminated }"
>
<q-item-tile
class="dest-values"
label
>
<span v-if="!isNonTerminating(destination.destination)">
<span v-if="index == 0">
<span v-if="index == 0 && !ownPhoneEnabled">
{{ $t('pages.callForward.firstRing') }}
</span>
<span v-else-if="index > 0">
<span v-else>
{{ $t('pages.callForward.thenRing') }}
</span>
</span>
{{ destination.destination | destinationFormat }}
</q-item-tile>
<q-item-tile class="dest-sublabel" sublabel>
<span v-if="index == 0 && isNonTerminating(destination.destination)">
<q-item-tile
class="dest-sublabel"
sublabel
>
<span v-if="index == 0 && isNonTerminating(destination.destination) && !ownPhoneEnabled">
{{ $t('pages.callForward.firstRing') }}
</span>
<span v-else-if="index > 0 && isNonTerminating(destination.destination)">
<span v-else>
{{ $t('pages.callForward.thenRing') }}
</span>
<span v-if="isNonTerminating(destination.destination)">
@ -63,20 +83,42 @@
</q-tooltip>
</div>
</q-item-main>
<q-item-side class="dest-btns" icon="more_vert" right>
<q-item-side
class="dest-btns"
icon="more_vert"
right
>
<q-popover ref="popover">
<q-list separator link>
<q-item v-if="destinations.length > 1 && !hasNoUpOption(index)" @click="moveDestination('up', index), $refs.popover[index].close()">
<q-list
separator
link
>
<q-item
v-if="destinations.length > 1 && !hasNoUpOption(index)"
@click="moveDestination('up', index), $refs.popover[index].close()"
>
<q-item-main :label="$t('buttons.moveUp')" />
<q-item-side icon="keyboard_arrow_up" color="secondary"></q-item-side>
<q-item-side
icon="keyboard_arrow_up"
color="secondary"
/>
</q-item>
<q-item v-if="destinations.length > 1 && !hasNoDownOption(index)" @click="moveDestination('down', index), $refs.popover[index].close()">
<q-item
v-if="destinations.length > 1 && !hasNoDownOption(index)"
@click="moveDestination('down', index), $refs.popover[index].close()"
>
<q-item-main :label="$t('buttons.moveDown')" />
<q-item-side icon="keyboard_arrow_down" color="secondary"></q-item-side>
<q-item-side
icon="keyboard_arrow_down"
color="secondary"
/>
</q-item>
<q-item @click="deleteDestination(index), $refs.popover[index].close()">
<q-item-main :label="$t('buttons.remove')" />
<q-item-side icon="delete" color="negative"></q-item-side>
<q-item-side
icon="delete"
color="negative"
/>
</q-item>
</q-list>
</q-popover>
@ -100,8 +142,7 @@
QBtn,
QTooltip,
QPopover,
QList,
Platform
QList
} from 'quasar-framework'
export default {
@ -110,7 +151,9 @@
'destinations',
'id',
'prevDestId',
'nextDestId'
'nextDestId',
'ownPhone',
'showOwnPhone'
],
components: {
QItem,
@ -127,12 +170,8 @@
'changeDestinationState',
'changeDestinationError'
]),
mobileClasses() {
let classes = ['dest-row'];
if(Platform.is.mobile) {
classes.push('mobile');
}
return classes;
ownPhoneEnabled() {
return this.ownPhone && this.showOwnPhone
}
},
watch: {
@ -221,9 +260,6 @@
.dest-row.terminated.mobile
color $grey
.q-item.csc-destination
padding 0
.q-item-highlight.csc-destination:hover
background-color lighten($primary, 70%)
@ -232,6 +268,7 @@
white-space nowrap
overflow hidden
font-size 16px
.dest-values
font-weight 500

@ -1,21 +1,208 @@
<template>
<div class="dest-section">
<div class="dest-title">
<q-icon :name="icon" class="dest-icon" size="24px" />
<q-icon
:name="icon"
class="dest-icon"
size="24px"
/>
{{ title }}
</div>
<q-list no-border>
<q-item v-if="group.length === 0" class="dest-row csc-no-destination">
<span> {{ $t('pages.callForward.forwardToNowhere') }} </span>
<q-item
highlight
inset-separator
v-if="showOwnPhone && group.length > 0"
:class="{ ['csc-own-phone']: !isMobile }"
>
<q-item-side v-if="!isMobile">
<q-field
:disabled="loading"
>
<q-toggle
:value="ownPhone"
@input="toggle()"
checked-icon="phone_in_talk"
unchecked-icon="phone_in_talk"
/>
</q-field>
</q-item-side>
<q-item-main>
<div
v-if="!isMobile"
class="dest-row own-phone-desktop"
>
<span v-if="ownPhone">
<span>
{{ $t('pages.callForward.firstRing') }}
</span>
<span class="dest-values">
{{ $t('pages.callForward.ownPhone') }}
</span>
<span>
{{ $t('pages.callForward.for') }}
</span>
<span class="dest-values">
{{ ownPhoneTimeout || 0 }}
</span>
<span>
{{ $t('pages.callForward.secs') }}
</span>
</span>
<span v-else>
{{ $t('pages.callForward.ownPhoneDisabled') }}
</span>
</div>
<div
v-if="isMobile"
class="dest-row mobile"
>
<q-item-tile
v-if="ownPhone"
class="dest-values"
label
>
{{ $t('pages.callForward.ownPhone') }}
</q-item-tile>
<q-item-tile
v-else
label
>
{{ $t('pages.callForward.ownPhoneDisabled') }}
</q-item-tile>
<q-item-tile
v-if="ownPhone"
class="dest-sublabel"
sublabel
>
<span v-if="ownPhone">
<span>
{{ $t('pages.callForward.firstRing') }}
</span>
<span>
{{ $t('pages.callForward.for') }}
</span>
<span class="dest-values">
{{ ownPhoneTimeout || 0 }}
</span>
<span>
{{ $t('pages.callForward.secs') }}
</span>
</span>
<span v-else>
{{ $t('pages.callForward.ownPhoneDisabled') }}
</span>
</q-item-tile>
</div>
</q-item-main>
<q-item-side
v-if="ownPhone || isMobile"
class="dest-btns"
icon="more_vert"
right
>
<q-popover ref="popover">
<q-list
separator
link
>
<q-item
v-if="isMobile"
@click="toggle();$refs.popover.close()"
>
<q-item-main :label="$t('pages.callForward.toggleTimeout', {
mode: this.ownPhone ? 'Disable' : 'Enable'
})" />
<q-item-side
:icon="toggleIcon"
color="secondary"
/>
</q-item>
<q-item @click="showModal();$refs.popover.close()">
<q-item-main :label="$t('pages.callForward.editTimeout')" />
<q-item-side
icon="fa-edit"
color="secondary"
/>
</q-item>
</q-list>
</q-popover>
</q-item-side>
</q-item>
<div v-else :key="index" v-for="(destinationset, index) in group">
<csc-destination v-bind="destinationset"
:prev-dest-id="previousDestinationsetId(index)"
:next-dest-id="nextDestinationsetId(index)"
/>
</div>
<q-item-separator
v-if="showOwnPhone && group.length > 0"
class="csc-dest-separator"
/>
<q-item
dense
v-if="group.length === 0"
class="dest-row csc-no-destination"
>
<span>
{{ $t('pages.callForward.forwardToNowhere') }}
</span>
</q-item>
<csc-destination
v-else
v-for="(destinationset, index) in group"
:key="index"
v-bind="destinationset"
:prev-dest-id="previousDestinationsetId(index)"
:next-dest-id="nextDestinationsetId(index)"
:ownPhone="ownPhone"
:showOwnPhone="showOwnPhone"
/>
</q-list>
<csc-add-destination-form v-bind="lastDestinationset" :sourcesetId="sourceset" />
<q-modal
v-model="isEditing"
:minimized="!isMobile"
:maximized="isMobile"
id="timeout-modal"
>
<div class="title">
{{ $t('pages.callForward.editTimeout') }}
</div>
<q-field :error-label="errorMessage">
<q-input
v-if="ownPhone"
v-model="editTimeout"
type="number"
suffix="seconds"
:before="[{ icon: 'schedule' }]"
:float-label="$t('pages.callForward.timeout')"
@input="$v.editTimeout.$touch"
@blur="$v.editTimeout.$touch"
:error="$v.editTimeout.$error"
/>
</q-field>
<q-btn
flat
dark
@click="hideModal"
>
{{ $t('buttons.cancel') }}
</q-btn>
<q-btn
flat
color="negative"
@click="resetTimeout"
>
{{ $t('buttons.reset') }}
</q-btn>
<q-btn
flat
color="primary"
@click="updateTimeout"
icon-right="fa-save"
:disable="$v.editTimeout.$error"
>
{{ $t('buttons.save') }}
</q-btn>
</q-modal>
<csc-add-destination-form
v-bind="lastDestinationset"
:sourcesetId="sourceset"
/>
</div>
</template>
@ -23,7 +210,25 @@
import _ from 'lodash'
import CscDestination from './CscDestination'
import CscAddDestinationForm from './CscAddDestinationForm'
import { QList, QItem, QIcon } from 'quasar-framework'
import {
required,
minValue
} from 'vuelidate/lib/validators'
import {
QList,
QItem,
QItemSide,
QItemMain,
QItemTile,
QItemSeparator,
QPopover,
QIcon,
QField,
QToggle,
QInput,
QModal,
QBtn
} from 'quasar-framework'
export default {
name: 'csc-destinations',
props: [
@ -32,16 +237,90 @@
'group',
'groupName',
'timeset',
'sourceset'
'sourceset',
'showOwnPhone',
'loading',
'ownPhoneTimeout'
],
data() {
return {
timeout: null,
isEditing: false,
isMobile: this.$q.platform.is.mobile
}
},
components: {
CscDestination,
CscAddDestinationForm,
QList,
QItem,
QItemSide,
QItemMain,
QItemTile,
QItemSeparator,
QPopover,
QIcon,
CscDestination,
CscAddDestinationForm
QField,
QToggle,
QInput,
QModal,
QBtn
},
validations: {
editTimeout: {
required,
minValue: minValue(1)
}
},
computed: {
toggleIcon() {
return this.ownPhone ? 'fa-toggle-off' : 'fa-toggle-on';
},
errorMessage() {
if (!this.$v.editTimeout.minValue) {
return this.$t('validationErrors.minValueSecond', {
field: this.$t('pages.callForward.timeout'),
minValue: this.$v.editTimeout.$params.minValue.min
});
}
else if (!this.$v.editTimeout.required) {
return this.$t('validationErrors.fieldRequired', {
field: this.$t('pages.callForward.timeout')
});
}
},
editTimeout: {
get() {
if (this.ownPhoneTimeout && !this.timeout) {
return this.ownPhoneTimeout;
}
else {
return this.timeout;
}
},
set(value) {
this.timeout = value;
}
},
ownPhone() {
if (this.group.length > 0) {
return this.group[0].ownPhone;
}
else {
return false;
}
},
timesetId() {
if (this.group.length > 0) {
return this.group[0].timesetId;
}
else {
return null;
}
},
sourcesetId() {
return this.sourceset;
},
lastDestinationset() {
let destinationset = _.findLast(this.group) || {};
destinationset.groupName = this.groupName;
@ -51,6 +330,30 @@
}
},
methods: {
showModal() {
this.timeout = null,
this.isEditing = true;
},
hideModal() {
this.isEditing = false;
},
resetTimeout() {
this.$store.dispatch('callForward/updateOwnPhoneTimeout', {
timeout: 15
});
},
updateTimeout() {
this.$store.dispatch('callForward/updateOwnPhoneTimeout', {
timeout: this.timeout
});
},
toggle() {
this.$store.dispatch('callForward/updateOwnPhone', {
toggle: !this.ownPhone,
sourcesetId: this.sourcesetId,
timesetId: this.timesetId
});
},
previousDestinationsetId(index) {
let destinationset = this.group[index-1] || {};
return destinationset.id || null;
@ -65,6 +368,8 @@
<style lang="stylus" rel="stylesheet/stylus">
@import '../../../themes/quasar.variables.styl'
.csc-destination
position relative
.dest-section
padding-top 30px
@ -80,4 +385,50 @@
.q-item.csc-no-destination
margin-left 0px
padding 0
#timeout-modal
.modal-content
min-width 40vw
padding 20px 15px
.title
color $primary
line-height $csc-subtitle-line-height
font-size $csc-subtitle-font-size
font-weight $csc-subtitle-font-weight
.q-item-highlight.csc-own-phone:hover
background-color lighten($primary, 70%)
.q-tab-pane
.csc-dest-separator
margin 0
.csc-own-phone.q-item
max-height 37px
.q-item-side
padding-bottom 9px
.dest-btns
padding-top 8px
.dest-row
color $secondary
white-space nowrap
overflow hidden
font-size 16px
.dest-values
font-weight 500
.dest-row.mobile
padding 16px
padding-left 0px
.dest-values > span
font-weight 300
.dest-row.mobile .dest-sublabel span
font-weight 300
</style>

@ -433,9 +433,6 @@
.q-tab-pane
padding 12px 0 0 0
.q-item
padding 8px 0 0 0
.sourceset-add-button
margin-top 8px

@ -16,12 +16,17 @@
"add": "Add",
"moveUp": "Move up",
"moveDown": "Move down",
"dismiss": "Dismiss"
"dismiss": "Dismiss",
"reset": "Reset"
},
"toasts": {
"callAvailable": "You are now able to start and receive calls",
"callNotAvailable": "Could not initialize call functionality properly"
},
"validationErrors": {
"fieldRequired": "{field} is required",
"minValueSecond": "{field} must be at least {minValue} second"
},
"navigation": {
"home": {
"title": "Home",
@ -186,6 +191,14 @@
"timeout": "Timeout",
"destination": "Destination",
"terminatedTooltip": "This destination comes after a terminating destinations, and is therefore inactive.",
"updateOwnPhoneToggleErrorMessage": "An error occured while trying to update own phone. Please try again",
"editTimeout": "Edit timeout",
"toggleTimeout": "{mode} own phone",
"ownPhoneDisabled": "do not ring own phone",
"updateOwnPhoneToggleSuccessMessage": "Own phone is {toggle}",
"updateOwnPhoneTimeoutSuccessMessage": "Own phone timeout set to {timeout}",
"updateOwnPhoneTimeoutErrorMessage": "An error occured while trying to update own phone timeout. Please try again",
"ownPhone": "own phone",
"times": {
"removeDialogTitle": "Remove call forward time",
"removeDialogText": "You are about to remove the time entry for {day}",

@ -24,7 +24,10 @@ import {
createSourcesetWithSource,
appendSourceToSourceset,
deleteSourcesetById,
deleteSourceFromSourcesetByIndex
deleteSourceFromSourcesetByIndex,
flipCfuAndCft,
getOwnPhoneTimeout,
updateOwnPhoneTimeout
} from '../api/call-forward';
export default {
@ -83,7 +86,14 @@ export default {
timesetId: null,
activeTimeForm: false,
addSourceFormEnabled: false,
timesetTimesLoaded: false
timesetTimesLoaded: false,
updateOwnPhoneToggleState: RequestState.initial,
updateOwnPhoneToggleError: null,
lastOwnPhoneToggle: null,
lastOwnPhoneTimeout: null,
ownPhoneTimeout: null,
updateOwnPhoneTimeoutState: RequestState.initial,
updateOwnPhoneTimeoutError: null
},
getters: {
hasFaxCapability(state, getters, rootState, rootGetters) {
@ -215,6 +225,32 @@ export default {
},
lastAddedSourceset(state) {
return state.lastAddedSourceset;
},
updateOwnPhoneToggleState(state) {
return state.updateOwnPhoneToggleState;
},
updateOwnPhoneToggleError(state) {
return state.updateOwnPhoneToggleError ||
i18n.t('pages.callForward.updateOwnPhoneToggleErrorMessage');
},
isUpdating(state) {
return state.updateOwnPhoneToggleState === RequestState.requesting;
},
ownPhoneTimeout(state) {
return state.ownPhoneTimeout;
},
lastOwnPhoneToggle(state) {
return state.lastOwnPhoneToggle;
},
updateOwnPhoneTimeoutState(state) {
return state.updateOwnPhoneTimeoutState;
},
updateOwnPhoneTimeoutError(state) {
return state.updateOwnPhoneTimeoutError ||
i18n.t('pages.callForward.updateOwnPhoneTimeoutErrorMessage');
},
lastOwnPhoneTimeout(state) {
return state.lastOwnPhoneTimeout;
}
},
mutations: {
@ -438,6 +474,36 @@ export default {
},
setLastRemovedSource(state, value) {
state.lastRemovedSource = value;
},
updateOwnPhoneRequesting(state) {
state.updateOwnPhoneToggleState = RequestState.requesting;
state.updateOwnPhoneToggleError = null
},
updateOwnPhoneSucceeded(state, type) {
let toggle = type === 'cft' ? 'enabled' : 'disabled';
state.lastOwnPhoneToggle = toggle;
state.updateOwnPhoneToggleState = RequestState.succeeded;
state.updateOwnPhoneToggleError = null
},
updateOwnPhoneFailed(state, error) {
state.updateOwnPhoneToggleState = RequestState.failed;
state.updateOwnPhoneToggleError = error;
},
loadOwnPhoneTimeout(state, value) {
state.ownPhoneTimeout = value;
},
updateOwnPhoneTimeoutRequesting(state) {
state.updateOwnPhoneTimeoutState = RequestState.requesting;
state.updateOwnPhoneTimeoutError = null
},
updateOwnPhoneTimeoutSucceeded(state, timeout) {
state.lastOwnPhoneTimeout = timeout;
state.updateOwnPhoneTimeoutState = RequestState.succeeded;
state.updateOwnPhoneTimeoutError = null
},
updateOwnPhoneTimeoutFailed(state, error) {
state.updateOwnPhoneTimeoutState = RequestState.failed;
state.updateOwnPhoneTimeoutError = error;
}
},
actions: {
@ -716,6 +782,42 @@ export default {
}).catch((err) => {
context.commit('removeSourceFailed', err.message);
});
},
updateOwnPhone(context, options) {
let fromType = options.toggle ? 'cfu' : 'cft';
let toType = !options.toggle ? 'cfu' : 'cft';
context.commit('updateOwnPhoneRequesting');
flipCfuAndCft({
fromType: fromType,
toType: toType,
sourcesetId: options.sourcesetId,
timesetId: options.timesetId,
subscriberId: context.getters.subscriberId
}).then(() => {
return context.dispatch('loadOwnPhoneTimeout');
}).then(() => {
context.commit('updateOwnPhoneSucceeded', toType);
}).catch((err) => {
context.commit('updateOwnPhoneFailed', err.message);
});
},
loadOwnPhoneTimeout(context) {
getOwnPhoneTimeout(context.getters.subscriberId).then((result) => {
context.commit('loadOwnPhoneTimeout', result);
});
},
updateOwnPhoneTimeout(context, options) {
context.commit('updateOwnPhoneTimeoutRequesting');
updateOwnPhoneTimeout({
subscriberId: context.getters.subscriberId,
timeout: options.timeout
}).then(() => {
return context.dispatch('loadOwnPhoneTimeout');
}).then(() => {
context.commit('updateOwnPhoneTimeoutSucceeded', options.timeout);
}).catch((err) => {
context.commit('updateOwnPhoneTimeoutFailed', err.message);
});
}
}
};

@ -14,8 +14,7 @@ import {
convertTimesetToWeekdays,
deleteTimeFromTimeset,
convertAddTime,
addNameIdAndTerminating,
createSourcesetWithSource,
addNameIdOwnPhoneAndTerminating,
deleteItemFromArrayByIndex
} from '../../src/api/call-forward';
import { assert } from 'chai';
@ -906,7 +905,8 @@ describe('CallForward', function() {
}
],
groupName: "cfu",
timesetId: null
timesetId: null,
ownPhone: false
};
let data = [
{
@ -938,6 +938,7 @@ describe('CallForward', function() {
groupName: "cfu",
id: 3,
name: "t1",
ownPhone: false,
priority: 1,
subscriber_id: 311,
timeset: null,
@ -957,6 +958,7 @@ describe('CallForward', function() {
groupName: "cfu",
id: 5,
name: "t2",
ownPhone: false,
priority: 1,
subscriber_id: 311,
timeset: null,
@ -964,7 +966,7 @@ describe('CallForward', function() {
}
];
assert.deepEqual(addNameIdAndTerminating(options), data);
assert.deepEqual(addNameIdOwnPhoneAndTerminating(options), data);
});

Loading…
Cancel
Save