arky-sdk 0.7.124 → 0.7.125

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.
@@ -1596,6 +1596,105 @@ function formFieldsFromBlocks(blocks) {
1596
1596
  value: block.value
1597
1597
  }));
1598
1598
  }
1599
+ function getFormBlockType(field) {
1600
+ if (field.key === "email") return "email";
1601
+ if (field.key === "phone") return "phone";
1602
+ if (field.type === "geo_location") return "address";
1603
+ return field.type;
1604
+ }
1605
+ function getFormBlockValue(field) {
1606
+ if (field.type === "boolean") return false;
1607
+ if (field.type === "number") return field.min ?? 0;
1608
+ if (field.type === "geo_location") return {};
1609
+ return "";
1610
+ }
1611
+ function formSchemaToBlock(field) {
1612
+ return {
1613
+ id: field.id,
1614
+ key: field.key,
1615
+ type: getFormBlockType(field),
1616
+ properties: {
1617
+ isRequired: field.required,
1618
+ minValues: field.required ? 1 : 0,
1619
+ min: field.min,
1620
+ max: field.max,
1621
+ options: field.options,
1622
+ pattern: field.key === "email" ? "^.+@.+\\..+$" : field.key === "phone" ? "^.{6,20}$" : void 0
1623
+ },
1624
+ value: getFormBlockValue(field)
1625
+ };
1626
+ }
1627
+ function formatBookingTime(ts, tz) {
1628
+ return new Date(ts * 1e3).toLocaleTimeString([], {
1629
+ hour: "2-digit",
1630
+ minute: "2-digit",
1631
+ timeZone: tz
1632
+ });
1633
+ }
1634
+ function formatBookingSlotTime(from, to, tz) {
1635
+ return `${formatBookingTime(from, tz)} - ${formatBookingTime(to, tz)}`;
1636
+ }
1637
+ function getSlotsForDate(availability, dateStr, providerId) {
1638
+ if (!availability) return [];
1639
+ const slots = [];
1640
+ for (const provider of availability.providers) {
1641
+ if (providerId && provider.provider_id !== providerId) continue;
1642
+ const day = provider.days.find((candidate) => candidate.date === dateStr);
1643
+ if (!day) continue;
1644
+ for (const slot of day.slots) {
1645
+ if (slot.spots > 0) slots.push({ from: slot.from, to: slot.to, providerId: provider.provider_id });
1646
+ }
1647
+ }
1648
+ return slots.sort((a, b) => a.from - b.from);
1649
+ }
1650
+ function hasAvailableSlotsForDate(availability, dateStr, providerId) {
1651
+ if (!availability) return false;
1652
+ return availability.providers.some((provider) => {
1653
+ if (providerId && provider.provider_id !== providerId) return false;
1654
+ const day = provider.days.find((candidate) => candidate.date === dateStr);
1655
+ return !!day?.slots.some((slot) => slot.spots > 0);
1656
+ });
1657
+ }
1658
+ var BOOKING_WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
1659
+ function createBookingInitialState() {
1660
+ return {
1661
+ service: null,
1662
+ availability: null,
1663
+ providers: [],
1664
+ selectedProviderId: null,
1665
+ currentMonth: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), 1),
1666
+ calendar: [],
1667
+ selectedDate: null,
1668
+ startDate: null,
1669
+ endDate: null,
1670
+ slots: [],
1671
+ selectedSlot: null,
1672
+ cart: [],
1673
+ timezone: typeof window !== "undefined" ? Intl.DateTimeFormat().resolvedOptions().timeZone : "UTC",
1674
+ tzGroups: {},
1675
+ loading: false,
1676
+ weekdays: BOOKING_WEEKDAYS,
1677
+ quote: null,
1678
+ fetchingQuote: false,
1679
+ quoteError: null,
1680
+ currency: null,
1681
+ dateTimeConfirmed: false,
1682
+ isMultiDay: false,
1683
+ availablePaymentMethods: [],
1684
+ cartId: null,
1685
+ promoCode: null
1686
+ };
1687
+ }
1688
+ function normalizeTimezoneGroups(groups) {
1689
+ const normalized = {};
1690
+ for (const group of groups) {
1691
+ normalized[group.label] = group.zones.map((zone) => ({
1692
+ zone: zone.value,
1693
+ name: zone.label
1694
+ }));
1695
+ }
1696
+ return normalized;
1697
+ }
1599
1698
  function createArkyStore(config) {
1600
1699
  const client = createStorefront(config);
1601
1700
  const session = nanostores.atom(client.session);
@@ -1659,9 +1758,19 @@ function createArkyStore(config) {
1659
1758
  loading_availability: false,
1660
1759
  error: null
1661
1760
  });
