MT#63356 Align CSC route guards with menu visibility

- Verify and correct route access for admin-only CSC pages
- Update route guard logic to support:
  * User role (admin / non-admin)
  * Exact match on user profile attribute
  * Presence of one or more profile attributes
  * Required licenses (all must exist)
  * Exact platfom feature
  * Exact capability match
- Ensure route guards match menu visibility restrictions
- Document route guards and menu visibility logic
- Note: for fax settings, we use the extra variable
  `isFaxServerSettingsActive` to determine whether the toggle
  in the fax server menu should be on or off.

Change-Id: Id60a0e8b2145701ed4ae52d0859da46172076a89
master
Debora Crescenzo 2 months ago committed by Crescenzo Debora
parent 973b448fd1
commit 2b46170467

@ -0,0 +1,174 @@
# Router Navigation Guard
The access control logic for the routes is handled in the file `routes.js`.
Here the `beforeEach` navigation guard controls access to routes in the application based on authentication status, user role, user profile attributes, license availability and user capabilities.
## Authentication Check
First, the guard checks if the user has a valid JWT token using `hasJwt()`
**Unauthenticated users**:
- Are redirected to `/login` if trying to access protected routes
- Can access public routes (`/login`, `/recoverpassword`, `/changepassword`)
## Route Redirects for Authenticated Users
- Users trying to access `/login` when already authenticated are redirected to the home page
- Users trying to access `/conference` are redirected to `/conference/room123` (default room)
## Route Authorization Checks
For all other routes, the guard implements a multi-layered authorization system:
### Route Authorization System
The implementation uses a sequential check system, evaluating each permission requirement in order:
```js
default: {
// 1. Admin check
if (to.meta?.adminOnly && !store.getters['user/isAdmin']) {
return next('/')
}
// 2. Profile attribute check
if (to.meta?.profileAttribute &&
!store.getters['user/hasSubscriberProfileAttribute'](to.meta.profileAttribute)) {
return next('/')
}
// 3. Profile attributes array check
if (to.meta?.profileAttributes &&
!store.getters['user/hasSomeSubscriberProfileAttributes'](to.meta.profileAttributes)) {
return next('/')
}
// 4. License check
if (to.meta?.license) {
const isSpCe = store.getters['user/isSpCe']
// CE-specific check
if (isSpCe && !to.meta.allowCE) {
return next('/')
}
// License check for non-CE users
if (!isSpCe && !store.getters['user/hasLicenses']([to.meta.license])) {
return next('/')
}
}
// 5. Platform Feature check
if (to.meta?.platformFeature &&
!store.getters['user/hasPlatformFeature'](to.meta.platformFeature)) {
return next('/')
}
// 5. Capability check
if (to.meta?.capability &&
!store.getters['user/hasCapability'](to.meta.capability)) {
return next('/')
}
// All checks passed
next()
}
```
### 1. Admin-Only Check
- Verifies if the route requires admin access (`adminOnly: true`)
- Redirects to home page (/) if user is not an admin
### 2. Single Profile Attribute Check
- Verifies the user has the specific profile attribute required by the route
- Redirects to home page (/) if the attribute is missing
### 3. Multiple Profile Attributes Check
- Checks if the user has at least one of the required profile attributes in the array
- Redirects to home page (/) if no matching attributes are found
### 4. License Check
- Two-part check based on user type:
- For Community Edition (CE) users: Only allows access if the route explicitly allows CE users (`allowCE: true`)
- For regular users: Verifies the required licenses are active
- Redirects to home page (/) if license requirements are not met
### 5. Platform Feature Check
- Verifies if the feature required by the route is active platform-wide
- Redirects to home page (/) if the platform feature is missing
### 6. Capability Check
- Verifies if the user has the specific capability required by the route
- Redirects to home page (/) if the capability is missing
### Default Case (All Checks Pass)
- If all applicable checks pass, the user is allowed to access the route
- Routes without any restrictions are accessible to all authenticated users
## Route Meta Fields:
- `profileAttribute`: Single profile attribute required (e.g., PROFILE_ATTRIBUTE_MAP.conversations)
- `profileAttributes`: Array of required group attributes (e.g., PROFILE_ATTRIBUTES_MAP.callSettings).
- `licenses`: Array of required license keys (e.g., LICENSES.fax).
- `platformFeature`: string of required ngcp feature
- `capability`: string of required user capability
Note: Attributes are the response of the call `/api/subscriberprofiles/:profile_id`. `profile_id` is one of the properties returned by `/api/subscribers`.
## Menu Visibility and Consistency with Route Authorization
The main menu, implemented in `CscMainMenuTop.vue`, must maintain consistency with the route authorization checks to ensure a seamless user experience. Menu items are conditionally rendered based on the same criteria used for route access control.
### Menu Item Visibility Logic
```js
{
to: '/user/home',
icon: 'call',
label: this.callStateTitle,
sublabel: this.callStateSubtitle,
visible: this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.cscCalls) &&
(this.isSpCe || this.hasLicenses([LICENSES.csc_calls]))
}
```
Each menu item includes a `visible` property that determines whether it should be displayed to the user. This visibility check typically includes:
1. **Profile Attribute Checks** - Using the same methods as the route guard:
- `hasSubscriberProfileAttribute()` for single attribute requirements
- `hasSomeSubscriberProfileAttributes()` for multiple attribute requirements
2. **License Validation** - For features requiring licenses:
- `hasLicenses()` checks if the required licenses are active
- Special handling for SpCe users with `isSpCe` flag
3. **Platform and User Capability Checks** - Verifies that the ngcp platform has the necessary modules activated and that the module is enabled for the user. This check also incudes the license check for the feature:
- `this.isFaxFeatureEnabled()` checks if the fax feature is enabled in the platform, if it is enabled for the user and if the license fax is active. Note, this doesn't include the checks about fax server settings.
- `this.isPbxEnabled()` checks if the pbx feature is enabled in the platform, if it's enabled for the user and if the license pbx is active.
- `this.isSmsEnabled()` checks if the sms feature is enabled in the platform, if it's enabled for the user and if the license sms is active.
**IMPORTANT** The Menu Item Visibility Logic needs to be aligned with with Route Guards
### Menu Hierarchy and Nested Items
Menu items with children (submenu items) follow additional rules:
1. **Parent Visibility**: A parent menu item may be visible even when some children are not
2. **Child Visibility**: Each child item has its own visibility condition
3. **Dynamic Expansion**: Some menu sections are automatically expanded based on the current route:
```js
opened: this.isPbxConfiguration
```
### Preventing UI/Navigation Inconsistency
This dual-layer approach ensures that:
1. Users only see menu items for features they can access
2. If a menu item is mistakenly visible, the route guard still prevents unauthorized access
3. Direct URL navigation attempts are blocked for unauthorized routes, even if a user bypasses the UI
By maintaining consistency between the menu visibility logic and the route authorization checks, the application provides a coherent user experience while maintaining strong access control.

