TT#117521 Auto-attendant - As a SubscriberAdmin, I want to configure Auto-attendant slots

- TT#118754 Create new menu point under PBXConfiguration + new page + new route
- TT#118755 Render a list of all slots of all Subscribers (Pilot, Seats, Groups)
- TT#118756 Load Subscriber (Pilot, Seats, Groups) name for each slot in the list
- TT#118757 Implement AddForm to be able to add a new AA-Slot
- TT#118758 Implement Deletion for a AA-Slot including a confirmation Dialog
- TT#118765 Implement EditForm to be able to edit the destination of an existing AA-Slot

NOTES

- In order to test the feature you need to create a Customer with Product = Cloud PBX Account, and at least one subscriber which you need to login into CSC.
- There are a couple of components which have been created and not used due tue specs revision, but left in place for future possible utilisation:
- Sorting by subscriber id and name has been removed as it is not supported by the endpoint

src/components/form/CscSelectLazy.vue
src/components/CscDataTableEditSelect.vue

Change-Id: Iec29baecfa75f3c818b9deb945625a1bf977ca88
mr9.5.2
Carlo Venusino 4 years ago
parent 180cc90857
commit 01032cad60

@ -0,0 +1,20 @@
import Vue from 'vue'
import { patchReplaceFull } from 'src/api/common'
export async function getAutoAttendants (options) {
const params = { ...options, ...{ expand: 1 } }
const res = await Vue.http.get('api/autoattendants/', {
params: params
})
return res.body.total_count > 0 ? res.body : []
}
export async function editSubscriberSlots (options) {
const res = await patchReplaceFull({
resource: 'autoattendants',
resourceId: options.subscriberId,
fieldPath: 'slots',
value: options.slots
})
return res.slots
}

@ -0,0 +1,111 @@
<template>
<span
class="cursor-pointer"
>
<q-input
v-model="internalValue"
dense
hide-bottom-space
borderless
filled
:error="error"
:error-message="errorMessage"
:disable="$attrs.disable"
@keyup="save"
@clear="clear"
/>
</span>
</template>
<script>
import { i18n } from 'src/boot/i18n'
export default {
name: 'CscDataTableEditInput',
props: {
column: {
type: Object,
required: true
},
row: {
type: Object,
required: true
},
value: {
type: [String, Number],
default: undefined
},
saveLabel: {
type: String,
default: i18n.t('Save')
}
},
data () {
return {
internalValue: this.value
}
},
validations () {
const config = {}
if (this.column.componentValidations) {
config.internalValue = {}
this.column.componentValidations.forEach((validation) => {
config.internalValue[validation.name] = validation.validator
})
}
return config
},
computed: {
error () {
if (this.column.componentValidations) {
return this.$v.internalValue.$error
} else {
return false
}
},
errorMessage () {
if (this.column.componentValidations) {
const validation = this.column.componentValidations.find(validation =>
this.$v.internalValue[validation.name] === false
)
if (validation) {
return validation.error
} else {
return undefined
}
} else {
return undefined
}
}
},
watch: {
value (value) {
this.internalValue = value
}
},
mounted () {
this.internalValue = this.value
this.$v.$reset()
},
methods: {
validate () {
if (this.column.componentValidations) {
this.$v.$touch()
return !this.$v.$invalid
} else {
return true
}
},
save () {
this.$v.$touch()
this.$emit('changed', {
column: this.column,
row: this.row,
value: this.internalValue
})
},
clear () {
this.$v.$reset()
}
}
}
</script>

@ -0,0 +1,99 @@
<template>
<span
class="cursor-pointer"
>
<template
v-if="value === '' || value === undefined || value === null"
>
<q-btn
icon="add"
dense
flat
size="sm"
:disable="$attrs.disable"
/>
</template>
{{ label }}
<q-popup-edit
v-model="internalValue"
buttons
:label-set="saveLabel"
@before-show="popupBeforeShowEvent"
@save="$emit('saved', {
column: column,
row: row,
value: internalValue
})"
>
<q-select
v-model="internalValue"
:options="filteredOptions || column.componentOptions"
:label="column.label"
emit-value
map-options
dense
autofocus
fill-input
:disable="$attrs.disable"
>
<template
v-if="column.componentIcon"
v-slot:prepend
>
<q-icon
:name="column.componentIcon"
/>
</template>
</q-select>
</q-popup-edit>
</span>
</template>
<script>
import { i18n } from 'src/boot/i18n'
export default {
name: 'CscDataTableEditSelect',
props: {
column: {
type: Object,
required: true
},
row: {
type: Object,
required: true
},
value: {
type: [String, Number],
default: undefined
},
filteredOptions: {
type: Array,
default: undefined
},
saveLabel: {
type: String,
default: i18n.t('Save')
}
},
data () {
return {
internalValue: this.value
}
},
computed: {
label () {
const refOption = this.column.componentOptions.find(option => option.value === this.value)
if (refOption) {
return refOption.label
} else {
return this.value
}
}
},
methods: {
popupBeforeShowEvent () {
this.internalValue = this.value
}
}
}
</script>

@ -179,6 +179,12 @@ export default {
icon: 'arrow_forward',
label: this.$t('Manager Secretary'),
visible: true
},
{
to: '/user/pbx-configuration/auto-attendant',
icon: 'dialpad',
label: this.$t('Auto-attendant'),
visible: true
}
]
},

