@aranova/tracking-next 0.20.2 → 0.21.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/dist/index.mjs CHANGED
@@ -946,6 +946,21 @@ function automaticThresholdMet(goal, eventType, metadata) {
946
946
  }
947
947
  }
948
948
 
949
+ // ../tracking-core/src/resources/intl/date.ts
950
+ function formatDateInTz(iso, timeZone, opts, locale) {
951
+ const date = new Date(iso);
952
+ if (Number.isNaN(date.getTime())) return iso;
953
+ return new Intl.DateTimeFormat(locale, {
954
+ year: "numeric",
955
+ month: "short",
956
+ day: "2-digit",
957
+ hour: "2-digit",
958
+ minute: "2-digit",
959
+ ...opts,
960
+ timeZone
961
+ }).format(date);
962
+ }
963
+
949
964
  // ../tracking-core/src/resources/sales/money.ts
950
965
  var MINOR_UNIT_EXPONENT = {
951
966
  USD: 2,
@@ -965,19 +980,6 @@ function formatMoney(cents, currency, locale) {
965
980
  fromMinor(cents, currency)
966
981
  );
967
982
  }
968
- function formatDateInTz(iso, timeZone, opts, locale) {
969
- const date = new Date(iso);
970
- if (Number.isNaN(date.getTime())) return iso;
971
- return new Intl.DateTimeFormat(locale, {
972
- year: "numeric",
973
- month: "short",
974
- day: "2-digit",
975
- hour: "2-digit",
976
- minute: "2-digit",
977
- ...opts,
978
- timeZone
979
- }).format(date);
980
- }
981
983
 
982
984
  // ../tracking-core/src/resources/tracking-config-runtime.ts
983
985
  var CACHE_PREFIX2 = "_aranova_cfg_runtime_";
@@ -2680,7 +2682,7 @@ function attachPhoneClickCapture(client, config) {
2680
2682
  };
2681
2683
  }
2682
2684
 
