@ohhwells/bridge 0.1.42-next.93 → 0.1.42-next.94
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 +12 -0
- package/dist/index.cjs +77 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +128 -72
- package/dist/index.js.map +1 -1
- package/dist/styles.css +0 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -318,6 +318,18 @@ npm run build
|
|
|
318
318
|
npm publish --access public
|
|
319
319
|
```
|
|
320
320
|
|
|
321
|
+
### Staging → rebound-template auto-bump
|
|
322
|
+
|
|
323
|
+
When `staging` publishes successfully, the bridge workflow dispatches a `bridge-published` event to `TheFlowOps-Eng/rebound-template`. That repo’s `bump-bridge.yml` workflow checks out `staging`, runs `npm install @ohhwells/bridge@<version> --save-exact`, and pushes the lockfile bump.
|
|
324
|
+
|
|
325
|
+
**One-time setup** (in `ohhwells-bridge` GitHub repo → Settings → Secrets):
|
|
326
|
+
|
|
327
|
+
| Secret | Value |
|
|
328
|
+
|--------|--------|
|
|
329
|
+
| `REBOUND_TEMPLATE_DISPATCH_TOKEN` | Fine-grained or classic PAT with `repo` access to `rebound-template` (needs permission to trigger `repository_dispatch`) |
|
|
330
|
+
|
|
331
|
+
The published staging version looks like `0.1.31-next.42` and is tagged `next` on npm.
|
|
332
|
+
|
|
321
333
|
---
|
|
322
334
|
|
|
323
335
|
## Link dialog (Canvas Editor)
|
package/dist/index.cjs
CHANGED
|
@@ -115,6 +115,7 @@ var import_react3 = require("react");
|
|
|
115
115
|
var import_react2 = require("react");
|
|
116
116
|
var import_radix_ui = require("radix-ui");
|
|
117
117
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
118
|
+
var isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
|
|
118
119
|
function Spinner() {
|
|
119
120
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
120
121
|
"svg",
|
|
@@ -148,12 +149,17 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
|
148
149
|
const [error, setError] = (0, import_react2.useState)(null);
|
|
149
150
|
const isBook = title === "Confirm your spot";
|
|
150
151
|
const handleSubmit = async () => {
|
|
151
|
-
|
|
152
|
+
const trimmed = email.trim();
|
|
153
|
+
if (!trimmed) return;
|
|
154
|
+
if (!isValidEmail(trimmed)) {
|
|
155
|
+
setError("Please enter a valid email address.");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
152
158
|
setLoading(true);
|
|
153
159
|
setError(null);
|
|
154
160
|
try {
|
|
155
|
-
await onSubmit(
|
|
156
|
-
setSubmittedEmail(
|
|
161
|
+
await onSubmit(trimmed);
|
|
162
|
+
setSubmittedEmail(trimmed);
|
|
157
163
|
setSuccess(true);
|
|
158
164
|
} catch (e) {
|
|
159
165
|
setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
|
|
@@ -228,7 +234,10 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
|
228
234
|
{
|
|
229
235
|
type: "email",
|
|
230
236
|
value: email,
|
|
231
|
-
onChange: (e) =>
|
|
237
|
+
onChange: (e) => {
|
|
238
|
+
setEmail(e.target.value);
|
|
239
|
+
if (error) setError(null);
|
|
240
|
+
},
|
|
232
241
|
onKeyDown: handleKeyDown,
|
|
233
242
|
placeholder: "hello@gmail.com",
|
|
234
243
|
autoFocus: true,
|
|
@@ -285,12 +294,20 @@ function formatClassTime(cls) {
|
|
|
285
294
|
const em = endTotal % 60;
|
|
286
295
|
return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
|
|
287
296
|
}
|
|
297
|
+
function formatBookingLabel(cls) {
|
|
298
|
+
const price = cls.price;
|
|
299
|
+
const isFree = price === null || price === void 0 || `${price}` === "" || `${price}` === "0";
|
|
300
|
+
return isFree ? "Book for free" : `Book for ${cls.currency ?? ""} ${price}`.trim();
|
|
301
|
+
}
|
|
288
302
|
function getBookingsOnDate(cls, date) {
|
|
289
303
|
if (!cls.bookings?.length) return 0;
|
|
290
|
-
const
|
|
304
|
+
const target = new Date(date);
|
|
305
|
+
target.setHours(0, 0, 0, 0);
|
|
291
306
|
return cls.bookings.filter((b) => {
|
|
292
307
|
try {
|
|
293
|
-
|
|
308
|
+
const bookingDate = new Date(b.classDate);
|
|
309
|
+
bookingDate.setHours(0, 0, 0, 0);
|
|
310
|
+
return bookingDate.getTime() === target.getTime();
|
|
294
311
|
} catch {
|
|
295
312
|
return false;
|
|
296
313
|
}
|
|
@@ -555,7 +572,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
|
|
|
555
572
|
}
|
|
556
573
|
)
|
|
557
574
|
] }),
|
|
558
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
575
|
+
cls.showAvailability !== false && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
559
576
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
560
577
|
available,
|
|
561
578
|
"/",
|
|
@@ -567,9 +584,17 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
|
|
|
567
584
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-1 flex-col gap-2 min-w-0 sm:contents", children: [
|
|
568
585
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col gap-2 min-w-0", children: [
|
|
569
586
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
|
|
570
|
-
cls.hostName && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
|
|
587
|
+
cls.hostName && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName }),
|
|
588
|
+
cls.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
589
|
+
"span",
|
|
590
|
+
{
|
|
591
|
+
className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block",
|
|
592
|
+
style: { wordBreak: "break-word" },
|
|
593
|
+
children: cls.description
|
|
594
|
+
}
|
|
595
|
+
)
|
|
571
596
|
] }),
|
|
572
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
597
|
+
cls.showAvailability !== false && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
573
598
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
|
|
574
599
|
available,
|
|
575
600
|
"/",
|
|
@@ -594,7 +619,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
|
|
|
594
619
|
if (!cls.id) return;
|
|
595
620
|
onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
|
|
596
621
|
},
|
|
597
|
-
children: isFull ? "Join Waitlist" :
|
|
622
|
+
children: isFull ? "Join Waitlist" : formatBookingLabel(cls)
|
|
598
623
|
}
|
|
599
624
|
)
|
|
600
625
|
] })
|
|
@@ -636,6 +661,17 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
636
661
|
const [isHovered, setIsHovered] = (0, import_react3.useState)(false);
|
|
637
662
|
const [modalState, setModalState] = (0, import_react3.useState)(null);
|
|
638
663
|
const switchScheduleIdRef = (0, import_react3.useRef)(null);
|
|
664
|
+
const liveScheduleIdRef = (0, import_react3.useRef)(null);
|
|
665
|
+
const refetchLiveSchedule = (0, import_react3.useCallback)(async () => {
|
|
666
|
+
const id = liveScheduleIdRef.current;
|
|
667
|
+
if (!id) return;
|
|
668
|
+
try {
|
|
669
|
+
const res = await fetch(`${API_URL}/api/schedule/id/${id}`);
|
|
670
|
+
const data = await res.json();
|
|
671
|
+
if (data?.id) setSchedule(data);
|
|
672
|
+
} catch {
|
|
673
|
+
}
|
|
674
|
+
}, []);
|
|
639
675
|
const dates = (0, import_react3.useMemo)(() => {
|
|
640
676
|
const today = /* @__PURE__ */ new Date();
|
|
641
677
|
today.setHours(0, 0, 0, 0);
|
|
@@ -655,6 +691,18 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
655
691
|
}
|
|
656
692
|
}
|
|
657
693
|
}, [schedule, dates]);
|
|
694
|
+
(0, import_react3.useEffect)(() => {
|
|
695
|
+
if (typeof window === "undefined") return;
|
|
696
|
+
const onVisible = () => {
|
|
697
|
+
if (!document.hidden) void refetchLiveSchedule();
|
|
698
|
+
};
|
|
699
|
+
window.addEventListener("focus", refetchLiveSchedule);
|
|
700
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
701
|
+
return () => {
|
|
702
|
+
window.removeEventListener("focus", refetchLiveSchedule);
|
|
703
|
+
document.removeEventListener("visibilitychange", onVisible);
|
|
704
|
+
};
|
|
705
|
+
}, [refetchLiveSchedule]);
|
|
658
706
|
(0, import_react3.useEffect)(() => {
|
|
659
707
|
if (typeof window === "undefined") return;
|
|
660
708
|
const isInEditor = window.self !== window.top;
|
|
@@ -716,7 +764,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
716
764
|
setLoading(false);
|
|
717
765
|
return;
|
|
718
766
|
}
|
|
719
|
-
;
|
|
767
|
+
liveScheduleIdRef.current = effectiveId;
|
|
720
768
|
(async () => {
|
|
721
769
|
try {
|
|
722
770
|
const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
|
|
@@ -773,18 +821,14 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
773
821
|
const data = await res.json().catch(() => ({}));
|
|
774
822
|
throw new Error(data?.message ?? "Something went wrong. Please try again.");
|
|
775
823
|
}
|
|
824
|
+
await refetchLiveSchedule();
|
|
776
825
|
};
|
|
777
826
|
const handleReplaceSchedule = () => {
|
|
778
827
|
setIsHovered(false);
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
insertAfter
|
|
784
|
-
}, "*");
|
|
785
|
-
} else {
|
|
786
|
-
window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
|
|
787
|
-
}
|
|
828
|
+
window.parent.postMessage(
|
|
829
|
+
{ type: "ow:scheduling-not-connected", insertAfter, currentScheduleId: schedule?.id },
|
|
830
|
+
"*"
|
|
831
|
+
);
|
|
788
832
|
};
|
|
789
833
|
if (!inEditor && !loading && !schedule) return null;
|
|
790
834
|
const sectionId = `scheduling-${insertAfter}`;
|
|
@@ -827,7 +871,19 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
827
871
|
),
|
|
828
872
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
|
|
829
873
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
|
|
830
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
874
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
875
|
+
"h2",
|
|
876
|
+
{
|
|
877
|
+
className: "font-display text-center m-0 text-(--color-dark,#200C02)",
|
|
878
|
+
style: {
|
|
879
|
+
fontSize: "var(--fs-section-h2, clamp(40px, 4.4vw, 64px))",
|
|
880
|
+
fontWeight: "var(--font-weight-heading, 400)",
|
|
881
|
+
lineHeight: 1.05,
|
|
882
|
+
letterSpacing: "-0.02em"
|
|
883
|
+
},
|
|
884
|
+
children: schedule?.name ?? "Book an appointment"
|
|
885
|
+
}
|
|
886
|
+
),
|
|
831
887
|
schedule?.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
|
|
832
888
|
] }),
|
|
833
889
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col items-end gap-3", children: [
|