Project object tree with add/edit/delete, drag & drop re-parenting, context menu and Excel export, 1:1 with the legacy web part. The tree widgets live in pages/_tree so the works page can reuse them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
28 KiB
Чек-лист миграции 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) + Dataversehttps://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
🐞 Перетаскивание меняет только родителя, порядок не сохраняется. 🐞 Удаление узла не удаляет детей. 🐞 Зона сброса «внутрь» нулевой высоты — на практике узел можно бросить только «до» / «после» (перенесено как есть).
Сознательные отличия от objects:
alert()→ сообщениеAlertна странице (ошибки переноса, удаления, экспорта) или в модалке (ошибка сохранения). Тексты те же.confirm()удаления →confirmModalс кнопкой «Удалить»; текст тот же, но переносы строк ui-kit показывает одним абзацем.- Адрес сайта — настройка «URL сайта SharePoint» (раньше
pageContext.web.absoluteUrl), экспорт — через клиент корпоративного API и настройку «Базовый URL API» (по умолчанию тот жеhttps://api.tnsu.kz).
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(fallbackcree9_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=/©FromId=) — по-прежнему в новой вкладке. «Создать на основе» теперь настоящая ссылка (работает 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=|©FromId=]
Режимы
- Нет projectId — «Не указан projectId в URL»
reportId: NoAccess / Approve (aproove && !approved) / Edit (edit0 && !approved && дней < editDays, по умолчанию 5) / ReadOnlycopyFromId: 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 скрывает ноль. - Кнопки «+» / «−» —
Buttonui-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(уAlertui-kit нет закрытия). - Статус, «Привязан» / «Не привязан» —
Badgeui-kit тех же цветов; кнопка «Связать» —primaryui-kit вместо принудительно синей Fluent.
Открытые вопросы
- Схема списка «Доступы»: в репозиториях встречаются и поля
projectId/userId, и lookupuser/project. Проверить на сайте. - Схема списка «Объекты»: родитель читается то как
parentId, то какparent/Id. Проверить на сайте.