diff --git a/src/webparts/requestPortal/features/myRequests/RequestsTableView.tsx b/src/webparts/requestPortal/features/myRequests/RequestsTableView.tsx index abf5aaf..a6ab522 100644 --- a/src/webparts/requestPortal/features/myRequests/RequestsTableView.tsx +++ b/src/webparts/requestPortal/features/myRequests/RequestsTableView.tsx @@ -16,6 +16,7 @@ import { IMyRequest } from './models/IMyRequest'; import { DataTable, IDataTableColumn } from '@tnsu/ui-kit'; import { REQUEST_STATUS, REQUEST_STATUS_CHOICES, IRequestType, StatusBadge } from '../../shared/approval'; import { onRequestsChanged } from '../../shared/requestEvents'; +import { traced } from './perfTrace'; import { useRoutedQuery } from '../../shared/usePersistedQuery'; import { IPage, pageKey, pageSignature, readPage, writePage } from './pageCache'; import { Pager } from './Pager'; @@ -394,7 +395,7 @@ export const RequestsTableView: React.FC = ({ const load = async (forceRender = false): Promise => { try { - const fresh = await fetchPage(); + const fresh = await traced(`ИТОГО таблица:${view}#${page}`, fetchPage); if (cancelled) return; // Перерисовываем, только если данные реально изменились — иначе при diff --git a/src/webparts/requestPortal/features/myRequests/SearchView.tsx b/src/webparts/requestPortal/features/myRequests/SearchView.tsx index 4b9cabf..3125a3b 100644 --- a/src/webparts/requestPortal/features/myRequests/SearchView.tsx +++ b/src/webparts/requestPortal/features/myRequests/SearchView.tsx @@ -4,6 +4,7 @@ import { getLinkedTypes, hydratePendingAuthors, searchMyRequests } from './myReq import { IMyRequest } from './models/IMyRequest'; import { IRequestType, StatusBadge } from '../../shared/approval'; import { onRequestsChanged } from '../../shared/requestEvents'; +import { traced } from './perfTrace'; import { useRoutedQuery } from '../../shared/usePersistedQuery'; import { pageKey, pageSignature, readPage, writePage } from './pageCache'; import { Pager } from './Pager'; @@ -184,7 +185,7 @@ export const SearchView: React.FC = ({ const load = async (forceRender = false): Promise => { try { - const fresh = await searchMyRequests(context, types, debouncedQuery); + const fresh = await traced('ИТОГО поиск', () => searchMyRequests(context, types, debouncedQuery)); if (cancelled) return; applyKnownAuthors(fresh.rows, authors.current); const prev = readPage(cacheKey); diff --git a/src/webparts/requestPortal/features/myRequests/myRequests.service.ts b/src/webparts/requestPortal/features/myRequests/myRequests.service.ts index f3de05a..a34c168 100644 --- a/src/webparts/requestPortal/features/myRequests/myRequests.service.ts +++ b/src/webparts/requestPortal/features/myRequests/myRequests.service.ts @@ -232,6 +232,10 @@ export interface INavCounts { // своим запросом — но параллельно, и ровно тем же вызовом, что наполняет саму // вкладку: иначе счётчик и список могли бы разойтись. export async function getNavCounts(context: WebPartContext): Promise { + return traced('ИТОГО getNavCounts', () => getNavCountsInner(context)); +} + +async function getNavCountsInner(context: WebPartContext): Promise { const types = await getLinkedTypes(context); const [turn, rework] = await Promise.all([ @@ -352,8 +356,15 @@ async function indexApprovalsForPartition( const sp = spForSite(target.siteUrl, context); const list = sp.web.getList(target.listId); - const dataListInfo = await traced(`dataListTitle:${type.name}#${target.listId}`, () => list.select('Title')()); - const dataTitle = (dataListInfo.Title as string) || ''; + // target.title — из bulk-ответа /api/request-types/lists, уже загруженного + // при старте страницы (см. IListTarget.title): 11 партиций — это 11 лишних + // REST-запросов только за тем, что уже пришло бесплатно одним запросом. + // REST — только если реестр партиций был недоступен (see listTargetsForType), + // тогда Title взять больше неоткуда. + const dataTitle = + target.title ?? + ((await traced(`dataListTitle:${type.name}#${target.listId}`, () => list.select('Title')())).Title as string) ?? + ''; if (!DATA_LIST_SUFFIX.test(dataTitle)) { debug.approvalSkippedReason = `«${dataTitle}» не оканчивается на «— данные» — список согласования не ищем`; return debug; @@ -707,6 +718,14 @@ export interface IListTarget { siteUrl: string; /** Абсолютный префикс пути элементов этого списка, со слешем на конце. */ prefix: string; + /** + * Title списка данных на момент регистрации партиции (см. IListPartition.title) — + * уже пришёл бесплатно вместе с bulk-ответом /api/request-types/lists при + * старте страницы. Заполнен только когда партиция известна реестру; для + * фолбэка по RequestType.listId (реестр недоступен) — undefined, тогда + * indexApprovalsForPartition сам дочитает Title через REST. + */ + title?: string; } /** @@ -731,7 +750,8 @@ export function listTargetsForType(type: IRequestType): IListTarget[] { return partitions.map((p) => ({ listId: p.listId, siteUrl: p.siteUrl, - prefix: `${siteOrigin(p.siteUrl).replace(/\/+$/, '')}${p.listId.replace(/\/+$/, '')}/` + prefix: `${siteOrigin(p.siteUrl).replace(/\/+$/, '')}${p.listId.replace(/\/+$/, '')}/`, + title: p.title })); } diff --git a/src/webparts/requestPortal/features/myRequests/perfTrace.ts b/src/webparts/requestPortal/features/myRequests/perfTrace.ts index c3fd89c..0d78e69 100644 --- a/src/webparts/requestPortal/features/myRequests/perfTrace.ts +++ b/src/webparts/requestPortal/features/myRequests/perfTrace.ts @@ -3,20 +3,15 @@ * (индекс согласований, свои заявки по типу, конкретная партиция, Search), * а не просто «страница долго грузится». * - * Включение/выключение БЕЗ пересборки — из консоли браузера: - * window.rpPerfTrace = true // писать тайминги - * window.rpPerfTrace = false // выключить (по умолчанию) + * ВРЕМЕННО: пишет всегда, без флага-переключателя — этот модуль и все traced() + * вокруг снимаются целиком, как только разбор текущей проблемы с + * производительностью закончится (см. myRequests.service.ts, RequestsTableView.tsx, + * SearchView.tsx). */ -function enabled(): boolean { - return !!(window as unknown as { rpPerfTrace?: boolean }).rpPerfTrace; -} - const PREFIX = '[Перф]'; -/** Оборачивает async-шаг замером времени. Без включённой трассировки — просто вызывает fn(). */ +/** Оборачивает async-шаг замером времени. */ export async function traced(label: string, fn: () => Promise): Promise { - if (!enabled()) return fn(); - const start = performance.now(); try { return await fn();