@sonordev/site-kit 1.2.10 → 1.3.1

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.
Files changed (67) hide show
  1. package/dist/{chunk-SQSBAPWA.mjs → chunk-3QKTOTWS.mjs} +5 -5
  2. package/dist/chunk-3QKTOTWS.mjs.map +1 -0
  3. package/dist/{chunk-BFJDUTXK.js → chunk-5NXUENL3.js} +3 -3
  4. package/dist/{chunk-BFJDUTXK.js.map → chunk-5NXUENL3.js.map} +1 -1
  5. package/dist/{chunk-6TFGPXXK.mjs → chunk-5TTTX6NN.mjs} +2 -2
  6. package/dist/{chunk-6TFGPXXK.mjs.map → chunk-5TTTX6NN.mjs.map} +1 -1
  7. package/dist/{chunk-5F7FFUPJ.mjs → chunk-J4K335PQ.mjs} +12 -5
  8. package/dist/chunk-J4K335PQ.mjs.map +1 -0
  9. package/dist/chunk-JB6I5PYV.js +939 -0
  10. package/dist/chunk-JB6I5PYV.js.map +1 -0
  11. package/dist/{chunk-7ROZJDXE.js → chunk-JKPJ2QCC.js} +2 -2
  12. package/dist/{chunk-7ROZJDXE.js.map → chunk-JKPJ2QCC.js.map} +1 -1
  13. package/dist/{chunk-JMNSED4O.mjs → chunk-KMLCQYEV.mjs} +3 -3
  14. package/dist/{chunk-JMNSED4O.mjs.map → chunk-KMLCQYEV.mjs.map} +1 -1
  15. package/dist/{chunk-2XOW276O.js → chunk-LTTTIJPN.js} +14 -7
  16. package/dist/chunk-LTTTIJPN.js.map +1 -0
  17. package/dist/{chunk-742WMKQC.js → chunk-NZHVGBAF.js} +2 -2
  18. package/dist/{chunk-742WMKQC.js.map → chunk-NZHVGBAF.js.map} +1 -1
  19. package/dist/{chunk-RMOL4TZ6.js → chunk-PJKHCYI5.js} +5 -5
  20. package/dist/chunk-PJKHCYI5.js.map +1 -0
  21. package/dist/{chunk-47Y3YSES.mjs → chunk-QBFTBORL.mjs} +2 -2
  22. package/dist/{chunk-47Y3YSES.mjs.map → chunk-QBFTBORL.mjs.map} +1 -1
  23. package/dist/{chunk-2RHO4KSK.mjs → chunk-RJTBSSZB.mjs} +4 -4
  24. package/dist/{chunk-2RHO4KSK.mjs.map → chunk-RJTBSSZB.mjs.map} +1 -1
  25. package/dist/{chunk-CFEOOJUT.js → chunk-RRBBMEDL.js} +7 -7
  26. package/dist/{chunk-CFEOOJUT.js.map → chunk-RRBBMEDL.js.map} +1 -1
  27. package/dist/chunk-W2TZYAUL.mjs +925 -0
  28. package/dist/chunk-W2TZYAUL.mjs.map +1 -0
  29. package/dist/cli/index.js +1 -1
  30. package/dist/cli/index.mjs +1 -1
  31. package/dist/index.js +60 -938
  32. package/dist/index.js.map +1 -1
  33. package/dist/index.mjs +3 -917
  34. package/dist/index.mjs.map +1 -1
  35. package/dist/layout/client.js +2 -2
  36. package/dist/layout/client.mjs +1 -1
  37. package/dist/layout/index.js +6 -6
  38. package/dist/layout/index.mjs +3 -3
  39. package/dist/llms/index.js +12 -12
  40. package/dist/llms/index.mjs +2 -2
  41. package/dist/seo/index.d.mts +19 -198
  42. package/dist/seo/index.d.ts +19 -198
  43. package/dist/seo/index.js +68 -69
  44. package/dist/seo/index.js.map +1 -1
  45. package/dist/seo/index.mjs +36 -37
  46. package/dist/seo/index.mjs.map +1 -1
  47. package/dist/seo/server.js +4 -4
  48. package/dist/seo/server.mjs +2 -2
  49. package/dist/{server-api-HTSLBT6F.mjs → server-api-FCTEQOTK.mjs} +3 -3
  50. package/dist/{server-api-HTSLBT6F.mjs.map → server-api-FCTEQOTK.mjs.map} +1 -1
  51. package/dist/{server-api-C5JXIROA.js → server-api-SYINXTN7.js} +21 -21
  52. package/dist/{server-api-C5JXIROA.js.map → server-api-SYINXTN7.js.map} +1 -1
  53. package/dist/setup/index.js +2 -2
  54. package/dist/setup/index.mjs +1 -1
  55. package/dist/setup/server.js +2 -2
  56. package/dist/setup/server.mjs +1 -1
  57. package/dist/sitemap/index.js +2 -2
  58. package/dist/sitemap/index.mjs +1 -1
  59. package/dist/sync/index.js +63 -0
  60. package/dist/sync/index.js.map +1 -0
  61. package/dist/sync/index.mjs +4 -0
  62. package/dist/sync/index.mjs.map +1 -0
  63. package/package.json +6 -1
  64. package/dist/chunk-2XOW276O.js.map +0 -1
  65. package/dist/chunk-5F7FFUPJ.mjs.map +0 -1
  66. package/dist/chunk-RMOL4TZ6.js.map +0 -1
  67. package/dist/chunk-SQSBAPWA.mjs.map +0 -1
package/dist/index.js CHANGED
@@ -1,12 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkYBTYDBWY_js = require('./chunk-YBTYDBWY.js');
4
+ var chunkX4J33XQD_js = require('./chunk-X4J33XQD.js');
4
5
  var chunkG6VGUAK2_js = require('./chunk-G6VGUAK2.js');
5
6
  var chunkTKQLH33E_js = require('./chunk-TKQLH33E.js');
6
7
  require('./chunk-DY4K6X3A.js');
7
8
  var chunkTLHRV3LZ_js = require('./chunk-TLHRV3LZ.js');
9
+ var chunkJB6I5PYV_js = require('./chunk-JB6I5PYV.js');
8
10
  var chunkIFAW7JFO_js = require('./chunk-IFAW7JFO.js');
9
- var chunkX4J33XQD_js = require('./chunk-X4J33XQD.js');
10
11
  require('./chunk-EUNL6GAL.js');
11
12
  var chunkUOBK3SSF_js = require('./chunk-UOBK3SSF.js');
12
13
  var chunkJAXKHEOM_js = require('./chunk-JAXKHEOM.js');
