Архивация списков (#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
@@ -20,6 +20,53 @@ export interface IApiResponse<T> {
data: T;
}
/**
* Одна партиция серии списков типа заявок.
*
* Списки заявок нумерованные: когда в текущем наборе элементов достигается
* порог, approval-route создаёт следующую пару списков («<Форма> <N> — данные» +
* «<Форма> <N> — согласование»), а прежняя пара остаётся архивом только для
* чтения. Писать нужно в партицию с isCurrent, а ИСКАТЬ — по всем сразу,
* иначе заявки из архива просто исчезнут из выдачи.
*/
export interface IListPartition {
/** Номер партиции в серии, с 1. */
number: number;
/** Абсолютный URL сайта, где живёт список этой партиции. */
siteUrl: string;
/** Серверный относительный URL списка данных — та же семантика, что у IRequestType.listId. */
listId: string;
/** Title списка данных на момент создания. null, если approval-route не смог его прочитать. */
title?: string;
/** true — в эту партицию идёт запись новых заявок. Ровно одна в серии. */
isCurrent: boolean;
createdAt: string;
}
// GET /api/request-types/lists → { data: IRequestTypeListSeries[] }
// Серии сразу по всем типам заявок: одним запросом, а не по одному на тип.
export interface IRequestTypeListSeries {
requestTypeId: number;
partitions: IListPartition[];
}
/**
* GET /api/request-types/{id}/lists/current → { data: ICurrentListPartition }
*
* Партиция, в которую нужно писать НОВУЮ заявку. Не чистый GET на бэкенде —
* этот же запрос проверяет порог элементов и при необходимости ставит в
* очередь создание следующей партиции, поэтому его нужно звать перед КАЖДЫМ
* созданием заявки, а не читать из кеша (см. shared/approval/listPartitions.ts,
* который для этого не подходит — грузится один раз при onInit).
*/
export interface ICurrentListPartition {
requestTypeId: number;
number: number;
siteUrl: string;
listId: string;
title?: string;
}
// Условие видимости шага маршрута (настраивается в админке маршрутов —
// «Показывать шаг только при выполнении условия», сравнение «Поле списка»/
// «Оператор»/«Значение»). fieldName — внутреннее имя колонки списка (напр.
@@ -41,11 +88,14 @@ export interface IResolvedRoute {
export interface IResolvedRouteStep {
id: number;
orderNo: number;
// eslint-disable-next-line @rushstack/no-new-null -- зеркалит nullable-контракт бэкенда
executorEmail: string | null;
// eslint-disable-next-line @rushstack/no-new-null -- см. executorEmail
position: string | null;
allowReassignToOtherApprovers: boolean;
// Предупреждение маршрута по этому шагу (например, роль не разрешилась в
// конкретного человека) — приходит от API, показываем автору при желании.
// eslint-disable-next-line @rushstack/no-new-null -- см. executorEmail
warning: string | null;
// SLA шага в часах и признак опроса причины просрочки. Наследование
// «шаг перекрывает тип заявки» бэкенд уже применил — здесь итоговое значение,
@@ -31,4 +31,16 @@ export interface IMatrixRow {
slaHours?: number | null;
// eslint-disable-next-line @rushstack/no-new-null -- см. slaHours
askOverdueReason?: boolean | null;
/**
* Предупреждение approval-route по ЭТОМУ шагу (см. IResolvedRouteStep.warning) —
* например, роль не разрешилась в конкретного человека, и почему именно.
* Только у ПЛАНИРУЕМОГО маршрута (см. getPlannedMatrix): у уже записанных строк
* (loadXxxRequest) неоткуда взяться — резолв запускался один раз при подаче.
*
* findUnassignedStep показывает его вместо своего общего текста, когда он есть —
* бэкенд знает причину точнее («роль не назначена в справочнике отдела» и т.п.),
* а не просто «нет назначенного человека».
*/
// eslint-disable-next-line @rushstack/no-new-null -- зеркалит nullable-контракт бэкенда (IResolvedRouteStep.warning)
warning?: string | null;
}