@ticketboothapp/booking 1.2.55 → 1.2.56

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.55",
3
+ "version": "1.2.56",
4
4
  "private": false,
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -673,6 +673,8 @@ export function BookingFlow({
673
673
  const [precomputedPricesByOption, setPrecomputedPricesByOption] = useState<Record<string, PrecomputedPricesByCategory> | null>(null);
674
674
  const pricingConfigSetRef = useRef(false); // Track if pricingConfig has been set (optimize: only set once)
675
675
  const fetchingRef = useRef(false); // Prevent concurrent fetches
676
+ const hasLoadedAvailabilitiesRef = useRef(false); // First successful availability paint completed
677
+ const inFlightRangeRef = useRef<{ start: Date; end: Date } | null>(null); // Range currently being fetched
676
678
  const fetchedRangesRef = useRef<Array<{ start: Date; end: Date }>>([]); // Track fetched date ranges
677
679
  const pendingRangeRef = useRef<{ start: Date; end: Date } | null>(null); // Range to fetch when current fetch completes (user navigated during fetch)
678
680
  const [visibleRange, setVisibleRange] = useState<{ start: Date; end: Date } | null>(null);
@@ -1068,6 +1070,14 @@ export function BookingFlow({
1068
1070
  async function fetchAvailabilities() {
1069
1071
  // Prevent concurrent fetches - store range to fetch when current one completes
1070
1072
  if (fetchingRef.current && visibleRange) {
1073
+ const inFlight = inFlightRangeRef.current;
1074
+ if (
1075
+ inFlight &&
1076
+ inFlight.start.getTime() === visibleRange.start.getTime() &&
1077
+ inFlight.end.getTime() === visibleRange.end.getTime()
1078
+ ) {
1079
+ return;
1080
+ }
1071
1081
  pendingRangeRef.current = { start: visibleRange.start, end: visibleRange.end };
1072
1082
  return;
1073
1083
  }
@@ -1119,6 +1129,9 @@ export function BookingFlow({
1119
1129
  const isStale = availabilitiesCache?.isStale(cached) ?? false;
1120
1130
  if (cacheCoversRange) {
1121
1131
  setAvailabilities(cached.availabilities);
1132
+ if (cached.availabilities.length > 0) {
1133
+ hasLoadedAvailabilitiesRef.current = true;
1134
+ }
1122
1135
  if (cached.pricingConfig) {
1123
1136
  setPricingConfig(cached.pricingConfig);
1124
1137
  pricingConfigSetRef.current = true;
@@ -1137,6 +1150,9 @@ export function BookingFlow({
1137
1150
  }
1138
1151
  // Partial cache: show cached data immediately, then fetch missing range below
1139
1152
  setAvailabilities(cached.availabilities);
1153
+ if (cached.availabilities.length > 0) {
1154
+ hasLoadedAvailabilitiesRef.current = true;
1155
+ }
1140
1156
  if (cached.pricingConfig) {
1141
1157
  setPricingConfig(cached.pricingConfig);
1142
1158
  pricingConfigSetRef.current = true;
@@ -1160,8 +1176,14 @@ export function BookingFlow({
1160
1176
  }
1161
1177
 
1162
1178
  const hasPartialCache = cached && cached.availabilities.length > 0;
1179
+ const shouldUsePrimaryLoader =
1180
+ !hasPartialCache && !hasLoadedAvailabilitiesRef.current;
1163
1181
  fetchingRef.current = true;
1164
- if (!hasPartialCache) setLoadingAvailabilities(true);
1182
+ inFlightRangeRef.current = {
1183
+ start: new Date(clampedStart),
1184
+ end: new Date(clampedEnd),
1185
+ };
1186
+ if (shouldUsePrimaryLoader) setLoadingAvailabilities(true);
1165
1187
  else setIsFetchingMoreAvailabilities(true);
1166
1188
 
1167
1189
  try {
@@ -1218,6 +1240,9 @@ export function BookingFlow({
1218
1240
 
1219
1241
  const results = await Promise.all(availabilityPromises);
1220
1242
  const allFetchedAvailabilities = results.flatMap(r => r.availabilities);
1243
+ if (allFetchedAvailabilities.length > 0) {
1244
+ hasLoadedAvailabilitiesRef.current = true;
1245
+ }
1221
1246
  setPrecomputedPricesByOption(prev => {
1222
1247
  const next = { ...(prev || {}) };
1223
1248
  results.forEach(r => {
@@ -1298,6 +1323,7 @@ export function BookingFlow({
1298
1323
  setLoadingAvailabilities(false);
1299
1324
  setIsFetchingMoreAvailabilities(false);
1300
1325
  fetchingRef.current = false;
1326
+ inFlightRangeRef.current = null;
1301
1327
  // If user navigated during fetch, trigger fetch for the pending range
1302
1328
  const pending = pendingRangeRef.current;
1303
1329
  if (pending) {
@@ -252,7 +252,7 @@ export function ItineraryBox({
252
252
  <button
253
253
  type="button"
254
254
  onClick={handlePickupLocationClick}
255
- className={styles.placeLink}
255
+ className={`${styles.placeLink} text-emerald-600`}
256
256
  >
257
257
  {getDisplayLabel(locationOnlyDisplay)}
258
258
  </button>
@@ -284,7 +284,7 @@ export function ItineraryBox({
284
284
  <button
285
285
  type="button"
286
286
  onClick={handlePickupLocationClick}
287
- className={styles.placeLink}
287
+ className={`${styles.placeLink} text-emerald-600`}
288
288
  >
289
289
  {getDisplayLabel(locationOnlyDisplay)}
290
290
  </button>
@@ -301,13 +301,16 @@
301
301
  color: #b91c1c !important;
302
302
  }
303
303
 
304
- /* Clickable "your pickup location" etc. - style as link (blue, underlined) like ticketbooth */
304
+ /* Clickable "your pickup location" etc. should stay booking green */
305
305
  .booking-flow-preflight button.text-stone-400.underline,
306
306
  .booking-flow-preflight button.text-stone-400.underline:hover {
307
- color: var(--booking-primary) !important;
307
+ color: var(--booking-emerald-600, #059669) !important;
308
308
  background: none;
309
309
  border: none;
310
310
  }
311
+ .booking-flow-preflight button.text-stone-400.underline:hover {
312
+ color: var(--booking-emerald-700, #047857) !important;
313
+ }
311
314
 
312
315
  /* ========== Itinerary section - orange bg, Poppins, lowercase (match TourDescription) ========== */
313
316
  .booking-flow-preflight [class*="ItineraryBox_box"],