Skip to content

HUB Templates Workbench System V1

Обновлено: 2026-03-04

Назначение

Это каноничный документ (single source of truth) по логике конструктора шаблонов HUB:

  1. registry -> build -> autosave -> publish -> runtime
  2. Единые data contracts: manifest, instance, i18n, responsive
  3. QA gate и критерии готовности для новых секций

Документ синхронизирован с текущим кодом в репозитории hub.

Границы и маршруты

  1. Админка конструктора: /{locale}/admin/templates
  2. Вьюхи админки:
    1. /{locale}/admin/templates (build)
    2. /{locale}/admin/templates/theme
    3. /{locale}/admin/templates/release
    4. /{locale}/admin/templates/registry
  3. Runtime шаблонов:
    1. static: /{locale}/offers/templates/beauty-courses, beauty-salon
    2. registry: /{locale}/offers/templates/{templateSlug}

End-to-End Flow

1) Registry

Источник: src/app/[locale]/admin/templates/page.tsx

  1. Каталог шаблонов собирается через listTemplateCatalog({ includeArchived: true }).
  2. Поддерживаются static и registry шаблоны в одном selector.
  3. CRUD registry выполняется server actions из admin/leads/actions.
  4. Базовый storage-key реестра: hub.templates.registry.v1 (см. smoke script).

2) Build (Library -> Canvas -> Inspector)

Источники:

  1. src/components/admin/TemplateSectionsEditor.tsx
  2. src/components/admin/templates-workbench/TemplateLibraryPanel.tsx
  3. src/components/admin/templates-workbench/TemplateCanvasPanel.tsx
  4. src/components/admin/templates-workbench/TemplateInspectorPanel.tsx

Фактическая модель:

  1. items: состояние секций (TemplateSectionConfig[]).
  2. instanceItems: состояние unified block instances (UnifiedBlockInstance[]).
  3. Inspector рендерится schema-driven по BlockManifest, без ветвления по конкретным section-id.
  4. Canvas поддерживает viewport scopes: desktop | tablet | mobile.

3) Autosave (единый draft write-path)

Источники:

  1. src/components/admin/templates-workbench/useTemplateSectionsAutosave.ts
  2. src/app/api/admin/templates/sections-autosave/route.ts
  3. src/lib/offers/template-instance-provisioning.ts

Фактический pipeline:

  1. Клиент отправляет sections + blockInstances + baseRevision.
  2. API проверяет owner session и revision guard.
  3. Секции пишутся в hub.templates.<template>.sections.
  4. Затем вызывается ensureTemplateSectionProvisioning(...):
    1. миграция legacy-моделей в unified instances
    2. merge responsive overrides из payload/хранилища
    3. запись в hub.templates.<template>.instances

Важно: это единый write-path для изменений секций в Build.

4) Publish (Draft -> Immutable Snapshot)

Источники:

  1. src/app/api/admin/templates/publish/route.ts
  2. src/lib/offers/template-published-snapshots.ts

Фактический pipeline:

  1. POST /api/admin/templates/publish вызывает publishTemplateSnapshot(templateKey).
  2. Формируется immutable snapshot payload:
    1. theme
    2. sections
    3. enrollmentBlockSettings
    4. programBlockSettings
    5. extraSectionContentByLocale (ru/en/pl/ua)
  3. Записываются:
    1. snapshot record
    2. current pointer
    3. history item (publish)

5) Runtime

Источники:

  1. src/app/[locale]/offers/templates/beauty-courses/page.tsx
  2. src/app/[locale]/offers/templates/beauty-salon/page.tsx
  3. src/app/[locale]/offers/templates/[templateSlug]/page.tsx
  4. src/components/templates/premium/PremiumTemplatePage.tsx
  5. src/lib/offers/template-block-instance-runtime.ts

Фактический pipeline:

  1. Runtime получает published payload.
  2. Legacy payload нормализуется в blockInstances через resolveTemplateInstances(...).
  3. PremiumTemplatePage резолвит контент из instances с учетом:
    1. locale chain
    2. responsive scope chain
  4. Fallback rules:
    1. responsive: mobile -> tablet -> desktop, tablet -> desktop, desktop
    2. locale: current -> configured fallback -> en -> known locales

