-
+
{{ $t('buttons.cancel') }}
{{ $t('buttons.save') }}
@@ -43,7 +43,7 @@
{{ number }}
-
{{ $t('buttons.save') }}
diff --git a/src/components/pages/Conversations/Conversations.vue b/src/components/pages/Conversations/Conversations.vue
index 25491f68..f1d71ea0 100644
--- a/src/components/pages/Conversations/Conversations.vue
+++ b/src/components/pages/Conversations/Conversations.vue
@@ -154,7 +154,7 @@
directives: {
BackToTop
},
- mounted() {
+ created() {
this.$store.commit('conversations/resetList');
},
inject: ['layout'],
diff --git a/src/components/pages/Conversations/CscVoiceMailItem.vue b/src/components/pages/Conversations/CscVoiceMailItem.vue
index 1153dbe1..ffda1b9c 100644
--- a/src/components/pages/Conversations/CscVoiceMailItem.vue
+++ b/src/components/pages/Conversations/CscVoiceMailItem.vue
@@ -28,9 +28,9 @@
- {{ t$('pages.conversations.duration') }}
+ {{ $t('pages.conversations.duration') }}
{{ voiceMail.duration }}
- {{ t$('pages.conversations.seconds') }}
+ {{ $t('pages.conversations.seconds') }}
diff --git a/src/components/pages/Voicebox/CscVoiceboxSettings.vue b/src/components/pages/Voicebox/CscVoiceboxSettings.vue
new file mode 100644
index 00000000..dc4b7573
--- /dev/null
+++ b/src/components/pages/Voicebox/CscVoiceboxSettings.vue
@@ -0,0 +1,202 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/pages/Voicebox/Voicebox.vue b/src/components/pages/Voicebox/Voicebox.vue
new file mode 100644
index 00000000..5ad79c51
--- /dev/null
+++ b/src/components/pages/Voicebox/Voicebox.vue
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/locales/en.json b/src/locales/en.json
index 87affda5..c51e4c7a 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -33,7 +33,8 @@
"alphaNum": "{field} must consist of numeric characters only",
"inputNumber": "Input a phone number",
"inputValidNumber": "Input a valid phone number",
- "fieldRequiredXor": "{fieldOne} or {fieldTwo} is required"
+ "fieldRequiredXor": "{fieldOne} or {fieldTwo} is required",
+ "email": "Input a valid email address"
},
"navigation": {
"home": {
@@ -72,6 +73,10 @@
"groups": "Groups",
"seats": "Seats",
"devices": "Devices"
+ },
+ "voicebox": {
+ "title": "Voicebox",
+ "subTitle": "Set your voicebox settings"
}
},
"pages": {
@@ -382,7 +387,8 @@
"privacyEnabledToast": "Your number is hidden to the callee",
"privacyEnabledLabel": "Your number is hidden to the callee",
"privacyDisabledToast": "Your number is visible to the callee",
- "privacyDisabledLabel": "Your number is visible to the callee"
+ "privacyDisabledLabel": "Your number is visible to the callee",
+ "number": "Number"
},
"communication": {
"sendFax": "Send Fax",
@@ -420,5 +426,25 @@
"addNoSlotsDialogText": "All available speed dial slots have already been assigned. Please delete one first.",
"assignSlotErrorMessage": "An error occured while trying to assign the speed dial slot. Please try again.",
"assignSlotSuccessMessage": "Assigned slot {slot}"
+ },
+ "voicebox": {
+ "label": {
+ "changeEmail": "Change Email",
+ "changePin": "Change PIN",
+ "deletionEnabled": "Voicemail will be deleted after email notification is delivered",
+ "deletionDisabled": "Voicemail will not be deleted after email notification is delivered",
+ "attachmentEnabled": "Voicemail will be attached to email notification",
+ "attachmentDisabled": "Voicemail will not be attached to email notification"
+ },
+ "pin": "PIN",
+ "loadSettingsErrorMessage": "An error occured while trying to load the settings. Please reload the page or check your network connection.",
+ "toggleDeleteSuccessMessage": "Toggled deletion successfully.",
+ "toggleDeleteErrorMessage": "An error occured while trying to toggle the delete option. Please try again.",
+ "toggleAttachSuccessMessage": "Toggled attachment successfully.",
+ "toggleAttachErrorMessage": "An error occured while trying to toggle the attach option. Please try again.",
+ "updatePinSuccessMessage": "Changed PIN successfully.",
+ "updatePinErrorMessage": "An error occured while trying to update the pin field. Please try again.",
+ "updateEmailSuccessMessage": "Changed email successfully.",
+ "updateEmailErrorMessage": "An error occured while trying to update the email field. Please try again."
}
}
diff --git a/src/routes.js b/src/routes.js
index 39bb2848..23f4a80f 100644
--- a/src/routes.js
+++ b/src/routes.js
@@ -14,6 +14,7 @@ import SpeedDial from './components/pages/SpeedDial/SpeedDial'
import PbxConfigurationGroups from './components/pages/PbxConfiguration/CscPbxGroups'
import PbxConfigurationSeats from './components/pages/PbxConfiguration/CscPbxSeats'
import PbxConfigurationDevices from './components/pages/PbxConfiguration/CscPbxDevices'
+import Voicebox from './components/pages/Voicebox/Voicebox';
import Login from './components/Login'
import Error404 from './components/Error404'
@@ -90,14 +91,16 @@ export default [
path: 'reminder',
component: Reminder,
meta: {
- title: i18n.t('navigation.reminder.title')
+ title: i18n.t('navigation.reminder.title'),
+ subtitle: i18n.t('navigation.reminder.subTitle')
}
},
{
path: 'speeddial',
component: SpeedDial,
meta: {
- title: i18n.t('navigation.speeddial.title')
+ title: i18n.t('navigation.speeddial.title'),
+ subtitle: i18n.t('navigation.speeddial.subTitle')
}
},
{
@@ -123,6 +126,14 @@ export default [
title: i18n.t('navigation.pbxConfiguration.title'),
subtitle: i18n.t('navigation.pbxConfiguration.devices')
}
+ },
+ {
+ path: 'voicebox',
+ component: Voicebox,
+ meta: {
+ title: i18n.t('navigation.voicebox.title'),
+ subtitle: i18n.t('navigation.voicebox.subTitle')
+ }
}
]
},
diff --git a/src/store/index.js b/src/store/index.js
index a6810c5f..4d5f44ce 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -13,6 +13,7 @@ import ReminderModule from './reminder'
import SpeedDialModule from './speed-dial'
import UserModule from './user'
import CommunicationModule from './communication'
+import VoiceboxModule from './voicebox'
Vue.use(Vuex);
@@ -27,7 +28,8 @@ export const store = new Vuex.Store({
reminder: ReminderModule,
speedDial: SpeedDialModule,
user: UserModule,
- communication: CommunicationModule
+ communication: CommunicationModule,
+ voicebox: VoiceboxModule
},
getters: {
pageTitle(state) {
diff --git a/src/store/reminder.js b/src/store/reminder.js
index 7e6917a8..89419da3 100644
--- a/src/store/reminder.js
+++ b/src/store/reminder.js
@@ -10,7 +10,6 @@ import {
setReminderRecurrence
} from '../api/reminder';
-
export default {
namespaced: true,
state: {
diff --git a/src/store/voicebox.js b/src/store/voicebox.js
new file mode 100644
index 00000000..ebe8b6ec
--- /dev/null
+++ b/src/store/voicebox.js
@@ -0,0 +1,234 @@
+
+'use strict';
+
+import _ from 'lodash'
+import { RequestState } from './common'
+import {
+ getVoiceboxSettings,
+ setVoiceboxDelete,
+ setVoiceboxAttach,
+ setVoiceboxPin,
+ setVoiceboxEmail
+} from '../api/voicebox';
+import { i18n } from '../i18n';
+
+export default {
+ namespaced: true,
+ state: {
+ voiceboxSettings: {
+ attach: null,
+ delete: null,
+ email: '',
+ id: null,
+ pin: null,
+ sms_number: ''
+ },
+ loadingState: RequestState.initial,
+ loadingError: null,
+ toggleDeleteState: RequestState.initial,
+ toggleDeleteError: null,
+ toggleAttachState: RequestState.initial,
+ toggleAttachError: null,
+ updatePinState: RequestState.initial,
+ updatePinError: null,
+ updateEmailState: RequestState.initial,
+ updateEmailError: null
+ },
+ getters: {
+ subscriberId(state, getters, rootState, rootGetters) {
+ return parseInt(rootGetters['user/getSubscriberId']);
+ },
+ isSettingsLoaded(state) {
+ return state.loadingState === 'succeeded';
+ },
+ isDeleteRequesting(state) {
+ return state.toggleDeleteState === 'requesting';
+ },
+ isAttachRequesting(state) {
+ return state.toggleAttachState === 'requesting';
+ },
+ isPinRequesting(state) {
+ return state.updatePinState === 'requesting';
+ },
+ isEmailRequesting(state) {
+ return state.updateEmailState === 'requesting';
+ },
+ loadingState(state) {
+ return state.loadingState;
+ },
+ loadingError(state) {
+ return state.loadingError ||
+ i18n.t('voicebox.loadSettingsErrorMessage');
+ },
+ voiceboxDelete(state) {
+ return _.get(state.voiceboxSettings, 'delete', false);
+ },
+ voiceboxAttach(state) {
+ return _.get(state.voiceboxSettings, 'attach', false);
+ },
+ deleteLabel(state) {
+ return state.voiceboxSettings.delete ?
+ i18n.t('voicebox.label.deletionEnabled') :
+ i18n.t('voicebox.label.deletionDisabled');
+ },
+ attachLabel(state) {
+ return state.voiceboxSettings.attach ?
+ i18n.t('voicebox.label.attachmentEnabled') :
+ i18n.t('voicebox.label.attachmentDisabled');
+ },
+ voiceboxSettings(state) {
+ return state.voiceboxSettings;
+ },
+ toggleDeleteState(state) {
+ return state.toggleDeleteState;
+ },
+ toggleDeleteError(state) {
+ return state.toggleDeleteError ||
+ i18n.t('voicebox.toggleDeleteErrorMessage');
+ },
+ toggleAttachState(state) {
+ return state.toggleAttachState;
+ },
+ toggleAttachError(state) {
+ return state.toggleAttachError ||
+ i18n.t('voicebox.toggleAttachErrorMessage');
+ },
+ updatePinState(state) {
+ return state.updatePinState;
+ },
+ updatePinError(state) {
+ return state.updatePinError ||
+ i18n.t('voicebox.updatePinErrorMessage');
+ },
+ updateEmailState(state) {
+ return state.updateEmailState;
+ },
+ updateEmailError(state) {
+ return state.updateEmailError ||
+ i18n.t('voicebox.updateEmailErrorMessage');
+ }
+ },
+ mutations: {
+ loadingRequesting(state) {
+ state.loadingState = RequestState.requesting;
+ state.loadingError = null;
+ },
+ loadingSucceeded(state, settings) {
+ state.loadingState = RequestState.succeeded;
+ state.voiceboxSettings = settings;
+ state.loadingError = null;
+ },
+ loadingFailed(state, error) {
+ state.loadingState = RequestState.failed;
+ state.loadingError = error;
+ },
+ toggleDeleteRequesting(state) {
+ state.toggleDeleteState = RequestState.requesting;
+ state.toggleDeleteError = null;
+ },
+ toggleDeleteSucceeded(state) {
+ state.toggleDeleteState = RequestState.succeeded;
+ state.toggleDeleteError = null;
+ },
+ toggleDeleteFailed(state, error) {
+ state.toggleDeleteState = RequestState.failed;
+ state.toggleDeleteError = error;
+ },
+ toggleAttachRequesting(state) {
+ state.toggleAttachState = RequestState.requesting;
+ state.toggleAttachError = null;
+ },
+ toggleAttachSucceeded(state) {
+ state.toggleAttachState = RequestState.succeeded;
+ state.toggleAttachError = null;
+ },
+ toggleAttachFailed(state, error) {
+ state.toggleAttachState = RequestState.failed;
+ state.toggleAttachError = error;
+ },
+ updatePinRequesting(state) {
+ state.updatePinState = RequestState.requesting;
+ state.updatePinError = null;
+ },
+ updatePinSucceeded(state) {
+ state.updatePinState = RequestState.succeeded;
+ state.updatePinError = null;
+ },
+ updatePinFailed(state, error) {
+ state.updatePinState = RequestState.failed;
+ state.updatePinError = error;
+ },
+ updateEmailRequesting(state) {
+ state.updateEmailState = RequestState.requesting;
+ state.updateEmailError = null;
+ },
+ updateEmailSucceeded(state) {
+ state.updateEmailState = RequestState.succeeded;
+ state.updateEmailError = null;
+ },
+ updateEmailFailed(state, error) {
+ state.updateEmailState = RequestState.failed;
+ state.updateEmailError = error;
+ }
+ },
+ actions: {
+ getVoiceboxSettings(context) {
+ context.commit('loadingRequesting');
+ getVoiceboxSettings(context.getters.subscriberId).then((settings) => {
+ context.commit('loadingSucceeded', settings);
+ }).catch((err) => {
+ context.commit('loadingFailed', err.message);
+ })
+ },
+ toggleDelete(context) {
+ context.commit('toggleDeleteRequesting');
+ setVoiceboxDelete({
+ subscriberId: context.getters.subscriberId,
+ value: !context.getters.voiceboxDelete
+ }).then(() => {
+ context.commit('toggleDeleteSucceeded');
+ context.dispatch('getVoiceboxSettings');
+ }).catch((err) => {
+ context.commit('toggleDeleteFailed', err.message);
+ context.dispatch('getVoiceboxSettings');
+ });
+ },
+ toggleAttach(context) {
+ context.commit('toggleAttachRequesting');
+ setVoiceboxAttach({
+ subscriberId: context.getters.subscriberId,
+ value: !context.getters.voiceboxAttach
+ }).then(() => {
+ context.commit('toggleAttachSucceeded');
+ context.dispatch('getVoiceboxSettings');
+ }).catch((err) => {
+ context.commit('toggleAttachFailed', err.message);
+ context.dispatch('getVoiceboxSettings');
+ });
+ },
+ updatePin(context, value) {
+ context.commit('updatePinRequesting');
+ setVoiceboxPin({
+ subscriberId: context.getters.subscriberId,
+ value: value
+ }).then(() => {
+ context.commit('updatePinSucceeded');
+ context.dispatch('getVoiceboxSettings');
+ }).catch((err) => {
+ context.commit('updatePinFailed', err.message);
+ });
+ },
+ updateEmail(context, value) {
+ context.commit('updateEmailRequesting');
+ setVoiceboxEmail({
+ subscriberId: context.getters.subscriberId,
+ value: value
+ }).then(() => {
+ context.commit('updateEmailSucceeded');
+ context.dispatch('getVoiceboxSettings');
+ }).catch((err) => {
+ context.commit('updateEmailFailed', err.message);
+ });
+ }
+ }
+};
diff --git a/src/themes/app.common.styl b/src/themes/app.common.styl
index ed76da68..d9bd22b5 100644
--- a/src/themes/app.common.styl
+++ b/src/themes/app.common.styl
@@ -112,3 +112,8 @@
padding-left 8px
padding-right 8px
+.csc-form-field
+ margin-bottom 40px
+
+ .q-field-icon
+ color $primary
diff --git a/t/api/voicebox.js b/t/api/voicebox.js
new file mode 100644
index 00000000..811b19cf
--- /dev/null
+++ b/t/api/voicebox.js
@@ -0,0 +1,80 @@
+
+'use strict';
+
+import Vue from 'vue';
+import VueResource from 'vue-resource';
+import {
+ get
+} from '../../src/api/common';
+import {
+ getVoiceboxSettings
+} from '../../src/api/voicebox';
+import { assert } from 'chai';
+
+Vue.use(VueResource);
+
+describe('Voicebox', function(){
+
+ const subscriberId = 123;
+
+ it('should get subscriber\'s voicebox settings', function(done){
+
+ let data = {
+ "_links" : {
+ "collection" : {
+ "href" : "/api/voicemailsettings/"
+ },
+ "curies" : {
+ "href" : "http://purl.org/sipwise/ngcp-api/#rel-{rel}",
+ "name" : "ngcp",
+ "templated" : true
+ },
+ "ngcp:journal" : [
+ {
+ "href" : "/api/voicemailsettings/123/journal/"
+ }
+ ],
+ "ngcp:subscribers" : [
+ {
+ "href" : "/api/subscribers/123"
+ }
+ ],
+ "profile" : {
+ "href" : "http://purl.org/sipwise/ngcp-api/"
+ },
+ "self" : {
+ "href" : "/api/voicemailsettings/123"
+ }
+ },
+ "attach" : true,
+ "delete" : false,
+ "email" : "",
+ "id" : 123,
+ "pin" : "1234",
+ "sms_number" : ""
+ };
+
+ let settings = {
+ "attach" : true,
+ "delete" : false,
+ "email" : "",
+ "id" : 123,
+ "pin" : "1234",
+ "sms_number" : ""
+ };
+
+ Vue.http.interceptors = [];
+ Vue.http.interceptors.unshift((request, next)=>{
+ next(request.respondWith(JSON.stringify(data), {
+ status: 200
+ }));
+ });
+ getVoiceboxSettings(subscriberId).then((result)=>{
+ assert.deepEqual(result, settings);
+ done();
+ }).catch((err)=>{
+ done(err);
+ });
+ });
+
+});
diff --git a/t/store/voicebox.js b/t/store/voicebox.js
new file mode 100644
index 00000000..40e36b22
--- /dev/null
+++ b/t/store/voicebox.js
@@ -0,0 +1,32 @@
+
+'use strict';
+
+import VoiceboxModule from '../../src/store/voicebox';
+import { assert } from 'chai';
+
+describe('Voicebox', function(){
+
+ it('should load all voicebox settings into store', function(){
+ let state = {
+ voiceboxSettings: {
+ attach: null,
+ delete: null,
+ email: '',
+ id: null,
+ pin: null,
+ sms_number: ''
+ }
+ };
+ let settings = {
+ attach: true,
+ delete: false,
+ email: '',
+ id: 123,
+ pin: 1234,
+ sms_number: ''
+ };
+ VoiceboxModule.mutations.loadingSucceeded(state, settings);
+ assert.deepEqual(state.voiceboxSettings, settings);
+ });
+
+});