@ -35,7 +35,7 @@ export default {
default: 'primary'
},
label: {
type: String,
type: [String, Number],
default: ''
},
sublabel: {

@ -0,0 +1,135 @@
<template>
<q-select
ref="select"
:value="$attrs.value"
:options="filteredOptions"
emit-value
map-options
use-input
input-debounce="500"
:loading="$wait.is(waitIdentifier) || $attrs.loading"
v-bind="$attrs"
v-on="$listeners"
@filter="filter"
>
<template
v-slot:prepend
>
<slot
name="prepend"
/>
<q-icon
v-if="icon"
:name="icon"
/>
</template>
<template
v-slot:append
>
<slot
name="append"
/>
</template>
<template
v-slot:after
>
<slot
name="after"
/>
</template>
</q-select>
</template>
<script>
import _ from 'lodash'
export default {
name: 'CscSelectLazy',
props: {
icon: {
type: String,
default: undefined
},
storeGetter: {
type: String,
required: true
},
storeAction: {
type: String,
required: true
},
storeActionParams: {
type: Object,
default: null
},
loadInitially: {
type: Boolean,
default: true
},
filterCustomizationFunction: {
type: Function,
default: (filter) => filter
},
initialOption: {
type: Object,
default: undefined
}
},
data () {
return {
optionsWereUpdated: false
}
},
computed: {
filteredOptions () {
let options = _.clone(this.$store.getters[this.storeGetter])
if (options === undefined || options === null) {
options = []
}
if (!this.optionsWereUpdated && this.initialOption && (options.length === 0 || options[0].disable === true)) {
options.splice(0, 1, this.initialOption)
}
if (options.length === 0) {
options.push({
label: this.$t('No data found'),
disable: true
})
}
return options
},
waitIdentifier () {
return this.$vnode.tag + this.$vnode.componentInstance?._uid
}
},
mounted () {
if (this.loadInitially) {
this.filter('')
}
},
methods: {
async filter (filter, update, abort) {
this.$wait.start(this.waitIdentifier)
try {
const filterFinalised = this.filterCustomizationFunction(filter)
let options = filterFinalised
if (_.isObject(this.storeActionParams)) {
options = _.merge(this.storeActionParams, {
filter: filterFinalised
})
}
await this.$store.dispatch(this.storeAction, options)
this.optionsWereUpdated = true
if (typeof update === 'function') {
update()
}
} catch (e) {
if (typeof abort === 'function') {
abort()
}
throw e
} finally {
this.$wait.end(this.waitIdentifier)
}
}
}
}
</script>

@ -391,13 +391,11 @@ export default {
}
</script>
<style lang="stylus" rel="stylesheet/stylus">
<style lang="stylus" rel="stylesheet/stylus" scoped>
.csc-cf-daterange-btn-cont
margin-top 10px
width 100%
text-align center
.q-menu
min-width auto !important
.q-datetime-days div:not(.q-datetime-day-active),
.q-datetime-dark,
.q-datetime-range .q-datetime-input .q-input-target,

@ -0,0 +1,149 @@
<template>
<div class="q-pa-xs">
<div
class="csc-pbx-aa-form-cont"
>
<q-form
@submit="save"
:loading="$wait.is('csc-pbx-auto-attendant-form')"
class="csc-pbx-aa-form justify-center"
>
<div class="row">
<csc-select-lazy
class="col-12"
icon="person"
:value="data.subscriberId"
:label="$t('Subscriber')"
clearable
store-getter="pbxAutoAttendants/subscribers"
store-action="pbxAutoAttendants/fetchSubscribers"
:load-initially="false"
v-bind="$attrs"
v-on="$listeners"
@input="setSubscriberId($event)"
/>
</div>
<div
v-for="(slotNumber, index) in slotsNumbers"
:key="index"
class="col-12"
>
<div
v-if="index % 2 === 0"
class="row"
>
<q-input
v-model="data.slots[index]"
:label="$t('Slot {number}', {
number: slotNumber
})"
dense
class="col-6 q-pa-xs"
/>
<q-input
v-model="data.slots[index +1]"
:label="$t('Slot {number}', {
number: slotsNumbers[index +1]
})"
dense
class="col-6 q-pa-xs"
/>
</div>
</div>
<div
class="row justify-center"
>
<q-btn
flat
color="default"
icon="clear"
:disable="$wait.is('csc-pbx-auto-attendant-form')"
:label="$t('Cancel')"
@click="$emit('closeForm')"
/>
<q-btn
flat
color="primary"
icon="check"
:loading="$wait.is('csc-pbx-auto-attendant-form')"
:disabled="disableSave"
:label="$t('Save')"
@click="save"
/>
</div>
</q-form>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { required } from 'vuelidate/lib/validators'
import { mapWaitingActions } from 'vue-wait'
import { showToast } from 'src/helpers/ui'
import CscSelectLazy from 'components/form/CscSelectLazy'
export default {
name: 'CscPbxAutoAttendantAddForm',
components: {
CscSelectLazy
},
data () {
return {
data: {
subscriberId: null,
slots: []
},
loading: false
}
},
validations: {
data: {
subscriberId: {
required
}
}
},
computed: {
...mapGetters('pbxAutoAttendants', [
'slotsNumbers'
]),
disableSave () {
return this.data.subscriberId === null || this.data.slots.length < 1
}
},
methods: {
...mapWaitingActions('pbxAutoAttendants', {
updateSubscriberSlots: 'csc-pbx-auto-attendant-form'
}),
setSubscriberId (subscriberId) {
this.data.subscriberId = subscriberId
this.data.slots = []
},
async save () {
await this.updateSubscriberSlots({
subscriberId: this.data.subscriberId,
slots: this.data.slots.map((slot, index) => {
if (slot) {
return {
slot: index,
destination: slot
}
}
}).filter(Boolean)
})
showToast(this.$t('Slots successfully added'))
this.$emit('newSubscriberSaved')
this.$emit('closeForm')
}
}
}
</script>
<style lang="stylus" rel="stylesheet/stylus" scoped>
.csc-pbx-aa-form-cont
width 100%
.csc-pbx-aa-form
width 50%
margin auto
</style>

