From 9a33663f1e15918babc043c2728de149fff925fb Mon Sep 17 00:00:00 2001 From: jangwoong Date: Tue, 13 Oct 2026 15:25:42 +0900 Subject: [PATCH] =?UTF-8?q?[as=20elapr]=20=EA=B3=B5=EA=B3=B5=ED=95=98?= =?UTF-8?q?=EC=88=98=EB=8F=84=20=EC=9E=AC=EC=9B=90=ED=98=91=EC=9D=98=20?= =?UTF-8?q?=EB=B3=B5=ED=95=A9=EC=82=AC=EC=97=85(SF+SP)=20Fork-Join=20?= =?UTF-8?q?=EC=A0=84=EC=9E=90=EA=B2=B0=EC=9E=AC=20=ED=94=84=EB=A1=A0?= =?UTF-8?q?=ED=8A=B8=EC=97=94=EB=93=9C=20=EB=B3=91=ED=95=A9=EC=9E=90(ATRZ0?= =?UTF-8?q?4)=20=EA=B2=B0=EC=9E=AC=20=EB=B2=84=ED=8A=BC=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99=20=EB=B0=8F=20=EC=88=9C=EC=B0=A8=EA=B2=B0=EC=9E=AC=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../elapr/model/elapr-forkjoin-types.ts | 2 +- .../elapr/ui/elapr-approval-dialog.tsx | 2 +- .../ui/elapr-doc-prgrs-forkjoin-step.tsx | 2 +- .../elapr/ui/elapr-forkjoin-picker-grid.tsx | 4 +- .../pages/fnrs-cnsltn/lib/button-config.ts | 139 +++++++++++++++++- .../ui/components/floating-action-buttons.tsx | 21 ++- 6 files changed, 158 insertions(+), 12 deletions(-) diff --git a/apps/as/src/entities/elapr/model/elapr-forkjoin-types.ts b/apps/as/src/entities/elapr/model/elapr-forkjoin-types.ts index 766ca3675..584a28c43 100644 --- a/apps/as/src/entities/elapr/model/elapr-forkjoin-types.ts +++ b/apps/as/src/entities/elapr/model/elapr-forkjoin-types.ts @@ -17,7 +17,7 @@ export type AtrzSeComCd = | "ATRZ02" // 검토 (실무 기술검토) | "ATRZ03" // 승인 (부서 승인 / 병합에 회신) | "ATRZ04" // 병합 (정책총괄 AI 병합의견 종합 심의 - 10-12 신설) - | "ATRZ99"; // 최종승인(회신) (환경청에 최종 회신) + | "ATRZ99"; // 최종승인(회신) (환경청에 회신) /** 브랜치 구분코드 */ export type BranchSeCd = "MAIN" | "PIPE" | "FACILITY" | "TOTAL_AGG"; diff --git a/apps/as/src/entities/elapr/ui/elapr-approval-dialog.tsx b/apps/as/src/entities/elapr/ui/elapr-approval-dialog.tsx index e38b8e961..9e8751b63 100644 --- a/apps/as/src/entities/elapr/ui/elapr-approval-dialog.tsx +++ b/apps/as/src/entities/elapr/ui/elapr-approval-dialog.tsx @@ -123,7 +123,7 @@ function ElaprApprovalDialog({ const approveButtonLabel = useMemo(() => { if (!isForkJoin) return "결재"; - if (branchSeCd === "TOTAL_AGG" && isLastInBranch) return "결재 (환경청에 최종회신)"; + if (branchSeCd === "TOTAL_AGG" && isLastInBranch) return "결재 (환경청에 회신)"; if (branchSeCd === "TOTAL_AGG") return "결재 (병합)"; if ((branchSeCd === "PIPE" || branchSeCd === "FACILITY") && isLastInBranch) return "결재 (병합에 회신)"; diff --git a/apps/as/src/entities/elapr/ui/elapr-doc-prgrs-forkjoin-step.tsx b/apps/as/src/entities/elapr/ui/elapr-doc-prgrs-forkjoin-step.tsx index 571e56668..7620d008f 100644 --- a/apps/as/src/entities/elapr/ui/elapr-doc-prgrs-forkjoin-step.tsx +++ b/apps/as/src/entities/elapr/ui/elapr-doc-prgrs-forkjoin-step.tsx @@ -200,7 +200,7 @@ export function ElaprDocPrgrsForkJoinStep({ steps }: ElaprDocPrgrsForkJoinStepPr - {/* Stage 3: 정책총괄부 종합취합 및 환경청 최종회신 트랙 */} + {/* Stage 3: 정책총괄부 종합취합 및 환경청 회신 트랙 */}
diff --git a/apps/as/src/entities/elapr/ui/elapr-forkjoin-picker-grid.tsx b/apps/as/src/entities/elapr/ui/elapr-forkjoin-picker-grid.tsx index 7aa92cde0..6f7428b4c 100644 --- a/apps/as/src/entities/elapr/ui/elapr-forkjoin-picker-grid.tsx +++ b/apps/as/src/entities/elapr/ui/elapr-forkjoin-picker-grid.tsx @@ -58,7 +58,7 @@ const TRACK_CONFIGS: TrackConfig[] = [ key: "TOTAL_AGG", label: "정책부 종합병합", defaultDeptNm: "정책총괄부", - guideText: "ℹ️ 두 부서(관로+시설) Join 완료 후 정책취합 및 환경청 최종회신 단계로 진행됩니다.", + guideText: "ℹ️ 두 부서(관로+시설) Join 완료 후 정책취합 및 환경청 회신 단계로 진행됩니다.", minCount: 2, maxCount: 5, }, @@ -107,7 +107,7 @@ export function ElaprForkJoinPickerGrid({ ], TOTAL_AGG: [ createDefaultRow("TOTAL_AGG", "정책총괄부", 1, "ATRZ04"), // 병합 (10-12 신설) - createDefaultRow("TOTAL_AGG", "정책총괄부", 2, "ATRZ99"), // 최종승인/전결 (환경청 최종회신) + createDefaultRow("TOTAL_AGG", "정책총괄부", 2, "ATRZ99"), // 최종승인/전결 (환경청 회신) ], })); diff --git a/apps/as/src/pages/fnrs-cnsltn/lib/button-config.ts b/apps/as/src/pages/fnrs-cnsltn/lib/button-config.ts index 243f72b27..d72503d1c 100644 --- a/apps/as/src/pages/fnrs-cnsltn/lib/button-config.ts +++ b/apps/as/src/pages/fnrs-cnsltn/lib/button-config.ts @@ -188,16 +188,149 @@ const ROLE_USER01_ATRZ_STATUS_ACTIONS: Record< }, }; +/** + * Fork-Join (병렬 결재 6인 체제: 관로2 + 시설2 + 정책병합2) 전용 상태별 버튼 액션 매핑 + * - Stage 2: 관로/시설 실무검토자(ATRZ02), 부서승인자(ATRZ03) + * - Stage 3: 정책총괄 종합병합자(ATRZ04), 최종회신자(ATRZ99) + * - 일반 순차 결재 라인과 분리하여 독립적으로 관리 + */ +const ROLE_USER01_FORK_JOIN_ATRZ_STATUS_ACTIONS: Record< + string, + Record +> = { + ER: { + ATRZ01: [ + FNRS_CNSLTN_VIEW_BUTTON_ACTION.fileSplmnt, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.fileVrfc, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.dsgntRvwr, + ], + }, + SL: { + ATRZ01: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ02: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ03: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ04: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ99: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + }, + TE: { + ATRZ01: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ02: [ + FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocCrt, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwRsltInq, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwStrg, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwCmptn, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.dmndSplmnt, + ], + ATRZ03: [ + FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.atrzAprv, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.atrzRply, + ], + ATRZ04: [ + FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwRsltInq, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.atrzAprv, + ], + ATRZ99: [ + FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.atrzRply, + ], + }, + TP: { + ATRZ01: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ02: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ03: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ04: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ99: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + }, + RB: { + ATRZ01: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ02: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ03: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ04: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ99: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + }, + RA: { + ATRZ01: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ02: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ03: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ04: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ99: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + }, + AV: { + ATRZ01: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ02: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ03: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ04: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ99: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + }, + EV: { + ATRZ01: [ + FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.dsgntRvwr, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.costVrfc, + ], + ATRZ02: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ03: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ04: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ99: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + }, + RK: { + ATRZ01: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ02: [ + FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwCmptn, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.dmndSplmnt, + ], + ATRZ03: [ + FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.atrzAprv, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.atrzRply, + ], + ATRZ04: [ + FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.atrzAprv, + ], + ATRZ99: [ + FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.atrzRply, + ], + }, + RR: { + ATRZ01: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ02: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ03: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ04: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ99: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + }, + EP: { + ATRZ01: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ02: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ03: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ04: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ99: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + }, + TA: { + ATRZ01: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ02: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ03: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ04: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + ATRZ99: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng], + }, +}; + function resolveButtonConfigs(actions: FnrsCnsltnViewButtonAction[]): FnrsCnsltnViewButtonAction[] { return actions.map((action) => FNRS_CNSLTN_VIEW_BUTTON_ACTION[action]); } -interface GetButtonConfigsOptions { +export interface GetButtonConfigsOptions { hasPendingApprovalRequest?: boolean; hasCompletedDraft?: boolean; canRetryFileVrfc?: boolean; canRetryDraftGen?: boolean; canRetryCostVrfc?: boolean; + isForkJoin?: boolean; } const PENDING_APPROVAL_ACTIONS = new Set([ @@ -214,7 +347,9 @@ function resolveRoleUser01AtrzButtonActions( atrzSeComCdList: string[], options?: GetButtonConfigsOptions, ): FnrsCnsltnViewButtonAction[] { - const atrzStatusMap = ROLE_USER01_ATRZ_STATUS_ACTIONS[prgrsSttsComCd]; + const atrzStatusMap = options?.isForkJoin + ? ROLE_USER01_FORK_JOIN_ATRZ_STATUS_ACTIONS[prgrsSttsComCd] + : ROLE_USER01_ATRZ_STATUS_ACTIONS[prgrsSttsComCd]; if (!atrzStatusMap || atrzSeComCdList.length === 0) return []; const seen = new Set(); diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/components/floating-action-buttons.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/components/floating-action-buttons.tsx index 28f23264d..90b27befa 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/components/floating-action-buttons.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/components/floating-action-buttons.tsx @@ -194,6 +194,15 @@ function FloatingActionButtons({ const normalizedTaskSeComCd = normalizeTaskSeComCdForElapr(data.detDto?.taskSeComCd); const isForkJoin = isForkJoinTask(data.detDto?.taskSeComCd); + const effectiveAtrzSeComCdList = useMemo(() => { + if (isForkJoin && userPendingAtrzSeComCd) { + if (!atrzSeComCdList.includes(userPendingAtrzSeComCd)) { + return [...atrzSeComCdList, userPendingAtrzSeComCd]; + } + } + return atrzSeComCdList; + }, [atrzSeComCdList, isForkJoin, userPendingAtrzSeComCd]); + const viewOnlyPwrkCnsltnDoc = prgrsSttsComCd === "SL" || (prgrsSttsComCd === "RB" && dtlInstCd !== "ROLE_USER05") || @@ -204,23 +213,25 @@ function FloatingActionButtons({ if (needsElaprPrgrsCheck && isElaprDocPrgrsLoading) return []; if (needsPwrkDraftCheck && isPwrkCnsltnLoading) return []; if (needsAiAutoLatest && isAiAutoLatestLoading) return []; - return getButtonConfigs(dtlInstCd, prgrsSttsComCd, atrzSeComCdList, { + return getButtonConfigs(dtlInstCd, prgrsSttsComCd, effectiveAtrzSeComCdList, { hasPendingApprovalRequest: needsElaprPrgrsCheck ? hasPendingApprovalRequest : undefined, hasCompletedDraft: needsPwrkDraftCheck ? hasCompletedDraft : undefined, canRetryFileVrfc: needsAiAutoLatest ? canRetryFileVrfc : undefined, canRetryDraftGen: needsAiAutoLatest ? canRetryDraftGen : undefined, canRetryCostVrfc: needsAiAutoLatest ? canRetryCostVrfc : undefined, + isForkJoin, }); }, [ - atrzSeComCdList, canRetryCostVrfc, canRetryDraftGen, canRetryFileVrfc, dtlInstCd, + effectiveAtrzSeComCdList, hasCompletedDraft, hasPendingApprovalRequest, isAiAutoLatestLoading, isElaprDocPrgrsLoading, + isForkJoin, isPwrkCnsltnLoading, isRvwLoginUserLoading, needsAiAutoLatest, @@ -234,7 +245,7 @@ function FloatingActionButtons({ isRegister && actionButtons.some((button) => button === FNRS_CNSLTN_VIEW_BUTTON_ACTION.strg); const canDmndSplmntInPwrkCnsltnDoc = prgrsSttsComCd === "RK" && - atrzSeComCdList.includes("ATRZ02") && + effectiveAtrzSeComCdList.includes("ATRZ02") && actionButtons.some((button) => button === FNRS_CNSLTN_VIEW_BUTTON_ACTION.dmndSplmnt); function handleGoToList() { @@ -817,7 +828,7 @@ function FloatingActionButtons({ > {isForkJoin ? userPendingBranchSeCd === "TOTAL_AGG" - ? "결재 (환경청에 최종회신)" + ? "결재 (환경청에 회신)" : "결재 (병합에 회신)" : "결재(회신)"} @@ -877,7 +888,7 @@ function FloatingActionButtons({