@ohhwells/bridge 0.1.22 → 0.1.24
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 +58 -0
- package/dist/index.cjs +148 -90
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +149 -91
- package/dist/index.js.map +1 -1
- package/dist/styles.css +4 -0
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -23,10 +23,10 @@ declare function ToggleGroupItem({ className, value, children, ...props }: {
|
|
|
23
23
|
children: React.ReactNode;
|
|
24
24
|
} & Omit<React.ComponentProps<typeof Toggle>, 'pressed' | 'onPressedChange'>): react_jsx_runtime.JSX.Element;
|
|
25
25
|
|
|
26
|
-
declare function SchedulingWidget({ notifyOnConnect, initialScheduleId, insertAfter }: {
|
|
26
|
+
declare function SchedulingWidget({ notifyOnConnect, initialScheduleId, insertAfter: insertAfterProp }: {
|
|
27
27
|
notifyOnConnect?: boolean;
|
|
28
28
|
initialScheduleId?: string | null;
|
|
29
|
-
insertAfter
|
|
29
|
+
insertAfter?: string;
|
|
30
30
|
}): react_jsx_runtime.JSX.Element | null;
|
|
31
31
|
|
|
32
32
|
export { OhhwellsBridge, SchedulingWidget, Toggle, ToggleGroup, ToggleGroupItem, toggleVariants };
|
package/dist/index.d.ts
CHANGED
|
@@ -23,10 +23,10 @@ declare function ToggleGroupItem({ className, value, children, ...props }: {
|
|
|
23
23
|
children: React.ReactNode;
|
|
24
24
|
} & Omit<React.ComponentProps<typeof Toggle>, 'pressed' | 'onPressedChange'>): react_jsx_runtime.JSX.Element;
|
|
25
25
|
|
|
26
|
-
declare function SchedulingWidget({ notifyOnConnect, initialScheduleId, insertAfter }: {
|
|
26
|
+
declare function SchedulingWidget({ notifyOnConnect, initialScheduleId, insertAfter: insertAfterProp }: {
|
|
27
27
|
notifyOnConnect?: boolean;
|
|
28
28
|
initialScheduleId?: string | null;
|
|
29
|
-
insertAfter
|
|
29
|
+
insertAfter?: string;
|
|
30
30
|
}): react_jsx_runtime.JSX.Element | null;
|
|
31
31
|
|
|
32
32
|
export { OhhwellsBridge, SchedulingWidget, Toggle, ToggleGroup, ToggleGroupItem, toggleVariants };
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createRoot } from "react-dom/client";
|
|
|
6
6
|
import { flushSync } from "react-dom";
|
|
7
7
|
|
|
8
8
|
// src/ui/SchedulingWidget.tsx
|
|
9
|
-
import { useEffect, useMemo, useRef, useState as useState2 } from "react";
|
|
9
|
+
import { useEffect, useId, useMemo, useRef, useState as useState2 } from "react";
|
|
10
10
|
|
|
11
11
|
// src/ui/EmailCaptureModal.tsx
|
|
12
12
|
import { useState } from "react";
|
|
@@ -164,13 +164,6 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
|
164
164
|
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
165
165
|
var API_URL = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
166
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
167
|
function classRunsOnDate(cls, date, type) {
|
|
175
168
|
if (type === "FIXED") {
|
|
176
169
|
const d = String(date.getDate()).padStart(2, "0");
|
|
@@ -444,18 +437,21 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
|
|
|
444
437
|
const booked = getBookingsOnDate(cls, selectedDate);
|
|
445
438
|
const available = cls.maxParticipants - booked;
|
|
446
439
|
const isFull = available <= 0;
|
|
440
|
+
const isPrivate = cls.maxParticipants === 1;
|
|
447
441
|
return /* @__PURE__ */ jsxs2("div", { children: [
|
|
448
442
|
/* @__PURE__ */ jsxs2("div", { className: "flex gap-4 py-4 sm:items-center sm:gap-[60px] box-border", children: [
|
|
449
443
|
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2 shrink-0 sm:contents", children: [
|
|
450
|
-
/* @__PURE__ */
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
444
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2 sm:w-[120px] sm:shrink-0", children: [
|
|
445
|
+
/* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block", children: formatClassTime(cls) }),
|
|
446
|
+
/* @__PURE__ */ jsx2(
|
|
447
|
+
"span",
|
|
448
|
+
{
|
|
449
|
+
className: "inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium w-fit",
|
|
450
|
+
style: isPrivate ? { background: "#DDFBE3", color: "#199130", border: "1px solid #199130", fontWeight: 500 } : { background: "#EEEDFF", color: "#5953FF", border: "1px solid #5953FF", fontWeight: 500 },
|
|
451
|
+
children: isPrivate ? "PRIVATE" : "GROUP"
|
|
452
|
+
}
|
|
453
|
+
)
|
|
454
|
+
] }),
|
|
459
455
|
/* @__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
456
|
/* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
461
457
|
available,
|
|
@@ -528,7 +524,9 @@ function CalendarFoldIcon() {
|
|
|
528
524
|
}
|
|
529
525
|
);
|
|
530
526
|
}
|
|
531
|
-
function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAfter }) {
|
|
527
|
+
function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAfter: insertAfterProp }) {
|
|
528
|
+
const autoId = useId();
|
|
529
|
+
const insertAfter = insertAfterProp ?? autoId;
|
|
532
530
|
const [schedule, setSchedule] = useState2(null);
|
|
533
531
|
const [loading, setLoading] = useState2(true);
|
|
534
532
|
const [inEditor, setInEditor] = useState2(false);
|
|
@@ -558,88 +556,99 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
558
556
|
if (typeof window === "undefined") return;
|
|
559
557
|
const isInEditor = window.self !== window.top;
|
|
560
558
|
setInEditor(isInEditor);
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
559
|
+
function loadWithId(effectiveId) {
|
|
560
|
+
if (isInEditor) {
|
|
561
|
+
const startEmpty = effectiveId === null;
|
|
562
|
+
if (startEmpty) setLoading(false);
|
|
563
|
+
let initialized = startEmpty;
|
|
564
|
+
const timer = !startEmpty ? setTimeout(() => {
|
|
565
|
+
if (!initialized) {
|
|
566
|
+
initialized = true;
|
|
567
|
+
setLoading(false);
|
|
568
|
+
}
|
|
569
|
+
}, 5e3) : null;
|
|
570
|
+
const handler = (e) => {
|
|
571
|
+
if (e.data?.type === "ow:clear-scheduling-widget") {
|
|
572
|
+
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
573
|
+
setSchedule(null);
|
|
574
|
+
setLoading(false);
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
if (e.data?.type === "ow:switch-schedule") {
|
|
578
|
+
if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
|
|
579
|
+
switchScheduleIdRef.current = e.data.scheduleId ?? null;
|
|
580
|
+
initialized = false;
|
|
581
|
+
setLoading(true);
|
|
582
|
+
window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (e.data?.type !== "ow:user-schedules-response") return;
|
|
586
|
+
if (e.data.insertAfter && e.data.insertAfter !== insertAfter) return;
|
|
587
|
+
if (initialized && switchScheduleIdRef.current === null) return;
|
|
567
588
|
initialized = true;
|
|
589
|
+
if (timer) clearTimeout(timer);
|
|
590
|
+
const schedules = e.data.schedules ?? [];
|
|
591
|
+
const isSwitching = switchScheduleIdRef.current !== null;
|
|
592
|
+
const target = isSwitching ? schedules.find((s) => s.id === switchScheduleIdRef.current) ?? schedules[0] ?? null : effectiveId ? schedules.find((s) => s.id === effectiveId) ?? schedules[0] ?? null : schedules[0] ?? null;
|
|
593
|
+
switchScheduleIdRef.current = null;
|
|
594
|
+
setSchedule(target);
|
|
568
595
|
setLoading(false);
|
|
596
|
+
if (notifyOnConnect && target && !isSwitching) {
|
|
597
|
+
window.parent.postMessage({
|
|
598
|
+
type: "ow:schedule-connected",
|
|
599
|
+
schedule: { id: target.id, name: target.name },
|
|
600
|
+
insertAfter
|
|
601
|
+
}, "*");
|
|
602
|
+
window.postMessage({ type: "ow:schedule-linked", scheduleId: target.id, insertAfter }, "*");
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
window.addEventListener("message", handler);
|
|
606
|
+
if (!startEmpty) window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
|
|
607
|
+
return () => {
|
|
608
|
+
window.removeEventListener("message", handler);
|
|
609
|
+
if (timer) clearTimeout(timer);
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
if (!effectiveId) {
|
|
613
|
+
setLoading(false);
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
;
|
|
617
|
+
(async () => {
|
|
618
|
+
try {
|
|
619
|
+
const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
|
|
620
|
+
const data = await res.json();
|
|
621
|
+
setSchedule(data?.id ? data : null);
|
|
622
|
+
} catch {
|
|
569
623
|
}
|
|
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
624
|
setLoading(false);
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
625
|
+
})();
|
|
626
|
+
}
|
|
627
|
+
if (initialScheduleId === void 0) {
|
|
628
|
+
let done = false;
|
|
629
|
+
let innerCleanup;
|
|
630
|
+
const timer = setTimeout(() => {
|
|
631
|
+
if (!done) {
|
|
632
|
+
done = true;
|
|
633
|
+
setLoading(false);
|
|
604
634
|
}
|
|
635
|
+
}, 2e3);
|
|
636
|
+
const configHandler = (e) => {
|
|
637
|
+
if (e.data?.type !== "ow:schedule-config" || e.data.insertAfter !== insertAfter || done) return;
|
|
638
|
+
done = true;
|
|
639
|
+
clearTimeout(timer);
|
|
640
|
+
window.removeEventListener("message", configHandler);
|
|
641
|
+
innerCleanup = loadWithId(e.data.scheduleId ?? null);
|
|
605
642
|
};
|
|
606
|
-
window.addEventListener("message",
|
|
607
|
-
|
|
643
|
+
window.addEventListener("message", configHandler);
|
|
644
|
+
window.postMessage({ type: "ow:request-schedule-config", insertAfter }, "*");
|
|
608
645
|
return () => {
|
|
609
|
-
window.removeEventListener("message",
|
|
610
|
-
|
|
646
|
+
window.removeEventListener("message", configHandler);
|
|
647
|
+
clearTimeout(timer);
|
|
648
|
+
innerCleanup?.();
|
|
611
649
|
};
|
|
612
650
|
}
|
|
613
|
-
|
|
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
|
-
})();
|
|
651
|
+
return loadWithId(initialScheduleId) ?? void 0;
|
|
643
652
|
}, []);
|
|
644
653
|
const timezoneLabel = schedule?.timezone ? (() => {
|
|
645
654
|
try {
|
|
@@ -4339,10 +4348,14 @@ function initSectionsFromContent(content, removeExisting = false) {
|
|
|
4339
4348
|
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
4340
4349
|
if (inEditor) getSectionsTracker().textContent = raw;
|
|
4341
4350
|
const pageEntries = getPageSchedulingEntries(raw);
|
|
4351
|
+
const preExisting = pageEntries.filter((e) => !isSchedulingWidgetMissing(e));
|
|
4342
4352
|
const notifyForEntry = inEditor ? (e) => !e.scheduleId : false;
|
|
4343
4353
|
mountSchedulingEntries(pageEntries, notifyForEntry);
|
|
4344
4354
|
requestAnimationFrame(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry));
|
|
4345
4355
|
setTimeout(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry), 250);
|
|
4356
|
+
for (const entry of preExisting) {
|
|
4357
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: entry.insertAfter, scheduleId: entry.scheduleId ?? null }, "*");
|
|
4358
|
+
}
|
|
4346
4359
|
} catch {
|
|
4347
4360
|
}
|
|
4348
4361
|
}
|
|
@@ -4796,6 +4809,8 @@ function OhhwellsBridge() {
|
|
|
4796
4809
|
});
|
|
4797
4810
|
const postToParentRef = useRef2(postToParent);
|
|
4798
4811
|
postToParentRef.current = postToParent;
|
|
4812
|
+
const sectionsLoadedRef = useRef2(false);
|
|
4813
|
+
const pendingScheduleConfigRequests = useRef2([]);
|
|
4799
4814
|
const [toolbarRect, setToolbarRect] = useState3(null);
|
|
4800
4815
|
const [toggleState, setToggleState] = useState3(null);
|
|
4801
4816
|
const [maxBadge, setMaxBadge] = useState3(null);
|
|
@@ -4828,6 +4843,29 @@ function OhhwellsBridge() {
|
|
|
4828
4843
|
const refreshStateRules = useCallback(() => {
|
|
4829
4844
|
editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
|
|
4830
4845
|
}, []);
|
|
4846
|
+
const processConfigRequest = useCallback((insertAfterVal) => {
|
|
4847
|
+
const tracker = getSectionsTracker();
|
|
4848
|
+
let entries = [];
|
|
4849
|
+
try {
|
|
4850
|
+
entries = JSON.parse(tracker.textContent || "[]");
|
|
4851
|
+
} catch {
|
|
4852
|
+
}
|
|
4853
|
+
const path = window.location.pathname;
|
|
4854
|
+
const entry = entries.find(
|
|
4855
|
+
(e) => e.type === "scheduling" && e.insertAfter === insertAfterVal && (!e.pagePath || e.pagePath === path)
|
|
4856
|
+
);
|
|
4857
|
+
if (entry) {
|
|
4858
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: entry.scheduleId ?? null }, "*");
|
|
4859
|
+
return;
|
|
4860
|
+
}
|
|
4861
|
+
const newEntry = { type: "scheduling", insertAfter: insertAfterVal, pagePath: path };
|
|
4862
|
+
entries.push(newEntry);
|
|
4863
|
+
tracker.textContent = JSON.stringify(entries);
|
|
4864
|
+
if (isEditMode) {
|
|
4865
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
|
|
4866
|
+
}
|
|
4867
|
+
window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
|
|
4868
|
+
}, [isEditMode]);
|
|
4831
4869
|
const deactivate = useCallback(() => {
|
|
4832
4870
|
const el = activeElRef.current;
|
|
4833
4871
|
if (!el) return;
|
|
@@ -4899,6 +4937,8 @@ function OhhwellsBridge() {
|
|
|
4899
4937
|
});
|
|
4900
4938
|
}
|
|
4901
4939
|
initSectionsFromContent(content, true);
|
|
4940
|
+
sectionsLoadedRef.current = true;
|
|
4941
|
+
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
4902
4942
|
return imageLoads.length > 0 ? Promise.all(imageLoads).then(() => {
|
|
4903
4943
|
}) : Promise.resolve();
|
|
4904
4944
|
};
|
|
@@ -5548,6 +5588,8 @@ function OhhwellsBridge() {
|
|
|
5548
5588
|
}
|
|
5549
5589
|
if (sectionsJson) {
|
|
5550
5590
|
initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
|
|
5591
|
+
sectionsLoadedRef.current = true;
|
|
5592
|
+
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
5551
5593
|
}
|
|
5552
5594
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
5553
5595
|
};
|
|
@@ -5811,6 +5853,22 @@ function OhhwellsBridge() {
|
|
|
5811
5853
|
if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
|
|
5812
5854
|
};
|
|
5813
5855
|
}, [isEditMode, refreshStateRules]);
|
|
5856
|
+
useEffect2(() => {
|
|
5857
|
+
const handler = (e) => {
|
|
5858
|
+
if (e.data?.type !== "ow:request-schedule-config") return;
|
|
5859
|
+
const insertAfterVal = e.data.insertAfter;
|
|
5860
|
+
if (!insertAfterVal) return;
|
|
5861
|
+
if (!sectionsLoadedRef.current) {
|
|
5862
|
+
if (!pendingScheduleConfigRequests.current.includes(insertAfterVal)) {
|
|
5863
|
+
pendingScheduleConfigRequests.current.push(insertAfterVal);
|
|
5864
|
+
}
|
|
5865
|
+
return;
|
|
5866
|
+
}
|
|
5867
|
+
processConfigRequest(insertAfterVal);
|
|
5868
|
+
};
|
|
5869
|
+
window.addEventListener("message", handler);
|
|
5870
|
+
return () => window.removeEventListener("message", handler);
|
|
5871
|
+
}, [processConfigRequest]);
|
|
5814
5872
|
useEffect2(() => {
|
|
5815
5873
|
if (!isEditMode) return;
|
|
5816
5874
|
document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
|