feat: перестановка ждущих шагов маршрута в поданных заявках на подбор и приём

Алтынай Бултурукова (право canEditRoute) просила менять порядок участников
уже поданной заявки. Стрелки у шагов согласования и исполнения: меняются
местами только соседние шаги одной фазы, оба «Ждут очереди» — активный и
пройденные шаги не трогаются, поэтому никто не теряет заявку и оповещать
некого. Строки перечитываются перед записью, StepOrder пишется одним батчем.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Тимур Городецкий
2026-10-08 12:39:10 +05:00
parent 962d8f9c5c
commit cfe97dc67e
11 changed files with 357 additions and 23 deletions
@@ -32,7 +32,9 @@ import {
canEditAllFields,
REQUEST_STATUS,
IMatrixRow,
ApproverDecision
ApproverDecision,
queuedStepMoveRule,
useQueuedStepMove
} from '../../shared/approval';
import { navigate } from '../../shared/routing';
import { effectiveUserEmail } from '../../shared/impersonation';
@@ -267,6 +269,15 @@ const RecruitingHireView: React.FC<IRecruitingHireViewProps> = ({ context, itemI
.catch((e) => setRouteError(friendlyErrorMessage(e)));
};
// Поменять местами ждущие шаги (согласующих или исполнителей) — то же право
// routeEditRights.ts; правило «только среди ждущих» — см. queuedStepMove.ts.
const handleMoveStep = useQueuedStepMove(
approverService,
itemId,
() => setDecisionSeq((n) => n + 1),
setRouteError
);
useEffect(() => {
let alive = true;
setLoading(true);
@@ -385,6 +396,8 @@ const RecruitingHireView: React.FC<IRecruitingHireViewProps> = ({ context, itemI
search={(q) => approval.searchUsers(q)}
canRemoveRow={canRemoveApprover ? (row) => row.status === REQUEST_STATUS.queued : undefined}
onRemoveRow={handleRemoveApprover}
canMoveRow={canRemoveApprover ? queuedStepMoveRule(approvers.rows) : undefined}
onMoveRow={handleMoveStep}
/>
{!executors.loading && executors.rows.length > 0 && (
@@ -394,6 +407,8 @@ const RecruitingHireView: React.FC<IRecruitingHireViewProps> = ({ context, itemI
title="Исполнение"
roleLabel="Исполнитель"
emptyText="Исполнители не назначены."
canMoveRow={canRemoveApprover ? queuedStepMoveRule(executors.rows) : undefined}
onMoveRow={handleMoveStep}
/>
)}
</div>
@@ -31,7 +31,9 @@ import {
IMatrixRow,
ISiteUser,
APPROVAL_PHASE,
ApproverDecision
ApproverDecision,
queuedStepMoveRule,
useQueuedStepMove
} from '../../shared/approval';
import { getAttachments } from '../../shared/attachments/attachments.service';
import { useAttachmentEditor } from '../../shared/attachments/useAttachmentEditor';
@@ -256,6 +258,15 @@ const RecruitingSelectionView: React.FC<IRecruitingSelectionViewProps> = ({ cont
.catch((e) => setRouteError(friendlyErrorMessage(e)));
};
// Поменять местами ждущие шаги (согласующих или исполнителей) — то же право
// routeEditRights.ts; правило «только среди ждущих» — см. queuedStepMove.ts.
const handleMoveStep = useQueuedStepMove(
approverService,
itemId,
() => setDecisionSeq((n) => n + 1),
setRouteError
);
useEffect(() => {
let alive = true;
setLoading(true);
@@ -358,6 +369,8 @@ const RecruitingSelectionView: React.FC<IRecruitingSelectionViewProps> = ({ cont
search={(q) => approval.searchUsers(q)}
canRemoveRow={canRemoveApprover ? (row) => row.status === REQUEST_STATUS.queued : undefined}
onRemoveRow={handleRemoveApprover}
canMoveRow={canRemoveApprover ? queuedStepMoveRule(approvers.rows) : undefined}
onMoveRow={handleMoveStep}
/>
{!executors.loading && executors.rows.length > 0 && (
@@ -373,6 +386,8 @@ const RecruitingSelectionView: React.FC<IRecruitingSelectionViewProps> = ({ cont
canRemoveApprover ? (row) => isPendingExecutor(row) && pendingExecutorsCount > 1 : undefined
}
onRemoveRow={handleRemoveExecutor}
canMoveRow={canRemoveApprover ? queuedStepMoveRule(executors.rows) : undefined}
onMoveRow={handleMoveStep}
/>
)}
</div>