@@ -1501,921 +1502,6 @@ ${moduleConfigs.join("\n")}
1501
1502
  }`;
1502
1503
  }
1503
1504
 
1504
- // src/sync/api.ts
1505
- var DEFAULT_API_URL = "https://api.sonor.io";
1506
- function getApiConfig2() {
1507
- const apiUrl = typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ || DEFAULT_API_URL : DEFAULT_API_URL;
1508
- const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0;
1509
- return { apiUrl, apiKey };
1510
- }
1511
- function buildHeaders(apiKey, isPost = false) {
1512
- const headers = {};
1513
- if (isPost) headers["Content-Type"] = "application/json";
1514
- if (apiKey) headers["x-api-key"] = apiKey;
1515
- return headers;
1516
- }
1517
- async function fetchBookingTypes(orgSlug, apiUrl, apiKey) {
1518
- const cfg = getApiConfig2();
1519
- const url = apiUrl || cfg.apiUrl;
1520
- const key = apiKey || cfg.apiKey;
1521
- const endpoint = key ? `${url}/sync/widget/types` : `${url}/sync/public/${orgSlug}/types`;
1522
- const response = await fetch(endpoint, { headers: buildHeaders(key) });
1523
- if (!response.ok) {
1524
- const msg = await response.json().catch(() => ({ message: response.statusText }));
1525
- const detail = msg && typeof msg.message === "string" ? msg.message : response.statusText;
1526
- throw new Error(`Failed to fetch booking types: ${response.status} ${detail}`);
1527
- }
1528
- const data = await response.json();
1529
- return data.types || [];
1530
- }
1531
- async function fetchBookingTypeDetails(typeSlug, orgSlug, apiUrl, apiKey) {
1532
- const cfg = getApiConfig2();
1533
- const url = apiUrl || cfg.apiUrl;
1534
- const key = apiKey || cfg.apiKey;
1535
- const endpoint = key ? `${url}/sync/widget/types/${typeSlug}` : `${url}/sync/public/${orgSlug}/types/${typeSlug}`;
1536
- const response = await fetch(endpoint, { headers: buildHeaders(key) });
1537
- if (!response.ok) {
1538
- const msg = await response.json().catch(() => ({ message: response.statusText }));
1539
- const detail = msg && typeof msg.message === "string" ? msg.message : response.statusText;
1540
- throw new Error(`Failed to fetch booking type: ${response.status} ${detail}`);
1541
- }
1542
- return response.json();
1543
- }
1544
- async function fetchAvailability(typeSlug, date, orgSlug, apiUrl, apiKey, timezone, hostId) {
1545
- const cfg = getApiConfig2();
1546
- const url = apiUrl || cfg.apiUrl;
1547
- const key = apiKey || cfg.apiKey;
1548
- const params = new URLSearchParams({ date });
1549
- if (timezone) params.append("timezone", timezone);
1550
- if (hostId) params.append("hostId", hostId);
1551
- const endpoint = key ? `${url}/sync/widget/availability/${typeSlug}?${params}` : `${url}/sync/public/${orgSlug}/availability/${typeSlug}?${params}`;
1552
- const response = await fetch(endpoint, { headers: buildHeaders(key) });
1553
- if (!response.ok) {
1554
- const msg = await response.json().catch(() => ({ message: response.statusText }));
1555
- let detail = msg && typeof msg.message === "string" ? String(msg.message).trim() : (response.statusText || "").trim();
1556
- if (detail === String(response.status) || /^\d+$/.test(detail)) detail = "";
1557
- const rest = detail ? `${response.status} ${detail}` : String(response.status);
1558
- const fallback = response.status === 404 && !detail ? "No times available. In the Portal, check Sync \u2192 Booking Types (Schedule a Tour) has a host assigned and the project API key is set for this site." : null;
1559
- throw new Error(fallback || `Failed to fetch availability: ${rest}`.trim());
1560
- }
1561
- const data = await response.json();
1562
- const raw = data.slots || [];
1563
- return raw.map((slot) => ({
1564
- start: slot.startTime ?? slot.start,
1565
- end: slot.endTime ?? slot.end,
1566
- hostId: slot.hostId,
1567
- available: slot.available !== false
1568
- }));
1569
- }
1570
- async function createSlotHold(holdData, apiUrl, apiKey) {
1571
- const cfg = getApiConfig2();
1572
- const url = apiUrl || cfg.apiUrl;
1573
- const key = apiKey || cfg.apiKey;
1574
- const endpoint = key ? `${url}/sync/widget/hold` : `${url}/sync/public/hold`;
1575
- const response = await fetch(endpoint, {
1576
- method: "POST",
1577
- headers: buildHeaders(key, true),
1578
- body: JSON.stringify(holdData)
1579
- });
1580
- if (!response.ok) {
1581
- throw new Error(`Failed to hold slot: ${response.statusText}`);
1582
- }
1583
- return response.json();
1584
- }
1585
- async function releaseSlotHold(holdId, apiUrl, apiKey) {
1586
- const cfg = getApiConfig2();
1587
- const url = apiUrl || cfg.apiUrl;
1588
- const key = apiKey || cfg.apiKey;
1589
- const endpoint = key ? `${url}/sync/widget/hold/${holdId}` : `${url}/sync/public/hold/${holdId}`;
1590
- await fetch(endpoint, {
1591
- method: "DELETE",
1592
- headers: buildHeaders(key)
1593
- });
1594
- }
1595
- async function createBooking(bookingData, apiUrl, apiKey) {
1596
- const cfg = getApiConfig2();
1597
- const url = apiUrl || cfg.apiUrl;
1598
- const key = apiKey || cfg.apiKey;
1599
- const endpoint = key ? `${url}/sync/widget/booking` : `${url}/sync/public/booking`;
1600
- const response = await fetch(endpoint, {
1601
- method: "POST",
1602
- headers: buildHeaders(key, true),
1603
- body: JSON.stringify(bookingData)
1604
- });
1605
- if (!response.ok) {
1606
- const errorData = await response.json().catch(() => ({}));
1607
- throw new Error(errorData.message || `Failed to create booking: ${response.statusText}`);
1608
- }
1609
- return response.json();
1610
- }
1611
- async function fetchAvailableDates(typeSlug, startDate, endDate, orgSlug, apiUrl, apiKey, timezone) {
1612
- const availableDates = [];
1613
- const start = new Date(startDate);
1614
- const end = new Date(endDate);
1615
- const current = new Date(start);
1616
- while (current <= end) {
1617
- const dateStr = current.toISOString().split("T")[0];
1618
- try {
1619
- const slots = await fetchAvailability(typeSlug, dateStr, orgSlug, apiUrl, apiKey, timezone);
1620
- if (slots.some((s) => s.available)) {
1621
- availableDates.push(dateStr);
1622
- }
1623
- } catch {
1624
- }
1625
- current.setDate(current.getDate() + 1);
1626
- }
1627
- return availableDates;
1628
- }
1629
- function detectTimezone() {
1630
- try {
1631
- return Intl.DateTimeFormat().resolvedOptions().timeZone;
1632
- } catch {
1633
- return "America/New_York";
1634
- }
1635
- }
1636
- function formatTime(isoString, timezone) {
1637
- const date = new Date(isoString);
1638
- return date.toLocaleTimeString("en-US", {
1639
- hour: "numeric",
1640
- minute: "2-digit",
1641
- hour12: true,
1642
- timeZone: timezone
1643
- });
1644
- }
1645
- function formatDate2(isoString, timezone) {
1646
- const date = new Date(isoString);
1647
- return date.toLocaleDateString("en-US", {
1648
- weekday: "long",
1649
- month: "long",
1650
- day: "numeric",
1651
- year: "numeric",
1652
- timeZone: timezone
1653
- });
1654
- }
1655
- function formatDuration(minutes) {
1656
- if (minutes < 60) {
1657
- return `${minutes} min`;
1658
- }
1659
- const hours = Math.floor(minutes / 60);
1660
- const mins = minutes % 60;
1661
- if (mins === 0) {
1662
- return `${hours} hour${hours > 1 ? "s" : ""}`;
1663
- }
1664
- return `${hours}h ${mins}m`;
1665
- }
1666
- var DEFAULT_API_URL2 = "https://api.sonor.io";
1667
- var DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
1668
- var MONTHS = [
1669
- "January",
1670
- "February",
1671
- "March",
1672
- "April",
1673
- "May",
1674
- "June",
1675
- "July",
1676
- "August",
1677
- "September",
1678
- "October",
1679
- "November",
1680
- "December"
1681
- ];
1682
- function isSameDay(a, b) {
1683
- return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
1684
- }
1685
- function isBeforeDay(a, b) {
1686
- const ac = new Date(a.getFullYear(), a.getMonth(), a.getDate());
1687
- const bc = new Date(b.getFullYear(), b.getMonth(), b.getDate());
1688
- return ac < bc;
1689
- }
1690
- function calendarDays(year, month) {
1691
- const first = new Date(year, month, 1);
1692
- const last = new Date(year, month + 1, 0);
1693
- const cells = [];
1694
- for (let i = 0; i < first.getDay(); i++) cells.push(null);
1695
- for (let d = 1; d <= last.getDate(); d++) cells.push(new Date(year, month, d));
1696
- return cells;
1697
- }
1698
- function ChevronLeft() {
1699
- return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12.5 15L7.5 10L12.5 5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
1700
- }
1701
- function ChevronRight() {
1702
- return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7.5 5L12.5 10L7.5 15", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
1703
- }
1704
- function ClockIcon() {
1705
- return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
1706
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8", cy: "8", r: "6.5", stroke: "currentColor", strokeWidth: "1.2" }),
1707
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 4.5V8L10 10", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
1708
- ] });
1709
- }
1710
- function GlobeIcon() {
1711
- return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
1712
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8", cy: "8", r: "6.5", stroke: "currentColor", strokeWidth: "1.2" }),
1713
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M1.5 8H14.5", stroke: "currentColor", strokeWidth: "1.2" }),
1714
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 1.5C9.66 3.34 10.61 5.62 10.61 8C10.61 10.38 9.66 12.66 8 14.5C6.34 12.66 5.39 10.38 5.39 8C5.39 5.62 6.34 3.34 8 1.5Z", stroke: "currentColor", strokeWidth: "1.2" })
1715
- ] });
1716
- }
1717
- function CheckCircleIcon() {
1718
- return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "56", height: "56", viewBox: "0 0 56 56", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
1719
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "28", cy: "28", r: "28", fill: "var(--bw-primary)" }),
1720
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 28.5L24.5 35L38 21.5", stroke: "white", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round" })
1721
- ] });
1722
- }
1723
- function CalendarPlusIcon() {
1724
- return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
1725
- /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "1.5", y: "2.5", width: "13", height: "12", rx: "1.5", stroke: "currentColor", strokeWidth: "1.2" }),
1726
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M1.5 6.5H14.5", stroke: "currentColor", strokeWidth: "1.2" }),
1727
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 1V4M11 1V4", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" }),
1728
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 9V12M6.5 10.5H9.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" })
1729
- ] });
1730
- }
1731
- function ArrowLeftIcon() {
1732
- return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10 3L5 8L10 13", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
1733
- }
1734
- function SpinnerIcon() {
1735
- return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "bw-spinner", children: [
1736
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "10", cy: "10", r: "8", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeDasharray: "50.265", strokeDashoffset: "25", opacity: "0.3" }),
1737
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "10", cy: "10", r: "8", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeDasharray: "50.265", strokeDashoffset: "37.7" })
1738
- ] });
1739
- }
1740
- function BookingWidget({
1741
- orgSlug,
1742
- apiKey: propApiKey,
1743
- apiUrl: propApiUrl,
1744
- bookingTypeSlug,
1745
- timezone: propTimezone,
1746
- className = "",
1747
- daysToShow = 60,
1748
- onBookingComplete,
1749
- onError,
1750
- hideTypeSelector = false,
1751
- styles: styles2 = {}
1752
- }) {
1753
- const apiKey = propApiKey || (typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0);
1754
- const apiUrl = propApiUrl || (typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ : void 0) || DEFAULT_API_URL2;
1755
- const [step, setStep] = React2.useState(bookingTypeSlug ? "datetime" : "type");
1756
- const [loading, setLoading] = React2.useState(false);
1757
- const [slotsLoading, setSlotsLoading] = React2.useState(false);
1758
- const [error, setError] = React2.useState(null);
1759
- const [bookingTypes, setBookingTypes] = React2.useState([]);
1760
- const [selectedType, setSelectedType] = React2.useState(null);
1761
- const [viewMonth, setViewMonth] = React2.useState(() => /* @__PURE__ */ new Date());
1762
- const [selectedDate, setSelectedDate] = React2.useState(null);
1763
- const [slots, setSlots] = React2.useState([]);
1764
- const [selectedSlot, setSelectedSlot] = React2.useState(null);
1765
- const [confirmedSlot, setConfirmedSlot] = React2.useState(false);
1766
- const [hold, setHold] = React2.useState(null);
1767
- const [guestInfo, setGuestInfo] = React2.useState({ name: "", email: "" });
1768
- const [bookingResult, setBookingResult] = React2.useState(null);
1769
- const [submitting, setSubmitting] = React2.useState(false);
1770
- const slotsRef = React2.useRef(null);
1771
- const today = React2.useMemo(() => {
1772
- const d = /* @__PURE__ */ new Date();
1773
- d.setHours(0, 0, 0, 0);
1774
- return d;
1775
- }, []);
1776
- const timezone = React2.useMemo(() => propTimezone || detectTimezone(), [propTimezone]);
1777
- const shortTz = React2.useMemo(() => {
1778
- try {
1779
- const parts = Intl.DateTimeFormat("en-US", { timeZone: timezone, timeZoneName: "short" }).formatToParts(/* @__PURE__ */ new Date());
1780
- return parts.find((p) => p.type === "timeZoneName")?.value || timezone;
1781
- } catch {
1782
- return timezone;
1783
- }
1784
- }, [timezone]);
1785
- const days = React2.useMemo(() => calendarDays(viewMonth.getFullYear(), viewMonth.getMonth()), [viewMonth]);
1786
- const maxDate = React2.useMemo(() => {
1787
- const d = new Date(today);
1788
- d.setDate(d.getDate() + daysToShow);
1789
- return d;
1790
- }, [today, daysToShow]);
1791
- React2.useEffect(() => {
1792
- if (bookingTypeSlug) {
1793
- setLoading(true);
1794
- fetchBookingTypeDetails(bookingTypeSlug, orgSlug, apiUrl, apiKey).then((type) => {
1795
- setSelectedType(type);
1796
- setStep("datetime");
1797
- }).catch((err) => {
1798
- setError(err.message);
1799
- onError?.(err);
1800
- }).finally(() => setLoading(false));
1801
- } else {
1802
- setLoading(true);
1803
- fetchBookingTypes(orgSlug, apiUrl, apiKey).then((types) => setBookingTypes(types.filter((t) => t.is_active))).catch((err) => {
1804
- setError(err.message);
1805
- onError?.(err);
1806
- }).finally(() => setLoading(false));
1807
- }
1808
- }, [orgSlug, apiKey, bookingTypeSlug, apiUrl, onError]);
1809
- React2.useEffect(() => {
1810
- if (!selectedDate || !selectedType) return;
1811
- const dateStr = selectedDate.toISOString().split("T")[0];
1812
- setSlotsLoading(true);
1813
- setSlots([]);
1814
- setSelectedSlot(null);
1815
- setConfirmedSlot(false);
1816
- fetchAvailability(selectedType.slug, dateStr, orgSlug, apiUrl, apiKey, timezone).then((s) => setSlots(s.filter((slot) => slot.available))).catch((err) => {
1817
- setError(err.message);
1818
- onError?.(err);
1819
- }).finally(() => setSlotsLoading(false));
1820
- }, [selectedDate, selectedType, orgSlug, apiKey, apiUrl, timezone, onError]);
1821
- const handleSlotSelect = React2.useCallback((slot) => {
1822
- setSelectedSlot(slot);
1823
- setConfirmedSlot(false);
1824
- }, []);
1825
- const handleSlotConfirm = React2.useCallback(async () => {
1826
- if (!selectedType || !selectedSlot) return;
1827
- if (hold) await releaseSlotHold(hold.holdId, apiUrl, apiKey).catch(() => {
1828
- });
1829
- setLoading(true);
1830
- try {
1831
- const newHold = await createSlotHold(
1832
- {
1833
- bookingType: selectedType.slug,
1834
- slotStart: selectedSlot.start,
1835
- slotEnd: selectedSlot.end,
1836
- hostId: selectedSlot.hostId || "",
1837
- sessionId: `bw-${Date.now()}`
1838
- },
1839
- apiUrl,
1840
- apiKey
1841
- );
1842
- setHold(newHold);
1843
- setConfirmedSlot(true);
1844
- setStep("form");
1845
- } catch (err) {
1846
- setError(err.message);
1847
- onError?.(err);
1848
- } finally {
1849
- setLoading(false);
1850
- }
1851
- }, [selectedType, selectedSlot, hold, timezone, apiUrl, apiKey, onError]);
1852
- const handleBookingSubmit = React2.useCallback(async (e) => {
1853
- e.preventDefault();
1854
- if (!selectedType || !selectedSlot) return;
1855
- setSubmitting(true);
1856
- setError(null);
1857
- try {
1858
- const result = await createBooking(
1859
- {
1860
- bookingType: selectedType.slug,
1861
- scheduledAt: selectedSlot.start,
1862
- hostId: selectedSlot.hostId || "",
1863
- name: guestInfo.name,
1864
- email: guestInfo.email,
1865
- phone: guestInfo.phone,
1866
- message: guestInfo.notes,
1867
- source: "main-site",
1868
- timezone,
1869
- holdId: hold?.holdId,
1870
- sessionId: `bw-${Date.now()}`,
1871
- sourceUrl: typeof window !== "undefined" ? window.location.href : void 0
1872
- },
1873
- apiUrl,
1874
- apiKey
1875
- );
1876
- setBookingResult(result);
1877
- setStep("success");
1878
- onBookingComplete?.(result);
1879
- } catch (err) {
1880
- setError(err.message);
1881
- onError?.(err);
1882
- } finally {
1883
- setSubmitting(false);
1884
- }
1885
- }, [selectedType, selectedSlot, guestInfo, timezone, hold, apiUrl, apiKey, onBookingComplete, onError]);
1886
- React2.useEffect(() => {
1887
- return () => {
1888
- if (hold) releaseSlotHold(hold.holdId, apiUrl, apiKey).catch(() => {
1889
- });
1890
- };
1891
- }, [hold, apiUrl, apiKey]);
1892
- const canGoPrev = viewMonth.getFullYear() > today.getFullYear() || viewMonth.getMonth() > today.getMonth();
1893
- const canGoNext = viewMonth < maxDate;
1894
- const goMonth = (dir) => {
1895
- setViewMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() + dir, 1));
1896
- };
1897
- const groupedSlots = React2.useMemo(() => {
1898
- const morning = [];
1899
- const afternoon = [];
1900
- const evening = [];
1901
- for (const s of slots) {
1902
- const h = new Date(s.start).getHours();
1903
- if (h < 12) morning.push(s);
1904
- else if (h < 17) afternoon.push(s);
1905
- else evening.push(s);
1906
- }
1907
- return { morning, afternoon, evening };
1908
- }, [slots]);
1909
- const cssVars = {
1910
- "--bw-primary": styles2.primaryColor || "#0069ff",
1911
- "--bw-primary-light": styles2.primaryColor ? `color-mix(in srgb, ${styles2.primaryColor} 12%, white)` : "#e8f1ff",
1912
- "--bw-primary-hover": styles2.primaryColor ? `color-mix(in srgb, ${styles2.primaryColor} 90%, black)` : "#0055d4",
1913
- "--bw-radius": styles2.borderRadius || "8px",
1914
- "--bw-font": styles2.fontFamily || "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
1915
- };
1916
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `bw-root ${className}`, style: cssVars, children: [
1917
- error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-error", role: "alert", children: [
1918
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: error }),
1919
- /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setError(null), "aria-label": "Dismiss error", children: "\xD7" })
1920
- ] }),
1921
- step === "type" && !hideTypeSelector && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-step bw-fade-in", children: [
1922
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "bw-heading", children: "Select a Service" }),
1923
- loading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-loading", children: [
1924
- /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}),
1925
- " Loading services..."
1926
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bw-type-list", children: bookingTypes.map((type) => /* @__PURE__ */ jsxRuntime.jsxs(
1927
- "button",
1928
- {
1929
- className: "bw-type-card",
1930
- onClick: () => {
1931
- setSelectedType(type);
1932
- setStep("datetime");
1933
- },
1934
- children: [
1935
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-type-name", children: type.name }),
1936
- type.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-type-desc", children: type.description }),
1937
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "bw-type-meta", children: [
1938
- /* @__PURE__ */ jsxRuntime.jsx(ClockIcon, {}),
1939
- " ",
1940
- formatDuration(type.duration_minutes),
1941
- type.price_cents ? ` \xB7 $${(type.price_cents / 100).toFixed(2)}` : " \xB7 Free"
1942
- ] })
1943
- ]
1944
- },
1945
- type.id
1946
- )) })
1947
- ] }),
1948
- step === "datetime" && selectedType && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-step bw-fade-in", children: [
1949
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-info-header", children: [
1950
- !bookingTypeSlug && /* @__PURE__ */ jsxRuntime.jsx("button", { className: "bw-back", onClick: () => {
1951
- setSelectedType(null);
1952
- setStep("type");
1953
- }, children: /* @__PURE__ */ jsxRuntime.jsx(ArrowLeftIcon, {}) }),
1954
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1955
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "bw-heading", children: selectedType.name }),
1956
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-meta-row", children: [
1957
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "bw-badge", children: [
1958
- /* @__PURE__ */ jsxRuntime.jsx(ClockIcon, {}),
1959
- " ",
1960
- formatDuration(selectedType.duration_minutes)
1961
- ] }),
1962
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "bw-badge", children: [
1963
- /* @__PURE__ */ jsxRuntime.jsx(GlobeIcon, {}),
1964
- " ",
1965
- shortTz
1966
- ] })
1967
- ] })
1968
- ] })
1969
- ] }),
1970
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-datetime-layout", children: [
1971
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-calendar", children: [
1972
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-cal-header", children: [
1973
- /* @__PURE__ */ jsxRuntime.jsx("button", { className: "bw-cal-nav", onClick: () => goMonth(-1), disabled: !canGoPrev, "aria-label": "Previous month", children: /* @__PURE__ */ jsxRuntime.jsx(ChevronLeft, {}) }),
1974
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "bw-cal-title", children: [
1975
- MONTHS[viewMonth.getMonth()],
1976
- " ",
1977
- viewMonth.getFullYear()
1978
- ] }),
1979
- /* @__PURE__ */ jsxRuntime.jsx("button", { className: "bw-cal-nav", onClick: () => goMonth(1), disabled: !canGoNext, "aria-label": "Next month", children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRight, {}) })
1980
- ] }),
1981
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bw-cal-weekdays", children: DAYS.map((d) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-cal-wd", children: d }, d)) }),
1982
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bw-cal-grid", children: days.map((date, i) => {
1983
- if (!date) return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-cal-empty" }, `e-${i}`);
1984
- const past = isBeforeDay(date, today);
1985
- const future = date > maxDate;
1986
- const disabled = past || future;
1987
- const sel = selectedDate && isSameDay(date, selectedDate);
1988
- const isToday = isSameDay(date, today);
1989
- return /* @__PURE__ */ jsxRuntime.jsx(
1990
- "button",
1991
- {
1992
- className: `bw-cal-day${sel ? " selected" : ""}${isToday ? " today" : ""}${disabled ? " disabled" : ""}`,
1993
- onClick: () => !disabled && setSelectedDate(date),
1994
- disabled,
1995
- "aria-label": date.toLocaleDateString("en-US", { weekday: "long", month: "long", day: "numeric" }),
1996
- "aria-pressed": sel || void 0,
1997
- children: date.getDate()
1998
- },
1999
- date.toISOString()
2000
- );
2001
- }) })
2002
- ] }),
2003
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `bw-times${selectedDate ? " visible" : ""}`, ref: slotsRef, children: !selectedDate ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-times-placeholder", children: [
2004
- /* @__PURE__ */ jsxRuntime.jsx(CalendarPlusIcon, {}),
2005
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Select a date to view available times" })
2006
- ] }) : slotsLoading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-loading", children: [
2007
- /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}),
2008
- " Loading times..."
2009
- ] }) : slots.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bw-times-empty", children: "No available times on this date. Try another day." }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-times-scroll", children: [
2010
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "bw-times-date", children: selectedDate.toLocaleDateString("en-US", { weekday: "long", month: "short", day: "numeric" }) }),
2011
- groupedSlots.morning.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-time-group", children: [
2012
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-time-label", children: "Morning" }),
2013
- groupedSlots.morning.map((slot) => /* @__PURE__ */ jsxRuntime.jsx(TimeButton, { slot, selected: selectedSlot?.start === slot.start, confirmed: confirmedSlot && selectedSlot?.start === slot.start, onClick: () => handleSlotSelect(slot), onConfirm: handleSlotConfirm, timezone, loading }, slot.start))
2014
- ] }),
2015
- groupedSlots.afternoon.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-time-group", children: [
2016
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-time-label", children: "Afternoon" }),
2017
- groupedSlots.afternoon.map((slot) => /* @__PURE__ */ jsxRuntime.jsx(TimeButton, { slot, selected: selectedSlot?.start === slot.start, confirmed: confirmedSlot && selectedSlot?.start === slot.start, onClick: () => handleSlotSelect(slot), onConfirm: handleSlotConfirm, timezone, loading }, slot.start))
2018
- ] }),
2019
- groupedSlots.evening.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-time-group", children: [
2020
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-time-label", children: "Evening" }),
2021
- groupedSlots.evening.map((slot) => /* @__PURE__ */ jsxRuntime.jsx(TimeButton, { slot, selected: selectedSlot?.start === slot.start, confirmed: confirmedSlot && selectedSlot?.start === slot.start, onClick: () => handleSlotSelect(slot), onConfirm: handleSlotConfirm, timezone, loading }, slot.start))
2022
- ] }),
2023
- (groupedSlots.afternoon.length > 0 || groupedSlots.evening.length > 0) && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "bw-times-scroll-hint", children: "Scroll for more times" })
2024
- ] }) })
2025
- ] })
2026
- ] }),
2027
- step === "form" && selectedType && selectedSlot && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-step bw-fade-in", children: [
2028
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-info-header", children: [
2029
- /* @__PURE__ */ jsxRuntime.jsx("button", { className: "bw-back", onClick: () => setStep("datetime"), children: /* @__PURE__ */ jsxRuntime.jsx(ArrowLeftIcon, {}) }),
2030
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2031
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "bw-heading", children: "Your Details" }),
2032
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-meta-row", children: [
2033
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "bw-badge", children: [
2034
- /* @__PURE__ */ jsxRuntime.jsx(ClockIcon, {}),
2035
- " ",
2036
- formatDuration(selectedType.duration_minutes)
2037
- ] }),
2038
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "bw-badge-accent", children: [
2039
- formatDate2(selectedSlot.start, timezone),
2040
- " \xB7 ",
2041
- formatTime(selectedSlot.start, timezone)
2042
- ] })
2043
- ] })
2044
- ] })
2045
- ] }),
2046
- hold && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-hold-notice", children: [
2047
- /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}),
2048
- " Slot held for you \u2014 complete your details to confirm."
2049
- ] }),
2050
- /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleBookingSubmit, className: "bw-form", children: [
2051
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-field", children: [
2052
- /* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: "bw-name", children: [
2053
- "Name ",
2054
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-req", children: "*" })
2055
- ] }),
2056
- /* @__PURE__ */ jsxRuntime.jsx(
2057
- "input",
2058
- {
2059
- id: "bw-name",
2060
- type: "text",
2061
- required: true,
2062
- value: guestInfo.name,
2063
- onChange: (e) => setGuestInfo((p) => ({ ...p, name: e.target.value })),
2064
- placeholder: "Jane Smith",
2065
- autoComplete: "name"
2066
- }
2067
- )
2068
- ] }),
2069
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-field", children: [
2070
- /* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: "bw-email", children: [
2071
- "Email ",
2072
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-req", children: "*" })
2073
- ] }),
2074
- /* @__PURE__ */ jsxRuntime.jsx(
2075
- "input",
2076
- {
2077
- id: "bw-email",
2078
- type: "email",
2079
- required: true,
2080
- value: guestInfo.email,
2081
- onChange: (e) => setGuestInfo((p) => ({ ...p, email: e.target.value })),
2082
- placeholder: "jane@example.com",
2083
- autoComplete: "email"
2084
- }
2085
- )
2086
- ] }),
2087
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-field", children: [
2088
- /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "bw-phone", children: "Phone" }),
2089
- /* @__PURE__ */ jsxRuntime.jsx(
2090
- "input",
2091
- {
2092
- id: "bw-phone",
2093
- type: "tel",
2094
- value: guestInfo.phone || "",
2095
- onChange: (e) => setGuestInfo((p) => ({ ...p, phone: e.target.value })),
2096
- placeholder: "(555) 123-4567",
2097
- autoComplete: "tel"
2098
- }
2099
- )
2100
- ] }),
2101
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-field", children: [
2102
- /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "bw-notes", children: "Notes" }),
2103
- /* @__PURE__ */ jsxRuntime.jsx(
2104
- "textarea",
2105
- {
2106
- id: "bw-notes",
2107
- value: guestInfo.notes || "",
2108
- onChange: (e) => setGuestInfo((p) => ({ ...p, notes: e.target.value })),
2109
- placeholder: "Anything you'd like us to know...",
2110
- rows: 3
2111
- }
2112
- )
2113
- ] }),
2114
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", className: "bw-submit", disabled: submitting || !guestInfo.name || !guestInfo.email, children: submitting ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2115
- /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}),
2116
- " Confirming..."
2117
- ] }) : "Confirm Booking" })
2118
- ] })
2119
- ] }),
2120
- step === "success" && bookingResult && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-step bw-fade-in bw-success", children: [
2121
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bw-success-icon", children: /* @__PURE__ */ jsxRuntime.jsx(CheckCircleIcon, {}) }),
2122
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "bw-heading", children: "You\u2019re Booked!" }),
2123
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-details-card", children: [
2124
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-detail-row", children: [
2125
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-detail-label", children: "When" }),
2126
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "bw-detail-value", children: [
2127
- formatDate2(bookingResult.booking.scheduledAt, timezone),
2128
- /* @__PURE__ */ jsxRuntime.jsx("br", {}),
2129
- formatTime(bookingResult.booking.scheduledAt, timezone),
2130
- " (",
2131
- shortTz,
2132
- ")"
2133
- ] })
2134
- ] }),
2135
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-detail-row", children: [
2136
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-detail-label", children: "Duration" }),
2137
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-detail-value", children: formatDuration(bookingResult.booking.durationMinutes) })
2138
- ] }),
2139
- bookingResult.booking.hostName && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-detail-row", children: [
2140
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-detail-label", children: "With" }),
2141
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-detail-value", children: bookingResult.booking.hostName })
2142
- ] })
2143
- ] }),
2144
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "bw-cal-links-label", children: "Add to your calendar" }),
2145
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-cal-links", children: [
2146
- /* @__PURE__ */ jsxRuntime.jsxs("a", { href: bookingResult.calendarLinks.google, target: "_blank", rel: "noopener noreferrer", className: "bw-cal-link", children: [
2147
- /* @__PURE__ */ jsxRuntime.jsx(CalendarPlusIcon, {}),
2148
- " Google"
2149
- ] }),
2150
- /* @__PURE__ */ jsxRuntime.jsxs("a", { href: bookingResult.calendarLinks.outlook, target: "_blank", rel: "noopener noreferrer", className: "bw-cal-link", children: [
2151
- /* @__PURE__ */ jsxRuntime.jsx(CalendarPlusIcon, {}),
2152
- " Outlook"
2153
- ] }),
2154
- /* @__PURE__ */ jsxRuntime.jsxs("a", { href: bookingResult.calendarLinks.ics, download: true, className: "bw-cal-link", children: [
2155
- /* @__PURE__ */ jsxRuntime.jsx(CalendarPlusIcon, {}),
2156
- " iCal"
2157
- ] })
2158
- ] }),
2159
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "bw-email-notice", children: [
2160
- "If your host uses Google Calendar, a calendar invite will be sent to ",
2161
- /* @__PURE__ */ jsxRuntime.jsx("strong", { children: guestInfo.email }),
2162
- "."
2163
- ] })
2164
- ] }),
2165
- loading && step === "datetime" && !selectedType && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-loading", children: [
2166
- /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}),
2167
- " Loading..."
2168
- ] }),
2169
- /* @__PURE__ */ jsxRuntime.jsx("style", { children: WIDGET_CSS })
2170
- ] });
2171
- }
2172
- function TimeButton({
2173
- slot,
2174
- selected,
2175
- confirmed,
2176
- onClick,
2177
- onConfirm,
2178
- timezone,
2179
- loading
2180
- }) {
2181
- if (selected && !confirmed) {
2182
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-time-btn-wrap selected", children: [
2183
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-time-text", children: formatTime(slot.start, timezone) }),
2184
- /* @__PURE__ */ jsxRuntime.jsx("button", { className: "bw-time-confirm", onClick: onConfirm, disabled: loading, children: loading ? /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}) : "Confirm" })
2185
- ] });
2186
- }
2187
- return /* @__PURE__ */ jsxRuntime.jsx("button", { className: `bw-time-btn${selected ? " selected" : ""}`, onClick, disabled: loading, children: formatTime(slot.start, timezone) });
2188
- }
2189
- var WIDGET_CSS = `
2190
- /* \u2500\u2500 Base \u2500\u2500 */
2191
- .bw-root {
2192
- font-family: var(--bw-font);
2193
- color: #1a1a1a;
2194
- line-height: 1.5;
2195
- -webkit-font-smoothing: antialiased;
2196
- box-sizing: border-box;
2197
- }
2198
- .bw-root *, .bw-root *::before, .bw-root *::after { box-sizing: border-box; }
2199
-
2200
- /* \u2500\u2500 Animation \u2500\u2500 */
2201
- @keyframes bw-fade-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
2202
- @keyframes bw-spin { to { transform: rotate(360deg); } }
2203
- .bw-fade-in { animation: bw-fade-in 0.25s ease-out; }
2204
- .bw-spinner { animation: bw-spin 0.8s linear infinite; }
2205
-
2206
- /* \u2500\u2500 Error \u2500\u2500 */
2207
- .bw-error {
2208
- display: flex; align-items: center; justify-content: space-between; gap: 12px;
2209
- background: #fef2f2; border: 1px solid #fecaca; color: #b91c1c;
2210
- padding: 10px 14px; border-radius: var(--bw-radius); margin-bottom: 16px; font-size: 0.875rem;
2211
- }
2212
- .bw-error button { background: none; border: none; color: inherit; cursor: pointer; font-size: 1.25rem; line-height: 1; padding: 0; }
2213
-
2214
- /* \u2500\u2500 Headings \u2500\u2500 */
2215
- .bw-heading { font-size: 1.125rem; font-weight: 600; margin: 0; letter-spacing: -0.01em; }
2216
- .bw-meta-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
2217
- .bw-badge {
2218
- display: inline-flex; align-items: center; gap: 4px;
2219
- font-size: 0.8125rem; color: #6b7280; font-weight: 400;
2220
- }
2221
- .bw-badge-accent {
2222
- display: inline-flex; align-items: center; gap: 4px;
2223
- font-size: 0.8125rem; color: var(--bw-primary); font-weight: 500;
2224
- }
2225
-
2226
- /* \u2500\u2500 Info Header \u2500\u2500 */
2227
- .bw-info-header { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 20px; }
2228
- .bw-back {
2229
- display: inline-flex; align-items: center; justify-content: center;
2230
- width: 32px; height: 32px; border-radius: 50%; border: 1px solid #e5e7eb;
2231
- background: #fff; cursor: pointer; color: #4b5563; flex-shrink: 0; margin-top: 1px;
2232
- transition: all 0.15s;
2233
- }
2234
- .bw-back:hover { background: #f3f4f6; border-color: #d1d5db; }
2235
-
2236
- /* \u2500\u2500 Loading \u2500\u2500 */
2237
- .bw-loading {
2238
- display: flex; align-items: center; justify-content: center; gap: 8px;
2239
- padding: 32px; color: #6b7280; font-size: 0.875rem;
2240
- }
2241
-
2242
- /* \u2500\u2500 Type Selection \u2500\u2500 */
2243
- .bw-type-list { display: flex; flex-direction: column; gap: 10px; }
2244
- .bw-type-card {
2245
- display: flex; flex-direction: column; text-align: left; gap: 4px;
2246
- padding: 16px; border: 1.5px solid #e5e7eb; border-radius: var(--bw-radius);
2247
- background: #fff; cursor: pointer; transition: all 0.15s;
2248
- }
2249
- .bw-type-card:hover { border-color: var(--bw-primary); box-shadow: 0 0 0 3px var(--bw-primary-light); }
2250
- .bw-type-name { font-weight: 600; font-size: 0.9375rem; }
2251
- .bw-type-desc { color: #6b7280; font-size: 0.8125rem; }
2252
- .bw-type-meta { display: flex; align-items: center; gap: 4px; font-size: 0.8125rem; color: #9ca3af; margin-top: 4px; }
2253
-
2254
- /* \u2500\u2500 Date-Time Layout \u2500\u2500 */
2255
- .bw-datetime-layout {
2256
- display: flex; gap: 0; border-top: 1px solid #f0f0f0; padding-top: 16px;
2257
- }
2258
- @media (max-width: 559px) {
2259
- .bw-datetime-layout { flex-direction: column; }
2260
- }
2261
- @media (min-width: 560px) {
2262
- .bw-datetime-layout { min-height: 400px; }
2263
- .bw-calendar { flex: 1 1 auto; padding-right: 16px; border-right: 1px solid #f0f0f0; }
2264
- .bw-times { width: 180px; flex-shrink: 0; padding-left: 16px; }
2265
- }
2266
-
2267
- /* \u2500\u2500 Calendar \u2500\u2500 */
2268
- .bw-cal-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
2269
- .bw-cal-title { font-weight: 600; font-size: 0.9375rem; }
2270
- .bw-cal-nav {
2271
- display: inline-flex; align-items: center; justify-content: center;
2272
- width: 32px; height: 32px; border-radius: 50%; border: none;
2273
- background: transparent; cursor: pointer; color: #374151; transition: background 0.15s;
2274
- }
2275
- .bw-cal-nav:hover:not(:disabled) { background: #f3f4f6; }
2276
- .bw-cal-nav:disabled { opacity: 0.25; cursor: default; }
2277
-
2278
- .bw-cal-weekdays { display: grid; grid-template-columns: repeat(7, 1fr); gap: 0; text-align: center; margin-bottom: 4px; }
2279
- .bw-cal-wd { font-size: 0.6875rem; font-weight: 600; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.04em; padding: 4px 0; }
2280
-
2281
- .bw-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
2282
- .bw-cal-empty { aspect-ratio: 1; }
2283
- .bw-cal-day {
2284
- aspect-ratio: 1; display: flex; align-items: center; justify-content: center;
2285
- border: none; background: none; border-radius: 50%;
2286
- font-size: 0.8125rem; font-weight: 500; cursor: pointer;
2287
- color: #1a1a1a; transition: all 0.15s; position: relative;
2288
- }
2289
- .bw-cal-day:hover:not(.disabled):not(.selected) { background: #f3f4f6; }
2290
- .bw-cal-day.today:not(.selected)::after {
2291
- content: ''; position: absolute; bottom: 3px; left: 50%; transform: translateX(-50%);
2292
- width: 4px; height: 4px; border-radius: 50%; background: var(--bw-primary);
2293
- }
2294
- .bw-cal-day.selected {
2295
- background: var(--bw-primary); color: #fff; font-weight: 600;
2296
- }
2297
- .bw-cal-day.disabled { color: #d1d5db; cursor: default; }
2298
-
2299
- /* \u2500\u2500 Time Slots \u2500\u2500 */
2300
- .bw-times { transition: opacity 0.2s; }
2301
- .bw-times:not(.visible) { opacity: 0.5; }
2302
- .bw-times.visible { opacity: 1; }
2303
- .bw-times-placeholder {
2304
- display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
2305
- height: 100%; min-height: 160px; color: #9ca3af; font-size: 0.8125rem; text-align: center; padding: 16px;
2306
- }
2307
- .bw-times-empty {
2308
- display: flex; align-items: center; justify-content: center;
2309
- height: 100%; min-height: 160px; color: #9ca3af; font-size: 0.8125rem; text-align: center; padding: 16px;
2310
- }
2311
- .bw-times-scroll { overflow-y: auto; max-height: 400px; overflow-x: hidden; }
2312
- .bw-times-date { font-weight: 600; font-size: 0.8125rem; color: #374151; margin: 0 0 12px 0; }
2313
- .bw-time-group { margin-bottom: 16px; }
2314
- .bw-times-scroll-hint {
2315
- font-size: 0.6875rem; color: #9ca3af; text-align: center; margin: 8px 0 0 0; padding-bottom: 8px;
2316
- }
2317
- .bw-time-label { display: block; font-size: 0.6875rem; font-weight: 600; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 6px; }
2318
-
2319
- .bw-time-btn {
2320
- display: block; width: 100%;
2321
- padding: 10px 12px; margin-bottom: 6px;
2322
- border: 1.5px solid #e5e7eb; border-radius: var(--bw-radius);
2323
- background: #fff; cursor: pointer;
2324
- font-size: 0.875rem; font-weight: 500; color: var(--bw-primary); text-align: center;
2325
- transition: all 0.15s;
2326
- }
2327
- .bw-time-btn:hover:not(:disabled) {
2328
- border-color: var(--bw-primary); background: var(--bw-primary-light);
2329
- }
2330
- .bw-time-btn.selected {
2331
- border-color: var(--bw-primary); background: var(--bw-primary); color: #fff;
2332
- }
2333
- .bw-time-btn:disabled { opacity: 0.5; cursor: not-allowed; }
2334
-
2335
- .bw-time-btn-wrap {
2336
- display: flex; align-items: center; gap: 6px; margin-bottom: 6px;
2337
- border: 1.5px solid var(--bw-primary); border-radius: var(--bw-radius); overflow: hidden;
2338
- animation: bw-fade-in 0.15s ease-out;
2339
- }
2340
- .bw-time-btn-wrap .bw-time-text {
2341
- flex: 1; padding: 10px 12px; font-size: 0.875rem; font-weight: 500; color: #374151; text-align: center;
2342
- background: var(--bw-primary-light);
2343
- }
2344
- .bw-time-confirm {
2345
- display: inline-flex; align-items: center; justify-content: center; gap: 4px;
2346
- padding: 10px 16px; border: none;
2347
- background: var(--bw-primary); color: #fff;
2348
- font-size: 0.8125rem; font-weight: 600; cursor: pointer;
2349
- transition: background 0.15s;
2350
- }
2351
- .bw-time-confirm:hover:not(:disabled) { background: var(--bw-primary-hover); }
2352
- .bw-time-confirm:disabled { opacity: 0.7; cursor: not-allowed; }
2353
-
2354
- /* \u2500\u2500 Form \u2500\u2500 */
2355
- .bw-hold-notice {
2356
- display: flex; align-items: center; gap: 8px;
2357
- background: var(--bw-primary-light); color: var(--bw-primary);
2358
- padding: 10px 14px; border-radius: var(--bw-radius);
2359
- font-size: 0.8125rem; font-weight: 500; margin-bottom: 20px;
2360
- }
2361
- .bw-form { display: flex; flex-direction: column; gap: 16px; }
2362
- .bw-field label {
2363
- display: block; font-size: 0.8125rem; font-weight: 500; color: #374151; margin-bottom: 4px;
2364
- }
2365
- .bw-req { color: #ef4444; }
2366
- .bw-field input, .bw-field textarea {
2367
- display: block; width: 100%;
2368
- padding: 10px 12px; border: 1.5px solid #e5e7eb; border-radius: var(--bw-radius);
2369
- font-size: 0.9375rem; font-family: inherit; color: #1a1a1a; background: #fff;
2370
- transition: border-color 0.15s, box-shadow 0.15s;
2371
- }
2372
- .bw-field input::placeholder, .bw-field textarea::placeholder { color: #c0c5cc; }
2373
- .bw-field input:focus, .bw-field textarea:focus {
2374
- outline: none; border-color: var(--bw-primary);
2375
- box-shadow: 0 0 0 3px var(--bw-primary-light);
2376
- }
2377
- .bw-submit {
2378
- display: inline-flex; align-items: center; justify-content: center; gap: 8px;
2379
- width: 100%; padding: 14px; margin-top: 4px;
2380
- background: var(--bw-primary); color: #fff; border: none;
2381
- border-radius: var(--bw-radius); font-size: 0.9375rem; font-weight: 600;
2382
- cursor: pointer; transition: background 0.15s;
2383
- }
2384
- .bw-submit:hover:not(:disabled) { background: var(--bw-primary-hover); }
2385
- .bw-submit:disabled { opacity: 0.55; cursor: not-allowed; }
2386
-
2387
- /* \u2500\u2500 Success \u2500\u2500 */
2388
- .bw-success { text-align: center; }
2389
- .bw-success-icon { margin: 0 auto 16px; width: 56px; height: 56px; animation: bw-fade-in 0.4s ease-out; }
2390
- .bw-conf-code {
2391
- display: inline-block; font-family: 'SF Mono', 'Fira Code', monospace;
2392
- background: #f3f4f6; padding: 6px 14px; border-radius: 6px;
2393
- font-size: 0.8125rem; color: #6b7280; margin: 4px 0 20px; letter-spacing: 0.04em;
2394
- }
2395
-
2396
- .bw-details-card {
2397
- text-align: left; background: #f9fafb; border: 1px solid #f0f0f0;
2398
- border-radius: var(--bw-radius); padding: 16px; margin-bottom: 20px;
2399
- }
2400
- .bw-detail-row { display: flex; gap: 12px; padding: 8px 0; border-bottom: 1px solid #f0f0f0; }
2401
- .bw-detail-row:last-child { border-bottom: none; }
2402
- .bw-detail-label { width: 70px; flex-shrink: 0; font-size: 0.8125rem; color: #9ca3af; font-weight: 500; }
2403
- .bw-detail-value { font-size: 0.875rem; font-weight: 500; color: #374151; }
2404
-
2405
- .bw-cal-links-label { font-size: 0.8125rem; color: #6b7280; margin: 0 0 8px 0; }
2406
- .bw-cal-links { display: flex; gap: 8px; justify-content: center; margin-bottom: 20px; flex-wrap: wrap; }
2407
- .bw-cal-link {
2408
- display: inline-flex; align-items: center; gap: 6px;
2409
- padding: 8px 14px; border: 1.5px solid #e5e7eb; border-radius: var(--bw-radius);
2410
- text-decoration: none; color: #374151; font-size: 0.8125rem; font-weight: 500;
2411
- transition: all 0.15s;
2412
- }
2413
- .bw-cal-link:hover { border-color: var(--bw-primary); color: var(--bw-primary); }
2414
-
2415
- .bw-email-notice { font-size: 0.8125rem; color: #9ca3af; margin: 0; }
2416
- .bw-email-notice strong { color: #374151; font-weight: 500; }
2417
- `;
2418
-
2419
1505
  Object.defineProperty(exports, "SignalBridge", {
2420
1506
  enumerable: true,
2421
1507
  get: function () { return chunkYBTYDBWY_js.SignalBridge; }
@@ -2440,6 +1526,22 @@ Object.defineProperty(exports, "useSignalOutcome", {
2440
1526
  enumerable: true,
2441
1527
  get: function () { return chunkYBTYDBWY_js.useSignalOutcome; }
2442
1528
  });
1529
+ Object.defineProperty(exports, "clearRedirectCache", {
1530
+ enumerable: true,
1531
+ get: function () { return chunkX4J33XQD_js.clearRedirectCache; }
1532
+ });
1533
+ Object.defineProperty(exports, "fetchRedirectRules", {
1534
+ enumerable: true,
1535
+ get: function () { return chunkX4J33XQD_js.fetchRedirectRules; }
1536
+ });
1537
+ Object.defineProperty(exports, "generateNextRedirects", {
1538
+ enumerable: true,
1539
+ get: function () { return chunkX4J33XQD_js.generateNextRedirects; }
1540
+ });
1541
+ Object.defineProperty(exports, "handleManagedRedirects", {
1542
+ enumerable: true,
1543
+ get: function () { return chunkX4J33XQD_js.handleManagedRedirects; }
1544
+ });
2443
1545
  Object.defineProperty(exports, "ManagedImage", {
2444
1546
  enumerable: true,
2445
1547
  get: function () { return chunkG6VGUAK2_js.ManagedImage; }
@@ -2576,46 +1678,66 @@ Object.defineProperty(exports, "useEventModal", {
2576
1678
  enumerable: true,
2577
1679
  get: function () { return chunkTLHRV3LZ_js.useEventModal; }
2578
1680
  });
2579
- Object.defineProperty(exports, "SetupWizard", {
1681
+ Object.defineProperty(exports, "BookingWidget", {
2580
1682
  enumerable: true,
2581
- get: function () { return chunkIFAW7JFO_js.SetupWizard; }
1683
+ get: function () { return chunkJB6I5PYV_js.BookingWidget; }
2582
1684
  });
2583
- Object.defineProperty(exports, "clearRedirectCache", {
1685
+ Object.defineProperty(exports, "createBooking", {
2584
1686
  enumerable: true,
2585
- get: function () { return chunkX4J33XQD_js.clearRedirectCache; }
1687
+ get: function () { return chunkJB6I5PYV_js.createBooking; }
2586
1688
  });
2587
- Object.defineProperty(exports, "fetchRedirectRules", {
1689
+ Object.defineProperty(exports, "createSlotHold", {
2588
1690
  enumerable: true,
2589
- get: function () { return chunkX4J33XQD_js.fetchRedirectRules; }
1691
+ get: function () { return chunkJB6I5PYV_js.createSlotHold; }
2590
1692
  });
2591
- Object.defineProperty(exports, "generateNextRedirects", {
1693
+ Object.defineProperty(exports, "detectTimezone", {
2592
1694
  enumerable: true,
2593
- get: function () { return chunkX4J33XQD_js.generateNextRedirects; }
1695
+ get: function () { return chunkJB6I5PYV_js.detectTimezone; }
2594
1696
  });
2595
- Object.defineProperty(exports, "handleManagedRedirects", {
1697
+ Object.defineProperty(exports, "fetchAvailability", {
2596
1698
  enumerable: true,
2597
- get: function () { return chunkX4J33XQD_js.handleManagedRedirects; }
1699
+ get: function () { return chunkJB6I5PYV_js.fetchAvailability; }
1700
+ });
1701
+ Object.defineProperty(exports, "fetchAvailableDates", {
1702
+ enumerable: true,
1703
+ get: function () { return chunkJB6I5PYV_js.fetchAvailableDates; }
1704
+ });
1705
+ Object.defineProperty(exports, "fetchBookingTypeDetails", {
1706
+ enumerable: true,
1707
+ get: function () { return chunkJB6I5PYV_js.fetchBookingTypeDetails; }
1708
+ });
1709
+ Object.defineProperty(exports, "fetchBookingTypes", {
1710
+ enumerable: true,
1711
+ get: function () { return chunkJB6I5PYV_js.fetchBookingTypes; }
1712
+ });
1713
+ Object.defineProperty(exports, "formatBookingDate", {
1714
+ enumerable: true,
1715
+ get: function () { return chunkJB6I5PYV_js.formatDate; }
1716
+ });
1717
+ Object.defineProperty(exports, "formatBookingTime", {
1718
+ enumerable: true,
1719
+ get: function () { return chunkJB6I5PYV_js.formatTime; }
1720
+ });
1721
+ Object.defineProperty(exports, "formatDuration", {
1722
+ enumerable: true,
1723
+ get: function () { return chunkJB6I5PYV_js.formatDuration; }
1724
+ });
1725
+ Object.defineProperty(exports, "releaseSlotHold", {
1726
+ enumerable: true,
1727
+ get: function () { return chunkJB6I5PYV_js.releaseSlotHold; }
1728
+ });
1729
+ Object.defineProperty(exports, "SetupWizard", {
1730
+ enumerable: true,
1731
+ get: function () { return chunkIFAW7JFO_js.SetupWizard; }
2598
1732
  });
2599
1733
  exports.AffiliateCard = AffiliateCard;
2600
1734
  exports.AffiliatesWidget = AffiliatesWidget;
2601
- exports.BookingWidget = BookingWidget;
2602
1735
  exports.ExperimentConversion = ExperimentConversion;
2603
1736
  exports.SetupAssistant = SetupAssistant;
2604
1737
  exports.SignalExperiment = SignalExperiment;
2605
1738
  exports.SiteKitProvider = SiteKitProvider;
2606
- exports.createBooking = createBooking;
2607
- exports.createSlotHold = createSlotHold;
2608
- exports.detectTimezone = detectTimezone;
2609
1739
  exports.fetchAffiliates = fetchAffiliates;
2610
- exports.fetchAvailability = fetchAvailability;
2611
- exports.fetchAvailableDates = fetchAvailableDates;
2612
- exports.fetchBookingTypeDetails = fetchBookingTypeDetails;
2613
- exports.fetchBookingTypes = fetchBookingTypes;
2614
- exports.formatBookingDate = formatDate2;
2615
- exports.formatBookingTime = formatTime;
2616
- exports.formatDuration = formatDuration;
2617
1740
  exports.getTrackingUrl = getTrackingUrl;
2618
- exports.releaseSlotHold = releaseSlotHold;
2619
1741
  exports.useAffiliates = useAffiliates;
2620
1742
  exports.useExperimentVariant = useExperimentVariant;
2621
1743
  exports.useSiteKit = useSiteKit;