@ -8,6 +8,7 @@ import {
post
} from 'src/api/common'
import { getFaxServerSettings } from 'src/api/fax'
import { LICENSES } from 'src/constants'
export function login ({ username, password, otp = null }) {
return new Promise((resolve, reject) => {
@ -70,17 +71,21 @@ export async function getUserData (id) {
])
try {
let isFaxServerSettingsActive = false
const [subscriber, capabilities, resellerBranding, platformInfo] = await allPromise
if (capabilities.faxserver && platformInfo.licenses.find((license) => license === 'fax')) {
const faxServerSettings = await getFaxServerSettings(id)
capabilities.faxactive = faxServerSettings.active
if (capabilities.faxserver && platformInfo.licenses.find((license) => license === LICENSES.fax)) {
// Note that isFaxServerSettingsActive determines if the menu has been enabled by admin
// or, in other words, if the relevant toggle is on/off.
const responseFaxServerSettings = await getFaxServerSettings(id)
isFaxServerSettingsActive = responseFaxServerSettings.active
}
return {
subscriber,
capabilities,
resellerBranding: resellerBranding?.items[0] || null,
platformInfo
platformInfo,
isFaxServerSettingsActive
}
} catch (error) {
throw new Error(error.response.data.message)
@ -99,6 +104,11 @@ export function getSubscriberById (id) {
})
}
/**
* Determines if specific users should have access to features based on their roles and profiles.
* Retrieves a list of capabilities and their enabled status from the API.
* @returns {Promise<Object>} A promise that resolves to an object of capabilities with their enabled status
*/
export function getCapabilities () {
return new Promise((resolve, reject) => {
getList({

@ -29,25 +29,54 @@ export default ({ app, router, store }) => {
path: '/conference/room123'
})
break
default:
if (to.meta?.profileAttribute) {
const hasSubscriberProfileAttribute = store.getters['user/hasSubscriberProfileAttribute'](to.meta.profileAttribute)
if (to.meta.license && hasSubscriberProfileAttribute) {
// Guard to assure that:
// CE users have access to all available menus as they do not have licenses
if (store.getters['user/isSpCe']) {
next()
}
// users cannot click on menu if it is mistakenly visible when the license is inactive
store.getters['user/isLicenseActive'](to.meta.license) ? next() : next('/')
default: {
// 1. Admin check
if (to.meta?.adminOnly && !store.getters['user/isAdmin']) {
return next('/')
}
// 2. Profile attribute check
if (to.meta?.profileAttribute &&
!store.getters['user/hasSubscriberProfileAttribute'](to.meta.profileAttribute)) {
return next('/')
}
// 3. Profile attributes array check
if (to.meta?.profileAttributes &&
!store.getters['user/hasSomeSubscriberProfileAttributes'](to.meta.profileAttributes)) {
return next('/')
}
// 4. License check
if (to.meta?.license) {
const isSpCe = store.getters['user/isSpCe']
// CE-specific check
if (isSpCe && !to.meta.allowCE) {
return next('/')
}
hasSubscriberProfileAttribute ? next() : next('/')
} else if (to.meta?.profileAttributes) {
store.getters['user/hasSubscriberProfileAttributes'](to.meta.profileAttributes) ? next() : next('/')
} else {
next()
// License check for non-CE users
if (!isSpCe && !store.getters['user/hasLicenses']([to.meta.licenses])) {
return next('/')
}
}
// 5. Platform Feature check
if (to.meta?.platformFeature &&
!store.getters['user/hasPlatformFeature'](to.meta.platformFeature)) {
return next('/')
}
// 6. Capability check
if (to.meta?.capability &&
!store.getters['user/hasCapability'](to.meta.capability)) {
return next('/')
}
// All checks passed, route is accessible
next()
}
}
}
})

@ -45,14 +45,33 @@ export default {
},
computed: {
...mapGetters('user', [
'hasFaxCapability',
'isFaxFeatureEnabled',
'hasSubscriberProfileAttribute',
'hasSubscriberProfileAttributes',
'isLicenseActive',
'hasSomeSubscriberProfileAttributes',
'hasLicenses',
'isPbxEnabled',
'isSpCe'
]),
items () {
const hasCallSettingsSubmenus = this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callSettings) ||
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.voiceMail) ||
this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callForwarding) ||
this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callBlockingIncoming) ||
this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callBlockingOutgoing) ||
this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callBlockingPrivacy) ||
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.speedDial) ||
(this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.recordings) &&
(this.isSpCe || this.hasLicenses([LICENSES.call_recording])))
const hasCustomerPreferenceSubmenus = this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.blockInClir) ||
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.blockInList) ||
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.blockOutList) ||
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.blockInMode) ||
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.blockOutMode) ||
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.blockOutOverridePin) ||
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.huntGroups) ||
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.playAnnounceBeforeCallSetup) ||
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.playAnnounceToCallee)
return [
{
to: '/user/dashboard',
@ -65,7 +84,8 @@ export default {
icon: 'call',
label: this.callStateTitle,
sublabel: this.callStateSubtitle,
visible: this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.cscCalls && (this.isSpCe || this.isLicenseActive(LICENSES.csc_calls)))
visible: this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.cscCalls) &&
(this.isSpCe || this.hasLicenses([LICENSES.csc_calls]))
},
{
to: '/user/conversations',
@ -78,18 +98,18 @@ export default {
to: '/user/subscriber-phonebook',
icon: 'fas fa-user',
label: this.$t('Subscriber Phonebook'),
visible: this.isLicenseActive(LICENSES.phonebook)
visible: this.hasLicenses([LICENSES.phonebook])
},
{
icon: 'settings_phone',
label: this.$t('Call Settings'),
visible: true,
visible: hasCallSettingsSubmenus,
children: [
{
to: '/user/call-settings',
icon: 'settings',
label: this.$t('General'),
visible: this.hasSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callSettings)
visible: this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callSettings)
},
{
to: '/user/voicebox',
@ -101,25 +121,25 @@ export default {
to: '/user/call-forwarding',
icon: 'phone_forwarded',
label: this.$t('Forwarding'),
visible: true
visible: this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callForwarding)
},
{
to: '/user/call-blocking/incoming',
icon: 'call_received',
label: this.$t('Block Incoming'),
visible: this.hasSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callBlockingIncoming)
visible: this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callBlockingIncoming)
},
{
to: '/user/call-blocking/outgoing',
icon: 'call_made',
label: this.$t('Block Outgoing'),
visible: this.hasSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callBlockingOutgoing)
visible: this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callBlockingOutgoing)
},
{
to: '/user/call-blocking/privacy',
icon: 'fas fa-user-secret',
label: this.$t('Privacy'),
visible: this.hasSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callBlockingPrivacy)
visible: this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callBlockingPrivacy)
},
{
to: '/user/speeddial',
@ -137,7 +157,8 @@ export default {
to: '/user/recordings',
icon: 'play_circle',
label: this.$t('Recordings'),
visible: this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.recordings) && (this.isSpCe || this.isLicenseActive(LICENSES.call_recording))
visible: this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.recordings) &&
(this.isSpCe || this.hasLicenses([LICENSES.call_recording]))
}
]
},
@ -145,84 +166,84 @@ export default {
to: '/user/fax-settings',
icon: 'fas fa-fax',
label: this.$t('Fax Settings'),
visible: this.hasFaxCapability &&
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.faxServer) &&
this.isLicenseActive(LICENSES.fax)
visible: this.isFaxFeatureEnabled
},
{
icon: 'fas fa-chart-line',
label: this.$t('PBX Statistics'),
visible: this.isPbxAdmin && this.isLicenseActive(LICENSES.pbx),
visible: this.isPbxAdmin,
opened: this.isPbxConfiguration,
children: [
{
to: '/user/pbx-statistics/cdr',
icon: 'fas fa-table',
label: this.$t('Cdr'),
visible: true
visible: this.isPbxAdmin
}
]
},
{
icon: 'miscellaneous_services',
label: this.$t('PBX Configuration'),
visible: this.isPbxAdmin && this.isLicenseActive(LICENSES.pbx),
visible: this.isPbxAdmin,
opened: this.isPbxConfiguration,
children: [
{
to: '/user/pbx-configuration/seats',
icon: 'person',
label: this.$t('Seats'),
visible: true
visible: this.isPbxAdmin
},
{
to: '/user/pbx-configuration/groups',
icon: 'group',
label: this.$t('Groups'),
visible: this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.huntGroups)
visible: this.isPbxAdmin && this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.huntGroups)
},
{
to: '/user/pbx-configuration/devices',
icon: 'fas fa-fax',
label: this.$t('Devices'),
visible: this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.deviceProvisioning) && this.isLicenseActive(LICENSES.device_provisioning)
visible: this.isPbxAdmin &&
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.deviceProvisioning) &&
this.hasLicenses([LICENSES.device_provisioning])
},
{
to: '/user/pbx-configuration/call-queues',
icon: 'filter_none',
label: this.$t('Call Queues'),
visible: this.hasSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.pbxSettingsCallQueue)
visible: this.isPbxAdmin && this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.cloudPbxCallQueue)
},
{
to: '/user/pbx-configuration/sound-sets',
icon: 'queue_music',
label: this.$t('Sound Sets'),
visible: this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.soundSet)
visible: this.isPbxAdmin && this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.soundSet)
},
{
to: '/user/pbx-configuration/ms-configs',
icon: 'arrow_forward',
label: this.$t('Manager Secretary'),
visible: this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.manager_secretary)
visible: this.isPbxAdmin && this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.managerSecretary)
},
{
to: '/user/pbx-configuration/auto-attendant',
icon: 'dialpad',
label: this.$t('Auto Attendant'),
visible: this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.auto_attendant)
visible: this.isPbxAdmin && this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.autoAttendant)
},
{
to: '/user/pbx-configuration/customer-phonebook',
icon: 'person',
label: this.$t('Customer Phonebook'),
visible: this.isLicenseActive(LICENSES.phonebook)
visible: this.isPbxAdmin && this.hasLicenses([LICENSES.phonebook])
},
{
to: '/user/pbx-configuration/customer-preferences',
icon: 'fas fa-user-cog',
label: this.$t('Customer Preferences'),
visible: true
visible: this.isPbxAdmin && this.hasLicenses([LICENSES.phonebook]) && hasCustomerPreferenceSubmenus
}
]
},
@ -230,26 +251,25 @@ export default {
icon: 'settings',
label: this.$t('Extension Settings'),
visible: this.isPbxEnabled &&
this.hasSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.pbxSettings) &&
this.isLicenseActive(LICENSES.pbx),
this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.pbxSettings),
children: [
{
to: '/user/extension-settings/call-queues',
icon: 'filter_none',
label: this.$t('Call Queues'),
visible: this.isPbxEnabled && this.hasSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.pbxSettingsCallQueue)
visible: this.isPbxEnabled && this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.cloudPbxCallQueue)
},
{
to: '/user/extension-settings/ms-configs',
icon: 'arrow_forward',
label: this.$t('Manager Secretary'),
visible: this.isPbxEnabled && this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.manager_secretary)
visible: this.isPbxEnabled && this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTES_MAP.manager_secretary)
},
{
to: '/user/extension-settings/auto-attendant',
icon: 'dialpad',
label: this.$t('Auto Attendant'),
visible: this.isPbxEnabled && this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.auto_attendant)
visible: this.isPbxEnabled && this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTES_MAP.autoAttendant)
}
]
},

