MT#64825 Add conference settings to Extension Settings

* Added a new Conference page under Extension Settings
    where PBX subscribers can manage their own
    conference_max_participants and conference_pin
  * Page and fields are gated through subscriber profile
    attributes for administrative purposes
  * Restricted the route and menu entry to PBX admin users
    and added the new Conference entry to the
    Extension Settings menu

Change-Id: I002c79ce6653e1183c6348775184a5a92ea8f737
(cherry picked from commit 589140c318)
mr26.0
Giancarlo Errigo Mattos 2 months ago committed by Giancarlo Mattos
parent 5ce09d2789
commit d02250f2dc

@ -72,6 +72,8 @@ export default {
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.huntGroups) ||
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.playAnnounceBeforeCallSetup) ||
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.playAnnounceToCallee)
const hasExtensionSettingsSubmenus = this.isPbxEnabled &&
this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.pbxExtensionSettings)
return [
{
to: '/user/dashboard',
@ -250,8 +252,7 @@ export default {
{
icon: 'settings',
label: this.$t('Extension Settings'),
visible: this.isPbxEnabled &&
this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.pbxSettings),
visible: hasExtensionSettingsSubmenus,
children: [
{
to: '/user/extension-settings/call-queues',
@ -270,6 +271,13 @@ export default {
icon: 'dialpad',
label: this.$t('Auto Attendant'),
visible: this.isPbxEnabled && this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.autoAttendant)
},
{
to: '/user/extension-settings/conference',
icon: 'groups',
label: this.$t('Conference'),
visible: this.isPbxAdmin &&
this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.pbxSettingsConference)
}
]
},

@ -26,6 +26,8 @@ export const PROFILE_ATTRIBUTE_MAP = {
clir_intrapbx: 'clir_intrapbx',
cstaClient: 'csta_client',
cstaController: 'csta_controller',
conferenceMaxParticipants: 'conference_max_participants',
conferencePin: 'conference_pin',
faxServer: 'fax_server',
cscCalls: 'csc_calls',
managerSecretary: 'manager_secretary',
@ -48,7 +50,8 @@ export const PROFILE_ATTRIBUTES_MAP = {
callBlockingOutgoing: ['block_out_mode', 'block_out_list', 'ncos', 'ncos_set'],
callBlockingPrivacy: ['clir', 'clir_intrapbx'],
callSettings: ['music_on_hold', 'language'],
pbxSettings: ['auto_attendant', 'cloud_pbx_callqueue', 'max_queue_length', 'queue_wrap_up_time', 'manager_secretary'],
pbxExtensionSettings: ['auto_attendant', 'manager_secretary', 'conference_max_participants', 'conference_pin'],
pbxSettingsConference: ['conference_max_participants', 'conference_pin'],
pbxSettingsCallQueue: ['cloud_pbx_callqueue', 'max_queue_length', 'queue_wrap_up_time'],
callForwarding: ['cfu', 'cfb', 'cfna', 'cft', 'cfs', 'cfo', 'cfr']
}

@ -0,0 +1,183 @@
<template>
<csc-page
id="csc-page-pbx-settings-conference"
class="row q-pa-lg"
>
<q-list
class="col col-xs-12 col-md-6"
>
<div>
<q-item v-if="showConferenceMaxParticipants">
<q-item-section>
<csc-input-saveable
v-model="changes.conferenceMaxParticipants"
:label="t('Maximum Conference Participants')"
data-cy="csc-conference-max-participants"
:value-changed="hasConferenceMaxParticipantsChanged"
:error="v$.conferenceMaxParticipants.$errors.length > 0"
:error-message="v$.conferenceMaxParticipants.$errors[0]?.$message ?? ''"
:loading="isConferenceMaxParticipantsLoading"
@undo="resetConferenceMaxParticipants"
@save="saveConferenceMaxParticipants"
@input="v$.conferenceMaxParticipants.$touch()"
@keypress.space.prevent
@keydown.space.prevent
@keyup.space.prevent
/>
</q-item-section>
</q-item>
<q-item v-if="showConferencePin">
<q-item-section>
<csc-input-saveable
v-model="changes.conferencePin"
:label="t('Conference PIN')"
data-cy="csc-conference-pin"
:value-changed="hasConferencePinChanged"
:error="v$.conferencePin.$errors.length > 0"
:error-message="v$.conferencePin.$errors[0]?.$message ?? ''"
:loading="isConferencePinLoading"
@undo="resetConferencePin"
@save="saveConferencePin"
@input="v$.conferencePin.$touch()"
@keypress.space.prevent
@keydown.space.prevent
@keyup.space.prevent
/>
</q-item-section>
</q-item>
</div>
</q-list>
</csc-page>
</template>
<script setup>
import { useVuelidate } from '@vuelidate/core'
import { helpers, numeric } from '@vuelidate/validators'
import CscPage from 'components/CscPage'
import CscInputSaveable from 'components/form/CscInputSaveable'
import { useWait } from 'src/composables/useWait'
import { PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import { showToast } from 'src/helpers/ui'
import {
computed,
onMounted,
reactive,
ref
} from 'vue'
import { useI18n } from 'vue-i18n'
import { useStore } from 'vuex'
defineOptions({ name: 'CscPagePbxSettingsConference' })
function normalizePreference (value) {
return value === null || value === undefined ? '' : value.toString()
}
const WAITERS = {
loadPreferences: 'csc-pbx-call-settings-load-preferences',
updateConferenceMaxParticipants: 'csc-pbx-call-settings-update-conference-max-participants',
updateConferencePin: 'csc-pbx-call-settings-update-conference-pin'
}
const store = useStore()
const wait = useWait()
const { t } = useI18n()
const isInitialized = ref(false)
const changes = reactive({
conferenceMaxParticipants: '',
conferencePin: ''
})
const subscriberPreferences = computed(() => store.state.callSettings.subscriberPreferences)
const hasSubscriberProfileAttribute = computed(() => store.getters['user/hasSubscriberProfileAttribute'])
const rules = computed(() => ({
conferenceMaxParticipants: {
numeric: helpers.withMessage(
() => t('{field} must consist of numeric characters only', { field: t('Maximum Conference Participants') }),
numeric
)
},
conferencePin: {
numeric: helpers.withMessage(
() => t('{field} must consist of numeric characters only', { field: t('Conference PIN') }),
numeric
)
}
}))
const v$ = useVuelidate(rules, changes)
const showConferenceMaxParticipants = computed(() => hasSubscriberProfileAttribute.value(PROFILE_ATTRIBUTE_MAP.conferenceMaxParticipants))
const showConferencePin = computed(() => hasSubscriberProfileAttribute.value(PROFILE_ATTRIBUTE_MAP.conferencePin))
const currentConferenceMaxParticipants = computed(() => normalizePreference(subscriberPreferences.value.conference_max_participants))
const currentConferencePin = computed(() => normalizePreference(subscriberPreferences.value.conference_pin))
const hasConferenceMaxParticipantsChanged = computed(() => isInitialized.value && changes.conferenceMaxParticipants !== currentConferenceMaxParticipants.value)
const hasConferencePinChanged = computed(() => isInitialized.value && changes.conferencePin !== currentConferencePin.value)
const isLoading = computed(() => wait.is(WAITERS.loadPreferences))
const isConferenceMaxParticipantsUpdating = computed(() => wait.is(WAITERS.updateConferenceMaxParticipants))
const isConferencePinUpdating = computed(() => wait.is(WAITERS.updateConferencePin))
const isConferenceMaxParticipantsLoading = computed(() => isLoading.value || isConferenceMaxParticipantsUpdating.value)
const isConferencePinLoading = computed(() => isLoading.value || isConferencePinUpdating.value)
function applyDefaultData () {
changes.conferenceMaxParticipants = currentConferenceMaxParticipants.value
changes.conferencePin = currentConferencePin.value
}
function resetConferenceMaxParticipants () {
changes.conferenceMaxParticipants = currentConferenceMaxParticipants.value
}
function resetConferencePin () {
changes.conferencePin = currentConferencePin.value
}
async function saveConferenceMaxParticipants () {
if (hasConferenceMaxParticipantsChanged.value && v$.value.conferenceMaxParticipants.$errors.length <= 0) {
wait.start(WAITERS.updateConferenceMaxParticipants)
try {
await store.dispatch('callSettings/fieldUpdateAction', {
field: 'conference_max_participants',
value: changes.conferenceMaxParticipants
})
resetConferenceMaxParticipants()
showToast(t('Updated {field} successfully', {
field: t('Maximum Conference Participants')
}))
} finally {
wait.end(WAITERS.updateConferenceMaxParticipants)
}
}
}
async function saveConferencePin () {
if (hasConferencePinChanged.value && v$.value.conferencePin.$errors.length <= 0) {
wait.start(WAITERS.updateConferencePin)
try {
await store.dispatch('callSettings/fieldUpdateAction', {
field: 'conference_pin',
value: changes.conferencePin
})
resetConferencePin()
showToast(t('Updated {field} successfully', {
field: t('Conference PIN')
}))
} finally {
wait.end(WAITERS.updateConferencePin)
}
}
}
onMounted(async () => {
wait.start(WAITERS.loadPreferences)
try {
await store.dispatch('callSettings/loadSubscriberPreferencesAction')
applyDefaultData()
isInitialized.value = true
} finally {
wait.end(WAITERS.loadPreferences)
}
})
</script>

@ -30,6 +30,7 @@ import CscPagePbxSeatDetails from 'pages/CscPagePbxSeatDetails'
import CscPagePbxSeats from 'pages/CscPagePbxSeats'
import CscPagePbxSettingsAutoAttendant from 'pages/CscPagePbxSettingsAutoAttendant'
import CscPagePbxSettingsCallQueues from 'pages/CscPagePbxSettingsCallQueues'
import CscPagePbxSettingsConference from 'pages/CscPagePbxSettingsConference'
import CscPagePbxSettingsMsConfigs from 'pages/CscPagePbxSettingsMsConfigs'
import CscPagePbxSoundSetDetails from 'pages/CscPagePbxSoundSetDetails'
import CscPagePbxSoundSets from 'pages/CscPagePbxSoundSets'
@ -609,6 +610,22 @@ const routes = [
licenses: [LICENSES.pbx]
}
},
{
path: 'extension-settings/conference',
component: CscPagePbxSettingsConference,
meta: {
get title () {
return i18n.global.t('Extension Settings')
},
get subtitle () {
return i18n.global.t('Conference')
},
platformFeature: FEATURES.cloudPbx,
profileAttributes: PROFILE_ATTRIBUTES_MAP.pbxSettingsConference,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
}
},
{
path: 'registered-devices',
name: 'RegisteredDevices',

Loading…
Cancel
Save