Data Contracts

BlockManifest

Источник: src/lib/offers/block-manifest.ts

Минимальный контракт:

  1. blockId, version, category
  2. inspectorTabs
  3. fields[]:
    1. key, type, tab, default
    2. optional: validators, i18n, responsive, scope
  4. preview metadata

UnifiedBlockInstance

Источник: src/lib/offers/template-block-manifest.ts

Минимальный контракт:

  1. instanceId, sectionId, blockId, manifestVersion
  2. enabled, removed, order, syncMode, syncState
  3. data: BlockInstanceData

Field Value Container

Источник: src/lib/offers/block-manifest.ts

BlockInstanceFieldValue поддерживает:

  1. value
  2. i18n[locale]
  3. responsive[scope]
  4. responsiveI18n[scope][locale]

Это единая модель для Inspector/Canvas/Runtime резолва.

Текущее ограничение (важно)

Состояние на 2026-03-04:

  1. Draft autosave уже сохраняет blockInstances и responsive overrides.
  2. Publish snapshot сейчас сериализует legacy payload (theme/sections/enrollment/program/extra), без отдельного raw blockInstances в snapshot схеме.
  3. Runtime для published шаблонов реконструирует instances из snapshot payload.

Следствие: responsive/tablet/mobile overrides, сохраненные только в draft instances, должны быть отдельно учтены в следующем шаге эволюции snapshot schema.

QA Gate (обязательно перед закрытием задач V2)

A. Автоматические проверки (hub repo)

  1. pnpm lint
  2. pnpm build
  3. pnpm test:smoke:new-template

B. Manual smoke (Build -> Publish -> Runtime)

  1. Создать registry-шаблон.
  2. Добавить секцию из Library.
  3. Проверить, что поля появились в Inspector без ручного кода.
  4. Изменить:
    1. style (включая color + HEX input)
    2. content для ru/en/pl/ua
    3. responsive scope для desktop/tablet/mobile
  5. Проверить reorder, enable/disable, remove/re-add.
  6. Publish и открыть runtime URL.
  7. Проверить, что published страница рендерится без fallback "no snapshot".

C. Docs gate (docs-designcorp repo)

  1. pnpm docs:build
  2. Проверить, что ссылки из этого документа открываются и якоря корректны.

Definition of Done для новой секции

Новая секция считается внедренной, если выполнены все пункты:

  1. Есть block entry в registry/section mapping.
  2. Есть manifest fields с default/validators/tab/scope.
  3. Секция появляется в Library и добавляется в Canvas.
  4. Поля секции автоматически рендерятся в Inspector (без hardcoded веток).
  5. Значения секции сохраняются через autosave write-path.
  6. Рендер секции работает в Canvas preview.
  7. Publish не ломается, snapshot создается.
  8. Runtime рендерит секцию из published данных.
  9. Для текстов поддержаны локали ru/en/pl/ua.
  10. Для style/layout полей проверены responsive scopes.
  11. Пройдены QA Gate A/B/C.

Anti-Regression Checklist

Перед merge/push проверять:

  1. Нет второго параллельного write-path для тех же данных.
  2. Нет section-specific ветвлений в Inspector для стандартных типов полей.
  3. Нет пустых/null состояний из-за неинициализированных instance fields.
  4. Draft статус (clean/dirty/saving/conflict/error) корректно обновляется.
  5. Publish не меняет live без создания snapshot.
  6. Runtime одинаково работает для static и registry шаблонов.

Связанные задачи

  1. Epic: #58
  2. Реализованные блоки V2: #59, #60, #61, #62, #63
  3. Документирующая задача: #64

Исходники

  1. src/app/[locale]/admin/templates/page.tsx
  2. src/components/admin/TemplateSectionsEditor.tsx
  3. src/app/api/admin/templates/sections-autosave/route.ts
  4. src/app/api/admin/templates/publish/route.ts
  5. src/lib/offers/block-manifest.ts
  6. src/lib/offers/template-block-manifest.ts
  7. src/lib/offers/template-block-instance-runtime.ts
  8. src/lib/offers/template-instance-provisioning.ts
  9. src/lib/offers/template-published-snapshots.ts
  10. scripts/smoke-new-template.mjs