diff --git a/apps/web/app/pages/preview/PreviewClient.tsx b/apps/web/app/pages/preview/PreviewClient.tsx index e9dd22d..8fd7491 100644 --- a/apps/web/app/pages/preview/PreviewClient.tsx +++ b/apps/web/app/pages/preview/PreviewClient.tsx @@ -11,6 +11,7 @@ import { ContactFormsBlock } from "@/components/blocks/ContactFormsBlock"; import { FooterBlock } from "@/components/blocks/FooterBlock"; const STORAGE_KEY = "bb-preview-created"; +const LS_PREFIX = "bb-block-preview:"; function BlockPlaceholder({ name, href }: { name: string; href: string }) { return ( @@ -119,11 +120,17 @@ export function PreviewClient() { const apiUrl = process.env.NEXT_PUBLIC_API_URL; + // Counter to force re-render when localStorage preview toggles change + const [, setRefresh] = useState(0); + useEffect(() => { setMounted(true); if (localStorage.getItem(STORAGE_KEY) === "true") { setCreated(true); } + const handler = () => setRefresh((n) => n + 1); + window.addEventListener("bb-preview-change", handler); + return () => window.removeEventListener("bb-preview-change", handler); }, []); useEffect(() => { @@ -141,9 +148,16 @@ export function PreviewClient() { }, [apiUrl]); function isReady(block: BlockDef): boolean { + // 1. API online → use API data if (apiMeta !== null && block.path in apiMeta) { return apiMeta[block.path] && !!block.component; } + // 2. Check localStorage (set by BlockMetaBar toggle) + const lsVal = typeof window !== "undefined" ? localStorage.getItem(LS_PREFIX + block.path) : null; + if (lsVal !== null) { + return lsVal === "true" && !!block.component; + } + // 3. Fallback to defaultReady return block.defaultReady && !!block.component; } diff --git a/apps/web/components/ui/BlockMetaBar.tsx b/apps/web/components/ui/BlockMetaBar.tsx index a80da4c..2dccd1e 100644 --- a/apps/web/components/ui/BlockMetaBar.tsx +++ b/apps/web/components/ui/BlockMetaBar.tsx @@ -2,6 +2,20 @@ import { useState, useEffect } from "react"; +const LS_PREFIX = "bb-block-preview:"; + +function readLocalPreview(path: string, fallback: boolean): boolean { + if (typeof window === "undefined") return fallback; + const v = localStorage.getItem(LS_PREFIX + path); + if (v === null) return fallback; + return v === "true"; +} + +function writeLocalPreview(path: string, value: boolean) { + localStorage.setItem(LS_PREFIX + path, String(value)); + window.dispatchEvent(new Event("bb-preview-change")); +} + interface BlockMeta { path: string; name: string; @@ -22,9 +36,14 @@ export function BlockMetaBar({ path, defaultVersion, defaultIsInPreview }: Block const [saving, setSaving] = useState(false); const [togglingPreview, setTogglingPreview] = useState(false); const [apiDown, setApiDown] = useState(false); + const [localPreview, setLocalPreview] = useState(defaultIsInPreview); const apiUrl = process.env.NEXT_PUBLIC_API_URL; + useEffect(() => { + setLocalPreview(readLocalPreview(path, defaultIsInPreview)); + }, [path, defaultIsInPreview]); + useEffect(() => { if (!apiUrl) { setApiDown(true); return; } fetch(`${apiUrl}/blocks/by-path?path=${encodeURIComponent(path)}`) @@ -58,6 +77,13 @@ export function BlockMetaBar({ path, defaultVersion, defaultIsInPreview }: Block } async function togglePreview() { + if (apiDown) { + // Fallback: toggle via localStorage + const newVal = !localPreview; + setLocalPreview(newVal); + writeLocalPreview(path, newVal); + return; + } if (!meta) return; setTogglingPreview(true); try { @@ -69,7 +95,7 @@ export function BlockMetaBar({ path, defaultVersion, defaultIsInPreview }: Block } const version = meta?.version ?? defaultVersion; - const isInPreview = meta?.isInPreview ?? defaultIsInPreview; + const isInPreview = apiDown ? localPreview : (meta?.isInPreview ?? localPreview); return (
· {/* Preview toggle */} - {apiDown ? ( + - )} + className="inline-block w-1.5 h-1.5 rounded-full" + style={{ background: isInPreview ? "#22c55e" : "#d1d5db" }} + /> + {togglingPreview + ? '...' + : isInPreview + ? "В превью" + : "Не в превью"} + {apiDown && ( <>