MT#64432 Refactor Subscriber Phonebook

Our nightly tests flagged the fact that
the feature was unstable and unrealiable.
Changes:
* Extract subscriber phonebook api and state
* Change direct use of http in the sub-phonebook
  method to use the relevant method in common.js
* Simplify logic to PATCH single properties
  with a unique PUT in the Phonebook entry form
* Amend getList, handleResponseError and
  put to allow use with API v2 endpoints
* Amend translations and methods to replace
  "phonebook" with "phonebook entry" where
  necessary

Change-Id: I189d45fe426a1ded400a251d7efdfa72f76f9061
(cherry picked from commit ff40864da0)
mr12.5
Debora Crescenzo 4 months ago
parent 3433a194b9
commit 8eb8cfb10d

@ -7,7 +7,7 @@ import { getJwt, hasJwt } from 'src/auth'
import { getCurrentLangAsV1Format } from 'src/i18n' import { getCurrentLangAsV1Format } from 'src/i18n'
export const LIST_DEFAULT_PAGE = 1 export const LIST_DEFAULT_PAGE = 1
export const LIST_DEFAULT_ROWS = 24 export const LIST_DEFAULT_ROWS = 20
export const LIST_ALL_ROWS = 1000 export const LIST_ALL_ROWS = 1000
export const API_REQUEST_DEFAULT_TIMEOUT = 30000 export const API_REQUEST_DEFAULT_TIMEOUT = 30000
@ -101,6 +101,7 @@ export async function getList (options) {
}, options) }, options)
if (requestConfig.all === true) { if (requestConfig.all === true) {
requestConfig.params.rows = LIST_ALL_ROWS requestConfig.params.rows = LIST_ALL_ROWS
requestConfig.params.page = LIST_DEFAULT_PAGE
} }
if (requestConfig.resource !== undefined) { if (requestConfig.resource !== undefined) {
requestConfig.path = `api/${requestConfig.resource}/` requestConfig.path = `api/${requestConfig.resource}/`
@ -135,7 +136,12 @@ export async function getList (options) {
if (lastPage === 0) { if (lastPage === 0) {
lastPage = null lastPage = null
} }
let items = _.get(body, requestConfig.root, [])
let items = requestConfig.root
// This gets the results for the API V1 which has the list in the root of the response, and if not found it tries to get it from the API V2 response format
? _.get(body, requestConfig.root, [])
// This gets the results for the API V2 which has the list in the data field, and if not found it tries to get it from the root of the response (for backward compatibility with API V1)
: _.get(body, 'data', [])
if (!Array.isArray(items)) { if (!Array.isArray(items)) {
items = [items] items = [items]
} }
@ -149,12 +155,40 @@ export async function getList (options) {
} }
} }
function extractMessages (messageArray) {
const messages = []
if (Array.isArray(messageArray)) {
messageArray.forEach((item) => {
Object.keys(item).forEach((fieldName) => {
const fieldErrors = item[fieldName]
if (Array.isArray(fieldErrors)) {
fieldErrors.forEach((errorObject) => {
Object.values(errorObject).forEach((errorMsg) => {
messages.push(errorMsg)
})
})
}
})
})
}
return messages.join(', ')
}
function handleResponseError (err) { function handleResponseError (err) {
const code = _.get(err, 'response.data.code', null) let code = _.get(err, 'response.data.code', null)
let message = _.get(err, 'response.data.message', null) let message = _.get(err, 'response.data.message', null)
if (code === 403 && message === 'Invalid license') { if (code === 403 && message === 'Invalid license') {
message = i18n.global.t('Contact your administrator to activate this functionality') message = i18n.global.t('Contact your administrator to activate this functionality')
} }
// API V2 returns an array of messages rather than a string
// and the code is available in the response status
if (Array.isArray(message)) {
message = extractMessages(message)
code = _.get(err, 'response.status', null)
}
if (code !== null && message !== null) { if (code !== null && message !== null) {
throw new ApiResponseError(code, message) throw new ApiResponseError(code, message)
} }
@ -287,7 +321,8 @@ export async function put (options) {
path = `api/${requestConfig.resource}/${requestConfig.resourceId}` path = `api/${requestConfig.resource}/${requestConfig.resourceId}`
} }
try { try {
const res = await httpApi.put(path, requestConfig.body, { const payload = requestConfig.body || requestConfig.data
const res = await httpApi.put(path, payload, {
headers: requestConfig.headers headers: requestConfig.headers
}) })
if (requestConfig.headers.Prefer === Prefer.representation) { if (requestConfig.headers.Prefer === Prefer.representation) {

@ -0,0 +1,65 @@
import {
del,
get,
getList,
patchReplace,
post,
putMinimal
} from 'src/api/common'
export async function createPhonebook (data) {
const payload = {
name: data.name,
number: data.number,
shared: data.shared,
subscriber_id: Number(data.subscriber_id)
}
return post({
resource: 'subscriberphonebookentries',
body: payload
})
}
export async function deleteEntry (id) {
return del({
resource: 'subscriberphonebookentries',
resourceId: id
})
}
export async function getEntryById (id) {
return get({
resource: 'subscriberphonebookentries',
resourceId: id
})
}
export async function getPhonebook (options) {
return getList({
resource: 'subscriberphonebookentries',
params: options,
...(options.rows === 0 ? { all: true } : {})
})
}
export function setSharedValue (id, value) {
return patchReplace({
resource: 'subscriberphonebookentries',
resourceId: id,
fieldPath: 'shared',
value
})
}
export async function updateEntry (data) {
return putMinimal({
resource: 'subscriberphonebookentries',
resourceId: data.id,
body: {
number: data.number,
shared: data.shared,
name: data.name,
subscriber_id: Number(data.subscriberId)
}
})
}

@ -57,19 +57,7 @@ export async function setPreference (id, field, value) {
} }
} }
} }
export async function setPreferencePhonebook (id, field, value) {
if (value === undefined || value === null || value === '' || (Array.isArray(value) && !value.length)) {
await removePreferencePhonebook(id, field)
} else {
try {
await replacePreferencePhonebook(id, field, value)
} catch (err) {
if (err) {
throw err
}
}
}
}
export async function setPreferencePhonebookCustomer (id, field, value) { export async function setPreferencePhonebookCustomer (id, field, value) {
if (value === undefined || value === null || value === '' || (Array.isArray(value) && !value.length)) { if (value === undefined || value === null || value === '' || (Array.isArray(value) && !value.length)) {
await removePreferencePhonebookCustomer(id, field) await removePreferencePhonebookCustomer(id, field)
@ -83,6 +71,7 @@ export async function setPreferencePhonebookCustomer (id, field, value) {
} }
} }
} }
export function getNcosLevels (options) { export function getNcosLevels (options) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const mergedOptions = _.merge(options || {}, { const mergedOptions = _.merge(options || {}, {
@ -115,12 +104,7 @@ export async function removePreference (id, field) {
fieldPath: field fieldPath: field
}) })
} }
export async function removePreferencePhonebook (id, field) {
return await patchRemove({
path: `api/subscriberphonebookentries/${id}`,
fieldPath: field
})
}
export async function removePreferencePhonebookCustomer (id, field) { export async function removePreferencePhonebookCustomer (id, field) {
return await patchRemove({ return await patchRemove({
path: `api/customerphonebookentries/${id}`, path: `api/customerphonebookentries/${id}`,
@ -167,19 +151,7 @@ export function replacePreference (id, field, value) {
}) })
}) })
} }
export function replacePreferencePhonebook (id, field, value) {
return new Promise((resolve, reject) => {
patchReplace({
path: `api/subscriberphonebookentries/${id}`,
fieldPath: field,
value
}).then(() => {
resolve()
}).catch((err) => {
reject(err)
})
})
}
export function replacePreferencePhonebookCustomer (id, field, value) { export function replacePreferencePhonebookCustomer (id, field, value) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
patchReplace({ patchReplace({
@ -763,24 +735,7 @@ export async function getSubscriberRegistrations (options) {
}) })
return list return list
} }
export async function getSubscriberPhonebook (options) {
let all = false
if (options.rows === 0) {
delete options.rows
delete options.page
all = true
}
if (!options.order_by) {
delete options.order_by
delete options.order_by_direction
}
const list = await getList({
resource: 'subscriberphonebookentries',
all,
params: options
})
return list
}
export async function getCustomerPhonebook (options) { export async function getCustomerPhonebook (options) {
let all = false let all = false
if (options.rows === 0) { if (options.rows === 0) {
@ -799,14 +754,7 @@ export async function getCustomerPhonebook (options) {
}) })
return list return list
} }
export async function createPhonebook (data) {
const payLoad = {
name: data.name,
number: data.number,
shared: data.shared
}
return await httpApi.post('api/subscriberphonebookentries/', payLoad)
}
export async function createCustomerPhonebook (data) { export async function createCustomerPhonebook (data) {
const payLoad = { const payLoad = {
name: data.name, name: data.name,
@ -827,18 +775,9 @@ export async function uploadCsv (context, formData) {
config config
}) })
} }
export function setValueShared (id, value) {
return setPreferencePhonebook(id, 'shared', value)
}
export function setValueName (id, value) {
return setPreferencePhonebook(id, 'name', value)
}
export function setValueNameCustomer (id, value) { export function setValueNameCustomer (id, value) {
return setPreferencePhonebookCustomer(id, 'name', value) return setPreferencePhonebookCustomer(id, 'name', value)
} }
export function setValueNumber (id, value) {
return setPreferencePhonebook(id, 'number', value)
}
export function setValueNumberCustomer (id, value) { export function setValueNumberCustomer (id, value) {
return setPreferencePhonebookCustomer(id, 'number', value) return setPreferencePhonebookCustomer(id, 'number', value)
} }

