diff --git a/apps/as/src/entities/fnrs-cnsltn/api/fnrs-cnsltn-service.ts b/apps/as/src/entities/fnrs-cnsltn/api/fnrs-cnsltn-service.ts index a74838d49..585008dc4 100644 --- a/apps/as/src/entities/fnrs-cnsltn/api/fnrs-cnsltn-service.ts +++ b/apps/as/src/entities/fnrs-cnsltn/api/fnrs-cnsltn-service.ts @@ -68,6 +68,14 @@ async function requestActnReview(docNo: string, body: EnvoActnReviewReqDto): Pro return response.data; } +async function tbActnRqst(docNo: string): Promise { + await client.put(`${BASE}/${docNo}/tb-actn-rqst`); +} + +async function tbSubmitActnResult(docNo: string, body: { isSubmit: boolean }): Promise { + await client.post(`${BASE}/${docNo}/tb-submit-actn-result`, body); +} + async function submitSupplement(id: string, body: FnrsCnsltnApplySaveRequestDto): Promise { const response = await client.post(`${BASE}/${id}/submitsupplement`, body); return response.data; @@ -111,6 +119,8 @@ export const fnrsCnsltnMngService = { techReview, requestExamine, requestActnReview, + tbActnRqst, + tbSubmitActnResult, submitSupplement, submitReqSupplement, remove, diff --git a/apps/as/src/entities/fnrs-cnsltn/model/fnrs-cnsltn-interface.ts b/apps/as/src/entities/fnrs-cnsltn/model/fnrs-cnsltn-interface.ts index adfd40338..a5e289137 100644 --- a/apps/as/src/entities/fnrs-cnsltn/model/fnrs-cnsltn-interface.ts +++ b/apps/as/src/entities/fnrs-cnsltn/model/fnrs-cnsltn-interface.ts @@ -140,6 +140,8 @@ export interface FnrsCnsltnMngService { techReview(id: string, body: FnrsCnsltnApplySaveRequestDto): Promise; requestExamine(docNo: string, body: FnrsExamineRequestDto): Promise; requestActnReview(docNo: string, body: EnvoActnReviewReqDto): Promise; + tbActnRqst(docNo: string): Promise; + tbSubmitActnResult(docNo: string, body: { isSubmit: boolean }): Promise; submitSupplement(id: string, body: FnrsCnsltnApplySaveRequestDto): Promise; submitReqSupplement(id: string, body: FnrsCnsltnApplySaveRequestDto): Promise; remove(fnrsCnsltnDocNo: string): Promise; 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 222adb75c..244dcdc9a 100644 --- a/apps/as/src/pages/fnrs-cnsltn/lib/button-config.ts +++ b/apps/as/src/pages/fnrs-cnsltn/lib/button-config.ts @@ -22,6 +22,8 @@ export const FNRS_CNSLTN_VIEW_BUTTON_ACTION = { actnCmptn: "actnCmptn", rqstActnRvw: "rqstActnRvw", costVrfc: "costVrfc", + tbActnRqst: "tbActnRqst", + tbSubmitActnRslt: "tbSubmitActnRslt", } as const; export type FnrsCnsltnViewButtonAction = @@ -40,6 +42,10 @@ const ROLE_STATUS_BUTTON_ACTIONS: Record([ FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwCmptn, FNRS_CNSLTN_VIEW_BUTTON_ACTION.atrzAprv, FNRS_CNSLTN_VIEW_BUTTON_ACTION.atrzRply, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.tbActnRqst, ]); function resolveRoleUser01AtrzButtonActions( @@ -349,7 +373,10 @@ function resolveRoleUser01AtrzButtonActions( ): FnrsCnsltnViewButtonAction[] { let effectiveStatus = prgrsSttsComCd; - if (options?.isForkJoin && options?.branchSttsCd) { + // TB는 문서 단위 상태이고 결재 브랜치는 IN_PROGRESS로 남는다. TE 맵으로 되돌리지 않는다. + if (prgrsSttsComCd === "TB") { + effectiveStatus = "TB"; + } else if (options?.isForkJoin && options?.branchSttsCd) { switch (options.branchSttsCd) { case "SUPPLEMENT": // 본인 브랜치가 보완요청 중이면 'SL' 맵핑 적용 (검토/승인 버튼 숨김, 실무협의서관리만 노출) @@ -415,7 +442,12 @@ function resolveRoleUser01AtrzButtonActions( } if (options?.hasCompletedDraft != null) { - if (effectiveStatus === "SL" || effectiveStatus === "TE" || effectiveStatus === "RK") { + if ( + effectiveStatus === "SL" || + effectiveStatus === "TE" || + effectiveStatus === "TB" || + effectiveStatus === "RK" + ) { if (action === FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng) { return options.hasCompletedDraft; } diff --git a/apps/as/src/pages/fnrs-cnsltn/model/view-types.ts b/apps/as/src/pages/fnrs-cnsltn/model/view-types.ts index 60e4e73f3..f5ef40035 100644 --- a/apps/as/src/pages/fnrs-cnsltn/model/view-types.ts +++ b/apps/as/src/pages/fnrs-cnsltn/model/view-types.ts @@ -82,6 +82,7 @@ export const PBADMS_PROGRESS_ORDER = [ // KECO "ER", "TE", + "TB", "EV", "RK", ] as const; 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 383f6caca..a3d52c446 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 @@ -146,10 +146,11 @@ function FloatingActionButtons({ const needsAtrzResolve = needsRoleUser01AtrzResolve(dtlInstCd, prgrsSttsComCd); const needsElaprPrgrsCheck = needsAtrzResolve && - (["TE", "RK"].includes(prgrsSttsComCd) || (isForkJoin && prgrsSttsComCd === "SL")); + (["TE", "TB", "RK"].includes(prgrsSttsComCd) || (isForkJoin && prgrsSttsComCd === "SL")); const needsPwrkDraftCheck = - dtlInstCd === "ROLE_USER01" && ["SL", "TE", "RK"].includes(prgrsSttsComCd); - const needsPwrkForRb = dtlInstCd === "ROLE_USER05" && prgrsSttsComCd === "RB"; + dtlInstCd === "ROLE_USER01" && ["SL", "TE", "TB", "RK"].includes(prgrsSttsComCd); + const needsPwrkForRb = + dtlInstCd === "ROLE_USER05" && (prgrsSttsComCd === "RB" || prgrsSttsComCd === "TB"); const needsPwrkForAiRetry = dtlInstCd === "ROLE_USER01" && ["TE", "EV"].includes(prgrsSttsComCd); const needsPwrkLookup = needsPwrkDraftCheck || needsPwrkForRb || needsPwrkForAiRetry; const needsAiAutoLatest = dtlInstCd === "ROLE_USER01" && AI_RETRY_PRGRS_STTS.has(prgrsSttsComCd); @@ -384,14 +385,22 @@ function FloatingActionButtons({ await handler({ request, data, - onSuccess: () => { + onSuccess: async () => { if (successMessage) alert(successMessage); - queryClient.invalidateQueries({ - queryKey: fnrsCnsltnKeys.detail(fnrsCnsltnDocNo), - }); - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.AS_ELAPR_SERVICE.ELAPR_BRANCH], - }); + await Promise.all([ + queryClient.invalidateQueries({ + queryKey: fnrsCnsltnKeys.detail(fnrsCnsltnDocNo), + refetchType: "all", + }), + queryClient.invalidateQueries({ + queryKey: [ENDPOINT.AS_ELAPR_SERVICE.ELAPR_BRANCH], + refetchType: "all", + }), + queryClient.invalidateQueries({ + queryKey: [ENDPOINT.AS_ELAPR_SERVICE.ELAPR_DMND], + refetchType: "all", + }), + ]); }, onError: (error) => { let errorMessage = ""; @@ -771,6 +780,37 @@ function FloatingActionButtons({ }); } + async function handleTbActnRqstClick() { + if ( + !(await confirm( + "지자체에 조치요청하시겠습니까?\n조치요청 후 7일 내 제출하지 않으면 자동으로 결재가 다음 단계로 진행됩니다.", + )) + ) { + return; + } + await executeAction({ + action: FNRS_CNSLTN_VIEW_BUTTON_ACTION.tbActnRqst, + successMessage: "조치요청이 완료되었습니다.", + }); + } + + async function handleTbSubmitActnRsltClick() { + if (!pwrkCnsltnDocNo) { + alert("실무협의 문서번호를 찾을 수 없습니다."); + return; + } + if (!(await confirm("조치결과를 제출하시겠습니까?"))) return; + + await executeAction({ + action: FNRS_CNSLTN_VIEW_BUTTON_ACTION.tbSubmitActnRslt, + request: { + pwrkCnsltnDocNo, + body: { isSubmit: true } satisfies PwrkActnResultSubmitDto, + }, + successMessage: "조치결과가 제출되었습니다.", + }); + } + function handleFileVrfcClick() { reTransferDocument.mutate( { fnrsCnsltnDocNo: docNo }, @@ -940,6 +980,17 @@ function FloatingActionButtons({ 조치 완료 )} + {shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.tbActnRqst) && + prgrsSttsComCd !== "TB" && ( + + )} + {shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.tbSubmitActnRslt) && ( + + )} {shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwRsltInq) && !isSecondApproval && !( @@ -970,6 +1021,7 @@ function FloatingActionButtons({ )} {shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.atrzAprv) && + prgrsSttsComCd !== "TB" && (!isForkJoin || (userPendingBranchSeCd === "TOTAL_AGG" && userPendingAtrzSeComCd === "ATRZ04")) && (