@happychef/algorithm 1.2.31 → 1.3.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.
Files changed (56) hide show
  1. package/.github/workflows/ci-cd.yml +80 -80
  2. package/CHANGELOG.md +8 -8
  3. package/RESERVERINGEN_GIDS.md +986 -986
  4. package/assignTables.js +444 -444
  5. package/changes/2025/December/PR2___change.md +14 -14
  6. package/changes/2025/December/PR3_add__change.md +20 -20
  7. package/changes/2025/December/PR4___.md +15 -15
  8. package/changes/2025/December/PR5___.md +15 -15
  9. package/changes/2025/December/PR6__del_.md +17 -17
  10. package/changes/2025/December/PR7_add__change.md +21 -21
  11. package/changes/2026/February/PR15_add__change.md +21 -21
  12. package/changes/2026/February/PR16_add_getDateClosingReasons.md +31 -0
  13. package/changes/2026/January/PR10_add__change.md +21 -21
  14. package/changes/2026/January/PR11_add__change.md +19 -19
  15. package/changes/2026/January/PR12_add__.md +21 -21
  16. package/changes/2026/January/PR13_add__change.md +20 -20
  17. package/changes/2026/January/PR14_add__change.md +19 -19
  18. package/changes/2026/January/PR8_add__change.md +38 -38
  19. package/changes/2026/January/PR9_add__change.md +19 -19
  20. package/filters/maxArrivalsFilter.js +114 -114
  21. package/filters/maxGroupsFilter.js +221 -221
  22. package/filters/timeFilter.js +89 -89
  23. package/getAvailableTimeblocks.js +158 -158
  24. package/getDateClosingReasons.js +193 -0
  25. package/grouping.js +162 -162
  26. package/index.js +43 -42
  27. package/isDateAvailable.js +80 -80
  28. package/isDateAvailableWithTableCheck.js +172 -172
  29. package/isTimeAvailable.js +26 -26
  30. package/package.json +27 -27
  31. package/processing/dailyGuestCounts.js +73 -73
  32. package/processing/mealTypeCount.js +133 -133
  33. package/processing/timeblocksAvailable.js +182 -182
  34. package/reservation_data/counter.js +74 -74
  35. package/restaurant_data/exceptions.js +150 -150
  36. package/restaurant_data/openinghours.js +142 -142
  37. package/simulateTableAssignment.js +726 -726
  38. package/tableHelpers.js +209 -209
  39. package/tables/time/parseTime.js +19 -19
  40. package/tables/time/shifts.js +7 -7
  41. package/tables/utils/calculateDistance.js +13 -13
  42. package/tables/utils/isTableFreeForAllSlots.js +14 -14
  43. package/tables/utils/isTemporaryTableValid.js +39 -39
  44. package/test/test_counter.js +194 -194
  45. package/test/test_dailyCount.js +81 -81
  46. package/test/test_datesAvailable.js +106 -106
  47. package/test/test_exceptions.js +172 -172
  48. package/test/test_isDateAvailable.js +330 -330
  49. package/test/test_mealTypeCount.js +54 -54
  50. package/test/test_timesAvailable.js +88 -88
  51. package/test-meal-stop-fix.js +147 -147
  52. package/test-meal-stop-simple.js +93 -93
  53. package/test.js +336 -336
  54. package/bundle_entry.js +0 -100
  55. package/moment-timezone-shim.js +0 -179
  56. package/nul +0 -0
