cronofy-elements 1.45.0 → 1.48.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/build/CronofyElements.v1.48.0.js +2 -0
- package/build/{CronofyElements.v1.45.0.js.LICENSE.txt → CronofyElements.v1.48.0.js.LICENSE.txt} +0 -0
- package/build/npm/CronofyElements.js +2 -2
- package/demo/date-time-picker.ejs +6 -32
- package/package.json +1 -1
- package/src/js/components/AvailabilityRules/Wrapper.js +4 -18
- package/src/js/components/DateTimePicker/Confirm.js +37 -1
- package/src/js/components/DateTimePicker/DateTimePicker.js +1 -3
- package/src/js/components/DateTimePicker/SequencedSlotButton.js +37 -11
- package/src/js/components/DateTimePicker/Wrapper.js +114 -37
- package/src/js/components/DateTimePicker/contexts/status-reducer.js +29 -8
- package/src/js/components/DateTimePicker/scss/_components.confirm.scss +10 -0
- package/src/js/components/DateTimePicker/scss/_components.slotslist.scss +10 -0
- package/src/js/components/DateTimePicker/utils/slots.js +22 -77
- package/src/js/components/generic/TimeZoneSelector.js +1 -0
- package/src/js/helpers/connections.js +1 -15
- package/src/js/helpers/init.DateTimePicker.js +44 -2
- package/src/js/helpers/mocks.js +97 -0
- package/tests/AvailabilityRules/__snapshots__/AvailabilityRules.test.js.snap +1 -0
- package/tests/DateTimePicker/SequencedSlotButton.test.js +28 -1
- package/tests/DateTimePicker/contexts/status-reducer.test.js +180 -24
- package/tests/DateTimePicker/dummy-data.js +186 -1
- package/tests/DateTimePicker/utils.test.js +32 -3
- package/build/CronofyElements.v1.45.0.js +0 -2
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
sequence: [
|
|
50
50
|
{
|
|
51
51
|
sequence_id: "test",
|
|
52
|
+
sequence_title: "Test Event One",
|
|
52
53
|
ordinal: 1,
|
|
53
54
|
participants: [
|
|
54
55
|
{
|
|
@@ -66,6 +67,7 @@
|
|
|
66
67
|
},
|
|
67
68
|
{
|
|
68
69
|
sequence_id: "test-1",
|
|
70
|
+
sequence_title: "Test Event Two",
|
|
69
71
|
ordinal: 2,
|
|
70
72
|
participants: [
|
|
71
73
|
{
|
|
@@ -86,31 +88,7 @@
|
|
|
86
88
|
minimum: { minutes: 15 }
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
sequence_id: "test-2",
|
|
92
|
-
ordinal: 3,
|
|
93
|
-
participants: [
|
|
94
|
-
{
|
|
95
|
-
required: "all",
|
|
96
|
-
members: [
|
|
97
|
-
{
|
|
98
|
-
sub: "<%= sub %>",
|
|
99
|
-
// managed_availability: true,
|
|
100
|
-
// availability_rule_ids: ["weekly_work_hours"]
|
|
101
|
-
}
|
|
102
|
-
]
|
|
103
|
-
}
|
|
104
|
-
],
|
|
105
|
-
required_duration: { minutes: 30 },
|
|
106
|
-
start_interval: { minutes: 10 },
|
|
107
|
-
buffer: {
|
|
108
|
-
before: {
|
|
109
|
-
minimum: { minutes: 15 }
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
|
|
91
|
+
},
|
|
114
92
|
],
|
|
115
93
|
query_periods: [
|
|
116
94
|
{ start: slotTimes(01,"08:00"), end: slotTimes(31,"17:00") }
|
|
@@ -136,15 +114,10 @@
|
|
|
136
114
|
]
|
|
137
115
|
}
|
|
138
116
|
],
|
|
139
|
-
required_duration: { minutes:
|
|
117
|
+
required_duration: { minutes: 15 },
|
|
140
118
|
//start_interval: { minutes: 15 },
|
|
141
119
|
query_periods: [
|
|
142
|
-
{ start: slotTimes(31,"03:00"), end: slotTimes(
|
|
143
|
-
{ start: slotTimes(32,"09:00"), end: slotTimes(32,"13:00") },
|
|
144
|
-
{ start: slotTimes(32,"14:00"), end: slotTimes(32,"17:00") },
|
|
145
|
-
{ start: slotTimes(35,"09:00"), end: slotTimes(35,"13:00") },
|
|
146
|
-
{ start: slotTimes(36,"14:00"), end: slotTimes(36,"17:00") },
|
|
147
|
-
{ start: slotTimes(38,"09:00"), end: slotTimes(65,"22:00") }
|
|
120
|
+
{ start: slotTimes(31,"03:00"), end: slotTimes(150,"12:00") },
|
|
148
121
|
]
|
|
149
122
|
},
|
|
150
123
|
// tzid: "America/Mexico_City",
|
|
@@ -156,6 +129,7 @@
|
|
|
156
129
|
},
|
|
157
130
|
config: {
|
|
158
131
|
logs: 'info',
|
|
132
|
+
//slot_button_mode: 'detailed' //summary | detailed
|
|
159
133
|
//selected_date: "2022-05-15"
|
|
160
134
|
//mode: 'no_confirm'
|
|
161
135
|
// week_start_day: "tuesday"
|
package/package.json
CHANGED
|
@@ -115,17 +115,7 @@ const Wrapper = ({ options }) => {
|
|
|
115
115
|
const allSlots = { ...slots, ...newSlots };
|
|
116
116
|
setSlots(allSlots);
|
|
117
117
|
const newRules = buildNewRules(allSlots);
|
|
118
|
-
|
|
119
|
-
notification: {
|
|
120
|
-
type: "availability_rule_edited",
|
|
121
|
-
},
|
|
122
|
-
availability_rule: {
|
|
123
|
-
...account,
|
|
124
|
-
calendar_ids: calendars.active,
|
|
125
|
-
weekly_periods: newRules,
|
|
126
|
-
},
|
|
127
|
-
};
|
|
128
|
-
status.callback(callbackContent);
|
|
118
|
+
setRules(newRules);
|
|
129
119
|
};
|
|
130
120
|
|
|
131
121
|
useEffect(() => {
|
|
@@ -148,7 +138,6 @@ const Wrapper = ({ options }) => {
|
|
|
148
138
|
|
|
149
139
|
useEffect(() => {
|
|
150
140
|
if (!status.loading && !status.error) {
|
|
151
|
-
const newRules = buildNewRules(slots);
|
|
152
141
|
const callbackContent = {
|
|
153
142
|
notification: {
|
|
154
143
|
type: "availability_rule_edited",
|
|
@@ -156,12 +145,12 @@ const Wrapper = ({ options }) => {
|
|
|
156
145
|
availability_rule: {
|
|
157
146
|
...account,
|
|
158
147
|
calendar_ids: calendars.active,
|
|
159
|
-
weekly_periods:
|
|
148
|
+
weekly_periods: rules,
|
|
160
149
|
},
|
|
161
150
|
};
|
|
162
151
|
status.callback(callbackContent);
|
|
163
152
|
}
|
|
164
|
-
}, [calendars]);
|
|
153
|
+
}, [calendars, account, rules]);
|
|
165
154
|
|
|
166
155
|
useEffect(() => {
|
|
167
156
|
// Query the API for rules and calendars, but don't do anything until
|
|
@@ -295,10 +284,7 @@ const Wrapper = ({ options }) => {
|
|
|
295
284
|
}, [tz.selectedTzid.tzid]);
|
|
296
285
|
|
|
297
286
|
const generateRules = () => {
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
setRules(newRules);
|
|
301
|
-
const rulesRequest = { ...account, weekly_periods: newRules };
|
|
287
|
+
const rulesRequest = { ...account, weekly_periods: rules };
|
|
302
288
|
|
|
303
289
|
let tempStatus = status;
|
|
304
290
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useState } from "react";
|
|
2
2
|
import moment from "moment-timezone";
|
|
3
3
|
|
|
4
4
|
import { useI18n } from "../../contexts/i18n-context";
|
|
@@ -12,6 +12,12 @@ const Confirm = ({ confirmButtonRef }) => {
|
|
|
12
12
|
const theme = useTheme();
|
|
13
13
|
const [tz] = useTz();
|
|
14
14
|
|
|
15
|
+
const [sequenceTitlePresent, setSequenceTitlePresent] = useState(
|
|
16
|
+
() =>
|
|
17
|
+
status.selected.sequence &&
|
|
18
|
+
status.selected.sequence.every(obj => Object.keys(obj).includes("sequence_title"))
|
|
19
|
+
);
|
|
20
|
+
|
|
15
21
|
const handleCancel = () => {
|
|
16
22
|
dispatchStatus({ type: "CANCEL_SLOT_SELECTION" });
|
|
17
23
|
};
|
|
@@ -52,6 +58,36 @@ const Confirm = ({ confirmButtonRef }) => {
|
|
|
52
58
|
)}{" "}
|
|
53
59
|
{`(${moment.tz(tz.selectedTzid.tzid).format("z")})`}
|
|
54
60
|
</p>
|
|
61
|
+
{status.selected.sequence && sequenceTitlePresent && (
|
|
62
|
+
<dl className={theme.classBuilder("confirm-details--sequence")}>
|
|
63
|
+
{status.selected.sequence.map((slot, k) => (
|
|
64
|
+
<div key={k}>
|
|
65
|
+
<dt
|
|
66
|
+
className={theme.classBuilder(
|
|
67
|
+
"confirm-details--sequence-title"
|
|
68
|
+
)}
|
|
69
|
+
>
|
|
70
|
+
{slot.sequence_title}
|
|
71
|
+
</dt>
|
|
72
|
+
<dd
|
|
73
|
+
className={theme.classBuilder("confirm-details--sequence-time")}
|
|
74
|
+
>
|
|
75
|
+
{i18n.customFormatedTimeZone(
|
|
76
|
+
moment(slot.start, "YYYY-MM-DDTHH:mm:00Z"),
|
|
77
|
+
tz.selectedTzid.tzid,
|
|
78
|
+
"LT"
|
|
79
|
+
)}
|
|
80
|
+
{" - "}
|
|
81
|
+
{i18n.customFormatedTimeZone(
|
|
82
|
+
moment(slot.end, "YYYY-MM-DDTHH:mm:00Z"),
|
|
83
|
+
tz.selectedTzid.tzid,
|
|
84
|
+
"LT"
|
|
85
|
+
)}
|
|
86
|
+
</dd>
|
|
87
|
+
</div>
|
|
88
|
+
))}
|
|
89
|
+
</dl>
|
|
90
|
+
)}
|
|
55
91
|
</div>
|
|
56
92
|
<button
|
|
57
93
|
className={theme.classBuilder("confirm-button")}
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
getMonthObjectsFromQuery,
|
|
6
6
|
parseTzList,
|
|
7
7
|
getInitialSelectedTzid,
|
|
8
|
-
getMonthsLoadingFromQuery,
|
|
9
8
|
getDurationFromQuery,
|
|
10
9
|
} from "./utils/slots";
|
|
11
10
|
import { getMonthsInDisplay, parseTimeSlots } from "./utils/calendar";
|
|
@@ -37,7 +36,6 @@ const DateTimePicker = ({ options }) => {
|
|
|
37
36
|
const startDateObject = moment(options.config.startDate, "YYYY-MM-DD");
|
|
38
37
|
const currentMonthObject = selectedDateObject ?? startDateObject;
|
|
39
38
|
const currentMonth = currentMonthObject.format("YYYY-MM");
|
|
40
|
-
const monthsLoading = getMonthsLoadingFromQuery(options.query, options.tzid);
|
|
41
39
|
const monthsInView = getMonthsInDisplay(currentMonth, options.config.startDay);
|
|
42
40
|
|
|
43
41
|
const monthlyView = {
|
|
@@ -70,7 +68,6 @@ const DateTimePicker = ({ options }) => {
|
|
|
70
68
|
query: options.query,
|
|
71
69
|
months,
|
|
72
70
|
monthlyView,
|
|
73
|
-
monthsLoading,
|
|
74
71
|
selected: false,
|
|
75
72
|
startDay: options.config.startDay,
|
|
76
73
|
focusedDay: options.config.selectedDay,
|
|
@@ -81,6 +78,7 @@ const DateTimePicker = ({ options }) => {
|
|
|
81
78
|
availableDays: [],
|
|
82
79
|
sequenced_availability: options.query.sequence ? true : false,
|
|
83
80
|
slots: {},
|
|
81
|
+
slotButtonMode: options.config.slotButtonMode,
|
|
84
82
|
slotFetchCount: 0,
|
|
85
83
|
slotInjectionPoint: undefined,
|
|
86
84
|
tzid: options.tzid,
|
|
@@ -41,29 +41,55 @@ const SequencedSlotButton = ({ slot }) => {
|
|
|
41
41
|
}, [status.focusedSlot]);
|
|
42
42
|
|
|
43
43
|
let classStub = "time-slot";
|
|
44
|
+
if (status.slotButtonMode === "detailed") classStub = classStub + " time-slot--detailed";
|
|
44
45
|
if (status.selected.start === start) classStub = classStub + " time-slot--selected";
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
<
|
|
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>
|
|
47
|
+
const detailedSlot = slot.map((s, i) => (
|
|
48
|
+
<li key={i} className={theme.classBuilder("detailed-slot-list--item")}>
|
|
55
49
|
{i18n.customFormatedTimeZone(
|
|
56
|
-
moment(start, "YYYY-MM-DDTHH:mm:00Z"),
|
|
50
|
+
moment(s.start, "YYYY-MM-DDTHH:mm:00Z"),
|
|
57
51
|
tz.selectedTzid.tzid,
|
|
58
52
|
"LT"
|
|
59
53
|
)}
|
|
60
54
|
{" - "}
|
|
61
55
|
{i18n.customFormatedTimeZone(
|
|
62
|
-
moment(end, "YYYY-MM-DDTHH:mm:00Z"),
|
|
56
|
+
moment(s.end, "YYYY-MM-DDTHH:mm:00Z"),
|
|
63
57
|
tz.selectedTzid.tzid,
|
|
64
58
|
"LT"
|
|
65
59
|
)}{" "}
|
|
66
60
|
{`(${moment(start, "YYYY-MM-DDTHH:mm:00Z").tz(tz.selectedTzid.tzid).format("z")})`}
|
|
61
|
+
</li>
|
|
62
|
+
));
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<button
|
|
66
|
+
className={theme.classBuilder(classStub)}
|
|
67
|
+
onClick={() => handleSlotSelection(slot)}
|
|
68
|
+
ref={slotButtonRef}
|
|
69
|
+
>
|
|
70
|
+
<span className={theme.classBuilder("visually-hidden")}>
|
|
71
|
+
{i18n.t("select_time_slot")}
|
|
72
|
+
</span>
|
|
73
|
+
{status.slotButtonMode === "detailed" ? (
|
|
74
|
+
<ul className={theme.classBuilder("detailed-slot-list")}>{detailedSlot}</ul>
|
|
75
|
+
) : (
|
|
76
|
+
<>
|
|
77
|
+
{i18n.customFormatedTimeZone(
|
|
78
|
+
moment(start, "YYYY-MM-DDTHH:mm:00Z"),
|
|
79
|
+
tz.selectedTzid.tzid,
|
|
80
|
+
"LT"
|
|
81
|
+
)}
|
|
82
|
+
{" - "}
|
|
83
|
+
{i18n.customFormatedTimeZone(
|
|
84
|
+
moment(end, "YYYY-MM-DDTHH:mm:00Z"),
|
|
85
|
+
tz.selectedTzid.tzid,
|
|
86
|
+
"LT"
|
|
87
|
+
)}{" "}
|
|
88
|
+
{`(${moment(start, "YYYY-MM-DDTHH:mm:00Z")
|
|
89
|
+
.tz(tz.selectedTzid.tzid)
|
|
90
|
+
.format("z")})`}
|
|
91
|
+
</>
|
|
92
|
+
)}
|
|
67
93
|
</button>
|
|
68
94
|
);
|
|
69
95
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from "react";
|
|
2
2
|
import moment from "moment";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { cropPeriodsArbitrarily, getSlots } from "./utils/slots";
|
|
5
5
|
|
|
6
6
|
import Calendar from "./Calendar";
|
|
7
7
|
import Confirm from "./Confirm";
|
|
@@ -24,51 +24,109 @@ const Wrapper = () => {
|
|
|
24
24
|
const [loadingCalendar, setLoadingCalendar] = useState(true);
|
|
25
25
|
|
|
26
26
|
const confirmButtonRef = useRef();
|
|
27
|
+
const firstRender = useRef(true);
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
const fetchMonthSlots = (query, month) => {
|
|
30
|
+
return getSlots({
|
|
31
|
+
query,
|
|
32
|
+
auth: status.auth,
|
|
33
|
+
tzid: status.tzid,
|
|
34
|
+
sequence: status.sequenced_availability,
|
|
35
|
+
})
|
|
36
|
+
.then(res => {
|
|
37
|
+
if (res.length < 512) {
|
|
38
|
+
dispatchStatus({
|
|
39
|
+
type: "SET_SLOTS",
|
|
40
|
+
slots: res,
|
|
41
|
+
tzid: tz.selectedTzid.tzid,
|
|
42
|
+
month,
|
|
43
|
+
});
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
35
46
|
|
|
36
|
-
return fetch({
|
|
37
|
-
query,
|
|
38
|
-
auth: status.auth,
|
|
39
|
-
tzid: status.tzid,
|
|
40
|
-
}).then(res => {
|
|
41
47
|
dispatchStatus({
|
|
42
48
|
type: "SET_SLOTS",
|
|
43
49
|
slots: res,
|
|
44
50
|
tzid: tz.selectedTzid.tzid,
|
|
45
|
-
month: month,
|
|
46
51
|
});
|
|
52
|
+
|
|
53
|
+
// If we get here, the API has returned the maximum number
|
|
54
|
+
// of slots allowed, so we need to crop the query and try
|
|
55
|
+
// again to ensure we haven't missed any slots.
|
|
56
|
+
const startOfLastSlot = res[res.length - 1].start;
|
|
57
|
+
const endOfLastPeriod = query.query_periods[query.query_periods.length - 1].end;
|
|
58
|
+
|
|
59
|
+
const boundsForCropping = {
|
|
60
|
+
start: startOfLastSlot,
|
|
61
|
+
end: endOfLastPeriod,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const croppedPeriods = cropPeriodsArbitrarily(
|
|
65
|
+
query.query_periods,
|
|
66
|
+
boundsForCropping
|
|
67
|
+
);
|
|
68
|
+
const croppedQuery = { ...query, query_periods: croppedPeriods };
|
|
69
|
+
|
|
70
|
+
// Rerun the query
|
|
71
|
+
return fetchMonthSlots(croppedQuery, month);
|
|
72
|
+
})
|
|
73
|
+
.catch(error => {
|
|
74
|
+
if (error.type === 422) {
|
|
75
|
+
dispatchStatus({
|
|
76
|
+
type: "ERROR_LOADING_SLOTS",
|
|
77
|
+
error,
|
|
78
|
+
month,
|
|
79
|
+
});
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
dispatchStatus({ type: "ERROR_GETTING_SLOTS", error });
|
|
47
83
|
});
|
|
48
|
-
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (!status.query.query_periods) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
49
90
|
|
|
50
91
|
const currentMonth = status.months.find(m => m.current);
|
|
51
92
|
|
|
93
|
+
const croppedMonths = status.months.filter(el => {
|
|
94
|
+
return status.monthlyView.monthsInView.some(f => {
|
|
95
|
+
return f === el.month && el.loading && !el.current;
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
52
99
|
// Get slots for current month
|
|
53
100
|
fetchMonthSlots(currentMonth.query, currentMonth.month)
|
|
54
101
|
.then(() => {
|
|
55
|
-
// Get slots for
|
|
56
|
-
|
|
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
|
-
);
|
|
102
|
+
// Get slots for remaining months
|
|
103
|
+
croppedMonths.forEach(month => fetchMonthSlots(month.query, month.month));
|
|
66
104
|
})
|
|
67
105
|
.catch(error => {
|
|
68
106
|
dispatchStatus({ type: "ERROR_GETTING_SLOTS", error });
|
|
69
107
|
});
|
|
70
108
|
}, []);
|
|
71
109
|
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
//stops this from firing on first render which would double up the initial API calls
|
|
112
|
+
if (firstRender.current) {
|
|
113
|
+
firstRender.current = false;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (status.monthlyView.monthsInView) {
|
|
117
|
+
//check if any of the months in view haven't been fetched yet
|
|
118
|
+
const croppedMonths = status.months.filter(el => {
|
|
119
|
+
return status.monthlyView.monthsInView.some(f => {
|
|
120
|
+
return f === el.month && el.loading === true;
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
//if they haven't, fetch them.
|
|
124
|
+
if (croppedMonths.length > 0) {
|
|
125
|
+
croppedMonths.forEach(month => fetchMonthSlots(month.query, month.month));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}, [status.monthlyView.monthsInView]);
|
|
129
|
+
|
|
72
130
|
useEffect(() => {
|
|
73
131
|
if (!status.slotInjectionPoint) {
|
|
74
132
|
return;
|
|
@@ -97,6 +155,15 @@ const Wrapper = () => {
|
|
|
97
155
|
const finishedCallingAllSlots = fetchCount === monthsCount;
|
|
98
156
|
const hasAvailableDays = status.availableDays.length;
|
|
99
157
|
|
|
158
|
+
// No slots found but not all months called so try the next month
|
|
159
|
+
if (!finishedCallingAllSlots && !hasAvailableDays && fetchCount >= 2) {
|
|
160
|
+
const croppedMonths = status.months.filter(el => {
|
|
161
|
+
return el.loading === true;
|
|
162
|
+
});
|
|
163
|
+
fetchMonthSlots(croppedMonths[0].query, croppedMonths[0].month);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
100
167
|
// No slots available after all queries have been made
|
|
101
168
|
if (finishedCallingAllSlots && !hasAvailableDays) {
|
|
102
169
|
dispatchStatus({ type: "NO_SLOTS_FOUND" });
|
|
@@ -117,15 +184,25 @@ const Wrapper = () => {
|
|
|
117
184
|
return;
|
|
118
185
|
}
|
|
119
186
|
|
|
120
|
-
// For allowing only this to be called once
|
|
187
|
+
// For allowing only this to be called once slots have been fetched
|
|
121
188
|
// Since we know the first month that is being fetched will be for the initial selected day
|
|
122
|
-
if (status.selectedDay && fetchCount
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
189
|
+
if (status.selectedDay && fetchCount >= 1 && !status.selected) {
|
|
190
|
+
const currentMonth = moment(status.selectedDay, "YYYY-MM-DD").format("YYYY-MM");
|
|
191
|
+
const month = status.months.filter(el => {
|
|
192
|
+
return el.month === currentMonth;
|
|
193
|
+
})[0];
|
|
194
|
+
const monthStillLoading = month ? month.loading : false;
|
|
195
|
+
const dayIsAvailable = status.availableDays.includes(status.selectedDay);
|
|
196
|
+
|
|
197
|
+
//Only set the selectedDay if the month is finished loading or if the day has slots available
|
|
198
|
+
if (!monthStillLoading || dayIsAvailable) {
|
|
199
|
+
dispatchStatus({
|
|
200
|
+
type: "SELECT_DAY",
|
|
201
|
+
day: status.selectedDay,
|
|
202
|
+
tzid: tz.selectedTzid.tzid,
|
|
203
|
+
});
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
129
206
|
}
|
|
130
207
|
}, [status.slotFetchCount, status.availableDays]);
|
|
131
208
|
|
|
@@ -159,9 +236,9 @@ const Wrapper = () => {
|
|
|
159
236
|
}, [tz.selectedTzid.tzid]);
|
|
160
237
|
|
|
161
238
|
useEffect(() => {
|
|
162
|
-
if (status.
|
|
239
|
+
if (status.months) {
|
|
163
240
|
for (let month of status.monthlyView.monthsInView) {
|
|
164
|
-
const monthLoading = status.
|
|
241
|
+
const monthLoading = status.months.find(m => m.month === month);
|
|
165
242
|
if (monthLoading && monthLoading.loading) {
|
|
166
243
|
setLoadingCalendar(true);
|
|
167
244
|
return;
|
|
@@ -170,7 +247,7 @@ const Wrapper = () => {
|
|
|
170
247
|
}
|
|
171
248
|
}
|
|
172
249
|
}
|
|
173
|
-
}, [status.monthlyView, status.
|
|
250
|
+
}, [status.monthlyView, status.months]);
|
|
174
251
|
|
|
175
252
|
return (
|
|
176
253
|
<section className={theme.classBuilder()} style={theme.customProperties}>
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
addSequencedSlotsToObject,
|
|
10
10
|
} from "../utils/slots";
|
|
11
11
|
import { getMonthsInDisplay, parseTimeSlots } from "../utils/calendar";
|
|
12
|
+
import { uniqueItems } from "../../../helpers/utils";
|
|
12
13
|
|
|
13
14
|
export const statusReducer = (state, action) => {
|
|
14
15
|
const { type, ...actionBody } = action;
|
|
@@ -62,9 +63,9 @@ export const statusReducer = (state, action) => {
|
|
|
62
63
|
body: action.error.body,
|
|
63
64
|
},
|
|
64
65
|
};
|
|
65
|
-
const
|
|
66
|
+
const months = removeMonthFromLoading(state.months, action.month);
|
|
66
67
|
state.callback(notification);
|
|
67
|
-
return { ...state,
|
|
68
|
+
return { ...state, months };
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
case "NO_SLOTS_FOUND": {
|
|
@@ -99,19 +100,39 @@ export const statusReducer = (state, action) => {
|
|
|
99
100
|
|
|
100
101
|
case "SET_SLOTS": {
|
|
101
102
|
if (!action.slots.length > 0) {
|
|
102
|
-
const
|
|
103
|
+
const months = removeMonthFromLoading(state.months, action.month);
|
|
103
104
|
return {
|
|
104
105
|
...state,
|
|
105
|
-
|
|
106
|
+
months,
|
|
106
107
|
slotFetchCount: state.slotFetchCount + 1,
|
|
107
108
|
};
|
|
108
109
|
}
|
|
109
110
|
|
|
111
|
+
// Add action slots to state slots
|
|
110
112
|
const slotsObject = state.sequenced_availability
|
|
111
|
-
? addSequencedSlotsToObject(state.slots, action.slots)
|
|
113
|
+
? addSequencedSlotsToObject(state.slots, action.slots, state.query.sequence)
|
|
112
114
|
: addSlotsToObject(state.slots, action.slots);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
|
|
116
|
+
// since we already know the available days for state slots
|
|
117
|
+
// find available days for action.slots and add them to state availableDays
|
|
118
|
+
// this way we don't have to loop through the entirety of the state slotsObject
|
|
119
|
+
// every time new slots are rendered
|
|
120
|
+
const actionSlotsObject = state.sequenced_availability
|
|
121
|
+
? addSequencedSlotsToObject({}, action.slots, [])
|
|
122
|
+
: addSlotsToObject({}, action.slots);
|
|
123
|
+
|
|
124
|
+
const availableDays = uniqueItems([
|
|
125
|
+
...state.availableDays,
|
|
126
|
+
...getAvailableDays(actionSlotsObject, action.tzid),
|
|
127
|
+
]).sort();
|
|
128
|
+
|
|
129
|
+
// if month is passed in, we render that month as done
|
|
130
|
+
// this change is for continiously loading slots as opposed to waiting for entire month
|
|
131
|
+
// to be loaded
|
|
132
|
+
let months = state.months;
|
|
133
|
+
if (action.month) {
|
|
134
|
+
months = removeMonthFromLoading(state.months, action.month);
|
|
135
|
+
}
|
|
115
136
|
|
|
116
137
|
const injectionPoint = state.sequenced_availability
|
|
117
138
|
? getLocalDayFromUtc(action.slots[0].sequence[0].start, action.tzid)
|
|
@@ -120,7 +141,7 @@ export const statusReducer = (state, action) => {
|
|
|
120
141
|
return {
|
|
121
142
|
...state,
|
|
122
143
|
availableDays,
|
|
123
|
-
|
|
144
|
+
months,
|
|
124
145
|
slots: slotsObject,
|
|
125
146
|
slotFetchCount: state.slotFetchCount + 1,
|
|
126
147
|
slotInjectionPoint: injectionPoint,
|
|
@@ -10,6 +10,16 @@
|
|
|
10
10
|
font-size: 1.5em;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
.DTP__confirm-details--sequence-title {
|
|
14
|
+
display: inline-block;
|
|
15
|
+
font-weight: bold;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.DTP__confirm-details--sequence-time {
|
|
19
|
+
display: inline-block;
|
|
20
|
+
margin-left: 0.5rem;
|
|
21
|
+
}
|
|
22
|
+
|
|
13
23
|
.DTP__confirm-button {
|
|
14
24
|
@extend .DTP__button;
|
|
15
25
|
background-color: var(--buttonConfirm);
|
|
@@ -25,6 +25,16 @@
|
|
|
25
25
|
border-radius: 0.4em;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
.DTP__time-slot--detailed {
|
|
29
|
+
height: unset;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.DTP__detailed-slot-list,
|
|
33
|
+
.DTP__detailed-slot-list--item {
|
|
34
|
+
@extend %unset;
|
|
35
|
+
list-style: none;
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
.DTP__time-slot--selected {
|
|
29
39
|
@extend .DTP__button;
|
|
30
40
|
background-color: var(--buttonActive);
|