From e85c01fca36292a3a43f9523d2b217812b08452a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=93=D0=BE=D1=80=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=D1=86=D0=BA=D0=B8=D0=B9?= Date: Mon, 5 Oct 2026 10:19:57 +0500 Subject: [PATCH] =?UTF-8?q?refactor:=20=D0=BE=D0=B1=D1=89=D0=B8=D0=B9=20?= =?UTF-8?q?=D1=85=D1=83=D0=BA=20=D0=BC=D0=B0=D1=80=D1=88=D1=80=D1=83=D1=82?= =?UTF-8?q?=D0=B0=20=D0=BF=D0=BE=20=D1=84=D0=B0=D0=B7=D0=B0=D0=BC=20(usePh?= =?UTF-8?q?asedRoute)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Хук, который грузит маршрут согласования отдельно по фазам «Согласование» и «Исполнение», был в папке «Объяснительной по СКУД». Он понадобился и заявке на командировку, поэтому перенесён в shared/approval под общим именем. Поведение СКУД не меняется — поменялись только импорты. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../forms/SkudExplanation/README.md | 2 +- .../SkudExplanation/SkudExplanationForm.tsx | 6 ++-- .../SkudExplanation/SkudExplanationView.tsx | 6 ++-- .../requestPortal/shared/approval/index.ts | 2 ++ .../approval/usePhasedRoute.ts} | 32 ++++++++++--------- 5 files changed, 26 insertions(+), 22 deletions(-) rename src/webparts/requestPortal/{forms/SkudExplanation/useSkudRoute.ts => shared/approval/usePhasedRoute.ts} (64%) diff --git a/src/webparts/requestPortal/forms/SkudExplanation/README.md b/src/webparts/requestPortal/forms/SkudExplanation/README.md index bb1d83b..6571747 100644 --- a/src/webparts/requestPortal/forms/SkudExplanation/README.md +++ b/src/webparts/requestPortal/forms/SkudExplanation/README.md @@ -10,7 +10,7 @@ ## Маршрут - Фазы: согласование (напр. непосредственный руководитель) + исполнение (напр. кадры) — обе фазы грузятся отдельными запросами `getPlannedMatrix(..., 'Approval' | 'Execution')` - в `useSkudRoute` (паттерн Referral). Без запроса 'Execution' шаг исполнения пропадает. + в общем `usePhasedRoute` (shared/approval, паттерн Referral). Без запроса 'Execution' шаг исполнения пропадает. - Планируемый маршрут резолвится по e-mail сотрудника; согласующих заявитель может править (новая / черновик / возврат), исполнителей — нет (только чтение). - Нет согласующих вовсе — `initialSubmitStatus` сразу ставит «Выполнение». diff --git a/src/webparts/requestPortal/forms/SkudExplanation/SkudExplanationForm.tsx b/src/webparts/requestPortal/forms/SkudExplanation/SkudExplanationForm.tsx index 72e9bd1..9f5c7c7 100644 --- a/src/webparts/requestPortal/forms/SkudExplanation/SkudExplanationForm.tsx +++ b/src/webparts/requestPortal/forms/SkudExplanation/SkudExplanationForm.tsx @@ -23,7 +23,8 @@ import { initialSubmitStatus, IApproverToWrite, unassignedStepMessage, - resolveWriteListId + resolveWriteListId, + usePhasedRoute } from '../../shared/approval'; import { IRequestFormProps } from '../../models/types'; import { getEmployeeOrganizations, formatEmployeeOrganizations } from '../../shared/dictionary'; @@ -43,7 +44,6 @@ import { loadSkudExplanationRequest } from './load'; import { submittedConflictMessage } from './submittedDays'; import { getSubmittedDays } from './submittedDays.service'; import { useSubmittedDays } from './useSubmittedDays'; -import { useSkudRoute } from './useSkudRoute'; import { todayDateKey } from './skud/skudDays'; import { EmployeeSection } from './sections/EmployeeSection'; import { DaysSection } from './sections/DaysSection'; @@ -126,7 +126,7 @@ const SkudExplanationForm: React.FC = ({ }, [editId, context]); // Маршрут по сотруднику; у открытой заявки — из уже записанных строк, чтобы не затереть правки. - const { approvers: matrix, executors } = useSkudRoute({ + const { approvers: matrix, executors } = usePhasedRoute({ context, approval, requestId: editId, diff --git a/src/webparts/requestPortal/forms/SkudExplanation/SkudExplanationView.tsx b/src/webparts/requestPortal/forms/SkudExplanation/SkudExplanationView.tsx index d00702b..d903c44 100644 --- a/src/webparts/requestPortal/forms/SkudExplanation/SkudExplanationView.tsx +++ b/src/webparts/requestPortal/forms/SkudExplanation/SkudExplanationView.tsx @@ -9,7 +9,8 @@ import { StatusBadge, ApprovalHistory, notifyActiveStep, - useRequestTypeId + useRequestTypeId, + usePhasedRoute } from '../../shared/approval'; import type { ISiteUser } from '../../shared/approval'; import { friendlyErrorMessage } from '../../shared/diagnostics'; @@ -23,7 +24,6 @@ import { useStableCallback } from '../../shared/useStableCallback'; import { FORM_ID, FORM_TITLE, IFormData, EMPTY_DATA, SetField } from './model'; import { loadSkudExplanationRequest } from './load'; import { skudExplanationApprovalService } from './submit'; -import { useSkudRoute } from './useSkudRoute'; import { EmployeeSection } from './sections/EmployeeSection'; import { DaysSection } from './sections/DaysSection'; import { ExplanationSection } from './sections/ExplanationSection'; @@ -65,7 +65,7 @@ const SkudExplanationView: React.FC = ({ context, ite // decisionSeq: после решения строки маршрута меняются — перечитываем обе фазы. const [decisionSeq, setDecisionSeq] = useState(0); - const { approvers, executors } = useSkudRoute({ + const { approvers, executors } = usePhasedRoute({ context, approval, requestId: itemId, diff --git a/src/webparts/requestPortal/shared/approval/index.ts b/src/webparts/requestPortal/shared/approval/index.ts index 5831fa6..2a4bcc1 100644 --- a/src/webparts/requestPortal/shared/approval/index.ts +++ b/src/webparts/requestPortal/shared/approval/index.ts @@ -63,6 +63,8 @@ export { notifyActiveStep } from './notify.service'; export type { INotifyTarget } from './notify.service'; export { useMatrixEditor, findUnassignedStep, unassignedStepMessage } from './useMatrixEditor'; export type { IUseMatrixEditor } from './useMatrixEditor'; +export { usePhasedRoute } from './usePhasedRoute'; +export type { IPhasedRoute } from './usePhasedRoute'; export { resolveApproverOrgInfo } from './approverOrgInfo'; export type { IApproverOrgInfo } from './approverOrgInfo'; export { canEditRoute } from './routeEditRights'; diff --git a/src/webparts/requestPortal/forms/SkudExplanation/useSkudRoute.ts b/src/webparts/requestPortal/shared/approval/usePhasedRoute.ts similarity index 64% rename from src/webparts/requestPortal/forms/SkudExplanation/useSkudRoute.ts rename to src/webparts/requestPortal/shared/approval/usePhasedRoute.ts index e798198..345e3c4 100644 --- a/src/webparts/requestPortal/forms/SkudExplanation/useSkudRoute.ts +++ b/src/webparts/requestPortal/shared/approval/usePhasedRoute.ts @@ -1,17 +1,15 @@ import { useCallback } from 'react'; import { WebPartContext } from '@microsoft/sp-webpart-base'; -import { - APPROVAL_PHASE, - ApprovalListService, - IMatrixRow, - getPlannedMatrix, - resolveApproverOrgInfo, - useMatrixEditor -} from '../../shared/approval'; +import { APPROVAL_PHASE } from './models/statuses'; +import { IMatrixRow } from './models/IMatrixRow'; +import { ApprovalListService } from './approvalList.service'; +import { getPlannedMatrix } from './approvalSubmission.service'; +import { resolveApproverOrgInfo } from './approverOrgInfo'; +import { useMatrixEditor } from './useMatrixEditor'; type MatrixEditor = ReturnType; -export interface ISkudRoute { +export interface IPhasedRoute { /** Согласующие — редактируемы заявителем в новой заявке / черновике / доработке. */ approvers: MatrixEditor; /** Исполнители по маршруту (фаза «Исполнение») — только чтение. */ @@ -23,6 +21,7 @@ interface IRouteSource { approval: ApprovalListService; /** Заявка уже записана — маршрут читаем из её строк, а не планируем заново. */ requestId: number | undefined; + /** E-mail сотрудника, по которому резолвится планируемый маршрут. */ employeeEmail: string; requestTypeId: number | undefined; /** Можно ли править согласующих. */ @@ -31,15 +30,18 @@ interface IRouteSource { reloadSeq?: number; } -const byPhase = (execution: boolean) => (rows: IMatrixRow[]): IMatrixRow[] => - rows.filter((r) => (r.phase === APPROVAL_PHASE.execution) === execution); +const byPhase = + (execution: boolean): ((rows: IMatrixRow[]) => IMatrixRow[]) => + (rows: IMatrixRow[]): IMatrixRow[] => + rows.filter((r) => (r.phase === APPROVAL_PHASE.execution) === execution); /** - * Маршрут объяснительной по двум фазам. Без отдельного запроса фазы - * 'Execution' getPlannedMatrix исполнителей не резолвит — шаг кадров из - * маршрута «пропадал» бы. Паттерн тот же, что в Referral. + * Маршрут заявки по двум фазам: согласование + исполнение. Без отдельного + * запроса фазы 'Execution' getPlannedMatrix исполнителей не резолвит — шаг + * исполнения из маршрута «пропадал» бы. Маршрут настраивают в админке + * approval-route, форма его только получает. */ -export function useSkudRoute(source: IRouteSource): ISkudRoute { +export function usePhasedRoute(source: IRouteSource): IPhasedRoute { const { context, approval, requestId, employeeEmail, requestTypeId, editable, reloadSeq = 0 } = source; const canLoad = !!requestId || (!!employeeEmail && !!requestTypeId); const reloadKey = `${employeeEmail}-${requestId ?? 'new'}-${reloadSeq}`;