@final-commerce/common 2.4.0-preprod.1 → 2.4.0-preprod.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/dist/index.d.mts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +41 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +39 -9
- package/dist/index.mjs.map +1 -1
- package/dist/pos-types/index.d.mts +3 -0
- package/dist/pos-types/index.d.ts +3 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3051,6 +3051,26 @@ var stateFor = (hasShift, offerable, free) => {
|
|
|
3051
3051
|
}
|
|
3052
3052
|
return free === 0 ? "fullyBooked" /* FULLY_BOOKED */ : "available" /* AVAILABLE */;
|
|
3053
3053
|
};
|
|
3054
|
+
function shopDayKey(instant, timeZone) {
|
|
3055
|
+
const { year, month, day } = zonedParts(instant, timeZone);
|
|
3056
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
3057
|
+
return `${year}-${pad(month)}-${pad(day)}`;
|
|
3058
|
+
}
|
|
3059
|
+
function shopDayRange(fromDayKey, count, timeZone) {
|
|
3060
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(fromDayKey ?? "");
|
|
3061
|
+
if (!match) {
|
|
3062
|
+
throw new BookingRulesError(`Booking rules: "${fromDayKey}" is not a shop day (YYYY-MM-DD)`);
|
|
3063
|
+
}
|
|
3064
|
+
if (!Number.isInteger(count) || count < 1) {
|
|
3065
|
+
throw new BookingRulesError(`Booking rules: "${count}" is not a number of days`);
|
|
3066
|
+
}
|
|
3067
|
+
const [, year, month, day] = match.map(Number);
|
|
3068
|
+
const from = instantFromLocal(year, month, day, 0, timeZone);
|
|
3069
|
+
if (shopDayKey(from, timeZone) !== fromDayKey) {
|
|
3070
|
+
throw new BookingRulesError(`Booking rules: "${fromDayKey}" is not a real day`);
|
|
3071
|
+
}
|
|
3072
|
+
return { from, to: instantFromLocal(year, month, day + count, 0, timeZone) };
|
|
3073
|
+
}
|
|
3054
3074
|
function buildAvailability({
|
|
3055
3075
|
productId,
|
|
3056
3076
|
bookingRulesId,
|
|
@@ -3088,20 +3108,20 @@ function buildAvailability({
|
|
|
3088
3108
|
const grids = resources.map((resource) => {
|
|
3089
3109
|
const mine = byResource.get(resource.id) ?? [];
|
|
3090
3110
|
const busy = (resource.seats ?? 1) === 1 ? mine : [];
|
|
3091
|
-
const
|
|
3111
|
+
const slots2 = buildGrid(rule, resource.timeZone, from, to, now, busy).filter(
|
|
3092
3112
|
(slot) => withinPresence(slot, resource.timeZone, resource.presence)
|
|
3093
3113
|
);
|
|
3094
|
-
return { resource, slots };
|
|
3114
|
+
return { resource, slots: slots2 };
|
|
3095
3115
|
});
|
|
3096
3116
|
const byInstant = /* @__PURE__ */ new Map();
|
|
3097
3117
|
const freeSlotsPerResource = /* @__PURE__ */ new Map();
|
|
3098
|
-
for (const { resource, slots } of grids) {
|
|
3118
|
+
for (const { resource, slots: slots2 } of grids) {
|
|
3099
3119
|
const capacity = resource.seats ?? 1;
|
|
3100
3120
|
const mine = byResource.get(resource.id) ?? [];
|
|
3101
|
-
for (const slot of
|
|
3121
|
+
for (const slot of slots2) {
|
|
3102
3122
|
const { taken, foreignProductId } = occupancyIn(mine, productId, slot.startAt, slot.bufferEndAt, shared);
|
|
3103
3123
|
const free = foreignProductId ? 0 : Math.max(capacity - taken, 0);
|
|
3104
|
-
const entry = byInstant.get(+slot.startAt) ?? { ...slot, capacity: 0, booked: 0, free: 0, resources: [] };
|
|
3124
|
+
const entry = byInstant.get(+slot.startAt) ?? { ...slot, capacity: 0, booked: 0, free: 0, resources: [], dayKey: shopDayKey(slot.startAt, resource.timeZone) };
|
|
3105
3125
|
entry.capacity += capacity;
|
|
3106
3126
|
entry.booked += foreignProductId ? capacity : taken;
|
|
3107
3127
|
entry.free += free;
|
|
@@ -3111,27 +3131,35 @@ function buildAvailability({
|
|
|
3111
3131
|
byInstant.set(+slot.startAt, entry);
|
|
3112
3132
|
}
|
|
3113
3133
|
}
|
|
3114
|
-
const inWindow = grids.map(({ resource, slots }) => {
|
|
3134
|
+
const inWindow = grids.map(({ resource, slots: slots2 }) => {
|
|
3115
3135
|
const free = freeSlotsPerResource.get(resource.id) ?? 0;
|
|
3116
3136
|
return {
|
|
3117
3137
|
resourceId: resource.id,
|
|
3118
3138
|
name: resource.name,
|
|
3119
|
-
slots:
|
|
3139
|
+
slots: slots2.length,
|
|
3120
3140
|
free,
|
|
3121
3141
|
// Lazily: the shift grid is a second walk, and only an empty answer needs it.
|
|
3122
3142
|
state: stateFor(
|
|
3123
3143
|
() => buildGrid(rule, resource.timeZone, from, to, now).filter(
|
|
3124
3144
|
(slot) => withinPresence(slot, resource.timeZone, resource.presence)
|
|
3125
3145
|
).length > 0,
|
|
3126
|
-
|
|
3146
|
+
slots2.length,
|
|
3127
3147
|
free
|
|
3128
3148
|
)
|
|
3129
3149
|
};
|
|
3130
3150
|
});
|
|
3151
|
+
const slots = [...byInstant.values()].sort((first, second) => +first.startAt - +second.startAt);
|
|
3131
3152
|
return {
|
|
3132
3153
|
...header,
|
|
3133
3154
|
resources: inWindow,
|
|
3134
|
-
slots
|
|
3155
|
+
slots,
|
|
3156
|
+
/**
|
|
3157
|
+
* The clock every instant above reads on. Returned rather than assumed: without it a caller
|
|
3158
|
+
* has to fetch the zone from somewhere else and hope it is the same one the grid used.
|
|
3159
|
+
*/
|
|
3160
|
+
timeZone: resources[0]?.timeZone ?? null,
|
|
3161
|
+
/** The shop days this answer covers, in order — what a day picker lists. */
|
|
3162
|
+
days: [...new Set(slots.map((slot) => slot.dayKey))]
|
|
3135
3163
|
};
|
|
3136
3164
|
}
|
|
3137
3165
|
function presentAt(resources, rows, outletId) {
|
|
@@ -3326,6 +3354,8 @@ export {
|
|
|
3326
3354
|
roundToPrecision,
|
|
3327
3355
|
sameFamily,
|
|
3328
3356
|
setOrderState,
|
|
3357
|
+
shopDayKey,
|
|
3358
|
+
shopDayRange,
|
|
3329
3359
|
signReportForDisplay,
|
|
3330
3360
|
slotsForWindow,
|
|
3331
3361
|
stringifyMinorUnits,
|