@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.
Files changed (78) hide show
  1. package/README.md +9 -8
  2. package/lib/constants.d.ts +37 -0
  3. package/lib/constants.d.ts.map +1 -0
  4. package/lib/constants.js +37 -0
  5. package/lib/constants.js.map +1 -0
  6. package/lib/index.d.ts +11 -0
  7. package/lib/index.d.ts.map +1 -0
  8. package/lib/index.js +41 -0
  9. package/lib/index.js.map +1 -0
  10. package/lib/schedule/available-dates.d.ts +3 -0
  11. package/lib/schedule/available-dates.d.ts.map +1 -0
  12. package/lib/schedule/available-dates.js +84 -0
  13. package/lib/schedule/available-dates.js.map +1 -0
  14. package/lib/schedule/generate.d.ts +3 -0
  15. package/lib/schedule/generate.d.ts.map +1 -0
  16. package/lib/schedule/generate.js +155 -0
  17. package/lib/schedule/generate.js.map +1 -0
  18. package/lib/schedule/get-schedules.d.ts +3 -0
  19. package/lib/schedule/get-schedules.d.ts.map +1 -0
  20. package/lib/schedule/get-schedules.js +182 -0
  21. package/lib/schedule/get-schedules.js.map +1 -0
  22. package/lib/schedule/location.d.ts +3 -0
  23. package/lib/schedule/location.d.ts.map +1 -0
  24. package/lib/schedule/location.js +38 -0
  25. package/lib/schedule/location.js.map +1 -0
  26. package/lib/types/index.d.ts +7 -0
  27. package/lib/types/index.d.ts.map +1 -0
  28. package/lib/types/index.js +3 -0
  29. package/lib/types/index.js.map +1 -0
  30. package/lib/types.d.ts +24 -0
  31. package/lib/types.js +2 -0
  32. package/lib/utils/business-hours.d.ts +6 -0
  33. package/lib/utils/business-hours.d.ts.map +1 -0
  34. package/lib/utils/business-hours.js +82 -0
  35. package/lib/utils/business-hours.js.map +1 -0
  36. package/lib/utils/catering.d.ts +9 -0
  37. package/lib/utils/catering.d.ts.map +1 -0
  38. package/lib/utils/catering.js +56 -0
  39. package/lib/utils/catering.js.map +1 -0
  40. package/lib/utils/date.d.ts +15 -0
  41. package/lib/utils/date.d.ts.map +1 -0
  42. package/lib/utils/date.js +109 -0
  43. package/lib/utils/date.js.map +1 -0
  44. package/lib/utils/schedule-filter.d.ts +8 -0
  45. package/lib/utils/schedule-filter.d.ts.map +1 -0
  46. package/lib/utils/schedule-filter.js +95 -0
  47. package/lib/utils/schedule-filter.js.map +1 -0
  48. package/lib/utils/store-hours.d.ts +14 -0
  49. package/lib/utils/store-hours.d.ts.map +1 -0
  50. package/lib/utils/store-hours.js +120 -0
  51. package/lib/utils/store-hours.js.map +1 -0
  52. package/lib/utils/time.d.ts +12 -0
  53. package/lib/utils/time.d.ts.map +1 -0
  54. package/lib/utils/time.js +30 -0
  55. package/lib/utils/time.js.map +1 -0
  56. package/lib/utils.d.ts +8 -0
  57. package/lib/utils.js +18 -0
  58. package/package.json +8 -7
  59. package/src/constants.ts +0 -45
  60. package/src/index.ts +0 -23
  61. package/src/schedule/available-dates.ts +0 -129
  62. package/src/schedule/generate.ts +0 -276
  63. package/src/schedule/get-schedules.ts +0 -264
  64. package/src/schedule/location.ts +0 -58
  65. package/src/types/business-hours.d.ts +0 -32
  66. package/src/types/common.d.ts +0 -5
  67. package/src/types/get-schedules.d.ts +0 -122
  68. package/src/types/index.ts +0 -34
  69. package/src/types/location.d.ts +0 -25
  70. package/src/types/schedule-filter.d.ts +0 -27
  71. package/src/types/schedule.d.ts +0 -83
  72. package/src/types/timezone-support.d.ts +0 -31
  73. package/src/utils/business-hours.ts +0 -120
  74. package/src/utils/catering.ts +0 -85
  75. package/src/utils/date.ts +0 -163
  76. package/src/utils/schedule-filter.ts +0 -140
  77. package/src/utils/store-hours.ts +0 -223
  78. 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
- }
@@ -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
- }