TT#40250 PBXConfig: As a Customer, I want to get notified about invalid input before creating a PBXGroup

Change-Id: Iaf18363545097ffe733034a94daa1e42fb228242
changes/61/22761/4
Hans-Peter Herzog 7 years ago
parent 40e15adff0
commit f867c69d26

@ -157,6 +157,7 @@
separator separator
sparse sparse
multiline multiline
:highlight="!isMobile"
> >
<csc-pbx-device <csc-pbx-device
v-for="device in devices" v-for="device in devices"

@ -1,114 +1,158 @@
<template> <template>
<q-card <q-item class="csc-list-item csc-pbx-group">
class="csc-entity csc-pbx-group shadow-1" <q-item-side
> v-if="!expanded"
<q-card-title class="csc-entity-title"> >
<q-icon <q-icon
size="32px"
name="group" name="group"
color="secondary" color="secondary"
size="24px"
/> />
<span class="csc-entity-title-text"> </q-item-side>
{{ entityTitle }} <q-item-main>
</span> <q-item-tile
<q-chip
v-if="!expanded" v-if="!expanded"
pointing="left" class="csc-item-title"
color="primary" label
class="gt-md cursor-pointer"
> >
{{ $t('pbxConfig.extension') }}: <span class="csc-important">{{ group.pbx_extension }}</span> {{ group.display_name }}
</q-chip> </q-item-tile>
<q-btn <q-item-tile
:icon="titleIcon" v-if="!expanded"
:small="isMobile" class="csc-item-subtitle"
color="primary" sublabel
slot="right" >
flat <div>
@click="toggleMain()" <span class="csc-item-label">{{ $t('pbxConfig.extension') }}:</span>
/> <span class="csc-item-value">{{ group.pbx_extension }}</span>
<q-btn </div>
icon="delete" </q-item-tile>
:small="isMobile" <q-item-tile
color="negative" v-if="!expanded"
slot="right" class="csc-item-subtitle"
flat sublabel
@click="remove()" >
/> <div
</q-card-title> v-if="!hasSeats"
<q-card-main class="csc-form-info"
v-if="expanded" >
class="transition-generic" <q-icon name="info" color="info" size="24px"/>
<span class="csc-info-text">{{ $t('pbxConfig.noSeatAssigned') }}</span>
</div>
<div
class="csc-item-field"
v-if="hasSeats"
>
<span
class="csc-item-label"
>{{ $t('pbxConfig.seats') }}:
</span>
<span
class="csc-item-value"
v-for="seat in group.seats"
>{{ seat.display_name }}
</span>
</div>
</q-item-tile>
<q-item-tile
class="csc-list-item-main"
v-if="expanded"
>
<q-field :label="$t('pbxConfig.groupName')">
<q-input
v-model="changes.name"
:after="nameButtons"
@keyup.enter="saveName"
/>
</q-field>
<q-field :label="$t('pbxConfig.extension')">
<q-input
v-model="changes.extension"
type="number"
:after="extensionButtons"
@keyup.enter="saveExtension"
/>
</q-field>
<q-field :label="$t('pbxConfig.huntPolicy')">
<q-select
v-model="changes.huntPolicy"
:options="huntPolicyOptions"
radio
@change="huntPolicyChanged"
/>
</q-field>
<q-field :label="$t('pbxConfig.huntTimeout')">
<q-input
v-model="changes.huntTimeout"
type="number"
suffix="seconds"
:after="huntTimeoutButtons"
:min="0"
@keyup.enter="saveHuntTimeout"
/>
</q-field>
<q-field :label="$t('pbxConfig.primaryNumber')">
<q-input
v-model="primaryNumber"
readonly
disable
/>
</q-field>
<q-field :label="$t('pbxConfig.aliasNumbers')">
<q-select
ref="aliasNumbers"
v-model="changes.aliasNumbers"
:options="aliasNumberOptions"
multiple
chips
clearable
:after="aliasNumberButtons"
/>
</q-field>
<q-field :label="$t('pbxConfig.seats')">
<q-select
v-model="changes.seats"
:options="seatOptions"
multiple
chips
clearable
:after="seatButtons"
/>
</q-field>
</q-item-tile>
</q-item-main>
<q-item-side
right
class="csc-item-buttons"
> >
<q-field :label="$t('pbxConfig.groupName')"> <q-item-tile>
<q-input <div class="csc-item-button">
v-model="changes.name" <q-icon
:after="nameButtons" size="26px"
@keyup.enter="saveName" :name="titleIcon"
/> color="primary"
</q-field> slot="right"
<q-field :label="$t('pbxConfig.extension')"> @click="toggleMain()"
<q-input />
v-model="changes.extension" </div>
type="number" <div class="csc-item-button">
:after="extensionButtons" <q-icon
@keyup.enter="saveExtension" size="26px"
/> name="delete"
</q-field> color="negative"
<q-field :label="$t('pbxConfig.huntPolicy')"> slot="right"
<q-select @click="remove()"
v-model="changes.huntPolicy" />
:options="huntPolicyOptions" </div>
radio </q-item-tile>
@change="huntPolicyChanged" </q-item-side>
/>
</q-field>
<q-field :label="$t('pbxConfig.huntTimeout')">
<q-input
v-model="changes.huntTimeout"
type="number"
suffix="seconds"
:after="huntTimeoutButtons"
:min="0"
@keyup.enter="saveHuntTimeout"
/>
</q-field>
<q-field :label="$t('pbxConfig.primaryNumber')">
<q-input
v-model="primaryNumber"
readonly
disable
/>
</q-field>
<q-field :label="$t('pbxConfig.aliasNumbers')">
<q-select
ref="aliasNumbers"
v-model="changes.aliasNumbers"
:options="aliasNumberOptions"
multiple
chips
clearable
:after="aliasNumberButtons"
/>
</q-field>
<q-field :label="$t('pbxConfig.seats')">
<q-select
v-model="changes.seats"
:options="seatOptions"
multiple
chips
clearable
:after="seatButtons"
/>
</q-field>
</q-card-main>
<q-inner-loading :visible="isLoading"> <q-inner-loading :visible="isLoading">
<q-spinner-mat <q-spinner-mat
size="60px" size="60px"
color="primary" color="primary"
/> />
</q-inner-loading> </q-inner-loading>
</q-card> </q-item>
</template> </template>
<script> <script>
@ -128,7 +172,12 @@
QInnerLoading, QInnerLoading,
QSpinnerMat, QSpinnerMat,
QTransition, QTransition,
Platform Platform,
QItem,
QItemSide,
QItemMain,
QItemTile,
QAlert
} from 'quasar-framework' } from 'quasar-framework'
export default { export default {
name: 'csc-pbx-group', name: 'csc-pbx-group',
@ -158,7 +207,12 @@
QBtn, QBtn,
QInnerLoading, QInnerLoading,
QSpinnerMat, QSpinnerMat,
QTransition QTransition,
QItem,
QItemSide,
QItemMain,
QItemTile,
QAlert
}, },
computed: { computed: {
entityTitle() { entityTitle() {
@ -366,6 +420,9 @@
); );
} }
return buttons; return buttons;
},
hasSeats() {
return _.isArray(_.get(this.group, 'seats')) && this.group.seats.length > 0;
} }
}, },
methods: { methods: {

@ -1,52 +1,127 @@
<template> <template>
<q-card class="csc-pbx-group-add-form shadow-1"> <div class="csc-form csc-pbx-seat-add-form">
<q-card-title> <q-field :error-label="groupNameErrorMessage">
<q-icon name="add" color="primary" size="22px"/> <q-input
<span>{{ $t('pbxConfig.addGroup') }}</span> @input="$v.data.name.$touch"
</q-card-title> :error="$v.data.name.$error"
<q-card-main> :disabled="loading"
<q-field> :readonly="loading"
<q-input :disabled="loading" :readonly="loading" v-model="data.name" autofocus v-model="data.name"
:float-label="$t('pbxConfig.groupName')" clearable /> autofocus
</q-field> :float-label="$t('pbxConfig.groupName')"
<q-field> clearable
<q-input :disabled="loading" :readonly="loading" type="number" v-model="data.extension" />
clearable :min="1" :max="1000000" :float-label="$t('pbxConfig.extension')" /> </q-field>
</q-field> <q-field :error-label="extensionErrorMessage">
<q-field> <q-input
<q-select :disabled="loading" :readonly="loading" v-model="data.huntPolicy" @input="$v.data.extension.$touch"
:float-label="$t('pbxConfig.huntPolicy')" :options="huntPolicyOptions" radio /> :error="$v.data.extension.$error"
</q-field> :disabled="loading"
<q-field> :readonly="loading"
<q-input :disabled="loading" :readonly="loading" type="number" v-model="data.huntTimeout" clearable v-model="data.extension"
:float-label="$t('pbxConfig.huntTimeout')" suffix="seconds" :min="1" :max="3600" /> :float-label="$t('pbxConfig.extension')"
</q-field> clearable
<q-field> />
<q-select :disabled="loading" :readonly="loading" v-model="data.aliasNumbers" multiple chips clearable </q-field>
:float-label="$t('pbxConfig.aliasNumbers')" :options="aliasNumberOptions" /> <q-field>
</q-field> <q-select
<q-field> :disabled="loading"
<q-select :disabled="loading" :readonly="loading" v-model="data.seats" multiple chips clearable :readonly="loading"
:float-label="$t('pbxConfig.seats')" :options="seatOptions" /> v-model="data.huntPolicy"
</q-field> :float-label="$t('pbxConfig.huntPolicy')"
</q-card-main> :options="huntPolicyOptions"
<q-card-separator/> radio
<q-card-actions align="center"> />
<q-btn v-if="!loading" flat color="secondary" icon="clear" </q-field>
@click="cancel()">{{ $t('buttons.cancel') }}</q-btn> <q-field :error-label="huntTimeoutErrorMessage">
<q-btn v-if="!loading" flat color="primary" icon="done" <q-input
@click="save()">{{ $t('buttons.save') }}</q-btn> @input="$v.data.huntTimeout.$touch"
</q-card-actions> :error="$v.data.huntTimeout.$error"
:disabled="loading"
:readonly="loading"
type="number"
v-model="data.huntTimeout"
clearable
:float-label="$t('pbxConfig.huntTimeout')"
suffix="seconds"
:min="1"
:max="3600"
/>
</q-field>
<q-field>
<q-select
:disabled="loading"
:readonly="loading"
v-model="data.aliasNumbers"
multiple
chips
clearable
:float-label="$t('pbxConfig.aliasNumbers')"
:options="aliasNumberOptions"
/>
</q-field>
<q-field>
<q-select
:disabled="loading"
:readonly="loading"
v-model="data.seats"
multiple
chips
clearable
:float-label="$t('pbxConfig.seats')"
:options="seatOptions"
/>
</q-field>
<div class="csc-form-actions row justify-center">
<q-btn
v-if="!loading"
flat
color="secondary"
icon="clear"
@click="cancel()"
>
{{ $t('buttons.cancel') }}
</q-btn>
<q-btn
v-if="!loading"
:disabled="$v.data.$invalid"
flat
color="primary"
icon="group"
@click="save()"
>
{{ $t('pbxConfig.createGroup') }}
</q-btn>
</div>
<q-inner-loading :visible="loading"> <q-inner-loading :visible="loading">
<q-spinner-mat size="60px" color="primary"></q-spinner-mat> <q-spinner-mat size="60px" color="primary" />
</q-inner-loading> </q-inner-loading>
</q-card> </div>
</template> </template>
<script> <script>
import { QCard, QCardTitle, QCardMain, QCardActions, QCardSeparator, QBtn, import {
QInnerLoading, QSpinnerMat, QField, QInput, QSelect, QIcon } from 'quasar-framework' required,
minValue,
maxValue,
maxLength,
numeric
} from 'vuelidate/lib/validators'
import {
QCard,
QCardTitle,
QCardMain,
QCardActions,
QCardSeparator,
QBtn,
QInnerLoading,
QSpinnerMat,
QField,
QInput,
QSelect,
QIcon
} from 'quasar-framework'
export default { export default {
name: 'csc-pbx-group-add-form', name: 'csc-pbx-group-add-form',
@ -57,14 +132,82 @@
'loading', 'loading',
], ],
components: { components: {
QCard, QCardTitle, QCardMain, QCardActions, QCardSeparator, QBtn, QCard,
QInnerLoading, QSpinnerMat, QField, QInput, QSelect, QIcon QCardTitle,
QCardMain,
QCardActions,
QCardSeparator,
QBtn,
QInnerLoading,
QSpinnerMat,
QField,
QInput,
QSelect,
QIcon
},
validations: {
data: {
name: {
required,
maxLength: maxLength(64)
},
extension: {
required,
maxLength: maxLength(64),
numeric
},
huntTimeout: {
required,
minValue: minValue(1),
maxValue: maxValue(3600)
}
}
}, },
data () { data () {
return { return {
data: this.getDefaults() data: this.getDefaults()
} }
}, },
computed: {
groupNameErrorMessage() {
if (!this.$v.data.name.required) {
return this.$t('pbxConfig.requiredGroupName');
}
else if (!this.$v.data.name.maxLength) {
return this.$t('pbxConfig.groupNameMaxLength', {
maxLength: this.$v.data.name.$params.maxLength.max
});
}
},
extensionErrorMessage() {
if (!this.$v.data.extension.required) {
return this.$t('pbxConfig.requiredExtension');
}
else if (!this.$v.data.name.maxLength) {
return this.$t('pbxConfig.extensionMaxLength', {
maxLength: this.$v.data.extension.$params.maxLength.max
});
}
else if (!this.$v.data.name.numeric) {
return this.$t('pbxConfig.extensionAlphaNum');
}
},
huntTimeoutErrorMessage() {
if (!this.$v.data.huntTimeout.required) {
return this.$t('pbxConfig.requiredHuntTimeout');
}
else if (!this.$v.data.huntTimeout.minValue) {
return this.$t('pbxConfig.huntTimeoutMinValue', {
minValue: this.$v.data.huntTimeout.$params.minValue.min
});
}
else if (!this.$v.data.huntTimeout.maxValue) {
return this.$t('pbxConfig.huntTimeoutMaxValue', {
maxValue: this.$v.data.huntTimeout.$params.maxValue.max
});
}
}
},
methods: { methods: {
getDefaults() { getDefaults() {
return { return {
@ -84,6 +227,7 @@
}, },
reset() { reset() {
this.data = this.getDefaults(); this.data = this.getDefaults();
this.$v.$reset();
} }
} }
} }

@ -1,15 +1,22 @@
<template> <template>
<csc-page :title="$t('pbxConfig.groupsTitle')"> <csc-page
<csc-pbx-group-add-form class="csc-list-page"
>
<div
v-show="addFormEnabled" v-show="addFormEnabled"
ref="addForm" class="row justify-center margin-sm"
@save="addGroup" >
@cancel="disableAddForm" <csc-pbx-group-add-form
:loading="isAdding" class="col-xs-12 col-md-6"
:alias-number-options="aliasNumberOptions" ref="addForm"
:seat-options="seatOptions" @save="addGroup"
:hunt-policy-options="huntPolicyOptions" @cancel="disableAddForm"
/> :loading="isAdding"
:alias-number-options="aliasNumberOptions"
:seat-options="seatOptions"
:hunt-policy-options="huntPolicyOptions"
/>
</div>
<div <div
v-show="!addFormEnabled" v-show="!addFormEnabled"
class="row justify-center" class="row justify-center"
@ -42,22 +49,31 @@
@change="changePage" @change="changePage"
/> />
</div> </div>
<csc-pbx-group <q-list
v-for="group in groups" class=""
:key="group.id" no-border
:group="group" separator
:alias-number-options="aliasNumberOptions" sparse
:seat-options="seatOptions" multiline
:hunt-policy-options="huntPolicyOptions" :highlight="!isMobile"
@remove="removeGroup" >
:loading="isItemLoading(group.id)" <csc-pbx-group
@save-name="setGroupName" v-for="group in groups"
@save-extension="setGroupExtension" :key="group.id"
@save-hunt-policy="setGroupHuntPolicy" :group="group"
@save-hunt-timeout="setGroupHuntTimeout" :alias-number-options="aliasNumberOptions"
@save-alias-numbers="updateAliasNumbers" :seat-options="seatOptions"
@save-seats="updateSeats" :hunt-policy-options="huntPolicyOptions"
/> @remove="removeGroup"
:loading="isItemLoading(group.id)"
@save-name="setGroupName"
@save-extension="setGroupExtension"
@save-hunt-policy="setGroupHuntPolicy"
@save-hunt-timeout="setGroupHuntTimeout"
@save-alias-numbers="updateAliasNumbers"
@save-seats="updateSeats"
/>
</q-list>
<div <div
v-if="groups.length === 0 && !isListRequesting" v-if="groups.length === 0 && !isListRequesting"
class="row justify-center csc-no-entities" class="row justify-center csc-no-entities"
@ -95,7 +111,8 @@
QSpinnerDots, QSpinnerDots,
QSpinnerMat, QSpinnerMat,
Dialog, Dialog,
QPagination QPagination,
Platform
} from 'quasar-framework' } from 'quasar-framework'
export default { export default {
@ -194,7 +211,10 @@
'lastUpdatedField', 'lastUpdatedField',
'updateAliasNumbersState', 'updateAliasNumbersState',
'updateGroupsAndSeatsState' 'updateGroupsAndSeatsState'
]) ]),
isMobile() {
return Platform.is.mobile;
},
}, },
watch: { watch: {
addState(state) { addState(state) {

@ -1,5 +1,4 @@
<template> <template>
<q-item class="csc-list-item csc-pbx-seat"> <q-item class="csc-list-item csc-pbx-seat">
<q-item-side <q-item-side
v-if="!expanded" v-if="!expanded"
@ -41,6 +40,7 @@
<span class="csc-info-text">{{ $t('pbxConfig.noGroupAssigned') }}</span> <span class="csc-info-text">{{ $t('pbxConfig.noGroupAssigned') }}</span>
</div> </div>
<div <div
class="csc-item-field"
v-if="hasGroups"> v-if="hasGroups">
<span <span
class="csc-item-label"> class="csc-item-label">
@ -134,97 +134,6 @@
/> />
</q-inner-loading> </q-inner-loading>
</q-item> </q-item>
<!--<q-card class="csc-entity csc-pbx-seat shadow-1">-->
<!--<q-card-title class="csc-entity-title">-->
<!--<q-icon-->
<!--name="person"-->
<!--color="secondary"-->
<!--size="24px"-->
<!--/>-->
<!--<span class="csc-entity-title-text">-->
<!--{{ entityTitle }}-->
<!--</span>-->
<!--<q-chip-->
<!--v-if="!expanded"-->
<!--pointing="left"-->
<!--color="primary"-->
<!--class="gt-md"-->
<!--&gt;-->
<!--{{ $t('pbxConfig.extension') }}: <span class="csc-important">{{ seat.pbx_extension }}</span>-->
<!--</q-chip>-->
<!--<q-btn-->
<!--:icon="titleIcon"-->
<!--:small="isMobile"-->
<!--color="primary"-->
<!--slot="right"-->
<!--flat-->
<!--@click="toggleMain()"-->
<!--/>-->
<!--<q-btn-->
<!--icon="delete"-->
<!--:small="isMobile"-->
<!--color="negative"-->
<!--slot="right"-->
<!--flat-->
<!--@click="remove()"-->
<!--/>-->
<!--</q-card-title>-->
<!--<q-card-main-->
<!--v-if="expanded"-->
<!--class="transition-generic"-->
<!--&gt;-->
<!--<q-field :label="$t('pbxConfig.seatName')">-->
<!--<q-input-->
<!--v-model="changes.name"-->
<!--:after="nameButtons"-->
<!--@keyup.enter="saveName"-->
<!--/>-->
<!--</q-field>-->
<!--<q-field :label="$t('pbxConfig.extension')">-->
<!--<q-input-->
<!--v-model="changes.extension"-->
<!--type="number"-->
<!--:after="extensionButtons"-->
<!--@keyup.enter="saveExtension"-->
<!--/>-->
<!--</q-field>-->
<!--<q-field :label="$t('pbxConfig.primaryNumber')">-->
<!--<q-input-->
<!--v-model="primaryNumber"-->
<!--readonly-->
<!--disable-->
<!--/>-->
<!--</q-field>-->
<!--<q-field :label="$t('pbxConfig.aliasNumbers')">-->
<!--<q-select-->
<!--ref="aliasNumbers"-->
<!--v-model="changes.aliasNumbers"-->
<!--:options="aliasNumberOptions"-->
<!--multiple-->
<!--chips-->
<!--clearable-->
<!--:after="aliasNumberButtons"-->
<!--/>-->
<!--</q-field>-->
<!--<q-field :label="$t('pbxConfig.groups')">-->
<!--<q-select-->
<!--v-model="changes.groups"-->
<!--:options="groupOptions"-->
<!--multiple-->
<!--chips-->
<!--clearable-->
<!--:after="groupButtons"-->
<!--/>-->
<!--</q-field>-->
<!--</q-card-main>-->
<!--<q-inner-loading :visible="isLoading">-->
<!--<q-spinner-mat-->
<!--size="60px"-->
<!--color="primary"-->
<!--/>-->
<!--</q-inner-loading>-->
<!--</q-card>-->
</template> </template>
<script> <script>

@ -16,11 +16,11 @@
</q-btn> </q-btn>
</div> </div>
<div <div
class="row justify-center" class="row justify-center margin-sm"
v-show="addFormEnabled"
> >
<csc-pbx-seat-add-form <csc-pbx-seat-add-form
class="col-xs-12 col-md-6" class="col-xs-12 col-md-6"
v-show="addFormEnabled"
ref="addForm" ref="addForm"
:alias-number-options="aliasNumberOptions" :alias-number-options="aliasNumberOptions"
:group-options="groupOptions" :group-options="groupOptions"

@ -314,9 +314,16 @@
"noStationName": "Could not find any device with station name matching the filter criteria", "noStationName": "Could not find any device with station name matching the filter criteria",
"noGroups": "No groups created yet", "noGroups": "No groups created yet",
"noSeats": "No seats created yet", "noSeats": "No seats created yet",
"requiredGroupName": "Group name is required",
"groupNameMaxLength": "Group name must have at most {maxLength} letters",
"requiredExtension": "Extension is required",
"extensionMinValue": "Extension must be at least {minValue}",
"extensionMaxValue": "Extension must be maximum {maxValue}",
"requiredHuntTimeout": "Hunt timeout is required",
"huntTimeoutMinValue": "Hunt timeout must be at least {minValue} second",
"huntTimeoutMaxValue": "Hunt timeout must be maximum {maxValue} seconds",
"requiredSeatName": "Seat name is required", "requiredSeatName": "Seat name is required",
"seatNameMaxLength": "Seat name must have at most {maxLength} letters", "seatNameMaxLength": "Seat name must have at most {maxLength} letters",
"requiredExtension": "Extension is required",
"extensionMaxLength": "Extension must have at most {maxLength} letters", "extensionMaxLength": "Extension must have at most {maxLength} letters",
"extensionAlphaNum": "Only numeric characters are allowed", "extensionAlphaNum": "Only numeric characters are allowed",
"toasts": { "toasts": {
@ -357,7 +364,9 @@
"resetFilters": "Reset filters", "resetFilters": "Reset filters",
"closeFilters": "Close filters", "closeFilters": "Close filters",
"createSeat": "Create seat", "createSeat": "Create seat",
"noGroupAssigned": "Not assigned to any group" "noGroupAssigned": "Not assigned to any group",
"noSeatAssigned": "No seats assigned",
"createGroup": "Create group"
}, },
"callBlocking": { "callBlocking": {
"privacyEnabledToast": "Your number is hidden to the callee", "privacyEnabledToast": "Your number is hidden to the callee",

@ -1,6 +1,15 @@
@import 'quasar.variables' @import 'quasar.variables'
.csc-item-field
.csc-item-value
display inline-block
margin-right 4px
.csc-item-value::after
content ','
.csc-item-value:last-child:after
content ''
.csc-list-item .csc-list-item
position relative position relative
.csc-list-item-main .csc-list-item-main

Loading…
Cancel
Save