Files
stroy-bot-monorepo/docs/MIGRATION-CHECKLIST.md
T
Тимур Городецкий c41df77c09 feat: port stroy-bot-import-works as import page
Moves estimate work import into the app as ?page=import&projectId=.
Behaviour is kept 1:1 with the legacy web part: project lookup with
UniqueId0 check, project objects query, works paging by 200 with the
progress text, 250 ms debounced search over the same fields, 100 rows
per page, select-all over all filtered not-imported works, duplicate
protection via cree9_comment, the same Dataverse POST body, import into
the selected object and import with object creation (find or create
root and child in «Объекты», cached per pair), toasts and all texts.

The object picker still shows only the first 100 matches (legacy bug).
HTTP-response errors are detected by their status field instead of
instanceof HttpError, which does not survive the ES5 build target.
Deviations are listed in docs/MIGRATION-CHECKLIST.md section 6.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 08:57:02 +05:00

27 KiB
Raw Blame History

Чек-лист миграции StroyBot → монорепо

Каждый раздел считается перенесённым только когда все пункты проверены в workbench. Пишущие операции (создание / правка / удаление / импорт) проверяем только на тестовом проекте «ТЕСТ StroyBot».

Правила переноса:

  • Запросы, поля, фильтры, права и тексты переносятся 1:1 из исходного репозитория.
  • Известные баги переносятся как есть (помечены 🐞) — исправляем позже отдельными коммитами.
  • Старые страницы и web parts не трогаем, поддержка старых URL не нужна.

Легенда: [ ] — не проверено, [x] — проверено в workbench.


0. Общее (оболочка)

  • Одна web part, разделы переключаются через ?page=…, проект — через ?projectId=…
  • Кнопки «назад/вперёд» браузера переключают разделы
  • Ссылку на любой раздел можно скопировать и открыть в новой вкладке
  • Выбранный проект сохраняется между перезагрузками (sessionStorage, ключ stroybot_selected_project)
  • Шрифты Inter и Lora подгружаются (раньше грузили report-list и assigner)
  • webApiPermissionRequests в одном пакете: api.tnsu.kz (9232d9e2-…, Reports.Read и user_impersonation) + Dataverse https://tansu.crm5.dynamics.com (user_impersonation)

Сознательные изменения (не функционал, а настройка):

  • Настройки property pane из 8 web parts сведены в одну группу «Подключение». Имена списков больше не настраиваются в property pane — они в константах.
  • worksPageUrl (objects → works) больше не нужен: переход внутри приложения.

1. Главная (stroy-bot-main)

Выбор проекта

  • Поле «Проект:», плейсхолдер «Выберите проект», иконка поиска
  • Список грузится при первом фокусе, «Загрузка проектов…»
  • При фокусе текст выделяется
  • Фильтр по Title (без учёта регистра, подстрока), максимум 7 результатов
  • У каждого — бейдж статуса, «Без статуса» если пусто
  • Сообщения: «Нет доступных проектов», «Проекты не найдены», «Ошибка загрузки. Попробуйте ещё раз.»
  • Клик вне / Esc — закрыть и вернуть выбранное название (или очистить)
  • Бейдж статуса рядом с полем: текущий — зелёный, завершен/завершён — серый, архивный — золотой, иначе — нейтральный
  • Сортировка по алфавиту, затем до 7 недавно активных (из Dataverse cree9_reports) — наверх
  • Права из «Доступы»: read | edit0 | aproove, canEdit = OR по edit0
  • Подсказка «Выберите проект, чтобы активировать ссылки ниже»

Разделы проекта

  • Объекты / «Объекты по проекту»
  • Отчёты по проекту / «Просмотр отчётности»
  • Отправить отчёт / «Отправка отчёта по проекту»
  • Загрузить работы из сметы / «Импорт работ в систему»
  • Архив проекта / «Библиотека документов» → {SITE}/Shared Documents/Архив/{Title} (внешняя ссылка, новая вкладка)
  • Ресурсы проекта / «Профессии и компании»
  • Без выбранного проекта карточки неактивны
  • Статус «архивный» — неактивно всё, кроме Архива; экспорт тоже неактивен
  • Статус «завершен/завершён» — неактивно «Отправить отчёт»
  • canEdit = false — неактивно «Отправить отчёт»