@ -156,7 +156,7 @@
"Delete recording": "Aufnahme löschen", "Delete recording": "Aufnahme löschen",
"Delete registered device": "Registriertes Gerät löschen", "Delete registered device": "Registriertes Gerät löschen",
"Delete slot?": "Eintrag löschen?", "Delete slot?": "Eintrag löschen?",
"Delete subscriber phonebook": "Telefonbuch des Teilnehmers löschen", "Delete subscriber phonebook entry": "Telefonbucheintrag des Teilnehmers löschen",
"Delete voicemail after email notification is delivered": "Voicemail nach dem Senden der E-Mail-Benachrichtigung löschen", "Delete voicemail after email notification is delivered": "Voicemail nach dem Senden der E-Mail-Benachrichtigung löschen",
"Deliver Incoming Faxes": "Eingehende Faxe zustellen", "Deliver Incoming Faxes": "Eingehende Faxe zustellen",
"Deliver Outgoing Faxes": "Ausgehende Faxe zustellen", "Deliver Outgoing Faxes": "Ausgehende Faxe zustellen",
@ -589,31 +589,31 @@
"Wrong username or password": "Falscher Benutzername oder Passwort", "Wrong username or password": "Falscher Benutzername oder Passwort",
"Yes": "Ja", "Yes": "Ja",
"Yesterday": "Gestern", "Yesterday": "Gestern",
"You are about to change your login password. After the password was changed successfully, you get automatically logged out to authenticate with the new password. ": "Anmeldepasswort ändern? Nachdem das Passwort erfolgreich geändert wurde, werden Sie automatisch abgemeldet, um sich mit dem neuen Passwort zu authentifizieren. ", "You are about to change your login password. After the password was changed successfully, you get automatically logged out to authenticate with the new password.": "Sie sind dabei, Ihr Anmeldepasswort zu ändern. Nach erfolgreicher Änderung werden Sie automatisch abgemeldet, um sich mit dem neuen Passwort erneut anzumelden.",
"You are about to delete recording #{id}": "Die Aufzeichnung #{id} löschen?", "You are about to delete recording #{id}": "Sie sind dabei, die Aufzeichnung #{id} zu löschen.",
"You are about to delete slot {slot}": "Den Eintrag {slot} löschen?", "You are about to delete slot {slot}": "Sie sind dabei, den Eintrag {slot} zu löschen.",
"You are about to delete this destination": "Das Ziel löschen?", "You are about to delete this destination": "Sie sind dabei, dieses Ziel zu löschen.",
"You are about to delete this forwarding": "Die Weiterleitung löschen?", "You are about to delete this forwarding": "Sie sind dabei, diese Weiterleitung zu löschen.",
"You are about to delete this phonebook": "Das Telefonbuch löschen?", "You are about to delete this phonebook entry": "Sie sind dabei, diesen Telefonbucheintrag zu löschen.",
"You are about to delete this registered device": "Das registrierte Gerät löschen?", "You are about to delete this registered device": "Sie sind dabei, dieses registrierte Gerät zu löschen.",
"You are about to delete time range \"{from} - {to}\"": "Den Zeitraum „{from} - {to}“ entfernen?", "You are about to delete time range \"{from} - {to}\"": "Sie sind dabei, den Zeitraum „{from} - {to}“ zu entfernen.",
"You are about to remove ACL: From email <{from_email}>": "Sie sind im Begriff, den folgenden ACL-Eintrag zu löschen: Absender-E-Mail-Adresse <{from_email}>", "You are about to remove ACL: From email <{from_email}>": "Sie sind dabei, die ACL-Regel für die E-Mail-Adresse <{from_email}> zu entfernen.",
"You are about to remove call queue for {subscriber}": "Anrufwarteschlange für {subscriber} löschen?", "You are about to remove call queue for {subscriber}": "Sie sind dabei, die Anrufwarteschlange für {subscriber} zu entfernen.",
"You are about to remove config for {msConfig}": "Konfiguration für {msConfig} löschen?", "You are about to remove config for {msConfig}": "Sie sind dabei, die Konfiguration für {msConfig} zu entfernen.",
"You are about to remove destination {destination}": "Das Ziel {destination} entfernen?", "You are about to remove destination {destination}": "Sie sind dabei, das Ziel {destination} zu entfernen.",
"You are about to remove device {device}": "Gerät {device} löschen?", "You are about to remove device {device}": "Sie sind dabei, das Gerät {device} zu entfernen.",
"You are about to remove group {group}": "Gruppe {group} löschen?", "You are about to remove group {group}": "Sie sind dabei, die Gruppe {group} zu entfernen.",
"You are about to remove seat {seat}": "Nebenstelle {seat} löschen?", "You are about to remove seat {seat}": "Sie sind dabei, die Nebenstelle {seat} zu entfernen.",
"You are about to remove secret key renew notify email: {email}": "Sie sind im Begriff, die E-Mail-Adresse zur Benachrichtigung bei Erneuerung des Geheimschlüssels zu entfernen: {email}", "You are about to remove secret key renew notify email: {email}": "Sie sind dabei, die Benachrichtigungs-E-Mail zur Erneuerung des Geheimschlüssels zu entfernen: {email}",
"You are about to remove sound set {soundSetName}": "Sound-Set {soundSetName} löschen?", "You are about to remove sound set {soundSetName}": "Sie sind dabei, das Sound-Set {soundSetName} zu löschen.",
"You are about to remove the number {number}": "Nummer {number} entfernen?", "You are about to remove the number {number}": "Sie sind dabei, die Nummer {number} zu entfernen.",
"You are about to remove the speed dial {slot}": "Kurzwahl {slot} löschen?", "You are about to remove the speed dial {slot}": "Sie sind dabei, die Kurzwahl {slot} zu löschen.",
"You are about to remove this Fax": "Das Fax löschen?", "You are about to remove this Fax": "Sie sind dabei, dieses Fax zu löschen.",
"You are about to remove this Voicemail": "Diese Sprachnachricht entfernen?", "You are about to remove this Voicemail": "Sie sind dabei, diese Sprachnachricht zu löschen.",
"You are about to reset the custom {type} greeting sound to defaults": "Benutzerdefinierten Begrüßungs-Sound {type} auf Standard-Sound zurücksetzen?", "You are about to reset the custom {type} greeting sound to defaults": "Sie sind dabei, den benutzerdefinierten Begrüßungston {type} auf die Standardeinstellung zurückzusetzen.",
"You are now able to start and receive calls": "Sie können nun Anrufe starten und empfangen", "You are now able to start and receive calls": "Sie können jetzt Anrufe tätigen und empfangen.",
"You can neither make a call nor receive one, since the RTC:engine is not active. If you operate a C5 CE then first upgrade to a C5 PRO to be able to use the RTC:engine.": "Sie können weder einen Anruf tätigen noch einen empfangen, da die RTC:engine nicht aktiv ist. Wenn Sie eine C5 CE betreiben, aktualisieren Sie zuerst auf eine C5 PRO, um die RTC:engine nutzen zu können.", "You can neither make a call nor receive one, since the RTC:engine is not active. If you operate a C5 CE then first upgrade to a C5 PRO to be able to use the RTC:engine.": "Sie können weder Anrufe tätigen noch empfangen, da die RTC:engine nicht aktiv ist. Wenn Sie ein C5 CE verwenden, aktualisieren Sie bitte zunächst auf ein C5 PRO, um die RTC:engine nutzen zu können.",
"You can not join a conference, since the RTC:engine is not active. If you operate a C5 CE then first upgrade to a C5 PRO to be able to use the RTC:engine.": "Sie können keiner Konferenz beitreten, da die RTC:engine nicht aktiv ist. Wenn Sie derzeit ein C5 CE betreiben, schaffen Sie sich zuerst ein C5 PRO an, um die RTC:engine nutzen zu können.", "You can not join a conference, since the RTC:engine is not active. If you operate a C5 CE then first upgrade to a C5 PRO to be able to use the RTC:engine.": "Sie können keiner Konferenz beitreten, da die RTC:engine nicht aktiv ist. Wenn Sie ein C5 CE verwenden, aktualisieren Sie bitte zunächst auf ein C5 PRO, um die RTC:engine nutzen zu können.",
"You have blocked incoming call notifications.": "Sie haben Benachrichtigungen über eingehende Anrufe blockiert.", "You have blocked incoming call notifications.": "Sie haben Benachrichtigungen über eingehende Anrufe blockiert.",
"You have invalid form input. Please check and try again.": "Sie haben eine ungültige Formulareingabe. Bitte überprüfen Sie diese und versuchen Sie es erneut.", "You have invalid form input. Please check and try again.": "Sie haben eine ungültige Formulareingabe. Bitte überprüfen Sie diese und versuchen Sie es erneut.",
"Your SIP password has been changed successfully": "Ihr SIP-Passwort wurde erfolgreich geändert", "Your SIP password has been changed successfully": "Ihr SIP-Passwort wurde erfolgreich geändert",