@ -36,7 +36,7 @@
<script>
export default {
name: 'CscPbxAttendantSelection',
name: 'CscPbxAutoAttendantSelection',
props: {
showSelectedItemIcon: {
type: Boolean,

@ -0,0 +1,261 @@
<template>
<q-table
:data="data"
:columns="columns"
:loading="$wait.is('csc-pbx-autoattendant-slots-table')"
:pagination.sync="pagination"
:hide-pagination="true"
row-key="name"
class="csc-item-odd"
>
<template v-slot:loading>
<q-inner-loading
showing
color="primary"
/>
</template>
<template v-slot:header="props">
<q-tr>
<q-th auto-width />
<q-th
v-for="col in props.cols"
:key="col.name"
class="text-left"
>
{{ col.label }}
</q-th>
<q-th auto-width />
<q-th auto-width />
</q-tr>
<q-tr
v-for="(row, index) in unsavedSlots"
:key="index"
>
<q-td auto-width />
<q-td>
{{ row.slot }}
</q-td>
<q-td>
<csc-data-table-edit-input
:column="{name:'destination', label: $t('Destination'), componentValidations: [getDestinationValidation()]}"
:row="{slot: row.slot, destination: row.destination}"
:value="row.destination"
:save-label="$t('Add')"
@changed="updateNewSlotDestination(index, $event.value)"
/>
</q-td>
<q-td>
<q-btn
icon="delete"
color="negative"
flat
dense
@click="resetNewSlot(index)"
/>
<q-btn
v-if="row.destination"
icon="check"
color="primary"
:label="$t('Save')"
flat
dense
@click="saveSlots"
/>
</q-td>
<q-td auto-width />
</q-tr>
</template>
<template v-slot:body="props">
<q-tr>
<q-td auto-width />
<q-td
v-for="col in props.cols"
:key="col.name"
>
<div
v-if="col.name === 'slot'"
>
{{ col.value }}
</div>
<csc-data-table-edit-input
v-if="col.name === 'destination'"
:column="col"
:row="props.row"
:value="col.value"
@changed="editDestination(props.rowIndex, $event.value)"
/>
</q-td>
<q-td>
<q-btn
icon="delete"
color="negative"
flat
dense
@click="confirmRowDeletion(props.row.slot, props.rowIndex)"
/>
<q-btn
v-if="isRowDirty(props.rowIndex)"
icon="check"
color="primary"
:label="$t('Save')"
flat
dense
@click="saveSlots"
/>
</q-td>
<q-td auto-width />
</q-tr>
</template>
</q-table>
</template>
<script>
import _ from 'lodash'
import { mapWaitingActions } from 'vue-wait'
import { mapGetters } from 'vuex'
import { required } from 'vuelidate/lib/validators'
import { showGlobalError, showToast } from 'src/helpers/ui'
import CscDataTableEditInput from 'components/CscDataTableEditInput'
import CscRemoveDialog from 'components/CscRemoveDialog'
export default {
name: 'CscPbxAutoAttendantSlotsTable',
components: {
CscDataTableEditInput
},
props: {
data: {
type: Array,
default: undefined
},
subscriberId: {
type: Number,
default: undefined
}
},
data () {
return {
slots: [],
unsavedSlots: [],
dirtySlots: [],
columns: [
{
name: 'slot',
align: 'left',
label: this.$t('Slot'),
field: row => row.slot,
componentOptions: this.slotsNumbers
},
{
name: 'destination',
align: 'left',
label: this.$t('Destination'),
field: row => row.destination,
componentValidations: [this.getDestinationValidation()]
}
],
pagination: {
page: 1,
rowsPerPage: 0 // 0 means all rows
}
}
},
computed: {
...mapGetters('pbxAutoAttendants', [
'slotsNumbers',
'newSlots'
]),
isRowDirty: (state) => (rowIndex) => {
return state.dirtySlots.includes(rowIndex)
}
},
watch: {
data () {
this.initTable()
}
},
mounted () {
this.initTable()
},
methods: {
...mapWaitingActions('pbxAutoAttendants', {
updateSubscriberSlots: 'csc-pbx-autoattendant-slots-table',
editNewSlot: 'csc-pbx-autoattendant-slots-table',
deleteNewSlot: 'csc-pbx-autoattendant-slots-table',
resetAllNewSlots: 'csc-pbx-autoattendant-slots-table'
}),
initTable () {
this.slots = _.cloneDeep(this.data)
this.unsavedSlots = this.newSlots.filter(slot => slot.subscriber_id === this.subscriberId)[0].slots
this.dirtySlots = []
},
updateNewSlotDestination (index, value) {
this.editNewSlot({
subscriberId: this.subscriberId,
index: index,
destination: value
})
},
resetNewSlot (index) {
this.deleteNewSlot({
subscriberId: this.subscriberId,
index: index
})
},
async editDestination (rowIndex, value) {
const destination = this.slots[rowIndex].destination
if (value !== null && value !== '' && destination !== value) {
this.slots[rowIndex].destination = value
this.dirtySlots.push(rowIndex)
} else {
this.dirtySlots = this.dirtySlots.filter(item => item !== rowIndex)
}
},
confirmRowDeletion (slot, rowIndex) {
this.$q.dialog({
component: CscRemoveDialog,
parent: this,
title: this.$t('Delete slot?'),
message: this.$t('You are about to delete slot {slot}', { slot: slot })
}).onOk(() => {
this.deleteSlot(rowIndex)
})
},
async deleteSlot (rowIndex) {
this.slots = this.slots.filter((slot, index) => index !== rowIndex)
this.saveSlots()
},
async saveSlots () {
for (const newSlot of this.unsavedSlots) {
if (!newSlot.destination) {
showGlobalError(this.$t('Please fill or remove the empty slots'))
return
}
}
await this.updateSubscriberSlots({
subscriberId: this.subscriberId,
slots: [...this.unsavedSlots, ...this.slots]
})
this.resetAllNewSlots(this.subscriberId)
showToast(this.$t('Slots saved successfully'))
},
hasExistingSlotValue (index, fieldName) {
return this.slots[index] ? this.slots[index][fieldName] : false
},
getDestinationValidation () {
return {
name: 'required',
validator: required,
error: this.$t('Destination must not be empty')
}
}
// This can be applied as format function in case we want
// the prevent the user to edit prefix/suffix (sip: and @domain)
// of the destinations
//
// extractDestination (value) {
// return value.match(/(?<=sip:)(.*?)(?=@)/)[0]
// }
}
}
</script>

@ -51,7 +51,7 @@
</div>
</div>
</div>
<csc-pbx-attendant-selection
<csc-pbx-auto-attendant-selection
:value="selectedKeySubscriber"
:options="subscriberOptions"
@input="keySubscriberChanged"
@ -100,18 +100,18 @@
<script>
import _ from 'lodash'
import CscPbxAutoAttendantSelection from './CscPbxAutoAttendantSelection'
import {
Platform
} from 'quasar'
import {
BoundingBox2D
} from 'src/helpers/graphics'
import CscPbxAttendantSelection from './CscPbxAttendantSelection'
export default {
name: 'CscPbxDeviceConfig',
components: {
CscPbxAttendantSelection
CscPbxAutoAttendantSelection
},
props: {
device: {

@ -49,7 +49,7 @@
@opened="$emit('model-select-opened')"
@input="triggerFilter"
/>
<csc-pbx-attendant-selection
<csc-pbx-auto-attendant-selection
v-if="filterType === 'display_name'"
use-input
dense
@ -88,14 +88,14 @@
<script>
import _ from 'lodash'
import CscPbxModelSelect from '../PbxConfiguration/CscPbxModelSelect'
import CscPbxAttendantSelection from '../PbxConfiguration/CscPbxAttendantSelection'
import CscPbxAutoAttendantSelection from './CscPbxAutoAttendantSelection'
import { mapActions, mapState } from 'vuex'
export default {
name: 'CscPbxDeviceFilters',
components: {
CscPbxModelSelect,
CscPbxAttendantSelection
CscPbxAutoAttendantSelection
},
props: {
loading: {

@ -12,6 +12,7 @@
"404 Not Found": "404 Not Found",
"A default subscriber sound set to be set before being able to select, in the Sound Sets page.": "Ein Standard-Soundset muss festgelegt werden, bevor ein Soundset ausgewählt werden kann.",
"ACL": "",
"ADD SLOT": "",
"Abort": "Abbrechen",
"Accepted email address to allow mail2fax transmission.": "",
"Active": "",
@ -23,6 +24,7 @@
"Add Group": "Gruppe hinzufügen",
"Add Number": "Weiterleitung zu Rufnummern hinzuğgen",
"Add Seat": "Seat hinzufügen",
"Add Slots": "",
"Add Sound Set": "Soundset hinzufügen",
"Add Speed Dial": "Kurzwahl hinzufügen",
"Add Time": "Zeitraum hinzufügen",
@ -34,6 +36,7 @@
"Add forwarding": "",
"Add new": "Neue hinzufügen",
"Add number": "Nummer hinzufügen",
"Add slot": "",
"Add source": "Anrufer hinzufügen",
"Add time": "",
"Add time range": "",
@ -94,6 +97,7 @@
"Audio + Video": "Audio und Video",
"Audio Call": "Audioanruf",
"Audio Only": "Nur Audio",
"Auto-attendant": "",
"Blacklist": "Blacklist",
"Block Incoming": "Eingehende Anrufe blockieren",
"Block Incoming/Outgoing": "Ein-/Ausgehende Anrufe blockieren",
@ -182,6 +186,7 @@
"Delete forwarding": "",
"Delete from {groupName} forwarding": "",
"Delete recording": "",
"Delete slot?": "",
"Delete sourceset": "Anruferliste löschen",
"Delete voicemail after email notification is delivered": "Voicemail löschen, wenn die E-Mail-Benachrichtigung gesendet wurde",
"Delete {groupName} forwarding group": "",
@ -196,6 +201,7 @@
"Destination Email": "",
"Destination Number": "Zielrufnummer",
"Destination email to send the secret key renew notification to.": "",
"Destination must not be empty": "",
"Destinations": "",
"Devices": "Geräte",
"Disable": "Deaktivieren",
@ -341,6 +347,7 @@
"No Voicemails found": "Keine Voicemails gefunden",
"No call goes to primary number": "",
"No call queues created yet": "Es wurden noch keine Anrufwarteschlangen erstellt.",
"No data found": "",
"No data to save. Please provide at least one time range.": "",
"No destinations created yet": "",
"No devices created yet": "Noch keine Geräte angelegt",
@ -390,6 +397,9 @@
"Play sound in loop": "Sound in Schleife abspielen",
"Playing in loop": "In Schleife abspielen",
"Please add a destination to the group before adding conditions": "",
"Please fill all the empty destinations": "",
"Please fill all the fields": "",
"Please fill or remove the empty slots": "",
"Please select a valid timerange": "",
"Please select an option": "",
"Primary Number": "Primär-Rufnummer",
@ -500,6 +510,12 @@
"Show filters": "Filter anzeigen",
"Sign In": "Log-in",
"Slot": "Kurzwahl",
"Slot added successfully": "",
"Slot edited successfully": "",
"Slot successfully deleted": "",
"Slot {number}": "",
"Slots saved successfully": "",
"Slots successfully added": "",
"Something went wrong. Please retry later": "",
"Sound Set": "Soundset",
"Sound Sets": "Sound Sets",
@ -516,6 +532,7 @@
"Start time should be less than End time": "",
"Station name": "Gerätename",
"Su": "",
"Subscriber": "",
"Subscriber Sign In": "Subscriber Log-in",
"Sunday": "Sonntag",
"Super": "Hoch",
@ -604,6 +621,7 @@
"You are about to delete {name} sourceset": "",
"You are about to delete {name} timeset": "",
"You are about to delete recording #{id}": "",
"You are about to delete slot {slot}": "",
"You are about to delete time range \"{from} - {to}\"": "",
"You are about to delete {destination} from {groupName} call forwarding": "",
"You are about to delete {groupName} call forwarding group": "",
@ -697,4 +715,4 @@
"{field} must have at most {maxLength} letters": "{field} darf höchstens {maxLength} Buchstaben beinhalten",
"{mode} of sources": "{mode} of sources",
"{mode} own phone": "Eigene Rufnummer {mode}"
}
}

@ -12,6 +12,7 @@
"404 Not Found": "404 Not Found",
"A default subscriber sound set to be set before being able to select, in the Sound Sets page.": "A default subscriber sound set to be set before being able to select, in the Sound Sets page.",
"ACL": "ACL",
"ADD SLOT": "ADD SLOT",
"Abort": "Abort",
"Accepted email address to allow mail2fax transmission.": "Accepted email address to allow mail2fax transmission.",
"Active": "Active",
@ -23,6 +24,7 @@
"Add Group": "Add Group",
"Add Number": "Add Number",
"Add Seat": "Add Seat",
"Add Slots": "Add Slots",
"Add Sound Set": "Add Sound Set",
"Add Speed Dial": "Add Speed Dial",
"Add Time": "Add Time",
@ -34,6 +36,7 @@
"Add forwarding": "Add forwarding",
"Add new": "Add new",
"Add number": "Add number",
"Add slot": "Add slot",
"Add source": "Add source",
"Add time": "Add time",
"Add time range": "Add time range",
@ -94,6 +97,7 @@
"Audio + Video": "Audio + Video",
"Audio Call": "Audio Call",
"Audio Only": "Audio Only",
"Auto-attendant": "Auto-attendant",
"Blacklist": "Blacklist",
"Block Incoming": "Block Incoming",
"Block Incoming/Outgoing": "Block Incoming/Outgoing",
@ -182,6 +186,7 @@
"Delete forwarding": "Delete forwarding",
"Delete from {groupName} forwarding": "Delete from {groupName} forwarding",
"Delete recording": "Delete recording",
"Delete slot?": "Delete slot?",
"Delete sourceset": "Delete sourceset",
"Delete voicemail after email notification is delivered": "Delete voicemail after email notification is delivered",
"Delete {groupName} forwarding group": "Delete {groupName} forwarding group",
@ -196,6 +201,7 @@
"Destination Email": "Destination Email",
"Destination Number": "Destination Number",
"Destination email to send the secret key renew notification to.": "Destination email to send the secret key renew notification to.",
"Destination must not be empty": "Destination must not be empty",
"Destinations": "Destinations",
"Devices": "Devices",
"Disable": "Disable",
@ -341,6 +347,7 @@
"No Voicemails found": "No Voicemails found",
"No call goes to primary number": "No call goes to primary number",
"No call queues created yet": "No call queues created yet",
"No data found": "No data found",
"No data to save. Please provide at least one time range.": "No data to save. Please provide at least one time range.",
"No destinations created yet": "No destinations created yet",
"No devices created yet": "No devices created yet",
@ -390,6 +397,9 @@
"Play sound in loop": "Play sound in loop",
"Playing in loop": "Playing in loop",
"Please add a destination to the group before adding conditions": "Please add a destination to the group before adding conditions",
"Please fill all the empty destinations": "Please fill all the empty destinations",
"Please fill all the fields": "Please fill all the fields",
"Please fill or remove the empty slots": "Please fill or remove the empty slots",
"Please select a valid timerange": "Please select a valid timerange",
"Please select an option": "Please select an option",
"Primary Number": "Primary Number",
@ -500,6 +510,12 @@
"Show filters": "Show filters",
"Sign In": "Sign In",
"Slot": "Slot",
"Slot added successfully": "Slot added successfully",
"Slot edited successfully": "Slot edited successfully",
"Slot successfully deleted": "Slot successfully deleted",
"Slot {number}": "Slot {number}",
"Slots saved successfully": "Slots saved successfully",
"Slots successfully added": "Slots successfully added",
"Something went wrong. Please retry later": "Something went wrong. Please retry later",
"Sound Set": "Sound Set",
"Sound Sets": "Sound Sets",
@ -516,6 +532,7 @@
"Start time should be less than End time": "Start time should be less than End time",
"Station name": "Station name",
"Su": "Su",
"Subscriber": "Subscriber",
"Subscriber Sign In": "Subscriber Sign In",
"Sunday": "Sunday",
"Super": "Super",
@ -604,6 +621,7 @@
"You are about to delete {name} sourceset": "You are about to delete {name} sourceset",
"You are about to delete {name} timeset": "You are about to delete {name} timeset",
"You are about to delete recording #{id}": "You are about to delete recording #{id}",
"You are about to delete slot {slot}": "You are about to delete slot {slot}",
"You are about to delete time range \"{from} - {to}\"": "You are about to delete time range \"{from} - {to}\"",
"You are about to delete {destination} from {groupName} call forwarding": "You are about to delete {destination} from {groupName} call forwarding",
"You are about to delete {groupName} call forwarding group": "You are about to delete {groupName} call forwarding group",
@ -697,4 +715,4 @@
"{field} must have at most {maxLength} letters": "{field} must have at most {maxLength} letters",
"{mode} of sources": "{mode} of sources",
"{mode} own phone": "{mode} own phone"
}
}

@ -12,6 +12,7 @@
"404 Not Found": "404 Not Encontrado",
"A default subscriber sound set to be set before being able to select, in the Sound Sets page.": "Un conjunto de sonido de suscriptor predeterminado que se establecerá antes de poder seleccionar, en la página de Conjuntos de Sonido.",
"ACL": "",
"ADD SLOT": "",
"Abort": "Abortar",
"Accepted email address to allow mail2fax transmission.": "",
"Active": "",
@ -23,6 +24,7 @@
"Add Group": "Agregar grupo",
"Add Number": "Agregar Número",
"Add Seat": "Agregar asiento",
"Add Slots": "",
"Add Sound Set": "Añadir Conjunto de Sonido",
"Add Speed Dial": "Agregar marcado rápido",
"Add Time": "Agregar tiempo",
@ -34,6 +36,7 @@
"Add forwarding": "",
"Add new": "Añadir nuevo",
"Add number": "Agregar número",
"Add slot": "",
"Add source": "Agregar fuente",
"Add time": "",
"Add time range": "",
@ -94,6 +97,7 @@
"Audio + Video": "Audio + Video",
"Audio Call": "Llamada de audio",
"Audio Only": "Solo Audio",
"Auto-attendant": "",
"Blacklist": "Lista Negra",
"Block Incoming": "Bloquear Entrantes",
"Block Incoming/Outgoing": "Bloquear Entrantes/Salientes",
@ -182,6 +186,7 @@
"Delete forwarding": "",
"Delete from {groupName} forwarding": "",
"Delete recording": "",
"Delete slot?": "",
"Delete sourceset": "Eliminar el conjunto de fuentes",
"Delete voicemail after email notification is delivered": "Eliminar el correo de voz después de enviar la notificación por correo electrónico",
"Delete {groupName} forwarding group": "",
@ -196,6 +201,7 @@
"Destination Email": "",
"Destination Number": "Número de destino",
"Destination email to send the secret key renew notification to.": "",
"Destination must not be empty": "",
"Destinations": "",
"Devices": "Dispositivos",
"Disable": "Desactivar",
@ -341,6 +347,7 @@
"No Voicemails found": "No se encontraron mensajes de voz",
"No call goes to primary number": "",
"No call queues created yet": "Aún no se han creado colas de llamadas",
"No data found": "",
"No data to save. Please provide at least one time range.": "",
"No destinations created yet": "",
"No devices created yet": "Aún no se han creado dispositivos",
@ -390,6 +397,9 @@
"Play sound in loop": "Reproducir sonido en bucle",
"Playing in loop": "Reproducir en bucle",
"Please add a destination to the group before adding conditions": "",
"Please fill all the empty destinations": "",
"Please fill all the fields": "",
"Please fill or remove the empty slots": "",
"Please select a valid timerange": "",
"Please select an option": "",
"Primary Number": "Número primario",
@ -500,6 +510,12 @@
"Show filters": "Mostrar filtros",
"Sign In": "Iniciar sesión",
"Slot": "Ranura",
"Slot added successfully": "",
"Slot edited successfully": "",
"Slot successfully deleted": "",
"Slot {number}": "",
"Slots saved successfully": "",
"Slots successfully added": "",
"Something went wrong. Please retry later": "",
"Sound Set": "Conjunto de sonido",
"Sound Sets": "Conjuntos de Sonido",
@ -516,6 +532,7 @@
"Start time should be less than End time": "",
"Station name": "Nombre de la estación",
"Su": "",
"Subscriber": "",
"Subscriber Sign In": "Iniciar sesión de suscriptor",
"Sunday": "Domingo",
"Super": "Super",
@ -604,6 +621,7 @@
"You are about to delete {name} sourceset": "",
"You are about to delete {name} timeset": "",
"You are about to delete recording #{id}": "",
"You are about to delete slot {slot}": "",
"You are about to delete time range \"{from} - {to}\"": "",
"You are about to delete {destination} from {groupName} call forwarding": "",
"You are about to delete {groupName} call forwarding group": "",
@ -697,4 +715,4 @@
"{field} must have at most {maxLength} letters": "{field} debe tener como máximo {maxLength} letras",
"{mode} of sources": "{mode} de fuentes",
"{mode} own phone": "{mode} teléfono propio"
}
}

@ -12,6 +12,7 @@
"404 Not Found": "Erreur 404",
"A default subscriber sound set to be set before being able to select, in the Sound Sets page.": "",
"ACL": "",
"ADD SLOT": "",
"Abort": "Abandonner",
"Accepted email address to allow mail2fax transmission.": "",
"Active": "",
@ -23,6 +24,7 @@
"Add Group": "Ajouter un groupe",
"Add Number": "Ajouter un numéro",
"Add Seat": "Ajouter un siège",
"Add Slots": "",
"Add Sound Set": "",
"Add Speed Dial": "Ajouter un raccourci",
"Add Time": "Ajouter une heure",
@ -34,6 +36,7 @@
"Add forwarding": "",
"Add new": "En ajouter un nouveau",
"Add number": "Ajouter un numéro",
"Add slot": "",
"Add source": "Ajouter une source",
"Add time": "",
"Add time range": "",
@ -94,6 +97,7 @@
"Audio + Video": "Audio et vidéo",
"Audio Call": "Appel audio",
"Audio Only": "Audio seulement",
"Auto-attendant": "",
"Blacklist": "Liste noire",
"Block Incoming": "",
"Block Incoming/Outgoing": "",
@ -182,6 +186,7 @@
"Delete forwarding": "",
"Delete from {groupName} forwarding": "",
"Delete recording": "",
"Delete slot?": "",
"Delete sourceset": "Supprimer la liste de sources",
"Delete voicemail after email notification is delivered": "Supprimer le message vocal une fois la notification e-mail délivrée",
"Delete {groupName} forwarding group": "",
@ -196,6 +201,7 @@
"Destination Email": "",
"Destination Number": "Numéro de destination",
"Destination email to send the secret key renew notification to.": "",
"Destination must not be empty": "",
"Destinations": "",
"Devices": "Postes",
"Disable": "Désactiver",
@ -341,6 +347,7 @@
"No Voicemails found": "Message vocaux introuvables",
"No call goes to primary number": "",
"No call queues created yet": "Aucune file dattente de créée",
"No data found": "",
"No data to save. Please provide at least one time range.": "",
"No destinations created yet": "",
"No devices created yet": "Aucun poste créé",
@ -390,6 +397,9 @@
"Play sound in loop": "",
"Playing in loop": "",
"Please add a destination to the group before adding conditions": "",
"Please fill all the empty destinations": "",
"Please fill all the fields": "",
"Please fill or remove the empty slots": "",
"Please select a valid timerange": "",
"Please select an option": "",
"Primary Number": "Numéro principal",
@ -500,6 +510,12 @@
"Show filters": "Afficher les filtre",
"Sign In": "Authentification",
"Slot": "Emplacement",
"Slot added successfully": "",
"Slot edited successfully": "",
"Slot successfully deleted": "",
"Slot {number}": "",
"Slots saved successfully": "",
"Slots successfully added": "",
"Something went wrong. Please retry later": "",
"Sound Set": "",
"Sound Sets": "",
@ -516,6 +532,7 @@
"Start time should be less than End time": "",
"Station name": "Nom du poste",
"Su": "",
"Subscriber": "",
"Subscriber Sign In": "Authentification de labonné",
"Sunday": "Dimanche",
"Super": "Supérieur",
@ -604,6 +621,7 @@
"You are about to delete {name} sourceset": "",
"You are about to delete {name} timeset": "",
"You are about to delete recording #{id}": "",
"You are about to delete slot {slot}": "",
"You are about to delete time range \"{from} - {to}\"": "",
"You are about to delete {destination} from {groupName} call forwarding": "",
"You are about to delete {groupName} call forwarding group": "",
@ -697,4 +715,4 @@
"{field} must have at most {maxLength} letters": "{field} doit avoir au maximum {maxLength} caractères",
"{mode} of sources": "Sources {mode}",
"{mode} own phone": "{mode} téléphone personnel"
}
}

@ -12,6 +12,7 @@
"404 Not Found": "404 Not Found",
"A default subscriber sound set to be set before being able to select, in the Sound Sets page.": "Un set di messaggi predefinito per gli utenti, disponibile prima che se ne possa selezionare uno nella pagina Set di Messaggi.",
"ACL": "",
"ADD SLOT": "",
"Abort": "Abbandona",
"Accepted email address to allow mail2fax transmission.": "",
"Active": "",
@ -23,6 +24,7 @@
"Add Group": "Aggiungi gruppo",
"Add Number": "Aggiungi Numero",
"Add Seat": "Aggiungi postazione",
"Add Slots": "",
"Add Sound Set": "Aggiungi set messaggi",
"Add Speed Dial": "Aggiungi Selezione Rapida",
"Add Time": "Aggiungi orario",
@ -34,6 +36,7 @@
"Add forwarding": "",
"Add new": "Aggiungi nuovo",
"Add number": "Aggiungi numero",
"Add slot": "",
"Add source": "Aggiungi ",
"Add time": "",
"Add time range": "",
@ -94,6 +97,7 @@
"Audio + Video": "Audio + Video",
"Audio Call": "Chiamata Vocale",
"Audio Only": "Solo Audio",
"Auto-attendant": "",
"Blacklist": "Lista Nera",
"Block Incoming": "Blocca Entranti",
"Block Incoming/Outgoing": "Blocca Entranti/Uscenti",
@ -182,6 +186,7 @@
"Delete forwarding": "",
"Delete from {groupName} forwarding": "",
"Delete recording": "",
"Delete slot?": "",
"Delete sourceset": "Cancella set pattern sorgente",
"Delete voicemail after email notification is delivered": "Cancella il messaggio vocale dopo che l'email di notifica è stata inoltrata",
"Delete {groupName} forwarding group": "",
@ -196,6 +201,7 @@
"Destination Email": "",
"Destination Number": "Numero di Destinazione",
"Destination email to send the secret key renew notification to.": "",
"Destination must not be empty": "",
"Destinations": "",
"Devices": "Dispositivi",
"Disable": "Disabilita",
@ -341,6 +347,7 @@
"No Voicemails found": "Non è stato trovato nessun Messaggio Vocale",
"No call goes to primary number": "",
"No call queues created yet": "Nessuna coda creata",
"No data found": "",
"No data to save. Please provide at least one time range.": "",
"No destinations created yet": "",
"No devices created yet": "Nessun dispositivo creato",
@ -390,6 +397,9 @@
"Play sound in loop": "Ripoduci il messaggio a ciclo continuo",
"Playing in loop": "Ripoduci a ciclo continuo",
"Please add a destination to the group before adding conditions": "",
"Please fill all the empty destinations": "",
"Please fill all the fields": "",
"Please fill or remove the empty slots": "",
"Please select a valid timerange": "",
"Please select an option": "",
"Primary Number": "Numero Principale",
@ -500,6 +510,12 @@
"Show filters": "Mostra filtri",
"Sign In": "Accedi",
"Slot": "Posizione",
"Slot added successfully": "",
"Slot edited successfully": "",
"Slot successfully deleted": "",
"Slot {number}": "",
"Slots saved successfully": "",
"Slots successfully added": "",
"Something went wrong. Please retry later": "",
"Sound Set": "Set Messaggi",
"Sound Sets": "Annunci",
@ -516,6 +532,7 @@
"Start time should be less than End time": "",
"Station name": "Nome stazione",
"Su": "",
"Subscriber": "",
"Subscriber Sign In": "Accedi come utente",
"Sunday": "Domenica",
"Super": "Super",
@ -604,6 +621,7 @@
"You are about to delete {name} sourceset": "",
"You are about to delete {name} timeset": "",
"You are about to delete recording #{id}": "",
"You are about to delete slot {slot}": "",
"You are about to delete time range \"{from} - {to}\"": "",
"You are about to delete {destination} from {groupName} call forwarding": "",
"You are about to delete {groupName} call forwarding group": "",
@ -697,4 +715,4 @@
"{field} must have at most {maxLength} letters": "Il campo {field} può contenere al massimo {maxLength} lettere",
"{mode} of sources": "{mode} dei pattern sorgente",
"{mode} own phone": "{mode} proprio telefono"
}
}

