TT#28066 Add a new Source to a SourceSet

What has been done:
- TT#36187, Create a custom UI form component
- TT#36190, Implement success toast, error handling and loading
  animation
- TT#36183, Implement api layer methods, plus corresponding store
  actions and mutations

Change-Id: Icb31af6456fafeb98ac4a7ca16f1fb83ce79d0a4
changes/13/21113/4
raxelsen 8 years ago
parent bcc3fa40c0
commit afbe0a031e

@ -621,14 +621,14 @@ export function resetTimesetByName(options) {
}
export function addTimeToTimeset(options) {
let headers = {
'Content-Type': 'application/json-patch+json'
};
return new Promise((resolve, reject) => {
let headers = {
'Content-Type': 'application/json-patch+json'
};
Vue.http.patch('/api/cftimesets/' + options.id, [{
op: 'replace',
path: '/times',
value: options.time
value: options.times
}], { headers: headers }).then(() => {
resolve();
}).catch(err => {
@ -705,12 +705,12 @@ export function convertAddTime(options) {
}
export function createTimesetWithTime(options) {
let convertedTime = convertAddTime({ time: options.time[0], weekday: options.weekday });
return new Promise((resolve, reject)=> {
let convertedTime = convertAddTime({ time: options.time[0], weekday: options.weekday });
Promise.resolve().then(() => {
return addNewTimeset(options.name);
}).then((timesetId) => {
return addTimeToTimeset({ id: timesetId, time: convertedTime });
return addTimeToTimeset({ id: timesetId, times: convertedTime });
}).then(() => {
resolve();
}).catch((err) => {
@ -732,13 +732,56 @@ export function getTimesByTimesetId(id) {
}
export function appendTimeToTimeset(options) {
let convertedTime = convertAddTime({ time: options.time[0], weekday: options.weekday });
return new Promise((resolve, reject)=> {
let convertedTime = convertAddTime({ time: options.time[0], weekday: options.weekday });
Promise.resolve().then(() => {
return getTimesByTimesetId(options.id);
}).then((times) => {
let concatTimes = times.concat(convertedTime);
return addTimeToTimeset({ id: options.id, time: concatTimes });
return addTimeToTimeset({ id: options.id, times: concatTimes });
}).then(() => {
resolve();
}).catch((err) => {
reject(err);
});
});
}
export function getSourcesBySourcesetId(id) {
return new Promise((resolve, reject)=>{
Vue.http.get('/api/cfsourcesets/' + id).then((res)=>{
let sourceset = getJsonBody(res.body);
resolve(sourceset.sources);
}).catch((err)=>{
reject(err);
});
});
}
export function addSourceToSourceset(options) {
return new Promise((resolve, reject) => {
let headers = {
'Content-Type': 'application/json-patch+json'
};
Vue.http.patch('/api/cfsourcesets/' + options.id, [{
op: 'replace',
path: '/sources',
value: options.sources
}], { headers: headers }).then(() => {
resolve();
}).catch(err => {
reject(err);
});
});
}
export function appendSourceToSourceset(options) {
return new Promise((resolve, reject)=> {
Promise.resolve().then(() => {
return getSourcesBySourcesetId(options.id);
}).then((sources) => {
let concatSources = sources.concat(options.source);
return addSourceToSourceset({ id: options.id, sources: concatSources });
}).then(() => {
resolve();
}).catch((err) => {

@ -1,7 +1,12 @@
<template>
<csc-page :title="$t('pages.callForward.titles.always')">
<csc-sourcesets v-if="destinationsLoaded" :sourcesets="sourcesets"
:destinations="destinations" :timesetName="timesetName" ref="sourcesets" />
<csc-sourcesets
v-if="destinationsLoaded"
:sourcesets="sourcesets"
:destinations="destinations"
:timesetName="timesetName"
ref="sourcesets"
/>
</csc-page>
</template>

@ -1,6 +1,6 @@
<template>
<div class="add-destination-form">
<q-btn v-if="!isFormEnabled" flat color="primary" icon="fa-plus" class="add-destination-button">
<q-btn v-if="!isFormEnabled" flat color="primary" icon="fa-plus">
{{ $t('pages.callForward.addDestinationButton') }}
<q-popover ref="popover">
<q-list separator link>

@ -168,7 +168,4 @@
}
}
.add-times
width 100%
</style>

@ -1,50 +1,71 @@
<template>
<div>
<div v-if="showTimesAndDestinations">
<csc-call-forward-times :times="timesetTimes"
:timesetName="timesetName" ref="times"></csc-call-forward-times>
<csc-sourcesets v-if="destinationsLoaded" :sourcesets="sourcesets"
:destinations="destinations" :timesetName="timesetName" ref="sourcesets" />
<csc-call-forward-times
:times="timesetTimes"
:timesetName="timesetName"
ref="times"
/>
<csc-sourcesets
v-if="destinationsLoaded"
:sourcesets="sourcesets"
:destinations="destinations"
:timesetName="timesetName"
ref="sourcesets"
/>
</div>
<q-card flat>
<div v-if="timesetHasDuplicate">
<q-alert color="red"
<q-alert
color="red"
v-model="showAlertDuplicate"
icon="date_range"
:actions="[{ label: labelReset, handler: resetTimeset }]"
appear>
{{ $t('pages.callForward.times.timesetDuplicate', { timeset: timesetName }) }}
appear
>
{{ $t('pages.callForward.times.timesetDuplicate', { timeset: timesetName }) }}
</q-alert>
</div>
<div v-else-if="!timesetIsCompatible">
<q-alert color="red"
<q-alert
color="red"
v-model="showAlertCompatible"
icon="date_range"
:actions="[{ label: labelReset, handler: resetTimeset }]"
appear>
{{ $t('pages.callForward.times.timesetIncompatible', { timeset: timesetName }) }}
appear
>
{{ $t('pages.callForward.times.timesetIncompatible', { timeset: timesetName }) }}
</q-alert>
</div>
<div v-else-if="timesetHasReverse">
<q-alert color="red"
<q-alert
color="red"
v-model="showAlertReverse"
icon="date_range"
:actions="[{ label: labelReset, handler: resetTimeset }]"
appear>
{{ $t('pages.callForward.times.timesetReverse', { timeset: timesetName }) }}
appear
>
{{ $t('pages.callForward.times.timesetReverse', { timeset: timesetName }) }}
</q-alert>
</div>
<div v-show="showDefinedAlert">
<q-alert color="warning"
<q-alert
color="warning"
v-model="showAlertDefined"
icon="date_range"
:actions="[{ label: labelAdd, handler: addTimeset }]"
appear>
{{ $t('pages.callForward.times.timesetNotDefined', { timeset: timesetName }) }}
appear
>
{{ $t('pages.callForward.times.timesetNotDefined', { timeset: timesetName }) }}
</q-alert>
</div>
<csc-add-time-form v-if="activeTimeForm && !timesetExists" type="new"
:title="getAddLabel" :timeset="timesetName" ref="addTimeNew" />
<csc-add-time-form
v-if="activeTimeForm && !timesetExists"
type="new"
:title="getAddLabel"
:timeset="timesetName"
ref="addTimeNew"
/>
</q-card>
</div>
</template>

@ -1,32 +1,77 @@
<template>
<q-tabs v-model="tab" no-pane-border inverted class="sourceset-tabs">
<q-tab v-for="(sourceset, index) in destinations" :default="index === 0"
<q-tabs
v-model="tab"
no-pane-border
inverted
class="sourceset-tabs"
>
<q-tab
v-for="(sourceset, index) in destinations"
:default="index === 0"
:count="destinationsCount(sourceset.destinationGroups)"
:key="sourceset.sourcesetId || 0" slot="title"
:name="sourceset.sourcesetName || 'Everybody'" icon="people"
:label="sourceset.sourcesetName || 'Everybody'" />
<q-tab slot="title" label="Add new" name="addnew" icon="fa-plus" />
<q-tab-pane v-for="sourceset in destinations"
:key="sourceset.sourcesetId || 0"
:name="sourceset.sourcesetName || 'Everybody'" class="sourceset-pane">
<div class="sources-section" v-if="sourceset.sourcesetId">
<div class="sources-title">
<q-icon name="contact_phone" class="sources-icon" />
{{ $t('pages.callForward.sources.sourcesTitleMode',
{ mode: capitalizedMode(sourceset.sourcesetMode) }) }}
</div>
<q-list no-border>
<q-item highlight separator
v-for="source in sourcesetSources(sourceset.sourcesetId)"
class="source-item">
{{ source.source }}
</q-item>
</q-list>
:name="sourceset.sourcesetName || 'Everybody'"
:label="sourceset.sourcesetName || 'Everybody'"
icon="people"
slot="title"
/>
<q-tab
slot="title"
label="Add new"
name="addnew"
icon="fa-plus"
/>
<q-tab-pane
v-for="sourceset in destinations"
:key="sourceset.sourcesetId || 0"
:name="sourceset.sourcesetName || 'Everybody'"
class="sourceset-pane"
>
<div
class="sources-section"
v-if="sourceset.sourcesetId"
>
<div class="sources-title">
<q-icon
name="contact_phone"
class="sources-icon"
/>
{{ $t('pages.callForward.sources.sourcesTitleMode',
{ mode: capitalizedMode(sourceset.sourcesetMode) }) }}
</div>
<csc-call-forward-destinations
:sourceset="sourceset.sourcesetId"
:timeset="timesetName"
:destinations="sourceset.destinationGroups" />
<q-list no-border>
<q-item
v-for="source in sourcesetSources(sourceset.sourcesetId)"
class="source-item"
highlight
separator
>
{{ source.source }}
</q-item>
</q-list>
<q-btn
v-if="!sourcesetsFormEnabled"
flat
color="primary"
icon="fa-plus"
@click="openForm()"
>
{{ $t('pages.callForward.sources.addSourceButton') }}
</q-btn>
<csc-sourcesets-form
v-if="sourcesetsFormEnabled"
:sourceset-id="sourceset.sourcesetId"
:form-enabled="addSourceFormEnabled"
@add-source="addSource"
@source-form-close="closeForm"
ref="sourcesetsForm"
/>
</div>
<csc-call-forward-destinations
:sourceset="sourceset.sourcesetId"
:timeset="timesetName"
:destinations="sourceset.destinationGroups"
/>
</q-tab-pane>
<q-tab-pane name="addnew">
<q-list no-border>
@ -73,27 +118,34 @@
<script>
import CscCallForwardDestinations from './CscCallForwardDestinations'
import { showGlobalError } from '../../../helpers/ui'
import CscSourcesetsForm from './CscSourcesetsForm'
import { mapGetters } from 'vuex'
import {
startLoading,
stopLoading,
showGlobalError,
showToast
} from '../../../helpers/ui'
import {
QTabs,
QTab,
QTabPane,
QBtn,
QList,
QItem,
QItemMain,
QItemTile,
QInput,
QIcon,
QSelect
QSelect,
QBtn
} from 'quasar-framework'
export default {
name: 'csc-sourcesets',
props: [
'destinations',
'sourcesets',
'timesetName'
],
props: {
destinations: Object,
sourcesets: Object,
timesetName: [String, Object]
},
data() {
return {
sourcesetName: '',
@ -109,24 +161,32 @@
value: 'blacklist'
}
],
tab: 'Everybody'
tab: 'Everybody',
sourcesetsFormEnabled: false
}
},
components: {
CscCallForwardDestinations,
CscSourcesetsForm,
QTabs,
QTab,
QTabPane,
QBtn,
QList,
QItem,
QItemMain,
QItemTile,
QInput,
QIcon,
QSelect
QSelect,
QBtn
},
computed: {
...mapGetters('callForward', [
'addSourceState',
'addSourceError',
'lastAddedSource',
'addSourceFormEnabled'
]),
isValid() {
return this.source.length > 0 && this.sourcesetName.length > 0;
}
@ -180,6 +240,38 @@
else {
showGlobalError(this.$t('pages.callForward.sources.fieldMissing'));
}
},
openForm() {
this.sourcesetsFormEnabled = true;
},
closeForm() {
this.$refs.sourcesetsForm[0].resetForm();
this.sourcesetsFormEnabled = false;
},
addSource(options) {
this.$store.dispatch('callForward/appendSourceToSourceset', options);
}
},
watch: {
addSourceState(state) {
if (state === 'requesting') {
startLoading();
}
else if (state === 'failed') {
stopLoading();
showGlobalError(this.addSourceError);
}
else if (state === 'succeeded') {
stopLoading();
showToast(this.$t('pages.callForward.sources.addSourceSuccessMessage', {
source: this.lastAddedSource
}));
this.$store.dispatch('callForward/loadSourcesets');
this.$store.dispatch('callForward/loadDestinations', {
timeset: this.timesetName
});
this.closeForm();
}
}
}
}
@ -212,4 +304,7 @@
color $secondary
font-size 16px
.sources-icon
margin-right 5px
</style>

@ -0,0 +1,74 @@
<template>
<div class="add-source-form">
<q-field>
<q-input
autofocus
v-model="source"
:float-label="$t('pages.callForward.sources.source')"
color="primary"
@keyup.enter="addSource()" />
</q-field>
<q-btn
flat
dark
@click="disableForm()"
>
{{ $t('buttons.cancel') }}
</q-btn>
<q-btn
flat
color="primary"
icon-right="fa-save"
@click="addSource()"
:disable="!isValid"
>
{{ $t('buttons.save') }}
</q-btn>
</div>
</template>
<script>
import {
QBtn,
QField,
QInput
} from 'quasar-framework'
export default {
name: 'csc-sourcesets-form',
props: {
sourcesetId: String
},
data() {
return {
source: ''
}
},
components: {
QBtn,
QField,
QInput
},
computed: {
isValid() {
return this.source.length > 0;
}
},
methods: {
disableForm() {
this.$emit('source-form-close');
},
addSource() {
this.$emit('add-source', {
source: [{ source: this.source}],
id: this.sourcesetId
});
},
resetForm() {
this.source = '';
}
}
}
</script>
<style>
</style>

@ -198,9 +198,11 @@
"sourceset": "Sourceset",
"source": "Source",
"fieldMissing": "Both sourceset name and source is required. Please provide both and try again.",
"addSourcesetErrorMessage": "An error occured while trying to create the sourceset. Please try again."
"addSourcesetErrorMessage": "An error occured while trying to create the sourceset. Please try again.",
"addSourceButton": "Add source",
"addSourceSuccessMessage": "Added new source {source}",
"addSourceErrorMessage": "An error occured while trying to add the source. Please try again."
}
},
"home": {
"title": "Home",

@ -22,7 +22,8 @@ import {
createTimesetWithTime,
appendTimeToTimeset,
loadDestinations,
createSourcesetWithSource
createSourcesetWithSource,
appendSourceToSourceset
} from '../api/call-forward';
const RequestState = {
@ -53,6 +54,7 @@ export default {
changeDestinationError: null,
removeTimeState: RequestState.button,
removeTimeError: null,
lastRemovedDay: null,
resetTimeState: RequestState.button,
resetTimeError: null,
addTimeState: RequestState.button,
@ -60,7 +62,9 @@ export default {
addSourcesetState: RequestState.button,
addSourcesetError: null,
lastAddedSourceset: null,
lastRemovedDay: null,
addSourceState: RequestState.button,
addSourceError: null,
lastAddedSource: null,
activeForm: '',
formType: '',
destinationsetId: '',
@ -77,7 +81,8 @@ export default {
timesetHasReverse: false,
timesetHasDuplicate: false,
timesetId: null,
activeTimeForm: false
activeTimeForm: false,
addSourceFormEnabled: false
},
getters: {
hasFaxCapability(state, getters, rootState, rootGetters) {
@ -134,6 +139,19 @@ export default {
addSourcesetError(state) {
return state.addSourcesetError ||
i18n.t('pages.callForward.sources.addSourcesetErrorMessage');
},
addSourceError(state) {
return state.addSourceError ||
i18n.t('pages.callForward.sources.addSourceErrorMessage');
},
addSourceState(state) {
return state.addSourceState;
},
lastAddedSource(state) {
return state.lastAddedSource;
},
addSourceFormEnabled(state) {
return state.addSourceFormEnabled;
}
},
mutations: {
@ -313,6 +331,24 @@ export default {
},
setLastAddedSourceset(state, value) {
state.lastAddedSourceset = value;
},
addSourceRequesting(state) {
state.addSourceState = RequestState.requesting;
state.addSourceError = null;
},
addSourceSucceeded(state) {
state.addSourceState = RequestState.succeeded;
state.addSourceError = null;
},
addSourceFailed(state, error) {
state.addSourceState = RequestState.failed;
state.addSourceError = error;
},
setLastAddedSource(state, value) {
state.lastAddedSource = value;
},
setAddSourceFormEnabled(state, value) {
state.addSourceFormEnabled = value;
}
},
actions: {
@ -552,8 +588,7 @@ export default {
appendTimeToTimeset({
time: options.time,
weekday: options.weekday,
id: context.getters.getTimesetId,
subscriberId: context.getters.getSubscriberId
id: context.getters.getTimesetId
}).then(() => {
context.commit('addTimeSucceeded');
}).catch((err) => {
@ -585,6 +620,15 @@ export default {
}).catch((err) => {
context.commit('addSourcesetFailed', err.message);
});
},
appendSourceToSourceset(context, options) {
context.commit('addSourceRequesting');
appendSourceToSourceset(options).then(() => {
context.commit('setLastAddedSource', options.source[0].source);
context.commit('addSourceSucceeded');
}).catch((err) => {
context.commit('addSourceFailed', err.message);
});
}
}
};

Loading…
Cancel
Save