@idkwebsites/components 0.1.17 → 0.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  "use client";
3
+ var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
4
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
6
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
9
  var __export = (target, all) => {
8
10
  for (var name in all)
@@ -16,6 +18,14 @@ var __copyProps = (to, from, except, desc) => {
16
18
  }
17
19
  return to;
18
20
  };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
19
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
30
 
21
31
  // src/index.ts
@@ -23,6 +33,7 @@ var src_exports = {};
23
33
  __export(src_exports, {
24
34
  AvailabilityPicker: () => AvailabilityPicker,
25
35
  BookingWidget: () => BookingWidget,
36
+ BookingWidgetPanel: () => BookingWidgetPanel,
26
37
  ContactForm: () => ContactForm,
27
38
  DatePicker: () => DatePicker,
28
39
  PlatformProvider: () => PlatformProvider,
@@ -1711,9 +1722,876 @@ function BookingWidget({
1711
1722
  ] });
1712
1723
  }
1713
1724
 
1714
- // src/components/AvailabilityPicker.tsx
1725
+ // src/components/BookingWidgetPanel.tsx
1715
1726
  var import_react9 = require("react");
1727
+ var import_react_dom = require("react-dom");
1728
+ var import_lucide_react = require("lucide-react");
1729
+ var import_framer_motion = require("framer-motion");
1730
+ var import_smooth_scrollbar = __toESM(require("smooth-scrollbar"), 1);
1731
+ var import_overscroll = __toESM(require("smooth-scrollbar/plugins/overscroll"), 1);
1716
1732
  var import_jsx_runtime9 = require("react/jsx-runtime");
