From 56efed61f265214a00581d154f4ba440ada0e14a Mon Sep 17 00:00:00 2001 From: Sergii Leonenko Date: Tue, 16 Feb 2021 00:10:17 +0200 Subject: [PATCH] TT#109763 Fix OfficeHours in the new CallForwarding implementation AC: Can see "Office Hours"-Condition working properly according to the Kamailio time periods Can save and see the same periods after loaded the data again In addition: * added logic to automatic detecion and seting "Same Time..." checkbox state based on the time ranges data in the timeset instead of timeset name * added time ranges validation * reproduced notification about invalid timeset which was in the old UI * fixed popup layout to support long list of time ranges Change-Id: Ic3a7b75a1d47c576beece660ca3fa56b9cc5b386 --- src/api/call-forwarding.js | 74 +-- .../CscCfGroupConditionOfficeHours.vue | 589 ++++++++++-------- .../call-forwarding/CscCfGroupItem.vue | 3 +- .../CscCfSelectionWeekdays.vue | 23 +- src/filters/time-set.js | 74 ++- src/helpers/kamailio-timesets-converter.js | 562 +++++++++-------- src/helpers/ui.js | 21 +- src/i18n/de.json | 16 +- src/i18n/en.json | 16 +- src/i18n/es.json | 16 +- src/i18n/fr.json | 16 +- src/i18n/it.json | 16 +- src/store/call-forwarding/actions.js | 40 +- 13 files changed, 782 insertions(+), 684 deletions(-) diff --git a/src/api/call-forwarding.js b/src/api/call-forwarding.js index e6a642a2..d0836dfb 100644 --- a/src/api/call-forwarding.js +++ b/src/api/call-forwarding.js @@ -242,88 +242,22 @@ export async function cfUpdateTimeSetWeekdays (timeSetId, weekdays) { }) } -function cfNormaliseOfficeHours (timesPerWeekday) { - const normalisedTimes = [] - timesPerWeekday.forEach((times, index) => { - times.forEach((time) => { - if (time.from !== '' && time.to !== '') { - const fromParts = time.from.split(':') - const toParts = time.to.split(':') - if (fromParts[0] !== '__' && fromParts[1] !== '__' && toParts[0] !== '__' && toParts[1] !== '__') { - normalisedTimes.push({ - minute: fromParts[1] + '-' + toParts[1], - month: null, - hour: fromParts[0] + '-' + toParts[0], - mday: null, - year: null, - wday: (index + 1) - }) - } - } - }) - }) - return normalisedTimes -} - -export async function cfCreateOfficeHours (subscriberId, timesPerWeekday) { +export async function cfCreateOfficeHours (subscriberId, times) { return post({ resource: 'cftimesets', body: { subscriber_id: subscriberId, name: 'csc-office-hours-' + v4(), - times: cfNormaliseOfficeHours(timesPerWeekday) - } - }) -} - -export async function cfUpdateOfficeHours (timeSetId, timesPerWeekday) { - return patchReplace({ - resource: 'cftimesets', - resourceId: timeSetId, - fieldPath: 'times', - value: cfNormaliseOfficeHours(timesPerWeekday) - }) -} - -function cfNormaliseOfficeHoursSameTimes (times, weekdays) { - const normalisedTimes = [] - weekdays.forEach((weekday) => { - times.forEach((time) => { - if (time.from !== '' && time.to !== '') { - const fromParts = time.from.split(':') - const toParts = time.to.split(':') - if (fromParts[0] !== '__' && fromParts[1] !== '__' && toParts[0] !== '__' && toParts[1] !== '__') { - normalisedTimes.push({ - minute: fromParts[1] + '-' + toParts[1], - month: null, - hour: fromParts[0] + '-' + toParts[0], - mday: null, - year: null, - wday: weekday - }) - } - } - }) - }) - return normalisedTimes -} - -export async function cfCreateOfficeHoursSameTimes (subscriberId, times, weekdays) { - return post({ - resource: 'cftimesets', - body: { - subscriber_id: subscriberId, - name: 'csc-office-hours-same-times-' + v4(), - times: cfNormaliseOfficeHoursSameTimes(times, weekdays) + times: times } }) } -export async function cfUpdateOfficeHoursSameTimes (timeSetId, times, weekdays) { +export async function cfUpdateOfficeHours (timeSetId, times) { return patchReplace({ resource: 'cftimesets', resourceId: timeSetId, fieldPath: 'times', - value: cfNormaliseOfficeHoursSameTimes(times, weekdays) + value: times }) } diff --git a/src/components/call-forwarding/CscCfGroupConditionOfficeHours.vue b/src/components/call-forwarding/CscCfGroupConditionOfficeHours.vue index cc3ff3a3..6479b49b 100644 --- a/src/components/call-forwarding/CscCfGroupConditionOfficeHours.vue +++ b/src/components/call-forwarding/CscCfGroupConditionOfficeHours.vue @@ -7,129 +7,152 @@ @back="back" @close="$emit('close')" > -
- -
- + + {{ $t('The "{timeset}" timeset contains incompatible values. You can resolve this by deleting it and recreating from the scratch.', { timeset: timeSet.name }) }} + + + - - - - - - - - - - - - + + + + + + + + + + + + +
+ +
+ @@ -155,11 +179,23 @@ + + diff --git a/src/components/call-forwarding/CscCfGroupItem.vue b/src/components/call-forwarding/CscCfGroupItem.vue index e14eea91..be5588a4 100644 --- a/src/components/call-forwarding/CscCfGroupItem.vue +++ b/src/components/call-forwarding/CscCfGroupItem.vue @@ -149,7 +149,8 @@ @save="updateAnnouncementEvent({ destinationIndex: destinationIndex, destinationSetId: destinationSet.id - })"> + })" + > -
+
@@ -37,6 +40,7 @@ outside-arrows mobile-arrows no-caps + :disable="disable" /> import { DAY_MAP, - DAY_NAME_MAP + getDayNameByNumber } from 'src/filters/time-set' export default { @@ -62,6 +66,10 @@ export default { tabs: { type: Boolean, default: false + }, + disable: { + type: Boolean, + default: false } }, data () { @@ -75,14 +83,14 @@ export default { return 'tab-' + this.selectedWeekdays[0] }, set (tab) { - this.selectedWeekdays = [parseInt(tab.replace('tab-', ''))] + this.selectedWeekdays = [Number(tab.replace('tab-', ''))] } }, days () { const options = [] DAY_MAP.forEach((day, index) => { options.push({ - label: DAY_NAME_MAP[index].substr(0, 2), + label: getDayNameByNumber(index, true), value: day }) }) @@ -111,3 +119,10 @@ export default { } } + + diff --git a/src/filters/time-set.js b/src/filters/time-set.js index 79636d18..42bc7063 100644 --- a/src/filters/time-set.js +++ b/src/filters/time-set.js @@ -1,17 +1,8 @@ import { i18n } from 'boot/i18n' +import { getKamailioRangeElements } from 'src/helpers/kamailio-timesets-converter' export const DAY_MAP = [2, 3, 4, 5, 6, 7, 1] -export const DAY_NAME_MAP = [ - i18n.t('Monday'), - i18n.t('Tuesday'), - i18n.t('Wednesday'), - i18n.t('Thursday'), - i18n.t('Friday'), - i18n.t('Saturday'), - i18n.t('Sunday') -] - export const DEFAULT_WEEKDAYS = [ DAY_MAP[0], DAY_MAP[1], @@ -20,6 +11,29 @@ export const DEFAULT_WEEKDAYS = [ DAY_MAP[4] ] +export function getDayNameByNumber (dayNumber, isShortName = false) { + const daysNamesMap = [ + i18n.t('Monday'), + i18n.t('Tuesday'), + i18n.t('Wednesday'), + i18n.t('Thursday'), + i18n.t('Friday'), + i18n.t('Saturday'), + i18n.t('Sunday') + ] + // NOTE: in some languages the short days names may be not just first two letters of the full day's name + const daysShortNamesMap = [ + i18n.t('Mo'), + i18n.t('Tu'), + i18n.t('We'), + i18n.t('Th'), + i18n.t('Fr'), + i18n.t('Sa'), + i18n.t('Su') + ] + return isShortName ? daysShortNamesMap[dayNumber] : daysNamesMap[dayNumber] +} + export function timeSetDateExact (times) { return times[0].year + '/' + times[0].month + '/' + times[0].mday } @@ -37,43 +51,25 @@ export function timeSetWeekdays (times) { return DAY_MAP.indexOf(parseInt(time.wday)) }) mappedWeekdays.sort() - let weekdays = '' - mappedWeekdays.forEach((weekday, index) => { - if (index > 0) { - weekdays = weekdays + ', ' - } - weekdays = weekdays + DAY_NAME_MAP[weekday] - }) - return weekdays + const weekdaysStr = mappedWeekdays.map(weekday => getDayNameByNumber(weekday)).join(', ') + return weekdaysStr } export function timeSetOfficeHoursSameTime (times) { const weekdays = new Set() - let weekdaysStr = '' - times.forEach((time) => { - weekdays.add(parseInt(time.wday)) - }) - const weekdaysSorted = Array.from(weekdays) - weekdaysSorted.sort((a, b) => { - if (a === 1) { - return 1 - } else if (a > b) { - return 1 - } else if (a < b) { - return -1 - } else { - return 0 - } - }) - weekdaysSorted.forEach((weekday, index) => { - if (index > 0) { - weekdaysStr += ', ' + times.forEach(kamailioTimeRange => { + const wdayRange = getKamailioRangeElements(kamailioTimeRange.wday)[0] + for (let day = wdayRange.from; day <= wdayRange.to; day++) { + // To have Monday the first day of a week we are mapping days to change order + weekdays.add(DAY_MAP.indexOf(day)) } - weekdaysStr += DAY_NAME_MAP[DAY_MAP.indexOf(weekday)] }) + const weekdaysSorted = Array.from(weekdays) + weekdaysSorted.sort() + const weekdaysStr = weekdaysSorted.map(weekday => getDayNameByNumber(weekday)).join(', ') return weekdaysStr } export function timeSetTimes () { - + return '...' } diff --git a/src/helpers/kamailio-timesets-converter.js b/src/helpers/kamailio-timesets-converter.js index e37758b7..29a9b9e6 100644 --- a/src/helpers/kamailio-timesets-converter.js +++ b/src/helpers/kamailio-timesets-converter.js @@ -1,4 +1,3 @@ -// /* eslint-disable */ /* humanReadableTimeset = [{ @@ -32,40 +31,39 @@ kamailioTimeset = [{ ] */ -export function getTimeStrElements(timeStr) { - const [hours, minutes] = timeStr.split(':').map(t => parseInt(t, 10)) - return { hours, minutes } +export function getTimeStrElements (timeStr) { + const [hours, minutes] = timeStr.split(':').map(t => Number(t)) + return { hours, minutes } } /** * @param timeStr - a string with hour and minutes divided by colon, like "0:00" or "23:59" * @returns {number} */ -export function timeStrToMinutes(timeStr) { - const { hours, minutes } = getTimeStrElements(timeStr) - return hours * 60 + minutes +export function timeStrToMinutes (timeStr) { + const { hours, minutes } = getTimeStrElements(timeStr) + return hours * 60 + minutes } -export function isTimeStrValid(timeStr) { - if (typeof timeStr === 'string') { - const { hours, minutes } = getTimeStrElements(timeStr) - return hours >= 0 && hours <= 23 && minutes >= 0 && minutes <= 59 - } - return false +export function isTimeStrValid (timeStr) { + if (typeof timeStr === 'string') { + const { hours, minutes } = getTimeStrElements(timeStr) + return hours >= 0 && hours <= 23 && minutes >= 0 && minutes <= 59 + } + return false } -export function validateHumanTimesets(hTimeset) { - hTimeset.forEach(timesetItem => { - const { weekday, from, to } = timesetItem - if (typeof weekday !== 'number' || isNaN(weekday) || weekday < 1 || weekday > 7 || - !isTimeStrValid(from) || !isTimeStrValid(to) - ) { - throw Error('A human timeset item has invalid format: ' + JSON.stringify(timesetItem)) - } - else if (timeStrToMinutes(from) > timeStrToMinutes(to)) { - throw Error('A human timeset item should have "from" time < or = "to" time: ' + JSON.stringify(timesetItem)) - } - }) +export function validateHumanTimesets (hTimeset) { + hTimeset.forEach(timesetItem => { + const { weekday, from, to } = timesetItem + if (typeof weekday !== 'number' || isNaN(weekday) || weekday < 1 || weekday > 7 || + !isTimeStrValid(from) || !isTimeStrValid(to) + ) { + throw Error('A human timeset item has invalid format: ' + JSON.stringify(timesetItem)) + } else if (timeStrToMinutes(from) > timeStrToMinutes(to)) { + throw Error('A human timeset item should have "from" time < or = "to" time: ' + JSON.stringify(timesetItem)) + } + }) } /** @@ -74,264 +72,284 @@ export function validateHumanTimesets(hTimeset) { * @param hTimeset {humanReadableTimeset} * @returns {humanReadableTimeset} */ -export function getHumanTimesetsNormalized(hTimeset = []) { - //sort timeset by "weekday" and "from" columns - //clone input data to prevent original data object mutation - const hTimesetCloned = hTimeset.map(i => ({...i})) - const htSorted = hTimesetCloned.sort((a, b) => { - const dayDiff = a.weekday - b.weekday - if (dayDiff) - return dayDiff - else - return timeStrToMinutes(a.from) - timeStrToMinutes(b.from) - }) - - //combine, merge periods for a day. For example 0:00-2:00 and 1:00-4:00 should be merged into 0:00-4:00 - //Important: the timeset should be sorted by "weekday" and "from"! - const htNormalized = htSorted.reduce((acc, currentItem) => { - const prevItem = acc.pop() - if (!prevItem) { - acc.push(currentItem) - } - else if (prevItem.weekday !== currentItem.weekday) { - acc.push(prevItem) - acc.push(currentItem) - } - else { - const prevItemMinutes = { fromM: timeStrToMinutes(prevItem.from), toM: timeStrToMinutes(prevItem.to) } - const currentItemMinutes = { fromM: timeStrToMinutes(currentItem.from), toM: timeStrToMinutes(currentItem.to) } - - if (prevItemMinutes.fromM <= currentItemMinutes.fromM && currentItemMinutes.toM <= prevItemMinutes.toM) { - //current time range is completely inside previous time range --> just skipping current timeSet item - acc.push(prevItem) - } - else if (prevItemMinutes.toM < currentItemMinutes.fromM) { - //current time range is not part of previous time range --> adding both as separate time ranges - acc.push(prevItem) - acc.push(currentItem) - } - else if (prevItemMinutes.toM >= currentItemMinutes.fromM) { - //current time range is the next chunk\part of the previous time range - // OR they are intercepting --> extending\combining previous time range with current time range - prevItem.to = currentItem.to - acc.push(prevItem) - } - else { - console.info('Acc:', acc, 'prevItem:', prevItem, 'currentItem:', currentItem) - console.info('prevItemMinutes:', prevItemMinutes, 'currentItemMinutes:', currentItemMinutes) - throw Error('Internal error in "getHumanTimesetsNormalized"') - } - } - return acc - }, []) - - return htNormalized +export function getHumanTimesetsNormalized (hTimeset = []) { + // sort timeset by "weekday" and "from" columns + // clone input data to prevent original data object mutation + const hTimesetCloned = hTimeset.map(i => ({ ...i })) + const htSorted = hTimesetCloned.sort((a, b) => { + const dayDiff = a.weekday - b.weekday + if (dayDiff) { + return dayDiff + } else { + return timeStrToMinutes(a.from) - timeStrToMinutes(b.from) + } + }) + + // combine, merge periods for a day. For example 0:00-2:00 and 1:00-4:00 should be merged into 0:00-4:00 + // Important: the timeset should be sorted by "weekday" and "from"! + const htNormalized = htSorted.reduce((acc, currentItem) => { + const prevItem = acc.pop() + if (!prevItem) { + acc.push(currentItem) + } else if (prevItem.weekday !== currentItem.weekday) { + acc.push(prevItem) + acc.push(currentItem) + } else { + const prevItemMinutes = { fromM: timeStrToMinutes(prevItem.from), toM: timeStrToMinutes(prevItem.to) } + const currentItemMinutes = { fromM: timeStrToMinutes(currentItem.from), toM: timeStrToMinutes(currentItem.to) } + + if (prevItemMinutes.fromM <= currentItemMinutes.fromM && currentItemMinutes.toM <= prevItemMinutes.toM) { + // current time range is completely inside previous time range --> just skipping current timeSet item + acc.push(prevItem) + } else if (prevItemMinutes.toM < currentItemMinutes.fromM) { + // current time range is not part of previous time range --> adding both as separate time ranges + acc.push(prevItem) + acc.push(currentItem) + } else if (prevItemMinutes.toM >= currentItemMinutes.fromM) { + // current time range is the next chunk\part of the previous time range + // OR they are intercepting --> extending\combining previous time range with current time range + prevItem.to = currentItem.to + acc.push(prevItem) + } else { + console.info('Acc:', acc, 'prevItem:', prevItem, 'currentItem:', currentItem) + console.info('prevItemMinutes:', prevItemMinutes, 'currentItemMinutes:', currentItemMinutes) + throw Error('Internal error in "getHumanTimesetsNormalized"') + } + } + return acc + }, []) + + return htNormalized } -export function humanTimesetToKamailio(hTimeset = []) { - validateHumanTimesets(hTimeset) - - const htNormalized = getHumanTimesetsNormalized(hTimeset) - const kamailioTimesetRaw = htNormalized.map(timesetItem => { - const { weekday, from } = timesetItem - const to = timesetItem.to === '23:59' ? '24:00' : timesetItem.to - - const fromHM = getTimeStrElements(from) - const toHM = getTimeStrElements(to) - - const result = [] - if (fromHM.hours === toHM.hours) { - if (fromHM.minutes === toHM.minutes || fromHM.minutes + 1 === toHM.minutes) - result.push({ wday: weekday, hour: `${fromHM.hours}`, minute: `${fromHM.minutes}` }) - else - result.push({ wday: weekday, hour: `${fromHM.hours}`, minute: `${fromHM.minutes}-${toHM.minutes-1}` }) - } - else { - //NOTE: any human readable time range for a day can be represented as next set of Kamailio ranges - //for example "1:10-5:35" can be represented as "1:10-2:00", "2:00-5:00", "5:00-5:35" and than coded in Kamailio format - // [ - // { wday: weekday, hour: `${fromHM.hours}`, minute: `${fromHM.minutes}-59` }, - // { wday: weekday, hour: `${fromHM.hours+1}-${toHM.hours-1}` }, - // { wday: weekday, hour: `${toHM.hours}`, minute: `0-${toHM.minutes-1}` } - // ] - //but code below will output a little more optimized version - - //"Starting" range - if (fromHM.minutes > 0) { - result.push({ wday: weekday, hour: `${fromHM.hours}`, minute: `${fromHM.minutes}-59` }) - } - else fromHM.hours -= 1 - - //"Middle" range - if (fromHM.hours + 1 <= toHM.hours-1) { - result.push({ wday: weekday, hour: `${fromHM.hours + 1}-${toHM.hours - 1}` }) - } - - //"Ending" range - if (toHM.minutes > 0) { - result.push({ wday: weekday, hour: `${toHM.hours}`, minute: `0-${toHM.minutes-1}` }) - } - } - return result - }) - - const kamailioTimeset = kamailioTimesetRaw.reduce((acc, item) => { - const optimizedItemRanges = item.map(item => { - //if minute or hour contains range like "a-a" convert to just "a" - if (item.hour) { - let [hourF, hourT] = item.hour.split('-') - if (hourF === hourT) item.hour = hourF - } - - if (item.minute) { - let [minuteF, minuteT] = item.minute.split('-') - if (minuteF === minuteT) item.minute = minuteF - } - - return item - }) - //similar to flatMap - return [...acc, ...optimizedItemRanges] - }, []) - .reduce((acc, item) => { - //combine the same time ranges by "wday" - if (acc.length === 0) - acc.push(item) - else { - const mergeCandidate = acc.find(accItem => - accItem.hour === item.hour && - accItem.minute === item.minute && - getKamailioRangeElements(accItem.wday)[0].to + 1 === getKamailioRangeElements(item.wday)[0].from - ) - if (mergeCandidate) { - const mergeCandidateWday = getKamailioRangeElements(mergeCandidate.wday)[0] - mergeCandidate.wday = `${mergeCandidateWday.from}-${mergeCandidateWday.to + 1}` - } - else { - acc.push(item) - } - } - return acc - }, []) - - return kamailioTimeset +export function humanTimesetToKamailio (hTimeset = []) { + validateHumanTimesets(hTimeset) + + const htNormalized = getHumanTimesetsNormalized(hTimeset) + const kamailioTimesetRaw = htNormalized.map(timesetItem => { + const { weekday, from } = timesetItem + const to = timesetItem.to === '23:59' ? '24:00' : timesetItem.to + + const fromHM = getTimeStrElements(from) + const toHM = getTimeStrElements(to) + + const result = [] + if (fromHM.hours === toHM.hours) { + if (fromHM.minutes === toHM.minutes || fromHM.minutes + 1 === toHM.minutes) { + result.push({ wday: weekday, hour: `${fromHM.hours}`, minute: `${fromHM.minutes}` }) + } else { + result.push({ wday: weekday, hour: `${fromHM.hours}`, minute: `${fromHM.minutes}-${toHM.minutes - 1}` }) + } + } else { + // NOTE: any human readable time range for a day can be represented as next set of Kamailio ranges + // for example "1:10-5:35" can be represented as "1:10-2:00", "2:00-5:00", "5:00-5:35" and than coded in Kamailio format + // [ + // { wday: weekday, hour: `${fromHM.hours}`, minute: `${fromHM.minutes}-59` }, + // { wday: weekday, hour: `${fromHM.hours+1}-${toHM.hours-1}` }, + // { wday: weekday, hour: `${toHM.hours}`, minute: `0-${toHM.minutes-1}` } + // ] + // but code below will output a little more optimized version + + // "Starting" range + if (fromHM.minutes > 0) { + result.push({ wday: weekday, hour: `${fromHM.hours}`, minute: `${fromHM.minutes}-59` }) + } else fromHM.hours -= 1 + + // "Middle" range + if (fromHM.hours + 1 <= toHM.hours - 1) { + result.push({ wday: weekday, hour: `${fromHM.hours + 1}-${toHM.hours - 1}` }) + } + + // "Ending" range + if (toHM.minutes > 0) { + result.push({ wday: weekday, hour: `${toHM.hours}`, minute: `0-${toHM.minutes - 1}` }) + } + } + return result + }) + + const kamailioTimeset = kamailioTimesetRaw.reduce((acc, item) => { + const optimizedItemRanges = item.map(item => { + // if minute or hour contains range like "a-a" convert to just "a" + if (item.hour) { + const [hourF, hourT] = item.hour.split('-') + if (hourF === hourT) item.hour = hourF + } + + if (item.minute) { + const [minuteF, minuteT] = item.minute.split('-') + if (minuteF === minuteT) item.minute = minuteF + } + + return item + }) + // similar to flatMap + return [...acc, ...optimizedItemRanges] + }, []) + .reduce((acc, item) => { + // combine the same time ranges by "wday" + if (acc.length === 0) { + acc.push(item) + } else { + const mergeCandidate = acc.find(accItem => + accItem.hour === item.hour && + accItem.minute === item.minute && + getKamailioRangeElements(accItem.wday)[0].to + 1 === getKamailioRangeElements(item.wday)[0].from + ) + if (mergeCandidate) { + const mergeCandidateWday = getKamailioRangeElements(mergeCandidate.wday)[0] + mergeCandidate.wday = `${mergeCandidateWday.from}-${mergeCandidateWday.to + 1}` + } else { + acc.push(item) + } + } + return acc + }, []) + + return kamailioTimeset } -//--------------- +// --------------- + +// TODO: does it used? +export function getSimpleRangeElements (rangeStr = '') { + const range = String(rangeStr).trim() + const rangeElements = range.split('-').map(r => r.trim()).filter(r => r.length).map(r => Number(r)) + + if (rangeElements.length === 0) { + return undefined + } else { + if (rangeElements.length > 2) { + throw Error('Invalid range format: "' + rangeStr + '"') + } + return { + from: rangeElements[0], + to: rangeElements.length === 2 ? rangeElements[1] : rangeElements[0] + } + } +} -export function getKamailioRangeElements(kamailioRangeStr = '') { - const ranges = String(kamailioRangeStr).trim().split(' ').map(r => r.trim()).filter(r => r.length) - return ranges.map(r => { - const rangeElements = r.split('-').map(r => r.trim()).map(r => parseInt(r, 10)) +export function getKamailioRangeElements (kamailioRangeStr = '') { + const ranges = String(kamailioRangeStr).trim().split(' ').map(r => r.trim()).filter(r => r.length) + return ranges.map(r => { + const rangeElements = r.split('-').map(r => r.trim()).map(r => Number(r)) - if (rangeElements.length > 2) - throw Error('Invalid Kamailio range format: "' + kamailioRangeStr + '"') + if (rangeElements.length > 2) { + throw Error('Invalid Kamailio range format: "' + kamailioRangeStr + '"') + } - return { - from: rangeElements[0], - to: rangeElements.length === 2 ? rangeElements[1] : rangeElements[0] - } - }) + return { + from: rangeElements[0], + to: rangeElements.length === 2 ? rangeElements[1] : rangeElements[0] + } + }) } -export function validateKamailioRange(kamailioRangeStr = '', minValue, maxValue) { - const rangeElements = getKamailioRangeElements(kamailioRangeStr) - if (rangeElements.length === 0) - throw Error('Kamailio range should not be empty') - else if (rangeElements.length > 1) - throw Error('Kamailio multiple ranges are not supported: "' + kamailioRangeStr + '"') - else { - if (isNaN(rangeElements[0].from) || isNaN(rangeElements[0].to)) - throw Error('Kamailio range has invalid characters or a wrong format: "' + kamailioRangeStr + '"') - if (rangeElements[0].from > rangeElements[0].to) - throw Error('Kamailio reversed ranges are not supported: "' + kamailioRangeStr + '"') - if (minValue !== undefined && maxValue !== undefined) { - if (rangeElements[0].from < minValue || maxValue < rangeElements[0].from || - rangeElements[0].to < minValue || maxValue < rangeElements[0].to) { - throw Error(`Kamailio range elements are out of allowed values range (${minValue}..${maxValue}) : "${kamailioRangeStr}"`) - } - } - } +export function validateKamailioRange (kamailioRangeStr = '', minValue, maxValue) { + const rangeElements = getKamailioRangeElements(kamailioRangeStr) + if (rangeElements.length === 0) { + throw Error('Kamailio range should not be empty') + } else if (rangeElements.length > 1) { + throw Error('Kamailio multiple ranges are not supported: "' + kamailioRangeStr + '"') + } else { + if (isNaN(rangeElements[0].from) || isNaN(rangeElements[0].to)) { + throw Error('Kamailio range has invalid characters or a wrong format: "' + kamailioRangeStr + '"') + } + if (rangeElements[0].from > rangeElements[0].to) { + throw Error('Kamailio reversed ranges are not supported: "' + kamailioRangeStr + '"') + } + if (minValue !== undefined && maxValue !== undefined) { + if (rangeElements[0].from < minValue || maxValue < rangeElements[0].from || + rangeElements[0].to < minValue || maxValue < rangeElements[0].to) { + throw Error(`Kamailio range elements are out of allowed values range (${minValue}..${maxValue}) : "${kamailioRangeStr}"`) + } + } + } } -export function validateKamailioTimesets(kTimeset) { - kTimeset.forEach(timesetItem => { - let { wday, hour, minute } = timesetItem - wday = (wday === null || wday === undefined) ? '' : String(wday).trim() - hour = (hour === null || hour === undefined) ? '' : String(hour).trim() - minute = (minute === null || minute === undefined) ? '' : String(minute).trim() - if (wday !== '') - validateKamailioRange(wday, 1, 7) - else - throw Error('A Kamailio timeset should have "wday" range: ' + JSON.stringify(timesetItem)) - if (hour !== '') - validateKamailioRange(hour, 0, 23) - if (minute !== '') - validateKamailioRange(minute, 0, 59) - - Object.entries(timesetItem) - .filter(([key]) => !['wday', 'hour', 'minute'].includes(key)) - .forEach(([key, value]) => { - if (!(value === null || value === undefined || String(value).trim().length === 0)) - throw Error(`The "${key}" scale of Kamailio timesets is not supported: ${JSON.stringify(timesetItem)}`) - }) - }) +export function validateKamailioTimesets (kTimeset) { + kTimeset.forEach(timesetItem => { + let { wday, hour, minute } = timesetItem + wday = (wday === null || wday === undefined) ? '' : String(wday).trim() + hour = (hour === null || hour === undefined) ? '' : String(hour).trim() + minute = (minute === null || minute === undefined) ? '' : String(minute).trim() + if (wday !== '') { + validateKamailioRange(wday, 1, 7) + } else { + throw Error('A Kamailio timeset should have "wday" range: ' + JSON.stringify(timesetItem)) + } + if (hour !== '') { + validateKamailioRange(hour, 0, 23) + } + if (minute !== '') { + validateKamailioRange(minute, 0, 59) + } + + Object.entries(timesetItem) + .filter(([key]) => !['wday', 'hour', 'minute'].includes(key)) + .forEach(([key, value]) => { + if (!(value === null || value === undefined || String(value).trim().length === 0)) { + throw Error(`The "${key}" scale of Kamailio timesets is not supported: ${JSON.stringify(timesetItem)}`) + } + }) + }) } -export function kamailioTimesetToHuman(kTimeset = []) { - validateKamailioTimesets(kTimeset) - - //convert Kamailio timeset into Human readable format - const hTimesetRaw = kTimeset.map(timesetItem => { - let { wday, hour, minute } = timesetItem - hour = (hour === null || hour === undefined) ? '' : String(hour).trim() - minute = (minute === null || minute === undefined) ? '' : String(minute).trim() - - wday = getKamailioRangeElements(wday)[0] - if (hour !== '') - hour = getKamailioRangeElements(hour)[0] - else - hour = { from: 0, to: 23 } - if (minute !== '') - minute = getKamailioRangeElements(minute)[0] - else - minute = { from: 0, to: 59 } - - const nestedRules = [wday, hour, minute].reverse() - const rulesOutput = nestedRules.reduce((acc, range) => { - const newAcc = [] - if (acc.length === 0) { - newAcc.push({ from: String(range.from), to: String(range.to + 1) }) - } - else { - for (let i = range.from; i <= range.to; i++) { - acc.forEach(accItem => - newAcc.push({ from: [i, accItem.from].join('_'), to: [i, accItem.to].join('_') }) - ) - } - } - return newAcc - }, []) - const hTimeset = rulesOutput.map(ruleOutput => { - const [fromWday, fromHour, fromMinute] = ruleOutput.from.split('_').map(i => Number(i)) - const [, toHour, toMinute] = ruleOutput.to.split('_').map(i => Number(i)) - const from = [fromHour, fromMinute] - .map((i, index) => String(i).padStart((index === 1) ? 2 : 1, '0')).join(':') - const to = [ - (toMinute === 60) ? toHour + 1 : toHour, - (toMinute === 60) ? 0 : toMinute - ].map((i, index) => String(i).padStart((index === 1) ? 2 : 1, '0')).join(':') - - return { - weekday: fromWday, - from, - to: (to === '24:00') ? '23:59': to - } - }) - - return hTimeset - }) - .reduce((acc, item) => [...acc, ...item], []) - - return getHumanTimesetsNormalized(hTimesetRaw) +export function kamailioTimesetToHuman (kTimeset = []) { + validateKamailioTimesets(kTimeset) + + // convert Kamailio timeset into Human readable format + const hTimesetRaw = kTimeset.map(timesetItem => { + let { wday, hour, minute } = timesetItem + hour = (hour === null || hour === undefined) ? '' : String(hour).trim() + minute = (minute === null || minute === undefined) ? '' : String(minute).trim() + + wday = getKamailioRangeElements(wday)[0] + if (hour !== '') { + hour = getKamailioRangeElements(hour)[0] + } else { + hour = { from: 0, to: 23 } + } + if (minute !== '') { + minute = getKamailioRangeElements(minute)[0] + } else { + minute = { from: 0, to: 59 } + } + + const nestedRules = [wday, hour, minute].reverse() + const rulesOutput = nestedRules.reduce((acc, range) => { + const newAcc = [] + if (acc.length === 0) { + newAcc.push({ from: String(range.from), to: String(range.to + 1) }) + } else { + for (let i = range.from; i <= range.to; i++) { + acc.forEach(accItem => + newAcc.push({ from: [i, accItem.from].join('_'), to: [i, accItem.to].join('_') }) + ) + } + } + return newAcc + }, []) + const hTimeset = rulesOutput.map(ruleOutput => { + const [fromWday, fromHour, fromMinute] = ruleOutput.from.split('_').map(i => Number(i)) + const [, toHour, toMinute] = ruleOutput.to.split('_').map(i => Number(i)) + const from = [fromHour, fromMinute] + .map((i, index) => String(i).padStart((index === 1) ? 2 : 1, '0')).join(':') + const to = [ + (toMinute === 60) ? toHour + 1 : toHour, + (toMinute === 60) ? 0 : toMinute + ].map((i, index) => String(i).padStart((index === 1) ? 2 : 1, '0')).join(':') + + return { + weekday: fromWday, + from, + to: (to === '24:00') ? '23:59' : to + } + }) + + return hTimeset + }) + .reduce((acc, item) => [...acc, ...item], []) + + return getHumanTimesetsNormalized(hTimesetRaw) } diff --git a/src/helpers/ui.js b/src/helpers/ui.js index 66ff311b..ec8a019c 100644 --- a/src/helpers/ui.js +++ b/src/helpers/ui.js @@ -19,13 +19,24 @@ export function stopLoading () { Loading.hide() } -export function showGlobalError (message, timeout) { - Alert.create({ - html: message, - position: 'top-center', +export function showGlobalError (messageOrException, timeout = 3000) { + let errorMessage = messageOrException + if (typeof messageOrException === 'object') { + // trying to get error message from the Axios response otherwise from the error itself + errorMessage = messageOrException?.response?.data?.message || messageOrException?.message + } + if (errorMessage === '' || errorMessage === undefined || errorMessage === null) { + errorMessage = i18n.t('Unknown error') + } + return Notify.create({ + message: errorMessage, + position: 'top', + type: 'negative', + icon: 'error', + textColor: 'dark', enter: 'bounceIn', leave: 'fadeOut', - color: 'negative' + timeout }) } diff --git a/src/i18n/de.json b/src/i18n/de.json index 34f6203d..b11db62a 100644 --- a/src/i18n/de.json +++ b/src/i18n/de.json @@ -243,6 +243,7 @@ "Forward to Voicebox": "", "Forwarded to": "", "Forwarding": "", + "Fr": "", "Free": "Frei", "Friday": "Freitag", "From": "Von", @@ -311,6 +312,7 @@ "Maximum allowed extension is {max}": "", "Maximum calls in queue": "Anzahl der Anrufe", "Minimum allowed extension is {min}": "", + "Mo": "", "Monday": "Montag", "Monthly": "", "More than one {timeset} timeset exists. You can resolve this by resetting the {timeset} timesets.": "Zeitbasierte Anrufweiterleitung ist durch einen Administrator konfiguriert", @@ -332,6 +334,7 @@ "No Voicemails found": "Keine Voicemails gefunden", "No call goes to primary number": "", "No call queues created yet": "Es wurden noch keine Anrufwarteschlangen erstellt.", + "No data to save. Please provide at least one time range.": "", "No destinations created yet": "", "No devices created yet": "Noch keine Geräte angelegt", "No groups": "Keine Gruppen", @@ -421,6 +424,7 @@ "Remove sound file": "Sound löschen", "Remove sound set": "Soundset löschen", "Remove speed dial": "Kurzwahl löschen", + "Remove time range": "", "Remove voicemail": "", "Removed call queue for {callQueue} successfully": "Anrufwarteschlange für {callQueue} gelöscht", "Removed destination {destination}": "Zielrufnummer {destination} entfernen", @@ -448,6 +452,7 @@ "Ring primary number": "", "Ringing at": "Klingelt", "Ringing at {number}...": "{number} klingelt...", + "Sa": "", "Same time for all selected days": "", "Same time for selected days": "", "Saturday": "Samstag", @@ -493,15 +498,20 @@ "Sources": "Anrufer", "Sourceset": "Anruferliste", "Speed Dial": "Kurzwahl", + "Start and End time should be set": "", "Start call": "Anruf starten", "Start date": "", "Start new call": "Neuen Anruf starten", "Start time": "", + "Start time should be less than End time": "", "Station name": "Gerätename", + "Su": "", "Subscriber Sign In": "Subscriber Log-in", "Sunday": "Sonntag", "Super": "Hoch", "T38": "", + "Th": "", + "The \"{timeset}\" timeset contains incompatible values. You can resolve this by deleting it and recreating from the scratch.": "", "The Destination Email is already used": "", "The Notify Email is already used": "", "The {timeset} timeset contain reverse order of values. You can resolve this by resetting the {timeset} timeset.": "Zeitbasierte Anrufweiterleitung ist durch einen Administrator konfiguriert", @@ -517,12 +527,14 @@ "This sound set is incomplete ({amount} file is missing)": "Soundset ist unvollständig ({amount} Sound fehlt)", "This sound set is incomplete ({amount} files are missing)": "Soundset ist unvollständig ({amount} Sounds fehlen)", "Thursday": "Donnerstag", + "Time is invalid": "", "Time of the day": "Uhrzeit des Tages", "Timeout": "Klingeldauer", "To": "An", "Today": "Heute", "Toggled attachment successfully": "Voicemail als Anhang geändert", "Toggled deletion successfully": "Löschen der Voicemail erfolgreich geändert", + "Tu": "", "Tuesday": "Dienstag", "Type something": "", "Unassign": "", @@ -561,6 +573,7 @@ "Voicemail downloaded successfully": "Voicemail erfolgreich heruntergeladen", "Voicemails": "Voicemails", "WARNING: Removing the last time entry will also delete this timeset and all corresponding destinations. Are you sure you want to do this?": "Zeitbasierte Anrufweiterleitung deaktivieren?", + "We": "", "Wednesday": "Mittwoch", "Weekday": "Wochentag", "Weekly": "", @@ -577,6 +590,7 @@ "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. ": "Du bist dabei, dein Login-Passwort zu ändern. Nachdem das Passwort erfolgreich geändert wurde, wirst du automatisch abgemeldet, um dich mit dem neuen Passwort zu authentifizieren.", "You are about to delete {name} sourceset": "", "You are about to delete {name} timeset": "", + "You are about to delete time range \"{from} - {to}\"": "", "You are about to delete {destination} from {groupName} call forwarding": "", "You are about to delete {groupName} call forwarding group": "", "You are about to end the current call. Are you sure?": "Anruf beenden?", @@ -667,4 +681,4 @@ "{field} must have at most {maxLength} letters": "{field} darf höchstens {maxLength} Buchstaben beinhalten", "{mode} of sources": "{mode} of sources", "{mode} own phone": "Eigene Rufnummer {mode}" -} \ No newline at end of file +} diff --git a/src/i18n/en.json b/src/i18n/en.json index 5a1bbd35..4bd559a5 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -243,6 +243,7 @@ "Forward to Voicebox": "Forward to Voicebox", "Forwarded to": "Forwarded to", "Forwarding": "Forwarding", + "Fr": "Fr", "Free": "Free", "Friday": "Friday", "From": "From", @@ -311,6 +312,7 @@ "Maximum allowed extension is {max}": "Maximum allowed extension is {max}", "Maximum calls in queue": "Maximum calls in queue", "Minimum allowed extension is {min}": "Minimum allowed extension is {min}", + "Mo": "Mo", "Monday": "Monday", "Monthly": "Monthly", "More than one {timeset} timeset exists. You can resolve this by resetting the {timeset} timesets.": "More than one {timeset} timeset exists. You can resolve this by resetting the {timeset} timesets.", @@ -332,6 +334,7 @@ "No Voicemails found": "No Voicemails found", "No call goes to primary number": "No call goes to primary number", "No call queues created yet": "No call queues created yet", + "No data to save. Please provide at least one time range.": "No data to save. Please provide at least one time range.", "No destinations created yet": "No destinations created yet", "No devices created yet": "No devices created yet", "No groups": "No groups", @@ -421,6 +424,7 @@ "Remove sound file": "Remove sound file", "Remove sound set": "Remove sound set", "Remove speed dial": "Remove speed dial", + "Remove time range": "Remove time range", "Remove voicemail": "Remove voicemail", "Removed call queue for {callQueue} successfully": "Removed call queue for {callQueue} successfully", "Removed destination {destination}": "Removed destination {destination}", @@ -448,6 +452,7 @@ "Ring primary number": "Ring primary number", "Ringing at": "Ringing at", "Ringing at {number}...": "Ringing at {number}...", + "Sa": "Sa", "Same time for all selected days": "Same time for all selected days", "Same time for selected days": "Same time for selected days", "Saturday": "Saturday", @@ -493,15 +498,20 @@ "Sources": "Sources", "Sourceset": "Sourceset", "Speed Dial": "Speed Dial", + "Start and End time should be set": "Start and End time should be set", "Start call": "Start call", "Start date": "Start date", "Start new call": "Start new call", "Start time": "Start time", + "Start time should be less than End time": "Start time should be less than End time", "Station name": "Station name", + "Su": "Su", "Subscriber Sign In": "Subscriber Sign In", "Sunday": "Sunday", "Super": "Super", "T38": "T38", + "Th": "Th", + "The \"{timeset}\" timeset contains incompatible values. You can resolve this by deleting it and recreating from the scratch.": "The \"{timeset}\" timeset contains incompatible values. You can resolve this by deleting it and recreating from the scratch.", "The Destination Email is already used": "The Destination Email is already used", "The Notify Email is already used": "The Notify Email is already used", "The {timeset} timeset contain reverse order of values. You can resolve this by resetting the {timeset} timeset.": "The {timeset} timeset contain reverse order of values. You can resolve this by resetting the {timeset} timeset.", @@ -517,12 +527,14 @@ "This sound set is incomplete ({amount} file is missing)": "This sound set is incomplete ({amount} file is missing)", "This sound set is incomplete ({amount} files are missing)": "This sound set is incomplete ({amount} files are missing)", "Thursday": "Thursday", + "Time is invalid": "Time is invalid", "Time of the day": "Time of the day", "Timeout": "Timeout", "To": "To", "Today": "Today", "Toggled attachment successfully": "Toggled attachment successfully", "Toggled deletion successfully": "Toggled deletion successfully", + "Tu": "Tu", "Tuesday": "Tuesday", "Type something": "Type something", "Unassign": "Unassign", @@ -561,6 +573,7 @@ "Voicemail downloaded successfully": "Voicemail downloaded successfully", "Voicemails": "Voicemails", "WARNING: Removing the last time entry will also delete this timeset and all corresponding destinations. Are you sure you want to do this?": "WARNING: Removing the last time entry will also delete this timeset and all corresponding destinations. Are you sure you want to do this?", + "We": "We", "Wednesday": "Wednesday", "Weekday": "Weekday", "Weekly": "Weekly", @@ -577,6 +590,7 @@ "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. ": "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. ", "You are about to delete {name} sourceset": "You are about to delete {name} sourceset", "You are about to delete {name} timeset": "You are about to delete {name} timeset", + "You are about to delete time range \"{from} - {to}\"": "You are about to delete time range \"{from} - {to}\"", "You are about to delete {destination} from {groupName} call forwarding": "You are about to delete {destination} from {groupName} call forwarding", "You are about to delete {groupName} call forwarding group": "You are about to delete {groupName} call forwarding group", "You are about to end the current call. Are you sure?": "You are about to end the current call. Are you sure?", @@ -667,4 +681,4 @@ "{field} must have at most {maxLength} letters": "{field} must have at most {maxLength} letters", "{mode} of sources": "{mode} of sources", "{mode} own phone": "{mode} own phone" -} \ No newline at end of file +} diff --git a/src/i18n/es.json b/src/i18n/es.json index ef651e77..f420dd40 100644 --- a/src/i18n/es.json +++ b/src/i18n/es.json @@ -243,6 +243,7 @@ "Forward to Voicebox": "", "Forwarded to": "", "Forwarding": "", + "Fr": "", "Free": "Libre", "Friday": "Viernes", "From": "De", @@ -311,6 +312,7 @@ "Maximum allowed extension is {max}": "", "Maximum calls in queue": "Máximo de llamadas en cola", "Minimum allowed extension is {min}": "", + "Mo": "", "Monday": "Lunes", "Monthly": "", "More than one {timeset} timeset exists. You can resolve this by resetting the {timeset} timesets.": "Existe más de un conjunto de tiempos {timeset}. Puede resolver esto restableciendo los tiempos de {timeset}.", @@ -332,6 +334,7 @@ "No Voicemails found": "No se encontraron mensajes de voz", "No call goes to primary number": "", "No call queues created yet": "Aún no se han creado colas de llamadas", + "No data to save. Please provide at least one time range.": "", "No destinations created yet": "", "No devices created yet": "Aún no se han creado dispositivos", "No groups": "No asignado a ningún grupo", @@ -421,6 +424,7 @@ "Remove sound file": "Remover conjunto de sonido", "Remove sound set": "Remover conjunto de sonido", "Remove speed dial": "Eliminar marcación rápida", + "Remove time range": "", "Remove voicemail": "", "Removed call queue for {callQueue} successfully": "Se eliminó correctamente la cola de llamadas {callQueue}", "Removed destination {destination}": "Destino {destination} eliminado", @@ -448,6 +452,7 @@ "Ring primary number": "", "Ringing at": "Timbrando en", "Ringing at {number}...": "Timbrando ...", + "Sa": "", "Same time for all selected days": "", "Same time for selected days": "", "Saturday": "Sábado", @@ -493,15 +498,20 @@ "Sources": "Fuentes", "Sourceset": "Conjunto de fuentes", "Speed Dial": "Marcación rápida", + "Start and End time should be set": "", "Start call": "Inicio", "Start date": "", "Start new call": "Iniciar nueva llamada", "Start time": "", + "Start time should be less than End time": "", "Station name": "Nombre de la estación", + "Su": "", "Subscriber Sign In": "Iniciar sesión de suscriptor", "Sunday": "Domingo", "Super": "Super", "T38": "", + "Th": "", + "The \"{timeset}\" timeset contains incompatible values. You can resolve this by deleting it and recreating from the scratch.": "", "The Destination Email is already used": "", "The Notify Email is already used": "", "The {timeset} timeset contain reverse order of values. You can resolve this by resetting the {timeset} timeset.": "El conjunto de tiempos {timeset} contiene el orden inverso de los valores. Puede resolver esto restableciendo el conjunto de tiempos {timeset}.", @@ -517,12 +527,14 @@ "This sound set is incomplete ({amount} file is missing)": "Este conjunto de sonidos está incompleto ({amount} falta archivo)", "This sound set is incomplete ({amount} files are missing)": "Este conjunto de sonidos está incompleto ({amount} faltan archivos)", "Thursday": "Jueves", + "Time is invalid": "", "Time of the day": "Hora del día", "Timeout": "Tiempo de espera", "To": "Para", "Today": "Hoy", "Toggled attachment successfully": "Adjunto cambiado correctamente", "Toggled deletion successfully": "Eliminación cambiada correctamente", + "Tu": "", "Tuesday": "Martes", "Type something": "", "Unassign": "", @@ -561,6 +573,7 @@ "Voicemail downloaded successfully": "Correo de voz descargado con éxito", "Voicemails": "Mensajes de voz", "WARNING: Removing the last time entry will also delete this timeset and all corresponding destinations. Are you sure you want to do this?": "ADVERTENCIA: Al eliminar la última entrada también se eliminará este conjunto de horas y todos los destinos correspondientes. ¿Está seguro de querer hacer esto?", + "We": "", "Wednesday": "Miércoles", "Weekday": "Día de la semana", "Weekly": "", @@ -577,6 +590,7 @@ "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. ": "Está a punto de cambiar su contraseña de inicio de sesión. Después de que la contraseña se cambie con éxito, se cerrará la sesión automáticamente para autenticarse con la nueva contraseña.", "You are about to delete {name} sourceset": "", "You are about to delete {name} timeset": "", + "You are about to delete time range \"{from} - {to}\"": "", "You are about to delete {destination} from {groupName} call forwarding": "", "You are about to delete {groupName} call forwarding group": "", "You are about to end the current call. Are you sure?": "Está a punto de finalizar la llamada actual. ¿Está seguro?", @@ -667,4 +681,4 @@ "{field} must have at most {maxLength} letters": "{field} debe tener como máximo {maxLength} letras", "{mode} of sources": "{mode} de fuentes", "{mode} own phone": "{mode} teléfono propio" -} \ No newline at end of file +} diff --git a/src/i18n/fr.json b/src/i18n/fr.json index 45264174..25d1b303 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -243,6 +243,7 @@ "Forward to Voicebox": "", "Forwarded to": "", "Forwarding": "", + "Fr": "", "Free": "Gratuit", "Friday": "Vendredi", "From": "De", @@ -311,6 +312,7 @@ "Maximum allowed extension is {max}": "", "Maximum calls in queue": "", "Minimum allowed extension is {min}": "", + "Mo": "", "Monday": "Lundi", "Monthly": "", "More than one {timeset} timeset exists. You can resolve this by resetting the {timeset} timesets.": "Il existe déjà une plage horaire {timeset}. Vous pouvez résoudre le problème en réinitialisant la plage horaire {timeset}.", @@ -332,6 +334,7 @@ "No Voicemails found": "Message vocaux introuvables", "No call goes to primary number": "", "No call queues created yet": "Aucune file d’attente de créée", + "No data to save. Please provide at least one time range.": "", "No destinations created yet": "", "No devices created yet": "Aucun poste créé", "No groups": "Aucun groupe", @@ -421,6 +424,7 @@ "Remove sound file": "", "Remove sound set": "", "Remove speed dial": "Supprimer le raccourci", + "Remove time range": "", "Remove voicemail": "", "Removed call queue for {callQueue} successfully": "", "Removed destination {destination}": "Destination {destination} supprimée", @@ -448,6 +452,7 @@ "Ring primary number": "", "Ringing at": "Appel présenté à", "Ringing at {number}...": "{number} sonne...", + "Sa": "", "Same time for all selected days": "", "Same time for selected days": "", "Saturday": "Samedi", @@ -493,15 +498,20 @@ "Sources": "Sources", "Sourceset": "Liste de sources", "Speed Dial": "Raccourcis", + "Start and End time should be set": "", "Start call": "Lancer un appel", "Start date": "", "Start new call": "Lancer un appel", "Start time": "", + "Start time should be less than End time": "", "Station name": "Nom du poste", + "Su": "", "Subscriber Sign In": "Authentification de l’abonné", "Sunday": "Dimanche", "Super": "Supérieur", "T38": "", + "Th": "", + "The \"{timeset}\" timeset contains incompatible values. You can resolve this by deleting it and recreating from the scratch.": "", "The Destination Email is already used": "", "The Notify Email is already used": "", "The {timeset} timeset contain reverse order of values. You can resolve this by resetting the {timeset} timeset.": "L’ordre des valeurs de la plage horaire {timeset} est inversé. Vous pouvez résoudre le problème en réinitialisant la plage horaire {timeset}.", @@ -517,12 +527,14 @@ "This sound set is incomplete ({amount} file is missing)": "", "This sound set is incomplete ({amount} files are missing)": "", "Thursday": "Jeudi", + "Time is invalid": "", "Time of the day": "Heure de la journée", "Timeout": "Temporisation", "To": "A", "Today": "", "Toggled attachment successfully": "Les pièces jointes ont été activées avec succès", "Toggled deletion successfully": "La suppression a été activée avec succès", + "Tu": "", "Tuesday": "Mardi", "Type something": "", "Unassign": "", @@ -561,6 +573,7 @@ "Voicemail downloaded successfully": "Le message vocal a été téléchargé avec succès", "Voicemails": "Message vocaux", "WARNING: Removing the last time entry will also delete this timeset and all corresponding destinations. Are you sure you want to do this?": "Attention: supprimer la dernière entrée d’heure de renvoi supprimera aussi cet intervalle de temps ainsi que toutes les destinations. Êtes-vous sûr de vouloir faire ceci?", + "We": "", "Wednesday": "Mercredi", "Weekday": "Jour de la semaine", "Weekly": "", @@ -577,6 +590,7 @@ "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. ": "", "You are about to delete {name} sourceset": "", "You are about to delete {name} timeset": "", + "You are about to delete time range \"{from} - {to}\"": "", "You are about to delete {destination} from {groupName} call forwarding": "", "You are about to delete {groupName} call forwarding group": "", "You are about to end the current call. Are you sure?": "Vous êtes sur le point de raccrocher l’appel. Êtes-vous sûr?", @@ -667,4 +681,4 @@ "{field} must have at most {maxLength} letters": "{field} doit avoir au maximum {maxLength} caractères", "{mode} of sources": "Sources {mode}", "{mode} own phone": "{mode} téléphone personnel" -} \ No newline at end of file +} diff --git a/src/i18n/it.json b/src/i18n/it.json index de477a90..e43d7782 100644 --- a/src/i18n/it.json +++ b/src/i18n/it.json @@ -243,6 +243,7 @@ "Forward to Voicebox": "", "Forwarded to": "", "Forwarding": "", + "Fr": "", "Free": "Libero", "Friday": "Venerdì", "From": "Da", @@ -311,6 +312,7 @@ "Maximum allowed extension is {max}": "", "Maximum calls in queue": "Numero massimo di chiamate nella coda", "Minimum allowed extension is {min}": "", + "Mo": "", "Monday": "Lunedì", "Monthly": "", "More than one {timeset} timeset exists. You can resolve this by resetting the {timeset} timesets.": "Esistono più tabelle orarie {timeset}. Puoi risolvere il problema ripristinando le tabelle orarie {timeset}.", @@ -332,6 +334,7 @@ "No Voicemails found": "Non è stato trovato nessun Messaggio Vocale", "No call goes to primary number": "", "No call queues created yet": "Nessuna coda creata", + "No data to save. Please provide at least one time range.": "", "No destinations created yet": "", "No devices created yet": "Nessun dispositivo creato", "No groups": "Non ci sono gruppi", @@ -421,6 +424,7 @@ "Remove sound file": "Rimuovi messaggio", "Remove sound set": "Rimuovi set messaggi", "Remove speed dial": "Rimuovi selezione rapida", + "Remove time range": "", "Remove voicemail": "", "Removed call queue for {callQueue} successfully": "Coda per {callQueue} rimossa con successo", "Removed destination {destination}": "Destinazione {destination} rimossa", @@ -448,6 +452,7 @@ "Ring primary number": "", "Ringing at": "Sta squillando", "Ringing at {number}...": "Sta squillando {number}...", + "Sa": "", "Same time for all selected days": "", "Same time for selected days": "", "Saturday": "Sabato", @@ -493,15 +498,20 @@ "Sources": "Sorgenti", "Sourceset": "Set di pattern sorgente", "Speed Dial": "Selezione Rapida", + "Start and End time should be set": "", "Start call": "Inizia chiamata", "Start date": "", "Start new call": "Inizia nuova chiamata", "Start time": "", + "Start time should be less than End time": "", "Station name": "Nome stazione", + "Su": "", "Subscriber Sign In": "Accedi come utente", "Sunday": "Domenica", "Super": "Super", "T38": "", + "Th": "", + "The \"{timeset}\" timeset contains incompatible values. You can resolve this by deleting it and recreating from the scratch.": "", "The Destination Email is already used": "", "The Notify Email is already used": "", "The {timeset} timeset contain reverse order of values. You can resolve this by resetting the {timeset} timeset.": "La tabella oraria {timeset} contiene valori in ordine inverso. Puoi risolvere il problema ripristinando la tabella oraria {timeset}.", @@ -517,12 +527,14 @@ "This sound set is incomplete ({amount} file is missing)": "Questo set di messaggi è incompleto ({amount} files mancanti)", "This sound set is incomplete ({amount} files are missing)": "Questo set di messaggi è incompleto ({amount} files mancanti)", "Thursday": "Giovedì", + "Time is invalid": "", "Time of the day": "Ora del giorno", "Timeout": "Timeout", "To": "A", "Today": "Oggi", "Toggled attachment successfully": "Opzione allegato modificata con successo", "Toggled deletion successfully": "Cancellazione modificata con successo", + "Tu": "", "Tuesday": "Martedì", "Type something": "", "Unassign": "", @@ -561,6 +573,7 @@ "Voicemail downloaded successfully": "Messaggio vocale scaricato con successo", "Voicemails": "Messaggi Vocali", "WARNING: Removing the last time entry will also delete this timeset and all corresponding destinations. Are you sure you want to do this?": "ATTENZIONE: Rimuovendo l'ultimo orario si cancellerà l'intera tabella oraria e tutte le destinazioni corrispondenti. Sei sicuro di volerlo fare?", + "We": "", "Wednesday": "Mercoledì", "Weekday": "Giorno della Settimana", "Weekly": "", @@ -577,6 +590,7 @@ "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. ": "Stai per modificare la tua password di login. Una volta che la password sarà modificata, verrai automaticamente disconnesso e dovrai accedere con la nuova password. ", "You are about to delete {name} sourceset": "", "You are about to delete {name} timeset": "", + "You are about to delete time range \"{from} - {to}\"": "", "You are about to delete {destination} from {groupName} call forwarding": "", "You are about to delete {groupName} call forwarding group": "", "You are about to end the current call. Are you sure?": "Stai per terminare la chiamata in corso. Sei sicuro?", @@ -667,4 +681,4 @@ "{field} must have at most {maxLength} letters": "Il campo {field} può contenere al massimo {maxLength} lettere", "{mode} of sources": "{mode} dei pattern sorgente", "{mode} own phone": "{mode} proprio telefono" -} \ No newline at end of file +} diff --git a/src/store/call-forwarding/actions.js b/src/store/call-forwarding/actions.js index bc594914..a1a99961 100644 --- a/src/store/call-forwarding/actions.js +++ b/src/store/call-forwarding/actions.js @@ -1,5 +1,5 @@ import { - cfCreateOfficeHours, cfCreateOfficeHoursSameTimes, + cfCreateOfficeHours, cfCreateSourceSet, cfCreateTimeSetDate, cfCreateTimeSetDateRange, @@ -10,7 +10,7 @@ import { cfLoadDestinationSets, cfLoadMappingsFull, cfLoadSourceSets, - cfLoadTimeSets, cfUpdateOfficeHours, cfUpdateOfficeHoursSameTimes, + cfLoadTimeSets, cfUpdateOfficeHours, cfUpdateSourceSet, cfUpdateTimeSetDate, cfUpdateTimeSetDateRange, @@ -505,42 +505,6 @@ export async function updateOfficeHours ({ dispatch, commit, rootGetters, state dispatch('wait/end', 'csc-cf-time-set-create', { root: true }) } -export async function createOfficeHoursSameTimes ({ dispatch, commit, rootGetters, state }, payload) { - dispatch('wait/start', 'csc-cf-time-set-create', { root: true }) - const timeSetId = await cfCreateOfficeHoursSameTimes( - rootGetters['user/getSubscriberId'], - payload.times, - payload.weekdays - ) - const updatedMapping = _.cloneDeep(state.mappings[payload.mapping.type]) - updatedMapping[payload.mapping.index].timeset_id = timeSetId - const updatedMappings = await patchReplaceFull({ - resource: 'cfmappings', - resourceId: rootGetters['user/getSubscriberId'], - fieldPath: payload.mapping.type, - value: updatedMapping - }) - if (payload.id) { - await cfDeleteTimeSet(payload.id) - } - const timeSets = await cfLoadTimeSets(rootGetters['user/getSubscriberId']) - commit('dataSucceeded', { - mappings: updatedMappings, - timeSets: timeSets.items - }) - dispatch('wait/end', 'csc-cf-time-set-create', { root: true }) -} - -export async function updateOfficeHoursSameTimes ({ dispatch, commit, rootGetters, state }, payload) { - dispatch('wait/start', 'csc-cf-time-set-create', { root: true }) - await cfUpdateOfficeHoursSameTimes(payload.id, payload.times, payload.weekdays) - const timeSets = await cfLoadTimeSets(rootGetters['user/getSubscriberId']) - commit('dataSucceeded', { - timeSets: timeSets.items - }) - dispatch('wait/end', 'csc-cf-time-set-create', { root: true }) -} - export async function loadAnnouncements ({ dispatch, commit }) { try { const announcements = await getList({