[as frontend] 체크리스트 입력 3개* 단계별 입력 *3개 전부 표시되도록 화면 수정

This commit is contained in:
2026-06-02 18:08:48 +09:00
parent b2282accb1
commit 56f52194a6
2 changed files with 64 additions and 5 deletions
@@ -24,6 +24,8 @@ export interface CklstTreeNode {
rvwCn: string;
bchckUseYn: "Y" | "N";
sortSeq: number;
// optional: backend may include the taskSeComCd per node
taskSeComCd?: string;
}
export interface CklstTreeSearchParams {
@@ -111,6 +111,13 @@ const getRoleStepCount = (dtlInstCd?: string): number => {
return mapped ? REVIEW_STEP_ROLE_RANK[mapped] : 0;
};
// 업무구분 코드 -> 표시명 매핑 (체크리스트 그룹 표시용)
const TASK_CODE_LABELS: Record<string, string> = {
RE: "재이용수",
SP: "하수관로",
SF: "하수처리시설",
};
const normalizeChecklistResultRows = (payload: any): any[] => {
// expect flat `cklstResultList` only
const raw = payload?.cklstResultList ?? [];
@@ -175,6 +182,7 @@ function CnfmRegistPage({ initialData }: { initialData?: any } = {}) {
return visibleReviewStepCodes.includes(step) ? step : null;
}, [isModifyMode, userInfo?.dtlInstCd, visibleReviewStepCodes]);
const [selectedReviewStep, setSelectedReviewStep] = useState<ReviewStepCode>("LOCAL");
useEffect(() => {
if (visibleReviewStepCodes.length === 0) return;
@@ -245,6 +253,14 @@ function CnfmRegistPage({ initialData }: { initialData?: any } = {}) {
staleTime: Infinity,
});
// selected task code for header filtering (null = none selected)
const [selectedTaskCode, setSelectedTaskCode] = useState<string | null>(null);
const [requiredFiles, setRequiredFiles] = useState<Record<RequiredAttachKey, RequiredAttachItem>>({});
const requiredFileInputRefs = useRef<Record<RequiredAttachKey, HTMLInputElement | null>>({});
@@ -608,6 +624,7 @@ function CnfmRegistPage({ initialData }: { initialData?: any } = {}) {
const { data: cklstTree = [] } = useQuery<CklstTreeNode[]>({
queryKey: ["cklstTree", docSeComCd, taskSeComCd],
queryFn: async () => {
console.log("Fetching checklist tree with", { docSeComCd, taskSeComCd });
// support comma-separated task codes (e.g. "RE,SP") by splitting
const raw = taskSeComCd ?? "";
const codes = raw
@@ -639,7 +656,9 @@ function CnfmRegistPage({ initialData }: { initialData?: any } = {}) {
// bchckUseYn 필터 제거: 부모가 있는 자식 노드(bchckUseYn=N)도 트리에 포함
// cklstTree 내의 피드 taskSeComCd 에 따라서 그룹핑 되도록 해줘
const useRows = cklstTree;
const useRows = selectedTaskCode
? cklstTree.filter((r) => (r as any).taskSeComCd === selectedTaskCode)
: cklstTree;
if (useRows.length === 0) return [] as CklstTreeNode[];
// artcl 단위로 분리하지 않고 전체 rows에서 단일 childMap을 구성한다.
@@ -673,7 +692,26 @@ function CnfmRegistPage({ initialData }: { initialData?: any } = {}) {
walk(null);
return ordered;
}, [cklstTree]);
}, [cklstTree, selectedTaskCode]);
// when cklstTree changes, default selectedTaskCode to first available code
useEffect(() => {
const codes = Array.from(
new Set((cklstTree ?? []).map((r) => (r as any).taskSeComCd).filter(Boolean)),
);
console.log("Available taskSeComCd codes in checklist tree:", codes);
if (codes.length === 0) {
if (selectedTaskCode !== null) setSelectedTaskCode(null);
return;
}
// if there's no selection yet, or the current selection is not in the new codes,
// pick the first available code
if (!selectedTaskCode || !codes.includes(selectedTaskCode)) {
setSelectedTaskCode(codes[0]);
}
}, [cklstTree, selectedTaskCode]);
const cklstKey = (row: CklstTreeNode, rvwStpComCd?: string) => {
const stepCode = rvwStpComCd ?? DEFAULT_RVW_STP_COM_CD;
@@ -800,6 +838,7 @@ function CnfmRegistPage({ initialData }: { initialData?: any } = {}) {
return { total, completed, percent };
}, [cklstRows, checkResults, selectedReviewStep]);
const toCklstSaveDtoList = () =>
{
const persistedStepCodeSet = new Set<ReviewStepCode>();
@@ -1506,9 +1545,27 @@ function CnfmRegistPage({ initialData }: { initialData?: any } = {}) {
<div className="cnfm-card p-4">
<div className="mb-3 flex items-center justify-between">
<h3 className="text-[1.5rem] font-bold text-gray-700">체크리스트</h3>
<button className="cnfm-btn-search text-[1.2rem]">
{(cklstRows[0]?.cklstNm ?? taskSeComCdNm) + `(${cklstStats.completed}/${cklstStats.total})`}
</button>
<div className="flex gap-2 items-center">
{Array.from(new Set((cklstTree ?? []).map((r) => (r as any).taskSeComCd).filter(Boolean))).map(
(code) => {
const name = TASK_CODE_LABELS[code] ?? code;
const active = selectedTaskCode === code;
return (
<button
key={code}
type="button"
className={`rounded border px-3 py-1 text-[1.2rem] font-semibold ${
active ? "border-blue-500 bg-blue-50 text-blue-700" : "border-gray-300 bg-white text-gray-600"
}`}
onClick={() => setSelectedTaskCode(code)}
>
{name}
</button>
);
},
)}
<div className="text-[1.1rem] text-gray-500">{`(${cklstStats.completed}/${cklstStats.total})`}</div>
</div>
</div>
<div className="mb-3 flex flex-wrap gap-2">
{visibleReviewStepCodes.length === 0 ? (