@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/README.md
CHANGED
|
@@ -278,6 +278,83 @@ npx @aranova/tracking-cli gen # reads ARANOVA_TRACKING_SECRET_KEY from
|
|
|
278
278
|
See the full guide: [sales-tracking.md](https://github.com/AranovaIO/aranova_internal/blob/master/docs/tracking-package/sales-tracking.md)
|
|
279
279
|
and the CLI reference: [cli.md](https://github.com/AranovaIO/aranova_internal/blob/master/docs/tracking-package/cli.md).
|
|
280
280
|
|
|
281
|
+
## Booking handoff (booking integrations)
|
|
282
|
+
|
|
283
|
+
When the business books through an integration (AutoLeap today), the published tracking config
|
|
284
|
+
carries `integrations.autoleap.booking`. After a lead form's successful submit (and after
|
|
285
|
+
`form_submit` fires), offer the optional "Pick your time" step:
|
|
286
|
+
|
|
287
|
+
```tsx
|
|
288
|
+
import { BookingHandoffLink } from "@aranova/tracking-react";
|
|
289
|
+
import { ARANOVA_TRACKING_CONFIG } from "@/aranova-services";
|
|
290
|
+
|
|
291
|
+
<BookingHandoffLink
|
|
292
|
+
trackingConfig={ARANOVA_TRACKING_CONFIG}
|
|
293
|
+
fallbackUrl="https://myalp.io/…" // baked last resort when no config has loaded
|
|
294
|
+
prefill={{ firstName, lastName, phone, email }}
|
|
295
|
+
>
|
|
296
|
+
Pick your time
|
|
297
|
+
</BookingHandoffLink>;
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
- The link is the shop's own booking page from the live config, else `fallbackUrl`; it renders
|
|
301
|
+
nothing unless it is `https://myalp.io/…`. The phone goes out as digits.
|
|
302
|
+
- It shows even when `enabled` is false: the customer always has a way to book.
|
|
303
|
+
- `useBookingIntegration(trackingConfig)` returns the live provider (or null before a config
|
|
304
|
+
loads), from the shared config runtime (no extra fetch): `enabled`, `fallbackUrl`,
|
|
305
|
+
`acceptsBookings`, `timezone`, `configVersion`, `rules` and `options`.
|
|
306
|
+
`getBookingIntegration(integrations)` and `buildBookingHandoffUrl(url, prefill)` are the
|
|
307
|
+
React-free equivalents.
|
|
308
|
+
- Reports the `booking_handoff` capability.
|
|
309
|
+
|
|
310
|
+
## Website bookings (booking integrations)
|
|
311
|
+
|
|
312
|
+
When `acceptsBookings` is true the site can book straight into the shop's system through Aranova's
|
|
313
|
+
API. The SDK is headless; build the form in the site's own design system:
|
|
314
|
+
|
|
315
|
+
```tsx
|
|
316
|
+
import { useBookingIntegration, useBookingSlots, useBookingSubmit } from "@aranova/tracking-react";
|
|
317
|
+
|
|
318
|
+
const booking = useBookingIntegration(ARANOVA_TRACKING_CONFIG);
|
|
319
|
+
const days = useBookingSlots(ARANOVA_TRACKING_CONFIG); // [{ date, weekday, morning, afternoon }]
|
|
320
|
+
const { status, outcome, submit } = useBookingSubmit(conversions.bookings, {
|
|
321
|
+
formSubmit: { tracking, formId: "booking-form" },
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
await submit({
|
|
325
|
+
startDate: "2026-10-06",
|
|
326
|
+
startHour: 9,
|
|
327
|
+
firstName,
|
|
328
|
+
lastName,
|
|
329
|
+
phone,
|
|
330
|
+
email,
|
|
331
|
+
vehicle: { year: 2019, make: "Toyota", model: "RAV4" },
|
|
332
|
+
dropOff: "dropOff", // only when booking.rules.dropOffOptions is non-empty
|
|
333
|
+
services: ["brakes"], // keys from booking.options
|
|
334
|
+
notes,
|
|
335
|
+
marketingSmsConsent, // the customer's own unchecked opt-in; never default it on
|
|
336
|
+
});
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
- `conversions` is the site's one `createSalesClient({ apiKey, endpoint, firing: getTrackingConfigRuntime(...) })`;
|
|
340
|
+
`.bookings` reads the rules from that runtime.
|
|
341
|
+
- `outcome.status`: `created` (the shop texts and emails the customer itself; send nothing of
|
|
342
|
+
your own), `pending` (same success; still being sent, the owner is alerted if it fails),
|
|
343
|
+
`failed` (success copy plus `outcome.fallbackUrl`, the prefilled shop page), `invalid`
|
|
344
|
+
(`outcome.message`, nothing sent), `stale` (the times changed; the config is refetched).
|
|
345
|
+
- `outcome.mode` (and `booking.mode` before submitting): `live` for every real customer;
|
|
346
|
+
`test` (sent to the shop's test lead source) or `simulated` (never sent) on a test visit or a
|
|
347
|
+
non-production API. Show a small "test booking" note when it isn't `live`.
|
|
348
|
+
- Development only: `useBookingSubmit(bookings, { simulate: "failed" })` forces a simulated
|
|
349
|
+
outcome (`created`, `rejected`, `unknown`, `failed`) to exercise each screen. The production
|
|
350
|
+
API ignores it. Test locally against the local API **and** the local CDN; the SDK warns in the
|
|
351
|
+
console when the endpoint is localhost but the config comes from production.
|
|
352
|
+
- `useBookingSlots` / `bookableSlots(rules, { timeZone })` offer exactly the portal's start hours,
|
|
353
|
+
judged by the shop's date. `useBookingSubmit` reuses one attempt key across a double-click or
|
|
354
|
+
a retry, and sends the visit's `form_submit` once: don't send your own for this form.
|
|
355
|
+
- Ask for the last name separately, and a mobile number (the shop's confirmation text goes to it).
|
|
356
|
+
- Reports the `booking_integration` capability.
|
|
357
|
+
|
|
281
358
|
## On-site conversion firing
|
|
282
359
|
|
|
283
360
|
Run `tracking-cli gen` once to emit `ARANOVA_TRACKING_CONFIG`, then use that
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { a as FormSubmitMetadata } from './form-submit-BLb28aCk.mjs';
|
|
2
|
+
|
|
1
3
|
interface ServiceFiring {
|
|
2
4
|
send_to: string;
|
|
3
5
|
/** Action's configured default value (cents); the SDK fires it only when a sale has no amount. */
|
|
@@ -41,6 +43,60 @@ interface ConversionConfig {
|
|
|
41
43
|
goals: ConversionGoal[];
|
|
42
44
|
/** Sibling hostnames of this site (roster minus self); the cross-site linker's targets. */
|
|
43
45
|
linked_hostnames?: string[];
|
|
46
|
+
/** Third-party integrations the site renders UI for; a missing provider means none. */
|
|
47
|
+
integrations?: ConversionIntegrations;
|
|
48
|
+
}
|
|
49
|
+
/** Bookable start hours on one weekday, shop wall time (8 = 08:00); both empty = closed. */
|
|
50
|
+
interface BookingDayWindows {
|
|
51
|
+
morning: number[];
|
|
52
|
+
afternoon: number[];
|
|
53
|
+
}
|
|
54
|
+
interface BookingRulesConfig {
|
|
55
|
+
min_days_ahead: number;
|
|
56
|
+
max_days_ahead: number;
|
|
57
|
+
/** Keyed `monday` … `sunday`. */
|
|
58
|
+
hours: Record<string, BookingDayWindows>;
|
|
59
|
+
/** `YYYY-MM-DD` shop dates the portal refuses although the weekday is open. */
|
|
60
|
+
closed_dates: string[];
|
|
61
|
+
/** `YYYY-MM-DD` shop dates whose start hours replace their weekday's; never also closed. */
|
|
62
|
+
special_hours: Record<string, BookingDayWindows>;
|
|
63
|
+
vehicle_required: boolean;
|
|
64
|
+
/** Empty when the shop hides the drop-off question. */
|
|
65
|
+
drop_off_options: string[];
|
|
66
|
+
}
|
|
67
|
+
/** One service choice on the site's booking form; the shop system's ids stay server-side. */
|
|
68
|
+
interface BookingOptionConfig {
|
|
69
|
+
/** What the site posts in `services`. */
|
|
70
|
+
key: string;
|
|
71
|
+
label: string;
|
|
72
|
+
}
|
|
73
|
+
/** Where a booking went; decided by the API. A real customer always gets `live`. */
|
|
74
|
+
declare const BOOKING_MODES: readonly ["live", "test", "simulated"];
|
|
75
|
+
/**
|
|
76
|
+
* `live`: the shop's real queue. `test`: a real request on the shop's test lead source, never
|
|
77
|
+
* counted. `simulated`: never sent; nothing reached the shop or the customer.
|
|
78
|
+
*/
|
|
79
|
+
type BookingMode = (typeof BOOKING_MODES)[number];
|
|
80
|
+
/** `integrations.autoleap.booking` — never carries a secret. */
|
|
81
|
+
interface AutoLeapBookingConfig {
|
|
82
|
+
/** False: show only `fallback_url`. The site never blocks the customer on it. */
|
|
83
|
+
enabled: boolean;
|
|
84
|
+
/** The shop's own booking page; always present. */
|
|
85
|
+
fallback_url: string;
|
|
86
|
+
timezone: string;
|
|
87
|
+
/** Snapshot the rules came from; null before the first one. */
|
|
88
|
+
config_version: number | null;
|
|
89
|
+
/** `enabled` and at least one option mapped: the site may post a booking. */
|
|
90
|
+
accepts_bookings: boolean;
|
|
91
|
+
rules: BookingRulesConfig | null;
|
|
92
|
+
options: BookingOptionConfig[];
|
|
93
|
+
/** Development objects only: `test` or `simulated`, so a dev page can say so up front. */
|
|
94
|
+
mode?: BookingMode;
|
|
95
|
+
}
|
|
96
|
+
interface ConversionIntegrations {
|
|
97
|
+
autoleap?: {
|
|
98
|
+
booking: AutoLeapBookingConfig;
|
|
99
|
+
};
|
|
44
100
|
}
|
|
45
101
|
interface ConversionConfigStore {
|
|
46
102
|
/** Firing config for a goal/service key, or null when it doesn't fire on-site. */
|
|
@@ -190,6 +246,11 @@ declare class TrackingConfigRuntime {
|
|
|
190
246
|
listGoals(): ConversionGoal[];
|
|
191
247
|
/** Sibling hostnames for the linker — read from the cached object even while unconfirmed. */
|
|
192
248
|
linkedHostnames(): readonly string[];
|
|
249
|
+
/**
|
|
250
|
+
* Integration blocks, from the cached object even while unconfirmed: the booking fallback
|
|
251
|
+
* link is safe to show stale. Same reference until the next adopt, for `useSyncExternalStore`.
|
|
252
|
+
*/
|
|
253
|
+
integrations(): ConversionIntegrations | null;
|
|
193
254
|
private revalidateNow;
|
|
194
255
|
private expireAuthority;
|
|
195
256
|
private confirm;
|
|
@@ -199,4 +260,107 @@ declare class TrackingConfigRuntime {
|
|
|
199
260
|
}
|
|
200
261
|
declare function getTrackingConfigRuntime(ref: TrackingConfigReference, fetchImpl?: typeof fetch): TrackingConfigRuntime;
|
|
201
262
|
|
|
202
|
-
|
|
263
|
+
/** A UUID v4 from Web Crypto, with a `getRandomValues` (then `Math.random`) fallback. */
|
|
264
|
+
declare function uuidV4(): string;
|
|
265
|
+
|
|
266
|
+
/** Developer-only outcomes for a simulated create; the production API ignores them. */
|
|
267
|
+
declare const BOOKING_SIMULATIONS: readonly ["created", "rejected", "unknown", "failed"];
|
|
268
|
+
declare const BOOKING_DROP_OFF_OPTIONS: readonly ["waitForIt", "dropOff"];
|
|
269
|
+
type BookingDropOff = (typeof BOOKING_DROP_OFF_OPTIONS)[number];
|
|
270
|
+
type BookingSimulation = (typeof BOOKING_SIMULATIONS)[number];
|
|
271
|
+
/** What the booking form collected. Dates and hours are the shop's wall time. */
|
|
272
|
+
interface BookingInput {
|
|
273
|
+
/** `YYYY-MM-DD`, one of `bookableSlots(...)`'s dates. */
|
|
274
|
+
startDate: string;
|
|
275
|
+
/** A start hour offered on that date (8 = 08:00). */
|
|
276
|
+
startHour: number;
|
|
277
|
+
firstName: string;
|
|
278
|
+
/** Required: the shop's system needs it, and a single-word name has none to split off. */
|
|
279
|
+
lastName: string;
|
|
280
|
+
/** Mobile number; the shop texts its confirmation to it. */
|
|
281
|
+
phone: string;
|
|
282
|
+
email?: string | null;
|
|
283
|
+
vehicle?: {
|
|
284
|
+
year: number;
|
|
285
|
+
make: string;
|
|
286
|
+
model: string;
|
|
287
|
+
} | null;
|
|
288
|
+
/** Ask only when the rules list drop-off options. */
|
|
289
|
+
dropOff?: BookingDropOff | null;
|
|
290
|
+
/** Option keys from `useBookingIntegration(...).options`. */
|
|
291
|
+
services: string[];
|
|
292
|
+
notes?: string | null;
|
|
293
|
+
/**
|
|
294
|
+
* The customer's own opt-in to marketing texts. Never default it on (CASL). Service texts
|
|
295
|
+
* about this booking need no opt-in and are always on.
|
|
296
|
+
*/
|
|
297
|
+
marketingSmsConsent?: boolean;
|
|
298
|
+
}
|
|
299
|
+
/** The form's `form_submit`, sent once per booking attempt and tied to this visit. */
|
|
300
|
+
interface BookingFormSubmit {
|
|
301
|
+
/** The site's tracking client (`useTracking()`): the booking takes the visit it reports. */
|
|
302
|
+
tracking: {
|
|
303
|
+
trackEvent(eventType: "form_submit", metadata: FormSubmitMetadata): void;
|
|
304
|
+
getSessionId(): string;
|
|
305
|
+
};
|
|
306
|
+
/** The `<form id>`, so `form_start` and `form_submit` line up. */
|
|
307
|
+
formId: string;
|
|
308
|
+
}
|
|
309
|
+
type BookingOutcome =
|
|
310
|
+
/** The shop has the request; it texts and emails the customer itself. */
|
|
311
|
+
{
|
|
312
|
+
status: "created";
|
|
313
|
+
id: string;
|
|
314
|
+
requestCode: string | null;
|
|
315
|
+
mode: BookingMode;
|
|
316
|
+
}
|
|
317
|
+
/** Received and still being sent; the shop confirms. Show the same success. */
|
|
318
|
+
| {
|
|
319
|
+
status: "pending";
|
|
320
|
+
id: string;
|
|
321
|
+
mode: BookingMode;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Not created. Still a success for the customer: show the prefilled shop booking link.
|
|
325
|
+
* `id` is null when the booking never reached Aranova (offline, rate-limited, booking off).
|
|
326
|
+
*/
|
|
327
|
+
| {
|
|
328
|
+
status: "failed";
|
|
329
|
+
id: string | null;
|
|
330
|
+
fallbackUrl: string | null;
|
|
331
|
+
mode: BookingMode;
|
|
332
|
+
}
|
|
333
|
+
/** The booking breaks the shop's rules; `message` is customer-safe. Nothing was sent. */
|
|
334
|
+
| {
|
|
335
|
+
status: "invalid";
|
|
336
|
+
message: string;
|
|
337
|
+
reasons: string[];
|
|
338
|
+
}
|
|
339
|
+
/** The shop's times changed since the form loaded; the config is being refetched. */
|
|
340
|
+
| {
|
|
341
|
+
status: "stale";
|
|
342
|
+
message: string;
|
|
343
|
+
};
|
|
344
|
+
interface BookingSubmitOptions {
|
|
345
|
+
attemptKey: string;
|
|
346
|
+
formSubmit?: BookingFormSubmit;
|
|
347
|
+
/**
|
|
348
|
+
* Development only: force a simulated create's outcome to exercise the success, pending and
|
|
349
|
+
* fallback screens. The production API ignores it, so it can never change a real booking.
|
|
350
|
+
*/
|
|
351
|
+
simulate?: BookingSimulation;
|
|
352
|
+
}
|
|
353
|
+
interface SalesBookingsClient {
|
|
354
|
+
/**
|
|
355
|
+
* Send one booking. Never rejects for an API outcome: every answer is a {@link BookingOutcome}.
|
|
356
|
+
* Pass the same `attemptKey` (a UUID, `newBookingAttemptKey()`) on a retry of the same submit,
|
|
357
|
+
* so a double-click or a network retry can never book twice.
|
|
358
|
+
*/
|
|
359
|
+
submit(input: BookingInput, options: BookingSubmitOptions): Promise<BookingOutcome>;
|
|
360
|
+
/** A booking's current status by the id `submit` returned. */
|
|
361
|
+
status(id: string): Promise<BookingOutcome>;
|
|
362
|
+
}
|
|
363
|
+
/** A fresh per-submit key. Keep it for retries of the same submit; mint a new one after. */
|
|
364
|
+
declare const newBookingAttemptKey: typeof uuidV4;
|
|
365
|
+
|
|
366
|
+
export { type BookingDropOff as B, type ConversionConfig as C, type SalesBookingsClient as S, type TrackingConfigReference as T, type BookingFormSubmit as a, type BookingInput as b, type BookingMode as c, type BookingOutcome as d, type BookingSimulation as e, type BookingSubmitOptions as f, type ConversionConfigStore as g, getTrackingConfigRuntime as h, resolveTrackingConfigUrl as i, type BookingDayWindows as j, type ConversionIntegrations as k, TrackingConfigRuntime as l, newBookingAttemptKey as n, resolveConversionConfig as r };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { a as FormSubmitMetadata } from './form-submit-BLb28aCk.js';
|
|
2
|
+
|
|
1
3
|
interface ServiceFiring {
|
|
2
4
|
send_to: string;
|
|
3
5
|
/** Action's configured default value (cents); the SDK fires it only when a sale has no amount. */
|
|
@@ -41,6 +43,60 @@ interface ConversionConfig {
|
|
|
41
43
|
goals: ConversionGoal[];
|
|
42
44
|
/** Sibling hostnames of this site (roster minus self); the cross-site linker's targets. */
|
|
43
45
|
linked_hostnames?: string[];
|
|
46
|
+
/** Third-party integrations the site renders UI for; a missing provider means none. */
|
|
47
|
+
integrations?: ConversionIntegrations;
|
|
48
|
+
}
|
|
49
|
+
/** Bookable start hours on one weekday, shop wall time (8 = 08:00); both empty = closed. */
|
|
50
|
+
interface BookingDayWindows {
|
|
51
|
+
morning: number[];
|
|
52
|
+
afternoon: number[];
|
|
53
|
+
}
|
|
54
|
+
interface BookingRulesConfig {
|
|
55
|
+
min_days_ahead: number;
|
|
56
|
+
max_days_ahead: number;
|
|
57
|
+
/** Keyed `monday` … `sunday`. */
|
|
58
|
+
hours: Record<string, BookingDayWindows>;
|
|
59
|
+
/** `YYYY-MM-DD` shop dates the portal refuses although the weekday is open. */
|
|
60
|
+
closed_dates: string[];
|
|
61
|
+
/** `YYYY-MM-DD` shop dates whose start hours replace their weekday's; never also closed. */
|
|
62
|
+
special_hours: Record<string, BookingDayWindows>;
|
|
63
|
+
vehicle_required: boolean;
|
|
64
|
+
/** Empty when the shop hides the drop-off question. */
|
|
65
|
+
drop_off_options: string[];
|
|
66
|
+
}
|
|
67
|
+
/** One service choice on the site's booking form; the shop system's ids stay server-side. */
|
|
68
|
+
interface BookingOptionConfig {
|
|
69
|
+
/** What the site posts in `services`. */
|
|
70
|
+
key: string;
|
|
71
|
+
label: string;
|
|
72
|
+
}
|
|
73
|
+
/** Where a booking went; decided by the API. A real customer always gets `live`. */
|
|
74
|
+
declare const BOOKING_MODES: readonly ["live", "test", "simulated"];
|
|
75
|
+
/**
|
|
76
|
+
* `live`: the shop's real queue. `test`: a real request on the shop's test lead source, never
|
|
77
|
+
* counted. `simulated`: never sent; nothing reached the shop or the customer.
|
|
78
|
+
*/
|
|
79
|
+
type BookingMode = (typeof BOOKING_MODES)[number];
|
|
80
|
+
/** `integrations.autoleap.booking` — never carries a secret. */
|
|
81
|
+
interface AutoLeapBookingConfig {
|
|
82
|
+
/** False: show only `fallback_url`. The site never blocks the customer on it. */
|
|
83
|
+
enabled: boolean;
|
|
84
|
+
/** The shop's own booking page; always present. */
|
|
85
|
+
fallback_url: string;
|
|
86
|
+
timezone: string;
|
|
87
|
+
/** Snapshot the rules came from; null before the first one. */
|
|
88
|
+
config_version: number | null;
|
|
89
|
+
/** `enabled` and at least one option mapped: the site may post a booking. */
|
|
90
|
+
accepts_bookings: boolean;
|
|
91
|
+
rules: BookingRulesConfig | null;
|
|
92
|
+
options: BookingOptionConfig[];
|
|
93
|
+
/** Development objects only: `test` or `simulated`, so a dev page can say so up front. */
|
|
94
|
+
mode?: BookingMode;
|
|
95
|
+
}
|
|
96
|
+
interface ConversionIntegrations {
|
|
97
|
+
autoleap?: {
|
|
98
|
+
booking: AutoLeapBookingConfig;
|
|
99
|
+
};
|
|
44
100
|
}
|
|
45
101
|
interface ConversionConfigStore {
|
|
46
102
|
/** Firing config for a goal/service key, or null when it doesn't fire on-site. */
|
|
@@ -190,6 +246,11 @@ declare class TrackingConfigRuntime {
|
|
|
190
246
|
listGoals(): ConversionGoal[];
|
|
191
247
|
/** Sibling hostnames for the linker — read from the cached object even while unconfirmed. */
|
|
192
248
|
linkedHostnames(): readonly string[];
|
|
249
|
+
/**
|
|
250
|
+
* Integration blocks, from the cached object even while unconfirmed: the booking fallback
|
|
251
|
+
* link is safe to show stale. Same reference until the next adopt, for `useSyncExternalStore`.
|
|
252
|
+
*/
|
|
253
|
+
integrations(): ConversionIntegrations | null;
|
|
193
254
|
private revalidateNow;
|
|
194
255
|
private expireAuthority;
|
|
195
256
|
private confirm;
|
|
@@ -199,4 +260,107 @@ declare class TrackingConfigRuntime {
|
|
|
199
260
|
}
|
|
200
261
|
declare function getTrackingConfigRuntime(ref: TrackingConfigReference, fetchImpl?: typeof fetch): TrackingConfigRuntime;
|
|
201
262
|
|
|
202
|
-
|
|
263
|
+
/** A UUID v4 from Web Crypto, with a `getRandomValues` (then `Math.random`) fallback. */
|
|
264
|
+
declare function uuidV4(): string;
|
|
265
|
+
|
|
266
|
+
/** Developer-only outcomes for a simulated create; the production API ignores them. */
|
|
267
|
+
declare const BOOKING_SIMULATIONS: readonly ["created", "rejected", "unknown", "failed"];
|
|
268
|
+
declare const BOOKING_DROP_OFF_OPTIONS: readonly ["waitForIt", "dropOff"];
|
|
269
|
+
type BookingDropOff = (typeof BOOKING_DROP_OFF_OPTIONS)[number];
|
|
270
|
+
type BookingSimulation = (typeof BOOKING_SIMULATIONS)[number];
|
|
271
|
+
/** What the booking form collected. Dates and hours are the shop's wall time. */
|
|
272
|
+
interface BookingInput {
|
|
273
|
+
/** `YYYY-MM-DD`, one of `bookableSlots(...)`'s dates. */
|
|
274
|
+
startDate: string;
|
|
275
|
+
/** A start hour offered on that date (8 = 08:00). */
|
|
276
|
+
startHour: number;
|
|
277
|
+
firstName: string;
|
|
278
|
+
/** Required: the shop's system needs it, and a single-word name has none to split off. */
|
|
279
|
+
lastName: string;
|
|
280
|
+
/** Mobile number; the shop texts its confirmation to it. */
|
|
281
|
+
phone: string;
|
|
282
|
+
email?: string | null;
|
|
283
|
+
vehicle?: {
|
|
284
|
+
year: number;
|
|
285
|
+
make: string;
|
|
286
|
+
model: string;
|
|
287
|
+
} | null;
|
|
288
|
+
/** Ask only when the rules list drop-off options. */
|
|
289
|
+
dropOff?: BookingDropOff | null;
|
|
290
|
+
/** Option keys from `useBookingIntegration(...).options`. */
|
|
291
|
+
services: string[];
|
|
292
|
+
notes?: string | null;
|
|
293
|
+
/**
|
|
294
|
+
* The customer's own opt-in to marketing texts. Never default it on (CASL). Service texts
|
|
295
|
+
* about this booking need no opt-in and are always on.
|
|
296
|
+
*/
|
|
297
|
+
marketingSmsConsent?: boolean;
|
|
298
|
+
}
|
|
299
|
+
/** The form's `form_submit`, sent once per booking attempt and tied to this visit. */
|
|
300
|
+
interface BookingFormSubmit {
|
|
301
|
+
/** The site's tracking client (`useTracking()`): the booking takes the visit it reports. */
|
|
302
|
+
tracking: {
|
|
303
|
+
trackEvent(eventType: "form_submit", metadata: FormSubmitMetadata): void;
|
|
304
|
+
getSessionId(): string;
|
|
305
|
+
};
|
|
306
|
+
/** The `<form id>`, so `form_start` and `form_submit` line up. */
|
|
307
|
+
formId: string;
|
|
308
|
+
}
|
|
309
|
+
type BookingOutcome =
|
|
310
|
+
/** The shop has the request; it texts and emails the customer itself. */
|
|
311
|
+
{
|
|
312
|
+
status: "created";
|
|
313
|
+
id: string;
|
|
314
|
+
requestCode: string | null;
|
|
315
|
+
mode: BookingMode;
|
|
316
|
+
}
|
|
317
|
+
/** Received and still being sent; the shop confirms. Show the same success. */
|
|
318
|
+
| {
|
|
319
|
+
status: "pending";
|
|
320
|
+
id: string;
|
|
321
|
+
mode: BookingMode;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Not created. Still a success for the customer: show the prefilled shop booking link.
|
|
325
|
+
* `id` is null when the booking never reached Aranova (offline, rate-limited, booking off).
|
|
326
|
+
*/
|
|
327
|
+
| {
|
|
328
|
+
status: "failed";
|
|
329
|
+
id: string | null;
|
|
330
|
+
fallbackUrl: string | null;
|
|
331
|
+
mode: BookingMode;
|
|
332
|
+
}
|
|
333
|
+
/** The booking breaks the shop's rules; `message` is customer-safe. Nothing was sent. */
|
|
334
|
+
| {
|
|
335
|
+
status: "invalid";
|
|
336
|
+
message: string;
|
|
337
|
+
reasons: string[];
|
|
338
|
+
}
|
|
339
|
+
/** The shop's times changed since the form loaded; the config is being refetched. */
|
|
340
|
+
| {
|
|
341
|
+
status: "stale";
|
|
342
|
+
message: string;
|
|
343
|
+
};
|
|
344
|
+
interface BookingSubmitOptions {
|
|
345
|
+
attemptKey: string;
|
|
346
|
+
formSubmit?: BookingFormSubmit;
|
|
347
|
+
/**
|
|
348
|
+
* Development only: force a simulated create's outcome to exercise the success, pending and
|
|
349
|
+
* fallback screens. The production API ignores it, so it can never change a real booking.
|
|
350
|
+
*/
|
|
351
|
+
simulate?: BookingSimulation;
|
|
352
|
+
}
|
|
353
|
+
interface SalesBookingsClient {
|
|
354
|
+
/**
|
|
355
|
+
* Send one booking. Never rejects for an API outcome: every answer is a {@link BookingOutcome}.
|
|
356
|
+
* Pass the same `attemptKey` (a UUID, `newBookingAttemptKey()`) on a retry of the same submit,
|
|
357
|
+
* so a double-click or a network retry can never book twice.
|
|
358
|
+
*/
|
|
359
|
+
submit(input: BookingInput, options: BookingSubmitOptions): Promise<BookingOutcome>;
|
|
360
|
+
/** A booking's current status by the id `submit` returned. */
|
|
361
|
+
status(id: string): Promise<BookingOutcome>;
|
|
362
|
+
}
|
|
363
|
+
/** A fresh per-submit key. Keep it for retries of the same submit; mint a new one after. */
|
|
364
|
+
declare const newBookingAttemptKey: typeof uuidV4;
|
|
365
|
+
|
|
366
|
+
export { type BookingDropOff as B, type ConversionConfig as C, type SalesBookingsClient as S, type TrackingConfigReference as T, type BookingFormSubmit as a, type BookingInput as b, type BookingMode as c, type BookingOutcome as d, type BookingSimulation as e, type BookingSubmitOptions as f, type ConversionConfigStore as g, getTrackingConfigRuntime as h, resolveTrackingConfigUrl as i, type BookingDayWindows as j, type ConversionIntegrations as k, TrackingConfigRuntime as l, newBookingAttemptKey as n, resolveConversionConfig as r };
|
package/dist/client.d.mts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode, CSSProperties, InputHTMLAttributes, ChangeEvent, FocusEvent } from 'react';
|
|
3
|
-
import { G as GtagEnvironmentMap, M as MetaPixelEnvironmentMap, h as ConsentState, f as ConsentChoiceState, g as ConsentSource, T as TrackingParams, b as TrackingEnvironment,
|
|
4
|
-
import { T as TrackingConfigReference, C as ConversionConfig } from './
|
|
5
|
-
import {
|
|
2
|
+
import { ReactNode, CSSProperties, InputHTMLAttributes, ChangeEvent, FocusEvent, AnchorHTMLAttributes } from 'react';
|
|
3
|
+
import { G as GtagEnvironmentMap, M as MetaPixelEnvironmentMap, h as ConsentState, f as ConsentChoiceState, g as ConsentSource, T as TrackingParams, b as TrackingEnvironment, j as PhoneConfig, P as ParsedPhone, k as PhoneDisplayFormat } from './phone-utils-CM8eUPz8.mjs';
|
|
4
|
+
import { T as TrackingConfigReference, C as ConversionConfig, d as BookingOutcome, b as BookingInput, S as SalesBookingsClient, a as BookingFormSubmit, e as BookingSimulation } from './bookings-CqCuuyP6.mjs';
|
|
5
|
+
import { W as TriggerRegistryConfig, Y as TypedTrackingClient, a as BookingHandoffPrefill, b as BookingIntegration, B as BookableDay } from './ingest-typed-D7UZr0EM.mjs';
|
|
6
6
|
import { CountryCode } from 'libphonenumber-js';
|
|
7
|
+
import './form-submit-BLb28aCk.mjs';
|
|
7
8
|
import 'zod/mini';
|
|
8
9
|
import 'zod/v4/core';
|
|
9
10
|
|
|
@@ -369,4 +370,56 @@ interface PhoneFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "t
|
|
|
369
370
|
*/
|
|
370
371
|
declare const PhoneField: react.ForwardRefExoticComponent<PhoneFieldProps & react.RefAttributes<HTMLInputElement>>;
|
|
371
372
|
|
|
372
|
-
|
|
373
|
+
/**
|
|
374
|
+
* The site's booking provider from the live runtime config, or null before one loads.
|
|
375
|
+
*
|
|
376
|
+
* Pass the same `ARANOVA_TRACKING_CONFIG` reference given to `createTracking`; the config
|
|
377
|
+
* runtime is shared, so this adds no fetch.
|
|
378
|
+
*/
|
|
379
|
+
declare function useBookingIntegration(trackingConfig: TrackingConfigReference | undefined): BookingIntegration | null;
|
|
380
|
+
interface BookingHandoffOptions {
|
|
381
|
+
/** Baked copy of the booking link, used when no runtime config has loaded. */
|
|
382
|
+
fallbackUrl?: string;
|
|
383
|
+
prefill?: BookingHandoffPrefill;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* The prefilled handoff URL, or null when neither the runtime config nor `fallbackUrl` names a
|
|
387
|
+
* usable link. For sites that render the link with their own button component.
|
|
388
|
+
*/
|
|
389
|
+
declare function useBookingHandoffUrl(trackingConfig: TrackingConfigReference | undefined, { fallbackUrl, prefill }?: BookingHandoffOptions): string | null;
|
|
390
|
+
interface BookingHandoffLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href">, BookingHandoffOptions {
|
|
391
|
+
trackingConfig?: TrackingConfigReference;
|
|
392
|
+
children: ReactNode;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* The optional "pick your time" step after a form submit: the shop's booking page, prefilled.
|
|
396
|
+
* Renders nothing when neither the runtime config nor `fallbackUrl` names a usable link.
|
|
397
|
+
*/
|
|
398
|
+
declare function BookingHandoffLink({ trackingConfig, fallbackUrl, prefill, children, target, rel, ...anchorProps }: BookingHandoffLinkProps): ReactNode;
|
|
399
|
+
/**
|
|
400
|
+
* Every bookable day and its start hours, split morning / afternoon, from the live rules.
|
|
401
|
+
* Empty until the config loads or when the shop takes no online bookings. Recomputed each
|
|
402
|
+
* render (cheap), so a tab left open past midnight never offers yesterday's notice window.
|
|
403
|
+
*/
|
|
404
|
+
declare function useBookingSlots(trackingConfig: TrackingConfigReference | undefined): BookableDay[];
|
|
405
|
+
type BookingSubmitStatus = "idle" | "submitting" | BookingOutcome["status"];
|
|
406
|
+
interface BookingSubmitState {
|
|
407
|
+
status: BookingSubmitStatus;
|
|
408
|
+
outcome: BookingOutcome | null;
|
|
409
|
+
/** Send the booking. A retry of an unanswered or refused submit reuses its attempt key. */
|
|
410
|
+
submit(input: BookingInput): Promise<BookingOutcome>;
|
|
411
|
+
/** Back to `idle` with a fresh attempt key, e.g. "book another car". */
|
|
412
|
+
reset(): void;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Submit state for a booking form over `salesClient.bookings`. Owns the parts that are easy to
|
|
416
|
+
* get wrong: one attempt key per submit (a double-click never books twice), the visit's
|
|
417
|
+
* `form_submit`, the stale-config refetch, and the fallback link on failure.
|
|
418
|
+
*/
|
|
419
|
+
declare function useBookingSubmit(bookings: SalesBookingsClient, options?: {
|
|
420
|
+
formSubmit?: BookingFormSubmit;
|
|
421
|
+
/** Development only: a simulated create's outcome. The production API ignores it. */
|
|
422
|
+
simulate?: BookingSimulation;
|
|
423
|
+
}): BookingSubmitState;
|
|
424
|
+
|
|
425
|
+
export { AdPlatformTracking, type AdPlatformTrackingProps, BookingHandoffLink, type BookingHandoffLinkProps, type BookingHandoffOptions, type BookingSubmitState, type BookingSubmitStatus, ConsentBanner, type ConsentBannerProps, type CreateTrackingOptions, type CreateTrackingResult, GoogleAdsTracking, type GoogleAdsTrackingProps, PhoneField, type PhoneFieldApi, type PhoneFieldProps, type PhoneInputProps, type TrackingProviderProps, type UseConsentResult, type UseCookiePreferencesOptions, type UseCookiePreferencesResult, type UsePhoneFieldOptions, createTracking, useBookingHandoffUrl, useBookingIntegration, useBookingSlots, useBookingSubmit, useConsent, useConsentState, useCookiePreferences, useGclid, usePhoneConfig, usePhoneField, useTrackingParams };
|
package/dist/client.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode, CSSProperties, InputHTMLAttributes, ChangeEvent, FocusEvent } from 'react';
|
|
3
|
-
import { G as GtagEnvironmentMap, M as MetaPixelEnvironmentMap, h as ConsentState, f as ConsentChoiceState, g as ConsentSource, T as TrackingParams, b as TrackingEnvironment,
|
|
4
|
-
import { T as TrackingConfigReference, C as ConversionConfig } from './
|
|
5
|
-
import {
|
|
2
|
+
import { ReactNode, CSSProperties, InputHTMLAttributes, ChangeEvent, FocusEvent, AnchorHTMLAttributes } from 'react';
|
|
3
|
+
import { G as GtagEnvironmentMap, M as MetaPixelEnvironmentMap, h as ConsentState, f as ConsentChoiceState, g as ConsentSource, T as TrackingParams, b as TrackingEnvironment, j as PhoneConfig, P as ParsedPhone, k as PhoneDisplayFormat } from './phone-utils-l8ey3Zdm.js';
|
|
4
|
+
import { T as TrackingConfigReference, C as ConversionConfig, d as BookingOutcome, b as BookingInput, S as SalesBookingsClient, a as BookingFormSubmit, e as BookingSimulation } from './bookings-DIu1ho6v.js';
|
|
5
|
+
import { W as TriggerRegistryConfig, Y as TypedTrackingClient, a as BookingHandoffPrefill, b as BookingIntegration, B as BookableDay } from './ingest-typed-D2S3Kb8m.js';
|
|
6
6
|
import { CountryCode } from 'libphonenumber-js';
|
|
7
|
+
import './form-submit-BLb28aCk.js';
|
|
7
8
|
import 'zod/mini';
|
|
8
9
|
import 'zod/v4/core';
|
|
9
10
|
|
|
@@ -369,4 +370,56 @@ interface PhoneFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "t
|
|
|
369
370
|
*/
|
|
370
371
|
declare const PhoneField: react.ForwardRefExoticComponent<PhoneFieldProps & react.RefAttributes<HTMLInputElement>>;
|
|
371
372
|
|
|
372
|
-
|
|
373
|
+
/**
|
|
374
|
+
* The site's booking provider from the live runtime config, or null before one loads.
|
|
375
|
+
*
|
|
376
|
+
* Pass the same `ARANOVA_TRACKING_CONFIG` reference given to `createTracking`; the config
|
|
377
|
+
* runtime is shared, so this adds no fetch.
|
|
378
|
+
*/
|
|
379
|
+
declare function useBookingIntegration(trackingConfig: TrackingConfigReference | undefined): BookingIntegration | null;
|
|
380
|
+
interface BookingHandoffOptions {
|
|
381
|
+
/** Baked copy of the booking link, used when no runtime config has loaded. */
|
|
382
|
+
fallbackUrl?: string;
|
|
383
|
+
prefill?: BookingHandoffPrefill;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* The prefilled handoff URL, or null when neither the runtime config nor `fallbackUrl` names a
|
|
387
|
+
* usable link. For sites that render the link with their own button component.
|
|
388
|
+
*/
|
|
389
|
+
declare function useBookingHandoffUrl(trackingConfig: TrackingConfigReference | undefined, { fallbackUrl, prefill }?: BookingHandoffOptions): string | null;
|
|
390
|
+
interface BookingHandoffLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href">, BookingHandoffOptions {
|
|
391
|
+
trackingConfig?: TrackingConfigReference;
|
|
392
|
+
children: ReactNode;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* The optional "pick your time" step after a form submit: the shop's booking page, prefilled.
|
|
396
|
+
* Renders nothing when neither the runtime config nor `fallbackUrl` names a usable link.
|
|
397
|
+
*/
|
|
398
|
+
declare function BookingHandoffLink({ trackingConfig, fallbackUrl, prefill, children, target, rel, ...anchorProps }: BookingHandoffLinkProps): ReactNode;
|
|
399
|
+
/**
|
|
400
|
+
* Every bookable day and its start hours, split morning / afternoon, from the live rules.
|
|
401
|
+
* Empty until the config loads or when the shop takes no online bookings. Recomputed each
|
|
402
|
+
* render (cheap), so a tab left open past midnight never offers yesterday's notice window.
|
|
403
|
+
*/
|
|
404
|
+
declare function useBookingSlots(trackingConfig: TrackingConfigReference | undefined): BookableDay[];
|
|
405
|
+
type BookingSubmitStatus = "idle" | "submitting" | BookingOutcome["status"];
|
|
406
|
+
interface BookingSubmitState {
|
|
407
|
+
status: BookingSubmitStatus;
|
|
408
|
+
outcome: BookingOutcome | null;
|
|
409
|
+
/** Send the booking. A retry of an unanswered or refused submit reuses its attempt key. */
|
|
410
|
+
submit(input: BookingInput): Promise<BookingOutcome>;
|
|
411
|
+
/** Back to `idle` with a fresh attempt key, e.g. "book another car". */
|
|
412
|
+
reset(): void;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Submit state for a booking form over `salesClient.bookings`. Owns the parts that are easy to
|
|
416
|
+
* get wrong: one attempt key per submit (a double-click never books twice), the visit's
|
|
417
|
+
* `form_submit`, the stale-config refetch, and the fallback link on failure.
|
|
418
|
+
*/
|
|
419
|
+
declare function useBookingSubmit(bookings: SalesBookingsClient, options?: {
|
|
420
|
+
formSubmit?: BookingFormSubmit;
|
|
421
|
+
/** Development only: a simulated create's outcome. The production API ignores it. */
|
|
422
|
+
simulate?: BookingSimulation;
|
|
423
|
+
}): BookingSubmitState;
|
|
424
|
+
|
|
425
|
+
export { AdPlatformTracking, type AdPlatformTrackingProps, BookingHandoffLink, type BookingHandoffLinkProps, type BookingHandoffOptions, type BookingSubmitState, type BookingSubmitStatus, ConsentBanner, type ConsentBannerProps, type CreateTrackingOptions, type CreateTrackingResult, GoogleAdsTracking, type GoogleAdsTrackingProps, PhoneField, type PhoneFieldApi, type PhoneFieldProps, type PhoneInputProps, type TrackingProviderProps, type UseConsentResult, type UseCookiePreferencesOptions, type UseCookiePreferencesResult, type UsePhoneFieldOptions, createTracking, useBookingHandoffUrl, useBookingIntegration, useBookingSlots, useBookingSubmit, useConsent, useConsentState, useCookiePreferences, useGclid, usePhoneConfig, usePhoneField, useTrackingParams };
|