feat: wire report form page, remove section placeholder

All eight sections of the old stroy-bot-* web parts are now reachable
from the app. Unknown routes fall back to the hub.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Тимур Городецкий
2026-10-16 10:08:07 +05:00
parent 072d246eed
commit 92b578d806
5 changed files with 27 additions and 22 deletions
+6
View File
@@ -190,6 +190,12 @@
🐞 Create без проверки NoAccess. 🐞 Номер может совпасть после мягкого удаления. 🐞 Дата «сегодня» — UTC. 🐞 Create без проверки NoAccess. 🐞 Номер может совпасть после мягкого удаления. 🐞 Дата «сегодня» — UTC.
**Сознательные отличия от report:**
- `confirm('Утвердить отчет? …')` → `confirmModal` из ui-kit, текст тот же.
- Адрес сайта для списков и ссылки на папку фото — настройка «URL сайта SharePoint» (раньше `pageContext.web.absoluteUrl`); путь папки при создании — по-прежнему от сайта, где стоит страница (`web.serverRelativeUrl`). По умолчанию это один и тот же сайт `/sites/stroy`.
- Если Dataverse ответит на создание записи `204` без тела, старый код брал id из заголовка `OData-EntityId`, а новый клиент заголовков не видит. С `Prefer: return=representation` Dataverse отвечает `201` с телом, поэтому на практике не встречается; при пустом id сохранение останавливается той же ошибкой `reportId is empty`.
- Логика режимов, валидации, сборки строк и порядка записи / удаления сверена со старым кодом построчно и покрыта тестами (45 тестов).
--- ---
## 6. Импорт работ (stroy-bot-import-works) — `?page=import&projectId=` ## 6. Импорт работ (stroy-bot-import-works) — `?page=import&projectId=`
+18 -17
View File
@@ -1,34 +1,35 @@
import * as React from 'react'; import * as React from 'react';
import { useRouter } from './routing/RouterContext'; import { useRouter } from './routing/RouterContext';
import { SectionPlaceholder } from './SectionPlaceholder';
import { HubPage } from '../pages/hub/HubPage'; import { HubPage } from '../pages/hub/HubPage';
import { ResourcesPage } from '../pages/resources/ResourcesPage';
import { BinderPage } from '../pages/binder/BinderPage';
import { ReportsPage } from '../pages/reports/ReportsPage';
import { ImportWorksPage } from '../pages/import/ImportWorksPage';
import { ObjectsPage } from '../pages/objects/ObjectsPage'; import { ObjectsPage } from '../pages/objects/ObjectsPage';
import { WorksPage } from '../pages/works/WorksPage'; import { WorksPage } from '../pages/works/WorksPage';
import { ReportsPage } from '../pages/reports/ReportsPage';
import { ReportPage } from '../pages/report/ReportPage';
import { ImportWorksPage } from '../pages/import/ImportWorksPage';
import { ResourcesPage } from '../pages/resources/ResourcesPage';
import { BinderPage } from '../pages/binder/BinderPage';
/** Показывает страницу текущего раздела. Разделы подключаются сюда по мере переноса. */ /** Показывает страницу текущего раздела (раздел берётся из ?page=). */
export const PageOutlet: React.FC = () => { export const PageOutlet: React.FC = () => {
const { route } = useRouter(); const { route } = useRouter();
switch (route.page) { switch (route.page) {
case 'hub':
return <HubPage />;
case 'resources':
return <ResourcesPage />;
case 'binder':
return <BinderPage />;
case 'reports':
return <ReportsPage />;
case 'import':
return <ImportWorksPage />;
case 'objects': case 'objects':
return <ObjectsPage />; return <ObjectsPage />;
case 'works': case 'works':
return <WorksPage />; return <WorksPage />;
case 'reports':
return <ReportsPage />;
case 'report':
return <ReportPage />;
case 'import':
return <ImportWorksPage />;
case 'resources':
return <ResourcesPage />;
case 'binder':
return <BinderPage />;
case 'hub':
default: default:
return <SectionPlaceholder />; return <HubPage />;
} }
}; };
+1 -3
View File
@@ -1,9 +1,7 @@
// Тексты оболочки приложения. Тексты разделов лежат рядом с разделами (pages/*/texts.ts). // Тексты оболочки приложения. Тексты разделов лежат рядом с разделами (pages/*/texts.ts).
export const APP_TEXTS = { export const APP_TEXTS = {
brand: 'StroyBot', brand: 'StroyBot'
sectionInProgress: 'Раздел переносится из старого web part',
sectionInProgressHint: 'Пока пользуйтесь старой страницей раздела.'
} as const; } as const;
export const PROPERTY_PANE_TEXTS = { export const PROPERTY_PANE_TEXTS = {
@@ -53,7 +53,7 @@ export const ReportForm: React.FC<Props> = ({ params }) => {
} }
const isSaved = !!report.successMessage; const isSaved = !!report.successMessage;
const runAsync = (action: () => Promise<void>) => (): void => { const runAsync = (action: () => Promise<void>): (() => void) => (): void => {
action().catch(() => undefined); action().catch(() => undefined);
}; };
@@ -23,7 +23,7 @@ const stopPropagation = (event: React.KeyboardEvent): void => event.stopPropagat
export const PickerCreateForm: React.FC<Props> = ({ create, requiresUnit, nestedInTitle, parentCandidates }) => { export const PickerCreateForm: React.FC<Props> = ({ create, requiresUnit, nestedInTitle, parentCandidates }) => {
const { draft, isCreating, errorMessage } = create; const { draft, isCreating, errorMessage } = create;
const change = const change =
(field: keyof IPickerCreateDraft) => (field: keyof IPickerCreateDraft): ((value: string) => void) =>
(value: string): void => (value: string): void =>
create.changeDraft({ [field]: value }); create.changeDraft({ [field]: value });