1761
+ const booking_state = nanostores.map(createBookingInitialState());
1762
+ const booking_form_node = nanostores.atom(null);
1763
+ const booking_form_blocks = nanostores.computed(booking_form_node, (node) => node?.blocks || []);
1662
1764
  client.onAuthStateChanged((value) => session.set(value));
1663
1765
  snapshot.subscribe((value) => config.onCartChange?.(value));
1664
1766
  last_order.subscribe((value) => writeStoredOrder(config.lastOrderStorageKey, value));
1767
+ currency.subscribe((value) => booking_state.setKey("currency", value));
1768
+ session.subscribe((value) => {
1769
+ const methods = value?.market?.payment_methods || [];
1770
+ if (methods.length && booking_state.get().availablePaymentMethods.length === 0) {
1771
+ booking_state.setKey("availablePaymentMethods", methods);
1772
+ }
1773
+ });
1665
1774
  function currentMarketKey() {
1666
1775
  return market_key.get() || client.getMarket() || market.get()?.key || "";
1667
1776
  }
@@ -1923,6 +2032,485 @@ function createArkyStore(config) {
1923
2032
  cart_status.setKey("processing_checkout", false);
1924
2033
  }
1925
2034
  }
2035
+ function bookingCalendar() {
2036
+ const state = booking_state.get();
2037
+ const { currentMonth, selectedDate, startDate, endDate, availability, selectedProviderId } = state;
2038
+ const year = currentMonth.getFullYear();
2039
+ const monthIndex = currentMonth.getMonth();
2040
+ const first = new Date(year, monthIndex, 1);
2041
+ const last = new Date(year, monthIndex + 1, 0);
2042
+ const today = /* @__PURE__ */ new Date();
2043
+ today.setHours(0, 0, 0, 0);
2044
+ const cells = [];
2045
+ const pad = (first.getDay() + 6) % 7;
2046
+ for (let i = 0; i < pad; i++) {
2047
+ cells.push({
2048
+ date: /* @__PURE__ */ new Date(0),
2049
+ iso: "",
2050
+ available: false,
2051
+ isSelected: false,
2052
+ isInRange: false,
2053
+ isToday: false,
2054
+ blank: true
2055
+ });
2056
+ }
2057
+ for (let day = 1; day <= last.getDate(); day++) {
2058
+ const date = new Date(year, monthIndex, day);
2059
+ const iso = `${year}-${String(monthIndex + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
2060
+ const isSelected = iso === selectedDate || iso === startDate || iso === endDate;
2061
+ let isInRange = false;
2062
+ if (startDate && endDate) {
2063
+ const time = date.getTime();
2064
+ isInRange = time > new Date(startDate).getTime() && time < new Date(endDate).getTime();
2065
+ }
2066
+ cells.push({
2067
+ date,
2068
+ iso,
2069
+ available: hasAvailableSlotsForDate(availability, iso, selectedProviderId),
2070
+ isSelected,
2071
+ isInRange,
2072
+ isToday: date.getTime() === today.getTime(),
2073
+ blank: false
2074
+ });
2075
+ }
2076
+ const suffix = (7 - cells.length % 7) % 7;
2077
+ for (let i = 0; i < suffix; i++) {
2078
+ cells.push({
2079
+ date: /* @__PURE__ */ new Date(0),
2080
+ iso: "",
2081
+ available: false,
2082
+ isSelected: false,
2083
+ isInRange: false,
2084
+ isToday: false,
2085
+ blank: true
2086
+ });
2087
+ }
2088
+ return cells;
2089
+ }
2090
+ function computeBookingSlots(dateStr) {
2091
+ const state = booking_state.get();
2092
+ const { availability, selectedProviderId, timezone, service } = state;
2093
+ return getSlotsForDate(availability, dateStr, selectedProviderId).map((slot, index) => ({
2094
+ id: `${service?.id || "service"}-${slot.from}-${index}`,
2095
+ serviceId: service?.id || "",
2096
+ providerId: slot.providerId,
2097
+ from: slot.from,
2098
+ to: slot.to,
2099
+ timeText: formatBookingSlotTime(slot.from, slot.to, timezone),
2100
+ dateText: new Date(slot.from * 1e3).toLocaleDateString([], {
2101
+ weekday: "short",
2102
+ month: "short",
2103
+ day: "numeric",
2104
+ timeZone: timezone
2105
+ })
2106
+ }));
2107
+ }
2108
+ function toServiceCartItem(slot) {
2109
+ return {
2110
+ id: slot.id,
2111
+ service_id: slot.serviceId,
2112
+ provider_id: slot.providerId,
2113
+ from: slot.from,
2114
+ to: slot.to,
2115
+ forms: [],
2116
+ service_name: slot.serviceName,
2117
+ date_text: slot.dateText,
2118
+ time_text: slot.timeText,
2119
+ is_multi_day: slot.isMultiDay
2120
+ };
2121
+ }
2122
+ function fromServiceCartItem(item) {
2123
+ return {
2124
+ id: item.id,
2125
+ serviceId: item.service_id,
2126
+ providerId: item.provider_id,
2127
+ from: item.from,
2128
+ to: item.to,
2129
+ serviceName: item.service_name || "",
2130
+ date: item.date_text || "",
2131
+ dateText: item.date_text || "",
2132
+ timeText: item.time_text || formatBookingSlotTime(item.from, item.to, booking_state.get().timezone),
2133
+ isMultiDay: item.is_multi_day
2134
+ };
2135
+ }
2136
+ function setBookingCartFromServiceItems(items) {
2137
+ const next = items.map(fromServiceCartItem);
2138
+ const current = booking_state.get().cart;
2139
+ if (JSON.stringify(current) !== JSON.stringify(next)) {
2140
+ booking_state.setKey("cart", next);
2141
+ }
2142
+ }
2143
+ async function syncBookingCart(slots) {
2144
+ try {
2145
+ return await syncCart({
2146
+ product_items: product_items.get(),
2147
+ service_items: slots.map(toServiceCartItem)
2148
+ });
2149
+ } catch (error) {
2150
+ booking_state.setKey("quoteError", readErrorMessage(error, "Failed to sync booking cart."));
2151
+ throw error;
2152
+ }
2153
+ }
2154
+ function bookingCurrentStepName() {
2155
+ const state = booking_state.get();
2156
+ if (!state.service) return "";
2157
+ if (!state.selectedSlot || !state.dateTimeConfirmed) return "datetime";
2158
+ return "review";
2159
+ }
2160
+ const booking_current_step_name = nanostores.computed(booking_state, bookingCurrentStepName);
2161
+ const booking_can_proceed = nanostores.computed(booking_state, (state) => {
2162
+ const step = bookingCurrentStepName();
2163
+ if (step === "datetime") {
2164
+ return state.isMultiDay ? !!(state.startDate && state.endDate && state.selectedSlot) : !!(state.selectedDate && state.selectedSlot);
2165
+ }
2166
+ if (step === "review") return true;
2167
+ return false;
2168
+ });
2169
+ const booking_month_year = nanostores.computed(
2170
+ booking_state,
2171
+ (state) => state.currentMonth.toLocaleString(void 0, { month: "long", year: "numeric" })
2172
+ );
2173
+ const booking_chain_start = nanostores.computed(booking_state, (state) => {
2174
+ if (!state.cart.length) return null;
2175
+ return Math.max(...state.cart.map((slot) => slot.to));
2176
+ });
2177
+ const booking_total_steps = nanostores.computed(booking_state, (state) => state.service ? 2 : 0);
2178
+ const booking_steps = nanostores.computed(booking_state, () => ({
2179
+ 1: { name: "datetime" },
2180
+ 2: { name: "review" }
2181
+ }));
2182
+ const booking_current_step = nanostores.computed([booking_current_step_name, booking_steps], (name, steps) => {
2183
+ for (const [idx, step] of Object.entries(steps)) {
2184
+ if (step.name === name) return Number(idx);
2185
+ }
2186
+ return 1;
2187
+ });
2188
+ function formatBookingDateDisplay(value) {
2189
+ if (!value) return "";
2190
+ return new Date(value).toLocaleDateString(void 0, { month: "short", day: "numeric" });
2191
+ }
2192
+ function serviceProviderId(provider) {
2193
+ return "provider_id" in provider ? provider.provider_id : provider.id;
2194
+ }
2195
+ function getFirstServiceProviderEntry(state) {
2196
+ const serviceWithProviders = state.service;
2197
+ const providers = serviceWithProviders?.providers;
2198
+ if (!providers?.length) return null;
2199
+ if (state.selectedProviderId) {
2200
+ const match = providers.find((provider) => provider.provider_id === state.selectedProviderId);
2201
+ if (match) return match;
2202
+ }
2203
+ return providers[0];
2204
+ }
2205
+ async function loadBookingForm() {
2206
+ try {
2207
+ const form = await loadForm({ key: config.serviceOrderFormKey || "order-form" });
2208
+ const blocks = (form.schema || []).map(formSchemaToBlock);
2209
+ booking_form_node.set({ blocks });
2210
+ return blocks;
2211
+ } catch {
2212
+ booking_form_node.set({ blocks: [] });
2213
+ return [];
2214
+ }
2215
+ }
2216
+ const booking_actions = {
2217
+ async initialize() {
2218
+ booking_state.setKey("tzGroups", normalizeTimezoneGroups(client.utils.tzGroups));
2219
+ await ensureCart();
2220
+ setBookingCartFromServiceItems(service_items.get());
2221
+ const methods = session.get()?.market?.payment_methods || [];
2222
+ if (methods.length) booking_state.setKey("availablePaymentMethods", methods);
2223
+ await loadBookingForm();
2224
+ },
2225
+ setTimezone(tz) {
2226
+ booking_state.setKey("timezone", tz);
2227
+ booking_state.setKey("calendar", bookingCalendar());
2228
+ const state = booking_state.get();
2229
+ if (state.selectedDate) {
2230
+ booking_state.setKey("slots", computeBookingSlots(state.selectedDate));
2231
+ booking_state.setKey("selectedSlot", null);
2232
+ }
2233
+ },
2234
+ async setService(service) {
2235
+ booking_state.setKey("loading", true);
2236
+ try {
2237
+ const isMultiDayBlock = service.blocks?.find((block) => block.key === "isMultiDay");
2238
+ const blockValue = isMultiDayBlock?.value;
2239
+ const isMultiDay = Array.isArray(blockValue) ? blockValue[0] === true : blockValue === true;
2240
+ const [fullService, serviceProviders] = await Promise.all([
2241
+ client.eshop.service.get({ id: service.id }),
2242
+ client.eshop.service.findProviders({ service_id: service.id })
2243
+ ]);
2244
+ const providerIds = [...new Set(serviceProviders.map(serviceProviderId))];
2245
+ const providerResults = await Promise.all(
2246
+ providerIds.map((id) => client.eshop.provider.get({ id }).catch(() => null))
2247
+ );
2248
+ booking_state.set({
2249
+ ...booking_state.get(),
2250
+ service: fullService,
2251
+ providers: providerResults.filter((provider) => provider !== null),
2252
+ selectedProviderId: null,
2253
+ availability: null,
2254
+ selectedDate: null,
2255
+ startDate: null,
2256
+ endDate: null,
2257
+ slots: [],
2258
+ selectedSlot: null,
2259
+ currentMonth: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), 1),
2260
+ loading: false,
2261
+ isMultiDay
2262
+ });
2263
+ await booking_actions.loadMonth();
2264
+ } catch (error) {
2265
+ booking_state.setKey("loading", false);
2266
+ throw error;
2267
+ }
2268
+ },
2269
+ async loadMonth() {
2270
+ const state = booking_state.get();
2271
+ if (!state.service) return;
2272
+ booking_state.setKey("loading", true);
2273
+ try {
2274
+ const chainedStart = booking_chain_start.get();
2275
+ let from;
2276
+ let to;
2277
+ if (chainedStart) {
2278
+ from = chainedStart;
2279
+ to = chainedStart;
2280
+ } else {
2281
+ const month = state.currentMonth;
2282
+ from = Math.floor(Date.UTC(month.getFullYear(), month.getMonth(), 1) / 1e3);
2283
+ to = Math.floor(Date.UTC(month.getFullYear(), month.getMonth() + 1, 1) / 1e3);
2284
+ }
2285
+ const availability = await loadAvailability({
2286
+ service_id: state.service.id,
2287
+ from,
2288
+ to
2289
+ });
2290
+ booking_state.setKey("availability", availability);
2291
+ booking_state.setKey("calendar", bookingCalendar());
2292
+ } finally {
2293
+ booking_state.setKey("loading", false);
2294
+ }
2295
+ },
2296
+ prevMonth() {
2297
+ const { currentMonth } = booking_state.get();
2298
+ booking_state.setKey("currentMonth", new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1));
2299
+ void booking_actions.loadMonth();
2300
+ },
2301
+ nextMonth() {
2302
+ const { currentMonth } = booking_state.get();
2303
+ booking_state.setKey("currentMonth", new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1));
2304
+ void booking_actions.loadMonth();
2305
+ },
2306
+ selectProvider(providerId) {
2307
+ booking_state.set({
2308
+ ...booking_state.get(),
2309
+ selectedProviderId: providerId,
2310
+ selectedDate: null,
2311
+ startDate: null,
2312
+ endDate: null,
2313
+ slots: [],
2314
+ selectedSlot: null
2315
+ });
2316
+ void booking_actions.loadMonth();
2317
+ },
2318
+ selectDate(cell) {
2319
+ if (cell.blank || !cell.available) return;
2320
+ booking_state.setKey("dateTimeConfirmed", false);
2321
+ const state = booking_state.get();
2322
+ if (state.isMultiDay) {
2323
+ if (!state.startDate) {
2324
+ booking_state.setKey("startDate", cell.iso);
2325
+ booking_state.setKey("selectedDate", cell.iso);
2326
+ booking_state.setKey("endDate", null);
2327
+ booking_state.setKey("selectedSlot", null);
2328
+ } else if (!state.endDate) {
2329
+ if (cell.date.getTime() < new Date(state.startDate).getTime()) {
2330
+ booking_state.setKey("startDate", cell.iso);
2331
+ booking_state.setKey("endDate", state.startDate);
2332
+ } else {
2333
+ booking_state.setKey("endDate", cell.iso);
2334
+ }
2335
+ booking_actions.createMultiDaySlots();
2336
+ } else {
2337
+ booking_state.setKey("startDate", cell.iso);
2338
+ booking_state.setKey("selectedDate", cell.iso);
2339
+ booking_state.setKey("endDate", null);
2340
+ booking_state.setKey("selectedSlot", null);
2341
+ }
2342
+ booking_actions.updateCalendar();
2343
+ } else {
2344
+ booking_state.set({
2345
+ ...state,
2346
+ selectedDate: cell.iso,
2347
+ slots: computeBookingSlots(cell.iso),
2348
+ selectedSlot: null
2349
+ });
2350
+ booking_state.setKey("calendar", bookingCalendar());
2351
+ }
2352
+ },
2353
+ createMultiDaySlots() {
2354
+ const state = booking_state.get();
2355
+ if (!state.startDate || !state.endDate || !state.availability) return;
2356
+ const slots = [];
2357
+ for (let day = new Date(state.startDate); day <= new Date(state.endDate); day.setDate(day.getDate() + 1)) {
2358
+ const iso = day.toISOString().slice(0, 10);
2359
+ for (const slot of getSlotsForDate(state.availability, iso, state.selectedProviderId)) {
2360
+ slots.push({
2361
+ id: `${state.service?.id || "service"}-${slot.from}-${slots.length}`,
2362
+ serviceId: state.service?.id || "",
2363
+ providerId: slot.providerId,
2364
+ from: slot.from,
2365
+ to: slot.to,
2366
+ timeText: formatBookingSlotTime(slot.from, slot.to, state.timezone),
2367
+ dateText: new Date(slot.from * 1e3).toLocaleDateString([], {
2368
+ weekday: "short",
2369
+ month: "short",
2370
+ day: "numeric",
2371
+ timeZone: state.timezone
2372
+ }),
2373
+ isMultiDay: true
2374
+ });
2375
+ }
2376
+ }
2377
+ booking_state.setKey("slots", slots);
2378
+ booking_state.setKey("selectedSlot", slots.length === 1 ? slots[0] : null);
2379
+ },
2380
+ selectTimeSlot(slot) {
2381
+ booking_state.setKey("dateTimeConfirmed", false);
2382
+ booking_state.setKey("selectedSlot", slot);
2383
+ },
2384
+ resetDateSelection() {
2385
+ booking_state.setKey("selectedDate", null);
2386
+ booking_state.setKey("startDate", null);
2387
+ booking_state.setKey("endDate", null);
2388
+ booking_state.setKey("slots", []);
2389
+ booking_state.setKey("selectedSlot", null);
2390
+ booking_state.setKey("dateTimeConfirmed", false);
2391
+ },
2392
+ updateCalendar() {
2393
+ booking_state.setKey("calendar", bookingCalendar());
2394
+ },
2395
+ findFirstAvailable() {
2396
+ for (const day of booking_state.get().calendar) {
2397
+ if (!day.blank && day.available) {
2398
+ booking_actions.selectDate(day);
2399
+ return;
2400
+ }
2401
+ }
2402
+ },
2403
+ async addToCart() {
2404
+ const state = booking_state.get();
2405
+ const serviceBlocks = state.service?.forms || [];
2406
+ const enrich = (slot) => ({
2407
+ ...slot,
2408
+ serviceName: state.service ? serviceName(state.service, currentLocale()) : "",
2409
+ date: slot.dateText,
2410
+ serviceBlocks
2411
+ });
2412
+ const selected = state.isMultiDay && state.slots.length > 0 ? state.slots.map(enrich) : state.selectedSlot ? [enrich(state.selectedSlot)] : [];
2413
+ if (!selected.length) return;
2414
+ const nextCart = [...state.cart, ...selected];
2415
+ booking_state.set({
2416
+ ...state,
2417
+ cart: nextCart,
2418
+ selectedDate: null,
2419
+ startDate: null,
2420
+ endDate: null,
2421
+ slots: [],
2422
+ selectedSlot: null
2423
+ });
2424
+ await syncBookingCart(nextCart);
2425
+ booking_state.setKey("calendar", bookingCalendar());
2426
+ },
2427
+ async removeFromCart(slotId) {
2428
+ const nextCart = booking_state.get().cart.filter((slot) => slot.id !== slotId);
2429
+ booking_state.setKey("cart", nextCart);
2430
+ await syncBookingCart(nextCart);
2431
+ },
2432
+ async clearCart() {
2433
+ booking_state.setKey("cart", []);
2434
+ await syncBookingCart([]);
2435
+ },
2436
+ async checkout(paymentMethodId, forms = []) {
2437
+ const state = booking_state.get();
2438
+ if (!state.cart.length) return { success: false, error: "Cart is empty" };
2439
+ booking_state.setKey("loading", true);
2440
+ try {
2441
+ const result = await checkout({
2442
+ service_items: state.cart.map((slot) => ({
2443
+ ...toServiceCartItem(slot),
2444
+ forms: []
2445
+ })),
2446
+ payment_method_id: paymentMethodId,
2447
+ promo_code: state.promoCode || void 0,
2448
+ forms
2449
+ });
2450
+ booking_state.setKey("cartId", cart.get()?.id || null);
2451
+ return { success: true, data: result };
2452
+ } catch (error) {
2453
+ return { success: false, error: readErrorMessage(error, "Checkout failed.") };
2454
+ } finally {
2455
+ booking_state.setKey("loading", false);
2456
+ }
2457
+ },
2458
+ async fetchQuote(paymentMethodId, promoCode) {
2459
+ const state = booking_state.get();
2460
+ if (!state.cart.length) return null;
2461
+ booking_state.setKey("fetchingQuote", true);
2462
+ booking_state.setKey("quoteError", null);
2463
+ try {
2464
+ booking_state.setKey("promoCode", promoCode || null);
2465
+ const response = await fetchQuote({
2466
+ service_items: state.cart.map(toServiceCartItem),
2467
+ payment_method_id: paymentMethodId,
2468
+ promo_code: promoCode || void 0
2469
+ });
2470
+ booking_state.setKey("cartId", cart.get()?.id || null);
2471
+ booking_state.setKey("quote", response);
2472
+ const methods = response?.payment_methods || session.get()?.market?.payment_methods || [];
2473
+ if (methods.length) booking_state.setKey("availablePaymentMethods", methods);
2474
+ return response;
2475
+ } catch (error) {
2476
+ booking_state.setKey("quoteError", readErrorMessage(error, "Failed to fetch quote."));
2477
+ return null;
2478
+ } finally {
2479
+ booking_state.setKey("fetchingQuote", false);
2480
+ }
2481
+ },
2482
+ getProvidersList() {
2483
+ return booking_state.get().providers;
2484
+ },
2485
+ prevStep() {
2486
+ const current = bookingCurrentStepName();
2487
+ if (current === "review") {
2488
+ booking_state.setKey("dateTimeConfirmed", false);
2489
+ return;
2490
+ }
2491
+ if (current === "datetime") {
2492
+ booking_state.setKey("selectedSlot", null);
2493
+ booking_state.setKey("dateTimeConfirmed", false);
2494
+ }
2495
+ },
2496
+ nextStep() {
2497
+ if (bookingCurrentStepName() === "datetime" && booking_can_proceed.get()) {
2498
+ booking_state.setKey("dateTimeConfirmed", true);
2499
+ }
2500
+ },
2501
+ getServicePrice() {
2502
+ const state = booking_state.get();
2503
+ if (state.quote?.total !== void 0) return String(state.quote.total);
2504
+ const provider = getFirstServiceProviderEntry(state);
2505
+ if (!provider?.prices) return "";
2506
+ return client.utils.formatPrice(provider.prices) || "0";
2507
+ },
2508
+ formatDateDisplay: formatBookingDateDisplay,
2509
+ serviceItemsFromSlots(slots) {
2510
+ return slots.map(toServiceCartItem);
2511
+ }
2512
+ };
2513
+ service_items.subscribe((items) => setBookingCartFromServiceItems(items));
1926
2514
  async function loadNode(params, options) {
1927
2515
  cms_state.setKey("loading", true);
1928
2516
  cms_state.setKey("error", null);
@@ -2126,6 +2714,18 @@ function createArkyStore(config) {
2126
2714
  buildServiceItems: toServiceCheckoutItems
2127
2715
  }
2128
2716
  },
2717
+ booking: {
2718
+ state: booking_state,
2719
+ form_blocks: booking_form_blocks,
2720
+ current_step_name: booking_current_step_name,
2721
+ can_proceed: booking_can_proceed,
2722
+ month_year: booking_month_year,
2723
+ chain_start: booking_chain_start,
2724
+ total_steps: booking_total_steps,
2725
+ steps: booking_steps,
2726
+ current_step: booking_current_step,
2727
+ actions: booking_actions
2728
+ },
2129
2729
  crm: client.crm,
2130
2730
  activity: {
2131
2731
  track(params) {