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 5 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', icon: 'arrow_forward',
label: this.$t('Manager Secretary'), label: this.$t('Manager Secretary'),
visible: true 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' default: 'primary'
}, },
label: { label: {
type: String, type: [String, Number],
default: '' default: ''
}, },
sublabel: { 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> </script>
<style lang="stylus" rel="stylesheet/stylus"> <style lang="stylus" rel="stylesheet/stylus" scoped>
.csc-cf-daterange-btn-cont .csc-cf-daterange-btn-cont
margin-top 10px margin-top 10px
width 100% width 100%
text-align center text-align center
.q-menu
min-width auto !important
.q-datetime-days div:not(.q-datetime-day-active), .q-datetime-days div:not(.q-datetime-day-active),
.q-datetime-dark, .q-datetime-dark,
.q-datetime-range .q-datetime-input .q-input-target, .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> <script>
export default { export default {
name: 'CscPbxAttendantSelection', name: 'CscPbxAutoAttendantSelection',
props: { props: {
showSelectedItemIcon: { showSelectedItemIcon: {
type: Boolean, 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> </div>
</div> </div>
<csc-pbx-attendant-selection <csc-pbx-auto-attendant-selection
:value="selectedKeySubscriber" :value="selectedKeySubscriber"
:options="subscriberOptions" :options="subscriberOptions"
@input="keySubscriberChanged" @input="keySubscriberChanged"
@ -100,18 +100,18 @@
<script> <script>
import _ from 'lodash' import _ from 'lodash'
import CscPbxAutoAttendantSelection from './CscPbxAutoAttendantSelection'
import { import {
Platform Platform
} from 'quasar' } from 'quasar'
import { import {
BoundingBox2D BoundingBox2D
} from 'src/helpers/graphics' } from 'src/helpers/graphics'
import CscPbxAttendantSelection from './CscPbxAttendantSelection'
export default { export default {
name: 'CscPbxDeviceConfig', name: 'CscPbxDeviceConfig',
components: { components: {
CscPbxAttendantSelection CscPbxAutoAttendantSelection
}, },
props: { props: {
device: { device: {

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

@ -12,6 +12,7 @@
"404 Not Found": "404 Not Found", "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.", "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": "", "ACL": "",
"ADD SLOT": "",
"Abort": "Abbrechen", "Abort": "Abbrechen",
"Accepted email address to allow mail2fax transmission.": "", "Accepted email address to allow mail2fax transmission.": "",
"Active": "", "Active": "",
@ -23,6 +24,7 @@
"Add Group": "Gruppe hinzufügen", "Add Group": "Gruppe hinzufügen",
"Add Number": "Weiterleitung zu Rufnummern hinzuğgen", "Add Number": "Weiterleitung zu Rufnummern hinzuğgen",
"Add Seat": "Seat hinzufügen", "Add Seat": "Seat hinzufügen",
"Add Slots": "",
"Add Sound Set": "Soundset hinzufügen", "Add Sound Set": "Soundset hinzufügen",
"Add Speed Dial": "Kurzwahl hinzufügen", "Add Speed Dial": "Kurzwahl hinzufügen",
"Add Time": "Zeitraum hinzufügen", "Add Time": "Zeitraum hinzufügen",
@ -34,6 +36,7 @@
"Add forwarding": "", "Add forwarding": "",
"Add new": "Neue hinzufügen", "Add new": "Neue hinzufügen",
"Add number": "Nummer hinzufügen", "Add number": "Nummer hinzufügen",
"Add slot": "",
"Add source": "Anrufer hinzufügen", "Add source": "Anrufer hinzufügen",
"Add time": "", "Add time": "",
"Add time range": "", "Add time range": "",
@ -94,6 +97,7 @@
"Audio + Video": "Audio und Video", "Audio + Video": "Audio und Video",
"Audio Call": "Audioanruf", "Audio Call": "Audioanruf",
"Audio Only": "Nur Audio", "Audio Only": "Nur Audio",
"Auto-attendant": "",
"Blacklist": "Blacklist", "Blacklist": "Blacklist",
"Block Incoming": "Eingehende Anrufe blockieren", "Block Incoming": "Eingehende Anrufe blockieren",
"Block Incoming/Outgoing": "Ein-/Ausgehende Anrufe blockieren", "Block Incoming/Outgoing": "Ein-/Ausgehende Anrufe blockieren",
@ -182,6 +186,7 @@
"Delete forwarding": "", "Delete forwarding": "",
"Delete from {groupName} forwarding": "", "Delete from {groupName} forwarding": "",
"Delete recording": "", "Delete recording": "",
"Delete slot?": "",
"Delete sourceset": "Anruferliste löschen", "Delete sourceset": "Anruferliste löschen",
"Delete voicemail after email notification is delivered": "Voicemail löschen, wenn die E-Mail-Benachrichtigung gesendet wurde", "Delete voicemail after email notification is delivered": "Voicemail löschen, wenn die E-Mail-Benachrichtigung gesendet wurde",
"Delete {groupName} forwarding group": "", "Delete {groupName} forwarding group": "",
@ -196,6 +201,7 @@
"Destination Email": "", "Destination Email": "",
"Destination Number": "Zielrufnummer", "Destination Number": "Zielrufnummer",
"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": "",
"Destinations": "", "Destinations": "",
"Devices": "Geräte", "Devices": "Geräte",
"Disable": "Deaktivieren", "Disable": "Deaktivieren",
@ -341,6 +347,7 @@
"No Voicemails found": "Keine Voicemails gefunden", "No Voicemails found": "Keine Voicemails gefunden",
"No call goes to primary number": "", "No call goes to primary number": "",
"No call queues created yet": "Es wurden noch keine Anrufwarteschlangen erstellt.", "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 data to save. Please provide at least one time range.": "",
"No destinations created yet": "", "No destinations created yet": "",
"No devices created yet": "Noch keine Geräte angelegt", "No devices created yet": "Noch keine Geräte angelegt",
@ -390,6 +397,9 @@
"Play sound in loop": "Sound in Schleife abspielen", "Play sound in loop": "Sound in Schleife abspielen",
"Playing in loop": "In Schleife abspielen", "Playing in loop": "In Schleife abspielen",
"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 fields": "",
"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": "Primär-Rufnummer", "Primary Number": "Primär-Rufnummer",
@ -500,6 +510,12 @@
"Show filters": "Filter anzeigen", "Show filters": "Filter anzeigen",
"Sign In": "Log-in", "Sign In": "Log-in",
"Slot": "Kurzwahl", "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": "", "Something went wrong. Please retry later": "",
"Sound Set": "Soundset", "Sound Set": "Soundset",
"Sound Sets": "Sound Sets", "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": "Gerätename", "Station name": "Gerätename",
"Su": "", "Su": "",
"Subscriber": "",
"Subscriber Sign In": "Subscriber Log-in", "Subscriber Sign In": "Subscriber Log-in",
"Sunday": "Sonntag", "Sunday": "Sonntag",
"Super": "Hoch", "Super": "Hoch",
@ -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 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} darf höchstens {maxLength} Buchstaben beinhalten", "{field} must have at most {maxLength} letters": "{field} darf höchstens {maxLength} Buchstaben beinhalten",
"{mode} of sources": "{mode} of sources", "{mode} of sources": "{mode} of sources",
"{mode} own phone": "Eigene Rufnummer {mode}" "{mode} own phone": "Eigene Rufnummer {mode}"
} }

@ -12,6 +12,7 @@
"404 Not Found": "404 Not Found", "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.", "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", "ACL": "ACL",
"ADD SLOT": "ADD SLOT",
"Abort": "Abort", "Abort": "Abort",
"Accepted email address to allow mail2fax transmission.": "Accepted email address to allow mail2fax transmission.", "Accepted email address to allow mail2fax transmission.": "Accepted email address to allow mail2fax transmission.",
"Active": "Active", "Active": "Active",
@ -23,6 +24,7 @@
"Add Group": "Add Group", "Add Group": "Add Group",
"Add Number": "Add Number", "Add Number": "Add Number",
"Add Seat": "Add Seat", "Add Seat": "Add Seat",
"Add Slots": "Add Slots",
"Add Sound Set": "Add Sound Set", "Add Sound Set": "Add Sound Set",
"Add Speed Dial": "Add Speed Dial", "Add Speed Dial": "Add Speed Dial",
"Add Time": "Add Time", "Add Time": "Add Time",
@ -34,6 +36,7 @@
"Add forwarding": "Add forwarding", "Add forwarding": "Add forwarding",
"Add new": "Add new", "Add new": "Add new",
"Add number": "Add number", "Add number": "Add number",
"Add slot": "Add slot",
"Add source": "Add source", "Add source": "Add source",
"Add time": "Add time", "Add time": "Add time",
"Add time range": "Add time range", "Add time range": "Add time range",
@ -94,6 +97,7 @@
"Audio + Video": "Audio + Video", "Audio + Video": "Audio + Video",
"Audio Call": "Audio Call", "Audio Call": "Audio Call",
"Audio Only": "Audio Only", "Audio Only": "Audio Only",
"Auto-attendant": "Auto-attendant",
"Blacklist": "Blacklist", "Blacklist": "Blacklist",
"Block Incoming": "Block Incoming", "Block Incoming": "Block Incoming",
"Block Incoming/Outgoing": "Block Incoming/Outgoing", "Block Incoming/Outgoing": "Block Incoming/Outgoing",
@ -182,6 +186,7 @@
"Delete forwarding": "Delete forwarding", "Delete forwarding": "Delete forwarding",
"Delete from {groupName} forwarding": "Delete from {groupName} forwarding", "Delete from {groupName} forwarding": "Delete from {groupName} forwarding",
"Delete recording": "Delete recording", "Delete recording": "Delete recording",
"Delete slot?": "Delete slot?",
"Delete sourceset": "Delete sourceset", "Delete sourceset": "Delete sourceset",
"Delete voicemail after email notification is delivered": "Delete voicemail after email notification is delivered", "Delete voicemail after email notification is delivered": "Delete voicemail after email notification is delivered",
"Delete {groupName} forwarding group": "Delete {groupName} forwarding group", "Delete {groupName} forwarding group": "Delete {groupName} forwarding group",
@ -196,6 +201,7 @@
"Destination Email": "Destination Email", "Destination Email": "Destination Email",
"Destination Number": "Destination Number", "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 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", "Destinations": "Destinations",
"Devices": "Devices", "Devices": "Devices",
"Disable": "Disable", "Disable": "Disable",
@ -341,6 +347,7 @@
"No Voicemails found": "No Voicemails found", "No Voicemails found": "No Voicemails found",
"No call goes to primary number": "No call goes to primary number", "No call goes to primary number": "No call goes to primary number",
"No call queues created yet": "No call queues created yet", "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 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 destinations created yet": "No destinations created yet",
"No devices created yet": "No devices created yet", "No devices created yet": "No devices created yet",
@ -390,6 +397,9 @@
"Play sound in loop": "Play sound in loop", "Play sound in loop": "Play sound in loop",
"Playing in loop": "Playing 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 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 a valid timerange": "Please select a valid timerange",
"Please select an option": "Please select an option", "Please select an option": "Please select an option",
"Primary Number": "Primary Number", "Primary Number": "Primary Number",
@ -500,6 +510,12 @@
"Show filters": "Show filters", "Show filters": "Show filters",
"Sign In": "Sign In", "Sign In": "Sign In",
"Slot": "Slot", "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", "Something went wrong. Please retry later": "Something went wrong. Please retry later",
"Sound Set": "Sound Set", "Sound Set": "Sound Set",
"Sound Sets": "Sound Sets", "Sound Sets": "Sound Sets",
@ -516,6 +532,7 @@
"Start time should be less than End time": "Start time should be less than End time", "Start time should be less than End time": "Start time should be less than End time",
"Station name": "Station name", "Station name": "Station name",
"Su": "Su", "Su": "Su",
"Subscriber": "Subscriber",
"Subscriber Sign In": "Subscriber Sign In", "Subscriber Sign In": "Subscriber Sign In",
"Sunday": "Sunday", "Sunday": "Sunday",
"Super": "Super", "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} 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 {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 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 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 {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", "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", "{field} must have at most {maxLength} letters": "{field} must have at most {maxLength} letters",
"{mode} of sources": "{mode} of sources", "{mode} of sources": "{mode} of sources",
"{mode} own phone": "{mode} own phone" "{mode} own phone": "{mode} own phone"
} }

@ -12,6 +12,7 @@
"404 Not Found": "404 Not Encontrado", "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.", "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": "", "ACL": "",
"ADD SLOT": "",
"Abort": "Abortar", "Abort": "Abortar",
"Accepted email address to allow mail2fax transmission.": "", "Accepted email address to allow mail2fax transmission.": "",
"Active": "", "Active": "",
@ -23,6 +24,7 @@
"Add Group": "Agregar grupo", "Add Group": "Agregar grupo",
"Add Number": "Agregar Número", "Add Number": "Agregar Número",
"Add Seat": "Agregar asiento", "Add Seat": "Agregar asiento",
"Add Slots": "",
"Add Sound Set": "Añadir Conjunto de Sonido", "Add Sound Set": "Añadir Conjunto de Sonido",
"Add Speed Dial": "Agregar marcado rápido", "Add Speed Dial": "Agregar marcado rápido",
"Add Time": "Agregar tiempo", "Add Time": "Agregar tiempo",
@ -34,6 +36,7 @@
"Add forwarding": "", "Add forwarding": "",
"Add new": "Añadir nuevo", "Add new": "Añadir nuevo",
"Add number": "Agregar número", "Add number": "Agregar número",
"Add slot": "",
"Add source": "Agregar fuente", "Add source": "Agregar fuente",
"Add time": "", "Add time": "",
"Add time range": "", "Add time range": "",
@ -94,6 +97,7 @@
"Audio + Video": "Audio + Video", "Audio + Video": "Audio + Video",
"Audio Call": "Llamada de audio", "Audio Call": "Llamada de audio",
"Audio Only": "Solo Audio", "Audio Only": "Solo Audio",
"Auto-attendant": "",
"Blacklist": "Lista Negra", "Blacklist": "Lista Negra",
"Block Incoming": "Bloquear Entrantes", "Block Incoming": "Bloquear Entrantes",
"Block Incoming/Outgoing": "Bloquear Entrantes/Salientes", "Block Incoming/Outgoing": "Bloquear Entrantes/Salientes",
@ -182,6 +186,7 @@
"Delete forwarding": "", "Delete forwarding": "",
"Delete from {groupName} forwarding": "", "Delete from {groupName} forwarding": "",
"Delete recording": "", "Delete recording": "",
"Delete slot?": "",
"Delete sourceset": "Eliminar el conjunto de fuentes", "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 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": "", "Delete {groupName} forwarding group": "",
@ -196,6 +201,7 @@
"Destination Email": "", "Destination Email": "",
"Destination Number": "Número de destino", "Destination Number": "Número de destino",
"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": "",
"Destinations": "", "Destinations": "",
"Devices": "Dispositivos", "Devices": "Dispositivos",
"Disable": "Desactivar", "Disable": "Desactivar",
@ -341,6 +347,7 @@
"No Voicemails found": "No se encontraron mensajes de voz", "No Voicemails found": "No se encontraron mensajes de voz",
"No call goes to primary number": "", "No call goes to primary number": "",
"No call queues created yet": "Aún no se han creado colas de llamadas", "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 data to save. Please provide at least one time range.": "",
"No destinations created yet": "", "No destinations created yet": "",
"No devices created yet": "Aún no se han creado dispositivos", "No devices created yet": "Aún no se han creado dispositivos",
@ -390,6 +397,9 @@
"Play sound in loop": "Reproducir sonido en bucle", "Play sound in loop": "Reproducir sonido en bucle",
"Playing in loop": "Reproducir en bucle", "Playing in loop": "Reproducir en bucle",
"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 fields": "",
"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": "Número primario", "Primary Number": "Número primario",
@ -500,6 +510,12 @@
"Show filters": "Mostrar filtros", "Show filters": "Mostrar filtros",
"Sign In": "Iniciar sesión", "Sign In": "Iniciar sesión",
"Slot": "Ranura", "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": "", "Something went wrong. Please retry later": "",
"Sound Set": "Conjunto de sonido", "Sound Set": "Conjunto de sonido",
"Sound Sets": "Conjuntos de Sonido", "Sound Sets": "Conjuntos de Sonido",
@ -516,6 +532,7 @@
"Start time should be less than End time": "", "Start time should be less than End time": "",
"Station name": "Nombre de la estación", "Station name": "Nombre de la estación",
"Su": "", "Su": "",
"Subscriber": "",
"Subscriber Sign In": "Iniciar sesión de suscriptor", "Subscriber Sign In": "Iniciar sesión de suscriptor",
"Sunday": "Domingo", "Sunday": "Domingo",
"Super": "Super", "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 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} debe tener como máximo {maxLength} letras", "{field} must have at most {maxLength} letters": "{field} debe tener como máximo {maxLength} letras",
"{mode} of sources": "{mode} de fuentes", "{mode} of sources": "{mode} de fuentes",
"{mode} own phone": "{mode} teléfono propio" "{mode} own phone": "{mode} teléfono propio"
} }

@ -12,6 +12,7 @@
"404 Not Found": "Erreur 404", "404 Not Found": "Erreur 404",
"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": "",
"Abort": "Abandonner", "Abort": "Abandonner",
"Accepted email address to allow mail2fax transmission.": "", "Accepted email address to allow mail2fax transmission.": "",
"Active": "", "Active": "",
@ -23,6 +24,7 @@
"Add Group": "Ajouter un groupe", "Add Group": "Ajouter un groupe",
"Add Number": "Ajouter un numéro", "Add Number": "Ajouter un numéro",
"Add Seat": "Ajouter un siège", "Add Seat": "Ajouter un siège",
"Add Slots": "",
"Add Sound Set": "", "Add Sound Set": "",
"Add Speed Dial": "Ajouter un raccourci", "Add Speed Dial": "Ajouter un raccourci",
"Add Time": "Ajouter une heure", "Add Time": "Ajouter une heure",
@ -34,6 +36,7 @@
"Add forwarding": "", "Add forwarding": "",
"Add new": "En ajouter un nouveau", "Add new": "En ajouter un nouveau",
"Add number": "Ajouter un numéro", "Add number": "Ajouter un numéro",
"Add slot": "",
"Add source": "Ajouter une source", "Add source": "Ajouter une source",
"Add time": "", "Add time": "",
"Add time range": "", "Add time range": "",
@ -94,6 +97,7 @@
"Audio + Video": "Audio et vidéo", "Audio + Video": "Audio et vidéo",
"Audio Call": "Appel audio", "Audio Call": "Appel audio",
"Audio Only": "Audio seulement", "Audio Only": "Audio seulement",
"Auto-attendant": "",
"Blacklist": "Liste noire", "Blacklist": "Liste noire",
"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 sourceset": "Supprimer la liste de sources", "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 voicemail after email notification is delivered": "Supprimer le message vocal une fois la notification e-mail délivrée",
"Delete {groupName} forwarding group": "", "Delete {groupName} forwarding group": "",
@ -196,6 +201,7 @@
"Destination Email": "", "Destination Email": "",
"Destination Number": "Numéro de destination", "Destination Number": "Numéro de destination",
"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": "",
"Destinations": "", "Destinations": "",
"Devices": "Postes", "Devices": "Postes",
"Disable": "Désactiver", "Disable": "Désactiver",
@ -341,6 +347,7 @@
"No Voicemails found": "Message vocaux introuvables", "No Voicemails found": "Message vocaux introuvables",
"No call goes to primary number": "", "No call goes to primary number": "",
"No call queues created yet": "Aucune file dattente de créée", "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 data to save. Please provide at least one time range.": "",
"No destinations created yet": "", "No destinations created yet": "",
"No devices created yet": "Aucun poste créé", "No devices created yet": "Aucun poste créé",
@ -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 fields": "",
"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": "Numéro principal", "Primary Number": "Numéro principal",
@ -500,6 +510,12 @@
"Show filters": "Afficher les filtre", "Show filters": "Afficher les filtre",
"Sign In": "Authentification", "Sign In": "Authentification",
"Slot": "Emplacement", "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": "", "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": "Nom du poste", "Station name": "Nom du poste",
"Su": "", "Su": "",
"Subscriber": "",
"Subscriber Sign In": "Authentification de labonné", "Subscriber Sign In": "Authentification de labonné",
"Sunday": "Dimanche", "Sunday": "Dimanche",
"Super": "Supérieur", "Super": "Supérieur",
@ -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 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} doit avoir au maximum {maxLength} caractères", "{field} must have at most {maxLength} letters": "{field} doit avoir au maximum {maxLength} caractères",
"{mode} of sources": "Sources {mode}", "{mode} of sources": "Sources {mode}",
"{mode} own phone": "{mode} téléphone personnel" "{mode} own phone": "{mode} téléphone personnel"
} }

@ -12,6 +12,7 @@
"404 Not Found": "404 Not Found", "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.", "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": "", "ACL": "",
"ADD SLOT": "",
"Abort": "Abbandona", "Abort": "Abbandona",
"Accepted email address to allow mail2fax transmission.": "", "Accepted email address to allow mail2fax transmission.": "",
"Active": "", "Active": "",
@ -23,6 +24,7 @@
"Add Group": "Aggiungi gruppo", "Add Group": "Aggiungi gruppo",
"Add Number": "Aggiungi Numero", "Add Number": "Aggiungi Numero",
"Add Seat": "Aggiungi postazione", "Add Seat": "Aggiungi postazione",
"Add Slots": "",
"Add Sound Set": "Aggiungi set messaggi", "Add Sound Set": "Aggiungi set messaggi",
"Add Speed Dial": "Aggiungi Selezione Rapida", "Add Speed Dial": "Aggiungi Selezione Rapida",
"Add Time": "Aggiungi orario", "Add Time": "Aggiungi orario",
@ -34,6 +36,7 @@
"Add forwarding": "", "Add forwarding": "",
"Add new": "Aggiungi nuovo", "Add new": "Aggiungi nuovo",
"Add number": "Aggiungi numero", "Add number": "Aggiungi numero",
"Add slot": "",
"Add source": "Aggiungi ", "Add source": "Aggiungi ",
"Add time": "", "Add time": "",
"Add time range": "", "Add time range": "",
@ -94,6 +97,7 @@
"Audio + Video": "Audio + Video", "Audio + Video": "Audio + Video",
"Audio Call": "Chiamata Vocale", "Audio Call": "Chiamata Vocale",
"Audio Only": "Solo Audio", "Audio Only": "Solo Audio",
"Auto-attendant": "",
"Blacklist": "Lista Nera", "Blacklist": "Lista Nera",
"Block Incoming": "Blocca Entranti", "Block Incoming": "Blocca Entranti",
"Block Incoming/Outgoing": "Blocca Entranti/Uscenti", "Block Incoming/Outgoing": "Blocca Entranti/Uscenti",
@ -182,6 +186,7 @@
"Delete forwarding": "", "Delete forwarding": "",
"Delete from {groupName} forwarding": "", "Delete from {groupName} forwarding": "",
"Delete recording": "", "Delete recording": "",
"Delete slot?": "",
"Delete sourceset": "Cancella set pattern sorgente", "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 voicemail after email notification is delivered": "Cancella il messaggio vocale dopo che l'email di notifica è stata inoltrata",
"Delete {groupName} forwarding group": "", "Delete {groupName} forwarding group": "",
@ -196,6 +201,7 @@
"Destination Email": "", "Destination Email": "",
"Destination Number": "Numero di Destinazione", "Destination Number": "Numero di Destinazione",
"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": "",
"Destinations": "", "Destinations": "",
"Devices": "Dispositivi", "Devices": "Dispositivi",
"Disable": "Disabilita", "Disable": "Disabilita",
@ -341,6 +347,7 @@
"No Voicemails found": "Non è stato trovato nessun Messaggio Vocale", "No Voicemails found": "Non è stato trovato nessun Messaggio Vocale",
"No call goes to primary number": "", "No call goes to primary number": "",
"No call queues created yet": "Nessuna coda creata", "No call queues created yet": "Nessuna coda creata",
"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": "Nessun dispositivo creato", "No devices created yet": "Nessun dispositivo creato",
@ -390,6 +397,9 @@
"Play sound in loop": "Ripoduci il messaggio a ciclo continuo", "Play sound in loop": "Ripoduci il messaggio a ciclo continuo",
"Playing in loop": "Ripoduci a ciclo continuo", "Playing in loop": "Ripoduci a ciclo continuo",
"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 fields": "",
"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": "Numero Principale", "Primary Number": "Numero Principale",
@ -500,6 +510,12 @@
"Show filters": "Mostra filtri", "Show filters": "Mostra filtri",
"Sign In": "Accedi", "Sign In": "Accedi",
"Slot": "Posizione", "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": "", "Something went wrong. Please retry later": "",
"Sound Set": "Set Messaggi", "Sound Set": "Set Messaggi",
"Sound Sets": "Annunci", "Sound Sets": "Annunci",
@ -516,6 +532,7 @@
"Start time should be less than End time": "", "Start time should be less than End time": "",
"Station name": "Nome stazione", "Station name": "Nome stazione",
"Su": "", "Su": "",
"Subscriber": "",
"Subscriber Sign In": "Accedi come utente", "Subscriber Sign In": "Accedi come utente",
"Sunday": "Domenica", "Sunday": "Domenica",
"Super": "Super", "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 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": "Il campo {field} può contenere al massimo {maxLength} lettere", "{field} must have at most {maxLength} letters": "Il campo {field} può contenere al massimo {maxLength} lettere",
"{mode} of sources": "{mode} dei pattern sorgente", "{mode} of sources": "{mode} dei pattern sorgente",
"{mode} own phone": "{mode} proprio telefono" "{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 CscPagePbxCallQueues from 'src/pages/CscPagePbxCallQueues'
import CscPagePbxSoundSets from 'src/pages/CscPagePbxSoundSets' import CscPagePbxSoundSets from 'src/pages/CscPagePbxSoundSets'
import CscPagePbxMsConfigs from 'src/pages/CscPagePbxMsConfigs' import CscPagePbxMsConfigs from 'src/pages/CscPagePbxMsConfigs'
import CscPagePbxAutoAttendant from 'src/pages/CscPagePbxAutoAttendant'
import CscPagePbxSettings from 'src/pages/CscPagePbxSettings' import CscPagePbxSettings from 'src/pages/CscPagePbxSettings'
import CscPageVoicebox from 'src/pages/CscPageVoicebox' import CscPageVoicebox from 'src/pages/CscPageVoicebox'
import CscPageFaxSettings from 'src/pages/CscPageFaxSettings' import CscPageFaxSettings from 'src/pages/CscPageFaxSettings'
@ -190,6 +191,14 @@ export default function routes (app) {
subtitle: i18n.t('Manager Secretary') 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', path: 'voicebox',
component: CscPageVoicebox, component: CscPageVoicebox,

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