2683
- // ../tracking-core/src/resources/sales/errors.ts
2685
+ // ../tracking-core/src/resources/http/errors.ts
2684
2686
  var AranovaApiError = class extends Error {
2685
2687
  constructor(message, options) {
2686
2688
  super(message);
@@ -2691,7 +2693,7 @@ var AranovaApiError = class extends Error {
2691
2693
  }
2692
2694
  };
2693
2695
 
2694
- // ../tracking-core/src/resources/sales/transport.ts
2696
+ // ../tracking-core/src/resources/http/request.ts
2695
2697
  function identityHeaders(config) {
2696
2698
  const headers = { [API_KEY_HEADER]: config.apiKey };
2697
2699
  if (config.sdkVersion) headers[SDK_VERSION_HEADER] = config.sdkVersion;
@@ -2703,8 +2705,8 @@ function identityHeaders(config) {
2703
2705
  function joinUrl(endpoint, path) {
2704
2706
  return `${endpoint.replace(/\/$/, "")}${path}`;
2705
2707
  }
2706
- async function salesRequest(config, method, path, body) {
2707
- const headers = identityHeaders(config);
2708
+ async function apiRequest(config, method, path, body, extraHeaders) {
2709
+ const headers = { ...identityHeaders(config), ...extraHeaders };
2708
2710
  if (body !== void 0) headers["Content-Type"] = "application/json";
2709
2711
  const response = await fetch(joinUrl(config.endpoint, path), {
2710
2712
  method,
@@ -2733,6 +2735,9 @@ async function salesRequest(config, method, path, body) {
2733
2735
  return await response.json();
2734
2736
  }
2735
2737
 
2738
+ // ../tracking-core/src/resources/sales/transport.ts
2739
+ var salesRequest = apiRequest;
2740
+
2736
2741
  // ../tracking-core/src/resources/sales/client.ts
2737
2742
  function fireRecordedConversions(firing, input, recorded, sale, currency) {
2738
2743
  if (!firing) return;
@@ -3378,7 +3383,7 @@ function GoogleAdsTracking(props) {
3378
3383
  import { usePathname, useSearchParams } from "next/navigation";
3379
3384
 
3380
3385
  // package.json
3381
- var version = "0.20.2";
3386
+ var version = "0.21.0";
3382
3387
 
3383
3388
  // src/factory.tsx
3384
3389
  import {
@@ -3674,42 +3679,488 @@ function createTracking(options) {
3674
3679
  }
3675
3680
  return { TrackingProvider, useTracking };
3676
3681
  }
3682
+
3683
+ // ../tracking-core/src/resources/calendar/transport.ts
3684
+ async function calendarRequest(config, method, path, body) {
3685
+ return apiRequest(config, method, `/calendar${path}`, body);
3686
+ }
3687
+
3688
+ // ../tracking-core/src/resources/calendar/time.ts
3689
+ function resolveBrowserTimeZone() {
3690
+ try {
3691
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
3692
+ } catch {
3693
+ return "UTC";
3694
+ }
3695
+ }
3696
+ function partsInTz(date, timeZone) {
3697
+ const formatter = new Intl.DateTimeFormat("en-US", {
3698
+ timeZone,
3699
+ hour12: false,
3700
+ year: "numeric",
3701
+ month: "2-digit",
3702
+ day: "2-digit",
3703
+ hour: "2-digit",
3704
+ minute: "2-digit",
3705
+ second: "2-digit"
3706
+ });
3707
+ const out = {};
3708
+ for (const part of formatter.formatToParts(date)) {
3709
+ if (part.type !== "literal") out[part.type] = Number(part.value);
3710
+ }
3711
+ if (out.hour === 24) out.hour = 0;
3712
+ return out;
3713
+ }
3714
+ function offsetMinutesAt(date, timeZone) {
3715
+ const p = partsInTz(date, timeZone);
3716
+ const asUtc = Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute, p.second);
3717
+ return (asUtc - Math.floor(date.getTime() / 1e3) * 1e3) / 6e4;
3718
+ }
3719
+ function instantFromWallClock(timeZone, year, month, day, hour = 0, minute = 0) {
3720
+ const naive = Date.UTC(year, month - 1, day, hour, minute);
3721
+ const firstGuess = new Date(naive - offsetMinutesAt(new Date(naive), timeZone) * 6e4);
3722
+ const corrected = naive - offsetMinutesAt(firstGuess, timeZone) * 6e4;
3723
+ return new Date(corrected);
3724
+ }
3725
+ function localDateKey(iso, timeZone) {
3726
+ const p = partsInTz(new Date(iso), timeZone);
3727
+ const pad = (n) => String(n).padStart(2, "0");
3728
+ return `${p.year}-${pad(p.month)}-${pad(p.day)}`;
3729
+ }
3730
+ function startOfDayInTz(dateKey, timeZone) {
3731
+ const [year, month, day] = dateKey.split("-").map(Number);
3732
+ return instantFromWallClock(timeZone, year, month, day, 0, 0);
3733
+ }
3734
+ function endOfDayInTz(dateKey, timeZone) {
3735
+ const start = startOfDayInTz(dateKey, timeZone);
3736
+ const p = partsInTz(start, timeZone);
3737
+ return instantFromWallClock(timeZone, p.year, p.month, p.day + 1, 0, 0);
3738
+ }
3739
+ function durationMinutes(startIso, endIso) {
3740
+ return Math.round((new Date(endIso).getTime() - new Date(startIso).getTime()) / 6e4);
3741
+ }
3742
+ function formatSlotRange(startIso, endIso, timeZone, locale) {
3743
+ const opts = { hour: "2-digit", minute: "2-digit", timeZone };
3744
+ const fmt = new Intl.DateTimeFormat(locale, opts);
3745
+ return `${fmt.format(new Date(startIso))} \u2013 ${fmt.format(new Date(endIso))}`;
3746
+ }
3747
+
3748
+ // ../tracking-core/src/resources/calendar/read-client.ts
3749
+ function withBusiness(config, path) {
3750
+ if (!config.businessId) return path;
3751
+ const separator = path.includes("?") ? "&" : "?";
3752
+ return `${path}${separator}business_id=${encodeURIComponent(config.businessId)}`;
3753
+ }
3754
+ function createCalendarReadClient(config) {
3755
+ return {
3756
+ async listCalendars() {
3757
+ return calendarRequest(config, "GET", withBusiness(config, "/calendars"));
3758
+ },
3759
+ async busy(calendarKey, query) {
3760
+ const params = new URLSearchParams({
3761
+ since: query.since,
3762
+ until: query.until
3763
+ });
3764
+ const timeZone = query.timeZone ?? config.defaultTimeZone ?? resolveBrowserTimeZone();
3765
+ params.set("time_zone", timeZone);
3766
+ return calendarRequest(
3767
+ config,
3768
+ "GET",
3769
+ withBusiness(config, `/${encodeURIComponent(calendarKey)}/busy?${params.toString()}`)
3770
+ );
3771
+ }
3772
+ };
3773
+ }
3774
+ function isCalendarError(error, code) {
3775
+ if (!(error instanceof AranovaApiError)) return false;
3776
+ return code === void 0 || error.code === code;
3777
+ }
3778
+
3779
+ // ../tracking-core/src/resources/calendar/schema.ts
3780
+ import { z as z13 } from "zod";
3781
+ var CALENDAR_METADATA_SCHEMA_VERSION = 1;
3782
+ var MAX_ATTENDEES = 20;
3783
+ var MAX_REMINDER_OVERRIDES = 5;
3784
+ var MAX_REMINDER_MINUTES = 40320;
3785
+ var MAX_BOOKING_MINUTES = 1440;
3786
+ var BOOKING_SOURCES = ["website", "phone", "walk_in", "import", "operator"];
3787
+ var BOOKING_STATUSES = ["pending", "confirmed", "cancelled", "no_show"];
3788
+ var GOOGLE_SYNC_STATES = ["queued", "synced", "uncertain", "failed", "drifted"];
3789
+ var CALENDAR_SYNC_STATUSES = [
3790
+ "pending_invite",
3791
+ "awaiting_calendar_selection",
3792
+ "connected",
3793
+ "needs_reconnect",
3794
+ "revoked",
3795
+ "disabled"
3796
+ ];
3797
+ var CALENDAR_ERROR_CODES = [
3798
+ "slot_unavailable",
3799
+ "calendar_not_connected",
3800
+ "calendar_disconnected",
3801
+ "booking_not_found",
3802
+ "outside_booking_window",
3803
+ "rate_limited",
3804
+ "google_error"
3805
+ ];
3806
+ var isoInstant = z13.string().datetime({ offset: true });
3807
+ function isKnownTimeZone(timeZone) {
3808
+ try {
3809
+ new Intl.DateTimeFormat(void 0, { timeZone });
3810
+ return true;
3811
+ } catch {
3812
+ return false;
3813
+ }
3814
+ }
3815
+ var ianaTz = z13.string().min(1).max(64).regex(/^[A-Za-z0-9_+\-/]+$/).refine(isKnownTimeZone, { message: "unknown IANA time zone" });
3816
+ var bookingAttendeeSchema = z13.object({
3817
+ email: z13.string().max(320).email(),
3818
+ display_name: z13.string().max(200).nullable().optional(),
3819
+ optional: z13.boolean().default(false)
3820
+ }).strict();
3821
+ var bookingReminderSchema = z13.object({
3822
+ method: z13.enum(["email", "popup"]),
3823
+ minutes_before: z13.number().int().min(0).max(MAX_REMINDER_MINUTES)
3824
+ }).strict();
3825
+ var bookingRemindersSchema = z13.object({
3826
+ use_calendar_defaults: z13.boolean().default(true),
3827
+ overrides: z13.array(bookingReminderSchema).max(MAX_REMINDER_OVERRIDES).default([])
3828
+ }).strict();
3829
+ var bookingLocationSchema = z13.union([
3830
+ z13.object({ kind: z13.literal("google_meet") }).strict(),
3831
+ z13.object({ kind: z13.literal("physical"), address: z13.string().max(1024) }).strict(),
3832
+ z13.object({ kind: z13.literal("phone"), phone: z13.string().max(64) }).strict(),
3833
+ z13.object({ kind: z13.literal("custom"), text: z13.string().max(1024) }).strict()
3834
+ ]);
3835
+ var bookingLocationNullableSchema = z13.union([...bookingLocationSchema.options, z13.null()]);
3836
+ var bookingCustomerSchema = z13.object({
3837
+ // Required: Google needs an address to send the invite that confirms the
3838
+ // booking, and that invite is the whole confirmation mechanism.
3839
+ email: z13.string().max(320).email(),
3840
+ name: z13.string().max(200).nullable().optional(),
3841
+ phone: z13.string().max(64).nullable().optional(),
3842
+ sms_consent: z13.boolean().default(false)
3843
+ }).strict();
3844
+ var jsonScalar = z13.union([z13.string(), z13.number(), z13.boolean(), z13.null()]);
3845
+ var bookingMetadataSchema = z13.object({
3846
+ schema_version: z13.number().int().default(CALENDAR_METADATA_SCHEMA_VERSION),
3847
+ source: z13.enum(BOOKING_SOURCES).nullable().optional(),
3848
+ service: z13.string().max(64).nullable().optional(),
3849
+ visitor_id: z13.string().max(64).nullable().optional(),
3850
+ session_id: z13.string().max(64).nullable().optional(),
3851
+ gclid: z13.string().max(256).nullable().optional(),
3852
+ fbclid: z13.string().max(256).nullable().optional(),
3853
+ utm_source: z13.string().max(128).nullable().optional(),
3854
+ utm_medium: z13.string().max(128).nullable().optional(),
3855
+ utm_campaign: z13.string().max(128).nullable().optional(),
3856
+ utm_term: z13.string().max(128).nullable().optional(),
3857
+ utm_content: z13.string().max(128).nullable().optional(),
3858
+ landing_page: z13.string().max(2048).nullable().optional(),
3859
+ referrer: z13.string().max(2048).nullable().optional(),
3860
+ notes: z13.string().max(2e3).nullable().optional(),
3861
+ custom: z13.record(jsonScalar).nullable().optional()
3862
+ }).strict();
3863
+ function refineEndXor(value, ctx) {
3864
+ const hasEnd = value.end_at !== void 0 && value.end_at !== null;
3865
+ const hasDuration = value.duration_minutes !== void 0 && value.duration_minutes !== null;
3866
+ if (hasEnd === hasDuration) {
3867
+ ctx.addIssue({
3868
+ code: z13.ZodIssueCode.custom,
3869
+ message: "provide exactly one of end_at or duration_minutes",
3870
+ path: ["end_at"]
3871
+ });
3872
+ return;
3873
+ }
3874
+ if (hasEnd && new Date(value.end_at) <= new Date(value.start_at)) {
3875
+ ctx.addIssue({
3876
+ code: z13.ZodIssueCode.custom,
3877
+ message: "end_at must be after start_at",
3878
+ path: ["end_at"]
3879
+ });
3880
+ }
3881
+ }
3882
+ var bookingCreateSchema = z13.object({
3883
+ calendar_key: z13.string().min(1).max(64),
3884
+ // Free-form: appointment types are the client site's own vocabulary, not
3885
+ // configuration the dashboard holds.
3886
+ appointment_type: z13.string().max(64).nullable().optional(),
3887
+ start_at: isoInstant,
3888
+ end_at: isoInstant.nullable().optional(),
3889
+ duration_minutes: z13.number().int().positive().max(MAX_BOOKING_MINUTES).nullable().optional(),
3890
+ /** The booker's zone — distinct from the business's and the calendar's. */
3891
+ time_zone: ianaTz,
3892
+ title: z13.string().max(300).nullable().optional(),
3893
+ description: z13.string().max(8e3).nullable().optional(),
3894
+ location: bookingLocationNullableSchema.optional(),
3895
+ customer: bookingCustomerSchema,
3896
+ attendees: z13.array(bookingAttendeeSchema).max(MAX_ATTENDEES).default([]),
3897
+ send_invites: z13.boolean().default(true),
3898
+ reminders: bookingRemindersSchema.nullable().optional(),
3899
+ metadata: bookingMetadataSchema.nullable().optional(),
3900
+ idempotency_key: z13.string().max(128).nullable().optional(),
3901
+ buffer_before_minutes: z13.number().int().min(0).max(MAX_BOOKING_MINUTES).default(0),
3902
+ buffer_after_minutes: z13.number().int().min(0).max(MAX_BOOKING_MINUTES).default(0)
3903
+ }).strict().superRefine(refineEndXor);
3904
+ var bookingRescheduleSchema = z13.object({
3905
+ start_at: isoInstant,
3906
+ end_at: isoInstant.nullable().optional(),
3907
+ duration_minutes: z13.number().int().positive().max(MAX_BOOKING_MINUTES).nullable().optional(),
3908
+ time_zone: ianaTz,
3909
+ notify_attendees: z13.boolean().default(true),
3910
+ reason: z13.string().max(500).nullable().optional()
3911
+ }).strict().superRefine(refineEndXor);
3912
+ var bookingCancelSchema = z13.object({
3913
+ reason: z13.string().max(500).nullable().optional(),
3914
+ notify_attendees: z13.boolean().default(true)
3915
+ }).strict();
3916
+
3917
+ // ../tracking-core/src/resources/calendar/slots.ts
3918
+ function parseHhMm(value) {
3919
+ const [hour, minute] = value.split(":").map(Number);
3920
+ return [hour || 0, minute || 0];
3921
+ }
3922
+ function dateKeyOf(date, timeZone) {
3923
+ const fmt = new Intl.DateTimeFormat("en-CA", {
3924
+ timeZone,
3925
+ year: "numeric",
3926
+ month: "2-digit",
3927
+ day: "2-digit"
3928
+ });
3929
+ return fmt.format(date);
3930
+ }
3931
+ function weekdayFromDateKey(dateKey) {
3932
+ const [year, month, day] = dateKey.split("-").map(Number);
3933
+ return new Date(Date.UTC(year, month - 1, day, 12)).getUTCDay();
3934
+ }
3935
+ function startOfLocalDay(at, timeZone) {
3936
+ const [year, month, day] = dateKeyOf(at, timeZone).split("-").map(Number);
3937
+ return instantFromWallClock(timeZone, year, month, day, 12, 0);
3938
+ }
3939
+ function overlaps(aStart, aEnd, bStart, bEnd) {
3940
+ return aStart < bEnd && aEnd > bStart;
3941
+ }
3942
+ function computeSlots(options) {
3943
+ const {
3944
+ busy,
3945
+ openingHours,
3946
+ timeZone,
3947
+ durationMinutes: durationMinutes2,
3948
+ granularityMinutes = 15,
3949
+ minNoticeMinutes = 0,
3950
+ bufferBeforeMinutes = 0,
3951
+ bufferAfterMinutes = 0,
3952
+ windowDays = 30,
3953
+ now = /* @__PURE__ */ new Date()
3954
+ } = options;
3955
+ const earliest = now.getTime() + minNoticeMinutes * 6e4;
3956
+ const busyRanges = busy.map((interval) => [
3957
+ new Date(interval.start_at).getTime(),
3958
+ new Date(interval.end_at).getTime()
3959
+ ]);
3960
+ const slots = [];
3961
+ let cursor = startOfLocalDay(now, timeZone);
3962
+ for (let dayOffset = 0; dayOffset < windowDays; dayOffset += 1) {
3963
+ const dateKey = dateKeyOf(cursor, timeZone);
3964
+ const [year, month, day] = dateKey.split("-").map(Number);
3965
+ cursor = instantFromWallClock(timeZone, year, month, day + 1, 12, 0);
3966
+ if (openingHours.closedDates?.includes(dateKey)) continue;
3967
+ const intervals = openingHours.overrides?.[dateKey] ?? openingHours.weekly[weekdayFromDateKey(dateKey)] ?? [];
3968
+ for (const interval of intervals) {
3969
+ const [openHour, openMinute] = parseHhMm(interval.start);
3970
+ const [closeHour, closeMinute] = parseHhMm(interval.end);
3971
+ const opensAt = instantFromWallClock(timeZone, year, month, day, openHour, openMinute);
3972
+ const closesAt = instantFromWallClock(timeZone, year, month, day, closeHour, closeMinute);
3973
+ for (let start = opensAt.getTime(); start + durationMinutes2 * 6e4 <= closesAt.getTime(); start += granularityMinutes * 6e4) {
3974
+ const end = start + durationMinutes2 * 6e4;
3975
+ if (start < earliest) continue;
3976
+ const blockedStart = start - bufferBeforeMinutes * 6e4;
3977
+ const blockedEnd = end + bufferAfterMinutes * 6e4;
3978
+ const taken = busyRanges.some(
3979
+ ([busyStart, busyEnd]) => overlaps(blockedStart, blockedEnd, busyStart, busyEnd)
3980
+ );
3981
+ if (taken) continue;
3982
+ slots.push({
3983
+ start_at: new Date(start).toISOString(),
3984
+ end_at: new Date(end).toISOString()
3985
+ });
3986
+ }
3987
+ }
3988
+ }
3989
+ slots.sort((a, b) => a.start_at.localeCompare(b.start_at));
3990
+ return { slots, nextAvailableAt: slots[0]?.start_at ?? null };
3991
+ }
3992
+ function groupSlotsByDay(slots, timeZone) {
3993
+ const byDay = /* @__PURE__ */ new Map();
3994
+ for (const slot of slots) {
3995
+ const key = dateKeyOf(new Date(slot.start_at), timeZone);
3996
+ const bucket = byDay.get(key);
3997
+ if (bucket) bucket.push(slot);
3998
+ else byDay.set(key, [slot]);
3999
+ }
4000
+ return [...byDay.entries()].map(([date, daySlots]) => ({ date, slots: daySlots })).sort((a, b) => a.date.localeCompare(b.date));
4001
+ }
4002
+
4003
+ // ../tracking-core/src/calendar-react.tsx
4004
+ import { createContext as createContext3, useCallback as useCallback4, useContext as useContext3, useEffect as useEffect5, useMemo as useMemo3, useState as useState4 } from "react";
4005
+ import { jsx as jsx6 } from "react/jsx-runtime";
4006
+ var _calendarClientContext;
4007
+ function calendarClientContext() {
4008
+ return _calendarClientContext ?? (_calendarClientContext = createContext3(null));
4009
+ }
4010
+ function CalendarClientProvider({
4011
+ value,
4012
+ children
4013
+ }) {
4014
+ const Ctx = calendarClientContext();
4015
+ return /* @__PURE__ */ jsx6(Ctx.Provider, { value, children });
4016
+ }
4017
+ function useCalendarClient() {
4018
+ const client = useContext3(calendarClientContext());
4019
+ if (!client) {
4020
+ throw new Error(
4021
+ "useCalendarClient requires <CalendarClientProvider>. Wire it via createTracking({ calendar })."
4022
+ );
4023
+ }
4024
+ return client;
4025
+ }
4026
+ function useCalendarBusy(calendarKey, window2) {
4027
+ const client = useCalendarClient();
4028
+ const [busy, setBusy] = useState4([]);
4029
+ const [timeZone, setTimeZone] = useState4(resolveBrowserTimeZone());
4030
+ const [isLoading, setIsLoading] = useState4(true);
4031
+ const [error, setError] = useState4(null);
4032
+ const [nonce, setNonce] = useState4(0);
4033
+ useEffect5(() => {
4034
+ const controller = new AbortController();
4035
+ setIsLoading(true);
4036
+ client.busy(calendarKey, { since: window2.since, until: window2.until }).then((response) => {
4037
+ if (controller.signal.aborted) return;
4038
+ setBusy(response.busy);
4039
+ setTimeZone(response.time_zone);
4040
+ setError(null);
4041
+ }).catch((cause) => {
4042
+ if (controller.signal.aborted) return;
4043
+ setError(cause instanceof Error ? cause : new Error(String(cause)));
4044
+ }).finally(() => {
4045
+ if (!controller.signal.aborted) setIsLoading(false);
4046
+ });
4047
+ return () => controller.abort();
4048
+ }, [client, calendarKey, window2.since, window2.until, nonce]);
4049
+ const refetch = useCallback4(() => setNonce((value) => value + 1), []);
4050
+ return { busy, timeZone, isLoading, error, refetch };
4051
+ }
4052
+ function useSlots(busy, timeZone, options) {
4053
+ const zone = options.timeZone ?? timeZone;
4054
+ return useMemo3(() => {
4055
+ const { slots, nextAvailableAt } = computeSlots({ ...options, busy, timeZone: zone });
4056
+ return { slots, days: groupSlotsByDay(slots, zone), nextAvailableAt };
4057
+ }, [busy, zone, options]);
4058
+ }
4059
+ function useBookingForm(options = {}) {
4060
+ const endpoint = options.endpoint ?? "/api/aranova/calendar/bookings";
4061
+ const [status, setStatus] = useState4("idle");
4062
+ const [error, setError] = useState4(null);
4063
+ const [booking, setBooking] = useState4(null);
4064
+ const submit = useCallback4(
4065
+ async (input) => {
4066
+ setStatus("submitting");
4067
+ setError(null);
4068
+ try {
4069
+ const response = await fetch(endpoint, {
4070
+ method: "POST",
4071
+ headers: { "Content-Type": "application/json" },
4072
+ body: JSON.stringify(input)
4073
+ });
4074
+ const body = await response.json().catch(() => null);
4075
+ if (!response.ok) {
4076
+ const detail = body && typeof body === "object" && typeof body.detail === "string" ? body.detail : "Booking failed";
4077
+ throw new Error(detail);
4078
+ }
4079
+ setBooking(body);
4080
+ setStatus("confirmed");
4081
+ options.onConfirmed?.(body);
4082
+ } catch (cause) {
4083
+ setError(cause instanceof Error ? cause : new Error(String(cause)));
4084
+ setStatus("failed");
4085
+ }
4086
+ },
4087
+ [endpoint, options]
4088
+ );
4089
+ const reset = useCallback4(() => {
4090
+ setStatus("idle");
4091
+ setError(null);
4092
+ setBooking(null);
4093
+ }, []);
4094
+ return { status, error, booking, submit, reset };
4095
+ }
3677
4096
  export {
3678
4097
  AdPlatformTracking,
3679
4098
  AranovaApiError,
4099
+ BOOKING_SOURCES,
4100
+ BOOKING_STATUSES,
4101
+ CALENDAR_ERROR_CODES,
4102
+ CALENDAR_METADATA_SCHEMA_VERSION,
4103
+ CALENDAR_SYNC_STATUSES,
4104
+ CalendarClientProvider,
3680
4105
  ConsentBanner,
3681
4106
  DEFAULT_DECLINE_TTL_DAYS,
3682
4107
  DEFAULT_PHONE_COUNTRY,
3683
4108
  EVENT_SEMANTICS,
4109
+ GOOGLE_SYNC_STATES,
3684
4110
  GoogleAdsTracking,
4111
+ MAX_ATTENDEES,
4112
+ MAX_BOOKING_MINUTES,
4113
+ MAX_REMINDER_MINUTES,
4114
+ MAX_REMINDER_OVERRIDES,
3685
4115
  NAMED_RANGES,
3686
4116
  PhoneField,
3687
4117
  SUPPORTED_CURRENCIES,
3688
4118
  TRACKING_PARAM_KEYS,
3689
4119
  TRACKING_RANGES,
4120
+ bookingAttendeeSchema,
4121
+ bookingCancelSchema,
4122
+ bookingCreateSchema,
4123
+ bookingCustomerSchema,
4124
+ bookingLocationNullableSchema,
4125
+ bookingLocationSchema,
4126
+ bookingMetadataSchema,
4127
+ bookingReminderSchema,
4128
+ bookingRemindersSchema,
4129
+ bookingRescheduleSchema,
3690
4130
  captureTrackingParamsFromLocation,
3691
4131
  clearStashedUserData,
4132
+ computeSlots,
4133
+ createCalendarReadClient,
3692
4134
  createSalesClient,
3693
4135
  createTracking,
3694
4136
  createTrackingClientContext,
3695
4137
  createTrackingEventCreatePayload,
3696
4138
  createTrackingSessionUpsertPayload,
4139
+ durationMinutes,
4140
+ endOfDayInTz,
3697
4141
  fetchServices,
3698
4142
  formatDateInTz,
3699
4143
  formatMoney,
3700
4144
  formatPhone,
3701
4145
  formatPhoneAsTyped,
4146
+ formatSlotRange,
3702
4147
  fromMinor,
3703
4148
  getConsentChoice,
3704
4149
  getConsentState,
3705
4150
  getEventSemantics,
3706
4151
  getTrackingConfigRuntime,
4152
+ groupSlotsByDay,
4153
+ instantFromWallClock,
4154
+ isCalendarError,
4155
+ localDateKey,
4156
+ offsetMinutesAt,
3707
4157
  onConsentChange,
3708
4158
  optIn,
3709
4159
  optOut,
3710
4160
  parsePhone,
3711
4161
  phoneField,
3712
4162
  resetConsent,
4163
+ resolveBrowserTimeZone,
3713
4164
  resolveConversionConfig,
3714
4165
  saleCreateSchema,
3715
4166
  saleItemSchema,
@@ -3717,15 +4168,20 @@ export {
3717
4168
  saleUpdateSchema,
3718
4169
  salesRequest,
3719
4170
  setConsentState,
4171
+ startOfDayInTz,
3720
4172
  stashUserData,
3721
4173
  toE164,
3722
4174
  toMinor,
4175
+ useBookingForm,
4176
+ useCalendarBusy,
4177
+ useCalendarClient,
3723
4178
  useConsent,
3724
4179
  useConsentState,
3725
4180
  useCookiePreferences,
3726
4181
  useGclid,
3727
4182
  usePhoneConfig,
3728
4183
  usePhoneField,
4184
+ useSlots,
3729
4185
  useTrackingParams
3730
4186
  };
3731
4187
  //# sourceMappingURL=index.mjs.map