Files
ct-forms-portal/docs/CT-PORT.md
T
Тимур Городецкий 109053e3af объединение CT, KPS, OID и дашборда в один портал
Перенос, декомпозиция и перекраска форм CT/KPS/OID на @tnsu/ui-kit,
config-driven дашборд-реестр по трём типам документов. Из отслеживания
исключены артефакты сборки (lib-commonjs/jest-output/release) — забыты
в .gitignore ранее.
2026-08-28 16:21:34 +05:00

175 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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'` → `<CtCreateForm .../>` + «← К реестру» (по образцу 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`→обёрнуто в `<div>`; `Toggle` без `label`→
обёрнут в `<label>` вручную (span рядом); иконки Fluent→спрайт (Delete→trash, Cancel→close,
Add→plus, Accept/CheckMark→check, ChevronLeft→arrow-left, PDF→document, Forward→chevron-right,
AddFriend/Switch→plus/swap, DoubleChevron*→chevron-left/right повторно, Lightbulb→без иконки).
- SCSS: все `:global(.ms-TextField-*)`-селекторы (таргетили Fluent DOM) заменены на `input`/`textarea`
(таргетят нативный тег внутри обёртки ui-kit — рабочий приём, т.к. хэши классов ui-kit непредсказуемы).
- ⚠️ **Осталось на будущее (сознательно НЕ делали сейчас):** финальное дробление `Step3ComparisonTable.tsx`
render на под-компоненты `ComparisonMatrix`/`OfferCells`/`MatrixRowView`/`AddExtraRow`/`CriteriaTable`
по образцу KPS `Step3BuildCt/*`. Причина отказа сейчас: KPS-хук `useStep3Matrix` даёт чистый API
(`matrix.rows`/`matrix.rateOf`/`matrix.editNumber`...), а CT-хук `useStep3Matrix` (856 строк, CP4)
перенесён ВЕРБАТИМ с сырыми именами (`updateEdit`/`updatePriceKzt`...) и НЕ верифицирован в runtime —
редизайн его публичного API сейчас добавил бы риск поверх уже непроверенного риска. Делать этот шаг
ТОЛЬКО после workbench-теста CP4 (см. ниже), отдельным заходом, вместе с рефакторингом API хука под
форму KPS.
- ✅ **Workbench happy-path пройден** (все 4 шага, создание→согласование, без ошибок) — основной риск CP4+перекраски
снят. Не проверены детально: bundle-строки, Toggle перевода ед.изм., доп.расходы/логистика, критерии/AI,
делегат/замена в согласовании, F5-persist. Смотреть по возможности, не блокирует дальнейшую работу.
- Отдельно найден и починен степпер (`Breadcrumbs.tsx`) — жил вне грепа по `@fluentui` (свой хендрол на
Fluent-синих литералах без Fluent-импорта), переведён на адаптер `Stepper`+`FormHeader`+`Badge` (как KPS).
- ✅✅ **Полный проход по литералам-цветам ЗАВЕРШЁН** (весь набор Fluent-палитры, не только синий):
создан мост `forms/ct/components/common/_theme.scss` (точное зеркало `forms/kps/.../common/_theme.scss`,
тот же маппинг: $accent/$accent-bg/$accent-border/$accent-dark/$accent-hover ← Fluent-синий,
$text/$muted/$muted-2/$border/$border-strong/$head-bg/$hover/$surface ← Fluent-серый,
$ok/$ok-bg/$danger/$danger-bg/$warn-fg/$warn-bg ← Fluent-статусы). Импорт `@import '../common/theme'`
добавлен в 9 файлов (Breadcrumbs, CtCreateForm, RequestsTable, Step1PositionSelection, CurrencyRatesForm,
FileUploadArea, Step2CommercialOffers, Step3ComparisonTable, Step4Approval) — bulk sed-заменой всех
Fluent hex-литералов на семантические переменные (~150+ подстановок). Один найденный баг при массовой
замене: в `CtCreateForm.module.scss` есть SPFx-плейсхолдер темы `"[theme:bodyText, default: #hex]"` —
это НЕ SCSS-код, а строка, которую парсит SPFx-лоадер во время сборки; sed случайно подставил туда
`$text` вместо литерала — исправлено обратно на реальный hex (`#111827`, эквивалент `$gray-900`).
Оставлены как есть (сознательно, вне палитры Fluent): dark-theme цвета (`#1b1a19`/`#252423`),
один `#fefefe` (незначим).
- Отдельно найден и починен степпер (`Breadcrumbs.tsx`) — жил вне грепа по `@fluentui` (свой хендрол на
Fluent-синих литералах без Fluent-импорта), переведён на адаптер `Stepper`+`FormHeader`+`Badge` (как KPS).
- Точечные UX-правки после визуальной проверки пользователем: степпер сужен до `30vw` (обёртка
`.stepperWrap`, прижат влево, откат на 100% ширины ниже `$bp-md`) — применено и в CT, и в KPS;
подсветка «выбран поставщик» (`.colSelected`) переведена с `$warn-bg` (семантически неверно —
«предупреждение») на полупрозрачную золотую заливку `rgba($gold-500, 0.02)`; заливка строк
Логистика/Доп.расходы (`.extraRow`) убрана полностью (были на `$warn-bg`, читались слишком ярко).
## ✅✅✅ ФОРМА CT ПОЛНОСТЬЮ ЗАВЕРШЕНА
Этап 1 (перенос) → Этап 2 (декомпозиция, включая рискованный CP4) → Этап 3 (перекраска компонентов +
полный проход по цветовым токенам + финальное дробление render Step3ComparisonTable) — всё сделано,
собрано и подтверждено пользователем в workbench (полный проход по 4 шагам после каждого крупного этапа).
Дальше по плану — **OID**, тем же маршрутом: перенос 1:1 → декомпозиция под структуру KPS → перекраска.