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}`;