[object Object]

← back to Homesonspec

admin: Live site toggle (/live) — embeds the customer storefront with desktop/mobile preview + open-full

6d7e380292c4c298aca09f9c6a5845c98976ccc9 · 2026-07-28 12:31:15 -0700 · Steve Abrams

Files touched

Diff

commit 6d7e380292c4c298aca09f9c6a5845c98976ccc9
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Jul 28 12:31:15 2026 -0700

    admin: Live site toggle (/live) — embeds the customer storefront with desktop/mobile preview + open-full
---
 apps/admin/src/app/layout.tsx           |  1 +
 apps/admin/src/app/live/LivePreview.tsx | 56 +++++++++++++++++++++++++++++++++
 apps/admin/src/app/live/page.tsx        | 15 +++++++++
 3 files changed, 72 insertions(+)

diff --git a/apps/admin/src/app/layout.tsx b/apps/admin/src/app/layout.tsx
index 7e0d6ce0..c02a640d 100644
--- a/apps/admin/src/app/layout.tsx
+++ b/apps/admin/src/app/layout.tsx
@@ -16,6 +16,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
             <Link href="/" className="text-lg font-bold text-neutral-900">
               HomesOnSpec <span className="text-teal-700">Admin</span>
             </Link>
+            <Link href="/live" className="rounded-full border border-teal-300 bg-teal-50 px-3 py-1 font-medium text-teal-700 hover:bg-teal-100">◉ Live site</Link>
             <Link href="/business" className="font-semibold text-teal-700 hover:text-teal-900">Business</Link>
             <Link href="/ingestion" className="hover:text-teal-700">Live ingestion</Link>
             <Link href="/engine" className="hover:text-teal-700">Engine</Link>
diff --git a/apps/admin/src/app/live/LivePreview.tsx b/apps/admin/src/app/live/LivePreview.tsx
new file mode 100644
index 00000000..c7f54245
--- /dev/null
+++ b/apps/admin/src/app/live/LivePreview.tsx
@@ -0,0 +1,56 @@
+"use client";
+
+import { useState } from "react";
+
+const URL = "https://homesonspec.com/";
+
+export default function LivePreview() {
+  const [mode, setMode] = useState<"desktop" | "mobile">("desktop");
+  const [nonce, setNonce] = useState(0); // bump to force-reload the iframe
+  const width = mode === "mobile" ? 390 : "100%";
+
+  return (
+    <div className="mt-4">
+      <div className="mb-3 flex flex-wrap items-center gap-2 text-sm">
+        <div className="inline-flex rounded-lg border border-neutral-200 bg-white p-0.5">
+          {(["desktop", "mobile"] as const).map((m) => (
+            <button
+              key={m}
+              onClick={() => setMode(m)}
+              className={`rounded-md px-3 py-1 text-xs capitalize ${mode === m ? "bg-teal-600 text-white" : "text-neutral-600 hover:text-teal-700"}`}
+            >
+              {m}
+            </button>
+          ))}
+        </div>
+        <button
+          onClick={() => setNonce((n) => n + 1)}
+          className="rounded-md border border-neutral-200 bg-white px-2 py-1 text-xs text-neutral-600 hover:text-teal-700"
+        >
+          ↻ Reload
+        </button>
+        <span className="text-xs text-neutral-400">viewing the customer-facing storefront</span>
+        <a
+          href={URL}
+          target="_blank"
+          rel="noopener noreferrer"
+          className="ml-auto rounded-md border border-teal-200 bg-teal-50 px-3 py-1 text-xs font-medium text-teal-700 hover:bg-teal-100"
+        >
+          Open full ↗
+        </a>
+      </div>
+      <div
+        className="flex justify-center overflow-hidden rounded-xl border border-neutral-200 bg-neutral-100 p-2"
+        style={{ height: "calc(100vh - 230px)", minHeight: 480 }}
+      >
+        <iframe
+          key={nonce}
+          src={URL}
+          title="homesonspec.com — live storefront"
+          style={{ width, height: "100%", border: 0, borderRadius: 8, background: "#fff", maxWidth: "100%" }}
+          className="shadow-sm"
+        />
+      </div>
+    </div>
+  );
+}
diff --git a/apps/admin/src/app/live/page.tsx b/apps/admin/src/app/live/page.tsx
new file mode 100644
index 00000000..21ac25b7
--- /dev/null
+++ b/apps/admin/src/app/live/page.tsx
@@ -0,0 +1,15 @@
+import LivePreview from "./LivePreview";
+
+export const dynamic = "force-dynamic";
+
+export default function LivePage() {
+  return (
+    <div>
+      <h1 className="text-2xl font-bold">Live site</h1>
+      <p className="mt-1 text-sm text-neutral-500">
+        The customer-facing storefront (<code>homesonspec.com</code>) embedded here — toggle desktop / mobile, reload, or open it full. Everything the ingestion loop publishes shows up here.
+      </p>
+      <LivePreview />
+    </div>
+  );
+}

← f90ea4b6 build-loop: Lennar + DR Horton page limit 120->300 (Cody: CA  ·  back to Homesonspec  ·  build-loop: uncap CAP (download ALL homes per state) + 40 sw 775ea10a →