1733
+ import_smooth_scrollbar.default.use(import_overscroll.default);
1734
+ var DEFAULT_STEP_TITLES = [
1735
+ "Schedule your visit",
1736
+ "Select your service",
1737
+ "Pick a date & time",
1738
+ "Your details"
1739
+ ];
1740
+ var DEFAULT_CATEGORY_ICONS = {
1741
+ hair: import_lucide_react.Scissors,
1742
+ cut: import_lucide_react.Scissors,
1743
+ barber: import_lucide_react.Scissors,
1744
+ styling: import_lucide_react.Scissors,
1745
+ skin: import_lucide_react.Sparkles,
1746
+ facial: import_lucide_react.Sparkles,
1747
+ beauty: import_lucide_react.Sparkles,
1748
+ massage: import_lucide_react.Heart,
1749
+ wellness: import_lucide_react.Heart,
1750
+ spa: import_lucide_react.Heart,
1751
+ nail: import_lucide_react.Palette,
1752
+ color: import_lucide_react.Palette,
1753
+ manicure: import_lucide_react.Palette,
1754
+ pedicure: import_lucide_react.Palette
1755
+ };
1756
+ function getCategoryIcon(name, overrides) {
1757
+ const map = { ...DEFAULT_CATEGORY_ICONS, ...overrides };
1758
+ const lower = name.toLowerCase();
1759
+ for (const [kw, Icon] of Object.entries(map)) {
1760
+ if (lower.includes(kw)) return Icon;
1761
+ }
1762
+ return import_lucide_react.CircleDot;
1763
+ }
1764
+ function getInitials(name) {
1765
+ return name.split(" ").map((w) => w[0]).join("").slice(0, 2).toUpperCase();
1766
+ }
1767
+ function truncate(str, max) {
1768
+ return str.length > max ? str.slice(0, max) + "\u2026" : str;
1769
+ }
1770
+ function DefaultMenuIcon() {
1771
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.Menu, { size: 20 });
1772
+ }
1773
+ function DefaultCloseIcon() {
1774
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.X, { size: 20 });
1775
+ }
1776
+ function DefaultLink({ href, className, onClick, children }) {
1777
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("a", { href, className, onClick, children });
1778
+ }
1779
+ function BookingWidgetPanel({
1780
+ navLinks = [],
1781
+ menuIcon,
1782
+ closeIcon,
1783
+ categoryIcons,
1784
+ stepTitles,
1785
+ linkComponent: LinkComp = DefaultLink
1786
+ } = {}) {
1787
+ const widgetRef = (0, import_react9.useRef)(null);
1788
+ const formRef = (0, import_react9.useRef)(null);
1789
+ const catScrollRef = (0, import_react9.useRef)(null);
1790
+ const svcScrollbarRef = (0, import_react9.useRef)(null);
1791
+ const rightScrollRef = (0, import_react9.useRef)(null);
1792
+ const staffTriggerRef = (0, import_react9.useRef)(null);
1793
+ const mobileBodyRef = (0, import_react9.useRef)(null);
1794
+ const mobileScrollbarRef = (0, import_react9.useRef)(null);
1795
+ const portalRef = (0, import_react9.useRef)(null);
1796
+ const [mobileStep, setMobileStep] = (0, import_react9.useState)(1);
1797
+ const [monthOpen, setMonthOpen] = (0, import_react9.useState)(false);
1798
+ const [staffOpen, setStaffOpen] = (0, import_react9.useState)(false);
1799
+ const [navOpen, setNavOpen] = (0, import_react9.useState)(false);
1800
+ const [localStaff, setLocalStaff] = (0, import_react9.useState)(null);
1801
+ const { data, isLoading: servicesLoading, error: servicesError } = useServices();
1802
+ const { data: teamData } = useTeam();
1803
+ const allStaff = teamData?.team || [];
1804
+ const bw = useBookingWidget({
1805
+ widgetRef,
1806
+ services: data?.services,
1807
+ autoAdvanceOnSelect: false,
1808
+ scrollToStep: false,
1809
+ datePickerMode: "calendar",
1810
+ calendarVariant: "default",
1811
+ enableCategoryFilter: true,
1812
+ enableServiceSearch: false,
1813
+ showStaffSelection: true,
1814
+ showAnyStaffOption: true,
1815
+ maxAdvanceMonths: 3,
1816
+ servicePageSize: 50
1817
+ });
1818
+ const todayStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1819
+ (0, import_react9.useEffect)(() => {
1820
+ if (bw.isAvailabilityLoading || !bw.selectedService || !bw.availabilityByDate) return;
1821
+ if (bw.selectedDate) return;
1822
+ for (const day of bw.calendarDays) {
1823
+ if (!day.isCurrentMonth || day.date < todayStr) continue;
1824
+ const entry = bw.availabilityByDate.get(day.date);
1825
+ if (entry && entry.slots?.some((s) => s.available)) {
1826
+ bw.selectDate(day.date);
1827
+ break;
1828
+ }
1829
+ }
1830
+ }, [bw.isAvailabilityLoading, bw.selectedService?.id, bw.availabilityByDate, bw.selectedDate]);
1831
+ const hasCategories = bw.categories.length >= 1;
1832
+ const hasMultipleCategories = bw.categories.length > 1;
1833
+ (0, import_react9.useEffect)(() => {
1834
+ if (hasCategories && bw.categoryFilter === "all") {
1835
+ bw.setCategoryFilter(bw.categories[0]);
1836
+ }
1837
+ }, [bw.categories.length]);
1838
+ (0, import_react9.useEffect)(() => {
1839
+ if (localStaff && !bw.selectedStaff) {
1840
+ bw.selectStaff(localStaff);
1841
+ }
1842
+ }, [bw.selectedStaff, localStaff]);
1843
+ const isReady = !servicesLoading && !bw.isServicesLoading && !!data?.services?.length && bw.step !== "done";
1844
+ (0, import_react9.useEffect)(() => {
1845
+ if (!isReady) return;
1846
+ const mq = window.matchMedia("(min-width: 1025px)");
1847
+ const scrollbars = [];
1848
+ const opts = {
1849
+ damping: 0.08,
1850
+ continuousScrolling: true,
1851
+ alwaysShowTracks: false,
1852
+ plugins: {
1853
+ overscroll: { effect: "bounce", damping: 0.15, maxOverscroll: 80 }
1854
+ }
1855
+ };
1856
+ const init = () => {
1857
+ scrollbars.forEach((sb) => sb.destroy());
1858
+ scrollbars.length = 0;
1859
+ if (mq.matches && catScrollRef.current) {
1860
+ scrollbars.push(import_smooth_scrollbar.default.init(catScrollRef.current, opts));
1861
+ }
1862
+ if (mq.matches && rightScrollRef.current) {
1863
+ scrollbars.push(import_smooth_scrollbar.default.init(rightScrollRef.current, opts));
1864
+ }
1865
+ };
1866
+ requestAnimationFrame(init);
1867
+ mq.addEventListener("change", init);
1868
+ return () => {
1869
+ mq.removeEventListener("change", init);
1870
+ scrollbars.forEach((sb) => sb.destroy());
1871
+ };
1872
+ }, [isReady, mobileStep]);
1873
+ const initSvcScrollbar = (el) => {
1874
+ if (!el || typeof window !== "undefined" && window.matchMedia("(max-width: 1024px)").matches) {
1875
+ if (svcScrollbarRef.current) {
1876
+ try {
1877
+ svcScrollbarRef.current.destroy();
1878
+ } catch {
1879
+ }
1880
+ svcScrollbarRef.current = null;
1881
+ }
1882
+ return;
1883
+ }
1884
+ if (svcScrollbarRef.current && svcScrollbarRef.current.containerEl === el) {
1885
+ svcScrollbarRef.current.update();
1886
+ return;
1887
+ }
1888
+ if (svcScrollbarRef.current) {
1889
+ try {
1890
+ svcScrollbarRef.current.destroy();
1891
+ } catch {
1892
+ }
1893
+ svcScrollbarRef.current = null;
1894
+ }
1895
+ svcScrollbarRef.current = import_smooth_scrollbar.default.init(el, {
1896
+ damping: 0.08,
1897
+ continuousScrolling: true,
1898
+ alwaysShowTracks: false,
1899
+ plugins: {
1900
+ overscroll: { effect: "bounce", damping: 0.15, maxOverscroll: 80 }
1901
+ }
1902
+ });
1903
+ };
1904
+ (0, import_react9.useEffect)(() => {
1905
+ if (!isReady) return;
1906
+ const mq = window.matchMedia("(max-width: 1024px)");
1907
+ const init = () => {
1908
+ if (mobileScrollbarRef.current) {
1909
+ try {
1910
+ mobileScrollbarRef.current.destroy();
1911
+ } catch {
1912
+ }
1913
+ mobileScrollbarRef.current = null;
1914
+ }
1915
+ if (mq.matches && mobileBodyRef.current) {
1916
+ mobileScrollbarRef.current = import_smooth_scrollbar.default.init(mobileBodyRef.current, {
1917
+ damping: 0.08,
1918
+ continuousScrolling: false,
1919
+ alwaysShowTracks: false,
1920
+ plugins: {
1921
+ overscroll: { effect: "bounce", damping: 0.15, maxOverscroll: 80 }
1922
+ }
1923
+ });
1924
+ }
1925
+ };
1926
+ requestAnimationFrame(init);
1927
+ mq.addEventListener("change", init);
1928
+ return () => {
1929
+ mq.removeEventListener("change", init);
1930
+ if (mobileScrollbarRef.current) {
1931
+ try {
1932
+ mobileScrollbarRef.current.destroy();
1933
+ } catch {
1934
+ }
1935
+ mobileScrollbarRef.current = null;
1936
+ }
1937
+ };
1938
+ }, [isReady]);
1939
+ (0, import_react9.useEffect)(() => {
1940
+ if (mobileScrollbarRef.current) {
1941
+ mobileScrollbarRef.current.scrollTo(0, 0, 0);
1942
+ mobileScrollbarRef.current.update();
1943
+ }
1944
+ }, [mobileStep]);
1945
+ const formatPrice = (cents) => {
1946
+ const d = cents / 100;
1947
+ return d % 1 === 0 ? `$${d.toFixed(0)}` : `$${d.toFixed(2)}`;
1948
+ };
1949
+ const handleFormSubmit = (e) => {
1950
+ e.preventDefault();
1951
+ bw.submitBooking(e);
1952
+ };
1953
+ const canAdvanceStep1 = bw.categoryFilter !== "all";
1954
+ const canAdvanceStep2 = !!bw.selectedService;
1955
+ const canAdvanceStep3 = !!bw.selectedTime;
1956
+ const canSubmit = !!bw.selectedService && !!bw.selectedTime && !bw.isSubmitting;
1957
+ const selectedStaff = localStaff;
1958
+ const isServiceForStaff = (svc) => {
1959
+ if (!selectedStaff) return true;
1960
+ if (!svc.assignedStaff?.length) return true;
1961
+ return svc.assignedStaff.some((s) => s.id === selectedStaff.id);
1962
+ };
1963
+ const staffFilteredServices = bw.pagedServices.filter(isServiceForStaff);
1964
+ if (servicesLoading || bw.isServicesLoading) {
1965
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw", ref: widgetRef, children: [
1966
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-header", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel bw-skel--text", style: { width: 220, height: 28 } }) }),
1967
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-body bw-skel-desktop", children: [
1968
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-col bw-col--left", children: [
1969
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel bw-skel--text", style: { width: 100, height: 12, marginBottom: 10 } }),
1970
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel bw-skel--card", style: { height: 52 } }),
1971
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel bw-skel--text", style: { width: 100, height: 12, marginTop: 20, marginBottom: 10 } }),
1972
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel-list", children: [...Array(3)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel bw-skel--card", style: { height: 56 } }, i)) })
1973
+ ] }),
1974
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-col bw-col--center", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel bw-skel--calendar", style: { height: "100%" } }) }),
1975
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-col bw-col--right", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-skel-list", children: [
1976
+ [...Array(3)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel bw-skel--input" }, i)),
1977
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel bw-skel--input", style: { height: 80 } }),
1978
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel bw-skel--card", style: { height: 48, marginTop: 16 } })
1979
+ ] }) })
1980
+ ] }),
1981
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-skel-mobile", children: [
1982
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel bw-skel--text", style: { width: 100, height: 12, marginBottom: 10 } }),
1983
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel bw-skel--card", style: { height: 52 } }),
1984
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel bw-skel--text", style: { width: 100, height: 12, marginTop: 24, marginBottom: 10 } }),
1985
+ [...Array(4)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel bw-skel--card", style: { height: 52 } }, i)),
1986
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel bw-skel--card", style: { height: 48, marginTop: "auto" } })
1987
+ ] })
1988
+ ] });
1989
+ }
1990
+ if (servicesError) {
1991
+ const message = servicesError instanceof Error ? servicesError.message : "Unable to load services.";
1992
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw", ref: widgetRef, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-empty", children: [
1993
+ "Booking is unavailable right now. ",
1994
+ message
1995
+ ] }) });
1996
+ }
1997
+ if (!data?.services?.length) {
1998
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw", ref: widgetRef, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-empty", children: "No services are configured yet." }) });
1999
+ }
2000
+ if (bw.step === "done") {
2001
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw", ref: widgetRef, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-done", children: [
2002
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-done-icon", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.Check, { size: 28, strokeWidth: 2.5 }) }),
2003
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { className: "bw-done-title", children: "You're All Set!" }),
2004
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "bw-done-text", children: "We've received your booking request. You'll get a confirmation email shortly with all the details." }),
2005
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { className: "bw-btn-primary", onClick: bw.reset, children: "Book Another Visit" })
2006
+ ] }) });
2007
+ }
2008
+ const renderServiceRow = (svc, variant = "inline") => {
2009
+ const sel = bw.selectedService?.id === svc.id;
2010
+ const imgUrl = svc.image?.url;
2011
+ const desc = svc.description;
2012
+ if (variant === "full") {
2013
+ const duration = svc.duration != null ? bw.formatDuration(svc.duration) : null;
2014
+ const hasImg = !!imgUrl;
2015
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2016
+ "button",
2017
+ {
2018
+ className: `bw-svc-row bw-svc-row--full ${hasImg ? "bw-svc-row--has-img" : ""} ${sel ? "is-active" : ""}`,
2019
+ onClick: () => bw.selectService(svc),
2020
+ children: [
2021
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: `bw-check bw-check--lg ${sel ? "is-checked" : ""}`, children: sel && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.Check, { size: 12 }) }),
2022
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "bw-svc-info", children: [
2023
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-svc-name", children: svc.name }),
2024
+ duration && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-svc-meta", children: duration }),
2025
+ desc && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-svc-desc", children: desc })
2026
+ ] }),
2027
+ hasImg ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "bw-svc-img", children: [
2028
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("img", { src: imgUrl, alt: svc.name }),
2029
+ svc.price != null && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-svc-badge", children: formatPrice(Number(svc.price)) })
2030
+ ] }) : svc.price != null && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-svc-price", children: formatPrice(Number(svc.price)) })
2031
+ ]
2032
+ },
2033
+ svc.id
2034
+ );
2035
+ }
2036
+ const meta = [
2037
+ svc.duration != null ? bw.formatDuration(svc.duration) : null,
2038
+ svc.price != null ? formatPrice(Number(svc.price)) : null
2039
+ ].filter(Boolean).join(" \xB7 ");
2040
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2041
+ "button",
2042
+ {
2043
+ className: `bw-svc-row ${sel ? "is-active" : ""}`,
2044
+ onClick: () => bw.selectService(svc),
2045
+ children: [
2046
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: `bw-check ${sel ? "is-checked" : ""}`, children: sel && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.Check, { size: 11 }) }),
2047
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "bw-svc-info", children: [
2048
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-svc-name", children: svc.name }),
2049
+ meta && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-svc-meta", children: meta }),
2050
+ desc && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-svc-desc", children: desc })
2051
+ ] }),
2052
+ imgUrl && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-svc-thumb", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("img", { src: imgUrl, alt: svc.name }) })
2053
+ ]
2054
+ },
2055
+ svc.id
2056
+ );
2057
+ };
2058
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw", ref: widgetRef, children: [
2059
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-topbar", children: [
2060
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-dots", children: Array.from({ length: 4 }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `bw-dot ${mobileStep > i ? "is-filled" : ""}` }, i)) }),
2061
+ navLinks.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2062
+ "button",
2063
+ {
2064
+ className: "bw-topbar-menu",
2065
+ onClick: () => setNavOpen((o) => !o),
2066
+ "aria-label": navOpen ? "Close menu" : "Open menu",
2067
+ children: navOpen ? closeIcon || /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(DefaultCloseIcon, {}) : menuIcon || /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(DefaultMenuIcon, {})
2068
+ }
2069
+ )
2070
+ ] }),
2071
+ navLinks.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_framer_motion.AnimatePresence, { children: navOpen && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2072
+ import_framer_motion.motion.div,
2073
+ {
2074
+ className: "bw-nav-overlay",
2075
+ initial: { opacity: 0 },
2076
+ animate: { opacity: 1 },
2077
+ exit: { opacity: 0 },
2078
+ transition: { duration: 0.2 },
2079
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("nav", { className: "bw-nav", children: navLinks.map((link) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2080
+ LinkComp,
2081
+ {
2082
+ href: link.href,
2083
+ className: "bw-nav-link",
2084
+ onClick: () => setNavOpen(false),
2085
+ children: link.label
2086
+ },
2087
+ link.href
2088
+ )) })
2089
+ }
2090
+ ) }),
2091
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-header", children: [
2092
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { className: "bw-title bw-title--desktop", children: "Schedule your visit" }),
2093
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { className: "bw-title bw-title--mobile", children: (stepTitles || DEFAULT_STEP_TITLES)[mobileStep - 1] })
2094
+ ] }),
2095
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-body", "data-mobile-step": mobileStep, ref: mobileBodyRef, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-body-inner", children: [
2096
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-col bw-col--left", children: [
2097
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-step-1", children: [
2098
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "bw-label", children: "Select Specialist" }),
2099
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-staff-dropdown", children: [
2100
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2101
+ "button",
2102
+ {
2103
+ ref: staffTriggerRef,
2104
+ className: `bw-staff-trigger${selectedStaff ? " has-value" : ""}`,
2105
+ onClick: () => setStaffOpen((o) => !o),
2106
+ children: [
2107
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_framer_motion.AnimatePresence, { mode: "wait", initial: false, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2108
+ import_framer_motion.motion.span,
2109
+ {
2110
+ className: "bw-staff-trigger-content",
2111
+ initial: { opacity: 0 },
2112
+ animate: { opacity: 1 },
2113
+ exit: { opacity: 0 },
2114
+ transition: { duration: 0.15 },
2115
+ children: selectedStaff ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2116
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-staff-avatar", children: selectedStaff.photo?.url ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("img", { src: selectedStaff.photo.url, alt: selectedStaff.name }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-staff-initials", children: getInitials(selectedStaff.name) }) }),
2117
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "bw-staff-trigger-info", children: [
2118
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-staff-trigger-name", children: selectedStaff.name }),
2119
+ selectedStaff.bio && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-staff-trigger-desc", children: truncate(selectedStaff.bio, 40) })
2120
+ ] })
2121
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2122
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-staff-avatar bw-staff-avatar--placeholder", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.User, { size: 14 }) }),
2123
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-staff-trigger-info", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-staff-trigger-name", children: "Any available specialist" }) })
2124
+ ] })
2125
+ },
2126
+ selectedStaff?.id ?? "any"
2127
+ ) }),
2128
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.ChevronDown, { size: 16, className: "bw-staff-trigger-chevron" })
2129
+ ]
2130
+ }
2131
+ ),
2132
+ portalRef.current && (0, import_react_dom.createPortal)(
2133
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_framer_motion.AnimatePresence, { children: staffOpen && (() => {
2134
+ const rect = staffTriggerRef.current?.getBoundingClientRect();
2135
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2136
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2137
+ import_framer_motion.motion.div,
2138
+ {
2139
+ className: "bw-staff-overlay",
2140
+ onClick: () => setStaffOpen(false),
2141
+ initial: { opacity: 0 },
2142
+ animate: { opacity: 1 },
2143
+ exit: { opacity: 0 },
2144
+ transition: { duration: 0.15 }
2145
+ }
2146
+ ),
2147
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2148
+ import_framer_motion.motion.div,
2149
+ {
2150
+ className: "bw-staff-list",
2151
+ initial: { opacity: 0, y: -8 },
2152
+ animate: { opacity: 1, y: 0 },
2153
+ exit: { opacity: 0, y: -8 },
2154
+ transition: { duration: 0.2, ease: [0.4, 0, 0.2, 1] },
2155
+ style: rect ? {
2156
+ top: rect.bottom + 4,
2157
+ left: rect.left,
2158
+ width: rect.width
2159
+ } : void 0,
2160
+ children: [
2161
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2162
+ "button",
2163
+ {
2164
+ className: `bw-staff-option${!selectedStaff ? " is-active" : ""}`,
2165
+ onClick: () => {
2166
+ setLocalStaff(null);
2167
+ bw.selectStaff(null);
2168
+ bw.selectService(null);
2169
+ setStaffOpen(false);
2170
+ },
2171
+ children: [
2172
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-staff-avatar bw-staff-avatar--placeholder", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.User, { size: 14 }) }),
2173
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "bw-staff-option-info", children: [
2174
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-staff-option-name", children: "Any Available" }),
2175
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-staff-option-desc", children: "First available specialist" })
2176
+ ] })
2177
+ ]
2178
+ }
2179
+ ),
2180
+ allStaff.map((member) => {
2181
+ const active = selectedStaff?.id === member.id;
2182
+ const photoUrl = member.photo?.url;
2183
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2184
+ "button",
2185
+ {
2186
+ className: `bw-staff-option${active ? " is-active" : ""}`,
2187
+ onClick: () => {
2188
+ setLocalStaff(member);
2189
+ bw.selectStaff(member);
2190
+ bw.selectService(null);
2191
+ setStaffOpen(false);
2192
+ },
2193
+ children: [
2194
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-staff-avatar", children: photoUrl ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("img", { src: photoUrl, alt: member.name }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-staff-initials", children: getInitials(member.name) }) }),
2195
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "bw-staff-option-info", children: [
2196
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-staff-option-name", children: member.name }),
2197
+ member.bio && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-staff-option-desc", children: truncate(member.bio, 60) })
2198
+ ] })
2199
+ ]
2200
+ },
2201
+ member.id
2202
+ );
2203
+ })
2204
+ ]
2205
+ }
2206
+ )
2207
+ ] });
2208
+ })() }),
2209
+ portalRef.current
2210
+ )
2211
+ ] }),
2212
+ hasMultipleCategories && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2213
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "bw-label", children: "Select Category" }),
2214
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-cat-scroll", ref: catScrollRef, children: bw.categories.map((cat, catIdx) => {
2215
+ const active = bw.categoryFilter === cat;
2216
+ const Icon = getCategoryIcon(cat, categoryIcons);
2217
+ const catServices = (data?.services || []).filter((s) => s.category === cat).filter(isServiceForStaff);
2218
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
2219
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2220
+ "button",
2221
+ {
2222
+ className: `bw-cat-card${active ? " is-active" : ""}`,
2223
+ onClick: () => bw.setCategoryFilter(active ? "all" : cat),
2224
+ children: [
2225
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Icon, { size: 20, className: "bw-cat-icon" }),
2226
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-cat-name", children: cat }),
2227
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2228
+ import_framer_motion.motion.span,
2229
+ {
2230
+ className: "bw-cat-chevron",
2231
+ animate: { rotate: active ? 90 : 0 },
2232
+ transition: { duration: 0.2, ease: "easeOut" },
2233
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.ChevronRight, { size: 16 })
2234
+ }
2235
+ ),
2236
+ active && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-cat-check", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.Check, { size: 12 }) })
2237
+ ]
2238
+ }
2239
+ ),
2240
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2241
+ "div",
2242
+ {
2243
+ className: `bw-svc-scroll ${active ? "is-open" : ""}`,
2244
+ ref: (el) => {
2245
+ if (!el) return;
2246
+ if (active) {
2247
+ const wrap = el.querySelector(".bw-svc-scroll-wrap");
2248
+ if (wrap) requestAnimationFrame(() => initSvcScrollbar(wrap));
2249
+ } else {
2250
+ if (svcScrollbarRef.current) {
2251
+ svcScrollbarRef.current.scrollTo(0, 0, 0);
2252
+ try {
2253
+ svcScrollbarRef.current.destroy();
2254
+ } catch {
2255
+ }
2256
+ svcScrollbarRef.current = null;
2257
+ }
2258
+ }
2259
+ },
2260
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-svc-scroll-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-svc-scroll-inner", children: catServices.length ? catServices.map((svc) => renderServiceRow(svc)) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "bw-no-services", children: "No services available" }) }) })
2261
+ }
2262
+ )
2263
+ ] }, cat);
2264
+ }) })
2265
+ ] }),
2266
+ !hasMultipleCategories && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-svc-scroll is-open", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-svc-scroll-wrap", ref: (el) => {
2267
+ if (el) requestAnimationFrame(() => initSvcScrollbar(el));
2268
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-svc-scroll-inner", children: staffFilteredServices.length ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2269
+ staffFilteredServices.map((svc) => renderServiceRow(svc)),
2270
+ bw.hasMoreServices && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { className: "bw-load-more", onClick: bw.loadMoreServices, children: "Show more services" })
2271
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "bw-no-services", children: "No services available" }) }) }) })
2272
+ ] }),
2273
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-step-2", children: [
2274
+ bw.categoryFilter !== "all" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "bw-step-2-heading", children: bw.categoryFilter }),
2275
+ staffFilteredServices.length ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2276
+ staffFilteredServices.map((svc) => renderServiceRow(svc, "full")),
2277
+ bw.hasMoreServices && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { className: "bw-load-more", onClick: bw.loadMoreServices, children: "Show more services" })
2278
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "bw-no-services", children: "No services available" })
2279
+ ] })
2280
+ ] }),
2281
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-col bw-col--center", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-cal-card", children: [
2282
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-cal-header", children: [
2283
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-month-dropdown", children: [
2284
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2285
+ "button",
2286
+ {
2287
+ className: "bw-month-btn",
2288
+ onClick: () => setMonthOpen((o) => !o),
2289
+ children: [
2290
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: bw.monthOptions.find(
2291
+ (o) => o.value === `${bw.calendarMonth.getFullYear()}-${String(bw.calendarMonth.getMonth() + 1).padStart(2, "0")}`
2292
+ )?.label ?? "Select month" }),
2293
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.ChevronDown, { size: 14 })
2294
+ ]
2295
+ }
2296
+ ),
2297
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_framer_motion.AnimatePresence, { children: monthOpen && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2298
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2299
+ import_framer_motion.motion.div,
2300
+ {
2301
+ className: "bw-month-overlay",
2302
+ onClick: () => setMonthOpen(false),
2303
+ initial: { opacity: 0 },
2304
+ animate: { opacity: 1 },
2305
+ exit: { opacity: 0 },
2306
+ transition: { duration: 0.15 }
2307
+ }
2308
+ ),
2309
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2310
+ import_framer_motion.motion.div,
2311
+ {
2312
+ className: "bw-month-list",
2313
+ initial: { opacity: 0, y: -8 },
2314
+ animate: { opacity: 1, y: 0 },
2315
+ exit: { opacity: 0, y: -8 },
2316
+ transition: { duration: 0.2, ease: [0.4, 0, 0.2, 1] },
2317
+ children: bw.monthOptions.map((opt) => {
2318
+ const cur = `${bw.calendarMonth.getFullYear()}-${String(bw.calendarMonth.getMonth() + 1).padStart(2, "0")}`;
2319
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2320
+ "button",
2321
+ {
2322
+ className: `bw-month-option${opt.value === cur ? " is-active" : ""}`,
2323
+ onClick: () => {
2324
+ bw.handleMonthSelect(opt.value);
2325
+ setMonthOpen(false);
2326
+ },
2327
+ children: opt.label
2328
+ },
2329
+ opt.value
2330
+ );
2331
+ })
2332
+ }
2333
+ )
2334
+ ] }) })
2335
+ ] }),
2336
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-cal-navs", children: [
2337
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { className: "bw-cal-nav", onClick: bw.handlePrevMonth, "aria-label": "Previous month", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.ChevronLeft, { size: 18 }) }),
2338
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2339
+ "button",
2340
+ {
2341
+ className: "bw-cal-nav",
2342
+ onClick: bw.handleNextMonth,
2343
+ disabled: bw.maxAdvanceMonthStart ? bw.calendarMonth >= bw.maxAdvanceMonthStart : false,
2344
+ "aria-label": "Next month",
2345
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.ChevronRight, { size: 18 })
2346
+ }
2347
+ )
2348
+ ] })
2349
+ ] }),
2350
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_framer_motion.AnimatePresence, { mode: "wait", initial: false, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2351
+ import_framer_motion.motion.div,
2352
+ {
2353
+ initial: { opacity: 0 },
2354
+ animate: { opacity: 1 },
2355
+ exit: { opacity: 0 },
2356
+ transition: { duration: 0.15 },
2357
+ children: [
2358
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-cal-weekdays", children: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((d) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: d }, d)) }),
2359
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-cal-grid", children: bw.calendarDays.map((day, i) => {
2360
+ const dateStr = day.date;
2361
+ const dateObj = /* @__PURE__ */ new Date(dateStr + "T00:00:00");
2362
+ const avail = bw.availabilityByDate?.get(dateStr);
2363
+ const isAvailable = avail && avail.slots?.some((s) => s.available);
2364
+ const isSelected = bw.selectedDate === dateStr;
2365
+ const isToday = dateStr === todayStr;
2366
+ const isPast = dateStr < todayStr;
2367
+ const isBeyondMax = bw.maxAdvanceDate ? dateStr > bw.maxAdvanceDate.toISOString().split("T")[0] : false;
2368
+ const isDisabled = !day.isCurrentMonth || isPast || isBeyondMax;
2369
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2370
+ "button",
2371
+ {
2372
+ className: [
2373
+ "bw-cal-day",
2374
+ !day.isCurrentMonth && "is-outside",
2375
+ isSelected && "is-selected",
2376
+ isAvailable && !isDisabled && "is-available",
2377
+ isToday && "is-today",
2378
+ isDisabled && "is-disabled",
2379
+ !isAvailable && day.isCurrentMonth && !isPast && !isBeyondMax && "is-blocked"
2380
+ ].filter(Boolean).join(" "),
2381
+ disabled: isDisabled || !isAvailable || isSelected,
2382
+ onClick: () => {
2383
+ bw.selectDate(dateStr);
2384
+ bw.selectTime("", "");
2385
+ },
2386
+ children: dateObj.getDate()
2387
+ },
2388
+ i
2389
+ );
2390
+ }) })
2391
+ ]
2392
+ },
2393
+ `${bw.calendarMonth.getFullYear()}-${bw.calendarMonth.getMonth()}`
2394
+ ) }),
2395
+ !bw.selectedService && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "bw-cal-prompt", children: "Please select a service to see availability." }),
2396
+ bw.selectedService && bw.isAvailabilityLoading && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel-slots", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel-slots-grid", children: [...Array(8)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-skel bw-skel--slot" }, i)) }) }),
2397
+ bw.selectedService && bw.selectedDate && !bw.isAvailabilityLoading && (() => {
2398
+ const dateAvail = bw.availabilityByDate?.get(bw.selectedDate);
2399
+ const slots = dateAvail?.slots?.filter((s) => s.available) || [];
2400
+ if (!slots.length) {
2401
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "bw-no-slots", children: "No available times for this date." });
2402
+ }
2403
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2404
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-time-divider" }),
2405
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-time-slots", children: slots.map((slot, idx) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2406
+ "button",
2407
+ {
2408
+ className: `bw-slot ${bw.selectedTime === slot.time ? "is-active" : ""}`,
2409
+ onClick: () => bw.selectTime(slot.time, slot.endTime),
2410
+ children: bw.formatTimeLabel(slot.time)
2411
+ },
2412
+ idx
2413
+ )) })
2414
+ ] });
2415
+ })(),
2416
+ bw.timeZoneLabel && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-timezone", children: [
2417
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.Globe, { size: 14 }),
2418
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: bw.timeZoneLabel })
2419
+ ] })
2420
+ ] }) }),
2421
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-col bw-col--right", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-right-scroll", ref: rightScrollRef, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-right-inner", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("form", { className: "bw-form", ref: formRef, onSubmit: handleFormSubmit, children: [
2422
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-form-fields", children: [
2423
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-field", children: [
2424
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { htmlFor: "bw-name", children: [
2425
+ "Full name ",
2426
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-required", children: "*" })
2427
+ ] }),
2428
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2429
+ "input",
2430
+ {
2431
+ id: "bw-name",
2432
+ type: "text",
2433
+ required: true,
2434
+ placeholder: "John",
2435
+ value: bw.details.name,
2436
+ onChange: (e) => bw.setDetails((p) => ({ ...p, name: e.target.value }))
2437
+ }
2438
+ )
2439
+ ] }),
2440
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-field", children: [
2441
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { htmlFor: "bw-email", children: [
2442
+ "Email ",
2443
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-required", children: "*" })
2444
+ ] }),
2445
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2446
+ "input",
2447
+ {
2448
+ id: "bw-email",
2449
+ type: "email",
2450
+ required: true,
2451
+ placeholder: "jane@example.com",
2452
+ value: bw.details.email,
2453
+ onChange: (e) => bw.setDetails((p) => ({ ...p, email: e.target.value }))
2454
+ }
2455
+ )
2456
+ ] }),
2457
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-field", children: [
2458
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { htmlFor: "bw-phone", children: "Phone" }),
2459
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2460
+ "input",
2461
+ {
2462
+ id: "bw-phone",
2463
+ type: "tel",
2464
+ placeholder: "+1 (555) 000-0000",
2465
+ value: bw.details.phone,
2466
+ onChange: (e) => bw.setDetails((p) => ({ ...p, phone: e.target.value }))
2467
+ }
2468
+ )
2469
+ ] })
2470
+ ] }),
2471
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-field bw-field--notes", children: [
2472
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { htmlFor: "bw-notes", children: "Additional notes" }),
2473
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2474
+ "textarea",
2475
+ {
2476
+ id: "bw-notes",
2477
+ placeholder: "Any preferences or special requests...",
2478
+ rows: 3,
2479
+ maxLength: 500,
2480
+ style: { resize: "none" },
2481
+ value: bw.details.notes,
2482
+ onChange: (e) => bw.setDetails((p) => ({ ...p, notes: e.target.value }))
2483
+ }
2484
+ ),
2485
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "bw-char-count", children: [
2486
+ bw.details.notes?.length || 0,
2487
+ "/500"
2488
+ ] })
2489
+ ] }),
2490
+ bw.selectedService && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-summary", children: [
2491
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-summary-title", children: "Booking Summary" }),
2492
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-summary-rows", children: [
2493
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-summary-row", children: [
2494
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Service" }),
2495
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-summary-val", children: bw.selectedService.name })
2496
+ ] }),
2497
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-summary-row", children: [
2498
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Specialist" }),
2499
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-summary-val", children: selectedStaff?.name || "Any Available" })
2500
+ ] }),
2501
+ bw.selectedDate && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-summary-row", children: [
2502
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Date" }),
2503
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-summary-val", children: bw.formatDateLabel(bw.selectedDate) })
2504
+ ] }),
2505
+ bw.selectedTime && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-summary-row", children: [
2506
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Time" }),
2507
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-summary-val", children: bw.formatTimeLabel(bw.selectedTime) })
2508
+ ] }),
2509
+ bw.selectedService.duration != null && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-summary-row", children: [
2510
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Duration" }),
2511
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-summary-val", children: bw.formatDuration(bw.selectedService.duration) })
2512
+ ] }),
2513
+ bw.selectedService.price != null && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-summary-row bw-summary-total", children: [
2514
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Estimated total" }),
2515
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-summary-val", children: formatPrice(Number(bw.selectedService.price)) })
2516
+ ] })
2517
+ ] })
2518
+ ] }),
2519
+ bw.bookingError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bw-error", children: bw.bookingError }),
2520
+ !canSubmit && !bw.isSubmitting && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "bw-confirm-hint", children: !bw.selectedService ? "Please select a service to confirm booking." : !bw.selectedTime ? "Please select a time to confirm booking." : "" }),
2521
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("button", { type: "submit", className: "bw-confirm-btn", disabled: !canSubmit, children: [
2522
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: bw.isSubmitting ? "Booking..." : "Confirm Booking" }),
2523
+ !bw.isSubmitting && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.ArrowRight, { size: 16 })
2524
+ ] })
2525
+ ] }) }) }) })
2526
+ ] }) }),
2527
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-footer", children: [
2528
+ mobileStep === 4 && bw.selectedService && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-footer-summary", children: [
2529
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-summary-title", children: "Booking Summary" }),
2530
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-summary-rows", children: [
2531
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-summary-row", children: [
2532
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Service" }),
2533
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-summary-val", children: bw.selectedService.name })
2534
+ ] }),
2535
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-summary-row", children: [
2536
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Specialist" }),
2537
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-summary-val", children: selectedStaff?.name || "Any Available" })
2538
+ ] }),
2539
+ bw.selectedDate && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-summary-row", children: [
2540
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Date" }),
2541
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-summary-val", children: bw.formatDateLabel(bw.selectedDate) })
2542
+ ] }),
2543
+ bw.selectedTime && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-summary-row", children: [
2544
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Time" }),
2545
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-summary-val", children: bw.formatTimeLabel(bw.selectedTime) })
2546
+ ] }),
2547
+ bw.selectedService.price != null && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-summary-row bw-summary-total", children: [
2548
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Estimated total" }),
2549
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bw-summary-val", children: formatPrice(Number(bw.selectedService.price)) })
2550
+ ] })
2551
+ ] })
2552
+ ] }),
2553
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bw-footer-btns", children: [
2554
+ mobileStep > 1 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2555
+ "button",
2556
+ {
2557
+ className: "bw-footer-back",
2558
+ onClick: () => setMobileStep((s) => s - 1),
2559
+ "aria-label": "Go back",
2560
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.ArrowLeft, { size: 20 })
2561
+ }
2562
+ ),
2563
+ mobileStep < 4 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2564
+ "button",
2565
+ {
2566
+ className: "bw-footer-next",
2567
+ disabled: mobileStep === 1 && !canAdvanceStep1 || mobileStep === 2 && !canAdvanceStep2 || mobileStep === 3 && !canAdvanceStep3,
2568
+ onClick: () => setMobileStep((s) => s + 1),
2569
+ children: [
2570
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Next" }),
2571
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.ArrowRight, { size: 16 })
2572
+ ]
2573
+ }
2574
+ ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2575
+ "button",
2576
+ {
2577
+ className: "bw-footer-next",
2578
+ disabled: !canSubmit,
2579
+ onClick: () => formRef.current?.requestSubmit(),
2580
+ children: [
2581
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: bw.isSubmitting ? "Booking..." : "Confirm Booking" }),
2582
+ !bw.isSubmitting && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react.ArrowRight, { size: 16 })
2583
+ ]
2584
+ }
2585
+ )
2586
+ ] })
2587
+ ] }),
2588
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ref: portalRef, className: "bw-portal" })
2589
+ ] });
2590
+ }
2591
+
2592
+ // src/components/AvailabilityPicker.tsx
2593
+ var import_react10 = require("react");
2594
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1717
2595
  function AvailabilityPicker({
1718
2596
  serviceId,
1719
2597
  staffId,
@@ -1722,8 +2600,8 @@ function AvailabilityPicker({
1722
2600
  className,
1723
2601
  onSelect
1724
2602
  }) {
1725
- const [selectedDate, setSelectedDate] = (0, import_react9.useState)(date || null);
1726
- const [selectedTime, setSelectedTime] = (0, import_react9.useState)(null);
2603
+ const [selectedDate, setSelectedDate] = (0, import_react10.useState)(date || null);
2604
+ const [selectedTime, setSelectedTime] = (0, import_react10.useState)(null);
1727
2605
  const { data, isLoading } = useAvailability({
1728
2606
  serviceId,
1729
2607
  staffId,
@@ -1731,15 +2609,15 @@ function AvailabilityPicker({
1731
2609
  days
1732
2610
  });
1733
2611
  if (isLoading) {
1734
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-state", children: "Loading availability..." });
2612
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "idk-state", children: "Loading availability..." });
1735
2613
  }
1736
2614
  if (!data?.dates?.length) {
1737
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-state", children: "No availability found." });
2615
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "idk-state", children: "No availability found." });
1738
2616
  }
1739
2617
  const activeDate = selectedDate || data.dates[0]?.date;
1740
2618
  const activeSlots = data.dates.find((entry) => entry.date === activeDate)?.slots || [];
1741
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: ["idk-availability", className].filter(Boolean).join(" "), children: [
1742
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-availability__dates", children: data.dates.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2619
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: ["idk-availability", className].filter(Boolean).join(" "), children: [
2620
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "idk-availability__dates", children: data.dates.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1743
2621
  "button",
1744
2622
  {
1745
2623
  type: "button",
@@ -1752,7 +2630,7 @@ function AvailabilityPicker({
1752
2630
  },
1753
2631
  entry.date
1754
2632
  )) }),
1755
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-availability__slots", children: activeSlots.filter((slot) => slot.available).length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "idk-state", children: "No slots available." }) : activeSlots.filter((slot) => slot.available).map((slot) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2633
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "idk-availability__slots", children: activeSlots.filter((slot) => slot.available).length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "idk-state", children: "No slots available." }) : activeSlots.filter((slot) => slot.available).map((slot) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1756
2634
  "button",
1757
2635
  {
1758
2636
  type: "button",
@@ -1775,22 +2653,22 @@ function AvailabilityPicker({
1775
2653
  }
1776
2654
 
1777
2655
  // src/components/ServicePicker.tsx
1778
- var import_jsx_runtime10 = require("react/jsx-runtime");
2656
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1779
2657
  function ServicePicker({
1780
2658
  services,
1781
2659
  selectedId,
1782
2660
  onSelect,
1783
2661
  className
1784
2662
  }) {
1785
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: services.map((service) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2663
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: services.map((service) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1786
2664
  "button",
1787
2665
  {
1788
2666
  type: "button",
1789
2667
  className: selectedId === service.id ? "is-active" : void 0,
1790
2668
  onClick: () => onSelect?.(service),
1791
2669
  children: [
1792
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: service.name }),
1793
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("small", { children: [
2670
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: service.name }),
2671
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("small", { children: [
1794
2672
  service.duration,
1795
2673
  " min"
1796
2674
  ] })
@@ -1801,22 +2679,22 @@ function ServicePicker({
1801
2679
  }
1802
2680
 
1803
2681
  // src/components/StaffPicker.tsx
1804
- var import_jsx_runtime11 = require("react/jsx-runtime");
2682
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1805
2683
  function StaffPicker({
1806
2684
  staff,
1807
2685
  selectedId,
1808
2686
  onSelect,
1809
2687
  className
1810
2688
  }) {
1811
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: staff.map((member) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2689
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: staff.map((member) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1812
2690
  "button",
1813
2691
  {
1814
2692
  type: "button",
1815
2693
  className: selectedId === member.id ? "is-active" : void 0,
1816
2694
  onClick: () => onSelect?.(member),
1817
2695
  children: [
1818
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: member.name }),
1819
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("small", { children: member.role || "Staff" })
2696
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: member.name }),
2697
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("small", { children: member.role || "Staff" })
1820
2698
  ]
1821
2699
  },
1822
2700
  member.id
@@ -1824,14 +2702,14 @@ function StaffPicker({
1824
2702
  }
1825
2703
 
1826
2704
  // src/components/DatePicker.tsx
1827
- var import_jsx_runtime12 = require("react/jsx-runtime");
2705
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1828
2706
  function DatePicker({
1829
2707
  dates,
1830
2708
  selectedDate,
1831
2709
  onSelect,
1832
2710
  className
1833
2711
  }) {
1834
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: dates.map((date) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2712
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: dates.map((date) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1835
2713
  "button",
1836
2714
  {
1837
2715
  type: "button",
@@ -1844,14 +2722,14 @@ function DatePicker({
1844
2722
  }
1845
2723
 
1846
2724
  // src/components/TimePicker.tsx
1847
- var import_jsx_runtime13 = require("react/jsx-runtime");
2725
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1848
2726
  function TimePicker({
1849
2727
  slots,
1850
2728
  selectedTime,
1851
2729
  onSelect,
1852
2730
  className
1853
2731
  }) {
1854
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: slots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2732
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: slots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1855
2733
  "button",
1856
2734
  {
1857
2735
  type: "button",
@@ -1867,6 +2745,7 @@ function TimePicker({
1867
2745
  0 && (module.exports = {
1868
2746
  AvailabilityPicker,
1869
2747
  BookingWidget,
2748
+ BookingWidgetPanel,
1870
2749
  ContactForm,
1871
2750
  DatePicker,
1872
2751
  PlatformProvider,