fix: Update business hour calculation (#4496)
This commit is contained in:
parent
e010f0c6f0
commit
17fb6b8d55
7 changed files with 125 additions and 46 deletions
|
@ -2,7 +2,6 @@ import fromUnixTime from 'date-fns/fromUnixTime';
|
||||||
import format from 'date-fns/format';
|
import format from 'date-fns/format';
|
||||||
import isToday from 'date-fns/isToday';
|
import isToday from 'date-fns/isToday';
|
||||||
import isYesterday from 'date-fns/isYesterday';
|
import isYesterday from 'date-fns/isYesterday';
|
||||||
import parseISO from 'date-fns/parseISO';
|
|
||||||
|
|
||||||
export const formatUnixDate = (date, dateFormat = 'MMM dd, yyyy') => {
|
export const formatUnixDate = (date, dateFormat = 'MMM dd, yyyy') => {
|
||||||
const unixDate = fromUnixTime(date);
|
const unixDate = fromUnixTime(date);
|
||||||
|
@ -20,10 +19,14 @@ export const formatDigitToString = val => {
|
||||||
return val > 9 ? `${val}` : `0${val}`;
|
return val > 9 ? `${val}` : `0${val}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const buildDateFromTime = (hr, min, utcOffset, date = new Date()) => {
|
export const isTimeAfter = (h1, m1, h2, m2) => {
|
||||||
const today = format(date, 'yyyy-MM-dd');
|
if (h1 < h2) {
|
||||||
const hour = formatDigitToString(hr);
|
return false;
|
||||||
const minute = formatDigitToString(min);
|
}
|
||||||
const timeString = `${today}T${hour}:${minute}:00${utcOffset}`;
|
|
||||||
return parseISO(timeString);
|
if (h1 === h2) {
|
||||||
|
return m1 >= m2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import {
|
import {
|
||||||
formatDate,
|
formatDate,
|
||||||
formatUnixDate,
|
formatUnixDate,
|
||||||
buildDateFromTime,
|
|
||||||
formatDigitToString,
|
formatDigitToString,
|
||||||
|
isTimeAfter,
|
||||||
} from '../DateHelper';
|
} from '../DateHelper';
|
||||||
|
|
||||||
describe('#DateHelper', () => {
|
describe('#DateHelper', () => {
|
||||||
|
@ -44,27 +44,21 @@ describe('#DateHelper', () => {
|
||||||
})
|
})
|
||||||
).toEqual('Yesterday');
|
).toEqual('Yesterday');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
describe('#buildDate', () => {
|
describe('#formatDigitToString', () => {
|
||||||
it('returns correctly parsed date', () => {
|
|
||||||
const date = new Date();
|
|
||||||
date.setFullYear(2021);
|
|
||||||
date.setMonth(2);
|
|
||||||
date.setDate(5);
|
|
||||||
|
|
||||||
const result = buildDateFromTime(12, 15, '.465Z', date);
|
|
||||||
expect(result + '').toEqual(
|
|
||||||
'Fri Mar 05 2021 12:15:00 GMT+0000 (Coordinated Universal Time)'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#formatDigitToString', () => {
|
|
||||||
it('returns date compatabile string from number is less than 9', () => {
|
it('returns date compatabile string from number is less than 9', () => {
|
||||||
expect(formatDigitToString(8)).toEqual('08');
|
expect(formatDigitToString(8)).toEqual('08');
|
||||||
});
|
});
|
||||||
it('returns date compatabile string from number is greater than 9', () => {
|
it('returns date compatabile string from number is greater than 9', () => {
|
||||||
expect(formatDigitToString(11)).toEqual('11');
|
expect(formatDigitToString(11)).toEqual('11');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#isTimeAfter', () => {
|
||||||
|
it('return correct values', () => {
|
||||||
|
expect(isTimeAfter(5, 30, 9, 30)).toEqual(false);
|
||||||
|
expect(isTimeAfter(9, 30, 9, 30)).toEqual(true);
|
||||||
|
expect(isTimeAfter(9, 29, 9, 30)).toEqual(false);
|
||||||
|
expect(isTimeAfter(11, 59, 12, 0)).toEqual(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -58,6 +58,7 @@ export default {
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
widgetColor: 'appConfig/getWidgetColor',
|
widgetColor: 'appConfig/getWidgetColor',
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import compareAsc from 'date-fns/compareAsc';
|
import { utcToZonedTime } from 'date-fns-tz';
|
||||||
import { buildDateFromTime } from 'shared/helpers/DateHelper';
|
import { isTimeAfter } from 'shared/helpers/DateHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -33,23 +33,32 @@ export default {
|
||||||
closedAllDay,
|
closedAllDay,
|
||||||
openAllDay,
|
openAllDay,
|
||||||
} = this.currentDayAvailability;
|
} = this.currentDayAvailability;
|
||||||
|
|
||||||
|
if (openAllDay || closedAllDay) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const { utcOffset } = this.channelConfig;
|
const { utcOffset } = this.channelConfig;
|
||||||
|
const today = this.getDateWithOffset(utcOffset);
|
||||||
if (openAllDay) return true;
|
const currentHours = today.getHours();
|
||||||
|
const currentMinutes = today.getMinutes();
|
||||||
if (closedAllDay) return false;
|
const isAfterStartTime = isTimeAfter(
|
||||||
|
currentHours,
|
||||||
const startTime = buildDateFromTime(openHour, openMinute, utcOffset);
|
currentMinutes,
|
||||||
const endTime = buildDateFromTime(closeHour, closeMinute, utcOffset);
|
openHour,
|
||||||
const isBetween =
|
openMinute
|
||||||
compareAsc(new Date(), startTime) === 1 &&
|
);
|
||||||
compareAsc(endTime, new Date()) === 1;
|
const isBeforeEndTime = isTimeAfter(
|
||||||
|
closeHour,
|
||||||
if (isBetween) return true;
|
closeMinute,
|
||||||
return false;
|
currentHours,
|
||||||
|
currentMinutes
|
||||||
|
);
|
||||||
|
return isAfterStartTime && isBeforeEndTime;
|
||||||
},
|
},
|
||||||
currentDayAvailability() {
|
currentDayAvailability() {
|
||||||
const dayOfTheWeek = new Date().getDay();
|
const { utcOffset } = this.channelConfig;
|
||||||
|
const dayOfTheWeek = this.getDateWithOffset(utcOffset).getDay();
|
||||||
const [workingHourConfig = {}] = this.channelConfig.workingHours.filter(
|
const [workingHourConfig = {}] = this.channelConfig.workingHours.filter(
|
||||||
workingHour => workingHour.day_of_week === dayOfTheWeek
|
workingHour => workingHour.day_of_week === dayOfTheWeek
|
||||||
);
|
);
|
||||||
|
@ -63,8 +72,14 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
isInBusinessHours() {
|
isInBusinessHours() {
|
||||||
const { workingHoursEnabled } = window.chatwootWebChannel;
|
const { workingHoursEnabled } = this.channelConfig;
|
||||||
return workingHoursEnabled ? this.isInBetweenTheWorkingHours : true;
|
return workingHoursEnabled ? this.isInBetweenTheWorkingHours : true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
getDateWithOffset(utcOffset) {
|
||||||
|
return utcToZonedTime(new Date().toISOString(), utcOffset);
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
60
app/javascript/widget/mixins/specs/availabilityMixin.spec.js
Normal file
60
app/javascript/widget/mixins/specs/availabilityMixin.spec.js
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import { createWrapper } from '@vue/test-utils';
|
||||||
|
import availabilityMixin from '../availability';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
global.chatwootWebChannel = {
|
||||||
|
workingHoursEnabled: true,
|
||||||
|
workingHours: [
|
||||||
|
{
|
||||||
|
day_of_week: 3,
|
||||||
|
closed_all_day: false,
|
||||||
|
open_hour: 8,
|
||||||
|
open_minutes: 30,
|
||||||
|
close_hour: 17,
|
||||||
|
close_minutes: 35,
|
||||||
|
open_all_day: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day_of_week: 4,
|
||||||
|
closed_all_day: false,
|
||||||
|
open_hour: 8,
|
||||||
|
open_minutes: 30,
|
||||||
|
close_hour: 17,
|
||||||
|
close_minutes: 30,
|
||||||
|
open_all_day: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
utcOffset: '-07:00',
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('availabilityMixin', () => {
|
||||||
|
it('returns valid isInBetweenWorkingHours if in different timezone', () => {
|
||||||
|
const Component = {
|
||||||
|
render() {},
|
||||||
|
mixins: [availabilityMixin],
|
||||||
|
};
|
||||||
|
jest
|
||||||
|
.useFakeTimers('modern')
|
||||||
|
.setSystemTime(new Date('Thu Apr 14 2022 06:04:46 GMT+0530'));
|
||||||
|
const Constructor = Vue.extend(Component);
|
||||||
|
const vm = new Constructor().$mount();
|
||||||
|
const wrapper = createWrapper(vm);
|
||||||
|
expect(wrapper.vm.isInBetweenTheWorkingHours).toBe(true);
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns valid isInBetweenWorkingHours if in same timezone', () => {
|
||||||
|
global.chatwootWebChannel.utcOffset = '+05:30';
|
||||||
|
const Component = {
|
||||||
|
render() {},
|
||||||
|
mixins: [availabilityMixin],
|
||||||
|
};
|
||||||
|
jest
|
||||||
|
.useFakeTimers('modern')
|
||||||
|
.setSystemTime(new Date('Thu Apr 14 2022 09:01:46 GMT+0530'));
|
||||||
|
const Constructor = Vue.extend(Component);
|
||||||
|
const wrapper = createWrapper(new Constructor().$mount());
|
||||||
|
expect(wrapper.vm.isInBetweenTheWorkingHours).toBe(true);
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
});
|
|
@ -35,6 +35,7 @@
|
||||||
"core-js": "3.11.0",
|
"core-js": "3.11.0",
|
||||||
"country-code-emoji": "^1.0.0",
|
"country-code-emoji": "^1.0.0",
|
||||||
"date-fns": "2.21.1",
|
"date-fns": "2.21.1",
|
||||||
|
"date-fns-tz": "^1.3.3",
|
||||||
"dompurify": "2.2.7",
|
"dompurify": "2.2.7",
|
||||||
"dotenv": "^8.0.0",
|
"dotenv": "^8.0.0",
|
||||||
"foundation-sites": "~6.5.3",
|
"foundation-sites": "~6.5.3",
|
||||||
|
|
|
@ -5454,6 +5454,11 @@ data-urls@^2.0.0:
|
||||||
whatwg-mimetype "^2.3.0"
|
whatwg-mimetype "^2.3.0"
|
||||||
whatwg-url "^8.0.0"
|
whatwg-url "^8.0.0"
|
||||||
|
|
||||||
|
date-fns-tz@^1.3.3:
|
||||||
|
version "1.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-fns-tz/-/date-fns-tz-1.3.3.tgz#7884a4b3ed6cd95bfd81831d608e5ef8be500c86"
|
||||||
|
integrity sha512-Gks46gwbSauBQnV3Oofluj1wTm8J0tM7sbSJ9P+cJq/ZnTCpMohTKmmO5Tn+jQ7dyn0+b8G7cY4O2DZ5P/LXcA==
|
||||||
|
|
||||||
date-fns@2.21.1:
|
date-fns@2.21.1:
|
||||||
version "2.21.1"
|
version "2.21.1"
|
||||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.21.1.tgz#679a4ccaa584c0706ea70b3fa92262ac3009d2b0"
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.21.1.tgz#679a4ccaa584c0706ea70b3fa92262ac3009d2b0"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue