Заявка на услуги #18

Merged
gorbenko.pavel merged 15 commits from service-request into main 2026-09-01 12:48:41 +00:00
Collaborator
No description provided.
gorbenko.pavel added 15 commits 2026-09-01 12:26:26 +00:00
Электронный аналог бумажной Service Requisition Form ПТО: заявитель,
проект (маршрут резолвится по нему), описание требований, табличные части
«Приложения» и «Смета» (JSON-колонки, сумма считается на лету), только
фаза согласования — без исполнения. Форма подачи + режим согласующего,
провижининг-манифесты данных и согласования, регистрация в реестрах форм.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Смета теперь — список групп (разделов работ), внутри каждой свои позиции;
название группы необязательно. Сумма по позиции/группе/общий итог по-прежнему
считаются на лету, не хранятся построчно. UI — карточка на группу с полями
без рамки таблицы, по макету коллеги.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Понятнее по смыслу колонки: это смета (Estimate), а не «costs» вообще.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Каждую строку таблицы приложений теперь можно связать с реальным файлом —
он хранится как встроенное вложение элемента списка (тот же механизм, что
у общего блока «Вложения»), новых списков/библиотек в SharePoint под это
заводить не нужно. В IAnnexRow добавлено fileName; выбранный, но ещё не
сохранённый файл держится в памяти формы и грузится вместе с общими
вложениями при save() (нужен id элемента). Удаление строки/замена файла
открепляет уже загруженное вложение, чтобы не оставалось висящих файлов.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Убрано прикрепление реальных файлов к строке (в исходной бумажной форме
их нет — только перечень с номером и подтверждением), а заодно колонки
«Подтверждение» и «Кол-во стр.». Приложения теперь просто опись: описание
документа + номер приложения. Для файлов остаётся общий блок «Вложения».

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Вернул возможность прикладывать файл прямо к строке «Приложения» (fileName
на IAnnexRow, хранится как встроенное вложение элемента списка) и убрал
общий блок «Вложения» — он был избыточен: файлы теперь привязаны к своим
строкам, отдельного места под них не нужно.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Под текстовым описанием объёма работ добавлена таблица групп/позиций с
ценой и суммой — в том же формате, что и смета (группа → позиции → сумма
по группе → общий итог). Дополняет текст итоговыми цифрами, не заменяет
его.

Редактор таблицы «группы → позиции» вынесен в отдельный переиспользуемый
компонент CostGroupsEditor — CostsSection и новый блок в ScopeSection
используют один и тот же код вместо двух копий.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Итог сметы и объёмов работ теперь кладём прямо в JSON-колонку рядом с
группами ({ groups, total } — см. serializeGroups/parseGroupedJson в
cost.ts), а не отдельной числовой колонкой SharePoint. Форма total из
хранилища не доверяет, всегда считает заново; в JSON он нужен только тем,
кто читает колонку напрямую (отчёты, sp-query), не поднимая форму.

Убраны EstimateTotal/WorkVolumesTotal из провижининг-манифеста — они ещё
не были провижинены на тенанте, миграция не нужна.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Форма открывается в боковом Drawer, а не на всю страницу — его ширина не
связана с viewport, поэтому брейкпоинты CollapseSection/FormGrid (следят
за шириной окна) не спасают. Фикс-колонки строки (ед.изм/кол-во/цена/
сумма) при этом не сжимались (grid-колонки по умолчанию не сжимаются
своего содержимого) и вылезали за рамку карточки.

Обернул строки в горизонтально скроллящийся контейнер (тот же приём, что
раньше был у плоской таблицы — .tableWrap в AnnexesSection) вместо попытки
сжать поля до нечитаемости.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CollapseSection.body у @tnsu/ui-kit уже сам является 2-колоночным грид-
контейнером (._body{grid-template-columns:1fr 1fr}) — FormGrid внутри
неё создавал ВЛОЖЕННЫЙ грид, который занимал только ОДНУ из двух колонок
родителя (как единственный grid-item без явного grid-column), а его
собственные две колонки делили уже урезанную пополам ширину. Из-за этого
«Объём работ», «Итого объёмы работ» и «Общие замечания» рендерились на
половине ширины карточки, хотя место было.

FormField кладём прямо в CollapseSection, без обёртки FormGrid — так уже
сделано во всех остальных формах (см. Clearance/RequestSection.tsx).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
useMemo с attachmentUrls стоял ПОСЛЕ `if (loading) return` — на первом
рендере (загрузка) он не вызывался, на втором вызывался, порядок хуков
менялся, и React ронял компонент с #310 «rendered more hooks than during
the previous render». Карточка заявки на услуги не открывалась вообще.
Перенёс расчёт выше ранних выходов.

