@perdieminc/time-slots 0.0.1 → 0.0.3
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/README.md +9 -8
- package/lib/constants.d.ts +37 -0
- package/lib/constants.d.ts.map +1 -0
- package/lib/constants.js +37 -0
- package/lib/constants.js.map +1 -0
- package/lib/index.d.ts +11 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +41 -0
- package/lib/index.js.map +1 -0
- package/lib/schedule/available-dates.d.ts +3 -0
- package/lib/schedule/available-dates.d.ts.map +1 -0
- package/lib/schedule/available-dates.js +84 -0
- package/lib/schedule/available-dates.js.map +1 -0
- package/lib/schedule/generate.d.ts +3 -0
- package/lib/schedule/generate.d.ts.map +1 -0
- package/lib/schedule/generate.js +155 -0
- package/lib/schedule/generate.js.map +1 -0
- package/lib/schedule/get-schedules.d.ts +3 -0
- package/lib/schedule/get-schedules.d.ts.map +1 -0
- package/lib/schedule/get-schedules.js +182 -0
- package/lib/schedule/get-schedules.js.map +1 -0
- package/lib/schedule/location.d.ts +3 -0
- package/lib/schedule/location.d.ts.map +1 -0
- package/lib/schedule/location.js +38 -0
- package/lib/schedule/location.js.map +1 -0
- package/lib/types/index.d.ts +7 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +3 -0
- package/lib/types/index.js.map +1 -0
- package/lib/types.d.ts +24 -0
- package/lib/types.js +2 -0
- package/lib/utils/business-hours.d.ts +6 -0
- package/lib/utils/business-hours.d.ts.map +1 -0
- package/lib/utils/business-hours.js +82 -0
- package/lib/utils/business-hours.js.map +1 -0
- package/lib/utils/catering.d.ts +9 -0
- package/lib/utils/catering.d.ts.map +1 -0
- package/lib/utils/catering.js +56 -0
- package/lib/utils/catering.js.map +1 -0
- package/lib/utils/date.d.ts +15 -0
- package/lib/utils/date.d.ts.map +1 -0
- package/lib/utils/date.js +109 -0
- package/lib/utils/date.js.map +1 -0
- package/lib/utils/schedule-filter.d.ts +8 -0
- package/lib/utils/schedule-filter.d.ts.map +1 -0
- package/lib/utils/schedule-filter.js +95 -0
- package/lib/utils/schedule-filter.js.map +1 -0
- package/lib/utils/store-hours.d.ts +14 -0
- package/lib/utils/store-hours.d.ts.map +1 -0
- package/lib/utils/store-hours.js +120 -0
- package/lib/utils/store-hours.js.map +1 -0
- package/lib/utils/time.d.ts +12 -0
- package/lib/utils/time.d.ts.map +1 -0
- package/lib/utils/time.js +30 -0
- package/lib/utils/time.js.map +1 -0
- package/lib/utils.d.ts +8 -0
- package/lib/utils.js +18 -0
- package/package.json +8 -7
- package/src/constants.ts +0 -45
- package/src/index.ts +0 -23
- package/src/schedule/available-dates.ts +0 -129
- package/src/schedule/generate.ts +0 -276
- package/src/schedule/get-schedules.ts +0 -264
- package/src/schedule/location.ts +0 -58
- package/src/types/business-hours.d.ts +0 -32
- package/src/types/common.d.ts +0 -5
- package/src/types/get-schedules.d.ts +0 -122
- package/src/types/index.ts +0 -34
- package/src/types/location.d.ts +0 -25
- package/src/types/schedule-filter.d.ts +0 -27
- package/src/types/schedule.d.ts +0 -83
- package/src/types/timezone-support.d.ts +0 -31
- package/src/utils/business-hours.ts +0 -120
- package/src/utils/catering.ts +0 -85
- package/src/utils/date.ts +0 -163
- package/src/utils/schedule-filter.ts +0 -140
- package/src/utils/store-hours.ts +0 -223
- package/src/utils/time.ts +0 -38
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { findTimeZone, getZonedTime } from "timezone-support";
|
|
2
|
-
|
|
3
|
-
import type {
|
|
4
|
-
BusyTimeItem,
|
|
5
|
-
FilterBusyTimesFromScheduleParams,
|
|
6
|
-
FulfillmentSchedule,
|
|
7
|
-
MenuType,
|
|
8
|
-
} from "../types";
|
|
9
|
-
import { isTimeInRange } from "./time";
|
|
10
|
-
|
|
11
|
-
// ── Private helpers ─────────────────────────────────────────────────────────
|
|
12
|
-
|
|
13
|
-
function isSlotBusy(
|
|
14
|
-
applicableBusyTimes: BusyTimeItem[],
|
|
15
|
-
slotTimeValue: Date | number,
|
|
16
|
-
): boolean {
|
|
17
|
-
const slotTime = new Date(slotTimeValue);
|
|
18
|
-
|
|
19
|
-
if (Number.isNaN(slotTime.getTime())) {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return applicableBusyTimes.some((busyTime) => {
|
|
24
|
-
const busyStart = new Date(busyTime?.startTime);
|
|
25
|
-
const busyEnd = new Date(busyTime?.endTime);
|
|
26
|
-
|
|
27
|
-
if (Number.isNaN(busyStart.getTime()) || Number.isNaN(busyEnd.getTime())) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return slotTime > busyStart && slotTime <= busyEnd;
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// ── Public API ──────────────────────────────────────────────────────────────
|
|
36
|
-
|
|
37
|
-
export function filterBusyTimesFromSchedule({
|
|
38
|
-
schedule = [],
|
|
39
|
-
busyTimes = [],
|
|
40
|
-
cartCategoryIds = [],
|
|
41
|
-
}: FilterBusyTimesFromScheduleParams): FulfillmentSchedule {
|
|
42
|
-
if (!Array.isArray(schedule) || schedule.length === 0) {
|
|
43
|
-
return [];
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (!Array.isArray(busyTimes) || busyTimes.length === 0) {
|
|
47
|
-
return schedule;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const uniqueCartCategoryIds = Array.isArray(cartCategoryIds)
|
|
51
|
-
? Array.from(new Set(cartCategoryIds.filter(Boolean)))
|
|
52
|
-
: [];
|
|
53
|
-
|
|
54
|
-
const applicableBusyTimes = busyTimes.filter((busyTime) => {
|
|
55
|
-
const thresholdCategoryIds = busyTime?.threshold?.categoryIds || [];
|
|
56
|
-
|
|
57
|
-
if (!thresholdCategoryIds.length) {
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (!uniqueCartCategoryIds.length) {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return uniqueCartCategoryIds.some((cartCategoryId) =>
|
|
66
|
-
thresholdCategoryIds.includes(cartCategoryId),
|
|
67
|
-
);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
if (!applicableBusyTimes.length) {
|
|
71
|
-
return schedule;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return schedule
|
|
75
|
-
.map((daySchedule) => {
|
|
76
|
-
const slots = Array.isArray(daySchedule?.slots) ? daySchedule.slots : [];
|
|
77
|
-
const filteredSlots = slots.filter(
|
|
78
|
-
(slot) => !isSlotBusy(applicableBusyTimes, slot),
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
...daySchedule,
|
|
83
|
-
slots: filteredSlots,
|
|
84
|
-
openingTime: filteredSlots[0],
|
|
85
|
-
closingTime: filteredSlots[filteredSlots.length - 1],
|
|
86
|
-
firstAvailableSlot: filteredSlots[0],
|
|
87
|
-
};
|
|
88
|
-
})
|
|
89
|
-
.filter((daySchedule) => (daySchedule?.slots || []).length > 0);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function filterMenusFromSchedule({
|
|
93
|
-
schedule = [],
|
|
94
|
-
menus = [],
|
|
95
|
-
timeZone,
|
|
96
|
-
}: {
|
|
97
|
-
schedule?: FulfillmentSchedule;
|
|
98
|
-
menus?: MenuType[];
|
|
99
|
-
timeZone: string;
|
|
100
|
-
}): FulfillmentSchedule {
|
|
101
|
-
return schedule
|
|
102
|
-
.map((daySchedule) => ({
|
|
103
|
-
...daySchedule,
|
|
104
|
-
slots: daySchedule.slots.filter((slot) => {
|
|
105
|
-
const zonedSlot = getZonedTime(slot, findTimeZone(timeZone));
|
|
106
|
-
const dayOfWeek = zonedSlot.dayOfWeek;
|
|
107
|
-
|
|
108
|
-
if (!menus.length) {
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return menus.some((menu) => {
|
|
113
|
-
const dayScheduleConfig = menu.times[String(dayOfWeek)];
|
|
114
|
-
if (!dayScheduleConfig) {
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (dayScheduleConfig.all_day) {
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
// Only show slot if it falls within the configured time range
|
|
122
|
-
// Check for null start_time or end_time
|
|
123
|
-
if (!dayScheduleConfig.start_time || !dayScheduleConfig.end_time) {
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
return isTimeInRange(
|
|
127
|
-
{
|
|
128
|
-
start_time: dayScheduleConfig.start_time,
|
|
129
|
-
end_time: dayScheduleConfig.end_time,
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
hours: Number(zonedSlot.hours),
|
|
133
|
-
minutes: Number(zonedSlot.minutes),
|
|
134
|
-
},
|
|
135
|
-
);
|
|
136
|
-
});
|
|
137
|
-
}),
|
|
138
|
-
}))
|
|
139
|
-
.filter((daySchedule) => daySchedule.slots.length > 0);
|
|
140
|
-
}
|
package/src/utils/store-hours.ts
DELETED
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import { compareAsc, isBefore } from "date-fns";
|
|
2
|
-
import { findTimeZone, getZonedTime } from "timezone-support";
|
|
3
|
-
|
|
4
|
-
import { getNextAvailableDates } from "../schedule/available-dates";
|
|
5
|
-
import type {
|
|
6
|
-
BusinessHour,
|
|
7
|
-
BusinessHoursOverrideOutput,
|
|
8
|
-
FulfillmentPreference,
|
|
9
|
-
GetOpeningClosingTimeOnDateParams,
|
|
10
|
-
LocationLike,
|
|
11
|
-
} from "../types";
|
|
12
|
-
import { getLocationBusinessHoursForFulfillment } from "./business-hours";
|
|
13
|
-
import { isMidnightTransition, isTodayInTimeZone, setHmOnDate } from "./date";
|
|
14
|
-
|
|
15
|
-
// ── Private helpers ─────────────────────────────────────────────────────────
|
|
16
|
-
|
|
17
|
-
interface GetAvailableBusinessHoursParams {
|
|
18
|
-
businessHours?: BusinessHour[];
|
|
19
|
-
businessHoursOverrides?: BusinessHoursOverrideOutput[];
|
|
20
|
-
timeZone: string;
|
|
21
|
-
nextAvailableDate: Date;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function getAvailableBusinessHours({
|
|
25
|
-
businessHours = [],
|
|
26
|
-
businessHoursOverrides = [],
|
|
27
|
-
timeZone,
|
|
28
|
-
nextAvailableDate,
|
|
29
|
-
}: GetAvailableBusinessHoursParams): {
|
|
30
|
-
dayBusinessTimes: Array<{ startDate: Date; endDate: Date }>;
|
|
31
|
-
businessHoursOverride: BusinessHoursOverrideOutput | undefined;
|
|
32
|
-
} {
|
|
33
|
-
const zonedDate = getZonedTime(nextAvailableDate, findTimeZone(timeZone));
|
|
34
|
-
|
|
35
|
-
const dayBusinessHours = businessHours.filter(
|
|
36
|
-
(bh) =>
|
|
37
|
-
bh.day ===
|
|
38
|
-
getZonedTime(nextAvailableDate, findTimeZone(timeZone)).dayOfWeek,
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
const businessHoursOverride = businessHoursOverrides.find(
|
|
42
|
-
(override) =>
|
|
43
|
-
override.day === zonedDate.day && override.month === zonedDate.month,
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
const dayBusinessTimes = dayBusinessHours
|
|
47
|
-
.map((businessHour) => {
|
|
48
|
-
const effectiveHour = businessHoursOverride
|
|
49
|
-
? {
|
|
50
|
-
day: businessHour.day,
|
|
51
|
-
startTime: businessHoursOverride.startTime ?? "00:00",
|
|
52
|
-
endTime: businessHoursOverride.endTime ?? "23:59",
|
|
53
|
-
}
|
|
54
|
-
: businessHour;
|
|
55
|
-
|
|
56
|
-
const startDate = setHmOnDate(
|
|
57
|
-
nextAvailableDate,
|
|
58
|
-
effectiveHour.startTime,
|
|
59
|
-
timeZone,
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
const endDate = setHmOnDate(
|
|
63
|
-
nextAvailableDate,
|
|
64
|
-
effectiveHour.endTime,
|
|
65
|
-
timeZone,
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
if (!isBefore(startDate, endDate)) {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return { startDate, endDate };
|
|
73
|
-
})
|
|
74
|
-
.filter((time): time is { startDate: Date; endDate: Date } => time !== null)
|
|
75
|
-
.sort((a, b) => compareAsc(a.startDate, b.startDate));
|
|
76
|
-
|
|
77
|
-
return { dayBusinessTimes, businessHoursOverride };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// ── Public API ──────────────────────────────────────────────────────────────
|
|
81
|
-
|
|
82
|
-
export function getOpeningClosingTimeOnDate({
|
|
83
|
-
date = new Date(),
|
|
84
|
-
businessHours = [],
|
|
85
|
-
businessHoursOverrides = [],
|
|
86
|
-
timeZone,
|
|
87
|
-
}: GetOpeningClosingTimeOnDateParams): {
|
|
88
|
-
openingTime: Date;
|
|
89
|
-
closingTime: Date;
|
|
90
|
-
} | null {
|
|
91
|
-
try {
|
|
92
|
-
const nextAvailableDates = getNextAvailableDates({
|
|
93
|
-
startDate: date,
|
|
94
|
-
businessHours,
|
|
95
|
-
businessHoursOverrides,
|
|
96
|
-
timeZone,
|
|
97
|
-
datesCount: 7,
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
if (!Array.isArray(nextAvailableDates) || !nextAvailableDates.length) {
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
for (
|
|
105
|
-
let nextDateIndex = 0;
|
|
106
|
-
nextDateIndex < nextAvailableDates.length;
|
|
107
|
-
++nextDateIndex
|
|
108
|
-
) {
|
|
109
|
-
const nextAvailableDate = nextAvailableDates[nextDateIndex];
|
|
110
|
-
|
|
111
|
-
const { dayBusinessTimes, businessHoursOverride } =
|
|
112
|
-
getAvailableBusinessHours({
|
|
113
|
-
businessHours,
|
|
114
|
-
businessHoursOverrides,
|
|
115
|
-
timeZone,
|
|
116
|
-
nextAvailableDate,
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
if (!Array.isArray(dayBusinessTimes) || dayBusinessTimes.length === 0) {
|
|
120
|
-
if (
|
|
121
|
-
businessHoursOverride?.startTime &&
|
|
122
|
-
businessHoursOverride?.endTime
|
|
123
|
-
) {
|
|
124
|
-
const openingTime = setHmOnDate(
|
|
125
|
-
nextAvailableDate,
|
|
126
|
-
businessHoursOverride.startTime,
|
|
127
|
-
timeZone,
|
|
128
|
-
);
|
|
129
|
-
const closingTime = setHmOnDate(
|
|
130
|
-
nextAvailableDate,
|
|
131
|
-
businessHoursOverride.endTime,
|
|
132
|
-
timeZone,
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
if (isBefore(closingTime, date)) {
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return { openingTime, closingTime };
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const currentTime = date;
|
|
146
|
-
let currentSlot: { startDate: Date; endDate: Date } | null = null;
|
|
147
|
-
|
|
148
|
-
for (const slot of dayBusinessTimes) {
|
|
149
|
-
if (isBefore(currentTime, slot.endDate)) {
|
|
150
|
-
currentSlot = slot;
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (!currentSlot) {
|
|
156
|
-
currentSlot = dayBusinessTimes[0];
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (isBefore(currentSlot.endDate, date)) {
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (
|
|
164
|
-
isTodayInTimeZone(nextAvailableDate, timeZone) &&
|
|
165
|
-
nextDateIndex + 1 < nextAvailableDates.length
|
|
166
|
-
) {
|
|
167
|
-
const { dayBusinessTimes: nextDayTimes } = getAvailableBusinessHours({
|
|
168
|
-
businessHours,
|
|
169
|
-
businessHoursOverrides,
|
|
170
|
-
timeZone,
|
|
171
|
-
nextAvailableDate: nextAvailableDates[nextDateIndex + 1],
|
|
172
|
-
});
|
|
173
|
-
if (nextDayTimes.length) {
|
|
174
|
-
const firstNextDaySlot = nextDayTimes?.[0];
|
|
175
|
-
if (
|
|
176
|
-
firstNextDaySlot &&
|
|
177
|
-
isMidnightTransition(
|
|
178
|
-
currentSlot.endDate,
|
|
179
|
-
firstNextDaySlot.startDate,
|
|
180
|
-
timeZone,
|
|
181
|
-
)
|
|
182
|
-
) {
|
|
183
|
-
currentSlot = {
|
|
184
|
-
...currentSlot,
|
|
185
|
-
endDate: firstNextDaySlot.endDate,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
openingTime: currentSlot.startDate,
|
|
193
|
-
closingTime: currentSlot.endDate,
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return null;
|
|
198
|
-
} catch {
|
|
199
|
-
return null;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
export function getOpeningClosingTime({
|
|
204
|
-
location,
|
|
205
|
-
fulfillmentPreference,
|
|
206
|
-
businessHoursOverrides,
|
|
207
|
-
}: {
|
|
208
|
-
location: LocationLike;
|
|
209
|
-
fulfillmentPreference: FulfillmentPreference;
|
|
210
|
-
businessHoursOverrides?: Record<string, BusinessHoursOverrideOutput[]>;
|
|
211
|
-
}): { openingTime: Date; closingTime: Date } | null {
|
|
212
|
-
const businessHours = getLocationBusinessHoursForFulfillment(
|
|
213
|
-
location,
|
|
214
|
-
fulfillmentPreference,
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
return getOpeningClosingTimeOnDate({
|
|
218
|
-
businessHours,
|
|
219
|
-
businessHoursOverrides:
|
|
220
|
-
businessHoursOverrides?.[location.location_id] ?? [],
|
|
221
|
-
timeZone: location.timezone,
|
|
222
|
-
});
|
|
223
|
-
}
|
package/src/utils/time.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
export function parseTimeString(timeString: string | null | undefined): {
|
|
2
|
-
hours: number;
|
|
3
|
-
minutes: number;
|
|
4
|
-
} {
|
|
5
|
-
if (!timeString) {
|
|
6
|
-
return { hours: 0, minutes: 0 };
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const [hours = 0, minutes = 0] = String(timeString).split(":");
|
|
10
|
-
|
|
11
|
-
return { hours: Number(hours), minutes: Number(minutes) };
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function isTimeInRange(
|
|
15
|
-
schedule: { start_time?: string; end_time?: string },
|
|
16
|
-
time: { hours: number; minutes: number },
|
|
17
|
-
): boolean {
|
|
18
|
-
const startTime = parseTimeString(schedule?.start_time);
|
|
19
|
-
const endTime = parseTimeString(schedule?.end_time);
|
|
20
|
-
|
|
21
|
-
if (time.hours < startTime.hours || time.hours > endTime.hours) {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (time.hours === startTime.hours) {
|
|
26
|
-
if (time.minutes < startTime.minutes) {
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (time.hours === endTime.hours) {
|
|
32
|
-
if (time.minutes > endTime.minutes) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return true;
|
|
38
|
-
}
|