From dc0792932634b72cf17482ddab4e485d92c679a6 Mon Sep 17 00:00:00 2001 From: "s.zotov" Date: Sun, 12 Apr 2026 20:46:36 +0000 Subject: [PATCH] Add "Gender" --- Gender.md | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 Gender.md diff --git a/Gender.md b/Gender.md new file mode 100644 index 0000000..e7089cd --- /dev/null +++ b/Gender.md @@ -0,0 +1,108 @@ +# Гендерная сегментация + +## Концепция + +Одно письмо может содержать блоки для женской и мужской аудитории. При рендере выбирается header/footer по гендеру, а блоки могут переставляться местами. + +## Сегменты блоков + +Каждый блок имеет свойство `segment`: +- `'common'` (по умолчанию) — общий блок, показывается в обеих версиях +- `'female'` — только для женской версии +- `'male'` — только для мужской версии + +Кнопки **О / Ж / М** в шапке каждого блока устанавливают сегмент (`setBlockSegment`). + +## Кнопки Жен / Муж + +Переключают `currentGenderVersion` ('female' / 'male') и перерендеривают превью. + +Функция `assembleGenderVersion(target)`: +- Меняет `currentGenderVersion = target` +- **Не переставляет блоки** в конструкторе +- При рендере backend подставит нужный header/footer + +## Кнопка ⇅ (Flip) + +Переставляет блоки местами и переключает гендер. + +Функция `flipSegmentOrder()`: +1. Вызывает `buildVersion(assembledBlocks, target)` +2. Физически перестраивает массив `assembledBlocks[]` +3. Переключает `segmentFlipped` (boolean toggle) +4. Синхронно меняет `currentGenderVersion` + +## Алгоритм buildVersion + +```javascript +buildVersion(list, target): + 1. Ищет блок с swapCenter = true (divider-ось) + + ЕСЛИ ось найдена: + - Блоки ДО оси → группа A + - Блоки ПОСЛЕ оси → группа B + - target === 'male' → [B, ось, A] + - target === 'female' → [A, ось, B] + + ЕСЛИ оси нет: + - commonsPrefix = общие блоки до первого сегментированного + - segmentA = блоки с segment === target + - commonsMid = общие блоки между сегментами + - segmentB = блоки с противоположным segment + - commonsSuffix = общие блоки после последнего сегментированного + - Результат: [commonsPrefix, segmentA, commonsMid, segmentB, commonsSuffix] +``` + +## Разделитель (swapCenter) + +Блок типа "dividerVA" (разделитель) может иметь флаг `swapCenter = true`: +- Устанавливается кнопкой **⊕** в шапке блока-разделителя +- Только один блок может быть осью одновременно +- Служит центральной точкой для `buildVersion()` — блоки до и после оси меняются местами + +## Gender Paths + +Настройки в `settings.genderPaths`: + +```json +{ + "headerFemale": "./parts/header/header-woman", + "headerMale": "./parts/header/header-man", + "footerFemale": "./parts/footer/footer-woman", + "footerMale": "./parts/footer/footer-man" +} +``` + +В UI — dropdown-пикеры, список файлов из `/api/parts-files` (сканирует `email-gen/emails/vipavenue/parts/`). + +## Цепочка передачи gender при рендере + +1. **App.svelte** → `apiRenderEmail(..., { gender: currentGenderVersion })` +2. **vite.config.js** → читает `genderPaths` из settings.json, форвардит в email-gen-api с `{ gender, genderPaths }` +3. **email-gen-api/server.js** → `rewriteHtmlPug()` — записывает `html.pug` с нужными путями: + ```pug + block header + include ./parts/header/header-woman // или header-man + block footer + include ./parts/footer/footer-woman // или footer-man + ``` + +## Кэш рендера и gender + +Хэш кэша: `MD5(slug + pug + gender + JSON(genderPaths))` + +Female и male версии кэшируются **отдельно**. + +## ID Pool + +Пул ID товаров для быстрого заполнения блоков: + +1. **Ввод** — textarea, по одному ID на строку или через запятую +2. **Из Yonote** — кнопка `refreshIdsFromYonote()`: + - Берёт ID из `assemblyInfo.extra` (свойство "ID товаров") + - Женские ID первыми (filter по `/жен|female|ж\b/i`) + - Затем неразмеченные, затем мужские +3. **Распределение** — `distributeIds()`: + - Проходит по блокам с полем `mixin-ids` + - Вставляет 3 или 4 ID в зависимости от имени миксина (products3 → 3, products4 → 4) + - Последовательно из очереди `idPoolQueue[]`