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