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 post
} from 'src/api/common' } from 'src/api/common'
import { getFaxServerSettings } from 'src/api/fax' import { getFaxServerSettings } from 'src/api/fax'
import { LICENSES } from 'src/constants'
export function login ({ username, password, otp = null }) { export function login ({ username, password, otp = null }) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -70,17 +71,21 @@ export async function getUserData (id) {
]) ])
try { try {
let isFaxServerSettingsActive = false
const [subscriber, capabilities, resellerBranding, platformInfo] = await allPromise const [subscriber, capabilities, resellerBranding, platformInfo] = await allPromise
if (capabilities.faxserver && platformInfo.licenses.find((license) => license === 'fax')) { if (capabilities.faxserver && platformInfo.licenses.find((license) => license === LICENSES.fax)) {
const faxServerSettings = await getFaxServerSettings(id) // Note that isFaxServerSettingsActive determines if the menu has been enabled by admin
capabilities.faxactive = faxServerSettings.active // or, in other words, if the relevant toggle is on/off.
const responseFaxServerSettings = await getFaxServerSettings(id)
isFaxServerSettingsActive = responseFaxServerSettings.active
} }
return { return {
subscriber, subscriber,
capabilities, capabilities,
resellerBranding: resellerBranding?.items[0] || null, resellerBranding: resellerBranding?.items[0] || null,
platformInfo platformInfo,
isFaxServerSettingsActive
} }
} catch (error) { } catch (error) {
throw new Error(error.response.data.message) 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 () { export function getCapabilities () {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
getList({ getList({

@ -29,25 +29,54 @@ export default ({ app, router, store }) => {
path: '/conference/room123' path: '/conference/room123'
}) })
break break
default: default: {
if (to.meta?.profileAttribute) { // 1. Admin check
const hasSubscriberProfileAttribute = store.getters['user/hasSubscriberProfileAttribute'](to.meta.profileAttribute) if (to.meta?.adminOnly && !store.getters['user/isAdmin']) {
if (to.meta.license && hasSubscriberProfileAttribute) { return next('/')
// Guard to assure that: }
// CE users have access to all available menus as they do not have licenses
if (store.getters['user/isSpCe']) { // 2. Profile attribute check
next() if (to.meta?.profileAttribute &&
} !store.getters['user/hasSubscriberProfileAttribute'](to.meta.profileAttribute)) {
// users cannot click on menu if it is mistakenly visible when the license is inactive return next('/')
store.getters['user/isLicenseActive'](to.meta.license) ? next() : 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('/') // License check for non-CE users
} else if (to.meta?.profileAttributes) { if (!isSpCe && !store.getters['user/hasLicenses']([to.meta.licenses])) {
store.getters['user/hasSubscriberProfileAttributes'](to.meta.profileAttributes) ? next() : next('/') return next('/')
} else { }
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: { computed: {
...mapGetters('user', [ ...mapGetters('user', [
'hasFaxCapability', 'isFaxFeatureEnabled',
'hasSubscriberProfileAttribute', 'hasSubscriberProfileAttribute',
'hasSubscriberProfileAttributes', 'hasSomeSubscriberProfileAttributes',
'isLicenseActive', 'hasLicenses',
'isPbxEnabled', 'isPbxEnabled',
'isSpCe' 'isSpCe'
]), ]),
items () { 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 [ return [
{ {
to: '/user/dashboard', to: '/user/dashboard',
@ -65,7 +84,8 @@ export default {
icon: 'call', icon: 'call',
label: this.callStateTitle, label: this.callStateTitle,
sublabel: this.callStateSubtitle, 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', to: '/user/conversations',
@ -78,18 +98,18 @@ export default {
to: '/user/subscriber-phonebook', to: '/user/subscriber-phonebook',
icon: 'fas fa-user', icon: 'fas fa-user',
label: this.$t('Subscriber Phonebook'), label: this.$t('Subscriber Phonebook'),
visible: this.isLicenseActive(LICENSES.phonebook) visible: this.hasLicenses([LICENSES.phonebook])
}, },
{ {
icon: 'settings_phone', icon: 'settings_phone',
label: this.$t('Call Settings'), label: this.$t('Call Settings'),
visible: true, visible: hasCallSettingsSubmenus,
children: [ children: [
{ {
to: '/user/call-settings', to: '/user/call-settings',
icon: 'settings', icon: 'settings',
label: this.$t('General'), label: this.$t('General'),
visible: this.hasSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callSettings) visible: this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callSettings)
}, },
{ {
to: '/user/voicebox', to: '/user/voicebox',
@ -101,25 +121,25 @@ export default {
to: '/user/call-forwarding', to: '/user/call-forwarding',
icon: 'phone_forwarded', icon: 'phone_forwarded',
label: this.$t('Forwarding'), label: this.$t('Forwarding'),
visible: true visible: this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callForwarding)
}, },
{ {
to: '/user/call-blocking/incoming', to: '/user/call-blocking/incoming',
icon: 'call_received', icon: 'call_received',
label: this.$t('Block Incoming'), label: this.$t('Block Incoming'),
visible: this.hasSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callBlockingIncoming) visible: this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callBlockingIncoming)
}, },
{ {
to: '/user/call-blocking/outgoing', to: '/user/call-blocking/outgoing',
icon: 'call_made', icon: 'call_made',
label: this.$t('Block Outgoing'), label: this.$t('Block Outgoing'),
visible: this.hasSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callBlockingOutgoing) visible: this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callBlockingOutgoing)
}, },
{ {
to: '/user/call-blocking/privacy', to: '/user/call-blocking/privacy',
icon: 'fas fa-user-secret', icon: 'fas fa-user-secret',
label: this.$t('Privacy'), label: this.$t('Privacy'),
visible: this.hasSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callBlockingPrivacy) visible: this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.callBlockingPrivacy)
}, },
{ {
to: '/user/speeddial', to: '/user/speeddial',
@ -137,7 +157,8 @@ export default {
to: '/user/recordings', to: '/user/recordings',
icon: 'play_circle', icon: 'play_circle',
label: this.$t('Recordings'), 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', to: '/user/fax-settings',
icon: 'fas fa-fax', icon: 'fas fa-fax',
label: this.$t('Fax Settings'), label: this.$t('Fax Settings'),
visible: this.hasFaxCapability && visible: this.isFaxFeatureEnabled
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.faxServer) &&
this.isLicenseActive(LICENSES.fax)
}, },
{ {
icon: 'fas fa-chart-line', icon: 'fas fa-chart-line',
label: this.$t('PBX Statistics'), label: this.$t('PBX Statistics'),
visible: this.isPbxAdmin && this.isLicenseActive(LICENSES.pbx), visible: this.isPbxAdmin,
opened: this.isPbxConfiguration, opened: this.isPbxConfiguration,
children: [ children: [
{ {
to: '/user/pbx-statistics/cdr', to: '/user/pbx-statistics/cdr',
icon: 'fas fa-table', icon: 'fas fa-table',
label: this.$t('Cdr'), label: this.$t('Cdr'),
visible: true visible: this.isPbxAdmin
} }
] ]
}, },
{ {
icon: 'miscellaneous_services', icon: 'miscellaneous_services',
label: this.$t('PBX Configuration'), label: this.$t('PBX Configuration'),
visible: this.isPbxAdmin && this.isLicenseActive(LICENSES.pbx), visible: this.isPbxAdmin,
opened: this.isPbxConfiguration, opened: this.isPbxConfiguration,
children: [ children: [
{ {
to: '/user/pbx-configuration/seats', to: '/user/pbx-configuration/seats',
icon: 'person', icon: 'person',
label: this.$t('Seats'), label: this.$t('Seats'),
visible: true visible: this.isPbxAdmin
}, },
{ {
to: '/user/pbx-configuration/groups', to: '/user/pbx-configuration/groups',
icon: 'group', icon: 'group',
label: this.$t('Groups'), 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', to: '/user/pbx-configuration/devices',
icon: 'fas fa-fax', icon: 'fas fa-fax',
label: this.$t('Devices'), 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', to: '/user/pbx-configuration/call-queues',
icon: 'filter_none', icon: 'filter_none',
label: this.$t('Call Queues'), 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', to: '/user/pbx-configuration/sound-sets',
icon: 'queue_music', icon: 'queue_music',
label: this.$t('Sound Sets'), 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', to: '/user/pbx-configuration/ms-configs',
icon: 'arrow_forward', icon: 'arrow_forward',
label: this.$t('Manager Secretary'), 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', to: '/user/pbx-configuration/auto-attendant',
icon: 'dialpad', icon: 'dialpad',
label: this.$t('Auto Attendant'), 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', to: '/user/pbx-configuration/customer-phonebook',
icon: 'person', icon: 'person',
label: this.$t('Customer Phonebook'), label: this.$t('Customer Phonebook'),
visible: this.isLicenseActive(LICENSES.phonebook) visible: this.isPbxAdmin && this.hasLicenses([LICENSES.phonebook])
}, },
{ {
to: '/user/pbx-configuration/customer-preferences', to: '/user/pbx-configuration/customer-preferences',
icon: 'fas fa-user-cog', icon: 'fas fa-user-cog',
label: this.$t('Customer Preferences'), label: this.$t('Customer Preferences'),
visible: true visible: this.isPbxAdmin && this.hasLicenses([LICENSES.phonebook]) && hasCustomerPreferenceSubmenus
} }
] ]
}, },
@ -230,26 +251,25 @@ export default {
icon: 'settings', icon: 'settings',
label: this.$t('Extension Settings'), label: this.$t('Extension Settings'),
visible: this.isPbxEnabled && visible: this.isPbxEnabled &&
this.hasSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.pbxSettings) && this.hasSomeSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.pbxSettings),
this.isLicenseActive(LICENSES.pbx),
children: [ children: [
{ {
to: '/user/extension-settings/call-queues', to: '/user/extension-settings/call-queues',
icon: 'filter_none', icon: 'filter_none',
label: this.$t('Call Queues'), 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', to: '/user/extension-settings/ms-configs',
icon: 'arrow_forward', icon: 'arrow_forward',
label: this.$t('Manager Secretary'), 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', to: '/user/extension-settings/auto-attendant',
icon: 'dialpad', icon: 'dialpad',
label: this.$t('Auto Attendant'), 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" :model-value="value"
emit-value emit-value
map-options map-options
:disable="loading" :disable="disabled || loading"
:readonly="loading" :readonly="disabled || loading"
:label="$t('Language for voicemail and app server')" :label="$t('Language for voicemail and app server')"
data-cy="voicebox-change-language" data-cy="voicebox-change-language"
:title="$t('Voice prompts language for voicemail, conference and application server')" :title="$t('Voice prompts language for voicemail, conference and application server')"
@ -54,6 +54,10 @@ export default {
loading: { loading: {
type: Boolean, type: Boolean,
default: false default: false
},
disabled: {
type: Boolean,
default: false
} }
}, },
emits: ['input'] emits: ['input']

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

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

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

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

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

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

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

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

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

@ -150,7 +150,8 @@
<script> <script>
import _ from 'lodash' import _ from 'lodash'
import { mapGetters } from 'vuex' import { PROFILE_ATTRIBUTE_MAP } from 'src/constants'
import { mapGetters, mapState } from 'vuex'
export default { export default {
name: 'CscCdrFilters', name: 'CscCdrFilters',
@ -175,6 +176,14 @@ export default {
...mapGetters([ ...mapGetters([
'getCurrentFormattedDateWithDash' 'getCurrentFormattedDateWithDash'
]), ]),
...mapGetters('user', [
'isFaxFeatureEnabled',
'hasSubscriberProfileAttribute',
'isSmsEnabled'
]),
...mapState('user', [
'platformInfo'
]),
filterType () { filterType () {
return this.filterTypeModel && this.filterTypeModel.value return this.filterTypeModel && this.filterTypeModel.value
}, },
@ -212,18 +221,18 @@ export default {
label: this.$t('Call'), label: this.$t('Call'),
value: 'call' value: 'call'
}, },
{ ...(this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.voiceMail) ? [{
label: this.$t('Voicemail'), label: this.$t('Voicemail'),
value: 'voicemail' value: 'voicemail'
}, }] : []),
{ ...(this.isSmsEnabled ? [{
label: this.$t('Sms'), label: this.$t('Sms'),
value: 'sms' value: 'sms'
}, }] : []),
{ ...(this.isFaxFeatureEnabled ? [{
label: this.$t('Fax'), label: this.$t('Fax'),
value: 'fax' value: 'fax'
} }] : [])
] ]
}, },
filtersList () { 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 INTERNAL_DATE_FORMAT_DASH_HOUR = 'YYYY-MM-DD HH:mm'
export const PROFILE_ATTRIBUTE_MAP = { 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', reminder: 'reminder',
speedDial: 'speed_dial', speedDial: 'speed_dial',
voiceMail: 'voice_mail', voiceMail: 'voice_mail',
clir: 'clir', clir: 'clir',
clir_intrapbx: 'clir_intrapbx', clir_intrapbx: 'clir_intrapbx',
cstaClient: 'csta_client',
cstaController: 'csta_controller',
faxServer: 'fax_server', faxServer: 'fax_server',
cscCalls: 'csc_calls', cscCalls: 'csc_calls',
manager_secretary: 'manager_secretary', managerSecretary: 'manager_secretary',
auto_attendant: 'auto_attendant', secretaryNumbers: 'secretary_numbers',
autoAttendant: 'auto_attendant',
soundSet: 'contract_sound_set', soundSet: 'contract_sound_set',
deviceProvisioning: 'csc_device_provisioning', deviceProvisioning: 'csc_device_provisioning',
conversations: 'csc_conversations', conversations: 'csc_conversations',
registeredDevices: 'csc_registered_devices', registeredDevices: 'csc_registered_devices',
recordings: 'record_call', 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 = { export const PROFILE_ATTRIBUTES_MAP = {
@ -30,7 +49,8 @@ export const PROFILE_ATTRIBUTES_MAP = {
callBlockingPrivacy: ['clir', 'clir_intrapbx'], callBlockingPrivacy: ['clir', 'clir_intrapbx'],
callSettings: ['music_on_hold', 'language'], callSettings: ['music_on_hold', 'language'],
pbxSettings: ['auto_attendant', 'cloud_pbx_callqueue', 'max_queue_length', 'queue_wrap_up_time', 'manager_secretary'], 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 = { export const LICENSES = {
@ -41,3 +61,11 @@ export const LICENSES = {
phonebook: 'phonebook', phonebook: 'phonebook',
call_recording: 'call_recording' 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()" @click="$refs.mainMenu.show()"
/> />
<q-btn <q-btn
v-if="hasFaxCapabilityAndFaxActive && hasSendFaxFeature" v-if="isFaxFeatureEnabled && isFaxServerSettingsActive"
class="q-mr-sm" class="q-mr-sm"
flat flat
dense dense
@ -330,17 +330,11 @@ export default {
'remoteOnHold' 'remoteOnHold'
]), ]),
...mapGetters('user', [ ...mapGetters('user', [
'isLogged',
'hasUser',
'getUsername', 'getUsername',
'isPbxAdmin', 'isPbxAdmin',
'hasSmsCapability', 'isFaxFeatureEnabled',
'hasFaxCapabilityAndFaxActive', 'isFaxServerSettingsActive',
'hasSendSmsFeature',
'hasSendFaxFeature',
'userDataRequesting',
'userDataSucceeded', 'userDataSucceeded',
'isLogoRequesting',
'isLogoRequested', 'isLogoRequested',
'hasSubscriberProfileAttribute' 'hasSubscriberProfileAttribute'
]), ]),

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

@ -4,7 +4,7 @@
class="q-pa-lg" class="q-pa-lg"
> >
<template <template
v-if="hasSubscriberProfileAttributes(['cfu', 'cfna', 'cfb'])" v-if="hasSomeSubscriberProfileAttributes(['cfu', 'cfna', 'cfb'])"
#header #header
> >
<q-btn <q-btn
@ -127,7 +127,7 @@ export default {
]), ]),
...mapGetters('user', [ ...mapGetters('user', [
'hasSubscriberProfileAttribute', 'hasSubscriberProfileAttribute',
'hasSubscriberProfileAttributes' 'hasSomeSubscriberProfileAttributes'
]), ]),
isRingTimeoutVisible () { isRingTimeoutVisible () {
return this.ringTimeout && this.groups.some((group) => group.type === 'cft') 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 CscConversationItem from 'components/pages/Conversations/CscConversationItem'
import CscConversationsCallsFilter from 'components/pages/Conversations/CscConversationsCallsFilter' import CscConversationsCallsFilter from 'components/pages/Conversations/CscConversationsCallsFilter'
import CscConversationsFilter from 'components/pages/Conversations/CscConversationsFilter' 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 { showGlobalError } from 'src/helpers/ui'
import platformMixin from 'src/mixins/platform' import platformMixin from 'src/mixins/platform'
import { RequestState } from 'src/store/common' import { RequestState } from 'src/store/common'
@ -151,7 +151,8 @@ export default {
}, },
computed: { computed: {
...mapGetters('user', [ ...mapGetters('user', [
'isLicenseActive' 'isFaxFeatureEnabled',
'hasSubscriberProfileAttribute'
]), ]),
...mapState('conversations', [ ...mapState('conversations', [
'reachedLastPage', 'reachedLastPage',
@ -177,7 +178,7 @@ export default {
'isCallEnabled' 'isCallEnabled'
]), ]),
tabs () { tabs () {
return [ const tabs = [
{ {
label: this.$t('All'), label: this.$t('All'),
value: 'call-fax-voicemail', value: 'call-fax-voicemail',
@ -188,19 +189,23 @@ export default {
value: 'call', value: 'call',
icon: 'call' icon: 'call'
}, },
{ ...(this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.voiceMail) ? [{
label: this.$t('Voicemails'), label: this.$t('Voicemails'),
value: 'voicemail', value: 'voicemail',
icon: 'voicemail' icon: 'voicemail'
}, }] : []),
this.isLicenseActive(LICENSES.fax) ...(this.isFaxFeatureEnabled ? [{
? { label: this.$t('Faxes'),
label: this.$t('Faxes'), value: 'fax',
value: 'fax', icon: 'description'
icon: 'description' }] : [])
} ]
: null
].filter((label) => label !== null) if (tabs.length === 2) {
return tabs.filter((tab) => tab.value !== 'call-fax-voicemail')
}
return tabs
}, },
pageStyle () { pageStyle () {
return { return {

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

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

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

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

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

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

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

@ -44,7 +44,12 @@ import CscPageSubscriberPhonebookDetails from 'pages/CscPageSubscriberPhonebookD
import CscPageUserSettings from 'pages/CscPageUserSettings' import CscPageUserSettings from 'pages/CscPageUserSettings'
import CscPageVoicebox from 'pages/CscPageVoicebox' import CscPageVoicebox from 'pages/CscPageVoicebox'
import CscRecoverPassword from 'pages/CscRecoverPassword' 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' export const PATH_CHANGE_PASSWORD = '/changepassword'
@ -75,7 +80,8 @@ const routes = [
get title () { get title () {
return i18n.global.t('Start new call') 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 () { get title () {
return i18n.global.t('Subscriber Phonebook') return i18n.global.t('Subscriber Phonebook')
}, },
license: LICENSES.phonebook licenses: [LICENSES.phonebook]
} }
}, },
{ {
@ -110,7 +116,8 @@ const routes = [
meta: { meta: {
get title () { get title () {
return i18n.global.t('Add Phonebook') return i18n.global.t('Add Phonebook')
} },
licenses: [LICENSES.phonebook]
} }
}, },
{ {
@ -119,7 +126,8 @@ const routes = [
meta: { meta: {
get title () { get title () {
return i18n.global.t('Subscriber Phonebook') return i18n.global.t('Subscriber Phonebook')
} },
licenses: [LICENSES.phonebook]
} }
}, },
{ {
@ -128,7 +136,11 @@ const routes = [
meta: { meta: {
get title () { get title () {
return i18n.global.t('Seats') return i18n.global.t('Seats')
} },
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
} }
}, },
{ {
@ -137,7 +149,8 @@ const routes = [
meta: { meta: {
get title () { get title () {
return i18n.global.t('Call Forwarding') return i18n.global.t('Call Forwarding')
} },
profileAttributes: PROFILE_ATTRIBUTES_MAP.callForwarding
} }
}, },
{ {
@ -189,7 +202,9 @@ const routes = [
get subtitle () { get subtitle () {
return i18n.global.t('Call recordings') 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 () { get subtitle () {
return i18n.global.t('CDR') return i18n.global.t('CDR')
} },
}, adminOnly: true,
license: LICENSES.pbx platformFeature: FEATURES.cloudPbx,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
}
}, },
{ {
path: 'pbx-configuration/groups', path: 'pbx-configuration/groups',
@ -241,7 +259,11 @@ const routes = [
get subtitle () { get subtitle () {
return i18n.global.t('Groups') 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 () { get subtitle () {
return i18n.global.t('Groups') 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 () { get subtitle () {
return i18n.global.t('Seats') return i18n.global.t('Seats')
} },
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
} }
}, },
{ {
@ -277,7 +308,11 @@ const routes = [
}, },
get subtitle () { get subtitle () {
return i18n.global.t('Customer Phonebook') 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: { meta: {
get title () { get title () {
return i18n.global.t('Add Phonebook') 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: { meta: {
get title () { get title () {
return i18n.global.t('Upload CSV') 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: { meta: {
get title () { get title () {
return i18n.global.t('Customer Phonebook') 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 () { get subtitle () {
return i18n.global.t('Devices') 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 () { get subtitle () {
return i18n.global.t('Seats') return i18n.global.t('Seats')
} },
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx]
} }
}, },
{ {
@ -341,7 +397,11 @@ const routes = [
get subtitle () { get subtitle () {
return i18n.global.t('Devices') 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 () { get subtitle () {
return i18n.global.t('Call Queues') 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 () { get subtitle () {
return i18n.global.t('Sound Sets') 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 () { get subtitle () {
return i18n.global.t('Sound Sets') 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 () { get subtitle () {
return i18n.global.t('Manager Secretary') 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 () { get subtitle () {
return i18n.global.t('Auto Attendant') 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 () { get subtitle () {
return i18n.global.t('Customer Preferences') return i18n.global.t('Customer Preferences')
} },
adminOnly: true,
platformFeature: FEATURES.cloudPbx,
capability: FEATURES.cloudPbx,
licenses: [LICENSES.pbx, LICENSES.phonebook]
} }
}, },
{ {
path: 'voicebox', path: 'voicebox',
@ -443,8 +529,10 @@ const routes = [
get subtitle () { get subtitle () {
return i18n.global.t('Set your fax settings') return i18n.global.t('Set your fax settings')
}, },
platformFeature: FEATURES.faxServer,
profileAttribute: PROFILE_ATTRIBUTE_MAP.faxServer, profileAttribute: PROFILE_ATTRIBUTE_MAP.faxServer,
license: LICENSES.fax capability: FEATURES.faxServer,
licenses: [LICENSES.fax]
} }
}, },
{ {
@ -482,7 +570,10 @@ const routes = [
get subtitle () { get subtitle () {
return i18n.global.t('Call Queues') 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 () { get subtitle () {
return i18n.global.t('Manager Secretary') 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 () { get subtitle () {
return i18n.global.t('Auto Attendant') 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, setFaxServerField,
setMailToFaxSettingField setMailToFaxSettingField
} from 'src/api/fax' } from 'src/api/fax'
import { getSubscriberId } from 'src/auth'
export default { export default {
namespaced: true, namespaced: true,
@ -40,6 +41,10 @@ export default {
context.commit('settingsSucceeded', { context.commit('settingsSucceeded', {
faxServerSettings faxServerSettings
}) })
if (toString(subscriberId) === toString(getSubscriberId())) {
context.commit('user/updateIsFaxServerSettingsActive', faxServerSettings.active, { root: true })
}
}, },
async faxServerSettingsUpdateAction (context, options) { async faxServerSettingsUpdateAction (context, options) {
const subscriberId = options.id || context.getters.subscriberId const subscriberId = options.id || context.getters.subscriberId
@ -51,8 +56,9 @@ export default {
context.commit('settingsSucceeded', { context.commit('settingsSucceeded', {
faxServerSettings 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) { subscribersSucceeded (state, subscribers) {
state.subscriberList = _.get(subscribers, 'items', []) state.subscriberList = _.get(subscribers, 'items', [])
state.subscriberListState = RequestState.succeeded
state.subscriberMap = {} state.subscriberMap = {}
state.subscriberList.forEach((subscriber) => { state.subscriberList.forEach((subscriber) => {
state.subscriberMap[subscriber.id] = subscriber state.subscriberMap[subscriber.id] = subscriber

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

Loading…
Cancel
Save