@ohhwells/bridge 0.1.15 → 0.1.16-next.12
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/README.md +198 -70
- package/dist/index.cjs +1165 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +1152 -66
- package/dist/index.js.map +1 -1
- package/dist/styles.css +504 -54
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,752 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/OhhwellsBridge.tsx
|
|
4
|
-
import
|
|
4
|
+
import React3, { useCallback, useEffect as useEffect2, useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
5
|
+
import { createRoot } from "react-dom/client";
|
|
6
|
+
import { flushSync } from "react-dom";
|
|
7
|
+
|
|
8
|
+
// src/ui/SchedulingWidget.tsx
|
|
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";
|
|
165
|
+
var API_URL = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
166
|
+
var DAY_ABBR = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
|
|
167
|
+
function getApiDomain() {
|
|
168
|
+
const h = window.location.hostname;
|
|
169
|
+
for (const base of ["ohhwells.site", "theflowops-staging.com", "theflowops.com"]) {
|
|
170
|
+
if (h.endsWith(`.${base}`)) return h.slice(0, -(base.length + 1));
|
|
171
|
+
}
|
|
172
|
+
return h;
|
|
173
|
+
}
|
|
174
|
+
function classRunsOnDate(cls, date, type) {
|
|
175
|
+
if (type === "FIXED") {
|
|
176
|
+
const d = String(date.getDate()).padStart(2, "0");
|
|
177
|
+
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
178
|
+
return cls.fixedDates?.some((fd) => fd.date === `${d}-${m}-${date.getFullYear()}`) ?? false;
|
|
179
|
+
}
|
|
180
|
+
return cls.weekdays?.some((w) => w.weekday === date.getDay()) ?? false;
|
|
181
|
+
}
|
|
182
|
+
function formatClassTime(cls) {
|
|
183
|
+
let h = parseInt(cls.startTime.hour, 10);
|
|
184
|
+
const m = parseInt(cls.startTime.minutes, 10);
|
|
185
|
+
if (cls.startTime.time === "PM" && h !== 12) h += 12;
|
|
186
|
+
if (cls.startTime.time === "AM" && h === 12) h = 0;
|
|
187
|
+
const endTotal = h * 60 + m + cls.duration;
|
|
188
|
+
const eh = Math.floor(endTotal / 60) % 24;
|
|
189
|
+
const em = endTotal % 60;
|
|
190
|
+
return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
|
|
191
|
+
}
|
|
192
|
+
function getBookingsOnDate(cls, date) {
|
|
193
|
+
if (!cls.bookings?.length) return 0;
|
|
194
|
+
const ds = date.toISOString().split("T")[0];
|
|
195
|
+
return cls.bookings.filter((b) => {
|
|
196
|
+
try {
|
|
197
|
+
return new Date(b.classDate).toISOString().split("T")[0] === ds;
|
|
198
|
+
} catch {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}).length;
|
|
202
|
+
}
|
|
203
|
+
function buildTimezoneLabel(tz) {
|
|
204
|
+
try {
|
|
205
|
+
const now = /* @__PURE__ */ new Date();
|
|
206
|
+
const timeStr = new Intl.DateTimeFormat("en-US", {
|
|
207
|
+
hour: "2-digit",
|
|
208
|
+
minute: "2-digit",
|
|
209
|
+
hour12: false,
|
|
210
|
+
timeZone: tz
|
|
211
|
+
}).format(now);
|
|
212
|
+
const offsetPart = new Intl.DateTimeFormat("en-US", { timeZoneName: "short", timeZone: tz }).formatToParts(now).find((p) => p.type === "timeZoneName")?.value ?? "";
|
|
213
|
+
const city = tz.split("/").pop()?.replace(/_/g, " ") ?? tz;
|
|
214
|
+
return `${timeStr} (${offsetPart}) ${city} time`;
|
|
215
|
+
} catch {
|
|
216
|
+
return tz;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function EmptyState({ inEditor }) {
|
|
220
|
+
const handleAddSchedule = () => {
|
|
221
|
+
if (inEditor) {
|
|
222
|
+
window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
|
|
223
|
+
}
|
|
224
|
+
};
|
|
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(
|
|
227
|
+
"svg",
|
|
228
|
+
{
|
|
229
|
+
width: "20",
|
|
230
|
+
height: "20",
|
|
231
|
+
viewBox: "0 0 24 24",
|
|
232
|
+
fill: "none",
|
|
233
|
+
stroke: "var(--color-accent, #A89B83)",
|
|
234
|
+
strokeWidth: "1.5",
|
|
235
|
+
strokeLinecap: "round",
|
|
236
|
+
strokeLinejoin: "round",
|
|
237
|
+
children: [
|
|
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" })
|
|
244
|
+
]
|
|
245
|
+
}
|
|
246
|
+
) }),
|
|
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." })
|
|
250
|
+
] }),
|
|
251
|
+
/* @__PURE__ */ jsx2(
|
|
252
|
+
"button",
|
|
253
|
+
{
|
|
254
|
+
onClick: handleAddSchedule,
|
|
255
|
+
className: "h-9 px-2 py-1.5 flex items-center justify-center bg-white border border-[#E7E5E4] rounded-md font-body text-sm font-medium text-(--color-dark,#200C02) cursor-pointer",
|
|
256
|
+
children: "Add Schedule"
|
|
257
|
+
}
|
|
258
|
+
)
|
|
259
|
+
] });
|
|
260
|
+
}
|
|
261
|
+
function LoadingSkeleton() {
|
|
262
|
+
const shimmer = {
|
|
263
|
+
background: "linear-gradient(90deg,#f0f0f0 25%,#e8e8e8 50%,#f0f0f0 75%)",
|
|
264
|
+
backgroundSize: "200% 100%",
|
|
265
|
+
animation: "ohw-shimmer 1.5s infinite"
|
|
266
|
+
};
|
|
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%" } })
|
|
272
|
+
] }, i)) }),
|
|
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" } })
|
|
291
|
+
] }),
|
|
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 } })
|
|
294
|
+
] }),
|
|
295
|
+
i < 2 && /* @__PURE__ */ jsx2("div", { className: "h-px bg-black/20" })
|
|
296
|
+
] }, i))
|
|
297
|
+
] });
|
|
298
|
+
}
|
|
299
|
+
function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal }) {
|
|
300
|
+
const todayMs = useMemo(() => {
|
|
301
|
+
const d = /* @__PURE__ */ new Date();
|
|
302
|
+
d.setHours(0, 0, 0, 0);
|
|
303
|
+
return d.getTime();
|
|
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
|
+
};
|
|
351
|
+
const datesWithClasses = useMemo(
|
|
352
|
+
() => new Set(dates.map(
|
|
353
|
+
(d, i) => schedule.classes?.some((c) => classRunsOnDate(c, d, schedule.type)) ? i : -1
|
|
354
|
+
).filter((i) => i >= 0)),
|
|
355
|
+
[dates, schedule]
|
|
356
|
+
);
|
|
357
|
+
const selectedDate = dates[selectedIdx];
|
|
358
|
+
const selectedClasses = useMemo(
|
|
359
|
+
() => (schedule.classes ?? []).filter((c) => selectedDate && classRunsOnDate(c, selectedDate, schedule.type)),
|
|
360
|
+
[schedule, selectedDate]
|
|
361
|
+
);
|
|
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(
|
|
385
|
+
"div",
|
|
386
|
+
{
|
|
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(
|
|
414
|
+
"div",
|
|
415
|
+
{
|
|
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) => {
|
|
444
|
+
const booked = getBookingsOnDate(cls, selectedDate);
|
|
445
|
+
const available = cls.maxParticipants - booked;
|
|
446
|
+
const isFull = available <= 0;
|
|
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
|
+
] }) })
|
|
467
|
+
] }),
|
|
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 })
|
|
472
|
+
] }),
|
|
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
|
+
] })
|
|
502
|
+
] }),
|
|
503
|
+
i < selectedClasses.length - 1 && /* @__PURE__ */ jsx2("div", { className: "h-px bg-black/20" })
|
|
504
|
+
] }, cls.id ?? i);
|
|
505
|
+
}) })
|
|
506
|
+
] });
|
|
507
|
+
}
|
|
508
|
+
function CalendarFoldIcon() {
|
|
509
|
+
return /* @__PURE__ */ jsxs2(
|
|
510
|
+
"svg",
|
|
511
|
+
{
|
|
512
|
+
width: "16",
|
|
513
|
+
height: "16",
|
|
514
|
+
viewBox: "0 0 24 24",
|
|
515
|
+
fill: "none",
|
|
516
|
+
stroke: "currentColor",
|
|
517
|
+
strokeWidth: "2",
|
|
518
|
+
strokeLinecap: "round",
|
|
519
|
+
strokeLinejoin: "round",
|
|
520
|
+
style: { flexShrink: 0 },
|
|
521
|
+
children: [
|
|
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" })
|
|
527
|
+
]
|
|
528
|
+
}
|
|
529
|
+
);
|
|
530
|
+
}
|
|
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);
|
|
538
|
+
const dates = useMemo(() => {
|
|
539
|
+
const today = /* @__PURE__ */ new Date();
|
|
540
|
+
today.setHours(0, 0, 0, 0);
|
|
541
|
+
return Array.from({ length: 14 }, (_, i) => {
|
|
542
|
+
const d = new Date(today);
|
|
543
|
+
d.setDate(today.getDate() + i);
|
|
544
|
+
return d;
|
|
545
|
+
});
|
|
546
|
+
}, []);
|
|
547
|
+
const [selectedIdx, setSelectedIdx] = useState2(0);
|
|
548
|
+
useEffect(() => {
|
|
549
|
+
if (!schedule?.classes) return;
|
|
550
|
+
for (let i = 0; i < dates.length; i++) {
|
|
551
|
+
if (schedule.classes.some((c) => classRunsOnDate(c, dates[i], schedule.type))) {
|
|
552
|
+
setSelectedIdx(i);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}, [schedule, dates]);
|
|
557
|
+
useEffect(() => {
|
|
558
|
+
if (typeof window === "undefined") return;
|
|
559
|
+
const isInEditor = window.self !== window.top;
|
|
560
|
+
setInEditor(isInEditor);
|
|
561
|
+
if (isInEditor) {
|
|
562
|
+
const startEmpty = initialScheduleId === null;
|
|
563
|
+
if (startEmpty) setLoading(false);
|
|
564
|
+
let initialized = startEmpty;
|
|
565
|
+
const timer = !startEmpty ? setTimeout(() => {
|
|
566
|
+
if (!initialized) {
|
|
567
|
+
initialized = true;
|
|
568
|
+
setLoading(false);
|
|
569
|
+
}
|
|
570
|
+
}, 5e3) : null;
|
|
571
|
+
const handler = (e) => {
|
|
572
|
+
if (e.data?.type === "ow:clear-scheduling-widget") {
|
|
573
|
+
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
574
|
+
setSchedule(null);
|
|
575
|
+
setLoading(false);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
if (e.data?.type === "ow:switch-schedule") {
|
|
579
|
+
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
580
|
+
switchScheduleIdRef.current = e.data.scheduleId ?? null;
|
|
581
|
+
initialized = false;
|
|
582
|
+
setLoading(true);
|
|
583
|
+
window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
if (e.data?.type !== "ow:user-schedules-response") return;
|
|
587
|
+
if (e.data.insertAfter && e.data.insertAfter !== insertAfter) return;
|
|
588
|
+
if (initialized && switchScheduleIdRef.current === null) return;
|
|
589
|
+
initialized = true;
|
|
590
|
+
if (timer) clearTimeout(timer);
|
|
591
|
+
const schedules = e.data.schedules ?? [];
|
|
592
|
+
const isSwitching = switchScheduleIdRef.current !== null;
|
|
593
|
+
const target = isSwitching ? schedules.find((s) => s.id === switchScheduleIdRef.current) ?? schedules[0] ?? null : initialScheduleId ? schedules.find((s) => s.id === initialScheduleId) ?? schedules[0] ?? null : schedules[0] ?? null;
|
|
594
|
+
switchScheduleIdRef.current = null;
|
|
595
|
+
setSchedule(target);
|
|
596
|
+
setLoading(false);
|
|
597
|
+
if (notifyOnConnect && target && !isSwitching) {
|
|
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 }, "*");
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
window.addEventListener("message", handler);
|
|
607
|
+
if (!startEmpty) window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
608
|
+
return () => {
|
|
609
|
+
window.removeEventListener("message", handler);
|
|
610
|
+
if (timer) clearTimeout(timer);
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
if (initialScheduleId === null) {
|
|
614
|
+
setLoading(false);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
;
|
|
618
|
+
(async () => {
|
|
619
|
+
try {
|
|
620
|
+
if (initialScheduleId) {
|
|
621
|
+
const res = await fetch(`${API_URL}/api/schedule/id/${initialScheduleId}`);
|
|
622
|
+
const data = await res.json();
|
|
623
|
+
setSchedule(data?.id ? data : null);
|
|
624
|
+
} else {
|
|
625
|
+
const domain = getApiDomain();
|
|
626
|
+
const sitemapRes = await fetch(`${API_URL}/api/schedule/sitemap/${domain}`);
|
|
627
|
+
const slugs = await sitemapRes.json();
|
|
628
|
+
if (!Array.isArray(slugs) || !slugs.length) {
|
|
629
|
+
setLoading(false);
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
const { slug } = [...slugs].sort(
|
|
633
|
+
(a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
|
|
634
|
+
)[0];
|
|
635
|
+
const res = await fetch(`${API_URL}/api/schedule/${domain}/${slug}`);
|
|
636
|
+
const data = await res.json();
|
|
637
|
+
setSchedule(data?.id ? data : null);
|
|
638
|
+
}
|
|
639
|
+
} catch {
|
|
640
|
+
}
|
|
641
|
+
setLoading(false);
|
|
642
|
+
})();
|
|
643
|
+
}, []);
|
|
644
|
+
const timezoneLabel = schedule?.timezone ? (() => {
|
|
645
|
+
try {
|
|
646
|
+
return buildTimezoneLabel(schedule.timezone);
|
|
647
|
+
} catch {
|
|
648
|
+
return void 0;
|
|
649
|
+
}
|
|
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
|
+
};
|
|
665
|
+
const handleReplaceSchedule = () => {
|
|
666
|
+
setIsHovered(false);
|
|
667
|
+
if (schedule) {
|
|
668
|
+
window.parent.postMessage({
|
|
669
|
+
type: "ow:schedule-connected",
|
|
670
|
+
schedule: { id: schedule.id, name: schedule.name },
|
|
671
|
+
insertAfter
|
|
672
|
+
}, "*");
|
|
673
|
+
} else {
|
|
674
|
+
window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
const sectionId = `scheduling-${insertAfter}`;
|
|
678
|
+
return /* @__PURE__ */ jsxs2(
|
|
679
|
+
"section",
|
|
680
|
+
{
|
|
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",
|
|
684
|
+
onMouseEnter: () => inEditor && setIsHovered(true),
|
|
685
|
+
onMouseLeave: () => setIsHovered(false),
|
|
686
|
+
children: [
|
|
687
|
+
inEditor && isHovered && !!schedule && /* @__PURE__ */ jsxs2(
|
|
688
|
+
"div",
|
|
689
|
+
{
|
|
690
|
+
className: "absolute inset-0 z-10 pointer-events-auto cursor-pointer",
|
|
691
|
+
onClick: handleReplaceSchedule,
|
|
692
|
+
children: [
|
|
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(
|
|
696
|
+
"div",
|
|
697
|
+
{
|
|
698
|
+
className: "bg-white border border-[#E7E5E4] rounded-md px-3 py-1.5 flex items-center gap-1.5",
|
|
699
|
+
style: {
|
|
700
|
+
fontSize: 14,
|
|
701
|
+
lineHeight: "24px",
|
|
702
|
+
fontFamily: "'Figtree', system-ui, sans-serif",
|
|
703
|
+
fontWeight: 500,
|
|
704
|
+
color: "#0C0A09"
|
|
705
|
+
},
|
|
706
|
+
children: [
|
|
707
|
+
/* @__PURE__ */ jsx2(CalendarFoldIcon, {}),
|
|
708
|
+
"Replace schedule"
|
|
709
|
+
]
|
|
710
|
+
}
|
|
711
|
+
) })
|
|
712
|
+
]
|
|
713
|
+
}
|
|
714
|
+
),
|
|
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 })
|
|
719
|
+
] }),
|
|
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(
|
|
723
|
+
ScheduleView,
|
|
724
|
+
{
|
|
725
|
+
schedule,
|
|
726
|
+
dates,
|
|
727
|
+
selectedIdx,
|
|
728
|
+
onSelectDate: setSelectedIdx,
|
|
729
|
+
onOpenModal: inEditor ? void 0 : (mode, classId, classDate) => setModalState({ mode, classId, classDate })
|
|
730
|
+
}
|
|
731
|
+
) })
|
|
732
|
+
] })
|
|
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
|
+
)
|
|
743
|
+
]
|
|
744
|
+
}
|
|
745
|
+
);
|
|
746
|
+
}
|
|
5
747
|
|
|
6
748
|
// src/ui/toggle-group.tsx
|
|
7
|
-
import * as
|
|
749
|
+
import * as React2 from "react";
|
|
8
750
|
|
|
9
751
|
// node_modules/clsx/dist/clsx.mjs
|
|
10
752
|
function r(e) {
|
|
@@ -3324,7 +4066,7 @@ var cva = (base, config) => (props) => {
|
|
|
3324
4066
|
|
|
3325
4067
|
// src/ui/toggle.tsx
|
|
3326
4068
|
import { Toggle as TogglePrimitive } from "radix-ui";
|
|
3327
|
-
import { jsx } from "react/jsx-runtime";
|
|
4069
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
3328
4070
|
var toggleVariants = cva(
|
|
3329
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",
|
|
3330
4072
|
{
|
|
@@ -3351,7 +4093,7 @@ function Toggle({
|
|
|
3351
4093
|
size,
|
|
3352
4094
|
...props
|
|
3353
4095
|
}) {
|
|
3354
|
-
return /* @__PURE__ */
|
|
4096
|
+
return /* @__PURE__ */ jsx3(
|
|
3355
4097
|
TogglePrimitive.Root,
|
|
3356
4098
|
{
|
|
3357
4099
|
"data-slot": "toggle",
|
|
@@ -3362,8 +4104,8 @@ function Toggle({
|
|
|
3362
4104
|
}
|
|
3363
4105
|
|
|
3364
4106
|
// src/ui/toggle-group.tsx
|
|
3365
|
-
import { jsx as
|
|
3366
|
-
var ToggleGroupContext =
|
|
4107
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
4108
|
+
var ToggleGroupContext = React2.createContext({
|
|
3367
4109
|
value: "",
|
|
3368
4110
|
onValueChange: () => {
|
|
3369
4111
|
}
|
|
@@ -3375,13 +4117,13 @@ function ToggleGroup({
|
|
|
3375
4117
|
children,
|
|
3376
4118
|
...props
|
|
3377
4119
|
}) {
|
|
3378
|
-
return /* @__PURE__ */
|
|
4120
|
+
return /* @__PURE__ */ jsx4(
|
|
3379
4121
|
"div",
|
|
3380
4122
|
{
|
|
3381
4123
|
role: "group",
|
|
3382
4124
|
className: cn("flex items-center gap-1 p-1 bg-muted rounded-md", className),
|
|
3383
4125
|
...props,
|
|
3384
|
-
children: /* @__PURE__ */
|
|
4126
|
+
children: /* @__PURE__ */ jsx4(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
|
|
3385
4127
|
}
|
|
3386
4128
|
);
|
|
3387
4129
|
}
|
|
@@ -3391,8 +4133,8 @@ function ToggleGroupItem({
|
|
|
3391
4133
|
children,
|
|
3392
4134
|
...props
|
|
3393
4135
|
}) {
|
|
3394
|
-
const { value: groupValue, onValueChange } =
|
|
3395
|
-
return /* @__PURE__ */
|
|
4136
|
+
const { value: groupValue, onValueChange } = React2.useContext(ToggleGroupContext);
|
|
4137
|
+
return /* @__PURE__ */ jsx4(
|
|
3396
4138
|
Toggle,
|
|
3397
4139
|
{
|
|
3398
4140
|
pressed: groupValue === value,
|
|
@@ -3433,8 +4175,30 @@ function isEditSessionActive() {
|
|
|
3433
4175
|
return new URLSearchParams(q).get("mode") === "edit";
|
|
3434
4176
|
}
|
|
3435
4177
|
|
|
4178
|
+
// src/ui/badge.tsx
|
|
4179
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
4180
|
+
var badgeVariants = cva(
|
|
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",
|
|
4182
|
+
{
|
|
4183
|
+
variants: {
|
|
4184
|
+
variant: {
|
|
4185
|
+
default: "border-transparent bg-primary text-primary-foreground",
|
|
4186
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
4187
|
+
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
|
4188
|
+
outline: "text-foreground"
|
|
4189
|
+
}
|
|
4190
|
+
},
|
|
4191
|
+
defaultVariants: {
|
|
4192
|
+
variant: "default"
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
);
|
|
4196
|
+
function Badge({ className, variant, ...props }) {
|
|
4197
|
+
return /* @__PURE__ */ jsx5("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
4198
|
+
}
|
|
4199
|
+
|
|
3436
4200
|
// src/OhhwellsBridge.tsx
|
|
3437
|
-
import { Fragment, jsx as
|
|
4201
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
3438
4202
|
var PRIMARY = "#0885FE";
|
|
3439
4203
|
var IMAGE_FADE_MS = 300;
|
|
3440
4204
|
function runOpacityFade(el, onDone) {
|
|
@@ -3485,6 +4249,163 @@ function fadeInBgImage(el, url, onReady) {
|
|
|
3485
4249
|
if (!prevPos || prevPos === "static") el.style.position = prevPos;
|
|
3486
4250
|
});
|
|
3487
4251
|
}
|
|
4252
|
+
function getSectionsTracker() {
|
|
4253
|
+
let el = document.querySelector("[data-ohw-sections-tracker]");
|
|
4254
|
+
if (!el) {
|
|
4255
|
+
el = document.createElement("div");
|
|
4256
|
+
el.setAttribute("data-ohw-sections-tracker", "");
|
|
4257
|
+
el.style.display = "none";
|
|
4258
|
+
document.body.appendChild(el);
|
|
4259
|
+
}
|
|
4260
|
+
return el;
|
|
4261
|
+
}
|
|
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;
|
|
4353
|
+
const container = document.createElement("div");
|
|
4354
|
+
container.dataset.ohwSectionContainer = "scheduling";
|
|
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
|
+
});
|
|
4380
|
+
const tracker = getSectionsTracker();
|
|
4381
|
+
let sections = [];
|
|
4382
|
+
try {
|
|
4383
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
4384
|
+
} catch {
|
|
4385
|
+
}
|
|
4386
|
+
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
4387
|
+
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
|
|
4388
|
+
sections.push({
|
|
4389
|
+
type: "scheduling",
|
|
4390
|
+
insertAfter: effectiveInsertAfter,
|
|
4391
|
+
pagePath: window.location.pathname,
|
|
4392
|
+
...scheduleId ? { scheduleId } : {}
|
|
4393
|
+
});
|
|
4394
|
+
tracker.textContent = JSON.stringify(sections);
|
|
4395
|
+
}
|
|
4396
|
+
return true;
|
|
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
|
+
}
|
|
3488
4409
|
function collectEditableNodes() {
|
|
3489
4410
|
return Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
|
|
3490
4411
|
if (el.dataset.ohwEditable === "image") {
|
|
@@ -3642,7 +4563,7 @@ var TOOLBAR_GROUPS = [
|
|
|
3642
4563
|
];
|
|
3643
4564
|
function GlowFrame({ rect, elRef }) {
|
|
3644
4565
|
const GAP = 6;
|
|
3645
|
-
return /* @__PURE__ */
|
|
4566
|
+
return /* @__PURE__ */ jsx6(
|
|
3646
4567
|
"div",
|
|
3647
4568
|
{
|
|
3648
4569
|
ref: elRef,
|
|
@@ -3693,7 +4614,7 @@ function FloatingToolbar({
|
|
|
3693
4614
|
activeCommands
|
|
3694
4615
|
}) {
|
|
3695
4616
|
const { top, left, transform } = calcToolbarPos(rect, parentScroll);
|
|
3696
|
-
return /* @__PURE__ */
|
|
4617
|
+
return /* @__PURE__ */ jsx6(
|
|
3697
4618
|
"div",
|
|
3698
4619
|
{
|
|
3699
4620
|
ref: elRef,
|
|
@@ -3716,11 +4637,11 @@ function FloatingToolbar({
|
|
|
3716
4637
|
pointerEvents: "auto",
|
|
3717
4638
|
whiteSpace: "nowrap"
|
|
3718
4639
|
},
|
|
3719
|
-
children: TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */
|
|
3720
|
-
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 } }),
|
|
3721
4642
|
btns.map((btn) => {
|
|
3722
4643
|
const isActive = activeCommands.has(btn.cmd);
|
|
3723
|
-
return /* @__PURE__ */
|
|
4644
|
+
return /* @__PURE__ */ jsx6(
|
|
3724
4645
|
"button",
|
|
3725
4646
|
{
|
|
3726
4647
|
title: btn.title,
|
|
@@ -3746,7 +4667,7 @@ function FloatingToolbar({
|
|
|
3746
4667
|
flexShrink: 0,
|
|
3747
4668
|
padding: 6
|
|
3748
4669
|
},
|
|
3749
|
-
children: /* @__PURE__ */
|
|
4670
|
+
children: /* @__PURE__ */ jsx6(
|
|
3750
4671
|
"svg",
|
|
3751
4672
|
{
|
|
3752
4673
|
width: "16",
|
|
@@ -3779,7 +4700,7 @@ function StateToggle({
|
|
|
3779
4700
|
states,
|
|
3780
4701
|
onStateChange
|
|
3781
4702
|
}) {
|
|
3782
|
-
return /* @__PURE__ */
|
|
4703
|
+
return /* @__PURE__ */ jsx6(
|
|
3783
4704
|
ToggleGroup,
|
|
3784
4705
|
{
|
|
3785
4706
|
"data-ohw-state-toggle": "",
|
|
@@ -3793,18 +4714,35 @@ function StateToggle({
|
|
|
3793
4714
|
left: rect.right - 8,
|
|
3794
4715
|
transform: "translateX(-100%)"
|
|
3795
4716
|
},
|
|
3796
|
-
children: states.map((state) => /* @__PURE__ */
|
|
4717
|
+
children: states.map((state) => /* @__PURE__ */ jsx6(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
|
|
3797
4718
|
}
|
|
3798
4719
|
);
|
|
3799
4720
|
}
|
|
3800
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
|
+
}
|
|
3801
4739
|
function OhhwellsBridge() {
|
|
3802
4740
|
const pathname = usePathname();
|
|
3803
4741
|
const router = useRouter();
|
|
3804
4742
|
const searchParams = useSearchParams();
|
|
3805
4743
|
const isEditMode = isEditSessionActive();
|
|
3806
|
-
const [bridgeRoot, setBridgeRoot] =
|
|
3807
|
-
|
|
4744
|
+
const [bridgeRoot, setBridgeRoot] = useState3(null);
|
|
4745
|
+
useEffect2(() => {
|
|
3808
4746
|
const figtreeFontId = "ohw-figtree-font";
|
|
3809
4747
|
if (!document.getElementById(figtreeFontId)) {
|
|
3810
4748
|
const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
|
|
@@ -3827,42 +4765,40 @@ function OhhwellsBridge() {
|
|
|
3827
4765
|
};
|
|
3828
4766
|
}, []);
|
|
3829
4767
|
const subdomainFromQuery = searchParams.get("subdomain");
|
|
3830
|
-
const subdomain = subdomainFromQuery
|
|
3831
|
-
if (typeof window === "undefined") return "";
|
|
3832
|
-
const parts = window.location.hostname.split(".");
|
|
3833
|
-
return parts.length >= 3 && parts[0] !== "www" ? parts[0] : "";
|
|
3834
|
-
})();
|
|
4768
|
+
const subdomain = resolveSubdomain(subdomainFromQuery);
|
|
3835
4769
|
const postToParent = useCallback((data) => {
|
|
3836
4770
|
if (typeof window !== "undefined" && window.parent !== window) {
|
|
3837
4771
|
window.parent.postMessage(data, "*");
|
|
3838
4772
|
}
|
|
3839
4773
|
}, []);
|
|
3840
|
-
const [fetchState, setFetchState] =
|
|
3841
|
-
const autoSaveTimers =
|
|
3842
|
-
const activeElRef =
|
|
3843
|
-
const originalContentRef =
|
|
3844
|
-
const activeStateElRef =
|
|
3845
|
-
const parentScrollRef =
|
|
3846
|
-
const toolbarElRef =
|
|
3847
|
-
const glowElRef =
|
|
3848
|
-
const hoveredImageRef =
|
|
3849
|
-
const hoveredImageHasTextOverlapRef =
|
|
3850
|
-
const
|
|
3851
|
-
const
|
|
3852
|
-
const
|
|
3853
|
-
const
|
|
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(() => {
|
|
3854
4789
|
});
|
|
3855
|
-
const deactivateRef =
|
|
4790
|
+
const deactivateRef = useRef2(() => {
|
|
3856
4791
|
});
|
|
3857
|
-
const refreshActiveCommandsRef =
|
|
4792
|
+
const refreshActiveCommandsRef = useRef2(() => {
|
|
3858
4793
|
});
|
|
3859
|
-
const postToParentRef =
|
|
4794
|
+
const postToParentRef = useRef2(postToParent);
|
|
3860
4795
|
postToParentRef.current = postToParent;
|
|
3861
|
-
const [toolbarRect, setToolbarRect] =
|
|
3862
|
-
const [toggleState, setToggleState] =
|
|
3863
|
-
const [maxBadge, setMaxBadge] =
|
|
3864
|
-
const [activeCommands, setActiveCommands] =
|
|
3865
|
-
|
|
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);
|
|
4801
|
+
useEffect2(() => {
|
|
3866
4802
|
const update = () => {
|
|
3867
4803
|
const el = activeElRef.current;
|
|
3868
4804
|
if (el) setToolbarRect(el.getBoundingClientRect());
|
|
@@ -3870,6 +4806,12 @@ function OhhwellsBridge() {
|
|
|
3870
4806
|
if (!prev || !activeStateElRef.current) return prev;
|
|
3871
4807
|
return { ...prev, rect: activeStateElRef.current.getBoundingClientRect() };
|
|
3872
4808
|
});
|
|
4809
|
+
setSectionGap((prev) => {
|
|
4810
|
+
if (!prev) return prev;
|
|
4811
|
+
const anchor = document.querySelector(`[data-ohw-section="${prev.insertAfter}"]`);
|
|
4812
|
+
if (!anchor) return prev;
|
|
4813
|
+
return { ...prev, y: anchor.getBoundingClientRect().bottom };
|
|
4814
|
+
});
|
|
3873
4815
|
};
|
|
3874
4816
|
const vvp = window.visualViewport;
|
|
3875
4817
|
if (!vvp) return;
|
|
@@ -3934,6 +4876,7 @@ function OhhwellsBridge() {
|
|
|
3934
4876
|
const applyContent = (content) => {
|
|
3935
4877
|
const imageLoads = [];
|
|
3936
4878
|
for (const [key, val] of Object.entries(content)) {
|
|
4879
|
+
if (key === "__ohw_sections") continue;
|
|
3937
4880
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
3938
4881
|
if (el.dataset.ohwEditable === "image") {
|
|
3939
4882
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -3952,6 +4895,7 @@ function OhhwellsBridge() {
|
|
|
3952
4895
|
}
|
|
3953
4896
|
});
|
|
3954
4897
|
}
|
|
4898
|
+
initSectionsFromContent(content, true);
|
|
3955
4899
|
return imageLoads.length > 0 ? Promise.all(imageLoads).then(() => {
|
|
3956
4900
|
}) : Promise.resolve();
|
|
3957
4901
|
};
|
|
@@ -3962,7 +4906,7 @@ function OhhwellsBridge() {
|
|
|
3962
4906
|
}
|
|
3963
4907
|
let cancelled = false;
|
|
3964
4908
|
setFetchState("loading");
|
|
3965
|
-
const apiUrl =
|
|
4909
|
+
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
3966
4910
|
fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
3967
4911
|
if (cancelled) return;
|
|
3968
4912
|
const content = data?.content ?? {};
|
|
@@ -3976,12 +4920,15 @@ function OhhwellsBridge() {
|
|
|
3976
4920
|
cancelled = true;
|
|
3977
4921
|
};
|
|
3978
4922
|
}, [subdomain, isEditMode, pathname]);
|
|
3979
|
-
|
|
4923
|
+
useEffect2(() => {
|
|
3980
4924
|
if (!subdomain || isEditMode) return;
|
|
4925
|
+
let debounceTimer = null;
|
|
3981
4926
|
const applyFromCache = () => {
|
|
3982
4927
|
const content = contentCache.get(subdomain);
|
|
3983
4928
|
if (!content) return;
|
|
4929
|
+
retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
|
|
3984
4930
|
for (const [key, val] of Object.entries(content)) {
|
|
4931
|
+
if (key === "__ohw_sections") continue;
|
|
3985
4932
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
3986
4933
|
if (el.dataset.ohwEditable === "image") {
|
|
3987
4934
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -3995,21 +4942,28 @@ function OhhwellsBridge() {
|
|
|
3995
4942
|
});
|
|
3996
4943
|
}
|
|
3997
4944
|
};
|
|
3998
|
-
|
|
3999
|
-
|
|
4945
|
+
const scheduleApply = () => {
|
|
4946
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
4947
|
+
debounceTimer = setTimeout(applyFromCache, 150);
|
|
4948
|
+
};
|
|
4949
|
+
scheduleApply();
|
|
4950
|
+
const observer = new MutationObserver(scheduleApply);
|
|
4000
4951
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
4001
|
-
return () =>
|
|
4002
|
-
|
|
4952
|
+
return () => {
|
|
4953
|
+
observer.disconnect();
|
|
4954
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
4955
|
+
};
|
|
4956
|
+
}, [subdomain, isEditMode, pathname]);
|
|
4003
4957
|
useLayoutEffect(() => {
|
|
4004
4958
|
const el = document.getElementById("ohw-loader");
|
|
4005
4959
|
if (!el) return;
|
|
4006
4960
|
const visible = Boolean(subdomain) && fetchState !== "done";
|
|
4007
4961
|
el.style.display = visible ? "flex" : "none";
|
|
4008
4962
|
}, [subdomain, fetchState]);
|
|
4009
|
-
|
|
4963
|
+
useEffect2(() => {
|
|
4010
4964
|
postToParent({ type: "ow:navigation", path: pathname });
|
|
4011
4965
|
}, [pathname, postToParent]);
|
|
4012
|
-
|
|
4966
|
+
useEffect2(() => {
|
|
4013
4967
|
if (!isEditMode) return;
|
|
4014
4968
|
const measure = () => {
|
|
4015
4969
|
const h = document.body.scrollHeight;
|
|
@@ -4033,7 +4987,7 @@ function OhhwellsBridge() {
|
|
|
4033
4987
|
window.removeEventListener("resize", handleResize);
|
|
4034
4988
|
};
|
|
4035
4989
|
}, [pathname, isEditMode, postToParent]);
|
|
4036
|
-
|
|
4990
|
+
useEffect2(() => {
|
|
4037
4991
|
if (!subdomainFromQuery || isEditMode) return;
|
|
4038
4992
|
const handleClick = (e) => {
|
|
4039
4993
|
const anchor = e.target.closest("a");
|
|
@@ -4049,7 +5003,7 @@ function OhhwellsBridge() {
|
|
|
4049
5003
|
document.addEventListener("click", handleClick, true);
|
|
4050
5004
|
return () => document.removeEventListener("click", handleClick, true);
|
|
4051
5005
|
}, [subdomainFromQuery, isEditMode, router]);
|
|
4052
|
-
|
|
5006
|
+
useEffect2(() => {
|
|
4053
5007
|
if (!isEditMode) {
|
|
4054
5008
|
editStylesRef.current?.base.remove();
|
|
4055
5009
|
editStylesRef.current?.forceHover.remove();
|
|
@@ -4309,6 +5263,15 @@ function OhhwellsBridge() {
|
|
|
4309
5263
|
textEditable.setAttribute("data-ohw-hovered", "");
|
|
4310
5264
|
return;
|
|
4311
5265
|
}
|
|
5266
|
+
if (hoveredGapRef.current) {
|
|
5267
|
+
if (hoveredImageRef.current) {
|
|
5268
|
+
hoveredImageRef.current = null;
|
|
5269
|
+
resumeAnimTracks();
|
|
5270
|
+
postToParentRef.current({ type: "ow:image-unhover" });
|
|
5271
|
+
}
|
|
5272
|
+
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
5273
|
+
return;
|
|
5274
|
+
}
|
|
4312
5275
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
4313
5276
|
if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
|
|
4314
5277
|
hoveredImageRef.current = imgEl;
|
|
@@ -4374,8 +5337,30 @@ function OhhwellsBridge() {
|
|
|
4374
5337
|
}
|
|
4375
5338
|
}
|
|
4376
5339
|
};
|
|
5340
|
+
const probeSectionGapAt = (clientX, clientY, fromParentViewport = false) => {
|
|
5341
|
+
const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
5342
|
+
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
|
|
5343
|
+
const ZONE = 20;
|
|
5344
|
+
for (let i = 0; i < sections.length; i++) {
|
|
5345
|
+
const a = sections[i];
|
|
5346
|
+
const b = sections[i + 1] ?? null;
|
|
5347
|
+
const boundaryY = a.getBoundingClientRect().bottom;
|
|
5348
|
+
if (Math.abs(y - boundaryY) <= ZONE) {
|
|
5349
|
+
const insertAfter = a.dataset.ohwSection ?? "";
|
|
5350
|
+
const insertBefore = b?.dataset.ohwSection ?? null;
|
|
5351
|
+
hoveredGapRef.current = { insertAfter, insertBefore };
|
|
5352
|
+
setSectionGap({ insertAfter, insertBefore, y: boundaryY });
|
|
5353
|
+
return;
|
|
5354
|
+
}
|
|
5355
|
+
}
|
|
5356
|
+
if (hoveredGapRef.current) {
|
|
5357
|
+
hoveredGapRef.current = null;
|
|
5358
|
+
setSectionGap(null);
|
|
5359
|
+
}
|
|
5360
|
+
};
|
|
4377
5361
|
const handleMouseMove = (e) => {
|
|
4378
5362
|
const { clientX, clientY } = e;
|
|
5363
|
+
probeSectionGapAt(clientX, clientY);
|
|
4379
5364
|
probeImageAt(clientX, clientY);
|
|
4380
5365
|
probeHoverCardsAt(clientX, clientY);
|
|
4381
5366
|
};
|
|
@@ -4383,6 +5368,7 @@ function OhhwellsBridge() {
|
|
|
4383
5368
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
4384
5369
|
const { clientX, clientY } = e.data;
|
|
4385
5370
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
5371
|
+
probeSectionGapAt(clientX, clientY);
|
|
4386
5372
|
probeImageAt(clientX, clientY);
|
|
4387
5373
|
probeHoverCardsAt(clientX, clientY);
|
|
4388
5374
|
};
|
|
@@ -4540,7 +5526,12 @@ function OhhwellsBridge() {
|
|
|
4540
5526
|
if (e.data?.type !== "ow:hydrate") return;
|
|
4541
5527
|
const content = e.data.content;
|
|
4542
5528
|
if (!content) return;
|
|
5529
|
+
let sectionsJson = null;
|
|
4543
5530
|
for (const [key, val] of Object.entries(content)) {
|
|
5531
|
+
if (key === "__ohw_sections") {
|
|
5532
|
+
sectionsJson = val;
|
|
5533
|
+
continue;
|
|
5534
|
+
}
|
|
4544
5535
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
4545
5536
|
if (el.dataset.ohwEditable === "image") {
|
|
4546
5537
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
@@ -4552,6 +5543,9 @@ function OhhwellsBridge() {
|
|
|
4552
5543
|
}
|
|
4553
5544
|
});
|
|
4554
5545
|
}
|
|
5546
|
+
if (sectionsJson) {
|
|
5547
|
+
initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
|
|
5548
|
+
}
|
|
4555
5549
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
4556
5550
|
};
|
|
4557
5551
|
window.addEventListener("message", handleHydrate);
|
|
@@ -4595,7 +5589,63 @@ function OhhwellsBridge() {
|
|
|
4595
5589
|
};
|
|
4596
5590
|
const handleSave = (e) => {
|
|
4597
5591
|
if (e.data?.type !== "ow:save") return;
|
|
4598
|
-
|
|
5592
|
+
const nodes = collectEditableNodes();
|
|
5593
|
+
const tracker = document.querySelector("[data-ohw-sections-tracker]");
|
|
5594
|
+
if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
|
|
5595
|
+
postToParentRef.current({ type: "ow:save-result", nodes });
|
|
5596
|
+
};
|
|
5597
|
+
const handleInsertSection = (e) => {
|
|
5598
|
+
if (e.data?.type !== "ow:insert-section") return;
|
|
5599
|
+
const { widgetType, insertAfter, insertBefore } = e.data;
|
|
5600
|
+
if (widgetType !== "scheduling") return;
|
|
5601
|
+
const inserted = mountSchedulingWidget(insertAfter, true, void 0, insertBefore ?? null);
|
|
5602
|
+
if (inserted) {
|
|
5603
|
+
const tracker = getSectionsTracker();
|
|
5604
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
5605
|
+
const h = document.documentElement.scrollHeight;
|
|
5606
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
5607
|
+
}
|
|
5608
|
+
};
|
|
5609
|
+
const handleSwitchSchedule = (e) => {
|
|
5610
|
+
if (e.data?.type !== "ow:switch-schedule") return;
|
|
5611
|
+
const scheduleId = e.data.scheduleId;
|
|
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 }] });
|
|
5624
|
+
};
|
|
5625
|
+
const handleClearSchedulingWidget = (e) => {
|
|
5626
|
+
if (e.data?.type !== "ow:clear-scheduling-widget") return;
|
|
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 }] });
|
|
5631
|
+
};
|
|
5632
|
+
const handleRemoveSchedulingSection = (e) => {
|
|
5633
|
+
if (e.data?.type !== "ow:remove-scheduling-section") return;
|
|
5634
|
+
document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => {
|
|
5635
|
+
el.remove();
|
|
5636
|
+
});
|
|
5637
|
+
const tracker = getSectionsTracker();
|
|
5638
|
+
let sections = [];
|
|
5639
|
+
try {
|
|
5640
|
+
sections = JSON.parse(tracker.textContent || "[]");
|
|
5641
|
+
} catch {
|
|
5642
|
+
}
|
|
5643
|
+
const currentPath = window.location.pathname;
|
|
5644
|
+
const updated = sections.filter((s) => !(s.type === "scheduling" && s.pagePath === currentPath));
|
|
5645
|
+
tracker.textContent = JSON.stringify(updated);
|
|
5646
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
5647
|
+
const h = document.documentElement.scrollHeight;
|
|
5648
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
4599
5649
|
};
|
|
4600
5650
|
const handleSelectionChange = () => {
|
|
4601
5651
|
if (!activeElRef.current) return;
|
|
@@ -4700,6 +5750,11 @@ function OhhwellsBridge() {
|
|
|
4700
5750
|
deactivateRef.current();
|
|
4701
5751
|
};
|
|
4702
5752
|
window.addEventListener("message", handleSave);
|
|
5753
|
+
window.addEventListener("message", handleInsertSection);
|
|
5754
|
+
window.addEventListener("message", handleSwitchSchedule);
|
|
5755
|
+
window.addEventListener("message", handleScheduleLinked);
|
|
5756
|
+
window.addEventListener("message", handleClearSchedulingWidget);
|
|
5757
|
+
window.addEventListener("message", handleRemoveSchedulingSection);
|
|
4703
5758
|
window.addEventListener("message", handleImageUrl);
|
|
4704
5759
|
window.addEventListener("message", handleAnimLock);
|
|
4705
5760
|
window.addEventListener("message", handleCanvasHeight);
|
|
@@ -4734,6 +5789,11 @@ function OhhwellsBridge() {
|
|
|
4734
5789
|
document.removeEventListener("selectionchange", handleSelectionChange);
|
|
4735
5790
|
window.removeEventListener("scroll", handleScroll, true);
|
|
4736
5791
|
window.removeEventListener("message", handleSave);
|
|
5792
|
+
window.removeEventListener("message", handleInsertSection);
|
|
5793
|
+
window.removeEventListener("message", handleSwitchSchedule);
|
|
5794
|
+
window.removeEventListener("message", handleScheduleLinked);
|
|
5795
|
+
window.removeEventListener("message", handleClearSchedulingWidget);
|
|
5796
|
+
window.removeEventListener("message", handleRemoveSchedulingSection);
|
|
4737
5797
|
window.removeEventListener("message", handleImageUrl);
|
|
4738
5798
|
window.removeEventListener("message", handleAnimLock);
|
|
4739
5799
|
window.removeEventListener("message", handleCanvasHeight);
|
|
@@ -4748,7 +5808,7 @@ function OhhwellsBridge() {
|
|
|
4748
5808
|
if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
|
|
4749
5809
|
};
|
|
4750
5810
|
}, [isEditMode, refreshStateRules]);
|
|
4751
|
-
|
|
5811
|
+
useEffect2(() => {
|
|
4752
5812
|
if (!isEditMode) return;
|
|
4753
5813
|
document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
|
|
4754
5814
|
el.removeAttribute("data-ohw-active-state");
|
|
@@ -4790,12 +5850,12 @@ function OhhwellsBridge() {
|
|
|
4790
5850
|
setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
|
|
4791
5851
|
}, [deactivate]);
|
|
4792
5852
|
return bridgeRoot ? createPortal(
|
|
4793
|
-
/* @__PURE__ */
|
|
4794
|
-
toolbarRect && /* @__PURE__ */
|
|
4795
|
-
/* @__PURE__ */
|
|
4796
|
-
/* @__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 })
|
|
4797
5857
|
] }),
|
|
4798
|
-
maxBadge && /* @__PURE__ */
|
|
5858
|
+
maxBadge && /* @__PURE__ */ jsxs3(
|
|
4799
5859
|
"div",
|
|
4800
5860
|
{
|
|
4801
5861
|
"data-ohw-max-badge": "",
|
|
@@ -4821,7 +5881,7 @@ function OhhwellsBridge() {
|
|
|
4821
5881
|
]
|
|
4822
5882
|
}
|
|
4823
5883
|
),
|
|
4824
|
-
toggleState && /* @__PURE__ */
|
|
5884
|
+
toggleState && /* @__PURE__ */ jsx6(
|
|
4825
5885
|
StateToggle,
|
|
4826
5886
|
{
|
|
4827
5887
|
rect: toggleState.rect,
|
|
@@ -4829,6 +5889,31 @@ function OhhwellsBridge() {
|
|
|
4829
5889
|
states: toggleState.states,
|
|
4830
5890
|
onStateChange: handleStateChange
|
|
4831
5891
|
}
|
|
5892
|
+
),
|
|
5893
|
+
sectionGap && /* @__PURE__ */ jsxs3(
|
|
5894
|
+
"div",
|
|
5895
|
+
{
|
|
5896
|
+
"data-ohw-section-insert-line": "",
|
|
5897
|
+
className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
|
|
5898
|
+
style: { top: sectionGap.y, transform: "translateY(-50%)" },
|
|
5899
|
+
children: [
|
|
5900
|
+
/* @__PURE__ */ jsx6("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
|
|
5901
|
+
/* @__PURE__ */ jsx6(
|
|
5902
|
+
Badge,
|
|
5903
|
+
{
|
|
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",
|
|
5905
|
+
onClick: () => {
|
|
5906
|
+
window.parent.postMessage(
|
|
5907
|
+
{ type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
|
|
5908
|
+
"*"
|
|
5909
|
+
);
|
|
5910
|
+
},
|
|
5911
|
+
children: "Add Section"
|
|
5912
|
+
}
|
|
5913
|
+
),
|
|
5914
|
+
/* @__PURE__ */ jsx6("div", { className: "flex-1 bg-primary", style: { height: 3 } })
|
|
5915
|
+
]
|
|
5916
|
+
}
|
|
4832
5917
|
)
|
|
4833
5918
|
] }),
|
|
4834
5919
|
bridgeRoot
|
|
@@ -4836,6 +5921,7 @@ function OhhwellsBridge() {
|
|
|
4836
5921
|
}
|
|
4837
5922
|
export {
|
|
4838
5923
|
OhhwellsBridge,
|
|
5924
|
+
SchedulingWidget,
|
|
4839
5925
|
Toggle,
|
|
4840
5926
|
ToggleGroup,
|
|
4841
5927
|
ToggleGroupItem,
|