# CT — перенос в ct-forms-portal (Фаза 3) Порядок (утверждён): **Этап 1 перенос 1:1 → Этап 2 декомпозиция под структуру KPS → Этап 3 перекраска на ui-kit.** Правило: сохранить 100% поведения; build после каждого этапа. Эталон структуры — `forms/kps/`. Связано: `docs/REPAINT-MAP.md` (перекраска), `docs/BEHAVIORS.md`. Источник: `ct-create-form/src/webparts/ctCreateForm/`. CT = 6953 стр, god-компоненты. ## Этап 1 — перенос 1:1 — ✅ ЗАВЕРШЁН (heft build зелёный) 1. ✅ Скопированы `components/` и `services/` → `forms/ct/{components,services}/` (17 файлов, 1:1). 2. ✅ Правки для сборки в портале (минимум, поведение не менялось): - `CtService`: конструктор `(context, siteUrl?)`, `baseUrl = siteUrl ?? absoluteUrl` (как KPS). `kpFolder` тоже параметризован через `siteUrl` (было жёстко на `context.pageContext.web.serverRelativeUrl`). - `ICtCreateFormProps` + `siteUrl?`; `CtCreateForm` → `new CtService(context, siteUrl)`. - Web-part-специфичных зависимостей (loc strings) в components/services не было — ничего убирать не пришлось. 3. ✅ Обвязка портала: - `ctDocumentConfig.ts`: `inPortal: true` + `buildInPortalParams: (row) => ({ [row.status==='Черновик'?'ctId':'ctid']: row.id })` (сверено с `useCtForm.ts` — читает именно `ctId`/`ctid`, совпало 1:1). siteUrl уже `/sites/sed`. - `CtFormsPortal.tsx`: ветка `form==='ct'` → `` + «← К реестру» (по образцу KPS-ветки). 4. ✅ `heft build` зелёный. **Открытие при переносе:** CT уже частично зависел от `@tnsu/ui-kit@^0.4.0` — 3 файла (`Step1PositionSelection.tsx`, `Step2CommercialOffers.tsx`, `CurrencyRatesForm.tsx`) уже используют `Button`/`Alert` из кита. API совпадает 1:1 с текущим `0.11.0` (variant="secondary"/"accent", Alert variant="err") — конфликтов не было, работает как есть. Ускорит Этап 3. ## Этап 2 — декомпозиция (карта резки god-компонентов) **Статус:** [x] Step2CommercialOffers ✅ | [x] Step4Approval ✅ | [~] Step3ComparisonTable (в работе) ### [~] Step3ComparisonTable (2217) — В РАБОТЕ, по чекпоинтам - ✅ **CP1 согласование** (build ок): `useStep3Approval.ts` (голосование/делегат, 1:1) + `InlineApprovalChain.tsx` (вью цепочки + диалоги решение/делегат + баннер notifyWarning; хелперы APPROVAL_STATUS_*). Контейнер 2217→1918. - ✅ **CP2 AI/PDF** (build ок): `useAiComment.ts` (aiComment/isAiLoading/aiError/isPdfLoading/showAiPromptDialog + handleAiComment/handleGeneratePdfReport/handleDeleteAiComment + loadCtComment-effect + decodeHtmlEntities, buildPayload передаётся аргументом из контейнера — матричные данные не копируются в хук) + `AiCommentBlock.tsx` (только блок отображения, диалог-промпт остался в контейнере — как у KPS). PDF-ошибка идёт через колбэк `onPdfError` в общий `saveError` (сохранили родной баннер). - ✅ **CP3 хелперы/типы** (build ок): типы (ExtraSupplier/ExtraPosition/PositionRow/CellEdit/EditableOffers) → `matrixTypes.ts`; константы (SUPPLIER_STATUS_OPTIONS/DELIVERY_TYPE_OPTIONS) + чистые ф-ции (formatKzt/Num/Qty/round2Str/getSubCols/computeOffer/buildOfferKey) → `matrixHelpers.ts`. `extraIdCounter` (мутабельный) оставлен в контейнере — уедет вместе с CP4. - **Контейнер после CP1-3: 2217 → 1735 строк (−22%).** - ✅ **CP4 ядро матрицы** (build ок): весь стейт/рефы(~15)/эффекты(loadCriteria/loadOffers/refs-sync/beforeunload/ unmount-persist)/мемо(suppliersWithOffer/allPositions/supplierCurrencies/computedOffers/minTotalByPosition/ recommendedSupplierId)/все update*/add*/delete*-хендлеры/handleSave → `useStep3Matrix.ts` (856, перенос ВЕРБАТИМ). В контейнере — **деструктуризация** результата, поэтому render (984) НЕ менялся. `CriteriaEntry` переехал в matrixTypes (общий). approval+ai хуки остались в контейнере (берут значения из деструктуризации). - **Контейнер: 2217 → 984 строки (−56%). Логика матрицы вся в хуке. Этап 2 для CT ЗАВЕРШЁН.** - ✅ **CP4 happy-path проверен в workbench**: полный проход по всем 4 шагам (создание СТ → загрузка КП → матрица → согласование) прошёл без ошибок. Основной риск (грубый регресс/краш) снят. ⚠️ Не проверены детально: bundle-строки (комплект), перевод ед. изм. (тумблер), доп.расходы/логистика, F5 (beforeunload-persist), критерии/AI-комментарий, делегат/замена в согласовании. Смотреть при случае, не блокирует дальнейшую работу. - ⏭️ **CP4 матрица-ядро** (САМЫЙ РИСК): ~15 рефов + эффекты (loadCriteria/loadOffers/beforeunload/unmount-persist) + мемо (suppliersWithOffer/allPositions/supplierCurrencies/computedOffers/minTotalByPosition/recommendedSupplierId) + все update*/add*/delete*-хендлеры + handleSave → `useStep3Matrix.ts` (перенос ВЕРБАТИМ, не переписывать!). ⚠️ Клубок рефов; компилятор не ловит регресс поведения. ЖЕЛАТЕЛЬНО делать при возможности workbench-теста (после провижининга списков). Рендер таблицы (thead/tbody/итоги/критерии) дробить на ComparisonMatrix/ MatrixRowView/OfferCells/CriteriaTable/AddExtraRow — на Этапе 3 вместе с перекраской. Метод: чисто механический перенос блоков (логику не трогать), Fluent оставляем (перекраска — Этап 3), build после каждого god-компонента. **Уточнённая граница Этап2/Этап3:** Этап 2 = вынос ЛОГИКИ (хуки/persist), «раз-god-ивание». Финальное дробление оставшейся ВЬЮХИ на под-компоненты (диалоги/списки) + размеры <200 — на Этапе 3 вместе с перекраской (диалоги всё равно → ui-kit Dialog), чтобы не трогать файлы дважды. ### ✅ Step4Approval (886) — СДЕЛАНО (build ок) 886 → `useStep4Approval.ts` (вся логика: chain-ops, add/delete/replace, handleActionConfirm, send/incore/restart, производные флаги + удобные методы selectAddSuggestion/cancelAddRow/openReplace/…) + view `Step4Approval.tsx` (~370, только разметка + helpers STATUS_LABELS/getStatusClass/formatDate; Fluent). Вьюху добьём на Этапе 3. ### ✅ Step2CommercialOffers (597) — СДЕЛАНО (build ок, 2 чекпоинта) 597 → контейнер `Step2CommercialOffers.tsx` (~155) + `kpTypes.ts` + `useKpProcessing.ts` (конвейер Syntex/Flow) + `step2Persist.ts` (handleSave→функция, возвращает patch) + вьюхи `SuppliersWithoutKp.tsx` (владеет add-стейтом) / `UploadedKpList.tsx`. Одно семантич. уточнение: в persist `formDataRef.current`→`formData` (в момент save совпадают). ### Step3ComparisonTable.tsx (2217) → по образцу `Step3BuildCt/*` + отдать approval в Step4 | Секции CT (строки) | Куда (аналог KPS) | |---|---| | 25–179 типы/хелперы/формат | `types/domain.ts`, `utils/format.ts`, `useStep3Matrix.ts` | | 196–435 стейт матрицы | `useStep3Matrix.ts` | | 285–394 approval-хендлеры | → Step4 `useStep4Approval.ts` | | 396–405, 947–1017 AI/PDF | `useAiComment.ts` + `AiCommentBlock.tsx` | | 439–604 useEffect загрузка/persist | `step3Persist.ts` + `useStep3Matrix.ts` | | 606–706 useMemo производные | `useStep3Matrix.ts` | | 708–789 редактируемые ячейки | `OfferCells.tsx` | | 791–945 хендлеры строк | `AddExtraRow.tsx` + `useStep3Matrix.ts` | | 1019–1193 handleSave | `step3Persist.ts` | | 1197–1239 тулбар + тумблер ед.изм. | `Step3BuildCt.tsx` (+ доменное `unitConversion`) | | 1241–1337 thead | `ComparisonMatrix.tsx` | | 1339–1806 tbody + итоги | `MatrixRowView.tsx` + `OfferCells.tsx` + `ComparisonMatrix.tsx` | | 1807–1864 инлайн-добавление | `AddExtraRow.tsx` | | 1865–2029 критерии + AI + PDF | `CriteriaTable.tsx` + `AiCommentBlock.tsx` | | 2030–2114 инлайн-цепочка (Teams) | → Step4 `InlineApprovalChain.tsx` | | 2115–2211 диалоги (делегирование/решение) | ui-kit `Dialog` в `Step4Approval.tsx` | ### Step4Approval.tsx (886) → `useStep4Approval.ts` + `Step4Approval.tsx` + `InlineApprovalChain.tsx` (стейт+манипуляции цепочкой 94–352, handleActionConfirm/Send/Incore/Restart 239–474 → хук; render 476–763; диалоги 764–885 → ui-kit Dialog) ### Step2CommercialOffers.tsx (597) → `useKpProcessing.ts`/`kpPipeline.ts`/`kpOffers.ts`/`step2Persist.ts` + view (`Step2LoadKp`,`KpFileList`,`CurrencyRatesForm`,`SuppliersWithoutKp`,`UploadedKpList`) ### CtService.ts (1532) — ввести `siteUrl?` (крит.) + при желании обобщённые CRUD-примитивы как в KPS. Создать `common/*` (StepShell/FormHeader/Pill) + единый `StepProps`. ## Доменные отличия CT (НЕ терять при декомпозиции!) - Шаг 1 — из **SP-списка** (`fetchRequests(days)`), не из TNSU API (KPS). - Колонки позиций: **disciplineCode (марка), smrType (SMR), agskCode**, uniqueCode, organization. - `CT.number` — строковый, `generateCtNumber(projectName)` (per-project). - `utils/unitConversion.ts` — пересчёт в ед. изм. заявки (тумблер) — нет в KPS. - `SUPPLIER_STATUS_OPTIONS` (Завод/Дистрибьютор…) — нет в KPS. - CurrencyRate/SupplierOffer — иные поля (rateToKzt; deliveryCostKzt/additionalCostsKzt/totalWithExtraKzt/pricePerUnitKzt). - `getCurrentUserCompany()==='astel-k'` → `isRestrictedView` (скрытие цен). ## Этап 3 — перекраска (карта A/B/C из REPAINT-MAP) Fluent в CT: PrimaryButton/DefaultButton→Button, TextField→TextInput/TextArea, MessageBar→Alert, IconButton/Icon, Spinner→Spinner, Dialog→ui-kit Dialog, Checkbox→Checkbox, статусы→Pill/Badge. Особые: **Toggle** (пересчёт ед.изм.) и **NormalPeoplePicker** (делегирование) — прямых аналогов в ките нет: Toggle → ui-kit Toggle (есть!); people picker → TextInput + searchUsers()+suggestions (как KPS Step4). **✅✅ Этап 3 ЗАВЕРШЁН (механическая перекраска) — Fluent полностью удалён из CT (0 импортов @fluentui во всей `forms/ct/`), heft build ожидается пользователем.** - Все 12 файлов с Fluent переведены на `@tnsu/ui-kit`: RequestsTable/FileUploadArea/UploadedKpList (Checkbox/IconButton), SuppliersWithoutKp/CurrencyRatesForm/Step1PositionSelection/ Step2CommercialOffers/AiCommentBlock (TextInput/Spinner/Alert/IconButton/FormField), useStep3Approval/InlineApprovalChain/Step4Approval (делегат/замена — NormalPeoplePicker заменён на паттерн KPS: query+debounce(300ms)+suggestions+select, `useStep4Approval`/`useStep3Approval` получили `handleAddQueryChange(val)`/`setDelegateQuery(val)` вместо Fluent-сигнатуры (event, val)), и главный контейнер `Step3ComparisonTable.tsx` (984 строки, вся матрица/критерии/футер/AI-диалог). - Правило замен применено: `TextField`→`TextInput`/`TextArea` (multiline→TextArea rows=1, автовысоты нет — принято как в KPS `CriteriaTable`); без `className`→обёрнуто в `
`; `Toggle` без `label`→ обёрнут в `