Экспорт данных «Выгрузить за период»

  • Кнопка «?» — поповер с 3 строками (день / Пн–Вс / произвольный), закрытие по клику мимо
  • Поля «Начало» и «Конец» видны только при выбранном проекте
  • «Укажите период для активации ссылки», пока даты не заданы
  • Ошибка «Дата начала не может быть позже даты конца», кнопка неактивна
  • Бейдж: «Ежедневный отчёт» (daily) / «Еженедельный отчёт» (Пн–Вс, weekly) / «Отчёт за период» (period)
  • «Выгрузить» → спиннер «Формируется…» → файл {prefix}_{from}_{to}.xlsx
  • Запрос GET https://api.tnsu.kz/Report/StroyBot?start=…&end=…&projectId=… (unix UTC 00:00:00 / 23:59:59)
  • Ошибки: «Ошибка API: {status} {statusText}» / «Ошибка при загрузке отчёта»

Сознательные отличия от main:

  • Лишний запрос к «Доступы» без фильтра по пользователю (его результат не использовался) — убран, на поведение не влияет.
  • Список проектов грузится сразу при открытии, а не при первом фокусе: от статуса и прав проекта теперь зависит и меню слева.
  • Карточки разделов открывают раздел в этой же вкладке; новая вкладка — Ctrl+клик или средняя кнопка (раньше всегда _blank). Архив — по-прежнему в новой вкладке.
  • Ошибки выгрузки показываются под кнопкой, а не через alert(). Тексты те же.
  • Поповер «?» всегда открывается вниз (раньше переворачивался вверх у края экрана).
  • Адрес выгрузки берётся из настройки «Базовый URL API» (по умолчанию тот же https://api.tnsu.kz).
  • Меню слева скрывает разделы по правилам карточек: архивный — только «Проект»; завершён или нет edit0 — без «Отправить отчёт»; «Привязка к справочнику» — только с edit0.

2. Объекты (stroy-bot-objects) — ?page=objects&projectId=

  • Шапка «Объекты проекта» + название проекта («Загрузка...» / «Ошибка»)
  • «Добавить объект» (только canEdit), «Экспорт» → «Экспортируется...» → objects-{projectId}.xlsx (GET https://api.tnsu.kz/Report/ProjectObjects?projectId=)
  • Без projectId — «Вы не выбрали проект»; «Проект с ID={id} не найден»; «Ошибка: …»
  • Пусто — «Объекты не найдены» + «+ Добавить объект» (если canEdit)
  • Тулбар «{N} объектов» (рекурсивно), «Развернуть», «Свернуть»
  • Дерево: отступ, иконка папки/файла, счётчик детей, шеврон, клик по строке — выделить + раскрыть
  • Раскрытые узлы сохраняются между перезагрузками в рамках сессии
  • Уровни: 0 — Объект-пятно, 1 — Раздел, 2 — Вид работ (не глубже 3 уровней)
  • Контекстное меню (⋮ и правый клик, только canEdit): Добавить объект (не на уровне 2), Редактировать, Работы, Удалить
  • Модалка: «Название *» (обязательно, Enter — сохранить), «Родительский объект» (select только при правке Раздела: узлы уровня 0, сортировка ru, без себя и потомков), «Проект» (read-only)
  • Заголовки модалки: «Добавить Объект-пятно / Раздел / Вид работ», «Редактировать …», «— (корневой)»
  • Удаление: подтверждение с числом дочерних, «Это действие нельзя отменить.»
  • Drag & drop (только canEdit): до / внутрь / после, запрет на себя и потомков, ошибка «Не удалось переместить: …»
  • «Работы» → раздел Работы с objectId
  • После любой операции дерево перезагружается
  • Список «Объекты»: Title, project (текст = название проекта), parent (self-lookup)
  • Права: «Доступы» projectId/userId → edit0

🐞 Перетаскивание меняет только родителя, порядок не сохраняется. 🐞 Удаление узла не удаляет детей.


3. Работы (stroy-bot-works) — ?page=works&projectId=&objectId=

  • Шапка с названием проекта
  • «Добавить работу» (только canEdit)
  • Таблица-дерево: #, Наименование, Ед. изм., Проектный объём (ru-RU, 3 знака), Дата (dd.mm.yy, «start – finish» / одна / «—»)
  • Родители свёрнуты по умолчанию, шеврон «Свернуть/Развернуть», нумерация по видимым строкам
  • «Загрузка данных...», «Работы не найдены» (+ кнопка), ошибки Dataverse
  • Меню строки ⋮ (canEdit): Добавить дочернюю, Редактировать, Удалить
  • Модалка: НАИМЕНОВАНИЕ *, ЕД. ИЗМ. (список «Единицы измерения», с пагинацией; текущее значение не из списка — добавить наверх), ПРОЕКТНЫЙ ОБЪЁМ (шаг 0.001), РОДИТЕЛЬСКАЯ РАБОТА (без себя и потомков), ОБЪЕКТ (read-only), ДАТА НАЧАЛА / ОКОНЧАНИЯ
  • «Сохранить и добавить еще» (в режиме добавления: модалка остаётся, родитель сохраняется)
  • Подтверждение удаления «Подтвердите удаление»
  • Dataverse cree9_tables, фильтр cree9_objectid eq '{objectId}'; без objectId — все работы
  • Навигационное свойство родителя определяется через EntityDefinitions (fallback cree9_parentid)
  • Проект находится подъёмом по родителям объекта до непустого project

🐞 Путь объекта (#headerTitle) не выводится. 🐞 Нет проверки «начало ≤ окончание».


4. Отчёты по проекту (stroy-bot-report-list) — ?page=reports&projectId=

  • Заголовок — название проекта
  • Нет доступа — «Нет доступа» / «У вас нет прав на просмотр отчётов данного проекта.»
  • Период по умолчанию — текущая неделя Пн–Вс; «С:», «По:», «Применить»
  • Поиск «Поиск по любому полю...» (дата, номер, автор, компания, путь объекта, комментарий)
  • Карточка «Отправить отчёт» (если edit0 || aproove)
  • Карточки: #номер, дата, «✓ Утверждено», Пользователь, Компания (или «ID: n»), Объект (путь), Комментарий
  • Копировать (edit0 || aproove) → форма отчёта с copyFromId
  • Удалить (aproove, или edit0 и не утверждён) → «Удалить отчёт?» → мягкое удаление cree9_deleted = true
  • Клик / Enter / Space по карточке → форма отчёта с reportId
  • Сортировка: дата desc, номер desc
  • Пусто — «Отчёты не найдены за выбранный период.»
  • Права: «Доступы» user/Id + project/Id (lookup)

🐞 Нет пагинации Dataverse (@odata.nextLink не читается). 🐞 Неделя по умолчанию считается в UTC.

Сознательные отличия от report-list:

  • Карточка отчёта и «Создать на основе» открывают раздел «Форма отчёта» этого приложения (?page=report&projectId=&reportId= / &copyFromId=) — по-прежнему в новой вкладке. «Создать на основе» теперь настоящая ссылка (работает Ctrl+клик / средняя кнопка).
  • «Отправить отчёт» — переход в раздел формы отчёта в этой же вкладке (раньше SendReport.aspx?projectId= тоже в этой вкладке).
  • Списки читаются с сайта из настройки «URL сайта SharePoint» (по умолчанию …/sites/stroy), а не с сайта, где стоит web part. Id пользователя — по-прежнему legacyPageContext.userId.
  • Тёмная тема (onThemeChanged) и загрузка шрифтов не переносились: шрифты грузит оболочка, оформление — ui-kit.
  • Модалка удаления — Dialog из ui-kit (тексты те же, номер отчёта жирным).

5. Форма отчёта (stroy-bot-report) — ?page=report&projectId=[&reportId=|&copyFromId=]

Режимы

  • Нет projectId — «Не указан projectId в URL»
  • reportId: NoAccess / Approve (aproove && !approved) / Edit (edit0 && !approved && дней < editDays, по умолчанию 5) / ReadOnly
  • copyFromId: Create, предзаполнены объект, компания, смена, комментарий, работы (с персоналом/техникой), персонал, техника; дата — сегодня, номер — 0, фото — пусто
  • Без параметров — Create, смена — первая из «Смены», дата — сегодня

Поля

  • «← Вернуться назад»
  • Заголовок «Отчет №N» / «Новый отчет», «Проект: …», бейдж «Утверждён» / «На утверждении»
  • Дата отчета*, Смена*, Объект* (пикер с путями; смена объекта очищает работы), Компания* (с фильтром по «Компании по проекту», fallback — все)
  • Работы: «+ Выбрать работу» («Сначала выберите объект»), Значение, План-факт (П / Ф / +/-, цвет), Комментарий
  • Вложенные «Персонал на работе» / «Техника на работе»: Кол-во, Часы, «+ Добавить профессию / технику»
  • Персонал: Кол-во, Часы, Тип часов (работы / простоя), Комментарий (обязателен для простоя)
  • Техника: то же + «Часы ремонта» (комментарий обязателен для простоя и ремонта)
  • Общий комментарий (contentEditable), Enter не отправляет форму SharePoint
  • Фото: «Открыть папку с фото», «Добавить фото» (multiple), превью с удалением

Пикер

  • Поиск, группы для техники («Фильтры», чипы, «Все»), drill-down для работ (крошки «Все работы › …», «Выбрать «X»», «Нет работ на этом уровне»)
  • Создание новой техники и новой работы (Ед. изм., План, Родительская работа)

Сохранение

  • Валидация (все сообщения из исходника)
  • Номер = количество неудалённых отчётов проекта + 1
  • При правке — удалить все старые строки (сначала дочерние), создать заново
  • Фото → DocLib/{projectId}_{number}
  • «Отчет успешно сохранен!», кнопка меняется на «← Вернуться назад»
  • Утверждение: подтверждение → cree9_aprooved = true → «Отчет утвержден!»
  • Права: «Доступы» projectId + userId* (несколько вариантов имени поля)

🐞 Create без проверки NoAccess. 🐞 Номер может совпасть после мягкого удаления. 🐞 Дата «сегодня» — UTC.


6. Импорт работ (stroy-bot-import-works) — ?page=import&projectId=

  • «ИМПОРТ РАБОТ» + название проекта
  • Нет projectId — «Не указан projectId в URL»; нет UniqueId0 — «Проект не привязан к справочнику проектов. Обратитесь к администратору.»
  • Загрузка работ из API постранично по 200 с прогрессом «Загрузка работ... X из Y»
  • Выбор объекта (модалка с поиском, сброс ×)
  • Таблица: чекбокс, #, Наименование (+ бейдж объекта, «✓ импортировано», подзаголовок), Код СНБ, Ед. изм., Объём
  • Поиск с debounce 250 мс по всем полям; пагинация по 100
  • «Выделить всё на странице» / «Снять все на странице» (фактически — все отфильтрованные)
  • Уже импортированные — неактивны, «Уже импортировано»
  • «Импортировать» (нужен объект, иначе тост) и «Импорт с созданием объекта» (поиск/создание объекта и раздела по названиям из сметы)
  • Подсказка «Объекты из сметы:» (3 строки + «ещё N»)
  • Тосты «✓ Успешно импортировано: N» / «Импортировано: N, ошибок: M»
  • Защита от дублей по cree9_comment = id работы из сметы

🐞 Пагинация в модалке объектов не выводится (видны только первые 100).

Сознательные отличия от import-works:

  • Адрес Dataverse берётся из настройки «URL Dataverse», адрес сайта со списками — из «URL сайта SharePoint», API — из «Базовый URL API» (значения по умолчанию прежние; раньше Dataverse был захардкожен).
  • Если объект выбрать до окончания загрузки работ, загрузка продолжается (раньше перерисовка показывала «Работы не найдены», пока загрузка не заканчивалась).
  • Клик по ячейке с флажком вне самого флажка строку не переключает (флажок ui-kit обёрнут в label — иначе было бы двойное переключение). Клик по остальной части строки работает как раньше.
  • Тосты при нескольких подряд показываются стопкой, а не друг поверх друга.
  • Окно «Выбор объекта» — Dialog из ui-kit (у крестика появилась подпись «Закрыть»); поле «Объект» доступно с клавиатуры.

7. Ресурсы проекта (stroy-bot-project-assigner) — ?page=resources&projectId=

  • Нет projectId — «Проект не найден. Откройте страницу через список проектов.»
  • Вкладки «Профессии (N)» и «Компании (N)»
  • Слева «Доступные»: поиск, очистка ✕, «уже добавлено», «+», «Ничего не найдено» / «Нет элементов»
  • Справа «Привязаны к проекту»: «−», «Ничего не добавлено»
  • Оптимистичное добавление / удаление с откатом и ошибкой
  • На узких экранах колонки друг под другом

🐞 Удаление ещё не сохранённой записи (временный отрицательный id) уходит на сервер, падает и откатывается с ошибкой. 🐞 Если запись удалили до ответа на добавление, она пропадает из списка, хотя на сервере создана. 🐞 Два добавления в одну миллисекунду получают одинаковый временный id.

Сознательные отличия от assigner:

  • PnPjs заменён на SharePoint REST с теми же $select / $expand / $filter. Постраничная загрузка PnP (for await) → проход по @odata.nextLink / odata.nextLink до конца (без $top, страницы по умолчанию — 100).
  • Добавление: POST …/items с тем же телом (Title, projectIdId, professionIdId / companyIdId), заголовки Accept и Content-Type: application/json;odata=nometadata + пустой odata-version (как в binder). Удаление: POST …/items({id}) с X-HTTP-Method: DELETE и IF-MATCH: * — так удаляет сам PnP.
  • Текст после префикса ошибки («Ошибка загрузки данных: …» и т.п.) — сообщение HttpError ({status} {statusText}: {ответ сервера}), а не формат ошибки PnP.
  • Счётчик во вкладке — часть подписи («Профессии (0)»): count в Tabs ui-kit скрывает ноль.
  • Кнопки «+» / «−» — Button ui-kit (с подсказками «Добавить» / «Удалить»), сообщение об ошибке закрывается крестиком рядом с Alert.

8. Привязка к справочнику (stroy-bot-projects-binder) — ?page=binder&projectId=

  • Карточка проекта: название, Описание, Адрес, Статус (цвет), Начало, Окончание, Координаты
  • Бейдж «Привязан» / «Не привязан», UniqueName · UniqueId0, «Отвязать»
  • Поиск по справочнику (debounce 400 мс, пустой — все), пагинация ← / →, «{page} / {total} · всего: N»
  • Карточки результатов: name, Код, Описание, Адрес, Координаты; выбор / снятие выбора; «Связать»
  • Сообщения успеха и ошибок
  • Запись UniqueId0 и UniqueName в «Проекты»

🐞 Ответы поиска не сверяются с последним запросом — при быстром вводе может победить устаревший. 🐞 У результата без полей latitude/longitude выводится «Координаты: undefined, undefined» (проверка только на null).

Сознательные отличия от binder:

  • Сайт списка «Проекты» — из общей настройки «URL сайта SharePoint» (было отдельное свойство listSiteUrl, значение по умолчанию то же), адрес API — из «Базовый URL API».
  • У поля поиска своя кнопка очистки ✕ («Очистить») вместо встроенной в Fluent SearchBox; очистка по Esc не переносилась.
  • Сообщения успеха / ошибки сохранения закрываются крестиком рядом с Alert (у Alert ui-kit нет закрытия).
  • Статус, «Привязан» / «Не привязан» — Badge ui-kit тех же цветов; кнопка «Связать» — primary ui-kit вместо принудительно синей Fluent.

Открытые вопросы

  • Схема списка «Доступы»: в репозиториях встречаются и поля projectId/userId, и lookup user/project. Проверить на сайте.
  • Схема списка «Объекты»: родитель читается то как parentId, то как parent/Id. Проверить на сайте.