@ -153,7 +153,7 @@
"Delete recording": "Delete recording", "Delete recording": "Delete recording",
"Delete registered device": "Delete registered device", "Delete registered device": "Delete registered device",
"Delete slot?": "Delete slot?", "Delete slot?": "Delete slot?",
"Delete subscriber phonebook": "Delete subscriber phonebook", "Delete subscriber phonebook entry": "Delete subscriber phonebook entry",
"Delete voicemail after email notification is delivered": "Delete voicemail after email notification is delivered", "Delete voicemail after email notification is delivered": "Delete voicemail after email notification is delivered",
"Deliver Incoming Faxes": "Deliver Incoming Faxes", "Deliver Incoming Faxes": "Deliver Incoming Faxes",
"Deliver Outgoing Faxes": "Deliver Outgoing Faxes", "Deliver Outgoing Faxes": "Deliver Outgoing Faxes",
@ -577,7 +577,7 @@
"You are about to delete slot {slot}": "You are about to delete slot {slot}", "You are about to delete slot {slot}": "You are about to delete slot {slot}",
"You are about to delete this destination": "You are about to delete this destination", "You are about to delete this destination": "You are about to delete this destination",
"You are about to delete this forwarding": "You are about to delete this forwarding", "You are about to delete this forwarding": "You are about to delete this forwarding",
"You are about to delete this phonebook": "You are about to delete this phonebook", "You are about to delete this phonebook entry": "You are about to delete this phonebook entry",
"You are about to delete this registered device": "You are about to delete this registered device", "You are about to delete this registered device": "You are about to delete this registered device",
"You are about to delete time range \"{from} - {to}\"": "You are about to delete time range \"{from} - {to}\"", "You are about to delete time range \"{from} - {to}\"": "You are about to delete time range \"{from} - {to}\"",
"You are about to remove ACL: From email <{from_email}>": "You are about to remove ACL: From email <{from_email}>", "You are about to remove ACL: From email <{from_email}>": "You are about to remove ACL: From email <{from_email}>",