Заодно включил `react-hooks/rules-of-hooks` (error) — плагин давно в
зависимостях и в коде есть eslint-disable для exhaustive-deps, то есть
правила подразумевались, но в .eslintrc их забыли объявить. По остальному
коду правило даёт 0 нарушений, так что этот баг был единственным.
`exhaustive-deps` пока оставил выключенным: на текущем коде это 16
предупреждений, их разбор — отдельная задача, а не довесок к фиксу.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Маршрут «Заявки на услуги» падал с 400 «Поле или свойство SLAhours не
существует»: общий ApprovalListService читает и пишет SLAhours/
AskOverdueReason у ЛЮБОЙ формы, а в манифесте согласования их не было.
Манифест копировался с TmcTransfer, где их тоже нет — там на тенанте
колонки почему-то есть, поэтому не проявлялось, но следующая партиция
списка создалась бы уже сломанной. Добавил обеим формам.

Текст ошибки при этом выводился как «Поле…»:
useMatrixEditor и useRequestTypeId клали в state сырой err.message, а
PnPjs кладёт туда тело ответа SharePoint целиком, где кириллица —
\u-escape-последовательности. Пропустил оба через friendlyErrorMessage,
который для этого и написан (он уже используется формами).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Правило было не задано вовсе: плагин eslint-plugin-react-hooks подключает
профиль @microsoft/eslint-config-spfx/lib/profiles/react, но правил из него
не включает. Теперь в .eslintrc.js явно заданы rules-of-hooks (error) и
exhaustive-deps (warn); сборка проходит с нулём предупреждений.

Что реально чинилось, а не затыкалось:

- shared/useStableCallback.ts — обёртка с неизменной ссылкой над колбэком-пропом
  (аналог будущего useEffectEvent). Ссылка стабильна, внутри всегда актуальная
  версия колбэка, поэтому её можно честно класть в зависимости.
- onCodeLoaded во всех forms/*/*View.tsx и *Form.tsx идёт в эффект загрузки
  через эту обёртку: просто дописать проп в зависимости нельзя — заявка
  перезапрашивалась бы на каждый рендер родителя.
- useRequestSubmit: run стабилизирован (useCallback + savingRef вместо чтения
  состояния), возвращаемый объект мемоизирован. Формы берут из него стабильный
  setError и указывают в зависимостях его, а не submit целиком.
- shared/usePersistedQuery.ts (useRoutedQuery) — было устаревшее замыкание:
  эффект навсегда запоминал первую версию onQueryChange, а та замкнута на route
  на момент монтирования, то есть набранный запрос мог уехать в маршрут чужого
  раздела. Теперь зовётся актуальная версия.
- propertyPane/DebugUserField.tsx — то же самое с onResolved.
- Формы: в эффект "новая заявка по умолчанию на себя" дописан context
  (единственный экземпляр на жизнь веб-части, лишних перезапусков не даёт),
  в ReferralForm ещё и мемоизированный approval.
- RequestsTableView: dataKey в зависимости (он и так выводится из view/
  statusFilter/debouncedQuery, которые там уже есть).

Осознанные отступления оставлены с объяснением ПОЧЕМУ: useMatrixEditor (момент
перечитывания матрицы задаёт вызывающий через reloadKey), companiesFieldValue
в RecruitingHire/RecruitingSelection.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
На печати заявки под строками сметы и объёмов работ выходила серая полоса
прокрутки со стрелками (видно в PDF): контейнеры с overflow-x: auto
печатаются вместе со своим скроллбаром, а уехавшее вбок на бумаге просто
теряется — прокрутить лист нельзя.

В @media print отпускаем ширину: overflow: visible и min-width: 0, тогда
таблица укладывается в лист целиком. Помимо новых блоков «Заявки на
услуги» это же исправлено у общей ItemsTable (печать СЗ на перемещение
ТМЦ) и у матрицы согласования — там ровно тот же контейнер.

Блоки @media print стоят В КОНЦЕ файлов намеренно: медиазапрос не
повышает специфичность, и базовое `.row { min-width: 640px }`, объявленное
ниже, перебивало бы правило печати (поймано на скомпилированном CSS).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Merge branch 'claude/heuristic-jones-cb2a26' into service-request
Tests / frontend-test (pull_request) Successful in 1m18s
63c674ed99
gorbenko.pavel merged commit cc2efd8771 into main 2026-09-01 12:48:41 +00:00
gorbenko.pavel deleted branch service-request 2026-09-01 12:48:41 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: timur.gorodetskiy/request-portal#18