Compare commits

...
Author SHA1 Message Date
Muhsin Keloth 5343b3f8ae spec cleanup 2024-03-15 00:26:07 +05:30
Muhsin Keloth 11ae25535a code cleanup 2024-03-15 00:14:36 +05:30
Muhsin Keloth d8708e5ee5 Update availabilityHelper.js 2024-03-14 23:45:26 +05:30
Muhsin Keloth 3efdd446a9 Update availabilityHelper.js 2024-03-14 23:41:43 +05:30
Muhsin Keloth 85d970754b cleanup 2024-03-14 23:38:21 +05:30
Muhsin Keloth 7cc6adbd9e feat: add availabilityHelper 2024-03-14 15:16:23 +05:30
Muhsin Keloth 6cad9c683a add helper 2024-03-14 15:13:09 +05:30
3 changed files with 326 additions and 4 deletions
@@ -0,0 +1,150 @@
import { utcToZonedTime } from 'date-fns-tz';
// Array defining days of the week
const daysOfWeek = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
/**
* Find the next available time for the specified day
* @param {Object[]} workingHours - Array of objects representing working hours for each day
* @param {number} dayOfWeek - Index of the day of the week (0 for Sunday, 1 for Monday, etc.)
* @param {number} currentHour - Current hour of the day (24-hour format)
* @param {number} currentMinutes - Current minutes of the hour
* @returns {Object|null} - Object containing next available time information or null if not available
*/
const findNextAvailableTimeForDay = (
workingHours,
dayOfWeek,
currentHour,
currentMinutes
) => {
const availableHours = workingHours.find(
hours =>
hours.day_of_week === dayOfWeek &&
!hours.closed_all_day &&
(currentHour < hours.close_hour ||
(currentHour === hours.close_hour &&
currentMinutes < hours.close_minutes))
);
if (availableHours) {
return {
hour: availableHours.open_hour,
minutes: availableHours.open_minutes,
};
}
return null;
};
/**
* Find the next available time across days
* @param {Object[]} workingHours - Array of objects representing working hours for each day
* @param {number} currentDayOfWeek - Index of the current day of the week (0 for Sunday, 1 for Monday, etc.)
* @returns {Object|null} - Object containing next available time information or null if not available
*/
const findNextAvailableTimeAcrossDays = (workingHours, currentDayOfWeek) => {
const nextAvailableDays = workingHours.filter(hours => !hours.closed_all_day);
for (let i = 0; i < 7; i += 1) {
const nextDayIndex = (currentDayOfWeek + 1 + i) % 7;
const nextDay = nextAvailableDays.find(
hours => hours.day_of_week === nextDayIndex
);
if (nextDay) {
return {
hour: nextDay.open_hour,
minutes: nextDay.open_minutes,
day: nextDayIndex,
};
}
}
return null;
};
/**
* Function to find the next available time
* @param {Array} workingHours - Array of working hours for each day
* @param {number} currentDayOfWeek - Index of the current day of the week (0 for Sunday, 1 for Monday, etc.)
* @param {number} currentHour - Current hour of the day (24-hour format)
* @param {number} currentMinutes - Current minutes of the hour
* @returns {Object|null} - Object containing next available time information or null if not available
*/
const findNextAvailableTime = (
workingHours,
currentDayOfWeek,
currentHour,
currentMinutes
) => {
const nextAvailableTimeToday = findNextAvailableTimeForDay(
workingHours,
currentDayOfWeek,
currentHour,
currentMinutes
);
if (nextAvailableTimeToday) {
return { ...nextAvailableTimeToday, day: 'today' };
}
return findNextAvailableTimeAcrossDays(workingHours, currentDayOfWeek);
};
/**
* Function to get the next availability message
* @param {Array} workingHours - Array of working hours for each day
* @param {Date} currentTime - Current time object
* @returns {string} - Next availability message
*/
export const getNextAvailabilityMessage = (
workingHours,
currentTime,
timezone
) => {
const utcZonedTime = utcToZonedTime(currentTime, timezone);
const currentDayOfWeek = utcZonedTime.getDay();
const currentHour = utcZonedTime.getHours();
const currentMinutes = utcZonedTime.getMinutes();
const nextAvailableTime = findNextAvailableTime(
workingHours,
currentDayOfWeek,
currentHour,
currentMinutes
);
// Calculate the time difference
if (nextAvailableTime !== null) {
const differenceInMinutes =
nextAvailableTime.hour * 60 +
nextAvailableTime.minutes -
(currentHour * 60 + currentMinutes);
const differenceInHours = Math.floor(differenceInMinutes / 60);
// Check if the next available time is tomorrow
if (nextAvailableTime.day === (currentDayOfWeek + 1) % 7) {
return `We will be back online tomorrow`;
}
// Check if the next available time is on a specific day
if (nextAvailableTime.day !== 'today') {
return `We will be back online on ${daysOfWeek[nextAvailableTime.day]}`;
}
// Generate appropriate message based on time difference
if (differenceInMinutes <= 1) {
return 'We will be back online in less than a minute.';
}
if (differenceInMinutes < 60) {
return `We will be back online in ${differenceInMinutes} minutes.`;
}
if (differenceInMinutes < 1440) {
return `We will be back online in ${differenceInHours} hours`;
}
}
return 'No available time found in working hours.';
};
@@ -0,0 +1,169 @@
import { getNextAvailabilityMessage } from '../availabilityHelper';
const workingHoursCommon = [
{ id: 8, inbox_id: 2, account_id: 1, day_of_week: 0, closed_all_day: true },
{
id: 9,
inbox_id: 2,
account_id: 1,
day_of_week: 1,
closed_all_day: false,
open_hour: 9,
open_minutes: 0,
close_hour: 17,
close_minutes: 0,
},
{
id: 10,
inbox_id: 2,
account_id: 1,
day_of_week: 2,
closed_all_day: true,
open_hour: 15,
open_minutes: 0,
close_hour: 23,
close_minutes: 30,
},
{ id: 11, inbox_id: 2, account_id: 1, day_of_week: 3, closed_all_day: true },
{
id: 12,
inbox_id: 2,
account_id: 1,
day_of_week: 4,
closed_all_day: false,
open_hour: 15,
open_minutes: 0,
close_hour: 20,
close_minutes: 0,
},
{
id: 13,
inbox_id: 2,
account_id: 1,
day_of_week: 5,
closed_all_day: false,
open_hour: 9,
open_minutes: 0,
close_hour: 17,
close_minutes: 0,
},
{
id: 14,
inbox_id: 2,
account_id: 1,
day_of_week: 6,
closed_all_day: false,
open_hour: 9,
open_minutes: 0,
close_hour: 7,
close_minutes: 0,
},
];
describe('getNextAvailabilityMessage without timezone', () => {
let workingHours;
beforeEach(() => {
workingHours = JSON.parse(JSON.stringify(workingHoursCommon));
});
test('less than a minute', () =>
expect(
getNextAvailabilityMessage(workingHours, new Date('2024-03-14T14:59:59'))
).toBe('We will be back online in less than a minute.'));
test('less than 10 minutes', () =>
expect(
getNextAvailabilityMessage(workingHours, new Date('2024-03-14T14:50:00'))
).toBe('We will be back online in 10 minutes.'));
test('minutes message for less than an hour', () =>
expect(
getNextAvailabilityMessage(workingHours, new Date('2024-03-14T14:45:00'))
).toBe('We will be back online in 15 minutes.'));
test('greater than 2 hours', () =>
expect(
getNextAvailabilityMessage(workingHours, new Date('2024-03-14T12:30:00'))
).toBe('We will be back online in 2 hours'));
test('next day', () => {
workingHours[4].closed_all_day = true;
expect(
getNextAvailabilityMessage(workingHours, new Date('2024-03-14T00:00:00'))
).toBe('We will be back online tomorrow');
});
test('specific day', () => {
workingHours[4].closed_all_day = true;
workingHours[5].closed_all_day = true;
expect(
getNextAvailabilityMessage(workingHours, new Date('2024-03-14T00:00:00'))
).toBe('We will be back online on Saturday');
});
test('next week', () => {
workingHours[5].closed_all_day = true;
workingHours[6].closed_all_day = true;
workingHours[0].closed_all_day = true;
workingHours[1].closed_all_day = true;
workingHours[2].closed_all_day = true;
workingHours[3].closed_all_day = true;
workingHours[4].closed_all_day = false;
expect(
getNextAvailabilityMessage(workingHours, new Date('2024-03-21T21:00:00'))
).toBe('We will be back online on Thursday');
});
});
describe('getNextAvailabilityMessage with timezone', () => {
let workingHours;
beforeEach(() => {
workingHours = JSON.parse(JSON.stringify(workingHoursCommon));
jest.useFakeTimers('modern');
jest.setSystemTime(new Date('2024-03-14T19:50:00'));
});
afterEach(() => jest.useRealTimers());
test('less than a minute', () =>
expect(
getNextAvailabilityMessage(
workingHours,
new Date('2024-03-14T19:50:00'),
'America/New_York'
)
).toBe('We will be back online in less than a minute.'));
test('less than 10 minutes', () =>
expect(
getNextAvailabilityMessage(
workingHours,
new Date('2024-03-14T19:50:00'),
'America/New_York'
)
).toBe('We will be back online in less than a minute.'));
test('greater than 2 hours', () =>
expect(
getNextAvailabilityMessage(
workingHours,
new Date('2024-03-14T14:59:59'),
'America/New_York'
)
).toBe('We will be back online in 4 hours'));
test('next day', () => {
workingHours[4].closed_all_day = true;
expect(
getNextAvailabilityMessage(
workingHours,
new Date('2024-03-15T00:00:00'),
'America/New_York'
)
).toBe('We will be back online tomorrow');
});
test('specific day', () => {
workingHours[4].closed_all_day = true;
workingHours[5].closed_all_day = true;
expect(
getNextAvailabilityMessage(
workingHours,
new Date('2024-03-17T00:00:00'),
'America/New_York'
)
).toBe('We will be back online on Monday');
});
});
+7 -4
View File
@@ -1,6 +1,6 @@
import { utcToZonedTime } from 'date-fns-tz';
import { isTimeAfter } from 'shared/helpers/DateHelper';
import { getNextAvailabilityMessage } from 'widget/helpers/availabilityHelper';
export default {
computed: {
channelConfig() {
@@ -23,10 +23,13 @@ export default {
},
replyWaitMessage() {
const { workingHoursEnabled } = this.channelConfig;
const nextAvailabilityMessage = getNextAvailabilityMessage(
this.channelConfig.workingHours,
new Date(),
this.channelConfig.timezone
);
if (workingHoursEnabled) {
return this.isOnline
? this.replyTimeStatus
: `${this.$t('REPLY_TIME.BACK_IN')} ${this.timeLeftToBackInOnline}`;
return this.isOnline ? this.replyTimeStatus : nextAvailabilityMessage;
}
return this.isOnline
? this.replyTimeStatus