[as frontend] 체크리스트 조회시 cklstResultList로 리스트로 처리 하도록 수정

This commit is contained in:
2026-06-02 13:17:09 +09:00
parent aa831141f6
commit 77fd719f96
3 changed files with 30 additions and 53 deletions
@@ -103,7 +103,6 @@ export interface InstlSncnApplySaveRequestDto {
bzScaleList: InstlSncnBzScaleSaveDto[];
bizCstList: InstlSncnBizCstSaveRequestDto[];
cklstResultList: InstlSncnCklstResultSaveDto[];
cklstResultMap?: Record<string, InstlSncnCklstResultSaveDto[]>;
esntlAtchFileList: InstlSncnEsntlAtchFileSaveDto[];
}
@@ -113,7 +112,7 @@ export interface InstlSncnMngDetResponse {
detDto: InstlSncnMng;
bizSclList: any[];
bizCstList: any[];
cklstResultMap?: Record<ReviewStepCode, any[]>;
cklstResultList?: any[];
esntlFileList: any[];
etcFileList: any[];
}
@@ -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<ReviewStepCode>();
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<string, any[]> = {};
// 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<HTMLInputElement>(
`input[name="chk_${invalidKey}"][value="Y"]`,
);
yesRadio?.focus();
alert(`${invalidYesRow.rvwCn} 검토내용은 예(Y)여야 합니다.`);
// 라디오 그룹의 첫 입력에 포커스(예:Y 또는 아니오:N)
const firstRadio = invalidRowEl?.querySelector<HTMLInputElement>(`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<string, any[]> } = {
// 저장 시에는 `cklstResultList`만 전송합니다 (map 필드 제거)
const payload: InstlSncnApplySaveRequestDto = {
instlSncnMng: toInstlSncnMngPayload("SV"), // 저장 시 진행상태는 SV (임시저장)
bzScaleList: toBzScaleSaveDtoList(),
bizCstList: toBizCstSaveDtoList(),
cklstResultList: toCklstSaveDtoList(),
esntlAtchFileList: toEsntlAtchFileSaveDtoList(),
cklstResultMap: toCklstSaveDtoMap(),
};
try {
@@ -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<ReviewStepCode, any[]> = { 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";