@final-commerce/command-frame 0.6.2-staging3.1 → 0.7.0-staging.10
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/CommonTypes.d.ts +26 -14
- package/dist/actions/add-booking-to-cart/action.d.ts +6 -0
- package/dist/actions/add-booking-to-cart/action.js +8 -0
- package/dist/actions/add-booking-to-cart/mock.d.ts +2 -0
- package/dist/actions/add-booking-to-cart/mock.js +42 -0
- package/dist/actions/add-booking-to-cart/types.d.ts +23 -0
- package/dist/actions/add-product-to-cart/mock.js +1 -18
- package/dist/actions/add-product-to-cart/types.d.ts +0 -10
- package/dist/actions/cancel-booking/action.d.ts +6 -0
- package/dist/actions/cancel-booking/action.js +8 -0
- package/dist/actions/cancel-booking/mock.d.ts +2 -0
- package/dist/actions/cancel-booking/mock.js +10 -0
- package/dist/actions/cancel-booking/types.d.ts +10 -0
- package/dist/actions/get-booking-availability/action.d.ts +6 -0
- package/dist/actions/get-booking-availability/action.js +8 -0
- package/dist/actions/get-booking-availability/mock.d.ts +2 -0
- package/dist/actions/get-booking-availability/mock.js +6 -0
- package/dist/actions/get-booking-availability/types.d.ts +18 -0
- package/dist/actions/get-booking-resources/action.d.ts +6 -0
- package/dist/actions/get-booking-resources/action.js +8 -0
- package/dist/actions/get-booking-resources/mock.d.ts +2 -0
- package/dist/actions/get-booking-resources/mock.js +7 -0
- package/dist/actions/get-booking-resources/types.d.ts +12 -0
- package/dist/actions/get-booking-resources/types.js +1 -0
- package/dist/actions/get-bookings/action.d.ts +6 -0
- package/dist/actions/get-bookings/action.js +8 -0
- package/dist/actions/get-bookings/mock.d.ts +2 -0
- package/dist/actions/get-bookings/mock.js +20 -0
- package/dist/actions/get-bookings/types.d.ts +18 -0
- package/dist/actions/get-bookings/types.js +1 -0
- package/dist/actions/get-context/mock.js +4 -0
- package/dist/actions/get-refund-plan/types.d.ts +5 -2
- package/dist/actions/get-remaining-refundable-quantities/mock.js +1 -0
- package/dist/actions/get-remaining-refundable-quantities/types.d.ts +8 -0
- package/dist/actions/hold-booking/action.d.ts +6 -0
- package/dist/actions/hold-booking/action.js +8 -0
- package/dist/actions/hold-booking/mock.d.ts +2 -0
- package/dist/actions/hold-booking/mock.js +33 -0
- package/dist/actions/hold-booking/types.d.ts +24 -0
- package/dist/actions/hold-booking/types.js +1 -0
- package/dist/actions/process-partial-refund/types.d.ts +2 -2
- package/dist/actions/remove-booking-from-cart/action.d.ts +6 -0
- package/dist/actions/remove-booking-from-cart/action.js +8 -0
- package/dist/actions/remove-booking-from-cart/mock.d.ts +2 -0
- package/dist/actions/remove-booking-from-cart/mock.js +26 -0
- package/dist/actions/remove-booking-from-cart/types.d.ts +12 -0
- package/dist/actions/remove-booking-from-cart/types.js +1 -0
- package/dist/demo/database.d.ts +12 -24
- package/dist/demo/database.js +121 -62
- package/dist/index.d.ts +16 -4
- package/dist/index.js +15 -4
- package/dist/projects/render/mocks.js +14 -4
- package/dist/projects/render/types.d.ts +8 -3
- package/dist/pubsub/topics/bookings/booking-created/types.d.ts +6 -0
- package/dist/pubsub/topics/bookings/booking-created/types.js +1 -0
- package/dist/pubsub/topics/bookings/booking-setup-changed/types.d.ts +15 -0
- package/dist/pubsub/topics/bookings/booking-setup-changed/types.js +1 -0
- package/dist/pubsub/topics/bookings/booking-updated/types.d.ts +6 -0
- package/dist/pubsub/topics/bookings/booking-updated/types.js +1 -0
- package/dist/pubsub/topics/bookings/index.d.ts +3 -0
- package/dist/pubsub/topics/bookings/index.js +27 -0
- package/dist/pubsub/topics/bookings/types.d.ts +8 -0
- package/dist/pubsub/topics/bookings/types.js +3 -0
- package/dist/pubsub/topics/cart/types.d.ts +2 -4
- package/dist/pubsub/topics/cart/types.js +0 -1
- package/dist/pubsub/topics/index.d.ts +17 -16
- package/dist/pubsub/topics/index.js +17 -16
- package/dist/pubsub/topics/types.d.ts +2 -0
- package/package.json +2 -2
- package/dist/actions/get-product-modifier-selections/action.d.ts +0 -6
- package/dist/actions/get-product-modifier-selections/action.js +0 -8
- package/dist/actions/get-product-modifier-selections/mock.d.ts +0 -2
- package/dist/actions/get-product-modifier-selections/mock.js +0 -30
- package/dist/actions/get-product-modifier-selections/types.d.ts +0 -23
- package/dist/actions/set-product-modifier-selections/action.d.ts +0 -6
- package/dist/actions/set-product-modifier-selections/action.js +0 -8
- package/dist/actions/set-product-modifier-selections/mock.d.ts +0 -2
- package/dist/actions/set-product-modifier-selections/mock.js +0 -49
- package/dist/actions/set-product-modifier-selections/types.d.ts +0 -27
- package/dist/pubsub/topics/cart/product-modifiers-updated/types.d.ts +0 -16
- /package/dist/actions/{get-product-modifier-selections → add-booking-to-cart}/types.js +0 -0
- /package/dist/actions/{set-product-modifier-selections → cancel-booking}/types.js +0 -0
- /package/dist/{pubsub/topics/cart/product-modifiers-updated → actions/get-booking-availability}/types.js +0 -0
package/dist/CommonTypes.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from './common-types';
|
|
2
|
-
import type { Tax, Tip, Address, MetadataItem, PosDataItem, CartFeeTaxEntry, CartFeeItem, CartDiscountItem, OrderNote, NonRevenueItem, DiscountDetail, FeeDetail, DiscountLineItem, FeeLineItem, LineItem, TipPayment, RefundedTipPayment, PaymentMethod, Discount, CustomFee, Summary, CustomSale, RefundedCustomSale, RefundItem, RefundedLineItem, ActiveStation, ActiveSession, ActiveOutlet, ActiveUser, ActiveUserRole, ActiveOrder, ActiveCart, ActivePark, ActiveCompany, ActiveCustomSales, ActiveProduct, ActiveCustomer, FullProduct, ProductVariant, Inventory,
|
|
2
|
+
import type { Tax, Tip, Address, MetadataItem, PosDataItem, CartFeeTaxEntry, CartFeeItem, CartDiscountItem, OrderNote, NonRevenueItem, DiscountDetail, FeeDetail, DiscountLineItem, FeeLineItem, LineItem, TipPayment, RefundedTipPayment, PaymentMethod, Discount, CustomFee, Summary, CustomSale, RefundedCustomSale, RefundItem, RefundedLineItem, ActiveStation, ActiveSession, ActiveOutlet, ActiveUser, ActiveUserRole, ActiveOrder, ActiveCart, ActivePark, ActiveCompany, ActiveCustomSales, ActiveProduct, ActiveCustomer, FullProduct, ProductVariant, Inventory, CustomerNote, Attribute, AttributeOption, Category, Transaction, ActiveSplitPayment, BookingAvailability, BookingSlot, BookingSlotResource, BookingResource, Booking, CartReservation, OrderReservation } from '@final-commerce/common/pos-types';
|
|
3
3
|
export { CurrencyCode, ProductType as CFProductType, UserTypes as CFUserTypes } from '@final-commerce/common';
|
|
4
4
|
/** Open record used as a base for active entities. */
|
|
5
5
|
export type CFActiveEntity = Record<string, unknown>;
|
|
@@ -18,19 +18,16 @@ export type CFTransaction = Transaction;
|
|
|
18
18
|
export type CFCategory = Category;
|
|
19
19
|
export type CFProductVariant = ProductVariant;
|
|
20
20
|
export type CFProduct = FullProduct;
|
|
21
|
-
|
|
22
|
-
export type
|
|
23
|
-
export type
|
|
24
|
-
export type
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
export type
|
|
30
|
-
|
|
31
|
-
export type CFCartLineModifier = CartLineModifier;
|
|
32
|
-
/** A priced, line-extended modifier row: "Toppings - Avocado" x2, `amount` 1000. */
|
|
33
|
-
export type CFProdModifierBreakdown = ProdModifierBreakdown;
|
|
21
|
+
export type CFBookingAvailability = BookingAvailability;
|
|
22
|
+
export type CFBookingSlot = BookingSlot;
|
|
23
|
+
export type CFBookingSlotResource = BookingSlotResource;
|
|
24
|
+
export type CFBookingResource = BookingResource;
|
|
25
|
+
export type CFBooking = Booking;
|
|
26
|
+
/** A service sitting in the cart, and the same sale once it is on an order. A flow that draws a
|
|
27
|
+
* cart or a receipt needs both by name — reaching into the package's dist for them is not an
|
|
28
|
+
* interface. */
|
|
29
|
+
export type CFCartReservation = CartReservation;
|
|
30
|
+
export type CFOrderReservation = OrderReservation;
|
|
34
31
|
export type CFActiveProduct = ActiveProduct;
|
|
35
32
|
export type CFCustomer = ActiveCustomer;
|
|
36
33
|
export type CFActiveCustomer = ActiveCustomer;
|
|
@@ -119,6 +116,21 @@ export interface CFContextRender {
|
|
|
119
116
|
stationName: string | null;
|
|
120
117
|
outletId: string | null;
|
|
121
118
|
outletName: string | null;
|
|
119
|
+
/**
|
|
120
|
+
* IANA zone the SHOP keeps, resolved as the outlet's own zone, else the
|
|
121
|
+
* company's — never the device's. A booking is stored as an instant, so which
|
|
122
|
+
* hour and which DAY it reads as is decided entirely by the zone it is printed
|
|
123
|
+
* in, and the screen's own machine is the one zone that is certainly wrong: a
|
|
124
|
+
* 17:00 Monday appointment in Vancouver is 21:30 for a till in St John's, and
|
|
125
|
+
* for some viewers it lands on Tuesday. Without this an app cannot label a
|
|
126
|
+
* slot or an appointment honestly — it can only guess, and it guesses wrong
|
|
127
|
+
* for every business that does not sit in the same zone as its screen.
|
|
128
|
+
*
|
|
129
|
+
* `null` when neither the outlet nor the company has one configured. Say so
|
|
130
|
+
* rather than falling back to the device: a missing timezone is a setup
|
|
131
|
+
* problem, and printing the machine's clock hides it.
|
|
132
|
+
*/
|
|
133
|
+
timeZone: string | null;
|
|
122
134
|
buildId: string | null;
|
|
123
135
|
buildName: string | null;
|
|
124
136
|
buildVersion: string | null;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Add booking to cart action
|
|
3
|
+
* Calls the addBookingToCart action on the parent window
|
|
4
|
+
*/
|
|
5
|
+
import { commandFrameClient } from '../../client';
|
|
6
|
+
export const addBookingToCart = async (params) => {
|
|
7
|
+
return await commandFrameClient.call('addBookingToCart', params);
|
|
8
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { MOCK_CART, MOCK_PRODUCTS, mockPublishEvent } from '../../demo/database';
|
|
2
|
+
import { mockHoldBooking } from '../hold-booking/mock';
|
|
3
|
+
export const mockAddBookingToCart = async (params) => {
|
|
4
|
+
console.log('[Mock] addBookingToCart called', params);
|
|
5
|
+
// One call, two effects — the window is claimed AND the service is in the cart. The claim goes
|
|
6
|
+
// first and its refusal propagates: a cart line for a window somebody else took is worse than
|
|
7
|
+
// no line at all.
|
|
8
|
+
const { booking } = await mockHoldBooking(params);
|
|
9
|
+
const product = MOCK_PRODUCTS.find(({ _id }) => _id === params.productId);
|
|
10
|
+
const variant = params.variantId
|
|
11
|
+
? product?.variants?.find(({ _id }) => _id === params.variantId)
|
|
12
|
+
: product?.variants?.[0];
|
|
13
|
+
const price = variant?.price ?? product?.minPrice ?? 0;
|
|
14
|
+
const reservation = {
|
|
15
|
+
internalId: `res_${booking.id}`,
|
|
16
|
+
bookingId: booking.id,
|
|
17
|
+
productId: params.productId,
|
|
18
|
+
variantId: variant?._id ?? null,
|
|
19
|
+
resourceId: params.resourceId,
|
|
20
|
+
name: product?.name ?? 'Booking',
|
|
21
|
+
resourceName: booking.resourceName,
|
|
22
|
+
price,
|
|
23
|
+
quantity: 1,
|
|
24
|
+
total: price,
|
|
25
|
+
taxTableId: product?.taxTable,
|
|
26
|
+
startAt: booking.startAt,
|
|
27
|
+
endAt: booking.endAt,
|
|
28
|
+
bufferEndAt: booking.bufferEndAt,
|
|
29
|
+
expiresAt: booking.expiresAt,
|
|
30
|
+
};
|
|
31
|
+
if (!MOCK_CART.reservations)
|
|
32
|
+
MOCK_CART.reservations = [];
|
|
33
|
+
MOCK_CART.reservations.push(reservation);
|
|
34
|
+
MOCK_CART.subtotal += price;
|
|
35
|
+
MOCK_CART.total += price;
|
|
36
|
+
MOCK_CART.amountToBeCharged = MOCK_CART.total;
|
|
37
|
+
MOCK_CART.remainingBalance = MOCK_CART.total;
|
|
38
|
+
// The cart topic, not the bookings one: a screen that only watches bookings still has to
|
|
39
|
+
// repaint its cart, and every other cart mutation announces itself the same way.
|
|
40
|
+
mockPublishEvent('cart', 'reservation-added', { reservation });
|
|
41
|
+
return { booking, reservationInternalId: reservation.internalId, timestamp: new Date().toISOString() };
|
|
42
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { CFBooking } from '../../CommonTypes';
|
|
2
|
+
export interface AddBookingToCartParams {
|
|
3
|
+
/** The bookable product (`productType: 'booking'`). */
|
|
4
|
+
productId: string;
|
|
5
|
+
/** Which resource serves it: a member of staff, a room, a scooter. */
|
|
6
|
+
resourceId: string;
|
|
7
|
+
/** Must be the START of a slot getBookingAvailability offered, ISO 8601. */
|
|
8
|
+
startAt: string;
|
|
9
|
+
/** End of the window — the slot's own `endAt`, or the last slot's for a stay. */
|
|
10
|
+
endAt: string;
|
|
11
|
+
/** The variant being sold. Defaults to the product's only variant. */
|
|
12
|
+
variantId?: string;
|
|
13
|
+
/** Whom the booking is for. Omit for a walk-in. */
|
|
14
|
+
customerId?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface AddBookingToCartResponse {
|
|
17
|
+
/** The claim the cart now stands on: `id` cancels or releases it, `expiresAt` is your clock. */
|
|
18
|
+
booking: CFBooking;
|
|
19
|
+
/** Identity of the reservation inside the cart and, later, the order. */
|
|
20
|
+
reservationInternalId: string;
|
|
21
|
+
timestamp: string;
|
|
22
|
+
}
|
|
23
|
+
export type AddBookingToCart = (params: AddBookingToCartParams) => Promise<AddBookingToCartResponse>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MOCK_CART, MOCK_PRODUCTS,
|
|
1
|
+
import { MOCK_CART, MOCK_PRODUCTS, mockPublishEvent } from '../../demo/database';
|
|
2
2
|
import { extendPrice, isValidQuantity, resolveUnit } from '@final-commerce/common';
|
|
3
3
|
export const mockAddProductToCart = async (params) => {
|
|
4
4
|
console.log('[Mock] addProductToCart called', params);
|
|
@@ -53,18 +53,6 @@ export const mockAddProductToCart = async (params) => {
|
|
|
53
53
|
note: note,
|
|
54
54
|
// discount/fee could be added here to mock object if CFActiveProduct supports it
|
|
55
55
|
};
|
|
56
|
-
// The spread above copied the PRODUCT's `modifiers` — a ResolvedModifier[] menu — into a
|
|
57
|
-
// field that means "the choices this line carries" (CartLineModifier[]). Two different
|
|
58
|
-
// shapes, one name. Drop it before anything reads the line, or every surface renders the
|
|
59
|
-
// whole catalogue as if the cashier had picked all of it.
|
|
60
|
-
delete activeProduct.modifiers;
|
|
61
|
-
// Keep the raw answers AND the priced rows, the same pair the real host writes. The cart
|
|
62
|
-
// TOTAL is deliberately left alone: like a product fee (see add-product-fee), per-line
|
|
63
|
-
// money is not accumulated by this mock — `total` stays Σ extendPrice(price, quantity).
|
|
64
|
-
if (params?.modifiers?.length) {
|
|
65
|
-
activeProduct.modifierSelections = params.modifiers;
|
|
66
|
-
activeProduct.modifiers = buildCartLineModifiers(product.modifiers, params.modifiers, product.taxTable);
|
|
67
|
-
}
|
|
68
56
|
MOCK_CART.products.push(activeProduct);
|
|
69
57
|
// Recalculate totals. extendPrice, not a raw multiply: a fractional quantity times an
|
|
70
58
|
// integer-minor price leaves fractional cents, which no order may carry.
|
|
@@ -76,9 +64,6 @@ export const mockAddProductToCart = async (params) => {
|
|
|
76
64
|
MOCK_CART.remainingBalance = MOCK_CART.total;
|
|
77
65
|
// Publish cart event to simulate real behavior
|
|
78
66
|
mockPublishEvent('cart', 'product-added', { product: activeProduct });
|
|
79
|
-
// Display-ready modifier rows for the line that was just created, so a flow can
|
|
80
|
-
// show what the modifiers added without re-reading the cart or multiplying.
|
|
81
|
-
const { rows, modifiersTotal } = buildModifierRows(activeProduct.modifiers, quantity);
|
|
82
67
|
return {
|
|
83
68
|
success: true,
|
|
84
69
|
productId: activeProduct.id,
|
|
@@ -86,8 +71,6 @@ export const mockAddProductToCart = async (params) => {
|
|
|
86
71
|
internalId: activeProduct.internalId,
|
|
87
72
|
name: activeProduct.name,
|
|
88
73
|
quantity: quantity,
|
|
89
|
-
rows,
|
|
90
|
-
modifiersTotal,
|
|
91
74
|
timestamp: new Date().toISOString(),
|
|
92
75
|
};
|
|
93
76
|
};
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { AddProductDiscountParams } from '../add-product-discount/types';
|
|
2
2
|
import type { AddProductFeeParams } from '../add-product-fee/types';
|
|
3
|
-
import type { ModifierSelection } from '../get-product-modifier-selections/types';
|
|
4
|
-
import type { CFProdModifierBreakdown } from '../../CommonTypes';
|
|
5
3
|
export interface AddProductToCartParams {
|
|
6
4
|
/** ID of the variant to add. */
|
|
7
5
|
variantId: string;
|
|
@@ -23,25 +21,17 @@ export interface AddProductToCartParams {
|
|
|
23
21
|
discounts?: AddProductDiscountParams[];
|
|
24
22
|
/** Array of fees to apply immediately. */
|
|
25
23
|
fees?: AddProductFeeParams[];
|
|
26
|
-
/** Modifier selections to apply immediately. */
|
|
27
|
-
modifiers?: ModifierSelection[];
|
|
28
24
|
/** Note or array of notes to add immediately. */
|
|
29
25
|
notes?: string | string[];
|
|
30
26
|
}
|
|
31
27
|
export interface AddProductToCartResponse {
|
|
32
28
|
success: boolean;
|
|
33
|
-
/** Set when the add was rejected (e.g. a required modifier is unanswered). */
|
|
34
|
-
reason?: string;
|
|
35
29
|
productId: string;
|
|
36
30
|
variantId: string;
|
|
37
31
|
/** The unique identifier for the specific item instance added to the cart. */
|
|
38
32
|
internalId: string;
|
|
39
33
|
name: string;
|
|
40
34
|
quantity: number;
|
|
41
|
-
/** The modifiers the line was created with, display-ready. Empty when there are none. */
|
|
42
|
-
rows: CFProdModifierBreakdown[];
|
|
43
|
-
/** Sum of `rows[].amount` — what the modifiers added to this line, in minor units. */
|
|
44
|
-
modifiersTotal: number;
|
|
45
35
|
timestamp: string;
|
|
46
36
|
}
|
|
47
37
|
export type AddProductToCart = (params?: AddProductToCartParams) => Promise<AddProductToCartResponse>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { MOCK_BOOKINGS } from '../../demo/database';
|
|
2
|
+
import { ReservationStatus } from '@final-commerce/common';
|
|
3
|
+
export const mockCancelBooking = async (params) => {
|
|
4
|
+
console.log('[Mock] cancelBooking called', params);
|
|
5
|
+
const booking = MOCK_BOOKINGS.find(({ id }) => id === params.bookingId);
|
|
6
|
+
if (!booking)
|
|
7
|
+
throw new Error(`No booking ${params.bookingId}`);
|
|
8
|
+
booking.status = ReservationStatus.CANCELLED;
|
|
9
|
+
return { booking, timestamp: new Date().toISOString() };
|
|
10
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CFBooking } from '../../CommonTypes';
|
|
2
|
+
export interface CancelBookingParams {
|
|
3
|
+
/** `id` of the booking returned by holdBooking or getBookings. */
|
|
4
|
+
bookingId: string;
|
|
5
|
+
}
|
|
6
|
+
export interface CancelBookingResponse {
|
|
7
|
+
booking: CFBooking;
|
|
8
|
+
timestamp: string;
|
|
9
|
+
}
|
|
10
|
+
export type CancelBooking = (params: CancelBookingParams) => Promise<CancelBookingResponse>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get booking availability action
|
|
3
|
+
* Calls the getBookingAvailability action on the parent window
|
|
4
|
+
*/
|
|
5
|
+
import { commandFrameClient } from '../../client';
|
|
6
|
+
export const getBookingAvailability = async (params) => {
|
|
7
|
+
return await commandFrameClient.call('getBookingAvailability', params);
|
|
8
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { mockBookingAvailability } from '../../demo/database';
|
|
2
|
+
export const mockGetBookingAvailability = async (params) => {
|
|
3
|
+
console.log('[Mock] getBookingAvailability called', params);
|
|
4
|
+
const availability = mockBookingAvailability(params.productId, new Date(params.from), new Date(params.to), params.resourceId);
|
|
5
|
+
return { availability, timestamp: new Date().toISOString() };
|
|
6
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CFBookingAvailability } from '../../CommonTypes';
|
|
2
|
+
export interface GetBookingAvailabilityParams {
|
|
3
|
+
/** The bookable product (`productType: 'booking'`) whose calendar you are drawing. */
|
|
4
|
+
productId: string;
|
|
5
|
+
/** Range start, ISO 8601. */
|
|
6
|
+
from: string;
|
|
7
|
+
/** Range end, ISO 8601. */
|
|
8
|
+
to: string;
|
|
9
|
+
/** Narrow the answer to one resource — "only Marco", "only room 4". */
|
|
10
|
+
resourceId?: string;
|
|
11
|
+
/** Narrow to the resources standing at one outlet. */
|
|
12
|
+
outletId?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface GetBookingAvailabilityResponse {
|
|
15
|
+
availability: CFBookingAvailability;
|
|
16
|
+
timestamp: string;
|
|
17
|
+
}
|
|
18
|
+
export type GetBookingAvailability = (params: GetBookingAvailabilityParams) => Promise<GetBookingAvailabilityResponse>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get booking resources action
|
|
3
|
+
* Calls the getBookingResources action on the parent window
|
|
4
|
+
*/
|
|
5
|
+
import { commandFrameClient } from '../../client';
|
|
6
|
+
export const getBookingResources = async (params) => {
|
|
7
|
+
return await commandFrameClient.call('getBookingResources', params);
|
|
8
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MOCK_BOOKING_RESOURCES } from '../../demo/database';
|
|
2
|
+
export const mockGetBookingResources = async (params) => {
|
|
3
|
+
console.log('[Mock] getBookingResources called', params);
|
|
4
|
+
// A resource knows nothing about outlets — where it works lives in its own records, which the
|
|
5
|
+
// host reads. The mock has no such records, so `outletId` narrows nothing here.
|
|
6
|
+
return { resources: MOCK_BOOKING_RESOURCES, timestamp: new Date().toISOString() };
|
|
7
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { CFBookingResource } from '../../CommonTypes';
|
|
2
|
+
export interface GetBookingResourcesParams {
|
|
3
|
+
/** Only the resources that serve this bookable product. Omit for the whole company. */
|
|
4
|
+
productId?: string;
|
|
5
|
+
/** Only resources standing at this outlet (a resource with no outlet serves all of them). */
|
|
6
|
+
outletId?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface GetBookingResourcesResponse {
|
|
9
|
+
resources: CFBookingResource[];
|
|
10
|
+
timestamp: string;
|
|
11
|
+
}
|
|
12
|
+
export type GetBookingResources = (params?: GetBookingResourcesParams) => Promise<GetBookingResourcesResponse>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { MOCK_BOOKINGS, mockLiveBookings } from '../../demo/database';
|
|
2
|
+
export const mockGetBookings = async (params) => {
|
|
3
|
+
console.log('[Mock] getBookings called', params);
|
|
4
|
+
let bookings = params?.includeExpired ? MOCK_BOOKINGS : mockLiveBookings();
|
|
5
|
+
if (params?.resourceId)
|
|
6
|
+
bookings = bookings.filter((entry) => entry.resourceId === params.resourceId);
|
|
7
|
+
if (params?.productId)
|
|
8
|
+
bookings = bookings.filter((entry) => entry.productId === params.productId);
|
|
9
|
+
if (params?.customerId)
|
|
10
|
+
bookings = bookings.filter((entry) => entry.customerId === params.customerId);
|
|
11
|
+
if (params?.from) {
|
|
12
|
+
const from = new Date(params.from);
|
|
13
|
+
bookings = bookings.filter((entry) => new Date(entry.bufferEndAt) > from);
|
|
14
|
+
}
|
|
15
|
+
if (params?.to) {
|
|
16
|
+
const to = new Date(params.to);
|
|
17
|
+
bookings = bookings.filter((entry) => new Date(entry.startAt) < to);
|
|
18
|
+
}
|
|
19
|
+
return { bookings, timestamp: new Date().toISOString() };
|
|
20
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CFBooking } from '../../CommonTypes';
|
|
2
|
+
export interface GetBookingsParams {
|
|
3
|
+
/** Everything taking this resource's time — the one query a day view needs. */
|
|
4
|
+
resourceId?: string;
|
|
5
|
+
productId?: string;
|
|
6
|
+
/** This customer's appointments. */
|
|
7
|
+
customerId?: string;
|
|
8
|
+
/** Bookings whose window overlaps this range, ISO 8601. */
|
|
9
|
+
from?: string;
|
|
10
|
+
to?: string;
|
|
11
|
+
/** Defaults to live bookings only (confirmed, plus holds that have not expired). */
|
|
12
|
+
includeExpired?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface GetBookingsResponse {
|
|
15
|
+
bookings: CFBooking[];
|
|
16
|
+
timestamp: string;
|
|
17
|
+
}
|
|
18
|
+
export type GetBookings = (params?: GetBookingsParams) => Promise<GetBookingsResponse>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -11,6 +11,10 @@ export const mockGetContext = () => {
|
|
|
11
11
|
stationName: MOCK_STATION.name,
|
|
12
12
|
outletId: MOCK_OUTLET.id,
|
|
13
13
|
outletName: MOCK_OUTLET.name || null,
|
|
14
|
+
// Deliberately NOT the developer's own zone: an app that quietly formats with
|
|
15
|
+
// the machine's clock looks correct in preview and is wrong on every till that
|
|
16
|
+
// sits in a different city. Here it cannot look correct by accident.
|
|
17
|
+
timeZone: "America/Vancouver",
|
|
14
18
|
buildId: "mock_build_id",
|
|
15
19
|
buildName: "Mock Build",
|
|
16
20
|
buildVersion: "1.0.0-mock",
|
|
@@ -18,11 +18,14 @@ export interface GetRefundPlanParams {
|
|
|
18
18
|
* nothing staged, no `allocation` comes back.
|
|
19
19
|
*/
|
|
20
20
|
items?: {
|
|
21
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* `internalId` / `variantId` for a product, `customSaleId`, cart-fee id, tip
|
|
23
|
+
* `transactionId`, or a booking's own `internalId` on `order.reservations[]`.
|
|
24
|
+
*/
|
|
22
25
|
itemKey: string;
|
|
23
26
|
quantity: number;
|
|
24
27
|
/** Optional hint; inferred from the order when omitted. */
|
|
25
|
-
type?: 'product' | 'customSale' | 'fee' | 'tip';
|
|
28
|
+
type?: 'product' | 'customSale' | 'fee' | 'tip' | 'reservation';
|
|
26
29
|
}[];
|
|
27
30
|
}
|
|
28
31
|
export interface RefundPlanSource {
|
|
@@ -18,6 +18,14 @@ export interface GetRemainingRefundableQuantitiesResponse {
|
|
|
18
18
|
* 0/1 semantics: `1` = still refundable, `0` = already refunded.
|
|
19
19
|
*/
|
|
20
20
|
tips: Record<string, number>;
|
|
21
|
+
/**
|
|
22
|
+
* Remaining refundable bookings, keyed by `order.reservations[].internalId` —
|
|
23
|
+
* the same key `processPartialRefund` takes for `type: 'reservation'` items.
|
|
24
|
+
* A service is sold as a reservation rather than a line item, so without this
|
|
25
|
+
* map a refund screen can show everything on the order EXCEPT the appointment.
|
|
26
|
+
* 0/1 semantics: `1` = still refundable, `0` = already refunded.
|
|
27
|
+
*/
|
|
28
|
+
reservations: Record<string, number>;
|
|
21
29
|
timestamp: string;
|
|
22
30
|
}
|
|
23
31
|
export type GetRemainingRefundableQuantities = (params?: GetRemainingRefundableQuantitiesParams) => Promise<GetRemainingRefundableQuantitiesResponse>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { MOCK_BOOKINGS, MOCK_BOOKING_RESOURCES, mockLiveBookings } from '../../demo/database';
|
|
2
|
+
import { ReservationStatus } from '@final-commerce/common';
|
|
3
|
+
const BUFFER_MINUTES = 5;
|
|
4
|
+
const HOLD_MINUTES = 10;
|
|
5
|
+
export const mockHoldBooking = async (params) => {
|
|
6
|
+
console.log('[Mock] holdBooking called', params);
|
|
7
|
+
const startAt = new Date(params.startAt);
|
|
8
|
+
const endAt = new Date(params.endAt);
|
|
9
|
+
const bufferEndAt = new Date(endAt.getTime() + BUFFER_MINUTES * 60000);
|
|
10
|
+
// The mock refuses a taken window like the host does. A mock that always said yes would
|
|
11
|
+
// let a booking screen ship without ever handling the one error it will actually meet.
|
|
12
|
+
const clash = mockLiveBookings().some((entry) => entry.resourceId === params.resourceId &&
|
|
13
|
+
new Date(entry.startAt) < bufferEndAt &&
|
|
14
|
+
new Date(entry.bufferEndAt) > startAt);
|
|
15
|
+
if (clash)
|
|
16
|
+
throw new Error('That window has just been taken — pick another slot.');
|
|
17
|
+
const booking = {
|
|
18
|
+
id: `bk_mock_${Date.now()}`,
|
|
19
|
+
productId: params.productId,
|
|
20
|
+
resourceId: params.resourceId,
|
|
21
|
+
variantId: params.variantId,
|
|
22
|
+
startAt: startAt.toISOString(),
|
|
23
|
+
endAt: endAt.toISOString(),
|
|
24
|
+
bufferEndAt: bufferEndAt.toISOString(),
|
|
25
|
+
customerId: params.customerId,
|
|
26
|
+
status: ReservationStatus.HELD,
|
|
27
|
+
expiresAt: new Date(Date.now() + HOLD_MINUTES * 60000).toISOString(),
|
|
28
|
+
resourceName: MOCK_BOOKING_RESOURCES.find(({ id }) => id === params.resourceId)?.name,
|
|
29
|
+
};
|
|
30
|
+
// Held in the same list availability reads, so the slot really does disappear.
|
|
31
|
+
MOCK_BOOKINGS.push(booking);
|
|
32
|
+
return { booking, timestamp: new Date().toISOString() };
|
|
33
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { CFBooking } from '../../CommonTypes';
|
|
2
|
+
export interface HoldBookingParams {
|
|
3
|
+
productId: string;
|
|
4
|
+
/** Which resource serves it: a member of staff, a room, a scooter. */
|
|
5
|
+
resourceId: string;
|
|
6
|
+
/** Must be the START of a slot the host offered, ISO 8601. */
|
|
7
|
+
startAt: string;
|
|
8
|
+
/**
|
|
9
|
+
* End of the window — the `endAt` of the slot you picked, or of the last slot in a stay.
|
|
10
|
+
* Required: the server refuses a window it cannot line up with its own grid, and the till
|
|
11
|
+
* has no local copy of the rules to derive it from.
|
|
12
|
+
*/
|
|
13
|
+
endAt: string;
|
|
14
|
+
/** The variant being sold, when the flow already knows which one. */
|
|
15
|
+
variantId?: string;
|
|
16
|
+
/** Whom the booking is for. Omit for a walk-in. */
|
|
17
|
+
customerId?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface HoldBookingResponse {
|
|
20
|
+
/** The claim. `expiresAt` says how long it survives unpaid; `id` is what you confirm or cancel. */
|
|
21
|
+
booking: CFBooking;
|
|
22
|
+
timestamp: string;
|
|
23
|
+
}
|
|
24
|
+
export type HoldBooking = (params: HoldBookingParams) => Promise<HoldBookingResponse>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -131,10 +131,10 @@ export interface ProcessPartialRefundParams {
|
|
|
131
131
|
};
|
|
132
132
|
/** Optional items to refund. */
|
|
133
133
|
items?: {
|
|
134
|
-
/** internalId or variantId or
|
|
134
|
+
/** internalId or variantId, customSaleId, or a booking's own internalId. */
|
|
135
135
|
itemKey: string;
|
|
136
136
|
quantity: number;
|
|
137
|
-
type?: 'product' | 'customSale' | 'fee' | 'tip';
|
|
137
|
+
type?: 'product' | 'customSale' | 'fee' | 'tip' | 'reservation';
|
|
138
138
|
/**
|
|
139
139
|
* Per-item stock disposition for a refunded **product** line — the
|
|
140
140
|
* headless equivalent of the old refund popup's per-row restock/damaged
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove booking from cart action
|
|
3
|
+
* Calls the removeBookingFromCart action on the parent window
|
|
4
|
+
*/
|
|
5
|
+
import { commandFrameClient } from '../../client';
|
|
6
|
+
export const removeBookingFromCart = async (params) => {
|
|
7
|
+
return await commandFrameClient.call('removeBookingFromCart', params);
|
|
8
|
+
};
|