[as instl service]체크리스트, 공종 리스트 화면 구성
This commit is contained in:
@@ -7,6 +7,8 @@ import type {
|
||||
CklstMngSaveRequest,
|
||||
CklstMngSearchParams,
|
||||
CklstMngService,
|
||||
CklstTreeNode,
|
||||
CklstTreeSearchParams,
|
||||
} from "../model/cklst-mng-interface";
|
||||
|
||||
async function findPage(params: CklstMngSearchParams): Promise<PageResponse<CklstMng>> {
|
||||
@@ -21,6 +23,13 @@ async function findById(cklstSn: number): Promise<CklstMng> {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function findTree(params: CklstTreeSearchParams): Promise<CklstTreeNode[]> {
|
||||
const response = await client.get<CklstTreeNode[]>(`${ENDPOINT.AS_CHECKLIST_SERVICE.CKLST}/tree`, {
|
||||
params,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function create(body: CklstMngSaveRequest): Promise<number> {
|
||||
const response = await client.post<number>(ENDPOINT.AS_CHECKLIST_SERVICE.CKLST, body);
|
||||
return response.data;
|
||||
@@ -42,6 +51,7 @@ async function remove(cklstSn: number): Promise<number> {
|
||||
export const cklstMngService = {
|
||||
findPage,
|
||||
findById,
|
||||
findTree,
|
||||
create,
|
||||
update,
|
||||
remove,
|
||||
|
||||
@@ -26,4 +26,6 @@ export type {
|
||||
CklstMngSaveRequest,
|
||||
CklstMngSearchParams,
|
||||
CklstMngService,
|
||||
CklstTreeNode,
|
||||
CklstTreeSearchParams,
|
||||
} from "./model/cklst-mng-interface";
|
||||
|
||||
@@ -13,6 +13,24 @@ export interface CklstMng {
|
||||
mdfcnDt?: string;
|
||||
}
|
||||
|
||||
export interface CklstTreeNode {
|
||||
cklstSn: number;
|
||||
cklstNm: string;
|
||||
cklstArtclSn: number;
|
||||
cklstArtclNm: string;
|
||||
cklstRvwSn: number;
|
||||
upCklstRvwSn: number | null;
|
||||
treeLevel: number;
|
||||
rvwCn: string;
|
||||
bchckUseYn: "Y" | "N";
|
||||
sortSeq: number;
|
||||
}
|
||||
|
||||
export interface CklstTreeSearchParams {
|
||||
docSeComCd?: string;
|
||||
taskSeComCd?: string;
|
||||
}
|
||||
|
||||
export interface CklstMngSearchParams extends PageableParams {
|
||||
cklstNm?: string;
|
||||
taskSeComCd?: string;
|
||||
@@ -30,6 +48,7 @@ export interface CklstMngSaveRequest {
|
||||
export interface CklstMngService {
|
||||
findPage(params: CklstMngSearchParams): Promise<PageResponse<CklstMng>>;
|
||||
findById(cklstSn: number): Promise<CklstMng>;
|
||||
findTree(params: CklstTreeSearchParams): Promise<CklstTreeNode[]>;
|
||||
create(body: CklstMngSaveRequest): Promise<number>;
|
||||
update(cklstSn: number, body: CklstMngSaveRequest): Promise<number>;
|
||||
remove(cklstSn: number): Promise<number>;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import type { CnstpSeMngTreeDto, CnstpService } from "../model/cnstp-interface";
|
||||
|
||||
async function findTree(): Promise<CnstpSeMngTreeDto[]> {
|
||||
const response = await client.get<CnstpSeMngTreeDto[]>(
|
||||
`${ENDPOINT.AS_COMMON_SERVICE.CNSTP_SE}/tree`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export const cnstpService = {
|
||||
findTree,
|
||||
} satisfies CnstpService;
|
||||
@@ -0,0 +1,2 @@
|
||||
export type { CnstpSeMngTreeDto, CnstpService } from "./model/cnstp-interface";
|
||||
export { cnstpService } from "./api/cnstp-service";
|
||||
@@ -0,0 +1,23 @@
|
||||
/** 공종구분 트리 노드 DTO */
|
||||
export interface CnstpSeMngTreeDto {
|
||||
/** 들여쓰기 포함 표시명 */
|
||||
indCnstpSeCdNm: string;
|
||||
/** 공종구분코드 */
|
||||
cnstpSeCd: string;
|
||||
/** 공종구분코드명 */
|
||||
cnstpSeCdNm: string;
|
||||
/** 상위 공종구분코드 (null = 최상위) */
|
||||
upCnstpCd: string | null;
|
||||
/** 사용여부 */
|
||||
useYn: string;
|
||||
/** 트리 레벨 */
|
||||
treeLevel: number;
|
||||
/** 최상위~현재 노드까지 "|" 구분 경로 */
|
||||
treePath: string;
|
||||
}
|
||||
|
||||
/** 공종구분 서비스 인터페이스 */
|
||||
export interface CnstpService {
|
||||
/** 공종구분 트리 전체 조회 */
|
||||
findTree(): Promise<CnstpSeMngTreeDto[]>;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
export { InstlSncnApi } from "./api/instl-sncn-service";
|
||||
export {
|
||||
INSTL_SNCN_REGISTER_SELECTION_KEY,
|
||||
type InstlSncnBzScaleSaveDto,
|
||||
type InstlSncnMng,
|
||||
type InstlSncnMngSearchParams,
|
||||
type SpringPage,
|
||||
|
||||
@@ -54,6 +54,32 @@ export interface InstlSncnMng {
|
||||
rgtrNm: string;
|
||||
}
|
||||
|
||||
/** 설치인가 신청 - 사업규모 저장 DTO(배열 원소) */
|
||||
export interface InstlSncnBzScaleSaveDto {
|
||||
/** 공종구분코드 */
|
||||
cnstpSeCd: string;
|
||||
/** 기본계획(A) */
|
||||
bscPl: number;
|
||||
/** 예산반영(B) */
|
||||
budgetReflct: number;
|
||||
/** 금회설계시(C) */
|
||||
thtiDsgn: number;
|
||||
/** 증감(C-B) */
|
||||
diff: number;
|
||||
}
|
||||
|
||||
/** 설치인가 신청 - 총사업비 저장 DTO(배열 원소) */
|
||||
export interface InstlSncnBizCstSaveRequestDto {
|
||||
/** 회계구분코드 */
|
||||
acntgSeCd: string;
|
||||
/** 기본계획금액 */
|
||||
bscPlanAmt: number;
|
||||
/** 예산반영금액 */
|
||||
bgtRfltAmt: number;
|
||||
/** 금회설계금액 */
|
||||
tstmDesignAmt: number;
|
||||
}
|
||||
|
||||
export interface InstlSncnMngService {
|
||||
getPage: (params: InstlSncnMngSearchParams) => Promise<SpringPage<InstlSncnMng>>;
|
||||
getById: (id: string) => Promise<InstlSncnMng>;
|
||||
|
||||
@@ -7,7 +7,14 @@ import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import type { AcntgSeMngTreeDto } from "@/entities/acntg";
|
||||
import { acntgService } from "@/entities/acntg";
|
||||
import { INSTL_SNCN_REGISTER_SELECTION_KEY } from "@/entities/instl-sncn";
|
||||
import type { CklstTreeNode } from "@/entities/cklst";
|
||||
import { cklstMngService } from "@/entities/cklst";
|
||||
import type { CnstpSeMngTreeDto } from "@/entities/cnstp";
|
||||
import { cnstpService } from "@/entities/cnstp";
|
||||
import {
|
||||
INSTL_SNCN_REGISTER_SELECTION_KEY,
|
||||
type InstlSncnBzScaleSaveDto,
|
||||
} from "@/entities/instl-sncn";
|
||||
import { Button, Input } from "@common/components";
|
||||
|
||||
import "./cnfm-page.css";
|
||||
@@ -96,25 +103,139 @@ function CnfmRegistPage() {
|
||||
const [etcNoticeOpen, setEtcNoticeOpen] = useState(true);
|
||||
|
||||
/* ── 체크리스트 ── */
|
||||
const CHECKLIST_ITEMS = [
|
||||
"사업계획의 적정성",
|
||||
"사업규모의 적정성",
|
||||
"사업방식의 적정성",
|
||||
"사업비의 적정성",
|
||||
"사업효과의 적정성",
|
||||
"사업일정의 적정성",
|
||||
];
|
||||
const [checkResults, setCheckResults] = useState<Record<string, "Y" | "N" | "">>(() =>
|
||||
Object.fromEntries(CHECKLIST_ITEMS.map((item) => [item, "" as "Y" | "N" | ""])),
|
||||
);
|
||||
type CheckResultVal = "Y" | "N" | "";
|
||||
const [checkResults, setCheckResults] = useState<Record<string, CheckResultVal>>({});
|
||||
|
||||
/* ── 사업규모 ── */
|
||||
const [bzSpecWwtp, setBzSpecWwtp] = useState<BzScaleRow>(emptyScale()); // 처리장용량
|
||||
const [bzSpecArea, setBzSpecArea] = useState<BzScaleRow>(emptyScale()); // 면적
|
||||
const [bzSpecNcr, setBzSpecNcr] = useState<BzScaleRow>(emptyScale()); // 하수관로 신설
|
||||
const [bzSpecRplc, setBzSpecRplc] = useState<BzScaleRow>(emptyScale()); // 교체(보수)
|
||||
const [bzSpecPump, setBzSpecPump] = useState<BzScaleRow>(emptyScale()); // 펌프장
|
||||
const [bzSpecDrng, setBzSpecDrng] = useState<BzScaleRow>(emptyScale()); // 배수설비
|
||||
const { data: cklstTree = [] } = useQuery<CklstTreeNode[]>({
|
||||
queryKey: ["cklstTree", "INSTL", taskSeComCd],
|
||||
queryFn: () => cklstMngService.findTree({ docSeComCd: "INSTL", taskSeComCd }),
|
||||
enabled: Boolean(taskSeComCd),
|
||||
staleTime: Infinity,
|
||||
});
|
||||
|
||||
const cklstRows = useMemo(() => {
|
||||
// bchckUseYn 필터 제거: 부모가 있는 자식 노드(bchckUseYn=N)도 트리에 포함
|
||||
const useRows = cklstTree;
|
||||
if (useRows.length === 0) return [] as CklstTreeNode[];
|
||||
|
||||
// artcl 단위로 분리하지 않고 전체 rows에서 단일 childMap을 구성한다.
|
||||
// upCklstRvwSn이 다른 artcl 소속 노드를 부모로 가질 수 있으므로
|
||||
// artcl 경계를 넘는 부모-자식 관계도 처리해야 한다.
|
||||
const childMap = new Map<number | null, CklstTreeNode[]>();
|
||||
for (const row of useRows) {
|
||||
const parent = row.upCklstRvwSn ?? null;
|
||||
if (!childMap.has(parent)) childMap.set(parent, []);
|
||||
childMap.get(parent)!.push(row);
|
||||
}
|
||||
|
||||
for (const list of childMap.values()) {
|
||||
list.sort((a, b) =>
|
||||
a.cklstArtclSn !== b.cklstArtclSn
|
||||
? a.cklstArtclSn - b.cklstArtclSn
|
||||
: a.sortSeq !== b.sortSeq
|
||||
? a.sortSeq - b.sortSeq
|
||||
: a.cklstRvwSn - b.cklstRvwSn,
|
||||
);
|
||||
}
|
||||
|
||||
const ordered: CklstTreeNode[] = [];
|
||||
const walk = (parent: number | null) => {
|
||||
const children = childMap.get(parent) ?? [];
|
||||
for (const child of children) {
|
||||
ordered.push(child);
|
||||
walk(child.cklstRvwSn);
|
||||
}
|
||||
};
|
||||
walk(null);
|
||||
|
||||
return ordered;
|
||||
}, [cklstTree]);
|
||||
|
||||
const cklstKey = (row: CklstTreeNode) =>
|
||||
`${row.cklstSn}_${row.cklstArtclSn}_${row.cklstRvwSn}`;
|
||||
|
||||
useEffect(() => {
|
||||
setCheckResults((prev) => {
|
||||
const next: Record<string, CheckResultVal> = {};
|
||||
cklstRows.forEach((row) => {
|
||||
const key = cklstKey(row);
|
||||
next[key] = prev[key] ?? "";
|
||||
});
|
||||
return next;
|
||||
});
|
||||
}, [cklstRows]);
|
||||
|
||||
const cklstStats = useMemo(() => {
|
||||
const total = cklstRows.length;
|
||||
const completed = cklstRows.reduce((acc, row) => {
|
||||
const val = checkResults[cklstKey(row)] ?? "";
|
||||
return acc + (val ? 1 : 0);
|
||||
}, 0);
|
||||
const percent = total > 0 ? Math.round((completed / total) * 100) : 0;
|
||||
return { total, completed, percent };
|
||||
}, [cklstRows, checkResults]);
|
||||
|
||||
const toCklstSaveDtoList = () =>
|
||||
cklstRows
|
||||
.map((row) => ({
|
||||
cklstSn: row.cklstSn,
|
||||
cklstArtclSn: row.cklstArtclSn,
|
||||
cklstRvwSn: row.cklstRvwSn,
|
||||
selected: checkResults[cklstKey(row)] ?? "",
|
||||
}))
|
||||
.filter((row) => row.selected !== "");
|
||||
|
||||
/* ── 사업규모 (공종구분 트리 연동) ── */
|
||||
// cnstpSeCd → { bscPl, budgetReflct, thtiDsgn } 단일 map
|
||||
const [bzScaleMap, setBzScaleMap] = useState<Record<string, BzScaleRow>>({});
|
||||
|
||||
/* ── 공종구분 트리 로드 ── */
|
||||
const { data: cnstpTree = [] } = useQuery<CnstpSeMngTreeDto[]>({
|
||||
queryKey: ["cnstpTree"],
|
||||
queryFn: cnstpService.findTree,
|
||||
staleTime: Infinity,
|
||||
});
|
||||
|
||||
/* ── 공종구분 리프 노드 집합 ── */
|
||||
const cnstpLeafSet = useMemo(() => {
|
||||
const parentCodes = new Set(cnstpTree.map((n) => n.upCnstpCd).filter(Boolean) as string[]);
|
||||
return new Set(cnstpTree.filter((n) => !parentCodes.has(n.cnstpSeCd)).map((n) => n.cnstpSeCd));
|
||||
}, [cnstpTree]);
|
||||
|
||||
const subtotalScale = (node: CnstpSeMngTreeDto, col: keyof BzScaleRow): number =>
|
||||
cnstpTree
|
||||
.filter((n) => cnstpLeafSet.has(n.cnstpSeCd) && n.treePath.split("|").includes(node.cnstpSeCd))
|
||||
.reduce((sum, n) => sum + num((bzScaleMap[n.cnstpSeCd] ?? emptyScale())[col]), 0);
|
||||
|
||||
const scaleNodeVal = (node: CnstpSeMngTreeDto, col: keyof BzScaleRow): number =>
|
||||
cnstpLeafSet.has(node.cnstpSeCd)
|
||||
? num((bzScaleMap[node.cnstpSeCd] ?? emptyScale())[col])
|
||||
: subtotalScale(node, col);
|
||||
|
||||
const handleScaleInput = (cnstpSeCd: string, col: keyof BzScaleRow, val: string) =>
|
||||
setBzScaleMap((prev) => ({
|
||||
...prev,
|
||||
[cnstpSeCd]: { ...(prev[cnstpSeCd] ?? emptyScale()), [col]: val },
|
||||
}));
|
||||
|
||||
/** 사업규모 맵 -> 백엔드 저장용 배열 DTO 변환 */
|
||||
const toBzScaleSaveDtoList = (): InstlSncnBzScaleSaveDto[] =>
|
||||
cnstpTree
|
||||
.filter((node) => cnstpLeafSet.has(node.cnstpSeCd))
|
||||
.map((node) => {
|
||||
const row = bzScaleMap[node.cnstpSeCd] ?? emptyScale();
|
||||
const bscPl = num(row.bscPl);
|
||||
const budgetReflct = num(row.budgetReflct);
|
||||
const thtiDsgn = num(row.thtiDsgn);
|
||||
|
||||
return {
|
||||
cnstpSeCd: node.cnstpSeCd,
|
||||
bscPl,
|
||||
budgetReflct,
|
||||
thtiDsgn,
|
||||
diff: thtiDsgn - budgetReflct,
|
||||
};
|
||||
});
|
||||
|
||||
/* ── 총사업비 (회계구분 트리 연동) ── */
|
||||
// acntgSeCd → { bscPl, budgetReflct, thtiDsgn } 단일 map
|
||||
@@ -173,6 +294,13 @@ function CnfmRegistPage() {
|
||||
return "";
|
||||
};
|
||||
|
||||
/** 공종 트리 행 스타일 분류 */
|
||||
const scaleRowStyle = (node: CnstpSeMngTreeDto) => {
|
||||
if (node.treeLevel === 1) return "bg-gray-50 font-semibold";
|
||||
if (!cnstpLeafSet.has(node.cnstpSeCd)) return "font-semibold";
|
||||
return "";
|
||||
};
|
||||
|
||||
/* ── 증감 라벨 ── */
|
||||
const diffLabel = (v: number) => (v > 0 ? `+${fmt(v)}` : fmt(v));
|
||||
|
||||
@@ -201,29 +329,24 @@ function CnfmRegistPage() {
|
||||
if (!fnValidation("SAVE")) return;
|
||||
const payload = {
|
||||
taskSeComCd,
|
||||
};
|
||||
bzScaleList: toBzScaleSaveDtoList(),
|
||||
cklstResultList: toCklstSaveDtoList(),
|
||||
};
|
||||
void payload;
|
||||
alert("저장(임시) API 연동 후 progrsSttus=SV 전송");
|
||||
alert("저장(임시) API 연동 후 progrsSttus=SV 전송",JSON.stringify(payload, null, 2));
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
if (!fnValidation("PRESENTN")) return;
|
||||
const payload = {
|
||||
taskSeComCd,
|
||||
bzScaleList: toBzScaleSaveDtoList(),
|
||||
cklstResultList: toCklstSaveDtoList(),
|
||||
};
|
||||
void payload;
|
||||
alert("제출 API 연동 후 progrsSttus=ST 전송");
|
||||
}
|
||||
|
||||
/* ── 공통 입력 핸들러 ── */
|
||||
function scaleInput(
|
||||
setter: React.Dispatch<React.SetStateAction<BzScaleRow>>,
|
||||
col: keyof BzScaleRow,
|
||||
val: string,
|
||||
) {
|
||||
setter((prev) => ({ ...prev, [col]: val }));
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
return (
|
||||
<div className="mt-6 space-y-4">
|
||||
@@ -467,7 +590,7 @@ function CnfmRegistPage() {
|
||||
<table className="cnfm-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan={2} className="w-[12rem]">
|
||||
<th rowSpan={2} className="w-[30rem]">
|
||||
구분
|
||||
</th>
|
||||
<th>기본계획(A)</th>
|
||||
@@ -477,31 +600,52 @@ function CnfmRegistPage() {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
{ label: "처리장용량(㎥/일)", state: bzSpecWwtp, setter: setBzSpecWwtp },
|
||||
{ label: "면적(㎡)", state: bzSpecArea, setter: setBzSpecArea },
|
||||
{ label: "하수관로 신설(m)", state: bzSpecNcr, setter: setBzSpecNcr },
|
||||
{ label: "교체/보수(m)", state: bzSpecRplc, setter: setBzSpecRplc },
|
||||
{ label: "펌프장(개소)", state: bzSpecPump, setter: setBzSpecPump },
|
||||
{ label: "배수설비(개소)", state: bzSpecDrng, setter: setBzSpecDrng },
|
||||
].map(({ label, state, setter }) => (
|
||||
<tr key={label}>
|
||||
<td className="bg-gray-50 pl-2 text-left font-semibold">{label}</td>
|
||||
{(["bscPl", "budgetReflct", "thtiDsgn"] as const).map((col) => (
|
||||
<td key={col}>
|
||||
<input
|
||||
type="text"
|
||||
className="cnfm-input w-full text-right"
|
||||
value={state[col]}
|
||||
onChange={(e) => scaleInput(setter, col, e.target.value)}
|
||||
/>
|
||||
</td>
|
||||
))}
|
||||
<td className="bg-gray-50 pr-2 text-right text-[1.25rem] font-semibold">
|
||||
{diffLabel(num(state.thtiDsgn) - num(state.budgetReflct))}
|
||||
{cnstpTree.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={5} className="cnfm-td-empty">
|
||||
조회된 사업규모 항목이 없습니다.
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
) : (
|
||||
cnstpTree.map((node) => {
|
||||
const isLeaf = cnstpLeafSet.has(node.cnstpSeCd);
|
||||
const bscPl = scaleNodeVal(node, "bscPl");
|
||||
const budgetReflct = scaleNodeVal(node, "budgetReflct");
|
||||
const thtiDsgn = scaleNodeVal(node, "thtiDsgn");
|
||||
const diffVal = thtiDsgn - budgetReflct;
|
||||
const pl = indentPx(node.indCnstpSeCdNm);
|
||||
|
||||
return (
|
||||
<tr key={node.cnstpSeCd} className={scaleRowStyle(node)}>
|
||||
<td
|
||||
className="bg-gray-50 pl-2 text-left"
|
||||
style={{ paddingLeft: `${pl + 8}px` }}
|
||||
>
|
||||
{node.cnstpSeCdNm}
|
||||
</td>
|
||||
|
||||
{isLeaf
|
||||
? (["bscPl", "budgetReflct", "thtiDsgn"] as const).map((col) => (
|
||||
<td key={col}>
|
||||
<input
|
||||
type="text"
|
||||
className="cnfm-input w-full text-right"
|
||||
value={(bzScaleMap[node.cnstpSeCd] ?? emptyScale())[col]}
|
||||
onChange={(e) =>
|
||||
handleScaleInput(node.cnstpSeCd, col, e.target.value)
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
))
|
||||
: ([bscPl, budgetReflct, thtiDsgn] as number[]).map((val, idx) => (
|
||||
<ReadCell key={`${node.cnstpSeCd}_${idx}`} value={fmt(val)} />
|
||||
))}
|
||||
|
||||
<ReadCell value={diffLabel(diffVal)} />
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -608,11 +752,13 @@ function CnfmRegistPage() {
|
||||
<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]">하수관로(0/8)</button>
|
||||
<button className="cnfm-btn-search text-[1.2rem]">
|
||||
{(cklstRows[0]?.cklstNm ?? taskSeComCdNm) + `(${cklstStats.completed}/${cklstStats.total})`}
|
||||
</button>
|
||||
</div>
|
||||
<div className="mb-2 text-[1.25rem] text-gray-500">
|
||||
체크리스트 진행상황 — 총합: <strong>0</strong> · 완료: <strong>0</strong> · 진행률:{" "}
|
||||
<strong>0%</strong>
|
||||
체크리스트 진행상황 — 총합: <strong>{cklstStats.total}</strong> · 완료:{" "}
|
||||
<strong>{cklstStats.completed}</strong> · 진행률: <strong>{cklstStats.percent}%</strong>
|
||||
</div>
|
||||
<div className="cnfm-table-wrap">
|
||||
<table className="cnfm-table">
|
||||
@@ -631,7 +777,7 @@ function CnfmRegistPage() {
|
||||
className="w-full rounded border border-green-300 bg-green-500 px-3 py-1 text-[1.1rem] font-semibold text-white hover:bg-green-600"
|
||||
onClick={() =>
|
||||
setCheckResults(
|
||||
Object.fromEntries(CHECKLIST_ITEMS.map((i) => [i, "Y" as const])),
|
||||
Object.fromEntries(cklstRows.map((row) => [cklstKey(row), "Y" as const])),
|
||||
)
|
||||
}
|
||||
>
|
||||
@@ -644,7 +790,7 @@ function CnfmRegistPage() {
|
||||
className="w-full rounded border border-red-300 bg-red-500 px-3 py-1 text-[1.1rem] font-semibold text-white hover:bg-red-600"
|
||||
onClick={() =>
|
||||
setCheckResults(
|
||||
Object.fromEntries(CHECKLIST_ITEMS.map((i) => [i, "N" as const])),
|
||||
Object.fromEntries(cklstRows.map((row) => [cklstKey(row), "N" as const])),
|
||||
)
|
||||
}
|
||||
>
|
||||
@@ -654,36 +800,50 @@ function CnfmRegistPage() {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{CHECKLIST_ITEMS.map((item) => (
|
||||
<tr key={item}>
|
||||
<td className="pl-2 text-left">{item}</td>
|
||||
<td className="pl-2 text-left text-[1.2rem] text-gray-400">
|
||||
금회 총사업비가 확정예산 범위 내
|
||||
</td>
|
||||
<td className="text-center">
|
||||
<label className="flex cursor-pointer items-center justify-center gap-1 text-[1.2rem] text-green-600">
|
||||
<input
|
||||
type="radio"
|
||||
name={`chk_${item}`}
|
||||
value="Y"
|
||||
checked={checkResults[item] === "Y"}
|
||||
onChange={() => setCheckResults((prev) => ({ ...prev, [item]: "Y" }))}
|
||||
/>
|
||||
</label>
|
||||
</td>
|
||||
<td className="text-center">
|
||||
<label className="flex cursor-pointer items-center justify-center gap-1 text-[1.2rem] text-red-500">
|
||||
<input
|
||||
type="radio"
|
||||
name={`chk_${item}`}
|
||||
value="N"
|
||||
checked={checkResults[item] === "N"}
|
||||
onChange={() => setCheckResults((prev) => ({ ...prev, [item]: "N" }))}
|
||||
/>
|
||||
</label>
|
||||
{cklstRows.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={4} className="cnfm-td-empty">
|
||||
조회된 체크리스트 항목이 없습니다.
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
) : (
|
||||
cklstRows.map((row) => {
|
||||
const key = cklstKey(row);
|
||||
const rvwPaddingLeft = `${Math.max(0, row.treeLevel - 1) * 16 + 8}px`;
|
||||
|
||||
return (
|
||||
<tr key={key}>
|
||||
<td className="text-center text-[1.2rem]">{row.cklstArtclNm}</td>
|
||||
<td className="!text-left pl-2 text-[1.2rem] text-gray-600" style={{ paddingLeft: rvwPaddingLeft }}>
|
||||
{row.treeLevel > 1 ? "└ " : ""}
|
||||
{row.rvwCn}
|
||||
</td>
|
||||
<td className="text-center">
|
||||
<label className="flex cursor-pointer items-center justify-center gap-1 text-[1.2rem] text-green-600">
|
||||
<input
|
||||
type="radio"
|
||||
name={`chk_${key}`}
|
||||
value="Y"
|
||||
checked={checkResults[key] === "Y"}
|
||||
onChange={() => setCheckResults((prev) => ({ ...prev, [key]: "Y" }))}
|
||||
/>
|
||||
</label>
|
||||
</td>
|
||||
<td className="text-center">
|
||||
<label className="flex cursor-pointer items-center justify-center gap-1 text-[1.2rem] text-red-500">
|
||||
<input
|
||||
type="radio"
|
||||
name={`chk_${key}`}
|
||||
value="N"
|
||||
checked={checkResults[key] === "N"}
|
||||
onChange={() => setCheckResults((prev) => ({ ...prev, [key]: "N" }))}
|
||||
/>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export const ENDPOINT = {
|
||||
AS_COMMON_SERVICE: {
|
||||
ACNTG_SE: "/as-common-service/api/v1/acntg-se",
|
||||
CNSTP_SE: "/as-common-service/api/v1/cnstp-se",
|
||||
},
|
||||
AS_INSTL_SERVICE: {
|
||||
INSTL_SNCN: "/as-instl-service/api/v1/instl-sncn",
|
||||
|
||||
Reference in New Issue
Block a user