@aranova/tracking-react 1.2.5 → 1.4.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.
- package/README.md +77 -0
- package/dist/{tracking-config-runtime-C6H7ofmo.d.ts → bookings-CqCuuyP6.d.mts} +165 -1
- package/dist/{tracking-config-runtime-C6H7ofmo.d.mts → bookings-DIu1ho6v.d.ts} +165 -1
- package/dist/client.d.mts +58 -5
- package/dist/client.d.ts +58 -5
- package/dist/client.js +287 -11
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +282 -11
- package/dist/client.mjs.map +1 -1
- package/dist/form-submit-BLb28aCk.d.mts +75 -0
- package/dist/form-submit-BLb28aCk.d.ts +75 -0
- package/dist/index.d.mts +7 -6
- package/dist/index.d.ts +7 -6
- package/dist/index.js +470 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +465 -4
- package/dist/index.mjs.map +1 -1
- package/dist/{ingest-typed-CUpdCtmM.d.ts → ingest-typed-D2S3Kb8m.d.ts} +83 -2
- package/dist/{ingest-typed-oAwnKqH2.d.mts → ingest-typed-D7UZr0EM.d.mts} +83 -2
- package/dist/{phone-utils-qLfVpQI7.d.mts → phone-utils-CM8eUPz8.d.mts} +2 -74
- package/dist/{phone-utils-qLfVpQI7.d.ts → phone-utils-l8ey3Zdm.d.ts} +2 -74
- package/dist/phone.d.mts +2 -1
- package/dist/phone.d.ts +2 -1
- package/dist/{sales-C3O_xxZ_.d.ts → sales-CxJVthx_.d.mts} +23 -2
- package/dist/{sales-BuKD_bdo.d.mts → sales-hlcVTJSS.d.ts} +23 -2
- package/dist/sales.d.mts +3 -2
- package/dist/sales.d.ts +3 -2
- package/dist/sales.js +386 -38
- package/dist/sales.js.map +1 -1
- package/dist/sales.mjs +384 -38
- package/dist/sales.mjs.map +1 -1
- package/package.json +1 -1
package/dist/client.mjs
CHANGED
|
@@ -243,6 +243,8 @@ var TRACKING_CAPABILITIES = [
|
|
|
243
243
|
"ad_tags_meta",
|
|
244
244
|
"base_tracking",
|
|
245
245
|
"blog_rendering",
|
|
246
|
+
"booking_handoff",
|
|
247
|
+
"booking_integration",
|
|
246
248
|
"calendar_read",
|
|
247
249
|
"calendar_write",
|
|
248
250
|
"consent_controls",
|
|
@@ -645,15 +647,23 @@ function buildLandingPayloadFields(sessionId, override) {
|
|
|
645
647
|
};
|
|
646
648
|
}
|
|
647
649
|
|
|
650
|
+
// ../tracking-core/src/uuid.ts
|
|
651
|
+
function uuidV4() {
|
|
652
|
+
const cryptoApi = globalThis.crypto;
|
|
653
|
+
if (typeof cryptoApi?.randomUUID === "function") return cryptoApi.randomUUID();
|
|
654
|
+
const bytes = new Uint8Array(16);
|
|
655
|
+
if (typeof cryptoApi?.getRandomValues === "function") cryptoApi.getRandomValues(bytes);
|
|
656
|
+
else for (let i = 0; i < 16; i += 1) bytes[i] = Math.floor(Math.random() * 256);
|
|
657
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
658
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
659
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
660
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
661
|
+
}
|
|
662
|
+
|
|
648
663
|
// ../tracking-core/src/session.ts
|
|
649
664
|
var VISITOR_STORAGE_KEY = "aranova_tracking_visitor";
|
|
650
665
|
var SESSION_STORAGE_KEY = "aranova_tracking_session";
|
|
651
666
|
var SESSION_IDLE_MS = 30 * 60 * 1e3;
|
|
652
|
-
function safeUuid() {
|
|
653
|
-
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function")
|
|
654
|
-
return crypto.randomUUID();
|
|
655
|
-
return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`;
|
|
656
|
-
}
|
|
657
667
|
function readLocalStorage(key) {
|
|
658
668
|
try {
|
|
659
669
|
return window.localStorage.getItem(key);
|
|
@@ -668,10 +678,10 @@ function writeLocalStorage(key, value) {
|
|
|
668
678
|
}
|
|
669
679
|
}
|
|
670
680
|
function getVisitorId() {
|
|
671
|
-
if (typeof window === "undefined") return
|
|
681
|
+
if (typeof window === "undefined") return uuidV4();
|
|
672
682
|
const existing = readLocalStorage(VISITOR_STORAGE_KEY);
|
|
673
683
|
if (existing && existing.length > 0) return existing;
|
|
674
|
-
const fresh =
|
|
684
|
+
const fresh = uuidV4();
|
|
675
685
|
writeLocalStorage(VISITOR_STORAGE_KEY, fresh);
|
|
676
686
|
return fresh;
|
|
677
687
|
}
|
|
@@ -683,7 +693,7 @@ function adoptVisitorId(visitorId) {
|
|
|
683
693
|
return readLocalStorage(VISITOR_STORAGE_KEY) === visitorId;
|
|
684
694
|
}
|
|
685
695
|
function getOrRotateSessionId(now = Date.now()) {
|
|
686
|
-
if (typeof window === "undefined") return { id:
|
|
696
|
+
if (typeof window === "undefined") return { id: uuidV4(), isNew: true };
|
|
687
697
|
const raw = readLocalStorage(SESSION_STORAGE_KEY);
|
|
688
698
|
if (raw) {
|
|
689
699
|
try {
|
|
@@ -698,7 +708,7 @@ function getOrRotateSessionId(now = Date.now()) {
|
|
|
698
708
|
} catch {
|
|
699
709
|
}
|
|
700
710
|
}
|
|
701
|
-
const fresh = { id:
|
|
711
|
+
const fresh = { id: uuidV4(), last_event_at: now };
|
|
702
712
|
writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));
|
|
703
713
|
return { id: fresh.id, isNew: true };
|
|
704
714
|
}
|
|
@@ -918,6 +928,10 @@ function fireConversionWithConsent(input, options) {
|
|
|
918
928
|
}
|
|
919
929
|
|
|
920
930
|
// ../tracking-core/src/resources/conversion-config.ts
|
|
931
|
+
var BOOKING_MODES = ["live", "test", "simulated"];
|
|
932
|
+
function isBookingMode(value) {
|
|
933
|
+
return BOOKING_MODES.includes(value);
|
|
934
|
+
}
|
|
921
935
|
function isStringMap(value) {
|
|
922
936
|
return typeof value === "object" && value !== null && Object.values(value).every((v) => typeof v === "string");
|
|
923
937
|
}
|
|
@@ -931,6 +945,70 @@ function parseFiring(value) {
|
|
|
931
945
|
currency: typeof f.currency === "string" ? f.currency : null
|
|
932
946
|
};
|
|
933
947
|
}
|
|
948
|
+
function isNumberList(value) {
|
|
949
|
+
return Array.isArray(value) && value.every((v) => typeof v === "number");
|
|
950
|
+
}
|
|
951
|
+
function isStringList(value) {
|
|
952
|
+
return Array.isArray(value) && value.every((v) => typeof v === "string");
|
|
953
|
+
}
|
|
954
|
+
function parseWindowsMap(value) {
|
|
955
|
+
const windowsByKey = {};
|
|
956
|
+
if (!value || typeof value !== "object") return windowsByKey;
|
|
957
|
+
for (const [key, windows] of Object.entries(value)) {
|
|
958
|
+
if (!windows || typeof windows !== "object") continue;
|
|
959
|
+
const w = windows;
|
|
960
|
+
windowsByKey[key] = {
|
|
961
|
+
morning: isNumberList(w.morning) ? w.morning : [],
|
|
962
|
+
afternoon: isNumberList(w.afternoon) ? w.afternoon : []
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
return windowsByKey;
|
|
966
|
+
}
|
|
967
|
+
function parseBookingRules(value) {
|
|
968
|
+
if (!value || typeof value !== "object") return null;
|
|
969
|
+
const r = value;
|
|
970
|
+
if (typeof r.min_days_ahead !== "number" || typeof r.max_days_ahead !== "number") return null;
|
|
971
|
+
if (!r.hours || typeof r.hours !== "object") return null;
|
|
972
|
+
const hours = parseWindowsMap(r.hours);
|
|
973
|
+
return {
|
|
974
|
+
min_days_ahead: r.min_days_ahead,
|
|
975
|
+
max_days_ahead: r.max_days_ahead,
|
|
976
|
+
hours,
|
|
977
|
+
closed_dates: isStringList(r.closed_dates) ? r.closed_dates : [],
|
|
978
|
+
special_hours: parseWindowsMap(r.special_hours),
|
|
979
|
+
vehicle_required: r.vehicle_required === true,
|
|
980
|
+
drop_off_options: isStringList(r.drop_off_options) ? r.drop_off_options : []
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
function parseBookingOption(value) {
|
|
984
|
+
if (!value || typeof value !== "object") return [];
|
|
985
|
+
const o = value;
|
|
986
|
+
if (typeof o.key !== "string" || !o.key || typeof o.label !== "string") return [];
|
|
987
|
+
return [{ key: o.key, label: o.label }];
|
|
988
|
+
}
|
|
989
|
+
function parseIntegrations(value) {
|
|
990
|
+
if (!value || typeof value !== "object") return {};
|
|
991
|
+
const autoleap = value.autoleap;
|
|
992
|
+
if (!autoleap || typeof autoleap !== "object") return {};
|
|
993
|
+
const booking = autoleap.booking;
|
|
994
|
+
if (!booking || typeof booking !== "object") return {};
|
|
995
|
+
const b = booking;
|
|
996
|
+
if (typeof b.fallback_url !== "string" || !b.fallback_url) return {};
|
|
997
|
+
return {
|
|
998
|
+
autoleap: {
|
|
999
|
+
booking: {
|
|
1000
|
+
enabled: b.enabled === true,
|
|
1001
|
+
fallback_url: b.fallback_url,
|
|
1002
|
+
timezone: typeof b.timezone === "string" ? b.timezone : "UTC",
|
|
1003
|
+
config_version: typeof b.config_version === "number" ? b.config_version : null,
|
|
1004
|
+
accepts_bookings: b.accepts_bookings === true,
|
|
1005
|
+
rules: parseBookingRules(b.rules),
|
|
1006
|
+
options: Array.isArray(b.options) ? b.options.flatMap(parseBookingOption) : [],
|
|
1007
|
+
...isBookingMode(b.mode) ? { mode: b.mode } : {}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
934
1012
|
function parseTrigger(value) {
|
|
935
1013
|
if (!value || typeof value !== "object") return null;
|
|
936
1014
|
const t = value;
|
|
@@ -990,7 +1068,8 @@ function parseConversionConfig(raw) {
|
|
|
990
1068
|
meta_pixel_ids: isStringMap(obj.meta_pixel_ids) ? obj.meta_pixel_ids : {},
|
|
991
1069
|
services,
|
|
992
1070
|
goals,
|
|
993
|
-
linked_hostnames: Array.isArray(obj.linked_hostnames) ? obj.linked_hostnames.filter((h) => typeof h === "string" && h.length > 0) : []
|
|
1071
|
+
linked_hostnames: Array.isArray(obj.linked_hostnames) ? obj.linked_hostnames.filter((h) => typeof h === "string" && h.length > 0) : [],
|
|
1072
|
+
integrations: parseIntegrations(obj.integrations)
|
|
994
1073
|
};
|
|
995
1074
|
}
|
|
996
1075
|
var CACHE_PREFIX = "_aranova_cfg_";
|
|
@@ -1414,6 +1493,13 @@ var TrackingConfigRuntime = class {
|
|
|
1414
1493
|
linkedHostnames() {
|
|
1415
1494
|
return this.current?.linked_hostnames ?? [];
|
|
1416
1495
|
}
|
|
1496
|
+
/**
|
|
1497
|
+
* Integration blocks, from the cached object even while unconfirmed: the booking fallback
|
|
1498
|
+
* link is safe to show stale. Same reference until the next adopt, for `useSyncExternalStore`.
|
|
1499
|
+
*/
|
|
1500
|
+
integrations() {
|
|
1501
|
+
return this.current?.integrations ?? null;
|
|
1502
|
+
}
|
|
1417
1503
|
async revalidateNow() {
|
|
1418
1504
|
try {
|
|
1419
1505
|
const headers = {};
|
|
@@ -1583,6 +1669,106 @@ function getTrackingConfigRuntime(ref, fetchImpl) {
|
|
|
1583
1669
|
return runtime;
|
|
1584
1670
|
}
|
|
1585
1671
|
|
|
1672
|
+
// ../../contracts/autoleap-booking-links.json
|
|
1673
|
+
var autoleap_booking_links_default = {
|
|
1674
|
+
hosts: ["myalp.io"]
|
|
1675
|
+
};
|
|
1676
|
+
|
|
1677
|
+
// ../tracking-core/src/integrations-public.ts
|
|
1678
|
+
var HANDOFF_HOSTS = new Set(autoleap_booking_links_default.hosts);
|
|
1679
|
+
function getBookingIntegration(integrations) {
|
|
1680
|
+
const booking = integrations?.autoleap?.booking;
|
|
1681
|
+
if (!booking) return null;
|
|
1682
|
+
const rules = booking.rules ? camelRules(booking.rules) : null;
|
|
1683
|
+
return {
|
|
1684
|
+
provider: "autoleap",
|
|
1685
|
+
enabled: booking.enabled,
|
|
1686
|
+
fallbackUrl: booking.fallback_url,
|
|
1687
|
+
acceptsBookings: booking.accepts_bookings && rules !== null && booking.config_version !== null,
|
|
1688
|
+
timezone: booking.timezone,
|
|
1689
|
+
configVersion: booking.config_version,
|
|
1690
|
+
rules,
|
|
1691
|
+
options: booking.options.map((option) => ({ key: option.key, label: option.label })),
|
|
1692
|
+
mode: booking.mode ?? "live"
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1695
|
+
function camelRules(rules) {
|
|
1696
|
+
return {
|
|
1697
|
+
minDaysAhead: rules.min_days_ahead,
|
|
1698
|
+
maxDaysAhead: rules.max_days_ahead,
|
|
1699
|
+
hours: rules.hours,
|
|
1700
|
+
closedDates: rules.closed_dates,
|
|
1701
|
+
specialHours: rules.special_hours,
|
|
1702
|
+
vehicleRequired: rules.vehicle_required,
|
|
1703
|
+
dropOffOptions: rules.drop_off_options
|
|
1704
|
+
};
|
|
1705
|
+
}
|
|
1706
|
+
var WEEKDAYS = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
|
|
1707
|
+
function shopToday(timeZone, now = /* @__PURE__ */ new Date()) {
|
|
1708
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
1709
|
+
timeZone,
|
|
1710
|
+
year: "numeric",
|
|
1711
|
+
month: "2-digit",
|
|
1712
|
+
day: "2-digit"
|
|
1713
|
+
}).formatToParts(now);
|
|
1714
|
+
const part = (type) => parts.find((p) => p.type === type)?.value ?? "";
|
|
1715
|
+
return `${part("year")}-${part("month")}-${part("day")}`;
|
|
1716
|
+
}
|
|
1717
|
+
function addDays(isoDate, days) {
|
|
1718
|
+
const [year, month, day] = isoDate.split("-").map(Number);
|
|
1719
|
+
const date = new Date(Date.UTC(year, month - 1, day + days));
|
|
1720
|
+
return date.toISOString().slice(0, 10);
|
|
1721
|
+
}
|
|
1722
|
+
function bookableSlots(rules, { timeZone, now = /* @__PURE__ */ new Date() }) {
|
|
1723
|
+
const today = shopToday(timeZone, now);
|
|
1724
|
+
const days = [];
|
|
1725
|
+
const closed = new Set(rules.closedDates);
|
|
1726
|
+
for (let offset = rules.minDaysAhead; offset <= rules.maxDaysAhead; offset += 1) {
|
|
1727
|
+
const date = addDays(today, offset);
|
|
1728
|
+
if (closed.has(date)) continue;
|
|
1729
|
+
const weekday = WEEKDAYS[(/* @__PURE__ */ new Date(`${date}T00:00:00Z`)).getUTCDay()];
|
|
1730
|
+
const windows = rules.specialHours[date] ?? rules.hours[weekday];
|
|
1731
|
+
if (!windows || windows.morning.length === 0 && windows.afternoon.length === 0) continue;
|
|
1732
|
+
days.push({
|
|
1733
|
+
date,
|
|
1734
|
+
weekday,
|
|
1735
|
+
morning: [...windows.morning],
|
|
1736
|
+
afternoon: [...windows.afternoon]
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
return days;
|
|
1740
|
+
}
|
|
1741
|
+
function buildBookingHandoffUrl(fallbackUrl, prefill = {}) {
|
|
1742
|
+
let url;
|
|
1743
|
+
try {
|
|
1744
|
+
url = new URL(fallbackUrl);
|
|
1745
|
+
} catch {
|
|
1746
|
+
return null;
|
|
1747
|
+
}
|
|
1748
|
+
if (url.protocol !== "https:" || !HANDOFF_HOSTS.has(url.hostname) || url.username || url.password || url.port) {
|
|
1749
|
+
return null;
|
|
1750
|
+
}
|
|
1751
|
+
const [firstName, lastName] = prefill.firstName?.trim() ? [prefill.firstName, prefill.lastName] : splitFullName(prefill.name);
|
|
1752
|
+
const values = [
|
|
1753
|
+
["firstName", firstName?.trim()],
|
|
1754
|
+
["lastName", lastName?.trim()],
|
|
1755
|
+
["phone", prefill.phone ? nationalDigits(prefill.phone) : ""],
|
|
1756
|
+
["email", prefill.email?.trim()]
|
|
1757
|
+
];
|
|
1758
|
+
for (const [key, value] of values) {
|
|
1759
|
+
if (value) url.searchParams.set(key, value);
|
|
1760
|
+
}
|
|
1761
|
+
return url.toString();
|
|
1762
|
+
}
|
|
1763
|
+
function splitFullName(name) {
|
|
1764
|
+
const [first = "", ...rest] = (name ?? "").trim().split(/\s+/);
|
|
1765
|
+
return [first, rest.join(" ")];
|
|
1766
|
+
}
|
|
1767
|
+
function nationalDigits(phone) {
|
|
1768
|
+
const parsed = parsePhone(phone);
|
|
1769
|
+
return (parsed.isValid ? parsed.national : phone).replace(/\D/g, "");
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1586
1772
|
// ../tracking-core/src/resources/conversion-autofire.ts
|
|
1587
1773
|
function currentPath() {
|
|
1588
1774
|
return typeof window === "undefined" ? "" : window.location.pathname;
|
|
@@ -3402,6 +3588,9 @@ function attachPhoneClickCapture(client, config) {
|
|
|
3402
3588
|
};
|
|
3403
3589
|
}
|
|
3404
3590
|
|
|
3591
|
+
// ../tracking-core/src/resources/sales/bookings.ts
|
|
3592
|
+
var newBookingAttemptKey = uuidV4;
|
|
3593
|
+
|
|
3405
3594
|
// src/hooks.ts
|
|
3406
3595
|
function useGclid() {
|
|
3407
3596
|
const [gclid, setGclid] = useState(null);
|
|
@@ -3733,7 +3922,7 @@ function GoogleAdsTracking(props) {
|
|
|
3733
3922
|
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect5, useMemo as useMemo4 } from "react";
|
|
3734
3923
|
|
|
3735
3924
|
// package.json
|
|
3736
|
-
var version = "1.
|
|
3925
|
+
var version = "1.4.0";
|
|
3737
3926
|
|
|
3738
3927
|
// ../tracking-core/src/phone-react.tsx
|
|
3739
3928
|
import {
|
|
@@ -4007,12 +4196,94 @@ function createTracking(options) {
|
|
|
4007
4196
|
)
|
|
4008
4197
|
};
|
|
4009
4198
|
}
|
|
4199
|
+
|
|
4200
|
+
// ../tracking-core/src/booking-react.tsx
|
|
4201
|
+
import { useCallback as useCallback3, useMemo as useMemo5, useRef, useState as useState4, useSyncExternalStore } from "react";
|
|
4202
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
4203
|
+
function serverSnapshot() {
|
|
4204
|
+
return null;
|
|
4205
|
+
}
|
|
4206
|
+
function useBookingIntegration(trackingConfig) {
|
|
4207
|
+
registerCapability("booking_handoff");
|
|
4208
|
+
const subscribe = useCallback3(
|
|
4209
|
+
(listener) => trackingConfig ? getTrackingConfigRuntime(trackingConfig).subscribe(listener) : () => {
|
|
4210
|
+
},
|
|
4211
|
+
[trackingConfig]
|
|
4212
|
+
);
|
|
4213
|
+
const integrations = useSyncExternalStore(
|
|
4214
|
+
subscribe,
|
|
4215
|
+
() => trackingConfig ? getTrackingConfigRuntime(trackingConfig).integrations() : null,
|
|
4216
|
+
serverSnapshot
|
|
4217
|
+
);
|
|
4218
|
+
return useMemo5(() => getBookingIntegration(integrations), [integrations]);
|
|
4219
|
+
}
|
|
4220
|
+
function useBookingHandoffUrl(trackingConfig, { fallbackUrl, prefill } = {}) {
|
|
4221
|
+
const booking = useBookingIntegration(trackingConfig);
|
|
4222
|
+
const base = booking?.fallbackUrl ?? fallbackUrl;
|
|
4223
|
+
return base ? buildBookingHandoffUrl(base, prefill) : null;
|
|
4224
|
+
}
|
|
4225
|
+
function BookingHandoffLink({
|
|
4226
|
+
trackingConfig,
|
|
4227
|
+
fallbackUrl,
|
|
4228
|
+
prefill,
|
|
4229
|
+
children,
|
|
4230
|
+
target = "_blank",
|
|
4231
|
+
rel = "noopener noreferrer",
|
|
4232
|
+
...anchorProps
|
|
4233
|
+
}) {
|
|
4234
|
+
const href = useBookingHandoffUrl(trackingConfig, { fallbackUrl, prefill });
|
|
4235
|
+
if (!href) return null;
|
|
4236
|
+
return /* @__PURE__ */ jsx4("a", { ...anchorProps, href, target, rel, children });
|
|
4237
|
+
}
|
|
4238
|
+
function useBookingSlots(trackingConfig) {
|
|
4239
|
+
const booking = useBookingIntegration(trackingConfig);
|
|
4240
|
+
if (!booking?.acceptsBookings || !booking.rules) return [];
|
|
4241
|
+
return bookableSlots(booking.rules, { timeZone: booking.timezone });
|
|
4242
|
+
}
|
|
4243
|
+
function useBookingSubmit(bookings, options = {}) {
|
|
4244
|
+
registerCapability("booking_integration");
|
|
4245
|
+
const [outcome, setOutcome] = useState4(null);
|
|
4246
|
+
const [submitting, setSubmitting] = useState4(false);
|
|
4247
|
+
const attemptKey = useRef(null);
|
|
4248
|
+
const inFlight = useRef(null);
|
|
4249
|
+
const { formSubmit, simulate } = options;
|
|
4250
|
+
const submit = useCallback3(
|
|
4251
|
+
(input) => {
|
|
4252
|
+
if (inFlight.current) return inFlight.current;
|
|
4253
|
+
attemptKey.current ?? (attemptKey.current = newBookingAttemptKey());
|
|
4254
|
+
setSubmitting(true);
|
|
4255
|
+
const run = bookings.submit(input, { attemptKey: attemptKey.current, formSubmit, simulate }).then((result) => {
|
|
4256
|
+
const unrecorded = result.status === "invalid" || result.status === "stale" || result.status === "failed" && result.id === null;
|
|
4257
|
+
if (!unrecorded) attemptKey.current = null;
|
|
4258
|
+
setOutcome(result);
|
|
4259
|
+
return result;
|
|
4260
|
+
}).finally(() => {
|
|
4261
|
+
inFlight.current = null;
|
|
4262
|
+
setSubmitting(false);
|
|
4263
|
+
});
|
|
4264
|
+
inFlight.current = run;
|
|
4265
|
+
return run;
|
|
4266
|
+
},
|
|
4267
|
+
[bookings, formSubmit, simulate]
|
|
4268
|
+
);
|
|
4269
|
+
const reset = useCallback3(() => {
|
|
4270
|
+
attemptKey.current = null;
|
|
4271
|
+
setOutcome(null);
|
|
4272
|
+
}, []);
|
|
4273
|
+
const status = submitting ? "submitting" : outcome?.status ?? "idle";
|
|
4274
|
+
return { status, outcome, submit, reset };
|
|
4275
|
+
}
|
|
4010
4276
|
export {
|
|
4011
4277
|
AdPlatformTracking,
|
|
4278
|
+
BookingHandoffLink,
|
|
4012
4279
|
ConsentBanner,
|
|
4013
4280
|
GoogleAdsTracking,
|
|
4014
4281
|
PhoneField,
|
|
4015
4282
|
createTracking,
|
|
4283
|
+
useBookingHandoffUrl,
|
|
4284
|
+
useBookingIntegration,
|
|
4285
|
+
useBookingSlots,
|
|
4286
|
+
useBookingSubmit,
|
|
4016
4287
|
useConsent,
|
|
4017
4288
|
useConsentState,
|
|
4018
4289
|
useCookiePreferences,
|