feat(sprint-5.5): add "Save version" button, update navigation block and block components

- Add "Сохранить версию" button to BlockMetaBar that persists current
  version + changelog from code to PostgreSQL via PATCH API
- Update navigation page: menu items section now renders like live example
  with underlined links, hover dropdowns, and submenus
- Restore uncommitted changes from previous session (thirsty-mayer worktree):
  navigation v1.3 with dropdowns, updated hero/ceo/doctors/reviews/news/
  contact-forms/footer blocks, navData.ts extraction, seed updates
- Extract nav menu data to shared navData.ts module

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
AR 15 M4
2026-03-24 23:28:05 +05:00
parent 3094e9a2b7
commit 196526ffc4
15 changed files with 465 additions and 225 deletions
+41
View File
@@ -37,6 +37,7 @@ export function BlockMetaBar({ path, defaultVersion, defaultIsInPreview, default
const [editing, setEditing] = useState(false);
const [versionInput, setVersionInput] = useState(defaultVersion);
const [saving, setSaving] = useState(false);
const [savingVersion, setSavingVersion] = useState<false | 'saving' | 'done'>(false);
const [togglingPreview, setTogglingPreview] = useState(false);
const [apiDown, setApiDown] = useState(false);
const [localPreview, setLocalPreview] = useState(defaultIsInPreview);
@@ -79,6 +80,22 @@ export function BlockMetaBar({ path, defaultVersion, defaultIsInPreview, default
}
}
async function saveFullVersion() {
if (apiDown) return;
setSavingVersion('saving');
try {
const updated = await patch({ version: defaultVersion, changelog: defaultChangelog });
if (updated) {
setMeta(updated);
setVersionInput(updated.version);
}
setSavingVersion('done');
setTimeout(() => setSavingVersion(false), 1500);
} catch {
setSavingVersion(false);
}
}
async function togglePreview() {
if (apiDown) {
const newVal = !localPreview;
@@ -180,6 +197,30 @@ export function BlockMetaBar({ path, defaultVersion, defaultIsInPreview, default
: "Не в превью"}
</button>
<span style={{ color: "var(--bb-border)" }}>·</span>
{/* Save version button */}
<button
onClick={saveFullVersion}
disabled={apiDown || savingVersion === 'saving'}
title={apiDown ? 'API недоступен' : 'Сохранить текущую версию и changelog в БД'}
className="px-2.5 py-1 rounded font-medium transition-colors"
style={savingVersion === 'done' ? {
background: "#dcfce7",
color: "#16a34a",
border: "1px solid #86efac",
cursor: "default",
} : {
background: "var(--bb-sidebar-bg)",
color: apiDown ? "var(--bb-text-muted)" : "var(--brand-053m)",
border: "1px solid var(--bb-border)",
cursor: apiDown ? "default" : "pointer",
opacity: apiDown ? 0.5 : 1,
}}
>
{savingVersion === 'saving' ? '...' : savingVersion === 'done' ? 'Сохранено' : 'Сохранить версию'}
</button>
{/* Subtle offline dot instead of "API офлайн" text */}
{apiDown && (
<span