feat: port stroy-bot-report-list as reports page

Moves the project report list into the app as ?page=reports&projectId=.
Behaviour is kept 1:1 with the legacy web part: the same «Доступы» query
(lookup user/Id + project/Id), «Компании»/«Объекты» reference loads,
Dataverse cree9_reports query with the date filter and ordering, client
side sort and search, permission rules for send/copy/delete, soft delete
via PATCH cree9_deleted=true and all user-facing texts.

Known bugs are ported as-is (no Dataverse paging, default week in UTC).
Links to SendReport.aspx now point to the in-app report form route; the
report and copy links still open in a new tab. Deviations are listed in
docs/MIGRATION-CHECKLIST.md section 4.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Тимур Городецкий
2026-10-15 17:16:01 +05:00
parent 660e059842
commit 704d5ce329
16 changed files with 1558 additions and 0 deletions
@@ -0,0 +1,45 @@
// Тексты раздела «Отчёты по проекту» — дословно из stroy-bot-report-list.
export const REPORTS_TEXTS = {
loading: 'Загрузка...',
accessDeniedTitle: 'Нет доступа',
accessDeniedText: 'У вас нет прав на просмотр отчётов данного проекта.',
dateFrom: 'С:',
dateTo: 'По:',
apply: 'Применить',
searchPlaceholder: 'Поиск по любому полю...',
sendReport: { title: 'Отправить отчёт', subtitle: 'Отправка отчёта по проекту' },
empty: 'Отчёты не найдены за выбранный период.',
card: {
approved: 'Утверждено',
user: 'Пользователь:',
company: 'Компания:',
object: 'Объект:',
comment: 'Комментарий:',
open: (number: number): string => `Открыть отчёт #${number}`,
copyTitle: (number: number): string => `Создать на основе отчёта #${number}`,
copyAriaLabel: 'Создать на основе',
deleteTitle: (number: number): string => `Удалить отчёт #${number}`,
deleteAriaLabel: 'Удалить отчёт'
},
deleteDialog: {
title: 'Удалить отчёт?',
// «Отчёт <strong>#N</strong> от DATE будет удален.» — номер выделяется жирным.
textBefore: 'Отчёт ',
textNumber: (number: number): string => `#${number}`,
textAfter: (date: string): string => ` от ${date} будет удален.`,
textUnknown: 'Отчёт будет удален.',
cancel: 'Отмена',
confirm: 'Удалить'
},
errors: {
init: (message: string): string => `Ошибка инициализации: ${message}`,
load: (message: string): string => `Ошибка загрузки: ${message}`,
delete: (message: string): string => `Ошибка удаления: ${message}`,
dataverse: (status: number, details: string): string => `Dataverse error: ${status} ${details}`
},
/** Пустые значения в карточке. */
noValue: '—',
// Значение подставляется как есть: пустой id из Dataverse даёт «ID: null» — как в старом web part.
idFallback: (id: unknown): string => `ID: ${id}`
} as const;