@ticketboothapp/booking 1.2.99 → 1.2.101

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ticketboothapp/booking",
3
- "version": "1.2.99",
3
+ "version": "1.2.101",
4
4
  "private": false,
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -14,6 +14,8 @@
14
14
  "./booking-flow.css": "./src/components/booking/booking-flow.css",
15
15
  "./runtime": "./src/runtime/index.ts",
16
16
  "./contexts/booking-app-context": "./src/contexts/BookingAppContext.tsx",
17
+ "./contexts/company-context": "./src/contexts/CompanyContext.tsx",
18
+ "./contexts/availabilities-cache-context": "./src/contexts/AvailabilitiesCacheContext.tsx",
17
19
  "./providers/booking-dialog-provider": "./src/providers/booking-dialog-provider.tsx",
18
20
  "./hooks/useBookingSourceMetadataFromLocation": "./src/hooks/useBookingSourceMetadataFromLocation.ts",
19
21
  "./hooks/useIsBookingLaunchLive": "./src/hooks/useIsBookingLaunchLive.ts",
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useEffect, useRef, useState } from 'react';
3
+ import { useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { useBookingSourceMetadataFromLocation } from '../../hooks/useBookingSourceMetadataFromLocation';
5
5
  import { useBookingDialog } from '../../providers/booking-dialog-provider';
6
6
  import { useBookingHost } from '../../runtime';
@@ -49,15 +49,28 @@ function BookFlowScreen({
49
49
  const apiProductId = config?.productId ?? productId;
50
50
 
51
51
  // Build minimal product from config for immediate availability fetch (no /products wait)
52
- const minimalProduct =
53
- config && catalog.buildMinimalProductFromConfig
54
- ? (catalog.buildMinimalProductFromConfig(config, env.COMPANY_ID) as Product)
55
- : null;
52
+ const staticProduct = useMemo(
53
+ () =>
54
+ catalog.getStaticProductByIdOrSlug
55
+ ? (catalog.getStaticProductByIdOrSlug(productId) as Product | null)
56
+ : null,
57
+ [catalog, productId]
58
+ );
59
+
60
+ const minimalProduct = useMemo(
61
+ () =>
62
+ config && catalog.buildMinimalProductFromConfig
63
+ ? (catalog.buildMinimalProductFromConfig(config, env.COMPANY_ID) as Product)
64
+ : null,
65
+ [catalog, config, env.COMPANY_ID]
66
+ );
56
67
 
57
68
  useEffect(() => {
58
69
  if (isPartialLaunch) return; // No API needed for partial launch
59
- let cancelled = false;
60
70
  setError(null);
71
+ setProduct(null);
72
+ if (staticProduct || minimalProduct) return;
73
+ let cancelled = false;
61
74
  getProduct(apiProductId, env.COMPANY_ID)
62
75
  .then((p) => {
63
76
  if (!cancelled && p) setProduct(p);
@@ -67,13 +80,14 @@ function BookFlowScreen({
67
80
  if (!cancelled) setError(err instanceof Error ? err.message : 'Failed to load product');
68
81
  });
69
82
  return () => { cancelled = true; };
70
- }, [apiProductId, config, isPartialLaunch]);
83
+ }, [apiProductId, config, env.COMPANY_ID, isPartialLaunch, minimalProduct, staticProduct]);
71
84
 
72
85
  useEffect(() => {
73
- if (product) {
74
- onProductLoaded?.(product.name);
86
+ const loadedProduct = product?.productId === apiProductId ? product : staticProduct ?? minimalProduct;
87
+ if (loadedProduct) {
88
+ onProductLoaded?.(loadedProduct.name);
75
89
  }
76
- }, [product?.name, onProductLoaded]);
90
+ }, [apiProductId, minimalProduct, onProductLoaded, product, staticProduct]);
77
91
 
78
92
  // Partial launch: show collage + description immediately (no API)
79
93
  if (isPartialLaunch) {
@@ -84,8 +98,10 @@ function BookFlowScreen({
84
98
  );
85
99
  }
86
100
 
87
- // No config: must wait for API (product not in products-config)
88
- if (!config) {
101
+ const displayProduct = product?.productId === apiProductId ? product : staticProduct ?? minimalProduct;
102
+
103
+ // No local/static product: must wait for API (product not in products-config)
104
+ if (!displayProduct) {
89
105
  if (error) {
90
106
  return (
91
107
  <div className={`${styles.screen} booking-flow-preflight`}>
@@ -107,10 +123,7 @@ function BookFlowScreen({
107
123
  );
108
124
  }
109
125
 
110
- // Use full product when loaded, otherwise minimal from config (availabilities fetch starts immediately)
111
- const displayProduct = product ?? minimalProduct!;
112
-
113
- if (error && !product) {
126
+ if (error && !displayProduct) {
114
127
  return (
115
128
  <div className={`${styles.screen} booking-flow-preflight`}>
116
129
  <div className="flex flex-col items-center justify-center py-16 gap-4">
@@ -107,12 +107,20 @@ export default function ChangeBookingDialog({
107
107
  : null,
108
108
  [config, catalog, env.COMPANY_ID]
109
109
  );
110
+ const staticProduct = useMemo(
111
+ () =>
112
+ catalog.getStaticProductByIdOrSlug
113
+ ? (catalog.getStaticProductByIdOrSlug(bookingProductId) as Product | null)
114
+ : null,
115
+ [bookingProductId, catalog]
116
+ );
110
117
 
111
118
  useEffect(() => {
112
119
  if (!isOpen) return;
113
120
  let cancelled = false;
114
121
  setError(null);
115
- if (minimalProduct) setProduct(minimalProduct);
122
+ setProduct(staticProduct ?? minimalProduct);
123
+ if (staticProduct || minimalProduct) return;
116
124
  getProduct(apiProductId, env.COMPANY_ID)
117
125
  .then((p) => {
118
126
  if (!cancelled && p) setProduct(p);
@@ -123,7 +131,7 @@ export default function ChangeBookingDialog({
123
131
  return () => {
124
132
  cancelled = true;
125
133
  };
126
- }, [isOpen, apiProductId, minimalProduct]);
134
+ }, [isOpen, apiProductId, minimalProduct, staticProduct]);
127
135
 
128
136
  useEffect(() => {
129
137
  if (isOpen) {
@@ -334,9 +334,12 @@ export function CheckoutModal({
334
334
  : []),
335
335
  ...feeLineItems.map((fee) => {
336
336
  const isMoraineLakeRoadAccessFee = fee.name.toLowerCase().includes('moraine') && (fee.name.toLowerCase().includes('access') || fee.name.toLowerCase().includes('road') || fee.name.toLowerCase().includes('license'));
337
+ const showQuantityLabel = fee.showQuantityLabel !== false;
337
338
  return {
338
339
  kind: 'line' as const,
339
- label: `${fee.name} (${totalQuantity} ${totalQuantity === 1 ? 'person' : 'people'})`,
340
+ label: showQuantityLabel
341
+ ? `${fee.name} (${totalQuantity} ${totalQuantity === 1 ? 'person' : 'people'})`
342
+ : fee.name,
340
343
  amount: fee.totalAmount,
341
344
  type: 'fee',
342
345
  tooltip: isMoraineLakeRoadAccessFee