@ohhwells/bridge 0.1.19 → 0.1.21
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 +715 -279
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +690 -254
- package/dist/index.js.map +1 -1
- package/dist/styles.css +193 -27
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,12 +1,167 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/OhhwellsBridge.tsx
|
|
4
|
-
import React3, { useCallback, useEffect as useEffect2, useLayoutEffect, useRef, useState as
|
|
4
|
+
import React3, { useCallback, useEffect as useEffect2, useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
5
5
|
import { createRoot } from "react-dom/client";
|
|
6
|
+
import { flushSync } from "react-dom";
|
|
6
7
|
|
|
7
8
|
// src/ui/SchedulingWidget.tsx
|
|
8
|
-
import
|
|
9
|
-
|
|
9
|
+
import { useEffect, useMemo, useRef, useState as useState2 } from "react";
|
|
10
|
+
|
|
11
|
+
// src/ui/EmailCaptureModal.tsx
|
|
12
|
+
import { useState } from "react";
|
|
13
|
+
import { Dialog } from "radix-ui";
|
|
14
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
15
|
+
function Spinner() {
|
|
16
|
+
return /* @__PURE__ */ jsxs(
|
|
17
|
+
"svg",
|
|
18
|
+
{
|
|
19
|
+
width: "32",
|
|
20
|
+
height: "32",
|
|
21
|
+
viewBox: "0 0 32 32",
|
|
22
|
+
fill: "none",
|
|
23
|
+
style: { animation: "ohw-spin 0.75s linear infinite" },
|
|
24
|
+
children: [
|
|
25
|
+
/* @__PURE__ */ jsx("style", { children: `@keyframes ohw-spin { to { transform: rotate(360deg); } }` }),
|
|
26
|
+
/* @__PURE__ */ jsx("circle", { cx: "16", cy: "16", r: "12", stroke: "#e5e7eb", strokeWidth: "3" }),
|
|
27
|
+
/* @__PURE__ */ jsx(
|
|
28
|
+
"path",
|
|
29
|
+
{
|
|
30
|
+
d: "M16 4a12 12 0 0 1 12 12",
|
|
31
|
+
stroke: "#3b82f6",
|
|
32
|
+
strokeWidth: "3",
|
|
33
|
+
strokeLinecap: "round"
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
41
|
+
const [email, setEmail] = useState("");
|
|
42
|
+
const [submittedEmail, setSubmittedEmail] = useState("");
|
|
43
|
+
const [loading, setLoading] = useState(false);
|
|
44
|
+
const [success, setSuccess] = useState(false);
|
|
45
|
+
const [error, setError] = useState(null);
|
|
46
|
+
const isBook = title === "Confirm your spot";
|
|
47
|
+
const handleSubmit = async () => {
|
|
48
|
+
if (!email.trim()) return;
|
|
49
|
+
setLoading(true);
|
|
50
|
+
setError(null);
|
|
51
|
+
try {
|
|
52
|
+
await onSubmit(email.trim());
|
|
53
|
+
setSubmittedEmail(email.trim());
|
|
54
|
+
setSuccess(true);
|
|
55
|
+
} catch (e) {
|
|
56
|
+
setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
|
|
57
|
+
} finally {
|
|
58
|
+
setLoading(false);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const handleKeyDown = (e) => {
|
|
62
|
+
if (e.key === "Enter" && !loading) handleSubmit();
|
|
63
|
+
};
|
|
64
|
+
return /* @__PURE__ */ jsx(Dialog.Root, { open: true, onOpenChange: (open) => {
|
|
65
|
+
if (!open) onClose();
|
|
66
|
+
}, children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [
|
|
67
|
+
/* @__PURE__ */ jsx(
|
|
68
|
+
Dialog.Overlay,
|
|
69
|
+
{
|
|
70
|
+
className: "fixed inset-0 z-50",
|
|
71
|
+
style: { background: "rgba(0,0,0,0.45)" }
|
|
72
|
+
}
|
|
73
|
+
),
|
|
74
|
+
/* @__PURE__ */ jsxs(
|
|
75
|
+
Dialog.Content,
|
|
76
|
+
{
|
|
77
|
+
className: "fixed left-1/2 top-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 bg-white rounded-xl shadow-xl outline-none font-body box-border overflow-hidden",
|
|
78
|
+
style: { maxWidth: 400 },
|
|
79
|
+
children: [
|
|
80
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 p-6", children: [
|
|
81
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
82
|
+
/* @__PURE__ */ jsx(
|
|
83
|
+
Dialog.Title,
|
|
84
|
+
{
|
|
85
|
+
className: "font-body text-xl font-bold m-0 leading-tight",
|
|
86
|
+
style: { color: "var(--color-dark,#200C02)" },
|
|
87
|
+
children: success ? isBook ? "Reservation confirmed!" : "You're on the waitlist!" : title
|
|
88
|
+
}
|
|
89
|
+
),
|
|
90
|
+
/* @__PURE__ */ jsx(
|
|
91
|
+
Dialog.Description,
|
|
92
|
+
{
|
|
93
|
+
className: "font-body text-sm m-0",
|
|
94
|
+
style: { color: "#6B7280" },
|
|
95
|
+
children: success ? isBook ? `A confirmation email has been sent to ${submittedEmail}.` : "We'll let you know via email if someone cancels." : subtitle
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
] }),
|
|
99
|
+
/* @__PURE__ */ jsx(
|
|
100
|
+
Dialog.Close,
|
|
101
|
+
{
|
|
102
|
+
className: "shrink-0 w-7 h-7 flex items-center justify-center rounded-full bg-transparent border-none cursor-pointer mt-0.5 absolute top-2.5 right-1.5",
|
|
103
|
+
style: { color: "#6B7280" },
|
|
104
|
+
"aria-label": "Close",
|
|
105
|
+
children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
106
|
+
/* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
107
|
+
/* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
108
|
+
] })
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
] }),
|
|
112
|
+
!success && (loading ? (
|
|
113
|
+
/* Loading state — full body replaced */
|
|
114
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center gap-3 px-7 py-12", children: [
|
|
115
|
+
/* @__PURE__ */ jsx(Spinner, {}),
|
|
116
|
+
/* @__PURE__ */ jsx("p", { className: "font-body text-sm m-0", style: { color: "#6B7280" }, children: "Please wait" })
|
|
117
|
+
] })
|
|
118
|
+
) : (
|
|
119
|
+
/* Email form */
|
|
120
|
+
/* @__PURE__ */ jsxs("div", { className: "px-7 pt-2 pb-6", children: [
|
|
121
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5 mb-8", children: [
|
|
122
|
+
/* @__PURE__ */ jsx("label", { className: "font-body text-sm font-medium", style: { color: "var(--color-dark,#200C02)" }, children: "Email" }),
|
|
123
|
+
/* @__PURE__ */ jsx(
|
|
124
|
+
"input",
|
|
125
|
+
{
|
|
126
|
+
type: "email",
|
|
127
|
+
value: email,
|
|
128
|
+
onChange: (e) => setEmail(e.target.value),
|
|
129
|
+
onKeyDown: handleKeyDown,
|
|
130
|
+
placeholder: "hello@gmail.com",
|
|
131
|
+
autoFocus: true,
|
|
132
|
+
className: "w-full h-10 px-3 rounded-lg border font-body text-sm outline-none box-border",
|
|
133
|
+
style: {
|
|
134
|
+
borderColor: "#E7E5E4",
|
|
135
|
+
background: "#fff",
|
|
136
|
+
color: "var(--color-dark,#200C02)"
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
),
|
|
140
|
+
error && /* @__PURE__ */ jsx("p", { className: "font-body text-xs text-red-500 m-0", children: error })
|
|
141
|
+
] }),
|
|
142
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
|
|
143
|
+
"button",
|
|
144
|
+
{
|
|
145
|
+
onClick: handleSubmit,
|
|
146
|
+
disabled: !email.trim(),
|
|
147
|
+
className: "h-9 px-5 rounded-lg font-body text-sm font-semibold cursor-pointer border-none disabled:opacity-50 disabled:cursor-not-allowed",
|
|
148
|
+
style: {
|
|
149
|
+
background: "var(--color-primary,#3D312B)",
|
|
150
|
+
color: "var(--color-light,#FEFFF0)"
|
|
151
|
+
},
|
|
152
|
+
children: "Send"
|
|
153
|
+
}
|
|
154
|
+
) })
|
|
155
|
+
] })
|
|
156
|
+
))
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
)
|
|
160
|
+
] }) });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/ui/SchedulingWidget.tsx
|
|
164
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
10
165
|
var API_URL = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
11
166
|
var DAY_ABBR = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
|
|
12
167
|
function getApiDomain() {
|
|
@@ -67,8 +222,8 @@ function EmptyState({ inEditor }) {
|
|
|
67
222
|
window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
|
|
68
223
|
}
|
|
69
224
|
};
|
|
70
|
-
return /* @__PURE__ */
|
|
71
|
-
/* @__PURE__ */
|
|
225
|
+
return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center gap-6 p-12 bg-[#FAFAF9] border-2 border-dashed border-[#E7E5E4] rounded-[10px] h-[264px] w-full box-border", children: [
|
|
226
|
+
/* @__PURE__ */ jsx2("div", { className: "w-10 h-10 bg-[#F5F5F4] rounded-[10px] flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxs2(
|
|
72
227
|
"svg",
|
|
73
228
|
{
|
|
74
229
|
width: "20",
|
|
@@ -80,20 +235,20 @@ function EmptyState({ inEditor }) {
|
|
|
80
235
|
strokeLinecap: "round",
|
|
81
236
|
strokeLinejoin: "round",
|
|
82
237
|
children: [
|
|
83
|
-
/* @__PURE__ */
|
|
84
|
-
/* @__PURE__ */
|
|
85
|
-
/* @__PURE__ */
|
|
86
|
-
/* @__PURE__ */
|
|
87
|
-
/* @__PURE__ */
|
|
88
|
-
/* @__PURE__ */
|
|
238
|
+
/* @__PURE__ */ jsx2("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
|
|
239
|
+
/* @__PURE__ */ jsx2("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
|
|
240
|
+
/* @__PURE__ */ jsx2("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
|
|
241
|
+
/* @__PURE__ */ jsx2("line", { x1: "3", y1: "10", x2: "21", y2: "10" }),
|
|
242
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", y1: "14", x2: "12", y2: "18" }),
|
|
243
|
+
/* @__PURE__ */ jsx2("line", { x1: "10", y1: "16", x2: "14", y2: "16" })
|
|
89
244
|
]
|
|
90
245
|
}
|
|
91
246
|
) }),
|
|
92
|
-
/* @__PURE__ */
|
|
93
|
-
/* @__PURE__ */
|
|
94
|
-
/* @__PURE__ */
|
|
247
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-2", children: [
|
|
248
|
+
/* @__PURE__ */ jsx2("p", { className: "font-body text-lg font-medium text-center text-[#0A0A0A] m-0", children: "No schedule yet" }),
|
|
249
|
+
/* @__PURE__ */ jsx2("p", { className: "font-body text-base font-normal text-center text-(--color-accent,#A89B83) m-0", children: "Add a scheduling widget to get instant bookings." })
|
|
95
250
|
] }),
|
|
96
|
-
/* @__PURE__ */
|
|
251
|
+
/* @__PURE__ */ jsx2(
|
|
97
252
|
"button",
|
|
98
253
|
{
|
|
99
254
|
onClick: handleAddSchedule,
|
|
@@ -109,32 +264,90 @@ function LoadingSkeleton() {
|
|
|
109
264
|
backgroundSize: "200% 100%",
|
|
110
265
|
animation: "ohw-shimmer 1.5s infinite"
|
|
111
266
|
};
|
|
112
|
-
return /* @__PURE__ */
|
|
113
|
-
/* @__PURE__ */
|
|
114
|
-
/* @__PURE__ */
|
|
115
|
-
/* @__PURE__ */
|
|
116
|
-
/* @__PURE__ */
|
|
267
|
+
return /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col gap-10", children: [
|
|
268
|
+
/* @__PURE__ */ jsx2("style", { children: `@keyframes ohw-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}` }),
|
|
269
|
+
/* @__PURE__ */ jsx2("div", { className: "flex h-[70px] items-stretch", children: Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col justify-between items-center px-1", children: [
|
|
270
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "32px", height: "14px", borderRadius: "4px" } }),
|
|
271
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "40px", height: "40px", borderRadius: "50%" } })
|
|
117
272
|
] }, i)) }),
|
|
118
|
-
[0, 1, 2].map((i) => /* @__PURE__ */
|
|
119
|
-
/* @__PURE__ */
|
|
120
|
-
/* @__PURE__ */
|
|
121
|
-
|
|
122
|
-
/* @__PURE__ */
|
|
123
|
-
/* @__PURE__ */
|
|
273
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ jsxs2("div", { children: [
|
|
274
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex gap-4 py-4 sm:hidden", children: [
|
|
275
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2 shrink-0", children: [
|
|
276
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "100px", height: "16px", borderRadius: "4px" } }),
|
|
277
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "52px", height: "20px", borderRadius: "999px" } }),
|
|
278
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "60px", height: "14px", borderRadius: "4px" } })
|
|
279
|
+
] }),
|
|
280
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-1 flex-col gap-2", children: [
|
|
281
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "75%", height: "16px", borderRadius: "4px" } }),
|
|
282
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "50%", height: "14px", borderRadius: "4px" } }),
|
|
283
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "100%", height: "44px", borderRadius: "8px" } })
|
|
284
|
+
] })
|
|
285
|
+
] }),
|
|
286
|
+
/* @__PURE__ */ jsxs2("div", { className: "hidden sm:flex items-center gap-[60px] py-4", children: [
|
|
287
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "120px", height: "16px", borderRadius: "4px", flexShrink: 0 } }),
|
|
288
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-2", children: [
|
|
289
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "55%", height: "16px", borderRadius: "4px" } }),
|
|
290
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "38%", height: "14px", borderRadius: "4px" } })
|
|
124
291
|
] }),
|
|
125
|
-
/* @__PURE__ */
|
|
126
|
-
/* @__PURE__ */
|
|
292
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "56px", height: "32px", borderRadius: "4px", flexShrink: 0 } }),
|
|
293
|
+
/* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "200px", height: "44px", borderRadius: "8px", flexShrink: 0 } })
|
|
127
294
|
] }),
|
|
128
|
-
i < 2 && /* @__PURE__ */
|
|
295
|
+
i < 2 && /* @__PURE__ */ jsx2("div", { className: "h-px bg-black/20" })
|
|
129
296
|
] }, i))
|
|
130
297
|
] });
|
|
131
298
|
}
|
|
132
|
-
function ScheduleView({ schedule, dates, selectedIdx, onSelectDate }) {
|
|
299
|
+
function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal }) {
|
|
133
300
|
const todayMs = useMemo(() => {
|
|
134
301
|
const d = /* @__PURE__ */ new Date();
|
|
135
302
|
d.setHours(0, 0, 0, 0);
|
|
136
303
|
return d.getTime();
|
|
137
304
|
}, []);
|
|
305
|
+
const scrollRef = useRef(null);
|
|
306
|
+
const [canScrollLeft, setCanScrollLeft] = useState2(false);
|
|
307
|
+
const [canScrollRight, setCanScrollRight] = useState2(false);
|
|
308
|
+
const updateScrollState = () => {
|
|
309
|
+
const el = scrollRef.current;
|
|
310
|
+
if (!el) return;
|
|
311
|
+
setCanScrollLeft(el.scrollLeft > 0);
|
|
312
|
+
setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
|
|
313
|
+
};
|
|
314
|
+
useEffect(() => {
|
|
315
|
+
const el = scrollRef.current;
|
|
316
|
+
if (!el) return;
|
|
317
|
+
updateScrollState();
|
|
318
|
+
el.addEventListener("scroll", updateScrollState);
|
|
319
|
+
const ro = new ResizeObserver(updateScrollState);
|
|
320
|
+
ro.observe(el);
|
|
321
|
+
return () => {
|
|
322
|
+
el.removeEventListener("scroll", updateScrollState);
|
|
323
|
+
ro.disconnect();
|
|
324
|
+
};
|
|
325
|
+
}, [dates]);
|
|
326
|
+
const scroll = (dir) => {
|
|
327
|
+
scrollRef.current?.scrollBy({ left: dir === "left" ? -216 : 216, behavior: "smooth" });
|
|
328
|
+
};
|
|
329
|
+
const isDragging = useRef(false);
|
|
330
|
+
const dragStartX = useRef(0);
|
|
331
|
+
const dragStartScrollLeft = useRef(0);
|
|
332
|
+
const onDragStart = (e) => {
|
|
333
|
+
if (!scrollRef.current) return;
|
|
334
|
+
isDragging.current = true;
|
|
335
|
+
dragStartX.current = e.clientX;
|
|
336
|
+
dragStartScrollLeft.current = scrollRef.current.scrollLeft;
|
|
337
|
+
scrollRef.current.style.cursor = "grabbing";
|
|
338
|
+
scrollRef.current.style.userSelect = "none";
|
|
339
|
+
};
|
|
340
|
+
const onDragMove = (e) => {
|
|
341
|
+
if (!isDragging.current || !scrollRef.current) return;
|
|
342
|
+
const delta = e.clientX - dragStartX.current;
|
|
343
|
+
scrollRef.current.scrollLeft = dragStartScrollLeft.current - delta;
|
|
344
|
+
};
|
|
345
|
+
const onDragEnd = () => {
|
|
346
|
+
if (!scrollRef.current) return;
|
|
347
|
+
isDragging.current = false;
|
|
348
|
+
scrollRef.current.style.cursor = "grab";
|
|
349
|
+
scrollRef.current.style.userSelect = "";
|
|
350
|
+
};
|
|
138
351
|
const datesWithClasses = useMemo(
|
|
139
352
|
() => new Set(dates.map(
|
|
140
353
|
(d, i) => schedule.classes?.some((c) => classRunsOnDate(c, d, schedule.type)) ? i : -1
|
|
@@ -146,82 +359,154 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate }) {
|
|
|
146
359
|
() => (schedule.classes ?? []).filter((c) => selectedDate && classRunsOnDate(c, selectedDate, schedule.type)),
|
|
147
360
|
[schedule, selectedDate]
|
|
148
361
|
);
|
|
149
|
-
return /* @__PURE__ */
|
|
150
|
-
/* @__PURE__ */
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
362
|
+
return /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col gap-10", children: [
|
|
363
|
+
/* @__PURE__ */ jsxs2("div", { className: "relative w-full", children: [
|
|
364
|
+
canScrollLeft && /* @__PURE__ */ jsx2(
|
|
365
|
+
"button",
|
|
366
|
+
{
|
|
367
|
+
onClick: () => scroll("left"),
|
|
368
|
+
className: "hidden sm:flex absolute left-0 top-1/2 -translate-y-1/2 z-10 w-8 h-8 items-center justify-center rounded-full border-none cursor-pointer",
|
|
369
|
+
style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
|
|
370
|
+
"aria-label": "Scroll left",
|
|
371
|
+
children: /* @__PURE__ */ jsx2("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "var(--color-dark,#200C02)", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx2("polyline", { points: "15 18 9 12 15 6" }) })
|
|
372
|
+
}
|
|
373
|
+
),
|
|
374
|
+
canScrollRight && /* @__PURE__ */ jsx2(
|
|
375
|
+
"button",
|
|
376
|
+
{
|
|
377
|
+
onClick: () => scroll("right"),
|
|
378
|
+
className: "hidden sm:flex absolute right-0 top-1/2 -translate-y-1/2 z-10 w-8 h-8 items-center justify-center rounded-full border-none cursor-pointer",
|
|
379
|
+
style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
|
|
380
|
+
"aria-label": "Scroll right",
|
|
381
|
+
children: /* @__PURE__ */ jsx2("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "var(--color-dark,#200C02)", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx2("polyline", { points: "9 18 15 12 9 6" }) })
|
|
382
|
+
}
|
|
383
|
+
),
|
|
384
|
+
canScrollLeft && /* @__PURE__ */ jsx2(
|
|
156
385
|
"div",
|
|
157
386
|
{
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
387
|
+
className: "sm:hidden absolute left-0 top-0 bottom-0 w-10 z-10 pointer-events-none",
|
|
388
|
+
style: { background: "linear-gradient(to right, var(--color-light,#FEFFF0), transparent)" }
|
|
389
|
+
}
|
|
390
|
+
),
|
|
391
|
+
canScrollRight && /* @__PURE__ */ jsx2(
|
|
392
|
+
"div",
|
|
393
|
+
{
|
|
394
|
+
className: "sm:hidden absolute right-0 top-0 bottom-0 w-10 z-10 pointer-events-none",
|
|
395
|
+
style: { background: "linear-gradient(to left, var(--color-light,#FEFFF0), transparent)" }
|
|
396
|
+
}
|
|
397
|
+
),
|
|
398
|
+
/* @__PURE__ */ jsx2(
|
|
399
|
+
"div",
|
|
400
|
+
{
|
|
401
|
+
ref: scrollRef,
|
|
402
|
+
className: "overflow-x-auto w-full",
|
|
403
|
+
style: { scrollbarWidth: "none", msOverflowStyle: "none", touchAction: "pan-x", cursor: "grab" },
|
|
404
|
+
onMouseDown: onDragStart,
|
|
405
|
+
onMouseMove: onDragMove,
|
|
406
|
+
onMouseUp: onDragEnd,
|
|
407
|
+
onMouseLeave: onDragEnd,
|
|
408
|
+
children: /* @__PURE__ */ jsx2("div", { className: "flex", style: { width: `max(100%, ${dates.length * 60}px)` }, children: dates.map((date, i) => {
|
|
409
|
+
const isToday = date.getTime() === todayMs;
|
|
410
|
+
const isSelected = i === selectedIdx;
|
|
411
|
+
const clickable = datesWithClasses.has(i);
|
|
412
|
+
const label = isToday ? "TODAY" : DAY_ABBR[date.getDay()];
|
|
413
|
+
return /* @__PURE__ */ jsxs2(
|
|
163
414
|
"div",
|
|
164
415
|
{
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
children:
|
|
168
|
-
"span",
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
416
|
+
onClick: () => clickable && onSelectDate(i),
|
|
417
|
+
className: `flex-1 h-[70px] flex flex-col justify-between items-center ${clickable ? "cursor-pointer opacity-100" : "cursor-default opacity-50"}`,
|
|
418
|
+
children: [
|
|
419
|
+
/* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-center text-(--color-dark,#200C02)", children: label }),
|
|
420
|
+
/* @__PURE__ */ jsx2(
|
|
421
|
+
"div",
|
|
422
|
+
{
|
|
423
|
+
className: "w-10 h-10 rounded-full flex items-center justify-center",
|
|
424
|
+
style: { background: isSelected ? "var(--color-primary, #3D312B)" : "transparent" },
|
|
425
|
+
children: /* @__PURE__ */ jsx2(
|
|
426
|
+
"span",
|
|
427
|
+
{
|
|
428
|
+
className: "font-body text-xl font-bold text-center tracking-display-tight",
|
|
429
|
+
style: { color: isSelected ? "var(--color-light, #FEFFF0)" : "var(--color-dark, #200C02)" },
|
|
430
|
+
children: date.getDate()
|
|
431
|
+
}
|
|
432
|
+
)
|
|
433
|
+
}
|
|
434
|
+
)
|
|
435
|
+
]
|
|
436
|
+
},
|
|
437
|
+
i
|
|
438
|
+
);
|
|
439
|
+
}) })
|
|
440
|
+
}
|
|
441
|
+
)
|
|
442
|
+
] }),
|
|
443
|
+
selectedClasses.length === 0 ? /* @__PURE__ */ jsx2("p", { className: "font-body text-base text-(--color-accent,#A89B83) text-center py-8 m-0", children: "No classes scheduled for this day." }) : /* @__PURE__ */ jsx2("div", { className: "flex flex-col", children: selectedClasses.map((cls, i) => {
|
|
183
444
|
const booked = getBookingsOnDate(cls, selectedDate);
|
|
184
445
|
const available = cls.maxParticipants - booked;
|
|
185
446
|
const isFull = available <= 0;
|
|
186
|
-
return /* @__PURE__ */
|
|
187
|
-
/* @__PURE__ */
|
|
188
|
-
/* @__PURE__ */
|
|
189
|
-
|
|
190
|
-
/* @__PURE__ */
|
|
191
|
-
|
|
447
|
+
return /* @__PURE__ */ jsxs2("div", { children: [
|
|
448
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex gap-4 py-4 sm:items-center sm:gap-[60px] box-border", children: [
|
|
449
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2 shrink-0 sm:contents", children: [
|
|
450
|
+
/* @__PURE__ */ jsx2("div", { className: "sm:w-[120px] sm:shrink-0", children: /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block", children: formatClassTime(cls) }) }),
|
|
451
|
+
/* @__PURE__ */ jsx2(
|
|
452
|
+
"span",
|
|
453
|
+
{
|
|
454
|
+
className: "inline-flex items-center px-2 py-0.5 rounded-full border text-xs font-medium sm:hidden",
|
|
455
|
+
style: { borderColor: "#0885FE", color: "#0885FE" },
|
|
456
|
+
children: "GROUP"
|
|
457
|
+
}
|
|
458
|
+
),
|
|
459
|
+
/* @__PURE__ */ jsx2("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
460
|
+
/* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
461
|
+
available,
|
|
462
|
+
"/",
|
|
463
|
+
cls.maxParticipants
|
|
464
|
+
] }),
|
|
465
|
+
/* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
|
|
466
|
+
] }) })
|
|
192
467
|
] }),
|
|
193
|
-
/* @__PURE__ */
|
|
194
|
-
/* @__PURE__ */
|
|
195
|
-
|
|
196
|
-
"
|
|
197
|
-
cls.maxParticipants
|
|
468
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-1 flex-col gap-2 min-w-0 sm:contents", children: [
|
|
469
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-2 min-w-0", children: [
|
|
470
|
+
/* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
|
|
471
|
+
cls.hostName && /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
|
|
198
472
|
] }),
|
|
199
|
-
/* @__PURE__ */
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
473
|
+
/* @__PURE__ */ jsx2("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
474
|
+
/* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
475
|
+
available,
|
|
476
|
+
"/",
|
|
477
|
+
cls.maxParticipants
|
|
478
|
+
] }),
|
|
479
|
+
/* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
|
|
480
|
+
] }) }),
|
|
481
|
+
/* @__PURE__ */ jsx2(
|
|
482
|
+
"button",
|
|
483
|
+
{
|
|
484
|
+
className: "w-full sm:w-[200px] sm:shrink-0 px-5 py-[10px] flex items-center justify-center rounded-lg font-body text-base font-bold cursor-pointer box-border",
|
|
485
|
+
style: isFull ? {
|
|
486
|
+
background: "transparent",
|
|
487
|
+
border: "1px solid var(--color-dark, #200C02)",
|
|
488
|
+
color: "var(--color-dark, #200C02)"
|
|
489
|
+
} : {
|
|
490
|
+
background: "var(--color-primary, #3D312B)",
|
|
491
|
+
border: "none",
|
|
492
|
+
color: "var(--color-light, #FEFFF0)"
|
|
493
|
+
},
|
|
494
|
+
onClick: () => {
|
|
495
|
+
if (!cls.id) return;
|
|
496
|
+
onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
|
|
497
|
+
},
|
|
498
|
+
children: isFull ? "Join Waitlist" : "Book Now"
|
|
499
|
+
}
|
|
500
|
+
)
|
|
501
|
+
] })
|
|
217
502
|
] }),
|
|
218
|
-
i < selectedClasses.length - 1 && /* @__PURE__ */
|
|
503
|
+
i < selectedClasses.length - 1 && /* @__PURE__ */ jsx2("div", { className: "h-px bg-black/20" })
|
|
219
504
|
] }, cls.id ?? i);
|
|
220
505
|
}) })
|
|
221
506
|
] });
|
|
222
507
|
}
|
|
223
508
|
function CalendarFoldIcon() {
|
|
224
|
-
return /* @__PURE__ */
|
|
509
|
+
return /* @__PURE__ */ jsxs2(
|
|
225
510
|
"svg",
|
|
226
511
|
{
|
|
227
512
|
width: "16",
|
|
@@ -234,21 +519,22 @@ function CalendarFoldIcon() {
|
|
|
234
519
|
strokeLinejoin: "round",
|
|
235
520
|
style: { flexShrink: 0 },
|
|
236
521
|
children: [
|
|
237
|
-
/* @__PURE__ */
|
|
238
|
-
/* @__PURE__ */
|
|
239
|
-
/* @__PURE__ */
|
|
240
|
-
/* @__PURE__ */
|
|
241
|
-
/* @__PURE__ */
|
|
522
|
+
/* @__PURE__ */ jsx2("path", { d: "M8 2v4" }),
|
|
523
|
+
/* @__PURE__ */ jsx2("path", { d: "M16 2v4" }),
|
|
524
|
+
/* @__PURE__ */ jsx2("path", { d: "M21 17V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11Z" }),
|
|
525
|
+
/* @__PURE__ */ jsx2("path", { d: "M3 10h18" }),
|
|
526
|
+
/* @__PURE__ */ jsx2("path", { d: "M15 22v-4a2 2 0 0 1 2-2h4" })
|
|
242
527
|
]
|
|
243
528
|
}
|
|
244
529
|
);
|
|
245
530
|
}
|
|
246
|
-
function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
|
|
247
|
-
const [schedule, setSchedule] =
|
|
248
|
-
const [loading, setLoading] =
|
|
249
|
-
const [inEditor, setInEditor] =
|
|
250
|
-
const [isHovered, setIsHovered] =
|
|
251
|
-
const
|
|
531
|
+
function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAfter }) {
|
|
532
|
+
const [schedule, setSchedule] = useState2(null);
|
|
533
|
+
const [loading, setLoading] = useState2(true);
|
|
534
|
+
const [inEditor, setInEditor] = useState2(false);
|
|
535
|
+
const [isHovered, setIsHovered] = useState2(false);
|
|
536
|
+
const [modalState, setModalState] = useState2(null);
|
|
537
|
+
const switchScheduleIdRef = useRef(null);
|
|
252
538
|
const dates = useMemo(() => {
|
|
253
539
|
const today = /* @__PURE__ */ new Date();
|
|
254
540
|
today.setHours(0, 0, 0, 0);
|
|
@@ -258,7 +544,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
|
|
|
258
544
|
return d;
|
|
259
545
|
});
|
|
260
546
|
}, []);
|
|
261
|
-
const [selectedIdx, setSelectedIdx] =
|
|
547
|
+
const [selectedIdx, setSelectedIdx] = useState2(0);
|
|
262
548
|
useEffect(() => {
|
|
263
549
|
if (!schedule?.classes) return;
|
|
264
550
|
for (let i = 0; i < dates.length; i++) {
|
|
@@ -284,18 +570,21 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
|
|
|
284
570
|
}, 5e3) : null;
|
|
285
571
|
const handler = (e) => {
|
|
286
572
|
if (e.data?.type === "ow:clear-scheduling-widget") {
|
|
573
|
+
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
287
574
|
setSchedule(null);
|
|
288
575
|
setLoading(false);
|
|
289
576
|
return;
|
|
290
577
|
}
|
|
291
578
|
if (e.data?.type === "ow:switch-schedule") {
|
|
579
|
+
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
292
580
|
switchScheduleIdRef.current = e.data.scheduleId ?? null;
|
|
293
581
|
initialized = false;
|
|
294
582
|
setLoading(true);
|
|
295
|
-
window.parent.postMessage({ type: "ow:request-user-schedules" }, "*");
|
|
583
|
+
window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
296
584
|
return;
|
|
297
585
|
}
|
|
298
586
|
if (e.data?.type !== "ow:user-schedules-response") return;
|
|
587
|
+
if (e.data.insertAfter && e.data.insertAfter !== insertAfter) return;
|
|
299
588
|
if (initialized && switchScheduleIdRef.current === null) return;
|
|
300
589
|
initialized = true;
|
|
301
590
|
if (timer) clearTimeout(timer);
|
|
@@ -306,11 +595,16 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
|
|
|
306
595
|
setSchedule(target);
|
|
307
596
|
setLoading(false);
|
|
308
597
|
if (notifyOnConnect && target && !isSwitching) {
|
|
309
|
-
window.parent.postMessage({
|
|
598
|
+
window.parent.postMessage({
|
|
599
|
+
type: "ow:schedule-connected",
|
|
600
|
+
schedule: { id: target.id, name: target.name },
|
|
601
|
+
insertAfter
|
|
602
|
+
}, "*");
|
|
603
|
+
window.postMessage({ type: "ow:schedule-linked", scheduleId: target.id, insertAfter }, "*");
|
|
310
604
|
}
|
|
311
605
|
};
|
|
312
606
|
window.addEventListener("message", handler);
|
|
313
|
-
if (!startEmpty) window.parent.postMessage({ type: "ow:request-user-schedules" }, "*");
|
|
607
|
+
if (!startEmpty) window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
314
608
|
return () => {
|
|
315
609
|
window.removeEventListener("message", handler);
|
|
316
610
|
if (timer) clearTimeout(timer);
|
|
@@ -354,31 +648,51 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
|
|
|
354
648
|
return void 0;
|
|
355
649
|
}
|
|
356
650
|
})() : void 0;
|
|
651
|
+
const handleModalSubmit = async (email) => {
|
|
652
|
+
if (!modalState) return;
|
|
653
|
+
const { mode, classId, classDate } = modalState;
|
|
654
|
+
const endpoint = mode === "book" ? `${API_URL}/api/schedule/class/${classId}/booking` : `${API_URL}/api/schedule/class/${classId}/waitlist`;
|
|
655
|
+
const res = await fetch(endpoint, {
|
|
656
|
+
method: "POST",
|
|
657
|
+
headers: { "Content-Type": "application/json" },
|
|
658
|
+
body: JSON.stringify({ classDate: classDate.toISOString(), email })
|
|
659
|
+
});
|
|
660
|
+
if (!res.ok) {
|
|
661
|
+
const data = await res.json().catch(() => ({}));
|
|
662
|
+
throw new Error(data?.message ?? "Something went wrong. Please try again.");
|
|
663
|
+
}
|
|
664
|
+
};
|
|
357
665
|
const handleReplaceSchedule = () => {
|
|
358
666
|
setIsHovered(false);
|
|
359
667
|
if (schedule) {
|
|
360
|
-
window.parent.postMessage({
|
|
668
|
+
window.parent.postMessage({
|
|
669
|
+
type: "ow:schedule-connected",
|
|
670
|
+
schedule: { id: schedule.id, name: schedule.name },
|
|
671
|
+
insertAfter
|
|
672
|
+
}, "*");
|
|
361
673
|
} else {
|
|
362
|
-
window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
|
|
674
|
+
window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
|
|
363
675
|
}
|
|
364
676
|
};
|
|
365
|
-
|
|
677
|
+
const sectionId = `scheduling-${insertAfter}`;
|
|
678
|
+
return /* @__PURE__ */ jsxs2(
|
|
366
679
|
"section",
|
|
367
680
|
{
|
|
368
|
-
"data-ohw-section":
|
|
369
|
-
|
|
681
|
+
"data-ohw-section": sectionId,
|
|
682
|
+
"data-ohw-scheduling-anchor": insertAfter,
|
|
683
|
+
className: "w-full box-border py-10 px-5 sm:py-20 sm:px-16 font-body bg-(--color-light,#FEFFF0) relative",
|
|
370
684
|
onMouseEnter: () => inEditor && setIsHovered(true),
|
|
371
685
|
onMouseLeave: () => setIsHovered(false),
|
|
372
686
|
children: [
|
|
373
|
-
inEditor && isHovered && !!schedule && /* @__PURE__ */
|
|
687
|
+
inEditor && isHovered && !!schedule && /* @__PURE__ */ jsxs2(
|
|
374
688
|
"div",
|
|
375
689
|
{
|
|
376
690
|
className: "absolute inset-0 z-10 pointer-events-auto cursor-pointer",
|
|
377
691
|
onClick: handleReplaceSchedule,
|
|
378
692
|
children: [
|
|
379
|
-
/* @__PURE__ */
|
|
380
|
-
/* @__PURE__ */
|
|
381
|
-
/* @__PURE__ */
|
|
693
|
+
/* @__PURE__ */ jsx2("div", { className: "absolute inset-0", style: { background: "#0885FE", opacity: 0.18 } }),
|
|
694
|
+
/* @__PURE__ */ jsx2("div", { className: "absolute inset-0", style: { boxShadow: "inset 0 0 0 1.5px #0885FE" } }),
|
|
695
|
+
/* @__PURE__ */ jsx2("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs2(
|
|
382
696
|
"div",
|
|
383
697
|
{
|
|
384
698
|
className: "bg-white border border-[#E7E5E4] rounded-md px-3 py-1.5 flex items-center gap-1.5",
|
|
@@ -390,7 +704,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
|
|
|
390
704
|
color: "#0C0A09"
|
|
391
705
|
},
|
|
392
706
|
children: [
|
|
393
|
-
/* @__PURE__ */
|
|
707
|
+
/* @__PURE__ */ jsx2(CalendarFoldIcon, {}),
|
|
394
708
|
"Replace schedule"
|
|
395
709
|
]
|
|
396
710
|
}
|
|
@@ -398,24 +712,34 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
|
|
|
398
712
|
]
|
|
399
713
|
}
|
|
400
714
|
),
|
|
401
|
-
/* @__PURE__ */
|
|
402
|
-
/* @__PURE__ */
|
|
403
|
-
/* @__PURE__ */
|
|
404
|
-
schedule?.description && /* @__PURE__ */
|
|
715
|
+
/* @__PURE__ */ jsxs2("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
|
|
716
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-4", children: [
|
|
717
|
+
/* @__PURE__ */ jsx2("h2", { className: "font-display text-4xl sm:text-5xl font-bold text-center m-0 text-(--color-dark,#200C02)", children: schedule?.name ?? "Book an appointment" }),
|
|
718
|
+
schedule?.description && /* @__PURE__ */ jsx2("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
|
|
405
719
|
] }),
|
|
406
|
-
/* @__PURE__ */
|
|
407
|
-
timezoneLabel && /* @__PURE__ */
|
|
408
|
-
/* @__PURE__ */
|
|
720
|
+
/* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col items-end gap-3", children: [
|
|
721
|
+
timezoneLabel && /* @__PURE__ */ jsx2("span", { className: "font-body text-sm font-normal text-(--color-accent,#A89B83)", children: timezoneLabel }),
|
|
722
|
+
/* @__PURE__ */ jsx2("div", { className: "w-full p-0 sm:p-10 box-border", children: loading ? /* @__PURE__ */ jsx2(LoadingSkeleton, {}) : !schedule ? /* @__PURE__ */ jsx2(EmptyState, { inEditor }) : /* @__PURE__ */ jsx2(
|
|
409
723
|
ScheduleView,
|
|
410
724
|
{
|
|
411
725
|
schedule,
|
|
412
726
|
dates,
|
|
413
727
|
selectedIdx,
|
|
414
|
-
onSelectDate: setSelectedIdx
|
|
728
|
+
onSelectDate: setSelectedIdx,
|
|
729
|
+
onOpenModal: inEditor ? void 0 : (mode, classId, classDate) => setModalState({ mode, classId, classDate })
|
|
415
730
|
}
|
|
416
731
|
) })
|
|
417
732
|
] })
|
|
418
|
-
] })
|
|
733
|
+
] }),
|
|
734
|
+
modalState && /* @__PURE__ */ jsx2(
|
|
735
|
+
EmailCaptureModal,
|
|
736
|
+
{
|
|
737
|
+
title: modalState.mode === "book" ? "Confirm your spot" : "Leave your email",
|
|
738
|
+
subtitle: modalState.mode === "book" ? "We'll send your booking confirmation here." : "We'll let you know when spots open up!",
|
|
739
|
+
onSubmit: handleModalSubmit,
|
|
740
|
+
onClose: () => setModalState(null)
|
|
741
|
+
}
|
|
742
|
+
)
|
|
419
743
|
]
|
|
420
744
|
}
|
|
421
745
|
);
|
|
@@ -3742,7 +4066,7 @@ var cva = (base, config) => (props) => {
|
|
|
3742
4066
|
|
|
3743
4067
|
// src/ui/toggle.tsx
|
|
3744
4068
|
import { Toggle as TogglePrimitive } from "radix-ui";
|
|
3745
|
-
import { jsx as
|
|
4069
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
3746
4070
|
var toggleVariants = cva(
|
|
3747
4071
|
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
3748
4072
|
{
|
|
@@ -3769,7 +4093,7 @@ function Toggle({
|
|
|
3769
4093
|
size,
|
|
3770
4094
|
...props
|
|
3771
4095
|
}) {
|
|
3772
|
-
return /* @__PURE__ */
|
|
4096
|
+
return /* @__PURE__ */ jsx3(
|
|
3773
4097
|
TogglePrimitive.Root,
|
|
3774
4098
|
{
|
|
3775
4099
|
"data-slot": "toggle",
|
|
@@ -3780,7 +4104,7 @@ function Toggle({
|
|
|
3780
4104
|
}
|
|
3781
4105
|
|
|
3782
4106
|
// src/ui/toggle-group.tsx
|
|
3783
|
-
import { jsx as
|
|
4107
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
3784
4108
|
var ToggleGroupContext = React2.createContext({
|
|
3785
4109
|
value: "",
|
|
3786
4110
|
onValueChange: () => {
|
|
@@ -3793,13 +4117,13 @@ function ToggleGroup({
|
|
|
3793
4117
|
children,
|
|
3794
4118
|
...props
|
|
3795
4119
|
}) {
|
|
3796
|
-
return /* @__PURE__ */
|
|
4120
|
+
return /* @__PURE__ */ jsx4(
|
|
3797
4121
|
"div",
|
|
3798
4122
|
{
|
|
3799
4123
|
role: "group",
|
|
3800
4124
|
className: cn("flex items-center gap-1 p-1 bg-muted rounded-md", className),
|
|
3801
4125
|
...props,
|
|
3802
|
-
children: /* @__PURE__ */
|
|
4126
|
+
children: /* @__PURE__ */ jsx4(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
|
|
3803
4127
|
}
|
|
3804
4128
|
);
|
|
3805
4129
|
}
|
|
@@ -3810,7 +4134,7 @@ function ToggleGroupItem({
|
|
|
3810
4134
|
...props
|
|
3811
4135
|
}) {
|
|
3812
4136
|
const { value: groupValue, onValueChange } = React2.useContext(ToggleGroupContext);
|
|
3813
|
-
return /* @__PURE__ */
|
|
4137
|
+
return /* @__PURE__ */ jsx4(
|
|
3814
4138
|
Toggle,
|
|
3815
4139
|
{
|
|
3816
4140
|
pressed: groupValue === value,
|
|
@@ -3852,7 +4176,7 @@ function isEditSessionActive() {
|
|
|
3852
4176
|
}
|
|
3853
4177
|
|
|
3854
4178
|
// src/ui/badge.tsx
|
|
3855
|
-
import { jsx as
|
|
4179
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
3856
4180
|
var badgeVariants = cva(
|
|
3857
4181
|
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
3858
4182
|
{
|
|
@@ -3870,11 +4194,11 @@ var badgeVariants = cva(
|
|
|
3870
4194
|
}
|
|
3871
4195
|
);
|
|
3872
4196
|
function Badge({ className, variant, ...props }) {
|
|
3873
|
-
return /* @__PURE__ */
|
|
4197
|
+
return /* @__PURE__ */ jsx5("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
3874
4198
|
}
|
|
3875
4199
|
|
|
3876
4200
|
// src/OhhwellsBridge.tsx
|
|
3877
|
-
import { Fragment as Fragment2, jsx as
|
|
4201
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
3878
4202
|
var PRIMARY = "#0885FE";
|
|
3879
4203
|
var IMAGE_FADE_MS = 300;
|
|
3880
4204
|
function runOpacityFade(el, onDone) {
|
|
@@ -3935,24 +4259,135 @@ function getSectionsTracker() {
|
|
|
3935
4259
|
}
|
|
3936
4260
|
return el;
|
|
3937
4261
|
}
|
|
3938
|
-
function
|
|
3939
|
-
const
|
|
3940
|
-
|
|
3941
|
-
|
|
4262
|
+
function updateSectionScheduleId(insertAfter, scheduleId) {
|
|
4263
|
+
const tracker = getSectionsTracker();
|
|
4264
|
+
let sections = [];
|
|
4265
|
+
try {
|
|
4266
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
4267
|
+
} catch {
|
|
4268
|
+
}
|
|
4269
|
+
const currentPath = window.location.pathname;
|
|
4270
|
+
const updated = sections.map((s) => {
|
|
4271
|
+
if (s.type !== "scheduling" || s.insertAfter !== insertAfter) return s;
|
|
4272
|
+
if (s.pagePath && s.pagePath !== currentPath) return s;
|
|
4273
|
+
return { ...s, scheduleId };
|
|
4274
|
+
});
|
|
4275
|
+
tracker.textContent = JSON.stringify(updated);
|
|
4276
|
+
return tracker.textContent ?? "[]";
|
|
4277
|
+
}
|
|
4278
|
+
function schedulingSectionId(insertAfter) {
|
|
4279
|
+
return `scheduling-${insertAfter}`;
|
|
4280
|
+
}
|
|
4281
|
+
var INSERT_BEFORE_MARKER = ":before:";
|
|
4282
|
+
function parseSchedulingInsertAfter(insertAfter) {
|
|
4283
|
+
const idx = insertAfter.indexOf(INSERT_BEFORE_MARKER);
|
|
4284
|
+
if (idx < 0) return { anchor: insertAfter, insertBefore: null };
|
|
4285
|
+
return {
|
|
4286
|
+
anchor: insertAfter.slice(0, idx),
|
|
4287
|
+
insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
|
|
4288
|
+
};
|
|
4289
|
+
}
|
|
4290
|
+
function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
|
|
4291
|
+
const parsed = parseSchedulingInsertAfter(insertAfter);
|
|
4292
|
+
const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
|
|
4293
|
+
const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
|
|
4294
|
+
return { effectiveInsertAfter, insertBefore };
|
|
4295
|
+
}
|
|
4296
|
+
function getSchedulingMountPoint(insertAfter) {
|
|
4297
|
+
const { anchor } = parseSchedulingInsertAfter(insertAfter);
|
|
4298
|
+
let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
|
|
4299
|
+
if (!anchorEl && anchor === "scheduling") {
|
|
4300
|
+
const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
|
|
4301
|
+
anchorEl = widgets.at(-1) ?? null;
|
|
4302
|
+
}
|
|
4303
|
+
if (!anchorEl) return null;
|
|
4304
|
+
return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
4305
|
+
}
|
|
4306
|
+
function schedulingMountDepth(insertAfter) {
|
|
4307
|
+
if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
|
|
4308
|
+
return insertAfter.split(INSERT_BEFORE_MARKER).length - 1;
|
|
4309
|
+
}
|
|
4310
|
+
function getPageSchedulingEntries(raw) {
|
|
4311
|
+
if (!raw) return [];
|
|
4312
|
+
try {
|
|
4313
|
+
const entries = JSON.parse(raw);
|
|
4314
|
+
const currentPath = window.location.pathname;
|
|
4315
|
+
return entries.filter((e) => e.type === "scheduling" && (!e.pagePath || e.pagePath === currentPath));
|
|
4316
|
+
} catch {
|
|
4317
|
+
return [];
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
function isSchedulingWidgetMissing(entry) {
|
|
4321
|
+
const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
|
|
4322
|
+
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
|
|
4323
|
+
}
|
|
4324
|
+
function hasMissingSchedulingWidgets(entries) {
|
|
4325
|
+
return entries.some(isSchedulingWidgetMissing);
|
|
4326
|
+
}
|
|
4327
|
+
function retryMissingSchedulingMounts(entries, notifyOnConnect = false) {
|
|
4328
|
+
if (!hasMissingSchedulingWidgets(entries)) return;
|
|
4329
|
+
mountSchedulingEntries(entries, notifyOnConnect);
|
|
4330
|
+
}
|
|
4331
|
+
function initSectionsFromContent(content, removeExisting = false) {
|
|
4332
|
+
const raw = content["__ohw_sections"];
|
|
4333
|
+
if (!raw) return;
|
|
4334
|
+
try {
|
|
4335
|
+
if (removeExisting) {
|
|
4336
|
+
document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => el.remove());
|
|
4337
|
+
}
|
|
4338
|
+
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
4339
|
+
if (inEditor) getSectionsTracker().textContent = raw;
|
|
4340
|
+
const pageEntries = getPageSchedulingEntries(raw);
|
|
4341
|
+
mountSchedulingEntries(pageEntries, false);
|
|
4342
|
+
requestAnimationFrame(() => retryMissingSchedulingMounts(pageEntries, false));
|
|
4343
|
+
setTimeout(() => retryMissingSchedulingMounts(pageEntries, false), 250);
|
|
4344
|
+
} catch {
|
|
4345
|
+
}
|
|
4346
|
+
}
|
|
4347
|
+
function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
|
|
4348
|
+
const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
|
|
4349
|
+
const sectionId = schedulingSectionId(effectiveInsertAfter);
|
|
4350
|
+
if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
|
|
4351
|
+
const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
|
|
4352
|
+
if (!mountPoint) return false;
|
|
3942
4353
|
const container = document.createElement("div");
|
|
3943
4354
|
container.dataset.ohwSectionContainer = "scheduling";
|
|
3944
|
-
|
|
3945
|
-
|
|
4355
|
+
if (insertBefore) {
|
|
4356
|
+
const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
|
|
4357
|
+
const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
|
|
4358
|
+
if (!beforePoint) return false;
|
|
4359
|
+
beforePoint.insertAdjacentElement("beforebegin", container);
|
|
4360
|
+
} else {
|
|
4361
|
+
let tail = mountPoint;
|
|
4362
|
+
while (tail.nextElementSibling?.dataset.ohwSectionContainer === "scheduling") {
|
|
4363
|
+
tail = tail.nextElementSibling;
|
|
4364
|
+
}
|
|
4365
|
+
tail.insertAdjacentElement("afterend", container);
|
|
4366
|
+
}
|
|
4367
|
+
const root = createRoot(container);
|
|
4368
|
+
flushSync(() => {
|
|
4369
|
+
root.render(
|
|
4370
|
+
/* @__PURE__ */ jsx6(
|
|
4371
|
+
SchedulingWidget,
|
|
4372
|
+
{
|
|
4373
|
+
notifyOnConnect,
|
|
4374
|
+
initialScheduleId: scheduleId,
|
|
4375
|
+
insertAfter: effectiveInsertAfter
|
|
4376
|
+
}
|
|
4377
|
+
)
|
|
4378
|
+
);
|
|
4379
|
+
});
|
|
3946
4380
|
const tracker = getSectionsTracker();
|
|
3947
4381
|
let sections = [];
|
|
3948
4382
|
try {
|
|
3949
4383
|
sections = JSON.parse(tracker.textContent || "[]");
|
|
3950
4384
|
} catch {
|
|
3951
4385
|
}
|
|
3952
|
-
|
|
4386
|
+
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
4387
|
+
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
|
|
3953
4388
|
sections.push({
|
|
3954
4389
|
type: "scheduling",
|
|
3955
|
-
insertAfter,
|
|
4390
|
+
insertAfter: effectiveInsertAfter,
|
|
3956
4391
|
pagePath: window.location.pathname,
|
|
3957
4392
|
...scheduleId ? { scheduleId } : {}
|
|
3958
4393
|
});
|
|
@@ -3960,6 +4395,17 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId)
|
|
|
3960
4395
|
}
|
|
3961
4396
|
return true;
|
|
3962
4397
|
}
|
|
4398
|
+
function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
4399
|
+
const pending = entries.filter((e) => e.type === "scheduling").sort((a, b) => schedulingMountDepth(a.insertAfter) - schedulingMountDepth(b.insertAfter));
|
|
4400
|
+
for (let attempt = 0; attempt < pending.length + 1 && pending.length > 0; attempt++) {
|
|
4401
|
+
for (let i = pending.length - 1; i >= 0; i--) {
|
|
4402
|
+
const { insertAfter, scheduleId } = pending[i];
|
|
4403
|
+
if (mountSchedulingWidget(insertAfter, notifyOnConnect, scheduleId)) {
|
|
4404
|
+
pending.splice(i, 1);
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4407
|
+
}
|
|
4408
|
+
}
|
|
3963
4409
|
function collectEditableNodes() {
|
|
3964
4410
|
return Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
|
|
3965
4411
|
if (el.dataset.ohwEditable === "image") {
|
|
@@ -4117,7 +4563,7 @@ var TOOLBAR_GROUPS = [
|
|
|
4117
4563
|
];
|
|
4118
4564
|
function GlowFrame({ rect, elRef }) {
|
|
4119
4565
|
const GAP = 6;
|
|
4120
|
-
return /* @__PURE__ */
|
|
4566
|
+
return /* @__PURE__ */ jsx6(
|
|
4121
4567
|
"div",
|
|
4122
4568
|
{
|
|
4123
4569
|
ref: elRef,
|
|
@@ -4168,7 +4614,7 @@ function FloatingToolbar({
|
|
|
4168
4614
|
activeCommands
|
|
4169
4615
|
}) {
|
|
4170
4616
|
const { top, left, transform } = calcToolbarPos(rect, parentScroll);
|
|
4171
|
-
return /* @__PURE__ */
|
|
4617
|
+
return /* @__PURE__ */ jsx6(
|
|
4172
4618
|
"div",
|
|
4173
4619
|
{
|
|
4174
4620
|
ref: elRef,
|
|
@@ -4191,11 +4637,11 @@ function FloatingToolbar({
|
|
|
4191
4637
|
pointerEvents: "auto",
|
|
4192
4638
|
whiteSpace: "nowrap"
|
|
4193
4639
|
},
|
|
4194
|
-
children: TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */
|
|
4195
|
-
gi > 0 && /* @__PURE__ */
|
|
4640
|
+
children: TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs3(React3.Fragment, { children: [
|
|
4641
|
+
gi > 0 && /* @__PURE__ */ jsx6("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
|
|
4196
4642
|
btns.map((btn) => {
|
|
4197
4643
|
const isActive = activeCommands.has(btn.cmd);
|
|
4198
|
-
return /* @__PURE__ */
|
|
4644
|
+
return /* @__PURE__ */ jsx6(
|
|
4199
4645
|
"button",
|
|
4200
4646
|
{
|
|
4201
4647
|
title: btn.title,
|
|
@@ -4221,7 +4667,7 @@ function FloatingToolbar({
|
|
|
4221
4667
|
flexShrink: 0,
|
|
4222
4668
|
padding: 6
|
|
4223
4669
|
},
|
|
4224
|
-
children: /* @__PURE__ */
|
|
4670
|
+
children: /* @__PURE__ */ jsx6(
|
|
4225
4671
|
"svg",
|
|
4226
4672
|
{
|
|
4227
4673
|
width: "16",
|
|
@@ -4254,7 +4700,7 @@ function StateToggle({
|
|
|
4254
4700
|
states,
|
|
4255
4701
|
onStateChange
|
|
4256
4702
|
}) {
|
|
4257
|
-
return /* @__PURE__ */
|
|
4703
|
+
return /* @__PURE__ */ jsx6(
|
|
4258
4704
|
ToggleGroup,
|
|
4259
4705
|
{
|
|
4260
4706
|
"data-ohw-state-toggle": "",
|
|
@@ -4268,17 +4714,34 @@ function StateToggle({
|
|
|
4268
4714
|
left: rect.right - 8,
|
|
4269
4715
|
transform: "translateX(-100%)"
|
|
4270
4716
|
},
|
|
4271
|
-
children: states.map((state) => /* @__PURE__ */
|
|
4717
|
+
children: states.map((state) => /* @__PURE__ */ jsx6(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
|
|
4272
4718
|
}
|
|
4273
4719
|
);
|
|
4274
4720
|
}
|
|
4275
4721
|
var contentCache = /* @__PURE__ */ new Map();
|
|
4722
|
+
function resolveSubdomain(subdomainFromQuery) {
|
|
4723
|
+
if (subdomainFromQuery) return subdomainFromQuery;
|
|
4724
|
+
if (typeof window !== "undefined") {
|
|
4725
|
+
const parts = window.location.hostname.split(".");
|
|
4726
|
+
if (parts.length >= 3 && parts[0] !== "www") return parts[0];
|
|
4727
|
+
}
|
|
4728
|
+
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL;
|
|
4729
|
+
if (siteUrl) {
|
|
4730
|
+
try {
|
|
4731
|
+
const host = new URL(siteUrl).hostname;
|
|
4732
|
+
const siteParts = host.split(".");
|
|
4733
|
+
if (siteParts.length >= 3 && siteParts[0] !== "www") return siteParts[0];
|
|
4734
|
+
} catch {
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4737
|
+
return "";
|
|
4738
|
+
}
|
|
4276
4739
|
function OhhwellsBridge() {
|
|
4277
4740
|
const pathname = usePathname();
|
|
4278
4741
|
const router = useRouter();
|
|
4279
4742
|
const searchParams = useSearchParams();
|
|
4280
4743
|
const isEditMode = isEditSessionActive();
|
|
4281
|
-
const [bridgeRoot, setBridgeRoot] =
|
|
4744
|
+
const [bridgeRoot, setBridgeRoot] = useState3(null);
|
|
4282
4745
|
useEffect2(() => {
|
|
4283
4746
|
const figtreeFontId = "ohw-figtree-font";
|
|
4284
4747
|
if (!document.getElementById(figtreeFontId)) {
|
|
@@ -4302,43 +4765,39 @@ function OhhwellsBridge() {
|
|
|
4302
4765
|
};
|
|
4303
4766
|
}, []);
|
|
4304
4767
|
const subdomainFromQuery = searchParams.get("subdomain");
|
|
4305
|
-
const subdomain = subdomainFromQuery
|
|
4306
|
-
if (typeof window === "undefined") return "";
|
|
4307
|
-
const parts = window.location.hostname.split(".");
|
|
4308
|
-
return parts.length >= 3 && parts[0] !== "www" ? parts[0] : "";
|
|
4309
|
-
})();
|
|
4768
|
+
const subdomain = resolveSubdomain(subdomainFromQuery);
|
|
4310
4769
|
const postToParent = useCallback((data) => {
|
|
4311
4770
|
if (typeof window !== "undefined" && window.parent !== window) {
|
|
4312
4771
|
window.parent.postMessage(data, "*");
|
|
4313
4772
|
}
|
|
4314
4773
|
}, []);
|
|
4315
|
-
const [fetchState, setFetchState] =
|
|
4316
|
-
const autoSaveTimers =
|
|
4317
|
-
const activeElRef =
|
|
4318
|
-
const originalContentRef =
|
|
4319
|
-
const activeStateElRef =
|
|
4320
|
-
const parentScrollRef =
|
|
4321
|
-
const toolbarElRef =
|
|
4322
|
-
const glowElRef =
|
|
4323
|
-
const hoveredImageRef =
|
|
4324
|
-
const hoveredImageHasTextOverlapRef =
|
|
4325
|
-
const hoveredGapRef =
|
|
4326
|
-
const imageUnhoverTimerRef =
|
|
4327
|
-
const imageShowTimerRef =
|
|
4328
|
-
const editStylesRef =
|
|
4329
|
-
const activateRef =
|
|
4774
|
+
const [fetchState, setFetchState] = useState3("idle");
|
|
4775
|
+
const autoSaveTimers = useRef2(/* @__PURE__ */ new Map());
|
|
4776
|
+
const activeElRef = useRef2(null);
|
|
4777
|
+
const originalContentRef = useRef2(null);
|
|
4778
|
+
const activeStateElRef = useRef2(null);
|
|
4779
|
+
const parentScrollRef = useRef2(null);
|
|
4780
|
+
const toolbarElRef = useRef2(null);
|
|
4781
|
+
const glowElRef = useRef2(null);
|
|
4782
|
+
const hoveredImageRef = useRef2(null);
|
|
4783
|
+
const hoveredImageHasTextOverlapRef = useRef2(false);
|
|
4784
|
+
const hoveredGapRef = useRef2(null);
|
|
4785
|
+
const imageUnhoverTimerRef = useRef2(null);
|
|
4786
|
+
const imageShowTimerRef = useRef2(null);
|
|
4787
|
+
const editStylesRef = useRef2(null);
|
|
4788
|
+
const activateRef = useRef2(() => {
|
|
4330
4789
|
});
|
|
4331
|
-
const deactivateRef =
|
|
4790
|
+
const deactivateRef = useRef2(() => {
|
|
4332
4791
|
});
|
|
4333
|
-
const refreshActiveCommandsRef =
|
|
4792
|
+
const refreshActiveCommandsRef = useRef2(() => {
|
|
4334
4793
|
});
|
|
4335
|
-
const postToParentRef =
|
|
4794
|
+
const postToParentRef = useRef2(postToParent);
|
|
4336
4795
|
postToParentRef.current = postToParent;
|
|
4337
|
-
const [toolbarRect, setToolbarRect] =
|
|
4338
|
-
const [toggleState, setToggleState] =
|
|
4339
|
-
const [maxBadge, setMaxBadge] =
|
|
4340
|
-
const [activeCommands, setActiveCommands] =
|
|
4341
|
-
const [sectionGap, setSectionGap] =
|
|
4796
|
+
const [toolbarRect, setToolbarRect] = useState3(null);
|
|
4797
|
+
const [toggleState, setToggleState] = useState3(null);
|
|
4798
|
+
const [maxBadge, setMaxBadge] = useState3(null);
|
|
4799
|
+
const [activeCommands, setActiveCommands] = useState3(/* @__PURE__ */ new Set());
|
|
4800
|
+
const [sectionGap, setSectionGap] = useState3(null);
|
|
4342
4801
|
useEffect2(() => {
|
|
4343
4802
|
const update = () => {
|
|
4344
4803
|
const el = activeElRef.current;
|
|
@@ -4416,18 +4875,6 @@ function OhhwellsBridge() {
|
|
|
4416
4875
|
}
|
|
4417
4876
|
const applyContent = (content) => {
|
|
4418
4877
|
const imageLoads = [];
|
|
4419
|
-
document.querySelectorAll("[data-ohw-section-container]").forEach((el) => el.remove());
|
|
4420
|
-
if (content["__ohw_sections"]) {
|
|
4421
|
-
try {
|
|
4422
|
-
const entries = JSON.parse(content["__ohw_sections"]);
|
|
4423
|
-
const currentPath = window.location.pathname;
|
|
4424
|
-
entries.forEach(({ type, insertAfter, scheduleId, pagePath }) => {
|
|
4425
|
-
if (pagePath && pagePath !== currentPath) return;
|
|
4426
|
-
if (type === "scheduling") mountSchedulingWidget(insertAfter, false, scheduleId);
|
|
4427
|
-
});
|
|
4428
|
-
} catch {
|
|
4429
|
-
}
|
|
4430
|
-
}
|
|
4431
4878
|
for (const [key, val] of Object.entries(content)) {
|
|
4432
4879
|
if (key === "__ohw_sections") continue;
|
|
4433
4880
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -4448,6 +4895,7 @@ function OhhwellsBridge() {
|
|
|
4448
4895
|
}
|
|
4449
4896
|
});
|
|
4450
4897
|
}
|
|
4898
|
+
initSectionsFromContent(content, true);
|
|
4451
4899
|
return imageLoads.length > 0 ? Promise.all(imageLoads).then(() => {
|
|
4452
4900
|
}) : Promise.resolve();
|
|
4453
4901
|
};
|
|
@@ -4474,20 +4922,11 @@ function OhhwellsBridge() {
|
|
|
4474
4922
|
}, [subdomain, isEditMode, pathname]);
|
|
4475
4923
|
useEffect2(() => {
|
|
4476
4924
|
if (!subdomain || isEditMode) return;
|
|
4925
|
+
let debounceTimer = null;
|
|
4477
4926
|
const applyFromCache = () => {
|
|
4478
4927
|
const content = contentCache.get(subdomain);
|
|
4479
4928
|
if (!content) return;
|
|
4480
|
-
|
|
4481
|
-
try {
|
|
4482
|
-
const entries = JSON.parse(content["__ohw_sections"]);
|
|
4483
|
-
const currentPath = window.location.pathname;
|
|
4484
|
-
entries.forEach(({ type, insertAfter, scheduleId, pagePath }) => {
|
|
4485
|
-
if (pagePath && pagePath !== currentPath) return;
|
|
4486
|
-
if (type === "scheduling") mountSchedulingWidget(insertAfter, false, scheduleId);
|
|
4487
|
-
});
|
|
4488
|
-
} catch {
|
|
4489
|
-
}
|
|
4490
|
-
}
|
|
4929
|
+
retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
|
|
4491
4930
|
for (const [key, val] of Object.entries(content)) {
|
|
4492
4931
|
if (key === "__ohw_sections") continue;
|
|
4493
4932
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -4503,11 +4942,18 @@ function OhhwellsBridge() {
|
|
|
4503
4942
|
});
|
|
4504
4943
|
}
|
|
4505
4944
|
};
|
|
4506
|
-
|
|
4507
|
-
|
|
4945
|
+
const scheduleApply = () => {
|
|
4946
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
4947
|
+
debounceTimer = setTimeout(applyFromCache, 150);
|
|
4948
|
+
};
|
|
4949
|
+
scheduleApply();
|
|
4950
|
+
const observer = new MutationObserver(scheduleApply);
|
|
4508
4951
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
4509
|
-
return () =>
|
|
4510
|
-
|
|
4952
|
+
return () => {
|
|
4953
|
+
observer.disconnect();
|
|
4954
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
4955
|
+
};
|
|
4956
|
+
}, [subdomain, isEditMode, pathname]);
|
|
4511
4957
|
useLayoutEffect(() => {
|
|
4512
4958
|
const el = document.getElementById("ohw-loader");
|
|
4513
4959
|
if (!el) return;
|
|
@@ -5080,17 +5526,10 @@ function OhhwellsBridge() {
|
|
|
5080
5526
|
if (e.data?.type !== "ow:hydrate") return;
|
|
5081
5527
|
const content = e.data.content;
|
|
5082
5528
|
if (!content) return;
|
|
5529
|
+
let sectionsJson = null;
|
|
5083
5530
|
for (const [key, val] of Object.entries(content)) {
|
|
5084
5531
|
if (key === "__ohw_sections") {
|
|
5085
|
-
|
|
5086
|
-
const entries = JSON.parse(val);
|
|
5087
|
-
const tracker = getSectionsTracker();
|
|
5088
|
-
tracker.textContent = val;
|
|
5089
|
-
entries.forEach(({ type, insertAfter, scheduleId }) => {
|
|
5090
|
-
if (type === "scheduling") mountSchedulingWidget(insertAfter, false, scheduleId);
|
|
5091
|
-
});
|
|
5092
|
-
} catch {
|
|
5093
|
-
}
|
|
5532
|
+
sectionsJson = val;
|
|
5094
5533
|
continue;
|
|
5095
5534
|
}
|
|
5096
5535
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -5104,6 +5543,9 @@ function OhhwellsBridge() {
|
|
|
5104
5543
|
}
|
|
5105
5544
|
});
|
|
5106
5545
|
}
|
|
5546
|
+
if (sectionsJson) {
|
|
5547
|
+
initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
|
|
5548
|
+
}
|
|
5107
5549
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
5108
5550
|
};
|
|
5109
5551
|
window.addEventListener("message", handleHydrate);
|
|
@@ -5154,9 +5596,9 @@ function OhhwellsBridge() {
|
|
|
5154
5596
|
};
|
|
5155
5597
|
const handleInsertSection = (e) => {
|
|
5156
5598
|
if (e.data?.type !== "ow:insert-section") return;
|
|
5157
|
-
const { widgetType, insertAfter } = e.data;
|
|
5599
|
+
const { widgetType, insertAfter, insertBefore } = e.data;
|
|
5158
5600
|
if (widgetType !== "scheduling") return;
|
|
5159
|
-
const inserted = mountSchedulingWidget(insertAfter, true);
|
|
5601
|
+
const inserted = mountSchedulingWidget(insertAfter, true, void 0, insertBefore ?? null);
|
|
5160
5602
|
if (inserted) {
|
|
5161
5603
|
const tracker = getSectionsTracker();
|
|
5162
5604
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
@@ -5167,39 +5609,31 @@ function OhhwellsBridge() {
|
|
|
5167
5609
|
const handleSwitchSchedule = (e) => {
|
|
5168
5610
|
if (e.data?.type !== "ow:switch-schedule") return;
|
|
5169
5611
|
const scheduleId = e.data.scheduleId;
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
const
|
|
5178
|
-
const
|
|
5179
|
-
|
|
5180
|
-
);
|
|
5181
|
-
|
|
5182
|
-
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
5612
|
+
const insertAfter = e.data.insertAfter;
|
|
5613
|
+
if (!scheduleId || !insertAfter) return;
|
|
5614
|
+
const text = updateSectionScheduleId(insertAfter, scheduleId);
|
|
5615
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
5616
|
+
};
|
|
5617
|
+
const handleScheduleLinked = (e) => {
|
|
5618
|
+
if (e.data?.type !== "ow:schedule-linked") return;
|
|
5619
|
+
const scheduleId = e.data.scheduleId;
|
|
5620
|
+
const insertAfter = e.data.insertAfter;
|
|
5621
|
+
if (!scheduleId || !insertAfter) return;
|
|
5622
|
+
const text = updateSectionScheduleId(insertAfter, scheduleId);
|
|
5623
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
5183
5624
|
};
|
|
5184
5625
|
const handleClearSchedulingWidget = (e) => {
|
|
5185
5626
|
if (e.data?.type !== "ow:clear-scheduling-widget") return;
|
|
5186
|
-
const
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
} catch {
|
|
5191
|
-
}
|
|
5192
|
-
const currentPath = window.location.pathname;
|
|
5193
|
-
const updated = sections.map(
|
|
5194
|
-
(s) => s.type === "scheduling" && s.pagePath === currentPath ? { ...s, scheduleId: null } : s
|
|
5195
|
-
);
|
|
5196
|
-
tracker.textContent = JSON.stringify(updated);
|
|
5197
|
-
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
5627
|
+
const insertAfter = e.data.insertAfter;
|
|
5628
|
+
if (!insertAfter) return;
|
|
5629
|
+
const text = updateSectionScheduleId(insertAfter, null);
|
|
5630
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
|
|
5198
5631
|
};
|
|
5199
5632
|
const handleRemoveSchedulingSection = (e) => {
|
|
5200
5633
|
if (e.data?.type !== "ow:remove-scheduling-section") return;
|
|
5201
|
-
|
|
5202
|
-
|
|
5634
|
+
document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => {
|
|
5635
|
+
el.remove();
|
|
5636
|
+
});
|
|
5203
5637
|
const tracker = getSectionsTracker();
|
|
5204
5638
|
let sections = [];
|
|
5205
5639
|
try {
|
|
@@ -5318,6 +5752,7 @@ function OhhwellsBridge() {
|
|
|
5318
5752
|
window.addEventListener("message", handleSave);
|
|
5319
5753
|
window.addEventListener("message", handleInsertSection);
|
|
5320
5754
|
window.addEventListener("message", handleSwitchSchedule);
|
|
5755
|
+
window.addEventListener("message", handleScheduleLinked);
|
|
5321
5756
|
window.addEventListener("message", handleClearSchedulingWidget);
|
|
5322
5757
|
window.addEventListener("message", handleRemoveSchedulingSection);
|
|
5323
5758
|
window.addEventListener("message", handleImageUrl);
|
|
@@ -5356,6 +5791,7 @@ function OhhwellsBridge() {
|
|
|
5356
5791
|
window.removeEventListener("message", handleSave);
|
|
5357
5792
|
window.removeEventListener("message", handleInsertSection);
|
|
5358
5793
|
window.removeEventListener("message", handleSwitchSchedule);
|
|
5794
|
+
window.removeEventListener("message", handleScheduleLinked);
|
|
5359
5795
|
window.removeEventListener("message", handleClearSchedulingWidget);
|
|
5360
5796
|
window.removeEventListener("message", handleRemoveSchedulingSection);
|
|
5361
5797
|
window.removeEventListener("message", handleImageUrl);
|
|
@@ -5414,12 +5850,12 @@ function OhhwellsBridge() {
|
|
|
5414
5850
|
setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
|
|
5415
5851
|
}, [deactivate]);
|
|
5416
5852
|
return bridgeRoot ? createPortal(
|
|
5417
|
-
/* @__PURE__ */
|
|
5418
|
-
toolbarRect && /* @__PURE__ */
|
|
5419
|
-
/* @__PURE__ */
|
|
5420
|
-
/* @__PURE__ */
|
|
5853
|
+
/* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
5854
|
+
toolbarRect && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
5855
|
+
/* @__PURE__ */ jsx6(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
|
|
5856
|
+
/* @__PURE__ */ jsx6(FloatingToolbar, { rect: toolbarRect, parentScroll: parentScrollRef.current, elRef: toolbarElRef, onCommand: handleCommand, activeCommands })
|
|
5421
5857
|
] }),
|
|
5422
|
-
maxBadge && /* @__PURE__ */
|
|
5858
|
+
maxBadge && /* @__PURE__ */ jsxs3(
|
|
5423
5859
|
"div",
|
|
5424
5860
|
{
|
|
5425
5861
|
"data-ohw-max-badge": "",
|
|
@@ -5445,7 +5881,7 @@ function OhhwellsBridge() {
|
|
|
5445
5881
|
]
|
|
5446
5882
|
}
|
|
5447
5883
|
),
|
|
5448
|
-
toggleState && /* @__PURE__ */
|
|
5884
|
+
toggleState && /* @__PURE__ */ jsx6(
|
|
5449
5885
|
StateToggle,
|
|
5450
5886
|
{
|
|
5451
5887
|
rect: toggleState.rect,
|
|
@@ -5454,15 +5890,15 @@ function OhhwellsBridge() {
|
|
|
5454
5890
|
onStateChange: handleStateChange
|
|
5455
5891
|
}
|
|
5456
5892
|
),
|
|
5457
|
-
sectionGap && /* @__PURE__ */
|
|
5893
|
+
sectionGap && /* @__PURE__ */ jsxs3(
|
|
5458
5894
|
"div",
|
|
5459
5895
|
{
|
|
5460
5896
|
"data-ohw-section-insert-line": "",
|
|
5461
5897
|
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
5462
5898
|
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
5463
5899
|
children: [
|
|
5464
|
-
/* @__PURE__ */
|
|
5465
|
-
/* @__PURE__ */
|
|
5900
|
+
/* @__PURE__ */ jsx6("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
5901
|
+
/* @__PURE__ */ jsx6(
|
|
5466
5902
|
Badge,
|
|
5467
5903
|
{
|
|
5468
5904
|
className: "px-8 py-1 bg-primary hover:bg-primary text-primary-foreground text-xs font-medium shrink-0 rounded-full cursor-pointer pointer-events-auto",
|
|
@@ -5475,7 +5911,7 @@ function OhhwellsBridge() {
|
|
|
5475
5911
|
children: "Add Section"
|
|
5476
5912
|
}
|
|
5477
5913
|
),
|
|
5478
|
-
/* @__PURE__ */
|
|
5914
|
+
/* @__PURE__ */ jsx6("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
5479
5915
|
]
|
|
5480
5916
|
}
|
|
5481
5917
|
)
|