новая статусная модель
This commit is contained in:
@@ -1,6 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import type { WebPartContext } from '@microsoft/sp-webpart-base';
|
||||
import { getLinkedTypes, getOwnRequests, getPendingApproval } from './myRequests.service';
|
||||
import {
|
||||
filterPendingByQuery,
|
||||
getLinkedTypes,
|
||||
getOwnRequests,
|
||||
getPendingApproval,
|
||||
getPendingApprovalIndex,
|
||||
hydratePendingAuthors,
|
||||
} from './myRequests.service';
|
||||
import { IMyRequest } from './models/IMyRequest';
|
||||
import { REQUEST_STATUS, IRequestType } from '../../shared/approval';
|
||||
import { onRequestsChanged } from '../../shared/requestEvents';
|
||||
@@ -166,15 +173,24 @@ const Table: React.FC<{
|
||||
</div>
|
||||
);
|
||||
|
||||
// Раздел с таблицей заявок: поиск по названию + постраничный вывод.
|
||||
// Раздел с таблицей заявок: поиск по всем полям + вывод постранично.
|
||||
//
|
||||
// 'approvals' грузится целиком (алгоритм «чья сейчас очередь» требует видеть
|
||||
// весь маршрут заявки), поиск и пагинация — на клиенте.
|
||||
// 'own' идёт постранично через обычный REST (см. myRequests.service), поиск —
|
||||
// серверным OData-фильтром по Title.
|
||||
// 'own' — Search сразу с пагинацией (RowLimit/StartRow): автор = текущий
|
||||
// пользователь + текст запроса по всем полям.
|
||||
//
|
||||
// 'approvals' без поиска — обычный REST, отфильтрованный по ApproverId(+Status),
|
||||
// страница нарезается на СЕРВЕРЕ (getPendingApproval сам берёт нужный срез
|
||||
// индекса и дочитывает автора только для него).
|
||||
// 'approvals' с поиском — область расширяется до «где пользователь вообще есть
|
||||
// в маршруте» (getPendingApprovalIndex, ПОЛНЫЙ список без автора — постранично
|
||||
// его тут не взять, т.к. фильтрация текстом идёт уже после), затем текстовый
|
||||
// фильтр через Search (filterPendingByQuery), и только после фильтрации —
|
||||
// нарезка на страницы и дочитывание автора (hydratePendingAuthors) — тоже
|
||||
// только для видимой страницы, а не для всего отфильтрованного набора.
|
||||
//
|
||||
// Оба варианта перечитываются по событию rp-requests-changed
|
||||
// (shared/requestEvents) — после создания заявки и после решения согласующего.
|
||||
// Поиск идёт по индексу, поэтому свежая правка может проявиться не мгновенно.
|
||||
export const RequestsTableView: React.FC<IRequestsTableViewProps> = ({ context, kind, onOpen }) => {
|
||||
const [types, setTypes] = React.useState<IRequestType[] | undefined>(undefined);
|
||||
const [rows, setRows] = React.useState<IMyRequest[] | undefined>(undefined);
|
||||
@@ -203,44 +219,63 @@ export const RequestsTableView: React.FC<IRequestsTableViewProps> = ({ context,
|
||||
if (types === undefined) return;
|
||||
let cancelled = false;
|
||||
|
||||
const load = (): void => {
|
||||
const request =
|
||||
kind === 'own'
|
||||
? getOwnRequests(context, types, query, page).then((r) => ({ rows: r.rows, total: r.totalRows }))
|
||||
: getPendingApproval(context).then((r) => {
|
||||
console.table(r.debug);
|
||||
return { rows: r.rows, total: r.rows.length };
|
||||
});
|
||||
|
||||
request
|
||||
.then((result) => {
|
||||
const load = async (): Promise<void> => {
|
||||
try {
|
||||
if (kind === 'own') {
|
||||
const r = await getOwnRequests(context, types, query, page);
|
||||
if (cancelled) return;
|
||||
setRows(result.rows);
|
||||
setTotalRows(result.total);
|
||||
setRows(r.rows);
|
||||
setTotalRows(r.totalRows);
|
||||
setError('');
|
||||
})
|
||||
.catch((e) => {
|
||||
if (!cancelled) setError((e as Error).message);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const hasQuery = query.trim().length > 0;
|
||||
|
||||
if (!hasQuery) {
|
||||
// Без поиска — страница уже нарезана и дочитана сервером.
|
||||
const r = await getPendingApproval(context, 'myTurn', page);
|
||||
if (cancelled) return;
|
||||
console.table(r.debug);
|
||||
setRows(r.rows);
|
||||
setTotalRows(r.totalRows);
|
||||
setError('');
|
||||
return;
|
||||
}
|
||||
|
||||
// С поиском — сначала полный список «где я есть в маршруте» (без
|
||||
// автора), фильтруем текстом, и только потом режем на страницы и
|
||||
// дочитываем автора — только для видимой страницы.
|
||||
const idx = await getPendingApprovalIndex(context, 'involved');
|
||||
if (cancelled) return;
|
||||
console.table(idx.debug);
|
||||
const filtered = await filterPendingByQuery(context, idx.types, idx.rows, query);
|
||||
if (cancelled) return;
|
||||
const pageRows = filtered.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);
|
||||
await hydratePendingAuthors(context, idx.types, pageRows);
|
||||
if (cancelled) return;
|
||||
setRows(pageRows);
|
||||
setTotalRows(filtered.length);
|
||||
setError('');
|
||||
} catch (e) {
|
||||
if (!cancelled) setError((e as Error).message);
|
||||
}
|
||||
};
|
||||
|
||||
load();
|
||||
const unsubscribe = onRequestsChanged(load);
|
||||
load().catch(() => undefined); // ошибки уже обработаны внутри load через setError
|
||||
const unsubscribe = onRequestsChanged(() => {
|
||||
load().catch(() => undefined);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
unsubscribe();
|
||||
};
|
||||
}, [context, types, kind, query, page]);
|
||||
|
||||
// 'approvals' приходит целиком — фильтруем и нарезаем страницы на клиенте.
|
||||
const q = query.trim().toLowerCase();
|
||||
let visible = rows || [];
|
||||
let total = totalRows;
|
||||
if (kind === 'approvals' && rows) {
|
||||
const filtered = q ? rows.filter((r) => r.title.toLowerCase().indexOf(q) !== -1) : rows;
|
||||
total = filtered.length;
|
||||
visible = filtered.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);
|
||||
}
|
||||
// Обе ветки теперь отдают уже нарезанную страницу — просто показываем как есть.
|
||||
const q = query.trim();
|
||||
const total = totalRows;
|
||||
const visible = rows || [];
|
||||
const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));
|
||||
|
||||
const heading = kind === 'approvals' ? 'Ждут вашего решения' : 'Мои заявки';
|
||||
@@ -255,7 +290,7 @@ export const RequestsTableView: React.FC<IRequestsTableViewProps> = ({ context,
|
||||
<input
|
||||
type="search"
|
||||
className={styles.search}
|
||||
placeholder="Поиск по названию…"
|
||||
placeholder="Поиск по заявкам…"
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user