Files
Тимур Городецкий 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

165 lines
20 KiB
Markdown
Raw Permalink 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.
# OID — перенос в ct-forms-portal (Фаза 4)
Порядок (тот же, что и CT): **Этап 1 перенос 1:1 → Этап 2 декомпозиция под структуру KPS → Этап 3 перекраска на ui-kit.**
Правило: сохранить 100% поведения; build после каждого этапа. Эталон структуры — `forms/kps/`, карта резки — `docs/CT-PORT.md` (Step2/3/4 у OID структурно близки к CT).
Источник: `oid-create-form/src/webparts/oidCreateForm/`. 10 файлов, самый большой — `Step3ComparisonTable.tsx` (2533 стр, ещё крупнее, чем был у CT).
## Разведка (перед переносом)
- `Step3ComparisonTable.tsx` (2533), `Step4Approval.tsx` (844), `Step1WorksInput.tsx` (646), `Step2CommercialOffers.tsx` (533), `hooks/useOidForm.ts` (299), `OidService.ts` (1470).
- **Шаг 1 у OID устроен ИНАЧЕ, чем у CT**: `Step1WorksInput` — это ручной ввод сгруппированных работ (`PositionGroup → OIDPosition[]`, поля `parentGroup`/`priceSmeta`/`summaSmeta`, `ComboBox` для группы), а не выборка из SP-списка потребностей (как `RequestsTable`+`useRequests` у CT). Ближе к паттерну ItemsTable из «СЗ на перемещение ТМЦ». Для Шага 1 нужен свой чертёж декомпозиции на Этапе 2 — карта CT/KPS сюда не подходит.
- Step2/Step3/Step4 структурно близки к CT (коммерческие предложения / матрица сравнения / согласование) — карта резки CT применима с поправкой на домен (parentGroup/priceSmeta вместо disciplineCode/SMR/agskCode).
- **0% ui-kit** — в отличие от CT (который уже частично сидел на `@tnsu/ui-kit@0.4.0`), OID полностью на raw Fluent. На Этапе 3 конвертировать всё, форы нет.
## Этап 1 — перенос 1:1 — ✅ ЗАВЕРШЁН
1. ✅ Скопированы `components/` и `services/` → `forms/oid/{components,services}/` (17 файлов, 1:1, без правок логики).
2. ✅ **`OidService.ts` НЕ ТРЕБУЕТ параметризации siteUrl** (в отличие от Ct/KpsCtFormService!): модуль-константа
`SITE_URL = "https://tnsukz.sharepoint.com/sites/sed"` уже прописана как раз на целевой сайт портала —
OID изначально жил на /sites/sed, миграции сайта (в отличие от CT/KPS) не было. Все ~15 обращений к SP-листам
используют эту константу напрямую (не `this.baseUrl`), so `context.pageContext.web.absoluteUrl` тоже будет
верно резолвиться при работе внутри портала. Решение: оставить `OidService.ts` полностью нетронутым —
любая правка (даже добавление неиспользуемого `siteUrl?`) была бы риском без пользы.
3. ✅ `IOidCreateFormProps.ts`: `hasTeamsContext` был объявлен, но нигде не читался (мёртвый пропс) —
сделан опциональным (`hasTeamsContext?: boolean`), чтобы порталу не нужно было его прокидывать.
`siteUrl?` НЕ добавлен (сервису он не нужен — см. п.2, не добавляем неиспользуемый пропс).
4. ✅ Обвязка портала:
- `oidDocumentConfig.ts`: добавлены `inPortal: true` + `buildInPortalParams` (условие `Черновик`/`formed` →
`oidId`, иначе → `oidid` — сверено с `useOidForm.ts`, который читает именно так; условие шире, чем у CT,
т.к. `oidDocumentConfig` уже поддерживал статус `formed` в `buildLinkUrl`).
- `CtFormsPortal.tsx`: добавлен импорт `OidCreateForm` + ветка `route.form === 'oid'` → `<OidCreateForm>` +
«← К реестру» (по образцу CT/KPS-веток). `FORM_ID_PARAMS` уже содержал `oidId`/`oidid` (задел на будущее).
- `Dashboard`/`config/index.ts` — уже были готовы (`createOidDocumentConfig()` зарегистрирован, `Dashboard.tsx`
полностью generic по `DOCUMENT_TYPES`, без хардкода typeKey) — правок не потребовалось.
5. ✅ `heft build` — зелёный (после фикса ниже), запущено и открывается в workbench.
**Build-фикс (tsconfig):** первый прогон дал ошибки `TS2550 flatMap/Object.entries does not exist` +
каскадные `TS7006 implicit any`. Причина: у исходного `oid-create-form/tsconfig.json` был явный
`"lib": ["es2019", "dom", "dom.iterable"]`, а у `ct-forms-portal/tsconfig.json` — только базовый
rig-конфиг (`dom, es5, es2015.core/collection/iterable/promise/proxy`, без ES2017/ES2019). Код OID
использует `Array.flatMap`/`Object.entries`, которых там нет. **Решение:** добавлен тот же `lib`-оверрайд
в `ct-forms-portal/tsconfig.json` (а не переписывание кода OID под ES5-совместимые аналоги) — так
проще, ничего не меняет в поведении, и это чистая надстройка типов (не влияет на CT/KPS, которые эти
методы не используют). Файлы OID остались нетронутыми.
## Этап 2 — декомпозиция
**Статус:** [x] Step2CommercialOffers ✅ | [x] Step4Approval ✅ | [ ] Step1WorksInput | [ ] Step3ComparisonTable
### ✅ Step2CommercialOffers (533) — СДЕЛАНО (build+workbench ок)
533 → контейнер `Step2CommercialOffers.tsx` (165) + `useKpProcessing.ts` (248, конвейер Syntex/Flow +
стейт uploadedFiles/suppliers/kpFiles) + `step2Persist.ts` (54, handleSave→функция, возвращает patch)
+ вьюхи `SupplierList.tsx` (43) / `UploadedKpList.tsx` (45) / `AddNoOfferDialog.tsx` (53, диалог
«Добавить без КП» — держит своё UI-состояние формы). Отличие от CT: список поставщиков ведётся прямо
на Шаге 2 (не как у CT), диалог вместо инлайн-формы добавления. Сохранён silent-guard
`if (!formData.oid) return;` перед вызовом persist (было тихим no-op, не стало видимой ошибкой).
### ✅ Step4Approval (844) — СДЕЛАНО (build+workbench ок)
844 → `useStep4Approval.ts` (478: цепочка-операции, add/delete/replace, handleActionConfirm,
send/export/restart, производные флаги + удобные методы selectAddSuggestion/cancelAddRow/openReplace/…)
+ view `Step4Approval.tsx` (426, только разметка + helpers STATUS_LABELS/getStatusClass/formatDate;
Fluent). Структурно почти идентично CT (кроме `handleExport`≈CT `handleSendToIncore`, методы сервиса
называются иначе: `generateOidReport`/`updateOIDStatus`/`resetApprovalChainToDraft`). Финальное
дробление вьюхи (add-member/replace-dialog поиск в под-компоненты) — отложено на Этап 3, как у CT.
### ✅ Step1WorksInput (646) — СДЕЛАНО (build ожидается)
646 → `useStep1WorksInput.ts` (445, перенос ВЕРБАТИМ: весь стейт/рефы(projectSearchTimer/groupsRef/
isMounted/dirtyRef/syncTimer/pendingOidRef/persistRef/scheduleSyncRef)/эффекты(sessionStorage-sync,
гидратация из formData с dirty-guard, cleanup-flush на unmount)/хендлеры(loadProjects/persistPositions/
scheduleSync/markDirtyAndSync/handleWorkNameBlur/handleProjectChange/updateGroupName/updatePosition/
addGroup/removeGroup/addPosition/removePosition/validate/handleSave)) + view `Step1WorksInput.tsx`
(239, только разметка). Риск: клубок рефов/дебаунс-синхронизации позиций в SP + защита от потери правок
при F5/гидратации (dirtyRef) — того же класса, что CP4 у CT, только меньшего масштаба. `dirtyRef`
экспортирован из хука как есть (вьюха дёргает `dirtyRef.current = true` инлайн в одном месте — сохранено
дословно, не оборачивал в сеттер). ✅ build+workbench-тест пройден пользователем (создание группы/позиции, debounce-запись, F5, blur
наименования работ → авто-создание ОИД) — без регрессий.
### ✅ Step3ComparisonTable (2533) — СДЕЛАНО по чекпоинтам (build ожидается)
Разведка подтвердила: структура похожа на CT (Step3+Step4-согласование смешаны), но домен другой
(группы позиций с колонкой «Группа» в шапке, нет валют/тумблера ед.изм., смета price/summa как read-only
референс) — и **два независимых debounce-автосейва** (офферы + критерии, оба по 1500мс) поверх
unmount-persist и beforeunload-persist — риск выше, чем был у CT.
- ✅ CP1 согласование → `useStep3Approval.ts` + `InlineApprovalChain.tsx` (голосование/делегат, 1:1,
NormalPeoplePicker/IPersonaProps оставлены как есть — Fluent, трогать на Этапе 3).
- ✅ CP2 AI/отчёт → `useAiComment.ts` (комментарий ИИ + отдельная генерация Excel-отчёта
`handleGenerateReport`/`generateOidReport` — своя фича, отсутствует в CT в таком виде) +
`AiCommentBlock.tsx` (проще, чем у CT — нет удаления комментария).
- ✅ CP3 хелперы/типы → `matrixTypes.ts` (CriteriaEntry/CellEdit/EditableOffers/ExtraSupplier/
ExtraPosition/PositionRow) + `matrixHelpers.ts` (FIXED_CRITERIA/FIXED_COLS/genId/fmt/fmtKzt/
buildOfferKey).
- ✅ CP4 ядро матрицы → `useStep3Matrix.ts` (1186 строк, перенос ВЕРБАТИМ: весь стейт/рефы/эффекты
ОБОИХ автосейвов(офферы+критерии)/unmount-persist/beforeunload-persist/мемо/хендлеры/handleSave).
`extraIdCounter` — модульная переменная в этом файле (как у CT). `getPosRowSpan` (для rowSpan группы
в шапке таблицы) тоже переехал в хук и возвращается наружу.
- **Контейнер: 2533 → 877 строк.** Финальное дробление render (таблица цен как отдельные
под-компоненты) сознательно НЕ делали — как и у CT/OID Step1/2/4, откладывается на Этап 3 вместе
с перекраской (не трогать вьюху дважды).
- ✅ Build+workbench-тест подтверждён пользователем — без регрессий (матрица, оба автосейва, F5,
unmount-persist, добавление/удаление поставщика/позиции, согласование, отчёт, AI).
## ✅✅✅ ЭТАП 2 ДЛЯ OID ПОЛНОСТЬЮ ЗАВЕРШЁН
Все 4 god-файла (Step1WorksInput/Step2CommercialOffers/Step3ComparisonTable/Step4Approval) разобраны,
build+workbench-тест пройден по каждому. Самый рискованный кусок всего OID (Step3, два автосейва) —
подтверждён рабочим. Дальше — **Этап 3: перекраска на @tnsu/ui-kit** (полный объём, 0% ui-kit форы нет,
как у KPS изначально; не забыть полный проход по цветовым токенам + проверить локальные компоненты
типа Breadcrumbs на скрытые Fluent-цвета вне `@fluentui`-импортов — урок с CT).
## Доменные отличия OID (НЕ терять при декомпозиции!)
- Шаг 1 — **ручной ввод** групп работ (`PositionGroup`/`OIDPosition`), не SP-список потребностей.
- Позиции: `parentGroup`, `positionType` ('Услуга' | 'Дополнительные затраты'), `priceSmeta`/`summaSmeta` (сметная цена — своё, у CT такого нет).
- Списки SP: суффикс «ОИД» уже в названиях (`Сравнительная таблица ОИД`, `Позиции СТ ОИД`, ...) — коллизий с CT/KPS нет.
- `SupplierOffer` в OID проще, чем у CT: нет `currencyCode`/мульти-валюты в базовых полях (нет `deliveryDistanceKm` тоже) — проверить при декомпозиции, не тащить из CT лишнюю доменку.
## ✅✅ Этап 3 — перекраска (механика) ЗАВЕРШЕНА
Все 13 файлов с Fluent → @tnsu/ui-kit (0 импортов `@fluentui` в forms/oid/, включая .tsx и .scss —
проверено grep). Порядок: тривиальные (Step2 IconButton-файлы) → простые (AiCommentBlock/
AddNoOfferDialog/Step2CommercialOffers) → Step1WorksInput → сложные (useStep3Approval+InlineApprovalChain,
Step4Approval) → главный контейнер Step3ComparisonTable.tsx (877 строк) → OidCreateForm+Breadcrumbs → токены.
- **ComboBox не тянется на ui-kit** (нет автокомплита с готовыми опциями) — сделан локальный
`Step1WorksInput/ComboField.tsx` (свободный ввод + подсказки, паттерн как у поиска участника в
Step4Approval: TextInput/native input + `<ul>` подсказок, debounce уже был в хуке). Используется для
«Проект» (серверный поиск через `a.loadProjects`) и «Ед. изм.» (локальная фильтрация статичного списка).
Эскейп-хэтч: поле «Наименование работ» тоже осталось на native `<input>` (не TextInput) — нужен `onBlur`
(создаёт ОИД по потере фокуса), которого у ui-kit `TextInput` нет.
- **`TextInput`/`TextArea` из ui-kit не имеют пропа `className`** — все места, где раньше вешали кастомный
класс на инпут (ячейки матрицы `offerInput`/`qtyInput`/`dataCellName`, критерии, тулбар) переведены на
обёртку `<div className={styles.xxx}><TextInput .../></div>` (как в CT `MatrixRowView`/`CriteriaTable`).
- **useStep3Approval/InlineApprovalChain**: `NormalPeoplePicker`+`IPersonaProps` заменены на паттерн CT —
`delegateDialog.query/suggestions/selected` + debounce 300мс через `service.searchUsers`, `Dialog`
open/title/subText/footer вместо `DialogType.normal`/`DialogFooter`.
- **Иконки**: Fluent→ui-kit спрайт (Add→plus, Delete/Cancel→trash/close, ChevronLeft→arrow-left,
ExcelDocument→document, Forward→chevron-right, Accept→check, Up/Down→arrow-up/arrow-down,
Switch→swap, AddFriend→(без иконки, нет аналога), Lightbulb→(без иконки, как у CT)).
- **Токены**: создан `forms/oid/components/common/_theme.scss` — точное зеркало CT/KPS-моста (тот же
navy/gold маппинг). Полный проход по hex-литералам (~30 уникальных кодов, ~200+ вхождений) →
bulk-sed по всем `*.module.scss` (кроме `OidCreateForm.module.scss`, где 2 литерала `#1b1a19`/`#252423`
сознательно оставлены как есть в `.dark`-блоке — тот же паттерн, что и у CT, dark-theme не токенизируем).
По ходу найдены и убраны 2 мёртвых класса (`.winBtn`/`.winBtnActive`, не использовались в разметке).
`OidCreateForm.module.scss` переписан по образцу `CtCreateForm.module.scss` (root/content/dark), Fluent
`References.scss`-импорт и мёртвые классы `.welcome`/`.welcomeImage`/`.links` убраны.
- ✅ Build+workbench-тест перекраски пройден пользователем. По итогам визуальной проверки — 2 доп. фикса:
- Билд-гоча: SPFx typings-генератор для scss выкидывает из `IExportStyles` **пустые CSS-правила**
(`.class {}` без объявлений) — TS2339 "Property does not exist", хотя класс использован в TSX.
Столкнулись дважды: (1) `.addRowBtn` в Step4Approval.module.scss забыт при переносе JSX-паттерна из
CT (там кнопки стилизовались инлайново); (2) при удалении заливки `.extraRow` (см. ниже) правило
стало пустым — пришлось оставить с `background: transparent;` вместо полного удаления класса.
Написан bash/awk-скрипт для проверки styles.X (tsx) ↔ определение класса (scss) по всему дереву —
полезно на будущее при переносе JSX между формами.
- Цвет: `.colSel`/`.thSupplierSel` (подсветка выбранного поставщика) были на `$warn-bg` — заменены на
`rgba($gold-500, 0.02)`, как у CT. `.extraRow` (доп. затраты) — заливка `$warn-bg` убрана полностью
(только `background: transparent`, класс сохранён пустым не может — см. гочу выше).
- ✅✅ **Финальное дробление render `Step3ComparisonTable.tsx` ЗАВЕРШЕНО** (877→138 строк контейнер),
по точному образцу CT: `ComparisonMatrix.tsx`(228, вся `<table>`)/`MatrixRowView.tsx`(258, обычная
позиция+bundle, с доменной ячейкой «Группа» rowSpan — которой нет у CT)/`ExtraPositionRow.tsx`(123,
доп.затраты, без bundle)/`AddExtraRow.tsx`(73)/`CriteriaTable.tsx`(125). Логика не трогалась — чистая
перекладка JSX, единый проп `matrix: Step3Matrix` (уже был экспортирован из useStep3Matrix.ts). Порядок
строк в `<tbody>` сохранён точно: позиции по группам → `AddExtraRow` → subtotal обычных → extra-позиции
→ subtotal доп.затрат → grand total. Проверка styles.X↔scss после дробления — чисто, `@fluentui` — 0
вхождений.
- ✅ Build + беглый workbench-прогон (Шаг 1→4) подтверждён пользователем после дробления — без регрессий.
## ✅✅✅ ФОРМА OID ПОЛНОСТЬЮ ЗАВЕРШЕНА
Этап 1 (перенос) + Этап 2 (декомпозиция логики) + Этап 3 (перекраска на @tnsu/ui-kit) + финальное
дробление render — всё сделано и подтверждено сборкой/workbench. OID теперь на одном уровне готовности
с CT. Три формы портала (CT/KPS/OID) в состоянии Fluent→ui-kit переведены полностью.