Архивация списков (#9)
Tests / frontend-test (push) Successful in 1m12s

Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz>
Reviewed-on: http://git.tnsu.kz/timur.gorodetskiy/request-portal/pulls/9
This commit was merged in pull request #9.
This commit is contained in:
2026-08-19 09:07:53 +00:00
parent 3a71d0df1f
commit 6f789f8f1c
67 changed files with 2617 additions and 901 deletions
@@ -1,7 +1,15 @@
import { AadHttpClient, HttpClientResponse } from '@microsoft/sp-http';
import { WebPartContext } from '@microsoft/sp-webpart-base';
import { APPROVAL_API_CLIENT_ID, APPROVAL_API_BASE_URL } from '../config';
import { ApprovalStatus, IApiResponse, IRequestType, IResolvedRoute, IResolveFieldValue } from './models/IApprovalRouteApi';
import { APPROVAL_API_CLIENT_ID, getApprovalApiBaseUrl } from '../config';
import {
ApprovalStatus,
ICurrentListPartition,
IApiResponse,
IRequestType,
IRequestTypeListSeries,
IResolvedRoute,
IResolveFieldValue
} from './models/IApprovalRouteApi';
// Все обращения к внешнему approval-route API — только здесь.
// Клиент авторизуется через AadHttpClient (SSO по текущему пользователю).
@@ -14,7 +22,7 @@ async function client(context: WebPartContext): Promise<AadHttpClient> {
export async function getRequestTypes(context: WebPartContext): Promise<IRequestType[]> {
const c = await client(context);
const response: HttpClientResponse = await c.get(
`${APPROVAL_API_BASE_URL}/api/request-types`,
`${getApprovalApiBaseUrl()}/api/request-types`,
AadHttpClient.configurations.v1
);
if (!response.ok) {
@@ -24,6 +32,53 @@ export async function getRequestTypes(context: WebPartContext): Promise<IRequest
return body.data ?? [];
}
/**
* GET /api/request-types/lists — серии партиций сразу по ВСЕМ типам заявок.
*
* Именно bulk-эндпоинт, а не по одному на тип: партиции нужны и для записи (куда
* писать), и для поиска (по каким спискам искать), причём поиск идёт сразу по
* всем типам — запрашивать серию отдельно на каждый тип означало бы N запросов
* на каждое открытие «Моих заявок».
*
* Типы без настроенного списка SharePoint в ответ не попадают.
*/
export async function getListPartitions(context: WebPartContext): Promise<IRequestTypeListSeries[]> {
const c = await client(context);
const response: HttpClientResponse = await c.get(
`${getApprovalApiBaseUrl()}/api/request-types/lists`,
AadHttpClient.configurations.v1
);
if (!response.ok) {
throw new Error(`request-types/lists: ${response.status} ${response.statusText}`);
}
const body: IApiResponse<IRequestTypeListSeries[]> = await response.json();
return body.data ?? [];
}
/**
* GET /api/request-types/{id}/lists/current — партиция, в которую нужно писать
* НОВУЮ заявку этого типа. Вызывать перед КАЖДЫМ созданием, не кешировать: этот
* запрос заодно проверяет порог элементов на бэкенде и ставит в очередь
* создание следующей партиции при достижении — без него архивация никогда не
* запустится, сколько бы заявок ни создали (bulk-эндпоинт lists, который
* портал и так грузит при onInit, только читает реестр и порог не проверяет).
*/
export async function getCurrentListPartition(
context: WebPartContext,
requestTypeId: number
): Promise<ICurrentListPartition> {
const c = await client(context);
const response: HttpClientResponse = await c.get(
`${getApprovalApiBaseUrl()}/api/request-types/${requestTypeId}/lists/current`,
AadHttpClient.configurations.v1
);
if (!response.ok) {
throw new Error(`request-types/${requestTypeId}/lists/current: ${response.status} ${response.statusText}`);
}
const body: IApiResponse<ICurrentListPartition> = await response.json();
return body.data;
}
// Кеш на уровне МОДУЛЯ (не useRef/useState) — список типов заявок админ меняет
// редко, а без кеша каждая форма и каждый переход в режим согласующего дёргали
// бы /api/request-types заново. Живёт до перезагрузки страницы — тот же принцип,
@@ -77,12 +132,14 @@ export async function resolveRoute(
requestTypeId: number,
userEmail: string,
status: ApprovalStatus = 'Approval',
// eslint-disable-next-line @rushstack/no-new-null -- null отправляется в теле запроса как «не задано», undefined в JSON.stringify пропал бы вовсе
departmentId: string | null = null,
fieldValues: IResolveFieldValue[] = [],
// eslint-disable-next-line @rushstack/no-new-null -- см. departmentId
projectId: string | null = null
): Promise<IResolvedRoute> {
const c = await client(context);
const url = `${APPROVAL_API_BASE_URL}/api/template-routes/resolve`;
const url = `${getApprovalApiBaseUrl()}/api/template-routes/resolve`;
// Эндпоинт сменился с GET (query) на POST (JSON-тело).
// projectId (uuid проекта) — для форм, где согласующий зависит от проекта