TT#38820 PBXConfig: As a Customer, I want to be able to assign groups and seats to keys coming from multiple line ranges

Change-Id: Ia016912470b33e5a28ccacff15c637a1ce7fd1a5
changes/44/22244/2
Hans-Peter Herzog 7 years ago
parent af8602d92b
commit 9c1f6ae2e5

14
npm-shrinkwrap.json generated

@ -1,6 +1,6 @@
{
"name": "ngcp-csc-ui",
"version": "0.2.1",
"version": "0.3.1",
"dependencies": {
"accepts": {
"version": "1.3.4",
@ -659,6 +659,12 @@
"resolved": "https://npm-registry.sipwise.com/better-assert/-/better-assert-1.0.2.tgz",
"dev": true
},
"bezier-easing": {
"version": "2.1.0",
"from": "bezier-easing@>=2.0.3 <3.0.0",
"resolved": "https://npm-registry.sipwise.com/bezier-easing/-/bezier-easing-2.1.0.tgz",
"dev": true
},
"big.js": {
"version": "3.2.0",
"from": "big.js@>=3.1.3 <4.0.0",
@ -6147,6 +6153,12 @@
"from": "vue-router@>=2.5.0 <3.0.0",
"resolved": "https://npm-registry.sipwise.com/vue-router/-/vue-router-2.8.1.tgz"
},
"vue-scrollto": {
"version": "2.11.0",
"from": "vue-scrollto@latest",
"resolved": "https://npm-registry.sipwise.com/vue-scrollto/-/vue-scrollto-2.11.0.tgz",
"dev": true
},
"vue-style-loader": {
"version": "3.0.3",
"from": "vue-style-loader@>=3.0.3 <4.0.0",

@ -78,6 +78,7 @@
"stylus-loader": "^3.0.1",
"url-loader": "^0.5.7",
"vue-loader": "^13.0.5",
"vue-scrollto": "2.11.0",
"vue-style-loader": "^3.0.3",
"vue-template-compiler": "^2.5.0",
"webpack": "^3.6.0",

@ -64,6 +64,7 @@
@select="selectProfile"
/>
</q-field>
<csc-pbx-device-config
:device="device"
:groupsAndSeatsOptions="groupsAndSeatsOptions"
@ -72,6 +73,7 @@
@keysChanged="keysChanged"
:subscribers="subscribers"
/>
</q-item-tile>
</q-item-main>
<q-item-side
@ -260,7 +262,7 @@
return buttons;
},
profileId() {
return _.get(this.device, 'profile.device_id', null);
return _.get(this.device, 'profile.id', null);
}
},
mounted() {
@ -329,8 +331,11 @@
right 10px
.q-item-avatar
overflow hidden
border-radius 0
img
border-radius 0
height auto
.q-item-label
font-size 18px
@ -342,4 +347,7 @@
.q-btn
padding-left 8px
padding-right 8px
.csc-pbx-device-buttons
margin-top 32px
</style>

@ -1,33 +1,97 @@
<template>
<div ref="config" class="csc-pbx-device-config justify-center row">
<div class="csc-pbx-device-canvas" :style="canvasStyles">
<div ref="imageWrapper" class="csc-pbx-device-image" :style="imageWrapperStyles">
<img ref="image" :src="imageUrl" @load="imageLoaded" :style="imageStyles" />
<div
ref="config"
class="csc-pbx-device-config justify-center row"
>
<div
class="csc-pbx-device-canvas"
:style="canvasStyles"
>
<div
ref="imageWrapper"
class="csc-pbx-device-image"
:style="imageWrapperStyles"
>
<img
ref="image"
:src="imageUrl"
:style="imageStyles"
@load="imageLoaded"s
/>
</div>
<div :class="spotClasses(index)" v-for="(key, index) in keys" :key="index"
:style="spotPosition(key)" @click="openKeyOverlay(key, index)">{{ index + 1 }}</div>
<div
v-for="(key, index) in keys"
:key="index"
:class="spotClasses(key)"
:style="spotPosition(key)"
@click="openKeyOverlay(key)"
>
{{ key.index + 1 }}
</div>
<div v-show="keyOverlayActive" class="csc-pbx-device-config-key-overlay animate-fade">
<div class="title">
<q-icon name="touch_app" size="32px"/>Key {{ selectedKeyIndex + 1 }}
</div>
<q-field :label="selectedKeyLabel" :icon="selectedKeyIcon">
<q-select ref="selectSubscriber" :value="selectedKeySubscriber" :options="groupsAndSeatsOptions"
@change="keySubscriberChanged" />
<div
v-show="keyOverlayActive"
class="csc-pbx-device-config-key-overlay animate-fade"
>
<div
class="title"
>
<q-icon
name="touch_app"
size="32px"
/>
{{ selectedKeySetName }} > Key {{ selectedKeyNumber }}
</div>
<q-field
:label="selectedKeyLabel"
:icon="selectedKeyIcon"
>
<q-select
ref="selectSubscriber"
:value="selectedKeySubscriber"
:options="groupsAndSeatsOptions"
@change="keySubscriberChanged"
/>
</q-field>
<q-field label="Type">
<q-select ref="selectType" v-model="selectedKeyType" :options="typeOptions"
@change="keyTypeChanged" />
<q-field
label="Type"
>
<q-select
ref="selectType"
v-model="selectedKeyType"
:options="typeOptions"
@change="keyTypeChanged"
/>
</q-field>
<div class="row justify-center actions">
<div class="column">
<q-btn icon="clear" :big="isMobile" @click="closeKeyOverlay()" flat color="negative">Close</q-btn>
<div
class="row justify-center actions"
>
<div
class="column"
>
<q-btn
icon="clear"
:big="isMobile"
@click="closeKeyOverlay()"
flat
color="negative"
>
Close
</q-btn>
</div>
</div>
<q-btn icon="clear" :big="isMobile" class="absolute-top-right"
@click="closeKeyOverlay()" flat color="primary" />
<q-btn
icon="clear"
:big="isMobile"
class="absolute-top-right"
@click="closeKeyOverlay()"
flat
color="primary"
/>
</div>
<q-window-resize-observable @resize="windowResize" />
<q-window-resize-observable
@resize="windowResize"
/>
</div>
</template>
@ -58,9 +122,7 @@
imageWidth: 0,
boundingBox: null,
scaledBoundingBox: null,
modalOpened: false,
selectedKey: null,
selectedKeyIndex: null,
selectedLine: null,
keyOverlayActive: false,
selectedKeyTypeData: null,
@ -115,27 +177,45 @@
if(this.selectedLine !== null) {
return this.selectedLine.type;
}
return 'private';
return _.get(this.typeOptions, '0.value', '');
},
set(type) {
this.selectedKeyTypeData = type;
}
},
typeOptions() {
return [
{
label: this.$t('pbxConfig.keyTypeShared'),
value: 'shared'
selectedKeySetName() {
if(this.selectedKey !== null) {
return this.selectedKey.keySet.name;
}
return '';
},
selectedKeyNumber() {
if(this.selectedKey !== null) {
return (this.selectedKey.index + 1);
}
return '';
},
{
typeOptions() {
let options = [];
if(this.selectedKey !== null && this.selectedKey.keySet.can_blf) {
options.push({
label: this.$t('pbxConfig.keyTypeBLF'),
value: 'blf'
},
{
});
}
if (this.selectedKey !== null && this.selectedKey.keySet.can_private) {
options.push({
label: this.$t('pbxConfig.keyTypePrivate'),
value: 'private'
});
}
if (this.selectedKey !== null && this.selectedKey.keySet.can_shared) {
options.push({
label: this.$t('pbxConfig.keyTypeShared'),
value: 'shared'
});
}
];
return options;
},
isMobile() {
return Platform.is.mobile;
@ -143,8 +223,24 @@
imageUrl() {
return _.get(this.device, 'profile.modelFrontImageUrl');
},
keySets() {
return _.get(this.device, 'profile.model.linerange', []);
},
keys() {
return _.get(this.device, 'profile.model.linerange[0].keys', []);
let keys = [];
this.keySets.forEach(($keySet)=>{
let $keys = _.get($keySet, 'keys', []);
$keys.forEach(($key, $index)=>{
let key = _.clone($key);
key.keySet = $keySet;
key.index = $index;
keys.push(key);
});
});
return keys;
},
lines() {
return _.get(this.device, 'lines', []);
},
canvasStyles() {
return {
@ -169,24 +265,12 @@
this.loadGroupsAndSeats();
},
methods: {
getLineIndexByKey(keyIndex) {
let lineIndex = null;
let lines = _.get(this.device, 'lines', []);
if(lines.length > 0) {
lines.forEach(($line, index)=>{
if($line.key_num === keyIndex) {
lineIndex = index;
}
});
}
return lineIndex;
},
getLineByKey(key) {
let line = null;
this.device.lines.forEach(($line)=>{
if($line.key_num === key) {
line = $line;
this.lines.forEach(($line, $lineIndex)=>{
if($line.key_num === key.index && $line.linerange === key.keySet.name) {
line = _.clone($line);
line.index = $lineIndex;
}
});
return line;
@ -282,19 +366,11 @@
}
return classes;
},
subscriberChanged(subscriberId) {
let clonedKey = _.clone(this.selectedKey);
clonedKey.subscriber_id = subscriberId;
this.$emit('keyChanged', {
key: clonedKey,
keyIndex: this.selectedKeyIndex
});
},
openKeyOverlay(key, index) {
openKeyOverlay(key) {
this.selectedKey = key;
this.selectedKeyIndex = index;
this.selectedLine = this.getLineByKey(index);
this.selectedLine = this.getLineByKey(key);
this.keyOverlayActive = true;
this.$scrollTo(this.$parent.$el);
},
closeKeyOverlay() {
this.keyOverlayActive = false;
@ -304,23 +380,24 @@
},
keySubscriberChanged(subscriberId) {
let newLines = [];
let lines = _.clone(_.get(this.device, 'lines', []));
let lineIndex = this.getLineIndexByKey(this.selectedKeyIndex);
let lines = _.clone(this.lines);
let line = this.getLineByKey(this.selectedKey);
let changed = false;
if(_.isNumber(lineIndex) && lineIndex < lines.length && subscriberId === null) {
delete lines[lineIndex];
if(line !== null && subscriberId === null) {
delete lines[line.index];
changed = true;
}
else if(_.isNumber(lineIndex) && lineIndex < lines.length) {
_.set(lines, lineIndex + '.subscriber_id', subscriberId);
else if(line !== null) {
_.set(lines, line.index + '.subscriber_id', subscriberId);
changed = true;
}
else if(subscriberId !== null) {
newLines.push({
extension_unit: 0,
key_num: this.selectedKeyIndex,
key_num: this.selectedKey.index,
subscriber_id: subscriberId,
linerange: _.get(this.device, 'profile.model.linerange.0.name'),
linerange: this.selectedKey.keySet.name,
type: this.$refs.selectType.value
});
changed = true;
@ -340,14 +417,10 @@
},
keyTypeChanged(type) {
let newLines = [];
let lines = _.clone(_.get(this.device, 'lines', []));
let lineIndex = this.getLineIndexByKey(this.selectedKeyIndex);
let changed = false;
if(_.isNumber(lineIndex) && _.isObject(lines[lineIndex])) {
_.set(lines, lineIndex + '.type', type);
changed = true;
}
if(changed === true) {
let lines = _.clone(this.lines);
let line = this.getLineByKey(this.selectedKey);
if(line != null) {
_.set(lines, line.index + '.type', type);
lines.forEach((line)=>{
newLines.push({
extension_unit: line.extension_unit,
@ -364,7 +437,7 @@
watch: {
device() {
if(this.keyOverlayActive) {
this.selectedLine = this.getLineByKey(this.selectedKeyIndex);
this.selectedLine = this.getLineByKey(this.selectedKey);
}
}
}

@ -30,7 +30,7 @@
:label="$t('pbxConfig.filterPhoneModel')"
@opened="modelSelectOpened()"
@select="filterByProfile"
@reseted="resetFilter"
@reseted="resetProfileFilter"
/>
</div>
<div
@ -153,28 +153,13 @@
},
methods: {
filterByProfile(profile) {
this.profile = profile;
this.$store.dispatch('pbxConfig/listDevices', {
page: this.listCurrentPage,
profile_id: profile.id
});
this.$store.dispatch('pbxConfig/filterByProfile', profile);
},
resetFilter() {
this.profile = null;
this.$store.dispatch('pbxConfig/listDevices', {
page: 1,
profile_id: null
});
resetProfileFilter() {
this.$store.dispatch('pbxConfig/resetProfileFilter');
},
changePage(page) {
let profileId = null;
if(this.profile !== null) {
profileId = this.profile.id;
}
this.$store.dispatch('pbxConfig/listDevices', {
page: page,
profile_id: profileId
});
this.$store.dispatch('pbxConfig/goToPage', page);
},
loadDevice(id) {
this.$store.dispatch('pbxConfig/loadDevice', id);

@ -134,7 +134,7 @@
},
watch: {
selectedId(id) {
this.selectedId(id);
this.selectById(id);
}
}
}
@ -144,8 +144,11 @@
@import '../../../themes/quasar.variables';
.csc-pbx-model-list
.q-item-avatar
overflow hidden
border-radius 0
img
border-radius 0
height auto
.csc-pbx-model-image
margin-top 16px

@ -65,10 +65,26 @@ export class BoundingBox2D {
}
addMargin(margin) {
this.topLeftX = this.topLeftX - margin;
this.topLeftY = this.topLeftY - margin;
this.bottomRightX = this.bottomRightX + margin;
this.bottomRightY = this.bottomRightY + margin;
this.addMarginTop(margin);
this.addMarginBottom(margin);
this.addMarginRight(margin);
this.addMarginLeft(margin);
}
addMarginTop(margin) {
this.topLeftY = this.topLeftY - Math.abs(margin);
}
addMarginBottom(margin) {
this.bottomRightY = this.bottomRightY + Math.abs(margin);
}
addMarginRight(margin) {
this.bottomRightX = this.bottomRightX + Math.abs(margin);
}
addMarginLeft(margin) {
this.topLeftX = this.topLeftX - Math.abs(margin);
}
scale(factor) {
@ -78,6 +94,16 @@ export class BoundingBox2D {
this.bottomRightY = this.bottomRightY * factor;
}
translateX(x) {
this.topLeftX = this.topLeftX + x;
this.bottomRightX = this.bottomRightX + x;
}
translateY(y) {
this.topLeftY = this.topLeftY + y;
this.bottomRightY = this.bottomRightY + y;
}
clone() {
let boundingBox = new BoundingBox2D();
boundingBox.topLeftX = this.getTopLeftX();
@ -87,6 +113,18 @@ export class BoundingBox2D {
return boundingBox;
}
normalizeX() {
this.bottomRightX = this.bottomRightX - this.getWidth();
this.topLeftX = 0;
}
normalize() {
this.bottomRightX = this.bottomRightX - this.topLeftX;
this.bottomRightY = this.bottomRightY - this.topLeftY;
this.topLeftX = 0;
this.topLeftY = 0;
}
static createFromPoints(points) {
return new BoundingBox2D(points);
}

@ -16,6 +16,7 @@ import { sync } from 'vuex-router-sync'
import { RtcEngineCall } from './plugins/call'
import App from './App.vue'
import './filters'
import VueScrollTo from 'vue-scrollto'
import config from './config'
@ -23,6 +24,7 @@ Vue.config.productionTip = false;
Vue.use(Quasar);
Vue.use(VueResource);
Vue.use(RtcEngineCall);
Vue.use(VueScrollTo);
sync(store, router);

@ -209,15 +209,10 @@ export default {
listDevices(context, options) {
return new Promise((resolve, reject)=>{
let silent = _.get(options, 'silent', false);
let page = _.get(options, 'page', 1);
let profile_id = _.get(options, 'profile_id', null);
context.commit('deviceListRequesting', {
silent: silent,
page: page
});
context.commit('deviceListRequesting', silent);
getDeviceList({
page: page,
profile_id: profile_id
page: _.get(context, 'getters.listCurrentPage', 1),
profile_id: _.get(context, 'getters.listProfileFilter', null)
}).then((devices)=>{
context.commit('deviceListSucceeded', devices);
devices.items.forEach((device)=>{
@ -265,7 +260,10 @@ export default {
context.commit('createDeviceRequesting', device);
createDevice(device).then(()=>{
context.commit('createDeviceSucceeded');
context.dispatch('listDevices');
context.dispatch('listDevices', {
page: context.getters.listCurrentPage,
silent: true
});
}).catch((err)=>{
context.commit('createDeviceFailed', err.message);
});
@ -274,7 +272,10 @@ export default {
context.commit('deviceRequesting', device.id);
removeDevice(device.id).then(()=>{
context.commit('deviceRemoved', device);
context.dispatch('listDevices');
context.dispatch('listDevices', {
page: context.getters.listCurrentPage,
silent: true
});
}).catch((err)=>{
context.commit('deviceFailed', device.id, err.message);
});
@ -330,5 +331,17 @@ export default {
}).catch((err) => {
context.commit('updateProfileFailed', err.message);
});
},
filterByProfile(context, profileId) {
context.commit('filterByProfile', profileId);
context.dispatch('listDevices');
},
resetProfileFilter(context) {
context.commit('resetProfileFilter');
context.dispatch('listDevices');
},
goToPage(context, page) {
context.commit('goToPage', page);
context.dispatch('listDevices');
}
}

@ -267,5 +267,8 @@ export default {
},
updatedDeviceProperty(state) {
return state.updatedDeviceProperty;
},
listProfileFilter(state) {
return state.listProfileFilter;
}
}

@ -161,7 +161,7 @@ export default {
},
deviceListRequesting(state, options) {
options = options || {};
state.listCurrentPage = _.get(options, 'page', 1);
// state.listCurrentPage = _.get(options, 'page', 1);
state.listLastPage = null;
state.listLoadingSilently = _.get(options, 'silent', false);
state.listState = RequestState.requesting;
@ -329,5 +329,15 @@ export default {
},
updateProfileFailed(state, error) {
updateDevicePropertyFailed(state, error);
},
filterByProfile(state, profile) {
state.listProfileFilter = profile.id;
state.listCurrentPage = 1;
},
resetProfileFilter(state) {
state.listProfileFilter = null;
},
goToPage(state, page) {
state.listCurrentPage = page;
}
}

@ -21,6 +21,7 @@ export default {
listLoadingSilently: false,
listCurrentPage: 1,
listLastPage: null,
listProfileFilter: null,
addState: RequestState.initiated,
addError: null,
addItem: null,

@ -5,7 +5,7 @@ FROM docker.mgm.sipwise.com/sipwise-stretch:latest
# is updated with the current date. It will force refresh of all
# of the base images and things like `apt-get update` won't be using
# old cached versions when the Dockerfile is built.
ENV REFRESHED_AT 2018-06-14
ENV REFRESHED_AT 2018-06-27
ENV DEBIAN_FRONTEND noninteractive
ENV DISPLAY=:0

Loading…
Cancel
Save