cronofy-elements 1.40.2 → 1.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.yaml +31 -30
- package/build/CronofyElements.v1.43.0.js +2 -0
- package/build/{CronofyElements.v1.40.2.js.LICENSE.txt → CronofyElements.v1.43.0.js.LICENSE.txt} +0 -0
- package/build/npm/CronofyElements.js +2 -2
- package/demo/date-time-picker.ejs +74 -0
- package/package.json +1 -1
- package/src/js/components/DateTimePicker/CalendarHeader.js +21 -14
- package/src/js/components/DateTimePicker/DateTimePicker.js +34 -10
- package/src/js/components/DateTimePicker/Details.js +5 -3
- package/src/js/components/DateTimePicker/LoadingCalendar.js +21 -0
- package/src/js/components/DateTimePicker/SequencedSlotButton.js +71 -0
- package/src/js/components/DateTimePicker/SlotsList.js +6 -1
- package/src/js/components/DateTimePicker/Wrapper.js +78 -23
- package/src/js/components/DateTimePicker/contexts/status-reducer.js +69 -28
- package/src/js/components/DateTimePicker/scss/_components.loading.scss +10 -0
- package/src/js/components/DateTimePicker/utils/calendar.js +23 -0
- package/src/js/components/DateTimePicker/utils/slots.js +115 -5
- package/src/js/helpers/connections.js +38 -0
- package/src/js/helpers/init.DateTimePicker.js +65 -2
- package/src/js/translations/en/date_time_picker.json +1 -0
- package/tests/DateTimePicker/SequencedSlotButton.test.js +130 -0
- package/tests/DateTimePicker/contexts/status-reducer.test.js +497 -187
- package/tests/DateTimePicker/dummy-data.js +277 -0
- package/tests/DateTimePicker/utils.test.js +53 -1
- package/build/CronofyElements.v1.40.2.js +0 -2
|
@@ -64,10 +64,83 @@
|
|
|
64
64
|
return output.utc().format();
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
+
const sequencedAvailabilityQuery = {
|
|
68
|
+
sequence: [
|
|
69
|
+
{
|
|
70
|
+
sequence_id: "test",
|
|
71
|
+
ordinal: 1,
|
|
72
|
+
participants: [
|
|
73
|
+
{
|
|
74
|
+
required: "all",
|
|
75
|
+
members: [
|
|
76
|
+
{
|
|
77
|
+
sub: "<%= sub %>",
|
|
78
|
+
// managed_availability: true,
|
|
79
|
+
// availability_rule_ids: ["weekly_work_hours"]
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
required_duration: { minutes: 5 },
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
sequence_id: "test-1",
|
|
88
|
+
ordinal: 2,
|
|
89
|
+
participants: [
|
|
90
|
+
{
|
|
91
|
+
required: "all",
|
|
92
|
+
members: [
|
|
93
|
+
{
|
|
94
|
+
sub: "<%= sub %>",
|
|
95
|
+
// managed_availability: true,
|
|
96
|
+
// availability_rule_ids: ["weekly_work_hours"]
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
required_duration: { minutes: 45 },
|
|
102
|
+
start_interval: { minutes: 5 },
|
|
103
|
+
buffer: {
|
|
104
|
+
before: {
|
|
105
|
+
minimum: { minutes: 15 }
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
sequence_id: "test-2",
|
|
111
|
+
ordinal: 3,
|
|
112
|
+
participants: [
|
|
113
|
+
{
|
|
114
|
+
required: "all",
|
|
115
|
+
members: [
|
|
116
|
+
{
|
|
117
|
+
sub: "<%= sub %>",
|
|
118
|
+
// managed_availability: true,
|
|
119
|
+
// availability_rule_ids: ["weekly_work_hours"]
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
],
|
|
124
|
+
required_duration: { minutes: 30 },
|
|
125
|
+
start_interval: { minutes: 10 },
|
|
126
|
+
buffer: {
|
|
127
|
+
before: {
|
|
128
|
+
minimum: { minutes: 15 }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
],
|
|
134
|
+
query_periods: [
|
|
135
|
+
{ start: slotTimes(01,"08:00"), end: slotTimes(31,"17:00") }
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
|
|
67
139
|
CronofyElements.DateTimePicker({
|
|
68
140
|
element_token: "<%= date_time_picker_token %>",
|
|
69
141
|
target_id: 'cronofy-date-time-picker',
|
|
70
142
|
api_domain:"<%= api_domain %>",
|
|
143
|
+
//availability_query: sequencedAvailabilityQuery,
|
|
71
144
|
availability_query: {
|
|
72
145
|
// response_format: "slots",
|
|
73
146
|
participants: [
|
|
@@ -102,6 +175,7 @@
|
|
|
102
175
|
},
|
|
103
176
|
config: {
|
|
104
177
|
logs: 'info',
|
|
178
|
+
//selected_date: "2022-05-15"
|
|
105
179
|
//mode: 'no_confirm'
|
|
106
180
|
// week_start_day: "tuesday"
|
|
107
181
|
// tz_list: [
|
package/package.json
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import React
|
|
2
|
-
|
|
3
|
-
import { calculateMonthNav } from "./utils/calendar";
|
|
1
|
+
import React from "react";
|
|
2
|
+
import moment from "moment-timezone";
|
|
4
3
|
|
|
5
4
|
import { useI18n } from "../../contexts/i18n-context";
|
|
6
5
|
import { useStatus } from "./contexts/status-context";
|
|
@@ -13,19 +12,27 @@ const CalendarHeader = () => {
|
|
|
13
12
|
const theme = useTheme();
|
|
14
13
|
const [tz] = useTz();
|
|
15
14
|
|
|
16
|
-
const
|
|
15
|
+
const handlePrevMonth = () => {
|
|
16
|
+
const month = moment(status.monthlyView.month, "YYYY-MM")
|
|
17
|
+
.subtract(1, "month")
|
|
18
|
+
.format("YYYY-MM");
|
|
17
19
|
|
|
18
|
-
const handleMonthNav = target => {
|
|
19
20
|
dispatchStatus({
|
|
20
21
|
type: "SELECT_MONTH",
|
|
21
|
-
month
|
|
22
|
+
month,
|
|
22
23
|
tzid: tz.selectedTzid.tzid,
|
|
23
24
|
});
|
|
24
25
|
};
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
const handleNextMonth = () => {
|
|
28
|
+
const month = moment(status.monthlyView.month, "YYYY-MM").add(1, "month").format("YYYY-MM");
|
|
29
|
+
|
|
30
|
+
dispatchStatus({
|
|
31
|
+
type: "SELECT_MONTH",
|
|
32
|
+
month,
|
|
33
|
+
tzid: tz.selectedTzid.tzid,
|
|
34
|
+
});
|
|
35
|
+
};
|
|
29
36
|
|
|
30
37
|
return (
|
|
31
38
|
<div className={theme.classBuilder("calendar-header")}>
|
|
@@ -33,8 +40,8 @@ const CalendarHeader = () => {
|
|
|
33
40
|
type="button"
|
|
34
41
|
className={theme.classBuilder("calendar-header-button")}
|
|
35
42
|
aria-label={i18n.t("nav_previous_month")}
|
|
36
|
-
disabled={!
|
|
37
|
-
onClick={
|
|
43
|
+
disabled={!status.monthlyView.hasPrev}
|
|
44
|
+
onClick={handlePrevMonth}
|
|
38
45
|
>
|
|
39
46
|
<svg
|
|
40
47
|
className={theme.classBuilder(
|
|
@@ -47,14 +54,14 @@ const CalendarHeader = () => {
|
|
|
47
54
|
</svg>
|
|
48
55
|
</button>
|
|
49
56
|
<h2 className={theme.classBuilder("calendar-header--title")} id="calendar-title">
|
|
50
|
-
{i18n.f(
|
|
57
|
+
{i18n.f(moment(status.monthlyView.month, "YYYY-MM"), "MMMM YYYY")}
|
|
51
58
|
</h2>
|
|
52
59
|
<button
|
|
53
60
|
type="button"
|
|
54
61
|
className={theme.classBuilder("calendar-header-button")}
|
|
55
62
|
aria-label={i18n.t("nav_next_month")}
|
|
56
|
-
disabled={!
|
|
57
|
-
onClick={
|
|
63
|
+
disabled={!status.monthlyView.hasNext}
|
|
64
|
+
onClick={handleNextMonth}
|
|
58
65
|
>
|
|
59
66
|
<svg
|
|
60
67
|
className={theme.classBuilder(
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
2
|
import moment from "moment-timezone";
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
getMonthObjectsFromQuery,
|
|
6
|
-
parseQuery,
|
|
7
6
|
parseTzList,
|
|
8
7
|
getInitialSelectedTzid,
|
|
8
|
+
getMonthsLoadingFromQuery,
|
|
9
|
+
getDurationFromQuery,
|
|
9
10
|
} from "./utils/slots";
|
|
10
|
-
import { parseTimeSlots } from "./utils/calendar";
|
|
11
|
-
import { queryForDateTimePicker } from "../../helpers/mocks";
|
|
11
|
+
import { getMonthsInDisplay, parseTimeSlots } from "./utils/calendar";
|
|
12
12
|
|
|
13
13
|
import Wrapper from "./Wrapper";
|
|
14
14
|
|
|
@@ -21,13 +21,30 @@ import { StatusProvider } from "./contexts/status-context";
|
|
|
21
21
|
import { TzProvider } from "./contexts/tz-context";
|
|
22
22
|
|
|
23
23
|
const DateTimePicker = ({ options }) => {
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
const statusOptions = useMemo(() => {
|
|
25
|
+
const selectedDateObject =
|
|
26
|
+
options.config.selectedDate && moment(options.config.selectedDate, "YYYY-MM-DD");
|
|
27
|
+
|
|
28
|
+
const months = getMonthObjectsFromQuery(
|
|
29
|
+
options.query,
|
|
30
|
+
options.tzid,
|
|
31
|
+
selectedDateObject?.format("YYYY-MM")
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const duration = getDurationFromQuery(options.query);
|
|
35
|
+
|
|
36
|
+
const endDateObject = moment(options.config.endDate, "YYYY-MM-DD");
|
|
37
|
+
const startDateObject = moment(options.config.startDate, "YYYY-MM-DD");
|
|
38
|
+
const currentMonthObject = selectedDateObject ?? startDateObject;
|
|
39
|
+
const currentMonth = currentMonthObject.format("YYYY-MM");
|
|
40
|
+
const monthsLoading = getMonthsLoadingFromQuery(options.query, options.tzid);
|
|
41
|
+
const monthsInView = getMonthsInDisplay(currentMonth, options.config.startDay);
|
|
28
42
|
|
|
29
43
|
const monthlyView = {
|
|
30
44
|
month: currentMonth,
|
|
45
|
+
hasNext: currentMonthObject.isBefore(endDateObject, "month"),
|
|
46
|
+
hasPrev: currentMonthObject.isAfter(startDateObject, "month"),
|
|
47
|
+
monthsInView,
|
|
31
48
|
days: parseTimeSlots({
|
|
32
49
|
slots: {},
|
|
33
50
|
month: currentMonth,
|
|
@@ -47,22 +64,29 @@ const DateTimePicker = ({ options }) => {
|
|
|
47
64
|
: () => options.log.info("No `callback` option has been provided"),
|
|
48
65
|
columnView: "loading", // loading | error | slots | confirm | no-slots
|
|
49
66
|
daySlots: [],
|
|
67
|
+
duration,
|
|
50
68
|
locale: options.locale,
|
|
51
69
|
mode: options.config.mode, // confirm (default) | no_confirm
|
|
52
|
-
query,
|
|
70
|
+
query: options.query,
|
|
53
71
|
months,
|
|
54
72
|
monthlyView,
|
|
73
|
+
monthsLoading,
|
|
55
74
|
selected: false,
|
|
56
75
|
startDay: options.config.startDay,
|
|
76
|
+
focusedDay: options.config.selectedDay,
|
|
77
|
+
selectedDay: options.config.selectedDate,
|
|
78
|
+
startDateObject,
|
|
79
|
+
endDateObject,
|
|
57
80
|
focusedSlot: false,
|
|
58
81
|
availableDays: [],
|
|
82
|
+
sequenced_availability: options.query.sequence ? true : false,
|
|
59
83
|
slots: {},
|
|
60
84
|
slotFetchCount: 0,
|
|
61
85
|
slotInjectionPoint: undefined,
|
|
62
86
|
tzid: options.tzid,
|
|
63
87
|
populated: false,
|
|
64
88
|
};
|
|
65
|
-
});
|
|
89
|
+
}, []);
|
|
66
90
|
|
|
67
91
|
const themeOptions = {
|
|
68
92
|
styles: { height: "auto", padding: "10px", ...options.styles },
|
|
@@ -17,9 +17,11 @@ const Details = ({ duration, locale }) => {
|
|
|
17
17
|
<TimeZoneSelector locale={locale} />
|
|
18
18
|
</div>
|
|
19
19
|
<div className={theme.classBuilder("details--duration")}>
|
|
20
|
-
|
|
21
|
-
<
|
|
22
|
-
|
|
20
|
+
{duration && (
|
|
21
|
+
<p>
|
|
22
|
+
<strong>{i18n.t("duration_label")}:</strong> {duration} {i18n.t("minutes")}
|
|
23
|
+
</p>
|
|
24
|
+
)}
|
|
23
25
|
</div>
|
|
24
26
|
</div>
|
|
25
27
|
);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { useI18n } from "../../contexts/i18n-context";
|
|
4
|
+
import { useTheme } from "./contexts/theme-context";
|
|
5
|
+
|
|
6
|
+
const LoadingCalendar = () => {
|
|
7
|
+
const i18n = useI18n();
|
|
8
|
+
const theme = useTheme();
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<div className={theme.classBuilder("calendar-loading")}>
|
|
12
|
+
<svg aria-hidden="true" viewBox="0 0 489.711 489.711">
|
|
13
|
+
<path d="M112.156,97.111c72.3-65.4,180.5-66.4,253.8-6.7l-58.1,2.2c-7.5,0.3-13.3,6.5-13,14c0.3,7.3,6.3,13,13.5,13 c0.2,0,0.3,0,0.5,0l89.2-3.3c7.3-0.3,13-6.2,13-13.5v-1c0-0.2,0-0.3,0-0.5v-0.1l0,0l-3.3-88.2c-0.3-7.5-6.6-13.3-14-13 c-7.5,0.3-13.3,6.5-13,14l2.1,55.3c-36.3-29.7-81-46.9-128.8-49.3c-59.2-3-116.1,17.3-160,57.1c-60.4,54.7-86,137.9-66.8,217.1 c1.5,6.2,7,10.3,13.1,10.3c1.1,0,2.1-0.1,3.2-0.4c7.2-1.8,11.7-9.1,9.9-16.3C36.656,218.211,59.056,145.111,112.156,97.111z" />
|
|
14
|
+
<path d="M462.456,195.511c-1.8-7.2-9.1-11.7-16.3-9.9c-7.2,1.8-11.7,9.1-9.9,16.3c16.9,69.6-5.6,142.7-58.7,190.7 c-37.3,33.7-84.1,50.3-130.7,50.3c-44.5,0-88.9-15.1-124.7-44.9l58.8-5.3c7.4-0.7,12.9-7.2,12.2-14.7s-7.2-12.9-14.7-12.2l-88.9,8 c-7.4,0.7-12.9,7.2-12.2,14.7l8,88.9c0.6,7,6.5,12.3,13.4,12.3c0.4,0,0.8,0,1.2-0.1c7.4-0.7,12.9-7.2,12.2-14.7l-4.8-54.1 c36.3,29.4,80.8,46.5,128.3,48.9c3.8,0.2,7.6,0.3,11.3,0.3c55.1,0,107.5-20.2,148.7-57.4 C456.056,357.911,481.656,274.811,462.456,195.511z" />
|
|
15
|
+
</svg>
|
|
16
|
+
<span className={"calendar-loading__text"}>{i18n.t("loading_calendar")}</span>
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default LoadingCalendar;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from "react";
|
|
2
|
+
import moment from "moment-timezone";
|
|
3
|
+
|
|
4
|
+
import { useI18n } from "../../contexts/i18n-context";
|
|
5
|
+
import { useStatus } from "./contexts/status-context";
|
|
6
|
+
import { useTheme } from "./contexts/theme-context";
|
|
7
|
+
import { useTz } from "./contexts/tz-context";
|
|
8
|
+
|
|
9
|
+
const SequencedSlotButton = ({ slot }) => {
|
|
10
|
+
const i18n = useI18n();
|
|
11
|
+
const theme = useTheme();
|
|
12
|
+
const [status, dispatchStatus] = useStatus();
|
|
13
|
+
const [tz] = useTz();
|
|
14
|
+
|
|
15
|
+
const slotButtonRef = useRef();
|
|
16
|
+
|
|
17
|
+
const startArray = slot.map(a => a.start);
|
|
18
|
+
const endArray = slot.map(a => a.end);
|
|
19
|
+
|
|
20
|
+
const start = startArray.reduce((prev, current) => {
|
|
21
|
+
return prev < current ? prev : current;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const end = endArray.reduce((prev, current) => {
|
|
25
|
+
return prev > current ? prev : current;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const handleSlotSelection = slot => {
|
|
29
|
+
const selectedSlot = {
|
|
30
|
+
start,
|
|
31
|
+
end,
|
|
32
|
+
sequence: slot,
|
|
33
|
+
};
|
|
34
|
+
dispatchStatus({ type: "SELECT_SLOT", slot: selectedSlot, tzid: tz.selectedTzid.tzid });
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (status.focusedSlot === start) {
|
|
39
|
+
slotButtonRef.current.focus();
|
|
40
|
+
}
|
|
41
|
+
}, [status.focusedSlot]);
|
|
42
|
+
|
|
43
|
+
let classStub = "time-slot";
|
|
44
|
+
if (status.selected.start === start) classStub = classStub + " time-slot--selected";
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<button
|
|
48
|
+
className={theme.classBuilder(classStub)}
|
|
49
|
+
onClick={() => handleSlotSelection(slot)}
|
|
50
|
+
ref={slotButtonRef}
|
|
51
|
+
>
|
|
52
|
+
<span className={theme.classBuilder("visually-hidden")}>
|
|
53
|
+
{i18n.t("select_time_slot")}
|
|
54
|
+
</span>
|
|
55
|
+
{i18n.customFormatedTimeZone(
|
|
56
|
+
moment(start, "YYYY-MM-DDTHH:mm:00Z"),
|
|
57
|
+
tz.selectedTzid.tzid,
|
|
58
|
+
"LT"
|
|
59
|
+
)}
|
|
60
|
+
{" - "}
|
|
61
|
+
{i18n.customFormatedTimeZone(
|
|
62
|
+
moment(end, "YYYY-MM-DDTHH:mm:00Z"),
|
|
63
|
+
tz.selectedTzid.tzid,
|
|
64
|
+
"LT"
|
|
65
|
+
)}{" "}
|
|
66
|
+
{`(${moment(start, "YYYY-MM-DDTHH:mm:00Z").tz(tz.selectedTzid.tzid).format("z")})`}
|
|
67
|
+
</button>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export default SequencedSlotButton;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
3
|
import SlotButton from "./SlotButton";
|
|
4
|
+
import SequencedSlotButton from "./SequencedSlotButton";
|
|
4
5
|
|
|
5
6
|
import { useI18n } from "../../contexts/i18n-context";
|
|
6
7
|
import { useStatus } from "./contexts/status-context";
|
|
@@ -14,7 +15,11 @@ const SlotsList = () => {
|
|
|
14
15
|
const renderSlots = status.daySlots.map((slot, key) => {
|
|
15
16
|
return (
|
|
16
17
|
<li key={key} className={theme.classBuilder("slot-list--item")}>
|
|
17
|
-
|
|
18
|
+
{status.sequenced_availability ? (
|
|
19
|
+
<SequencedSlotButton slot={slot} />
|
|
20
|
+
) : (
|
|
21
|
+
<SlotButton slot={slot} />
|
|
22
|
+
)}
|
|
18
23
|
</li>
|
|
19
24
|
);
|
|
20
25
|
});
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import React, { useEffect, useRef } from "react";
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
2
|
import moment from "moment";
|
|
3
3
|
|
|
4
|
-
import { getSlots } from "./utils/slots";
|
|
4
|
+
import { getSequencedSlots, getSlots } from "./utils/slots";
|
|
5
5
|
|
|
6
6
|
import Calendar from "./Calendar";
|
|
7
7
|
import Confirm from "./Confirm";
|
|
8
8
|
import Error from "./Error";
|
|
9
9
|
import Details from "./Details";
|
|
10
10
|
import Loading from "./Loading";
|
|
11
|
+
import LoadingCalendar from "./LoadingCalendar";
|
|
11
12
|
import NoSlotsFound from "./NoSlotsFound";
|
|
12
13
|
import SlotsList from "./SlotsList";
|
|
13
14
|
|
|
@@ -20,6 +21,8 @@ const Wrapper = () => {
|
|
|
20
21
|
const [tz] = useTz();
|
|
21
22
|
const theme = useTheme();
|
|
22
23
|
|
|
24
|
+
const [loadingCalendar, setLoadingCalendar] = useState(true);
|
|
25
|
+
|
|
23
26
|
const confirmButtonRef = useRef();
|
|
24
27
|
|
|
25
28
|
useEffect(() => {
|
|
@@ -27,8 +30,10 @@ const Wrapper = () => {
|
|
|
27
30
|
return;
|
|
28
31
|
}
|
|
29
32
|
|
|
30
|
-
const fetchMonthSlots = query => {
|
|
31
|
-
|
|
33
|
+
const fetchMonthSlots = (query, month) => {
|
|
34
|
+
const fetch = status.sequenced_availability ? getSequencedSlots : getSlots;
|
|
35
|
+
|
|
36
|
+
return fetch({
|
|
32
37
|
query,
|
|
33
38
|
auth: status.auth,
|
|
34
39
|
tzid: status.tzid,
|
|
@@ -37,6 +42,7 @@ const Wrapper = () => {
|
|
|
37
42
|
type: "SET_SLOTS",
|
|
38
43
|
slots: res,
|
|
39
44
|
tzid: tz.selectedTzid.tzid,
|
|
45
|
+
month: month,
|
|
40
46
|
});
|
|
41
47
|
});
|
|
42
48
|
};
|
|
@@ -44,11 +50,19 @@ const Wrapper = () => {
|
|
|
44
50
|
const currentMonth = status.months.find(m => m.current);
|
|
45
51
|
|
|
46
52
|
// Get slots for current month
|
|
47
|
-
fetchMonthSlots(currentMonth.query)
|
|
53
|
+
fetchMonthSlots(currentMonth.query, currentMonth.month)
|
|
48
54
|
.then(() => {
|
|
49
55
|
// Get slots for remianing months
|
|
50
56
|
const remainingMonths = status.months.filter(month => !month.current);
|
|
51
|
-
remainingMonths.forEach(month =>
|
|
57
|
+
remainingMonths.forEach(month =>
|
|
58
|
+
fetchMonthSlots(month.query, month.month).catch(res => {
|
|
59
|
+
dispatchStatus({
|
|
60
|
+
type: "ERROR_LOADING_SLOTS",
|
|
61
|
+
error: res,
|
|
62
|
+
month: month.month,
|
|
63
|
+
});
|
|
64
|
+
})
|
|
65
|
+
);
|
|
52
66
|
})
|
|
53
67
|
.catch(error => {
|
|
54
68
|
dispatchStatus({ type: "ERROR_GETTING_SLOTS", error });
|
|
@@ -56,17 +70,7 @@ const Wrapper = () => {
|
|
|
56
70
|
}, []);
|
|
57
71
|
|
|
58
72
|
useEffect(() => {
|
|
59
|
-
|
|
60
|
-
const fetchCount = status.slotFetchCount;
|
|
61
|
-
const monthsCount = status.months.length;
|
|
62
|
-
const slotsCount = Object.keys(status.slots).length;
|
|
63
|
-
if (fetchCount === monthsCount && slotsCount < 1) {
|
|
64
|
-
dispatchStatus({ type: "NO_SLOTS_FOUND" });
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Set grid display if there are available slots within the current month
|
|
69
|
-
if (slotsCount < 1) {
|
|
73
|
+
if (!status.slotInjectionPoint) {
|
|
70
74
|
return;
|
|
71
75
|
}
|
|
72
76
|
|
|
@@ -75,11 +79,9 @@ const Wrapper = () => {
|
|
|
75
79
|
const lastWeekInMonth = weeksInMonth[weeksInMonth.length - 1];
|
|
76
80
|
const lastDay = moment(lastWeekInMonth[lastWeekInMonth.length - 1].date, "YYYY-MM-DD");
|
|
77
81
|
|
|
78
|
-
const injectionPoint = status.slotInjectionPoint
|
|
79
|
-
? moment(status.slotInjectionPoint, "YYYY-MM-DD")
|
|
80
|
-
: undefined;
|
|
82
|
+
const injectionPoint = moment(status.slotInjectionPoint, "YYYY-MM-DD");
|
|
81
83
|
|
|
82
|
-
if (
|
|
84
|
+
if (!injectionPoint.isBetween(firstDay, lastDay, "day", "[]")) {
|
|
83
85
|
return;
|
|
84
86
|
}
|
|
85
87
|
|
|
@@ -87,7 +89,45 @@ const Wrapper = () => {
|
|
|
87
89
|
type: "RECALCULATE_MONTH_VIEW",
|
|
88
90
|
tzid: tz.selectedTzid.tzid,
|
|
89
91
|
});
|
|
90
|
-
}, [status.
|
|
92
|
+
}, [status.slotInjectionPoint]);
|
|
93
|
+
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
const fetchCount = status.slotFetchCount;
|
|
96
|
+
const monthsCount = status.months.length;
|
|
97
|
+
const finishedCallingAllSlots = fetchCount === monthsCount;
|
|
98
|
+
const hasAvailableDays = status.availableDays.length;
|
|
99
|
+
|
|
100
|
+
// No slots available after all queries have been made
|
|
101
|
+
if (finishedCallingAllSlots && !hasAvailableDays) {
|
|
102
|
+
dispatchStatus({ type: "NO_SLOTS_FOUND" });
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Set selectedDay to first available day
|
|
107
|
+
if (!status.selectedDay && hasAvailableDays) {
|
|
108
|
+
dispatchStatus({
|
|
109
|
+
type: "SELECT_DAY",
|
|
110
|
+
day: status.availableDays[0],
|
|
111
|
+
tzid: tz.selectedTzid.tzid,
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!status.selectedDay && !hasAvailableDays) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// For allowing only this to be called once
|
|
121
|
+
// Since we know the first month that is being fetched will be for the initial selected day
|
|
122
|
+
if (status.selectedDay && fetchCount === 1) {
|
|
123
|
+
dispatchStatus({
|
|
124
|
+
type: "SELECT_DAY",
|
|
125
|
+
day: status.selectedDay,
|
|
126
|
+
tzid: tz.selectedTzid.tzid,
|
|
127
|
+
});
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}, [status.slotFetchCount, status.availableDays]);
|
|
91
131
|
|
|
92
132
|
useEffect(() => {
|
|
93
133
|
if (status.selectedDay) {
|
|
@@ -118,9 +158,23 @@ const Wrapper = () => {
|
|
|
118
158
|
}
|
|
119
159
|
}, [tz.selectedTzid.tzid]);
|
|
120
160
|
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
if (status.monthsLoading) {
|
|
163
|
+
for (let month of status.monthlyView.monthsInView) {
|
|
164
|
+
const monthLoading = status.monthsLoading.find(m => m.month === month);
|
|
165
|
+
if (monthLoading && monthLoading.loading) {
|
|
166
|
+
setLoadingCalendar(true);
|
|
167
|
+
return;
|
|
168
|
+
} else {
|
|
169
|
+
setLoadingCalendar(false);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}, [status.monthlyView, status.monthsLoading]);
|
|
174
|
+
|
|
121
175
|
return (
|
|
122
176
|
<section className={theme.classBuilder()} style={theme.customProperties}>
|
|
123
|
-
<Details duration={status.
|
|
177
|
+
<Details duration={status.duration} locale={status.locale} />
|
|
124
178
|
<div className={theme.classBuilder("wrapper")}>
|
|
125
179
|
<div
|
|
126
180
|
className={
|
|
@@ -131,6 +185,7 @@ const Wrapper = () => {
|
|
|
131
185
|
}
|
|
132
186
|
>
|
|
133
187
|
<Calendar />
|
|
188
|
+
{loadingCalendar && <LoadingCalendar />}
|
|
134
189
|
</div>
|
|
135
190
|
<div className={theme.classBuilder("column--right")}>
|
|
136
191
|
{status.columnView === "error" && <Error />}
|