Files
stroy-bot-monorepo/docs/MIGRATION-CHECKLIST.md
T
Тимур Городецкий e7527081b4 feat: port stroy-bot-projects-binder as binder page
Add the "binder" section (?page=binder&projectId=): links a SharePoint
project to the TNSU API project catalogue by writing UniqueId0/UniqueName
to the "Проекты" list.

The class component is rewritten as function components with hooks while
keeping the legacy behaviour and texts: the same project GET with
odata=nometadata and empty odata-version, MERGE with IF-MATCH * for
bind/unbind (no confirmation), catalogue search with 400 ms debounce and
20-per-page paging, card select/deselect, status colours, ru-RU dates and
HTML-entity decoding of the address. Fluent UI is replaced with ui-kit.

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

24 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.


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).


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. Проверить на сайте.