@ -5,8 +5,8 @@
:model-value="value"
emit-value
map-options
:disable="loading"
:readonly="loading"
:disable="disabled || loading"
:readonly="disabled || loading"
:label="$t('Language for voicemail and app server')"
data-cy="voicebox-change-language"
:title="$t('Voice prompts language for voicemail, conference and application server')"
@ -54,6 +54,10 @@ export default {
loading: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
}
},
emits: ['input']

@ -271,7 +271,7 @@
})"
/>
<csc-popup-menu-item
v-if="hasSubscriberProfileAttribute('voice_mail')"
v-if="showVoicebox"
:icon="destinationIconByType('VoiceBox')"
:label="$t('Forward to Voicebox')"
data-cy="csc-forwarding-to-voicebox"
@ -293,7 +293,7 @@
})"
/>
<csc-popup-menu-item
v-if="platformInfo.faxserver"
v-if="isFaxFeatureEnabled"
:icon="destinationIconByType('Fax2Mail')"
:label="$t('Forward to Fax2Mail')"
data-cy="csc-forwarding-to-fax2mail"
@ -304,7 +304,7 @@
})"
/>
<csc-popup-menu-item
v-if="platformInfo.manager_secretary"
v-if="showManagerSecretary"
:icon="destinationIconByType('ManagerSecretary')"
:label="$t('Forward to Manager Secretary')"
data-cy="csc-forwarding-to-manager-secretary"
@ -329,7 +329,7 @@
#grid-column-2
>
<csc-popup-menu-item
v-if="isPbxAttendant && platformInfo.cloudpbx"
v-if="isPbxAttendant && isPbxEnabled"
:icon="destinationIconByType('AutoAttendant')"
:label="$t('Forward to Auto Attendant')"
data-cy="csc-forwarding-to-auto-attendant"
@ -340,7 +340,7 @@
})"
/>
<csc-popup-menu-item
v-if="isPbxAttendant && platformInfo.cloudpbx"
v-if="isPbxAttendant && isPbxEnabled"
:icon="destinationIconByType('OfficeHoursAnnouncement')"
:label="$t('Forward to Office Hours Announcement')"
data-cy="csc-forwarding-to-office-hours-announcement"
@ -414,6 +414,7 @@ import CscCfConditionPopupDateRange from 'components/call-forwarding/CscCfCondit
import CscCfConditionPopupOfficeHours from 'components/call-forwarding/CscCfConditionPopupOfficeHours'
import CscCfConditionPopupWeekdays from 'components/call-forwarding/CscCfConditionPopupWeekdays'
import _ from 'lodash'
import { PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import numberFilter from 'src/filters/number'
import destination from 'src/mixins/destination'
import { mapActions, mapGetters, mapState } from 'vuex'
@ -475,7 +476,9 @@ export default {
]),
...mapGetters('user', [
'hasSubscriberProfileAttribute',
'isPbxAttendant'
'isFaxFeatureEnabled',
'isPbxAttendant',
'isPbxEnabled'
]),
...mapState('user', [
'platformInfo'
@ -497,6 +500,13 @@ export default {
_.endsWith(lastDestination, 'managersecretary.local') ||
_.endsWith(lastDestination, 'conference.local') ||
(_.endsWith(lastDestination, 'app.local') && !lastDestinationId)
},
showVoicebox () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.voiceMail)
},
showManagerSecretary () {
return this.platformInfo.manager_secretary &&
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.managerSecretary)
}
},
methods: {

@ -393,7 +393,6 @@ export default {
},
phonebookEntryName: {
type: String,
required: false,
default: null
},
numberInput: {

@ -25,7 +25,7 @@
</q-item>
</q-list>
<csc-ncos
v-if="pageName === 'outgoing'"
v-if="pageName === 'outgoing' && showNcosMenus"
/>
<div
v-if="hasSubscriberProfileAttribute(blockMode)"
@ -134,6 +134,7 @@ import CscSpinner from 'components/CscSpinner'
import CscBlockedNumber from 'components/pages/CallBlocking/CscBlockedNumber'
import CscCallBlockingAddForm from 'components/pages/CallBlocking/CscCallBlockingAddForm'
import _ from 'lodash'
import { PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import { mapGetters } from 'vuex'
export default {
@ -183,7 +184,8 @@ export default {
'isAnonymousBlockRequesting'
]),
...mapGetters('user', [
'hasSubscriberProfileAttribute'
'hasSubscriberProfileAttribute',
'hasSomeSubscriberProfileAttributes'
]),
toggleButtonLabel () {
if (!this.enabled) {
@ -214,6 +216,9 @@ export default {
classes.push('csc-toggle-disabled')
}
return classes
},
showNcosMenus () {
return this.hasSomeSubscriberProfileAttributes([PROFILE_ATTRIBUTE_MAP.ncos, PROFILE_ATTRIBUTE_MAP.ncosSet])
}
},
watch: {

@ -131,7 +131,7 @@ export default {
]),
...mapGetters('user', [
'hasSubscriberProfileAttribute',
'hasSubscriberProfileAttributes'
'hasSomeSubscriberProfileAttributes'
]),
...mapGetters('callForwarding', [
'groups',

@ -163,10 +163,6 @@ export default {
id: {
type: String,
default: ''
},
isPbxConfigurationContext: {
type: Boolean,
default: false
}
},
data () {
@ -216,7 +212,7 @@ export default {
},
async setChangedData (field, value) {
try {
await this.faxServerSettingsUpdateAction({ field, value, id: this.id, fromPbxConfiguration: this.isPbxConfigurationContext })
await this.faxServerSettingsUpdateAction({ field, value, id: this.id })
this.updateDataFromStore()
} catch (err) {
showGlobalError(err?.message)

@ -47,6 +47,7 @@
:label="$t('Maximum calls in queue')"
:error="v$.changes.max_queue_length.$errors.length > 0"
:error-message="queueMaxLengthErrorMessage"
:disable="disableMaxQueueLength"
@update:model-value="v$.changes.max_queue_length.$touch()"
@keyup.enter="save"
>
@ -69,6 +70,7 @@
:label="$t('Wrap up time')"
:error="v$.changes.queue_wrap_up_time.$errors.length > 0"
:error-message="queueWrapUpTimeErrorMessage"
:disable="disableQueueWrapUpTime"
@update:model-value="v$.changes.queue_wrap_up_time.$touch()"
@keyup.enter="save"
>
@ -102,6 +104,8 @@ import CscListItemTitle from 'components/CscListItemTitle'
import CscListMenuItem from 'components/CscListMenuItem'
import CscInputButtonReset from 'components/form/CscInputButtonReset'
import CscInputButtonSave from 'components/form/CscInputButtonSave'
import { PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import { mapGetters } from 'vuex'
export default {
name: 'CscPbxCallQueue',
@ -165,6 +169,15 @@ export default {
}
},
computed: {
...mapGetters('user', [
'hasSubscriberProfileAttribute'
]),
disableMaxQueueLength () {
return !this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.maxQueueLength)
},
disableQueueWrapUpTime () {
return !this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.queueWrapUpTime)
},
getTitleIcon () {
let icon = 'person'
if (this.subscriber.is_pbx_group) {

@ -13,17 +13,16 @@
v-model="data.max_queue_length"
:error="v$.data.max_queue_length.$errors.length > 0"
:error-message="maxQueueLengthErrorMessage"
:disable="loading"
:disable="disableMaxQueueLength || loading"
:readonly="loading"
:label="$t('Queue Length')"
default="3"
@update:model-value="v$.data.max_queue_length.$touch()"
/>
<q-input
v-model="data.queue_wrap_up_time"
:error="v$.data.queue_wrap_up_time.$errors.length > 0"
:error-message="wrapUpTimeErrorMessage"
:disable="loading"
:disable="disableQueueWrapUpTime || loading"
:readonly="loading"
:label="$t('Wrap up time')"
:suffix="$t('seconds')"
@ -68,6 +67,8 @@ import {
required
} from '@vuelidate/validators'
import CscObjectSpinner from 'components/CscObjectSpinner'
import { PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import { mapGetters } from 'vuex'
export default {
name: 'CscPbxCallQueueAddForm',
@ -123,6 +124,15 @@ export default {
}
},
computed: {
...mapGetters('user', [
'hasSubscriberProfileAttribute'
]),
disableMaxQueueLength () {
return !this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.maxQueueLength)
},
disableQueueWrapUpTime () {
return !this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.queueWrapUpTime)
},
maxQueueLengthErrorMessage () {
const errorsTab = this.v$.data.max_queue_length.$errors
if (errorsTab && errorsTab.length > 0 && errorsTab[0].$validator === 'numeric') {
@ -170,7 +180,7 @@ export default {
return {
subscriber_id: null,
max_queue_length: this.defaultMaxQueueLength,
queue_wrap_up_time: this.defaultWrapUpTime
queue_wrap_up_time: this.defaultQueueWrapUpTime
}
},
cancel () {

@ -128,7 +128,8 @@ import CscInput from 'components/form/CscInput'
import CscPbxAutoAttendantSelection from 'components/pages/PbxConfiguration/CscPbxAutoAttendantSelection'
import _ from 'lodash'
import { Platform } from 'quasar'
import { mapState } from 'vuex'
import { PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import { mapGetters, mapState } from 'vuex'
export default {
name: 'CscPbxDeviceConfigKeyForm',
@ -181,6 +182,9 @@ export default {
...mapState('pbx', [
'subscriberList'
]),
...mapGetters('user', [
'hasSubscriberProfileAttribute'
]),
hasSubscriberChanged () {
return this.keyData.subscriber_id !== this.changes.subscriber_id
},
@ -272,7 +276,7 @@ export default {
value: 'shared'
})
}
if (this.selectedKey !== null && this.selectedKey.keySet.can_speeddial) {
if (this.selectedKey !== null && this.selectedKey.keySet.can_speeddial && this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.speedDial)) {
options.push({
label: this.$t('Speed Dial'),
value: 'speeddial'

@ -81,8 +81,9 @@
<csc-popup-menu-item-delete
@click="deleteSeat"
/>
<q-separator />
<q-separator v-if="showClirIntraPbx || showMusicOnHold" />
<q-item
v-if="showClirIntraPbx"
class="no-padding"
>
<q-item-section>
@ -96,6 +97,7 @@
</q-item-section>
</q-item>
<q-item
v-if="showMusicOnHold"
class="no-padding"
>
<q-item-section>
@ -119,6 +121,8 @@ import CscMoreMenu from 'components/CscMoreMenu'
import CscPopupMenuItem from 'components/CscPopupMenuItem'
import CscPopupMenuItemDelete from 'components/CscPopupMenuItemDelete'
import _ from 'lodash'
import { PROFILE_ATTRIBUTES_MAP, PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import { mapGetters } from 'vuex'
export default {
name: 'CscPbxSeat',
components: {
@ -154,6 +158,18 @@ export default {
changes: this.getSeatData()
}
},
computed: {
...mapGetters('user', [
'hasSubscriberProfileAttribute',
'hasSomeSubscriberProfileAttributes'
]),
showClirIntraPbx () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.clir_intrapbx)
},
showMusicOnHold () {
return this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callSettings)
}
},
watch: {
seat () {
this.changes = this.getSeatData()

@ -150,7 +150,8 @@
<script>
import _ from 'lodash'
import { mapGetters } from 'vuex'
import { PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import { mapGetters, mapState } from 'vuex'
export default {
name: 'CscCdrFilters',
@ -175,6 +176,14 @@ export default {
...mapGetters([
'getCurrentFormattedDateWithDash'
]),
...mapGetters('user', [
'isFaxFeatureEnabled',
'hasSubscriberProfileAttribute',
'isSmsEnabled'
]),
...mapState('user', [
'platformInfo'
]),
filterType () {
return this.filterTypeModel && this.filterTypeModel.value
},
@ -212,18 +221,18 @@ export default {
label: this.$t('Call'),
value: 'call'
},
{
...(this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.voiceMail) ? [{
label: this.$t('Voicemail'),
value: 'voicemail'
},
{
}] : []),
...(this.isSmsEnabled ? [{
label: this.$t('Sms'),
value: 'sms'
},
{
}] : []),
...(this.isFaxFeatureEnabled ? [{
label: this.$t('Fax'),
value: 'fax'
}
}] : [])
]
},
filtersList () {

@ -7,21 +7,40 @@ export const INTERNAL_DATE_FORMAT_DASH = 'YYYY-MM-DD'
export const INTERNAL_DATE_FORMAT_DASH_HOUR = 'YYYY-MM-DD HH:mm'
export const PROFILE_ATTRIBUTE_MAP = {
blockInMode: 'block_in_mode',
blockInList: 'block_in_list',
blockInClir: 'block_in_clir',
blockOutMode: 'block_out_mode',
blockOutList: 'block_out_list',
blockOutOverridePin: 'block_out_override_pin',
cloudPbxCallQueue: 'cloud_pbx_callqueue',
language: 'language',
ncos: 'ncos',
ncosSet: 'ncos_set',
maxQueueLength: 'max_queue_length',
queueWrapUpTime: 'queue_wrap_up_time',
reminder: 'reminder',
speedDial: 'speed_dial',
voiceMail: 'voice_mail',
clir: 'clir',
clir_intrapbx: 'clir_intrapbx',
cstaClient: 'csta_client',
cstaController: 'csta_controller',
faxServer: 'fax_server',
cscCalls: 'csc_calls',
manager_secretary: 'manager_secretary',
auto_attendant: 'auto_attendant',
managerSecretary: 'manager_secretary',
secretaryNumbers: 'secretary_numbers',
autoAttendant: 'auto_attendant',
soundSet: 'contract_sound_set',
deviceProvisioning: 'csc_device_provisioning',
conversations: 'csc_conversations',
registeredDevices: 'csc_registered_devices',
recordings: 'record_call',
huntGroups: 'csc_hunt_groups'
huntGroups: 'csc_hunt_groups',
playAnnounceBeforeCallSetup: 'play_announce_before_call_setup',
playAnnounceBeforeCF: 'play_announce_before_cf',
playAnnounceBeforeRecording: 'play_announce_before_recording',
playAnnounceToCallee: 'play_announce_to_callee'
}
export const PROFILE_ATTRIBUTES_MAP = {
@ -30,7 +49,8 @@ export const PROFILE_ATTRIBUTES_MAP = {
callBlockingPrivacy: ['clir', 'clir_intrapbx'],
callSettings: ['music_on_hold', 'language'],
pbxSettings: ['auto_attendant', 'cloud_pbx_callqueue', 'max_queue_length', 'queue_wrap_up_time', 'manager_secretary'],
pbxSettingsCallQueue: ['cloud_pbx_callqueue', 'max_queue_length', 'queue_wrap_up_time']
pbxSettingsCallQueue: ['cloud_pbx_callqueue', 'max_queue_length', 'queue_wrap_up_time'],
callForwarding: ['cfu', 'cfb', 'cfna', 'cft', 'cfs', 'cfo', 'cfr', 'rerouting_mode', 'rerouting_codes']
}
export const LICENSES = {
@ -41,3 +61,11 @@ export const LICENSES = {
phonebook: 'phonebook',
call_recording: 'call_recording'
}
export const FEATURES = {
cloudPbx: 'cloudpbx',
sms: 'sms',
faxServer: 'faxserver',
fileShare: 'fileshare',
mobilePush: 'mobilepush'
}

@ -21,7 +21,7 @@
@click="$refs.mainMenu.show()"
/>
<q-btn
v-if="hasFaxCapabilityAndFaxActive && hasSendFaxFeature"
v-if="isFaxFeatureEnabled && isFaxServerSettingsActive"
class="q-mr-sm"
flat
dense
@ -330,17 +330,11 @@ export default {
'remoteOnHold'
]),
...mapGetters('user', [
'isLogged',
'hasUser',
'getUsername',
'isPbxAdmin',
'hasSmsCapability',
'hasFaxCapabilityAndFaxActive',
'hasSendSmsFeature',
'hasSendFaxFeature',
'userDataRequesting',
'isFaxFeatureEnabled',
'isFaxServerSettingsActive',
'userDataSucceeded',
'isLogoRequesting',
'isLogoRequested',
'hasSubscriberProfileAttribute'
]),

@ -6,10 +6,10 @@
<q-list
class="col col-xs-12 col-md-6"
>
<q-item
v-if="hasSubscriberProfileAttribute('music_on_hold')"
>
<q-item-section>
<q-item>
<q-item-section
v-if="showToggles"
>
<q-toggle
:model-value="musicOnHoldValue"
:disable="dataLoading"
@ -45,6 +45,7 @@
<script>
import CscPage from 'components/CscPage'
import CscSpinner from 'components/CscSpinner'
import { PROFILE_ATTRIBUTES_MAP } from 'src/constants'
import { showGlobalError } from 'src/helpers/ui'
import {
mapWaitingActions,
@ -69,7 +70,7 @@ export default {
'dnd'
]),
...mapGetters('user', [
'hasSubscriberProfileAttribute'
'hasSomeSubscriberProfileAttributes'
]),
...mapWaitingGetters({
processingSubscriberPreferences: 'processing subscriberPreferences'
@ -77,6 +78,9 @@ export default {
dataLoading () {
return !this.subscriberPreferencesInitialized || this.processingSubscriberPreferences
},
showToggles () {
return this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callSettings)
},
musicOnHoldValue () {
return this.musicOnHold || false
},

@ -4,7 +4,7 @@
class="q-pa-lg"
>
<template
v-if="hasSubscriberProfileAttributes(['cfu', 'cfna', 'cfb'])"
v-if="hasSomeSubscriberProfileAttributes(['cfu', 'cfna', 'cfb'])"
#header
>
<q-btn
@ -127,7 +127,7 @@ export default {
]),
...mapGetters('user', [
'hasSubscriberProfileAttribute',
'hasSubscriberProfileAttributes'
'hasSomeSubscriberProfileAttributes'
]),
isRingTimeoutVisible () {
return this.ringTimeout && this.groups.some((group) => group.type === 'cft')

@ -118,7 +118,7 @@ import CscRemoveDialog from 'components/CscRemoveDialog'
import CscConversationItem from 'components/pages/Conversations/CscConversationItem'
import CscConversationsCallsFilter from 'components/pages/Conversations/CscConversationsCallsFilter'
import CscConversationsFilter from 'components/pages/Conversations/CscConversationsFilter'
import { LICENSES } from 'src/constants'
import { PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import { showGlobalError } from 'src/helpers/ui'
import platformMixin from 'src/mixins/platform'
import { RequestState } from 'src/store/common'
@ -151,7 +151,8 @@ export default {
},
computed: {
...mapGetters('user', [
'isLicenseActive'
'isFaxFeatureEnabled',
'hasSubscriberProfileAttribute'
]),
...mapState('conversations', [
'reachedLastPage',
@ -177,7 +178,7 @@ export default {
'isCallEnabled'
]),
tabs () {
return [
const tabs = [
{
label: this.$t('All'),
value: 'call-fax-voicemail',
@ -188,19 +189,23 @@ export default {
value: 'call',
icon: 'call'
},
{
...(this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.voiceMail) ? [{
label: this.$t('Voicemails'),
value: 'voicemail',
icon: 'voicemail'
},
this.isLicenseActive(LICENSES.fax)
? {
label: this.$t('Faxes'),
value: 'fax',
icon: 'description'
}
: null
].filter((label) => label !== null)
}] : []),
...(this.isFaxFeatureEnabled ? [{
label: this.$t('Faxes'),
value: 'fax',
icon: 'description'
}] : [])
]
if (tabs.length === 2) {
return tabs.filter((tab) => tab.value !== 'call-fax-voicemail')
}
return tabs
},
pageStyle () {
return {

@ -10,6 +10,7 @@
<q-item>
<q-item-section>
<q-toggle
v-if="showIgnoreMembersCFWhenHunting"
v-model="changes.ignore_cf_when_hunting"
class="q-pa-sm"
:label="$t('Ignore Members Call Forwards when Hunting')"
@ -20,6 +21,7 @@
<q-item>
<q-item-section>
<q-toggle
v-if="showBlockModeForInboundCalls"
v-model="changes.block_in_mode"
class="q-pa-sm"
:label="$t('Block Mode for inbound calls')"
@ -30,6 +32,7 @@
<q-item>
<q-item-section>
<csc-input-chips
v-if="showBlockListForInboundCalls"
:value="changes.block_in_list"
:label="$t('Block List for inbound calls')"
:emit-array="true"
@ -41,6 +44,7 @@
<q-item>
<q-item-section>
<q-toggle
v-if="showBlockAnonymousInboundCalls"
v-model="changes.block_in_clir"
class="q-pa-sm"
:label="$t('Block anonymous inbound calls')"
@ -51,6 +55,7 @@
<q-item>
<q-item-section>
<q-toggle
v-if="showBlockModeForOutboundCalls"
v-model="changes.block_out_mode"
class="q-pa-sm"
:label="$t('Block Mode for outbounds calls')"
@ -61,6 +66,7 @@
<q-item>
<q-item-section>
<csc-input-chips
v-if="showBlockListForOutboundCalls"
:value="changes.block_out_list"
:label="$t('Block List for outbounds calls')"
:emit-array="true"
@ -72,6 +78,7 @@
<q-item>
<q-item-section>
<q-input
v-if="showBlockOutOverridePin"
v-model.trim="changes.block_out_override_pin"
:label="$t('PIN to bypass outbound Block List')"
clearable
@ -97,6 +104,7 @@
<q-item>
<q-item-section>
<q-toggle
v-if="showPlayAnnounceBeforeCallSetup"
v-model="changes.play_announce_before_call_setup"
class="q-pa-sm"
:label="$t('Play announcement before call setup')"
@ -107,6 +115,7 @@
<q-item>
<q-item-section>
<q-toggle
v-if="showPlayAnnounceToCallee"
v-model="changes.play_announce_to_callee"
class="q-pa-sm"
:label="$t('Play announcement to callee after answer')"
@ -126,6 +135,7 @@ import CscPage from 'components/CscPage'
import CscInputButtonReset from 'components/form/CscInputButtonReset'
import CscInputButtonSave from 'components/form/CscInputButtonSave'
import _ from 'lodash'
import { PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import { showGlobalError } from 'src/helpers/ui'
import { RequestState } from 'src/store/common'
import {
@ -161,7 +171,8 @@ export default {
},
computed: {
...mapGetters('user', [
'getCustomerId'
'getCustomerId',
'hasSubscriberProfileAttribute'
]),
...mapState('customer', [
'customerPreferences',
@ -171,6 +182,33 @@ export default {
]),
hasBlockOutOverridePinChanged () {
return this.changes.block_out_override_pin !== this.customerPreferences.block_out_override_pin
},
showBlockAnonymousInboundCalls () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.blockInClir)
},
showBlockListForInboundCalls () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.blockInList)
},
showBlockListForOutboundCalls () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.blockOutList)
},
showBlockModeForInboundCalls () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.blockInMode)
},
showBlockModeForOutboundCalls () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.blockOutMode)
},
showBlockOutOverridePin () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.blockOutOverridePin)
},
showIgnoreMembersCFWhenHunting () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.huntGroups)
},
showPlayAnnounceBeforeCallSetup () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.playAnnounceBeforeCallSetup)
},
showPlayAnnounceToCallee () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.playAnnounceToCallee)
}
},
watch: {

@ -4,7 +4,7 @@
class="row justify-center"
>
<csc-card-dashboard
v-if="showConversationsCard"
v-if="showVoicemailCard"
:title="$t('Voicebox Messages')"
:count="voicemailsCount"
:count-title="$t('Messages')"
@ -104,6 +104,10 @@ export default {
showConversationsCard () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.conversations)
},
showVoicemailCard () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.conversations) &&
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.voiceMail)
},
showRegDevices () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.registeredDevices)
}

@ -207,6 +207,7 @@
</template>
</q-select>
<q-select
v-if="showSoundSet"
v-model="changes.soundSet"
emit-value
map-options
@ -245,6 +246,7 @@
no-wrap
>
<q-toggle
v-if="showPlayAnnounceBeforeCF"
v-model="changes.announcementCfu"
class="q-pa-sm"
:label="$t('Play announcement before routing to CFU/CFNA')"
@ -252,6 +254,7 @@
@update:model-value="changeAnnouncementCfu"
/>
<q-toggle
v-if="showPlayAnnounceBeforeCallSetup"
v-model="changes.announcementCallSetup"
class="q-pa-sm"
:label="$t('Play announcement before call setup')"
@ -295,6 +298,7 @@ import CscFaxToMailSettings from 'components/pages/FaxSettings/CscFaxToMailSetti
import CscMailToFaxSettings from 'components/pages/FaxSettings/CscMailToFaxSettings'
import _ from 'lodash'
import CscPageVoicebox from 'pages/CscPageVoicebox'
import { PROFILE_ATTRIBUTES_MAP, PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import numberFilter from 'src/filters/number'
import { showGlobalError, showToast } from 'src/helpers/ui'
import { inRange } from 'src/helpers/validation'
@ -339,26 +343,26 @@ export default {
value: 'preferences',
icon: 'perm_phone_msg'
},
{
...(this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callForwarding) ? [{
label: this.$t('Call Forwards'),
value: 'callForwards',
icon: 'forward_to_inbox'
},
{
}] : []),
...(this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.voiceMail) ? [{
label: this.$t('Voicebox'),
value: 'voicebox',
icon: 'voicemail'
},
{
}] : []),
...(this.isFaxFeatureEnabled ? [{
label: this.$t('Fax to mail and sendfax'),
value: 'fax2mail',
icon: 'perm_phone_msg'
},
{
}] : []),
...(this.isFaxFeatureEnabled ? [{
label: this.$t('Mail to Fax'),
value: 'mail2fax',
icon: 'forward_to_inbox'
}
}] : [])
]
},
...mapState('pbxGroups', [
@ -387,6 +391,11 @@ export default {
...mapGetters('callForwarding', [
'groups'
]),
...mapGetters('user', [
'isFaxFeatureEnabled',
'hasSubscriberProfileAttribute',
'hasSomeSubscriberProfileAttributes'
]),
hasNameChanged () {
return this.changes.name !== this.groupSelected.display_name
},
@ -426,6 +435,15 @@ export default {
},
isLoading () {
return this.isGroupLoading(this.groupSelected.id)
},
showPlayAnnounceBeforeCallSetup () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.playAnnounceBeforeCallSetup)
},
showPlayAnnounceBeforeCF () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.playAnnounceBeforeCF)
},
showSoundSet () {
return this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.soundSet)
}
},
watch: {

@ -223,6 +223,7 @@
</template>
</q-select>
<q-select
v-if="showNcos"
v-model="changes.ncos"
use-chips
radio
@ -246,6 +247,7 @@
</template>
</q-select>
<q-select
v-if="showNcosSet"
v-model="changes.ncosSet"
use-chips
radio
@ -286,6 +288,7 @@
no-wrap
>
<q-toggle
v-if="showClirIntraPbx"
v-model="changes.clirIntrapbx"
class="q-pa-sm"
:label="$t('Hide number within own PBX')"
@ -293,6 +296,7 @@
@update:model-value="changeIntraPbx"
/>
<q-toggle
v-if="showMusicOnHold"
v-model="changes.musicOnHold"
class="q-pa-sm"
:label="$t('Music on Hold')"
@ -300,6 +304,7 @@
@update:model-value="changeMusicOnHold"
/>
<q-toggle
v-if="showPlayAnnounceBeforeCF"
v-model="changes.announcementCfu"
class="q-pa-sm"
:label="$t('Play announcement before routing to CFU/CFNA')"
@ -307,6 +312,7 @@
@update:model-value="changeAnnouncementCfu"
/>
<q-toggle
v-if="showPlayAnnounceBeforeCallSetup"
v-model="changes.announcementCallSetup"
class="q-pa-sm"
:label="$t('Play announcement before call setup')"
@ -314,6 +320,7 @@
@update:model-value="changeAnnouncementCallSetup"
/>
<q-toggle
v-if="showPlayAnnounceToCallee"
v-model="changes.announcementToCallee"
class="q-pa-sm"
:label="$t('Play announcement to callee after answer')"
@ -321,6 +328,7 @@
@update:model-value="changeAnnouncementToCallee"
/>
<q-toggle
v-if="showHuntGroups"
v-model="changes.ignoreCfWhenHunting"
class="q-pa-sm"
:label="$t('Ignore Members Call Forwards when Hunting')"
@ -328,6 +336,7 @@
@update:model-value="changeIgnoreCfWhenHunting"
/>
<q-toggle
v-if="showCstaClient"
v-model="changes.cstaClient"
class="q-pa-sm"
:label="$t('CSTA Client')"
@ -335,6 +344,7 @@
@update:model-value="changeCstaClient"
/>
<q-toggle
v-if="showCstaController"
v-model="changes.cstaController"
class="q-pa-sm"
:label="$t('CSTA Controller')"
@ -380,6 +390,7 @@ import CscFaxToMailSettings from 'components/pages/FaxSettings/CscFaxToMailSetti
import CscMailToFaxSettings from 'components/pages/FaxSettings/CscMailToFaxSettings'
import _ from 'lodash'
import CscPageVoicebox from 'pages/CscPageVoicebox'
import { PROFILE_ATTRIBUTES_MAP, PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import numberFilter from 'src/filters/number'
import { showGlobalError, showToast } from 'src/helpers/ui'
import { inRange } from 'src/helpers/validation'
@ -429,26 +440,26 @@ export default {
value: 'preferences',
icon: 'perm_phone_msg'
},
{
...(this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callForwarding) ? [{
label: this.$t('Call Forwards'),
value: 'callForwards',
icon: 'forward_to_inbox'
},
{
}] : []),
...(this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.voiceMail) ? [{
label: this.$t('Voicebox'),
value: 'voicebox',
icon: 'voicemail'
},
{
}] : []),
...(this.isFaxFeatureEnabled ? [{
label: this.$t('Fax to mail and sendfax'),
value: 'fax2mail',
icon: 'perm_phone_msg'
},
{
}] : []),
...(this.isFaxFeatureEnabled ? [{
label: this.$t('Mail to Fax'),
value: 'mail2fax',
icon: 'forward_to_inbox'
}
}] : [])
]
},
...mapState('pbxSeats', [
@ -460,8 +471,9 @@ export default {
'groups'
]),
...mapGetters('user', [
'isFaxFeatureEnabled',
'hasSubscriberProfileAttribute',
'hasSubscriberProfileAttributes'
'hasSomeSubscriberProfileAttributes'
]),
...mapGetters('pbxSeats', [
'getSoundSetBySeatId',
@ -555,6 +567,36 @@ export default {
}
return ''
},
showClirIntraPbx () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.clir_intrapbx)
},
showCstaClient () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.cstaClient)
},
showCstaController () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.cstaController)
},
showHuntGroups () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.huntGroups)
},
showMusicOnHold () {
return this.hasSomeSubscriberProfileAttributes([PROFILE_ATTRIBUTES_MAP.callSettings])
},
showNcos () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.ncos)
},
showNcosSet () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.ncosSet)
},
showPlayAnnounceBeforeCallSetup () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.playAnnounceBeforeCallSetup)
},
showPlayAnnounceBeforeCF () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.playAnnounceBeforeCF)
},
showPlayAnnounceToCallee () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.playAnnounceToCallee)
},
internalSoundSetOptions () {
const items = []
if (this.soundSet) {

@ -25,7 +25,7 @@
:value-changed="hasMaxQueueLengthChanged"
:error="v$.changes.max_queue_length.$errors.length > 0"
:error-message="queueMaxLengthErrorMessage"
:disable="isLoading || !cloud_pbx_callqueue"
:disable="disableMaxQueueLength || isLoading"
@undo="resetMaxQueueLength"
@save="save"
@update:model-value="v$.changes.max_queue_length.$touch()"
@ -43,7 +43,7 @@
:value-changed="hasQueueWrapUpTimeChanged"
:error="v$.changes.queue_wrap_up_time.$errors.length > 0"
:error-message="queueWrapUpTimeErrorMessage"
:disable="isLoading || !cloud_pbx_callqueue"
:disable="disableQueueWrapUpTime || isLoading"
@undo="resetQueueWrapUpTime"
@save="save"
@update:model-value="v$.changes.queue_wrap_up_time.$touch()"
@ -69,6 +69,7 @@ import CscPage from 'components/CscPage'
import CscSpinner from 'components/CscSpinner'
import CscInputSaveable from 'components/form/CscInputSaveable'
import { getSubscriberId } from 'src/auth'
import { PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import { showToast } from 'src/helpers/ui'
import { mapWaitingActions } from 'vue-wait'
import {
@ -86,7 +87,7 @@ export default {
return {
callQueue: null,
changes: null,
cloud_pbx_callqueue: false,
cloud_pbx_callqueue: null,
v$: useValidate()
}
},
@ -113,8 +114,15 @@ export default {
'subscriberPreferences'
]),
...mapGetters('user', [
'getUsername'
'getUsername',
'hasSubscriberProfileAttribute'
]),
disableMaxQueueLength () {
return !this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.maxQueueLength)
},
disableQueueWrapUpTime () {
return !this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.queueWrapUpTime)
},
hasMaxQueueLengthChanged () {
return this.callQueue.max_queue_length !== this.changes.max_queue_length
},

@ -16,7 +16,7 @@
v-if="isLoading || !msConfig || !changes"
class="q-ml-xl"
/>
<q-item v-if="msConfig && changes">
<q-item v-if="msConfig && changes && showSecretaryNumbers">
<q-item-section>
<q-select
v-model="changes.secretaryNumbers"
@ -53,6 +53,7 @@ import CscInputButtonReset from 'components/form/CscInputButtonReset'
import CscInputButtonSave from 'components/form/CscInputButtonSave'
import _ from 'lodash'
import { getSubscriberId } from 'src/auth'
import { PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import { showToast } from 'src/helpers/ui'
import { mapWaitingActions } from 'vue-wait'
import {
@ -83,7 +84,8 @@ export default {
'subscriberPreferences'
]),
...mapGetters('user', [
'getUsername'
'getUsername',
'hasSubscriberProfileAttribute'
]),
hasSecretaryNumbersChanged () {
const changedSecretaryNumbers = _.clone(_.get(this.changes, 'secretaryNumbers', []))
@ -93,6 +95,9 @@ export default {
isLoading () {
return this.$wait.is('csc-pbx-manager-secretary-numbers') || this.$wait.is('csc-pbx-call-settings-load-preferences') ||
this.$wait.is('csc-pbx-call-settings-update-preferences')
},
showSecretaryNumbers () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.secretaryNumbers)
}
},
async mounted () {

@ -14,6 +14,7 @@
label: defaultLanguage,
value: defaultLanguage
}"
:disabled="disableLanguage"
:loading="$wait.is('processing subscriberPreferences')"
@input="selectLanguage"
/>
@ -215,6 +216,7 @@ import CscSpinner from 'components/CscSpinner'
import CscVoiceboxLanguage from 'components/CscVoiceboxLanguage'
import CscInputSaveable from 'components/form/CscInputSaveable'
import CscSoundFileUpload from 'components/form/CscSoundFileUpload'
import { PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import {
showGlobalError,
showToast
@ -348,6 +350,12 @@ export default {
'defaultLanguage',
'languages'
]),
...mapGetters('user', [
'hasSubscriberProfileAttribute'
]),
disableLanguage () {
return !this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.language)
},
soundFileIcon () {
return 'music_note'
},

@ -44,7 +44,12 @@ import CscPageSubscriberPhonebookDetails from 'pages/CscPageSubscriberPhonebookD
import CscPageUserSettings from 'pages/CscPageUserSettings'
import CscPageVoicebox from 'pages/CscPageVoicebox'
import CscRecoverPassword from 'pages/CscRecoverPassword'
import { LICENSES, PROFILE_ATTRIBUTES_MAP, PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import {
FEATURES,
LICENSES,
PROFILE_ATTRIBUTES_MAP,
PROFILE_ATTRIBUTE_MAP
} from 'src/constants'
export const PATH_CHANGE_PASSWORD = '/changepassword'
@ -75,7 +80,8 @@ const routes = [
get title () {
return i18n.global.t('Start new call')
},
subscriberProfile: ['csc_calls']
profileAttribute: PROFILE_ATTRIBUTE_MAP.cscCalls,
allowCE: true
}
},
{
@ -101,7 +107,7 @@ const routes = [
get title () {
return i18n.global.t('Subscriber Phonebook')
},
license: LICENSES.phonebook
licenses: [LICENSES.phonebook]
}
},
{
@ -110,7 +116,8 @@ const routes = [
meta: {
get title () {
return i18n.global.t('Add Phonebook')
}
},
licenses: [LICENSES.phonebook]
}
},
{
@ -119,7 +126,8 @@ const routes = [
meta: {
get title () {
return i18n.global.t('Subscriber Phonebook')
}
},
licenses: [LICENSES.phonebook]
}
},
{
@ -128,7 +136,11 @@ const routes = [
meta: {
get title () {
return i18n.global.t('Seats')
}
},
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
}
},
{
@ -137,7 +149,8 @@ const routes = [
meta: {
get title () {
return i18n.global.t('Call Forwarding')
}
},
profileAttributes: PROFILE_ATTRIBUTES_MAP.callForwarding
}
},
{
@ -189,7 +202,9 @@ const routes = [
get subtitle () {
return i18n.global.t('Call recordings')
},
profileAttribute: PROFILE_ATTRIBUTE_MAP.recordings
profileAttribute: PROFILE_ATTRIBUTE_MAP.recordings,
licenses: [LICENSES.recordings],
allowCE: true
}
},
{
@ -227,9 +242,12 @@ const routes = [
},
get subtitle () {
return i18n.global.t('CDR')
}
},
license: LICENSES.pbx
},
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
}
},
{
path: 'pbx-configuration/groups',
@ -241,7 +259,11 @@ const routes = [
get subtitle () {
return i18n.global.t('Groups')
},
profileAttribute: PROFILE_ATTRIBUTE_MAP.huntGroups
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
profileAttribute: PROFILE_ATTRIBUTE_MAP.huntGroups,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
}
},
{
@ -253,7 +275,12 @@ const routes = [
},
get subtitle () {
return i18n.global.t('Groups')
}
},
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
profileAttribute: PROFILE_ATTRIBUTE_MAP.huntGroups,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
}
},
{
@ -265,7 +292,11 @@ const routes = [
},
get subtitle () {
return i18n.global.t('Seats')
}
},
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
}
},
{
@ -277,7 +308,11 @@ const routes = [
},
get subtitle () {
return i18n.global.t('Customer Phonebook')
}
},
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx, LICENSES.phonebook]
}
},
{
@ -286,7 +321,11 @@ const routes = [
meta: {
get title () {
return i18n.global.t('Add Phonebook')
}
},
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx, LICENSES.phonebook]
}
},
{
@ -295,7 +334,11 @@ const routes = [
meta: {
get title () {
return i18n.global.t('Upload CSV')
}
},
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx, LICENSES.phonebook]
}
},
{
@ -304,7 +347,11 @@ const routes = [
meta: {
get title () {
return i18n.global.t('Customer Phonebook')
}
},
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx, LICENSES.phonebook]
}
},
{
@ -316,7 +363,12 @@ const routes = [
},
get subtitle () {
return i18n.global.t('Devices')
}
},
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
profileAttribute: PROFILE_ATTRIBUTE_MAP.deviceProvisioning,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx, LICENSES.device_provisioning]
}
},
{
@ -328,7 +380,11 @@ const routes = [
},
get subtitle () {
return i18n.global.t('Seats')
}
},
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
}
},
{
@ -341,7 +397,11 @@ const routes = [
get subtitle () {
return i18n.global.t('Devices')
},
profileAttribute: PROFILE_ATTRIBUTE_MAP.deviceProvisioning
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
profileAttribute: PROFILE_ATTRIBUTE_MAP.deviceProvisioning,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx, LICENSES.device_provisioning]
}
},
{
@ -354,7 +414,11 @@ const routes = [
get subtitle () {
return i18n.global.t('Call Queues')
},
profileAttributes: PROFILE_ATTRIBUTES_MAP.extSettingsCallQueue
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
profileAttributes: PROFILE_ATTRIBUTE_MAP.cloudPbxCallQueue,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
}
},
{
@ -367,7 +431,11 @@ const routes = [
get subtitle () {
return i18n.global.t('Sound Sets')
},
profileAttribute: PROFILE_ATTRIBUTE_MAP.soundSet
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
profileAttribute: PROFILE_ATTRIBUTE_MAP.soundSet,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
}
},
{
@ -379,7 +447,12 @@ const routes = [
},
get subtitle () {
return i18n.global.t('Sound Sets')
}
},
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
profileAttribute: PROFILE_ATTRIBUTE_MAP.soundSet,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
}
},
{
@ -392,7 +465,11 @@ const routes = [
get subtitle () {
return i18n.global.t('Manager Secretary')
},
profileAttribute: PROFILE_ATTRIBUTE_MAP.manager_secretary
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
profileAttribute: PROFILE_ATTRIBUTE_MAP.managerSecretary,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
}
},
{
@ -405,7 +482,11 @@ const routes = [
get subtitle () {
return i18n.global.t('Auto Attendant')
},
profileAttribute: PROFILE_ATTRIBUTE_MAP.auto_attendant
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
profileAttribute: PROFILE_ATTRIBUTE_MAP.autoAttendant,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
}
},
{
@ -417,8 +498,13 @@ const routes = [
},
get subtitle () {
return i18n.global.t('Customer Preferences')
}
},
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx, LICENSES.phonebook]
}
},
{
path: 'voicebox',
@ -443,8 +529,10 @@ const routes = [
get subtitle () {
return i18n.global.t('Set your fax settings')
},
platformFeature: FEATURES.faxServer,
profileAttribute: PROFILE_ATTRIBUTE_MAP.faxServer,
license: LICENSES.fax
capability: FEATURES.faxServer,
licenses: [LICENSES.fax]
}
},
{
@ -482,7 +570,10 @@ const routes = [
get subtitle () {
return i18n.global.t('Call Queues')
},
profileAttributes: PROFILE_ATTRIBUTES_MAP.extSettingsCallQueue
platformFeature: FEATURES.cloudPbx,
profileAttributes: PROFILE_ATTRIBUTE_MAP.cloudPbxCallQueue,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
}
},
{
@ -495,7 +586,10 @@ const routes = [
get subtitle () {
return i18n.global.t('Manager Secretary')
},
profileAttribute: PROFILE_ATTRIBUTE_MAP.manager_secretary
profileAttribute: PROFILE_ATTRIBUTE_MAP.managerSecretary,
platformFeature: FEATURES.cloudPbx,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
}
},
{
@ -508,7 +602,10 @@ const routes = [
get subtitle () {
return i18n.global.t('Auto Attendant')
},
profileAttribute: PROFILE_ATTRIBUTE_MAP.auto_attendant
platformFeature: FEATURES.cloudPbx,
profileAttribute: PROFILE_ATTRIBUTE_MAP.autoAttendant,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
}
},
{

@ -5,6 +5,7 @@ import {
setFaxServerField,
setMailToFaxSettingField
} from 'src/api/fax'
import { getSubscriberId } from 'src/auth'
export default {
namespaced: true,
@ -40,6 +41,10 @@ export default {
context.commit('settingsSucceeded', {
faxServerSettings
})
if (toString(subscriberId) === toString(getSubscriberId())) {
context.commit('user/updateIsFaxServerSettingsActive', faxServerSettings.active, { root: true })
}
},
async faxServerSettingsUpdateAction (context, options) {
const subscriberId = options.id || context.getters.subscriberId
@ -51,8 +56,9 @@ export default {
context.commit('settingsSucceeded', {
faxServerSettings
})
if (!options.fromPbxConfiguration) {
context.commit('user/updateFaxActiveCapabilityState', faxServerSettings.active, { root: true })
if (toString(subscriberId) === toString(getSubscriberId())) {
context.commit('user/updateIsFaxServerSettingsActive', faxServerSettings.active, { root: true })
}
},

@ -302,6 +302,7 @@ export default {
},
subscribersSucceeded (state, subscribers) {
state.subscriberList = _.get(subscribers, 'items', [])
state.subscriberListState = RequestState.succeeded
state.subscriberMap = {}
state.subscriberList.forEach((subscriber) => {
state.subscriberMap[subscriber.id] = subscriber

@ -38,9 +38,13 @@ import {
login
} from 'src/api/user'
import {
deleteJwt, getJwt, getSubscriberId, setJwt, setSubscriberId
deleteJwt,
getJwt,
getSubscriberId,
setJwt,
setSubscriberId
} from 'src/auth'
import { PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import { LICENSES, PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import { getSipInstanceId } from 'src/helpers/call-utils'
import { parseBlobToObject } from 'src/helpers/parse-blob-to-object'
import { qrPayload } from 'src/helpers/qr'
@ -56,10 +60,6 @@ export default {
subscriber: null,
capabilities: null,
profile: null,
features: {
sendFax: true,
sendSms: false
},
loginRequesting: false,
loginSucceeded: false,
loginError: null,
@ -84,7 +84,8 @@ export default {
platformInfo: null,
qrCode: null,
qrExpiringTime: null,
numberInput: ''
numberInput: '',
isFaxServerSettingsActive: false
},
getters: {
isLogged (state) {
@ -104,30 +105,37 @@ export default {
isAdmin (state) {
return state.subscriber !== null && state.subscriber.administrative
},
isPbxAdmin (state, getters) {
return getters.isAdmin && state.capabilities !== null && state.capabilities.cloudpbx
isPbxAdmin (_, getters) {
return getters.isAdmin && getters.isPbxEnabled
},
isPbxEnabled (state) {
return state.capabilities !== null && state.capabilities.cloudpbx
return state?.capabilities?.cloudpbx &&
state?.platformInfo?.cloudpbx &&
state.platformInfo.licenses.includes(LICENSES.pbx)
},
hasSmsCapability (state) {
return state.capabilities !== null &&
state.capabilities.sms === true
hasCapability (state) {
return (capability) => {
return state?.capabilities?.[capability]
}
},
hasSendSmsFeature (state) {
return state.features.sendSms
hasPlatformFeature (state) {
return (feature) => {
return state?.platformInfo?.[feature]
}
},
hasSendFaxFeature (state) {
return state.features.sendFax
isSmsEnabled (state) {
return state?.platformInfo?.sms &&
state?.capabilities?.sms &&
state?.platformInfo?.licenses?.includes(LICENSES.sms)
},
hasFaxCapability (state) {
return state.capabilities !== null &&
state.capabilities.faxserver
isFaxFeatureEnabled (state, getters) {
return state?.capabilities?.faxserver &&
state?.platformInfo?.faxserver &&
getters.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.faxServer) &&
state?.platformInfo?.licenses?.includes(LICENSES.fax)
},
hasFaxCapabilityAndFaxActive (state) {
return state.capabilities !== null &&
state.capabilities.faxserver &&
state.capabilities.faxactive
isFaxServerSettingsActive (state) {
return state?.isFaxServerSettingsActive
},
getSubscriberId (state) {
return state.subscriberId
@ -201,21 +209,24 @@ export default {
},
hasSubscriberProfileAttribute: (state) => {
return (attribute) => {
return state.profile ? state.profile.attributes.includes(attribute) : true
return state?.profile ? state?.profile?.attributes?.includes(attribute) : true
}
},
hasSubscriberProfileAttributes: (state) => {
hasSomeSubscriberProfileAttributes: (state) => {
return (attributes) => {
return state.profile
? state.profile.attributes.some((item) => {
return attributes.includes(item)
return state?.profile
? state.profile.attributes?.some((item) => {
return attributes?.includes(item)
})
: true
}
},
isLicenseActive: (state) => {
return (license) => {
return state?.platformInfo.licenses.includes(license)
hasLicenses: (state) => {
return (licenses) => {
if (!state?.platformInfo?.licenses) {
return false
}
return licenses?.every((license) => state?.platformInfo?.licenses?.includes(license))
}
},
isPbxPilot (state) {
@ -231,7 +242,7 @@ export default {
return getters.isPbxPilot || getters.isPbxGroup || getters.isPbxSeat
},
isSpCe (state) {
return state.platformInfo.type === 'spce'
return state?.platformInfo?.type === 'spce'
},
prefilledNumber (state) {
return state.numberInput
@ -272,6 +283,7 @@ export default {
state.capabilities = options.capabilities
state.resellerBranding = options.resellerBranding
state.platformInfo = options.platformInfo
state.isFaxServerSettingsActive = options.isFaxServerSettingsActive
state.userDataSucceeded = true
state.userDataRequesting = false
@ -315,8 +327,8 @@ export default {
updateLogo (state, value) {
state.logo = value
},
updateFaxActiveCapabilityState (state, value) {
state.capabilities.faxactive = value
updateIsFaxServerSettingsActive (state, value) {
state.isFaxServerSettingsActive = value
},
updateLogoRequestState (state, isRequesting) {
state.logoRequesting = isRequesting
@ -472,6 +484,7 @@ export default {
context.commit('userDataRequesting')
const userData = await getUserData(getSubscriberId())
context.commit('userDataSucceeded', userData)
if (_.isNumber(context.getters.jwtTTL)) {
setTimeout(() => {
setLocal('show_session_expired_msg', true)

Loading…
Cancel
Save