@ -156,7 +156,7 @@
"Delete recording": "Eliminar grabación", "Delete recording": "Eliminar grabación",
"Delete registered device": "Eliminar dispositivo registrado", "Delete registered device": "Eliminar dispositivo registrado",
"Delete slot?": "¿Eliminar ranura?", "Delete slot?": "¿Eliminar ranura?",
"Delete subscriber phonebook": "Eliminar agenda del suscriptor", "Delete subscriber phonebook entry": "Eliminar la entrada de la agenda del abonado",
"Delete voicemail after email notification is delivered": "Eliminar el correo de voz después de enviar la notificación por correo electrónico", "Delete voicemail after email notification is delivered": "Eliminar el correo de voz después de enviar la notificación por correo electrónico",
"Deliver Incoming Faxes": "Entregar faxes entrantes", "Deliver Incoming Faxes": "Entregar faxes entrantes",
"Deliver Outgoing Faxes": "Entregar faxes salientes", "Deliver Outgoing Faxes": "Entregar faxes salientes",
@ -607,7 +607,7 @@
"You are about to delete slot {slot}": "Está a punto de eliminar la ranura {slot}", "You are about to delete slot {slot}": "Está a punto de eliminar la ranura {slot}",
"You are about to delete this destination": "Está a punto de eliminar este destino", "You are about to delete this destination": "Está a punto de eliminar este destino",
"You are about to delete this forwarding": "Está a punto de eliminar este reenvío", "You are about to delete this forwarding": "Está a punto de eliminar este reenvío",
"You are about to delete this phonebook": "Está a punto de eliminar esta agenda", "You are about to delete this phonebook entry": "Está a punto de eliminar esta entrada de la agenda",
"You are about to delete this registered device": "Está a punto de eliminar este dispositivo registrado", "You are about to delete this registered device": "Está a punto de eliminar este dispositivo registrado",
"You are about to delete time range \"{from} - {to}\"": "Está a punto de borrar el rango de tiempo \"{from} - {to}\"", "You are about to delete time range \"{from} - {to}\"": "Está a punto de borrar el rango de tiempo \"{from} - {to}\"",
"You are about to remove ACL: From email <{from_email}>": "Está a punto de eliminar la ACL: Del correo electrónico <{from_email}>", "You are about to remove ACL: From email <{from_email}>": "Está a punto de eliminar la ACL: Del correo electrónico <{from_email}>",