@@ -1,172 +1,172 @@
1
- // file: /src/Pages/NewReservation/StepOne/algorithm/isDateAvailableWithTableCheck.js
2
- const moment = require('moment-timezone');
3
- const { isDateAvailable } = require('./isDateAvailable');
4
- const { getAvailableTimeblocks } = require('./getAvailableTimeblocks');
5
- const { isTimeAvailableSync } = require('./simulateTableAssignment');
6
- const { daysOfWeekEnglish, getMealTypeByTime } = require('./restaurant_data/openinghours');
7
-
8
- const TIMEZONE = 'Europe/Brussels';
9
-
10
- /**
11
- * Checks if the specific meal/time also contains the giftcard.
12
- * For instance, if Wednesday-lunch includes "Woensdag bon,"
13
- * but the user picks 19:00 (dinner), we should exclude that time.
14
- *
15
- * @param {Object} data - The restaurant data object.
16
- * @param {string} dateStr - "YYYY-MM-DD".
17
- * @param {string} timeStr - "HH:MM".
18
- * @param {string} giftcard - The selected giftcard.
19
- * @returns {boolean} true if the meal block for timeStr on dateStr includes the giftcard, else false.
20
- */
21
- function timeHasGiftcard(data, dateStr, timeStr, giftcard) {
22
- if (!giftcard || !giftcard.trim()) return true; // No giftcard => no restriction
23
-
24
- // Figure out the meal type from timeStr
25
- const mealType = getMealTypeByTime(timeStr); // returns "breakfast", "lunch", or "dinner"
26
- if (!mealType) {
27
- return false; // Not in any recognized meal
28
- }
29
-
30
- // Day-of-week in Brussels TZ
31
- const m = moment.tz(dateStr, 'YYYY-MM-DD', TIMEZONE);
32
- if (!m.isValid()) return false;
33
- const englishDay = daysOfWeekEnglish[m.day()];
34
-
35
- const ohKey = `openinghours-${mealType}`;
36
- const daySettings =
37
- data[ohKey] &&
38
- data[ohKey].schemeSettings &&
39
- data[ohKey].schemeSettings[englishDay]
40
- ? data[ohKey].schemeSettings[englishDay]
41
- : null;
42
-
43
- if (!daySettings) {
44
- console.error(`No day settings found for ${englishDay} ${mealType}`);
45
- return false;
46
- }
47
-
48
- if (daySettings.giftcardsEnabled !== true) {
49
- return false;
50
- }
51
-
52
- if (!Array.isArray(daySettings.giftcards) || daySettings.giftcards.length === 0) {
53
- return false;
54
- }
55
-
56
- // Check if the selected giftcard is in the allowed list
57
- return daySettings.giftcards.includes(giftcard);
58
- }
59
-
60
- /**
61
- * Checks if a date is available for a reservation with a table assignment check.
62
- * This extends isDateAvailable by also verifying that at least one time slot
63
- * has an available table AND (if giftcard is selected) that the time belongs
64
- * to a meal that includes that giftcard. It also filters dates based on the
65
- * selected menu item's filterStartDate and filterEndDate from
66
- * window.currentReservationTicketItem, but only when normalOpeningTimes is false.
67
- *
68
- * @param {Object} data - The restaurant data object containing settings and table information.
69
- * @param {string} dateStr - The date string in "YYYY-MM-DD" format.
70
- * @param {Array} reservations - An array of reservation objects.
71
- * @param {number} guests - The number of guests for the reservation.
72
- * @param {Array} blockedSlots - Optional array of blocked time slots to exclude.
73
- * @param {string|null} selectedGiftcard - The selected giftcard (if any).
74
- * @param {boolean} isAdmin - Optional flag to bypass time restrictions for admin users.
75
- * @returns {boolean} - Returns true if the date passes all checks to be available.
76
- */
77
- function isDateAvailableWithTableCheck(data, dateStr, reservations, guests, blockedSlots = [], selectedGiftcard = null, isAdmin = false, duration = null, selectedZitplaats = null) {
78
- // 0) Optionally filter by selected menu item's date range (only when normalOpeningTimes = false)
79
- const currentItem = typeof window !== 'undefined' ? window.currentReservationTicketItem : null;
80
- if (
81
- currentItem &&
82
- !currentItem.normalOpeningTimes
83
- ) {
84
- const dateM = moment.tz(dateStr, 'YYYY-MM-DD', TIMEZONE).startOf('day');
85
- const startM = currentItem.filterStartDate
86
- ? moment.tz(currentItem.filterStartDate, 'YYYY-MM-DD', TIMEZONE).startOf('day')
87
- : null;
88
- const endM = currentItem.filterEndDate
89
- ? moment.tz(currentItem.filterEndDate, 'YYYY-MM-DD', TIMEZONE).endOf('day')
90
- : null;
91
-
92
- if (startM && dateM.isBefore(startM)) {
93
- console.log(`Date ${dateStr} is before the allowed start ${currentItem.filterStartDate}`);
94
- return false;
95
- }
96
- if (endM && dateM.isAfter(endM)) {
97
- console.log(`Date ${dateStr} is after the allowed end ${currentItem.filterEndDate}`);
98
- return false;
99
- }
100
- }
101
-
102
- console.log(`\n===== Checking date availability for ${guests} guests on ${dateStr} =====`);
103
- console.log('Restaurant data floors:', data.floors ? 'present' : 'missing');
104
- console.log('Table settings:', JSON.stringify(data['table-settings'] || {}, null, 2));
105
-
106
- // Calculate table assignment flag once to reduce redundant checks
107
- const tableSettings = data?.['table-settings'] || {};
108
- const isTableAssignmentEnabled =
109
- tableSettings.isInstalled === true &&
110
- tableSettings.assignmentMode === 'automatic' &&
111
- data.floors &&
112
- Array.isArray(data.floors);
113
-
114
- console.log(`Table assignment is ${isTableAssignmentEnabled ? 'ENABLED' : 'DISABLED'} for date ${dateStr}`);
115
-
116
- // 1) First, do the standard day-level checks (including simple giftcard check).
117
- const basicDateAvailable = isDateAvailable(data, dateStr, reservations, guests, blockedSlots, selectedGiftcard, isAdmin, duration);
118
- if (!basicDateAvailable) {
119
- console.log(`Date ${dateStr} fails basic availability check`);
120
- return false;
121
- }
122
-
123
- // If table assignment is not enabled, stop here - the date is available
124
- if (!isTableAssignmentEnabled) {
125
- console.log(`Table assignment disabled, date ${dateStr} is available`);
126
- return true;
127
- }
128
-
129
- // 2) Get all available timeblocks for this date
130
- console.log(`Getting available timeblocks for ${dateStr}`);
131
- const availableTimeblocks = getAvailableTimeblocks(data, dateStr, reservations, guests, blockedSlots, selectedGiftcard, isAdmin, duration);
132
- console.log(`Found ${Object.keys(availableTimeblocks).length} available timeblocks before table check`);
133
-
134
- // If no timeblocks are available at all, exit early
135
- if (Object.keys(availableTimeblocks).length === 0) {
136
- console.log(`No available timeblocks on ${dateStr}, date is unavailable`);
137
- return false;
138
- }
139
-
140
- // 3) Check if any timeblock has an available table and satisfies giftcard requirements
141
- console.log(`Checking table availability for each timeblock on ${dateStr}`);
142
-
143
- let atLeastOneAvailable = false;
144
- let checkedCount = 0;
145
-
146
- for (const time of Object.keys(availableTimeblocks)) {
147
- checkedCount++;
148
-
149
- // If there's a giftcard, confirm this particular time belongs to a meal that has that giftcard
150
- if (selectedGiftcard && !timeHasGiftcard(data, dateStr, time, selectedGiftcard)) {
151
- console.log(`Time ${time} doesn't match giftcard meal requirements`);
152
- continue;
153
- }
154
-
155
- console.log(`Checking table availability for ${time} on ${dateStr} for ${guests} guests`);
156
- // Pass selectedZitplaats so floor link constraints are respected
157
- if (isTimeAvailableSync(data, dateStr, time, guests, reservations, selectedZitplaats, duration)) {
158
- console.log(`Found available time ${time} with table assignment!`);
159
- atLeastOneAvailable = true;
160
- break;
161
- }
162
- }
163
-
164
- console.log(`Checked ${checkedCount}/${Object.keys(availableTimeblocks).length} timeblocks`);
165
- console.log(`Date ${dateStr} with table check: ${atLeastOneAvailable ? 'Available' : 'Unavailable'}`);
166
-
167
- return atLeastOneAvailable;
168
- }
169
-
170
- module.exports = {
171
- isDateAvailableWithTableCheck,
172
- };
1
+ // file: /src/Pages/NewReservation/StepOne/algorithm/isDateAvailableWithTableCheck.js
2
+ const moment = require('moment-timezone');
3
+ const { isDateAvailable } = require('./isDateAvailable');
4
+ const { getAvailableTimeblocks } = require('./getAvailableTimeblocks');
5
+ const { isTimeAvailableSync } = require('./simulateTableAssignment');
6
+ const { daysOfWeekEnglish, getMealTypeByTime } = require('./restaurant_data/openinghours');
7
+
8
+ const TIMEZONE = 'Europe/Brussels';
9
+
10
+ /**
11
+ * Checks if the specific meal/time also contains the giftcard.
12
+ * For instance, if Wednesday-lunch includes "Woensdag bon,"
13
+ * but the user picks 19:00 (dinner), we should exclude that time.
14
+ *
15
+ * @param {Object} data - The restaurant data object.
16
+ * @param {string} dateStr - "YYYY-MM-DD".
17
+ * @param {string} timeStr - "HH:MM".
18
+ * @param {string} giftcard - The selected giftcard.
19
+ * @returns {boolean} true if the meal block for timeStr on dateStr includes the giftcard, else false.
20
+ */
21
+ function timeHasGiftcard(data, dateStr, timeStr, giftcard) {
22
+ if (!giftcard || !giftcard.trim()) return true; // No giftcard => no restriction
23
+
24
+ // Figure out the meal type from timeStr
25
+ const mealType = getMealTypeByTime(timeStr); // returns "breakfast", "lunch", or "dinner"
26
+ if (!mealType) {
27
+ return false; // Not in any recognized meal
28
+ }
29
+
30
+ // Day-of-week in Brussels TZ
31
+ const m = moment.tz(dateStr, 'YYYY-MM-DD', TIMEZONE);
32
+ if (!m.isValid()) return false;
33
+ const englishDay = daysOfWeekEnglish[m.day()];
34
+
35
+ const ohKey = `openinghours-${mealType}`;
36
+ const daySettings =
37
+ data[ohKey] &&
38
+ data[ohKey].schemeSettings &&
39
+ data[ohKey].schemeSettings[englishDay]
40
+ ? data[ohKey].schemeSettings[englishDay]
41
+ : null;
42
+
43
+ if (!daySettings) {
44
+ console.error(`No day settings found for ${englishDay} ${mealType}`);
45
+ return false;
46
+ }
47
+
48
+ if (daySettings.giftcardsEnabled !== true) {
49
+ return false;
50
+ }
51
+
52
+ if (!Array.isArray(daySettings.giftcards) || daySettings.giftcards.length === 0) {
53
+ return false;
54
+ }
55
+
56
+ // Check if the selected giftcard is in the allowed list
57
+ return daySettings.giftcards.includes(giftcard);
58
+ }
59
+
60
+ /**
61
+ * Checks if a date is available for a reservation with a table assignment check.
62
+ * This extends isDateAvailable by also verifying that at least one time slot
63
+ * has an available table AND (if giftcard is selected) that the time belongs
64
+ * to a meal that includes that giftcard. It also filters dates based on the
65
+ * selected menu item's filterStartDate and filterEndDate from
66
+ * window.currentReservationTicketItem, but only when normalOpeningTimes is false.
67
+ *
68
+ * @param {Object} data - The restaurant data object containing settings and table information.
69
+ * @param {string} dateStr - The date string in "YYYY-MM-DD" format.
70
+ * @param {Array} reservations - An array of reservation objects.
71
+ * @param {number} guests - The number of guests for the reservation.
72
+ * @param {Array} blockedSlots - Optional array of blocked time slots to exclude.
73
+ * @param {string|null} selectedGiftcard - The selected giftcard (if any).
74
+ * @param {boolean} isAdmin - Optional flag to bypass time restrictions for admin users.
75
+ * @returns {boolean} - Returns true if the date passes all checks to be available.
76
+ */
77
+ function isDateAvailableWithTableCheck(data, dateStr, reservations, guests, blockedSlots = [], selectedGiftcard = null, isAdmin = false, duration = null, selectedZitplaats = null) {
78
+ // 0) Optionally filter by selected menu item's date range (only when normalOpeningTimes = false)
79
+ const currentItem = typeof window !== 'undefined' ? window.currentReservationTicketItem : null;
80
+ if (
81
+ currentItem &&
82
+ !currentItem.normalOpeningTimes
83
+ ) {
84
+ const dateM = moment.tz(dateStr, 'YYYY-MM-DD', TIMEZONE).startOf('day');
85
+ const startM = currentItem.filterStartDate
86
+ ? moment.tz(currentItem.filterStartDate, 'YYYY-MM-DD', TIMEZONE).startOf('day')
87
+ : null;
88
+ const endM = currentItem.filterEndDate
89
+ ? moment.tz(currentItem.filterEndDate, 'YYYY-MM-DD', TIMEZONE).endOf('day')
90
+ : null;
91
+
92
+ if (startM && dateM.isBefore(startM)) {
93
+ console.log(`Date ${dateStr} is before the allowed start ${currentItem.filterStartDate}`);
94
+ return false;
95
+ }
96
+ if (endM && dateM.isAfter(endM)) {
97
+ console.log(`Date ${dateStr} is after the allowed end ${currentItem.filterEndDate}`);
98
+ return false;
99
+ }
100
+ }
101
+
102
+ console.log(`\n===== Checking date availability for ${guests} guests on ${dateStr} =====`);
103
+ console.log('Restaurant data floors:', data.floors ? 'present' : 'missing');
104
+ console.log('Table settings:', JSON.stringify(data['table-settings'] || {}, null, 2));
105
+
106
+ // Calculate table assignment flag once to reduce redundant checks
107
+ const tableSettings = data?.['table-settings'] || {};
108
+ const isTableAssignmentEnabled =
109
+ tableSettings.isInstalled === true &&
110
+ tableSettings.assignmentMode === 'automatic' &&
111
+ data.floors &&
112
+ Array.isArray(data.floors);
113
+
114
+ console.log(`Table assignment is ${isTableAssignmentEnabled ? 'ENABLED' : 'DISABLED'} for date ${dateStr}`);
115
+
116
+ // 1) First, do the standard day-level checks (including simple giftcard check).
117
+ const basicDateAvailable = isDateAvailable(data, dateStr, reservations, guests, blockedSlots, selectedGiftcard, isAdmin, duration);
118
+ if (!basicDateAvailable) {
119
+ console.log(`Date ${dateStr} fails basic availability check`);
120
+ return false;
121
+ }
122
+
123
+ // If table assignment is not enabled, stop here - the date is available
124
+ if (!isTableAssignmentEnabled) {
125
+ console.log(`Table assignment disabled, date ${dateStr} is available`);
126
+ return true;
127
+ }
128
+
129
+ // 2) Get all available timeblocks for this date
130
+ console.log(`Getting available timeblocks for ${dateStr}`);
131
+ const availableTimeblocks = getAvailableTimeblocks(data, dateStr, reservations, guests, blockedSlots, selectedGiftcard, isAdmin, duration);
132
+ console.log(`Found ${Object.keys(availableTimeblocks).length} available timeblocks before table check`);
133
+
134
+ // If no timeblocks are available at all, exit early
135
+ if (Object.keys(availableTimeblocks).length === 0) {
136
+ console.log(`No available timeblocks on ${dateStr}, date is unavailable`);
137
+ return false;
138
+ }
139
+
140
+ // 3) Check if any timeblock has an available table and satisfies giftcard requirements
141
+ console.log(`Checking table availability for each timeblock on ${dateStr}`);
142
+
143
+ let atLeastOneAvailable = false;
144
+ let checkedCount = 0;
145
+
146
+ for (const time of Object.keys(availableTimeblocks)) {
147
+ checkedCount++;
148
+
149
+ // If there's a giftcard, confirm this particular time belongs to a meal that has that giftcard
150
+ if (selectedGiftcard && !timeHasGiftcard(data, dateStr, time, selectedGiftcard)) {
151
+ console.log(`Time ${time} doesn't match giftcard meal requirements`);
152
+ continue;
153
+ }
154
+
155
+ console.log(`Checking table availability for ${time} on ${dateStr} for ${guests} guests`);
156
+ // Pass selectedZitplaats so floor link constraints are respected
157
+ if (isTimeAvailableSync(data, dateStr, time, guests, reservations, selectedZitplaats, duration)) {
158
+ console.log(`Found available time ${time} with table assignment!`);
159
+ atLeastOneAvailable = true;
160
+ break;
161
+ }
162
+ }
163
+
164
+ console.log(`Checked ${checkedCount}/${Object.keys(availableTimeblocks).length} timeblocks`);
165
+ console.log(`Date ${dateStr} with table check: ${atLeastOneAvailable ? 'Available' : 'Unavailable'}`);
166
+
167
+ return atLeastOneAvailable;
168
+ }
169
+
170
+ module.exports = {
171
+ isDateAvailableWithTableCheck,
172
+ };
@@ -1,26 +1,26 @@
1
- const { timeblocksAvailable } = require('./processing/timeblocksAvailable');
2
-
3
- /**
4
- * Checks if a given time (HH:MM) is available for a reservation.
5
- * @param {Object} data - The main data object containing settings and opening hours.
6
- * @param {string} dateStr - The date string in "YYYY-MM-DD" format.
7
- * @param {string} timeStr - The time string in "HH:MM" format.
8
- * @param {Array} reservations - An array of existing reservation objects.
9
- * @param {number} guests - The number of guests for the new reservation request.
10
- * @param {Array} blockedSlots - Optional array of blocked time slots to exclude.
11
- * @param {string|null} giftcard - Optional giftcard to filter times by meal.
12
- * @param {boolean} isAdmin - Optional flag to bypass time restrictions for admin users.
13
- * @param {number|null} duration - Optional duration in minutes (for bowling restaurants). Falls back to settings if not provided.
14
- * @returns {boolean} - Returns true if the time is available, false otherwise.
15
- */
16
- function isTimeAvailable(data, dateStr, timeStr, reservations, guests, blockedSlots = [], giftcard = null, isAdmin = false, duration = null) {
17
- // Get all available timeblocks for the specified date and guest count
18
- const availableTimeblocks = timeblocksAvailable(data, dateStr, reservations, guests, blockedSlots, giftcard, isAdmin, duration);
19
-
20
- // Check if the specific timeStr is one of the available keys
21
- return Object.prototype.hasOwnProperty.call(availableTimeblocks, timeStr);
22
- }
23
-
24
- module.exports = {
25
- isTimeAvailable,
26
- };
1
+ const { timeblocksAvailable } = require('./processing/timeblocksAvailable');
2
+
3
+ /**
4
+ * Checks if a given time (HH:MM) is available for a reservation.
5
+ * @param {Object} data - The main data object containing settings and opening hours.
6
+ * @param {string} dateStr - The date string in "YYYY-MM-DD" format.
7
+ * @param {string} timeStr - The time string in "HH:MM" format.
8
+ * @param {Array} reservations - An array of existing reservation objects.
9
+ * @param {number} guests - The number of guests for the new reservation request.
10
+ * @param {Array} blockedSlots - Optional array of blocked time slots to exclude.
11
+ * @param {string|null} giftcard - Optional giftcard to filter times by meal.
12
+ * @param {boolean} isAdmin - Optional flag to bypass time restrictions for admin users.
13
+ * @param {number|null} duration - Optional duration in minutes (for bowling restaurants). Falls back to settings if not provided.
14
+ * @returns {boolean} - Returns true if the time is available, false otherwise.
15
+ */
16
+ function isTimeAvailable(data, dateStr, timeStr, reservations, guests, blockedSlots = [], giftcard = null, isAdmin = false, duration = null) {
17
+ // Get all available timeblocks for the specified date and guest count
18
+ const availableTimeblocks = timeblocksAvailable(data, dateStr, reservations, guests, blockedSlots, giftcard, isAdmin, duration);
19
+
20
+ // Check if the specific timeStr is one of the available keys
21
+ return Object.prototype.hasOwnProperty.call(availableTimeblocks, timeStr);
22
+ }
23
+
24
+ module.exports = {
25
+ isTimeAvailable,
26
+ };
package/package.json CHANGED
@@ -1,27 +1,27 @@
1
- {
2
- "name": "@happychef/algorithm",
3
- "version": "1.2.31",
4
- "description": "Restaurant and reservation algorithm utilities",
5
- "main": "index.js",
6
- "scripts": {
7
- "test": "jest",
8
- "test:watch": "jest --watch",
9
- "test:coverage": "jest --coverage"
10
- },
11
- "author": "happy chef",
12
- "license": "MIT",
13
- "private": false,
14
- "keywords": [
15
- "algorithm",
16
- "restaurant",
17
- "reservation",
18
- "utilities"
19
- ],
20
- "dependencies": {
21
- "moment-timezone": "^0.6.0"
22
- },
23
- "devDependencies": {
24
- "@types/jest": "^30.0.0",
25
- "jest": "^30.2.0"
26
- }
27
- }
1
+ {
2
+ "name": "@happychef/algorithm",
3
+ "version": "1.3.0",
4
+ "description": "Restaurant and reservation algorithm utilities",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "jest",
8
+ "test:watch": "jest --watch",
9
+ "test:coverage": "jest --coverage"
10
+ },
11
+ "author": "happy chef",
12
+ "license": "MIT",
13
+ "private": false,
14
+ "keywords": [
15
+ "algorithm",
16
+ "restaurant",
17
+ "reservation",
18
+ "utilities"
19
+ ],
20
+ "dependencies": {
21
+ "moment-timezone": "^0.6.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/jest": "^30.0.0",
25
+ "jest": "^30.2.0"
26
+ }
27
+ }
@@ -1,73 +1,73 @@
1
- // dailyGuestCounts.js
2
-
3
- const { getGuestCountsForMeal } = require('./mealTypeCount');
4
-
5
- /**
6
- * Calculates guest counts for breakfast, lunch, and dinner, and combines the results into a flat object.
7
- * If time slots overlap, the available seats from the latest meal (dinner > lunch > breakfast) are used.
8
- * @param {Object} data - The main data object.
9
- * @param {string} dateStr - The date string (YYYY-MM-DD).
10
- * @param {Array} reservations - An array of reservation objects.
11
- * @returns {Object} An object containing combined guest counts for all meals with time slots as keys,
12
- * and an array of shiftsInfo containing shift details.
13
- */
14
- function getDailyGuestCounts(data, dateStr, reservations) {
15
- // Define meal types in order of priority (lowest to highest)
16
- const mealTypes = ['breakfast', 'lunch', 'dinner'];
17
- const combinedGuestCounts = {};
18
- const shiftsInfo = [];
19
- const mealPriority = {
20
- 'breakfast': 1,
21
- 'lunch': 2,
22
- 'dinner': 3
23
- };
24
-
25
- for (const mealType of mealTypes) {
26
- const result = getGuestCountsForMeal(data, dateStr, mealType, reservations);
27
- if (result) {
28
- const { guestCounts, shiftsInfo: mealShiftsInfo } = result;
29
-
30
- // Merge guestCounts into combinedGuestCounts
31
- for (const [time, availableSeats] of Object.entries(guestCounts)) {
32
- if (combinedGuestCounts.hasOwnProperty(time)) {
33
- // Compare meal priorities
34
- const existingMealPriority = combinedGuestCounts[time].mealPriority;
35
- const currentMealPriority = mealPriority[mealType];
36
-
37
- if (currentMealPriority >= existingMealPriority) {
38
- // Update with the current meal's available seats and priority
39
- combinedGuestCounts[time] = {
40
- availableSeats,
41
- mealPriority: currentMealPriority
42
- };
43
- }
44
- // Else, keep the existing value
45
- } else {
46
- // Add new time slot with available seats and meal priority
47
- combinedGuestCounts[time] = {
48
- availableSeats,
49
- mealPriority: mealPriority[mealType]
50
- };
51
- }
52
- }
53
-
54
- // Merge shiftsInfo
55
- if (mealShiftsInfo && mealShiftsInfo.length > 0) {
56
- shiftsInfo.push(...mealShiftsInfo);
57
- }
58
- }
59
- // Else do nothing if the meal is not available
60
- }
61
-
62
- // Extract only the availableSeats for the final output
63
- const finalGuestCounts = {};
64
- for (const [time, data] of Object.entries(combinedGuestCounts)) {
65
- finalGuestCounts[time] = data.availableSeats;
66
- }
67
-
68
- return { guestCounts: finalGuestCounts, shiftsInfo };
69
- }
70
-
71
- module.exports = {
72
- getDailyGuestCounts,
73
- };
1
+ // dailyGuestCounts.js
2
+
3
+ const { getGuestCountsForMeal } = require('./mealTypeCount');
4
+
5
+ /**
6
+ * Calculates guest counts for breakfast, lunch, and dinner, and combines the results into a flat object.
7
+ * If time slots overlap, the available seats from the latest meal (dinner > lunch > breakfast) are used.
8
+ * @param {Object} data - The main data object.
9
+ * @param {string} dateStr - The date string (YYYY-MM-DD).
10
+ * @param {Array} reservations - An array of reservation objects.
11
+ * @returns {Object} An object containing combined guest counts for all meals with time slots as keys,
12
+ * and an array of shiftsInfo containing shift details.
13
+ */
14
+ function getDailyGuestCounts(data, dateStr, reservations) {
15
+ // Define meal types in order of priority (lowest to highest)
16
+ const mealTypes = ['breakfast', 'lunch', 'dinner'];
17
+ const combinedGuestCounts = {};
18
+ const shiftsInfo = [];
19
+ const mealPriority = {
20
+ 'breakfast': 1,
21
+ 'lunch': 2,
22
+ 'dinner': 3
23
+ };
24
+
25
+ for (const mealType of mealTypes) {
26
+ const result = getGuestCountsForMeal(data, dateStr, mealType, reservations);
27
+ if (result) {
28
+ const { guestCounts, shiftsInfo: mealShiftsInfo } = result;
29
+
30
+ // Merge guestCounts into combinedGuestCounts
31
+ for (const [time, availableSeats] of Object.entries(guestCounts)) {
32
+ if (combinedGuestCounts.hasOwnProperty(time)) {
33
+ // Compare meal priorities
34
+ const existingMealPriority = combinedGuestCounts[time].mealPriority;
35
+ const currentMealPriority = mealPriority[mealType];
36
+
37
+ if (currentMealPriority >= existingMealPriority) {
38
+ // Update with the current meal's available seats and priority
39
+ combinedGuestCounts[time] = {
40
+ availableSeats,
41
+ mealPriority: currentMealPriority
42
+ };
43
+ }
44
+ // Else, keep the existing value
45
+ } else {
46
+ // Add new time slot with available seats and meal priority
47
+ combinedGuestCounts[time] = {
48
+ availableSeats,
49
+ mealPriority: mealPriority[mealType]
50
+ };
51
+ }
52
+ }
53
+
54
+ // Merge shiftsInfo
55
+ if (mealShiftsInfo && mealShiftsInfo.length > 0) {
56
+ shiftsInfo.push(...mealShiftsInfo);
57
+ }
58
+ }
59
+ // Else do nothing if the meal is not available
60
+ }
61
+
62
+ // Extract only the availableSeats for the final output
63
+ const finalGuestCounts = {};
64
+ for (const [time, data] of Object.entries(combinedGuestCounts)) {
65
+ finalGuestCounts[time] = data.availableSeats;
66
+ }
67
+
68
+ return { guestCounts: finalGuestCounts, shiftsInfo };
69
+ }
70
+
71
+ module.exports = {
72
+ getDailyGuestCounts,
73
+ };