Sla (#11)
Tests / backend-test (push) Successful in 1m50s
Tests / frontend-test (push) Successful in 49s

Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz>
Reviewed-on: http://git.tnsu.kz/gorbenko.pavel/tnsu-approval-route/pulls/11
This commit was merged in pull request #11.
This commit is contained in:
2026-08-13 04:44:08 +00:00
parent 7f2aabbf29
commit d076f4e98d
103 changed files with 2606 additions and 1265 deletions
@@ -366,21 +366,40 @@ export const ManageRequestsScreen: React.FC<IManageRequestsScreenProps> = ({ api
// загруженного справочника сотрудников (тот же источник, что подставляет
// должность, — см. ManageRequestsApproverModal), чтобы не делать лишний
// запрос только за отображаемым именем.
const saveModal = (email: string, position: string): Promise<void> => {
const saveModal = (
email: string,
position: string,
// eslint-disable-next-line @rushstack/no-new-null -- null = «не задано», работает SLA типа заявки
slaHours: number | null,
// eslint-disable-next-line @rushstack/no-new-null -- см. выше
askOverdueReason: boolean | null
): Promise<void> => {
if (!matrix || !modal) return Promise.resolve();
setSaveError(undefined);
const rowId = modal.id;
// Два запроса последовательно, а не параллельно: оба правят ОДНУ строку
// SharePoint, и параллельные update по одному элементу списка могут
// затереть друг друга (последний выигрывает).
return requestManage
.reassignApprover(matrix, rowId, email, position)
.then(() => requestManage.updateRowSla(matrix, rowId, slaHours, askOverdueReason))
.then(() => {
const found = findEmployeeByEmail(employees.items, email);
setMatrix(prev =>
prev ? patchRow(prev, rowId, { approverEmail: email, approverName: found?.fio || email, position }) : prev
prev
? patchRow(prev, rowId, {
approverEmail: email,
approverName: found?.fio || email,
position,
slaHours,
askOverdueReason
})
: prev
);
closeModal();
})
.catch((e: Error) => {
setSaveError(reportError(e, 'saveModal: reassignApprover'));
setSaveError(reportError(e, 'saveModal: reassignApprover/updateRowSla'));
throw e;
});
};
@@ -421,10 +440,15 @@ export const ManageRequestsScreen: React.FC<IManageRequestsScreenProps> = ({ api
position,
status: initialStatus,
date: '',
dateIso: '',
comment: '',
order,
allowReassign: false,
phase
phase,
// Шаг, добавленный вручную, маршрутом-шаблоном не порождён и своего SLA не
// имеет — работает SLA типа заявки, пока его не зададут отдельно.
slaHours: null,
askOverdueReason: null
};
const previous = matrix;
@@ -544,6 +568,9 @@ export const ManageRequestsScreen: React.FC<IManageRequestsScreenProps> = ({ api
<ManageRequestsPhaseTable
title={texts.manageRequests.approvalPhaseTitle}
rows={approvalRows}
allRows={matrix ? matrix.rows : []}
requestCreatedIso={matrix ? matrix.request.created : ''}
requestTypeSlaHours={requestType?.slaHours ?? null}
readOnly={readOnly}
requestRejected={requestRejected}
statusVariant={statusVariant}
@@ -560,6 +587,9 @@ export const ManageRequestsScreen: React.FC<IManageRequestsScreenProps> = ({ api
<ManageRequestsPhaseTable
title={texts.manageRequests.executionPhaseTitle}
rows={executionRows}
allRows={matrix ? matrix.rows : []}
requestCreatedIso={matrix ? matrix.request.created : ''}
requestTypeSlaHours={requestType?.slaHours ?? null}
readOnly={readOnly}
requestRejected={requestRejected}
statusVariant={statusVariant}
@@ -582,6 +612,8 @@ export const ManageRequestsScreen: React.FC<IManageRequestsScreenProps> = ({ api
<ManageRequestsApproverModal
title={texts.manageRequests.reassignTitle}
initialEmail={modal.approverEmail}
initialSlaHours={modal.slaHours}
initialAskOverdueReason={modal.askOverdueReason}
employees={employees.items}
employeesLoading={employees.isLoading}
excludedEmails={reassignExcludedEmails}