@ -156,7 +156,7 @@
"Delete recording": "Supprimer l'enregistrement", "Delete recording": "Supprimer l'enregistrement",
"Delete registered device": "Supprimer l'appareil enregistré", "Delete registered device": "Supprimer l'appareil enregistré",
"Delete slot?": "Supprimer emplacement ?", "Delete slot?": "Supprimer emplacement ?",
"Delete subscriber phonebook": "Supprimer le répertoire de l'abonné", "Delete subscriber phonebook entry": "Supprimer lentrée du répertoire de labonné",
"Delete voicemail after email notification is delivered": "Supprimer le message vocal une fois la notification e-mail délivrée", "Delete voicemail after email notification is delivered": "Supprimer le message vocal une fois la notification e-mail délivrée",
"Deliver Incoming Faxes": "Transmission des fax entrants", "Deliver Incoming Faxes": "Transmission des fax entrants",
"Deliver Outgoing Faxes": "Livrer des fax sortants", "Deliver Outgoing Faxes": "Livrer des fax sortants",
@ -600,7 +600,7 @@
"You are about to delete slot {slot}": "Vous êtes sur le point de supprimer l'emplacement {slot}", "You are about to delete slot {slot}": "Vous êtes sur le point de supprimer l'emplacement {slot}",
"You are about to delete this destination": "Vous êtes sur le point de supprimer cette destination", "You are about to delete this destination": "Vous êtes sur le point de supprimer cette destination",
"You are about to delete this forwarding": "Vous êtes sur le point de supprimer cette redirection", "You are about to delete this forwarding": "Vous êtes sur le point de supprimer cette redirection",
"You are about to delete this phonebook": "Vous êtes sur le point de supprimer ce répertoire téléphonique", "You are about to delete this phonebook entry": "Vous êtes sur le point de supprimer cette entrée du répertoire",
"You are about to delete this registered device": "Vous êtes sur le point de supprimer cet appareil enregistré", "You are about to delete this registered device": "Vous êtes sur le point de supprimer cet appareil enregistré",
"You are about to delete time range \"{from} - {to}\"": "Vous êtes sur le point de supprimer la plage horaire \"{from} - {to}\"", "You are about to delete time range \"{from} - {to}\"": "Vous êtes sur le point de supprimer la plage horaire \"{from} - {to}\"",
"You are about to remove ACL: From email <{from_email}>": "Vous êtes sur le point de supprimer l'ACL : De l'e-mail <{from_email}>", "You are about to remove ACL: From email <{from_email}>": "Vous êtes sur le point de supprimer l'ACL : De l'e-mail <{from_email}>",

@ -154,7 +154,7 @@
"Delete recording": "Elimina registrazione", "Delete recording": "Elimina registrazione",
"Delete registered device": "Elimina dispositivo registrato", "Delete registered device": "Elimina dispositivo registrato",
"Delete slot?": "Eliminare slot?", "Delete slot?": "Eliminare slot?",
"Delete subscriber phonebook": "Elimina rubrica abbonato", "Delete subscriber phonebook entry": "Eliminare la voce della rubrica dellabbonato",
"Delete voicemail after email notification is delivered": "Cancella il messaggio vocale dopo l'invio della notifica email", "Delete voicemail after email notification is delivered": "Cancella il messaggio vocale dopo l'invio della notifica email",
"Deliver Incoming Faxes": "Consegna fax in arrivo", "Deliver Incoming Faxes": "Consegna fax in arrivo",
"Deliver Outgoing Faxes": "Consegna fax in uscita", "Deliver Outgoing Faxes": "Consegna fax in uscita",
@ -592,7 +592,7 @@
"You are about to delete slot {slot}": "Stai per eliminare la postazione {slot}", "You are about to delete slot {slot}": "Stai per eliminare la postazione {slot}",
"You are about to delete this destination": "Stai per eliminare questa destinazione", "You are about to delete this destination": "Stai per eliminare questa destinazione",
"You are about to delete this forwarding": "Stai per eliminare questo inoltro", "You are about to delete this forwarding": "Stai per eliminare questo inoltro",
"You are about to delete this phonebook": "Stai per eliminare questa rubrica", "You are about to delete this phonebook entry": "Stai per eliminare questa voce della rubrica",
"You are about to delete this registered device": "Stai per eliminare questo dispositivo registrato", "You are about to delete this registered device": "Stai per eliminare questo dispositivo registrato",
"You are about to delete time range \"{from} - {to}\"": "Stai per eliminare l'intervallo di tempo \"{from} - {to}\"", "You are about to delete time range \"{from} - {to}\"": "Stai per eliminare l'intervallo di tempo \"{from} - {to}\"",
"You are about to remove ACL: From email <{from_email}>": "Stai per rimuovere ACL: Dall'email <{from_email}>", "You are about to remove ACL: From email <{from_email}>": "Stai per rimuovere ACL: Dall'email <{from_email}>",

@ -387,6 +387,10 @@ export default {
this.$scrollTo(this.$parent.$el) this.$scrollTo(this.$parent.$el)
this.filterDirection = filter this.filterDirection = filter
this.forceReload() this.forceReload()
},
addToPhonebookAction (number) {
this.$store.commit('subscriber-phonebook/setNumber', number)
this.$router.push('subscriber-phonebook/create')
} }
} }
} }

