This commit is contained in:
@@ -33,8 +33,8 @@ type CaseItem = {
|
||||
};
|
||||
|
||||
const TABS: { key: TabKey; label: string }[] = [
|
||||
{ key: "rain", label: "빗물 재이용 사례" },
|
||||
{ key: "reuse", label: "중수 재이용 사례" },
|
||||
{ key: "rain", label: "빗물이용시설 사례" },
|
||||
{ key: "reuse", label: "중수도 사례" },
|
||||
{ key: "sewage", label: "하수 재이용 사례" },
|
||||
{ key: "global", label: "해외 재이용 사례" },
|
||||
];
|
||||
@@ -59,7 +59,7 @@ const MOCK: Record<TabKey, CaseItem[]> = {
|
||||
],
|
||||
reuse: Array.from({ length: 6 }).map((_, i) => ({
|
||||
id: `reuse-${i}`,
|
||||
title: `중수 재이용 사례 ${i + 1}`,
|
||||
title: `중수도 사례 ${i + 1}`,
|
||||
desc: "설명 문구가 들어갑니다. (최대 2~3줄) …",
|
||||
imageSrc: "/images/landing/sample_case.png",
|
||||
href: "#",
|
||||
|
||||
@@ -23,7 +23,7 @@ export const LNB_CASES = [
|
||||
// 물 재이용 사례처럼 2depth가 있는 메뉴
|
||||
export const LNB_BUSINESS: LnbItem[] = [
|
||||
{
|
||||
label: "빗물 재이용 사례",
|
||||
label: "빗물이용시설 사례",
|
||||
children: [
|
||||
{ label: "경기도 수원시", href: "/reuse/business/rain/suwon" },
|
||||
// { label: "충청남도 보령시", href: "/reuse/business/rain/bolyeong" },
|
||||
@@ -31,7 +31,7 @@ export const LNB_BUSINESS: LnbItem[] = [
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "중수 재이용 사례",
|
||||
label: "중수도 사례",
|
||||
children: [{ label: "사례 목록", href: "/reuse/business/graywater" }],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -109,7 +109,7 @@ function InfoList({
|
||||
}
|
||||
|
||||
export default function GreywaterExampleSection() {
|
||||
const title = "중수 재이용 사례";
|
||||
const title = "중수도 사례";
|
||||
return (
|
||||
<div className="bg-white">
|
||||
{/* 상단 타이틀 */}
|
||||
|
||||
@@ -17,7 +17,7 @@ function Hr({ className = "" }: { className?: string }) {
|
||||
}
|
||||
|
||||
export default function RainExampleSection() {
|
||||
const title = "빗물 재이용 사례";
|
||||
const title = "빗물이용시설 사례";
|
||||
return (
|
||||
<div className="bg-white">
|
||||
{/* 상단 타이틀 */}
|
||||
|
||||
@@ -9,7 +9,7 @@ import GlobalCaseSection from "./_components/GlobalCaseSection";
|
||||
const menus: BusinessSubNavMenu[] = [
|
||||
{
|
||||
key: "example-rain",
|
||||
label: "빗물 재이용 사례",
|
||||
label: "빗물이용시설 사례",
|
||||
sections: [
|
||||
{ id: "example-rain-suwon", label: "경기도 수원시" },
|
||||
{ id: "example-rain-jeju", label: "제주특별자치도" },
|
||||
@@ -17,7 +17,7 @@ const menus: BusinessSubNavMenu[] = [
|
||||
},
|
||||
{
|
||||
key: "example-greywater",
|
||||
label: "중수 재이용 사례",
|
||||
label: "중수도 사례",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "example-greywater-ansan", label: "경기도 안산시" },
|
||||
|
||||
@@ -46,7 +46,7 @@ export default function GreywaterPage() {
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "빗물 재이용 사례" },
|
||||
{ label: "빗물이용시설 사례" },
|
||||
{ label: "수원시", href: "/reuse/business/rain/suwon" },
|
||||
]}
|
||||
hideFooterLine
|
||||
|
||||
@@ -650,8 +650,7 @@ const globalCases: GlobalCase[] = [
|
||||
depth: 1,
|
||||
},
|
||||
|
||||
{ text: "안핑을 포함한 16건 공사 완료시 628,100 CMD 공급 가능" },
|
||||
{ text: "공급단가는 수질과 운영조건에 따라 다르게 책정" },
|
||||
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -109,7 +109,7 @@ function InfoList({
|
||||
}
|
||||
|
||||
export default function GreywaterExampleSection() {
|
||||
const title = "중수 재이용 사례";
|
||||
const title = "중수도 사례";
|
||||
return (
|
||||
<div className="bg-white">
|
||||
{/* 상단 타이틀 */}
|
||||
|
||||
@@ -123,7 +123,7 @@ function RainTable({ rows }: { rows: RainTableRow[] }) {
|
||||
}
|
||||
|
||||
export default function RainExampleSection() {
|
||||
const title = "빗물 재이용 사례";
|
||||
const title = "빗물이용시설 사례";
|
||||
|
||||
return (
|
||||
<div className="bg-white">
|
||||
|
||||
@@ -334,7 +334,7 @@ const sewageCases: SewageCase[] = [
|
||||
];
|
||||
|
||||
export default function SewageExampleSection() {
|
||||
const title = "하수처리 재이용 사례";
|
||||
const title = "하수처리수 재이용 사례";
|
||||
|
||||
return (
|
||||
<div className="bg-white">
|
||||
|
||||
@@ -115,11 +115,11 @@ export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="중수 재이용 사례"
|
||||
title="중수도 사례"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "중수 재이용 사례" },
|
||||
{ label: "중수도 사례" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
|
||||
@@ -9,7 +9,7 @@ import GlobalCaseSection from "./_components/GlobalCaseSection";
|
||||
const menus: BusinessSubNavMenu[] = [
|
||||
{
|
||||
key: "example-rain",
|
||||
label: "빗물 재이용 사례",
|
||||
label: "빗물이용시설 사례",
|
||||
sections: [
|
||||
{ id: "example-rain-suwon", label: "경기도 수원시" },
|
||||
{ id: "example-rain-jeju", label: "제주특별자치도" },
|
||||
@@ -17,7 +17,7 @@ const menus: BusinessSubNavMenu[] = [
|
||||
},
|
||||
{
|
||||
key: "example-greywater",
|
||||
label: "중수 재이용 사례",
|
||||
label: "중수도 사례",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "example-greywater-ansan", label: "경기도 안산시" },
|
||||
@@ -27,7 +27,7 @@ const menus: BusinessSubNavMenu[] = [
|
||||
},
|
||||
{
|
||||
key: "example-sewage",
|
||||
label: "하수처리 재이용 사례",
|
||||
label: "하수처리수 재이용 사례",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "example-sewage-guri", label: "경기도 구리시" },
|
||||
|
||||
@@ -24,11 +24,11 @@ export default function RainPage() {
|
||||
<BusinessSubNav />
|
||||
|
||||
<SubPageHeader
|
||||
title="빗물 재이용 사례"
|
||||
title="빗물이용시설 사례"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "물 재이용 사례", href: "/example/rain" },
|
||||
{ label: "빗물 재이용 사례" },
|
||||
{ label: "빗물이용시설 사례" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
|
||||
@@ -56,7 +56,7 @@ function SectionShell({
|
||||
}
|
||||
|
||||
export default function LawIntroSection() {
|
||||
const title = "물 재이용 법";
|
||||
const title = "물재이용법";
|
||||
|
||||
return (
|
||||
<div className="bg-white">
|
||||
|
||||
@@ -54,7 +54,7 @@ function SectionShell({
|
||||
}
|
||||
|
||||
export default function RainIntroSection() {
|
||||
const title = "빗물 이용시설";
|
||||
const title = "빗물이용시설";
|
||||
|
||||
return (
|
||||
<div className="mt-[120px] bg-white">
|
||||
|
||||
@@ -52,7 +52,7 @@ function SectionShell({
|
||||
}
|
||||
|
||||
export default function TreatedIntroSection() {
|
||||
const title = "하수처리수";
|
||||
const title = "하수처리수 재이용";
|
||||
|
||||
return (
|
||||
<div className="mt-[120px] bg-white">
|
||||
|
||||
@@ -9,7 +9,7 @@ import TreatedIntroSection from "./_components/TreatedIntroSection";
|
||||
const menus: BusinessSubNavMenu[] = [
|
||||
{
|
||||
key: "intro-law",
|
||||
label: "물 재이용 법",
|
||||
label: "물재이용법",
|
||||
sections: [
|
||||
{ id: "law-plan", label: "법률 제·개정 및 기본계획 수립" },
|
||||
{ id: "plan-performance", label: "제2차 계획 대비 실적 현황" },
|
||||
@@ -21,7 +21,7 @@ const menus: BusinessSubNavMenu[] = [
|
||||
},
|
||||
{
|
||||
key: "intro-rain",
|
||||
label: "빗물 이용시설",
|
||||
label: "빗물이용시설",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "rain-intro", label: "빗물 재이용 소개" },
|
||||
@@ -48,7 +48,7 @@ const menus: BusinessSubNavMenu[] = [
|
||||
},
|
||||
{
|
||||
key: "intro-treated",
|
||||
label: "하수처리수",
|
||||
label: "하수처리수 재이용",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "treatedwater-intro", label: "하수처리수 소개" },
|
||||
|
||||
@@ -33,8 +33,8 @@ type CaseItem = {
|
||||
};
|
||||
|
||||
const TABS: { key: TabKey; label: string }[] = [
|
||||
{ key: "rain", label: "빗물 재이용 사례" },
|
||||
{ key: "greywater", label: "중수 재이용 사례" },
|
||||
{ key: "rain", label: "빗물이용시설 사례" },
|
||||
{ key: "greywater", label: "중수도 사례" },
|
||||
{ key: "sewage", label: "하수 재이용 사례" },
|
||||
{ key: "global", label: "해외 재이용 사례" },
|
||||
];
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 126 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 292 KiB After Width: | Height: | Size: 284 KiB |
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: "./tsconfig.eslint.json",
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* 표준공사비 시뮬레이션 Popup
|
||||
*/
|
||||
import { useForm } from "react-hook-form";
|
||||
import ConCostForm from "@/components/ui/form/coMat/conCostForm/conCostForm";
|
||||
import type { DmdCoReqType } from "@/app/store/matDmd/coMatRvw/type";
|
||||
import ListDiv from "@common/business_components/ui/list_div";
|
||||
|
||||
export default function ConstCostSimulationPage() {
|
||||
const form = useForm<DmdCoReqType>({
|
||||
defaultValues: {
|
||||
dmndQty: 0,
|
||||
demandCst: { cstrnCst: 0, usgPrcsProcs: "", prcsFcltCpctCstrnCst: 0, fcltSbsdCstrnCst: 0 },
|
||||
totalConstCost: 0
|
||||
} as any
|
||||
});
|
||||
|
||||
return (
|
||||
<ListDiv>
|
||||
<div className="card-header flex items-center justify-between">
|
||||
<h3 className="text-2xl font-medium text-label">표준공사비 산정</h3>
|
||||
</div>
|
||||
<ConCostForm form={form} type="simulation" readonly={false} />
|
||||
</ListDiv>
|
||||
)
|
||||
}
|
||||
@@ -52,6 +52,7 @@ export default function CoMatRvw() {
|
||||
const { auth, code, coRvw, coMatRvwInit, coMatRvwSearch, coMatRvwInqua } =
|
||||
useWrStore((state) => state);
|
||||
const { coMatRvwRes, coMatRvwCount } = coRvw;
|
||||
// const [mounted, setMounted] = useState(false);
|
||||
const { regAuthrtYn } = auth;
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
|
||||
@@ -74,9 +75,7 @@ export default function CoMatRvw() {
|
||||
const [toDate, setToDate] = useState<string>(
|
||||
String(new Date().getFullYear())
|
||||
);
|
||||
const [selectData, setSelectData] = useState<DmdCoReqType>(
|
||||
{} as DmdCoReqType
|
||||
);
|
||||
const [selectData, setSelectData] = useState<DmdCoReqType | null>(null); // selectData를 null로 초기화
|
||||
const [selectionVersion, setSelectionVersion] = useState(0);
|
||||
const bottomRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
@@ -122,7 +121,7 @@ export default function CoMatRvw() {
|
||||
if (coMatRvwRes && coMatRvwRes[index]) {
|
||||
setSelectData(coMatRvwRes[index]);
|
||||
setSelectionVersion((v) => v + 1); // 행을 클릭할 때마다 버전을 1씩 증가
|
||||
bottomRef.current?.scrollIntoView({ behavior: "smooth" }); // 화면 스크로 처리
|
||||
bottomRef.current?.scrollIntoView({ behavior: "smooth" }); // 화면 스크롤 처리
|
||||
}
|
||||
};
|
||||
const useUsgCdWatch = useWatch({ control: form.control, name: "useUsgCd" });
|
||||
@@ -131,7 +130,6 @@ export default function CoMatRvw() {
|
||||
String(useUsgCdWatch ?? "");
|
||||
useEffect(() => {
|
||||
if (useUsgCdWatch) {
|
||||
console.log("useUsgCdWatch:" , useUsgCdWatch);
|
||||
const normalizedUseUsgCd =
|
||||
code.sewageOutFclttUse?.find((item) => item.val === useUsgCdWatch)?.val ??
|
||||
code.sewageOutFclttUse?.find((item) => item.text === useUsgCdWatch)?.val ??
|
||||
@@ -188,6 +186,7 @@ export default function CoMatRvw() {
|
||||
useEffect(() => {
|
||||
if (!initRef.current) {
|
||||
coMatRvwInit(1, 13, fromDate, toDate);
|
||||
//setMounted(true);
|
||||
initRef.current = true;
|
||||
}
|
||||
}, [coMatRvwInit, initRef, fromDate, toDate]);
|
||||
@@ -205,6 +204,20 @@ export default function CoMatRvw() {
|
||||
setSelectionVersion((v) => v + 1); // 강제로 바뀌었다고 알리기
|
||||
}
|
||||
}, [selectData, form]);
|
||||
|
||||
/**
|
||||
* 하수 표준공사비 시뮬레이션(모의산정)
|
||||
*/
|
||||
const constCostSimulation = () => {
|
||||
const url = `/constCostSimulation`;
|
||||
const width = Math.floor(window.screen.availWidth * 0.6);
|
||||
const height = Math.floor(window.screen.availHeight * 0.6);
|
||||
const left = Math.floor(window.screenX + (window.outerWidth - width) / 2);
|
||||
const top = Math.floor(window.screenY + (window.outerHeight - height) / 2);
|
||||
const popupOptions = `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`;
|
||||
window.open(url, "candidatePopup", popupOptions);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ContentsDiv>
|
||||
@@ -290,6 +303,9 @@ export default function CoMatRvw() {
|
||||
>
|
||||
조회
|
||||
</Button>
|
||||
<Button className={btnClass} onClick={()=>constCostSimulation()} >
|
||||
표준공사비 모의산정
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</SearchFormRight>
|
||||
@@ -318,10 +334,9 @@ export default function CoMatRvw() {
|
||||
getSearch(val, Number(fromDate), Number(toDate), con, dmdCoNm);
|
||||
}}
|
||||
></UiTable>
|
||||
<div ref={bottomRef} />
|
||||
<div ref={bottomRef} className="scroll-mt-[45px]"/>
|
||||
</ListDiv>
|
||||
<br /><hr />
|
||||
<br />
|
||||
<hr className="my-4"/>
|
||||
{/* <ScrollArea className="h-[49vh]"> */}
|
||||
<ListDiv>
|
||||
<div className="card-header flex items-center justify-between">
|
||||
@@ -343,10 +358,11 @@ export default function CoMatRvw() {
|
||||
onChange={(e) => {
|
||||
field.onChange(String(e.target.value));
|
||||
}}
|
||||
value={
|
||||
moment(field.value).format("YYYY-MM-DD") ?? ""
|
||||
value={ // 값이 있을 때만 moment 실행, 없으면 빈 문자열
|
||||
field.value ? moment(field.value).format("YYYY-MM-DD") : ""
|
||||
}
|
||||
readOnly
|
||||
suppressHydrationWarning // 시간 차이로 인한 경고 무시
|
||||
className={readOnlyInputClass}
|
||||
/>
|
||||
</FormUi>
|
||||
@@ -773,5 +789,3 @@ export default function CoMatRvw() {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -53,17 +53,19 @@ export type comFcltState = {
|
||||
searchFclts: pubHasuType[];
|
||||
demandCompList: DemandCompType[];
|
||||
checkDemand: DemandCompType;
|
||||
constCostRes:conCostFeeType[], // 표준사업비
|
||||
constCostRes:conCostFeeType[], // 표준사업비 (공사비 산정표)
|
||||
rateFeeRes:rateFeeType[],
|
||||
|
||||
};
|
||||
type comFcltActions = {
|
||||
type comFcltActions = {
|
||||
getSwtrmtFcltNm: (searchNm: string) => void;
|
||||
getDemandCompList: (searchNm: string, cnt: number) => void;
|
||||
regDemandCompany: (data: DemandCompType) => Promise<string>;
|
||||
updateDemandCompany: (data: DemandCompType) => Promise<string>;
|
||||
getConstCostFee: (year: string) => void;
|
||||
getRateFee: (year: string) => void;
|
||||
getRateFeeRtn: (year: string) => Promise<rateFeeType[]>;
|
||||
getConstCostFeeRtn: (year: string) => Promise<conCostFeeType[]>;
|
||||
};
|
||||
|
||||
export const comFcltSearchAction: (set: any) => comFcltActions = (set: any) => {
|
||||
@@ -168,7 +170,20 @@ export const comFcltSearchAction: (set: any) => comFcltActions = (set: any) => {
|
||||
console.log(" error ", e);
|
||||
}
|
||||
},
|
||||
// 공사금액 조회 : ONSEMIRO_NEW.EM_STD_COST_REUSE 설치관리표준사업비 재이용시설 공사비
|
||||
// 요율정보 조회 : ONSEMIRO_NEW.EM_STD_COST_INC_EXP_RT 설치관리표준사업비 부대경비요율
|
||||
// 팝업에서 연도 변경하여 볼 수 있도록 데이터를 return한다.
|
||||
getRateFeeRtn: async (year: string) => {
|
||||
try {
|
||||
const url = `/prcsFclt/CstRt/${year}`;
|
||||
const rateFeeRes = await getFetch(fetchType.COMMON, url);
|
||||
|
||||
return rateFeeRes;
|
||||
} catch (e) {
|
||||
console.log(" error ", e);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
// 공사금액 (공사비 산정표) 조회 : ONSEMIRO_NEW.EM_STD_COST_REUSE 설치관리표준사업비 재이용시설 공사비
|
||||
getConstCostFee: async (year: string) => {
|
||||
try {
|
||||
const url = `/prcsFclt/CstRuse/${year}`;
|
||||
@@ -182,9 +197,21 @@ export const comFcltSearchAction: (set: any) => comFcltActions = (set: any) => {
|
||||
|
||||
});
|
||||
} catch (e) {
|
||||
console.log('getConstCostFee ', e)
|
||||
console.log('getConstCostFee ', e);
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
// 공사금액 (공사비 산정표) 조회 : ONSEMIRO_NEW.EM_STD_COST_REUSE 설치관리표준사업비 재이용시설 공사비
|
||||
getConstCostFeeRtn: async (year: string): Promise<conCostFeeType[]> => {
|
||||
try {
|
||||
const url = `/prcsFclt/CstRuse/${year}`;
|
||||
const constCost = await getFetch(fetchType.COMMON, url);
|
||||
return constCost;
|
||||
} catch (e) {
|
||||
console.log('getConstCostFee ', e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -24,17 +24,18 @@ export const coRvwNodeListExport: CommonTableProps[] = [
|
||||
];
|
||||
|
||||
export const constCostHeadListExport: CommonTableProps[] = [
|
||||
{ id: "gubun", title: "구분", visible: true, },
|
||||
{ id: "500", title: "500", visible: true, },
|
||||
{ id: "1000", title: "1,000", visible: true, },
|
||||
{ id: "2000", title: "2,000", visible: true, },
|
||||
{ id: "3000", title: "3,000", visible: true, }, //바꾸기
|
||||
{ id: "5000", title: "5,000", visible: true, }, //바꾸기
|
||||
{ id: "10000", title: "10,000", visible: true, }, //
|
||||
{ id: "20000", title: "20,000", visible: true, }, //
|
||||
{ id: "30000", title: "30,000", visible: true, }, //
|
||||
{ id: "50000", title: "50,000", visible: true, }, //
|
||||
{ id: "100000", title: "100,000", visible: true, }, //바꾸기
|
||||
{ id: "gubun", title: "구분", visible: true, row: 0, rowSpan: 2, colSpan: 1 },
|
||||
{ id: "A", title: "처리시설용량(m³)", visible: true, row: 0, rowSpan: 1, colSpan: 10 },
|
||||
{ id: "500", title: "500", visible: true, row: 1, rowSpan: 1, colSpan: 1 },
|
||||
{ id: "1000", title: "1,000", visible: true, row: 1, rowSpan: 1, colSpan: 1 },
|
||||
{ id: "2000", title: "2,000", visible: true, row: 1, rowSpan: 1, colSpan: 1 },
|
||||
{ id: "3000", title: "3,000", visible: true, row: 1, rowSpan: 1, colSpan: 1 },
|
||||
{ id: "5000", title: "5,000", visible: true, row: 1, rowSpan: 1, colSpan: 1 },
|
||||
{ id: "10000", title: "10,000", visible: true, row: 1, rowSpan: 1, colSpan: 1 },
|
||||
{ id: "20000", title: "20,000", visible: true, row: 1, rowSpan: 1, colSpan: 1 },
|
||||
{ id: "30000", title: "30,000", visible: true, row: 1, rowSpan: 1, colSpan: 1 },
|
||||
{ id: "50000", title: "50,000", visible: true, row: 1, rowSpan: 1, colSpan: 1 },
|
||||
{ id: "100000", title: "100,000", visible: true, row: 1, rowSpan: 1, colSpan: 1 },
|
||||
];
|
||||
|
||||
export const rateFeeHeadListExport: CommonTableProps[] = [
|
||||
|
||||
@@ -66,7 +66,7 @@ type coRvwActions = {
|
||||
sggCd: string
|
||||
) => void;
|
||||
};
|
||||
|
||||
// 수요매칭검토현황 목록
|
||||
const coMatInit = async (
|
||||
pageNo: number,
|
||||
pageCnt: number,
|
||||
@@ -99,9 +99,10 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => {
|
||||
},
|
||||
}));
|
||||
},
|
||||
// 수요처 매칭 검토 저장
|
||||
coMatRvwUpdate: async (
|
||||
splyCd: string,
|
||||
dmdCd: string,
|
||||
splyCd: string, // 공공기관재사용시설아이디 - 하수재이용시설id : pbnst_ruse_fclt_id
|
||||
dmdCd: string, // 수요정보id dmd_info_id
|
||||
data: DedMatUpdateType
|
||||
): Promise<string> => {
|
||||
try {
|
||||
@@ -148,6 +149,7 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => {
|
||||
sggCd: string,
|
||||
dmdCoNm: string
|
||||
) => {
|
||||
// 수요처 년도별 정보 검색
|
||||
let url = `/reuseFc/request/infoList/Sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`;
|
||||
|
||||
if (sggCd.length > 0) {
|
||||
|
||||
@@ -60,47 +60,47 @@ const DemandCoSchema = z.object({
|
||||
});
|
||||
|
||||
const DemandCstSchema = z.object({
|
||||
dmdInfoSn: z.number().nullish(),
|
||||
usgPrcsProcs: z.string().nullish(),
|
||||
cstrnCst: z.number().default(0),
|
||||
fcltSbsdCstrnCst: z.number().default(0),
|
||||
prcsFcltCpctCstrnCst: z.number().default(0),
|
||||
dmdInfoSn: z.number().nullish(), // 수요정보 일련번호
|
||||
usgPrcsProcs: z.string().nullish(), // 용도에 따른 처리공정
|
||||
cstrnCst: z.number().default(0), // 공급관로 표준공사비(백만원)
|
||||
fcltSbsdCstrnCst: z.number().default(0), // 시설부대경비
|
||||
prcsFcltCpctCstrnCst: z.number().default(0), // 처리시설용량에 따른 공사비(백만원) - 처리시설용량공사비용
|
||||
});
|
||||
|
||||
export const DmdCoReqSchema = z.object({
|
||||
cityText: z.string().nullish(),
|
||||
sggText: z.string().nullish(),
|
||||
dmdInfoSn: z.number().default(0),
|
||||
dmdInfoId: z.string().default(''),
|
||||
dmndTmng: z.coerce.date().nullish(),
|
||||
dmndQty: z.number().nullish(), // null 또는 undefined 허용
|
||||
blgcOxygDmndQnt: z.number().nullish(),
|
||||
wntUntprc: z.number().nullish(),
|
||||
grssClfg: z.number().nullish(),
|
||||
combResdch: z.number().nullish(),
|
||||
trbt: z.number().nullish(),
|
||||
smll: z.string().nullish(),
|
||||
crmty: z.number().nullish(),
|
||||
tn: z.number().nullish(),
|
||||
tph: z.number().nullish(),
|
||||
ph: z.string().nullish(),
|
||||
clIonQnt: z.number().nullish(),
|
||||
cdtnv: z.number().nullish(),
|
||||
rmrk: z.string().nullish(),
|
||||
useUsgCd: z.string().nullish(),
|
||||
mtchnCmptnYmd: z.string().nullish(),
|
||||
rvwCmptnYmd: z.string().nullish(),
|
||||
demandCo: DemandCoSchema.nullish(),
|
||||
demandMatching: DemandMatchingSchema.nullish(),
|
||||
demandCst: DemandCstSchema.nullish(),
|
||||
ruseUtztnFcltNm: z.string().nullish(),
|
||||
cityText: z.string().nullish(), // 시도
|
||||
sggText: z.string().nullish(), // 시군구
|
||||
dmdInfoSn: z.number().default(0), // 수요정보 일련번호
|
||||
dmdInfoId: z.string().default(''), // 수요정보 ID
|
||||
dmndTmng: z.coerce.date().nullish(), // 요구시기
|
||||
dmndQty: z.number().nullish(), // 요구수량(m³/일)
|
||||
blgcOxygDmndQnt: z.number().nullish(), // 생물학적 산소요구량(BOD)(mg/L)
|
||||
wntUntprc: z.number().nullish(), // 희망단가(원/m3)
|
||||
grssClfg: z.number().nullish(), // 총대장균군수(개/mg)
|
||||
combResdch: z.number().nullish(), // 결합잔류염소(mg/L)
|
||||
trbt: z.number().nullish(), // 탁도(NTU)
|
||||
smll: z.string().nullish(), // 냄새
|
||||
crmty: z.number().nullish(), // 색도(도)
|
||||
tn: z.number().nullish(), // 총질소(T-N)(mg/L)
|
||||
tph: z.number().nullish(), // 총인(T-P)(mg/L)
|
||||
ph: z.string().nullish(), // 수소이온농도(pH)
|
||||
clIonQnt: z.number().nullish(), // 염화물(mgCL/L)
|
||||
cdtnv: z.number().nullish(), // 전기전도도(㎲/cm)
|
||||
rmrk: z.string().nullish(), // 비고
|
||||
useUsgCd: z.string().nullish(), // 사용용도
|
||||
mtchnCmptnYmd: z.string().nullish(), // 매칭완료일
|
||||
rvwCmptnYmd: z.string().nullish(), // 검토완료일
|
||||
demandCo: DemandCoSchema.nullish(), // 수요처 정보
|
||||
demandMatching: DemandMatchingSchema.nullish(), // 매칭 정보
|
||||
demandCst: DemandCstSchema.nullish(), // 공사비 정보
|
||||
ruseUtztnFcltNm: z.string().nullish(), // 공급처명
|
||||
splyCndtList: z.array(
|
||||
z.object({
|
||||
ruseUtztnFcltNm: z.string(),
|
||||
ruseUtztnFcltCd: z.string(),
|
||||
ruseUtztnFcltNm: z.string(), // 공급처명
|
||||
ruseUtztnFcltCd: z.string(), // 공급처 코드
|
||||
}).nullish()
|
||||
).nullish(),
|
||||
totalConstCost: z.number().nullish(),
|
||||
).nullish(), // 공급처 후보지 목록
|
||||
totalConstCost: z.number().nullish(), // 처리시설 총사업비(백만원)
|
||||
});
|
||||
|
||||
export type DmdCoReqType = z.infer<typeof DmdCoReqSchema>;
|
||||
@@ -159,4 +159,4 @@ export type rateFeeType = {
|
||||
export type SupplyCandidate = {
|
||||
ruseUtztnFcltNm: string;
|
||||
ruseUtztnFcltCd: string;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -20,20 +20,21 @@ import {
|
||||
Form,
|
||||
FormField,
|
||||
FormUi,
|
||||
SelectBox,
|
||||
UiTable,
|
||||
} from "@common/business_components/ui";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
import PopoverUi from "@common/business_components/ui/popover_ui";
|
||||
import { Button, Checkbox, Input, Label } from "@common/components/ui";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { type UseFormReturn } from "react-hook-form";
|
||||
import { useForm, type UseFormReturn } from "react-hook-form";
|
||||
/**
|
||||
* ConCostForm
|
||||
* 호출: (with-layout)/matMng/coMat/coMatRvw 에서 호출
|
||||
* ConCostForm
|
||||
* 호출: (with-layout)/matMng/coMat/coMatRvw
|
||||
*
|
||||
* 공사비를 산정하고
|
||||
*
|
||||
* */
|
||||
* @param form - 외부에서 전달된 폼 객체 (없을 경우 시뮬레이션용 로컬 폼 사용)
|
||||
* @param type - "mat": 매칭, "hist": 이력, "simulation": 모의산정
|
||||
*/
|
||||
|
||||
export default function ConCostForm({
|
||||
form,
|
||||
@@ -41,11 +42,27 @@ export default function ConCostForm({
|
||||
selectionVersion,
|
||||
readonly = false,
|
||||
}: {
|
||||
form: UseFormReturn<DmdCoReqType>;
|
||||
form?: UseFormReturn<DmdCoReqType>;
|
||||
type: string;
|
||||
selectionVersion: number;
|
||||
readonly: boolean;
|
||||
selectionVersion?: number;
|
||||
readonly?: boolean;
|
||||
}) {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
// 시뮬레이션 모드를 위한 로컬 폼 생성
|
||||
const localForm = useForm<DmdCoReqType>({
|
||||
defaultValues: {
|
||||
dmndQty: 0,
|
||||
demandCst: { cstrnCst: 0, usgPrcsProcs: "" }
|
||||
} as any
|
||||
});
|
||||
|
||||
const activeForm = form || localForm;
|
||||
const isSim = type === "simulation";
|
||||
|
||||
const readOnlyInputClass = "bg-slate-50 text-gray-700";
|
||||
const {
|
||||
constCostRes, // 표준사업비
|
||||
@@ -105,23 +122,54 @@ export default function ConCostForm({
|
||||
link.click();
|
||||
});
|
||||
};
|
||||
const usgPrcsProcsWatch = form.watch("demandCst.usgPrcsProcs");
|
||||
const dmndQtyWatch = form.watch("dmndQty");
|
||||
const totalConstCost = form.watch("totalConstCost");
|
||||
const cstrnCst = form.watch("demandCst.cstrnCst");
|
||||
const splyList = form.watch("splyCndtList");
|
||||
const useUsgCdWatch = form.watch("useUsgCd");
|
||||
const usgPrcsProcsWatch = activeForm.watch("demandCst.usgPrcsProcs"); // 용도에 따른 처리공정
|
||||
const dmndQtyWatch = activeForm.watch("dmndQty"); // 요구수량(m³/일)
|
||||
const totalConstCost = activeForm.watch("totalConstCost"); // 처리시설 총사업비(백만원)
|
||||
const cstrnCst = activeForm.watch("demandCst.cstrnCst") || 0; // 공급관로 표준공사비(백만원)
|
||||
const prcsFcltCpctCstrnCst = activeForm.watch("demandCst.prcsFcltCpctCstrnCst") || 0; // 처리시설용량에 따른 공사비(백만원)
|
||||
const fcltSbsdCstrnCst = activeForm.watch("demandCst.fcltSbsdCstrnCst") || 0; // 시설부대경비
|
||||
const splyList = activeForm.watch("splyCndtList"); // 공급처 후보지 목록
|
||||
const useUsgCdWatch = activeForm.watch("useUsgCd"); // 사용 용도 코드
|
||||
const ruseUtztnFcltNmWatch = activeForm.watch("ruseUtztnFcltNm"); // 선정된 공급처 명
|
||||
const useUsgCdLabel =
|
||||
code.sewageOutFclttUse?.find((item) => item.val === useUsgCdWatch)?.text ??
|
||||
code.sewageOutFclttUse?.find((item) => item.val === useUsgCdWatch)?.text ?? // 사용 용도 명칭(라벨)
|
||||
String(useUsgCdWatch ?? "");
|
||||
|
||||
useEffect(() => {
|
||||
const calculatedTotalConstCost = Number(prcsFcltCpctCstrnCst) + Number(fcltSbsdCstrnCst);
|
||||
|
||||
if ((totalConstCost ?? 0) !== calculatedTotalConstCost) {
|
||||
activeForm.setValue("totalConstCost", calculatedTotalConstCost);
|
||||
}
|
||||
}, [
|
||||
activeForm,
|
||||
prcsFcltCpctCstrnCst,
|
||||
fcltSbsdCstrnCst,
|
||||
totalConstCost,
|
||||
selectionVersion,
|
||||
]);
|
||||
|
||||
// 용도 변경 시 처리공정 자동 매핑 (시뮬레이션 대응)
|
||||
useEffect(() => {
|
||||
if (useUsgCdWatch) {
|
||||
const matched = usageProcData.find((item) => item.useUsgCd === useUsgCdWatch);
|
||||
if (matched && matched.process !== usgPrcsProcsWatch) {
|
||||
activeForm.setValue("demandCst.usgPrcsProcs", matched.process);
|
||||
}
|
||||
}
|
||||
}, [useUsgCdWatch, activeForm, usgPrcsProcsWatch]);
|
||||
|
||||
/**
|
||||
* 표준공산비 산정 : "계산"버튼 click시
|
||||
*/
|
||||
const calculateConstCost = useCallback(() => {
|
||||
const usgPrcsProcs = usgPrcsProcsWatch;
|
||||
if (selectionVersion == 0) {
|
||||
alert("먼저 목록에서 수요신청 건을 선택하세요.");
|
||||
if (!isSim && selectionVersion == 0) {
|
||||
alert("목록에서 수요신청 건을 선택하세요.");
|
||||
return;
|
||||
}
|
||||
if (!useUsgCdWatch) {
|
||||
alert("용도 값이 없습니다.");
|
||||
return;
|
||||
}
|
||||
if (!dmndQtyWatch) {
|
||||
@@ -148,7 +196,7 @@ export default function ConCostForm({
|
||||
const firstProc = getConstFctCode(usgPrcsProcs?.split("+")[0] || "") || ""; // 첫번째 공정
|
||||
const secondProc = getConstFctCode(usgPrcsProcs?.split("+")[1] || "") || ""; // 두번째 공정
|
||||
|
||||
// 첫번째 처리공정 코드에 해당하는 표준공사비 테이블이서 요구수량을 감싸는 하한/상한 구간을 찾는 부분이다.
|
||||
// 첫번째 처리공정 코드에 해당하는 표준공사비 테이블에서 요구수량을 감싸는 하한/상한 구간을 찾는 부분이다.
|
||||
const { lower: firstLower, upper: firstUpper } = findBounds(
|
||||
constCostRes, // 표준공사비
|
||||
firstProc || "",
|
||||
@@ -194,12 +242,12 @@ export default function ConCostForm({
|
||||
100;
|
||||
const realResult = result + fcltSbsdCstrnCst;
|
||||
|
||||
form.setValue("demandCst.prcsFcltCpctCstrnCst", Math.round(result));
|
||||
form.setValue("demandCst.fcltSbsdCstrnCst", Math.round(fcltSbsdCstrnCst));
|
||||
form.setValue("totalConstCost", Math.round(realResult));
|
||||
activeForm.setValue("demandCst.prcsFcltCpctCstrnCst", Math.round(result));
|
||||
activeForm.setValue("demandCst.fcltSbsdCstrnCst", Math.round(fcltSbsdCstrnCst));
|
||||
activeForm.setValue("totalConstCost", Math.round(realResult));
|
||||
alert("표준공산비 산정이 완료되었습니다.");
|
||||
}
|
||||
}, [constCostRes, rateFeeRes, usgPrcsProcsWatch, dmndQtyWatch]);
|
||||
}, [constCostRes, rateFeeRes, usgPrcsProcsWatch, dmndQtyWatch, isSim, selectionVersion, activeForm]);
|
||||
|
||||
/**
|
||||
* 저장 버튼 click 시 실행
|
||||
@@ -212,22 +260,22 @@ export default function ConCostForm({
|
||||
return;
|
||||
}
|
||||
|
||||
// const pk =
|
||||
// splycd && splycd[0]?.ruseUtztnFcltCd
|
||||
// ? splycd[0]?.ruseUtztnFcltCd
|
||||
// : "11000SW001R1";
|
||||
// 1순위: 팝업에서 새로 선택한 코드 (lastSub)
|
||||
// 2순위: 기존 DB에서 불러와 폼에 저장된 코드 (demandMatching.pbnstRuseFcltId)
|
||||
const pk =
|
||||
lastSub?.ruseUtztnFcltCd ??
|
||||
activeForm.getValues("demandMatching.pbnstRuseFcltId") ??
|
||||
null;
|
||||
|
||||
const pk =lastSub?.ruseUtztnFcltCd??null; // 후보지
|
||||
|
||||
if (type === "mat") {
|
||||
if (value.demandCst) {
|
||||
const data: DedMatUpdateType = {
|
||||
dmdInfoSn: value.dmdInfoSn,
|
||||
usgPrcsProcs: value.demandCst.usgPrcsProcs || "",
|
||||
cstrnCst: value.demandCst.cstrnCst || 0,
|
||||
fcltSbsdCstrnCst: value.demandCst.fcltSbsdCstrnCst || 0,
|
||||
prcsFcltCpctCstrnCst: value.demandCst.prcsFcltCpctCstrnCst || 0,
|
||||
flg: checkFinish ? "Checked" : "Sub",
|
||||
dmdInfoSn: value.dmdInfoSn, // 수요정보 일련번호
|
||||
usgPrcsProcs: value.demandCst.usgPrcsProcs || "", // 용도에 따른 처리공정
|
||||
cstrnCst: value.demandCst.cstrnCst || 0, // 공급관로 표준공사비(백만원)
|
||||
fcltSbsdCstrnCst: value.demandCst.fcltSbsdCstrnCst || 0, // 시설부대경비
|
||||
prcsFcltCpctCstrnCst: value.demandCst.prcsFcltCpctCstrnCst || 0, // 처리시설용량에 따른 공사비(백만원)
|
||||
flg: checkFinish ? "Checked" : "Sub", // "Sub": 검토중, "Checked": 검토완료(재이용검토이력 화면에 나타남)
|
||||
rgtr: TEST_UUID,
|
||||
};
|
||||
|
||||
@@ -235,11 +283,11 @@ export default function ConCostForm({
|
||||
|
||||
if (checkFinish) {
|
||||
if (await confirm("검토완료 변경시 공사비는 변경할수 없습니다.\n진행하시겠습니까?")) {
|
||||
const message = await coMatRvwUpdate( pk, form.getValues("dmdInfoId"), data );
|
||||
const message = await coMatRvwUpdate( pk, activeForm.getValues("dmdInfoId"), data );
|
||||
alert(message);
|
||||
}
|
||||
} else {
|
||||
const message = await coMatRvwUpdate( pk, form.getValues("dmdInfoId"), data );
|
||||
const message = await coMatRvwUpdate( pk, activeForm.getValues("dmdInfoId"), data );
|
||||
alert(message);
|
||||
}
|
||||
}else{
|
||||
@@ -292,10 +340,11 @@ export default function ConCostForm({
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
form.register("dmdInfoSn");
|
||||
form.register("dmdInfoId");
|
||||
form.register("ruseUtztnFcltNm");
|
||||
}, []);
|
||||
if (!activeForm) return;
|
||||
activeForm.register("dmdInfoSn");
|
||||
activeForm.register("dmdInfoId");
|
||||
activeForm.register("ruseUtztnFcltNm");
|
||||
}, [activeForm]);
|
||||
|
||||
|
||||
/**
|
||||
@@ -309,7 +358,7 @@ export default function ConCostForm({
|
||||
alert("먼저 목록에서 수요신청 건을 선택하세요.");
|
||||
return;
|
||||
}
|
||||
const dmdInfoId = form.getValues("dmdInfoId");
|
||||
const dmdInfoId = activeForm.getValues("dmdInfoId");
|
||||
console.log(dmdInfoId)
|
||||
|
||||
const url = `/splyPopup?dmdInfoId=${encodeURIComponent(dmdInfoId)}`;
|
||||
@@ -321,10 +370,12 @@ export default function ConCostForm({
|
||||
window.open(url, "candidatePopup", popupOptions);
|
||||
};
|
||||
|
||||
if (!mounted) return null; // 서버 사이드 렌더링 시에는 아무것도 그리지 않음 (선택 사항)
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<Form {...form}>
|
||||
<Form {...activeForm}>
|
||||
{/* <h3 className="mt-4 ml-2 text-2xl font-medium text-label">
|
||||
표준공사비 산정
|
||||
</h3> */}
|
||||
@@ -334,62 +385,72 @@ export default function ConCostForm({
|
||||
<Button className="mx-3" size={"sm"} onClick={calculateConstCost}>
|
||||
계산
|
||||
</Button>
|
||||
<Button
|
||||
className="mr-3"
|
||||
size={"sm"}
|
||||
onClick={form.handleSubmit(onSubmit)}
|
||||
disabled={readonly}
|
||||
>
|
||||
저장
|
||||
</Button>
|
||||
<Label>검토완료</Label>
|
||||
<Checkbox
|
||||
className="ml-3 mr-6"
|
||||
checked={checkFinish}
|
||||
onCheckedChange={
|
||||
(checked) => {
|
||||
if (readonly) return;
|
||||
setCheckFinish(checked === true);
|
||||
}
|
||||
|
||||
// 'indeterminate' 상태를 무시하고 순수 boolean 값으로만 상태를 업데이트합니다.
|
||||
}
|
||||
></Checkbox>
|
||||
<Label>관로 표준공사비 산정 프로그램</Label>
|
||||
<Button
|
||||
color="green"
|
||||
size={"sm"}
|
||||
onClick={zipDowonLoad}
|
||||
className="bg-none ml-6"
|
||||
>
|
||||
다운로드
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-12 ml-4 mr-4">
|
||||
<h1 className={formH1Class}>기본사항</h1>
|
||||
{!isSim && (
|
||||
<>
|
||||
<Button
|
||||
className="mr-3"
|
||||
size={"sm"}
|
||||
onClick={activeForm.handleSubmit(onSubmit)}
|
||||
disabled={readonly}
|
||||
>
|
||||
저장
|
||||
</Button>
|
||||
<Label>검토완료</Label>
|
||||
<Checkbox
|
||||
className="ml-3 mr-6"
|
||||
checked={checkFinish}
|
||||
onCheckedChange={(checked) => {
|
||||
if (readonly) return;
|
||||
setCheckFinish(checked === true);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{!isSim && (
|
||||
<>
|
||||
<Label>관로 표준공사비 산정 프로그램</Label>
|
||||
<Button color="green" size={"sm"} onClick={zipDowonLoad} className="bg-none ml-6">
|
||||
다운로드
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-12 gap-4 ml-4 mr-4">
|
||||
<div className="col-span-12 w-full">
|
||||
<h1 className={formH1Class}>기본사항</h1>
|
||||
</div>
|
||||
<div className="col-span-3 mt-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
control={activeForm.control}
|
||||
name="useUsgCd"
|
||||
render={({ field }) => (
|
||||
<FormUi label="용도">
|
||||
<Input
|
||||
placeholder=""
|
||||
type="text"
|
||||
onChange={field.onChange}
|
||||
value={useUsgCdLabel}
|
||||
readOnly
|
||||
className={readOnlyInputClass}
|
||||
/>
|
||||
{isSim ? (
|
||||
<SelectBox
|
||||
label=""
|
||||
selectClass="rounded-md"
|
||||
selectArray={code.sewageOutFclttUse || []}
|
||||
onSelectValue={field.onChange}
|
||||
value={field.value ?? ""}
|
||||
className=""
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
placeholder=""
|
||||
type="text"
|
||||
value={useUsgCdLabel}
|
||||
readOnly
|
||||
className={readOnlyInputClass}
|
||||
/>
|
||||
)}
|
||||
</FormUi>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-3 mt-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
control={activeForm.control}
|
||||
name="dmndQty"
|
||||
render={({ field }) => (
|
||||
<FormUi label="요구수량(m³/일)">
|
||||
@@ -401,8 +462,8 @@ export default function ConCostForm({
|
||||
field.onChange(Number(e.target.value));
|
||||
}}
|
||||
value={field.value ?? 0}
|
||||
readOnly={true}
|
||||
className={readOnlyInputClass}
|
||||
readOnly={!isSim}
|
||||
className={!isSim ? readOnlyInputClass : ""}
|
||||
/>
|
||||
</FormUi>
|
||||
)}
|
||||
@@ -439,7 +500,7 @@ export default function ConCostForm({
|
||||
bodyClassName={"w-[25vw] max-w-none"}
|
||||
></PopoverUi>
|
||||
<FormField
|
||||
control={form.control}
|
||||
control={activeForm.control}
|
||||
name="demandCst.usgPrcsProcs"
|
||||
render={({ field }) => (
|
||||
<FormUi label="">
|
||||
@@ -457,7 +518,7 @@ export default function ConCostForm({
|
||||
</div>
|
||||
<div className="col-span-3 mt-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
control={activeForm.control}
|
||||
name="dmndQty"
|
||||
render={({ field }) => (
|
||||
<FormUi label="처리시설용량(m³/일)">
|
||||
@@ -482,7 +543,7 @@ export default function ConCostForm({
|
||||
<h1 className={formH1Class}>공사비 입력사항</h1>
|
||||
</div>
|
||||
|
||||
<div className="col-span-2 mt-4">
|
||||
<div className="col-span-3 mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<PopoverUi
|
||||
childrenTriger={
|
||||
@@ -498,11 +559,11 @@ export default function ConCostForm({
|
||||
<ConstCostFeeTable />
|
||||
</>
|
||||
}
|
||||
bodyClassName={"w-fit max-w-[40vw]"}
|
||||
bodyClassName={"w-fit max-w-[400] overflow-x-auto"}
|
||||
></PopoverUi>
|
||||
</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
control={activeForm.control}
|
||||
name="demandCst.prcsFcltCpctCstrnCst"
|
||||
render={({ field }) => (
|
||||
<FormUi label="">
|
||||
@@ -515,7 +576,7 @@ export default function ConCostForm({
|
||||
}}
|
||||
value={field.value ?? 0}
|
||||
readOnly
|
||||
className={readOnlyInputClass}
|
||||
className={`mt-2 ${readOnlyInputClass}`}
|
||||
/>
|
||||
</FormUi>
|
||||
)}
|
||||
@@ -536,11 +597,11 @@ export default function ConCostForm({
|
||||
<RateFeeTableModal />
|
||||
</>
|
||||
}
|
||||
bodyClassName={"w-[22vw]"}
|
||||
bodyClassName={"w-fit max-w-[100] overflow-x-auto"}
|
||||
></PopoverUi>
|
||||
</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
control={activeForm.control}
|
||||
name="demandCst.fcltSbsdCstrnCst"
|
||||
render={({ field }) => (
|
||||
<FormUi label="">
|
||||
@@ -553,7 +614,7 @@ export default function ConCostForm({
|
||||
}}
|
||||
value={field.value ?? 0}
|
||||
readOnly
|
||||
className={readOnlyInputClass}
|
||||
className={`mt-2 ${readOnlyInputClass}`}
|
||||
/>
|
||||
</FormUi>
|
||||
)}
|
||||
@@ -585,7 +646,7 @@ export default function ConCostForm({
|
||||
bodyClassName={"w-[22vw]"}
|
||||
></PopoverUi>
|
||||
<FormField
|
||||
control={form.control}
|
||||
control={activeForm.control}
|
||||
name="totalConstCost"
|
||||
render={({ field }) => (
|
||||
<FormUi label="">
|
||||
@@ -604,35 +665,30 @@ export default function ConCostForm({
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-3 mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label>공급처 선정</Label>
|
||||
<Button
|
||||
size={"sm"}
|
||||
onClick={(e) => {
|
||||
searchCandidate(e);
|
||||
|
||||
//splyList => 해당객체가 대민에서 선택한 후보처 리스트
|
||||
//setLastSub => 해당 스테이트가 선택한 마지막 후보처를 넣어야하는 스테이트
|
||||
}}
|
||||
className="bg-none"
|
||||
>
|
||||
후보지 조회
|
||||
</Button>
|
||||
{!isSim && (
|
||||
<div className="col-span-3 mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label>공급처 선정</Label>
|
||||
<Button size={"xs"} onClick={searchCandidate} className="bg-none">
|
||||
후보지 조회
|
||||
</Button>
|
||||
</div>
|
||||
<FormUi label="">
|
||||
<Input
|
||||
placeholder="공급처를 선정해주세요"
|
||||
type="text"
|
||||
// 1순위: 팝업에서 새로 선택한 값(lastSub)
|
||||
// 2순위: 기존 DB에서 불러온 값(ruseUtztnFcltNmWatch)
|
||||
value={lastSub?.ruseUtztnFcltNm || ruseUtztnFcltNmWatch || ""}
|
||||
readOnly
|
||||
className={`mt-2 ${readOnlyInputClass}`}
|
||||
/>
|
||||
</FormUi>
|
||||
</div>
|
||||
<FormUi label="">
|
||||
<Input
|
||||
placeholder=""
|
||||
type="text"
|
||||
value={lastSub?.ruseUtztnFcltNm || ""}
|
||||
readOnly
|
||||
className={readOnlyInputClass}
|
||||
/>
|
||||
</FormUi>
|
||||
</div>
|
||||
<div className="col-span-3 mt-4">
|
||||
)}
|
||||
<div className="col-span-2 mt-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
control={activeForm.control}
|
||||
name="demandCst.cstrnCst"
|
||||
render={({ field }) => (
|
||||
<FormUi label="공급관로 표준공사비(백만원)">
|
||||
@@ -641,7 +697,7 @@ export default function ConCostForm({
|
||||
type="number"
|
||||
step="0.01"
|
||||
onChange={(e) => {
|
||||
if (readonly) return;
|
||||
if (readonly && !isSim) return;
|
||||
|
||||
field.onChange(Number(e.target.value));
|
||||
}}
|
||||
@@ -652,17 +708,16 @@ export default function ConCostForm({
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-3 mt-4 mb-3">
|
||||
<Label className="text-xl">
|
||||
처리시설 + 공급관로 총사업비(백만원)
|
||||
</Label>
|
||||
<Input
|
||||
placeholder=""
|
||||
type="number"
|
||||
step="0.001"
|
||||
readOnly={true}
|
||||
value={(totalConstCost ?? 0) + (cstrnCst ?? 0)}
|
||||
className={readOnlyInputClass}
|
||||
/>
|
||||
<FormUi label="처리시설 + 공급관로 총사업비(백만원)">
|
||||
<Input
|
||||
placeholder=""
|
||||
type="number"
|
||||
step="0.001"
|
||||
readOnly={true}
|
||||
value={(totalConstCost ?? 0) + (cstrnCst ?? 0)}
|
||||
className={readOnlyInputClass}
|
||||
/>
|
||||
</FormUi>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
/**
|
||||
* 공사비 산정표
|
||||
*/
|
||||
"use client";
|
||||
|
||||
import { useWrStore } from "@/app/store";
|
||||
@@ -5,29 +8,24 @@ import type {
|
||||
conCostFeeType,
|
||||
ContCostMap,
|
||||
} from "@/app/store/common/searchCode/type";
|
||||
import { UiTable } from "@common/business_components/ui";
|
||||
import { SelectBox, UiTable } from "@common/business_components/ui";
|
||||
import ListDiv from "@common/business_components/ui/list_div";
|
||||
import { Label, } from "@common/components/ui";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export function ConstCostFeeTable() {
|
||||
const { coRvw, constCostRes } = useWrStore((state) => state);
|
||||
const { coRvw, constCostRes, getConstCostFeeRtn } = useWrStore((state) => state);
|
||||
|
||||
const [constCostData, setConstCostData] = useState<ContCostMap[]>([]);
|
||||
const 기준년도 = constCostRes?.[0]?.yr ?? "";
|
||||
const [selectedYear, setSelectedYear] = useState<string>(String(new Date().getFullYear()));
|
||||
|
||||
// 연도 선택을 위한 옵션 생성 (예: 현재 연도부터 10년 전까지)
|
||||
const yearOptions = Array.from({ length: 10 }, (_, i) => {
|
||||
const year = new Date().getFullYear() - i;
|
||||
return { val: String(year), text: String(year) };
|
||||
});
|
||||
|
||||
const getFctDescription = (fctCd: string) => {
|
||||
const fctMap: { [key: string]: string } = {
|
||||
"001": "총인처리시설",
|
||||
"002": "염소소독",
|
||||
"003": "UV소독",
|
||||
"004": "전처리시설",
|
||||
"005": "RO",
|
||||
};
|
||||
|
||||
return fctMap[fctCd];
|
||||
}
|
||||
useEffect(() => {
|
||||
const fetchData = async (constCostRes: conCostFeeType[]) => {
|
||||
const tableData = constCostRes?.reduce(
|
||||
(acc: ContCostMap[], cur: conCostFeeType) => {
|
||||
const key = cur.fctCd;
|
||||
@@ -48,7 +46,7 @@ export function ConstCostFeeTable() {
|
||||
};
|
||||
acc?.push(target);
|
||||
}
|
||||
target[cur.fctCpc] = cur.stdCost;
|
||||
target[cur.fctCpc] = Number(cur.stdCost);
|
||||
|
||||
return acc;
|
||||
},
|
||||
@@ -60,19 +58,66 @@ export function ConstCostFeeTable() {
|
||||
gubun: getFctDescription(item.gubun) || item.gubun,
|
||||
};
|
||||
});
|
||||
setConstCostData(formattedTableData);
|
||||
|
||||
return formattedTableData;
|
||||
};
|
||||
|
||||
const getFctDescription = (fctCd: string) => {
|
||||
const fctMap: { [key: string]: string } = {
|
||||
"001": "총인처리시설",
|
||||
"002": "염소소독",
|
||||
"003": "UV소독",
|
||||
"004": "전처리시설",
|
||||
"005": "RO",
|
||||
};
|
||||
|
||||
return fctMap[fctCd];
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const fetchConstCost = async () => {
|
||||
const data = await fetchData(constCostRes);
|
||||
setConstCostData(data);
|
||||
};
|
||||
fetchConstCost();
|
||||
}, [constCostRes]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchConstCost = async () => {
|
||||
if (selectedYear) {
|
||||
const result = await getConstCostFeeRtn(selectedYear);
|
||||
if (result && result.length > 0) {
|
||||
const data = await fetchData(result);
|
||||
setConstCostData(data);
|
||||
} else {
|
||||
alert(selectedYear+'년 데이터가 없습니다.');
|
||||
}
|
||||
}
|
||||
}
|
||||
fetchConstCost();
|
||||
}, [selectedYear]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ListDiv className="h-[calc(100vh-60vh)] overflow-auto">
|
||||
<h3 className="mt-4 ml-2 text-2xl font-medium text-label">
|
||||
<ListDiv className="min-w-[600px] h-[48vh] overflow-auto">
|
||||
<div className="p-4">
|
||||
<h3 className="text-2xl font-medium text-label">
|
||||
공사비 산정표 보기
|
||||
</h3>
|
||||
<hr />
|
||||
<div className="col-span-5 flex items-center">
|
||||
<Label className="ml-2 mr-1 text-xl text-gray-600">기준년도</Label>
|
||||
<span>{기준년도}</span>
|
||||
<hr className="mt-4 mb-4"/>
|
||||
<div className="col-span-5 flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<Label className="mr-1 text-xl text-gray-600">기준년도</Label>
|
||||
<SelectBox
|
||||
className="h-full w-32 text-2xl"
|
||||
label=""
|
||||
selectClass={""}
|
||||
selectArray={yearOptions}
|
||||
value={selectedYear}
|
||||
onSelectValue={(val) => setSelectedYear(String(val))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<span className="mr-2 text-xl text-gray-600">단위: 백만원</span>
|
||||
</div>
|
||||
<UiTable
|
||||
headName={""}
|
||||
@@ -83,7 +128,10 @@ export function ConstCostFeeTable() {
|
||||
cellClick={undefined}
|
||||
onPageChange={() => { }}
|
||||
/>
|
||||
</ListDiv>
|
||||
</>
|
||||
<div className="px-3 text-lg text-gray-700 leading-relaxed">
|
||||
시설용량 구간내 공사비는 직선보간법에 의하여 산출
|
||||
</div>
|
||||
</div>
|
||||
</ListDiv>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,15 +2,24 @@
|
||||
|
||||
import { useWrStore } from "@/app/store";
|
||||
import type { rateFeeType } from "@/app/store/common/searchCode/type";
|
||||
import { UiTable } from "@common/business_components/ui";
|
||||
import { SelectBox, UiTable } from "@common/business_components/ui";
|
||||
import ListDiv from "@common/business_components/ui/list_div";
|
||||
import { Label } from "@common/components/ui";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export function RateFeeTableModal() {
|
||||
const { coRvw, rateFeeRes } = useWrStore((state) => state);
|
||||
const [reateFeeTable, setRateFeeTable] = useState<rateFeeType[]>([]);
|
||||
const 기준년도 = rateFeeRes?.[0]?.yr ?? "";
|
||||
const { coRvw, rateFeeRes, getRateFeeRtn } = useWrStore((state) => state);
|
||||
const [rateFeeTable, setRateFeeTable] = useState<rateFeeType[]>([]);
|
||||
const [selectedYear, setSelectedYear] = useState<string>(String(new Date().getFullYear()));
|
||||
|
||||
// 연도 선택을 위한 옵션 생성 (예: 현재 연도부터 10년 전까지)
|
||||
const yearOptions = Array.from({ length: 10 }, (_, i) => {
|
||||
const year = new Date().getFullYear() - i;
|
||||
return { val: String(year), text: String(year) };
|
||||
});
|
||||
|
||||
// const 기준년도 = rateFeeRes?.[0]?.yr ?? selectedYear; // 실제 데이터의 기준년도 또는 선택된 연도
|
||||
// setSelectedYear(rateFeeRes?.[0]?.yr ?? selectedYear);
|
||||
|
||||
const gubunLabelMap: Record<number, string> = {
|
||||
50: "5천만원까지",
|
||||
@@ -32,6 +41,11 @@ export function RateFeeTableModal() {
|
||||
500000: "5000억원까지",
|
||||
};
|
||||
|
||||
/**
|
||||
* rateFeeRes 데이터를 가공하여 테이블에 표시하기 적합한 형태로 변환합니다.
|
||||
* - 'SS' 코드를 제외하고, stdCost 기준으로 그룹화하여 BD, SE, CE 요율을 컬럼으로 피벗합니다.
|
||||
* - gubunLabelMap을 사용하여 stdCost에 해당하는 한글 라벨을 추가합니다.
|
||||
*/
|
||||
const fetchData = async (rateFeeRes: rateFeeType[]) => {
|
||||
const data = await rateFeeRes
|
||||
?.filter((item: rateFeeType) => {
|
||||
@@ -65,40 +79,68 @@ export function RateFeeTableModal() {
|
||||
|
||||
useEffect(() => {
|
||||
if (rateFeeRes?.length >= 0) {
|
||||
setSelectedYear(rateFeeRes?.[0]?.yr ?? selectedYear);
|
||||
fetchData(rateFeeRes).then((data) => {
|
||||
setRateFeeTable(data);
|
||||
});
|
||||
}
|
||||
}, [rateFeeRes]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ListDiv className="min-w-[600px] h-[calc(100vh-60vh)] overflow-auto">
|
||||
<div className="sticky top-0 z-10 grid grid-cols-6 gap-4 p-4 bg-white">
|
||||
<h3 className="col-span-6 text-2xl font-medium text-label">
|
||||
요율표 보기
|
||||
</h3>
|
||||
<hr className="col-span-6 border-gray-300" />
|
||||
// 컴포넌트 마운트 시 또는 선택된 연도가 변경될 때 데이터 조회
|
||||
useEffect(() => {
|
||||
const fetchRateFee = async () => {
|
||||
if (selectedYear) {
|
||||
const result = await getRateFeeRtn(selectedYear);
|
||||
if (result && result.length > 0) {
|
||||
const data = await fetchData(result);
|
||||
setRateFeeTable(data);
|
||||
} else {
|
||||
alert(selectedYear+'년 데이터가 없습니다.');
|
||||
}
|
||||
}
|
||||
}
|
||||
fetchRateFee();
|
||||
}, [selectedYear]);
|
||||
|
||||
<div className="col-span-5 flex items-center">
|
||||
<Label className="mr-1 text-xl text-gray-600">기준년도</Label>
|
||||
<span>{기준년도}</span>
|
||||
</div>
|
||||
<div className="col-span-6 pr-6">
|
||||
<UiTable
|
||||
className="min-w-full pr-12"
|
||||
key={"rateTable" + reateFeeTable?.length}
|
||||
headName={""}
|
||||
tableData={reateFeeTable || []}
|
||||
headlist={coRvw.rateFeeHeadList}
|
||||
onPageChange={() => {}}
|
||||
pageSize={0}
|
||||
total={0}
|
||||
cellClick={undefined}
|
||||
/>
|
||||
</div>
|
||||
return (
|
||||
<ListDiv className="min-w-[460px] h-[50vh] overflow-auto">
|
||||
<div className="sticky top-0 z-10 grid grid-cols-6 gap-4 p-4 bg-white">
|
||||
<h3 className="col-span-6 text-2xl font-medium text-label">
|
||||
시설부대경비 요율(%)
|
||||
</h3>
|
||||
<hr className="col-span-6 border-gray-300" />
|
||||
|
||||
<div className="col-span-5 flex items-center">
|
||||
<Label className="mr-1 text-xl text-gray-600">기준년도</Label>
|
||||
<SelectBox
|
||||
className="h-full w-32 text-2xl"
|
||||
label=""
|
||||
selectClass={""}
|
||||
selectArray={yearOptions}
|
||||
value={selectedYear}
|
||||
onSelectValue={(val) => setSelectedYear(String(val))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</ListDiv>
|
||||
</>
|
||||
<div className="col-span-6 pr-6">
|
||||
<UiTable
|
||||
className="min-w-full pr-12"
|
||||
key={"rateTable" + rateFeeTable?.length}
|
||||
headName={""}
|
||||
tableData={rateFeeTable || []}
|
||||
headlist={coRvw.rateFeeHeadList}
|
||||
onPageChange={() => {}}
|
||||
pageSize={0}
|
||||
total={0}
|
||||
cellClick={undefined}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-6 px-3 text-lg text-gray-700 leading-relaxed">
|
||||
출처 : 하수도분야 보조금 편성 및 실무요령
|
||||
<br />
|
||||
시설부대경비 및 감리비 요율적용시 공사비가 요율표의 중간에 있을 때는 직선보간법에 의하여 산출
|
||||
</div>
|
||||
</div>
|
||||
</ListDiv>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function PopoverUi({
|
||||
<>
|
||||
<Popover>
|
||||
<PopoverTrigger>{childrenTriger}</PopoverTrigger>
|
||||
<PopoverContent className={bodyClassName}>
|
||||
<PopoverContent className={bodyClassName} onOpenAutoFocus={(e) => e.preventDefault()}>
|
||||
{childrenBody}
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
Reference in New Issue
Block a user