Admin Franchise web — BR 3.4

Контракты

Что делаем

API layer — web/src/api/paykeeper.ts

  • Импортировать типы из @erp-admin/shared (CatalogSyncStatus, CatalogSyncRunSummary, CatalogSyncRunDetail, ResyncCatalogResponse).
  • Функции:
    • resyncCatalog(accountId: string): Promise<ResyncCatalogResponse> → POST /api/v1/admin/paykeeper/accounts/${id}/resync-catalog
    • getCatalogSyncStatus(accountId): Promise<CatalogSyncStatus> → GET
    • listCatalogSyncRuns(accountId, { limit, since }): Promise<{ data: CatalogSyncRunSummary[], meta: { limit, total } }>
    • getCatalogSyncRun(accountId, runId): Promise<CatalogSyncRunDetail>
  • Обработка ошибок: типы CatalogSyncError с кодами (ACCOUNT_NOT_ACTIVE, SYNC_ALREADY_RUNNING, RATE_LIMITED, FORBIDDEN, NOT_FOUND).

React-hook для polling

  • web/src/hooks/useCatalogSyncStatus.ts:
    function useCatalogSyncStatus(accountId: string) {
      // useQuery с refetchInterval:
      //   5000 мс если last_run.status === 'running'
      //   30000 мс иначе
      // Возвращает { data, isLoading, error, refetch }
    }

Компонент блока «Каталог в PayKeeper»

  • web/src/pages/legal-entities/components/CatalogSyncBlock.tsx (новый):
    • Показывается когда account.status === 'active'.
    • Иначе — либо скрыт (not_configured), либо text-note для suspended.
    • Использует useCatalogSyncStatus(accountId) + usePermissions().
    • Layout — grid с label/value:
      • «Товаров в ЛК PK: {pk_products_synced}» (с пометкой ✓ если diverged_count=0)
      • «(из {erp_products_total} товаров ERP × модификаторы)» — справочно
      • «Последняя синхронизация: DD.MM.YYYY, HH:mm»
      • «Триггер: …»
      • «Статус: …» (badge по состоянию)
      • При diverged_count > 0 — оранжевая строка «⚠ N вариантов требуют пересинхронизации»
    • Категорий и модификаторов как отдельных счётчиков нет — они растворены в общем счётчике товаров.
    • Кнопки:
      • «Пересинхронизировать» — при integrations.manage, disabled во время running. Handler — resyncCatalog(), toast на успех/ошибки.
      • «Журнал прогонов» — при integrations.read, открывает CatalogSyncRunsModal.

Компонент модалки «Журнал прогонов»

  • web/src/pages/legal-entities/components/CatalogSyncRunsModal.tsx:
    • Таблица с колонками: Время, Триггер, Длительность, Статус, Товаров (+/-), Категорий (+/-), Модификаторов (+/-), Ошибок.
    • Данные — listCatalogSyncRuns(accountId, { limit: 20, since }) + кнопка «Загрузить ещё».
    • Клик на строку — раскрывается inline-accordion с деталями (getCatalogSyncRun(accountId, runId)):
      • last_error — красная плашка если не null
      • errors_json — scrollable список [{entity_type, erp_id, message}]
      • Пустой список → «Без ошибок» серым
    • Закрытие — стандартная кнопка.

Интеграция в PaykeeperTab

  • web/src/pages/legal-entities/PaykeeperTab.tsx:
    • Импорт CatalogSyncBlock
    • Размещение: между карточкой интеграции и подвкладкой «Журнал» (новый div внутри tab content).

i18n / копирайты

  • Все тексты на русском (как в BR 3.3 PaykeeperTab). Без отдельного i18n слоя — админка на русском по умолчанию.

Визуальные состояния для QA

  • Проверить:
    • Успешный прогон — зелёная галочка
    • Частичный (errors > 0) — жёлтый треугольник, tooltip → журнал
    • Failed — красный крест, раскрытие last_error
    • Running — синий спиннер + timer
    • Ещё не синхронизировался (last_run: null) — серый текст
    • diverged_count > 0 — оранжевая строка
    • account не active — блок скрыт / с подсказкой

Тесты

  • Unit CatalogSyncBlock.test.tsx — render для каждого из 6 состояний.
  • Unit useCatalogSyncStatus.test.ts — polling interval переключается при running.
  • Visual (Storybook / Chromatic, если есть) — все состояния.
  • E2E (если есть Cypress/Playwright): сценарий «нажать Пересинхронизировать → увидеть running → success».

Документация

  • Обновить компонентный README если есть.

Deploy

После merge — /deploy-all admin-bff (web билдится вместе с BFF в этом репо).

Ссылки