"use client"; import { useState, useEffect } from "react"; import { BlockChangelog, type ChangelogEntry } from "./BlockChangelog"; 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; version: string; isInPreview: boolean; changelog: ChangelogEntry[]; } interface BlockMetaBarProps { path: string; defaultVersion: string; defaultIsInPreview: boolean; defaultChangelog?: ChangelogEntry[]; } export function BlockMetaBar({ path, defaultVersion, defaultIsInPreview, defaultChangelog = [] }: BlockMetaBarProps) { const [meta, setMeta] = useState(null); const [editing, setEditing] = useState(false); const [versionInput, setVersionInput] = useState(defaultVersion); 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)}`) .then((r) => r.json()) .then((data: BlockMeta) => { setMeta(data); setVersionInput(data.version); }) .catch(() => setApiDown(true)); }, [apiUrl, path]); async function patch(data: { version?: string; isInPreview?: boolean; changelog?: ChangelogEntry[] }) { if (!apiUrl) return null; const r = await fetch(`${apiUrl}/blocks/by-path?path=${encodeURIComponent(path)}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }); return r.json() as Promise; } async function saveVersion() { if (!meta) return; setSaving(true); try { const updated = await patch({ version: versionInput }); if (updated) { setMeta(updated); setEditing(false); } } finally { setSaving(false); } } async function togglePreview() { if (apiDown) { const newVal = !localPreview; setLocalPreview(newVal); writeLocalPreview(path, newVal); return; } if (!meta) return; setTogglingPreview(true); try { const updated = await patch({ isInPreview: !meta.isInPreview }); if (updated) setMeta(updated); } finally { setTogglingPreview(false); } } const version = meta?.version ?? defaultVersion; const isInPreview = apiDown ? localPreview : (meta?.isInPreview ?? localPreview); const changelog: ChangelogEntry[] = (meta?.changelog && meta.changelog.length > 0) ? meta.changelog : defaultChangelog; return ( <>
{/* Version badge */} Версия: {editing ? ( setVersionInput(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') saveVersion(); if (e.key === 'Escape') setEditing(false); }} autoFocus /> ) : ( )} · {/* Preview toggle */} {/* Subtle offline dot instead of "API офлайн" text */} {apiDown && ( )}
{/* Changelog rendered from API or fallback */} {changelog.length > 0 && } ); }