---
title: "Vertical Presets (SSOT)"
description: "Именованные пресеты выбирают shared или clone-local каталоги; сборка мержит platform, затем overlay клона"
locale: "ru"
---
# Vertical Presets (SSOT)

White-label Ring разделяют **одну platform codebase**: `ring-platform.org`. Вертикали выбираются по **имени пресета** в `ring-config.json`. Типизированные каталоги живут в `features/*/presets/.ts` (и home в `components/pages/home-presets/.tsx`).

## Два источника модулей пресетов

| Source | What lives there | When it wins |
|--------|------------------|--------------|
| **Platform** (`ring-platform.org`) | Shared niches (`platform`, `agricultural`, …) | База каждой сборки |
| **Clone project dir** (напр. `ring-greenfood-live`) | Branding, `ring-config.json`, **custom** `.ts` + registry overlays | **Overwrite при коллизии** после copy platform |

### Merge сборки (истина клона побеждает)

Используйте **`ringdom-clone-build`** (ringdom-mcp):

1. Скопируйте свежий `ring-platform.org` → build tmp (пропуская `node_modules` / `.next` / `.git`)
2. Скопируйте **все** файлы из clone project dir поверх tmp — **overwrite collisions**
3. Docker / CI build из tmp dir

```text
ringdom-clone-build({ project_name: "ring-greenfood-live", dry_run?: true })
→ .ringdom-build//
```

**Не** ожидайте, что thin overlay сам по себе будет runnable app — сначала merge.

## Два уровня контента

| Tier | What | Where |
|------|------|--------|
| **1 — data** | Category lists, badge tokens, labels | карты `ring-config.json` + `locales/` |
| **2 — typed logic** | Entity catalogs, zod/forms, niche field UIs | `features/*/presets/.ts` (platform и/или clone overlay) |

## Ключи конфига

```json
{
  "entities": { "preset": "platform" },
  "home": { "preset": "platform" },
  "productFields": { "preset": "platform" },
  "productBadges": { "preset": "platform" },
  "productFieldsPresets": {
    "platform": { "storeCategories": ["commerce", "education", "…"] },
    "agricultural": { "storeCategories": ["organic-produce", "honey-sweets", "…"] }
  },
  "productBadgesPresets": {
    "platform": { "productBadges": ["new", "featured", "popular"] },
    "agricultural": { "productBadges": ["organic", "local", "regenerative"] }
  }
}
```

GreenFood задаёт `entities` / `productFields` / `productBadges` в `"agricultural"` и `home.preset` в `"mvm-landing"`.

### Accessors (именованный preset SSOT)

Все используют настроенную строку `*.preset`. Если singular-ключ не задан, **entities** и **productBadges** падают на `productFields.preset`, затем `"platform"` — без hard-coded niche if/else.

- `getEntitiesPreset()` → реестр `features/entities/presets`
- `getHomePreset()` → реестр `components/pages/home-presets`
- `getProductFieldsPreset()` / `getProductFieldsPresets()`
- `getProductBadgesPreset()` / `getProductBadgesPresets()`

## Реестр entities

```
features/entities/presets/
  types.ts             # EntityTypeCatalog contract
  platform.ts          # export const entityTypes (26 industries)
  agricultural.ts      # healthy-living UI catalog (lightweight)
  agricultural-erp.ts  # heavy ERP field catalogs — async-only via loadErpEntityTypes()
  index.ts             # ENTITIES_PRESET_REGISTRY + getEntityTypes()
```

**Правило:** *имена* пресетов появляются в ring-config + карте реестра. Consumers вызывают `getEntityTypes()` / `getEntityTypeList()` — никогда не импортируйте `agricultural.ts` напрямую.

**Своя ниша на клоне:** добавьте `features/entities/presets/.ts` в clone project, overlay `index.ts` для регистрации, задайте `entities.preset` этим именем, затем `ringdom-clone-build`.

**Дисциплина бандла:** sync-каталоги — лёгкие UI-списки. Тяжёлые ERP field catalogs живут в модулях `*-erp.ts`, загружаемых только через async `loadErpEntityTypes()`.

### Слои copy / icons (без hardcoded UI strings)

- **Catalog** (`presets/.ts`): ids, emoji, English fallback names/descriptions
- **Copy**: `locales/*/modules/entities.json` → `types.` + `types.Desc`
- **Visual skin**: `components/entities/entity-type-icons.tsx` карта `TYPE_VISUALS`
- **Render**: `useEntityTypeLabel()` / `useEntityTypeDescription()` — сначала i18n, fallback каталога

## Реестр home landing

```
components/pages/
  home.tsx                       # platform default landing
  home-presets/mvm-landing.tsx   # multi-vendor-marketplace e-commerce landing
  home-content-resolver.tsx      # HOME_PRESET_REGISTRY + getHomePreset()
```

### Добавить shared vertical (platform)

1. Добавьте `features/entities/presets/.ts` (+ опционально `-erp.ts`)
2. Одна строка в `ENTITIES_PRESET_REGISTRY` (+ `ERP_CATALOG_REGISTRY` при необходимости)
3. Опциональный landing → `components/pages/home-presets/.tsx`
4. Опциональные Tier-1 карты под `productFieldsPresets` / `productBadgesPresets`
5. Ключи i18n в `locales/*/modules/entities.json`
6. Задокументируйте preset id в `ring-config.template.json`

Целевой roster: `platform`, `agricultural`, затем `tourism`, `sport`, `business`, `pet`, `auto`, `connect`, `technopark`, `manufacturing`.

## Thin exclude (propagation)

`.reggie-propagate-exclude.json` защищает пути **только overlay** от перезаписи Reggie:

- `ring-config.json`
- `public/logo*.svg`, `favicon.ico`, `public/branding/`
- `locales/{en,uk,ru}/config.json`, `vendor.json`
- Custom `features/*/presets/.ts` (+ registry overlay), пока не upstream'нуты
- **Tier-3 plug:** только `lib/overlay/registry.ts` (maps). **Не** исключайте `lib/overlay/types.ts` или `lib/overlay/runtime.ts` — это platform SSOT.

### Tier-3 overlay socket (platform) + plug (clone)

| Path | Owner | Role |
|------|--------|------|
| `lib/overlay/types.ts` | Platform | Contracts |
| `lib/overlay/runtime.ts` | Platform | `loadOverlayMessages` / `resolveOverlayHomeRail` |
| `lib/overlay/registry.ts` | Platform empty maps; **clone overlays maps** | Domain feature loaders |

Consumers импортируют из `@/lib/overlay/runtime`. Клоны никогда не дублируют loaders в `registry.ts`.

Чеклист оператора (калькулятор / Order Lab): [Ringization playbook](/docs/customization/ringization-playbook.md).

Источник propagation остаётся **`ring-platform.org`** → clones для shared code. Clone-local custom presets остаются в clone dir и побеждают на **`ringdom-clone-build`**.

## Cutover GreenFood

```text
ensure ring-config: entities/productFields/productBadges = agricultural, home = mvm-landing
keep branding + locale deltas in ring-greenfood-live
ringdom-clone-build({ project_name: "ring-greenfood-live" })
build/deploy from .ringdom-build/ring-greenfood-live/
```

## Deprecated

- Hard-coded `if (productPreset === 'agricultural')` в accessors
- Per-clone `custom-types.ts` / `overrides.ts` без именованного preset module
- `switch (vertical)` в feature code — используйте реестр
- Сборка Docker-образов из thin overlay без merge platform
