From d92aa70d542cc5b4e0ba355d5efdf2ae64023f38 Mon Sep 17 00:00:00 2001 From: MarkoKCOM Date: Mon, 20 Apr 2026 15:18:50 +0000 Subject: [PATCH 1/2] fix: default marketing site to hebrew --- apps/marketing-site/src/LandingPage.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/marketing-site/src/LandingPage.tsx b/apps/marketing-site/src/LandingPage.tsx index dc851f5..a8df632 100644 --- a/apps/marketing-site/src/LandingPage.tsx +++ b/apps/marketing-site/src/LandingPage.tsx @@ -1398,10 +1398,7 @@ export function LandingPage() { const [lang, setLang] = useState(() => { const params = new URLSearchParams(window.location.search); const p = params.get("lang"); - if (p === "en" || p === "ar") return p; - const nav = navigator.language?.toLowerCase() || ""; - if (nav.startsWith("ar")) return "ar"; - if (nav.startsWith("en")) return "en"; + if (p === "he" || p === "en" || p === "ar") return p; return "he"; }); From 0f0d3a9e1d01eed0c86458a40bf6548eb8f52727 Mon Sep 17 00:00:00 2001 From: MarkoKCOM Date: Mon, 20 Apr 2026 16:20:53 +0000 Subject: [PATCH 2/2] fix: replace marketing widget code block with live embed --- apps/marketing-site/src/LandingPage.tsx | 119 ++++++++++++++++++++++-- 1 file changed, 110 insertions(+), 9 deletions(-) diff --git a/apps/marketing-site/src/LandingPage.tsx b/apps/marketing-site/src/LandingPage.tsx index a8df632..2e46885 100644 --- a/apps/marketing-site/src/LandingPage.tsx +++ b/apps/marketing-site/src/LandingPage.tsx @@ -144,7 +144,10 @@ const I18N = { dateFull: "שישי, 24.4", dateRelative: "היום + 4 ימים", demoName: "דני ל.", repeatGuests: "אורחים חוזרים", waAutoConfirm: "WhatsApp · אישור אוטומטי", aiTyping: "AI מקליד תשובה", - embedLabel: "EMBED ON YOUR SITE", + embedLabel: "רץ עכשיו באתר שלך", + embedNote: "זה הווידג׳ט האמיתי, לא תמונה ולא קישור. האורח מזמין כאן וההזמנה נכנסת ישר למערכת.", + widgetLoading: "טוען את הווידג׳ט החי...", + widgetError: "הווידג׳ט לא נטען כרגע.", formName: "שם מלא", formEmail: "Email", formRestaurant: "שם המסעדה", formPhone: "טלפון", formSeats: "גודל מסעדה", formSend: "שלח בקשה", formSending: "...", formSent: "נשלח ✓", cmpLegend: { yes: "יש", partial: "חלקי", no: "אין" }, @@ -287,7 +290,10 @@ const I18N = { dateFull: "Fri, Apr 24", dateRelative: "Today + 4 days", demoName: "Danny L.", repeatGuests: "repeat guests", waAutoConfirm: "WhatsApp \u00B7 auto-confirm", aiTyping: "AI typing\u2026", - embedLabel: "EMBED ON YOUR SITE", + embedLabel: "RUNNING LIVE ON YOUR SITE", + embedNote: "This is the real widget, not a screenshot and not a link. Guests book here and the reservation goes straight into the system.", + widgetLoading: "Loading the live widget...", + widgetError: "The widget could not load right now.", formName: "Full name", formEmail: "Email", formRestaurant: "Restaurant", formPhone: "Phone", formSeats: "Seats", formSend: "Send request", formSending: "...", formSent: "Sent \u2713", cmpLegend: { yes: "Included", partial: "Partial", no: "Missing" }, @@ -430,7 +436,10 @@ const I18N = { dateFull: "الجمعة 24.4", dateRelative: "اليوم + 4 أيام", demoName: "دني ل.", repeatGuests: "عائدون", waAutoConfirm: "واتساب · تأكيد تلقائي", aiTyping: "AI يكتب...", - embedLabel: "EMBED ON YOUR SITE", + embedLabel: "يعمل الآن على موقعك", + embedNote: "هذه هي الودجة الحقيقية، ليست صورة ولا رابطًا. الضيف يحجز هنا والحجز يدخل مباشرة إلى النظام.", + widgetLoading: "يتم تحميل الودجة الحية...", + widgetError: "تعذر تحميل الودجة الآن.", formName: "الاسم", formEmail: "البريد الإلكتروني", formRestaurant: "اسم المطعم", formPhone: "هاتف", formSeats: "حجم المطعم", formSend: "أرسل", formSending: "...", formSent: "أُرسل ✓", cmpLegend: { yes: "موجود", partial: "جزئي", no: "غير موجود" }, @@ -439,6 +448,44 @@ const I18N = { type I18NData = (typeof I18N)[Lang]; +type OpenSeatBookingWindow = Window & typeof globalThis & { + OpenSeatBooking?: { + mount: (el: HTMLElement, config: { restaurantId: string; apiUrl?: string }) => void; + }; +}; + +const LIVE_WIDGET_RESTAURANT_ID = "c3c22e37-a309-4fde-aa6c-6e714212a3bc"; +const LIVE_WIDGET_API_URL = "https://booking-widget-rust.vercel.app"; +const LIVE_WIDGET_SCRIPT_URL = `${LIVE_WIDGET_API_URL}/openseat-booking.iife.js`; + +let liveWidgetScriptPromise: Promise | null = null; + +function ensureLiveWidgetScript() { + const widgetWindow = window as OpenSeatBookingWindow; + if (widgetWindow.OpenSeatBooking) return Promise.resolve(); + + if (!liveWidgetScriptPromise) { + liveWidgetScriptPromise = new Promise((resolve, reject) => { + const existing = document.querySelector('script[data-openseat-live-widget="true"]'); + if (existing) { + existing.addEventListener("load", () => resolve(), { once: true }); + existing.addEventListener("error", () => reject(new Error("Failed to load OpenSeat widget script")), { once: true }); + return; + } + + const script = document.createElement("script"); + script.src = LIVE_WIDGET_SCRIPT_URL; + script.async = true; + script.dataset.openseatLiveWidget = "true"; + script.onload = () => resolve(); + script.onerror = () => reject(new Error("Failed to load OpenSeat widget script")); + document.body.appendChild(script); + }); + } + + return liveWidgetScriptPromise; +} + /* ═══════════════════════════════════════════════════════════ Hooks ═══════════════════════════════════════════════════════════ */ @@ -1179,6 +1226,65 @@ function FAQ({ L }: { L: I18NData }) { /* ═══════════════════════════════════════════════════════════ Live Demo (interactive phone mockup) ═══════════════════════════════════════════════════════════ */ +function LiveWidgetEmbed({ L }: { L: I18NData }) { + const hostRef = useRef(null); + const [status, setStatus] = useState<"loading" | "ready" | "error">("loading"); + + useEffect(() => { + let cancelled = false; + + const mountWidget = async () => { + const host = hostRef.current; + if (!host) return; + + setStatus("loading"); + host.innerHTML = ""; + + try { + await ensureLiveWidgetScript(); + if (cancelled || !hostRef.current) return; + + const widgetWindow = window as OpenSeatBookingWindow; + widgetWindow.OpenSeatBooking?.mount(hostRef.current, { + restaurantId: LIVE_WIDGET_RESTAURANT_ID, + apiUrl: LIVE_WIDGET_API_URL, + }); + setStatus("ready"); + } catch { + if (!cancelled) setStatus("error"); + } + }; + + void mountWidget(); + + return () => { + cancelled = true; + if (hostRef.current) hostRef.current.innerHTML = ""; + }; + }, []); + + return ( +
+
{L.embedLabel}
+

{L.embedNote}

+
+ {status === "error" ? ( +
{L.widgetError}
+ ) : ( +
+ {status === "loading" && ( +
+ {L.widgetLoading} +
+ )} +
+
+ )} +
+
+ ); +} + function LiveDemo({ L }: { L: I18NData }) { const ref = useReveal(); const w = L.widget; @@ -1283,12 +1389,7 @@ function LiveDemo({ L }: { L: I18NData }) {
))} -
-
{L.embedLabel}
- - {''} - -
+