@ -22,8 +22,8 @@
v-model:pagination="pagination" v-model:pagination="pagination"
class="no-shadow" class="no-shadow"
:columns="columns" :columns="columns"
:rows="subscriberPhonebook" :rows="phonebookRows"
:loading="$wait.is('loadSubscriberPhonebook')" :loading="$wait.is('loadPhonebook')"
row-key="id" row-key="id"
@request="fetchPaginatedRegistrations" @request="fetchPaginatedRegistrations"
> >
@ -89,9 +89,8 @@ import CscPage from 'components/CscPage'
import CscPageSticky from 'components/CscPageSticky' import CscPageSticky from 'components/CscPageSticky'
import CscPopupMenuItem from 'components/CscPopupMenuItem' import CscPopupMenuItem from 'components/CscPopupMenuItem'
import CscSpinner from 'components/CscSpinner' import CscSpinner from 'components/CscSpinner'
import { LIST_DEFAULT_ROWS } from 'src/api/common'
import { mapWaitingActions } from 'vue-wait' import { mapWaitingActions } from 'vue-wait'
import { mapState } from 'vuex' import { mapGetters, mapState } from 'vuex'
export default { export default {
name: 'CscPageSubscriberPhonebook', name: 'CscPageSubscriberPhonebook',
components: { components: {
@ -101,21 +100,14 @@ export default {
CscPopupMenuItem, CscPopupMenuItem,
CscPageSticky CscPageSticky
}, },
data () {
return {
data: [],
pagination: {
sortBy: 'id',
descending: false,
page: 1,
rowsPerPage: LIST_DEFAULT_ROWS,
rowsNumber: 0
}
}
},
computed: { computed: {
...mapState('user', [ ...mapState('subscriber-phonebook', {
'subscriberPhonebook' phonebookRows: 'phonebookRows',
pagination: 'pagination'
}),
...mapGetters('user', [
'isPbxEnabled',
'getSubscriberId'
]), ]),
columns () { columns () {
return [ return [
@ -165,10 +157,10 @@ export default {
await this.refresh() await this.refresh()
}, },
methods: { methods: {
...mapWaitingActions('user', { ...mapWaitingActions('subscriber-phonebook', {
loadSubscriberPhonebook: 'loadSubscriberPhonebook', loadPhonebook: 'loadPhonebook',
removeSubscriberPhonebook: 'removeSubscriberPhonebook', removeEntry: 'removeEntry',
updateValueShared: 'updateValueShared' updateSharedValue: 'updateSharedValue'
}), }),
async refresh () { async refresh () {
await this.fetchPaginatedRegistrations({ await this.fetchPaginatedRegistrations({
@ -177,14 +169,13 @@ export default {
}, },
async fetchPaginatedRegistrations (props) { async fetchPaginatedRegistrations (props) {
const { page, rowsPerPage, sortBy, descending } = props.pagination const { page, rowsPerPage, sortBy, descending } = props.pagination
const count = await this.loadSubscriberPhonebook({ await this.loadPhonebook({
subscriber_id: this.getSubscriberId,
page, page,
rows: rowsPerPage, rows: rowsPerPage,
order_by: sortBy, order_by: sortBy,
order_by_direction: descending ? 'desc' : 'asc' order_by_direction: descending ? 'desc' : 'asc'
}) })
this.pagination = { ...props.pagination }
this.pagination.rowsNumber = count
}, },
async showPhonebookDetails (row) { async showPhonebookDetails (row) {
this.$router.push(`/user/subscriber-phonebook/${row.id}`) this.$router.push(`/user/subscriber-phonebook/${row.id}`)
@ -206,18 +197,18 @@ export default {
}, },
async deleteRow (row) { async deleteRow (row) {
this.$q.dialog({ this.$q.dialog({
title: this.$t('Delete subscriber phonebook'), title: this.$t('Delete subscriber phonebook entry'),
message: this.$t('You are about to delete this phonebook'), message: this.$t('You are about to delete this phonebook entry'),
color: 'negative', color: 'negative',
cancel: true, cancel: true,
persistent: true persistent: true
}).onOk(async (data) => { }).onOk(async (data) => {
await this.removeSubscriberPhonebook(row) await this.removeEntry(row.id)
await this.refresh() await this.refresh()
}) })
}, },
async toggleShared (row) { async toggleShared (row) {
await this.updateValueShared(row) await this.updateSharedValue(row)
} }
} }
} }

@ -76,6 +76,7 @@ import CscPageSticky from 'components/CscPageSticky'
import { showGlobalError } from 'src/helpers/ui' import { showGlobalError } from 'src/helpers/ui'
import { mapWaitingActions } from 'vue-wait' import { mapWaitingActions } from 'vue-wait'
import { required } from 'vuelidate/lib/validators' import { required } from 'vuelidate/lib/validators'
import { mapGetters } from 'vuex'
export default { export default {
name: 'CscPageSubscriberPhonebookAdd', name: 'CscPageSubscriberPhonebookAdd',
components: { components: {
@ -101,6 +102,12 @@ export default {
} }
}, },
computed: { computed: {
...mapGetters('user', [
'getSubscriberId'
]),
...mapGetters('subscriber-phonebook', [
'getPrefilledNumber'
]),
nameErrorMessage () { nameErrorMessage () {
const errorsTab = this.v$.formData.name.$errors const errorsTab = this.v$.formData.name.$errors
if (errorsTab && errorsTab.length > 0 && errorsTab[0].$validator === 'required') { if (errorsTab && errorsTab.length > 0 && errorsTab[0].$validator === 'required') {
@ -111,9 +118,15 @@ export default {
return '' return ''
} }
}, },
mounted () {
if (this.getPrefilledNumber) {
this.formData.number = this.getPrefilledNumber
this.$store.commit('subscriber-phonebook/setNumber', '')
}
},
methods: { methods: {
...mapWaitingActions('user', { ...mapWaitingActions('subscriber-phonebook', {
createPhonebookSubscriber: 'createPhonebookSubscriber' createPhonebook: 'createPhonebook'
}), }),
getDefaultFormData () { getDefaultFormData () {
return { return {
@ -144,7 +157,8 @@ export default {
}, },
async confirm () { async confirm () {
try { try {
await this.createPhonebookSubscriber(this.formData) this.formData.subscriber_id = this.getSubscriberId
await this.createPhonebook(this.formData)
await this.$router.push('/user/subscriber-phonebook/') await this.$router.push('/user/subscriber-phonebook/')
} catch (error) { } catch (error) {
if (error.response && error.response.status === 422) { if (error.response && error.response.status === 422) {

@ -70,6 +70,7 @@
<script> <script>
import CscPageSticky from 'components/CscPageSticky' import CscPageSticky from 'components/CscPageSticky'
import { mapWaitingActions } from 'vue-wait' import { mapWaitingActions } from 'vue-wait'
import { mapGetters } from 'vuex'
export default { export default {
name: 'CscPageSubscriberPhonebookDetails', name: 'CscPageSubscriberPhonebookDetails',
components: { components: {
@ -84,49 +85,34 @@ export default {
shared: false shared: false
} }
}, },
computed: {
...mapGetters('user', [
'getSubscriberId'
])
},
async mounted () { async mounted () {
await this.getPhonebook(this.id) const response = await this.getEntry(this.id)
this.name = response.name
this.number = response.number
this.shared = response.shared
}, },
methods: { methods: {
...mapWaitingActions('user', { ...mapWaitingActions('subscriber-phonebook', {
getPhonebookDetails: 'getPhonebookDetails', getEntry: 'getEntry',
getValueShared: 'getValueShared', updateEntry: 'updateEntry'
getValueName: 'getValueName',
getValueNumber: 'getValueNumber'
}), }),
async getPhonebook (id) {
const response = await this.getPhonebookDetails(id)
this.name = response.data.name
this.number = response.data.number
this.shared = response.data.shared
},
cancel () { cancel () {
this.$router.push('/user/subscriber-phonebook/') this.$router.push('/user/subscriber-phonebook/')
this.$emit('cancel') this.$emit('cancel')
}, },
async changeValueName () { async confirm () {
await this.getValueName({ await this.updateEntry({
phonebookId: this.id, id: this.id,
number: this.number,
shared: this.shared,
name: this.name name: this.name
}) })
}, this.$router.push('/user/subscriber-phonebook/')
changeValueShared () {
this.getValueShared({
phonebookId: this.id,
shared: this.shared
})
},
changeValueNumber () {
this.getValueNumber({
phonebookId: this.id,
number: this.number
})
},
async confirm () {
await this.changeValueName()
await this.changeValueShared()
await this.changeValueNumber()
await this.$router.push('/user/subscriber-phonebook/')
} }
} }
} }

@ -20,6 +20,7 @@ import PbxSeatsModule from 'src/store/pbx-seats'
import PbxSoundSetsModule from 'src/store/pbx-soundsets' import PbxSoundSetsModule from 'src/store/pbx-soundsets'
import ReminderModule from 'src/store/reminder' import ReminderModule from 'src/store/reminder'
import SpeedDialModule from 'src/store/speed-dial' import SpeedDialModule from 'src/store/speed-dial'
import SubscriberPhonebookModule from 'src/store/subscriber-phonebook'
import UserModule from 'src/store/user' import UserModule from 'src/store/user'
import VoiceboxModule from 'src/store/voicebox' import VoiceboxModule from 'src/store/voicebox'
import { createStore } from 'vuex' import { createStore } from 'vuex'
@ -57,7 +58,8 @@ export default function (/* { ssrContext } */) {
callForwarding: CallForwardingModule, callForwarding: CallForwardingModule,
pbxAutoAttendants: PbxAutoAttendants, pbxAutoAttendants: PbxAutoAttendants,
dashboard: DashboardModule, dashboard: DashboardModule,
customer: Customer customer: Customer,
'subscriber-phonebook': SubscriberPhonebookModule
}, },
state: { state: {
route: null route: null

@ -0,0 +1,91 @@
import { LIST_DEFAULT_ROWS } from 'src/api/common'
import {
createPhonebook,
deleteEntry,
getEntryById,
getPhonebook,
setSharedValue,
updateEntry
} from 'src/api/subscriber-phonebook'
export default {
namespaced: true,
state: {
phonebookRows: [],
pagination: {
sortBy: 'id',
descending: false,
page: 1,
rowsPerPage: LIST_DEFAULT_ROWS,
rowsNumber: 0
},
numberInput: null
},
getters: {
getEntryById: (state) => (id) => {
return state.phonebookRows.find((entry) => entry.id === id)
},
getPrefilledNumber: (state) => state.numberInput,
phonebookRows: (state) => state.phonebookRows,
pagination: (state) => state.pagination
},
mutations: {
setPhonebookRows (state, rows) {
state.phonebookRows = rows
},
setPagination (state, pagination) {
state.pagination = { ...state.pagination, ...pagination }
},
setSharedValue (state, { id, value }) {
const rowIndex = state.phonebookRows.findIndex((row) => row.id === id)
if (rowIndex > -1) {
state.phonebookRows[rowIndex].shared = value
}
},
setNumber (state, numberInput) {
state.numberInput = numberInput
}
},
actions: {
async loadPhonebook ({ commit }, options) {
try {
const list = await getPhonebook({
...options
})
commit('setPhonebookRows', list.items || [])
commit('setPagination', {
page: options.page,
rowsPerPage: options.rows,
rowsNumber: list.totalCount
})
return list
} catch (err) {
commit('setPhonebookRows', [])
throw err
}
},
async removeEntry (context, id) {
await deleteEntry(id)
},
async getEntry (context, id) {
const cachedEntry = context.getters.getEntryById(id)
if (cachedEntry) {
return cachedEntry
}
// Fetch from API if not in store
const response = await getEntryById(id)
return response
},
async createPhonebook (context, data) {
await createPhonebook(data)
},
async updateEntry (context, data) {
await updateEntry(data)
},
async updateSharedValue (context, row) {
const newValue = !row.shared
context.commit('setSharedValue', { id: row.id, value: newValue })
await setSharedValue(row.id, newValue)
}
}
}

@ -12,24 +12,19 @@ import {
changePassword, changePassword,
changeSIPPassword, changeSIPPassword,
createCustomerPhonebook, createCustomerPhonebook,
createPhonebook,
generateGeneralPassword, generateGeneralPassword,
getBrandingLogo, getBrandingLogo,
getCustomerPhonebook, getCustomerPhonebook,
getNcosLevels, getNcosLevels,
getNcosSet, getNcosSet,
getPreferences, getPreferences,
getSubscriberPhonebook,
getSubscriberProfile, getSubscriberProfile,
getSubscriberRegistrations, getSubscriberRegistrations,
recoverPassword, recoverPassword,
resetPassword, resetPassword,
setPreference, setPreference,
setValueName,
setValueNameCustomer, setValueNameCustomer,
setValueNumber,
setValueNumberCustomer, setValueNumberCustomer,
setValueShared,
uploadCsv uploadCsv
} from 'src/api/subscriber' } from 'src/api/subscriber'
import { createAuthToken, getUserData, login } from 'src/api/user' import { createAuthToken, getUserData, login } from 'src/api/user'
@ -65,9 +60,7 @@ export default {
resellerBranding: null, resellerBranding: null,
defaultBranding: {}, defaultBranding: {},
subscriberRegistrations: [], subscriberRegistrations: [],
subscriberPhonebook: [],
customerPhonebook: [], customerPhonebook: [],
phonebookMap: {},
platformInfo: null, platformInfo: null,
qrCode: null, qrCode: null,
qrExpiringTime: null, qrExpiringTime: null,
@ -241,9 +234,6 @@ export default {
}, },
isSpCe (state) { isSpCe (state) {
return state?.platformInfo?.type === 'spce' return state?.platformInfo?.type === 'spce'
},
prefilledNumber (state) {
return state.numberInput
} }
}, },
mutations: { mutations: {
@ -332,9 +322,6 @@ export default {
setSubscriberRegistrations (state, value) { setSubscriberRegistrations (state, value) {
state.subscriberRegistrations = value state.subscriberRegistrations = value
}, },
setSubscriberPhonebook (state, value) {
state.subscriberPhonebook = value
},
setCustomerPhonebook (state, value) { setCustomerPhonebook (state, value) {
state.customerPhonebook = value state.customerPhonebook = value
}, },
@ -346,14 +333,6 @@ export default {
}, },
setQrExpiringTime (state, qrExpiringTime) { setQrExpiringTime (state, qrExpiringTime) {
state.qrExpiringTime = qrExpiringTime state.qrExpiringTime = qrExpiringTime
},
setPhonebookShared (state, { id, value }) {
const index = state.subscriberPhonebook.findIndex((row) => {
return row.id === id
})
if (index > -1) {
state.subscriberPhonebook[index].shared = value
}
} }
}, },
actions: { actions: {
@ -464,18 +443,6 @@ export default {
throw err throw err
} }
}, },
async loadSubscriberPhonebook ({ commit, dispatch, state, rootGetters }, options) {
try {
const list = await getSubscriberPhonebook({
...options
})
commit('setSubscriberPhonebook', list.items)
return list.totalCount
} catch (err) {
commit('setSubscriberPhonebook', [])
throw err
}
},
async loadCustomerPhonebook ({ commit, dispatch, state, rootGetters }, options) { async loadCustomerPhonebook ({ commit, dispatch, state, rootGetters }, options) {
try { try {
const list = await getCustomerPhonebook({ const list = await getCustomerPhonebook({
@ -509,11 +476,8 @@ export default {
async removeSubscriberRegistration (context, row) { async removeSubscriberRegistration (context, row) {
await httpApi.delete(`api/subscriberregistrations/${row.id}`) await httpApi.delete(`api/subscriberregistrations/${row.id}`)
}, },
async removeSubscriberPhonebook (context, row) { async removeCustomerPhonebook (context, { row, customerId }) {
await httpApi.delete(`api/subscriberphonebookentries/${row.id}`) await httpApi.delete(`api/v2/customers/${customerId}/phonebook/${row.id}`)
},
async removeCustomerPhonebook (context, row) {
await httpApi.delete(`api/customerphonebookentries/${row.id}`)
}, },
async getNcosLevelsSubscriber () { async getNcosLevelsSubscriber () {
const ncosLevel = [] const ncosLevel = []
@ -550,35 +514,15 @@ export default {
async setNcosLevelsSubscriber (value) { async setNcosLevelsSubscriber (value) {
await setPreference(getSubscriberId(), 'ncos', value) await setPreference(getSubscriberId(), 'ncos', value)
}, },
async getPhonebookDetails (context, id) { async getPhonebookCustomerDetails (context, { phonebookId, customerId }) {
const list = await httpApi.get(`api/subscriberphonebookentries/${id}`) const list = await httpApi.get(`api/v2/customers/${customerId}/phonebook/${phonebookId}`)
return list
},
async getPhonebookCustomerDetails (context, id) {
const list = await httpApi.get(`api/customerphonebookentries/${id}`)
return list return list
}, },
async getValueShared (context, options) {
await setValueShared(options.phonebookId, options.shared)
},
async updateValueShared (context, row) {
context.commit('setPhonebookShared', { id: row.id, value: !row.shared })
await setValueShared(row.id, row.shared)
},
async getValueName (context, options) {
await setValueName(options.phonebookId, options.name)
},
async getValueNameCustomer (context, options) { async getValueNameCustomer (context, options) {
await setValueNameCustomer(options.phonebookId, options.name) await setValueNameCustomer(options.phonebookId, options.name)
}, },
async getValueNumber (context, options) {
await setValueNumber(options.phonebookId, options.number)
},
async getValueNumberCustomer (context, options) { async getValueNumberCustomer (context, options) {
await setValueNumberCustomer(options.phonebookId, options.number) await setValueNumberCustomer(options.customerId, options.phonebookId, options.number)
},
async createPhonebookSubscriber (context, data) {
await createPhonebook(data)
}, },
async createPhonebookCustomer (context, data) { async createPhonebookCustomer (context, data) {
await createCustomerPhonebook(data) await createCustomerPhonebook(data)

Loading…
Cancel
Save