@ -0,0 +1,212 @@
<template>
<csc-page
class="q-pa-lg"
>
<div class="q-pa-md">
<q-table
:data="data"
:columns="columns"
:loading="$wait.is('csc-pbx-auto-attendant')"
row-key="name"
flat
:pagination.sync="pagination"
@request="fetchWithPagination"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width />
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
class="table-header"
>
{{ col.label }}
</q-th>
<q-th auto-width />
<q-th auto-width />
</q-tr>
</template>
<template
v-slot:body="props"
>
<q-tr>
<q-td auto-width />
<q-td
v-for="col in props.cols"
:key="col.name"
>
{{ col.value }}
</q-td>
<q-td auto-width>
<q-btn-dropdown
size="md"
color="primary"
:label="$t('Add slot')"
:disabled="getAvailableSlots(props.row.slots, props.row.subscriber_id).length === 0"
icon="add"
dropdown-icon=" "
flat
>
<q-list
v-for="availableSlot in getAvailableSlots(props.row.slots, props.row.subscriber_id)"
:key="availableSlot"
>
<csc-popup-menu-item
:label="availableSlot"
@click="addSlot(props.row.subscriber_id, availableSlot)"
/>
</q-list>
</q-btn-dropdown>
<q-btn
size="md"
color="primary"
round
flat
:icon="isRowExpanded(props.row.subscriber_id) ? 'expand_less' : 'expand_more'"
@click="updateCollapseArray(props.row.subscriber_id)"
/>
</q-td>
<q-td auto-width />
</q-tr>
<q-tr
v-show="isRowExpanded(props.row.subscriber_id)"
no-hover
>
<q-td
colspan="100%"
class="table-cell"
>
<csc-pbx-auto-attendant-slots-table
:data="sortedSlots(props.row.slots)"
:subscriber-id="props.row.subscriber_id"
/>
</q-td>
</q-tr>
</template>
</q-table>
</div>
</csc-page>
</template>
<script>
import _ from 'lodash'
import { mapGetters } from 'vuex'
import { mapWaitingActions } from 'vue-wait'
import { displayName } from 'src/filters/subscriber'
import CscPage from 'components/CscPage'
import CscPbxAutoAttendantSlotsTable from 'components/pages/PbxConfiguration/CscPbxAutoAttendantSlotsTable'
import CscPopupMenuItem from 'components/CscPopupMenuItem'
export default {
name: 'CscPagePbxAutoAttendant',
components: {
CscPage,
CscPopupMenuItem,
CscPbxAutoAttendantSlotsTable
},
data () {
return {
data: [],
rowStatus: [],
columns: [
{
name: 'Subscriber Id',
required: true,
label: this.$t('Id'),
align: 'left',
field: row => row.subscriber_id,
format: val => `${val}`
},
{
name: 'Name',
required: true,
align: 'left',
label: this.$t('Name'),
field: row => displayName(row.subscriber)
}
],
pagination: {
page: 1,
rowsPerPage: 5,
rowsNumber: 0
}
}
},
computed: {
...mapGetters('pbxAutoAttendants', [
'slots',
'slotsNumbers',
'newSlots'
])
},
watch: {
slots () {
this.data = this.slots
this.rowStatus = this.slots.map(slot => {
return {
subscriber_id: slot.subscriber_id,
expanded: false
}
})
}
},
mounted () {
this.fetchWithPagination({
pagination: this.pagination
})
},
methods: {
...mapWaitingActions('pbxAutoAttendants', {
fetchAutoAttendants: 'csc-pbx-auto-attendant',
createNewSlot: 'csc-pbx-auto-attendant'
}),
async fetchWithPagination (props) {
const { page, rowsPerPage } = props.pagination
const count = await this.fetchAutoAttendants({
page: page,
rows: rowsPerPage
})
this.pagination = { ...props.pagination }
this.pagination.rowsNumber = count
},
async addSlot (subscriberId, slot) {
this.createNewSlot({
subscriberId: subscriberId,
slot: slot
})
this.expandRow(subscriberId)
},
isRowExpanded (subscriberId) {
const rowStatus = this.rowStatus.filter(row => row.subscriber_id === subscriberId)[0] || null
return rowStatus && rowStatus.expanded
},
updateCollapseArray (subscriberId) {
const rowStatus = this.rowStatus.filter(row => row.subscriber_id === subscriberId)[0]
rowStatus.expanded = !rowStatus.expanded
},
getAvailableSlots (subscriberSlots, subscriberId) {
const subscriberSavedSlots = subscriberSlots.map(item => item.slot)
const subscriberNewSlots = this.newSlots.filter(item => item.subscriber_id === subscriberId)
subscriberSlots = subscriberNewSlots.length > 0
? [...subscriberSavedSlots, ...subscriberNewSlots[0].slots.map(item => item.slot)]
: subscriberSavedSlots
const availableSlots = this.slotsNumbers.filter(slot => !subscriberSlots.includes(slot))
return availableSlots
},
expandRow (subscriberId) {
const status = this.rowStatus.filter(row => row.subscriber_id === subscriberId)[0]
status.expanded = true
},
sortedSlots (slots) {
const sorted = _.cloneDeep(slots)
return sorted.sort((a, b) => a.slot > b.slot ? 1 : -1)
}
}
}
</script>
<style lang="stylus" rel="stylesheet/stylus" scoped>
.table-header
font-size 15px
.table-cell
padding 0
</style>

