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