From 77fd719f96e1fc374f32dec6f61c9a4c3e52d7a6 Mon Sep 17 00:00:00 2001 From: jangwoong Date: Tue, 2 Jun 2026 13:17:09 +0900 Subject: [PATCH] =?UTF-8?q?[as=20frontend]=20=EC=B2=B4=ED=81=AC=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=EC=8B=9C=20cklstResultL?= =?UTF-8?q?ist=EB=A1=9C=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=EB=A1=9C=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../instl-sncn/model/instl-sncn-interface.ts | 3 +- .../pages/instl-sncn/ui/cnfm-regist-page.tsx | 60 +++++-------------- .../pages/instl-sncn/ui/cnfm-view-page.tsx | 20 +++++-- 3 files changed, 30 insertions(+), 53 deletions(-) diff --git a/apps/as/src/entities/instl-sncn/model/instl-sncn-interface.ts b/apps/as/src/entities/instl-sncn/model/instl-sncn-interface.ts index 98713e4f5..e9a4060ab 100644 --- a/apps/as/src/entities/instl-sncn/model/instl-sncn-interface.ts +++ b/apps/as/src/entities/instl-sncn/model/instl-sncn-interface.ts @@ -103,7 +103,6 @@ export interface InstlSncnApplySaveRequestDto { bzScaleList: InstlSncnBzScaleSaveDto[]; bizCstList: InstlSncnBizCstSaveRequestDto[]; cklstResultList: InstlSncnCklstResultSaveDto[]; - cklstResultMap?: Record; esntlAtchFileList: InstlSncnEsntlAtchFileSaveDto[]; } @@ -113,7 +112,7 @@ export interface InstlSncnMngDetResponse { detDto: InstlSncnMng; bizSclList: any[]; bizCstList: any[]; - cklstResultMap?: Record; + cklstResultList?: any[]; esntlFileList: any[]; etcFileList: any[]; } diff --git a/apps/as/src/pages/instl-sncn/ui/cnfm-regist-page.tsx b/apps/as/src/pages/instl-sncn/ui/cnfm-regist-page.tsx index c7e9b1473..1d419096a 100644 --- a/apps/as/src/pages/instl-sncn/ui/cnfm-regist-page.tsx +++ b/apps/as/src/pages/instl-sncn/ui/cnfm-regist-page.tsx @@ -112,13 +112,9 @@ const getRoleStepCount = (dtlInstCd?: string): number => { }; const normalizeChecklistResultRows = (payload: any): any[] => { - // support new API field `cklstResultMap` while keeping backward-compatibility - const raw = payload?.cklstResultMap ?? payload?.cklstResultList; - if (Array.isArray(raw)) return raw; - if (raw && typeof raw === "object") { - return Object.values(raw).flatMap((rows) => (Array.isArray(rows) ? rows : [])); - } - return []; + // expect flat `cklstResultList` only + const raw = payload?.cklstResultList ?? []; + return Array.isArray(raw) ? raw : []; }; /* ------------------------------------------------------------------ */ @@ -156,7 +152,7 @@ function CnfmRegistPage({ initialData }: { initialData?: any } = {}) { // userInfo?.dtlInstCd 테스트 코드 ENV.ROLE_USER03 , KECO 권한 테스트용 (실제 권한에 맞게 변경 필요) const userInfo = { ...userInfo2, - dtlInstCd: "ROLE_USER02", //ROLE_USER03, ROLE_USER02, ROLE_USER01 KECO 권한 테스트용 (실제 권한에 맞게 변경 필요) + dtlInstCd: "ROLE_USER05", //ROLE_USER03, ROLE_USER02, ROLE_USER01 KECO 권한 테스트용 (실제 권한에 맞게 변경 필요) }; const alert = useAlertStore((state) => state.setMessage); const qc = useQueryClient(); @@ -798,32 +794,7 @@ function CnfmRegistPage({ initialData }: { initialData?: any } = {}) { ); }; - // 맵 형식으로 저장할 경우(업서트 처리용) - 키는 검토단계 코드(LOCAL/ENV/KECO) 사용 - const toCklstSaveDtoMap = () => { - const persistedStepCodeSet = new Set(); - normalizeChecklistResultRows(initialData).forEach((r: any) => { - const code = r?.rvwStpComCd as ReviewStepCode | undefined; - if (code && REVIEW_STEP_CODES.includes(code)) persistedStepCodeSet.add(code); - }); - visibleReviewStepCodes.forEach((code) => persistedStepCodeSet.add(code)); - - const result: Record = {}; - // only persist rows that are enabled for checkbox - const relevantRows = cklstRows.filter((row) => row.bchckUseYn === "Y"); - Array.from(persistedStepCodeSet).forEach((rvwStpComCd) => { - // use code as key (e.g., "LOCAL") and set rvwStpComCd to the same code - result[rvwStpComCd] = relevantRows.map((row) => ({ - cklstSn: row.cklstSn, - cklstArtclSn: row.cklstArtclSn, - cklstRvwSn: row.cklstRvwSn, - rvwStpComCd: rvwStpComCd, - selected: checkResults[cklstKey(row, rvwStpComCd)] ?? null, - picRvwCn: checkComments[cklstKey(row, rvwStpComCd)] ?? null, - })); - }); - - return result; - }; + // removed map-shaped payload generation — server now expects `cklstResultList` only const toEsntlAtchFileSaveDtoList = () => Object.values(requiredFiles) @@ -1014,20 +985,19 @@ function CnfmRegistPage({ initialData }: { initialData?: any } = {}) { return false; } - // 저장 시: 사용 항목(Y)은 검토 결과가 반드시 예(Y)여야 함 + // 저장 시: bchckUseYn =="Y" 일경우 검토 결과 라디오 박스가 반드시 선택되어야 함 const invalidYesRow = cklstRows.find( - (row) => row.bchckUseYn === "Y" && checkResults[cklstKey(row, editableReviewStep)] !== "Y", + (row) => row.bchckUseYn === "Y" && !checkResults[cklstKey(row, editableReviewStep)], ); if (invalidYesRow) { const invalidKey = cklstKey(invalidYesRow, editableReviewStep); - // console.warn("검토 결과가 예(Y)가 아닌 항목 발견:", invalidKey, invalidYesRow); + console.warn("검토 결과 미선택 항목 발견:", invalidKey, invalidYesRow); const invalidRowEl = cklstRowRefs.current[invalidKey]; invalidRowEl?.scrollIntoView({ behavior: "smooth", block: "center" }); - const yesRadio = invalidRowEl?.querySelector( - `input[name="chk_${invalidKey}"][value="Y"]`, - ); - yesRadio?.focus(); - alert(`${invalidYesRow.rvwCn} 검토내용은 예(Y)여야 합니다.`); + // 라디오 그룹의 첫 입력에 포커스(예:Y 또는 아니오:N) + const firstRadio = invalidRowEl?.querySelector(`input[name="chk_${invalidKey}"]`); + firstRadio?.focus(); + alert(`체크리스트 항목 "${invalidYesRow.rvwCn} 검토결과를 선택하세요.`); return false; } } @@ -1071,15 +1041,13 @@ function CnfmRegistPage({ initialData }: { initialData?: any } = {}) { async function handleSave() { if (!fnValidation("SAVE")) return; - // 저장 시에는 하위 호환성을 위해 기존 배열 필드도 함께 전송하되, - // 추가로 서버에서 upsert 처리할 수 있도록 `cklstResultMap` 형태로도 전송합니다. - const payload: InstlSncnApplySaveRequestDto & { cklstResultMap?: Record } = { + // 저장 시에는 `cklstResultList`만 전송합니다 (map 필드 제거) + const payload: InstlSncnApplySaveRequestDto = { instlSncnMng: toInstlSncnMngPayload("SV"), // 저장 시 진행상태는 SV (임시저장) bzScaleList: toBzScaleSaveDtoList(), bizCstList: toBizCstSaveDtoList(), cklstResultList: toCklstSaveDtoList(), esntlAtchFileList: toEsntlAtchFileSaveDtoList(), - cklstResultMap: toCklstSaveDtoMap(), }; try { diff --git a/apps/as/src/pages/instl-sncn/ui/cnfm-view-page.tsx b/apps/as/src/pages/instl-sncn/ui/cnfm-view-page.tsx index 62fa321d9..606c9c31c 100644 --- a/apps/as/src/pages/instl-sncn/ui/cnfm-view-page.tsx +++ b/apps/as/src/pages/instl-sncn/ui/cnfm-view-page.tsx @@ -96,11 +96,21 @@ function CnfmViewPage({ id }: { id: string }) { const det = data.detDto ?? data; const bizSclList = data.bizSclList ?? []; const bizCstList = data.bizCstList ?? []; - // prefer new API field `cklstResultMap`, fallback to legacy `cklstResultList` - const cklstResultMap = useMemo( - () => normalizeChecklistResultMap(data.cklstResultMap ?? data.cklstResultList), - [data.cklstResultMap, data.cklstResultList], - ); + // API now provides a flat checklist result list: `cklstResultList` + const rawCklst = useMemo(() => data.cklstResultList ?? [], [data.cklstResultList]); + + // ensure we have a flat array + const cklstAll = useMemo(() => (Array.isArray(rawCklst) ? (rawCklst as any[]) : []), [rawCklst]); + + // build a step-keyed map from the flat list so the rest of the view can use same access pattern + const cklstResultMap = useMemo(() => { + const base: Record = { LOCAL: [], ENV: [], KECO: [] }; + (cklstAll ?? []).forEach((row: any) => { + const step = (row?.rvwStpComCd ?? "LOCAL") as ReviewStepCode; + if (REVIEW_STEP_CODES.includes(step)) base[step].push(row); + }); + return base; + }, [cklstAll]); const esntlFileList = data.esntlFileList ?? []; const etcFileList = data.etcFileList ?? []; const currentPrgrsSttsComCd = (det.prgrsSttsComCd as string | undefined) ?? "ST";