@ -22,6 +22,7 @@ import CscPagePbxDevices from 'src/pages/CscPagePbxDevices'
import CscPagePbxCallQueues from 'src/pages/CscPagePbxCallQueues'
import CscPagePbxSoundSets from 'src/pages/CscPagePbxSoundSets'
import CscPagePbxMsConfigs from 'src/pages/CscPagePbxMsConfigs'
import CscPagePbxAutoAttendant from 'src/pages/CscPagePbxAutoAttendant'
import CscPagePbxSettings from 'src/pages/CscPagePbxSettings'
import CscPageVoicebox from 'src/pages/CscPageVoicebox'
import CscPageFaxSettings from 'src/pages/CscPageFaxSettings'
@ -190,6 +191,14 @@ export default function routes (app) {
subtitle: i18n.t('Manager Secretary')
}
},
{
path: 'pbx-configuration/auto-attendant',
component: CscPagePbxAutoAttendant,
meta: {
title: i18n.t('PBX Configuration'),
subtitle: i18n.t('Auto-attendant')
}
},
{
path: 'voicebox',
component: CscPageVoicebox,

@ -21,6 +21,7 @@ import PbxDevicesModule from './pbx-devices'
import PbxCallQueuesModule from './pbx-callqueues'
import PbxSoundSetsModule from './pbx-soundsets'
import PbxMsConfigsModule from './pbx-ms-configs'
import PbxAutoAttendants from './pbx-auto-attendants'
import ReminderModule from './reminder'
import SpeedDialModule from './speed-dial'
@ -76,7 +77,8 @@ export default function (/* { ssrContext } */) {
pbxCallQueues: PbxCallQueuesModule,
pbxSoundSets: PbxSoundSetsModule,
pbxMsConfigs: PbxMsConfigsModule,
callForwarding: CallForwardingModule
callForwarding: CallForwardingModule,
pbxAutoAttendants: PbxAutoAttendants
},
state: {

@ -0,0 +1,115 @@
import { getAutoAttendants, editSubscriberSlots } from '../api/pbx-auto-attendants'
import { getSubscribers } from '../api/subscriber'
import { displayName } from 'src/filters/subscriber'
export default {
namespaced: true,
state: {
slots: [],
newSlots: [],
subscribers: [],
slotsNumbers: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
},
getters: {
slots (state) {
return state.slots
},
slotsNumbers (state) {
return state.slotsNumbers
},
newSlots (state) {
return state.newSlots
},
subscribers (state) {
return state.subscribers.map(subscriber => {
return {
label: displayName(subscriber),
value: subscriber.id
}
})
}
},
mutations: {
slots (state, data) {
state.slots = data
},
newSlots (state, data) {
for (const slot of data) {
state.newSlots.push({
subscriber_id: slot.subscriber_id,
slots: []
})
}
},
subscriberSlots (state, data) {
const subscriberSlots = state.slots.filter(slot => slot.subscriber_id === data.subscriberId)[0]
subscriberSlots.slots = data.slots
},
createNewSlot (state, data) {
const subscriberSlots = state.newSlots.filter(slot => slot.subscriber_id === data.subscriberId)[0]
subscriberSlots.slots.push({
slot: data.slot,
destination: null
})
},
editNewSlot (state, data) {
const subscriberSlots = state.newSlots.filter(slot => slot.subscriber_id === data.subscriberId)[0]
subscriberSlots.slots[data.index].destination = data.destination
},
deleteNewSlot (state, data) {
const subscriberSlots = state.newSlots.filter(slot => slot.subscriber_id === data.subscriberId)[0]
subscriberSlots.slots.splice(data.index, 1)
},
resetNewSlots (state, subscriberId) {
const subscriberSlots = state.newSlots.filter(slot => slot.subscriber_id === subscriberId)[0]
subscriberSlots.slots.splice(0, subscriberSlots.slots.length)
},
subscribers (state, subscribers) {
state.subscribers = subscribers
}
},
actions: {
async fetchAutoAttendants (context, options) {
const autoAttendants = await getAutoAttendants(options)
context.commit('slots', autoAttendants._embedded['ngcp:autoattendants'])
context.commit('newSlots', autoAttendants._embedded['ngcp:autoattendants'])
return autoAttendants.total_count
},
async fetchSubscribers (context, subscriberName) {
const subscribers = await getSubscribers({
params: {
display_name: subscriberName || '*'
}
})
context.commit('subscribers', subscribers.items)
},
async updateSubscriberSlots (context, options) {
const slots = await editSubscriberSlots(options)
context.commit('subscriberSlots', {
subscriberId: options.subscriberId,
slots: slots
})
},
createNewSlot (context, options) {
context.commit('createNewSlot', {
subscriberId: options.subscriberId,
slot: options.slot
})
},
editNewSlot (context, options) {
context.commit('editNewSlot', {
subscriberId: options.subscriberId,
index: options.index,
destination: options.destination
})
},
deleteNewSlot (context, options) {
context.commit('deleteNewSlot', {
subscriberId: options.subscriberId,
index: options.index
})
},
resetAllNewSlots (context, subscriberId) {
context.commit('resetNewSlots', subscriberId)
}
}
}
Loading…
Cancel
Save